1 /*
2  #
3  #  File            : CImg.h
4  #                    ( C++ header file )
5  #
6  #  Description     : The C++ Template Image Processing Toolkit.
7  #                    This file is the main component of the CImg Library project.
8  #                    ( http://cimg.sourceforge.net )
9  #
10  #  Project manager : David Tschumperle.
11  #                    ( http://tschumperle.users.greyc.fr/ )
12  #
13  #                    A complete list of contributors is available in file 'README.txt'
14  #                    distributed within the CImg package.
15  #
16  #  Licenses        : This file is 'dual-licensed', you have to choose one
17  #                    of the two licenses below to apply.
18  #
19  #                    CeCILL-C
20  #                    The CeCILL-C license is close to the GNU LGPL.
21  #                    ( http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html )
22  #
23  #                or  CeCILL v2.0
24  #                    The CeCILL license is compatible with the GNU GPL.
25  #                    ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html )
26  #
27  #  This software is governed either by the CeCILL or the CeCILL-C license
28  #  under French law and abiding by the rules of distribution of free software.
29  #  You can  use, modify and or redistribute the software under the terms of
30  #  the CeCILL or CeCILL-C licenses as circulated by CEA, CNRS and INRIA
31  #  at the following URL : "http://www.cecill.info".
32  #
33  #  As a counterpart to the access to the source code and  rights to copy,
34  #  modify and redistribute granted by the license, users are provided only
35  #  with a limited warranty  and the software's author,  the holder of the
36  #  economic rights,  and the successive licensors  have only  limited
37  #  liability.
38  #
39  #  In this respect, the user's attention is drawn to the risks associated
40  #  with loading,  using,  modifying and/or developing or reproducing the
41  #  software by the user in light of its specific status of free software,
42  #  that may mean  that it is complicated to manipulate,  and  that  also
43  #  therefore means  that it is reserved for developers  and  experienced
44  #  professionals having in-depth computer knowledge. Users are therefore
45  #  encouraged to load and test the software's suitability as regards their
46  #  requirements in conditions enabling the security of their systems and/or
47  #  data to be ensured and,  more generally, to use and operate it in the
48  #  same conditions as regards security.
49  #
50  #  The fact that you are presently reading this means that you have had
51  #  knowledge of the CeCILL and CeCILL-C licenses and that you accept its terms.
52  #
53 */
54 
55 // Set version number of the library.
56 #ifndef cimg_version
57 #define cimg_version 151
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 <ctime>
79 #include <exception>
80 
81 // Detect/configure OS variables.
82 //
83 // Define 'cimg_OS' to : '0' for an unknown OS (will try to minize library dependencies).
84 //                       '1' for a Unix-like OS (Linux, Solaris, BSD, MacOSX, Irix, ...).
85 //                       '2' for Microsoft Windows.
86 //                       (auto-detection is performed if 'cimg_OS' is not set by the user).
87 #ifndef cimg_OS
88 #if defined(unix)        || defined(__unix)      || defined(__unix__) \
89  || defined(linux)       || defined(__linux)     || defined(__linux__) \
90  || defined(sun)         || defined(__sun) \
91  || defined(BSD)         || defined(__OpenBSD__) || defined(__NetBSD__) \
92  || defined(__FreeBSD__) || defined __DragonFly__ \
93  || defined(sgi)         || defined(__sgi) \
94  || defined(__MACOSX__)  || defined(__APPLE__) \
95  || defined(__CYGWIN__)
96 #define cimg_OS 1
97 #elif defined(_MSC_VER) || defined(WIN32)  || defined(_WIN32) || defined(__WIN32__) \
98    || defined(WIN64)    || defined(_WIN64) || defined(__WIN64__)
99 #define cimg_OS 2
100 #else
101 #define cimg_OS 0
102 #endif
103 #elif !(cimg_OS==0 || cimg_OS==1 || cimg_OS==2)
104 #error CImg Library : Invalid configuration variable 'cimg_OS'.
105 #error (correct values are '0 = unknown OS', '1 = Unix-like OS', '2 = Microsoft Windows').
106 #endif
107 
108 // Disable silly warnings on some Microsoft VC++ compilers.
109 #ifdef _MSC_VER
110 #pragma warning(push)
111 #pragma warning(disable:4311)
112 #pragma warning(disable:4312)
113 #pragma warning(disable:4800)
114 #pragma warning(disable:4804)
115 #pragma warning(disable:4996)
116 #define _CRT_SECURE_NO_DEPRECATE 1
117 #define _CRT_NONSTDC_NO_DEPRECATE 1
118 #endif
119 
120 // Include OS-specific headers.
121 #if cimg_OS==1
122 #include <sys/types.h>
123 #include <sys/time.h>
124 #include <unistd.h>
125 #elif cimg_OS==2
126 #ifndef NOMINMAX
127 #define NOMINMAX
128 #endif
129 #include <windows.h>
130 #ifndef _WIN32_IE
131 #define _WIN32_IE 0x0400
132 #endif
133 #include <shlobj.h>
134 #include <process.h>
135 #include <io.h>
136 #define cimg_snprintf _snprintf
137 #define cimg_vsnprintf _vsnprintf
138 #endif
139 #ifndef cimg_snprintf
140 #include <stdio.h>
141 #define cimg_snprintf snprintf
142 #define cimg_vsnprintf vsnprintf
143 #endif
144 
145 // Configure filename separator.
146 //
147 // Filename separator is set by default to '/', except for Windows where it is '\'.
148 #ifndef cimg_file_separator
149 #if cimg_OS==2
150 #define cimg_file_separator '\\'
151 #else
152 #define cimg_file_separator '/'
153 #endif
154 #endif
155 
156 // Configure verbosity of output messages.
157 //
158 // Define 'cimg_verbosity' to : '0' to hide library messages (quiet mode).
159 //                              '1' to output library messages on the console.
160 //                              '2' to output library messages on a basic dialog window (default behavior).
161 //                              '3' to do as '1' + add extra warnings (may slow down the code !).
162 //                              '4' to do as '2' + add extra warnings (may slow down the code !).
163 //
164 // Define 'cimg_strict_warnings' to replace warning messages by exception throwns.
165 //
166 // Define 'cimg_use_vt100' to allow output of color messages on VT100-compatible terminals.
167 #ifndef cimg_verbosity
168 #define cimg_verbosity 2
169 #elif !(cimg_verbosity==0 || cimg_verbosity==1 || cimg_verbosity==2 || cimg_verbosity==3 || cimg_verbosity==4)
170 #error CImg Library : Configuration variable 'cimg_verbosity' is badly defined.
171 #error (should be { 0=quiet | 1=console | 2=dialog | 3=console+warnings | 4=dialog+warnings }).
172 #endif
173 
174 // Configure display framework.
175 //
176 // Define 'cimg_display' to : '0' to disable display capabilities.
177 //                            '1' to use the X-Window framework (X11).
178 //                            '2' to use the Microsoft GDI32 framework.
179 #ifndef cimg_display
180 #if cimg_OS==0
181 #define cimg_display 0
182 #elif cimg_OS==1
183 #if defined(__MACOSX__) || defined(__APPLE__)
184 #define cimg_display 1
185 #else
186 #define cimg_display 1
187 #endif
188 #elif cimg_OS==2
189 #define cimg_display 2
190 #endif
191 #elif !(cimg_display==0 || cimg_display==1 || cimg_display==2)
192 #error CImg Library : Configuration variable 'cimg_display' is badly defined.
193 #error (should be { 0=none | 1=X-Window (X11) | 2=Microsoft GDI32 }).
194 #endif
195 
196 // Include display-specific headers.
197 #if cimg_display==1
198 #include <X11/Xlib.h>
199 #include <X11/Xutil.h>
200 #include <X11/keysym.h>
201 #include <pthread.h>
202 #ifdef cimg_use_xshm
203 #include <sys/ipc.h>
204 #include <sys/shm.h>
205 #include <X11/extensions/XShm.h>
206 #endif
207 #ifdef cimg_use_xrandr
208 #include <X11/extensions/Xrandr.h>
209 #endif
210 #endif
211 #ifndef cimg_appname
212 #define cimg_appname "CImg"
213 #endif
214 
215 // Configure OpenMP support.
216 // (http://www.openmp.org)
217 //
218 // Define 'cimg_use_openmp' to enable OpenMP support.
219 //
220 // OpenMP directives may be used in a (very) few CImg functions to get
221 // advantages of multi-core CPUs.
222 #ifdef cimg_use_openmp
223 #include "omp.h"
224 #define _cimg_static
225 #else
226 #define _cimg_static static
227 #endif
228 
229 // Configure OpenCV support.
230 // (http://opencv.willowgarage.com/wiki/)
231 //
232 // Define 'cimg_use_opencv' to enable OpenCV support.
233 //
234 // OpenCV library may be used to access images from cameras
235 // (see method 'CImg<T>::load_camera()').
236 #ifdef cimg_use_opencv
237 #ifdef True
238 #undef True
239 #define _cimg_redefine_True
240 #endif
241 #ifdef False
242 #undef False
243 #define _cimg_redefine_False
244 #endif
245 #include <cstddef>
246 #include "cv.h"
247 #include "highgui.h"
248 #endif
249 
250 // Configure LibPNG support.
251 // (http://www.libpng.org)
252 //
253 // Define 'cimg_use_png' to enable LibPNG support.
254 //
255 // PNG library may be used to get a native support of '.png' files.
256 // (see methods 'CImg<T>::{load,save}_png()'.
257 #ifdef cimg_use_png
258 extern "C" {
259 #include "png.h"
260 }
261 #endif
262 
263 // Configure LibJPEG support.
264 // (http://en.wikipedia.org/wiki/Libjpeg)
265 //
266 // Define 'cimg_use_jpeg' to enable LibJPEG support.
267 //
268 // JPEG library may be used to get a native support of '.jpg' files.
269 // (see methods 'CImg<T>::{load,save}_jpeg()').
270 #ifdef cimg_use_jpeg
271 extern "C" {
272 #include "jpeglib.h"
273 #include "setjmp.h"
274 }
275 #endif
276 
277 // Configure LibTIFF support.
278 // (http://www.libtiff.org)
279 //
280 // Define 'cimg_use_tiff' to enable LibTIFF support.
281 //
282 // TIFF library may be used to get a native support of '.tif' files.
283 // (see methods 'CImg[List]<T>::{load,save}_tiff()').
284 #ifdef cimg_use_tiff
285 extern "C" {
286 #include "tiffio.h"
287 }
288 #endif
289 
290 // Configure LibMINC2 support.
291 // (http://en.wikibooks.org/wiki/MINC/Reference/MINC2.0_File_Format_Reference)
292 //
293 // Define 'cimg_use_minc2' to enable LibMINC2 support.
294 //
295 // MINC2 library may be used to get a native support of '.mnc' files.
296 // (see methods 'CImg<T>::{load,save}_minc2()').
297 #ifdef cimg_use_minc2
298 #include "minc_io_simple_volume.h"
299 #include "minc_1_simple.h"
300 #include "minc_1_simple_rw.h"
301 #endif
302 
303 // Configure FFMPEG support.
304 // (http://www.ffmpeg.org)
305 //
306 // Define 'cimg_use_ffmpeg' to enable FFMPEG lib support.
307 //
308 // Avcodec and Avformat libraries from FFMPEG may be used
309 // to get a native support of various video file formats.
310 // (see methods 'CImg[List]<T>::load_ffmpeg()').
311 #ifdef cimg_use_ffmpeg
312 #if (defined(_STDINT_H) || defined(_STDINT_H_)) && !defined(UINT64_C)
313 #warning "__STDC_CONSTANT_MACROS has to be defined before including <stdint.h>, this file will probably not compile."
314 #endif
315 #ifndef __STDC_CONSTANT_MACROS
316 #define __STDC_CONSTANT_MACROS // ...or stdint.h wont' define UINT64_C, needed by libavutil
317 #endif
318 extern "C" {
319 #include "avformat.h"
320 #include "avcodec.h"
321 #include "swscale.h"
322 }
323 #endif
324 
325 // Configure Zlib support.
326 // (http://www.zlib.net)
327 //
328 // Define 'cimg_use_zlib' to enable Zlib support.
329 //
330 // Zlib library may be used to allow compressed data in '.cimgz' files
331 // (see methods 'CImg[List]<T>::{load,save}_cimg()').
332 #ifdef cimg_use_zlib
333 extern "C" {
334 #include "zlib.h"
335 }
336 #endif
337 
338 // Configure Magick++ support.
339 // (http://www.imagemagick.org/Magick++)
340 //
341 // Define 'cimg_use_magick' to enable Magick++ support.
342 //
343 // Magick++ library may be used to get a native support of various image file formats.
344 // (see methods 'CImg<T>::{load,save}()').
345 #ifdef cimg_use_magick
346 #include "Magick++.h"
347 #endif
348 
349 // Configure FFTW3 support.
350 // (http://www.fftw.org)
351 //
352 // Define 'cimg_use_fftw3' to enable libFFTW3 support.
353 //
354 // FFTW3 library may be used to efficiently compute the Fast Fourier Transform
355 // of image data, without restriction on the image size.
356 // (see method 'CImg[List]<T>::FFT()').
357 #ifdef cimg_use_fftw3
358 extern "C" {
359 #include "fftw3.h"
360 }
361 #endif
362 
363 // Configure LibBoard support.
364 // (http://libboard.sourceforge.net/)
365 //
366 // Define 'cimg_use_board' to enable Board support.
367 //
368 // Board library may be used to draw 3d objects in vector-graphics canvas
369 // that can be saved as '.ps' or '.svg' files afterwards.
370 // (see method 'CImg<T>::draw_object3d()').
371 #ifdef cimg_use_board
372 #ifdef None
373 #undef None
374 #define _cimg_redefine_None
375 #endif
376 #include "Board.h"
377 #endif
378 
379 // Configure OpenEXR support.
380 // (http://www.openexr.com/)
381 //
382 // Define 'cimg_use_openexr' to enable OpenEXR support.
383 //
384 // OpenEXR library may be used to get a native support of '.exr' files.
385 // (see methods 'CImg<T>::{load,save}_exr()').
386 #ifdef cimg_use_openexr
387 #include "ImfRgbaFile.h"
388 #include "ImfInputFile.h"
389 #include "ImfChannelList.h"
390 #include "ImfMatrixAttribute.h"
391 #include "ImfArray.h"
392 #endif
393 
394 // Lapack configuration.
395 // (http://www.netlib.org/lapack)
396 //
397 // Define 'cimg_use_lapack' to enable LAPACK support.
398 //
399 // Lapack library may be used in several CImg methods to speed up
400 // matrix computations (eigenvalues, inverse, ...).
401 #ifdef cimg_use_lapack
402 extern "C" {
403   extern void sgetrf_(int*, int*, float*, int*, int*, int*);
404   extern void sgetri_(int*, float*, int*, int*, float*, int*, int*);
405   extern void sgetrs_(char*, int*, int*, float*, int*, int*, float*, int*, int*);
406   extern void sgesvd_(char*, char*, int*, int*, float*, int*, float*, float*, int*, float*, int*, float*, int*, int*);
407   extern void ssyev_(char*, char*, int*, float*, int*, float*, float*, int*, int*);
408   extern void dgetrf_(int*, int*, double*, int*, int*, int*);
409   extern void dgetri_(int*, double*, int*, int*, double*, int*, int*);
410   extern void dgetrs_(char*, int*, int*, double*, int*, int*, double*, int*, int*);
411   extern void dgesvd_(char*, char*, int*, int*, double*, int*, double*, double*, int*, double*, int*, double*, int*, int*);
412   extern void dsyev_(char*, char*, int*, double*, int*, double*, double*, int*, int*);
413   extern void dgels_(char*, int*,int*,int*,double*,int*,double*,int*,double*,int*,int*);
414   extern void sgels_(char*, int*,int*,int*,float*,int*,float*,int*,float*,int*,int*);
415 }
416 #endif
417 
418 // Check if min/max/PI macros are defined.
419 //
420 // CImg does not compile if macros 'min', 'max' or 'PI' are defined,
421 // because it redefines functions min(), max() and const variable PI in the cimg:: namespace.
422 // so it '#undef' these macros if necessary, and restore them to reasonable
423 // values at the end of this file.
424 #ifdef min
425 #undef min
426 #define _cimg_redefine_min
427 #endif
428 #ifdef max
429 #undef max
430 #define _cimg_redefine_max
431 #endif
432 #ifdef PI
433 #undef PI
434 #define _cimg_redefine_PI
435 #endif
436 
437 // Define 'cimg_library' namespace suffix.
438 //
439 // You may want to add a suffix to the 'cimg_library' namespace, for instance if you need to work
440 // with several versions of the library at the same time.
441 #ifdef cimg_namespace_suffix
442 #define __cimg_library_suffixed(s) cimg_library_##s
443 #define _cimg_library_suffixed(s) __cimg_library_suffixed(s)
444 #define cimg_library_suffixed _cimg_library_suffixed(cimg_namespace_suffix)
445 #else
446 #define cimg_library_suffixed cimg_library
447 #endif
448 
449 /*------------------------------------------------------------------------------
450   #
451   # Define user-friendly macros.
452   #
453   # These CImg macros are prefixed by 'cimg_' and can be used safely in your own
454   # code. They are useful to parse command line options, or to write image loops.
455   #
456   ------------------------------------------------------------------------------*/
457 
458 // Macros to define program usage, and retrieve command line arguments.
459 #define cimg_usage(usage) cimg_library_suffixed::cimg::option((char*)0,argc,argv,(char*)0,usage,false)
460 #define cimg_help(str) cimg_library_suffixed::cimg::option((char*)0,argc,argv,str,(char*)0)
461 #define cimg_option(name,defaut,usage) cimg_library_suffixed::cimg::option(name,argc,argv,defaut,usage)
462 #define cimg_argument(pos) cimg_library_suffixed::cimg::argument(pos,argc,argv)
463 #define cimg_argument1(pos,s0) cimg_library_suffixed::cimg::argument(pos,argc,argv,1,s0)
464 #define cimg_argument2(pos,s0,s1) cimg_library_suffixed::cimg::argument(pos,argc,argv,2,s0,s1)
465 #define cimg_argument3(pos,s0,s1,s2) cimg_library_suffixed::cimg::argument(pos,argc,argv,3,s0,s1,s2)
466 #define cimg_argument4(pos,s0,s1,s2,s3) cimg_library_suffixed::cimg::argument(pos,argc,argv,4,s0,s1,s2,s3)
467 #define cimg_argument5(pos,s0,s1,s2,s3,s4) cimg_library_suffixed::cimg::argument(pos,argc,argv,5,s0,s1,s2,s3,s4)
468 #define cimg_argument6(pos,s0,s1,s2,s3,s4,s5) cimg_library_suffixed::cimg::argument(pos,argc,argv,6,s0,s1,s2,s3,s4,s5)
469 #define cimg_argument7(pos,s0,s1,s2,s3,s4,s5,s6) cimg_library_suffixed::cimg::argument(pos,argc,argv,7,s0,s1,s2,s3,s4,s5,s6)
470 #define cimg_argument8(pos,s0,s1,s2,s3,s4,s5,s6,s7) cimg_library_suffixed::cimg::argument(pos,argc,argv,8,s0,s1,s2,s3,s4,s5,s6,s7)
471 #define cimg_argument9(pos,s0,s1,s2,s3,s4,s5,s6,s7,s8) cimg_library_suffixed::cimg::argument(pos,argc,argv,9,s0,s1,s2,s3,s4,s5,s6,s7,s8)
472 
473 // Macros to define and manipulate local neighborhoods.
474 #define CImg_2x2(I,T) T I[4]; \
475                       T& I##cc = I[0]; T& I##nc = I[1]; \
476                       T& I##cn = I[2]; T& I##nn = I[3]; \
477                       I##cc = I##nc = \
478                       I##cn = I##nn = 0
479 
480 #define CImg_3x3(I,T) T I[9]; \
481                       T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; \
482                       T& I##pc = I[3]; T& I##cc = I[4]; T& I##nc = I[5]; \
483                       T& I##pn = I[6]; T& I##cn = I[7]; T& I##nn = I[8]; \
484                       I##pp = I##cp = I##np = \
485                       I##pc = I##cc = I##nc = \
486                       I##pn = I##cn = I##nn = 0
487 
488 #define CImg_4x4(I,T) T I[16]; \
489                       T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; T& I##ap = I[3]; \
490                       T& I##pc = I[4]; T& I##cc = I[5]; T& I##nc = I[6]; T& I##ac = I[7]; \
491                       T& I##pn = I[8]; T& I##cn = I[9]; T& I##nn = I[10]; T& I##an = I[11]; \
492                       T& I##pa = I[12]; T& I##ca = I[13]; T& I##na = I[14]; T& I##aa = I[15]; \
493                       I##pp = I##cp = I##np = I##ap = \
494                       I##pc = I##cc = I##nc = I##ac = \
495                       I##pn = I##cn = I##nn = I##an = \
496                       I##pa = I##ca = I##na = I##aa = 0
497 
498 #define CImg_5x5(I,T) T I[25]; \
499                       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]; \
500                       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]; \
501                       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]; \
502                       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]; \
503                       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]; \
504                       I##bb = I##pb = I##cb = I##nb = I##ab = \
505                       I##bp = I##pp = I##cp = I##np = I##ap = \
506                       I##bc = I##pc = I##cc = I##nc = I##ac = \
507                       I##bn = I##pn = I##cn = I##nn = I##an = \
508                       I##ba = I##pa = I##ca = I##na = I##aa = 0
509 
510 #define CImg_2x2x2(I,T) T I[8]; \
511                       T& I##ccc = I[0]; T& I##ncc = I[1]; \
512                       T& I##cnc = I[2]; T& I##nnc = I[3]; \
513                       T& I##ccn = I[4]; T& I##ncn = I[5]; \
514                       T& I##cnn = I[6]; T& I##nnn = I[7]; \
515                       I##ccc = I##ncc = \
516                       I##cnc = I##nnc = \
517                       I##ccn = I##ncn = \
518                       I##cnn = I##nnn = 0
519 
520 #define CImg_3x3x3(I,T) T I[27]; \
521                       T& I##ppp = I[0]; T& I##cpp = I[1]; T& I##npp = I[2]; \
522                       T& I##pcp = I[3]; T& I##ccp = I[4]; T& I##ncp = I[5]; \
523                       T& I##pnp = I[6]; T& I##cnp = I[7]; T& I##nnp = I[8]; \
524                       T& I##ppc = I[9]; T& I##cpc = I[10]; T& I##npc = I[11]; \
525                       T& I##pcc = I[12]; T& I##ccc = I[13]; T& I##ncc = I[14]; \
526                       T& I##pnc = I[15]; T& I##cnc = I[16]; T& I##nnc = I[17]; \
527                       T& I##ppn = I[18]; T& I##cpn = I[19]; T& I##npn = I[20]; \
528                       T& I##pcn = I[21]; T& I##ccn = I[22]; T& I##ncn = I[23]; \
529                       T& I##pnn = I[24]; T& I##cnn = I[25]; T& I##nnn = I[26]; \
530                       I##ppp = I##cpp = I##npp = \
531                       I##pcp = I##ccp = I##ncp = \
532                       I##pnp = I##cnp = I##nnp = \
533                       I##ppc = I##cpc = I##npc = \
534                       I##pcc = I##ccc = I##ncc = \
535                       I##pnc = I##cnc = I##nnc = \
536                       I##ppn = I##cpn = I##npn = \
537                       I##pcn = I##ccn = I##ncn = \
538                       I##pnn = I##cnn = I##nnn = 0
539 
540 #define cimg_get2x2(img,x,y,z,c,I,T) \
541   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), I[3] = (T)(img)(_n1##x,_n1##y,z,c)
542 
543 #define cimg_get3x3(img,x,y,z,c,I,T) \
544   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), I[3] = (T)(img)(_p1##x,y,z,c), \
545   I[4] = (T)(img)(x,y,z,c), I[5] = (T)(img)(_n1##x,y,z,c), I[6] = (T)(img)(_p1##x,_n1##y,z,c), I[7] = (T)(img)(x,_n1##y,z,c), \
546   I[8] = (T)(img)(_n1##x,_n1##y,z,c)
547 
548 #define cimg_get4x4(img,x,y,z,c,I,T) \
549   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), I[3] = (T)(img)(_n2##x,_p1##y,z,c), \
550   I[4] = (T)(img)(_p1##x,y,z,c), I[5] = (T)(img)(x,y,z,c), I[6] = (T)(img)(_n1##x,y,z,c), I[7] = (T)(img)(_n2##x,y,z,c), \
551   I[8] = (T)(img)(_p1##x,_n1##y,z,c), 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), \
552   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), I[15] = (T)(img)(_n2##x,_n2##y,z,c)
553 
554 #define cimg_get5x5(img,x,y,z,c,I,T) \
555   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), I[3] = (T)(img)(_n1##x,_p2##y,z,c), \
556   I[4] = (T)(img)(_n2##x,_p2##y,z,c), I[5] = (T)(img)(_p2##x,_p1##y,z,c), I[6] = (T)(img)(_p1##x,_p1##y,z,c), I[7] = (T)(img)(x,_p1##y,z,c), \
557   I[8] = (T)(img)(_n1##x,_p1##y,z,c), 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), \
558   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), I[15] = (T)(img)(_p2##x,_n1##y,z,c), \
559   I[16] = (T)(img)(_p1##x,_n1##y,z,c), I[17] = (T)(img)(x,_n1##y,z,c), I[18] = (T)(img)(_n1##x,_n1##y,z,c), I[19] = (T)(img)(_n2##x,_n1##y,z,c), \
560   I[20] = (T)(img)(_p2##x,_n2##y,z,c), 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), \
561   I[24] = (T)(img)(_n2##x,_n2##y,z,c)
562 
563 #define cimg_get6x6(img,x,y,z,c,I,T) \
564  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), I[3] = (T)(img)(_n1##x,_p2##y,z,c), \
565  I[4] = (T)(img)(_n2##x,_p2##y,z,c), I[5] = (T)(img)(_n3##x,_p2##y,z,c), I[6] = (T)(img)(_p2##x,_p1##y,z,c), I[7] = (T)(img)(_p1##x,_p1##y,z,c), \
566  I[8] = (T)(img)(x,_p1##y,z,c), 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), \
567  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), I[15] = (T)(img)(_n1##x,y,z,c), \
568  I[16] = (T)(img)(_n2##x,y,z,c), I[17] = (T)(img)(_n3##x,y,z,c), I[18] = (T)(img)(_p2##x,_n1##y,z,c), I[19] = (T)(img)(_p1##x,_n1##y,z,c), \
569  I[20] = (T)(img)(x,_n1##y,z,c), 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), \
570  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), I[27] = (T)(img)(_n1##x,_n2##y,z,c), \
571  I[28] = (T)(img)(_n2##x,_n2##y,z,c), I[29] = (T)(img)(_n3##x,_n2##y,z,c), I[30] = (T)(img)(_p2##x,_n3##y,z,c), I[31] = (T)(img)(_p1##x,_n3##y,z,c), \
572  I[32] = (T)(img)(x,_n3##y,z,c), 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)
573 
574 #define cimg_get7x7(img,x,y,z,c,I,T) \
575  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), I[3] = (T)(img)(x,_p3##y,z,c), \
576  I[4] = (T)(img)(_n1##x,_p3##y,z,c), I[5] = (T)(img)(_n2##x,_p3##y,z,c), I[6] = (T)(img)(_n3##x,_p3##y,z,c), I[7] = (T)(img)(_p3##x,_p2##y,z,c), \
577  I[8] = (T)(img)(_p2##x,_p2##y,z,c), 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), \
578  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), I[15] = (T)(img)(_p2##x,_p1##y,z,c), \
579  I[16] = (T)(img)(_p1##x,_p1##y,z,c), I[17] = (T)(img)(x,_p1##y,z,c), I[18] = (T)(img)(_n1##x,_p1##y,z,c), I[19] = (T)(img)(_n2##x,_p1##y,z,c), \
580  I[20] = (T)(img)(_n3##x,_p1##y,z,c), 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), \
581  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), I[27] = (T)(img)(_n3##x,y,z,c), \
582  I[28] = (T)(img)(_p3##x,_n1##y,z,c), I[29] = (T)(img)(_p2##x,_n1##y,z,c), I[30] = (T)(img)(_p1##x,_n1##y,z,c), I[31] = (T)(img)(x,_n1##y,z,c), \
583  I[32] = (T)(img)(_n1##x,_n1##y,z,c), 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), \
584  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), I[39] = (T)(img)(_n1##x,_n2##y,z,c), \
585  I[40] = (T)(img)(_n2##x,_n2##y,z,c), I[41] = (T)(img)(_n3##x,_n2##y,z,c), I[42] = (T)(img)(_p3##x,_n3##y,z,c), I[43] = (T)(img)(_p2##x,_n3##y,z,c), \
586  I[44] = (T)(img)(_p1##x,_n3##y,z,c), 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), \
587  I[48] = (T)(img)(_n3##x,_n3##y,z,c)
588 
589 #define cimg_get8x8(img,x,y,z,c,I,T) \
590  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), I[3] = (T)(img)(x,_p3##y,z,c), \
591  I[4] = (T)(img)(_n1##x,_p3##y,z,c), I[5] = (T)(img)(_n2##x,_p3##y,z,c), I[6] = (T)(img)(_n3##x,_p3##y,z,c), I[7] = (T)(img)(_n4##x,_p3##y,z,c), \
592  I[8] = (T)(img)(_p3##x,_p2##y,z,c), 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), \
593  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), I[15] = (T)(img)(_n4##x,_p2##y,z,c), \
594  I[16] = (T)(img)(_p3##x,_p1##y,z,c), I[17] = (T)(img)(_p2##x,_p1##y,z,c), I[18] = (T)(img)(_p1##x,_p1##y,z,c), I[19] = (T)(img)(x,_p1##y,z,c), \
595  I[20] = (T)(img)(_n1##x,_p1##y,z,c), 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), \
596  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), I[27] = (T)(img)(x,y,z,c), \
597  I[28] = (T)(img)(_n1##x,y,z,c), I[29] = (T)(img)(_n2##x,y,z,c), I[30] = (T)(img)(_n3##x,y,z,c), I[31] = (T)(img)(_n4##x,y,z,c), \
598  I[32] = (T)(img)(_p3##x,_n1##y,z,c), 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), \
599  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), I[39] = (T)(img)(_n4##x,_n1##y,z,c), \
600  I[40] = (T)(img)(_p3##x,_n2##y,z,c), I[41] = (T)(img)(_p2##x,_n2##y,z,c), I[42] = (T)(img)(_p1##x,_n2##y,z,c), I[43] = (T)(img)(x,_n2##y,z,c), \
601  I[44] = (T)(img)(_n1##x,_n2##y,z,c), 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), \
602  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), I[51] = (T)(img)(x,_n3##y,z,c), \
603  I[52] = (T)(img)(_n1##x,_n3##y,z,c), I[53] = (T)(img)(_n2##x,_n3##y,z,c), I[54] = (T)(img)(_n3##x,_n3##y,z,c), I[55] = (T)(img)(_n4##x,_n3##y,z,c), \
604  I[56] = (T)(img)(_p3##x,_n4##y,z,c), 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), \
605  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), I[63] = (T)(img)(_n4##x,_n4##y,z,c);
606 
607 #define cimg_get9x9(img,x,y,z,c,I,T) \
608  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), I[3] = (T)(img)(_p1##x,_p4##y,z,c), \
609  I[4] = (T)(img)(x,_p4##y,z,c), I[5] = (T)(img)(_n1##x,_p4##y,z,c), I[6] = (T)(img)(_n2##x,_p4##y,z,c), I[7] = (T)(img)(_n3##x,_p4##y,z,c), \
610  I[8] = (T)(img)(_n4##x,_p4##y,z,c), 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), \
611  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), I[15] = (T)(img)(_n2##x,_p3##y,z,c), \
612  I[16] = (T)(img)(_n3##x,_p3##y,z,c), I[17] = (T)(img)(_n4##x,_p3##y,z,c), I[18] = (T)(img)(_p4##x,_p2##y,z,c), I[19] = (T)(img)(_p3##x,_p2##y,z,c), \
613  I[20] = (T)(img)(_p2##x,_p2##y,z,c), 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), \
614  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), I[27] = (T)(img)(_p4##x,_p1##y,z,c), \
615  I[28] = (T)(img)(_p3##x,_p1##y,z,c), I[29] = (T)(img)(_p2##x,_p1##y,z,c), I[30] = (T)(img)(_p1##x,_p1##y,z,c), I[31] = (T)(img)(x,_p1##y,z,c), \
616  I[32] = (T)(img)(_n1##x,_p1##y,z,c), 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), \
617  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), I[39] = (T)(img)(_p1##x,y,z,c), \
618  I[40] = (T)(img)(x,y,z,c), I[41] = (T)(img)(_n1##x,y,z,c), I[42] = (T)(img)(_n2##x,y,z,c), I[43] = (T)(img)(_n3##x,y,z,c), \
619  I[44] = (T)(img)(_n4##x,y,z,c), 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), \
620  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), I[51] = (T)(img)(_n2##x,_n1##y,z,c), \
621  I[52] = (T)(img)(_n3##x,_n1##y,z,c), I[53] = (T)(img)(_n4##x,_n1##y,z,c), I[54] = (T)(img)(_p4##x,_n2##y,z,c), I[55] = (T)(img)(_p3##x,_n2##y,z,c), \
622  I[56] = (T)(img)(_p2##x,_n2##y,z,c), 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), \
623  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), I[63] = (T)(img)(_p4##x,_n3##y,z,c), \
624  I[64] = (T)(img)(_p3##x,_n3##y,z,c), I[65] = (T)(img)(_p2##x,_n3##y,z,c), I[66] = (T)(img)(_p1##x,_n3##y,z,c), I[67] = (T)(img)(x,_n3##y,z,c), \
625  I[68] = (T)(img)(_n1##x,_n3##y,z,c), 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), \
626  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), I[75] = (T)(img)(_p1##x,_n4##y,z,c), \
627  I[76] = (T)(img)(x,_n4##y,z,c), I[77] = (T)(img)(_n1##x,_n4##y,z,c), I[78] = (T)(img)(_n2##x,_n4##y,z,c), I[79] = (T)(img)(_n3##x,_n4##y,z,c), \
628  I[80] = (T)(img)(_n4##x,_n4##y,z,c)
629 
630 #define cimg_get2x2x2(img,x,y,z,c,I,T) \
631   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), I[3] = (T)(img)(_n1##x,_n1##y,z,c), \
632   I[4] = (T)(img)(x,y,_n1##z,c), I[5] = (T)(img)(_n1##x,y,_n1##z,c), I[6] = (T)(img)(x,_n1##y,_n1##z,c), I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)
633 
634 #define cimg_get3x3x3(img,x,y,z,c,I,T) \
635   I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c), I[1] = (T)(img)(x,_p1##y,_p1##z,c), I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c), \
636   I[3] = (T)(img)(_p1##x,y,_p1##z,c), I[4] = (T)(img)(x,y,_p1##z,c), I[5] = (T)(img)(_n1##x,y,_p1##z,c), \
637   I[6] = (T)(img)(_p1##x,_n1##y,_p1##z,c), I[7] = (T)(img)(x,_n1##y,_p1##z,c), I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c), \
638   I[9] = (T)(img)(_p1##x,_p1##y,z,c), I[10] = (T)(img)(x,_p1##y,z,c), I[11] = (T)(img)(_n1##x,_p1##y,z,c), \
639   I[12] = (T)(img)(_p1##x,y,z,c), I[13] = (T)(img)(x,y,z,c), I[14] = (T)(img)(_n1##x,y,z,c), \
640   I[15] = (T)(img)(_p1##x,_n1##y,z,c), I[16] = (T)(img)(x,_n1##y,z,c), I[17] = (T)(img)(_n1##x,_n1##y,z,c), \
641   I[18] = (T)(img)(_p1##x,_p1##y,_n1##z,c), I[19] = (T)(img)(x,_p1##y,_n1##z,c), I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c), \
642   I[21] = (T)(img)(_p1##x,y,_n1##z,c), I[22] = (T)(img)(x,y,_n1##z,c), I[23] = (T)(img)(_n1##x,y,_n1##z,c), \
643   I[24] = (T)(img)(_p1##x,_n1##y,_n1##z,c), I[25] = (T)(img)(x,_n1##y,_n1##z,c), I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)
644 
645 // Macros to perform various image loops.
646 //
647 // These macros are simpler to use than loops with C++ iterators.
648 #define cimg_for(img,ptrs,T_ptrs) for (T_ptrs *ptrs = (img)._data, *_max##ptrs = (img)._data + (img).size(); ptrs<_max##ptrs; ++ptrs)
649 #define cimg_rof(img,ptrs,T_ptrs) for (T_ptrs *ptrs = (img)._data + (img).size(); (ptrs--)>(img)._data; )
650 #define cimg_foroff(img,off) for (unsigned long off = 0, _max##off = (img).size(); off<_max##off; ++off)
651 
652 #define cimg_for1(bound,i) for (int i = 0; i<(int)(bound); ++i)
653 #define cimg_forX(img,x) cimg_for1((img)._width,x)
654 #define cimg_forY(img,y) cimg_for1((img)._height,y)
655 #define cimg_forZ(img,z) cimg_for1((img)._depth,z)
656 #define cimg_forC(img,c) cimg_for1((img)._spectrum,c)
657 #define cimg_forXY(img,x,y) cimg_forY(img,y) cimg_forX(img,x)
658 #define cimg_forXZ(img,x,z) cimg_forZ(img,z) cimg_forX(img,x)
659 #define cimg_forYZ(img,y,z) cimg_forZ(img,z) cimg_forY(img,y)
660 #define cimg_forXC(img,x,c) cimg_forC(img,c) cimg_forX(img,x)
661 #define cimg_forYC(img,y,c) cimg_forC(img,c) cimg_forY(img,y)
662 #define cimg_forZC(img,z,c) cimg_forC(img,c) cimg_forZ(img,z)
663 #define cimg_forXYZ(img,x,y,z) cimg_forZ(img,z) cimg_forXY(img,x,y)
664 #define cimg_forXYC(img,x,y,c) cimg_forC(img,c) cimg_forXY(img,x,y)
665 #define cimg_forXZC(img,x,z,c) cimg_forC(img,c) cimg_forXZ(img,x,z)
666 #define cimg_forYZC(img,y,z,c) cimg_forC(img,c) cimg_forYZ(img,y,z)
667 #define cimg_forXYZC(img,x,y,z,c) cimg_forC(img,c) cimg_forXYZ(img,x,y,z)
668 
669 #define cimg_for_in1(bound,i0,i1,i) \
670  for (int i = (int)(i0)<0?0:(int)(i0), _max##i = (int)(i1)<(int)(bound)?(int)(i1):(int)(bound)-1; i<=_max##i; ++i)
671 #define cimg_for_inX(img,x0,x1,x) cimg_for_in1((img)._width,x0,x1,x)
672 #define cimg_for_inY(img,y0,y1,y) cimg_for_in1((img)._height,y0,y1,y)
673 #define cimg_for_inZ(img,z0,z1,z) cimg_for_in1((img)._depth,z0,z1,z)
674 #define cimg_for_inC(img,c0,c1,c) cimg_for_in1((img)._spectrum,c0,c1,c)
675 #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)
676 #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)
677 #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)
678 #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)
679 #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)
680 #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)
681 #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)
682 #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)
683 #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)
684 #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)
685 #define cimg_for_inXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
686 #define cimg_for_insideX(img,x,n) cimg_for_inX(img,n,(img)._width-1-(n),x)
687 #define cimg_for_insideY(img,y,n) cimg_for_inY(img,n,(img)._height-1-(n),y)
688 #define cimg_for_insideZ(img,z,n) cimg_for_inZ(img,n,(img)._depth-1-(n),z)
689 #define cimg_for_insideC(img,c,n) cimg_for_inC(img,n,(img)._spectrum-1-(n),c)
690 #define cimg_for_insideXY(img,x,y,n) cimg_for_inXY(img,n,n,(img)._width-1-(n),(img)._height-1-(n),x,y)
691 #define cimg_for_insideXYZ(img,x,y,z,n) cimg_for_inXYZ(img,n,n,n,(img)._width-1-(n),(img)._height-1-(n),(img)._depth-1-(n),x,y,z)
692 #define cimg_for_insideXYZC(img,x,y,z,c,n) cimg_for_inXYZ(img,n,n,n,(img)._width-1-(n),(img)._height-1-(n),(img)._depth-1-(n),x,y,z)
693 
694 #define cimg_for_out1(boundi,i0,i1,i) \
695  for (int i = (int)(i0)>0?0:(int)(i1)+1; i<(int)(boundi); ++i, i = i==(int)(i0)?(int)(i1)+1:i)
696 #define cimg_for_out2(boundi,boundj,i0,j0,i1,j1,i,j) \
697  for (int j = 0; j<(int)(boundj); ++j) \
698  for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j?0:(int)(i0)>0?0:(int)(i1)+1; i<(int)(boundi); \
699   ++i, i = _n1j?i:(i==(int)(i0)?(int)(i1)+1:i))
700 #define cimg_for_out3(boundi,boundj,boundk,i0,j0,k0,i1,j1,k1,i,j,k) \
701  for (int k = 0; k<(int)(boundk); ++k) \
702  for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \
703  for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k?0:(int)(i0)>0?0:(int)(i1)+1; i<(int)(boundi); \
704   ++i, i = _n1j || _n1k?i:(i==(int)(i0)?(int)(i1)+1:i))
705 #define cimg_for_out4(boundi,boundj,boundk,boundl,i0,j0,k0,l0,i1,j1,k1,l1,i,j,k,l) \
706  for (int l = 0; l<(int)(boundl); ++l) \
707  for (int _n1l = (int)(l<(int)(l0) || l>(int)(l1)), k = 0; k<(int)(boundk); ++k) \
708  for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \
709  for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k || _n1l?0:(int)(i0)>0?0:(int)(i1)+1; i<(int)(boundi); \
710   ++i, i = _n1j || _n1k || _n1l?i:(i==(int)(i0)?(int)(i1)+1:i))
711 #define cimg_for_outX(img,x0,x1,x) cimg_for_out1((img)._width,x0,x1,x)
712 #define cimg_for_outY(img,y0,y1,y) cimg_for_out1((img)._height,y0,y1,y)
713 #define cimg_for_outZ(img,z0,z1,z) cimg_for_out1((img)._depth,z0,z1,z)
714 #define cimg_for_outC(img,c0,c1,c) cimg_for_out1((img)._spectrum,c0,c1,c)
715 #define cimg_for_outXY(img,x0,y0,x1,y1,x,y) cimg_for_out2((img)._width,(img)._height,x0,y0,x1,y1,x,y)
716 #define cimg_for_outXZ(img,x0,z0,x1,z1,x,z) cimg_for_out2((img)._width,(img)._depth,x0,z0,x1,z1,x,z)
717 #define cimg_for_outXC(img,x0,c0,x1,c1,x,c) cimg_for_out2((img)._width,(img)._spectrum,x0,c0,x1,c1,x,c)
718 #define cimg_for_outYZ(img,y0,z0,y1,z1,y,z) cimg_for_out2((img)._height,(img)._depth,y0,z0,y1,z1,y,z)
719 #define cimg_for_outYC(img,y0,c0,y1,c1,y,c) cimg_for_out2((img)._height,(img)._spectrum,y0,c0,y1,c1,y,c)
720 #define cimg_for_outZC(img,z0,c0,z1,c1,z,c) cimg_for_out2((img)._depth,(img)._spectrum,z0,c0,z1,c1,z,c)
721 #define cimg_for_outXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_out3((img)._width,(img)._height,(img)._depth,x0,y0,z0,x1,y1,z1,x,y,z)
722 #define cimg_for_outXYC(img,x0,y0,c0,x1,y1,c1,x,y,c) cimg_for_out3((img)._width,(img)._height,(img)._spectrum,x0,y0,c0,x1,y1,c1,x,y,c)
723 #define cimg_for_outXZC(img,x0,z0,c0,x1,z1,c1,x,z,c) cimg_for_out3((img)._width,(img)._depth,(img)._spectrum,x0,z0,c0,x1,z1,c1,x,z,c)
724 #define cimg_for_outYZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_out3((img)._height,(img)._depth,(img)._spectrum,y0,z0,c0,y1,z1,c1,y,z,c)
725 #define cimg_for_outXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
726  cimg_for_out4((img)._width,(img)._height,(img)._depth,(img)._spectrum,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c)
727 #define cimg_for_borderX(img,x,n) cimg_for_outX(img,n,(img)._width-1-(n),x)
728 #define cimg_for_borderY(img,y,n) cimg_for_outY(img,n,(img)._height-1-(n),y)
729 #define cimg_for_borderZ(img,z,n) cimg_for_outZ(img,n,(img)._depth-1-(n),z)
730 #define cimg_for_borderC(img,c,n) cimg_for_outC(img,n,(img)._spectrum-1-(n),c)
731 #define cimg_for_borderXY(img,x,y,n) cimg_for_outXY(img,n,n,(img)._width-1-(n),(img)._height-1-(n),x,y)
732 #define cimg_for_borderXYZ(img,x,y,z,n) cimg_for_outXYZ(img,n,n,n,(img)._width-1-(n),(img)._height-1-(n),(img)._depth-1-(n),x,y,z)
733 #define cimg_for_borderXYZC(img,x,y,z,c,n) \
734  cimg_for_outXYZC(img,n,n,n,n,(img)._width-1-(n),(img)._height-1-(n),(img)._depth-1-(n),(img)._spectrum-1-(n),x,y,z,c)
735 
736 #define cimg_for_spiralXY(img,x,y) \
737  for (int x = 0, y = 0, _n1##x = 1, _n1##y = (img).width()*(img).height(); _n1##y; \
738       --_n1##y, _n1##x+=(_n1##x>>2)-((!(_n1##x&3)?--y:((_n1##x&3)==1?(img)._width-1-++x:((_n1##x&3)==2?(img)._height-1-++y:--x))))?0:1)
739 
740 #define cimg_for_lineXY(x,y,x0,y0,x1,y1) \
741  for (int x = (int)(x0), y = (int)(y0), _sx = 1, _sy = 1, _steep = 0, \
742       _dx=(x1)>(x0)?(int)(x1)-(int)(x0):(_sx=-1,(int)(x0)-(int)(x1)), \
743       _dy=(y1)>(y0)?(int)(y1)-(int)(y0):(_sy=-1,(int)(y0)-(int)(y1)), \
744       _counter = _dx, \
745       _err = _dx>_dy?(_dy>>1):((_steep=1),(_counter=_dy),(_dx>>1)); \
746       _counter>=0; \
747       --_counter, x+=_steep? \
748       (y+=_sy,(_err-=_dx)<0?_err+=_dy,_sx:0): \
749       (y+=(_err-=_dy)<0?_err+=_dx,_sy:0,_sx))
750 
751 #define cimg_for2(bound,i) \
752  for (int i = 0, _n1##i = 1>=(bound)?(int)(bound)-1:1; \
753       _n1##i<(int)(bound) || i==--_n1##i; \
754       ++i, ++_n1##i)
755 #define cimg_for2X(img,x) cimg_for2((img)._width,x)
756 #define cimg_for2Y(img,y) cimg_for2((img)._height,y)
757 #define cimg_for2Z(img,z) cimg_for2((img)._depth,z)
758 #define cimg_for2C(img,c) cimg_for2((img)._spectrum,c)
759 #define cimg_for2XY(img,x,y) cimg_for2Y(img,y) cimg_for2X(img,x)
760 #define cimg_for2XZ(img,x,z) cimg_for2Z(img,z) cimg_for2X(img,x)
761 #define cimg_for2XC(img,x,c) cimg_for2C(img,c) cimg_for2X(img,x)
762 #define cimg_for2YZ(img,y,z) cimg_for2Z(img,z) cimg_for2Y(img,y)
763 #define cimg_for2YC(img,y,c) cimg_for2C(img,c) cimg_for2Y(img,y)
764 #define cimg_for2ZC(img,z,c) cimg_for2C(img,c) cimg_for2Z(img,z)
765 #define cimg_for2XYZ(img,x,y,z) cimg_for2Z(img,z) cimg_for2XY(img,x,y)
766 #define cimg_for2XZC(img,x,z,c) cimg_for2C(img,c) cimg_for2XZ(img,x,z)
767 #define cimg_for2YZC(img,y,z,c) cimg_for2C(img,c) cimg_for2YZ(img,y,z)
768 #define cimg_for2XYZC(img,x,y,z,c) cimg_for2C(img,c) cimg_for2XYZ(img,x,y,z)
769 
770 #define cimg_for_in2(bound,i0,i1,i) \
771  for (int i = (int)(i0)<0?0:(int)(i0), \
772       _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1; \
773       i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \
774       ++i, ++_n1##i)
775 #define cimg_for_in2X(img,x0,x1,x) cimg_for_in2((img)._width,x0,x1,x)
776 #define cimg_for_in2Y(img,y0,y1,y) cimg_for_in2((img)._height,y0,y1,y)
777 #define cimg_for_in2Z(img,z0,z1,z) cimg_for_in2((img)._depth,z0,z1,z)
778 #define cimg_for_in2C(img,c0,c1,c) cimg_for_in2((img)._spectrum,c0,c1,c)
779 #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)
780 #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)
781 #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)
782 #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)
783 #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)
784 #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)
785 #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)
786 #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)
787 #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)
788 #define cimg_for_in2XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
789 
790 #define cimg_for3(bound,i) \
791  for (int i = 0, _p1##i = 0, \
792       _n1##i = 1>=(bound)?(int)(bound)-1:1; \
793       _n1##i<(int)(bound) || i==--_n1##i; \
794       _p1##i = i++, ++_n1##i)
795 #define cimg_for3X(img,x) cimg_for3((img)._width,x)
796 #define cimg_for3Y(img,y) cimg_for3((img)._height,y)
797 #define cimg_for3Z(img,z) cimg_for3((img)._depth,z)
798 #define cimg_for3C(img,c) cimg_for3((img)._spectrum,c)
799 #define cimg_for3XY(img,x,y) cimg_for3Y(img,y) cimg_for3X(img,x)
800 #define cimg_for3XZ(img,x,z) cimg_for3Z(img,z) cimg_for3X(img,x)
801 #define cimg_for3XC(img,x,c) cimg_for3C(img,c) cimg_for3X(img,x)
802 #define cimg_for3YZ(img,y,z) cimg_for3Z(img,z) cimg_for3Y(img,y)
803 #define cimg_for3YC(img,y,c) cimg_for3C(img,c) cimg_for3Y(img,y)
804 #define cimg_for3ZC(img,z,c) cimg_for3C(img,c) cimg_for3Z(img,z)
805 #define cimg_for3XYZ(img,x,y,z) cimg_for3Z(img,z) cimg_for3XY(img,x,y)
806 #define cimg_for3XZC(img,x,z,c) cimg_for3C(img,c) cimg_for3XZ(img,x,z)
807 #define cimg_for3YZC(img,y,z,c) cimg_for3C(img,c) cimg_for3YZ(img,y,z)
808 #define cimg_for3XYZC(img,x,y,z,c) cimg_for3C(img,c) cimg_for3XYZ(img,x,y,z)
809 
810 #define cimg_for_in3(bound,i0,i1,i) \
811  for (int i = (int)(i0)<0?0:(int)(i0), \
812       _p1##i = i-1<0?0:i-1, \
813       _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1; \
814       i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \
815       _p1##i = i++, ++_n1##i)
816 #define cimg_for_in3X(img,x0,x1,x) cimg_for_in3((img)._width,x0,x1,x)
817 #define cimg_for_in3Y(img,y0,y1,y) cimg_for_in3((img)._height,y0,y1,y)
818 #define cimg_for_in3Z(img,z0,z1,z) cimg_for_in3((img)._depth,z0,z1,z)
819 #define cimg_for_in3C(img,c0,c1,c) cimg_for_in3((img)._spectrum,c0,c1,c)
820 #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)
821 #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)
822 #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)
823 #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)
824 #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)
825 #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)
826 #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)
827 #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)
828 #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)
829 #define cimg_for_in3XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
830 
831 #define cimg_for4(bound,i) \
832  for (int i = 0, _p1##i = 0, _n1##i = 1>=(bound)?(int)(bound)-1:1, \
833       _n2##i = 2>=(bound)?(int)(bound)-1:2; \
834       _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \
835       _p1##i = i++, ++_n1##i, ++_n2##i)
836 #define cimg_for4X(img,x) cimg_for4((img)._width,x)
837 #define cimg_for4Y(img,y) cimg_for4((img)._height,y)
838 #define cimg_for4Z(img,z) cimg_for4((img)._depth,z)
839 #define cimg_for4C(img,c) cimg_for4((img)._spectrum,c)
840 #define cimg_for4XY(img,x,y) cimg_for4Y(img,y) cimg_for4X(img,x)
841 #define cimg_for4XZ(img,x,z) cimg_for4Z(img,z) cimg_for4X(img,x)
842 #define cimg_for4XC(img,x,c) cimg_for4C(img,c) cimg_for4X(img,x)
843 #define cimg_for4YZ(img,y,z) cimg_for4Z(img,z) cimg_for4Y(img,y)
844 #define cimg_for4YC(img,y,c) cimg_for4C(img,c) cimg_for4Y(img,y)
845 #define cimg_for4ZC(img,z,c) cimg_for4C(img,c) cimg_for4Z(img,z)
846 #define cimg_for4XYZ(img,x,y,z) cimg_for4Z(img,z) cimg_for4XY(img,x,y)
847 #define cimg_for4XZC(img,x,z,c) cimg_for4C(img,c) cimg_for4XZ(img,x,z)
848 #define cimg_for4YZC(img,y,z,c) cimg_for4C(img,c) cimg_for4YZ(img,y,z)
849 #define cimg_for4XYZC(img,x,y,z,c) cimg_for4C(img,c) cimg_for4XYZ(img,x,y,z)
850 
851 #define cimg_for_in4(bound,i0,i1,i) \
852  for (int i = (int)(i0)<0?0:(int)(i0), \
853       _p1##i = i-1<0?0:i-1, \
854       _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
855       _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2; \
856       i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \
857       _p1##i = i++, ++_n1##i, ++_n2##i)
858 #define cimg_for_in4X(img,x0,x1,x) cimg_for_in4((img)._width,x0,x1,x)
859 #define cimg_for_in4Y(img,y0,y1,y) cimg_for_in4((img)._height,y0,y1,y)
860 #define cimg_for_in4Z(img,z0,z1,z) cimg_for_in4((img)._depth,z0,z1,z)
861 #define cimg_for_in4C(img,c0,c1,c) cimg_for_in4((img)._spectrum,c0,c1,c)
862 #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)
863 #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)
864 #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)
865 #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)
866 #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)
867 #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)
868 #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)
869 #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)
870 #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)
871 #define cimg_for_in4XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
872 
873 #define cimg_for5(bound,i) \
874  for (int i = 0, _p2##i = 0, _p1##i = 0, \
875       _n1##i = 1>=(bound)?(int)(bound)-1:1, \
876       _n2##i = 2>=(bound)?(int)(bound)-1:2; \
877       _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \
878       _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i)
879 #define cimg_for5X(img,x) cimg_for5((img)._width,x)
880 #define cimg_for5Y(img,y) cimg_for5((img)._height,y)
881 #define cimg_for5Z(img,z) cimg_for5((img)._depth,z)
882 #define cimg_for5C(img,c) cimg_for5((img)._spectrum,c)
883 #define cimg_for5XY(img,x,y) cimg_for5Y(img,y) cimg_for5X(img,x)
884 #define cimg_for5XZ(img,x,z) cimg_for5Z(img,z) cimg_for5X(img,x)
885 #define cimg_for5XC(img,x,c) cimg_for5C(img,c) cimg_for5X(img,x)
886 #define cimg_for5YZ(img,y,z) cimg_for5Z(img,z) cimg_for5Y(img,y)
887 #define cimg_for5YC(img,y,c) cimg_for5C(img,c) cimg_for5Y(img,y)
888 #define cimg_for5ZC(img,z,c) cimg_for5C(img,c) cimg_for5Z(img,z)
889 #define cimg_for5XYZ(img,x,y,z) cimg_for5Z(img,z) cimg_for5XY(img,x,y)
890 #define cimg_for5XZC(img,x,z,c) cimg_for5C(img,c) cimg_for5XZ(img,x,z)
891 #define cimg_for5YZC(img,y,z,c) cimg_for5C(img,c) cimg_for5YZ(img,y,z)
892 #define cimg_for5XYZC(img,x,y,z,c) cimg_for5C(img,c) cimg_for5XYZ(img,x,y,z)
893 
894 #define cimg_for_in5(bound,i0,i1,i) \
895  for (int i = (int)(i0)<0?0:(int)(i0), \
896       _p2##i = i-2<0?0:i-2, \
897       _p1##i = i-1<0?0:i-1, \
898       _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
899       _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2; \
900       i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \
901       _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i)
902 #define cimg_for_in5X(img,x0,x1,x) cimg_for_in5((img)._width,x0,x1,x)
903 #define cimg_for_in5Y(img,y0,y1,y) cimg_for_in5((img)._height,y0,y1,y)
904 #define cimg_for_in5Z(img,z0,z1,z) cimg_for_in5((img)._depth,z0,z1,z)
905 #define cimg_for_in5C(img,c0,c1,c) cimg_for_in5((img)._spectrum,c0,c1,c)
906 #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)
907 #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)
908 #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)
909 #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)
910 #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)
911 #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)
912 #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)
913 #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)
914 #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)
915 #define cimg_for_in5XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
916 
917 #define cimg_for6(bound,i) \
918  for (int i = 0, _p2##i = 0, _p1##i = 0, \
919       _n1##i = 1>=(bound)?(int)(bound)-1:1, \
920       _n2##i = 2>=(bound)?(int)(bound)-1:2, \
921       _n3##i = 3>=(bound)?(int)(bound)-1:3; \
922       _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \
923       _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
924 #define cimg_for6X(img,x) cimg_for6((img)._width,x)
925 #define cimg_for6Y(img,y) cimg_for6((img)._height,y)
926 #define cimg_for6Z(img,z) cimg_for6((img)._depth,z)
927 #define cimg_for6C(img,c) cimg_for6((img)._spectrum,c)
928 #define cimg_for6XY(img,x,y) cimg_for6Y(img,y) cimg_for6X(img,x)
929 #define cimg_for6XZ(img,x,z) cimg_for6Z(img,z) cimg_for6X(img,x)
930 #define cimg_for6XC(img,x,c) cimg_for6C(img,c) cimg_for6X(img,x)
931 #define cimg_for6YZ(img,y,z) cimg_for6Z(img,z) cimg_for6Y(img,y)
932 #define cimg_for6YC(img,y,c) cimg_for6C(img,c) cimg_for6Y(img,y)
933 #define cimg_for6ZC(img,z,c) cimg_for6C(img,c) cimg_for6Z(img,z)
934 #define cimg_for6XYZ(img,x,y,z) cimg_for6Z(img,z) cimg_for6XY(img,x,y)
935 #define cimg_for6XZC(img,x,z,c) cimg_for6C(img,c) cimg_for6XZ(img,x,z)
936 #define cimg_for6YZC(img,y,z,c) cimg_for6C(img,c) cimg_for6YZ(img,y,z)
937 #define cimg_for6XYZC(img,x,y,z,c) cimg_for6C(img,c) cimg_for6XYZ(img,x,y,z)
938 
939 #define cimg_for_in6(bound,i0,i1,i) \
940  for (int i = (int)(i0)<0?0:(int)(i0), \
941       _p2##i = i-2<0?0:i-2, \
942       _p1##i = i-1<0?0:i-1, \
943       _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
944       _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \
945       _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3; \
946       i<=(int)(i1) && (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \
947       _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
948 #define cimg_for_in6X(img,x0,x1,x) cimg_for_in6((img)._width,x0,x1,x)
949 #define cimg_for_in6Y(img,y0,y1,y) cimg_for_in6((img)._height,y0,y1,y)
950 #define cimg_for_in6Z(img,z0,z1,z) cimg_for_in6((img)._depth,z0,z1,z)
951 #define cimg_for_in6C(img,c0,c1,c) cimg_for_in6((img)._spectrum,c0,c1,c)
952 #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)
953 #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)
954 #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)
955 #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)
956 #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)
957 #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)
958 #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)
959 #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)
960 #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)
961 #define cimg_for_in6XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
962 
963 #define cimg_for7(bound,i) \
964  for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
965       _n1##i = 1>=(bound)?(int)(bound)-1:1, \
966       _n2##i = 2>=(bound)?(int)(bound)-1:2, \
967       _n3##i = 3>=(bound)?(int)(bound)-1:3; \
968       _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \
969       _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
970 #define cimg_for7X(img,x) cimg_for7((img)._width,x)
971 #define cimg_for7Y(img,y) cimg_for7((img)._height,y)
972 #define cimg_for7Z(img,z) cimg_for7((img)._depth,z)
973 #define cimg_for7C(img,c) cimg_for7((img)._spectrum,c)
974 #define cimg_for7XY(img,x,y) cimg_for7Y(img,y) cimg_for7X(img,x)
975 #define cimg_for7XZ(img,x,z) cimg_for7Z(img,z) cimg_for7X(img,x)
976 #define cimg_for7XC(img,x,c) cimg_for7C(img,c) cimg_for7X(img,x)
977 #define cimg_for7YZ(img,y,z) cimg_for7Z(img,z) cimg_for7Y(img,y)
978 #define cimg_for7YC(img,y,c) cimg_for7C(img,c) cimg_for7Y(img,y)
979 #define cimg_for7ZC(img,z,c) cimg_for7C(img,c) cimg_for7Z(img,z)
980 #define cimg_for7XYZ(img,x,y,z) cimg_for7Z(img,z) cimg_for7XY(img,x,y)
981 #define cimg_for7XZC(img,x,z,c) cimg_for7C(img,c) cimg_for7XZ(img,x,z)
982 #define cimg_for7YZC(img,y,z,c) cimg_for7C(img,c) cimg_for7YZ(img,y,z)
983 #define cimg_for7XYZC(img,x,y,z,c) cimg_for7C(img,c) cimg_for7XYZ(img,x,y,z)
984 
985 #define cimg_for_in7(bound,i0,i1,i) \
986  for (int i = (int)(i0)<0?0:(int)(i0), \
987       _p3##i = i-3<0?0:i-3, \
988       _p2##i = i-2<0?0:i-2, \
989       _p1##i = i-1<0?0:i-1, \
990       _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
991       _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \
992       _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3; \
993       i<=(int)(i1) && (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \
994       _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
995 #define cimg_for_in7X(img,x0,x1,x) cimg_for_in7((img)._width,x0,x1,x)
996 #define cimg_for_in7Y(img,y0,y1,y) cimg_for_in7((img)._height,y0,y1,y)
997 #define cimg_for_in7Z(img,z0,z1,z) cimg_for_in7((img)._depth,z0,z1,z)
998 #define cimg_for_in7C(img,c0,c1,c) cimg_for_in7((img)._spectrum,c0,c1,c)
999 #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)
1000 #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)
1001 #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)
1002 #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)
1003 #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)
1004 #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)
1005 #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)
1006 #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)
1007 #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)
1008 #define cimg_for_in7XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
1009 
1010 #define cimg_for8(bound,i) \
1011  for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
1012       _n1##i = 1>=(bound)?(int)(bound)-1:1, \
1013       _n2##i = 2>=(bound)?(int)(bound)-1:2, \
1014       _n3##i = 3>=(bound)?(int)(bound)-1:3, \
1015       _n4##i = 4>=(bound)?(int)(bound)-1:4; \
1016       _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
1017       i==(_n4##i = _n3##i = _n2##i = --_n1##i); \
1018       _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
1019 #define cimg_for8X(img,x) cimg_for8((img)._width,x)
1020 #define cimg_for8Y(img,y) cimg_for8((img)._height,y)
1021 #define cimg_for8Z(img,z) cimg_for8((img)._depth,z)
1022 #define cimg_for8C(img,c) cimg_for8((img)._spectrum,c)
1023 #define cimg_for8XY(img,x,y) cimg_for8Y(img,y) cimg_for8X(img,x)
1024 #define cimg_for8XZ(img,x,z) cimg_for8Z(img,z) cimg_for8X(img,x)
1025 #define cimg_for8XC(img,x,c) cimg_for8C(img,c) cimg_for8X(img,x)
1026 #define cimg_for8YZ(img,y,z) cimg_for8Z(img,z) cimg_for8Y(img,y)
1027 #define cimg_for8YC(img,y,c) cimg_for8C(img,c) cimg_for8Y(img,y)
1028 #define cimg_for8ZC(img,z,c) cimg_for8C(img,c) cimg_for8Z(img,z)
1029 #define cimg_for8XYZ(img,x,y,z) cimg_for8Z(img,z) cimg_for8XY(img,x,y)
1030 #define cimg_for8XZC(img,x,z,c) cimg_for8C(img,c) cimg_for8XZ(img,x,z)
1031 #define cimg_for8YZC(img,y,z,c) cimg_for8C(img,c) cimg_for8YZ(img,y,z)
1032 #define cimg_for8XYZC(img,x,y,z,c) cimg_for8C(img,c) cimg_for8XYZ(img,x,y,z)
1033 
1034 #define cimg_for_in8(bound,i0,i1,i) \
1035  for (int i = (int)(i0)<0?0:(int)(i0), \
1036       _p3##i = i-3<0?0:i-3, \
1037       _p2##i = i-2<0?0:i-2, \
1038       _p1##i = i-1<0?0:i-1, \
1039       _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
1040       _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \
1041       _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3, \
1042       _n4##i = i+4>=(int)(bound)?(int)(bound)-1:i+4; \
1043       i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
1044       i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \
1045       _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
1046 #define cimg_for_in8X(img,x0,x1,x) cimg_for_in8((img)._width,x0,x1,x)
1047 #define cimg_for_in8Y(img,y0,y1,y) cimg_for_in8((img)._height,y0,y1,y)
1048 #define cimg_for_in8Z(img,z0,z1,z) cimg_for_in8((img)._depth,z0,z1,z)
1049 #define cimg_for_in8C(img,c0,c1,c) cimg_for_in8((img)._spectrum,c0,c1,c)
1050 #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)
1051 #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)
1052 #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)
1053 #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)
1054 #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)
1055 #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)
1056 #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)
1057 #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)
1058 #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)
1059 #define cimg_for_in8XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
1060 
1061 #define cimg_for9(bound,i) \
1062   for (int i = 0, _p4##i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
1063        _n1##i = 1>=(int)(bound)?(int)(bound)-1:1, \
1064        _n2##i = 2>=(int)(bound)?(int)(bound)-1:2, \
1065        _n3##i = 3>=(int)(bound)?(int)(bound)-1:3, \
1066        _n4##i = 4>=(int)(bound)?(int)(bound)-1:4; \
1067        _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
1068        i==(_n4##i = _n3##i = _n2##i = --_n1##i); \
1069        _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
1070 #define cimg_for9X(img,x) cimg_for9((img)._width,x)
1071 #define cimg_for9Y(img,y) cimg_for9((img)._height,y)
1072 #define cimg_for9Z(img,z) cimg_for9((img)._depth,z)
1073 #define cimg_for9C(img,c) cimg_for9((img)._spectrum,c)
1074 #define cimg_for9XY(img,x,y) cimg_for9Y(img,y) cimg_for9X(img,x)
1075 #define cimg_for9XZ(img,x,z) cimg_for9Z(img,z) cimg_for9X(img,x)
1076 #define cimg_for9XC(img,x,c) cimg_for9C(img,c) cimg_for9X(img,x)
1077 #define cimg_for9YZ(img,y,z) cimg_for9Z(img,z) cimg_for9Y(img,y)
1078 #define cimg_for9YC(img,y,c) cimg_for9C(img,c) cimg_for9Y(img,y)
1079 #define cimg_for9ZC(img,z,c) cimg_for9C(img,c) cimg_for9Z(img,z)
1080 #define cimg_for9XYZ(img,x,y,z) cimg_for9Z(img,z) cimg_for9XY(img,x,y)
1081 #define cimg_for9XZC(img,x,z,c) cimg_for9C(img,c) cimg_for9XZ(img,x,z)
1082 #define cimg_for9YZC(img,y,z,c) cimg_for9C(img,c) cimg_for9YZ(img,y,z)
1083 #define cimg_for9XYZC(img,x,y,z,c) cimg_for9C(img,c) cimg_for9XYZ(img,x,y,z)
1084 
1085 #define cimg_for_in9(bound,i0,i1,i) \
1086   for (int i = (int)(i0)<0?0:(int)(i0), \
1087        _p4##i = i-4<0?0:i-4, \
1088        _p3##i = i-3<0?0:i-3, \
1089        _p2##i = i-2<0?0:i-2, \
1090        _p1##i = i-1<0?0:i-1, \
1091        _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
1092        _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \
1093        _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3, \
1094        _n4##i = i+4>=(int)(bound)?(int)(bound)-1:i+4; \
1095        i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
1096        i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \
1097        _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
1098 #define cimg_for_in9X(img,x0,x1,x) cimg_for_in9((img)._width,x0,x1,x)
1099 #define cimg_for_in9Y(img,y0,y1,y) cimg_for_in9((img)._height,y0,y1,y)
1100 #define cimg_for_in9Z(img,z0,z1,z) cimg_for_in9((img)._depth,z0,z1,z)
1101 #define cimg_for_in9C(img,c0,c1,c) cimg_for_in9((img)._spectrum,c0,c1,c)
1102 #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)
1103 #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)
1104 #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)
1105 #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)
1106 #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)
1107 #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)
1108 #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)
1109 #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)
1110 #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)
1111 #define cimg_for_in9XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
1112 
1113 #define cimg_for2x2(img,x,y,z,c,I,T) \
1114   cimg_for2((img)._height,y) for (int x = 0, \
1115    _n1##x = (int)( \
1116    (I[0] = (T)(img)(0,y,z,c)), \
1117    (I[2] = (T)(img)(0,_n1##y,z,c)), \
1118    1>=(img)._width?(img).width()-1:1);  \
1119    (_n1##x<(img).width() && ( \
1120    (I[1] = (T)(img)(_n1##x,y,z,c)), \
1121    (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
1122    x==--_n1##x; \
1123    I[0] = I[1], \
1124    I[2] = I[3], \
1125    ++x, ++_n1##x)
1126 
1127 #define cimg_for_in2x2(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1128   cimg_for_in2((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1129    _n1##x = (int)( \
1130    (I[0] = (T)(img)(x,y,z,c)), \
1131    (I[2] = (T)(img)(x,_n1##y,z,c)), \
1132    x+1>=(int)(img)._width?(img).width()-1:x+1); \
1133    x<=(int)(x1) && ((_n1##x<(img).width() && (  \
1134    (I[1] = (T)(img)(_n1##x,y,z,c)), \
1135    (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
1136    x==--_n1##x); \
1137    I[0] = I[1], \
1138    I[2] = I[3], \
1139    ++x, ++_n1##x)
1140 
1141 #define cimg_for3x3(img,x,y,z,c,I,T) \
1142   cimg_for3((img)._height,y) for (int x = 0, \
1143    _p1##x = 0, \
1144    _n1##x = (int)( \
1145    (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
1146    (I[3] = I[4] = (T)(img)(0,y,z,c)), \
1147    (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)),      \
1148    1>=(img)._width?(img).width()-1:1); \
1149    (_n1##x<(img).width() && ( \
1150    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
1151    (I[5] = (T)(img)(_n1##x,y,z,c)), \
1152    (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
1153    x==--_n1##x; \
1154    I[0] = I[1], I[1] = I[2], \
1155    I[3] = I[4], I[4] = I[5], \
1156    I[6] = I[7], I[7] = I[8], \
1157    _p1##x = x++, ++_n1##x)
1158 
1159 #define cimg_for_in3x3(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1160   cimg_for_in3((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1161    _p1##x = x-1<0?0:x-1, \
1162    _n1##x = (int)( \
1163    (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \
1164    (I[3] = (T)(img)(_p1##x,y,z,c)), \
1165    (I[6] = (T)(img)(_p1##x,_n1##y,z,c)), \
1166    (I[1] = (T)(img)(x,_p1##y,z,c)), \
1167    (I[4] = (T)(img)(x,y,z,c)), \
1168    (I[7] = (T)(img)(x,_n1##y,z,c)), \
1169    x+1>=(int)(img)._width?(img).width()-1:x+1); \
1170    x<=(int)(x1) && ((_n1##x<(img).width() && ( \
1171    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
1172    (I[5] = (T)(img)(_n1##x,y,z,c)), \
1173    (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
1174    x==--_n1##x);            \
1175    I[0] = I[1], I[1] = I[2], \
1176    I[3] = I[4], I[4] = I[5], \
1177    I[6] = I[7], I[7] = I[8], \
1178    _p1##x = x++, ++_n1##x)
1179 
1180 #define cimg_for4x4(img,x,y,z,c,I,T) \
1181   cimg_for4((img)._height,y) for (int x = 0, \
1182    _p1##x = 0, \
1183    _n1##x = 1>=(img)._width?(img).width()-1:1, \
1184    _n2##x = (int)( \
1185    (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
1186    (I[4] = I[5] = (T)(img)(0,y,z,c)), \
1187    (I[8] = I[9] = (T)(img)(0,_n1##y,z,c)), \
1188    (I[12] = I[13] = (T)(img)(0,_n2##y,z,c)), \
1189    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
1190    (I[6] = (T)(img)(_n1##x,y,z,c)), \
1191    (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \
1192    (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \
1193    2>=(img)._width?(img).width()-1:2); \
1194    (_n2##x<(img).width() && ( \
1195    (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \
1196    (I[7] = (T)(img)(_n2##x,y,z,c)), \
1197    (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \
1198    (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
1199    _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \
1200    I[0] = I[1], I[1] = I[2], I[2] = I[3], \
1201    I[4] = I[5], I[5] = I[6], I[6] = I[7], \
1202    I[8] = I[9], I[9] = I[10], I[10] = I[11], \
1203    I[12] = I[13], I[13] = I[14], I[14] = I[15], \
1204    _p1##x = x++, ++_n1##x, ++_n2##x)
1205 
1206 #define cimg_for_in4x4(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1207   cimg_for_in4((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1208    _p1##x = x-1<0?0:x-1, \
1209    _n1##x = x+1>=(int)(img)._width?(img).width()-1:x+1, \
1210    _n2##x = (int)( \
1211    (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \
1212    (I[4] = (T)(img)(_p1##x,y,z,c)), \
1213    (I[8] = (T)(img)(_p1##x,_n1##y,z,c)), \
1214    (I[12] = (T)(img)(_p1##x,_n2##y,z,c)), \
1215    (I[1] = (T)(img)(x,_p1##y,z,c)), \
1216    (I[5] = (T)(img)(x,y,z,c)), \
1217    (I[9] = (T)(img)(x,_n1##y,z,c)), \
1218    (I[13] = (T)(img)(x,_n2##y,z,c)), \
1219    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
1220    (I[6] = (T)(img)(_n1##x,y,z,c)), \
1221    (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \
1222    (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \
1223    x+2>=(int)(img)._width?(img).width()-1:x+2); \
1224    x<=(int)(x1) && ((_n2##x<(img).width() && ( \
1225    (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \
1226    (I[7] = (T)(img)(_n2##x,y,z,c)), \
1227    (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \
1228    (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
1229    _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \
1230    I[0] = I[1], I[1] = I[2], I[2] = I[3], \
1231    I[4] = I[5], I[5] = I[6], I[6] = I[7], \
1232    I[8] = I[9], I[9] = I[10], I[10] = I[11], \
1233    I[12] = I[13], I[13] = I[14], I[14] = I[15], \
1234    _p1##x = x++, ++_n1##x, ++_n2##x)
1235 
1236 #define cimg_for5x5(img,x,y,z,c,I,T) \
1237  cimg_for5((img)._height,y) for (int x = 0, \
1238    _p2##x = 0, _p1##x = 0, \
1239    _n1##x = 1>=(img)._width?(img).width()-1:1, \
1240    _n2##x = (int)( \
1241    (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \
1242    (I[5] = I[6] = I[7] = (T)(img)(0,_p1##y,z,c)), \
1243    (I[10] = I[11] = I[12] = (T)(img)(0,y,z,c)), \
1244    (I[15] = I[16] = I[17] = (T)(img)(0,_n1##y,z,c)), \
1245    (I[20] = I[21] = I[22] = (T)(img)(0,_n2##y,z,c)), \
1246    (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
1247    (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \
1248    (I[13] = (T)(img)(_n1##x,y,z,c)), \
1249    (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \
1250    (I[23] = (T)(img)(_n1##x,_n2##y,z,c)),  \
1251    2>=(img)._width?(img).width()-1:2); \
1252    (_n2##x<(img).width() && ( \
1253    (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
1254    (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \
1255    (I[14] = (T)(img)(_n2##x,y,z,c)), \
1256    (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \
1257    (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
1258    _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \
1259    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \
1260    I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \
1261    I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \
1262    I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \
1263    I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \
1264    _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x)
1265 
1266 #define cimg_for_in5x5(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1267  cimg_for_in5((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1268    _p2##x = x-2<0?0:x-2, \
1269    _p1##x = x-1<0?0:x-1, \
1270    _n1##x = x+1>=(int)(img)._width?(img).width()-1:x+1, \
1271    _n2##x = (int)( \
1272    (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \
1273    (I[5] = (T)(img)(_p2##x,_p1##y,z,c)), \
1274    (I[10] = (T)(img)(_p2##x,y,z,c)), \
1275    (I[15] = (T)(img)(_p2##x,_n1##y,z,c)), \
1276    (I[20] = (T)(img)(_p2##x,_n2##y,z,c)), \
1277    (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \
1278    (I[6] = (T)(img)(_p1##x,_p1##y,z,c)), \
1279    (I[11] = (T)(img)(_p1##x,y,z,c)), \
1280    (I[16] = (T)(img)(_p1##x,_n1##y,z,c)), \
1281    (I[21] = (T)(img)(_p1##x,_n2##y,z,c)), \
1282    (I[2] = (T)(img)(x,_p2##y,z,c)), \
1283    (I[7] = (T)(img)(x,_p1##y,z,c)), \
1284    (I[12] = (T)(img)(x,y,z,c)), \
1285    (I[17] = (T)(img)(x,_n1##y,z,c)), \
1286    (I[22] = (T)(img)(x,_n2##y,z,c)), \
1287    (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
1288    (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \
1289    (I[13] = (T)(img)(_n1##x,y,z,c)), \
1290    (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \
1291    (I[23] = (T)(img)(_n1##x,_n2##y,z,c)), \
1292    x+2>=(int)(img)._width?(img).width()-1:x+2); \
1293    x<=(int)(x1) && ((_n2##x<(img).width() && ( \
1294    (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
1295    (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \
1296    (I[14] = (T)(img)(_n2##x,y,z,c)), \
1297    (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \
1298    (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
1299    _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \
1300    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \
1301    I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \
1302    I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \
1303    I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \
1304    I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \
1305    _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x)
1306 
1307 #define cimg_for6x6(img,x,y,z,c,I,T) \
1308  cimg_for6((img)._height,y) for (int x = 0, \
1309    _p2##x = 0, _p1##x = 0, \
1310    _n1##x = 1>=(img)._width?(img).width()-1:1, \
1311    _n2##x = 2>=(img)._width?(img).width()-1:2, \
1312    _n3##x = (int)( \
1313    (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \
1314    (I[6] = I[7] = I[8] = (T)(img)(0,_p1##y,z,c)), \
1315    (I[12] = I[13] = I[14] = (T)(img)(0,y,z,c)), \
1316    (I[18] = I[19] = I[20] = (T)(img)(0,_n1##y,z,c)), \
1317    (I[24] = I[25] = I[26] = (T)(img)(0,_n2##y,z,c)), \
1318    (I[30] = I[31] = I[32] = (T)(img)(0,_n3##y,z,c)), \
1319    (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
1320    (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \
1321    (I[15] = (T)(img)(_n1##x,y,z,c)), \
1322    (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \
1323    (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \
1324    (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \
1325    (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
1326    (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \
1327    (I[16] = (T)(img)(_n2##x,y,z,c)), \
1328    (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \
1329    (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \
1330    (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \
1331    3>=(img)._width?(img).width()-1:3); \
1332    (_n3##x<(img).width() && ( \
1333    (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \
1334    (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \
1335    (I[17] = (T)(img)(_n3##x,y,z,c)), \
1336    (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \
1337    (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \
1338    (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
1339    _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x); \
1340    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \
1341    I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \
1342    I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \
1343    I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \
1344    I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \
1345    I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \
1346    _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
1347 
1348 #define cimg_for_in6x6(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1349   cimg_for_in6((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)x0, \
1350    _p2##x = x-2<0?0:x-2, \
1351    _p1##x = x-1<0?0:x-1, \
1352    _n1##x = x+1>=(int)(img)._width?(img).width()-1:x+1, \
1353    _n2##x = x+2>=(int)(img)._width?(img).width()-1:x+2, \
1354    _n3##x = (int)( \
1355    (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \
1356    (I[6] = (T)(img)(_p2##x,_p1##y,z,c)), \
1357    (I[12] = (T)(img)(_p2##x,y,z,c)), \
1358    (I[18] = (T)(img)(_p2##x,_n1##y,z,c)), \
1359    (I[24] = (T)(img)(_p2##x,_n2##y,z,c)), \
1360    (I[30] = (T)(img)(_p2##x,_n3##y,z,c)), \
1361    (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \
1362    (I[7] = (T)(img)(_p1##x,_p1##y,z,c)), \
1363    (I[13] = (T)(img)(_p1##x,y,z,c)), \
1364    (I[19] = (T)(img)(_p1##x,_n1##y,z,c)), \
1365    (I[25] = (T)(img)(_p1##x,_n2##y,z,c)), \
1366    (I[31] = (T)(img)(_p1##x,_n3##y,z,c)), \
1367    (I[2] = (T)(img)(x,_p2##y,z,c)), \
1368    (I[8] = (T)(img)(x,_p1##y,z,c)), \
1369    (I[14] = (T)(img)(x,y,z,c)), \
1370    (I[20] = (T)(img)(x,_n1##y,z,c)), \
1371    (I[26] = (T)(img)(x,_n2##y,z,c)), \
1372    (I[32] = (T)(img)(x,_n3##y,z,c)), \
1373    (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
1374    (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \
1375    (I[15] = (T)(img)(_n1##x,y,z,c)), \
1376    (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \
1377    (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \
1378    (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \
1379    (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
1380    (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \
1381    (I[16] = (T)(img)(_n2##x,y,z,c)), \
1382    (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \
1383    (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \
1384    (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \
1385    x+3>=(int)(img)._width?(img).width()-1:x+3); \
1386    x<=(int)(x1) && ((_n3##x<(img).width() && ( \
1387    (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \
1388    (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \
1389    (I[17] = (T)(img)(_n3##x,y,z,c)), \
1390    (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \
1391    (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \
1392    (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
1393    _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x)); \
1394    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \
1395    I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \
1396    I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \
1397    I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \
1398    I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \
1399    I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \
1400    _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
1401 
1402 #define cimg_for7x7(img,x,y,z,c,I,T) \
1403   cimg_for7((img)._height,y) for (int x = 0, \
1404    _p3##x = 0, _p2##x = 0, _p1##x = 0, \
1405    _n1##x = 1>=(img)._width?(img).width()-1:1, \
1406    _n2##x = 2>=(img)._width?(img).width()-1:2, \
1407    _n3##x = (int)( \
1408    (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \
1409    (I[7] = I[8] = I[9] = I[10] = (T)(img)(0,_p2##y,z,c)), \
1410    (I[14] = I[15] = I[16] = I[17] = (T)(img)(0,_p1##y,z,c)), \
1411    (I[21] = I[22] = I[23] = I[24] = (T)(img)(0,y,z,c)), \
1412    (I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_n1##y,z,c)), \
1413    (I[35] = I[36] = I[37] = I[38] = (T)(img)(0,_n2##y,z,c)), \
1414    (I[42] = I[43] = I[44] = I[45] = (T)(img)(0,_n3##y,z,c)), \
1415    (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
1416    (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \
1417    (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \
1418    (I[25] = (T)(img)(_n1##x,y,z,c)), \
1419    (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \
1420    (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \
1421    (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \
1422    (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
1423    (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \
1424    (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \
1425    (I[26] = (T)(img)(_n2##x,y,z,c)), \
1426    (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \
1427    (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \
1428    (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \
1429    3>=(img)._width?(img).width()-1:3); \
1430    (_n3##x<(img).width() && ( \
1431    (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
1432    (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \
1433    (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \
1434    (I[27] = (T)(img)(_n3##x,y,z,c)), \
1435    (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \
1436    (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \
1437    (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
1438    _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x); \
1439    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \
1440    I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \
1441    I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \
1442    I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \
1443    I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \
1444    I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \
1445    I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \
1446    _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
1447 
1448 #define cimg_for_in7x7(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1449   cimg_for_in7((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1450    _p3##x = x-3<0?0:x-3, \
1451    _p2##x = x-2<0?0:x-2, \
1452    _p1##x = x-1<0?0:x-1, \
1453    _n1##x = x+1>=(int)(img)._width?(img).width()-1:x+1, \
1454    _n2##x = x+2>=(int)(img)._width?(img).width()-1:x+2, \
1455    _n3##x = (int)( \
1456    (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \
1457    (I[7] = (T)(img)(_p3##x,_p2##y,z,c)), \
1458    (I[14] = (T)(img)(_p3##x,_p1##y,z,c)), \
1459    (I[21] = (T)(img)(_p3##x,y,z,c)), \
1460    (I[28] = (T)(img)(_p3##x,_n1##y,z,c)), \
1461    (I[35] = (T)(img)(_p3##x,_n2##y,z,c)), \
1462    (I[42] = (T)(img)(_p3##x,_n3##y,z,c)), \
1463    (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \
1464    (I[8] = (T)(img)(_p2##x,_p2##y,z,c)), \
1465    (I[15] = (T)(img)(_p2##x,_p1##y,z,c)), \
1466    (I[22] = (T)(img)(_p2##x,y,z,c)), \
1467    (I[29] = (T)(img)(_p2##x,_n1##y,z,c)), \
1468    (I[36] = (T)(img)(_p2##x,_n2##y,z,c)), \
1469    (I[43] = (T)(img)(_p2##x,_n3##y,z,c)), \
1470    (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \
1471    (I[9] = (T)(img)(_p1##x,_p2##y,z,c)), \
1472    (I[16] = (T)(img)(_p1##x,_p1##y,z,c)), \
1473    (I[23] = (T)(img)(_p1##x,y,z,c)), \
1474    (I[30] = (T)(img)(_p1##x,_n1##y,z,c)), \
1475    (I[37] = (T)(img)(_p1##x,_n2##y,z,c)), \
1476    (I[44] = (T)(img)(_p1##x,_n3##y,z,c)), \
1477    (I[3] = (T)(img)(x,_p3##y,z,c)), \
1478    (I[10] = (T)(img)(x,_p2##y,z,c)), \
1479    (I[17] = (T)(img)(x,_p1##y,z,c)), \
1480    (I[24] = (T)(img)(x,y,z,c)), \
1481    (I[31] = (T)(img)(x,_n1##y,z,c)), \
1482    (I[38] = (T)(img)(x,_n2##y,z,c)), \
1483    (I[45] = (T)(img)(x,_n3##y,z,c)), \
1484    (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
1485    (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \
1486    (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \
1487    (I[25] = (T)(img)(_n1##x,y,z,c)), \
1488    (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \
1489    (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \
1490    (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \
1491    (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
1492    (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \
1493    (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \
1494    (I[26] = (T)(img)(_n2##x,y,z,c)), \
1495    (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \
1496    (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \
1497    (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \
1498    x+3>=(int)(img)._width?(img).width()-1:x+3); \
1499    x<=(int)(x1) && ((_n3##x<(img).width() && ( \
1500    (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
1501    (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \
1502    (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \
1503    (I[27] = (T)(img)(_n3##x,y,z,c)), \
1504    (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \
1505    (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \
1506    (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
1507    _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x)); \
1508    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \
1509    I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \
1510    I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \
1511    I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \
1512    I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \
1513    I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \
1514    I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \
1515    _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
1516 
1517 #define cimg_for8x8(img,x,y,z,c,I,T) \
1518   cimg_for8((img)._height,y) for (int x = 0, \
1519    _p3##x = 0, _p2##x = 0, _p1##x = 0, \
1520    _n1##x = 1>=((img)._width)?(img).width()-1:1, \
1521    _n2##x = 2>=((img)._width)?(img).width()-1:2, \
1522    _n3##x = 3>=((img)._width)?(img).width()-1:3, \
1523    _n4##x = (int)( \
1524    (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \
1525    (I[8] = I[9] = I[10] = I[11] = (T)(img)(0,_p2##y,z,c)), \
1526    (I[16] = I[17] = I[18] = I[19] = (T)(img)(0,_p1##y,z,c)), \
1527    (I[24] = I[25] = I[26] = I[27] = (T)(img)(0,y,z,c)), \
1528    (I[32] = I[33] = I[34] = I[35] = (T)(img)(0,_n1##y,z,c)), \
1529    (I[40] = I[41] = I[42] = I[43] = (T)(img)(0,_n2##y,z,c)), \
1530    (I[48] = I[49] = I[50] = I[51] = (T)(img)(0,_n3##y,z,c)), \
1531    (I[56] = I[57] = I[58] = I[59] = (T)(img)(0,_n4##y,z,c)), \
1532    (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
1533    (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \
1534    (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \
1535    (I[28] = (T)(img)(_n1##x,y,z,c)), \
1536    (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \
1537    (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \
1538    (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \
1539    (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \
1540    (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
1541    (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \
1542    (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \
1543    (I[29] = (T)(img)(_n2##x,y,z,c)), \
1544    (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \
1545    (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \
1546    (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \
1547    (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \
1548    (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
1549    (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \
1550    (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \
1551    (I[30] = (T)(img)(_n3##x,y,z,c)), \
1552    (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \
1553    (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \
1554    (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \
1555    (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \
1556    4>=((img)._width)?(img).width()-1:4); \
1557    (_n4##x<(img).width() && ( \
1558    (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \
1559    (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \
1560    (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \
1561    (I[31] = (T)(img)(_n4##x,y,z,c)), \
1562    (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \
1563    (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \
1564    (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \
1565    (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
1566    _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \
1567    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], \
1568    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], \
1569    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], \
1570    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], \
1571    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], \
1572    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], \
1573    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], \
1574    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], \
1575    _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
1576 
1577 #define cimg_for_in8x8(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1578   cimg_for_in8((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1579    _p3##x = x-3<0?0:x-3, \
1580    _p2##x = x-2<0?0:x-2, \
1581    _p1##x = x-1<0?0:x-1, \
1582    _n1##x = x+1>=(img).width()?(img).width()-1:x+1, \
1583    _n2##x = x+2>=(img).width()?(img).width()-1:x+2, \
1584    _n3##x = x+3>=(img).width()?(img).width()-1:x+3, \
1585    _n4##x = (int)( \
1586    (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \
1587    (I[8] = (T)(img)(_p3##x,_p2##y,z,c)), \
1588    (I[16] = (T)(img)(_p3##x,_p1##y,z,c)), \
1589    (I[24] = (T)(img)(_p3##x,y,z,c)), \
1590    (I[32] = (T)(img)(_p3##x,_n1##y,z,c)), \
1591    (I[40] = (T)(img)(_p3##x,_n2##y,z,c)), \
1592    (I[48] = (T)(img)(_p3##x,_n3##y,z,c)), \
1593    (I[56] = (T)(img)(_p3##x,_n4##y,z,c)), \
1594    (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \
1595    (I[9] = (T)(img)(_p2##x,_p2##y,z,c)), \
1596    (I[17] = (T)(img)(_p2##x,_p1##y,z,c)), \
1597    (I[25] = (T)(img)(_p2##x,y,z,c)), \
1598    (I[33] = (T)(img)(_p2##x,_n1##y,z,c)), \
1599    (I[41] = (T)(img)(_p2##x,_n2##y,z,c)), \
1600    (I[49] = (T)(img)(_p2##x,_n3##y,z,c)), \
1601    (I[57] = (T)(img)(_p2##x,_n4##y,z,c)), \
1602    (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \
1603    (I[10] = (T)(img)(_p1##x,_p2##y,z,c)), \
1604    (I[18] = (T)(img)(_p1##x,_p1##y,z,c)), \
1605    (I[26] = (T)(img)(_p1##x,y,z,c)), \
1606    (I[34] = (T)(img)(_p1##x,_n1##y,z,c)), \
1607    (I[42] = (T)(img)(_p1##x,_n2##y,z,c)), \
1608    (I[50] = (T)(img)(_p1##x,_n3##y,z,c)), \
1609    (I[58] = (T)(img)(_p1##x,_n4##y,z,c)), \
1610    (I[3] = (T)(img)(x,_p3##y,z,c)), \
1611    (I[11] = (T)(img)(x,_p2##y,z,c)), \
1612    (I[19] = (T)(img)(x,_p1##y,z,c)), \
1613    (I[27] = (T)(img)(x,y,z,c)), \
1614    (I[35] = (T)(img)(x,_n1##y,z,c)), \
1615    (I[43] = (T)(img)(x,_n2##y,z,c)), \
1616    (I[51] = (T)(img)(x,_n3##y,z,c)), \
1617    (I[59] = (T)(img)(x,_n4##y,z,c)), \
1618    (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
1619    (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \
1620    (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \
1621    (I[28] = (T)(img)(_n1##x,y,z,c)), \
1622    (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \
1623    (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \
1624    (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \
1625    (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \
1626    (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
1627    (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \
1628    (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \
1629    (I[29] = (T)(img)(_n2##x,y,z,c)), \
1630    (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \
1631    (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \
1632    (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \
1633    (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \
1634    (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
1635    (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \
1636    (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \
1637    (I[30] = (T)(img)(_n3##x,y,z,c)), \
1638    (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \
1639    (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \
1640    (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \
1641    (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \
1642    x+4>=(img).width()?(img).width()-1:x+4); \
1643    x<=(int)(x1) && ((_n4##x<(img).width() && ( \
1644    (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \
1645    (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \
1646    (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \
1647    (I[31] = (T)(img)(_n4##x,y,z,c)), \
1648    (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \
1649    (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \
1650    (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \
1651    (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
1652    _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \
1653    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], \
1654    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], \
1655    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], \
1656    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], \
1657    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], \
1658    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], \
1659    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], \
1660    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], \
1661    _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
1662 
1663 #define cimg_for9x9(img,x,y,z,c,I,T) \
1664   cimg_for9((img)._height,y) for (int x = 0, \
1665    _p4##x = 0, _p3##x = 0, _p2##x = 0, _p1##x = 0, \
1666    _n1##x = 1>=((img)._width)?(img).width()-1:1, \
1667    _n2##x = 2>=((img)._width)?(img).width()-1:2, \
1668    _n3##x = 3>=((img)._width)?(img).width()-1:3, \
1669    _n4##x = (int)( \
1670    (I[0] = I[1] = I[2] = I[3] = I[4] = (T)(img)(_p4##x,_p4##y,z,c)), \
1671    (I[9] = I[10] = I[11] = I[12] = I[13] = (T)(img)(0,_p3##y,z,c)), \
1672    (I[18] = I[19] = I[20] = I[21] = I[22] = (T)(img)(0,_p2##y,z,c)), \
1673    (I[27] = I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_p1##y,z,c)), \
1674    (I[36] = I[37] = I[38] = I[39] = I[40] = (T)(img)(0,y,z,c)), \
1675    (I[45] = I[46] = I[47] = I[48] = I[49] = (T)(img)(0,_n1##y,z,c)), \
1676    (I[54] = I[55] = I[56] = I[57] = I[58] = (T)(img)(0,_n2##y,z,c)), \
1677    (I[63] = I[64] = I[65] = I[66] = I[67] = (T)(img)(0,_n3##y,z,c)), \
1678    (I[72] = I[73] = I[74] = I[75] = I[76] = (T)(img)(0,_n4##y,z,c)), \
1679    (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \
1680    (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \
1681    (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \
1682    (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \
1683    (I[41] = (T)(img)(_n1##x,y,z,c)), \
1684    (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \
1685    (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \
1686    (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \
1687    (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \
1688    (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \
1689    (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \
1690    (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \
1691    (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \
1692    (I[42] = (T)(img)(_n2##x,y,z,c)), \
1693    (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \
1694    (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \
1695    (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \
1696    (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \
1697    (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \
1698    (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \
1699    (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \
1700    (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \
1701    (I[43] = (T)(img)(_n3##x,y,z,c)), \
1702    (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \
1703    (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \
1704    (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \
1705    (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \
1706    4>=((img)._width)?(img).width()-1:4); \
1707    (_n4##x<(img).width() && ( \
1708    (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \
1709    (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \
1710    (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \
1711    (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \
1712    (I[44] = (T)(img)(_n4##x,y,z,c)), \
1713    (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \
1714    (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \
1715    (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \
1716    (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
1717    _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \
1718    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], \
1719    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], I[16] = I[17], \
1720    I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], \
1721    I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \
1722    I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], \
1723    I[45] = I[46], I[46] = I[47], I[47] = I[48], I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], \
1724    I[54] = I[55], I[55] = I[56], I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], \
1725    I[63] = I[64], 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], \
1726    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], I[79] = I[80], \
1727    _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
1728 
1729 #define cimg_for_in9x9(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1730   cimg_for_in9((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1731    _p4##x = x-4<0?0:x-4, \
1732    _p3##x = x-3<0?0:x-3, \
1733    _p2##x = x-2<0?0:x-2, \
1734    _p1##x = x-1<0?0:x-1, \
1735    _n1##x = x+1>=(img).width()?(img).width()-1:x+1, \
1736    _n2##x = x+2>=(img).width()?(img).width()-1:x+2, \
1737    _n3##x = x+3>=(img).width()?(img).width()-1:x+3, \
1738    _n4##x = (int)( \
1739    (I[0] = (T)(img)(_p4##x,_p4##y,z,c)), \
1740    (I[9] = (T)(img)(_p4##x,_p3##y,z,c)), \
1741    (I[18] = (T)(img)(_p4##x,_p2##y,z,c)), \
1742    (I[27] = (T)(img)(_p4##x,_p1##y,z,c)), \
1743    (I[36] = (T)(img)(_p4##x,y,z,c)), \
1744    (I[45] = (T)(img)(_p4##x,_n1##y,z,c)), \
1745    (I[54] = (T)(img)(_p4##x,_n2##y,z,c)), \
1746    (I[63] = (T)(img)(_p4##x,_n3##y,z,c)), \
1747    (I[72] = (T)(img)(_p4##x,_n4##y,z,c)), \
1748    (I[1] = (T)(img)(_p3##x,_p4##y,z,c)), \
1749    (I[10] = (T)(img)(_p3##x,_p3##y,z,c)), \
1750    (I[19] = (T)(img)(_p3##x,_p2##y,z,c)), \
1751    (I[28] = (T)(img)(_p3##x,_p1##y,z,c)), \
1752    (I[37] = (T)(img)(_p3##x,y,z,c)), \
1753    (I[46] = (T)(img)(_p3##x,_n1##y,z,c)), \
1754    (I[55] = (T)(img)(_p3##x,_n2##y,z,c)), \
1755    (I[64] = (T)(img)(_p3##x,_n3##y,z,c)), \
1756    (I[73] = (T)(img)(_p3##x,_n4##y,z,c)), \
1757    (I[2] = (T)(img)(_p2##x,_p4##y,z,c)), \
1758    (I[11] = (T)(img)(_p2##x,_p3##y,z,c)), \
1759    (I[20] = (T)(img)(_p2##x,_p2##y,z,c)), \
1760    (I[29] = (T)(img)(_p2##x,_p1##y,z,c)), \
1761    (I[38] = (T)(img)(_p2##x,y,z,c)), \
1762    (I[47] = (T)(img)(_p2##x,_n1##y,z,c)), \
1763    (I[56] = (T)(img)(_p2##x,_n2##y,z,c)), \
1764    (I[65] = (T)(img)(_p2##x,_n3##y,z,c)), \
1765    (I[74] = (T)(img)(_p2##x,_n4##y,z,c)), \
1766    (I[3] = (T)(img)(_p1##x,_p4##y,z,c)), \
1767    (I[12] = (T)(img)(_p1##x,_p3##y,z,c)), \
1768    (I[21] = (T)(img)(_p1##x,_p2##y,z,c)), \
1769    (I[30] = (T)(img)(_p1##x,_p1##y,z,c)), \
1770    (I[39] = (T)(img)(_p1##x,y,z,c)), \
1771    (I[48] = (T)(img)(_p1##x,_n1##y,z,c)), \
1772    (I[57] = (T)(img)(_p1##x,_n2##y,z,c)), \
1773    (I[66] = (T)(img)(_p1##x,_n3##y,z,c)), \
1774    (I[75] = (T)(img)(_p1##x,_n4##y,z,c)), \
1775    (I[4] = (T)(img)(x,_p4##y,z,c)), \
1776    (I[13] = (T)(img)(x,_p3##y,z,c)), \
1777    (I[22] = (T)(img)(x,_p2##y,z,c)), \
1778    (I[31] = (T)(img)(x,_p1##y,z,c)), \
1779    (I[40] = (T)(img)(x,y,z,c)), \
1780    (I[49] = (T)(img)(x,_n1##y,z,c)), \
1781    (I[58] = (T)(img)(x,_n2##y,z,c)), \
1782    (I[67] = (T)(img)(x,_n3##y,z,c)), \
1783    (I[76] = (T)(img)(x,_n4##y,z,c)), \
1784    (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \
1785    (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \
1786    (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \
1787    (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \
1788    (I[41] = (T)(img)(_n1##x,y,z,c)), \
1789    (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \
1790    (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \
1791    (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \
1792    (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \
1793    (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \
1794    (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \
1795    (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \
1796    (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \
1797    (I[42] = (T)(img)(_n2##x,y,z,c)), \
1798    (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \
1799    (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \
1800    (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \
1801    (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \
1802    (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \
1803    (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \
1804    (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \
1805    (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \
1806    (I[43] = (T)(img)(_n3##x,y,z,c)), \
1807    (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \
1808    (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \
1809    (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \
1810    (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \
1811    x+4>=(img).width()?(img).width()-1:x+4); \
1812    x<=(int)(x1) && ((_n4##x<(img).width() && ( \
1813    (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \
1814    (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \
1815    (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \
1816    (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \
1817    (I[44] = (T)(img)(_n4##x,y,z,c)), \
1818    (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \
1819    (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \
1820    (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \
1821    (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
1822    _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \
1823    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], \
1824    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], I[16] = I[17], \
1825    I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], \
1826    I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \
1827    I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], \
1828    I[45] = I[46], I[46] = I[47], I[47] = I[48], I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], \
1829    I[54] = I[55], I[55] = I[56], I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], \
1830    I[63] = I[64], 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], \
1831    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], I[79] = I[80], \
1832    _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
1833 
1834 #define cimg_for2x2x2(img,x,y,z,c,I,T) \
1835  cimg_for2((img)._depth,z) cimg_for2((img)._height,y) for (int x = 0, \
1836    _n1##x = (int)( \
1837    (I[0] = (T)(img)(0,y,z,c)), \
1838    (I[2] = (T)(img)(0,_n1##y,z,c)), \
1839    (I[4] = (T)(img)(0,y,_n1##z,c)), \
1840    (I[6] = (T)(img)(0,_n1##y,_n1##z,c)), \
1841    1>=(img)._width?(img).width()-1:1); \
1842    (_n1##x<(img).width() && ( \
1843    (I[1] = (T)(img)(_n1##x,y,z,c)), \
1844    (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \
1845    (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \
1846    (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
1847    x==--_n1##x; \
1848    I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \
1849    ++x, ++_n1##x)
1850 
1851 #define cimg_for_in2x2x2(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \
1852  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), \
1853    _n1##x = (int)( \
1854    (I[0] = (T)(img)(x,y,z,c)), \
1855    (I[2] = (T)(img)(x,_n1##y,z,c)), \
1856    (I[4] = (T)(img)(x,y,_n1##z,c)), \
1857    (I[6] = (T)(img)(x,_n1##y,_n1##z,c)), \
1858    x+1>=(int)(img)._width?(img).width()-1:x+1); \
1859    x<=(int)(x1) && ((_n1##x<(img).width() && ( \
1860    (I[1] = (T)(img)(_n1##x,y,z,c)), \
1861    (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \
1862    (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \
1863    (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
1864    x==--_n1##x); \
1865    I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \
1866    ++x, ++_n1##x)
1867 
1868 #define cimg_for3x3x3(img,x,y,z,c,I,T) \
1869  cimg_for3((img)._depth,z) cimg_for3((img)._height,y) for (int x = 0, \
1870    _p1##x = 0, \
1871    _n1##x = (int)( \
1872    (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \
1873    (I[3] = I[4] = (T)(img)(0,y,_p1##z,c)),  \
1874    (I[6] = I[7] = (T)(img)(0,_n1##y,_p1##z,c)), \
1875    (I[9] = I[10] = (T)(img)(0,_p1##y,z,c)), \
1876    (I[12] = I[13] = (T)(img)(0,y,z,c)), \
1877    (I[15] = I[16] = (T)(img)(0,_n1##y,z,c)), \
1878    (I[18] = I[19] = (T)(img)(0,_p1##y,_n1##z,c)), \
1879    (I[21] = I[22] = (T)(img)(0,y,_n1##z,c)), \
1880    (I[24] = I[25] = (T)(img)(0,_n1##y,_n1##z,c)), \
1881    1>=(img)._width?(img).width()-1:1); \
1882    (_n1##x<(img).width() && ( \
1883    (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \
1884    (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \
1885    (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \
1886    (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \
1887    (I[14] = (T)(img)(_n1##x,y,z,c)), \
1888    (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \
1889    (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \
1890    (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \
1891    (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
1892    x==--_n1##x; \
1893    I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \
1894    I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \
1895    I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \
1896    _p1##x = x++, ++_n1##x)
1897 
1898 #define cimg_for_in3x3x3(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \
1899  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), \
1900    _p1##x = x-1<0?0:x-1, \
1901    _n1##x = (int)( \
1902    (I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \
1903    (I[3] = (T)(img)(_p1##x,y,_p1##z,c)),  \
1904    (I[6] = (T)(img)(_p1##x,_n1##y,_p1##z,c)), \
1905    (I[9] = (T)(img)(_p1##x,_p1##y,z,c)), \
1906    (I[12] = (T)(img)(_p1##x,y,z,c)), \
1907    (I[15] = (T)(img)(_p1##x,_n1##y,z,c)), \
1908    (I[18] = (T)(img)(_p1##x,_p1##y,_n1##z,c)), \
1909    (I[21] = (T)(img)(_p1##x,y,_n1##z,c)), \
1910    (I[24] = (T)(img)(_p1##x,_n1##y,_n1##z,c)), \
1911    (I[1] = (T)(img)(x,_p1##y,_p1##z,c)), \
1912    (I[4] = (T)(img)(x,y,_p1##z,c)),  \
1913    (I[7] = (T)(img)(x,_n1##y,_p1##z,c)), \
1914    (I[10] = (T)(img)(x,_p1##y,z,c)), \
1915    (I[13] = (T)(img)(x,y,z,c)), \
1916    (I[16] = (T)(img)(x,_n1##y,z,c)), \
1917    (I[19] = (T)(img)(x,_p1##y,_n1##z,c)), \
1918    (I[22] = (T)(img)(x,y,_n1##z,c)), \
1919    (I[25] = (T)(img)(x,_n1##y,_n1##z,c)), \
1920    x+1>=(int)(img)._width?(img).width()-1:x+1); \
1921    x<=(int)(x1) && ((_n1##x<(img).width() && ( \
1922    (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \
1923    (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \
1924    (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \
1925    (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \
1926    (I[14] = (T)(img)(_n1##x,y,z,c)), \
1927    (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \
1928    (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \
1929    (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \
1930    (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
1931    x==--_n1##x); \
1932    I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \
1933    I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \
1934    I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \
1935    _p1##x = x++, ++_n1##x)
1936 
1937 #define cimglist_for(list,l) for (int l = 0; l<(int)(list)._width; ++l)
1938 #define cimglist_for_in(list,l0,l1,l) \
1939   for (int l = (int)(l0)<0?0:(int)(l0), _max##l = (unsigned int)l1<(list)._width?(int)(l1):(int)(list)._width-1; l<=_max##l; ++l)
1940 
1941 #define cimglist_apply(list,fn) cimglist_for(list,__##fn) (list)[__##fn].fn
1942 
1943 // Macros used to display error messages when exceptions are thrown.
1944 // You should not use these macros is your own code.
1945 #define _cimgdisplay_instance "[instance(%u,%u,%u,%c%s%c)] CImgDisplay::"
1946 #define cimgdisplay_instance _width,_height,_normalization,_title?'\"':'[',_title?_title:"untitled",_title?'\"':']'
1947 #define _cimg_instance "[instance(%u,%u,%u,%u,%p,%sshared)] CImg<%s>::"
1948 #define cimg_instance _width,_height,_depth,_spectrum,_data,_is_shared?"":"non-",pixel_type()
1949 #define _cimglist_instance "[instance(%u,%u,%p)] CImgList<%s>::"
1950 #define cimglist_instance _width,_allocated_width,_data,pixel_type()
1951 
1952 /*------------------------------------------------
1953  #
1954  #
1955  #  Define cimg_library:: namespace
1956  #
1957  #
1958  -------------------------------------------------*/
1959 //! Contains <i>all classes and functions</i> of the \CImg library.
1960 /**
1961    This namespace is defined to avoid functions and class names collisions
1962    that could happen with the inclusion of other C++ header files.
1963    Anyway, it should not happen often and you should reasonnably start most of your
1964    \CImg-based programs with
1965    \code
1966    #include "CImg.h"
1967    using namespace cimg_library;
1968    \endcode
1969    to simplify the declaration of \CImg Library objects afterwards.
1970 **/
1971 namespace cimg_library_suffixed {
1972 
1973   // Declare the four classes of the CImg Library.
1974   template<typename T=float> struct CImg;
1975   template<typename T=float> struct CImgList;
1976   struct CImgDisplay;
1977   struct CImgException;
1978 
1979   // Declare cimg:: namespace.
1980   // This is an uncomplete namespace definition here. It only contains some
1981   // necessary stuffs to ensure a correct declaration order of the classes and functions
1982   // defined afterwards.
1983   namespace cimg {
1984 
1985     // Define ascii sequences for colored terminal output.
1986 #ifdef cimg_use_vt100
1987     const char t_normal[] = { 0x1b, '[', '0', ';', '0', ';', '0', 'm', 0 };
1988     const char t_black[] = { 0x1b, '[', '0', ';', '3', '0', ';', '5', '9', 'm', 0 };
1989     const char t_red[] = { 0x1b, '[', '0', ';', '3', '1', ';', '5', '9', 'm', 0 };
1990     const char t_green[] = { 0x1b, '[', '0', ';', '3', '2', ';', '5', '9', 'm', 0 };
1991     const char t_yellow[] = { 0x1b, '[', '0', ';', '3', '3', ';', '5', '9', 'm', 0 };
1992     const char t_blue[] = { 0x1b, '[', '0', ';', '3', '4', ';', '5', '9', 'm', 0 };
1993     const char t_magenta[] = { 0x1b, '[', '0', ';', '3', '5', ';', '5', '9', 'm', 0 };
1994     const char t_cyan[] = { 0x1b, '[', '0', ';', '3', '6', ';', '5', '9', 'm', 0 };
1995     const char t_white[] = { 0x1b, '[', '0', ';', '3', '7', ';', '5', '9', 'm', 0 };
1996     const char t_bold[] = { 0x1b, '[', '1', 'm', 0 };
1997     const char t_underscore[] = { 0x1b, '[', '4', 'm', 0 };
1998 #else
1999     const char t_normal[] = { 0 };
2000     const char *const t_black = cimg::t_normal,
2001       *const t_red = cimg::t_normal,
2002       *const t_green = cimg::t_normal,
2003       *const t_yellow = cimg::t_normal,
2004       *const t_blue = cimg::t_normal,
2005       *const t_magenta = cimg::t_normal,
2006       *const t_cyan = cimg::t_normal,
2007       *const t_white = cimg::t_normal,
2008       *const t_bold = cimg::t_normal,
2009       *const t_underscore = cimg::t_normal;
2010 #endif
2011 
2012     inline std::FILE* output(std::FILE *file=0);
2013     inline void info();
2014 
2015     //! Avoid warning messages due to unused parameters. Do nothing actually.
2016     template<typename T>
unused(const T &,...)2017     inline void unused(const T&, ...) {}
2018 
_exception_mode(const unsigned int value,const bool is_set)2019     inline unsigned int& _exception_mode(const unsigned int value, const bool is_set) {
2020       static unsigned int mode = cimg_verbosity;
2021       if (is_set) mode = value;
2022       return mode;
2023     }
2024 
2025     //! Set current \CImg exception mode.
2026     /**
2027        The way error messages are handled by \CImg can be changed dynamically, using this function.
2028        \param mode Desired exception mode. Possible values are :
2029        - \c 0 : Hide library messages (quiet mode).
2030        - \c 1 : Print library messages on the console.
2031        - \c 2 : Display library messages on a dialog window (default behavior).
2032        - \c 3 : Do as \c 1 + add extra debug warnings (slow down the code !).
2033        - \c 4 : Do as \c 2 + add extra debug warnings (slow down the code !).
2034      **/
exception_mode(const unsigned int mode)2035     inline unsigned int& exception_mode(const unsigned int mode) {
2036       return _exception_mode(mode,true);
2037     }
2038 
2039     //! Return current \CImg exception mode.
2040     /**
2041        \note By default, return the value of configuration macro \c cimg_verbosity
2042     **/
exception_mode()2043     inline unsigned int& exception_mode() {
2044       return _exception_mode(0,false);
2045     }
2046 
2047     inline int dialog(const char *const title, const char *const msg, const char *const button1_label="OK",
2048                       const char *const button2_label=0, const char *const button3_label=0,
2049                       const char *const button4_label=0, const char *const button5_label=0,
2050                       const char *const button6_label=0, const bool centering=false);
2051 
2052     inline double eval(const char *const expression, const double x=0, const double y=0, const double z=0, const double c=0);
2053   }
2054 
2055   /*---------------------------------------
2056     #
2057     # Define the CImgException structures
2058     #
2059     --------------------------------------*/
2060   //! Instances of \c CImgException are thrown when errors are encountered in a \CImg function call.
2061   /**
2062      \par Overview
2063 
2064       CImgException is the base class of all exceptions thrown by \CImg.
2065       CImgException is never thrown itself. Derived classes that specify the type of errord are thrown instead.
2066       These derived classes can be :
2067 
2068       - \b CImgArgumentException : Thrown when one argument of a called \CImg function is invalid.
2069       This is probably one of the most thrown exception by \CImg.
2070       For instance, the following example throws a \c CImgArgumentException :
2071       \code
2072       CImg<float> img(100,100,1,3); // Define a 100x100 color image with float-valued pixels.
2073       img.mirror('e');              // Try to mirror image along the (non-existing) 'e'-axis.
2074       \endcode
2075 
2076       - \b CImgDisplayException : Thrown when something went wrong during the display of images in CImgDisplay instances.
2077 
2078       - \b CImgInstanceException : Thrown when an instance associated to a called \CImg method does not fit
2079       the function requirements. For instance, the following example throws a \c CImgInstanceException :
2080       \code
2081       const CImg<float> img;           // Define an empty image.
2082       const float value = img.at(0);   // Try to read first pixel value (does not exist).
2083       \endcode
2084 
2085       - \b CImgIOException : Thrown when an error occured when trying to load or save image files.
2086       This happens when trying to read files that do not exist or with invalid formats.
2087       For instance, the following example throws a \c CImgIOException :
2088       \code
2089       const CImg<float> img("missing_file.jpg");  // Try to load a file that does not exist.
2090       \endcode
2091 
2092       - \b CImgWarningException : Thrown only if configuration macro \c cimg_strict_warnings is set, and
2093       when a \CImg function has to display a warning message (see cimg::warn()).
2094 
2095       It is not recommended to throw CImgException instances by yourself, since they are expected to be thrown only by \CImg.
2096       When an error occurs in a library function call, \CImg may display error messages on the screen or on the
2097       standard output, depending on the current \CImg exception mode.
2098       The \CImg exception mode can be get and set by functions cimg::exception_mode() and cimg::exception_mode(unsigned int).
2099 
2100       \par Exceptions handling
2101 
2102       In all cases, when an error occurs in \CImg, an instance of the corresponding exception class is thrown.
2103       This may lead the program to break (this is the default behavior), but you can bypass this behavior by handling the exceptions by yourself,
2104       using a usual <tt>try { ... } catch () { ... }</tt> bloc, as in the following example :
2105       \code
2106       #define "CImg.h"
2107       using namespace cimg_library;
2108       int main() {
2109         cimg::exception_mode(0);                                    // Enable quiet exception mode.
2110         try {
2111           ...                                                       // Here, do what you want to stress the CImg library.
2112         } catch (CImgException &e) {                                // You succeeded : something went wrong !
2113           std::fprintf(stderr,"CImg Library Error : %s",e.what());  // Display your custom error message.
2114           ...                                                       // Do what you want now to save the ship !
2115           }
2116         }
2117       \endcode
2118   **/
2119   struct CImgException : public std::exception {
2120 #define _cimg_exception_err(etype,disp_flag) \
2121   std::va_list ap; va_start(ap,format); cimg_vsnprintf(_message,sizeof(_message),format,ap); va_end(ap); \
2122   if (cimg::exception_mode()) { \
2123     std::fprintf(cimg::output(),"\n%s[CImg] *** %s ***%s %s\n",cimg::t_red,etype,cimg::t_normal,_message); \
2124     if (cimg_display && disp_flag && !(cimg::exception_mode()%2)) try { cimg::dialog(etype,_message,"Abort"); } catch (CImgException&) {} \
2125     if (cimg::exception_mode()>=3) cimg_library_suffixed::cimg::info(); \
2126   }
2127 
2128     char _message[16384];
CImgExceptionCImgException2129     CImgException() { *_message = 0; }
CImgExceptionCImgException2130     CImgException(const char *const format, ...) { _cimg_exception_err("CImgException",true); }
2131     //! Return a C-string containing the error message associated to the thrown exception.
whatCImgException2132     const char *what() const throw() { return _message; }
2133   };
2134 
2135   // The CImgInstanceException class is used to throw an exception related
2136   // to an invalid instance encountered in a library function call.
2137   struct CImgInstanceException : public CImgException {
CImgInstanceExceptionCImgInstanceException2138     CImgInstanceException(const char *const format, ...) { _cimg_exception_err("CImgInstanceException",true); }
2139   };
2140 
2141   // The CImgArgumentException class is used to throw an exception related
2142   // to invalid arguments encountered in a library function call.
2143   struct CImgArgumentException : public CImgException {
CImgArgumentExceptionCImgArgumentException2144     CImgArgumentException(const char *const format, ...) { _cimg_exception_err("CImgArgumentException",true); }
2145   };
2146 
2147   // The CImgIOException class is used to throw an exception related
2148   // to input/output file problems encountered in a library function call.
2149   struct CImgIOException : public CImgException {
CImgIOExceptionCImgIOException2150     CImgIOException(const char *const format, ...) { _cimg_exception_err("CImgIOException",true); }
2151   };
2152 
2153   // The CImgDisplayException class is used to throw an exception related
2154   // to display problems encountered in a library function call.
2155   struct CImgDisplayException : public CImgException {
CImgDisplayExceptionCImgDisplayException2156     CImgDisplayException(const char *const format, ...) { _cimg_exception_err("CImgDisplayException",false); }
2157   };
2158 
2159   // The CImgWarningException class is used to throw an exception for warnings
2160   // encountered in a library function call.
2161   struct CImgWarningException : public CImgException {
CImgWarningExceptionCImgWarningException2162     CImgWarningException(const char *const format, ...) { _cimg_exception_err("CImgWarningException",false); }
2163   };
2164 
2165   /*-------------------------------------
2166     #
2167     # Define cimg:: namespace
2168     #
2169     -----------------------------------*/
2170   //! Contains \a low-level functions and variables of the \CImg Library.
2171   /**
2172      Most of the functions and variables within this namespace are used by the \CImg library for low-level operations.
2173      You may use them to access specific const values or environment variables internally used by \CImg.
2174      \warning Never write <tt>using namespace cimg_library::cimg;</tt> in your source code. Lot of functions in the
2175      <tt>cimg:: namespace</tt> have the same names as standard C functions that may be defined in the global namespace <tt>::</tt>.
2176   **/
2177   namespace cimg {
2178 
2179     // Define traits that will be used to determine the best data type to work in CImg functions.
2180     //
2181     template<typename T> struct type {
stringtype2182       static const char* string() {
2183         static const char* s[] = { "unknown",   "unknown8",   "unknown16",  "unknown24",
2184                                    "unknown32", "unknown40",  "unknown48",  "unknown56",
2185                                    "unknown64", "unknown72",  "unknown80",  "unknown88",
2186                                    "unknown96", "unknown104", "unknown112", "unknown120",
2187                                    "unknown128" };
2188         return s[(sizeof(T)<17)?sizeof(T):0];
2189       }
is_floattype2190       static bool is_float() { return false; }
is_inftype2191       static bool is_inf(const T) { return false; }
is_nantype2192       static bool is_nan(const T) { return false; }
mintype2193       static T min() { return (T)-1>0?(T)0:(T)-1<<(8*sizeof(T)-1); }
maxtype2194       static T max() { return (T)-1>0?(T)-1:~((T)-1<<(8*sizeof(T)-1)); }
inftype2195       static T inf() { return max(); }
cuttype2196       static T cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(T)val; }
formattype2197       static const char* format() { return "%s"; }
formattype2198       static const char* format(const T val) { static const char *const s = "unknown"; cimg::unused(val); return s; }
2199     };
2200 
2201     template<> struct type<bool> {
2202       static const char* string() { static const char *const s = "bool"; return s; }
2203       static bool is_float() { return false; }
2204       static bool is_inf(const bool) { return false; }
2205       static bool is_nan(const bool) { return false; }
2206       static bool min() { return false; }
2207       static bool max() { return true; }
2208       static bool inf() { return max(); }
2209       static bool is_inf() { return false; }
2210       static bool cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(bool)val; }
2211       static const char* format() { return "%s"; }
2212       static const char* format(const bool val) { static const char* s[] = { "false", "true" }; return s[val?1:0]; }
2213     };
2214 
2215     template<> struct type<unsigned char> {
2216       static const char* string() { static const char *const s = "unsigned char"; return s; }
2217       static bool is_float() { return false; }
2218       static bool is_inf(const unsigned char) { return false; }
2219       static bool is_nan(const unsigned char) { return false; }
2220       static unsigned char min() { return 0; }
2221       static unsigned char max() { return (unsigned char)~0U; }
2222       static unsigned char inf() { return max(); }
2223       static unsigned char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; }
2224       static const char* format() { return "%u"; }
2225       static unsigned int format(const unsigned char val) { return (unsigned int)val; }
2226     };
2227 
2228     template<> struct type<char> {
2229       static const char* string() { static const char *const s = "char"; return s; }
2230       static bool is_float() { return false; }
2231       static bool is_inf(const char) { return false; }
2232       static bool is_nan(const char) { return false; }
2233       static char min() { return (char)(-1L<<(8*sizeof(char)-1)); }
2234       static char max() { return (char)~((char)(-1L<<(8*sizeof(char)-1))); }
2235       static char inf() { return max(); }
2236       static char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(char)val; }
2237       static const char* format() { return "%d"; }
2238       static int format(const char val) { return (int)val; }
2239     };
2240 
2241     template<> struct type<signed char> {
2242       static const char* string() { static const char *const s = "signed char"; return s; }
2243       static bool is_float() { return false; }
2244       static bool is_inf(const signed char) { return false; }
2245       static bool is_nan(const signed char) { return false; }
2246       static signed char min() { return (signed char)(-1L<<(8*sizeof(signed char)-1)); }
2247       static signed char max() { return ~((signed char)(-1L<<(8*sizeof(signed char)-1))); }
2248       static signed char inf() { return max(); }
2249       static signed char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(signed char)val; }
2250       static const char* format() { return "%d"; }
2251       static unsigned int format(const signed char val) { return (int)val; }
2252     };
2253 
2254     template<> struct type<unsigned short> {
2255       static const char* string() { static const char *const s = "unsigned short"; return s; }
2256       static bool is_float() { return false; }
2257       static bool is_inf(const unsigned short) { return false; }
2258       static bool is_nan(const unsigned short) { return false; }
2259       static unsigned short min() { return 0; }
2260       static unsigned short max() { return (unsigned short)~0U; }
2261       static unsigned short inf() { return max(); }
2262       static unsigned short cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned short)val; }
2263       static const char* format() { return "%u"; }
2264       static unsigned int format(const unsigned short val) { return (unsigned int)val; }
2265     };
2266 
2267     template<> struct type<short> {
2268       static const char* string() { static const char *const s = "short"; return s; }
2269       static bool is_float() { return false; }
2270       static bool is_inf(const short) { return false; }
2271       static bool is_nan(const short) { return false; }
2272       static short min() { return (short)(-1L<<(8*sizeof(short)-1)); }
2273       static short max() { return ~((short)(-1L<<(8*sizeof(short)-1))); }
2274       static short inf() { return max(); }
2275       static short cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(short)val; }
2276       static const char* format() { return "%d"; }
2277       static int format(const short val) { return (int)val; }
2278     };
2279 
2280     template<> struct type<unsigned int> {
2281       static const char* string() { static const char *const s = "unsigned int"; return s; }
2282       static bool is_float() { return false; }
2283       static bool is_inf(const unsigned int) { return false; }
2284       static bool is_nan(const unsigned int) { return false; }
2285       static unsigned int min() { return 0; }
2286       static unsigned int max() { return (unsigned int)~0U; }
2287       static unsigned int inf() { return max(); }
2288       static unsigned int cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned int)val; }
2289       static const char* format() { return "%u"; }
2290       static unsigned int format(const unsigned int val) { return val; }
2291     };
2292 
2293     template<> struct type<int> {
2294       static const char* string() { static const char *const s = "int"; return s; }
2295       static bool is_float() { return false; }
2296       static bool is_inf(const int) { return false; }
2297       static bool is_nan(const int) { return false; }
2298       static int min() { return (int)(-1L<<(8*sizeof(int)-1)); }
2299       static int max() { return ~((int)(-1L<<(8*sizeof(int)-1))); }
2300       static int inf() { return max(); }
2301       static int cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(int)val; }
2302       static const char* format() { return "%d"; }
2303       static int format(const int val) { return val; }
2304     };
2305 
2306     template<> struct type<unsigned long> {
2307       static const char* string() { static const char *const s = "unsigned long"; return s; }
2308       static bool is_float() { return false; }
2309       static bool is_inf(const unsigned long) { return false; }
2310       static bool is_nan(const unsigned long) { return false; }
2311       static unsigned long min() { return 0; }
2312       static unsigned long max() { return (unsigned long)~0UL; }
2313       static unsigned long inf() { return max(); }
2314       static unsigned long cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned long)val; }
2315       static const char* format() { return "%lu"; }
2316       static unsigned long format(const unsigned long val) { return val; }
2317     };
2318 
2319     template<> struct type<long> {
2320       static const char* string() { static const char *const s = "long"; return s; }
2321       static bool is_float() { return false; }
2322       static bool is_inf(const long) { return false; }
2323       static bool is_nan(const long) { return false; }
2324       static long min() { return (long)(-1L<<(8*sizeof(long)-1)); }
2325       static long max() { return ~((long)(-1L<<(8*sizeof(long)-1))); }
2326       static long inf() { return max(); }
2327       static long cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(long)val; }
2328       static const char* format() { return "%ld"; }
2329       static long format(const long val) { return val; }
2330     };
2331 
2332     template<> struct type<double> {
2333       static const char* string() { static const char *const s = "double"; return s; }
2334       static bool is_float() { return true; }
2335       static bool is_inf(const double val) { return !is_nan(val) && val+1==val; }
2336       static bool is_nan(const double val) { return !(val<=0 || val>=0); }
2337       static double min() { return -1.7E308; }
2338       static double max() { return  1.7E308; }
2339       static double inf() { return max()*max(); }
2340       static double nan() { static const double val_nan = -std::sqrt(-1.0); return val_nan; }
2341       static double cut(const double val) { return val<min()?min():val>max()?max():val; }
2342       static const char* format() { return "%.16g"; }
2343       static double format(const double val) { return val; }
2344     };
2345 
2346     template<> struct type<float> {
2347       static const char* string() { static const char *const s = "float"; return s; }
2348       static bool is_float() { return true; }
2349       static bool is_inf(const float val) { return cimg::type<double>::is_inf((double)val); }
2350       static bool is_nan(const float val) { return cimg::type<double>::is_nan((double)val); }
2351       static float min() { return -3.4E38f; }
2352       static float max() { return  3.4E38f; }
2353       static float inf() { return (float)cimg::type<double>::inf(); }
2354       static float nan() { return (float)cimg::type<double>::nan(); }
2355       static float cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(float)val; }
2356       static const char* format() { return "%.16g"; }
2357       static double format(const float val) { return (double)val; }
2358     };
2359 
2360     template<typename T, typename t> struct superset { typedef T type; };
2361     template<> struct superset<bool,unsigned char> { typedef unsigned char type; };
2362     template<> struct superset<bool,char> { typedef char type; };
2363     template<> struct superset<bool,signed char> { typedef signed char type; };
2364     template<> struct superset<bool,unsigned short> { typedef unsigned short type; };
2365     template<> struct superset<bool,short> { typedef short type; };
2366     template<> struct superset<bool,unsigned int> { typedef unsigned int type; };
2367     template<> struct superset<bool,int> { typedef int type; };
2368     template<> struct superset<bool,unsigned long> { typedef unsigned long type; };
2369     template<> struct superset<bool,long> { typedef long type; };
2370     template<> struct superset<bool,float> { typedef float type; };
2371     template<> struct superset<bool,double> { typedef double type; };
2372     template<> struct superset<unsigned char,char> { typedef short type; };
2373     template<> struct superset<unsigned char,signed char> { typedef short type; };
2374     template<> struct superset<unsigned char,unsigned short> { typedef unsigned short type; };
2375     template<> struct superset<unsigned char,short> { typedef short type; };
2376     template<> struct superset<unsigned char,unsigned int> { typedef unsigned int type; };
2377     template<> struct superset<unsigned char,int> { typedef int type; };
2378     template<> struct superset<unsigned char,unsigned long> { typedef unsigned long type; };
2379     template<> struct superset<unsigned char,long> { typedef long type; };
2380     template<> struct superset<unsigned char,float> { typedef float type; };
2381     template<> struct superset<unsigned char,double> { typedef double type; };
2382     template<> struct superset<signed char,unsigned char> { typedef short type; };
2383     template<> struct superset<signed char,char> { typedef short type; };
2384     template<> struct superset<signed char,unsigned short> { typedef int type; };
2385     template<> struct superset<signed char,short> { typedef short type; };
2386     template<> struct superset<signed char,unsigned int> { typedef long type; };
2387     template<> struct superset<signed char,int> { typedef int type; };
2388     template<> struct superset<signed char,unsigned long> { typedef long type; };
2389     template<> struct superset<signed char,long> { typedef long type; };
2390     template<> struct superset<signed char,float> { typedef float type; };
2391     template<> struct superset<signed char,double> { typedef double type; };
2392     template<> struct superset<char,unsigned char> { typedef short type; };
2393     template<> struct superset<char,signed char> { typedef short type; };
2394     template<> struct superset<char,unsigned short> { typedef int type; };
2395     template<> struct superset<char,short> { typedef short type; };
2396     template<> struct superset<char,unsigned int> { typedef long type; };
2397     template<> struct superset<char,int> { typedef int type; };
2398     template<> struct superset<char,unsigned long> { typedef long type; };
2399     template<> struct superset<char,long> { typedef long type; };
2400     template<> struct superset<char,float> { typedef float type; };
2401     template<> struct superset<char,double> { typedef double type; };
2402     template<> struct superset<unsigned short,char> { typedef int type; };
2403     template<> struct superset<unsigned short,signed char> { typedef int type; };
2404     template<> struct superset<unsigned short,short> { typedef int type; };
2405     template<> struct superset<unsigned short,unsigned int> { typedef unsigned int type; };
2406     template<> struct superset<unsigned short,int> { typedef int type; };
2407     template<> struct superset<unsigned short,unsigned long> { typedef unsigned long type; };
2408     template<> struct superset<unsigned short,long> { typedef long type; };
2409     template<> struct superset<unsigned short,float> { typedef float type; };
2410     template<> struct superset<unsigned short,double> { typedef double type; };
2411     template<> struct superset<short,unsigned short> { typedef int type; };
2412     template<> struct superset<short,unsigned int> { typedef long type; };
2413     template<> struct superset<short,int> { typedef int type; };
2414     template<> struct superset<short,unsigned long> { typedef long type; };
2415     template<> struct superset<short,long> { typedef long type; };
2416     template<> struct superset<short,float> { typedef float type; };
2417     template<> struct superset<short,double> { typedef double type; };
2418     template<> struct superset<unsigned int,char> { typedef long type; };
2419     template<> struct superset<unsigned int,signed char> { typedef long type; };
2420     template<> struct superset<unsigned int,short> { typedef long type; };
2421     template<> struct superset<unsigned int,int> { typedef long type; };
2422     template<> struct superset<unsigned int,unsigned long> { typedef unsigned long type; };
2423     template<> struct superset<unsigned int,long> { typedef long type; };
2424     template<> struct superset<unsigned int,float> { typedef float type; };
2425     template<> struct superset<unsigned int,double> { typedef double type; };
2426     template<> struct superset<int,unsigned int> { typedef long type; };
2427     template<> struct superset<int,unsigned long> { typedef long type; };
2428     template<> struct superset<int,long> { typedef long type; };
2429     template<> struct superset<int,float> { typedef float type; };
2430     template<> struct superset<int,double> { typedef double type; };
2431     template<> struct superset<unsigned long,char> { typedef long type; };
2432     template<> struct superset<unsigned long,signed char> { typedef long type; };
2433     template<> struct superset<unsigned long,short> { typedef long type; };
2434     template<> struct superset<unsigned long,int> { typedef long type; };
2435     template<> struct superset<unsigned long,long> { typedef long type; };
2436     template<> struct superset<unsigned long,float> { typedef float type; };
2437     template<> struct superset<unsigned long,double> { typedef double type; };
2438     template<> struct superset<long,float> { typedef float type; };
2439     template<> struct superset<long,double> { typedef double type; };
2440     template<> struct superset<float,double> { typedef double type; };
2441 
2442     template<typename t1, typename t2, typename t3> struct superset2 {
2443       typedef typename superset<t1, typename superset<t2,t3>::type>::type type;
2444     };
2445 
2446     template<typename t1, typename t2, typename t3, typename t4> struct superset3 {
2447       typedef typename superset<t1, typename superset2<t2,t3,t4>::type>::type type;
2448     };
2449 
2450     template<typename t1, typename t2> struct last { typedef t2 type; };
2451 
2452 #define _cimg_Tt       typename cimg::superset<T,t>::type
2453 #define _cimg_Tfloat   typename cimg::superset<T,float>::type
2454 #define _cimg_Ttfloat  typename cimg::superset2<T,t,float>::type
2455 #define _cimg_Ttdouble typename cimg::superset2<T,t,double>::type
2456 
2457     // Define variables used internally by CImg.
2458 #if cimg_display==1
2459     struct X11_info {
2460       volatile unsigned int nb_wins;
2461       pthread_t*       event_thread;
2462       CImgDisplay*     wins[1024];
2463       Display*         display;
2464       unsigned int     nb_bits;
2465       bool             is_blue_first;
2466       bool             is_shm_enabled;
2467       bool             byte_order;
2468 #ifdef cimg_use_xrandr
2469       XRRScreenSize *resolutions;
2470       Rotation curr_rotation;
2471       unsigned int curr_resolution;
2472       unsigned int nb_resolutions;
2473 #endif
2474       X11_info():nb_wins(0),event_thread(0),display(0),
2475                  nb_bits(0),is_blue_first(false),is_shm_enabled(false),byte_order(false) {
2476 #ifdef cimg_use_xrandr
2477         resolutions = 0;
2478         curr_rotation = 0;
2479         curr_resolution = nb_resolutions = 0;
2480 #endif
2481       }
2482     };
2483 #if defined(cimg_module)
2484     X11_info& X11_attr();
2485 #elif defined(cimg_main)
2486     X11_info& X11_attr() { static X11_info val; return val; }
2487 #else
2488     inline X11_info& X11_attr() { static X11_info val; return val; }
2489 #endif
2490 
2491 #elif cimg_display==2
2492     struct Win32_info {
2493       HANDLE wait_event;
2494       Win32_info() { wait_event = CreateEvent(0,FALSE,FALSE,0); }
2495     };
2496 #if defined(cimg_module)
2497     Win32_info& Win32_attr();
2498 #elif defined(cimg_main)
2499     Win32_info& Win32_attr() { static Win32_info val; return val; }
2500 #else
2501     inline Win32_info& Win32_attr() { static Win32_info val; return val; }
2502 #endif
2503 #endif
2504 
2505 #if defined(cimg_use_magick)
2506     static struct Magick_info {
2507       Magick_info() {
2508         Magick::InitializeMagick("");
2509       }
2510     } _Magick_info;
2511 #endif
2512 
2513 #if cimg_display==1
2514     // Define keycodes for X11-based graphical systems.
2515     const unsigned int keyESC        = XK_Escape;
2516     const unsigned int keyF1         = XK_F1;
2517     const unsigned int keyF2         = XK_F2;
2518     const unsigned int keyF3         = XK_F3;
2519     const unsigned int keyF4         = XK_F4;
2520     const unsigned int keyF5         = XK_F5;
2521     const unsigned int keyF6         = XK_F6;
2522     const unsigned int keyF7         = XK_F7;
2523     const unsigned int keyF8         = XK_F8;
2524     const unsigned int keyF9         = XK_F9;
2525     const unsigned int keyF10        = XK_F10;
2526     const unsigned int keyF11        = XK_F11;
2527     const unsigned int keyF12        = XK_F12;
2528     const unsigned int keyPAUSE      = XK_Pause;
2529     const unsigned int key1          = XK_1;
2530     const unsigned int key2          = XK_2;
2531     const unsigned int key3          = XK_3;
2532     const unsigned int key4          = XK_4;
2533     const unsigned int key5          = XK_5;
2534     const unsigned int key6          = XK_6;
2535     const unsigned int key7          = XK_7;
2536     const unsigned int key8          = XK_8;
2537     const unsigned int key9          = XK_9;
2538     const unsigned int key0          = XK_0;
2539     const unsigned int keyBACKSPACE  = XK_BackSpace;
2540     const unsigned int keyINSERT     = XK_Insert;
2541     const unsigned int keyHOME       = XK_Home;
2542     const unsigned int keyPAGEUP     = XK_Page_Up;
2543     const unsigned int keyTAB        = XK_Tab;
2544     const unsigned int keyQ          = XK_q;
2545     const unsigned int keyW          = XK_w;
2546     const unsigned int keyE          = XK_e;
2547     const unsigned int keyR          = XK_r;
2548     const unsigned int keyT          = XK_t;
2549     const unsigned int keyY          = XK_y;
2550     const unsigned int keyU          = XK_u;
2551     const unsigned int keyI          = XK_i;
2552     const unsigned int keyO          = XK_o;
2553     const unsigned int keyP          = XK_p;
2554     const unsigned int keyDELETE     = XK_Delete;
2555     const unsigned int keyEND        = XK_End;
2556     const unsigned int keyPAGEDOWN   = XK_Page_Down;
2557     const unsigned int keyCAPSLOCK   = XK_Caps_Lock;
2558     const unsigned int keyA          = XK_a;
2559     const unsigned int keyS          = XK_s;
2560     const unsigned int keyD          = XK_d;
2561     const unsigned int keyF          = XK_f;
2562     const unsigned int keyG          = XK_g;
2563     const unsigned int keyH          = XK_h;
2564     const unsigned int keyJ          = XK_j;
2565     const unsigned int keyK          = XK_k;
2566     const unsigned int keyL          = XK_l;
2567     const unsigned int keyENTER      = XK_Return;
2568     const unsigned int keySHIFTLEFT  = XK_Shift_L;
2569     const unsigned int keyZ          = XK_z;
2570     const unsigned int keyX          = XK_x;
2571     const unsigned int keyC          = XK_c;
2572     const unsigned int keyV          = XK_v;
2573     const unsigned int keyB          = XK_b;
2574     const unsigned int keyN          = XK_n;
2575     const unsigned int keyM          = XK_m;
2576     const unsigned int keySHIFTRIGHT = XK_Shift_R;
2577     const unsigned int keyARROWUP    = XK_Up;
2578     const unsigned int keyCTRLLEFT   = XK_Control_L;
2579     const unsigned int keyAPPLEFT    = XK_Super_L;
2580     const unsigned int keyALT        = XK_Alt_L;
2581     const unsigned int keySPACE      = XK_space;
2582     const unsigned int keyALTGR      = XK_Alt_R;
2583     const unsigned int keyAPPRIGHT   = XK_Super_R;
2584     const unsigned int keyMENU       = XK_Menu;
2585     const unsigned int keyCTRLRIGHT  = XK_Control_R;
2586     const unsigned int keyARROWLEFT  = XK_Left;
2587     const unsigned int keyARROWDOWN  = XK_Down;
2588     const unsigned int keyARROWRIGHT = XK_Right;
2589     const unsigned int keyPAD0       = XK_KP_0;
2590     const unsigned int keyPAD1       = XK_KP_1;
2591     const unsigned int keyPAD2       = XK_KP_2;
2592     const unsigned int keyPAD3       = XK_KP_3;
2593     const unsigned int keyPAD4       = XK_KP_4;
2594     const unsigned int keyPAD5       = XK_KP_5;
2595     const unsigned int keyPAD6       = XK_KP_6;
2596     const unsigned int keyPAD7       = XK_KP_7;
2597     const unsigned int keyPAD8       = XK_KP_8;
2598     const unsigned int keyPAD9       = XK_KP_9;
2599     const unsigned int keyPADADD     = XK_KP_Add;
2600     const unsigned int keyPADSUB     = XK_KP_Subtract;
2601     const unsigned int keyPADMUL     = XK_KP_Multiply;
2602     const unsigned int keyPADDIV     = XK_KP_Divide;
2603 
2604 #elif cimg_display==2
2605     // Define keycodes for Windows.
2606     const unsigned int keyESC        = VK_ESCAPE;
2607     const unsigned int keyF1         = VK_F1;
2608     const unsigned int keyF2         = VK_F2;
2609     const unsigned int keyF3         = VK_F3;
2610     const unsigned int keyF4         = VK_F4;
2611     const unsigned int keyF5         = VK_F5;
2612     const unsigned int keyF6         = VK_F6;
2613     const unsigned int keyF7         = VK_F7;
2614     const unsigned int keyF8         = VK_F8;
2615     const unsigned int keyF9         = VK_F9;
2616     const unsigned int keyF10        = VK_F10;
2617     const unsigned int keyF11        = VK_F11;
2618     const unsigned int keyF12        = VK_F12;
2619     const unsigned int keyPAUSE      = VK_PAUSE;
2620     const unsigned int key1          = '1';
2621     const unsigned int key2          = '2';
2622     const unsigned int key3          = '3';
2623     const unsigned int key4          = '4';
2624     const unsigned int key5          = '5';
2625     const unsigned int key6          = '6';
2626     const unsigned int key7          = '7';
2627     const unsigned int key8          = '8';
2628     const unsigned int key9          = '9';
2629     const unsigned int key0          = '0';
2630     const unsigned int keyBACKSPACE  = VK_BACK;
2631     const unsigned int keyINSERT     = VK_INSERT;
2632     const unsigned int keyHOME       = VK_HOME;
2633     const unsigned int keyPAGEUP     = VK_PRIOR;
2634     const unsigned int keyTAB        = VK_TAB;
2635     const unsigned int keyQ          = 'Q';
2636     const unsigned int keyW          = 'W';
2637     const unsigned int keyE          = 'E';
2638     const unsigned int keyR          = 'R';
2639     const unsigned int keyT          = 'T';
2640     const unsigned int keyY          = 'Y';
2641     const unsigned int keyU          = 'U';
2642     const unsigned int keyI          = 'I';
2643     const unsigned int keyO          = 'O';
2644     const unsigned int keyP          = 'P';
2645     const unsigned int keyDELETE     = VK_DELETE;
2646     const unsigned int keyEND        = VK_END;
2647     const unsigned int keyPAGEDOWN   = VK_NEXT;
2648     const unsigned int keyCAPSLOCK   = VK_CAPITAL;
2649     const unsigned int keyA          = 'A';
2650     const unsigned int keyS          = 'S';
2651     const unsigned int keyD          = 'D';
2652     const unsigned int keyF          = 'F';
2653     const unsigned int keyG          = 'G';
2654     const unsigned int keyH          = 'H';
2655     const unsigned int keyJ          = 'J';
2656     const unsigned int keyK          = 'K';
2657     const unsigned int keyL          = 'L';
2658     const unsigned int keyENTER      = VK_RETURN;
2659     const unsigned int keySHIFTLEFT  = VK_SHIFT;
2660     const unsigned int keyZ          = 'Z';
2661     const unsigned int keyX          = 'X';
2662     const unsigned int keyC          = 'C';
2663     const unsigned int keyV          = 'V';
2664     const unsigned int keyB          = 'B';
2665     const unsigned int keyN          = 'N';
2666     const unsigned int keyM          = 'M';
2667     const unsigned int keySHIFTRIGHT = VK_SHIFT;
2668     const unsigned int keyARROWUP    = VK_UP;
2669     const unsigned int keyCTRLLEFT   = VK_CONTROL;
2670     const unsigned int keyAPPLEFT    = VK_LWIN;
2671     const unsigned int keyALT        = VK_LMENU;
2672     const unsigned int keySPACE      = VK_SPACE;
2673     const unsigned int keyALTGR      = VK_CONTROL;
2674     const unsigned int keyAPPRIGHT   = VK_RWIN;
2675     const unsigned int keyMENU       = VK_APPS;
2676     const unsigned int keyCTRLRIGHT  = VK_CONTROL;
2677     const unsigned int keyARROWLEFT  = VK_LEFT;
2678     const unsigned int keyARROWDOWN  = VK_DOWN;
2679     const unsigned int keyARROWRIGHT = VK_RIGHT;
2680     const unsigned int keyPAD0       = 0x60;
2681     const unsigned int keyPAD1       = 0x61;
2682     const unsigned int keyPAD2       = 0x62;
2683     const unsigned int keyPAD3       = 0x63;
2684     const unsigned int keyPAD4       = 0x64;
2685     const unsigned int keyPAD5       = 0x65;
2686     const unsigned int keyPAD6       = 0x66;
2687     const unsigned int keyPAD7       = 0x67;
2688     const unsigned int keyPAD8       = 0x68;
2689     const unsigned int keyPAD9       = 0x69;
2690     const unsigned int keyPADADD     = VK_ADD;
2691     const unsigned int keyPADSUB     = VK_SUBTRACT;
2692     const unsigned int keyPADMUL     = VK_MULTIPLY;
2693     const unsigned int keyPADDIV     = VK_DIVIDE;
2694 
2695 #else
2696     // Define random keycodes when no display is available.
2697     // (should rarely be used then !).
2698     const unsigned int keyESC        = 1U;   //!< Keycode for the \c ESC key (architecture-dependent).
2699     const unsigned int keyF1         = 2U;   //!< Keycode for the \c F1 key (architecture-dependent).
2700     const unsigned int keyF2         = 3U;   //!< Keycode for the \c F2 key (architecture-dependent).
2701     const unsigned int keyF3         = 4U;   //!< Keycode for the \c F3 key (architecture-dependent).
2702     const unsigned int keyF4         = 5U;   //!< Keycode for the \c F4 key (architecture-dependent).
2703     const unsigned int keyF5         = 6U;   //!< Keycode for the \c F5 key (architecture-dependent).
2704     const unsigned int keyF6         = 7U;   //!< Keycode for the \c F6 key (architecture-dependent).
2705     const unsigned int keyF7         = 8U;   //!< Keycode for the \c F7 key (architecture-dependent).
2706     const unsigned int keyF8         = 9U;   //!< Keycode for the \c F8 key (architecture-dependent).
2707     const unsigned int keyF9         = 10U;  //!< Keycode for the \c F9 key (architecture-dependent).
2708     const unsigned int keyF10        = 11U;  //!< Keycode for the \c F10 key (architecture-dependent).
2709     const unsigned int keyF11        = 12U;  //!< Keycode for the \c F11 key (architecture-dependent).
2710     const unsigned int keyF12        = 13U;  //!< Keycode for the \c F12 key (architecture-dependent).
2711     const unsigned int keyPAUSE      = 14U;  //!< Keycode for the \c PAUSE key (architecture-dependent).
2712     const unsigned int key1          = 15U;  //!< Keycode for the \c 1 key (architecture-dependent).
2713     const unsigned int key2          = 16U;  //!< Keycode for the \c 2 key (architecture-dependent).
2714     const unsigned int key3          = 17U;  //!< Keycode for the \c 3 key (architecture-dependent).
2715     const unsigned int key4          = 18U;  //!< Keycode for the \c 4 key (architecture-dependent).
2716     const unsigned int key5          = 19U;  //!< Keycode for the \c 5 key (architecture-dependent).
2717     const unsigned int key6          = 20U;  //!< Keycode for the \c 6 key (architecture-dependent).
2718     const unsigned int key7          = 21U;  //!< Keycode for the \c 7 key (architecture-dependent).
2719     const unsigned int key8          = 22U;  //!< Keycode for the \c 8 key (architecture-dependent).
2720     const unsigned int key9          = 23U;  //!< Keycode for the \c 9 key (architecture-dependent).
2721     const unsigned int key0          = 24U;  //!< Keycode for the \c 0 key (architecture-dependent).
2722     const unsigned int keyBACKSPACE  = 25U;  //!< Keycode for the \c BACKSPACE key (architecture-dependent).
2723     const unsigned int keyINSERT     = 26U;  //!< Keycode for the \c INSERT key (architecture-dependent).
2724     const unsigned int keyHOME       = 27U;  //!< Keycode for the \c HOME key (architecture-dependent).
2725     const unsigned int keyPAGEUP     = 28U;  //!< Keycode for the \c PAGEUP key (architecture-dependent).
2726     const unsigned int keyTAB        = 29U;  //!< Keycode for the \c TAB key (architecture-dependent).
2727     const unsigned int keyQ          = 30U;  //!< Keycode for the \c Q key (architecture-dependent).
2728     const unsigned int keyW          = 31U;  //!< Keycode for the \c W key (architecture-dependent).
2729     const unsigned int keyE          = 32U;  //!< Keycode for the \c E key (architecture-dependent).
2730     const unsigned int keyR          = 33U;  //!< Keycode for the \c R key (architecture-dependent).
2731     const unsigned int keyT          = 34U;  //!< Keycode for the \c T key (architecture-dependent).
2732     const unsigned int keyY          = 35U;  //!< Keycode for the \c Y key (architecture-dependent).
2733     const unsigned int keyU          = 36U;  //!< Keycode for the \c U key (architecture-dependent).
2734     const unsigned int keyI          = 37U;  //!< Keycode for the \c I key (architecture-dependent).
2735     const unsigned int keyO          = 38U;  //!< Keycode for the \c O key (architecture-dependent).
2736     const unsigned int keyP          = 39U;  //!< Keycode for the \c P key (architecture-dependent).
2737     const unsigned int keyDELETE     = 40U;  //!< Keycode for the \c DELETE key (architecture-dependent).
2738     const unsigned int keyEND        = 41U;  //!< Keycode for the \c END key (architecture-dependent).
2739     const unsigned int keyPAGEDOWN   = 42U;  //!< Keycode for the \c PAGEDOWN key (architecture-dependent).
2740     const unsigned int keyCAPSLOCK   = 43U;  //!< Keycode for the \c CAPSLOCK key (architecture-dependent).
2741     const unsigned int keyA          = 44U;  //!< Keycode for the \c A key (architecture-dependent).
2742     const unsigned int keyS          = 45U;  //!< Keycode for the \c S key (architecture-dependent).
2743     const unsigned int keyD          = 46U;  //!< Keycode for the \c D key (architecture-dependent).
2744     const unsigned int keyF          = 47U;  //!< Keycode for the \c F key (architecture-dependent).
2745     const unsigned int keyG          = 48U;  //!< Keycode for the \c G key (architecture-dependent).
2746     const unsigned int keyH          = 49U;  //!< Keycode for the \c H key (architecture-dependent).
2747     const unsigned int keyJ          = 50U;  //!< Keycode for the \c J key (architecture-dependent).
2748     const unsigned int keyK          = 51U;  //!< Keycode for the \c K key (architecture-dependent).
2749     const unsigned int keyL          = 52U;  //!< Keycode for the \c L key (architecture-dependent).
2750     const unsigned int keyENTER      = 53U;  //!< Keycode for the \c ENTER key (architecture-dependent).
2751     const unsigned int keySHIFTLEFT  = 54U;  //!< Keycode for the \c SHIFTLEFT key (architecture-dependent).
2752     const unsigned int keyZ          = 55U;  //!< Keycode for the \c Z key (architecture-dependent).
2753     const unsigned int keyX          = 56U;  //!< Keycode for the \c X key (architecture-dependent).
2754     const unsigned int keyC          = 57U;  //!< Keycode for the \c C key (architecture-dependent).
2755     const unsigned int keyV          = 58U;  //!< Keycode for the \c V key (architecture-dependent).
2756     const unsigned int keyB          = 59U;  //!< Keycode for the \c B key (architecture-dependent).
2757     const unsigned int keyN          = 60U;  //!< Keycode for the \c N key (architecture-dependent).
2758     const unsigned int keyM          = 61U;  //!< Keycode for the \c M key (architecture-dependent).
2759     const unsigned int keySHIFTRIGHT = 62U;  //!< Keycode for the \c SHIFTRIGHT key (architecture-dependent).
2760     const unsigned int keyARROWUP    = 63U;  //!< Keycode for the \c ARROWUP key (architecture-dependent).
2761     const unsigned int keyCTRLLEFT   = 64U;  //!< Keycode for the \c CTRLLEFT key (architecture-dependent).
2762     const unsigned int keyAPPLEFT    = 65U;  //!< Keycode for the \c APPLEFT key (architecture-dependent).
2763     const unsigned int keyALT        = 66U;  //!< Keycode for the \c ALT key (architecture-dependent).
2764     const unsigned int keySPACE      = 67U;  //!< Keycode for the \c SPACE key (architecture-dependent).
2765     const unsigned int keyALTGR      = 68U;  //!< Keycode for the \c ALTGR key (architecture-dependent).
2766     const unsigned int keyAPPRIGHT   = 69U;  //!< Keycode for the \c APPRIGHT key (architecture-dependent).
2767     const unsigned int keyMENU       = 70U;  //!< Keycode for the \c MENU key (architecture-dependent).
2768     const unsigned int keyCTRLRIGHT  = 71U;  //!< Keycode for the \c CTRLRIGHT key (architecture-dependent).
2769     const unsigned int keyARROWLEFT  = 72U;  //!< Keycode for the \c ARROWLEFT key (architecture-dependent).
2770     const unsigned int keyARROWDOWN  = 73U;  //!< Keycode for the \c ARROWDOWN key (architecture-dependent).
2771     const unsigned int keyARROWRIGHT = 74U;  //!< Keycode for the \c ARROWRIGHT key (architecture-dependent).
2772     const unsigned int keyPAD0       = 75U;  //!< Keycode for the \c PAD0 key (architecture-dependent).
2773     const unsigned int keyPAD1       = 76U;  //!< Keycode for the \c PAD1 key (architecture-dependent).
2774     const unsigned int keyPAD2       = 77U;  //!< Keycode for the \c PAD2 key (architecture-dependent).
2775     const unsigned int keyPAD3       = 78U;  //!< Keycode for the \c PAD3 key (architecture-dependent).
2776     const unsigned int keyPAD4       = 79U;  //!< Keycode for the \c PAD4 key (architecture-dependent).
2777     const unsigned int keyPAD5       = 80U;  //!< Keycode for the \c PAD5 key (architecture-dependent).
2778     const unsigned int keyPAD6       = 81U;  //!< Keycode for the \c PAD6 key (architecture-dependent).
2779     const unsigned int keyPAD7       = 82U;  //!< Keycode for the \c PAD7 key (architecture-dependent).
2780     const unsigned int keyPAD8       = 83U;  //!< Keycode for the \c PAD8 key (architecture-dependent).
2781     const unsigned int keyPAD9       = 84U;  //!< Keycode for the \c PAD9 key (architecture-dependent).
2782     const unsigned int keyPADADD     = 85U;  //!< Keycode for the \c PADADD key (architecture-dependent).
2783     const unsigned int keyPADSUB     = 86U;  //!< Keycode for the \c PADSUB key (architecture-dependent).
2784     const unsigned int keyPADMUL     = 87U;  //!< Keycode for the \c PADMUL key (architecture-dependent).
2785     const unsigned int keyPADDIV     = 88U;  //!< Keycode for the \c PADDDIV key (architecture-dependent).
2786 #endif
2787 
2788     const double PI = 3.14159265358979323846;   //!< Value of the mathematical constant PI
2789 
2790     // Define a 10x13 font (small size).
2791     const unsigned int font10x13[256*10*13/32] = {
2792       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2793       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80100c0,
2794       0x68000300,0x801,0xc00010,0x100c000,0x68100,0x100c0680,0x2,0x403000,0x1000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2795       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2796       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x0,0x0,0x0,0x0,0x0,0x4020120,
2797       0x58120480,0x402,0x1205008,0x2012050,0x58080,0x20120581,0x40000001,0x804812,0x2000000,0x0,0x300,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2798       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x140,0x80000,0x200402,0x800000,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2799       0x0,0x7010,0x7000000,0x8000200,0x20000,0xc0002000,0x8008,0x0,0x0,0x0,0x0,0x808,0x4000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2800       0x0,0x0,0x80000000,0x0,0x0,0x0,0x40000,0x0,0x0,0x0,0x0,0x480,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x80100c0,0x68000480,0x1001,
2801       0xc00010,0x1018000,0x68100,0x100c0680,0x4,0x403000,0x1020000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20140,0x28081883,0x200801,
2802       0x2a00000,0x10,0x1c0201c0,0x70040f80,0xc0f81c07,0x0,0x70,0x3e0303c0,0x3c3c0f83,0xe03c2107,0xe08810,0x18c31070,0x3c0703c0,
2803       0x783e0842,0x22222208,0x83e04010,0x1008000,0x4000200,0x20001,0x2002,0x408008,0x0,0x0,0x100000,0x0,0x1008,0x2000000,0x0,0x0,0x0,
2804       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20080,0x38000880,0x8078140f,0x81c00000,0x3e000,0xc020180,0x60080001,0xe0000002,0xc00042,0x108e2010,
2805       0xc0300c0,0x300c0303,0xf83c3e0f,0x83e0f81c,0x701c070,0x3c0c41c0,0x701c0701,0xc0001d08,0x42108421,0x8820088,0x4020120,0x58140480,
2806       0x802,0x1205008,0x3014050,0xc058080,0x20120581,0x40000002,0x804814,0x2020050,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20140,
2807       0x281e2484,0x80200801,0x1c02000,0x10,0x22060220,0x880c0801,0x82208,0x80000001,0x20008,0x41030220,0x40220802,0x402102,0x209010,
2808       0x18c31088,0x22088220,0x80080842,0x22222208,0x80204010,0x1014000,0x200,0x20001,0x2000,0x8008,0x0,0x0,0x100000,0x0,0x1008,
2809       0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x40000500,0x80800010,0x40200000,0x41000,0x12020040,0x10000003,0xa0000006,
2810       0x12000c4,0x31014000,0xc0300c0,0x300c0302,0x80402008,0x2008008,0x2008020,0x220c4220,0x88220882,0x20002208,0x42108421,0x8820088,
2811       0x0,0x300,0x0,0x0,0x0,0x14000000,0x0,0x200200,0x0,0x20000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0xfc282504,0x80001000,
2812       0x82a02000,0x20,0x22020020,0x8140802,0x102208,0x80801006,0x18008,0x9c848220,0x80210802,0x802102,0x20a010,0x15429104,0x22104220,
2813       0x80080842,0x22221405,0x404008,0x1022000,0x703c0,0x381e0701,0xc0783c02,0xc09008,0x1d83c070,0x3c078140,0x381c0882,0x21242208,
2814       0x81e01008,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x201e0,0x40220500,0x80800027,0x20e02800,0x9c800,0x12020040,
2815       0x20000883,0xa0200002,0x120a044,0x11064010,0x12048120,0x48120484,0x80802008,0x2008008,0x2008020,0x210a4411,0x4411044,0x10884508,
2816       0x42108421,0x503c0b0,0x1c0701c0,0x701c0707,0x70381c07,0x1c07008,0x2008020,0x20f01c0,0x701c0701,0xc0201c08,0x82208822,0x883c088,
2817       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0x50281903,0x20001000,0x80802000,0x20,0x22020040,0x30240f03,0xc0101c08,0x80801018,
2818       0x1fc06010,0xa48483c0,0x80210f03,0xe0803f02,0x20c010,0x15429104,0x22104220,0x70080841,0x41540805,0x804008,0x1041000,0x8220,
2819       0x40220881,0x882202,0x40a008,0x12422088,0x22088180,0x40100882,0x21241408,0x80201008,0x2031000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2820       0x0,0x20280,0x401c0200,0x700028,0x21205000,0x92800,0xc1fc080,0x10000883,0xa0200002,0x1205049,0x12c19010,0x12048120,0x48120484,
2821       0xf0803c0f,0x3c0f008,0x2008020,0x790a4411,0x4411044,0x10504908,0x42108421,0x5022088,0x2008020,0x8020080,0x88402208,0x82208808,
2822       0x2008020,0x1e088220,0x88220882,0x20002608,0x82208822,0x8822088,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0x501c0264,
2823       0xa0001000,0x8001fc00,0x7000020,0x22020080,0x83e0082,0x20202207,0x80000020,0x1020,0xa4848220,0x80210802,0x9c2102,0x20c010,
2824       0x12425104,0x3c1043c0,0x8080841,0x41540802,0x804008,0x1000000,0x78220,0x40220f81,0x882202,0x40c008,0x12422088,0x22088100,
2825       0x60100881,0x41540805,0x406008,0x1849000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20280,0xf0140200,0x880028,0x20e0a03f,0x709c800,
2826       0x201c0,0x60000881,0xa0000007,0xc0284b,0x122eb020,0x12048120,0x48120487,0x80802008,0x2008008,0x2008020,0x21094411,0x4411044,
2827       0x10204908,0x42108421,0x2022088,0x1e0781e0,0x781e0787,0xf8403e0f,0x83e0f808,0x2008020,0x22088220,0x88220882,0x21fc2a08,0x82208822,
2828       0x5022050,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20001,0xf80a0294,0x40001000,0x80002000,0x20,0x22020100,0x8040082,0x20202200,
2829       0x80000018,0x1fc06020,0xa48fc220,0x80210802,0x842102,0x20a010,0x12425104,0x20104240,0x8080841,0x41541402,0x1004008,0x1000000,
2830       0x88220,0x40220801,0x882202,0x40a008,0x12422088,0x22088100,0x18100881,0x41540805,0x801008,0x2046000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2831       0x0,0x0,0x0,0x20280,0x401c0f80,0x80880028,0x20005001,0x94800,0x20000,0x880,0xa0000000,0x5015,0x4215040,0x3f0fc3f0,0xfc3f0fc8,
2832       0x80802008,0x2008008,0x2008020,0x21094411,0x4411044,0x10505108,0x42108421,0x203c088,0x22088220,0x88220888,0x80402008,0x2008008,
2833       0x2008020,0x22088220,0x88220882,0x20002a08,0x82208822,0x5022050,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa00a0494,0x60001000,
2834       0x80002004,0x8020,0x22020200,0x88040882,0x20402201,0x801006,0x18000,0x9f084220,0x40220802,0x442102,0x209010,0x10423088,0x20088220,
2835       0x8080840,0x80882202,0x2004008,0x1000000,0x88220,0x40220881,0x882202,0x409008,0x12422088,0x22088100,0x8100880,0x80881402,
2836       0x1001008,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20280,0x40220200,0x80700027,0x20002801,0x92800,0x1fc000,0x980,
2837       0xa0000000,0xa017,0x84417840,0x21084210,0x84210848,0x80402008,0x2008008,0x2008020,0x2208c220,0x88220882,0x20882208,0x42108421,
2838       0x2020088,0x22088220,0x88220888,0xc8402208,0x82208808,0x2008020,0x22088220,0x88220882,0x20203208,0x82208822,0x2022020,0x0,0x0,0x0,
2839       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0xa03c0463,0x90000801,0x2004,0x8040,0x1c0703e0,0x70040701,0xc0401c06,0x801001,0x20020,
2840       0x400843c0,0x3c3c0f82,0x3c2107,0x1c0881e,0x10423070,0x20070210,0xf0080780,0x80882202,0x3e04004,0x1000000,0x783c0,0x381e0701,
2841       0x782202,0x408808,0x12422070,0x3c078100,0x700c0780,0x80882202,0x1e01008,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x201e0,
2842       0xf8000200,0x80080010,0x40000001,0x41000,0x0,0xe80,0xa0000000,0x21,0x8e21038,0x21084210,0x84210848,0xf83c3e0f,0x83e0f81c,
2843       0x701c070,0x3c08c1c0,0x701c0701,0xc0005c07,0x81e0781e,0x20200b0,0x1e0781e0,0x781e0787,0x30381c07,0x1c07008,0x2008020,0x1c0881c0,
2844       0x701c0701,0xc0201c07,0x81e0781e,0x203c020,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000,0x801,0x4,0x40,0x0,0x0,0x0,0x1000,
2845       0x0,0x3c000000,0x0,0x0,0x0,0x0,0x10000,0x0,0x0,0x4004,0x1000000,0x0,0x0,0x80000,0x400000,0x0,0x20008000,0x0,0x4,0x1008,0x2000000,
2846       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x0,0x8008000f,0x80000000,0x3e000,0x0,0x800,0xa0000400,0x0,0x0,0x0,0x0,0x80000,0x0,
2847       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100000,0x0,0x0,0x0,0x0,0x2000,0x0,0x4020040,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000,
2848       0x402,0x8,0x40,0x0,0x0,0x0,0x2000,0x0,0x0,0x0,0x0,0x0,0x0,0xc000,0x0,0x0,0x7004,0x70000fc,0x0,0x0,0x700000,0x800000,0x0,0x20008000,
2849       0x0,0x4,0x808,0x4000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x0,0x80f00000,0x0,0x0,0x0,0x800,0xa0001800,0x0,0x0,0x0,0x0,
2850       0x300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600000,0x0,0x0,0x0,0x0,0x0,0x0,0x4020040
2851     };
2852 
2853     // Define a 12x24 font (normal size).
2854     const unsigned int font12x24[12*24*256/32] = {
2855       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2856       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x80000000,0x198000,0x0,0x0,0x0,0x0,
2857       0x0,0x198,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc001806,0xc81980,0x60000000,0xc001806,0x1980c00,0x18060198,0xc80c,
2858       0x180600,0xc8198000,0xc001,0x80601980,0x18000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2859       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2860       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x0,0xf0000,0x0,0x0,0x0,0x0,0x0,0x198,0x0,0x0,0x0,0x0,0x0,0x0,
2861       0x0,0x0,0x0,0x0,0x0,0x0,0x600300f,0x1301980,0x90000000,0x600300f,0x1980600,0x300f0198,0x13006,0x300f01,0x30198000,0x6003,
2862       0xf01980,0x30000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2863       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2864       0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x0,0x60000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7007,0x3c0000,0x3006019,
2865       0x80000000,0x90000000,0x3006019,0x80000300,0x60198000,0x3,0x601980,0x0,0x3006,0x1980000,0x60000000,0x0,0x0,0xe0000000,0x0,
2866       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2867       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000000,
2868       0x0,0x0,0x0,0x0,0x0,0xc800019,0x80000000,0x198000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x0,0x0,0x1001,0x420000,0x0,0x0,0x90000000,
2869       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18000c06,0xc80001,0x10000000,0x18000c06,0x1800,0xc060000,0xc818,0xc0600,0xc8000000,
2870       0x18000,0xc0600000,0xc000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x80660207,0x800f8060,0x300c004,0x0,0x6,
2871       0xe00703f,0x3f00383,0xf80f07fc,0x1f01f000,0x0,0xf8,0x607f,0x7c7e07,0xfe7fe0f8,0x6063fc1f,0x86066007,0xe7060f0,0x7f80f07f,
2872       0x81f8fff6,0x6606c03,0x70ee077f,0xe0786000,0xf0070000,0xc000060,0xc0,0x3e000,0x60006003,0x600fc00,0x0,0x0,0x0,0x0,0x0,0x3c0603,
2873       0xc0000000,0x7800000,0xf0000,0x0,0xf00001f,0x80001fe0,0x7fe000,0x0,0x0,0x0,0x168fe609,0x0,0x90e07,0x6000,0x3c000e,0x70000f8,
2874       0x1980001f,0x0,0x1f8,0xf00000f,0xf00180,0xfe000,0xe00e,0x1001,0x20060,0x6006006,0x600600,0x600fe07c,0x7fe7fe7f,0xe7fe3fc3,
2875       0xfc3fc3fc,0x7e07060f,0xf00f00,0xf00f0000,0xf360660,0x6606606e,0x76001e0,0xc00180f,0x1681981,0x10000000,0xc00180f,0x1980c00,
2876       0x180f0198,0x3801680c,0x180f01,0x68198000,0xc001,0x80f01980,0x18600198,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,
2877       0x8044020c,0xc01f8060,0x2004004,0x0,0xc,0x3f81f07f,0x87f80383,0xf81f87fc,0x3f83f800,0x0,0x1fc,0x780607f,0x81fe7f87,0xfe7fe1fc,
2878       0x6063fc1f,0x860c6007,0xe7061f8,0x7fc1f87f,0xc3fcfff6,0x6606c03,0x30c6067f,0xe0783000,0xf00d8000,0x6000060,0xc0,0x7e000,0x60006003,
2879       0x600fc00,0x0,0x0,0xc00,0x0,0x0,0x7c0603,0xe0000000,0xfc00000,0x1f0000,0x0,0x900003f,0xc0003fe0,0x7fe000,0x0,0x0,0x0,0x1302660f,
2880       0x0,0xf0606,0x6004,0x7e0006,0x60601f8,0x19800001,0x80000000,0x1f8,0x19800010,0x81080300,0x3f2000,0x2011,0x1001,0x1c0060,0x6006006,
2881       0x600600,0x601fe1fe,0x7fe7fe7f,0xe7fe3fc3,0xfc3fc3fc,0x7f87061f,0x81f81f81,0xf81f8000,0x3fa60660,0x66066066,0x66003f0,0x6003009,
2882       0x1301981,0x10000000,0x6003009,0x1980600,0x30090198,0x1f013006,0x300901,0x30198000,0x6003,0x901980,0x30600198,0x0,0x0,0x0,
2883       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x80cc0f8c,0xc0180060,0x6006044,0x40000000,0xc,0x3181b041,0xc41c0783,0x388018,
2884       0x71c71800,0x0,0x106,0x18c0f061,0xc38261c6,0x600384,0x60606001,0x86186007,0xe78630c,0x60e30c60,0xe7040606,0x630cc03,0x39c30c00,
2885       0xc0603000,0x3018c000,0x3000060,0xc0,0x60000,0x60000000,0x6000c00,0x0,0x0,0xc00,0x0,0x0,0x600600,0x60000000,0x18400000,0x180000,
2886       0x0,0x19800070,0x40003600,0xc000,0x0,0x0,0x0,0x25a06,0x0,0x6030c,0x4,0xe20007,0xe060180,0xf000,0x80000000,0xf0000,0x10800000,
2887       0x80080600,0x7f2000,0x2020,0x80001001,0x20000,0xf00f00f,0xf00f00,0x601b0382,0x60060060,0x6000600,0x60060060,0x61c78630,0xc30c30c3,
2888       0xc30c000,0x30e60660,0x66066063,0xc600738,0x3006019,0x80000000,0xe0000000,0x3006019,0x80000300,0x60198000,0x3e000003,0x601980,
2889       0x0,0x3006,0x1980000,0x60600000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x80cc1fcc,0xc0180060,0x6006035,0x80000000,
2890       0x18,0x71c03000,0xc00c0583,0x300018,0x60c60c00,0x0,0x6,0x3060f060,0xc30060c6,0x600300,0x60606001,0x86306007,0x9e78670e,0x60670e60,
2891       0x66000606,0x630c606,0x19830c01,0xc0601800,0x30306000,0x60,0xc0,0x60000,0x60000000,0x6000c00,0x0,0x0,0xc00,0x0,0x0,0x600600,
2892       0x60000000,0x18000000,0x300000,0x0,0x78060,0x6600,0x1c000,0x300c,0x39819c0,0x0,0x25a00,0x0,0x30c,0x4,0xc00003,0xc060180,0x30c1f,
2893       0x80000000,0x30c000,0x10800001,0x80700000,0x7f2000,0x2020,0x80001001,0x20060,0xf00f00f,0xf00f00,0xf01b0300,0x60060060,0x6000600,
2894       0x60060060,0x60c78670,0xe70e70e7,0xe70e000,0x70c60660,0x66066063,0xc7f8618,0x0,0x0,0x0,0x0,0x0,0x0,0x7000000,0x0,0x0,0x0,
2895       0x0,0x600000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x87ff3a4c,0xc0180060,0x400600e,0x600000,0x18,0x60c03000,
2896       0xc00c0d83,0x700018,0x60c60c00,0x20,0x400006,0x3060f060,0xc6006066,0x600600,0x60606001,0x86606006,0x966c6606,0x60660660,0x66000606,
2897       0x630c666,0xf019801,0x80601800,0x30603000,0x1f06f,0xf01ec0,0xf03fe1ec,0x6703e01f,0x61c0c06,0xdc6701f0,0x6f01ec0c,0xe1f87fc6,
2898       0xc60cc03,0x71c60c7f,0xc0600600,0x60000000,0x30000000,0x300000,0x40040,0x88060,0x6600,0x18000,0x300c,0x1981980,0x0,0x2421f,
2899       0x80003ce0,0x7fc198,0x601f,0xc02021,0x980600c0,0x40230,0x80000000,0x402000,0x19806003,0x80006,0xc7f2000,0x2020,0x80001001,
2900       0x420060,0xf00f00f,0xf00f00,0xf01b0600,0x60060060,0x6000600,0x60060060,0x6066c660,0x66066066,0x6606208,0x60e60660,0x66066061,
2901       0x987fc670,0x1f01f01f,0x1f01f01,0xf039c0f0,0xf00f00f,0xf03e03,0xe03e03e0,0x1f06701f,0x1f01f01,0xf01f0060,0x1e660c60,0xc60c60c6,
2902       0xc6f060c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x7ff3207,0x8c0c0000,0xc00300e,0x600000,0x30,0x60c03000,
2903       0xc01c0983,0xf0600030,0x31860c06,0x6001e0,0x78000e,0x23e1f861,0xc6006066,0x600600,0x60606001,0x86c06006,0x966c6606,0x60660660,
2904       0xe7000606,0x630c666,0xf01f803,0x600c00,0x30000000,0x3f87f,0x83f83fc3,0xf83fe3fc,0x7f83e01f,0x6380c07,0xfe7f83f8,0x7f83fc0d,
2905       0xf3fc7fc6,0xc71cc03,0x3183187f,0xc0600600,0x60000000,0xff806000,0x300000,0x40040,0x88070,0x6600,0x60030060,0x6001818,0x1883180,
2906       0x0,0x2423f,0xc0007ff0,0x607fc1f8,0x603f,0x80c01fc1,0xf80601e0,0x5f220,0x80420000,0x5f2000,0xf006006,0x80006,0xc7f2000,0x2020,
2907       0x82107c07,0xc03c0060,0x1f81f81f,0x81f81f80,0xf03b0600,0x60060060,0x6000600,0x60060060,0x6066c660,0x66066066,0x660671c,0x61660660,
2908       0x66066061,0xf860e6c0,0x3f83f83f,0x83f83f83,0xf87fe3f8,0x3f83f83f,0x83f83e03,0xe03e03e0,0x3f87f83f,0x83f83f83,0xf83f8060,
2909       0x3fc60c60,0xc60c60c3,0x187f8318,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x883200,0x300c0000,0xc003035,0x80600000,
2910       0x30,0x66c03001,0xc0f81983,0xf86f0030,0x1f071c06,0x600787,0xfe1e001c,0x6261987f,0x86006067,0xfe7fc600,0x7fe06001,0x87c06006,
2911       0xf6646606,0x60e6067f,0xc3e00606,0x61986f6,0x600f007,0x600c00,0x30000000,0x21c71,0x830831c3,0x1c06031c,0x71c06003,0x6700c06,
2912       0x6671c318,0x71831c0f,0x16040c06,0xc318606,0x1b031803,0x80600600,0x60000000,0x30009000,0x300000,0x40040,0x7003e,0x67e0,0x90070090,
2913       0x9001818,0x8c3100,0x0,0x60,0x4000e730,0x900380f0,0x6034,0x80c018c7,0xfe060338,0xb0121,0x80c60000,0x909000,0x6008,0x1080006,
2914       0xc3f2000,0x2011,0x3180060,0x60060e0,0x19819819,0x81981981,0x9833c600,0x7fe7fe7f,0xe7fe0600,0x60060060,0x60664660,0x66066066,
2915       0x66063b8,0x62660660,0x66066060,0xf06066c0,0x21c21c21,0xc21c21c2,0x1c466308,0x31c31c31,0xc31c0600,0x60060060,0x31871c31,0x83183183,
2916       0x18318000,0x71860c60,0xc60c60c3,0x18718318,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x1981a00,0xe03e0000,0xc003044,
2917       0x40600000,0x60,0x66c03001,0x80f03182,0x1c7f8030,0x3f83fc06,0x601e07,0xfe078038,0x6661987f,0x86006067,0xfe7fc61e,0x7fe06001,
2918       0x87e06006,0x66666606,0x7fc6067f,0x81f80606,0x61986f6,0x6006006,0x600600,0x30000000,0xc60,0xc60060c6,0xc06060c,0x60c06003,
2919       0x6e00c06,0x6660c60c,0x60c60c0e,0x6000c06,0xc318666,0x1f031803,0x600600,0x603c2000,0x30016800,0x1fe0000,0x1f81f8,0x1c1f,0x804067e1,
2920       0x68060168,0x16800810,0xc42300,0x0,0x60,0x20c331,0x68030060,0x6064,0x3fc1040,0xf006031c,0xa011e,0x818c7fe0,0x909000,0x7fe1f,
2921       0x80f00006,0xc0f2060,0xf80e,0x18c0780,0x780781c0,0x19819819,0x81981981,0x9833c600,0x7fe7fe7f,0xe7fe0600,0x60060060,0xfc666660,
2922       0x66066066,0x66061f0,0x66660660,0x66066060,0x606066e0,0xc00c00,0xc00c00c0,0xc066600,0x60c60c60,0xc60c0600,0x60060060,0x60c60c60,
2923       0xc60c60c6,0xc60c000,0x61c60c60,0xc60c60c3,0x1860c318,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x1980f81,0x80373000,
2924       0xc003004,0x7fe0001,0xf0000060,0x60c03003,0x183180,0xc71c060,0x3181ec00,0x7000,0xe070,0x66619860,0xc6006066,0x60061e,0x60606001,
2925       0x87606006,0x66626606,0x7f860661,0xc01c0606,0x6198696,0xf00600e,0x600600,0x30000000,0x1fc60,0xc60060c7,0xfc06060c,0x60c06003,
2926       0x7c00c06,0x6660c60c,0x60c60c0c,0x7f00c06,0xc3b8666,0xe01b007,0x3c00600,0x3c7fe000,0xff03ec00,0x1fe0000,0x40040,0xe001,0xc0806603,
2927       0xec0e03ec,0x3ec00010,0x0,0x60000000,0x7f,0x10c3f3,0xec070060,0x6064,0x3fc1040,0x6000030c,0xa0100,0x3187fe1,0xf09f1000,0x7fe00,
2928       0x6,0xc012060,0x0,0xc63c03,0xc03c0380,0x19819819,0x81981981,0x98330600,0x60060060,0x6000600,0x60060060,0xfc662660,0x66066066,
2929       0x66060e0,0x6c660660,0x66066060,0x6060e630,0x1fc1fc1f,0xc1fc1fc1,0xfc3fe600,0x7fc7fc7f,0xc7fc0600,0x60060060,0x60c60c60,0xc60c60c6,
2930       0xc60c7fe,0x62c60c60,0xc60c60c1,0xb060c1b0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0xffe02c6,0x3c633000,0xc003004,
2931       0x7fe0001,0xf00000c0,0x60c03006,0xc6180,0xc60c060,0x60c00c00,0x7000,0xe060,0x66639c60,0x66006066,0x600606,0x60606001,0x86306006,
2932       0x66636606,0x60060660,0xc0060606,0x61f8696,0xf00600c,0x600300,0x30000000,0x3fc60,0xc60060c7,0xfc06060c,0x60c06003,0x7c00c06,
2933       0x6660c60c,0x60c60c0c,0x1f80c06,0xc1b0666,0xe01b00e,0x3c00600,0x3c43c000,0x3007de00,0x600000,0x40040,0x30000,0x61006607,0xde0c07de,
2934       0x7de00000,0x0,0xf07fefff,0x1f,0x8008c3f7,0xde0e0060,0x6064,0xc01047,0xfe00018c,0xb013f,0x86300061,0xf0911000,0x6000,0x6,
2935       0xc012060,0x3f,0x8063c0cc,0x3cc0c700,0x39c39c39,0xc39c39c1,0x98630600,0x60060060,0x6000600,0x60060060,0x60663660,0x66066066,
2936       0x66061f0,0x78660660,0x66066060,0x607fc618,0x3fc3fc3f,0xc3fc3fc3,0xfc7fe600,0x7fc7fc7f,0xc7fc0600,0x60060060,0x60c60c60,0xc60c60c6,
2937       0xc60c7fe,0x64c60c60,0xc60c60c1,0xb060c1b0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0xffe0260,0x6661b000,0xc003000,
2938       0x600000,0xc0,0x60c0300c,0xc7fe0,0xc60c060,0x60c01c00,0x1e07,0xfe078060,0x6663fc60,0x66006066,0x600606,0x60606001,0x86386006,
2939       0x6636606,0x60060660,0xe0060606,0x60f039c,0x1b806018,0x600300,0x30000000,0x70c60,0xc60060c6,0x6060c,0x60c06003,0x7600c06,
2940       0x6660c60c,0x60c60c0c,0x1c0c06,0xc1b03fc,0xe01f01c,0xe00600,0x70000000,0x3007fc00,0x600000,0x40040,0x0,0x62006607,0xfc1807fc,
2941       0x7fc00000,0x0,0xf0000000,0x1,0xc004c307,0xfc1c0060,0x6064,0xc018c0,0x600000d8,0x5f200,0x3180060,0x50a000,0x6000,0x6,0xc012000,
2942       0x0,0xc601c0,0x4201c600,0x3fc3fc3f,0xc3fc3fc3,0xfc7f0600,0x60060060,0x6000600,0x60060060,0x60663660,0x66066066,0x66063b8,
2943       0x70660660,0x66066060,0x607f860c,0x70c70c70,0xc70c70c7,0xcc60600,0x60060060,0x6000600,0x60060060,0x60c60c60,0xc60c60c6,0xc60c000,
2944       0x68c60c60,0xc60c60c1,0xf060c1f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3300260,0x6661e000,0xc003000,0x600000,
2945       0x180,0x71c03018,0xc7fe0,0xc60c0c0,0x60c01800,0x787,0xfe1e0060,0x6663fc60,0x630060c6,0x600306,0x60606001,0x86186006,0x661e70e,
2946       0x60070c60,0x60060606,0x60f039c,0x19806038,0x600180,0x30000000,0x60c60,0xc60060c6,0x6060c,0x60c06003,0x6700c06,0x6660c60c,
2947       0x60c60c0c,0xc0c06,0xc1b039c,0x1f00e018,0x600600,0x60000000,0x1803f800,0x600000,0x40040,0x39e00,0x63006603,0xf83803f8,0x3f800000,
2948       0x0,0x60000000,0x0,0xc00cc303,0xf8180060,0x6064,0xc01fc0,0x60060070,0x40200,0x18c0060,0x402000,0x6000,0x6,0xc012000,0x0,0x18c0140,
2949       0x2014600,0x3fc3fc3f,0xc3fc3fc3,0xfc7f0300,0x60060060,0x6000600,0x60060060,0x60c61e70,0xe70e70e7,0xe70e71c,0x60e60660,0x66066060,
2950       0x6060060c,0x60c60c60,0xc60c60c6,0xcc60600,0x60060060,0x6000600,0x60060060,0x60c60c60,0xc60c60c6,0xc60c000,0x70c60c60,0xc60c60c0,
2951       0xe060c0e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33022e0,0x6670c000,0xc003000,0x600600,0x60180,0x31803030,
2952       0x41c0184,0x1831c0c0,0x71c23806,0x6001e0,0x780000,0x62630c60,0xe38261c6,0x600386,0x60606043,0x860c6006,0x661e30c,0x60030c60,
2953       0x740e0607,0xe0f039c,0x31c06030,0x600180,0x30000000,0x61c71,0x830831c3,0x406031c,0x60c06003,0x6300c06,0x6660c318,0x71831c0c,
2954       0x41c0c07,0x1c0e039c,0x1b00e030,0x600600,0x60000000,0x1c41b00e,0x601cc0,0x401f8,0x45240,0xe1803601,0xb03001b0,0x1b000000,
2955       0x0,0x0,0x41,0xc008e711,0xb0300060,0x6034,0x80c02020,0x60060030,0x30c00,0xc60000,0x30c000,0x0,0x7,0x1c012000,0x0,0x3180240,
2956       0x6024608,0x30c30c30,0xc30c30c3,0xc630382,0x60060060,0x6000600,0x60060060,0x61c61e30,0xc30c30c3,0xc30c208,0x70c70e70,0xe70e70e0,
2957       0x6060068c,0x61c61c61,0xc61c61c6,0x1cc62308,0x30430430,0x43040600,0x60060060,0x31860c31,0x83183183,0x18318060,0x31c71c71,
2958       0xc71c71c0,0xe07180e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x2203fc0,0x663f6000,0x6006000,0x600600,0x60300,
2959       0x3f81fe7f,0xc7f80187,0xf83f80c0,0x3f83f006,0x600020,0x400060,0x33e6067f,0xc1fe7f87,0xfe6001fe,0x6063fc7f,0x60e7fe6,0x660e3f8,
2960       0x6001f860,0x37fc0603,0xfc06030c,0x30c0607f,0xe06000c0,0x30000000,0x7fc7f,0x83f83fc3,0xfc0603fc,0x60c7fe03,0x61807c6,0x6660c3f8,
2961       0x7f83fc0c,0x7f80fc3,0xfc0e039c,0x3180607f,0xc0600600,0x60000000,0xfc0e00c,0x601986,0x66040040,0x4527f,0xc0803fe0,0xe07fe0e0,
2962       0xe000000,0x0,0x0,0x7f,0x80107ff0,0xe07fc060,0x603f,0x83fe0000,0x60060018,0xf000,0x420000,0xf0000,0x7fe00,0x7,0xfe012000,
2963       0x0,0x2100640,0xc0643f8,0x60660660,0x66066067,0xec3e1fe,0x7fe7fe7f,0xe7fe3fc3,0xfc3fc3fc,0x7f860e3f,0x83f83f83,0xf83f8000,
2964       0x5fc3fc3f,0xc3fc3fc0,0x606006fc,0x7fc7fc7f,0xc7fc7fc7,0xfcffe3f8,0x3fc3fc3f,0xc3fc7fe7,0xfe7fe7fe,0x3f860c3f,0x83f83f83,
2965       0xf83f8060,0x7f83fc3f,0xc3fc3fc0,0x607f8060,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x2201f80,0x3c1e7000,0x6006000,
2966       0x600,0x60300,0xe01fe7f,0xc3f00183,0xe01f0180,0x1f01e006,0x600000,0x60,0x3006067f,0x807c7e07,0xfe6000f8,0x6063fc3e,0x6067fe6,
2967       0x660e0f0,0x6000f060,0x3bf80601,0xf806030c,0x60e0607f,0xe06000c0,0x30000000,0x1ec6f,0xf01ec0,0xf80601ec,0x60c7fe03,0x61c03c6,
2968       0x6660c1f0,0x6f01ec0c,0x3f007c1,0xcc0e030c,0x71c0c07f,0xc0600600,0x60000000,0x7804018,0xe01186,0x66040040,0x39e3f,0x80401fe0,
2969       0x407fe040,0x4000000,0x0,0x0,0x3f,0x203ce0,0x407fc060,0x601f,0x3fe0000,0x60060018,0x0,0x0,0x0,0x7fe00,0x6,0xe6012000,0x0,
2970       0x7e0,0x1807e1f0,0x60660660,0x66066066,0x6c3e07c,0x7fe7fe7f,0xe7fe3fc3,0xfc3fc3fc,0x7e060e0f,0xf00f00,0xf00f0000,0x8f01f81f,
2971       0x81f81f80,0x60600670,0x1ec1ec1e,0xc1ec1ec1,0xec79c0f0,0xf80f80f,0x80f87fe7,0xfe7fe7fe,0x1f060c1f,0x1f01f01,0xf01f0000,0x4f01cc1c,
2972       0xc1cc1cc0,0xc06f00c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200,0x0,0x6006000,0x600,0x600,0x0,0x0,0x0,0x0,
2973       0x600000,0x0,0x18000000,0x0,0x0,0x0,0x0,0x0,0x1800,0x0,0x0,0x0,0x600060,0x30000000,0x0,0x0,0xc,0x3,0x0,0x0,0x60000c00,0x0,
2974       0x0,0xc000,0x600600,0x60000000,0x18,0xc03100,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x601f8,0x0,0x0,0x0,0x0,0x6,
2975       0x12000,0x2000000,0x40,0x20004000,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2976       0x0,0xc06000c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200,0x0,0x2004000,0xc00,0x0,0x0,0x0,0x0,0x0,0xc00000,
2977       0x0,0x1c000000,0x0,0x0,0x0,0x0,0x0,0xc00,0x0,0x0,0x0,0x780000,0xf0000000,0x0,0x0,0x21c,0x3,0x0,0x0,0x60000c00,0x0,0x0,0xc000,
2978       0x7c0603,0xe0000000,0x10,0xc02300,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x601f0,0x0,0x0,0x0,0x0,0x6,0x12000,0x1000000,
2979       0x40,0x7e004000,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc06000c0,0x0,
2980       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200,0x0,0x300c000,0xc00,0x0,0x0,0x0,0x0,0x0,0xc00000,0x0,0x7800000,0x0,
2981       0x0,0x0,0x0,0x0,0x800,0x0,0x0,0x0,0x780000,0xf0000000,0x0,0x0,0x3f8,0x3e,0x0,0x0,0x60000c00,0x0,0x0,0x38000,0x3c0603,0xc0000000,
2982       0x10,0xfc00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x60000,0x0,0x0,0x0,0x0,0x6,0x0,0x1000000,0x0,0x0,0x0,0x0,
2983       0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x80600380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2984       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xffc,0x0,
2985       0x0,0x1f0,0x3c,0x0,0x0,0x60000c00,0x0,0x0,0x38000,0x600,0x0,0x0,0xf000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2986       0x0,0x0,0x0,0x0,0x0,0x6,0x0,0xe000000,0x0,0x0,0x0,0x0,0x70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x0,0x0,0x0,0x0,
2987       0x0,0x0,0x0,0x3,0x80600380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2988       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xffc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2989       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2990       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 };
2991 
2992     // Define a 16x32 font (large size).
2993     const unsigned int font16x32[16*32*256/32] = {
2994       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2995       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2996       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc300000,0x0,0xc300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2997       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70000e0,0x3c00730,0xe7001c0,0x0,0x70000e0,0x3c00e70,0x70000e0,0x3c00e70,0x730,0x70000e0,0x3c00730,
2998       0xe700000,0x700,0xe003c0,0xe7000e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2999       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3000       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3001       0x0,0x0,0x6600000,0x0,0x6600000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3002       0x0,0x0,0x18001c0,0x6600ff0,0xe7003e0,0x0,0x18001c0,0x6600e70,0x18001c0,0x6600e70,0xff0,0x18001c0,0x6600ff0,0xe700000,0x180,
3003       0x1c00660,0xe7001c0,0x0,0x0,0x0,0x380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3004       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3005       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,
3006       0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c00380,
3007       0xc300ce0,0xe700630,0x0,0x1c00380,0xc300e70,0x1c00380,0xc300e70,0xce0,0x1c00380,0xc300ce0,0xe700000,0x1c0,0x3800c30,0xe700380,
3008       0x0,0x0,0x0,0x7c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3009       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3010       0x0,0x0,0x0,0x0,0xe000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1800000,0x0,0x0,0x0,
3011       0x0,0x0,0x0,0x0,0x0,0xc300000,0x0,0xc300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x700000,0x0,0x0,0x0,0x7c007c00,0x3e000000,
3012       0x0,0x0,0x630,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe000070,0x1800000,0xc60,0x0,0xe000070,0x1800000,0xe000070,
3013       0x1800000,0x0,0xe000070,0x1800000,0x0,0xe00,0x700180,0x70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3014       0x0,0x0,0x0,0x800000,0x0,0x600600,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3015       0x0,0x0,0x3f0,0xfc0,0x0,0x7000000,0x38000000,0x1c0000,0xfc0000,0x380001c0,0xe01c00,0x7f800000,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,
3016       0x1801f00,0x0,0x0,0x1c,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7300000,0x6600000,0x0,0x6600000,0x0,0x0,0x0,0x0,0xe700000,
3017       0x0,0x0,0x0,0x0,0x0,0xe00000,0x0,0x0,0x0,0xc000c00,0x43800000,0x0,0x0,0x630,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3018       0xf80,0x70000e0,0x3c00730,0xe700c60,0x0,0x70000e0,0x3c00e70,0x70000e0,0x3c00e70,0xe000730,0x70000e0,0x3c00730,0xe700000,0x700,
3019       0xe003c0,0xe7000e0,0x38000e70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6300000,0x803c00,0x7c00180,
3020       0xc00300,0x1000000,0x0,0x1c,0x3c007c0,0xfc007e0,0xe01ff8,0x3f03ffc,0x7e007c0,0x0,0x0,0x7c0,0x1c0,0x7f8003f0,0x7f007ff8,0x7ff803f0,
3021       0x70381ffc,0xff0700e,0x7000783c,0x783807c0,0x7fc007c0,0x7fc00fc0,0x7fff7038,0x700ee007,0x780f780f,0x7ffc03f0,0x70000fc0,0x3c00000,
3022       0x3000000,0x38000000,0x1c0000,0x1fc0000,0x380001c0,0xe01c00,0x7f800000,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x1801f80,0x0,0x1f80000,
3023       0x7e,0x0,0x0,0x2400000,0xfc00000,0x7ff0000,0x7ffc0000,0x0,0x0,0x0,0x0,0xf30fb0c,0x2400000,0x0,0x240780f,0x1c0,0xfc,0x780f,
3024       0x18003f0,0xe700000,0x7c00000,0x0,0xff0,0x3c00000,0x78007c0,0xc00000,0xff80000,0xf80,0x7c00000,0xc000c00,0x18001c0,0x1c001c0,
3025       0x1c001c0,0x1c003e0,0x7fe03f0,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,0x1ffc1ffc,0x7f007838,0x7c007c0,0x7c007c0,0x7c00000,0x7c67038,
3026       0x70387038,0x7038780f,0x70001fe0,0x30000c0,0x2400f30,0xe700c60,0x0,0x30000c0,0x2400e70,0x30000c0,0x2400e70,0xf700f30,0x30000c0,
3027       0x2400f30,0xe700000,0x300,0xc00240,0xe7000c0,0x38000e70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,
3028       0x630018c,0x807e00,0xfe00180,0xc00300,0x1000000,0x0,0x38,0xff01fc0,0x3ff01ff0,0x1e01ff8,0x7f83ffc,0x1ff80ff0,0x0,0x0,0xff0,
3029       0x1f003e0,0x7fe00ff8,0x7fc07ff8,0x7ff80ff8,0x70381ffc,0xff0701c,0x7000783c,0x78381ff0,0x7fe01ff0,0x7fe01ff0,0x7fff7038,0x781ee007,
3030       0x3c1e380e,0x7ffc0380,0x380001c0,0x3c00000,0x1800000,0x38000000,0x1c0000,0x3c00000,0x380001c0,0xe01c00,0x3800000,0x0,0x0,
3031       0x0,0x7000000,0x0,0x0,0x1e0,0x18003c0,0x0,0x3fc0000,0x70,0x0,0x0,0x6600000,0x1ff00000,0x1fff0000,0x7ffc0000,0x0,0x0,0x0,0x0,
3032       0xcf0239c,0x3c00000,0x0,0x3c0380e,0x1c0,0x2001fe,0x380e,0x18007f8,0xe700000,0x8600000,0x0,0xff0,0x7e00000,0x8c00870,0x1800000,
3033       0x1ff80000,0x180,0xc600000,0xc000c00,0x38001c0,0x3e003e0,0x3e003e0,0x3e001c0,0x7fe0ff8,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,0x1ffc1ffc,
3034       0x7fc07838,0x1ff01ff0,0x1ff01ff0,0x1ff00000,0x1fec7038,0x70387038,0x7038380e,0x70003ce0,0x1800180,0x6600cf0,0xe7007c0,0x0,
3035       0x1800180,0x6600e70,0x1800180,0x6600e70,0x7c00cf0,0x1800180,0x6600cf0,0xe700000,0x180,0x1800660,0xe700180,0x38000e70,0x0,
3036       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x630030c,0x3f0e700,0x1e200180,0x1800180,0x21100000,0x0,
3037       0x38,0x1e7819c0,0x38781038,0x1e01c00,0xf080038,0x1c381c38,0x0,0x0,0x1878,0x7fc03e0,0x70e01e18,0x70e07000,0x70001e18,0x703801c0,
3038       0x707038,0x70007c7c,0x7c381c70,0x70701c70,0x70703830,0x1c07038,0x381ce007,0x1c1c3c1e,0x3c0380,0x380001c0,0x7e00000,0xc00000,
3039       0x38000000,0x1c0000,0x3800000,0x38000000,0x1c00,0x3800000,0x0,0x0,0x0,0x7000000,0x0,0x0,0x1c0,0x18001c0,0x0,0x70c0000,0xe0,
3040       0x0,0x0,0xc300000,0x38300000,0x3c700000,0x3c0000,0x0,0x0,0x0,0x0,0xce022f4,0x1800000,0x0,0x1803c1e,0x1c0,0x2003c2,0x3c1e,
3041       0x1800e08,0x7e0,0x300000,0x0,0x7e00000,0xe700000,0x600030,0x3000000,0x3f980000,0x180,0x18200000,0xc000c00,0x1e0001c0,0x3e003e0,
3042       0x3e003e0,0x3e003e0,0xfe01e18,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70e07c38,0x1c701c70,0x1c701c70,0x1c700000,0x3c787038,
3043       0x70387038,0x70383c1e,0x70003870,0xc00300,0xc300ce0,0x380,0x0,0xc00300,0xc300000,0xc00300,0xc300000,0xfc00ce0,0xc00300,0xc300ce0,
3044       0x0,0xc0,0x3000c30,0x300,0x38000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x630031c,0xff8c300,
3045       0x1c000180,0x1800180,0x39380000,0x0,0x70,0x1c3801c0,0x203c001c,0x3e01c00,0x1c000038,0x381c3838,0x0,0x0,0x1038,0xe0e03e0,0x70703c08,
3046       0x70707000,0x70003808,0x703801c0,0x707070,0x70007c7c,0x7c383838,0x70383838,0x70387010,0x1c07038,0x381c700e,0x1e3c1c1c,0x780380,
3047       0x1c0001c0,0xe700000,0x0,0x38000000,0x1c0000,0x3800000,0x38000000,0x1c00,0x3800000,0x0,0x0,0x0,0x7000000,0x0,0x0,0x1c0,0x18001c0,
3048       0x0,0xe000000,0xe0,0x0,0x1000100,0x3800,0x70100000,0x38700000,0x780000,0x1c0,0x7801ce0,0xe380000,0x0,0x2264,0x0,0x0,0x1c1c,
3049       0x0,0x200780,0x1c1c,0x1800c00,0x1818,0x7f00000,0x0,0x18180000,0xc300000,0x600070,0x0,0x7f980000,0x180,0x18300000,0xc000c00,
3050       0x3000000,0x3e003e0,0x3e003e0,0x3e003e0,0xee03c08,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70707c38,0x38383838,0x38383838,
3051       0x38380000,0x38387038,0x70387038,0x70381c1c,0x7fc03870,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc00000,0x0,0x0,0x0,0x0,0x0,0x0,
3052       0x38000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6300318,0xe88c300,0x1c000180,0x38001c0,
3053       0xfe00180,0x0,0x70,0x1c3801c0,0x1c001c,0x6e01c00,0x1c000078,0x381c3818,0x0,0x40000,0x40000038,0x1c0607e0,0x70703800,0x70707000,
3054       0x70003800,0x703801c0,0x7070e0,0x70007c7c,0x7c383838,0x70383838,0x70387000,0x1c07038,0x381c700e,0xf780e38,0x700380,0x1c0001c0,
3055       0x1c380000,0x0,0x38000000,0x1c0000,0x3800000,0x38000000,0x1c00,0x3800000,0x0,0x0,0x0,0x7000000,0x0,0x0,0x1c0,0x18001c0,0x0,
3056       0xe000000,0xe0,0x0,0x1000100,0x4400,0x70000000,0x38700000,0x700000,0xe0,0x7001c70,0xe380000,0x0,0x2264,0x0,0x0,0xe38,0x0,
3057       0x200700,0xe38,0x1800c00,0x300c,0xc300000,0x0,0x300c0000,0xc300180,0x6003c0,0x0,0x7f980000,0x180,0x18300000,0xc000c00,0x1800000,
3058       0x7e007e0,0x7e007e0,0x7e003e0,0xee03800,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70707c38,0x38383838,0x38383838,0x38380000,
3059       0x38387038,0x70387038,0x70380e38,0x7ff039f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e00000,0x0,0x0,0x0,0x40000,0x0,0x0,0x38000000,
3060       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6300318,0x1c80e700,0x1c000180,0x38001c0,0x3800180,
3061       0x0,0xe0,0x381c01c0,0x1c001c,0x6e01c00,0x38000070,0x381c381c,0x0,0x3c0000,0x78000078,0x38030770,0x70707800,0x70387000,0x70007000,
3062       0x703801c0,0x7071c0,0x7000745c,0x7638701c,0x7038701c,0x70387000,0x1c07038,0x1c38718e,0x7700f78,0xf00380,0xe0001c0,0x381c0000,
3063       0x7e0,0x39e003e0,0x79c03f0,0x3ffc079c,0x39e01fc0,0xfe01c1e,0x3807778,0x39e007e0,0x39e0079c,0x73c07e0,0x7ff83838,0x701ce007,
3064       0x783c701c,0x1ffc01c0,0x18001c0,0x0,0x1c000100,0xe0,0x0,0x1000100,0x4200,0x70000000,0x70700100,0xf00100,0x10000e0,0x7000c70,
3065       0xc700000,0x0,0x2204,0x7e00000,0x1e380100,0x1ffc0f78,0x0,0xf80700,0xf78,0x1800e00,0x63e6,0x18300000,0x0,0x6fe60000,0xe700180,
3066       0xc00060,0x3838,0x7f980000,0x180,0x18300000,0xc000c00,0x18001c0,0x7700770,0x7700770,0x77007f0,0xee07800,0x70007000,0x70007000,
3067       0x1c001c0,0x1c001c0,0x70387638,0x701c701c,0x701c701c,0x701c1008,0x707c7038,0x70387038,0x70380f78,0x707039c0,0x7e007e0,0x7e007e0,
3068       0x7e007e0,0x1f3c03e0,0x3f003f0,0x3f003f0,0x1fc01fc0,0x1fc01fc0,0x7f039e0,0x7e007e0,0x7e007e0,0x7e00380,0x7ce3838,0x38383838,
3069       0x3838701c,0x39e0701c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6307fff,0x1c807e0c,0xe000180,
3070       0x30000c0,0x3800180,0x0,0xe0,0x381c01c0,0x1c001c,0xce01fe0,0x38000070,0x381c381c,0x3800380,0xfc0000,0x7e0000f0,0x30030770,
3071       0x70707000,0x70387000,0x70007000,0x703801c0,0x707380,0x700076dc,0x7638701c,0x7038701c,0x70387800,0x1c07038,0x1c3873ce,0x7f00770,
3072       0xe00380,0xe0001c0,0x700e0000,0x1ff8,0x3ff00ff0,0xffc0ff8,0x3ffc0ffc,0x3bf01fc0,0xfe01c3c,0x3807f78,0x3bf00ff0,0x3ff00ffc,
3073       0x77e0ff0,0x7ff83838,0x3838e007,0x3c783838,0x1ffc01c0,0x18001c0,0x0,0x7ff00380,0x1e0,0x0,0x1000100,0x4200,0x78000000,0x70700380,
3074       0xe00380,0x3800060,0xe000e30,0x1c600000,0x0,0x2204,0xff00000,0x7f7c0380,0x1ffc0770,0x1c0,0x3fc0700,0x18040770,0x1800780,0x4e12,
3075       0x18300104,0x0,0x4c320000,0x7e00180,0x1c00030,0x3838,0x7f980000,0x180,0x18302080,0xc000c00,0x18001c0,0x7700770,0x7700770,
3076       0x7700770,0x1ee07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70387638,0x701c701c,0x701c701c,0x701c381c,0x705c7038,0x70387038,
3077       0x70380770,0x70383b80,0x1ff81ff8,0x1ff81ff8,0x1ff81ff8,0x3fbe0ff0,0xff80ff8,0xff80ff8,0x1fc01fc0,0x1fc01fc0,0xff83bf0,0xff00ff0,
3078       0xff00ff0,0xff00380,0xffc3838,0x38383838,0x38383838,0x3ff03838,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3079       0x0,0x1c0,0x7fff,0x1c803c38,0xf000000,0x70000e0,0xfe00180,0x0,0x1c0,0x381c01c0,0x3c0078,0xce01ff0,0x39e000f0,0x1c38381c,0x3800380,
3080       0x3e07ffc,0xf8001f0,0x307b0770,0x70e07000,0x70387000,0x70007000,0x703801c0,0x707700,0x700076dc,0x7638701c,0x7038701c,0x70387e00,
3081       0x1c07038,0x1c3873ce,0x3e007f0,0x1e00380,0x70001c0,0x0,0x1038,0x3c381e18,0x1c7c1e3c,0x3801e3c,0x3c7801c0,0xe01c78,0x380739c,
3082       0x3c781c38,0x3c381c3c,0x7c21e10,0x7003838,0x3838700e,0x1ef03838,0x3c01c0,0x18001c0,0x0,0x7fe007c0,0x1c0,0x0,0x1000100,0x6400,
3083       0x7e000000,0x707007c0,0x1e007c0,0x7c00070,0xe000638,0x18600000,0x0,0x0,0x1e100000,0x73ce07c0,0x3c07f0,0x1c0,0x7240700,0x1ddc3ffe,
3084       0x1800de0,0x8c01,0x1870030c,0x0,0x8c310000,0x3c00180,0x3800030,0x3838,0x7f980000,0x180,0x183030c0,0xc000c00,0x430001c0,0x7700770,
3085       0x7700770,0x7700770,0x1ce07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70387638,0x701c701c,0x701c701c,0x701c1c38,0x70dc7038,
3086       0x70387038,0x703807f0,0x70383b80,0x10381038,0x10381038,0x10381038,0x21e71e18,0x1e3c1e3c,0x1e3c1e3c,0x1c001c0,0x1c001c0,0x1e383c78,
3087       0x1c381c38,0x1c381c38,0x1c380380,0x1c383838,0x38383838,0x38383838,0x3c383838,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3088       0x0,0x0,0x0,0x0,0x0,0x1c0,0x630,0x1e8000e0,0x1f000000,0x70000e0,0x39380180,0x0,0x1c0,0x3b9c01c0,0x3c07f0,0x18e01078,0x3bf800e0,
3089       0x7e0383c,0x3800380,0x1f807ffc,0x3f001c0,0x61ff0e38,0x7fc07000,0x70387ff0,0x7ff07000,0x7ff801c0,0x707f00,0x7000729c,0x7338701c,
3090       0x7070701c,0x70703fc0,0x1c07038,0x1e7873ce,0x1c003e0,0x3c00380,0x70001c0,0x0,0x1c,0x3c381c00,0x1c3c1c1c,0x3801c3c,0x383801c0,
3091       0xe01cf0,0x380739c,0x38381c38,0x3c381c3c,0x7801c00,0x7003838,0x3838700e,0xfe03c78,0x7801c0,0x18001c0,0x0,0x1c000c20,0xff8,
3092       0x0,0x1ff01ff0,0x3818,0x3fc00100,0x707e0c20,0x3c00c20,0xc200030,0xc000618,0x18c00000,0x0,0x0,0x1c000080,0xe1ce0c20,0x7803e0,
3093       0x1c0,0xe200700,0xff83ffe,0x1801878,0x9801,0x1cf0071c,0x7ffc0000,0x8c310000,0x7ffe,0x7000030,0x3838,0x3f980380,0x180,0xc6038e0,
3094       0x7f9c7f9c,0x3e1c01c0,0xe380e38,0xe380e38,0xe380f78,0x1cfc7000,0x7ff07ff0,0x7ff07ff0,0x1c001c0,0x1c001c0,0xfe387338,0x701c701c,
3095       0x701c701c,0x701c0e70,0x719c7038,0x70387038,0x703803e0,0x70383b80,0x1c001c,0x1c001c,0x1c001c,0xe71c00,0x1c1c1c1c,0x1c1c1c1c,
3096       0x1c001c0,0x1c001c0,0x1c383838,0x1c381c38,0x1c381c38,0x1c380000,0x3c383838,0x38383838,0x38383c78,0x3c383c78,0x0,0x0,0x0,0x0,
3097       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x630,0xf800380,0x3f830000,0x70000e0,0x31080180,0x0,0x380,0x3b9c01c0,
3098       0x7807e0,0x38e00038,0x3c3800e0,0xff01c3c,0x3800380,0x7c000000,0x7c03c0,0x61870e38,0x7fc07000,0x70387ff0,0x7ff070fc,0x7ff801c0,
3099       0x707f80,0x7000739c,0x7338701c,0x7ff0701c,0x7fe00ff0,0x1c07038,0xe7073ce,0x1c003e0,0x3800380,0x38001c0,0x0,0x1c,0x381c3800,
3100       0x381c380e,0x380381c,0x383801c0,0xe01de0,0x380739c,0x3838381c,0x381c381c,0x7001e00,0x7003838,0x1c70718e,0x7e01c70,0xf00380,
3101       0x18001e0,0x1e000000,0x1c001bb0,0xff8,0x0,0x1000100,0xe0,0xff00300,0x707e1bb0,0x3801bb0,0x1bb00010,0x8000308,0x30c00000,0x0,
3102       0x0,0x1e0000c0,0xe1ce1bb0,0xf003e0,0x1c0,0x1c203ff8,0x63003e0,0x180181c,0x9801,0xfb00e38,0x7ffc0000,0x8fc10000,0x7ffe,0xe000860,
3103       0x3838,0x1f980380,0x180,0x7c01c70,0x1f001f0,0x1f003c0,0xe380e38,0xe380e38,0xe380e38,0x1cfc7000,0x7ff07ff0,0x7ff07ff0,0x1c001c0,
3104       0x1c001c0,0xfe387338,0x701c701c,0x701c701c,0x701c07e0,0x731c7038,0x70387038,0x703803e0,0x70383980,0x1c001c,0x1c001c,0x1c001c,
3105       0xe73800,0x380e380e,0x380e380e,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0000,0x387c3838,0x38383838,0x38381c70,
3106       0x381c1c70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0xc30,0x7f00e00,0x33c30000,0x70000e0,0x1007ffe,
3107       0x0,0x380,0x3b9c01c0,0xf00078,0x30e0001c,0x3c1c01c0,0x1c381fdc,0x0,0x70000000,0x1c0380,0x63030e38,0x70707000,0x70387000,0x700070fc,
3108       0x703801c0,0x707b80,0x7000739c,0x7338701c,0x7fc0701c,0x7fc001f0,0x1c07038,0xe703e5c,0x3e001c0,0x7800380,0x38001c0,0x0,0x7fc,
3109       0x381c3800,0x381c380e,0x380381c,0x383801c0,0xe01fe0,0x380739c,0x3838381c,0x381c381c,0x7001fc0,0x7003838,0x1c70718e,0x7c01c70,
3110       0xe01f00,0x180007c,0x7f8c0000,0x7fc03fb8,0x1c0,0x0,0x1000100,0x700,0x1f00600,0x70703fb8,0x7803fb8,0x3fb80000,0x8000000,0x180,
3111       0x0,0x0,0x1fc00060,0xe1ce3fb8,0xe001c0,0x1c0,0x1c203ff8,0xc1801c0,0x180c,0x9801,0x1c70,0xc0000,0x8cc10000,0x180,0xfe007c0,
3112       0x3838,0x7980380,0xff0,0xe38,0x3e003e00,0x3e000380,0xe380e38,0xe380e38,0xe380e38,0x38e07000,0x70007000,0x70007000,0x1c001c0,
3113       0x1c001c0,0x70387338,0x701c701c,0x701c701c,0x701c03c0,0x731c7038,0x70387038,0x703801c0,0x703838e0,0x7fc07fc,0x7fc07fc,0x7fc07fc,
3114       0xe73800,0x380e380e,0x380e380e,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c7ffc,0x38dc3838,0x38383838,0x38381c70,
3115       0x381c1c70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0xc60,0xf83878,0x71e30000,0x70000e0,0x1007ffe,
3116       0x7f0,0x380,0x381c01c0,0x1e0003c,0x60e0001c,0x381c01c0,0x381c079c,0x0,0x7c000000,0x7c0380,0x63031c1c,0x70307000,0x70387000,
3117       0x7000701c,0x703801c0,0x7071c0,0x7000739c,0x71b8701c,0x7000701c,0x71e00078,0x1c07038,0xe703e7c,0x7e001c0,0xf000380,0x38001c0,
3118       0x0,0x1ffc,0x381c3800,0x381c3ffe,0x380381c,0x383801c0,0xe01fc0,0x380739c,0x3838381c,0x381c381c,0x7000ff0,0x7003838,0x1ef03bdc,
3119       0x3800ee0,0x1e01f00,0x180007c,0x61fc0000,0x7fc07f3c,0x1c0,0x0,0x1000100,0x1800,0x780c00,0x70707f3c,0xf007f3c,0x7f3c0000,0x0,
3120       0x3c0,0x3ffcffff,0x0,0xff00030,0xe1fe7f3c,0x1e001c0,0x1c0,0x1c200700,0xc183ffe,0xe0c,0x9801,0x1ff038e0,0xc07f0,0x8c610000,
3121       0x180,0x0,0x3838,0x1980380,0x0,0x1ff0071c,0xe000e000,0xe0000f80,0x1c1c1c1c,0x1c1c1c1c,0x1c1c1e38,0x38e07000,0x70007000,0x70007000,
3122       0x1c001c0,0x1c001c0,0x703871b8,0x701c701c,0x701c701c,0x701c03c0,0x761c7038,0x70387038,0x703801c0,0x70703870,0x1ffc1ffc,0x1ffc1ffc,
3123       0x1ffc1ffc,0xfff3800,0x3ffe3ffe,0x3ffe3ffe,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c7ffc,0x389c3838,0x38383838,
3124       0x38380ee0,0x381c0ee0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0xfffc,0xbc60fc,0x70e30000,0x70000e0,
3125       0x180,0x7f0,0x700,0x381c01c0,0x3e0001c,0x7ffc001c,0x381c03c0,0x381c001c,0x0,0x1f807ffc,0x3f00380,0x63031ffc,0x70387000,0x70387000,
3126       0x7000701c,0x703801c0,0x7071e0,0x7000701c,0x71b8701c,0x7000701c,0x70f00038,0x1c07038,0x7e03e7c,0x77001c0,0xe000380,0x1c001c0,
3127       0x0,0x3c1c,0x381c3800,0x381c3ffe,0x380381c,0x383801c0,0xe01fe0,0x380739c,0x3838381c,0x381c381c,0x70003f8,0x7003838,0xee03bdc,
3128       0x3c00ee0,0x3c00380,0x18000e0,0xf00000,0x1c007e7c,0x3c0,0x0,0x1000100,0x0,0x381800,0x70707e7c,0xe007e7c,0x7e7c0000,0x0,0x7c0,
3129       0x0,0x0,0x3f80018,0xe1fe7e7c,0x3c001c0,0x1c0,0x1c200700,0xc183ffe,0xf0c,0x8c01,0x38e0,0xc07f0,0x8c710000,0x180,0x0,0x3838,
3130       0x1980000,0x0,0x71c,0x7000f0,0x700f00,0x1ffc1ffc,0x1ffc1ffc,0x1ffc1ffc,0x3fe07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0,
3131       0x703871b8,0x701c701c,0x701c701c,0x701c07e0,0x7c1c7038,0x70387038,0x703801c0,0x7ff03838,0x3c1c3c1c,0x3c1c3c1c,0x3c1c3c1c,
3132       0x3fff3800,0x3ffe3ffe,0x3ffe3ffe,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0000,0x391c3838,0x38383838,0x38380ee0,
3133       0x381c0ee0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfffc,0x9c01ce,0x70f60000,0x70000e0,0x180,
3134       0x0,0x700,0x381c01c0,0x780001c,0x7ffc001c,0x381c0380,0x381c003c,0x0,0x3e07ffc,0xf800380,0x63031ffc,0x70387000,0x70387000,
3135       0x7000701c,0x703801c0,0x7070f0,0x7000701c,0x71b8701c,0x7000701c,0x70700038,0x1c07038,0x7e03e7c,0xf7801c0,0x1e000380,0x1c001c0,
3136       0x0,0x381c,0x381c3800,0x381c3800,0x380381c,0x383801c0,0xe01fe0,0x380739c,0x3838381c,0x381c381c,0x7000078,0x7003838,0xee03a5c,
3137       0x7c00fe0,0x78001c0,0x18001c0,0x0,0x1c003ef8,0x380,0x0,0x1000100,0x810,0x383000,0x70703ef8,0x1e003ef8,0x3ef80000,0x0,0x7c0,
3138       0x0,0x0,0x78000c,0xe1c03ef8,0x78001c0,0x1c0,0x1c200700,0x63001c0,0x18003f8,0x4e12,0x1c70,0xc0000,0x4c320000,0x180,0x0,0x3838,
3139       0x1980000,0x0,0xe38,0x700118,0x701e00,0x1ffc1ffc,0x1ffc1ffc,0x1ffc1ffc,0x7fe07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0,
3140       0x703871b8,0x701c701c,0x701c701c,0x701c0e70,0x7c1c7038,0x70387038,0x703801c0,0x7fc0381c,0x381c381c,0x381c381c,0x381c381c,
3141       0x78e03800,0x38003800,0x38003800,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0000,0x3b1c3838,0x38383838,0x38380fe0,
3142       0x381c0fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1860,0x9c0186,0x707e0000,0x30000c0,0x180,
3143       0x0,0xe00,0x183801c0,0xf00001c,0xe0001c,0x181c0380,0x381c0038,0x0,0xfc0000,0x7e000000,0x61873c1e,0x70383800,0x70707000,0x7000381c,
3144       0x703801c0,0x707070,0x7000701c,0x70f83838,0x70003838,0x70780038,0x1c07038,0x7e03c3c,0xe3801c0,0x1c000380,0xe001c0,0x0,0x381c,
3145       0x381c3800,0x381c3800,0x380381c,0x383801c0,0xe01ef0,0x380739c,0x3838381c,0x381c381c,0x7000038,0x7003838,0xfe03e7c,0xfe007c0,
3146       0x70001c0,0x18001c0,0x0,0xe001ff0,0x380,0x0,0x1000100,0x162c,0x381800,0x30701ff0,0x1c001ff0,0x1ff00000,0x0,0x3c0,0x0,0x0,
3147       0x380018,0xe1c01ff0,0x70001c0,0x1c0,0x1c200700,0xff801c0,0x18000f0,0x63e6,0xe38,0x0,0x6c3e0000,0x0,0x0,0x3838,0x1980000,0x0,
3148       0x1c70,0xf0000c,0xf01c00,0x3c1e3c1e,0x3c1e3c1e,0x3c1e3c1c,0x70e03800,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x707070f8,
3149       0x38383838,0x38383838,0x38381c38,0x38387038,0x70387038,0x703801c0,0x7000381c,0x381c381c,0x381c381c,0x381c381c,0x70e03800,
3150       0x38003800,0x38003800,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0380,0x3e1c3838,0x38383838,0x383807c0,0x381c07c0,
3151       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18c0,0x9c0186,0x783c0000,0x38001c0,0x180,0x3800000,
3152       0x3800e00,0x1c3801c0,0x1e00003c,0xe00038,0x1c1c0780,0x381c0038,0x3800380,0x3c0000,0x78000000,0x61ff380e,0x70383808,0x70707000,
3153       0x7000381c,0x703801c0,0x40707078,0x7000701c,0x70f83838,0x70003838,0x70384038,0x1c07038,0x7e03c3c,0x1e3c01c0,0x3c000380,0xe001c0,
3154       0x0,0x383c,0x3c381c00,0x1c3c1c00,0x3801c3c,0x383801c0,0xe01c78,0x380739c,0x38381c38,0x3c381c3c,0x7000038,0x7003878,0x7c01e78,
3155       0x1ef007c0,0xf0001c0,0x18001c0,0x0,0xe000ee0,0x7800380,0xe380000,0x1001ff0,0x2242,0x40380c00,0x38700ee0,0x3c000ee0,0xee00000,
3156       0x0,0x0,0x0,0x0,0x380030,0xe1c00ee0,0xf0001c0,0x1c0,0xe200700,0xdd801c0,0x1800038,0x300c,0x71c,0x0,0x300c0000,0x0,0x0,0x3838,
3157       0x1980000,0x0,0x38e0,0xb0000c,0xb01c08,0x380e380e,0x380e380e,0x380e380e,0x70e03808,0x70007000,0x70007000,0x1c001c0,0x1c001c0,
3158       0x707070f8,0x38383838,0x38383838,0x3838381c,0x38387038,0x70387038,0x703801c0,0x7000381c,0x383c383c,0x383c383c,0x383c383c,
3159       0x70e01c00,0x1c001c00,0x1c001c00,0x1c001c0,0x1c001c0,0x1c383838,0x1c381c38,0x1c381c38,0x1c380380,0x1c383878,0x38783878,0x387807c0,
3160       0x3c3807c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x18c0,0x10b801ce,0x3c3e0000,0x38001c0,0x180,
3161       0x3800000,0x3801c00,0x1e7801c0,0x3c002078,0xe02078,0x1c380700,0x1c3810f0,0x3800380,0x40000,0x40000380,0x307b380e,0x70701e18,
3162       0x70e07000,0x70001c1c,0x703801c0,0x60e0703c,0x7000701c,0x70f83c78,0x70003c70,0x703c70f0,0x1c03870,0x3c01c3c,0x3c1c01c0,0x78000380,
3163       0x7001c0,0x0,0x3c7c,0x3c381e18,0x1c7c1e0c,0x3801c3c,0x383801c0,0xe01c38,0x3c0739c,0x38381c38,0x3c381c3c,0x7001078,0x7803c78,
3164       0x7c01c38,0x1c780380,0x1e0001c0,0x18001c0,0x0,0x70c06c0,0x7000380,0xe300000,0x1000100,0x2142,0x70f00600,0x3c7006c0,0x780006c0,
3165       0x6c00000,0x0,0x0,0x0,0x0,0x10780060,0x73e206c0,0x1e0001c0,0x1c0,0x7240700,0x180c01c0,0x1800018,0x1818,0x30c,0x0,0x18180000,
3166       0x0,0x0,0x3c78,0x1980000,0x0,0x30c0,0x130000c,0x1301c18,0x380e380e,0x380e380e,0x380e380e,0x70e01e18,0x70007000,0x70007000,
3167       0x1c001c0,0x1c001c0,0x70e070f8,0x3c783c78,0x3c783c78,0x3c781008,0x7c783870,0x38703870,0x387001c0,0x70003a3c,0x3c7c3c7c,0x3c7c3c7c,
3168       0x3c7c3c7c,0x79f11e18,0x1e0c1e0c,0x1e0c1e0c,0x1c001c0,0x1c001c0,0x1c783838,0x1c381c38,0x1c381c38,0x1c380380,0x1c383c78,0x3c783c78,
3169       0x3c780380,0x3c380380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x38c0,0x1ff800fc,0x1fee0000,
3170       0x1800180,0x180,0x3800000,0x3801c00,0xff01ffc,0x3ffc3ff0,0xe03ff0,0xff00700,0x1ff81fe0,0x3800380,0x0,0x380,0x3000780f,0x7ff00ff8,
3171       0x7fc07ff8,0x70000ffc,0x70381ffc,0x7fe0701c,0x7ff8701c,0x70781ff0,0x70001ff0,0x701c7ff0,0x1c01fe0,0x3c01c38,0x380e01c0,0x7ffc0380,
3172       0x7001c0,0x0,0x1fdc,0x3ff00ff0,0xffc0ffc,0x3800fdc,0x38383ffe,0xe01c3c,0x1fc739c,0x38380ff0,0x3ff00ffc,0x7001ff0,0x3f81fb8,
3173       0x7c01c38,0x3c3c0380,0x1ffc01c0,0x18001c0,0x0,0x3fc0380,0x7000380,0xc70718c,0x1000100,0x2244,0x7ff00200,0x1fff0380,0x7ffc0380,
3174       0x3800000,0x0,0x0,0x0,0x0,0x1ff000c0,0x7f7e0380,0x1ffc01c0,0x1c0,0x3fc3ffe,0x1c0,0x1800018,0x7e0,0x104,0x0,0x7e00000,0x7ffe,
3175       0x0,0x3fde,0x1980000,0x0,0x2080,0x3300018,0x3300ff0,0x780f780f,0x780f780f,0x780f780e,0xf0fe0ff8,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,
3176       0x1ffc1ffc,0x7fc07078,0x1ff01ff0,0x1ff01ff0,0x1ff00000,0x7ff01fe0,0x1fe01fe0,0x1fe001c0,0x70003bf8,0x1fdc1fdc,0x1fdc1fdc,
3177       0x1fdc1fdc,0x3fbf0ff0,0xffc0ffc,0xffc0ffc,0x3ffe3ffe,0x3ffe3ffe,0xff03838,0xff00ff0,0xff00ff0,0xff00000,0x3ff01fb8,0x1fb81fb8,
3178       0x1fb80380,0x3ff00380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x31c0,0x7e00078,0x7cf0000,0x1800180,
3179       0x0,0x3800000,0x3803800,0x3c01ffc,0x3ffc0fe0,0xe01fc0,0x3e00e00,0x7e00f80,0x3800380,0x0,0x380,0x18007007,0x7fc003f0,0x7f007ff8,
3180       0x700003f0,0x70381ffc,0x3f80701e,0x7ff8701c,0x707807c0,0x700007c0,0x701e1fc0,0x1c00fc0,0x3c01818,0x780f01c0,0x7ffc0380,0x3801c0,
3181       0x0,0xf9c,0x39e003e0,0x79c03f0,0x380079c,0x38383ffe,0xe01c1e,0x7c739c,0x383807e0,0x39e0079c,0x7000fc0,0x1f80f38,0x3801c38,
3182       0x781e0380,0x1ffc01c0,0x18001c0,0x0,0x1f80100,0xe000700,0x1c60718c,0x1000100,0x1e3c,0x1fc00100,0x7ff0100,0x7ffc0100,0x1000000,
3183       0x0,0x0,0x0,0x0,0xfc00080,0x3e3c0100,0x1ffc01c0,0x1c0,0xf83ffe,0x1c0,0x1800838,0x0,0x0,0x0,0x0,0x7ffe,0x0,0x3b9e,0x1980000,
3184       0x0,0x0,0x2300038,0x23003e0,0x70077007,0x70077007,0x70077007,0xe0fe03f0,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,0x1ffc1ffc,0x7f007078,
3185       0x7c007c0,0x7c007c0,0x7c00000,0xc7c00fc0,0xfc00fc0,0xfc001c0,0x700039f0,0xf9c0f9c,0xf9c0f9c,0xf9c0f9c,0x1f1e03e0,0x3f003f0,
3186       0x3f003f0,0x3ffe3ffe,0x3ffe3ffe,0x7e03838,0x7e007e0,0x7e007e0,0x7e00000,0x63e00f38,0xf380f38,0xf380380,0x39e00380,0x0,0x0,
3187       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000,0x0,0xc00300,0x0,0x3000000,0x3800,0x0,0x0,0x0,0x0,
3188       0x0,0x300,0x0,0x0,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0x0,0x0,0x0,0x0,0x380,0x3801c0,0x0,0x0,0x0,0x0,0x1c,0x0,0xe00000,
3189       0x0,0x0,0x3800001c,0x0,0x0,0x0,0x700,0x1c0,0x18001c0,0x0,0x0,0xe000700,0x18600000,0x1000100,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3190       0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x1800ff0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x1980000,0x1800000,0x0,0x6300070,0x6300000,0x0,
3191       0x0,0x0,0xc0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40000000,
3192       0x0,0x700,0x38000700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000,0x0,0xc00300,0x0,0x7000000,
3193       0x7000,0x0,0x0,0x0,0x0,0x0,0x700,0x0,0x0,0xf040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x0,0x3f0,0x1c0fc0,0x0,0x0,
3194       0x0,0x0,0x1c,0x0,0xe00000,0x0,0x0,0x3800001c,0x0,0x0,0x0,0x700,0x1e0,0x18003c0,0x0,0x0,0xc000700,0x18c00000,0x1000000,0x0,
3195       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x18007e0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x1980000,0xc00000,
3196       0x0,0x7f800e0,0x7f80000,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,
3197       0x0,0x0,0x0,0x0,0x0,0x0,0x700,0x38000700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000,
3198       0x0,0x600600,0x0,0x6000000,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x7fc0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,
3199       0x3f0,0xfc0,0x0,0x0,0x0,0x0,0x838,0x0,0x1e00000,0x0,0x0,0x3800001c,0x0,0x0,0x0,0xf00,0xfc,0x1801f80,0x0,0x0,0x8008e00,0x30c00000,
3200       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x1800000,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x1980000,0xc00000,
3201       0x0,0x3001c0,0x300000,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,
3202       0x0,0x0,0x0,0x0,0x0,0xf00,0x38000f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000,0x0,
3203       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3204       0x0,0x0,0xff0,0x0,0x1fc00000,0x0,0x0,0x3800001c,0x0,0x0,0x0,0x3e00,0x7c,0x1801f00,0x0,0x0,0x800fe00,0x0,0x0,0x0,0x0,0x0,0x0,
3205       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x1800000,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x0,0x7c00000,0x0,0x3001fc,0x300000,
3206       0x0,0x0,0x0,0x3e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3207       0x3e00,0x38003e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3208       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfff8,0x0,0x0,0x0,0x7e0,0x0,0x1f000000,
3209       0x0,0x0,0x3800001c,0x0,0x0,0x0,0x3c00,0x0,0x1800000,0x0,0x0,0x7800,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3210       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x0,0x7800000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3211       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00,0x38003c00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3212       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3213       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfff8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1800000,0x0,0x0,0x0,0x0,
3214       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3215       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3216       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3217       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3218       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3219       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3220       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3221       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3222       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3223       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3224       0x0,0x0,0x0,0x0,0x0,0x0,0x0 };
3225 
3226     // Define a 29x57 font (extra large size).
3227     const unsigned int font29x57[29*57*256/32] = {
3228       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3229       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3230       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3231       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3232       0x0,0x781e00,0x0,0x0,0x7,0x81e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3233       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c0000,0xf8000,0x7e00000,0x0,0x7,
3234       0xc0000000,0x0,0x7c00,0xf80,0x7e000,0x0,0x7c00000,0xf80000,0x7e000000,0x0,0x0,0x1f00,0x3e0,0x1f800,0x0,0x0,0x0,0x3,0xe0000000,
3235       0x7c00003f,0x0,0xf8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3236       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3237       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3238       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3239       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3240       0x0,0x0,0x0,0x0,0x0,0x0,0x3c3c00,0x0,0x0,0x3,0xc3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00,
3241       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e0000,
3242       0x1f0000,0x7e00000,0xf838001f,0xf80001f,0xf0000000,0x0,0x3e00,0x1f00,0x7e000,0x3e1f000,0x3e00000,0x1f00000,0x7e00003e,0x1f000000,
3243       0x3e0,0xe0000f80,0x7c0,0x1f800,0x3e0e00,0x7c3e000,0x0,0x1,0xf0000000,0xf800003f,0x1f0f,0x800001f0,0x0,0x0,0x0,0x0,0x0,0x0,
3244       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3245       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3246       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3247       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3248       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e7800,0x0,0x0,
3249       0x1,0xe7800000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3250       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e0000,0x1e0000,0xff00001,0xfe38001f,0xf80003f,
3251       0xf8000000,0x0,0x1e00,0x1e00,0xff000,0x3e1f000,0x1e00000,0x1e00000,0xff00003e,0x1f000000,0x7f8,0xe0000780,0x780,0x3fc00,0x7f8e00,
3252       0x7c3e000,0x0,0x0,0xf0000000,0xf000007f,0x80001f0f,0x800001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3253       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3254       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3255       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3256       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3257       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xef000,0x0,0x0,0x0,0xef000000,0x0,0x0,0x0,0x0,0x0,0x0,
3258       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3259       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0000,0x3c0000,0x1e780003,0xfff8001f,0xf80003c,0x78000000,0x0,0xf00,0x3c00,0x1e7800,
3260       0x3e1f000,0xf00000,0x3c00001,0xe780003e,0x1f000000,0xfff,0xe00003c0,0xf00,0x79e00,0xfffe00,0x7c3e000,0x0,0x0,0x78000001,0xe00000f3,
3261       0xc0001f0f,0x800003c0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3262       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3263       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3264       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3265       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3266       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e000,0x0,0x0,0x0,0x7e000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3267       0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3268       0x0,0x78000,0x780000,0x3c3c0003,0x8ff0001f,0xf800078,0x3c000000,0x0,0x780,0x7800,0x3c3c00,0x3e1f000,0x780000,0x7800003,0xc3c0003e,
3269       0x1f000000,0xe3f,0xc00001e0,0x1e00,0xf0f00,0xe3fc00,0x7c3e000,0x0,0x0,0x3c000003,0xc00001e1,0xe0001f0f,0x80000780,0x0,0x0,
3270       0x0,0x0,0x0,0x0,0x1f,0xf0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3271       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3272       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3273       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3274       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3275       0x0,0x7e000,0x0,0x0,0x0,0x7e000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,
3276       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc00,0x7e000,0xfe000,0x0,0x3c000,0xf00000,0x781e0003,
3277       0x83e0001f,0xf800070,0x1c000000,0x0,0x3c0,0xf000,0x781e00,0x3e1f000,0x3c0000,0xf000007,0x81e0003e,0x1f000000,0xe0f,0x800000f0,
3278       0x3c00,0x1e0780,0xe0f800,0x7c3e000,0x0,0x0,0x1e000007,0x800003c0,0xf0001f0f,0x80000f00,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf8000000,
3279       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3280       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3281       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3282       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3283       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3284       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3285       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3fc00,0x1fe000,0x3ff800,0x0,0x0,0x0,0x0,0x0,0x70,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3286       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x78000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3287       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3288       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3289       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3290       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3291       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3292       0x0,0x0,0x78,0xf000000,0x0,0x0,0x780f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c0,
3293       0x0,0x0,0x0,0x0,0x0,0x0,0x3fc00,0x1fe000,0x3ffc00,0x0,0x0,0x0,0x0,0x0,0x70,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3294       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f00000,0x3e000,0x3e00000,0x0,0x78,0x3c000000,0x0,0x1f000,0x3e0,
3295       0x3e000,0x0,0x1f000000,0x3e0000,0x3e000000,0x0,0x0,0x7c00,0xf8,0xf800,0x0,0x0,0x0,0xf,0x80000000,0x1f00001f,0x0,0x3e,0x0,
3296       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3297       0x0,0x0,0x0,0x30000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3298       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80000,
3299       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3300       0x0,0x0,0x0,0x0,0xf80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x781c0000,0x38,0xe000000,0x0,0x0,0x380e0,0x0,
3301       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80,0x0,0x0,0x0,0x0,0x0,0x0,0x39c00,0x1ce000,0x303e00,
3302       0x0,0x0,0x0,0x0,0x0,0x78,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4000,0x0,0x0,0x0,0x0,
3303       0x0,0x0,0xf80000,0x7c000,0x3e00000,0xf0380000,0x70,0x1c000000,0x0,0xf800,0x7c0,0x3e000,0x0,0xf800000,0x7c0000,0x3e000000,
3304       0x0,0x3c0,0xe0003e00,0x1f0,0xf800,0x3c0e00,0x0,0x0,0x7,0xc0000000,0x3e00001f,0x0,0x7c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3305       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0xff,0x0,
3306       0xf8,0xf8000,0x1c000,0x0,0x0,0x0,0x0,0x1f,0xc0000000,0x1ff8,0xff00,0x0,0x0,0x3fe000,0x0,0x1fc00001,0xfe000000,0x0,0x0,0x0,
3307       0x0,0x7f800,0x0,0x0,0x0,0xff00000,0x0,0x0,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xf8000000,0xfe,0x0,0x7f80,0x0,0x0,0x0,0x0,0x0,
3308       0x0,0x3f,0xf0000000,0x7fe0,0x0,0x0,0x780000,0x1,0xe0000000,0x0,0x780000,0x3,0xfe000000,0x78000,0x3c00,0xf000,0x7800003,0xffe00000,
3309       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc0000f0,0x3f000,0x0,0x0,0x3fc00,0x0,0x0,0x1fc000,0x0,0x0,0x0,0x1fc0,
3310       0x0,0xff000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe1c0000,0x1c,0x1c000000,0x0,0x0,0x1c1c0,0x0,0x0,0x0,0x0,0x1fe0000,
3311       0x0,0x0,0x1ff,0x1f0f8,0x0,0xff000,0x0,0x0,0x0,0x3f,0xff00000f,0x80000000,0xfe0,0x3f80,0xf00,0x0,0x0,0x0,0x1,0xf8000003,0xe0000000,
3312       0x1c00,0xe000,0xe00,0x0,0x0,0x0,0x0,0x0,0x3c,0x78000000,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f0,0x3f80,0x1fc00,0xfe000,
3313       0x7f0000,0x0,0x1fc07000,0x0,0x0,0x0,0x0,0x0,0x3f800,0x780000,0x78000,0x7f00001,0xfc38001f,0xf800070,0x1c000000,0x0,0x7800,
3314       0x780,0x7f000,0x3e1f000,0x7800000,0x780000,0x7f00003e,0x1f0003f0,0x7f0,0xe0001e00,0x1e0,0x1fc00,0x7f0e00,0x7c3e000,0x0,0x3,
3315       0xc0000000,0x3c00003f,0x80001f0f,0x80000078,0x1e0000,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3316       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x1e078000,0x30000000,0x3ff,0xc00001e0,0xf0,
3317       0x78000,0x1c000,0x0,0x0,0x0,0x0,0x1e0007f,0xf000007e,0x1ffff,0x7ffe0,0x1f80,0x3ffff80,0xfff803,0xfffff800,0xfff80007,0xff800000,
3318       0x0,0x0,0x0,0x0,0x1ffe00,0x0,0xfe0003,0xfff80000,0x3ffe01ff,0xe00003ff,0xffe01fff,0xff0003ff,0xe01e0007,0x803ffff0,0xfff80,
3319       0x3c000fc0,0x7800001f,0x8003f07e,0x1e000f,0xfe0007ff,0xf00003ff,0x8007ffe0,0x1fff8,0x7fffffe,0xf0003c1,0xe000079e,0xf1f,0x1f3e0,
3320       0x1f01ff,0xfff8003f,0xf003c000,0x7fe0,0x3f00,0x0,0x3c0000,0x1,0xe0000000,0x0,0x780000,0xf,0xfe000000,0x78000,0x3c00,0xf000,
3321       0x7800003,0xffe00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xfc0000f0,0x3fe00,0x0,0x0,0xfff00,0x0,0x0,0x3fe000,
3322       0x0,0x0,0x0,0x1dc0,0x0,0x3fff00,0x0,0x3ffff80,0x1f,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff1c07ff,0x3c0f001e,0x3c000000,
3323       0x0,0x0,0x1e3c0,0xf80007c,0x0,0x780000,0x0,0xfff8000,0x3e00,0x1f00000,0x7ff,0xc001f0f8,0x0,0x3ffc00,0x0,0x0,0x0,0x3f,0xff00003f,
3324       0xe0000000,0x3ff8,0xffe0,0x1e00,0x0,0xfffc00,0x0,0x7,0xf800000f,0xf8000000,0x1c00,0xe000,0xe00,0xf000,0x1fc000,0xfe0000,0x7f00000,
3325       0x3f800001,0xfc00003f,0xf80000ff,0xffc003ff,0xe007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01ffc,
3326       0xfc00,0x3c001ffc,0xffe0,0x7ff00,0x3ff800,0x1ffc000,0x0,0x7ff8f0f0,0x3c0780,0x1e03c00,0xf01e000,0x783e0001,0xf01e0000,0xffe00,
3327       0x3c0000,0xf0000,0x7700001,0xfe38001f,0xf800070,0x1c000000,0x0,0x3c00,0xf00,0x77000,0x3e1f000,0x3c00000,0xf00000,0x7700003e,
3328       0x1f0000f8,0xc0007f8,0xe0000f00,0x3c0,0x1dc00,0x7f8e00,0x7c3e000,0x0,0x1,0xe0000000,0x7800003b,0x80001f0f,0x800000f0,0x1e0000,
3329       0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3330       0x0,0x0,0x780000,0x3c1e0000,0x1e070000,0x300001f0,0x7ff,0xc00001e0,0x1e0,0x7c000,0x1c000,0x0,0x0,0x0,0x0,0x3c000ff,0xf80007fe,
3331       0x3ffff,0x801ffff8,0x1f80,0x3ffff80,0x3fff803,0xfffff801,0xfffc000f,0xffc00000,0x0,0x0,0x0,0x0,0x7fff80,0x0,0xfe0003,0xffff0000,
3332       0xffff01ff,0xfc0003ff,0xffe01fff,0xff000fff,0xf01e0007,0x803ffff0,0xfff80,0x3c001f80,0x7800001f,0xc007f07e,0x1e001f,0xff0007ff,
3333       0xfc0007ff,0xc007fffc,0x3fffc,0x7fffffe,0xf0003c1,0xf0000f9e,0xf0f,0x8003e1e0,0x1e01ff,0xfff8003f,0xf001e000,0x7fe0,0x3f00,
3334       0x0,0x1e0000,0x1,0xe0000000,0x0,0x780000,0x1f,0xfe000000,0x78000,0x3c00,0xf000,0x7800003,0xffe00000,0x0,0x0,0x0,0x0,0x0,0x0,
3335       0x0,0x0,0x0,0x0,0x0,0x0,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x1fff80,0x0,0x0,0xffe000,0x0,0x0,0x0,0x3de0,0x0,0x7fff80,0x0,0xfffff80,
3336       0x1f,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xe7bc07ff,0x3e1f000f,0x78000000,0x0,0x0,0xf780,0x7800078,0x0,0x780000,0x180000,
3337       0x1fff8000,0x1e00,0x1e0003c,0xfff,0xc001f0f8,0x0,0x7ffe00,0x0,0x0,0x0,0x3f,0xff00007f,0xf0000000,0x3ffc,0xfff0,0x3c00,0x0,
3338       0x7fffc00,0x0,0x7,0xf800003f,0xfe000000,0x1c00,0xe000,0xe00,0xf000,0x1fc000,0xfe0000,0x7f00000,0x3f800001,0xfc00001f,0xe00001ff,
3339       0xffc00fff,0xf007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xc000fc00,0x3c003ffe,0x1fff0,
3340       0xfff80,0x7ffc00,0x3ffe000,0x0,0xfffce0f0,0x3c0780,0x1e03c00,0xf01e000,0x781e0001,0xe01e0000,0x3fff00,0x1e0000,0x1e0000,0xf780003,
3341       0xcf78001f,0xf800078,0x3c000000,0x0,0x1e00,0x1e00,0xf7800,0x3e1f000,0x1e00000,0x1e00000,0xf780003e,0x1f0000fc,0x7c000f3d,
3342       0xe0000780,0x780,0x3de00,0xf3de00,0x7c3e000,0x0,0x0,0xf0000000,0xf000007b,0xc0001f0f,0x800001e0,0x1e0000,0x3e1f00,0x0,0x0,
3343       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,
3344       0x3c1e0000,0x1e0f0000,0x300007fc,0xfff,0xc00001e0,0x1e0,0x3c000,0x1c000,0x0,0x0,0x0,0x0,0x3c001ff,0xfc001ffe,0x3ffff,0xc01ffffc,
3345       0x3f80,0x3ffff80,0x7fff803,0xfffff803,0xfffe001f,0xffe00000,0x0,0x0,0x0,0x0,0xffff80,0x7f800,0xfe0003,0xffff8001,0xffff01ff,
3346       0xff0003ff,0xffe01fff,0xff001fff,0xf01e0007,0x803ffff0,0xfff80,0x3c003f00,0x7800001f,0xc007f07f,0x1e003f,0xff8007ff,0xff000fff,
3347       0xe007ffff,0x7fffc,0x7fffffe,0xf0003c0,0xf0000f1e,0xf07,0x8003c1f0,0x3e01ff,0xfff8003f,0xf001e000,0x7fe0,0x7f80,0x0,0xe0000,
3348       0x1,0xe0000000,0x0,0x780000,0x1f,0xfe000000,0x78000,0x3c00,0xf000,0x7800003,0xffe00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,
3349       0x0,0x0,0x0,0x0,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x3fff80,0x0,0x0,0xffe000,0x0,0x0,0x0,0x78f0,0x0,0xffff80,0x0,0x3fffff80,0x1f,
3350       0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xc7f80070,0x3e1f0007,0x70000000,0x0,0x0,0x7700,0x7c000f8,0x0,0x780000,0x180000,
3351       0x3fff8000,0x1f00,0x3e0003c,0x1f03,0xc001f0f8,0x0,0x703f00,0x0,0x0,0x0,0x3f,0xff0000f0,0xf8000000,0x303e,0xc0f8,0x7800,0x0,
3352       0xffffc00,0x0,0x7,0x3800003e,0x3e000000,0x1c00,0xe000,0x3c00,0xf000,0x1fc000,0xfe0000,0x7f00000,0x3f800001,0xfc00000f,0xe00001ff,
3353       0xffc01fff,0xf007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xf000fe00,0x3c007fff,0x3fff8,
3354       0x1fffc0,0xfffe00,0x7fff000,0x1,0xffffc0f0,0x3c0780,0x1e03c00,0xf01e000,0x781f0003,0xe01e0000,0x3fff80,0xe0000,0x3c0000,0x1e3c0003,
3355       0x8ff0001f,0xf80003c,0x78000000,0x0,0xe00,0x3c00,0x1e3c00,0x3e1f000,0xe00000,0x3c00001,0xe3c0003e,0x1f00007f,0xf8000e3f,0xc0000380,
3356       0xf00,0x78f00,0xe3fc00,0x7c3e000,0x0,0x0,0x70000001,0xe00000f1,0xe0001f0f,0x800003c0,0x1e0000,0x3e1f00,0x0,0x0,0x0,0x0,0x0,
3357       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x3c0f0000,
3358       0x30000ffe,0xf80,0xc00001e0,0x3c0,0x1e000,0x101c040,0x0,0x0,0x0,0x0,0x78003f0,0x7e001ffe,0x3f807,0xe01f00fe,0x3f80,0x3ffff80,
3359       0x7e01803,0xfffff007,0xe03f003f,0x3f00000,0x0,0x0,0x0,0x0,0xfc0fc0,0x3ffe00,0xfe0003,0xffffc003,0xf81f01ff,0xff8003ff,0xffe01fff,
3360       0xff003f01,0xf01e0007,0x803ffff0,0xfff80,0x3c007e00,0x7800001f,0xc007f07f,0x1e007e,0xfc007ff,0xff801f83,0xf007ffff,0x800fc07c,
3361       0x7fffffe,0xf0003c0,0xf0000f0f,0x1e07,0xc007c0f8,0x7c01ff,0xfff8003c,0xf000,0x1e0,0xffc0,0x0,0xf0000,0x1,0xe0000000,0x0,0x780000,
3362       0x3e,0x0,0x78000,0x3c00,0xf000,0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1f,0x800000f0,0x1f80,
3363       0x0,0x0,0x7e0780,0x0,0x0,0x1f82000,0x0,0x0,0x0,0x7070,0x0,0x1f80f80,0x0,0x7fffff80,0x1f,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,
3364       0x0,0x1,0xc3f80070,0x3f3f0007,0xf0000000,0x0,0x0,0x7f00,0x3e001f0,0x0,0x780000,0x180000,0x7f018000,0xf80,0x7c0003c,0x3e00,
3365       0x4001f0f8,0xfe00,0x400f00,0x0,0x0,0x0,0x7f000000,0xe0,0x38000000,0x1e,0x38,0x7800,0x0,0x1ffe1c00,0x0,0x0,0x38000078,0xf000000,
3366       0x1c00,0xe000,0x7f800,0xf000,0x1fc000,0xfe0000,0x7f00000,0x3f800001,0xfc00001f,0xf00001ff,0xffc03f81,0xf007ffff,0xc03ffffe,
3367       0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xf800fe00,0x3c00fc1f,0x8007e0fc,0x3f07e0,0x1f83f00,0xfc1f800,
3368       0x3,0xf07fc0f0,0x3c0780,0x1e03c00,0xf01e000,0x780f8007,0xc01e0000,0x7e0fc0,0xf0000,0x3c0000,0x1c1c0003,0x87f0001f,0xf80003f,
3369       0xf8000000,0x0,0xf00,0x3c00,0x1c1c00,0x3e1f000,0xf00000,0x3c00001,0xc1c0003e,0x1f00003f,0xc0000e1f,0xc00003c0,0xf00,0x70700,
3370       0xe1fc00,0x7c3e000,0x0,0x0,0x78000001,0xe00000e0,0xe0001f0f,0x800003c0,0x1e0000,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3371       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x3c0f0001,0xff801e0f,
3372       0x1f00,0x1e0,0x3c0,0x1e000,0x3c1c1e0,0x0,0x0,0x0,0x0,0x78007c0,0x1f001f9e,0x3c001,0xf010003e,0x7780,0x3c00000,0xf800000,0xf007,
3373       0xc01f007c,0x1f80000,0x0,0x0,0x0,0x0,0xe003e0,0x7fff00,0x1ef0003,0xc007e007,0xc00301e0,0x1fc003c0,0x1e00,0x7c00,0x301e0007,
3374       0x80007800,0x780,0x3c00fc00,0x7800001f,0xe00ff07f,0x1e00f8,0x3e00780,0x1fc03e00,0xf807801f,0xc01f001c,0xf000,0xf0003c0,0xf0000f0f,
3375       0x1e03,0xc00f8078,0x780000,0xf0003c,0xf000,0x1e0,0x1f3e0,0x0,0x78000,0x1,0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,
3376       0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1f,0xf0,0xf80,0x0,0x0,0xf80180,0x0,0x0,0x1e00000,
3377       0x0,0x0,0x0,0xe038,0x0,0x3e00380,0x0,0xfe0f0000,0x0,0xf0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xc0f00070,0x3b370003,0xe0000000,
3378       0x0,0x0,0x3e00,0x1e001e0,0x0,0x780000,0x180000,0x7c000000,0x780,0x780003c,0x3c00,0x0,0x7ffc0,0x780,0x0,0x0,0x3,0xffe00000,
3379       0x1c0,0x3c000000,0xe,0x38,0xf000,0x0,0x3ffe1c00,0x0,0x0,0x38000078,0xf000000,0x1c00,0xe000,0x7f000,0xf000,0x3de000,0x1ef0000,
3380       0xf780000,0x7bc00003,0xde00001e,0xf00003e7,0x80007c00,0x30078000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,
3381       0xe0001e03,0xfc00fe00,0x3c01f007,0xc00f803e,0x7c01f0,0x3e00f80,0x1f007c00,0x7,0xc01f80f0,0x3c0780,0x1e03c00,0xf01e000,0x78078007,
3382       0x801e0000,0x7803c0,0x78000,0x780000,0x380e0003,0x81e00000,0x1f,0xf0000000,0x0,0x780,0x7800,0x380e00,0x0,0x780000,0x7800003,
3383       0x80e00000,0x1ff,0x80000e07,0x800001e0,0x1e00,0xe0380,0xe07800,0x0,0x0,0x0,0x3c000003,0xc00001c0,0x70000000,0x780,0x1e0000,
3384       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3385       0x780000,0x3c1e0000,0x3c0e0007,0xfff01c07,0x1e00,0x1e0,0x780,0xf000,0x3e1c3e0,0x0,0x0,0x0,0x0,0xf0007c0,0x1f00181e,0x20000,
3386       0xf000001f,0xf780,0x3c00000,0x1f000000,0x1f00f,0x800f8078,0xf80000,0x0,0x0,0x0,0x0,0x8003e0,0x1fc0f80,0x1ef0003,0xc001e007,
3387       0x800101e0,0x7e003c0,0x1e00,0x7800,0x101e0007,0x80007800,0x780,0x3c00f800,0x7800001e,0xe00ef07f,0x801e00f0,0x1e00780,0x7c03c00,
3388       0x78078007,0xc01e0004,0xf000,0xf0003c0,0x78001e0f,0x1e03,0xe00f807c,0xf80000,0x1f0003c,0x7800,0x1e0,0x3e1f0,0x0,0x3c000,0x1,
3389       0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0,
3390       0x1e,0xf0,0x780,0x0,0x0,0x1f00080,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x1e03c,0x0,0x3c00080,0x0,0xf80f0000,0x0,0x1f0000,0x0,0x0,
3391       0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x3bf70003,0xe0000000,0x0,0x0,0x3e00,0x1f003e0,0x0,0x780000,0x180000,0x78000000,0x7c0,0xf80003c,
3392       0x3c00,0x0,0x1f01f0,0x780,0x0,0x0,0xf,0x80f80000,0x1c0,0x1c000000,0xe,0x38,0x1e000,0x0,0x7ffe1c00,0x0,0x0,0x380000f0,0x7800000,
3393       0x1c00,0xe000,0x7fc00,0xf000,0x3de000,0x1ef0000,0xf780000,0x7bc00003,0xde00001e,0xf00003c7,0x80007800,0x10078000,0x3c0000,
3394       0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x7e00ff00,0x3c01e003,0xc00f001e,0x7800f0,0x3c00780,0x1e003c00,
3395       0x7,0x800f00f0,0x3c0780,0x1e03c00,0xf01e000,0x7807c00f,0x801e0000,0xf803c0,0x3c000,0xf00000,0x780f0000,0x0,0x7,0xc0000000,
3396       0x0,0x3c0,0xf000,0x780f00,0x0,0x3c0000,0xf000007,0x80f00000,0x7ff,0xc0000000,0xf0,0x3c00,0x1e03c0,0x0,0x0,0x0,0x0,0x1e000007,
3397       0x800003c0,0x78000000,0xf00,0x1e0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3398       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x3c1e001f,0xfff03803,0x80001e00,0x1e0,0x780,0xf000,0xf9cf80,
3399       0x0,0x0,0x0,0x0,0xf000780,0xf00001e,0x0,0xf800000f,0xe780,0x3c00000,0x1e000000,0x1e00f,0x78078,0x7c0000,0x0,0x0,0x0,0x0,0x1e0,
3400       0x3f003c0,0x1ef0003,0xc000f00f,0x800001e0,0x1f003c0,0x1e00,0xf000,0x1e0007,0x80007800,0x780,0x3c01f000,0x7800001e,0xe00ef07f,
3401       0x801e01f0,0x1e00780,0x3c07c00,0x78078003,0xc03e0000,0xf000,0xf0003c0,0x78001e0f,0x1e01,0xf01f003c,0xf00000,0x3e0003c,0x7800,
3402       0x1e0,0x7c0f8,0x0,0x0,0x1,0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,
3403       0x0,0x0,0x0,0x0,0x0,0x1e,0xf0,0x780,0x0,0x0,0x1e00000,0x0,0x0,0x3c00000,0x0,0x8,0x40,0x0,0x7e0000,0x7c00000,0x1,0xf00f0000,
3404       0x0,0x3e0000,0x0,0x3f,0xfc0,0xfc3f0,0xfc3f0,0x0,0x0,0x0,0x70,0x39e70000,0x0,0x0,0x0,0x0,0xf003c0,0x0,0x0,0x180000,0xf8000000,
3405       0x3c0,0xf00003c,0x3c00,0x0,0x3c0078,0x7ff80,0x0,0x0,0x1e,0x3c0000,0x1c0,0x1c000000,0xe,0xf0,0x0,0x0,0x7ffe1c00,0x0,0x0,0x380000f0,
3406       0x7800000,0x1c00,0xe000,0x3c00,0x0,0x3de000,0x1ef0000,0xf780000,0x7bc00003,0xde00001e,0xf00003c7,0x8000f800,0x78000,0x3c0000,
3407       0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x1f00ff00,0x3c03e003,0xc01f001e,0xf800f0,0x7c00780,0x3e003c00,
3408       0xf,0x800f80f0,0x3c0780,0x1e03c00,0xf01e000,0x7803c00f,0x1fffc0,0xf001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3409       0x0,0x0,0x307,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3410       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x781e003f,0xfff03803,
3411       0x80001e00,0x1e0,0xf80,0xf000,0x3dde00,0x0,0x0,0x0,0x0,0xf000f00,0x780001e,0x0,0x7800000f,0x1e780,0x3c00000,0x3e000000,0x3e00f,
3412       0x780f0,0x7c0000,0x0,0x0,0x0,0x0,0x1e0,0x7c001e0,0x3ef8003,0xc000f00f,0x1e0,0xf003c0,0x1e00,0xf000,0x1e0007,0x80007800,0x780,
3413       0x3c03e000,0x7800001e,0xf01ef07b,0xc01e01e0,0xf00780,0x3e07800,0x3c078003,0xe03c0000,0xf000,0xf0003c0,0x78001e0f,0x1e00,0xf01e003e,
3414       0x1f00000,0x3c0003c,0x7800,0x1e0,0x78078,0x0,0x0,0x1,0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,0x7800000,0x1e00000,
3415       0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1e,0xf0,0x780,0x0,0x0,0x1e00000,0x0,0x0,0x3c00000,0x0,0x18,0xc0,0x0,
3416       0xe70000,0x7800000,0x1,0xe00f0000,0x0,0x3c0000,0x0,0x3f,0xfc0,0xfc1f0,0x1f83f0,0x0,0x0,0x0,0x70,0x39e70000,0x0,0x0,0x0,0x0,
3417       0xf807c0,0x0,0x0,0x180000,0xf0000000,0x3e0,0x1f00003c,0x3e00,0x0,0x70001c,0x3fff80,0x0,0x0,0x38,0xe0000,0x1c0,0x1c000078,
3418       0x1c,0x1fe0,0x0,0x0,0xfffe1c00,0x0,0x0,0x380000f0,0x7800000,0x1c00,0xe000,0xe00,0x0,0x7df000,0x3ef8000,0x1f7c0000,0xfbe00007,
3419       0xdf00003c,0x780003c7,0x8000f000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0xf00f780,
3420       0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,0xf,0xf80f0,0x3c0780,0x1e03c00,0xf01e000,0x7803e01f,0x1ffff8,0xf001e0,
3421       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0xc000,0x0,0x0,0x0,0x0,0x1e0000,
3422       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3423       0x780000,0x3c1e0000,0x781e003e,0x30703803,0x80001e00,0x1e0,0xf00,0x7800,0xff800,0x1e0000,0x0,0x0,0x0,0x1e000f00,0x780001e,
3424       0x0,0x7800000f,0x3c780,0x3c00000,0x3c000000,0x3c00f,0x780f0,0x3c0000,0x0,0x0,0x2000000,0x800000,0x1e0,0x78000e0,0x3c78003,
3425       0xc000f01e,0x1e0,0xf803c0,0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c07c000,0x7800001e,0x701cf07b,0xc01e01e0,0xf00780,0x1e07800,
3426       0x3c078001,0xe03c0000,0xf000,0xf0003c0,0x7c003e0f,0x1e00,0xf83e001e,0x1e00000,0x7c0003c,0x3c00,0x1e0,0xf807c,0x0,0x0,0x1fe0001,
3427       0xe1fc0000,0x7f00003,0xf8780007,0xf000003c,0x7f0,0x783f0,0x0,0x0,0x7800000,0x1e00000,0x3e0f8000,0xfc00007,0xf8000007,0xf00001fc,
3428       0xf,0xc0003fc0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1e,0xf0,0x780,0x0,0x0,0x3c00000,0x0,0x0,0x3c00000,0x0,0x18,0xc0,0x0,0x1818000,
3429       0x7800000,0x1,0xe00f0000,0x0,0x7c0000,0x0,0x1f,0x80001f80,0x7c1f8,0x1f83e0,0x0,0x0,0x0,0x70,0x38c70007,0xf8000000,0x7f03,
3430       0xf0000000,0x0,0x780780,0x0,0x0,0xfe0000,0xf0000000,0x1e0,0x1e00003c,0x3f00,0x0,0xe07f0e,0x7fff80,0x0,0x0,0x70,0x70000,0x1c0,
3431       0x1c000078,0x3c,0x1fc0,0x0,0x0,0xfffe1c00,0x0,0x0,0x380000f0,0x7800000,0x1c00,0xe000,0xe00,0x0,0x78f000,0x3c78000,0x1e3c0000,
3432       0xf1e00007,0x8f00003c,0x78000787,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,
3433       0xf80f780,0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,0xf,0x1f80f0,0x3c0780,0x1e03c00,0xf01e000,0x7801e01e,0x1ffffc,
3434       0xf007e0,0x3fc000,0x1fe0000,0xff00000,0x7f800003,0xfc00001f,0xe0000fc0,0xfc00007f,0xfe0,0x7f00,0x3f800,0x1fc000,0x0,0x0,0x0,
3435       0x1,0xf000001f,0x80000ff0,0x7f80,0x3fc00,0x1fe000,0xff0000,0x1f80000,0x1fc1e000,0x0,0x0,0x0,0x0,0x1e1fc0,0x0,0x0,0x0,0x0,
3436       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,
3437       0x781c007c,0x30003803,0x80001f00,0x1e0,0xf00,0x7800,0x7f000,0x1e0000,0x0,0x0,0x0,0x1e000f00,0x780001e,0x0,0x7800000f,0x3c780,
3438       0x3c00000,0x3c000000,0x3c00f,0x780f0,0x3c0000,0x0,0x0,0x1e000000,0xf00000,0x3e0,0xf0000e0,0x3c78003,0xc000f01e,0x1e0,0x7803c0,
3439       0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c0f8000,0x7800001e,0x701cf079,0xe01e01e0,0xf00780,0x1e07800,0x3c078001,0xe03c0000,
3440       0xf000,0xf0003c0,0x3c003c0f,0x3e00,0x787c001f,0x3e00000,0xf80003c,0x3c00,0x1e0,0x1f003e,0x0,0x0,0x1fffc001,0xe7ff0000,0x3ffe000f,
3441       0xfe78003f,0xfc001fff,0xfe001ffc,0xf0078ffc,0x1ffc00,0x7ff000,0x7800f80,0x1e0000f,0x7f1fc01e,0x3ff0001f,0xfe00079f,0xfc0007ff,
3442       0x3c003c7f,0xf001fff8,0x1fffff0,0x3c003c0,0xf0000f1e,0xf1f,0x7c1f0,0x1f00ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x3c00000,0x100000,
3443       0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1818000,0x7800000,0x1,0xe00f0000,0x1000000,0xf80000,0x40000002,0xf,0x80001f00,0x7e0f8,0x1f07c0,
3444       0x0,0x0,0x0,0x70,0x38c7003f,0xff000000,0xff8f,0xf8000100,0xffffe,0x7c0f80,0x0,0x0,0x3ffc000,0xf0000020,0x1001f0,0x3c00003c,
3445       0x1f80,0x0,0x1c3ffc7,0x7c0780,0x0,0x0,0xe3,0xff038000,0xe0,0x38000078,0x78,0x1ff0,0x0,0x3c003c0,0xfffe1c00,0x0,0x0,0x380000f0,
3446       0x7800000,0x1c00,0xe000,0xe00,0xf000,0x78f000,0x3c78000,0x1e3c0000,0xf1e00007,0x8f00003c,0x78000787,0x8001e000,0x78000,0x3c0000,
3447       0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f3c0,0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,
3448       0x4000200f,0x3f80f0,0x3c0780,0x1e03c00,0xf01e000,0x7801f03e,0x1ffffe,0xf01fe0,0x3fff800,0x1fffc000,0xfffe0007,0xfff0003f,
3449       0xff8001ff,0xfc003ff3,0xfe0003ff,0xe0007ff8,0x3ffc0,0x1ffe00,0xfff000,0x3ff80001,0xffc0000f,0xfe00007f,0xf000003f,0xf8003c7f,
3450       0xe0003ffc,0x1ffe0,0xfff00,0x7ff800,0x3ffc000,0x1f80000,0xfff1c03c,0x3c01e0,0x1e00f00,0xf007800,0x781f0001,0xf01e7ff0,0x7c0007c,
3451       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,
3452       0x3c1e003f,0xfffff078,0x30003803,0x80000f00,0x1e0,0x1f00,0x7800,0x7f000,0x1e0000,0x0,0x0,0x0,0x3c000f00,0x780001e,0x0,0x7800000f,
3453       0x78780,0x3c00000,0x3c000000,0x7c00f,0x780f0,0x3c0007,0xe000003f,0x0,0xfe000000,0xfe0000,0x3c0,0x1f000070,0x7c7c003,0xc000f01e,
3454       0x1e0,0x7803c0,0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c1f0000,0x7800001e,0x783cf079,0xe01e03c0,0xf00780,0x1e0f000,0x3c078001,
3455       0xe03c0000,0xf000,0xf0003c0,0x3c003c07,0x81f03c00,0x7c7c000f,0x87c00000,0xf00003c,0x1e00,0x1e0,0x3e001f,0x0,0x0,0x3fffe001,
3456       0xefff8000,0x7fff001f,0xff78007f,0xfe001fff,0xfe003ffe,0xf0079ffe,0x1ffc00,0x7ff000,0x7801f00,0x1e0000f,0xffbfe01e,0x7ff8003f,
3457       0xff0007bf,0xfe000fff,0xbc003cff,0xf803fffc,0x1fffff0,0x3c003c0,0x78001e1e,0xf0f,0x800f80f0,0x1e00ff,0xffe0001e,0xf0,0x780,
3458       0x0,0x0,0x3c00000,0x380000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1008000,0x7800000,0x3,0xe00f0000,0x3800000,0xf00000,0xe0000007,
3459       0xf,0x80001f00,0x3e0f8,0x1e07c0,0x0,0x0,0x0,0x70,0x3807007f,0xff800000,0x1ffdf,0xfc000380,0xffffe,0x3e1f00,0x0,0x0,0xfffe000,
3460       0xf0000030,0x3800f8,0x7c00003c,0xfc0,0x0,0x18780c3,0xf00780,0x80100,0x0,0xc3,0xffc18000,0xf0,0x78000078,0xf0,0xf0,0x0,0x3c003c0,
3461       0xfffe1c00,0x0,0x0,0x380000f0,0x7800801,0x1c00,0xe000,0x1e00,0xf000,0xf8f800,0x7c7c000,0x3e3e0001,0xf1f0000f,0x8f80007c,0x7c000787,
3462       0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f3c0,0x3c078001,0xe03c000f,
3463       0x1e00078,0xf0003c0,0x78001e00,0xe000701f,0x3fc0f0,0x3c0780,0x1e03c00,0xf01e000,0x7800f87c,0x1e007f,0xf07e00,0x7fffc00,0x3fffe001,
3464       0xffff000f,0xfff8007f,0xffc003ff,0xfe007ff7,0xff0007ff,0xf000fffc,0x7ffe0,0x3fff00,0x1fff800,0x3ff80001,0xffc0000f,0xfe00007f,
3465       0xf00000ff,0xf8003cff,0xf0007ffe,0x3fff0,0x1fff80,0xfffc00,0x7ffe000,0x1f80001,0xfffb803c,0x3c01e0,0x1e00f00,0xf007800,0x780f0001,
3466       0xe01efff8,0x3c00078,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3467       0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e003f,0xfffff078,0x30001c07,0xf80,0x1e0,0x1e00,0x3c00,0xff800,0x1e0000,0x0,0x0,0x0,0x3c001e00,
3468       0x3c0001e,0x0,0x7800001e,0x70780,0x3c00000,0x78000000,0x78007,0x800f00f0,0x3e0007,0xe000003f,0x3,0xfe000000,0xff8000,0x7c0,
3469       0x1e000070,0x783c003,0xc001f01e,0x1e0,0x7803c0,0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c3e0000,0x7800001e,0x3838f079,
3470       0xe01e03c0,0x780780,0x1e0f000,0x1e078001,0xe03c0000,0xf000,0xf0003c0,0x3c007c07,0x81f03c00,0x3ef80007,0x87800000,0x1f00003c,
3471       0x1e00,0x1e0,0x7c000f,0x80000000,0x0,0x3ffff001,0xffffc000,0xffff003f,0xff7800ff,0xff001fff,0xfe007ffe,0xf007bffe,0x1ffc00,
3472       0x7ff000,0x7803e00,0x1e0000f,0xffffe01e,0xfff8007f,0xff8007ff,0xff001fff,0xbc003dff,0xf807fffc,0x1fffff0,0x3c003c0,0x78001e0f,
3473       0x1e07,0xc01f00f0,0x1e00ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x7c00000,0x7c0000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1018000,0x7800000,
3474       0x3,0xc00f0000,0x7c00000,0x1f00001,0xf000000f,0x80000007,0xc0003e00,0x1e07c,0x3e0780,0x0,0x0,0x0,0x70,0x380700ff,0xff800000,
3475       0x3ffff,0xfe0007c0,0xffffe,0x1e1e00,0x0,0x780000,0x1fffe000,0xf0000078,0x7c0078,0x7800003c,0xff0,0x0,0x38e0003,0x80f00780,
3476       0x180300,0x0,0x1c3,0x81e1c000,0x7f,0xf0000078,0x1e0,0x38,0x0,0x3c003c0,0xfffe1c00,0x0,0x0,0x380000f0,0x7800c01,0x80001c00,
3477       0xe000,0x603e00,0xf000,0xf07800,0x783c000,0x3c1e0001,0xe0f0000f,0x7800078,0x3c000f87,0x8001e000,0x78000,0x3c0000,0x1e00000,
3478       0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f3c0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f01,0xf000f81e,
3479       0x7bc0f0,0x3c0780,0x1e03c00,0xf01e000,0x78007878,0x1e001f,0xf0f800,0x7fffe00,0x3ffff001,0xffff800f,0xfffc007f,0xffe003ff,
3480       0xff007fff,0xff800fff,0xf001fffe,0xffff0,0x7fff80,0x3fffc00,0x3ff80001,0xffc0000f,0xfe00007f,0xf00001ff,0xfc003dff,0xf000ffff,
3481       0x7fff8,0x3fffc0,0x1fffe00,0xffff000,0x1f80003,0xffff803c,0x3c01e0,0x1e00f00,0xf007800,0x780f0001,0xe01ffffc,0x3c00078,0x0,
3482       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,
3483       0x3c1e003f,0xfffff078,0x30001e0f,0x300780,0x1e0,0x1e00,0x3c00,0x3dde00,0x1e0000,0x0,0x0,0x0,0x78001e00,0x3c0001e,0x0,0xf800003e,
3484       0xf0780,0x3dfc000,0x783f8000,0xf8007,0xc01f00f0,0x3e0007,0xe000003f,0x1f,0xfc000000,0x7ff000,0xf80,0x3e007c70,0x783c003,0xc001e03c,
3485       0x1e0,0x3c03c0,0x1e00,0x3c000,0x1e0007,0x80007800,0x780,0x3c7c0000,0x7800001e,0x3878f078,0xf01e03c0,0x780780,0x1e0f000,0x1e078001,
3486       0xe03e0000,0xf000,0xf0003c0,0x1e007807,0x83f03c00,0x3ef00007,0xcf800000,0x3e00003c,0xf00,0x1e0,0xf80007,0xc0000000,0x0,0x3e01f801,
3487       0xfe07e001,0xf80f007e,0x7f801f8,0x1f801fff,0xfe00fc0f,0xf007f83f,0x1ffc00,0x7ff000,0x7807c00,0x1e0000f,0x87e1e01f,0xe0fc00fc,
3488       0xfc007f8,0x1f803f03,0xfc003df0,0x3807e03c,0x1fffff0,0x3c003c0,0x78003e0f,0x1e03,0xe03e00f8,0x3e00ff,0xffe0001e,0xf0,0x780,
3489       0x0,0x0,0x7800000,0xfe0000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1818000,0x7c00000,0x3,0xc00f0000,0xfe00000,0x3e00003,0xf800001f,
3490       0xc0000007,0xc0003e00,0x1e03c,0x3c0f80,0x0,0x0,0x0,0x70,0x380700fc,0x7800000,0x7c1fe,0x3e000fe0,0xffffe,0x1f3e00,0x0,0x780000,
3491       0x3f98e000,0xf000003c,0xfcf8007c,0xf800003c,0x3ffc,0x0,0x31c0001,0x80f00f80,0x380700,0x0,0x183,0x80e0c000,0x3f,0xe0000078,
3492       0x3c0,0x38,0x0,0x3c003c0,0xfffe1c00,0x0,0x0,0x38000078,0xf000e01,0xc003ffe0,0x1fff00,0x7ffc00,0xf000,0xf07800,0x783c000,0x3c1e0001,
3493       0xe0f0000f,0x7800078,0x3c000f07,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,
3494       0x3c0f1e0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0xf801f01e,0xf3c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78007cf8,
3495       0x1e000f,0x80f0f000,0x7c03f00,0x3e01f801,0xf00fc00f,0x807e007c,0x3f003e0,0x1f80707f,0x8f801f80,0xf003f03f,0x1f81f8,0xfc0fc0,
3496       0x7e07e00,0x3ff80001,0xffc0000f,0xfe00007f,0xf00003ff,0xfc003fc1,0xf801f81f,0x800fc0fc,0x7e07e0,0x3f03f00,0x1f81f800,0x1f80007,
3497       0xe07f003c,0x3c01e0,0x1e00f00,0xf007800,0x780f8003,0xe01fe07e,0x3e000f8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3498       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3f,0xfffff078,0x30000ffe,0x1f007c0,0x0,0x1e00,
3499       0x3c00,0xf9cf80,0x1e0000,0x0,0x0,0x0,0x78001e00,0x3c0001e,0x0,0xf00000fc,0x1e0780,0x3fff800,0x78ffe000,0xf0003,0xe03e00f0,
3500       0x3e0007,0xe000003f,0x7f,0xe01fffff,0xf00ffc00,0x1f80,0x3c01ff70,0x783c003,0xc007e03c,0x1e0,0x3c03c0,0x1e00,0x3c000,0x1e0007,
3501       0x80007800,0x780,0x3cfc0000,0x7800001e,0x3c78f078,0xf01e03c0,0x780780,0x3e0f000,0x1e078003,0xc01f0000,0xf000,0xf0003c0,0x1e007807,
3502       0x83f83c00,0x1ff00003,0xcf000000,0x3e00003c,0xf00,0x1e0,0x0,0x0,0x0,0x20007801,0xfc03e003,0xe003007c,0x3f803e0,0x7c0003c,
3503       0xf807,0xf007e00f,0x3c00,0xf000,0x780f800,0x1e0000f,0x87e1f01f,0x803c00f8,0x7c007f0,0xf803e01,0xfc003f80,0x80f8004,0x3c000,
3504       0x3c003c0,0x3c003c0f,0x1e03,0xe03e0078,0x3c0000,0x7c0001e,0xf0,0x780,0x0,0x0,0x3ffff800,0x1ff0000,0x0,0x7800000,0x0,0x18,
3505       0xc0,0x0,0x1818000,0x3e00000,0x3,0xc00f0000,0x1ff00000,0x3e00007,0xfc00003f,0xe0000003,0xc0003c00,0xf03c,0x3c0f00,0x0,0x0,
3506       0x0,0x70,0x380701f0,0x800000,0x780fc,0x1e001ff0,0x7c,0xf3c00,0x0,0x780000,0x7e182000,0xf000001f,0xfff00ffc,0xffc0003c,0x3cfe,
3507       0x0,0x31c0001,0x80f01f80,0x780f00,0x0,0x183,0x80e0c000,0xf,0x80000078,0x780,0x38,0x0,0x3c003c0,0x7ffe1c00,0x0,0x0,0x38000078,
3508       0xf000f01,0xe003ffe0,0x1fff00,0x7ff800,0xf000,0xf07800,0x783c000,0x3c1e0001,0xe0f0000f,0x78000f8,0x3e000f07,0x8003c000,0x78000,
3509       0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x3c0f1e0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,
3510       0x78000f00,0x7c03e01e,0x1e3c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78003cf0,0x1e0007,0x80f1e000,0x4000f00,0x20007801,0x3c008,
3511       0x1e0040,0xf00200,0x780403f,0x7803e00,0x3007c00f,0x803e007c,0x1f003e0,0xf801f00,0x780000,0x3c00000,0x1e000000,0xf00007f0,
3512       0x3e003f00,0x7801f00f,0x800f807c,0x7c03e0,0x3e01f00,0x1f00f800,0x1f80007,0xc03e003c,0x3c01e0,0x1e00f00,0xf007800,0x78078003,
3513       0xc01fc03e,0x1e000f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3514       0x0,0x0,0x0,0x0,0x0,0x780000,0x0,0xf078007c,0x300007fc,0x7e00fe0,0x0,0x1e00,0x3c00,0x3e1c3e0,0x1e0000,0x0,0x0,0x0,0xf0001e00,
3515       0x3c0001e,0x1,0xf000fff8,0x1e0780,0x3fffe00,0x79fff000,0x1f0001,0xfffc00f0,0x7e0007,0xe000003f,0x3ff,0x801fffff,0xf003ff80,
3516       0x3f00,0x3c03fff0,0xf01e003,0xffffc03c,0x1e0,0x3c03ff,0xffc01fff,0xfe03c000,0x1fffff,0x80007800,0x780,0x3df80000,0x7800001e,
3517       0x1c70f078,0x781e03c0,0x780780,0x3c0f000,0x1e078007,0xc01f8000,0xf000,0xf0003c0,0x1e007807,0x83f83c00,0xfe00003,0xff000000,
3518       0x7c00003c,0x780,0x1e0,0x0,0x0,0x0,0x7c01,0xf801f007,0xc00100f8,0x1f803c0,0x3c0003c,0x1f003,0xf007c00f,0x80003c00,0xf000,
3519       0x783f000,0x1e0000f,0x3c0f01f,0x3e01f0,0x3e007e0,0x7c07c00,0xfc003f00,0xf0000,0x3c000,0x3c003c0,0x3c003c0f,0x1e01,0xf07c007c,
3520       0x7c0000,0xfc0001e,0xf0,0x780,0x0,0x0,0x3ffff000,0x3838000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0xff0000,0x3f00000,0x3,0xc00fff00,
3521       0x38380000,0x7c0000e,0xe000070,0x70000001,0xe0003c00,0xf01e,0x780e00,0x0,0x0,0x0,0x0,0x1e0,0x0,0x780f8,0xf003838,0xfc,0xffc00,
3522       0x0,0x780000,0x7c180000,0xf000000f,0xffe00fff,0xffc0003c,0x783f,0x80000000,0x6380000,0xc0f83f80,0xf81f00,0x0,0x303,0x80e06000,
3523       0x0,0x78,0xf00,0x78,0x0,0x3c003c0,0x7ffe1c00,0x0,0x0,0x3800003c,0x3e000f81,0xf003ffe0,0x1fff00,0x1fc000,0xf000,0x1e03c00,
3524       0xf01e000,0x780f0003,0xc078001e,0x3c000f0,0x1e000f07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,0xfffff00,0xf00000,0x7800000,
3525       0x3c000001,0xe0001e00,0x3c0f0f0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x3e07c01e,0x1e3c0f0,0x3c0780,0x1e03c00,
3526       0xf01e000,0x78003ff0,0x1e0007,0x80f1e000,0xf80,0x7c00,0x3e000,0x1f0000,0xf80000,0x7c0001e,0x3c07c00,0x10078007,0x803c003c,
3527       0x1e001e0,0xf000f00,0x780000,0x3c00000,0x1e000000,0xf00007c0,0x1e003e00,0x7c03e007,0xc01f003e,0xf801f0,0x7c00f80,0x3e007c00,
3528       0xf,0x801f003c,0x3c01e0,0x1e00f00,0xf007800,0x7807c007,0xc01f801f,0x1f001f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3529       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x0,0xe078003c,0x300001f0,0x3f801ff0,0x0,
3530       0x3c00,0x1e00,0x3c1c1e0,0x1e0000,0x0,0x0,0x0,0xf0001e0f,0x3c0001e,0x3,0xe000fff0,0x3c0780,0x3ffff00,0x7bfff800,0x1e0000,0x7ff00078,
3531       0x7e0007,0xe000003f,0x1ffc,0x1fffff,0xf0007ff0,0x7e00,0x3c07c3f0,0xf01e003,0xffff003c,0x1e0,0x3c03ff,0xffc01fff,0xfe03c000,
3532       0x1fffff,0x80007800,0x780,0x3ffc0000,0x7800001e,0x1ef0f078,0x781e03c0,0x780780,0x7c0f000,0x1e07801f,0x800ff000,0xf000,0xf0003c0,
3533       0xf00f807,0x83b83c00,0xfc00001,0xfe000000,0xf800003c,0x780,0x1e0,0x0,0x0,0x0,0x3c01,0xf000f007,0xc00000f0,0xf80780,0x3c0003c,
3534       0x1e001,0xf007c007,0x80003c00,0xf000,0x787e000,0x1e0000f,0x3c0f01f,0x1e01e0,0x1e007c0,0x3c07800,0x7c003f00,0xf0000,0x3c000,
3535       0x3c003c0,0x3e007c07,0x80003c00,0xf8f8003c,0x780000,0xf80001e,0xf0,0x780,0x0,0x0,0x7ffff000,0x601c000,0x3,0xffff0000,0x0,
3536       0xfff,0xf8007fff,0xc0000000,0x7e003c,0x1fe0000,0xc0003,0xc00fff00,0x601c0000,0xf800018,0x70000c0,0x38000001,0xe0007800,0x701e,
3537       0x701e00,0x0,0x0,0x0,0x0,0x1e0,0x6,0x700f8,0xf00601c,0xf8,0x7f800,0x0,0x780000,0xf8180000,0xf000000f,0x87c00fff,0xffc0003c,
3538       0xf01f,0xc0000000,0x6380000,0xc07ff780,0x1f03e03,0xfffffe00,0x303,0x81c06000,0x0,0x1ffff,0xfe001e00,0x180f8,0x0,0x3c003c0,
3539       0x3ffe1c00,0x3f00000,0x0,0x3800003f,0xfe0007c0,0xf8000000,0x18000000,0xc0000006,0x1f000,0x1e03c00,0xf01e000,0x780f0003,0xc078001e,
3540       0x3c000f0,0x1e001f07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,0xfffff00,0xf00000,0x7800000,0x3c000001,0xe000fff8,0x3c0f0f0,
3541       0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x1f0f801e,0x3c3c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78001fe0,0x1e0007,
3542       0x80f1e000,0x780,0x3c00,0x1e000,0xf0000,0x780000,0x3c0001e,0x3c07c00,0xf0007,0x8078003c,0x3c001e0,0x1e000f00,0x780000,0x3c00000,
3543       0x1e000000,0xf0000f80,0x1f003e00,0x3c03c003,0xc01e001e,0xf000f0,0x7800780,0x3c003c00,0xf,0x3f003c,0x3c01e0,0x1e00f00,0xf007800,
3544       0x7803c007,0x801f000f,0xf001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3545       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1,0xe078003f,0xb0000000,0xfc003cf0,0x0,0x3c00,0x1e00,0x101c040,0x1e0000,0x0,0x0,0x1,
3546       0xe0001e1f,0x83c0001e,0x7,0xe000fff0,0x3c0780,0x3c03f80,0x7fc0fc00,0x1e0000,0xfff80078,0xfe0007,0xe000003f,0x7fe0,0x1fffff,
3547       0xf0000ffc,0xfc00,0x780f81f0,0xf01e003,0xffff003c,0x1e0,0x3c03ff,0xffc01fff,0xfe03c000,0x1fffff,0x80007800,0x780,0x3ffc0000,
3548       0x7800001e,0x1ef0f078,0x3c1e03c0,0x780780,0x1fc0f000,0x1e07ffff,0x7ff00,0xf000,0xf0003c0,0xf00f007,0xc3b87c00,0x7c00001,0xfe000000,
3549       0xf800003c,0x3c0,0x1e0,0x0,0x0,0x0,0x3c01,0xf000f007,0x800000f0,0xf80780,0x1e0003c,0x1e001,0xf0078007,0x80003c00,0xf000,0x78fc000,
3550       0x1e0000f,0x3c0f01e,0x1e01e0,0x1e007c0,0x3c07800,0x7c003e00,0xf0000,0x3c000,0x3c003c0,0x1e007807,0x80003c00,0x7df0003c,0x780000,
3551       0x1f00001e,0xf0,0x780,0x0,0x0,0x7800000,0xe7ce000,0x3,0xffff0000,0x0,0xfff,0xf8007fff,0xc0000000,0x1f0,0xffe000,0x1c0003,
3552       0xc00fff00,0xe7ce0000,0xf800039,0xf38001cf,0x9c000000,0xe0007800,0x780e,0x701c00,0x0,0x0,0x0,0x0,0x1e0,0x7,0xf0078,0xf00e7ce,
3553       0x1f0,0x7f800,0x0,0x780000,0xf0180000,0xf000000e,0x1c0001f,0xe000003c,0xf007,0xe0000000,0x6380000,0xc03fe780,0x3e07c03,0xfffffe00,
3554       0x303,0xffc06000,0x0,0x1ffff,0xfe003ffe,0x1fff0,0x0,0x3c003c0,0x1ffe1c00,0x3f00000,0x7,0xffc0001f,0xfc0003e0,0x7c000001,0xfc00000f,
3555       0xe000007f,0x1e000,0x1e03c00,0xf01e000,0x780f0003,0xc078001e,0x3c000f0,0x1e001e07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,
3556       0xfffff00,0xf00000,0x7800000,0x3c000001,0xe000fff8,0x3c0f078,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0xf9f001e,
3557       0x783c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78001fe0,0x1e0007,0x80f1e000,0x780,0x3c00,0x1e000,0xf0000,0x780000,0x3c0001e,0x3c07800,
3558       0xf0003,0xc078001e,0x3c000f0,0x1e000780,0x780000,0x3c00000,0x1e000000,0xf0000f00,0xf003c00,0x3c03c003,0xc01e001e,0xf000f0,
3559       0x7800780,0x3c003c00,0xf,0x7f003c,0x3c01e0,0x1e00f00,0xf007800,0x7803c007,0x801f000f,0xf001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3560       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1,0xe070001f,0xf8000007,
3561       0xf0007cf8,0x7800000,0x3c00,0x1e00,0x1c000,0x1e0000,0x0,0x0,0x1,0xe0001e1f,0x83c0001e,0xf,0xc000fff8,0x780780,0x2000f80,0x7f803e00,
3562       0x3e0003,0xfffe007c,0x1fe0000,0x0,0x3ff00,0x0,0x1ff,0x8001f000,0x780f00f0,0x1f00f003,0xffffc03c,0x1e0,0x3c03ff,0xffc01fff,
3563       0xfe03c00f,0xf81fffff,0x80007800,0x780,0x3ffe0000,0x7800001e,0xee0f078,0x3c1e03c0,0x7807ff,0xff80f000,0x1e07fffe,0x3ffe0,
3564       0xf000,0xf0003c0,0xf00f003,0xc7bc7800,0xfc00000,0xfc000001,0xf000003c,0x3c0,0x1e0,0x0,0x0,0x0,0x3c01,0xe000f80f,0x800001e0,
3565       0xf80f00,0x1e0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x79f8000,0x1e0000f,0x3c0f01e,0x1e03c0,0x1f00780,0x3e0f000,0x7c003e00,
3566       0xf0000,0x3c000,0x3c003c0,0x1e007807,0x81e03c00,0x7df0003e,0xf80000,0x3e00003e,0xf0,0x7c0,0xfc000,0x80000000,0x7800000,0x1e7cf000,
3567       0x3,0xffff0000,0x0,0x18,0xc0,0x0,0xf80,0x7ffc00,0x380003,0xc00fff01,0xe7cf0000,0x1f000079,0xf3c003cf,0x9e000000,0xe0007000,
3568       0x380e,0xe01c00,0x0,0x0,0x0,0x0,0x1e0,0x3,0x800f0078,0xf01e7cf,0x3e0,0x3f000,0x0,0x780000,0xf018001f,0xfff8001e,0x1e0000f,
3569       0xc000003c,0xf003,0xe0000000,0x6380000,0xc00fc780,0x7c0f803,0xfffffe00,0x303,0xfe006000,0x0,0x1ffff,0xfe003ffe,0x1ffe0,0x0,
3570       0x3c003c0,0xffe1c00,0x3f00000,0x7,0xffc00007,0xf00001f0,0x3e00001f,0xfc0000ff,0xe00007ff,0x3e000,0x3e01e00,0x1f00f000,0xf8078007,
3571       0xc03c003e,0x1e001e0,0xf001e07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,0xfffff00,0xf00000,0x7800000,0x3c000001,0xe000fff8,
3572       0x3c0f078,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x7fe001e,0xf03c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78000fc0,
3573       0x1e0007,0x80f1f000,0x780,0x3c00,0x1e000,0xf0000,0x780000,0x3c0001e,0x3c0f800,0x1e0003,0xc0f0001e,0x78000f0,0x3c000780,0x780000,
3574       0x3c00000,0x1e000000,0xf0000f00,0xf003c00,0x3c078003,0xe03c001f,0x1e000f8,0xf0007c0,0x78003e00,0x1e,0xf7803c,0x3c01e0,0x1e00f00,
3575       0xf007800,0x7803e00f,0x801e000f,0x80f803e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3576       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1,0xe0f0000f,0xff00001f,0x8000f87c,0x7800000,0x3c00,0x1e00,0x1c000,0x7fffff80,
3577       0x0,0x0,0x3,0xc0001e1f,0x83c0001e,0x1f,0x800000fe,0xf00780,0x7c0,0x7f001e00,0x3c0007,0xe03f003f,0x3fe0000,0x0,0x3fc00,0x0,
3578       0x7f,0x8001e000,0x781f00f0,0x1e00f003,0xc007e03c,0x1e0,0x3c03c0,0x1e00,0x3c00f,0xf81e0007,0x80007800,0x780,0x3f9f0000,0x7800001e,
3579       0xfe0f078,0x3c1e03c0,0x7807ff,0xff00f000,0x1e07fff8,0xfff8,0xf000,0xf0003c0,0xf81f003,0xc7bc7800,0xfe00000,0x78000003,0xe000003c,
3580       0x1e0,0x1e0,0x0,0x0,0x0,0x1fffc01,0xe000780f,0x1e0,0x780f00,0x1e0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x7bf0000,0x1e0000f,
3581       0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0xf8000,0x3c000,0x3c003c0,0x1f00f807,0x81f03c00,0x3fe0001e,0xf00000,0x7c00007c,
3582       0xf0,0x3e0,0x3ff801,0x80000000,0x7800000,0x3cfcf800,0x3,0xffff0000,0x0,0x18,0xc0,0x0,0x7c00,0x1fff00,0x700003,0xc00f0003,
3583       0xcfcf8000,0x3e0000f3,0xf3e0079f,0x9f000000,0xf000,0x1000,0x0,0x0,0x0,0x0,0x0,0x1f0,0x1,0xc00f0078,0xf03cfcf,0x800007c0,0x1e000,
3584       0x0,0x780001,0xe018001f,0xfff8001c,0xe00007,0x8000003c,0xf001,0xf0000000,0x6380000,0xc0000000,0xf81f003,0xfffffe00,0x303,
3585       0x87006000,0x0,0x1ffff,0xfe003ffe,0x7f00,0x0,0x3c003c0,0x3fe1c00,0x3f00000,0x7,0xffc00000,0xf8,0x1f0001ff,0xf0000fff,0x80007ffc,
3586       0xfc000,0x3c01e00,0x1e00f000,0xf0078007,0x803c003c,0x1e001e0,0xf001e07,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,
3587       0x7800000,0x3c000001,0xe000fff8,0x3c0f078,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x3fc001e,0x1e03c0f0,0x3c0780,
3588       0x1e03c00,0xf01e000,0x78000780,0x1e0007,0x80f0fc00,0x3fff80,0x1fffc00,0xfffe000,0x7fff0003,0xfff8001f,0xffc0001e,0x3c0f000,
3589       0x1e0003,0xc0f0001e,0x78000f0,0x3c000780,0x780000,0x3c00000,0x1e000000,0xf0001e00,0xf803c00,0x3c078001,0xe03c000f,0x1e00078,
3590       0xf0003c0,0x78001e07,0xfffffe1e,0x1e7803c,0x3c01e0,0x1e00f00,0xf007800,0x7801e00f,0x1e0007,0x807803c0,0x0,0x0,0x0,0x0,0x0,
3591       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3,0xc0f00007,
3592       0xffc0007e,0xf03e,0x7800000,0x3c00,0x1e00,0x1c000,0x7fffff80,0x0,0x0,0x3,0xc0001e1f,0x83c0001e,0x3f,0x3e,0xf00780,0x3c0,0x7e001e00,
3593       0x7c000f,0x800f001f,0xffde0000,0x0,0x3e000,0x0,0xf,0x8003e000,0x781e0070,0x1e00f003,0xc001f03c,0x1e0,0x3c03c0,0x1e00,0x3c00f,
3594       0xf81e0007,0x80007800,0x780,0x3f1f0000,0x7800001e,0x7c0f078,0x1e1e03c0,0x7807ff,0xfc00f000,0x1e07fffe,0xffc,0xf000,0xf0003c0,
3595       0x781e003,0xc71c7800,0x1ff00000,0x78000003,0xe000003c,0x1e0,0x1e0,0x0,0x0,0x0,0xffffc01,0xe000780f,0x1e0,0x780fff,0xffe0003c,
3596       0x3c000,0xf0078007,0x80003c00,0xf000,0x7ff0000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x7f000,0x3c000,
3597       0x3c003c0,0xf00f007,0xc1f07c00,0x1fc0001f,0x1f00000,0xfc000ff8,0xf0,0x1ff,0xfffe07,0x80000000,0x7800000,0x7ffcfc00,0x0,0xf000000,
3598       0x0,0x18,0xc0,0x0,0x3e000,0x1ff80,0xe00003,0xc00f0007,0xffcfc000,0x3e0001ff,0xf3f00fff,0x9f800000,0x6000,0x0,0x0,0x7c000,
3599       0x0,0x0,0x0,0xfe,0x0,0xe00f007f,0xff07ffcf,0xc0000fc0,0x1e000,0x0,0x780001,0xe018001f,0xfff8001c,0xe00007,0x80000000,0xf800,
3600       0xf0000000,0x6380000,0xc0000000,0x1f03c000,0x1e00,0x303,0x83806000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xfe1c00,0x3f00000,0x0,
3601       0x0,0x3c,0xf801fff,0xfff8,0x7ffc0,0x1f8000,0x3c01e00,0x1e00f000,0xf0078007,0x803c003c,0x1e001e0,0xf003c07,0x8003c000,0x78000,
3602       0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x3c0f03c,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,
3603       0x78000f00,0x1f8001e,0x1e03c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e000f,0x80f0ff00,0x1ffff80,0xffffc00,0x7fffe003,
3604       0xffff001f,0xfff800ff,0xffc007ff,0xffc0f000,0x1fffff,0xc0fffffe,0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,0xf0001e00,
3605       0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e07,0xfffffe1e,0x3c7803c,0x3c01e0,0x1e00f00,0xf007800,0x7801f01f,
3606       0x1e0007,0x807c07c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3607       0x0,0x0,0x0,0x0,0x780000,0x3,0xc0f00000,0xfff003f0,0x1f00f03e,0x7800000,0x3c00,0x1e00,0x1c000,0x7fffff80,0x0,0x7ff80000,0x3,
3608       0xc0001e0f,0x3c0001e,0x7e,0x1f,0x1e00780,0x3e0,0x7e000f00,0x78000f,0x7800f,0xff9e0000,0x0,0x3fc00,0x0,0x7f,0x8003c000,0x781e0070,
3609       0x3e00f803,0xc000f03c,0x1e0,0x3c03c0,0x1e00,0x3c00f,0xf81e0007,0x80007800,0x780,0x3e0f8000,0x7800001e,0x7c0f078,0x1e1e03c0,
3610       0x7807ff,0xf000f000,0x1e07807f,0xfe,0xf000,0xf0003c0,0x781e003,0xc71c7800,0x3ef00000,0x78000007,0xc000003c,0x1e0,0x1e0,0x0,
3611       0x0,0x0,0x1ffffc01,0xe000780f,0x1e0,0x780fff,0xffe0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x7ff0000,0x1e0000f,0x3c0f01e,
3612       0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x7ff80,0x3c000,0x3c003c0,0xf00f003,0xc1f07800,0x1fc0000f,0x1e00000,0xf8000ff0,0xf0,
3613       0xff,0xffffff,0x80000000,0x3fffc000,0xfff9fe00,0x0,0xf000000,0x0,0x18,0xc0,0x0,0x1f0000,0x1fc0,0x1c00003,0xc00f000f,0xff9fe000,
3614       0x7c0003ff,0xe7f81fff,0x3fc00000,0x0,0x0,0x0,0xfe000,0x1ffffc0f,0xfffffc00,0x0,0xff,0xf0000000,0x700f007f,0xff0fff9f,0xe0000f80,
3615       0x1e000,0x0,0x780001,0xe018001f,0xfff8001c,0xe00fff,0xffc00000,0xf800,0xf0000000,0x6380000,0xc0ffff80,0x3e078000,0x1e00,0x7ff80303,
3616       0x83c06000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x3f00000,0x0,0x7f,0xff00001e,0x7c1fff0,0xfff80,0x7ffc00,0x3f0000,0x7c01f00,
3617       0x3e00f801,0xf007c00f,0x803e007c,0x1f003e0,0xf803c07,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,
3618       0xe0001e00,0x3c0f03c,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x1f8001e,0x3c03c0f0,0x3c0780,0x1e03c00,0xf01e000,
3619       0x78000780,0x1e001f,0xf07f80,0x3ffff80,0x1ffffc00,0xffffe007,0xffff003f,0xfff801ff,0xffc03fff,0xffc0f000,0x1fffff,0xc0fffffe,
3620       0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e07,
3621       0xfffffe1e,0x787803c,0x3c01e0,0x1e00f00,0xf007800,0x7800f01e,0x1e0007,0x803c0780,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3622       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1ff,0xffff8000,0x3ff80fc0,0x7fc1e01f,
3623       0x7800000,0x3c00,0x1e00,0x0,0x7fffff80,0x0,0x7ff80000,0x7,0x80001e00,0x3c0001e,0xfc,0xf,0x1e00780,0x1e0,0x7c000f00,0x78000f,
3624       0x78007,0xff1e0000,0x0,0x3ff00,0x0,0x1ff,0x8003c000,0x781e0070,0x3c007803,0xc000f03c,0x1e0,0x3c03c0,0x1e00,0x3c000,0x781e0007,
3625       0x80007800,0x780,0x3c07c000,0x7800001e,0x7c0f078,0xf1e03c0,0x780780,0xf000,0x1e07801f,0x3e,0xf000,0xf0003c0,0x781e003,0xcf1c7800,
3626       0x3cf80000,0x7800000f,0x8000003c,0xf0,0x1e0,0x0,0x0,0x0,0x3ffffc01,0xe000780f,0x1e0,0x780fff,0xffe0003c,0x3c000,0xf0078007,
3627       0x80003c00,0xf000,0x7ff8000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x3fff0,0x3c000,0x3c003c0,0xf81f003,
3628       0xc3b87800,0xf80000f,0x1e00001,0xf0000ff0,0xf0,0xff,0xf03fff,0x80000000,0x3fff8001,0xfff1ff00,0x0,0xf000000,0x0,0x18,0xc0,
3629       0x0,0x380000,0x7c0,0x3c00003,0xc00f001f,0xff1ff000,0xf80007ff,0xc7fc3ffe,0x3fe00000,0x0,0x0,0x0,0x1ff000,0x7ffffe1f,0xffffff00,
3630       0x0,0x7f,0xfe000000,0x780f007f,0xff1fff1f,0xf0001f00,0x1e000,0x0,0x780001,0xe0180000,0xf000001c,0xe00fff,0xffc00000,0x7c00,
3631       0xf0000000,0x31c0001,0x80ffff80,0x3e078000,0x1e00,0x7ff80183,0x81c0c000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x3f00000,
3632       0x0,0x7f,0xff00001e,0x7c7ff03,0xc03ff8fe,0x1ffc0f0,0x7e0000,0x7800f00,0x3c007801,0xe003c00f,0x1e0078,0xf003c0,0x7803c07,0x8003c000,
3633       0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x3c0f01e,0x3c078000,0xf03c0007,0x81e0003c,
3634       0xf0001e0,0x78000f00,0x3fc001e,0x7803c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e007f,0xf03fe0,0x7ffff80,0x3ffffc01,
3635       0xffffe00f,0xffff007f,0xfff803ff,0xffc07fff,0xffc0f000,0x1fffff,0xc0fffffe,0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,
3636       0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e07,0xfffffe1e,0x707803c,0x3c01e0,0x1e00f00,0xf007800,
3637       0x7800f01e,0x1e0007,0x803c0780,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3638       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1ff,0xffff8000,0x30f81f00,0xffe1e00f,0x87800000,0x3c00,0x1e00,0x0,0x1e0000,0x0,0x7ff80000,
3639       0x7,0x80001e00,0x3c0001e,0x1f8,0x7,0x83c00780,0x1e0,0x7c000f00,0xf8001e,0x3c001,0xfc1e0000,0x0,0x7fe0,0x0,0xffc,0x3c000,0x781e0070,
3640       0x3ffff803,0xc000783c,0x1e0,0x3c03c0,0x1e00,0x3c000,0x781e0007,0x80007800,0x780,0x3c07c000,0x7800001e,0x380f078,0xf1e03c0,
3641       0x780780,0xf000,0x1e07800f,0x8000001e,0xf000,0xf0003c0,0x3c3c003,0xcf1e7800,0x7c780000,0x7800000f,0x8000003c,0xf0,0x1e0,0x0,
3642       0x0,0x0,0x7f003c01,0xe000780f,0x1e0,0x780fff,0xffe0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x7f7c000,0x1e0000f,0x3c0f01e,
3643       0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0xfff8,0x3c000,0x3c003c0,0x781e003,0xc3b87800,0x1fc00007,0x83e00003,0xe0000ff8,0xf0,
3644       0x1ff,0xc007fe,0x0,0x7fff8001,0xffe3ff00,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x0,0x3c0,0x7800003,0xc00f001f,0xfe3ff000,0xf80007ff,
3645       0x8ffc3ffc,0x7fe00000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x1f,0xff000000,0x3c0f007f,0xff1ffe3f,0xf0003e00,0x1e000,0x0,0x780001,
3646       0xe0180000,0xf000001e,0x1e00fff,0xffc00000,0x3f00,0xf0000000,0x31c0001,0x80ffff80,0x1f03c000,0x1e00,0x7ff80183,0x81c0c000,
3647       0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x7f,0xff00003c,0xf87f007,0xc03f83ff,0x81fc01f0,0x7c0000,0x7ffff00,0x3ffff801,
3648       0xffffc00f,0xfffe007f,0xfff003ff,0xff807fff,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,
3649       0xe0001e00,0x3c0f01e,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x7fe001e,0xf003c0f0,0x3c0780,0x1e03c00,0xf01e000,
3650       0x78000780,0x1ffffe,0xf00ff0,0xfe00780,0x7f003c03,0xf801e01f,0xc00f00fe,0x7807f0,0x3c0ffff,0xffc0f000,0x1fffff,0xc0fffffe,
3651       0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00,
3652       0x1e,0xf07803c,0x3c01e0,0x1e00f00,0xf007800,0x7800783e,0x1e0007,0x801e0f80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3653       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1ff,0xffff8000,0x307c0801,0xe1f1e00f,0x87000000,
3654       0x3c00,0x1e00,0x0,0x1e0000,0x0,0x7ff80000,0xf,0x1e00,0x3c0001e,0x3f0,0x7,0x83fffffc,0x1e0,0x7c000f00,0xf0001e,0x3c000,0x3e0000,
3655       0x0,0x1ffc,0x1fffff,0xf0007ff0,0x3c000,0x781e0070,0x7ffffc03,0xc000781e,0x1e0,0x7803c0,0x1e00,0x3c000,0x781e0007,0x80007800,
3656       0x780,0x3c03e000,0x7800001e,0xf078,0x79e03c0,0x780780,0xf000,0x1e078007,0x8000000f,0xf000,0xf0003c0,0x3c3c001,0xee0ef000,
3657       0xf87c0000,0x7800001f,0x3c,0x78,0x1e0,0x0,0x0,0x0,0x7c003c01,0xe000780f,0x1e0,0x780f00,0x3c,0x3c000,0xf0078007,0x80003c00,
3658       0xf000,0x7e3e000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x1ffc,0x3c000,0x3c003c0,0x781e003,0xe3b8f800,
3659       0x1fc00007,0x83c00007,0xc00000fc,0xf0,0x3e0,0x8001f8,0x0,0x7800000,0xffc7fe00,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x0,0x1e0,
3660       0xf000003,0xc00f000f,0xfc7fe001,0xf00003ff,0x1ff81ff8,0xffc00000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x3,0xff800000,0x1e0f0078,
3661       0xffc7f,0xe0007c00,0x1e000,0x0,0x780001,0xe0180000,0xf000000e,0x1c00007,0x80000000,0x1f81,0xe0000000,0x38e0003,0x80000000,
3662       0xf81f000,0x1e00,0x7ff801c3,0x80e1c000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0xf8,0x1f070007,0xc03803ff,0xc1c001f0,
3663       0xf80000,0xfffff00,0x7ffff803,0xffffc01f,0xfffe00ff,0xfff007ff,0xffc07fff,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,
3664       0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f00f,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0xf9f001e,0xf003c0f0,
3665       0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1ffffc,0xf003f8,0xf800780,0x7c003c03,0xe001e01f,0xf00f8,0x7807c0,0x3c0fc1e,0xf000,
3666       0x1e0000,0xf00000,0x7800000,0x3c000000,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,
3667       0xf0003c0,0x78001e00,0x1e,0x1e07803c,0x3c01e0,0x1e00f00,0xf007800,0x7800783c,0x1e0007,0x801e0f00,0x0,0x0,0x0,0x0,0x0,0x0,
3668       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0xffff8000,0x303c0001,
3669       0xc071e007,0xcf000000,0x3c00,0x1e00,0x0,0x1e0000,0x0,0x0,0xf,0xf00,0x780001e,0x7e0,0x7,0x83fffffc,0x1e0,0x7c000f00,0x1f0001e,
3670       0x3c000,0x3c0000,0x0,0x3ff,0x801fffff,0xf003ff80,0x3c000,0x781e0070,0x7ffffc03,0xc000781e,0x1e0,0x7803c0,0x1e00,0x1e000,0x781e0007,
3671       0x80007800,0x780,0x3c01f000,0x7800001e,0xf078,0x79e03c0,0xf00780,0xf000,0x3e078007,0xc000000f,0xf000,0xf0003c0,0x3c3c001,
3672       0xee0ef000,0xf03e0000,0x7800003e,0x3c,0x78,0x1e0,0x0,0x0,0x0,0xf8003c01,0xe000780f,0x1e0,0x780f00,0x3c,0x3c000,0xf0078007,
3673       0x80003c00,0xf000,0x7c3e000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0xfc,0x3c000,0x3c003c0,0x3c3e001,0xe7b8f000,
3674       0x3fe00007,0xc7c0000f,0xc000003e,0xf0,0x7c0,0x0,0x0,0x7c00000,0x7fcffc00,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x0,0x1e0,0x1e000003,
3675       0xc00f0007,0xfcffc003,0xe00001ff,0x3ff00ff9,0xff800000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x0,0x1f800000,0xf0f0078,0x7fcff,
3676       0xc000fc00,0x1e000,0x0,0x780001,0xe0180000,0xf000000f,0x87c00007,0x80000000,0xfe3,0xe0000000,0x18780c3,0x0,0x7c0f800,0x1e00,
3677       0xc3,0x80e18000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0x1f0,0x3e00000f,0xc0000303,0xe00003f0,0xf00000,0xfffff80,
3678       0x7ffffc03,0xffffe01f,0xffff00ff,0xfff807ff,0xffc07fff,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,
3679       0x3c000001,0xe0001e00,0x780f00f,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00,0x1f0f801f,0xe00780f0,0x3c0780,0x1e03c00,
3680       0xf01e000,0x78000780,0x1ffff8,0xf000f8,0x1f000780,0xf8003c07,0xc001e03e,0xf01f0,0x780f80,0x3c1f01e,0xf000,0x1e0000,0xf00000,
3681       0x7800000,0x3c000000,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00,
3682       0x1e,0x3c07803c,0x3c01e0,0x1e00f00,0xf007800,0x78007c7c,0x1e0007,0x801f1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3683       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x81c00000,0x303c0003,0x8039e003,0xef000000,
3684       0x3c00,0x1e00,0x0,0x1e0000,0x0,0x0,0x1e,0xf00,0x780001e,0xfc0,0x7,0x83fffffc,0x1e0,0x3c000f00,0x1e0001e,0x3c000,0x3c0000,
3685       0x0,0x7f,0xe01fffff,0xf00ffc00,0x3c000,0x781f00f0,0x7ffffc03,0xc000781e,0x1e0,0x7803c0,0x1e00,0x1e000,0x781e0007,0x80007800,
3686       0x780,0x3c01f000,0x7800001e,0xf078,0x7de01e0,0xf00780,0x7800,0x3c078003,0xc000000f,0xf000,0xf0003c0,0x3e7c001,0xee0ef001,
3687       0xf01e0000,0x7800003e,0x3c,0x3c,0x1e0,0x0,0x0,0x0,0xf0003c01,0xe000780f,0x1e0,0x780f00,0x3c,0x3c000,0xf0078007,0x80003c00,
3688       0xf000,0x781f000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x3e,0x3c000,0x3c003c0,0x3c3c001,0xe71cf000,0x7df00003,
3689       0xc780000f,0x8000003e,0xf0,0x780,0x0,0x0,0x3c00000,0x3fcff800,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x1f00fc,0x1e0,0x1e000001,
3690       0xe00f0003,0xfcff8003,0xe00000ff,0x3fe007f9,0xff000000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x0,0x7c00000,0xf0f0078,0x3fcff,0x8000f800,
3691       0x1e000,0x0,0x780001,0xe0180000,0xf000001f,0xffe00007,0x8000003c,0x7ff,0xc0000000,0x1c3ffc7,0x0,0x3e07c00,0x1e00,0xe3,0x80738000,
3692       0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0x3e0,0x7c00001d,0xc0000001,0xe0000770,0x1f00000,0xfffff80,0x7ffffc03,
3693       0xffffe01f,0xffff00ff,0xfff807ff,0xffc07fff,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,
3694       0xe0001e00,0x780f00f,0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,0x3e07c01f,0xc00780f0,0x3c0780,0x1e03c00,0xf01e000,
3695       0x78000780,0x1fffc0,0xf0007c,0x1e000780,0xf0003c07,0x8001e03c,0xf01e0,0x780f00,0x3c1e01e,0xf000,0x1e0000,0xf00000,0x7800000,
3696       0x3c000000,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00,0x1e,0x7807803c,
3697       0x3c01e0,0x1e00f00,0xf007800,0x78003c78,0x1e0007,0x800f1e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3698       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x83c00000,0x303c0003,0x8039e001,0xee000000,0x1e00,0x3c00,
3699       0x0,0x1e0000,0x0,0x0,0x1e,0xf00,0x780001e,0x1f80,0x7,0x83fffffc,0x1e0,0x3c000f00,0x1e0001e,0x3c000,0x3c0000,0x0,0x1f,0xfc1fffff,
3700       0xf07ff000,0x0,0x780f00f0,0x78003c03,0xc000781e,0x1e0,0xf803c0,0x1e00,0x1e000,0x781e0007,0x80007800,0x780,0x3c00f800,0x7800001e,
3701       0xf078,0x3de01e0,0xf00780,0x7800,0x3c078003,0xe000000f,0xf000,0xf0003c0,0x1e78001,0xfe0ff003,0xe01f0000,0x7800007c,0x3c,0x3c,
3702       0x1e0,0x0,0x0,0x0,0xf0007c01,0xe000f80f,0x800001e0,0xf80f00,0x3c,0x1e001,0xf0078007,0x80003c00,0xf000,0x780f800,0x1e0000f,
3703       0x3c0f01e,0x1e03c0,0x1f00780,0x3e0f000,0x7c003c00,0x1e,0x3c000,0x3c003c0,0x3c3c001,0xe71cf000,0xf8f80003,0xe780001f,0x1e,
3704       0xf0,0x780,0x0,0x0,0x3c00000,0x1ffff000,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x3bc1de,0x1e0,0xf000001,0xe00f0001,0xffff0007,0xc000007f,
3705       0xffc003ff,0xfe000000,0x0,0x0,0x0,0xfe000,0x0,0x0,0x0,0x0,0x3c00000,0x1e0f0078,0x1ffff,0x1f000,0x1e000,0x0,0x780000,0xf0180000,
3706       0xf000001f,0xfff00007,0x8000003c,0x1ff,0x80000000,0xe0ff0e,0x0,0x1f03e00,0x1e00,0x70,0x70000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,
3707       0xe1c00,0x0,0x0,0x0,0x7c0,0xf8000019,0xc0000000,0xe0000670,0x1e00000,0xf000780,0x78003c03,0xc001e01e,0xf00f0,0x780780,0x3c0f807,
3708       0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0xf80f007,0xbc03c001,0xe01e000f,
3709       0xf00078,0x78003c0,0x3c001e00,0x7c03e00f,0x800780f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e0000,0xf0003c,0x1e000f80,
3710       0xf0007c07,0x8003e03c,0x1f01e0,0xf80f00,0x7c1e01e,0xf800,0x1e0000,0xf00000,0x7800000,0x3c000000,0x780000,0x3c00000,0x1e000000,
3711       0xf0001e00,0x7803c00,0x3c078003,0xe03c001f,0x1e000f8,0xf0007c0,0x78003e00,0x1f8001f,0xf00f803c,0x3c01e0,0x1e00f00,0xf007800,
3712       0x78003e78,0x1e000f,0x800f9e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3713       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x3c00000,0x303c0003,0x8039f001,0xfe000000,0x1e00,0x3c00,0x0,0x1e0000,0x0,0x0,0x3c,0xf00,
3714       0x780001e,0x3f00,0x7,0x80000780,0x3e0,0x3e000f00,0x3c0001e,0x3c000,0x7c0000,0x0,0x3,0xfe000000,0xff8000,0x0,0x3c0f81f0,0xf0001e03,
3715       0xc000780f,0x1e0,0xf003c0,0x1e00,0xf000,0x781e0007,0x80007800,0x780,0x3c007c00,0x7800001e,0xf078,0x3de01e0,0xf00780,0x7800,
3716       0x3c078001,0xe000000f,0xf000,0xf0003c0,0x1e78001,0xfc07f003,0xe00f0000,0x78000078,0x3c,0x1e,0x1e0,0x0,0x0,0x0,0xf0007c01,
3717       0xf000f007,0x800000f0,0xf80780,0x3c,0x1e001,0xf0078007,0x80003c00,0xf000,0x7807c00,0x1e0000f,0x3c0f01e,0x1e01e0,0x1e007c0,
3718       0x3c07800,0x7c003c00,0x1e,0x3c000,0x3c007c0,0x1e78001,0xe71df000,0xf8f80001,0xef80003e,0x1e,0xf0,0x780,0x0,0x0,0x3c00000,
3719       0xfffe000,0x0,0x3e000000,0x0,0x18,0x7fff,0xc0000000,0x60c306,0x1e0,0x7800001,0xe00f0000,0xfffe0007,0x8000003f,0xff8001ff,
3720       0xfc000000,0x0,0x0,0x0,0x7c000,0x0,0x0,0x0,0x0,0x3c00000,0x3c0f0078,0xfffe,0x3e000,0x1e000,0x0,0x780000,0xf0180000,0xf000003c,
3721       0xfcf80007,0x8000003c,0x7f,0x0,0x70001c,0x0,0xf81f00,0x0,0x38,0xe0000,0x0,0x0,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0xf81,
3722       0xf0000039,0xc0000000,0xe0000e70,0x1e00000,0x1e0003c0,0xf0001e07,0x8000f03c,0x781e0,0x3c0f00,0x1e0f007,0x8000f000,0x78000,
3723       0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0xf00f007,0xbc03c001,0xe01e000f,0xf00078,0x78003c0,
3724       0x3c001e00,0xf801f00f,0x800780f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e0000,0xf0003c,0x1e000f80,0xf0007c07,0x8003e03c,
3725       0x1f01e0,0xf80f00,0x7c1e01e,0x7800,0xf0000,0x780000,0x3c00000,0x1e000000,0x780000,0x3c00000,0x1e000000,0xf0000f00,0xf003c00,
3726       0x3c03c003,0xc01e001e,0xf000f0,0x7800780,0x3c003c00,0x1f8000f,0xe00f003c,0x7c01e0,0x3e00f00,0x1f007800,0xf8001ef8,0x1f000f,
3727       0x7be00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3728       0x0,0x0,0xf,0x3c00000,0x307c0003,0x8038f000,0xfc000000,0x1e00,0x3c00,0x0,0x1e0000,0xfc0000,0x0,0x7e00003c,0x780,0xf00001e,
3729       0x7e00,0xf,0x80000780,0x3c0,0x3e001e00,0x3c0001f,0x7c000,0x780007,0xe000003f,0x0,0xfe000000,0xfe0000,0x0,0x3c07c3f0,0xf0001e03,
3730       0xc000f80f,0x800001e0,0x1f003c0,0x1e00,0xf000,0x781e0007,0x80007800,0x4000f80,0x3c003c00,0x7800001e,0xf078,0x1fe01f0,0x1f00780,
3731       0x7c00,0x7c078001,0xf000001f,0xf000,0xf0003c0,0x1e78001,0xfc07f007,0xc00f8000,0x780000f8,0x3c,0x1e,0x1e0,0x0,0x0,0x0,0xf0007c01,
3732       0xf000f007,0xc00000f0,0xf80780,0x3c,0x1f003,0xf0078007,0x80003c00,0xf000,0x7807c00,0x1e0000f,0x3c0f01e,0x1e01e0,0x1e007c0,
3733       0x3c07800,0x7c003c00,0x1e,0x3c000,0x3c007c0,0x1e78000,0xfe0fe001,0xf07c0001,0xef00007c,0x1e,0xf0,0x780,0x0,0x0,0x1e00000,
3734       0x7cfc000,0xfc00000,0x3c00000f,0xc3f00000,0x18,0x7fff,0xc0000000,0x406303,0x3e0,0x3c00001,0xf00f0000,0x7cfc000f,0x8000001f,
3735       0x3f0000f9,0xf8000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x780700f8,0x7cfc,0x7c000,0x1e000,0x0,0x780000,0xf8180000,
3736       0xf0000070,0x3c0007,0x8000003c,0x3f,0x80000000,0x3c0078,0x0,0x780f00,0x0,0x1e,0x3c0000,0x0,0x0,0x0,0x0,0x0,0x3e007c0,0xe1c00,
3737       0x0,0x0,0x0,0xf01,0xe0000071,0xc0000000,0xe0001c70,0x1e00000,0x1e0003c0,0xf0001e07,0x8000f03c,0x781e0,0x3c0f00,0x1e0f007,
3738       0x8000f800,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x1f00f003,0xfc03e003,0xe01f001f,
3739       0xf800f8,0x7c007c0,0x3e003e01,0xf000f80f,0xf00f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e0000,0xf0003c,0x1e000f80,0xf0007c07,
3740       0x8003e03c,0x1f01e0,0xf80f00,0x7c1e01e,0x7c00,0xf0000,0x780000,0x3c00000,0x1e000000,0x780000,0x3c00000,0x1e000000,0xf0000f00,
3741       0xf003c00,0x3c03c003,0xc01e001e,0xf000f0,0x7800780,0x3c003c00,0x1f8000f,0xc00f003c,0x7c01e0,0x3e00f00,0x1f007800,0xf8001ef0,
3742       0x1f000f,0x7bc00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3743       0x0,0x0,0x0,0x0,0x780000,0xf,0x3800040,0x30780003,0x8038f800,0x78000000,0x1e00,0x3c00,0x0,0x1e0000,0xfc0000,0x0,0x7e000078,
3744       0x780,0x1f00001e,0xfc00,0x20001f,0x780,0x80007c0,0x1f001e00,0x7c0000f,0x78000,0xf80007,0xe000003f,0x0,0x1e000000,0xf00000,
3745       0x3c000,0x3c03fff0,0xf0001e03,0xc001f007,0x800101e0,0x7e003c0,0x1e00,0x7800,0x781e0007,0x80007800,0x6000f00,0x3c003e00,0x7800001e,
3746       0xf078,0x1fe00f0,0x1e00780,0x3c00,0x78078000,0xf020001e,0xf000,0x7800780,0xff0001,0xfc07f00f,0x8007c000,0x780001f0,0x3c,0xf,
3747       0x1e0,0x0,0x0,0x0,0xf800fc01,0xf801f007,0xc00100f8,0x1f807c0,0x40003c,0xf807,0xf0078007,0x80003c00,0xf000,0x7803e00,0x1f0000f,
3748       0x3c0f01e,0x1e01f0,0x3e007e0,0x7c07c00,0xfc003c00,0x1e,0x3e000,0x3e007c0,0x1ff8000,0xfe0fe003,0xe03e0001,0xff0000fc,0x1e,
3749       0xf0,0x780,0x0,0x0,0x1f00080,0x3cf8000,0xfc00000,0x3c00001f,0x83f00000,0x18,0xc0,0x0,0xc06203,0x40003c0,0x1c00000,0xf80f0000,
3750       0x3cf8001f,0xf,0x3e000079,0xf0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x700780fc,0x3cf8,0xfc000,0x1e000,0x0,0x780000,
3751       0x7c180000,0xf0000020,0x100007,0x8000003c,0xf,0x80000000,0x1f01f0,0x0,0x380700,0x0,0xf,0x80f80000,0x0,0x0,0x0,0x0,0x0,0x3e007c0,
3752       0xe1c00,0x0,0x0,0x0,0xe01,0xc0000071,0xc0000001,0xc0001c70,0x1e00040,0x1e0003c0,0xf0001e07,0x8000f03c,0x781e0,0x3c0f00,0x1e0f007,
3753       0x80007800,0x10078000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x7e00f003,0xfc01e003,0xc00f001e,
3754       0x7800f0,0x3c00780,0x1e003c00,0xe000700f,0x800f0078,0x7803c0,0x3c01e00,0x1e00f000,0xf0000780,0x1e0000,0xf0003c,0x1f001f80,
3755       0xf800fc07,0xc007e03e,0x3f01f0,0x1f80f80,0xfc1e01f,0x7c00,0x100f8000,0x807c0004,0x3e00020,0x1f000100,0x780000,0x3c00000,0x1e000000,
3756       0xf0000f80,0x1f003c00,0x3c03e007,0xc01f003e,0xf801f0,0x7c00f80,0x3e007c00,0x1f8000f,0x801f003e,0x7c01f0,0x3e00f80,0x1f007c00,
3757       0xf8001ff0,0x1f801f,0x7fc00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3758       0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0xf,0x7800078,0x31f80001,0xc070fc00,0xfc000000,0x1e00,0x7c00,0x0,0x1e0000,0xfc0000,0x0,0x7e000078,
3759       0x7c0,0x1f00001e,0x1f000,0x38003f,0x780,0xe000f80,0x1f803e00,0x780000f,0x800f8000,0x1f00007,0xe000003f,0x0,0x2000000,0x800000,
3760       0x3c000,0x3e01ff71,0xf0001f03,0xc007f007,0xc00301e0,0x1fc003c0,0x1e00,0x7c00,0x781e0007,0x80007800,0x7801f00,0x3c001f00,0x7800001e,
3761       0xf078,0xfe00f8,0x3e00780,0x3e00,0xf8078000,0xf838003e,0xf000,0x7c00f80,0xff0000,0xfc07e00f,0x8003c000,0x780001e0,0x3c,0xf,
3762       0x1e0,0x0,0x0,0x0,0xf801fc01,0xfc03e003,0xe003007c,0x3f803e0,0x1c0003c,0xfc0f,0xf0078007,0x80003c00,0xf000,0x7801f00,0xf8000f,
3763       0x3c0f01e,0x1e00f8,0x7c007f0,0xf803e01,0xfc003c00,0x8003e,0x1f000,0x1e00fc0,0xff0000,0xfe0fe007,0xc01f0000,0xfe0000f8,0x1e,
3764       0xf0,0x780,0x0,0x0,0xf80180,0x1cf0000,0x1f800000,0x3c00001f,0x83e00000,0x18,0xc0,0x0,0xc06203,0x70007c0,0xe00000,0x7e0f0000,
3765       0x1cf0001e,0x7,0x3c000039,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100,0x7c00000,0xe00780fc,0x2001cf0,0xf8000,0x1e000,0x0,
3766       0x780000,0x7e182000,0xf0000000,0x7,0x8000003c,0x7,0xc0000000,0x7ffc0,0x0,0x180300,0x0,0x3,0xffe00000,0x0,0x0,0x0,0x0,0x0,
3767       0x3f00fc0,0xe1c00,0x0,0x0,0x0,0xc01,0x800000e1,0xc0000003,0xc0003870,0x1f001c0,0x3e0003e1,0xf0001f0f,0x8000f87c,0x7c3e0,0x3e1f00,
3768       0x1f1e007,0x80007c00,0x30078000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e03,0xfc00f001,0xfc01f007,
3769       0xc00f803e,0x7c01f0,0x3e00f80,0x1f007c00,0x4000201f,0xc01f007c,0xf803e0,0x7c01f00,0x3e00f801,0xf0000780,0x1e0000,0xf0007c,
3770       0x1f003f80,0xf801fc07,0xc00fe03e,0x7f01f0,0x3f80f80,0x1fc1f03f,0x803e00,0x3007c003,0x803e001c,0x1f000e0,0xf800700,0x780000,
3771       0x3c00000,0x1e000000,0xf00007c0,0x3e003c00,0x3c01f00f,0x800f807c,0x7c03e0,0x3e01f00,0x1f00f800,0x1f80007,0xc03e001e,0xfc00f0,
3772       0x7e00780,0x3f003c01,0xf8000fe0,0x1fc03e,0x3f800,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3773       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0x780007f,0xfff00001,0xe0f07f03,0xfe000000,0xf00,0x7800,0x0,
3774       0x1e0000,0xfc0000,0x0,0x7e0000f0,0x3f0,0x7e000fff,0xfc03ffff,0xf83f00fe,0x780,0xfc03f80,0xfc0fc00,0xf800007,0xe03f0018,0x7e00007,
3775       0xe000003f,0x0,0x0,0x0,0x3c000,0x1e007c71,0xe0000f03,0xffffe003,0xf01f01ff,0xff8003ff,0xffe01e00,0x3f01,0xf81e0007,0x803ffff0,
3776       0x7e03f00,0x3c000f00,0x7ffffe1e,0xf078,0xfe007e,0xfc00780,0x1f83,0xf0078000,0x783f00fe,0xf000,0x3f03f00,0xff0000,0xfc07e01f,
3777       0x3e000,0x780003ff,0xfffc003c,0x7,0x800001e0,0x0,0x0,0x0,0x7e07fc01,0xfe07e001,0xf80f007e,0x7f801f8,0xfc0003c,0x7ffe,0xf0078007,
3778       0x807ffffe,0xf000,0x7801f00,0xfff00f,0x3c0f01e,0x1e00fc,0xfc007f8,0x1f803f03,0xfc003c00,0xf80fc,0x1fff0,0x1f83fc0,0xff0000,
3779       0xfc07e007,0xc01f0000,0xfe0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0xfe0780,0xfe0000,0x1f000000,0x3c00001f,0x7c00e03,0x81c00018,
3780       0xc0,0x0,0x406203,0x7e01fc0,0x700000,0x7fffff80,0xfe0003f,0xffffc003,0xf800001f,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f0,
3781       0x1f800001,0xc007c1fe,0x6000fe0,0x1ffffe,0x1e000,0x0,0x780000,0x3f98e03f,0xffff8000,0x7,0x8000003c,0x7,0xc0000000,0xfe00,
3782       0x0,0x80100,0x0,0x0,0x7f000000,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3f83fe8,0xe1c00,0x0,0x0,0x0,0x801,0xc1,0xc0000007,0x80003070,
3783       0xfc0fc0,0x3c0001e1,0xe0000f0f,0x7878,0x3c3c0,0x1e1e00,0xf1e007,0xffc03f01,0xf007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,
3784       0xffff001f,0xfff800ff,0xffc01fff,0xf800f001,0xfc00fc1f,0x8007e0fc,0x3f07e0,0x1f83f00,0xfc1f800,0x1f,0xf07e003f,0x3f001f8,
3785       0x1f800fc0,0xfc007e07,0xe0000780,0x1e0000,0xf301f8,0xfc0ff80,0x7e07fc03,0xf03fe01f,0x81ff00fc,0xff807e0,0x7fc0f87f,0x81801f80,
3786       0xf003f01f,0x801f80fc,0xfc07e0,0x7e03f00,0xfffffc07,0xffffe03f,0xffff01ff,0xfff807e0,0x7e003c00,0x3c01f81f,0x800fc0fc,0x7e07e0,
3787       0x3f03f00,0x1f81f800,0x1f8000f,0xe07e001f,0x83fc00fc,0x1fe007e0,0xff003f07,0xf8000fe0,0x1fe07e,0x3f800,0x0,0x0,0x0,0x0,0x0,
3788       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0x780007f,
3789       0xffe00000,0xffe03fff,0xdf000000,0xf00,0x7800,0x0,0x0,0xfc0000,0x0,0x7e0000f0,0x1ff,0xfc000fff,0xfc03ffff,0xf83ffffc,0x780,
3790       0xfffff00,0x7fff800,0xf000007,0xffff001f,0xffe00007,0xe000003f,0x0,0x0,0x0,0x3c000,0x1e000001,0xe0000f03,0xffffc001,0xffff01ff,
3791       0xff0003ff,0xffe01e00,0x1fff,0xf81e0007,0x803ffff0,0x7fffe00,0x3c000f80,0x7ffffe1e,0xf078,0xfe003f,0xff800780,0xfff,0xf0078000,
3792       0x7c3ffffc,0xf000,0x3ffff00,0xff0000,0xf803e01e,0x1e000,0x780003ff,0xfffc003c,0x7,0x800001e0,0x0,0x0,0x0,0x7fffbc01,0xffffc000,
3793       0xffff003f,0xfff800ff,0xffc0003c,0x3ffe,0xf0078007,0x807ffffe,0xf000,0x7800f80,0x7ff00f,0x3c0f01e,0x1e007f,0xff8007ff,0xff001fff,
3794       0xbc003c00,0xffffc,0x1fff0,0x1fffbc0,0xff0000,0x7c07c00f,0x800f8000,0x7e0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x7fff80,0x7c0000,
3795       0x1f000000,0x3c00001e,0x7c00f07,0xc1e00018,0xc0,0x0,0x60e303,0x7ffff80,0x380000,0x3fffff80,0x7c0003f,0xffffc001,0xf000000f,
3796       0x80000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0xff800003,0x8003ffff,0xfe0007c0,0x1ffffe,0x1e000,0x0,0x780000,0x1fffe03f,0xffff8000,
3797       0x7,0x8000003c,0x3,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3fffdf8,0xe1c00,0x0,0x0,0x0,0x0,0x1c1,
3798       0xc000000f,0x7070,0x7fffc0,0x3c0001e1,0xe0000f0f,0x7878,0x3c3c0,0x1e1e00,0xf1e007,0xffc01fff,0xf007ffff,0xc03ffffe,0x1fffff0,
3799       0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xf000f001,0xfc007fff,0x3fff8,0x1fffc0,0xfffe00,0x7fff000,0x3b,0xfffc003f,
3800       0xfff001ff,0xff800fff,0xfc007fff,0xe0000780,0x1e0000,0xf3fff8,0xffff780,0x7fffbc03,0xfffde01f,0xffef00ff,0xff7807ff,0xfbc0ffff,
3801       0xff800fff,0xf001ffff,0x800ffffc,0x7fffe0,0x3ffff00,0xfffffc07,0xffffe03f,0xffff01ff,0xfff803ff,0xfc003c00,0x3c00ffff,0x7fff8,
3802       0x3fffc0,0x1fffe00,0xffff000,0x1f,0xfffc001f,0xffbc00ff,0xfde007ff,0xef003fff,0x780007e0,0x1ffffc,0x1f800,0x0,0x0,0x0,0x0,
3803       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0x700003f,
3804       0xffc00000,0x7fc01fff,0x9f800000,0xf80,0xf800,0x0,0x0,0xfc0000,0x0,0x7e0000f0,0xff,0xf8000fff,0xfc03ffff,0xf83ffff8,0x780,
3805       0xffffe00,0x7fff000,0xf000003,0xfffe001f,0xffc00007,0xe000003f,0x0,0x0,0x0,0x3c000,0xf000003,0xe0000f83,0xffff0000,0xffff01ff,
3806       0xfc0003ff,0xffe01e00,0xfff,0xf01e0007,0x803ffff0,0x7fffc00,0x3c0007c0,0x7ffffe1e,0xf078,0x7e003f,0xff000780,0x7ff,0xe0078000,
3807       0x3c3ffff8,0xf000,0x1fffe00,0x7e0000,0xf803e03e,0x1f000,0x780003ff,0xfffc003c,0x7,0x800001e0,0x0,0x0,0x0,0x3fff3c01,0xefff8000,
3808       0x7ffe001f,0xff78007f,0xff80003c,0x1ffc,0xf0078007,0x807ffffe,0xf000,0x78007c0,0x3ff00f,0x3c0f01e,0x1e003f,0xff0007bf,0xfe000fff,
3809       0xbc003c00,0xffff8,0xfff0,0xfff3c0,0x7e0000,0x7c07c01f,0x7c000,0x7c0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x3fff80,0x380000,
3810       0x3e000000,0x7c00003e,0x7801f07,0xc1e00018,0xc0,0x0,0x39c1ce,0x7ffff00,0x1c0000,0xfffff80,0x380003f,0xffffc000,0xe0000007,
3811       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0xff000007,0x1ffcf,0xfe000380,0x1ffffe,0x1e000,0x0,0x780000,0xfffe03f,0xffff8000,0x7,
3812       0x8000003c,0x3,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3dffdf8,0xe1c00,0x0,0x0,0x0,0x0,0x381,
3813       0xc000001e,0xe070,0x7fff80,0x7c0001f3,0xe0000f9f,0x7cf8,0x3e7c0,0x1f3e00,0xfbe007,0xffc00fff,0xf007ffff,0xc03ffffe,0x1fffff0,
3814       0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xc000f000,0xfc007ffe,0x3fff0,0x1fff80,0xfffc00,0x7ffe000,0x79,0xfff8001f,
3815       0xffe000ff,0xff0007ff,0xf8003fff,0xc0000780,0x1e0000,0xf3fff0,0x7ffe780,0x3fff3c01,0xfff9e00f,0xffcf007f,0xfe7803ff,0xf3c07ff3,
3816       0xff8007ff,0xe000ffff,0x7fff8,0x3fffc0,0x1fffe00,0xfffffc07,0xffffe03f,0xffff01ff,0xfff801ff,0xf8003c00,0x3c007ffe,0x3fff0,
3817       0x1fff80,0xfffc00,0x7ffe000,0x1d,0xfff8000f,0xff3c007f,0xf9e003ff,0xcf001ffe,0x780007c0,0x1efff8,0x1f000,0x0,0x0,0x0,0x0,
3818       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0xf000003,
3819       0xfe000000,0x1f000fff,0xfc00000,0x780,0xf000,0x0,0x0,0xf80000,0x0,0x7e0001e0,0x7f,0xf0000fff,0xfc03ffff,0xf81ffff0,0x780,
3820       0x7fff800,0x1ffe000,0x1f000000,0xfff8001f,0xff000007,0xe000003e,0x0,0x0,0x0,0x3c000,0xf800003,0xc0000783,0xfff80000,0x3ffe01ff,
3821       0xe00003ff,0xffe01e00,0x7ff,0xc01e0007,0x803ffff0,0x3fff800,0x3c0003c0,0x7ffffe1e,0xf078,0x7e000f,0xfe000780,0x3ff,0xc0078000,
3822       0x3e1fffe0,0xf000,0x7ff800,0x7e0000,0xf803e07c,0xf800,0x780003ff,0xfffc003c,0x3,0xc00001e0,0x0,0x0,0x0,0xffe3c01,0xe7ff0000,
3823       0x3ffc000f,0xfe78003f,0xfe00003c,0x7f0,0xf0078007,0x807ffffe,0xf000,0x78003e0,0xff00f,0x3c0f01e,0x1e001f,0xfe00079f,0xfc0007ff,
3824       0x3c003c00,0x7ffe0,0x1ff0,0x7fe3c0,0x7e0000,0x7c07c03e,0x3e000,0x7c0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0xfff00,0x100000,
3825       0x3e000000,0x7800003c,0xf800f07,0xc1e00018,0xc0,0x0,0x1f80fc,0x3fffc00,0xc0000,0x3ffff80,0x100003f,0xffffc000,0x40000002,
3826       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xfc000006,0xff87,0xfc000100,0x1ffffe,0x1e000,0x0,0x780000,0x3ffc03f,0xffff8000,0x7,
3827       0x8000003c,0x3,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3dff9f8,0xe1c00,0x0,0x0,0x0,0x0,0x3ff,
3828       0xf800003c,0xfffe,0x1ffe00,0x780000f3,0xc000079e,0x3cf0,0x1e780,0xf3c00,0x7bc007,0xffc003ff,0xe007ffff,0xc03ffffe,0x1fffff0,
3829       0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01ffc,0xf000,0xfc001ffc,0xffe0,0x7ff00,0x3ff800,0x1ffc000,0x70,0xfff00007,
3830       0xff80003f,0xfc0001ff,0xe0000fff,0x780,0x1e0000,0xf3ffe0,0x1ffc780,0xffe3c00,0x7ff1e003,0xff8f001f,0xfc7800ff,0xe3c03fe1,
3831       0xff0003ff,0xc0007ffc,0x3ffe0,0x1fff00,0xfff800,0xfffffc07,0xffffe03f,0xffff01ff,0xfff800ff,0xf0003c00,0x3c003ffc,0x1ffe0,
3832       0xfff00,0x7ff800,0x3ffc000,0x38,0xfff00007,0xfe3c003f,0xf1e001ff,0x8f000ffc,0x780007c0,0x1e7ff0,0x1f000,0x0,0x0,0x0,0x0,0x0,
3833       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,
3834       0x1fc,0x0,0x780,0xf000,0x0,0x0,0x1f80000,0x0,0x1e0,0x1f,0xc0000000,0x0,0x1ff80,0x0,0xffc000,0x7f8000,0x0,0x3fe00007,0xfc000000,
3835       0x7e,0x0,0x0,0x0,0x0,0x7c00000,0x0,0x0,0xff00000,0x0,0x0,0xfe,0x0,0x0,0x3fc000,0x0,0x0,0x0,0x3,0xf8000000,0xff,0xc0000000,
3836       0x1ff00,0x0,0x1fe000,0x0,0x0,0x0,0x0,0x3c,0x3,0xc00001e0,0x0,0x0,0x0,0x3f80000,0x1fc0000,0x7f00003,0xf8000007,0xf0000000,
3837       0x0,0xf0000000,0x0,0xf000,0x0,0x0,0x0,0x7,0xf8000787,0xf00001fc,0x3c000000,0x7f80,0x0,0x1f8000,0x0,0x0,0x0,0x7c000000,0x1e,
3838       0xf0,0x780,0x0,0x0,0x3fc00,0x0,0x3c000000,0x7800003c,0xf000601,0xc00018,0xc0,0x0,0x0,0x3fe000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3839       0x0,0x0,0x0,0x0,0x0,0x0,0xf,0xf0000000,0x7e03,0xf0000000,0x0,0x0,0x0,0x0,0xfe0000,0x0,0x0,0x3c,0x2007,0x80000000,0x0,0x0,
3840       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c7e0f0,0xe1c00,0x0,0x3800000,0x0,0x0,0x3ff,0xf8000078,0xfffe,0x7f800,0x0,0x0,0x0,0x0,
3841       0x0,0x0,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f0,0x3f80,0x1fc00,0xfe000,0x7f0000,0x70,0x3fc00001,0xfe00000f,0xf000007f,
3842       0x800003fc,0x0,0x0,0xff00,0x7f0000,0x3f80000,0x1fc00000,0xfe000007,0xf000003f,0x80001f80,0xfc00007f,0xfe0,0x7f00,0x3f800,
3843       0x1fc000,0x0,0x0,0x0,0x3f,0xc0000000,0xff0,0x7f80,0x3fc00,0x1fe000,0xff0000,0x78,0x3fc00001,0xf800000f,0xc000007e,0x3f0,0x7c0,
3844       0x1e1fc0,0x1f000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3845       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0x3c0,0x1e000,0x0,0x0,0x1f00000,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3846       0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0x3e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xe0000000,0x0,0x0,0x0,
3847       0x0,0x0,0x0,0x0,0x3c,0x1,0xe00001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0000000,0x0,0xf000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,
3848       0x0,0x0,0x0,0x0,0x0,0x0,0x78000000,0x1e,0xf0,0x780,0x0,0x0,0x0,0x0,0x3c000000,0x78000078,0xf000000,0x18,0xc0,0x0,0x0,0x0,
3849       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x3c0f,0x80000000,
3850       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0xe1c00,0x0,0x1800000,0x0,0x0,0x3ff,0xf80000f0,0xfffe,0x0,0x0,0x0,0x0,
3851       0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3852       0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x780,0x1e0000,0x1e000,0x0,0x0,0x0,
3853       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,
3854       0x0,0x0,0x3c0,0x1e000,0x0,0x0,0x1f00000,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0x1f80000,
3855       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xf0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x1,0xe00001e0,0x0,
3856       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xe0000000,0x0,0xf000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x0,0xf8000000,
3857       0x1f,0xf0,0xf80,0x0,0x0,0x0,0x0,0x78000000,0xf8000078,0x1e000000,0x8,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3858       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x3fff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3859       0x0,0x3c00000,0xe1c00,0x0,0x1c00000,0x0,0x0,0x1,0xc00001e0,0x70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3860       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3861       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80,0x1e0000,0x3e000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3862       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0x1e0,0x3c000,0x0,0x0,0x1f00000,
3863       0x0,0x780,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0xfe0100,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3864       0x0,0x0,0x0,0x0,0xf8000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf0000000,0xf0007fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xe0000000,
3865       0x0,0xf000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x0,0xf0000000,0x1f,0x800000f0,0x1f80,0x0,0x0,0x0,0x0,
3866       0x78000000,0xf0000070,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3867       0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x3ffe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0xe1c00,0x0,0xe00000,
3868       0x0,0x0,0x1,0xc00003ff,0xe0000070,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3869       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3870       0x0,0x0,0x0,0xf00,0x1e0000,0x3c000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3871       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0x1e0,0x7c000,0x0,0x0,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3872       0x0,0x0,0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x0,0x7fff80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78000000,
3873       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf0000000,0x7fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4003,0xe0000000,0x0,0x1f000,0x0,0x0,
3874       0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x1,0xf0000000,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x0,0x0,0x70000001,0xf00000e0,
3875       0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,
3876       0x0,0x0,0x3c,0xff8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0xe1c00,0x0,0xe00000,0x0,0x0,0x1,0xc00003ff,
3877       0xe0000070,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3878       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f00,0x1e0000,
3879       0x7c000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3880       0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0xf0,0x78000,0x0,0x0,0x3e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x0,
3881       0x0,0x0,0x0,0x1fff80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,
3882       0xf0000000,0x7fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780f,0xc0000000,0x0,0x3e000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,
3883       0x0,0x0,0x0,0x0,0x3,0xe0000000,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x0,0x0,0xf0000103,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3884       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,0x0,0x0,
3885       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x21e00000,0x0,0x0,0x1,0xc00003ff,0xe0000070,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10f,
3886       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10f,0x0,
3887       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e00,0x1e0000,0xf8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3888       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,
3889       0xf8,0xf8000,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x0,0x0,0x0,0x0,0x1fe00,0x0,0x0,0x0,0x0,
3890       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf0000000,0x7fe0,0x0,0x0,0x0,0x0,0x0,0x0,
3891       0x0,0x0,0x7fff,0xc0000000,0x0,0x3ffe000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x7f,0xe0000000,0x7,0xfc0000f0,
3892       0x3fe00,0x0,0x0,0x0,0x0,0x600001ff,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3893       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,
3894       0x3fe00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3895       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3896       0x0,0x0,0x0,0x0,0x7fe00,0x1e0000,0x1ff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3897       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3898       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3899       0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x7fff,0x80000000,0x0,0x3ffc000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,
3900       0x0,0x0,0x0,0x0,0x7f,0xc0000000,0x0,0xfc0000f0,0x3f000,0x0,0x0,0x0,0x0,0x1ff,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3901       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3902       0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x3fc00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fe,0x0,0x0,0x0,0x0,0x0,
3903       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fe,0x0,0x0,0x0,0x0,0x0,0x0,
3904       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7fc00,0x1e0000,0x1ff0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3905       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3906       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3907       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x3ffe,0x0,0x0,0x3ff8000,0x0,0x0,0x0,
3908       0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x7f,0x80000000,0x0,0xf0,0x0,0x0,0x0,0x0,0x0,0x1ff,0x80000000,0x0,0x0,0x0,0x0,
3909       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3910       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x3f800000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fc,0x0,
3911       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fc,0x0,0x0,
3912       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f800,0x1e0000,0x1fe0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3913       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3914       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3915       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f8,0x0,0x0,0x3fe0000,
3916       0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x7e,0x0,0x0,0xf0,0x0,0x0,0x0,0x0,0x0,0xfe,0x0,0x0,0x0,0x0,0x0,0x0,
3917       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3918       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3919       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3920       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e000,0x1e0000,0x1f80000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3921       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3922       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3923       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3924       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3925       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3926       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3927       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3928       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3929       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3930       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3931       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x0,0x0,0x0,
3932       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3933       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3934       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3935       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 };
3936 
3937     // Define a 40x38 'danger' color logo (used by cimg::dialog()).
3938     const unsigned char logo40x38[4576] = {
3939       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,
3940       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,
3941       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,
3942       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,
3943       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,
3944       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,
3945       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,
3946       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,
3947       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,
3948       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,
3949       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,
3950       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,
3951       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,
3952       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,
3953       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,255,
3954       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,123,
3955       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,1,189,
3956       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,255,255,
3957       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,1,189,189,
3958       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,255,255,0,1,
3959       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,123,0,26,255,
3960       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,0,4,123,123,
3961       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,123,123,123,86,
3962       200,200,200,91,49,124,118,124,71,32,124,95,49,56,114,52,82,121,0};
3963 
3964     //! Get/set default output stream for the \CImg library messages.
3965     /**
3966        \param file Desired output stream. Set to \c 0 to get the currently used output stream only.
3967        \return Currently used output stream.
3968     **/
3969     inline std::FILE* output(std::FILE *file) {
3970       static std::FILE *res = stderr;
3971       if (file) res = file;
3972       return res;
3973     }
3974 
3975     //! Display a warning message on the default output stream.
3976     /**
3977        \param format C-string containing the format of the message, as with <tt>std::printf()</tt>.
3978        \note If configuration macro \c cimg_strict_warnings is set, this function throws a \c CImgWarningException instead.
3979        \warning As the first argument is a format string, it is highly recommended to write
3980        \code
3981        cimg::warn("%s",warning_message);
3982        \endcode
3983        instead of
3984        \code
3985        cimg::warn(warning_message);
3986        \endcode
3987        if \c warning_message can be arbitrary, to prevent nasty memory access.
3988     **/
3989     inline void warn(const char *const format, ...) {
3990       if (cimg::exception_mode()>=1) {
3991         char message[16384] = { 0 };
3992         std::va_list ap;
3993         va_start(ap,format);
3994         cimg_vsnprintf(message,sizeof(message),format,ap);
3995         va_end(ap);
3996 #ifdef cimg_strict_warnings
3997         throw CImgWarningException(message);
3998 #else
3999         std::fprintf(cimg::output(),"\n%s[CImg] *** Warning ***%s%s",cimg::t_red,cimg::t_normal,message);
4000 #endif
4001       }
4002     }
4003 
4004     // Execute an external system command.
4005     /**
4006        \param command C-string containing the command line to execute.
4007        \param module_name Module name.
4008        \return Status value of the executed command, whose meaning is OS-dependent.
4009        \note This function is similar to <tt>std::system()</tt>
4010        but it does not open an extra console windows
4011        on Windows-based systems.
4012     **/
4013     inline int system(const char *const command, const char *const module_name=0) {
4014 #if cimg_OS==2
4015       PROCESS_INFORMATION pi;
4016       STARTUPINFO si;
4017       std::memset(&pi,0,sizeof(PROCESS_INFORMATION));
4018       std::memset(&si,0,sizeof(STARTUPINFO));
4019       GetStartupInfo(&si);
4020       si.cb = sizeof(si);
4021       si.wShowWindow = SW_HIDE;
4022       si.dwFlags |= SW_HIDE | STARTF_USESHOWWINDOW;
4023       const BOOL res = CreateProcess((LPCTSTR)module_name,(LPTSTR)command,0,0,FALSE,0,0,0,&si,&pi);
4024       if (res) {
4025         WaitForSingleObject(pi.hProcess, INFINITE);
4026         CloseHandle(pi.hThread);
4027         CloseHandle(pi.hProcess);
4028         return 0;
4029       } else
4030 #endif
4031         return std::system(command);
4032       return module_name?0:1;
4033     }
4034 
4035     //! Return a reference to a temporary variable of type T.
4036     template<typename T>
4037     inline T& temporary(const T&) {
4038       static T temp;
4039       return temp;
4040     }
4041 
4042     //! Exchange values of variables \c a and \c b.
4043     template<typename T>
4044     inline void swap(T& a, T& b) { T t = a; a = b; b = t; }
4045 
4046     //! Exchange values of variables (\c a1,\c a2) and (\c b1,\c b2).
4047     template<typename T1, typename T2>
4048     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2) {
4049       cimg::swap(a1,b1); cimg::swap(a2,b2);
4050     }
4051 
4052     //! Exchange values of variables (\c a1,\c a2,\c a3) and (\c b1,\c b2,\c b3).
4053     template<typename T1, typename T2, typename T3>
4054     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3) {
4055       cimg::swap(a1,b1,a2,b2); cimg::swap(a3,b3);
4056     }
4057 
4058     //! Exchange values of variables (\c a1,\c a2,...,\c a4) and (\c b1,\c b2,...,\c b4).
4059     template<typename T1, typename T2, typename T3, typename T4>
4060     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4) {
4061       cimg::swap(a1,b1,a2,b2,a3,b3); cimg::swap(a4,b4);
4062     }
4063 
4064     //! Exchange values of variables (\c a1,\c a2,...,\c a5) and (\c b1,\c b2,...,\c b5).
4065     template<typename T1, typename T2, typename T3, typename T4, typename T5>
4066     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5) {
4067       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4); cimg::swap(a5,b5);
4068     }
4069 
4070     //! Exchange values of variables (\c a1,\c a2,...,\c a6) and (\c b1,\c b2,...,\c b6).
4071     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
4072     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) {
4073       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5); cimg::swap(a6,b6);
4074     }
4075 
4076     //! Exchange values of variables (\c a1,\c a2,...,\c a7) and (\c b1,\c b2,...,\c b7).
4077     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
4078     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,
4079                      T7& a7, T7& b7) {
4080       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6); cimg::swap(a7,b7);
4081     }
4082 
4083     //! Exchange values of variables (\c a1,\c a2,...,\c a8) and (\c b1,\c b2,...,\c b8).
4084     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
4085     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,
4086                      T7& a7, T7& b7, T8& a8, T8& b8) {
4087       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6,a7,b7); cimg::swap(a8,b8);
4088     }
4089 
4090     //! Return the endianness of the current architecture.
4091     /**
4092        \return \c false for <i>Little Endian</i> or \c true for <i>Big Endian</i>.
4093     **/
4094     inline bool endianness() {
4095       const int x = 1;
4096       return ((unsigned char*)&x)[0]?false:true;
4097     }
4098 
4099     //! Reverse endianness of all elements in a memory buffer.
4100     /**
4101        \param[in,out] buffer Memory buffer whose endianness must be reversed.
4102        \param size Number of buffer elements to reverse.
4103     **/
4104     template<typename T>
4105     inline void invert_endianness(T* const buffer, const unsigned long size) {
4106       if (size) switch (sizeof(T)) {
4107       case 1 : break;
4108       case 2 : { for (unsigned short *ptr = (unsigned short*)buffer+size; ptr>(unsigned short*)buffer; ) {
4109         const unsigned short val = *(--ptr);
4110         *ptr = (unsigned short)((val>>8)|((val<<8)));
4111       }
4112       } break;
4113       case 4 : { for (unsigned int *ptr = (unsigned int*)buffer+size; ptr>(unsigned int*)buffer; ) {
4114         const unsigned int val = *(--ptr);
4115         *ptr = (val>>24)|((val>>8)&0xff00)|((val<<8)&0xff0000)|(val<<24);
4116       }
4117       } break;
4118       default : { for (T* ptr = buffer+size; ptr>buffer; ) {
4119         unsigned char *pb = (unsigned char*)(--ptr), *pe = pb + sizeof(T);
4120         for (int i = 0; i<(int)sizeof(T)/2; ++i) swap(*(pb++),*(--pe));
4121       }
4122       }
4123       }
4124     }
4125 
4126     //! Reverse endianness of a single variable.
4127     /**
4128        \param[in,out] a Variable to reverse.
4129        \return Reference to reversed variable.
4130     **/
4131     template<typename T>
4132     inline T& invert_endianness(T& a) {
4133       invert_endianness(&a,1);
4134       return a;
4135     }
4136 
4137     // Conversion functions to get more precision when trying to store unsigned ints values as floats.
4138     inline unsigned int float2uint(const float f) {
4139       int tmp = 0;
4140       std::memcpy(&tmp,&f,sizeof(float));
4141       if (tmp>=0) return (unsigned int)f;
4142       unsigned int u;
4143       std::memcpy(&u,&f,sizeof(float));  // use memcpy instead of assignment to avoid undesired optimizations by C++-compiler.
4144       return ((u)<<1)>>1; // set sign bit to 0.
4145     }
4146 
4147     inline float uint2float(const unsigned int u) {
4148       if (u<(1U<<19)) return (float)u;  // Consider safe storage of unsigned int as floats until 19bits (i.e 524287).
4149       float f;
4150       const unsigned int v = u|(1U<<(8*sizeof(unsigned int)-1)); // set sign bit to 1.
4151       std::memcpy(&f,&v,sizeof(float)); // use memcpy instead of simple assignment to avoid undesired optimizations by C++-compiler.
4152       return f;
4153     }
4154 
4155     //! Return the value of a system timer, with a millisecond precision.
4156     /**
4157        \note The timer does not necessarily starts from \c 0.
4158     **/
4159     inline unsigned long time() {
4160 #if cimg_OS==1
4161       struct timeval st_time;
4162       gettimeofday(&st_time,0);
4163       return (unsigned long)(st_time.tv_usec/1000 + st_time.tv_sec*1000);
4164 #elif cimg_OS==2
4165       SYSTEMTIME st_time;
4166       GetSystemTime(&st_time);
4167       return (unsigned long)(st_time.wMilliseconds + 1000*(st_time.wSecond + 60*(st_time.wMinute + 60*st_time.wHour)));
4168 #else
4169       return 0;
4170 #endif
4171     }
4172 
4173     // Implement a tic/toc mechanism to display elapsed time of algorithms.
4174     inline unsigned long tictoc(const bool is_tic) {
4175       static unsigned long t0 = 0;
4176       const unsigned long t = cimg::time();
4177       if (is_tic) return (t0 = t);
4178       const unsigned long dt = t>=t0?(t - t0):cimg::type<unsigned long>::max();
4179       const unsigned int
4180         edays = (unsigned int)(dt/86400000.0),
4181         ehours = (unsigned int)((dt - edays*86400000.0)/3600000.0),
4182         emin = (unsigned int)((dt - edays*86400000.0 - ehours*3600000.0)/60000.0),
4183         esec = (unsigned int)((dt - edays*86400000.0 - ehours*3600000.0 - emin*60000.0)/1000.0),
4184         ems = (unsigned int)(dt - edays*86400000.0 - ehours*3600000.0 - emin*60000.0 - esec*1000.0);
4185       if (!edays && !ehours && !emin && !esec)
4186         std::fprintf(cimg::output(),"%s[CImg] Elapsed time : %u ms%s\n",cimg::t_red,ems,cimg::t_normal);
4187       else {
4188         if (!edays && !ehours && !emin)
4189           std::fprintf(cimg::output(),"%s[CImg] Elapsed time : %u sec %u ms%s\n",cimg::t_red,esec,ems,cimg::t_normal);
4190         else {
4191           if (!edays && !ehours)
4192             std::fprintf(cimg::output(),"%s[CImg] Elapsed time : %u min %u sec %u ms%s\n",cimg::t_red,emin,esec,ems,cimg::t_normal);
4193           else{
4194             if (!edays)
4195               std::fprintf(cimg::output(),"%s[CImg] Elapsed time : %u hours %u min %u sec %u ms%s\n",cimg::t_red,ehours,emin,esec,ems,cimg::t_normal);
4196             else{
4197               std::fprintf(cimg::output(),"%s[CImg] Elapsed time : %u days %u hours %u min %u sec %u ms%s\n",cimg::t_red,edays,ehours,emin,esec,ems,cimg::t_normal);
4198             }
4199           }
4200         }
4201       }
4202       return t;
4203     }
4204 
4205     //! Start tic/toc timer for time measurement between code instructions.
4206     /**
4207        \return Current value of the timer (same value as time()).
4208     **/
4209     inline unsigned long tic() {
4210       return cimg::tictoc(true);
4211     }
4212 
4213     //! End tic/toc timer and displays elapsed time from last call to tic().
4214     /**
4215        \return Time elapsed (in ms) since last call to tic().
4216     **/
4217     inline unsigned long toc() {
4218       return cimg::tictoc(false);
4219     }
4220 
4221     //! Sleep for a given numbers of milliseconds.
4222     /**
4223        \param milliseconds Number of milliseconds to wait for.
4224        \note This function frees the CPU ressources during the sleeping time.
4225        It can be used to temporize your program properly, without wasting CPU time.
4226     **/
4227     inline void sleep(const unsigned int milliseconds) {
4228 #if cimg_OS==1
4229       struct timespec tv;
4230       tv.tv_sec = milliseconds/1000;
4231       tv.tv_nsec = (milliseconds%1000)*1000000;
4232       nanosleep(&tv,0);
4233 #elif cimg_OS==2
4234       Sleep(milliseconds);
4235 #endif
4236     }
4237 
4238     inline unsigned int _wait(const unsigned int milliseconds, unsigned long& timer) {
4239       if (!timer) timer = cimg::time();
4240       const unsigned long current_time = cimg::time();
4241       if (current_time>=timer+milliseconds) { timer = current_time; return 0; }
4242       const unsigned long time_diff = timer + milliseconds - current_time;
4243       timer = current_time + time_diff;
4244       cimg::sleep(time_diff);
4245       return (unsigned int)time_diff;
4246     }
4247 
4248     //! Wait for a given number of milliseconds since the last call to wait().
4249     /**
4250        \param milliseconds Number of milliseconds to wait for.
4251        \return Number of milliseconds elapsed since the last call to wait().
4252        \note Same as sleep() with a waiting time computed with regard to the last call
4253        of wait(). It may be used to temporize your program properly, without wasting CPU time.
4254     **/
4255     inline unsigned int wait(const unsigned int milliseconds) {
4256       static unsigned long timer = 0;
4257       if (!timer) timer = cimg::time();
4258       return _wait(milliseconds,timer);
4259     }
4260 
4261     // Random number generators.
4262     // CImg may use its own Random Number Generator (RNG) if configuration macro 'cimg_use_rng' is set.
4263     // Use it for instance when you have to deal with concurrent threads trying to call std::srand()
4264     // at the same time !
4265 #ifdef cimg_use_rng
4266 
4267     // Use a custom RNG.
4268     inline unsigned int _rand(const unsigned long seed=0, const bool set_seed=false) {
4269       static unsigned long next = 1;
4270       if (set_seed) next = seed;
4271       next = next*1103515245 + 12345;
4272       return (unsigned int)((next>>16)&0x7FFF);
4273     }
4274 
4275     inline void srand() {
4276       static bool is_first_time = true;
4277       if (is_first_time) {
4278         const unsigned long t = cimg::time();
4279 #if cimg_OS==1
4280         cimg::_rand(t+(unsigned long)getpid(),true);
4281 #elif cimg_OS==2
4282         cimg::_rand(t+(unsigned long)_getpid(),true);
4283 #else
4284         cimg::_rand(t,true);
4285 #endif
4286         is_first_time = false;
4287       }
4288     }
4289 
4290     inline double rand() {
4291       cimg::srand();
4292       return cimg::_rand()/32767.;
4293     }
4294 
4295 #else
4296 
4297     // Use the system RNG.
4298     inline void srand() {
4299       static bool is_first_time = true;
4300       if (is_first_time) {
4301         const unsigned int t = (unsigned int)cimg::time();
4302 #if cimg_OS==1
4303         std::srand(t+(unsigned int)getpid());
4304 #elif cimg_OS==2
4305         std::srand(t+(unsigned int)_getpid());
4306 #else
4307         std::srand(t);
4308 #endif
4309         is_first_time = false;
4310       }
4311     }
4312 
4313     //! Return a random variable between [0,1] with respect to an uniform distribution.
4314     /**
4315     **/
4316     inline double rand() {
4317       cimg::srand();
4318       return (double)std::rand()/RAND_MAX;
4319     }
4320 #endif
4321 
4322     //! Return a random variable between [-1,1] with respect to an uniform distribution.
4323     /**
4324     **/
4325     inline double crand() {
4326       return 1-2*cimg::rand();
4327     }
4328 
4329     //! Return a random variable following a gaussian distribution and a standard deviation of 1.
4330     /**
4331     **/
4332     inline double grand() {
4333       double x1, w;
4334       do {
4335         const double x2 = 2*cimg::rand() - 1.0;
4336         x1 = 2*cimg::rand()-1.0;
4337         w = x1*x1 + x2*x2;
4338       } while (w<=0 || w>=1.0);
4339       return x1*std::sqrt((-2*std::log(w))/w);
4340     }
4341 
4342     //! Return a random variable following a Poisson distribution of parameter z.
4343     /**
4344     **/
4345     inline unsigned int prand(const double z) {
4346       if (z<=1.0e-10) return 0;
4347       if (z>100) return (unsigned int)((std::sqrt(z) * cimg::grand()) + z);
4348       unsigned int k = 0;
4349       const double y = std::exp(-z);
4350       for (double s = 1.0; s>=y; ++k) s*=cimg::rand();
4351       return k-1;
4352     }
4353 
4354     //! Bitwise-rotate value on the left.
4355     template<typename T>
4356     inline T rol(const T a, const unsigned int n=1) {
4357       return n?(T)((a<<n)|(a>>((sizeof(T)<<3)-n))):a;
4358     }
4359 
4360     inline float rol(const float a, const unsigned int n=1) {
4361       return (float)rol((int)a,n);
4362     }
4363 
4364     inline double rol(const double a, const unsigned int n=1) {
4365       return (double)rol((long)a,n);
4366     }
4367 
4368     //! Bitwise-rotate value on the right.
4369     template<typename T>
4370     inline T ror(const T a, const unsigned int n=1) {
4371       return n?(T)((a>>n)|(a<<((sizeof(T)<<3)-n))):a;
4372     }
4373 
4374     inline float ror(const float a, const unsigned int n=1) {
4375       return (float)ror((int)a,n);
4376     }
4377 
4378     inline double ror(const double a, const unsigned int n=1) {
4379       return (double)ror((long)a,n);
4380     }
4381 
4382     //! Return absolute value of a value.
4383     template<typename T>
4384     inline T abs(const T a) {
4385       return a>=0?a:-a;
4386     }
4387     inline bool abs(const bool a) {
4388       return a;
4389     }
4390     inline unsigned char abs(const unsigned char a) {
4391       return a;
4392     }
4393     inline unsigned short abs(const unsigned short a) {
4394       return a;
4395     }
4396     inline unsigned int abs(const unsigned int a) {
4397       return a;
4398     }
4399     inline unsigned long abs(const unsigned long a) {
4400       return a;
4401     }
4402     inline double abs(const double a) {
4403       return std::fabs(a);
4404     }
4405     inline float abs(const float a) {
4406       return (float)std::fabs((double)a);
4407     }
4408     inline int abs(const int a) {
4409       return std::abs(a);
4410     }
4411 
4412     //! Return square of a value.
4413     template<typename T>
4414     inline T sqr(const T val) {
4415       return val*val;
4416     }
4417 
4418     //! Return <tt>1 + log_10(x)</tt> of a value \c x.
4419     inline int xln(const int x) {
4420       return x>0?(int)(1+std::log10((double)x)):1;
4421     }
4422 
4423     //! Return the minimum between two values.
4424     template<typename t1, typename t2>
4425     inline typename cimg::superset<t1,t2>::type min(const t1& a, const t2& b) {
4426       typedef typename cimg::superset<t1,t2>::type t1t2;
4427       return (t1t2)(a<=b?a:b);
4428     }
4429 
4430     //! Return the minimum between three values.
4431     template<typename t1, typename t2, typename t3>
4432     inline typename cimg::superset2<t1,t2,t3>::type min(const t1& a, const t2& b, const t3& c) {
4433       typedef typename cimg::superset2<t1,t2,t3>::type t1t2t3;
4434       return (t1t2t3)cimg::min(cimg::min(a,b),c);
4435     }
4436 
4437     //! Return the minimum between four values.
4438     template<typename t1, typename t2, typename t3, typename t4>
4439     inline typename cimg::superset3<t1,t2,t3,t4>::type min(const t1& a, const t2& b, const t3& c, const t4& d) {
4440       typedef typename cimg::superset3<t1,t2,t3,t4>::type t1t2t3t4;
4441       return (t1t2t3t4)cimg::min(cimg::min(a,b,c),d);
4442     }
4443 
4444     //! Return the maximum between two values.
4445     template<typename t1, typename t2>
4446     inline typename cimg::superset<t1,t2>::type max(const t1& a, const t2& b) {
4447       typedef typename cimg::superset<t1,t2>::type t1t2;
4448       return (t1t2)(a>=b?a:b);
4449     }
4450 
4451     //! Return the maximum between three values.
4452     template<typename t1, typename t2, typename t3>
4453     inline typename cimg::superset2<t1,t2,t3>::type max(const t1& a, const t2& b, const t3& c) {
4454       typedef typename cimg::superset2<t1,t2,t3>::type t1t2t3;
4455       return (t1t2t3)cimg::max(cimg::max(a,b),c);
4456     }
4457 
4458     //! Return the maximum between four values.
4459     template<typename t1, typename t2, typename t3, typename t4>
4460     inline typename cimg::superset3<t1,t2,t3,t4>::type max(const t1& a, const t2& b, const t3& c, const t4& d) {
4461       typedef typename cimg::superset3<t1,t2,t3,t4>::type t1t2t3t4;
4462       return (t1t2t3t4)cimg::max(cimg::max(a,b,c),d);
4463     }
4464 
4465     //! Return the sign of a value.
4466     template<typename T>
4467     inline T sign(const T x) {
4468       return (x<0)?(T)(-1):(x==0?(T)0:(T)1);
4469     }
4470 
4471     //! Return the nearest power of 2 higher than given value.
4472     template<typename T>
4473     inline unsigned long nearest_pow2(const T x) {
4474       unsigned long i = 1;
4475       while (x>i) i<<=1;
4476       return i;
4477     }
4478 
4479     //! Return the sinc of a given value.
4480     inline double sinc(const double x) {
4481       return x?std::sin(x)/x:1;
4482     }
4483 
4484     //! Return the modulo of a value.
4485     /**
4486        \param x Input value.
4487        \param m Modulo value.
4488        \note This modulo function accepts negative and floating-points modulo numbers, as well as variables of any type.
4489     **/
4490     template<typename T>
4491     inline T mod(const T& x, const T& m) {
4492       const double dx = (double)x, dm = (double)m;
4493       return (T)(dx - dm * std::floor(dx / dm));
4494     }
4495     inline int mod(const bool x, const bool m) {
4496       return m?(x?1:0):0;
4497     }
4498     inline int mod(const char x, const char m) {
4499       return x>=0?x%m:(x%m?m+x%m:0);
4500     }
4501     inline int mod(const short x, const short m) {
4502       return x>=0?x%m:(x%m?m+x%m:0);
4503     }
4504     inline int mod(const int x, const int m) {
4505       return x>=0?x%m:(x%m?m+x%m:0);
4506     }
4507     inline int mod(const long x, const long m) {
4508       return x>=0?x%m:(x%m?m+x%m:0);
4509     }
4510     inline int mod(const unsigned char x, const unsigned char m) {
4511       return x%m;
4512     }
4513     inline int mod(const unsigned short x, const unsigned short m) {
4514       return x%m;
4515     }
4516     inline int mod(const unsigned int x, const unsigned int m) {
4517       return x%m;
4518     }
4519     inline int mod(const unsigned long x, const unsigned long m) {
4520       return x%m;
4521     }
4522 
4523     //! Return the min-mod of two values.
4524     /**
4525        \note <i>minmod(\p a,\p b)</i> is defined to be :
4526        - <i>minmod(\p a,\p b) = min(\p a,\p b)</i>, if \p a and \p b have the same sign.
4527        - <i>minmod(\p a,\p b) = 0</i>, if \p a and \p b have different signs.
4528     **/
4529     template<typename T>
4530     inline T minmod(const T a, const T b) {
4531       return a*b<=0?0:(a>0?(a<b?a:b):(a<b?b:a));
4532     }
4533 
4534     //! Return base-2 logarithm of a value.
4535     inline double log2(const double x) {
4536       static const double base = std::log(2.0);
4537       return std::log(x)/base;
4538     }
4539 
4540     //! Return rounded value.
4541     /**
4542        \param x Value to be rounded.
4543        \param y Rounding precision.
4544        \param rounding_type Type of rounding operation (\c 0 = nearest, \c -1 = backward, \c 1 = forward).
4545        \return Rounded value, having the same type as input value \c x.
4546     **/
4547     template<typename T>
4548     inline T round(const T x, const double y=1, const int rounding_type=0) {
4549       if (y<=0) return x;
4550       const double sx = (double)x/y, floor = std::floor(sx), delta =  sx - floor;
4551       return (T)(y*(rounding_type<0?floor:rounding_type>0?std::ceil(sx):delta<0.5?floor:std::ceil(sx)));
4552     }
4553 
4554     inline double _pythagore(double a, double b) {
4555       const double absa = cimg::abs(a), absb = cimg::abs(b);
4556       if (absa>absb) { const double tmp = absb/absa; return absa*std::sqrt(1.0+tmp*tmp); }
4557       else { const double tmp = absa/absb; return absb==0?0:absb*std::sqrt(1.0+tmp*tmp); }
4558     }
4559 
4560     //! Convert ascii character to lower case.
4561     inline char uncase(const char x) {
4562       return (char)((x<'A'||x>'Z')?x:x-'A'+'a');
4563     }
4564 
4565     //! Convert C-string to lower case.
4566     inline void uncase(char *const str) {
4567       if (str) for (char *ptr = str; *ptr; ++ptr) *ptr = uncase(*ptr);
4568     }
4569 
4570     //! Read value in a C-string.
4571     /**
4572        \param str C-string containing the float value to read.
4573        \return Read value.
4574        \note Same as <tt>std::atof()</tt> extended to manage the retrieval of fractions from C-strings, as in <em>"1/2"</em>.
4575     **/
4576     inline double atof(const char *const str) {
4577       double x = 0, y = 1;
4578       if (!str) return 0; else { std::sscanf(str,"%lf/%lf",&x,&y); return x/y; }
4579     }
4580 
4581     //! Compare the first \p l characters of two C-strings, ignoring the case.
4582     /**
4583        \param str1 C-string.
4584        \param str2 C-string.
4585        \param l Number of characters to compare.
4586        \return \c 0 if the two strings are equal, something else otherwise.
4587        \note This function has to be defined since it is not provided by all C++-compilers (not ANSI).
4588     **/
4589     inline int strncasecmp(const char *const str1, const char *const str2, const int l) {
4590       if (!l) return 0;
4591       if (!str1) return str2?-1:0;
4592       const char *nstr1 = str1, *nstr2 = str2;
4593       int k, diff = 0; for (k = 0; k<l && !(diff = uncase(*nstr1)-uncase(*nstr2)); ++k) { ++nstr1; ++nstr2; }
4594       return k!=l?diff:0;
4595     }
4596 
4597     //! Compare two C-strings, ignoring the case.
4598     /**
4599        \param str1 C-string.
4600        \param str2 C-string.
4601        \return \c 0 if the two strings are equal, something else otherwise.
4602        \note This function has to be defined since it is not provided by all C++-compilers (not ANSI).
4603     **/
4604     inline int strcasecmp(const char *const str1, const char *const str2) {
4605       if (!str1) return str2?-1:0;
4606       const unsigned int
4607         l1 = (unsigned int)std::strlen(str1),
4608         l2 = (unsigned int)std::strlen(str2);
4609       return cimg::strncasecmp(str1,str2,1+(l1<l2?l1:l2));
4610     }
4611 
4612     //! Remove delimiters on the start and/or end of a C-string.
4613     /**
4614        \param[in,out] str C-string to work with (modified at output).
4615        \param delimiter Delimiter character code to remove.
4616        \param is_symmetric Tells if the removal is done only if delimiters are symmetric (both at the beginning and the end of \c s).
4617        \param is_iterative Tells if the removal is done if several iterations are possible.
4618        \return \c true if delimiters have been removed, \c false otherwise.
4619    **/
4620     inline bool strpare(char *const str, const char delimiter=' ', const bool is_symmetric=false, const bool is_iterative=false) {
4621       if (!str) return false;
4622       const int l = (int)std::strlen(str);
4623       int p, q;
4624       if (is_symmetric) for (p = 0, q = l-1; p<q && str[p]==delimiter && str[q]==delimiter; ) { --q; ++p; if (!is_iterative) break; }
4625       else {
4626         for (p = 0; p<l && str[p]==delimiter; ) { ++p; if (!is_iterative) break; }
4627         for (q = l-1; q>p && str[q]==delimiter; ) { --q; if (!is_iterative) break; }
4628       }
4629       const int n = q - p + 1;
4630       if (n!=l) { std::memmove(str,str+p,n); str[n] = 0; return true; }
4631       return false;
4632     }
4633 
4634     //! Replace escape sequences in C-strings by their binary ascii values.
4635     /**
4636        \param[in,out] str C-string to work with (modified at output).
4637      **/
4638     inline void strescape(char *const str) {
4639 #define cimg_strescape(ci,co) case ci: *nd = co; ++ns; break;
4640       static unsigned int val = 0;
4641       for (char *ns = str, *nd = str; *ns || (bool)(*nd=0); ++nd) if (*ns=='\\') switch (*(++ns)) {
4642             cimg_strescape('n','\n');
4643             cimg_strescape('t','\t');
4644             cimg_strescape('v','\v');
4645             cimg_strescape('b','\b');
4646             cimg_strescape('r','\r');
4647             cimg_strescape('f','\f');
4648             cimg_strescape('a','\a');
4649             cimg_strescape('\\','\\');
4650             cimg_strescape('\?','\?');
4651             cimg_strescape('\'','\'');
4652             cimg_strescape('\"','\"');
4653           case 0 : *nd = 0; break;
4654           case '0' : case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' :
4655             std::sscanf(ns,"%o",&val); while (*ns>='0' && *ns<='7') ++ns;
4656             *nd = val; break;
4657           case 'x':
4658             std::sscanf(++ns,"%x",&val); while ((*ns>='0' && *ns<='7') || (*ns>='a' && *ns<='f') || (*ns>='A' && *ns<='F')) ++ns;
4659             *nd = val; break;
4660           default : *nd = *(ns++);
4661           } else *nd = *(ns++);
4662     }
4663 
4664     // Return a temporary string describing the size of a memory buffer.
4665     inline const char *strbuffersize(const unsigned long size) {
4666       static char res[256] = { 0 };
4667       if (size<1024LU) cimg_snprintf(res,sizeof(res),"%lu byte%s",size,size>1?"s":"");
4668       else if (size<1024*1024LU) { const float nsize = size/1024.0f; cimg_snprintf(res,sizeof(res),"%.1f Kio",nsize); }
4669       else if (size<1024*1024*1024LU) { const float nsize = size/(1024*1024.0f); cimg_snprintf(res,sizeof(res),"%.1f Mio",nsize); }
4670       else { const float nsize = size/(1024*1024*1024.0f); cimg_snprintf(res,sizeof(res),"%.1f Gio",nsize); }
4671       return res;
4672     }
4673 
4674     //! Return the basename of a filename.
4675     inline const char* basename(const char *const str)  {
4676       const char *p = 0;
4677       for (const char *np = str; np>=str && (p=np); np = std::strchr(np,cimg_file_separator)+1) {}
4678       return p;
4679     }
4680 
4681     // Return a random filename.
4682     inline const char* filenamerand() {
4683       static char randomid[9] = { 0,0,0,0,0,0,0,0,0 };
4684       cimg::srand();
4685       for (unsigned int k = 0; k<8; ++k) {
4686         const int v = (int)std::rand()%3;
4687         randomid[k] = (char)(v==0?('0'+(std::rand()%10)):(v==1?('a'+(std::rand()%26)):('A'+(std::rand()%26))));
4688       }
4689       return randomid;
4690     }
4691 
4692     // Convert filename as a Windows-style filename (short path name).
4693     inline void winformat_string(char *const str) {
4694       if (str && *str) {
4695 #if cimg_OS==2
4696         char *const nstr = new char[MAX_PATH];
4697         if (GetShortPathNameA(str,nstr,MAX_PATH)) std::strcpy(str,nstr);
4698 #endif
4699       }
4700     }
4701 
4702     //! Open a file.
4703     /**
4704        \param path Path of the filename to open.
4705        \param mode C-string describing the opening mode.
4706        \return Opened file.
4707        \note Same as <tt>std::fopen()</tt> but throw a \c CImgIOException when
4708        the specified file cannot be opened, instead of returning \c 0.
4709     **/
4710     inline std::FILE *fopen(const char *const path, const char *const mode) {
4711       if (!path)
4712         throw CImgArgumentException("cimg::fopen() : Specified file path is (null).");
4713       if (!mode)
4714         throw CImgArgumentException("cimg::fopen() : File '%s', specified mode is (null).",
4715                                     path);
4716       std::FILE *res = 0;
4717       if (*path=='-' && (!path[1] || path[1]=='.')) {
4718         res = (*mode=='r')?stdin:stdout;
4719 #if cimg_OS==2
4720         if (*mode && mode[1]=='b') { // Force stdin/stdout to be in binary mode.
4721           if (_setmode(_fileno(res),0x8000)==-1) res = 0;
4722         }
4723 #endif
4724       } else res = std::fopen(path,mode);
4725       if (!res) throw CImgIOException("cimg::fopen() : Failed to open file '%s' with mode '%s'.",
4726                                       path,mode);
4727       return res;
4728     }
4729 
4730     //! Close a file.
4731     /**
4732        \param file File to close.
4733        \return \c 0 if file has been closed properly, something else otherwise.
4734        \note Same as <tt>std::fclose()</tt> but display a warning message if
4735        the file has not been closed properly.
4736     **/
4737     inline int fclose(std::FILE *file) {
4738       if (!file) warn("cimg::fclose() : Specified file is (null).");
4739       if (!file || file==stdin || file==stdout) return 0;
4740       const int errn = std::fclose(file);
4741       if (errn!=0) warn("cimg::fclose() : Error code %d returned during file closing.",
4742                         errn);
4743       return errn;
4744     }
4745 
4746     //! Get/set path to store temporary files.
4747     /**
4748        \param user_path Specified path, or \c 0 to get the path currently used.
4749        \param reinit_path Force path to be recalculated (may take some time).
4750        \return Path where temporary files can be saved.
4751     **/
4752     inline const char* temporary_path(const char *const user_path=0, const bool reinit_path=false) {
4753 #define _cimg_test_temporary_path(p) \
4754       if (!path_found) { \
4755         cimg_snprintf(st_path,1024,"%s",p); \
4756         cimg_snprintf(tmp,sizeof(tmp),"%s%c%s",st_path,cimg_file_separator,filetmp); \
4757         if ((file=std::fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } \
4758       }
4759       static char *st_path = 0;
4760       if (reinit_path) { delete[] st_path; st_path = 0; }
4761       if (user_path) {
4762         if (!st_path) st_path = new char[1024];
4763         std::memset(st_path,0,1024);
4764         std::strncpy(st_path,user_path,1023);
4765       } else if (!st_path) {
4766         st_path = new char[1024];
4767         std::memset(st_path,0,1024);
4768         bool path_found = false;
4769         char tmp[1024] = { 0 }, filetmp[512] = { 0 };
4770         std::FILE *file = 0;
4771         cimg_snprintf(filetmp,sizeof(filetmp),"%s.tmp",cimg::filenamerand());
4772         char *tmpPath = std::getenv("TMP");
4773         if (!tmpPath) { tmpPath = std::getenv("TEMP"); winformat_string(tmpPath); }
4774         if (tmpPath) _cimg_test_temporary_path(tmpPath);
4775 #if cimg_OS==2
4776         _cimg_test_temporary_path("C:\\WINNT\\Temp");
4777         _cimg_test_temporary_path("C:\\WINDOWS\\Temp");
4778         _cimg_test_temporary_path("C:\\Temp");
4779         _cimg_test_temporary_path("C:");
4780         _cimg_test_temporary_path("D:\\WINNT\\Temp");
4781         _cimg_test_temporary_path("D:\\WINDOWS\\Temp");
4782         _cimg_test_temporary_path("D:\\Temp");
4783         _cimg_test_temporary_path("D:");
4784 #else
4785         _cimg_test_temporary_path("/tmp");
4786         _cimg_test_temporary_path("/var/tmp");
4787 #endif
4788         if (!path_found) {
4789           *st_path = 0;
4790           std::strncpy(tmp,filetmp,sizeof(tmp)-1);
4791           if ((file=std::fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; }
4792         }
4793         if (!path_found)
4794           throw CImgIOException("cimg::temporary_path() : Failed to locate path for writing temporary files.\n");
4795       }
4796       return st_path;
4797     }
4798 
4799     //! Get/set path to the <i>Program Files/</i> directory (Windows only).
4800     /**
4801        \param user_path Specified path, or \c 0 to get the path currently used.
4802        \param reinit_path Force path to be recalculated (may take some time).
4803        \return Path containing the program files.
4804     **/
4805 #if cimg_OS==2
4806     inline const char* programfiles_path(const char *const user_path=0, const bool reinit_path=false) {
4807       static char *st_path = 0;
4808       if (reinit_path) { delete[] st_path; st_path = 0; }
4809       if (user_path) {
4810         if (!st_path) st_path = new char[1024];
4811         std::memset(st_path,0,1024);
4812         std::strncpy(st_path,user_path,1023);
4813       } else if (!st_path) {
4814         st_path = new char[MAX_PATH];
4815         std::memset(st_path,0,MAX_PATH);
4816         // Note : in the following line, 0x26 = CSIDL_PROGRAM_FILES (not defined on every compiler).
4817 #if !defined(__INTEL_COMPILER)
4818         if (!SHGetSpecialFolderPathA(0,st_path,0x0026,false)) {
4819           const char *const pfPath = std::getenv("PROGRAMFILES");
4820           if (pfPath) std::strncpy(st_path,pfPath,MAX_PATH-1);
4821           else std::strcpy(st_path,"C:\\PROGRA~1");
4822         }
4823 #else
4824         std::strcpy(st_path,"C:\\PROGRA~1");
4825 #endif
4826       }
4827       return st_path;
4828     }
4829 #endif
4830 
4831     //! Get/set path to the ImageMagick's \c convert binary.
4832     /**
4833        \param user_path Specified path, or \c 0 to get the path currently used.
4834        \param reinit_path Force path to be recalculated (may take some time).
4835        \return Path containing the \c convert binary.
4836     **/
4837     inline const char* imagemagick_path(const char *const user_path=0, const bool reinit_path=false) {
4838       static char *st_path = 0;
4839       if (reinit_path) { delete[] st_path; st_path = 0; }
4840       if (user_path) {
4841         if (!st_path) st_path = new char[1024];
4842         std::memset(st_path,0,1024);
4843         std::strncpy(st_path,user_path,1023);
4844       } else if (!st_path) {
4845         st_path = new char[1024];
4846         std::memset(st_path,0,1024);
4847         bool path_found = false;
4848         std::FILE *file = 0;
4849 #if cimg_OS==2
4850         const char *const pf_path = programfiles_path();
4851         if (!path_found) {
4852           std::strcpy(st_path,".\\convert.exe");
4853           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4854         }
4855         for (int k = 32; k>=10 && !path_found; --k) {
4856           cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%.2d-\\convert.exe",pf_path,k);
4857           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4858         }
4859         for (int k = 9; k>=0 && !path_found; --k) {
4860           cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%d-Q\\convert.exe",pf_path,k);
4861           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4862         }
4863         for (int k = 32; k>=0 && !path_found; --k) {
4864           cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%d\\convert.exe",pf_path,k);
4865           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4866         }
4867         for (int k = 32; k>=10 && !path_found; --k) {
4868           cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",pf_path,k);
4869           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4870         }
4871         for (int k = 9; k>=0 && !path_found; --k) {
4872           cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",pf_path,k);
4873           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4874         }
4875         for (int k = 32; k>=0 && !path_found; --k) {
4876           cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",pf_path,k);
4877           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4878         }
4879         for (int k = 32; k>=10 && !path_found; --k) {
4880           cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%.2d-\\convert.exe",k);
4881           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4882         }
4883         for (int k = 9; k>=0 && !path_found; --k) {
4884           cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%d-Q\\convert.exe",k);
4885           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4886         }
4887         for (int k = 32; k>=0 && !path_found; --k) {
4888           cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%d\\convert.exe",k);
4889           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4890         }
4891         for (int k = 32; k>=10 && !path_found; --k) {
4892           cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",k);
4893           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4894         }
4895         for (int k = 9; k>=0 && !path_found; --k) {
4896           cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",k);
4897           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4898         }
4899         for (int k = 32; k>=0 && !path_found; --k) {
4900           cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",k);
4901           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4902         }
4903         for (int k = 32; k>=10 && !path_found; --k) {
4904           cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%.2d-\\convert.exe",k);
4905           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4906         }
4907         for (int k = 9; k>=0 && !path_found; --k) {
4908           cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%d-Q\\convert.exe",k);
4909           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4910         }
4911         for (int k = 32; k>=0 && !path_found; --k) {
4912           cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%d\\convert.exe",k);
4913           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4914         }
4915         for (int k = 32; k>=10 && !path_found; --k) {
4916           cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",k);
4917           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4918         }
4919         for (int k = 9; k>=0 && !path_found; --k) {
4920           cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",k);
4921           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4922         }
4923         for (int k = 32; k>=0 && !path_found; --k) {
4924           cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",k);
4925           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4926         }
4927         if (!path_found) std::strcpy(st_path,"convert.exe");
4928 #else
4929         if (!path_found) {
4930           std::strcpy(st_path,"./convert");
4931           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4932         }
4933         if (!path_found) std::strcpy(st_path,"convert");
4934 #endif
4935         winformat_string(st_path);
4936       }
4937       return st_path;
4938     }
4939 
4940     //! Get/set path to the GraphicsMagick's \c gm binary.
4941     /**
4942        \param user_path Specified path, or \c 0 to get the path currently used.
4943        \param reinit_path Force path to be recalculated (may take some time).
4944        \return Path containing the \c gm binary.
4945     **/
4946     inline const char* graphicsmagick_path(const char *const user_path=0, const bool reinit_path=false) {
4947       static char *st_path = 0;
4948       if (reinit_path) { delete[] st_path; st_path = 0; }
4949       if (user_path) {
4950         if (!st_path) st_path = new char[1024];
4951         std::memset(st_path,0,1024);
4952         std::strncpy(st_path,user_path,1023);
4953       } else if (!st_path) {
4954         st_path = new char[1024];
4955         std::memset(st_path,0,1024);
4956         bool path_found = false;
4957         std::FILE *file = 0;
4958 #if cimg_OS==2
4959         const char *const pf_path = programfiles_path();
4960         if (!path_found) {
4961           std::strcpy(st_path,".\\gm.exe");
4962           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4963         }
4964         for (int k = 32; k>=10 && !path_found; --k) {
4965           cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%.2d-\\gm.exe",pf_path,k);
4966           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4967         }
4968         for (int k = 9; k>=0 && !path_found; --k) {
4969           cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%d-Q\\gm.exe",pf_path,k);
4970           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4971         }
4972         for (int k = 32; k>=0 && !path_found; --k) {
4973           cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%d\\gm.exe",pf_path,k);
4974           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4975         }
4976         for (int k = 32; k>=10 && !path_found; --k) {
4977           cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",pf_path,k);
4978           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4979         }
4980         for (int k = 9; k>=0 && !path_found; --k) {
4981           cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",pf_path,k);
4982           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4983         }
4984         for (int k = 32; k>=0 && !path_found; --k) {
4985           cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",pf_path,k);
4986           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4987         }
4988         for (int k = 32; k>=10 && !path_found; --k) {
4989           cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%.2d-\\gm.exe",k);
4990           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4991         }
4992         for (int k = 9; k>=0 && !path_found; --k) {
4993           cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%d-Q\\gm.exe",k);
4994           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4995         }
4996         for (int k = 32; k>=0 && !path_found; --k) {
4997           cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%d\\gm.exe",k);
4998           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4999         }
5000         for (int k = 32; k>=10 && !path_found; --k) {
5001           cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k);
5002           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5003         }
5004         for (int k = 9; k>=0 && !path_found; --k) {
5005           cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k);
5006           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5007         }
5008         for (int k = 32; k>=0 && !path_found; --k) {
5009           cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k);
5010           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5011         }
5012         for (int k = 32; k>=10 && !path_found; --k) {
5013           cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%.2d-\\gm.exe",k);
5014           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5015         }
5016         for (int k = 9; k>=0 && !path_found; --k) {
5017           cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%d-Q\\gm.exe",k);
5018           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5019         }
5020         for (int k = 32; k>=0 && !path_found; --k) {
5021           cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%d\\gm.exe",k);
5022           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5023         }
5024         for (int k = 32; k>=10 && !path_found; --k) {
5025           cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k);
5026           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5027         }
5028         for (int k = 9; k>=0 && !path_found; --k) {
5029           cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k);
5030           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5031         }
5032         for (int k = 32; k>=0 && !path_found; --k) {
5033           cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k);
5034           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5035         }
5036         if (!path_found) std::strcpy(st_path,"gm.exe");
5037 #else
5038         if (!path_found) {
5039           std::strcpy(st_path,"./gm");
5040           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5041         }
5042         if (!path_found) std::strcpy(st_path,"gm");
5043 #endif
5044         winformat_string(st_path);
5045       }
5046       return st_path;
5047     }
5048 
5049     //! Get/set path to the XMedcon's \c medcon binary.
5050     /**
5051        \param user_path Specified path, or \c 0 to get the path currently used.
5052        \param reinit_path Force path to be recalculated (may take some time).
5053        \return Path containing the \c medcon binary.
5054     **/
5055     inline const char* medcon_path(const char *const user_path=0, const bool reinit_path=false) {
5056       static char *st_path = 0;
5057       if (reinit_path) { delete[] st_path; st_path = 0; }
5058       if (user_path) {
5059         if (!st_path) st_path = new char[1024];
5060         std::memset(st_path,0,1024);
5061         std::strncpy(st_path,user_path,1023);
5062       } else if (!st_path) {
5063         st_path = new char[1024];
5064         std::memset(st_path,0,1024);
5065         bool path_found = false;
5066         std::FILE *file = 0;
5067 #if cimg_OS==2
5068         const char *const pf_path = programfiles_path();
5069         if (!path_found) {
5070           std::strcpy(st_path,".\\medcon.exe");
5071           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5072         }
5073         if (!path_found) {
5074           cimg_snprintf(st_path,sizeof(st_path),"%s\\XMedCon\\bin\\medcon.bat",pf_path);
5075           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5076         }
5077         if (!path_found) {
5078           cimg_snprintf(st_path,sizeof(st_path),"%s\\XMedCon\\bin\\medcon.exe",pf_path);
5079           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5080         }
5081         if (!path_found) std::strcpy(st_path,"medcon.exe");
5082 #else
5083         if (!path_found) {
5084           std::strcpy(st_path,"./medcon");
5085           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5086         }
5087         if (!path_found) std::strcpy(st_path,"medcon");
5088 #endif
5089         winformat_string(st_path);
5090       }
5091       return st_path;
5092     }
5093 
5094     //! Get/set path to the FFMPEG's \c ffmpeg binary.
5095     /**
5096        \param user_path Specified path, or \c 0 to get the path currently used.
5097        \param reinit_path Force path to be recalculated (may take some time).
5098        \return Path containing the \c ffmpeg binary.
5099     **/
5100     inline const char *ffmpeg_path(const char *const user_path=0, const bool reinit_path=false) {
5101       static char *st_path = 0;
5102       if (reinit_path) { delete[] st_path; st_path = 0; }
5103       if (user_path) {
5104         if (!st_path) st_path = new char[1024];
5105         std::memset(st_path,0,1024);
5106         std::strncpy(st_path,user_path,1023);
5107       } else if (!st_path) {
5108         st_path = new char[1024];
5109         std::memset(st_path,0,1024);
5110         bool path_found = false;
5111         std::FILE *file = 0;
5112 #if cimg_OS==2
5113         if (!path_found) {
5114           std::strcpy(st_path,".\\ffmpeg.exe");
5115           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5116         }
5117         if (!path_found) std::strcpy(st_path,"ffmpeg.exe");
5118 #else
5119         if (!path_found) {
5120           std::strcpy(st_path,"./ffmpeg");
5121           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5122         }
5123         if (!path_found) std::strcpy(st_path,"ffmpeg");
5124 #endif
5125         winformat_string(st_path);
5126       }
5127       return st_path;
5128     }
5129 
5130     //! Get/set path to the \c gzip binary.
5131     /**
5132        \param user_path Specified path, or \c 0 to get the path currently used.
5133        \param reinit_path Force path to be recalculated (may take some time).
5134        \return Path containing the \c gzip binary.
5135     **/
5136     inline const char *gzip_path(const char *const user_path=0, const bool reinit_path=false) {
5137       static char *st_path = 0;
5138       if (reinit_path) { delete[] st_path; st_path = 0; }
5139       if (user_path) {
5140         if (!st_path) st_path = new char[1024];
5141         std::memset(st_path,0,1024);
5142         std::strncpy(st_path,user_path,1023);
5143       } else if (!st_path) {
5144         st_path = new char[1024];
5145         std::memset(st_path,0,1024);
5146         bool path_found = false;
5147         std::FILE *file = 0;
5148 #if cimg_OS==2
5149         if (!path_found) {
5150           std::strcpy(st_path,".\\gzip.exe");
5151           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5152         }
5153         if (!path_found) std::strcpy(st_path,"gzip.exe");
5154 #else
5155         if (!path_found) {
5156           std::strcpy(st_path,"./gzip");
5157           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5158         }
5159         if (!path_found) std::strcpy(st_path,"gzip");
5160 #endif
5161         winformat_string(st_path);
5162       }
5163       return st_path;
5164     }
5165 
5166     //! Get/set path to the \c gzip binary.
5167     /**
5168        \param user_path Specified path, or \c 0 to get the path currently used.
5169        \param reinit_path Force path to be recalculated (may take some time).
5170        \return Path containing the \c gunzip binary.
5171     **/
5172     inline const char *gunzip_path(const char *const user_path=0, const bool reinit_path=false) {
5173       static char *st_path = 0;
5174       if (reinit_path) { delete[] st_path; st_path = 0; }
5175       if (user_path) {
5176         if (!st_path) st_path = new char[1024];
5177         std::memset(st_path,0,1024);
5178         std::strncpy(st_path,user_path,1023);
5179       } else if (!st_path) {
5180         st_path = new char[1024];
5181         std::memset(st_path,0,1024);
5182         bool path_found = false;
5183         std::FILE *file = 0;
5184 #if cimg_OS==2
5185         if (!path_found) {
5186           std::strcpy(st_path,".\\gunzip.exe");
5187           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5188         }
5189         if (!path_found) std::strcpy(st_path,"gunzip.exe");
5190 #else
5191         if (!path_found) {
5192           std::strcpy(st_path,"./gunzip");
5193           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5194         }
5195         if (!path_found) std::strcpy(st_path,"gunzip");
5196 #endif
5197         winformat_string(st_path);
5198       }
5199       return st_path;
5200     }
5201 
5202     //! Get/set path to the \c dcraw binary.
5203     /**
5204        \param user_path Specified path, or \c 0 to get the path currently used.
5205        \param reinit_path Force path to be recalculated (may take some time).
5206        \return Path containing the \c dcraw binary.
5207     **/
5208     inline const char *dcraw_path(const char *const user_path=0, const bool reinit_path=false) {
5209       static char *st_path = 0;
5210       if (reinit_path) { delete[] st_path; st_path = 0; }
5211       if (user_path) {
5212         if (!st_path) st_path = new char[1024];
5213         std::memset(st_path,0,1024);
5214         std::strncpy(st_path,user_path,1023);
5215       } else if (!st_path) {
5216         st_path = new char[1024];
5217         std::memset(st_path,0,1024);
5218         bool path_found = false;
5219         std::FILE *file = 0;
5220 #if cimg_OS==2
5221         if (!path_found) {
5222           std::strcpy(st_path,".\\dcraw.exe");
5223           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5224         }
5225         if (!path_found) std::strcpy(st_path,"dcraw.exe");
5226 #else
5227         if (!path_found) {
5228           std::strcpy(st_path,"./dcraw");
5229           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5230         }
5231         if (!path_found) std::strcpy(st_path,"dcraw");
5232 #endif
5233         winformat_string(st_path);
5234       }
5235       return st_path;
5236     }
5237 
5238     //! Get/set path to the \c wget binary.
5239     /**
5240        \param user_path Specified path, or \c 0 to get the path currently used.
5241        \param reinit_path Force path to be recalculated (may take some time).
5242        \return Path containing the \c wget binary.
5243     **/
5244     inline const char *wget_path(const char *const user_path=0, const bool reinit_path=false) {
5245       static char *st_path = 0;
5246       if (reinit_path) { delete[] st_path; st_path = 0; }
5247       if (user_path) {
5248         if (!st_path) st_path = new char[1024];
5249         std::memset(st_path,0,1024);
5250         std::strncpy(st_path,user_path,1023);
5251       } else if (!st_path) {
5252         st_path = new char[1024];
5253         std::memset(st_path,0,1024);
5254         bool path_found = false;
5255         std::FILE *file = 0;
5256 #if cimg_OS==2
5257         if (!path_found) {
5258           std::strcpy(st_path,".\\wget.exe");
5259           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5260         }
5261         if (!path_found) std::strcpy(st_path,"wget.exe");
5262 #else
5263         if (!path_found) {
5264           std::strcpy(st_path,"./wget");
5265           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5266         }
5267         if (!path_found) std::strcpy(st_path,"wget");
5268 #endif
5269         winformat_string(st_path);
5270       }
5271       return st_path;
5272     }
5273 
5274     //! Get/set path to the \c curl binary.
5275     /**
5276        \param user_path Specified path, or \c 0 to get the path currently used.
5277        \param reinit_path Force path to be recalculated (may take some time).
5278        \return Path containing the \c curl binary.
5279     **/
5280     inline const char *curl_path(const char *const user_path=0, const bool reinit_path=false) {
5281       static char *st_path = 0;
5282       if (reinit_path) { delete[] st_path; st_path = 0; }
5283       if (user_path) {
5284         if (!st_path) st_path = new char[1024];
5285         std::memset(st_path,0,1024);
5286         std::strncpy(st_path,user_path,1023);
5287       } else if (!st_path) {
5288         st_path = new char[1024];
5289         std::memset(st_path,0,1024);
5290         bool path_found = false;
5291         std::FILE *file = 0;
5292 #if cimg_OS==2
5293         if (!path_found) {
5294           std::strcpy(st_path,".\\curl.exe");
5295           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5296         }
5297         if (!path_found) std::strcpy(st_path,"curl.exe");
5298 #else
5299         if (!path_found) {
5300           std::strcpy(st_path,"./curl");
5301           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5302         }
5303         if (!path_found) std::strcpy(st_path,"curl");
5304 #endif
5305         winformat_string(st_path);
5306       }
5307       return st_path;
5308     }
5309 
5310     //! Split filename into two C-strings \c body and \c extension.
5311     inline const char *split_filename(const char *const filename, char *const body=0) {
5312       if (!filename) { if (body) *body = 0; return 0; }
5313       const char *p = 0; for (const char *np = filename; np>=filename && (p=np); np = std::strchr(np,'.')+1) {}
5314       if (p==filename) {
5315         if (body) std::strcpy(body,filename);
5316         return filename + std::strlen(filename);
5317       }
5318       const unsigned int l = (unsigned int)(p - filename - 1);
5319       if (body) { std::memcpy(body,filename,l); body[l] = 0; }
5320       return p;
5321     }
5322 
5323     //! Generate a numbered version of a filename.
5324     inline char* number_filename(const char *const filename, const int number, const unsigned int n, char *const str) {
5325       if (!filename) { if (str) *str = 0; return 0; }
5326       char format[1024] = { 0 }, body[1024] = { 0 };
5327       const char *const ext = cimg::split_filename(filename,body);
5328       if (n>0) cimg_snprintf(format,sizeof(format),"%s_%%.%ud.%s",body,n,ext);
5329       else cimg_snprintf(format,sizeof(format),"%s_%%d.%s",body,ext);
5330       std::sprintf(str,format,number);
5331       return str;
5332     }
5333 
5334     //! Try to guess format from an image file.
5335     /**
5336        \param file Input file (can be \c 0 if \c filename is set).
5337        \param filename Filename, as a C-string (can be \c 0 if \c file is set).
5338        \return C-string containing the guessed file format, or \c 0 if nothing has been guessed.
5339      **/
5340     inline const char *file_type(std::FILE *const file, const char *const filename) {
5341       if (!file && !filename)
5342         throw CImgArgumentException("cimg::file_type() : Specified filename is (null).");
5343       static const char
5344         *const _pnm = "pnm",
5345         *const _pfm = "pfm",
5346         *const _bmp = "bmp",
5347         *const _gif = "gif",
5348         *const _jpg = "jpg",
5349         *const _off = "off",
5350         *const _pan = "pan",
5351         *const _png = "png",
5352         *const _tif = "tif",
5353         *const _inr = "inr",
5354         *const _dcm = "dcm";
5355       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
5356       const char *f_type = 0, *head;
5357       char header[2048] = { 0 }, item[1024] = { 0 };
5358       const unsigned char *const uheader = (unsigned char*)header;
5359       int err; char cerr;
5360       const unsigned int siz = (unsigned int)std::fread(header,2048,1,nfile);   // Read first 2048 bytes.
5361       if (!file) cimg::fclose(nfile);
5362 
5363       if (!std::strncmp(header,"OFF\n",4)) f_type = _off; // Check for OFF format.
5364       else if (!std::strncmp(header,"#INRIMAGE",9)) f_type = _inr; // Check for INRIMAGE format.
5365       else if (!std::strncmp(header,"PANDORE",7)) f_type = _pan; // Check for PANDORE format.
5366       else if (!std::strncmp(header+128,"DICM",4)) f_type = _dcm; // Check for DICOM format.
5367       else if (uheader[0]==0xFF && uheader[1]==0xD8 && uheader[2]==0xFF) f_type = _jpg;  // Check for JPEG format.
5368       else if (header[0]=='B' && header[1]=='M') f_type = _bmp;  // Check for BMP format.
5369       else if (header[0]=='G' && header[1]=='I' && header[2]=='F' && header[3]=='8' && header[5]=='a' && // Check for GIF format.
5370                (header[4]=='7' || header[4]=='9')) f_type = _gif;
5371       else if (uheader[0]==0x89 && uheader[1]==0x50 && uheader[2]==0x4E && uheader[3]==0x47 &&  // Check for PNG format.
5372                uheader[4]==0x0D && uheader[5]==0x0A && uheader[6]==0x1A && uheader[7]==0x0A) f_type = _png;
5373       else if ((uheader[0]==0x49 && uheader[1]==0x49) || (uheader[0]==0x4D && uheader[1]==0x4D)) f_type = _tif; // Check for TIFF format.
5374       else { // Check for PNM or PFM format.
5375         head = header;
5376         while (head<header+siz && (err=std::sscanf(head,"%1023[^\n]",item))!=EOF && (*item=='#' || !err))
5377           head+=1+(err?std::strlen(item):0);
5378         if (std::sscanf(item," P%d",&err)==1) f_type = _pnm;
5379         else if (std::sscanf(item," P%c",&cerr)==1 && (cerr=='f' || cerr=='F')) f_type = _pfm;
5380       }
5381       return f_type;
5382     }
5383 
5384     //! Read data from file.
5385     /**
5386        \param[out] ptr Pointer to memory buffer that will contain the binary data read from file.
5387        \param nmemb Number of elements to read.
5388        \param stream File to read data from.
5389        \return Number of read elements.
5390        \note Same as <tt>std::fread()</tt> but may display warning message if all elements could not be read.
5391     **/
5392     template<typename T>
5393     inline int fread(T *const ptr, const unsigned long nmemb, std::FILE *stream) {
5394       if (!ptr || nmemb<=0 || !stream)
5395         throw CImgArgumentException("cimg::fread() : Invalid reading request of %u %s%s from file %p to buffer %p.",
5396                                     nmemb,cimg::type<T>::string(),nmemb>1?"s":"",stream,ptr);
5397 
5398       const unsigned long wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T);
5399       unsigned long to_read = nmemb, al_read = 0, l_to_read = 0, l_al_read = 0;
5400       do {
5401         l_to_read = (to_read*sizeof(T))<wlimitT?to_read:wlimit;
5402         l_al_read = (unsigned long)std::fread((void*)(ptr+al_read),sizeof(T),l_to_read,stream);
5403         al_read+=l_al_read;
5404         to_read-=l_al_read;
5405       } while (l_to_read==l_al_read && to_read>0);
5406       if (to_read>0)
5407         warn("cimg::fread() : Only %u/%u elements could be read from file.",
5408              al_read,nmemb);
5409       return al_read;
5410     }
5411 
5412     //! Write data to file.
5413     /**
5414        \param ptr Pointer to memory buffer containing the binary data to write on file.
5415        \param nmemb Number of elements to write.
5416        \param[out] stream File to write data on.
5417        \return Number of written elements.
5418        \note Similar to <tt>std::fwrite</tt> but may display warning messages if all elements could not be written.
5419     **/
5420     template<typename T>
5421     inline int fwrite(const T *ptr, const unsigned long nmemb, std::FILE *stream) {
5422       if (!ptr || !stream)
5423         throw CImgArgumentException("cimg::fwrite() : Invalid writing request of %u %s%s from buffer %p to file %p.",
5424                                     nmemb,cimg::type<T>::string(),nmemb>1?"s":"",ptr,stream);
5425       if (nmemb<=0) return 0;
5426       const unsigned long wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T);
5427       unsigned long to_write = nmemb, al_write = 0, l_to_write = 0, l_al_write = 0;
5428       do {
5429         l_to_write = (to_write*sizeof(T))<wlimitT?to_write:wlimit;
5430         l_al_write = (unsigned long)std::fwrite((void*)(ptr+al_write),sizeof(T),l_to_write,stream);
5431         al_write+=l_al_write;
5432         to_write-=l_al_write;
5433       } while (l_to_write==l_al_write && to_write>0);
5434       if (to_write>0)
5435         warn("cimg::fwrite() : Only %u/%u elements could be written in file.",
5436              al_write,nmemb);
5437       return al_write;
5438     }
5439 
5440     //! Load file from network as a local temporary file.
5441     /**
5442        \param filename Filename, as a C-string.
5443        \param[out] filename_local C-string containing the path to a local copy of \c filename.
5444        \return Value of \c filename_local.
5445        \note Use external binaries \c wget or \c curl to perform. You must have one of these tools installed
5446        to be able to use this function.
5447     **/
5448     inline char *load_network_external(const char *const filename, char *const filename_local) {
5449       if (!filename)
5450         throw CImgArgumentException("cimg::load_network_external() : Specified filename is (null).");
5451       if (!filename_local)
5452         throw CImgArgumentException("cimg::load_network_external() : Specified destination string is (null).");
5453       const char *const _ext = cimg::split_filename(filename), *const ext = (*_ext && _ext>filename)?_ext-1:_ext;
5454       char command[1024] = { 0 };
5455       std::FILE *file = 0;
5456       *filename_local = 0;
5457       do {
5458         cimg_snprintf(filename_local,512,"%s%c%s%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
5459         if ((file=std::fopen(filename_local,"rb"))!=0) cimg::fclose(file);
5460       } while (file);
5461 
5462       // Try with 'curl' first.
5463       cimg_snprintf(command,sizeof(command),"%s -f --silent --compressed -o \"%s\" \"%s\"",cimg::curl_path(),filename_local,filename);
5464       cimg::system(command);
5465       if (!(file = std::fopen(filename_local,"rb"))) {
5466 
5467         // Try with 'wget' else.
5468         cimg_snprintf(command,sizeof(command),"%s -q -r -l 0 --no-cache -O \"%s\" \"%s\"",cimg::wget_path(),filename_local,filename);
5469         cimg::system(command);
5470         if (!(file = std::fopen(filename_local,"rb")))
5471           throw CImgIOException("cimg::load_network_external() : Failed to load file '%s' with external tools 'wget' or 'curl'.",filename);
5472         cimg::fclose(file);
5473 
5474         // Try gunzip it.
5475         cimg_snprintf(command,sizeof(command),"%s.gz",filename_local);
5476         std::rename(filename_local,command);
5477         cimg_snprintf(command,sizeof(command),"%s --quiet %s.gz",gunzip_path(),filename_local);
5478         cimg::system(command);
5479         file = std::fopen(filename_local,"rb");
5480         if (!file) {
5481           cimg_snprintf(command,sizeof(command),"%s.gz",filename_local);
5482           std::rename(command,filename_local);
5483           file = std::fopen(filename_local,"rb");
5484         }
5485       }
5486       std::fseek(file,0,SEEK_END); // Check if file size is 0.
5487       if (std::ftell(file)<=0)
5488         throw CImgIOException("cimg::load_network_external() : Failed to load file '%s' with external commands 'wget' or 'curl'.",filename);
5489       cimg::fclose(file);
5490       return filename_local;
5491     }
5492 
5493     //! Return options specified on the command line.
5494     inline const char* option(const char *const name, const int argc, const char *const *const argv,
5495                               const char *const defaut, const char *const usage, const bool reset_static) {
5496       static bool first = true, visu = false;
5497       if (reset_static) { first = true; return 0; }
5498       const char *res = 0;
5499       if (first) {
5500         first = false;
5501         visu = cimg::option("-h",argc,argv,(char*)0,(char*)0,false)!=0;
5502         visu |= cimg::option("-help",argc,argv,(char*)0,(char*)0,false)!=0;
5503         visu |= cimg::option("--help",argc,argv,(char*)0,(char*)0,false)!=0;
5504       }
5505       if (!name && visu) {
5506         if (usage) {
5507           std::fprintf(cimg::output(),"\n %s%s%s",cimg::t_red,cimg::basename(argv[0]),cimg::t_normal);
5508           std::fprintf(cimg::output()," : %s",usage);
5509           std::fprintf(cimg::output()," (%s, %s)\n\n",__DATE__,__TIME__);
5510         }
5511         if (defaut) std::fprintf(cimg::output(),"%s\n",defaut);
5512       }
5513       if (name) {
5514         if (argc>0) {
5515           int k = 0;
5516           while (k<argc && std::strcmp(argv[k],name)) ++k;
5517           res = (k++==argc?defaut:(k==argc?argv[--k]:argv[k]));
5518         } else res = defaut;
5519         if (visu && usage) std::fprintf(cimg::output(),"    %s%-16s%s %-24s %s%s%s\n",
5520                                         cimg::t_bold,name,cimg::t_normal,res?res:"0",cimg::t_green,usage,cimg::t_normal);
5521       }
5522       return res;
5523     }
5524 
5525     inline const char* option(const char *const name, const int argc, const char *const *const argv,
5526                               const char *const defaut, const char *const usage=0) {
5527       return option(name,argc,argv,defaut,usage,false);
5528     }
5529 
5530     inline bool option(const char *const name, const int argc, const char *const *const argv,
5531                        const bool defaut, const char *const usage=0) {
5532       const char *const s = cimg::option(name,argc,argv,(char*)0);
5533       const bool res = s?(cimg::strcasecmp(s,"false") && cimg::strcasecmp(s,"off") && cimg::strcasecmp(s,"0")):defaut;
5534       cimg::option(name,0,0,res?"true":"false",usage);
5535       return res;
5536     }
5537 
5538     inline int option(const char *const name, const int argc, const char *const *const argv,
5539                       const int defaut, const char *const usage=0) {
5540       const char *const s = cimg::option(name,argc,argv,(char*)0);
5541       const int res = s?std::atoi(s):defaut;
5542       char tmp[256] = { 0 };
5543       cimg_snprintf(tmp,sizeof(tmp),"%d",res);
5544       cimg::option(name,0,0,tmp,usage);
5545       return res;
5546     }
5547 
5548     inline char option(const char *const name, const int argc, const char *const *const argv,
5549                        const char defaut, const char *const usage=0) {
5550       const char *const s = cimg::option(name,argc,argv,(char*)0);
5551       const char res = s?*s:defaut;
5552       char tmp[8] = { 0 };
5553       *tmp = res;
5554       cimg::option(name,0,0,tmp,usage);
5555       return res;
5556     }
5557 
5558     inline float option(const char *const name, const int argc, const char *const *const argv,
5559                         const float defaut, const char *const usage=0) {
5560       const char *const s = cimg::option(name,argc,argv,(char*)0);
5561       const float res = s?(float)cimg::atof(s):defaut;
5562       char tmp[256] = { 0 };
5563       cimg_snprintf(tmp,sizeof(tmp),"%g",res);
5564       cimg::option(name,0,0,tmp,usage);
5565       return res;
5566     }
5567 
5568     inline double option(const char *const name, const int argc, const char *const *const argv,
5569                          const double defaut, const char *const usage=0) {
5570       const char *const s = cimg::option(name,argc,argv,(char*)0);
5571       const double res = s?cimg::atof(s):defaut;
5572       char tmp[256] = { 0 };
5573       cimg_snprintf(tmp,sizeof(tmp),"%g",res);
5574       cimg::option(name,0,0,tmp,usage);
5575       return res;
5576     }
5577 
5578     inline const char* argument(const unsigned int nb, const int argc, const char *const *const argv, const unsigned int nb_singles=0, ...) {
5579       for (int k = 1, pos = 0; k<argc;) {
5580         const char *const item = argv[k];
5581         bool option = (*item=='-'), single_option = false;
5582         if (option) {
5583           va_list ap;
5584           va_start(ap,nb_singles);
5585           for (unsigned int i = 0; i<nb_singles; ++i) if (!cimg::strcasecmp(item,va_arg(ap,char*))) { single_option = true; break; }
5586           va_end(ap);
5587         }
5588         if (option) { ++k; if (!single_option) ++k; }
5589         else { if (pos++==(int)nb) return item; else ++k; }
5590       }
5591       return 0;
5592     }
5593 
5594     //! Print informations about \CImg environement variables.
5595     /**
5596        \note Output is done on the default output stream.
5597     **/
5598     inline void info() {
5599       char tmp[1024] = { 0 };
5600       std::fprintf(cimg::output(),"\n %s%sCImg Library %u.%u.%u%s, compiled %s ( %s ) with the following flags :\n\n",
5601                    cimg::t_red,cimg::t_bold,cimg_version/100,(cimg_version/10)%10,cimg_version%10,
5602                    cimg::t_normal,__DATE__,__TIME__);
5603 
5604       std::fprintf(cimg::output(),"  > Operating System :       %s%-13s%s %s('cimg_OS'=%d)%s\n",
5605                    cimg::t_bold,
5606                    cimg_OS==1?"Unix":(cimg_OS==2?"Windows":"Unknow"),
5607                    cimg::t_normal,cimg::t_green,
5608                    cimg_OS,
5609                    cimg::t_normal);
5610 
5611       std::fprintf(cimg::output(),"  > CPU endianness :         %s%s Endian%s\n",
5612                    cimg::t_bold,
5613                    cimg::endianness()?"Big":"Little",
5614                    cimg::t_normal);
5615 
5616       std::fprintf(cimg::output(),"  > Verbosity mode :         %s%-13s%s %s('cimg_verbosity'=%d)%s\n",
5617                    cimg::t_bold,
5618                    cimg_verbosity==0?"Quiet":(cimg_verbosity==1?"Console":(cimg_verbosity==2?"Dialog":(cimg_verbosity==3?"Console+Warnings":"Dialog+Warnings"))),
5619                    cimg::t_normal,cimg::t_green,
5620                    cimg_verbosity,
5621                    cimg::t_normal);
5622 
5623       std::fprintf(cimg::output(),"  > Stricts warnings :       %s%-13s%s %s('cimg_strict_warnings' %s)%s\n",
5624                    cimg::t_bold,
5625 #ifdef cimg_strict_warnings
5626                    "Yes",cimg::t_normal,cimg::t_green,"defined",
5627 #else
5628                    "No",cimg::t_normal,cimg::t_green,"undefined",
5629 #endif
5630                    cimg::t_normal);
5631 
5632       std::fprintf(cimg::output(),"  > Using VT100 messages :   %s%-13s%s %s('cimg_use_vt100' %s)%s\n",
5633                    cimg::t_bold,
5634 #ifdef cimg_use_vt100
5635                    "Yes",cimg::t_normal,cimg::t_green,"defined",
5636 #else
5637                    "No",cimg::t_normal,cimg::t_green,"undefined",
5638 #endif
5639                    cimg::t_normal);
5640 
5641       std::fprintf(cimg::output(),"  > Display type :           %s%-13s%s %s('cimg_display'=%d)%s\n",
5642                    cimg::t_bold,
5643                    cimg_display==0?"No display":cimg_display==1?"X11":cimg_display==2?"Windows GDI":"Unknown",
5644                    cimg::t_normal,cimg::t_green,
5645                    cimg_display,
5646                    cimg::t_normal);
5647 
5648 #if cimg_display==1
5649       std::fprintf(cimg::output(),"  > Using XShm for X11 :     %s%-13s%s %s('cimg_use_xshm' %s)%s\n",
5650                    cimg::t_bold,
5651 #ifdef cimg_use_xshm
5652                    "Yes",cimg::t_normal,cimg::t_green,"defined",
5653 #else
5654                    "No",cimg::t_normal,cimg::t_green,"undefined",
5655 #endif
5656                    cimg::t_normal);
5657 
5658       std::fprintf(cimg::output(),"  > Using XRand for X11 :    %s%-13s%s %s('cimg_use_xrandr' %s)%s\n",
5659                    cimg::t_bold,
5660 #ifdef cimg_use_xrandr
5661                    "Yes",cimg::t_normal,cimg::t_green,"defined",
5662 #else
5663                    "No",cimg::t_normal,cimg::t_green,"undefined",
5664 #endif
5665                    cimg::t_normal);
5666 #endif
5667       std::fprintf(cimg::output(),"  > Using OpenMP :           %s%-13s%s %s('cimg_use_openmp' %s)%s\n",
5668                    cimg::t_bold,
5669 #ifdef cimg_use_openmp
5670                    "Yes",cimg::t_normal,cimg::t_green,"defined",
5671 #else
5672                    "No",cimg::t_normal,cimg::t_green,"undefined",
5673 #endif
5674                    cimg::t_normal);
5675       std::fprintf(cimg::output(),"  > Using PNG library :      %s%-13s%s %s('cimg_use_png' %s)%s\n",
5676                    cimg::t_bold,
5677 #ifdef cimg_use_png
5678                    "Yes",cimg::t_normal,cimg::t_green,"defined",
5679 #else
5680                    "No",cimg::t_normal,cimg::t_green,"undefined",
5681 #endif
5682                    cimg::t_normal);
5683       std::fprintf(cimg::output(),"  > Using JPEG library :     %s%-13s%s %s('cimg_use_jpeg' %s)%s\n",
5684                    cimg::t_bold,
5685 #ifdef cimg_use_jpeg
5686                    "Yes",cimg::t_normal,cimg::t_green,"defined",
5687 #else
5688                    "No",cimg::t_normal,cimg::t_green,"undefined",
5689 #endif
5690                    cimg::t_normal);
5691 
5692       std::fprintf(cimg::output(),"  > Using TIFF library :     %s%-13s%s %s('cimg_use_tiff' %s)%s\n",
5693                    cimg::t_bold,
5694 #ifdef cimg_use_tiff
5695                    "Yes",cimg::t_normal,cimg::t_green,"defined",
5696 #else
5697                    "No",cimg::t_normal,cimg::t_green,"undefined",
5698 #endif
5699                    cimg::t_normal);
5700 
5701       std::fprintf(cimg::output(),"  > Using Magick++ library : %s%-13s%s %s('cimg_use_magick' %s)%s\n",
5702                    cimg::t_bold,
5703 #ifdef cimg_use_magick
5704                    "Yes",cimg::t_normal,cimg::t_green,"defined",
5705 #else
5706                    "No",cimg::t_normal,cimg::t_green,"undefined",
5707 #endif
5708                    cimg::t_normal);
5709 
5710       std::fprintf(cimg::output(),"  > Using FFTW3 library :    %s%-13s%s %s('cimg_use_fftw3' %s)%s\n",
5711                    cimg::t_bold,
5712 #ifdef cimg_use_fftw3
5713                    "Yes",cimg::t_normal,cimg::t_green,"defined",
5714 #else
5715                    "No",cimg::t_normal,cimg::t_green,"undefined",
5716 #endif
5717                    cimg::t_normal);
5718 
5719       std::fprintf(cimg::output(),"  > Using LAPACK library :   %s%-13s%s %s('cimg_use_lapack' %s)%s\n",
5720                    cimg::t_bold,
5721 #ifdef cimg_use_lapack
5722                    "Yes",cimg::t_normal,cimg::t_green,"defined",
5723 #else
5724                    "No",cimg::t_normal,cimg::t_green,"undefined",
5725 #endif
5726                    cimg::t_normal);
5727 
5728       cimg_snprintf(tmp,sizeof(tmp),"\"%.1020s\"",cimg::imagemagick_path());
5729       std::fprintf(cimg::output(),"  > Path of ImageMagick :    %s%-13s%s\n",
5730                    cimg::t_bold,
5731                    tmp,
5732                    cimg::t_normal);
5733 
5734       cimg_snprintf(tmp,sizeof(tmp),"\"%.1020s\"",cimg::graphicsmagick_path());
5735       std::fprintf(cimg::output(),"  > Path of GraphicsMagick : %s%-13s%s\n",
5736                    cimg::t_bold,
5737                    tmp,
5738                    cimg::t_normal);
5739 
5740       cimg_snprintf(tmp,sizeof(tmp),"\"%.1020s\"",cimg::medcon_path());
5741       std::fprintf(cimg::output(),"  > Path of 'medcon' :       %s%-13s%s\n",
5742                    cimg::t_bold,
5743                    tmp,
5744                    cimg::t_normal);
5745 
5746       cimg_snprintf(tmp,sizeof(tmp),"\"%.1020s\"",cimg::temporary_path());
5747       std::fprintf(cimg::output(),"  > Temporary path :         %s%-13s%s\n",
5748                    cimg::t_bold,
5749                    tmp,
5750                    cimg::t_normal);
5751 
5752       std::fprintf(cimg::output(),"\n");
5753     }
5754 
5755     // Declare LAPACK function signatures if LAPACK support is enabled.
5756 #ifdef cimg_use_lapack
5757     template<typename T>
5758     inline void getrf(int &N, T *lapA, int *IPIV, int &INFO) {
5759       dgetrf_(&N,&N,lapA,&N,IPIV,&INFO);
5760     }
5761 
5762     inline void getrf(int &N, float *lapA, int *IPIV, int &INFO) {
5763       sgetrf_(&N,&N,lapA,&N,IPIV,&INFO);
5764     }
5765 
5766     template<typename T>
5767     inline void getri(int &N, T *lapA, int *IPIV, T* WORK, int &LWORK, int &INFO) {
5768       dgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO);
5769     }
5770 
5771     inline void getri(int &N, float *lapA, int *IPIV, float* WORK, int &LWORK, int &INFO) {
5772       sgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO);
5773     }
5774 
5775     template<typename T>
5776     inline void gesvd(char &JOB, int &M, int &N, T *lapA, int &MN,
5777                       T *lapS, T *lapU, T *lapV, T *WORK, int &LWORK, int &INFO) {
5778       dgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO);
5779     }
5780 
5781     inline void gesvd(char &JOB, int &M, int &N, float *lapA, int &MN,
5782                       float *lapS, float *lapU, float *lapV, float *WORK, int &LWORK, int &INFO) {
5783       sgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO);
5784     }
5785 
5786     template<typename T>
5787     inline void getrs(char &TRANS, int &N, T *lapA, int *IPIV, T *lapB, int &INFO) {
5788       int one = 1;
5789       dgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO);
5790     }
5791 
5792     inline void getrs(char &TRANS, int &N, float *lapA, int *IPIV, float *lapB, int &INFO) {
5793       int one = 1;
5794       sgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO);
5795     }
5796 
5797     template<typename T>
5798     inline void syev(char &JOB, char &UPLO, int &N, T *lapA, T *lapW, T *WORK, int &LWORK, int &INFO) {
5799       dsyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO);
5800     }
5801 
5802     inline void syev(char &JOB, char &UPLO, int &N, float *lapA, float *lapW, float *WORK, int &LWORK, int &INFO) {
5803       ssyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO);
5804     }
5805 
5806     template<typename T>
5807     inline void sgels(char & TRANS, int &M, int &N, int &NRHS, T* lapA, int &LDA,
5808                       T* lapB, int &LDB, T* WORK, int &LWORK, int &INFO){
5809       dgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO);
5810     }
5811 
5812     inline void sgels(char & TRANS, int &M, int &N, int &NRHS, float* lapA, int &LDA,
5813                       float* lapB, int &LDB, float* WORK, int &LWORK, int &INFO){
5814       sgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO);
5815     }
5816 
5817 #endif
5818 
5819     // End of the 'cimg' namespace
5820   }
5821 
5822   /*------------------------------------------------
5823    #
5824    #
5825    #   Definition of mathematical operators and
5826    #   external functions.
5827    #
5828    #
5829    -------------------------------------------------*/
5830 
5831 #define _cimg_create_ext_operators(typ) \
5832   template<typename T> \
5833   inline CImg<typename cimg::superset<T,typ>::type> operator+(const typ val, const CImg<T>& img) { \
5834     return img + val; \
5835   } \
5836   template<typename T> \
5837   inline CImg<typename cimg::superset<T,typ>::type> operator-(const typ val, const CImg<T>& img) { \
5838     typedef typename cimg::superset<T,typ>::type Tt; \
5839     return CImg<Tt>(img._width,img._height,img._depth,img._spectrum,val)-=img; \
5840   } \
5841   template<typename T> \
5842   inline CImg<typename cimg::superset<T,typ>::type> operator*(const typ val, const CImg<T>& img) { \
5843     return img*val; \
5844   } \
5845   template<typename T> \
5846   inline CImg<typename cimg::superset<T,typ>::type> operator/(const typ val, const CImg<T>& img) { \
5847     return val*img.get_invert(); \
5848   } \
5849   template<typename T> \
5850   inline CImg<typename cimg::superset<T,typ>::type> operator&(const typ val, const CImg<T>& img) { \
5851     return img & val; \
5852   } \
5853   template<typename T> \
5854   inline CImg<typename cimg::superset<T,typ>::type> operator|(const typ val, const CImg<T>& img) { \
5855     return img | val; \
5856   } \
5857   template<typename T> \
5858   inline CImg<typename cimg::superset<T,typ>::type> operator^(const typ val, const CImg<T>& img) { \
5859     return img ^ val; \
5860   } \
5861   template<typename T> \
5862   inline bool operator==(const typ val, const CImg<T>& img) {   \
5863     return img == val; \
5864   } \
5865   template<typename T> \
5866   inline bool operator!=(const typ val, const CImg<T>& img) { \
5867     return img != val; \
5868   }
5869 
5870   _cimg_create_ext_operators(bool)
5871   _cimg_create_ext_operators(unsigned char)
5872   _cimg_create_ext_operators(char)
5873   _cimg_create_ext_operators(signed char)
5874   _cimg_create_ext_operators(unsigned short)
5875   _cimg_create_ext_operators(short)
5876   _cimg_create_ext_operators(unsigned int)
5877   _cimg_create_ext_operators(int)
5878   _cimg_create_ext_operators(unsigned long)
5879   _cimg_create_ext_operators(long)
5880   _cimg_create_ext_operators(float)
5881   _cimg_create_ext_operators(double)
5882 
5883   template<typename T>
5884   inline CImg<_cimg_Tfloat> operator+(const char *const expression, const CImg<T>& img) {
5885     return img + expression;
5886   }
5887 
5888   template<typename T>
5889   inline CImg<_cimg_Tfloat> operator-(const char *const expression, const CImg<T>& img) {
5890     return CImg<_cimg_Tfloat>(img._width,img._height,img._depth,img._spectrum,expression,true)-=img;
5891   }
5892 
5893   template<typename T>
5894   inline CImg<_cimg_Tfloat> operator*(const char *const expression, const CImg<T>& img) {
5895     return img*expression;
5896   }
5897 
5898   template<typename T>
5899   inline CImg<_cimg_Tfloat> operator/(const char *const expression, const CImg<T>& img) {
5900     return expression*img.get_invert();
5901   }
5902 
5903   template<typename T>
5904   inline CImg<T> operator&(const char *const expression, const CImg<T>& img) {
5905     return img & expression;
5906   }
5907 
5908   template<typename T>
5909   inline CImg<T> operator|(const char *const expression, const CImg<T>& img) {
5910     return img | expression;
5911   }
5912 
5913   template<typename T>
5914   inline CImg<T> operator^(const char *const expression, const CImg<T>& img) {
5915     return img ^ expression;
5916   }
5917 
5918   template<typename T>
5919   inline bool operator==(const char *const expression, const CImg<T>& img) {
5920     return img == expression;
5921   }
5922 
5923   template<typename T>
5924   inline bool operator!=(const char *const expression, const CImg<T>& img) {
5925     return img != expression;
5926   }
5927 
5928   template<typename T>
5929   inline CImg<_cimg_Tfloat> sqr(const CImg<T>& instance) {
5930     return instance.get_sqr();
5931   }
5932 
5933   template<typename T>
5934   inline CImg<_cimg_Tfloat> sqrt(const CImg<T>& instance) {
5935     return instance.get_sqrt();
5936   }
5937 
5938   template<typename T>
5939   inline CImg<_cimg_Tfloat> exp(const CImg<T>& instance) {
5940     return instance.get_exp();
5941   }
5942 
5943   template<typename T>
5944   inline CImg<_cimg_Tfloat> log(const CImg<T>& instance) {
5945     return instance.get_log();
5946   }
5947 
5948   template<typename T>
5949   inline CImg<_cimg_Tfloat> log2(const CImg<T>& instance) {
5950     return instance.get_log2();
5951   }
5952 
5953   template<typename T>
5954   inline CImg<_cimg_Tfloat> log10(const CImg<T>& instance) {
5955     return instance.get_log10();
5956   }
5957 
5958   template<typename T>
5959   inline CImg<_cimg_Tfloat> abs(const CImg<T>& instance) {
5960     return instance.get_abs();
5961   }
5962 
5963   template<typename T>
5964   inline CImg<_cimg_Tfloat> sign(const CImg<T>& instance) {
5965     return instance.get_sign();
5966   }
5967 
5968   template<typename T>
5969   inline CImg<_cimg_Tfloat> cos(const CImg<T>& instance) {
5970     return instance.get_cos();
5971   }
5972 
5973   template<typename T>
5974   inline CImg<_cimg_Tfloat> sin(const CImg<T>& instance) {
5975     return instance.get_sin();
5976   }
5977 
5978   template<typename T>
5979   inline CImg<_cimg_Tfloat> sinc(const CImg<T>& instance) {
5980     return instance.get_sinc();
5981   }
5982 
5983   template<typename T>
5984   inline CImg<_cimg_Tfloat> tan(const CImg<T>& instance) {
5985     return instance.get_tan();
5986   }
5987 
5988   template<typename T>
5989   inline CImg<_cimg_Tfloat> acos(const CImg<T>& instance) {
5990     return instance.get_acos();
5991   }
5992 
5993   template<typename T>
5994   inline CImg<_cimg_Tfloat> asin(const CImg<T>& instance) {
5995     return instance.get_asin();
5996   }
5997 
5998   template<typename T>
5999   inline CImg<_cimg_Tfloat> atan(const CImg<T>& instance) {
6000     return instance.get_atan();
6001   }
6002 
6003   template<typename T>
6004   inline CImg<_cimg_Tfloat> cosh(const CImg<T>& instance) {
6005     return instance.get_cosh();
6006   }
6007 
6008   template<typename T>
6009   inline CImg<_cimg_Tfloat> sinh(const CImg<T>& instance) {
6010     return instance.get_sinh();
6011   }
6012 
6013   template<typename T>
6014   inline CImg<_cimg_Tfloat> tanh(const CImg<T>& instance) {
6015     return instance.get_tanh();
6016   }
6017 
6018   template<typename T>
6019   inline CImg<T> transpose(const CImg<T>& instance) {
6020     return instance.get_transpose();
6021   }
6022 
6023   template<typename T>
6024   inline CImg<_cimg_Tfloat> invert(const CImg<T>& instance) {
6025     return instance.get_invert();
6026   }
6027 
6028   template<typename T>
6029   inline CImg<_cimg_Tfloat> pseudoinvert(const CImg<T>& instance) {
6030     return instance.get_pseudoinvert();
6031   }
6032 
6033   /*-----------------------------------
6034    #
6035    # Define the CImgDisplay structure
6036    #
6037    ----------------------------------*/
6038   //! Allow to create windows, display images on them and manage user events (keyboard, mouse and windows events).
6039   /**
6040      CImgDisplay methods rely on a low-level graphic library to perform : it can be either \b X-Window (X11, for Unix-based systems)
6041      or \b GDI32 (for Windows-based systems).
6042      If both libraries are missing, CImgDisplay will not be able to display images on screen, and will enter a minimal mode
6043      where warning messages will be outputed each time the program is trying to call one of the CImgDisplay method.
6044 
6045      The configuration variable \c cimg_display tells about the graphic library used.
6046      It is set automatically by \CImg when one of these graphic libraries has been detected.
6047      But, you can override its value if necessary. Valid choices are :
6048      - 0 : Disable display capabilities.
6049      - 1 : Use \b X-Window (X11) library.
6050      - 2 : Use \b GDI32 library.
6051 
6052      Remember to link your program against \b X11 or \b GDI32 libraries if you use CImgDisplay.
6053   **/
6054   struct CImgDisplay {
6055     unsigned long _timer, _fps_frames, _fps_timer;
6056     unsigned int _width, _height, _normalization;
6057     float _fps_fps, _min, _max;
6058     bool _is_fullscreen;
6059     char *_title;
6060     volatile unsigned int _window_width, _window_height, _button, _keys[128], _released_keys[128];
6061     volatile int _window_x, _window_y, _mouse_x, _mouse_y, _wheel;
6062     volatile bool _is_closed, _is_resized, _is_moved, _is_event,
6063       _is_keyESC, _is_keyF1, _is_keyF2, _is_keyF3, _is_keyF4, _is_keyF5, _is_keyF6, _is_keyF7,
6064       _is_keyF8, _is_keyF9, _is_keyF10, _is_keyF11, _is_keyF12, _is_keyPAUSE, _is_key1, _is_key2,
6065       _is_key3, _is_key4, _is_key5, _is_key6, _is_key7, _is_key8, _is_key9, _is_key0,
6066       _is_keyBACKSPACE, _is_keyINSERT, _is_keyHOME, _is_keyPAGEUP, _is_keyTAB, _is_keyQ, _is_keyW, _is_keyE,
6067       _is_keyR, _is_keyT, _is_keyY, _is_keyU, _is_keyI, _is_keyO, _is_keyP, _is_keyDELETE,
6068       _is_keyEND, _is_keyPAGEDOWN, _is_keyCAPSLOCK, _is_keyA, _is_keyS, _is_keyD, _is_keyF, _is_keyG,
6069       _is_keyH, _is_keyJ, _is_keyK, _is_keyL, _is_keyENTER, _is_keySHIFTLEFT, _is_keyZ, _is_keyX,
6070       _is_keyC, _is_keyV, _is_keyB, _is_keyN, _is_keyM, _is_keySHIFTRIGHT, _is_keyARROWUP, _is_keyCTRLLEFT,
6071       _is_keyAPPLEFT, _is_keyALT, _is_keySPACE, _is_keyALTGR, _is_keyAPPRIGHT, _is_keyMENU, _is_keyCTRLRIGHT, _is_keyARROWLEFT,
6072       _is_keyARROWDOWN, _is_keyARROWRIGHT, _is_keyPAD0, _is_keyPAD1, _is_keyPAD2, _is_keyPAD3, _is_keyPAD4, _is_keyPAD5,
6073       _is_keyPAD6, _is_keyPAD7, _is_keyPAD8, _is_keyPAD9, _is_keyPADADD, _is_keyPADSUB, _is_keyPADMUL, _is_keyPADDIV;
6074 
6075     //@}
6076     //---------------------------
6077     //
6078     //! \name Plugins
6079     //@{
6080     //---------------------------
6081 
6082 #ifdef cimgdisplay_plugin
6083 #include cimgdisplay_plugin
6084 #endif
6085 #ifdef cimgdisplay_plugin1
6086 #include cimgdisplay_plugin1
6087 #endif
6088 #ifdef cimgdisplay_plugin2
6089 #include cimgdisplay_plugin2
6090 #endif
6091 #ifdef cimgdisplay_plugin3
6092 #include cimgdisplay_plugin3
6093 #endif
6094 #ifdef cimgdisplay_plugin4
6095 #include cimgdisplay_plugin4
6096 #endif
6097 #ifdef cimgdisplay_plugin5
6098 #include cimgdisplay_plugin5
6099 #endif
6100 #ifdef cimgdisplay_plugin6
6101 #include cimgdisplay_plugin6
6102 #endif
6103 #ifdef cimgdisplay_plugin7
6104 #include cimgdisplay_plugin7
6105 #endif
6106 #ifdef cimgdisplay_plugin8
6107 #include cimgdisplay_plugin8
6108 #endif
6109 
6110     //@}
6111     //--------------------------------------------------------
6112     //
6113     //! \name Constructors / Destructor / Instance Management
6114     //@{
6115     //--------------------------------------------------------
6116 
6117     //! Destructor.
6118     /**
6119        \note If the associated window is visible on the screen, it is closed by the call to the destructor.
6120     **/
6121     ~CImgDisplay() {
6122       assign();
6123     }
6124 
6125     //! Construct an empty display.
6126     /**
6127        \note Constructing an empty CImgDisplay instance does not make a window appearing on the screen, until
6128        display of valid data is performed.
6129        \par Example
6130        \code
6131        CImgDisplay disp;  // Does actually nothing.
6132        ...
6133        disp.display(img); // Construct new window and display image in it.
6134        \endcode
6135     **/
6136     CImgDisplay():
6137       _width(0),_height(0),_normalization(0),
6138       _min(0),_max(0),
6139       _is_fullscreen(false),
6140       _title(0),
6141       _window_width(0),_window_height(0),_button(0),
6142       _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0),
6143       _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
6144       assign();
6145     }
6146 
6147     //! Construct a display with specified dimensions.
6148     /** \param width Window width.
6149         \param height Window height.
6150         \param title Window title.
6151         \param normalization Normalization type (<tt>0</tt>=none, <tt>1</tt>=always, <tt>2</tt>=once, <tt>3</tt>=pixel type-dependent, see normalization()).
6152         \param is_fullscreen Tells if fullscreen mode is enabled.
6153         \param is_closed Tells if associated window is initially visible or not.
6154         \note A black background is initially displayed on the associated window.
6155     **/
6156     CImgDisplay(const unsigned int width, const unsigned int height,
6157                 const char *const title=0, const unsigned int normalization=3,
6158                 const bool is_fullscreen=false, const bool is_closed=false):
6159       _width(0),_height(0),_normalization(0),
6160       _min(0),_max(0),
6161       _is_fullscreen(false),
6162       _title(0),
6163       _window_width(0),_window_height(0),_button(0),
6164       _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0),
6165       _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
6166       assign(width,height,title,normalization,is_fullscreen,is_closed);
6167     }
6168 
6169     //! Construct a display from an image.
6170     /** \param img Image used as a model to create the window.
6171         \param title Window title.
6172         \param normalization Normalization type (<tt>0</tt>=none, <tt>1</tt>=always, <tt>2</tt>=once, <tt>3</tt>=pixel type-dependent, see normalization()).
6173         \param is_fullscreen Tells if fullscreen mode is enabled.
6174         \param is_closed Tells if associated window is initially visible or not.
6175         \note The pixels of the input image are initially displayed on the associated window.
6176     **/
6177     template<typename T>
6178     explicit CImgDisplay(const CImg<T>& img,
6179                          const char *const title=0, const unsigned int normalization=3,
6180                          const bool is_fullscreen=false, const bool is_closed=false):
6181       _width(0),_height(0),_normalization(0),
6182       _min(0),_max(0),
6183       _is_fullscreen(false),
6184       _title(0),
6185       _window_width(0),_window_height(0),_button(0),
6186       _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0),
6187       _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
6188       assign(img,title,normalization,is_fullscreen,is_closed);
6189     }
6190 
6191     //! Construct a display from an image list.
6192     /** \param list The images list to display.
6193         \param title Window title.
6194         \param normalization Normalization type (<tt>0</tt>=none, <tt>1</tt>=always, <tt>2</tt>=once, <tt>3</tt>=pixel type-dependent, see normalization()).
6195         \param is_fullscreen Tells if fullscreen mode is enabled.
6196         \param is_closed Tells if associated window is initially visible or not.
6197         \note All images of the list, appended along the X-axis, are initially displayed on the associated window.
6198     **/
6199     template<typename T>
6200     explicit CImgDisplay(const CImgList<T>& list,
6201                          const char *const title=0, const unsigned int normalization=3,
6202                          const bool is_fullscreen=false, const bool is_closed=false):
6203       _width(0),_height(0),_normalization(0),
6204       _min(0),_max(0),
6205       _is_fullscreen(false),
6206       _title(0),
6207       _window_width(0),_window_height(0),_button(0),
6208       _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0),
6209       _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
6210       assign(list,title,normalization,is_fullscreen,is_closed);
6211     }
6212 
6213     //! Construct a display as a copy of an existing one.
6214     /**
6215         \param disp  : Display instance to copy.
6216         \note The pixel buffer of the input window is initially displayed on the associated window.
6217     **/
6218     CImgDisplay(const CImgDisplay& disp):
6219       _width(0),_height(0),_normalization(0),
6220       _min(0),_max(0),
6221       _is_fullscreen(false),
6222       _title(0),
6223       _window_width(0),_window_height(0),_button(0),
6224       _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0),
6225       _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
6226       assign(disp);
6227     }
6228 
6229 #if cimg_display==0
6230 
6231     static void _no_display_exception() {
6232       throw CImgDisplayException("CImgDisplay() : No display available.");
6233     }
6234 
6235     //! Destructor - Empty constructor \inplace.
6236     /**
6237        \note Replace the current instance by an empty display.
6238     **/
6239     CImgDisplay& assign() {
6240       return flush();
6241     }
6242 
6243     //! Construct a display with specified dimensions \inplace.
6244     /**
6245     **/
6246     CImgDisplay& assign(const unsigned int width, const unsigned int height,
6247                         const char *const title=0, const unsigned int normalization=3,
6248                         const bool is_fullscreen=false, const bool is_closed=false) {
6249       cimg::unused(width,height,title,normalization,is_fullscreen,is_closed);
6250       _no_display_exception();
6251       return assign();
6252     }
6253 
6254     //! Construct a display from an image \inplace.
6255     /**
6256     **/
6257     template<typename T>
6258     CImgDisplay& assign(const CImg<T>& img,
6259                         const char *const title=0, const unsigned int normalization=3,
6260                         const bool is_fullscreen=false, const bool is_closed=false) {
6261       _no_display_exception();
6262       return assign(img._width,img._height,title,normalization,is_fullscreen,is_closed);
6263     }
6264 
6265     //! Construct a display from an image list \inplace.
6266     /**
6267     **/
6268     template<typename T>
6269     CImgDisplay& assign(const CImgList<T>& list,
6270                         const char *const title=0, const unsigned int normalization=3,
6271                         const bool is_fullscreen=false, const bool is_closed=false) {
6272       _no_display_exception();
6273       return assign(list._width,list._width,title,normalization,is_fullscreen,is_closed);
6274     }
6275 
6276     //! Construct a display as a copy of another one \inplace.
6277     /**
6278     **/
6279     CImgDisplay& assign(const CImgDisplay &disp) {
6280       _no_display_exception();
6281       return assign(disp._width,disp._height);
6282     }
6283 
6284 #endif
6285 
6286     //! Return a reference to an empty display.
6287     /**
6288        \note Can be useful for writing function prototypes where one of the argument (of type CImgDisplay&)
6289        must have a default value.
6290        \par Example
6291        \code
6292        void foo(CImgDisplay& disp=CImgDisplay::empty());
6293        \endcode
6294     **/
6295     static CImgDisplay& empty() {
6296       static CImgDisplay _empty;
6297       return _empty.assign();
6298     }
6299 
6300 #define cimg_fitscreen(dx,dy,dz) CImgDisplay::_fitscreen(dx,dy,dz,128,-85,false),CImgDisplay::_fitscreen(dx,dy,dz,128,-85,true)
6301     static unsigned int _fitscreen(const unsigned int dx, const unsigned int dy, const unsigned int dz,
6302                                    const int dmin, const int dmax,const bool return_y) {
6303       const unsigned int _nw = dx + (dz>1?dz:0), _nh = dy + (dz>1?dz:0);
6304       unsigned int nw = _nw?_nw:1, nh = _nh?_nh:1;
6305       const unsigned int
6306         sw = CImgDisplay::screen_width(), sh = CImgDisplay::screen_height(),
6307         mw = dmin<0?(unsigned int)(sw*-dmin/100):(unsigned int)dmin,
6308         mh = dmin<0?(unsigned int)(sh*-dmin/100):(unsigned int)dmin,
6309         Mw = dmax<0?(unsigned int)(sw*-dmax/100):(unsigned int)dmax,
6310         Mh = dmax<0?(unsigned int)(sh*-dmax/100):(unsigned int)dmax;
6311       if (nw<mw) { nh = nh*mw/nw; nh+=(nh==0?1:0); nw = mw; }
6312       if (nh<mh) { nw = nw*mh/nh; nw+=(nw==0?1:0); nh = mh; }
6313       if (nw>Mw) { nh = nh*Mw/nw; nh+=(nh==0?1:0); nw = Mw; }
6314       if (nh>Mh) { nw = nw*Mh/nh; nw+=(nw==0?1:0); nh = Mh; }
6315       if (nw<mw) nw = mw;
6316       if (nh<mh) nh = mh;
6317       return return_y?nh:nw;
6318     }
6319 
6320     //@}
6321     //------------------------------------------
6322     //
6323     //! \name Overloaded Operators
6324     //@{
6325     //------------------------------------------
6326 
6327     //! Display image on associated window.
6328     /**
6329        \note <tt>disp = img</tt> is equivalent to <tt>disp.display(img)</tt>.
6330     **/
6331     template<typename t>
6332     CImgDisplay& operator=(const CImg<t>& img) {
6333       return display(img);
6334     }
6335 
6336     //! Display list of images on associated window.
6337     /**
6338        \note <tt>disp = list</tt> is equivalent to <tt>disp.display(list)</tt>.
6339     **/
6340     template<typename t>
6341     CImgDisplay& operator=(const CImgList<t>& list) {
6342       return display(list);
6343     }
6344 
6345     //! Construct a display as a copy of another one \inplace.
6346     /**
6347        \note Equivalent to assign(const CImgDisplay&).
6348      **/
6349     CImgDisplay& operator=(const CImgDisplay& disp) {
6350       return assign(disp);
6351     }
6352 
6353     //! Return \c false if display is empty, \c true otherwise.
6354     /**
6355        \note <tt>if (disp) { ... }</tt> is equivalent to <tt>if (!disp.is_empty()) { ... }</tt>.
6356     **/
6357     operator bool() const {
6358       return !is_empty();
6359     }
6360 
6361     //@}
6362     //------------------------------------------
6363     //
6364     //! \name Instance Checking
6365     //@{
6366     //------------------------------------------
6367 
6368     //! Return \c true if display is empty, \c false otherwise.
6369     /**
6370     **/
6371     bool is_empty() const {
6372       return !(_width && _height);
6373     }
6374 
6375     //! Return \c true if display is closed (i.e. not visible on the screen), \c false otherwise.
6376     /**
6377        \note
6378        - When a user physically closes the associated window, the display is set to closed.
6379        - A closed display is not destroyed. Its associated window can be show again on the screen using show().
6380     **/
6381     bool is_closed() const {
6382       return _is_closed;
6383     }
6384 
6385     //! Return \c true if associated window has been resized on the screen, \c false otherwise.
6386     /**
6387     **/
6388     bool is_resized() const {
6389       return _is_resized;
6390     }
6391 
6392     //! Return \c true if associated window has been moved on the screen, \c false otherwise.
6393     /**
6394     **/
6395     bool is_moved() const {
6396       return _is_moved;
6397     }
6398 
6399     //! Return \c true if any event has occured on the associated window, \c false otherwise.
6400     /**
6401     **/
6402     bool is_event() const {
6403       return _is_event;
6404     }
6405 
6406     //! Return \c true if current display is in fullscreen mode, \c false otherwise.
6407     /**
6408     **/
6409     bool is_fullscreen() const {
6410       return _is_fullscreen;
6411     }
6412 
6413     //! Return \c true if any key is being pressed on the associated window, \c false otherwise.
6414     /**
6415        \note The methods below do the same only for specific keys.
6416     **/
6417     bool is_key() const {
6418       return _is_keyESC || _is_keyF1 || _is_keyF2 || _is_keyF3 ||
6419         _is_keyF4 || _is_keyF5 || _is_keyF6 || _is_keyF7 ||
6420         _is_keyF8 || _is_keyF9 || _is_keyF10 || _is_keyF11 ||
6421         _is_keyF12 || _is_keyPAUSE || _is_key1 || _is_key2 ||
6422         _is_key3 || _is_key4 || _is_key5 || _is_key6 ||
6423         _is_key7 || _is_key8 || _is_key9 || _is_key0 ||
6424         _is_keyBACKSPACE || _is_keyINSERT || _is_keyHOME ||
6425         _is_keyPAGEUP || _is_keyTAB || _is_keyQ || _is_keyW ||
6426         _is_keyE || _is_keyR || _is_keyT || _is_keyY ||
6427         _is_keyU || _is_keyI || _is_keyO || _is_keyP ||
6428         _is_keyDELETE || _is_keyEND || _is_keyPAGEDOWN ||
6429         _is_keyCAPSLOCK || _is_keyA || _is_keyS || _is_keyD ||
6430         _is_keyF || _is_keyG || _is_keyH || _is_keyJ ||
6431         _is_keyK || _is_keyL || _is_keyENTER ||
6432         _is_keySHIFTLEFT || _is_keyZ || _is_keyX || _is_keyC ||
6433         _is_keyV || _is_keyB || _is_keyN || _is_keyM ||
6434         _is_keySHIFTRIGHT || _is_keyARROWUP || _is_keyCTRLLEFT ||
6435         _is_keyAPPLEFT || _is_keyALT || _is_keySPACE || _is_keyALTGR ||
6436         _is_keyAPPRIGHT || _is_keyMENU || _is_keyCTRLRIGHT ||
6437         _is_keyARROWLEFT || _is_keyARROWDOWN || _is_keyARROWRIGHT ||
6438         _is_keyPAD0 || _is_keyPAD1 || _is_keyPAD2 ||
6439         _is_keyPAD3 || _is_keyPAD4 || _is_keyPAD5 ||
6440         _is_keyPAD6 || _is_keyPAD7 || _is_keyPAD8 ||
6441         _is_keyPAD9 || _is_keyPADADD || _is_keyPADSUB ||
6442         _is_keyPADMUL || _is_keyPADDIV;
6443     }
6444 
6445     //! Return \c true if key specified by given keycode is being pressed on the associated window, \c false otherwise.
6446     /**
6447        \param keycode Keycode to test.
6448        \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
6449        your code stay portable (see cimg::keyESC).
6450        \par Example
6451        \code
6452        CImgDisplay disp(400,400);
6453        while (!disp.is_closed()) {
6454          if (disp.key(cimg::keyTAB)) { ... }  // Equivalent to 'if (disp.is_keyTAB())'.
6455          disp.wait();
6456        }
6457        \endcode
6458     **/
6459     bool is_key(const unsigned int keycode) const {
6460 #define _cimg_iskey_test(k) if (keycode==cimg::key##k) return _is_key##k;
6461       _cimg_iskey_test(ESC); _cimg_iskey_test(F1); _cimg_iskey_test(F2); _cimg_iskey_test(F3);
6462       _cimg_iskey_test(F4); _cimg_iskey_test(F5); _cimg_iskey_test(F6); _cimg_iskey_test(F7);
6463       _cimg_iskey_test(F8); _cimg_iskey_test(F9); _cimg_iskey_test(F10); _cimg_iskey_test(F11);
6464       _cimg_iskey_test(F12); _cimg_iskey_test(PAUSE); _cimg_iskey_test(1); _cimg_iskey_test(2);
6465       _cimg_iskey_test(3); _cimg_iskey_test(4); _cimg_iskey_test(5); _cimg_iskey_test(6);
6466       _cimg_iskey_test(7); _cimg_iskey_test(8); _cimg_iskey_test(9); _cimg_iskey_test(0);
6467       _cimg_iskey_test(BACKSPACE); _cimg_iskey_test(INSERT); _cimg_iskey_test(HOME);
6468       _cimg_iskey_test(PAGEUP); _cimg_iskey_test(TAB); _cimg_iskey_test(Q); _cimg_iskey_test(W);
6469       _cimg_iskey_test(E); _cimg_iskey_test(R); _cimg_iskey_test(T); _cimg_iskey_test(Y);
6470       _cimg_iskey_test(U); _cimg_iskey_test(I); _cimg_iskey_test(O); _cimg_iskey_test(P);
6471       _cimg_iskey_test(DELETE); _cimg_iskey_test(END); _cimg_iskey_test(PAGEDOWN);
6472       _cimg_iskey_test(CAPSLOCK); _cimg_iskey_test(A); _cimg_iskey_test(S); _cimg_iskey_test(D);
6473       _cimg_iskey_test(F); _cimg_iskey_test(G); _cimg_iskey_test(H); _cimg_iskey_test(J);
6474       _cimg_iskey_test(K); _cimg_iskey_test(L); _cimg_iskey_test(ENTER);
6475       _cimg_iskey_test(SHIFTLEFT); _cimg_iskey_test(Z); _cimg_iskey_test(X); _cimg_iskey_test(C);
6476       _cimg_iskey_test(V); _cimg_iskey_test(B); _cimg_iskey_test(N); _cimg_iskey_test(M);
6477       _cimg_iskey_test(SHIFTRIGHT); _cimg_iskey_test(ARROWUP); _cimg_iskey_test(CTRLLEFT);
6478       _cimg_iskey_test(APPLEFT); _cimg_iskey_test(ALT); _cimg_iskey_test(SPACE); _cimg_iskey_test(ALTGR);
6479       _cimg_iskey_test(APPRIGHT); _cimg_iskey_test(MENU); _cimg_iskey_test(CTRLRIGHT);
6480       _cimg_iskey_test(ARROWLEFT); _cimg_iskey_test(ARROWDOWN); _cimg_iskey_test(ARROWRIGHT);
6481       _cimg_iskey_test(PAD0); _cimg_iskey_test(PAD1); _cimg_iskey_test(PAD2);
6482       _cimg_iskey_test(PAD3); _cimg_iskey_test(PAD4); _cimg_iskey_test(PAD5);
6483       _cimg_iskey_test(PAD6); _cimg_iskey_test(PAD7); _cimg_iskey_test(PAD8);
6484       _cimg_iskey_test(PAD9); _cimg_iskey_test(PADADD); _cimg_iskey_test(PADSUB);
6485       _cimg_iskey_test(PADMUL); _cimg_iskey_test(PADDIV);
6486       return false;
6487     }
6488 
6489     //! Return \c true if key specified by given keycode is being pressed on the associated window, \c false otherwise.
6490     /**
6491        \param keycode C-string containing the keycode label of the key to test.
6492        \note Use it when the key you want to test can be dynamically set by the user.
6493        \par Example
6494        \code
6495        CImgDisplay disp(400,400);
6496        const char *const keycode = "TAB";
6497        while (!disp.is_closed()) {
6498          if (disp.is_key(keycode)) { ... }  // Equivalent to 'if (disp.is_keyTAB())'.
6499          disp.wait();
6500        }
6501        \endcode
6502     **/
6503     bool is_key(const char *const keycode) const {
6504 #define _cimg_iskey_test2(k) if (!cimg::strcasecmp(keycode,#k)) return _is_key##k;
6505       _cimg_iskey_test2(ESC); _cimg_iskey_test2(F1); _cimg_iskey_test2(F2); _cimg_iskey_test2(F3);
6506       _cimg_iskey_test2(F4); _cimg_iskey_test2(F5); _cimg_iskey_test2(F6); _cimg_iskey_test2(F7);
6507       _cimg_iskey_test2(F8); _cimg_iskey_test2(F9); _cimg_iskey_test2(F10); _cimg_iskey_test2(F11);
6508       _cimg_iskey_test2(F12); _cimg_iskey_test2(PAUSE); _cimg_iskey_test2(1); _cimg_iskey_test2(2);
6509       _cimg_iskey_test2(3); _cimg_iskey_test2(4); _cimg_iskey_test2(5); _cimg_iskey_test2(6);
6510       _cimg_iskey_test2(7); _cimg_iskey_test2(8); _cimg_iskey_test2(9); _cimg_iskey_test2(0);
6511       _cimg_iskey_test2(BACKSPACE); _cimg_iskey_test2(INSERT); _cimg_iskey_test2(HOME);
6512       _cimg_iskey_test2(PAGEUP); _cimg_iskey_test2(TAB); _cimg_iskey_test2(Q); _cimg_iskey_test2(W);
6513       _cimg_iskey_test2(E); _cimg_iskey_test2(R); _cimg_iskey_test2(T); _cimg_iskey_test2(Y);
6514       _cimg_iskey_test2(U); _cimg_iskey_test2(I); _cimg_iskey_test2(O); _cimg_iskey_test2(P);
6515       _cimg_iskey_test2(DELETE); _cimg_iskey_test2(END); _cimg_iskey_test2(PAGEDOWN);
6516       _cimg_iskey_test2(CAPSLOCK); _cimg_iskey_test2(A); _cimg_iskey_test2(S); _cimg_iskey_test2(D);
6517       _cimg_iskey_test2(F); _cimg_iskey_test2(G); _cimg_iskey_test2(H); _cimg_iskey_test2(J);
6518       _cimg_iskey_test2(K); _cimg_iskey_test2(L); _cimg_iskey_test2(ENTER);
6519       _cimg_iskey_test2(SHIFTLEFT); _cimg_iskey_test2(Z); _cimg_iskey_test2(X); _cimg_iskey_test2(C);
6520       _cimg_iskey_test2(V); _cimg_iskey_test2(B); _cimg_iskey_test2(N); _cimg_iskey_test2(M);
6521       _cimg_iskey_test2(SHIFTRIGHT); _cimg_iskey_test2(ARROWUP); _cimg_iskey_test2(CTRLLEFT);
6522       _cimg_iskey_test2(APPLEFT); _cimg_iskey_test2(ALT); _cimg_iskey_test2(SPACE); _cimg_iskey_test2(ALTGR);
6523       _cimg_iskey_test2(APPRIGHT); _cimg_iskey_test2(MENU); _cimg_iskey_test2(CTRLRIGHT);
6524       _cimg_iskey_test2(ARROWLEFT); _cimg_iskey_test2(ARROWDOWN); _cimg_iskey_test2(ARROWRIGHT);
6525       _cimg_iskey_test2(PAD0); _cimg_iskey_test2(PAD1); _cimg_iskey_test2(PAD2);
6526       _cimg_iskey_test2(PAD3); _cimg_iskey_test2(PAD4); _cimg_iskey_test2(PAD5);
6527       _cimg_iskey_test2(PAD6); _cimg_iskey_test2(PAD7); _cimg_iskey_test2(PAD8);
6528       _cimg_iskey_test2(PAD9); _cimg_iskey_test2(PADADD); _cimg_iskey_test2(PADSUB);
6529       _cimg_iskey_test2(PADMUL); _cimg_iskey_test2(PADDIV);
6530       return false;
6531     }
6532 
6533     //! Return \c true if specified key sequence has been typed on the associated window, \c false otherwise.
6534     /**
6535        \param keycodes_sequence Buffer of keycodes to test.
6536        \param length Number of keys in the \c keycodes_sequence buffer.
6537        \param remove_sequence Tells if the key sequence must be removed from the key history, if found.
6538        \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
6539        your code stay portable (see cimg::keyESC).
6540        \par Example
6541        \code
6542        CImgDisplay disp(400,400);
6543        const unsigned int key_seq[] = { cimg::keyCTRLLEFT, cimg::keyD };
6544        while (!disp.is_closed()) {
6545          if (disp.is_key_sequence(key_seq,2)) { ... }  // Test for the 'CTRL+D' keyboard event.
6546          disp.wait();
6547        }
6548        \endcode
6549     **/
6550     bool is_key_sequence(const unsigned int *const keycodes_sequence, const unsigned int length, const bool remove_sequence=false) {
6551       if (keycodes_sequence && length) {
6552         const unsigned int
6553           *const ps_end = keycodes_sequence + length - 1,
6554           *const pk_end = (unsigned int*)_keys + 1 + sizeof(_keys)/sizeof(unsigned int) - length,
6555           k = *ps_end;
6556         for (unsigned int *pk = (unsigned int*)_keys; pk<pk_end; ) {
6557           if (*(pk++)==k) {
6558             bool res = true;
6559             const unsigned int *ps = ps_end, *pk2 = pk;
6560             for (unsigned int i = 1; i<length; ++i) res = (*(--ps)==*(pk2++));
6561             if (res) {
6562               if (remove_sequence) std::memset((void*)(pk-1),0,sizeof(unsigned int)*length);
6563               return true;
6564             }
6565           }
6566         }
6567       }
6568       return false;
6569     }
6570 
6571 #define _cimg_iskey_def(k) \
6572     bool is_key##k() const { \
6573       return _is_key##k; \
6574     }
6575 
6576     //! Return \c true if the \c ESC key is being pressed on the associated window, \c false otherwise.
6577     /**
6578        \note Similar methods exist for all keys managed by \CImg (see cimg::keyESC).
6579     **/
6580     _cimg_iskey_def(ESC); _cimg_iskey_def(F1); _cimg_iskey_def(F2); _cimg_iskey_def(F3);
6581     _cimg_iskey_def(F4); _cimg_iskey_def(F5); _cimg_iskey_def(F6); _cimg_iskey_def(F7);
6582     _cimg_iskey_def(F8); _cimg_iskey_def(F9); _cimg_iskey_def(F10); _cimg_iskey_def(F11);
6583     _cimg_iskey_def(F12); _cimg_iskey_def(PAUSE); _cimg_iskey_def(1); _cimg_iskey_def(2);
6584     _cimg_iskey_def(3); _cimg_iskey_def(4); _cimg_iskey_def(5); _cimg_iskey_def(6);
6585     _cimg_iskey_def(7); _cimg_iskey_def(8); _cimg_iskey_def(9); _cimg_iskey_def(0);
6586     _cimg_iskey_def(BACKSPACE); _cimg_iskey_def(INSERT); _cimg_iskey_def(HOME);
6587     _cimg_iskey_def(PAGEUP); _cimg_iskey_def(TAB); _cimg_iskey_def(Q); _cimg_iskey_def(W);
6588     _cimg_iskey_def(E); _cimg_iskey_def(R); _cimg_iskey_def(T); _cimg_iskey_def(Y);
6589     _cimg_iskey_def(U); _cimg_iskey_def(I); _cimg_iskey_def(O); _cimg_iskey_def(P);
6590     _cimg_iskey_def(DELETE); _cimg_iskey_def(END); _cimg_iskey_def(PAGEDOWN);
6591     _cimg_iskey_def(CAPSLOCK); _cimg_iskey_def(A); _cimg_iskey_def(S); _cimg_iskey_def(D);
6592     _cimg_iskey_def(F); _cimg_iskey_def(G); _cimg_iskey_def(H); _cimg_iskey_def(J);
6593     _cimg_iskey_def(K); _cimg_iskey_def(L); _cimg_iskey_def(ENTER);
6594     _cimg_iskey_def(SHIFTLEFT); _cimg_iskey_def(Z); _cimg_iskey_def(X); _cimg_iskey_def(C);
6595     _cimg_iskey_def(V); _cimg_iskey_def(B); _cimg_iskey_def(N); _cimg_iskey_def(M);
6596     _cimg_iskey_def(SHIFTRIGHT); _cimg_iskey_def(ARROWUP); _cimg_iskey_def(CTRLLEFT);
6597     _cimg_iskey_def(APPLEFT); _cimg_iskey_def(ALT); _cimg_iskey_def(SPACE); _cimg_iskey_def(ALTGR);
6598     _cimg_iskey_def(APPRIGHT); _cimg_iskey_def(MENU); _cimg_iskey_def(CTRLRIGHT);
6599     _cimg_iskey_def(ARROWLEFT); _cimg_iskey_def(ARROWDOWN); _cimg_iskey_def(ARROWRIGHT);
6600     _cimg_iskey_def(PAD0); _cimg_iskey_def(PAD1); _cimg_iskey_def(PAD2);
6601     _cimg_iskey_def(PAD3); _cimg_iskey_def(PAD4); _cimg_iskey_def(PAD5);
6602     _cimg_iskey_def(PAD6); _cimg_iskey_def(PAD7); _cimg_iskey_def(PAD8);
6603     _cimg_iskey_def(PAD9); _cimg_iskey_def(PADADD); _cimg_iskey_def(PADSUB);
6604     _cimg_iskey_def(PADMUL); _cimg_iskey_def(PADDIV);
6605 
6606     //@}
6607     //------------------------------------------
6608     //
6609     //! \name Instance Characteristics
6610     //@{
6611     //------------------------------------------
6612 
6613 #if cimg_display==0
6614 
6615     //! Return width of the screen (current resolution along the X-axis).
6616     /**
6617     **/
6618     static int screen_width() {
6619       _no_display_exception();
6620       return 0;
6621     }
6622 
6623     //! Return height of the screen (current resolution along the Y-axis).
6624     /**
6625     **/
6626     static int screen_height() {
6627       _no_display_exception();
6628       return 0;
6629     }
6630 
6631 #endif
6632 
6633     //! Return display width.
6634     /**
6635        \note The width of the display (i.e. the width of the pixel data buffer associated to the CImgDisplay instance)
6636        may be different from the actual width of the associated window.
6637     **/
6638     int width() const {
6639       return (int)_width;
6640     }
6641 
6642     //! Return display height.
6643     /**
6644        \note The height of the display (i.e. the height of the pixel data buffer associated to the CImgDisplay instance)
6645        may be different from the actual height of the associated window.
6646     **/
6647     int height() const {
6648       return (int)_height;
6649     }
6650 
6651     //! Return normalization type of the display.
6652     /**
6653        The normalization type tells about how the values of an input image are normalized by the CImgDisplay to be correctly displayed.
6654        The range of values for pixels displayed on screen is <tt>[0,255]</tt>. If the range of values of the data to display
6655        is different, a normalization may be required for displaying the data in a correct way.
6656        The normalization type can be one of :
6657        - \c 0 : Value normalization is disabled. It is then assumed that all input data to be displayed by the CImgDisplay instance
6658        have values in range <tt>[0,255]</tt>.
6659        - \c 1 : Value normalization is always performed (this is the default behavior).
6660        Before displaying an input image, its values will be (virtually) stretched
6661        in range <tt>[0,255]</tt>, so that the contrast of the displayed pixels will be maximum.
6662        Use this mode for images whose minimum and maximum values are not prescribed to known values (e.g. float-valued images).
6663        Note that when normalized versions of images are computed for display purposes, the actual values of these images are not modified.
6664        - \c 2 : Value normalization is performed once (on the first image display), then the same normalization coefficients are kept for
6665        next displayed frames.
6666        - \c 3 : Value normalization depends on the pixel type of the data to display. For integer pixel types, the normalization
6667        is done regarding the minimum/maximum values of the type (no normalization occurs then for <tt>unsigned char</tt>).
6668        For float-valued pixel types, the normalization is done regarding the minimum/maximum value of the image data instead.
6669 
6670     **/
6671     unsigned int normalization() const {
6672       return _normalization;
6673     }
6674 
6675     //! Return title of the associated window as a C-string.
6676     /**
6677        \note Window title may be not visible, depending on the used window manager or if the current display is in fullscreen mode.
6678     **/
6679     const char *title() const {
6680       return _title;
6681     }
6682 
6683     //! Return width of the associated window.
6684     /**
6685        \note The width of the display (i.e. the width of the pixel data buffer associated to the CImgDisplay instance)
6686        may be different from the actual width of the associated window.
6687     **/
6688     int window_width() const {
6689       return (int)_window_width;
6690     }
6691 
6692     //! Return height of the associated window.
6693     /**
6694        \note The height of the display (i.e. the height of the pixel data buffer associated to the CImgDisplay instance)
6695        may be different from the actual height of the associated window.
6696     **/
6697     int window_height() const {
6698       return (int)_window_height;
6699     }
6700 
6701     //! Return X-coordinate of the associated window.
6702     /**
6703        \note The returned coordinate corresponds to the location of the upper-left corner of the associated window.
6704     **/
6705     int window_x() const {
6706       return _window_x;
6707     }
6708 
6709     //! Return Y-coordinate of the associated window.
6710     /**
6711        \note The returned coordinate corresponds to the location of the upper-left corner of the associated window.
6712     **/
6713     int window_y() const {
6714       return _window_y;
6715     }
6716 
6717     //! Return X-coordinate of the mouse pointer.
6718     /**
6719        \note
6720        - If the mouse pointer is outside window area, \c -1 is returned.
6721        - Otherwise, the returned value is in the range [0,width()-1].
6722     **/
6723     int mouse_x() const {
6724       return _mouse_x;
6725     }
6726 
6727     //! Return Y-coordinate of the mouse pointer.
6728     /**
6729        \note
6730        - If the mouse pointer is outside window area, \c -1 is returned.
6731        - Otherwise, the returned value is in the range [0,height()-1].
6732     **/
6733     int mouse_y() const {
6734       return _mouse_y;
6735     }
6736 
6737     //! Return current state of the mouse buttons.
6738     /**
6739        \note Three mouse buttons can be managed. If one button is pressed, its corresponding bit in the returned value is set :
6740        - bit \c 0 (value \c 0x1) : State of the left mouse button.
6741        - bit \c 1 (value \c 0x2) : State of the right mouse button.
6742        - bit \c 2 (value \c 0x4) : State of the middle mouse button.
6743 
6744        Several bits can be activated if more than one button are pressed at the same time.
6745        \par Example
6746        \code
6747        CImgDisplay disp(400,400);
6748        while (!disp.is_closed()) {
6749          if (disp.button()&1) { // Left button clicked.
6750            ...
6751          }
6752          if (disp.button()&2) { // Right button clicked.
6753            ...
6754          }
6755          if (disp.button()&4) { // Middle button clicked.
6756            ...
6757          }
6758          disp.wait();
6759        }
6760        \endcode
6761     **/
6762     unsigned int button() const {
6763       return _button;
6764     }
6765 
6766     //! Return current state of the mouse wheel.
6767     /**
6768        \note
6769        - The returned value can be positive or negative depending on whether the mouse wheel has been scrolled forward or backward.
6770        - Scrolling the wheel forward add \c 1 to the wheel value.
6771        - Scrolling the wheel backward substract \c 1 to the wheel value.
6772        - The returned value cumulates the number of forward of backward scrolls since the creation of the display, or since the
6773        last reset of the wheel value (using set_wheel()). It is strongly recommended to quickly reset the wheel counter
6774        when an action has been performed regarding the current wheel value. Otherwise, the returned wheel value may be for instance \c 0
6775        despite the fact that many scrolls have been done (as many in forward as in backward directions).
6776        \par Example
6777        \code
6778        CImgDisplay disp(400,400);
6779        while (!disp.is_closed()) {
6780          if (disp.wheel()) {
6781            int counter = disp.wheel();  // Read the state of the mouse wheel.
6782            ...                          // Do what you want with 'counter'.
6783            disp.set_wheel();            // Reset the wheel value to 0.
6784          }
6785          disp.wait();
6786        }
6787        \endcode
6788     **/
6789     int wheel() const {
6790       return _wheel;
6791     }
6792 
6793     //! Return one entry from the pressed keys history.
6794     /**
6795        \param pos Indice to read from the pressed keys history (indice \c 0 corresponds to latest entry).
6796        \return Keycode of a pressed key or \c 0 for a released key.
6797        \note
6798        - Each CImgDisplay stores a history of the pressed keys in a buffer of size \c 128. When a new key is pressed,
6799        its keycode is stored in the pressed keys history. When a key is released, \c 0 is put instead.
6800        This means that up to the 64 last pressed keys may be read from the pressed keys history.
6801        When a new value is stored, the pressed keys history is shifted so that the latest entry is always
6802        stored at position \c 0.
6803        - Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
6804        your code stay portable (see cimg::keyESC).
6805     **/
6806     unsigned int key(const unsigned int pos=0) const {
6807       return pos<(sizeof(_keys)/sizeof(unsigned int))?_keys[pos]:0;
6808     }
6809 
6810     //! Return one entry from the released keys history.
6811     /**
6812        \param pos Indice to read from the released keys history (indice \c 0 corresponds to latest entry).
6813        \return Keycode of a released key or \c 0 for a pressed key.
6814        \note
6815        - Each CImgDisplay stores a history of the released keys in a buffer of size \c 128. When a new key is released,
6816        its keycode is stored in the pressed keys history. When a key is pressed, \c 0 is put instead.
6817        This means that up to the 64 last released keys may be read from the released keys history.
6818        When a new value is stored, the released keys history is shifted so that the latest entry is always
6819        stored at position \c 0.
6820        - Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
6821        your code stay portable (see cimg::keyESC).
6822     **/
6823     unsigned int released_key(const unsigned int pos=0) const {
6824       return pos<(sizeof(_released_keys)/sizeof(unsigned int))?_released_keys[pos]:0;
6825     }
6826 
6827     //! Return keycode corresponding to the specified string.
6828     /**
6829        \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
6830        your code stay portable (see cimg::keyESC).
6831        \par Example
6832        \code
6833        const unsigned int keyTAB = CImgDisplay::keycode("TAB");  // Return cimg::keyTAB.
6834        \endcode
6835     **/
6836     static unsigned int keycode(const char *const keycode) {
6837 #define _cimg_keycode(k) if (!cimg::strcasecmp(keycode,#k)) return cimg::key##k;
6838       _cimg_keycode(ESC); _cimg_keycode(F1); _cimg_keycode(F2); _cimg_keycode(F3);
6839       _cimg_keycode(F4); _cimg_keycode(F5); _cimg_keycode(F6); _cimg_keycode(F7);
6840       _cimg_keycode(F8); _cimg_keycode(F9); _cimg_keycode(F10); _cimg_keycode(F11);
6841       _cimg_keycode(F12); _cimg_keycode(PAUSE); _cimg_keycode(1); _cimg_keycode(2);
6842       _cimg_keycode(3); _cimg_keycode(4); _cimg_keycode(5); _cimg_keycode(6);
6843       _cimg_keycode(7); _cimg_keycode(8); _cimg_keycode(9); _cimg_keycode(0);
6844       _cimg_keycode(BACKSPACE); _cimg_keycode(INSERT); _cimg_keycode(HOME);
6845       _cimg_keycode(PAGEUP); _cimg_keycode(TAB); _cimg_keycode(Q); _cimg_keycode(W);
6846       _cimg_keycode(E); _cimg_keycode(R); _cimg_keycode(T); _cimg_keycode(Y);
6847       _cimg_keycode(U); _cimg_keycode(I); _cimg_keycode(O); _cimg_keycode(P);
6848       _cimg_keycode(DELETE); _cimg_keycode(END); _cimg_keycode(PAGEDOWN);
6849       _cimg_keycode(CAPSLOCK); _cimg_keycode(A); _cimg_keycode(S); _cimg_keycode(D);
6850       _cimg_keycode(F); _cimg_keycode(G); _cimg_keycode(H); _cimg_keycode(J);
6851       _cimg_keycode(K); _cimg_keycode(L); _cimg_keycode(ENTER);
6852       _cimg_keycode(SHIFTLEFT); _cimg_keycode(Z); _cimg_keycode(X); _cimg_keycode(C);
6853       _cimg_keycode(V); _cimg_keycode(B); _cimg_keycode(N); _cimg_keycode(M);
6854       _cimg_keycode(SHIFTRIGHT); _cimg_keycode(ARROWUP); _cimg_keycode(CTRLLEFT);
6855       _cimg_keycode(APPLEFT); _cimg_keycode(ALT); _cimg_keycode(SPACE); _cimg_keycode(ALTGR);
6856       _cimg_keycode(APPRIGHT); _cimg_keycode(MENU); _cimg_keycode(CTRLRIGHT);
6857       _cimg_keycode(ARROWLEFT); _cimg_keycode(ARROWDOWN); _cimg_keycode(ARROWRIGHT);
6858       _cimg_keycode(PAD0); _cimg_keycode(PAD1); _cimg_keycode(PAD2);
6859       _cimg_keycode(PAD3); _cimg_keycode(PAD4); _cimg_keycode(PAD5);
6860       _cimg_keycode(PAD6); _cimg_keycode(PAD7); _cimg_keycode(PAD8);
6861       _cimg_keycode(PAD9); _cimg_keycode(PADADD); _cimg_keycode(PADSUB);
6862       _cimg_keycode(PADMUL); _cimg_keycode(PADDIV);
6863       return 0;
6864     }
6865 
6866     //! Return the current refresh rate, in frames per second.
6867     /**
6868        \note Returns a significant value when the current instance is used to display successive frames.
6869        It measures the delay between successive calls to frames_per_second().
6870     **/
6871     float frames_per_second() {
6872       if (!_fps_timer) _fps_timer = cimg::time();
6873       const float delta = (cimg::time()-_fps_timer)/1000.0f;
6874       ++_fps_frames;
6875       if (delta>=1) {
6876         _fps_fps = _fps_frames/delta;
6877         _fps_frames = 0;
6878         _fps_timer = cimg::time();
6879       }
6880       return _fps_fps;
6881     }
6882 
6883     //@}
6884     //---------------------------------------
6885     //
6886     //! \name Window Manipulation
6887     //@{
6888     //---------------------------------------
6889 
6890 #if cimg_display==0
6891 
6892     //! Display image on associated window.
6893     /**
6894        \param img Input image to display.
6895        \note This method returns immediately.
6896     **/
6897     template<typename T>
6898     CImgDisplay& display(const CImg<T>& img) {
6899       return assign(img);
6900     }
6901 
6902 #endif
6903 
6904     //! Display list of images on associated window.
6905     /**
6906        \param list List of images to display.
6907        \param axis Axis used to append the images along, for the visualization (can be \c x, \c y, \c z or \c c).
6908        \param align Relative position of aligned images when displaying lists with images of different sizes
6909        (\c 0 for upper-left, \c 0.5 for centering and \c 1 for lower-right).
6910        \note This method returns immediately.
6911     **/
6912     template<typename T>
6913     CImgDisplay& display(const CImgList<T>& list, const char axis='x', const float align=0) {
6914       return display(list.get_append(axis,align));
6915     }
6916 
6917 #if cimg_display==0
6918 
6919     //! Show (closed) associated window on the screen.
6920     /**
6921        \note
6922        - Force the associated window of a display to be visible on the screen, even if it has been closed before.
6923        - Using show() on a visible display does nothing.
6924     **/
6925     CImgDisplay& show() {
6926       return assign();
6927     }
6928 
6929     //! Close (visible) associated window and make it disappear from the screen.
6930     /**
6931        \note
6932        - A closed display only means the associated window is not visible anymore. This does not mean the display has been destroyed.
6933        Use show() to make the associated window reappear.
6934        - Using close() on a closed display does nothing.
6935     **/
6936     CImgDisplay& close() {
6937       return assign();
6938     }
6939 
6940     //! Move associated window to a new location.
6941     /**
6942        \param pos_x X-coordinate of the new window location.
6943        \param pos_y Y-coordinate of the new window location.
6944        \note Depending on the window manager behavior, this method may not succeed (no exceptions are thrown nevertheless).
6945     **/
6946     CImgDisplay& move(const int pos_x, const int pos_y) {
6947       return assign(pos_x,pos_y);
6948     }
6949 
6950 #endif
6951 
6952     //! Resize display to the size of the associated window.
6953     /**
6954        \param force_redraw Tells if the previous window content must be updated and refreshed as well.
6955        \note
6956        - Calling this method ensures that width() and window_width() become equal, as well as height() and window_height().
6957        - The associated window is also resized to specified dimensions.
6958     **/
6959     CImgDisplay& resize(const bool force_redraw=true) {
6960       resize(_window_width,_window_height,force_redraw);
6961       return *this;
6962     }
6963 
6964 #if cimg_display==0
6965 
6966     //! Resize display to the specified size.
6967     /**
6968        \param width Requested display width.
6969        \param height Requested display height.
6970        \param force_redraw Tells if the previous window content must be updated and refreshed as well.
6971        \note The associated window is also resized to specified dimensions.
6972     **/
6973     CImgDisplay& resize(const int width, const int height, const bool force_redraw=true) {
6974       return assign(width,height,0,3,force_redraw);
6975     }
6976 
6977 #endif
6978 
6979     //! Resize display to the size of an input image.
6980     /**
6981        \param img Input image to take size from.
6982        \param force_redraw Tells if the previous window content must be resized and updated as well.
6983        \note
6984        - Calling this method ensures that width() and <tt>img.width()</tt> become equal, as well as height() and <tt>img.height()</tt>.
6985        - The associated window is also resized to specified dimensions.
6986     **/
6987     template<typename T>
6988     CImgDisplay& resize(const CImg<T>& img, const bool force_redraw=true) {
6989       return resize(img._width,img._height,force_redraw);
6990     }
6991 
6992     //! Resize display to the size of another CImgDisplay instance.
6993     /**
6994        \param disp Input display to take size from.
6995        \param force_redraw Tells if the previous window content must be resized and updated as well.
6996        \note
6997        - Calling this method ensures that width() and <tt>disp.width()</tt> become equal, as well as height() and <tt>disp.height()</tt>.
6998        - The associated window is also resized to specified dimensions.
6999     **/
7000     CImgDisplay& resize(const CImgDisplay& disp, const bool force_redraw=true) {
7001       return resize(disp._width,disp._height,force_redraw);
7002     }
7003 
7004     // [internal] Render pixel buffer with size (wd,hd) from source buffer of size (ws,hs).
7005     template<typename t, typename T>
7006     static void _render_resize(const T *ptrs, const unsigned int ws, const unsigned int hs,
7007                                t *ptrd, const unsigned int wd, const unsigned int hd) {
7008       unsigned int *const offx = new unsigned int[wd], *const offy = new unsigned int[hd+1], *poffx, *poffy;
7009       float s, curr, old;
7010       s = (float)ws/wd;
7011       poffx = offx; curr = 0; for (unsigned int x = 0; x<wd; ++x) { old = curr; curr+=s; *(poffx++) = (unsigned int)curr - (unsigned int)old; }
7012       s = (float)hs/hd;
7013       poffy = offy; curr = 0; for (unsigned int y = 0; y<hd; ++y) { old = curr; curr+=s; *(poffy++) = ws*((unsigned int)curr - (unsigned int)old); }
7014       *poffy = 0;
7015       poffy = offy;
7016       for (unsigned int y = 0; y<hd; ) {
7017         const T *ptr = ptrs;
7018         poffx = offx;
7019         for (unsigned int x = 0; x<wd; ++x) { *(ptrd++) = *ptr; ptr+=*(poffx++); }
7020         ++y;
7021         unsigned int dy = *(poffy++);
7022         for ( ; !dy && y<hd; std::memcpy(ptrd,ptrd - wd,sizeof(t)*wd), ++y, ptrd+=wd, dy = *(poffy++)) {}
7023         ptrs+=dy;
7024       }
7025       delete[] offx; delete[] offy;
7026     }
7027 
7028     //! Set normalization type.
7029     /**
7030        \param normalization New normalization mode.
7031     **/
7032     CImgDisplay& set_normalization(const unsigned int normalization) {
7033       _normalization = normalization;
7034       _min = _max = 0;
7035       return *this;
7036     }
7037 
7038 #if cimg_display==0
7039 
7040     //! Set title of the associated window.
7041     /**
7042        \param format C-string containing the format of the title, as with <tt>std::printf()</tt>.
7043        \warning As the first argument is a format string, it is highly recommended to write
7044        \code
7045        disp.set_title("%s",window_title);
7046        \endcode
7047        instead of
7048        \code
7049        disp.set_title(window_title);
7050        \endcode
7051        if \c window_title can be arbitrary, to prevent nasty memory access.
7052     **/
7053     CImgDisplay& set_title(const char *const format, ...) {
7054       return assign(0,0,format);
7055     }
7056 
7057 #endif
7058 
7059     //! Enable or disable fullscreen mode.
7060     /**
7061        \param is_fullscreen Tells is the fullscreen mode must be activated or not.
7062        \param force_redraw Tells if the previous window content must be displayed as well.
7063        \note
7064        - When the fullscreen mode is enabled, the associated window fills the entire screen but the size of the current display
7065        is not modified.
7066        - The screen resolution may be switched to fit the associated window size and ensure it appears the largest as possible.
7067        For X-Window (X11) users, the configuration flag \c cimg_use_xrandr has to be set to allow the screen resolution change
7068        (requires the X11 extensions to be enabled).
7069     **/
7070     CImgDisplay& set_fullscreen(const bool is_fullscreen, const bool force_redraw=true) {
7071       if (is_empty() || _is_fullscreen==is_fullscreen) return *this;
7072       return toggle_fullscreen(force_redraw);
7073     }
7074 
7075 #if cimg_display==0
7076 
7077     //! Toggle fullscreen mode.
7078     /**
7079        \param force_redraw Tells if the previous window content must be displayed as well.
7080        \note Enable fullscreen mode if it was not enabled, and disable it otherwise.
7081     **/
7082     CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
7083       return assign(_width,_height,0,3,force_redraw);
7084     }
7085 
7086     //! Show mouse pointer.
7087     /**
7088        \note Depending on the window manager behavior, this method may not succeed (no exceptions are thrown nevertheless).
7089     **/
7090     CImgDisplay& show_mouse() {
7091       return assign();
7092     }
7093 
7094     //! Hide mouse pointer.
7095     /**
7096        \note Depending on the window manager behavior, this method may not succeed (no exceptions are thrown nevertheless).
7097     **/
7098     CImgDisplay& hide_mouse() {
7099       return assign();
7100     }
7101 
7102     //! Move mouse pointer to a specified location.
7103     /**
7104        \note Depending on the window manager behavior, this method may not succeed (no exceptions are thrown nevertheless).
7105     **/
7106     CImgDisplay& set_mouse(const int pos_x, const int pos_y) {
7107       return assign(pos_x,pos_y);
7108     }
7109 
7110 #endif
7111 
7112     //! Simulate a mouse button release event.
7113     /**
7114        \note All mouse buttons are considered released at the same time.
7115     **/
7116     CImgDisplay& set_button() {
7117       _button = 0;
7118       _is_event = true;
7119       return *this;
7120     }
7121 
7122     //! Simulate a mouse button press or release event.
7123     /**
7124        \param button Buttons event code, where each button is associated to a single bit.
7125        \param is_pressed Tells if the mouse button is considered as pressed or released.
7126     **/
7127     CImgDisplay& set_button(const unsigned int button, const bool is_pressed=true) {
7128       const unsigned int buttoncode = button==1?1:button==2?2:button==3?4:0;
7129       if (is_pressed) _button |= buttoncode; else _button &= ~buttoncode;
7130       _is_event = buttoncode?true:false;
7131       return *this;
7132     }
7133 
7134     //! Flush all mouse wheel events.
7135     /**
7136        \note Make wheel() to return \c 0, if called afterwards.
7137     **/
7138     CImgDisplay& set_wheel() {
7139       _wheel = 0;
7140       _is_event = true;
7141       return *this;
7142     }
7143 
7144     //! Simulate a wheel event.
7145     /**
7146        \param amplitude Amplitude of the wheel scrolling to simulate.
7147        \note Make wheel() to return \c amplitude, if called afterwards.
7148     **/
7149     CImgDisplay& set_wheel(const int amplitude) {
7150       _wheel+=amplitude;
7151       _is_event = amplitude?true:false;
7152       return *this;
7153     }
7154 
7155     //! Flush all key events.
7156     /**
7157        \note Make key() to return \c 0, if called afterwards.
7158     **/
7159     CImgDisplay& set_key() {
7160       std::memset((void*)_keys,0,sizeof(_keys));
7161       std::memset((void*)_released_keys,0,sizeof(_released_keys));
7162       _is_keyESC = _is_keyF1 = _is_keyF2 = _is_keyF3 = _is_keyF4 = _is_keyF5 = _is_keyF6 = _is_keyF7 = _is_keyF8 = _is_keyF9 =
7163         _is_keyF10 = _is_keyF11 = _is_keyF12 = _is_keyPAUSE = _is_key1 = _is_key2 = _is_key3 = _is_key4 = _is_key5 = _is_key6 =
7164         _is_key7 = _is_key8 = _is_key9 = _is_key0 = _is_keyBACKSPACE = _is_keyINSERT = _is_keyHOME = _is_keyPAGEUP = _is_keyTAB =
7165         _is_keyQ = _is_keyW = _is_keyE = _is_keyR = _is_keyT = _is_keyY = _is_keyU = _is_keyI = _is_keyO = _is_keyP = _is_keyDELETE =
7166         _is_keyEND = _is_keyPAGEDOWN = _is_keyCAPSLOCK = _is_keyA = _is_keyS = _is_keyD = _is_keyF = _is_keyG = _is_keyH = _is_keyJ =
7167         _is_keyK = _is_keyL = _is_keyENTER = _is_keySHIFTLEFT = _is_keyZ = _is_keyX = _is_keyC = _is_keyV = _is_keyB = _is_keyN =
7168         _is_keyM = _is_keySHIFTRIGHT = _is_keyARROWUP = _is_keyCTRLLEFT = _is_keyAPPLEFT = _is_keyALT = _is_keySPACE = _is_keyALTGR = _is_keyAPPRIGHT =
7169         _is_keyMENU = _is_keyCTRLRIGHT = _is_keyARROWLEFT = _is_keyARROWDOWN = _is_keyARROWRIGHT = _is_keyPAD0 = _is_keyPAD1 = _is_keyPAD2 =
7170         _is_keyPAD3 = _is_keyPAD4 = _is_keyPAD5 = _is_keyPAD6 = _is_keyPAD7 = _is_keyPAD8 = _is_keyPAD9 = _is_keyPADADD = _is_keyPADSUB =
7171         _is_keyPADMUL = _is_keyPADDIV = false;
7172       _is_event = true;
7173       return *this;
7174     }
7175 
7176     //! Simulate a keyboard press/release event.
7177     /**
7178        \param keycode Keycode of the associated key.
7179        \param is_pressed Tells if the key is considered as pressed or released.
7180        \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
7181        your code stay portable (see cimg::keyESC).
7182     **/
7183     CImgDisplay& set_key(const unsigned int keycode, const bool is_pressed=true) {
7184 #define _cimg_set_key(k) if (keycode==cimg::key##k) _is_key##k = is_pressed;
7185       _cimg_set_key(ESC); _cimg_set_key(F1); _cimg_set_key(F2); _cimg_set_key(F3);
7186       _cimg_set_key(F4); _cimg_set_key(F5); _cimg_set_key(F6); _cimg_set_key(F7);
7187       _cimg_set_key(F8); _cimg_set_key(F9); _cimg_set_key(F10); _cimg_set_key(F11);
7188       _cimg_set_key(F12); _cimg_set_key(PAUSE); _cimg_set_key(1); _cimg_set_key(2);
7189       _cimg_set_key(3); _cimg_set_key(4); _cimg_set_key(5); _cimg_set_key(6);
7190       _cimg_set_key(7); _cimg_set_key(8); _cimg_set_key(9); _cimg_set_key(0);
7191       _cimg_set_key(BACKSPACE); _cimg_set_key(INSERT); _cimg_set_key(HOME);
7192       _cimg_set_key(PAGEUP); _cimg_set_key(TAB); _cimg_set_key(Q); _cimg_set_key(W);
7193       _cimg_set_key(E); _cimg_set_key(R); _cimg_set_key(T); _cimg_set_key(Y);
7194       _cimg_set_key(U); _cimg_set_key(I); _cimg_set_key(O); _cimg_set_key(P);
7195       _cimg_set_key(DELETE); _cimg_set_key(END); _cimg_set_key(PAGEDOWN);
7196       _cimg_set_key(CAPSLOCK); _cimg_set_key(A); _cimg_set_key(S); _cimg_set_key(D);
7197       _cimg_set_key(F); _cimg_set_key(G); _cimg_set_key(H); _cimg_set_key(J);
7198       _cimg_set_key(K); _cimg_set_key(L); _cimg_set_key(ENTER);
7199       _cimg_set_key(SHIFTLEFT); _cimg_set_key(Z); _cimg_set_key(X); _cimg_set_key(C);
7200       _cimg_set_key(V); _cimg_set_key(B); _cimg_set_key(N); _cimg_set_key(M);
7201       _cimg_set_key(SHIFTRIGHT); _cimg_set_key(ARROWUP); _cimg_set_key(CTRLLEFT);
7202       _cimg_set_key(APPLEFT); _cimg_set_key(ALT); _cimg_set_key(SPACE); _cimg_set_key(ALTGR);
7203       _cimg_set_key(APPRIGHT); _cimg_set_key(MENU); _cimg_set_key(CTRLRIGHT);
7204       _cimg_set_key(ARROWLEFT); _cimg_set_key(ARROWDOWN); _cimg_set_key(ARROWRIGHT);
7205       _cimg_set_key(PAD0); _cimg_set_key(PAD1); _cimg_set_key(PAD2);
7206       _cimg_set_key(PAD3); _cimg_set_key(PAD4); _cimg_set_key(PAD5);
7207       _cimg_set_key(PAD6); _cimg_set_key(PAD7); _cimg_set_key(PAD8);
7208       _cimg_set_key(PAD9); _cimg_set_key(PADADD); _cimg_set_key(PADSUB);
7209       _cimg_set_key(PADMUL); _cimg_set_key(PADDIV);
7210       if (is_pressed) {
7211         if (*_keys)
7212           std::memmove((void*)(_keys+1),(void*)_keys,sizeof(_keys) - sizeof(unsigned int));
7213         *_keys = keycode;
7214         if (*_released_keys) {
7215           std::memmove((void*)(_released_keys+1),(void*)_released_keys,sizeof(_released_keys) - sizeof(unsigned int));
7216           *_released_keys = 0;
7217         }
7218       } else {
7219         if (*_keys) {
7220           std::memmove((void*)(_keys+1),(void*)_keys,sizeof(_keys) - sizeof(unsigned int));
7221           *_keys = 0;
7222         }
7223         if (*_released_keys)
7224           std::memmove((void*)(_released_keys+1),(void*)_released_keys,sizeof(_released_keys) - sizeof(unsigned int));
7225         *_released_keys = keycode;
7226       }
7227       _is_event = keycode?true:false;
7228       return *this;
7229     }
7230 
7231     //! Flush all display events.
7232     /**
7233        \note Remove all passed events from the current display.
7234     **/
7235     CImgDisplay& flush() {
7236       set_key().set_button().set_wheel();
7237       _is_resized = _is_moved = _is_event = false;
7238       _fps_timer = _fps_frames = _timer = 0;
7239       _fps_fps = 0;
7240       return *this;
7241     }
7242 
7243     //! Wait for any user event occuring on the current display.
7244     CImgDisplay& wait() {
7245       wait(*this);
7246       return *this;
7247     }
7248 
7249     //! Wait for a given number of milliseconds since the last call to wait().
7250     /**
7251        \param milliseconds Number of milliseconds to wait for.
7252        \note Similar to cimg::wait().
7253     **/
7254     CImgDisplay& wait(const unsigned int milliseconds) {
7255       cimg::_wait(milliseconds,_timer);
7256       return *this;
7257     }
7258 
7259     //! Wait for any event occuring on the display \c disp1.
7260     static void wait(CImgDisplay& disp1) {
7261       disp1._is_event = 0;
7262       while (!disp1._is_closed && !disp1._is_event) wait_all();
7263     }
7264 
7265     //! Wait for any event occuring either on the display \c disp1 or \c disp2.
7266     static void wait(CImgDisplay& disp1, CImgDisplay& disp2) {
7267       disp1._is_event = disp2._is_event = 0;
7268       while ((!disp1._is_closed || !disp2._is_closed) &&
7269              !disp1._is_event && !disp2._is_event) wait_all();
7270     }
7271 
7272     //! Wait for any event occuring either on the display \c disp1, \c disp2 or \c disp3.
7273     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3) {
7274       disp1._is_event = disp2._is_event = disp3._is_event = 0;
7275       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed) &&
7276              !disp1._is_event && !disp2._is_event && !disp3._is_event) wait_all();
7277     }
7278 
7279     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3 or \c disp4.
7280     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4) {
7281       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = 0;
7282       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed) &&
7283              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event) wait_all();
7284     }
7285 
7286     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4 or \c disp5.
7287     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5) {
7288       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = 0;
7289       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed) &&
7290              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event) wait_all();
7291     }
7292 
7293     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp6.
7294     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
7295                      CImgDisplay& disp6) {
7296       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
7297         disp6._is_event = 0;
7298       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
7299               !disp6._is_closed) &&
7300              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
7301              !disp6._is_event) wait_all();
7302     }
7303 
7304     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp7.
7305     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
7306                      CImgDisplay& disp6, CImgDisplay& disp7) {
7307       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
7308         disp6._is_event = disp7._is_event = 0;
7309       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
7310               !disp6._is_closed || !disp7._is_closed) &&
7311              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
7312              !disp6._is_event && !disp7._is_event) wait_all();
7313     }
7314 
7315     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp8.
7316     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
7317                      CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8) {
7318       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
7319         disp6._is_event = disp7._is_event = disp8._is_event = 0;
7320       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
7321               !disp6._is_closed || !disp7._is_closed || !disp8._is_closed) &&
7322              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
7323              !disp6._is_event && !disp7._is_event && !disp8._is_event) wait_all();
7324     }
7325 
7326     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp9.
7327     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
7328                      CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9) {
7329       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
7330         disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = 0;
7331       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
7332               !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed) &&
7333              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
7334              !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event) wait_all();
7335     }
7336 
7337     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp10.
7338     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
7339                      CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9, CImgDisplay& disp10) {
7340       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
7341         disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = disp10._is_event = 0;
7342       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
7343               !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed || !disp10._is_closed) &&
7344              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
7345              !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event && !disp10._is_event) wait_all();
7346     }
7347 
7348 #if cimg_display==0
7349 
7350     //! Wait for any window event occuring in any opened CImgDisplay.
7351     static void wait_all() {
7352       return _no_display_exception();
7353     }
7354 
7355     //! Render image into internal display buffer.
7356     /**
7357        \param img Input image data to render.
7358        \note
7359        - Convert image data representation into the internal display buffer (architecture-dependent structure).
7360        - The content of the associated window is not modified, until paint() is called.
7361        - Should not be used for common CImgDisplay uses, since display() is more useful.
7362     **/
7363     template<typename T>
7364     CImgDisplay& render(const CImg<T>& img) {
7365       return assign(img);
7366     }
7367 
7368     //! Paint internal display buffer on associated window.
7369     /**
7370        \note
7371        - Update the content of the associated window with the internal display buffer, e.g. after a render() call.
7372        - Should not be used for common CImgDisplay uses, since display() is more useful.
7373     **/
7374     CImgDisplay& paint() {
7375       return assign();
7376     }
7377 
7378     //! Take a snapshot of the associated window content.
7379     /**
7380        \param[out] img Output snapshot. Can be empty on input.
7381     **/
7382     template<typename T>
7383     const CImgDisplay& snapshot(CImg<T>& img) const {
7384       cimg::unused(img);
7385       _no_display_exception();
7386       return *this;
7387     }
7388 #endif
7389 
7390     // X11-based implementation
7391     //--------------------------
7392 #if cimg_display==1
7393 
7394     Atom _wm_window_atom, _wm_protocol_atom;
7395     Window _window, _background_window;
7396     Colormap _colormap;
7397     XImage *_image;
7398     void *_data;
7399 #ifdef cimg_use_xshm
7400     XShmSegmentInfo *_shminfo;
7401 #endif
7402 
7403     static int screen_width() {
7404       Display *const dpy = cimg::X11_attr().display;
7405       int res = 0;
7406       if (!dpy) {
7407         Display *const _dpy = XOpenDisplay(0);
7408         if (!_dpy)
7409           throw CImgDisplayException("CImgDisplay::screen_width() : Failed to open X11 display.");
7410         res = DisplayWidth(_dpy,DefaultScreen(_dpy));
7411         XCloseDisplay(_dpy);
7412       } else {
7413 #ifdef cimg_use_xrandr
7414         if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution)
7415           res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width;
7416         else res = DisplayWidth(dpy,DefaultScreen(dpy));
7417 #else
7418         res = DisplayWidth(dpy,DefaultScreen(dpy));
7419 #endif
7420       }
7421       return res;
7422     }
7423 
7424     static int screen_height() {
7425       Display *const dpy = cimg::X11_attr().display;
7426       int res = 0;
7427       if (!dpy) {
7428         Display *const _dpy = XOpenDisplay(0);
7429         if (!_dpy)
7430           throw CImgDisplayException("CImgDisplay::screen_height() : Failed to open X11 display.");
7431         res = DisplayHeight(_dpy,DefaultScreen(_dpy));
7432         XCloseDisplay(_dpy);
7433       } else {
7434 #ifdef cimg_use_xrandr
7435         if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution)
7436           res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height;
7437         else res = DisplayHeight(dpy,DefaultScreen(dpy));
7438 #else
7439         res = DisplayHeight(dpy,DefaultScreen(dpy));
7440 #endif
7441       }
7442       return res;
7443     }
7444 
7445     static void wait_all() {
7446       Display *const dpy = cimg::X11_attr().display;
7447       if (!dpy) return;
7448       XLockDisplay(dpy);
7449       bool flag = true;
7450       XEvent event;
7451       while (flag) {
7452         XNextEvent(dpy,&event);
7453         for (unsigned int i = 0; i<cimg::X11_attr().nb_wins; ++i)
7454           if (!cimg::X11_attr().wins[i]->_is_closed && event.xany.window==cimg::X11_attr().wins[i]->_window) {
7455             cimg::X11_attr().wins[i]->_handle_events(&event);
7456             if (cimg::X11_attr().wins[i]->_is_event) flag = false;
7457           }
7458       }
7459       XUnlockDisplay(dpy);
7460     }
7461 
7462     void _handle_events(const XEvent *const pevent) {
7463       Display *const dpy = cimg::X11_attr().display;
7464       XEvent event = *pevent;
7465       switch (event.type) {
7466       case ClientMessage : {
7467         if ((int)event.xclient.message_type==(int)_wm_protocol_atom &&
7468             (int)event.xclient.data.l[0]==(int)_wm_window_atom) {
7469           XUnmapWindow(cimg::X11_attr().display,_window);
7470           _is_closed = _is_event = true;
7471         }
7472       } break;
7473       case ConfigureNotify : {
7474         while (XCheckWindowEvent(dpy,_window,StructureNotifyMask,&event)) {}
7475         const unsigned int nw = event.xconfigure.width, nh = event.xconfigure.height;
7476         const int nx = event.xconfigure.x, ny = event.xconfigure.y;
7477         if (nw && nh && (nw!=_window_width || nh!=_window_height)) {
7478           _window_width = nw; _window_height = nh; _mouse_x = _mouse_y = -1;
7479           XResizeWindow(dpy,_window,_window_width,_window_height);
7480           _is_resized = _is_event = true;
7481         }
7482         if (nx!=_window_x || ny!=_window_y) { _window_x = nx; _window_y = ny; _is_moved = _is_event = true; }
7483       } break;
7484       case Expose : {
7485         while (XCheckWindowEvent(dpy,_window,ExposureMask,&event)) {}
7486         _paint(false);
7487         if (_is_fullscreen) {
7488           XWindowAttributes attr;
7489           XGetWindowAttributes(dpy,_window,&attr);
7490           while (attr.map_state!=IsViewable) XSync(dpy,0);
7491           XSetInputFocus(dpy,_window,RevertToParent,CurrentTime);
7492         }
7493       } break;
7494       case ButtonPress : {
7495         do {
7496           _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y;
7497           if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
7498           switch (event.xbutton.button) {
7499           case 1 : set_button(1); break;
7500           case 3 : set_button(2); break;
7501           case 2 : set_button(3); break;
7502           }
7503         } while (XCheckWindowEvent(dpy,_window,ButtonPressMask,&event));
7504       } break;
7505       case ButtonRelease : {
7506         do {
7507           _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y;
7508           if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
7509           switch (event.xbutton.button) {
7510           case 1 : set_button(1,false); break;
7511           case 3 : set_button(2,false); break;
7512           case 2 : set_button(3,false); break;
7513           case 4 : set_wheel(1); break;
7514           case 5 : set_wheel(-1); break;
7515           }
7516         } while (XCheckWindowEvent(dpy,_window,ButtonReleaseMask,&event));
7517       } break;
7518       case KeyPress : {
7519         char tmp = 0; KeySym ksym;
7520         XLookupString(&event.xkey,&tmp,1,&ksym,0);
7521         set_key((unsigned int)ksym,true);
7522       } break;
7523       case KeyRelease : {
7524         char keys_return[32];  // Check that the key has been physically unpressed.
7525         XQueryKeymap(dpy,keys_return);
7526         const unsigned int kc = event.xkey.keycode, kc1 = kc/8, kc2 = kc%8;
7527         const bool is_key_pressed = kc1>=32?false:(keys_return[kc1]>>kc2)&1;
7528         if (!is_key_pressed) {
7529           char tmp = 0; KeySym ksym;
7530           XLookupString(&event.xkey,&tmp,1,&ksym,0);
7531           set_key((unsigned int)ksym,false);
7532         }
7533       } break;
7534       case EnterNotify: {
7535         while (XCheckWindowEvent(dpy,_window,EnterWindowMask,&event)) {}
7536         _mouse_x = event.xmotion.x;
7537         _mouse_y = event.xmotion.y;
7538         if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
7539       } break;
7540       case LeaveNotify : {
7541         while (XCheckWindowEvent(dpy,_window,LeaveWindowMask,&event)) {}
7542         _mouse_x = _mouse_y =-1; _is_event = true;
7543       } break;
7544       case MotionNotify : {
7545         while (XCheckWindowEvent(dpy,_window,PointerMotionMask,&event)) {}
7546         _mouse_x = event.xmotion.x;
7547         _mouse_y = event.xmotion.y;
7548         if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
7549         _is_event = true;
7550       } break;
7551       }
7552     }
7553 
7554     static void* _events_thread(void *) { // Only one thread to handle events for all opened display windows.
7555       Display *const dpy = cimg::X11_attr().display;
7556       XEvent event;
7557       pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0);
7558       pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0);
7559       for (;;) {
7560         XLockDisplay(dpy);
7561         bool event_flag = XCheckTypedEvent(dpy,ClientMessage,&event);
7562         if (!event_flag) event_flag = XCheckMaskEvent(dpy,
7563                                                       ExposureMask | StructureNotifyMask | ButtonPressMask|
7564                                                       KeyPressMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask|
7565                                                       ButtonReleaseMask | KeyReleaseMask,&event);
7566         if (event_flag)
7567           for (unsigned int i = 0; i<cimg::X11_attr().nb_wins; ++i)
7568             if (!cimg::X11_attr().wins[i]->_is_closed && event.xany.window==cimg::X11_attr().wins[i]->_window)
7569               cimg::X11_attr().wins[i]->_handle_events(&event);
7570         XUnlockDisplay(dpy);
7571         pthread_testcancel();
7572         cimg::sleep(8);
7573       }
7574       return 0;
7575     }
7576 
7577     void _set_colormap(Colormap& _colormap, const unsigned int dim) {
7578       XColor colormap[256];
7579       switch (dim) {
7580       case 1 : { // colormap for greyscale images
7581         for (unsigned int index = 0; index<256; ++index) {
7582           colormap[index].pixel = index;
7583           colormap[index].red = colormap[index].green = colormap[index].blue = (unsigned short)(index<<8);
7584           colormap[index].flags = DoRed | DoGreen | DoBlue;
7585         }
7586       } break;
7587       case 2 : { // colormap for RG images
7588         for (unsigned int index = 0, r = 8; r<256; r+=16)
7589           for (unsigned int g = 8; g<256; g+=16) {
7590             colormap[index].pixel = index;
7591             colormap[index].red = colormap[index].blue = (unsigned short)(r<<8);
7592             colormap[index].green = (unsigned short)(g<<8);
7593             colormap[index++].flags = DoRed | DoGreen | DoBlue;
7594           }
7595       } break;
7596       default : { // colormap for RGB images
7597         for (unsigned int index = 0, r = 16; r<256; r+=32)
7598           for (unsigned int g = 16; g<256; g+=32)
7599             for (unsigned int b = 32; b<256; b+=64) {
7600               colormap[index].pixel = index;
7601               colormap[index].red = (unsigned short)(r<<8);
7602               colormap[index].green = (unsigned short)(g<<8);
7603               colormap[index].blue = (unsigned short)(b<<8);
7604               colormap[index++].flags = DoRed | DoGreen | DoBlue;
7605             }
7606       }
7607       }
7608       XStoreColors(cimg::X11_attr().display,_colormap,colormap,256);
7609     }
7610 
7611     void _map_window() {
7612       Display *const dpy = cimg::X11_attr().display;
7613       bool is_exposed = false, is_mapped = false;
7614       XWindowAttributes attr;
7615       XEvent event;
7616       XMapRaised(dpy,_window);
7617       do { // Wait for the window to be mapped.
7618         XWindowEvent(dpy,_window,StructureNotifyMask | ExposureMask,&event);
7619         switch (event.type) {
7620         case MapNotify : is_mapped = true; break;
7621         case Expose : is_exposed = true; break;
7622         }
7623       } while (!is_exposed || !is_mapped);
7624       do { // Wait for the window to be visible.
7625         XGetWindowAttributes(dpy,_window,&attr);
7626         if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); }
7627       } while (attr.map_state!=IsViewable);
7628       _window_x = attr.x;
7629       _window_y = attr.y;
7630     }
7631 
7632     void _paint(const bool wait_expose=true) {
7633       if (_is_closed || !_image) return;
7634       Display *const dpy = cimg::X11_attr().display;
7635       if (wait_expose) { // Send an expose event sticked to display window to force repaint.
7636         static XEvent event;
7637         event.xexpose.type = Expose;
7638         event.xexpose.serial = 0;
7639         event.xexpose.send_event = 1;
7640         event.xexpose.display = dpy;
7641         event.xexpose.window = _window;
7642         event.xexpose.x = 0;
7643         event.xexpose.y = 0;
7644         event.xexpose.width = width();
7645         event.xexpose.height = height();
7646         event.xexpose.count = 0;
7647         XSendEvent(dpy,_window,0,0,&event);
7648       } else { // Repaint directly (may be called from the expose event).
7649         GC gc = DefaultGC(dpy,DefaultScreen(dpy));
7650 #ifdef cimg_use_xshm
7651         if (_shminfo) {
7652           const int completion_type = XShmGetEventBase(dpy) + ShmCompletion;
7653           XEvent event;
7654           XShmPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height,1);
7655           do { XNextEvent(dpy,&event); } while (event.type!=completion_type);  // Wait for the image drawing to be completed.
7656         } else XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height);
7657 #else
7658         XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height);
7659 #endif
7660       }
7661     }
7662 
7663     template<typename T>
7664     void _resize(T pixel_type, const unsigned int ndimx, const unsigned int ndimy, const bool force_redraw) {
7665       Display *const dpy = cimg::X11_attr().display;
7666       cimg::unused(pixel_type);
7667 
7668 #ifdef cimg_use_xshm
7669       if (_shminfo) {
7670         XShmSegmentInfo *const nshminfo = new XShmSegmentInfo;
7671         XImage *const nimage = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),
7672                                                cimg::X11_attr().nb_bits,ZPixmap,0,nshminfo,ndimx,ndimy);
7673         if (!nimage) { delete nshminfo; return; }
7674         else {
7675           nshminfo->shmid = shmget(IPC_PRIVATE,ndimx*ndimy*sizeof(T),IPC_CREAT | 0777);
7676           if (nshminfo->shmid==-1) { XDestroyImage(nimage); delete nshminfo; return; }
7677           else {
7678             nshminfo->shmaddr = nimage->data = (char*)shmat(nshminfo->shmid,0,0);
7679             if (nshminfo->shmaddr==(char*)-1) { shmctl(nshminfo->shmid,IPC_RMID,0); XDestroyImage(nimage); delete nshminfo; return; }
7680             else {
7681               nshminfo->readOnly = 0;
7682               cimg::X11_attr().is_shm_enabled = true;
7683               XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm);
7684               XShmAttach(dpy,nshminfo);
7685               XFlush(dpy);
7686               XSetErrorHandler(oldXErrorHandler);
7687               if (!cimg::X11_attr().is_shm_enabled) {
7688                 shmdt(nshminfo->shmaddr);
7689                 shmctl(nshminfo->shmid,IPC_RMID,0);
7690                 XDestroyImage(nimage);
7691                 delete nshminfo;
7692                 return;
7693               } else {
7694                 T *const ndata = (T*)nimage->data;
7695                 if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy);
7696                 else std::memset(ndata,0,sizeof(T)*ndimx*ndimy);
7697                 XShmDetach(dpy,_shminfo);
7698                 XDestroyImage(_image);
7699                 shmdt(_shminfo->shmaddr);
7700                 shmctl(_shminfo->shmid,IPC_RMID,0);
7701                 delete _shminfo;
7702                 _shminfo = nshminfo;
7703                 _image = nimage;
7704                 _data = (void*)ndata;
7705               }
7706             }
7707           }
7708         }
7709       } else
7710 #endif
7711         {
7712           T *ndata = (T*)std::malloc(ndimx*ndimy*sizeof(T));
7713           if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy);
7714           else std::memset(ndata,0,sizeof(T)*ndimx*ndimy);
7715           _data = (void*)ndata;
7716           XDestroyImage(_image);
7717           _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),
7718                                 cimg::X11_attr().nb_bits,ZPixmap,0,(char*)_data,ndimx,ndimy,8,0);
7719         }
7720     }
7721 
7722     void _init_fullscreen() {
7723       if (!_is_fullscreen || _is_closed) return;
7724       Display *const dpy = cimg::X11_attr().display;
7725       _background_window = 0;
7726 
7727 #ifdef cimg_use_xrandr
7728       int foo;
7729       if (XRRQueryExtension(dpy,&foo,&foo)) {
7730         XRRRotations(dpy,DefaultScreen(dpy),&cimg::X11_attr().curr_rotation);
7731         if (!cimg::X11_attr().resolutions) {
7732           cimg::X11_attr().resolutions = XRRSizes(dpy,DefaultScreen(dpy),&foo);
7733           cimg::X11_attr().nb_resolutions = (unsigned int)foo;
7734         }
7735         if (cimg::X11_attr().resolutions) {
7736           cimg::X11_attr().curr_resolution = 0;
7737           for (unsigned int i = 0; i<cimg::X11_attr().nb_resolutions; ++i) {
7738             const unsigned int
7739               nw = (unsigned int)(cimg::X11_attr().resolutions[i].width),
7740               nh = (unsigned int)(cimg::X11_attr().resolutions[i].height);
7741             if (nw>=_width && nh>=_height &&
7742                 nw<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width) &&
7743                 nh<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height))
7744               cimg::X11_attr().curr_resolution = i;
7745           }
7746           if (cimg::X11_attr().curr_resolution>0) {
7747             XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy));
7748             XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),
7749                                cimg::X11_attr().curr_resolution,cimg::X11_attr().curr_rotation,CurrentTime);
7750             XRRFreeScreenConfigInfo(config);
7751             XSync(dpy,0);
7752           }
7753         }
7754       }
7755       if (!cimg::X11_attr().resolutions)
7756         cimg::warn(_cimgdisplay_instance
7757                    "init_fullscreen() : Xrandr extension not supported by the X server.",
7758                    cimgdisplay_instance);
7759 #endif
7760 
7761       const unsigned int sx = screen_width(), sy = screen_height();
7762       if (sx==_width && sy==_height) return;
7763       XSetWindowAttributes winattr;
7764       winattr.override_redirect = 1;
7765       _background_window = XCreateWindow(dpy,DefaultRootWindow(dpy),0,0,sx,sy,0,0,
7766                                          InputOutput,CopyFromParent,CWOverrideRedirect,&winattr);
7767       const unsigned long buf_size = (unsigned long)sx*sy*(cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4));
7768       void *background_data = std::malloc(buf_size);
7769       std::memset(background_data,0,buf_size);
7770       XImage *background_image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,
7771                                               ZPixmap,0,(char*)background_data,sx,sy,8,0);
7772       XEvent event;
7773       XSelectInput(dpy,_background_window,StructureNotifyMask);
7774       XMapRaised(dpy,_background_window);
7775       do XWindowEvent(dpy,_background_window,StructureNotifyMask,&event);
7776       while (event.type!=MapNotify);
7777       GC gc = DefaultGC(dpy,DefaultScreen(dpy));
7778 #ifdef cimg_use_xshm
7779       if (_shminfo) XShmPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy,0);
7780       else XPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy);
7781 #else
7782       XPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy);
7783 #endif
7784       XWindowAttributes attr;
7785       XGetWindowAttributes(dpy,_background_window,&attr);
7786       while (attr.map_state!=IsViewable) XSync(dpy,0);
7787       XDestroyImage(background_image);
7788     }
7789 
7790     void _desinit_fullscreen() {
7791       if (!_is_fullscreen) return;
7792       Display *const dpy = cimg::X11_attr().display;
7793       XUngrabKeyboard(dpy,CurrentTime);
7794 #ifdef cimg_use_xrandr
7795       if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) {
7796         XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy));
7797         XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),0,cimg::X11_attr().curr_rotation,CurrentTime);
7798         XRRFreeScreenConfigInfo(config);
7799         XSync(dpy,0);
7800         cimg::X11_attr().curr_resolution = 0;
7801       }
7802 #endif
7803       if (_background_window) XDestroyWindow(dpy,_background_window);
7804       _background_window = 0;
7805       _is_fullscreen = false;
7806     }
7807 
7808     static int _assign_xshm(Display *dpy, XErrorEvent *error) {
7809       cimg::unused(dpy,error);
7810       cimg::X11_attr().is_shm_enabled = false;
7811       return 0;
7812     }
7813 
7814     void _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0,
7815                  const unsigned int normalization_type=3,
7816                  const bool fullscreen_flag=false, const bool closed_flag=false) {
7817 
7818       // Allocate space for window title
7819       const char *const nptitle = ptitle?ptitle:"";
7820       const unsigned int s = std::strlen(nptitle) + 1;
7821       char *const tmp_title = s?new char[s]:0;
7822       if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char));
7823 
7824       // Destroy previous display window if existing
7825       if (!is_empty()) assign();
7826 
7827       // Open X11 display and retrieve graphical properties.
7828       Display* &dpy = cimg::X11_attr().display;
7829       if (!dpy) {
7830         static const int xinit_status = XInitThreads();
7831         cimg::unused(xinit_status);
7832         dpy = XOpenDisplay(0);
7833         if (!dpy)
7834           throw CImgDisplayException(_cimgdisplay_instance
7835                                      "assign() : Failed to open X11 display.",
7836                                      cimgdisplay_instance);
7837 
7838         cimg::X11_attr().nb_bits = DefaultDepth(dpy,DefaultScreen(dpy));
7839         if (cimg::X11_attr().nb_bits!=8 && cimg::X11_attr().nb_bits!=16 && cimg::X11_attr().nb_bits!=24 && cimg::X11_attr().nb_bits!=32)
7840           throw CImgDisplayException(_cimgdisplay_instance
7841                                      "assign() : Invalid %u bits screen mode detected "
7842                                      "(only 8, 16, 24 and 32 bits modes are managed).",
7843                                      cimgdisplay_instance,
7844                                      cimg::X11_attr().nb_bits);
7845         XVisualInfo vtemplate;
7846         vtemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,DefaultScreen(dpy)));
7847         int nb_visuals;
7848         XVisualInfo *vinfo = XGetVisualInfo(dpy,VisualIDMask,&vtemplate,&nb_visuals);
7849         if (vinfo && vinfo->red_mask<vinfo->blue_mask) cimg::X11_attr().is_blue_first = true;
7850         cimg::X11_attr().byte_order = ImageByteOrder(dpy);
7851         XFree(vinfo);
7852 
7853         XLockDisplay(dpy);
7854         cimg::X11_attr().event_thread = new pthread_t;
7855         pthread_create(cimg::X11_attr().event_thread,0,_events_thread,0);
7856       } else XLockDisplay(dpy);
7857 
7858       // Set display variables.
7859       _width = cimg::min(dimw,(unsigned int)screen_width());
7860       _height = cimg::min(dimh,(unsigned int)screen_height());
7861       _normalization = normalization_type<4?normalization_type:3;
7862       _is_fullscreen = fullscreen_flag;
7863       _window_x = _window_y = 0;
7864       _is_closed = closed_flag;
7865       _title = tmp_title;
7866       flush();
7867 
7868       // Create X11 window (and LUT, if 8bits display)
7869       if (_is_fullscreen) {
7870         if (!_is_closed) _init_fullscreen();
7871         const unsigned int sx = screen_width(), sy = screen_height();
7872         XSetWindowAttributes winattr;
7873         winattr.override_redirect = 1;
7874         _window = XCreateWindow(dpy,DefaultRootWindow(dpy),(sx-_width)/2,(sy-_height)/2,_width,_height,0,0,
7875                                 InputOutput,CopyFromParent,CWOverrideRedirect,&winattr);
7876       } else
7877         _window = XCreateSimpleWindow(dpy,DefaultRootWindow(dpy),0,0,_width,_height,0,0L,0L);
7878 
7879       XSelectInput(dpy,_window,
7880                    ExposureMask | StructureNotifyMask | ButtonPressMask | KeyPressMask | PointerMotionMask |
7881                    EnterWindowMask | LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask);
7882 
7883       XStoreName(dpy,_window,_title?_title:" ");
7884       if (cimg::X11_attr().nb_bits==8) {
7885         _colormap = XCreateColormap(dpy,_window,DefaultVisual(dpy,DefaultScreen(dpy)),AllocAll);
7886         _set_colormap(_colormap,3);
7887         XSetWindowColormap(dpy,_window,_colormap);
7888       }
7889 
7890       static const char *const _window_class = cimg_appname;
7891       XClassHint *const window_class = XAllocClassHint();
7892       window_class->res_name = (char*)_window_class;
7893       window_class->res_class = (char*)_window_class;
7894       XSetClassHint(dpy,_window,window_class);
7895       XFree(window_class);
7896 
7897       _window_width = _width;
7898       _window_height = _height;
7899 
7900       // Create XImage
7901 #ifdef cimg_use_xshm
7902       _shminfo = 0;
7903       if (XShmQueryExtension(dpy)) {
7904         _shminfo = new XShmSegmentInfo;
7905         _image = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,ZPixmap,0,_shminfo,_width,_height);
7906         if (!_image) { delete _shminfo; _shminfo = 0; }
7907         else {
7908           _shminfo->shmid = shmget(IPC_PRIVATE,_image->bytes_per_line*_image->height,IPC_CREAT|0777);
7909           if (_shminfo->shmid==-1) { XDestroyImage(_image); delete _shminfo; _shminfo = 0; }
7910           else {
7911             _shminfo->shmaddr = _image->data = (char*)(_data = shmat(_shminfo->shmid,0,0));
7912             if (_shminfo->shmaddr==(char*)-1) { shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image); delete _shminfo; _shminfo = 0; }
7913             else {
7914               _shminfo->readOnly = 0;
7915               cimg::X11_attr().is_shm_enabled = true;
7916               XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm);
7917               XShmAttach(dpy,_shminfo);
7918               XSync(dpy,0);
7919               XSetErrorHandler(oldXErrorHandler);
7920               if (!cimg::X11_attr().is_shm_enabled) {
7921                 shmdt(_shminfo->shmaddr); shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image); delete _shminfo; _shminfo = 0;
7922               }
7923             }
7924           }
7925         }
7926       }
7927       if (!_shminfo)
7928 #endif
7929         {
7930           const unsigned long buf_size = (unsigned long)_width*_height*(cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4));
7931           _data = std::malloc(buf_size);
7932           _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,ZPixmap,0,(char*)_data,_width,_height,8,0);
7933         }
7934 
7935       _wm_window_atom = XInternAtom(dpy,"WM_DELETE_WINDOW",0);
7936       _wm_protocol_atom = XInternAtom(dpy,"WM_PROTOCOLS",0);
7937       XSetWMProtocols(dpy,_window,&_wm_window_atom,1);
7938 
7939       if (_is_fullscreen) XGrabKeyboard(dpy,_window,1,GrabModeAsync,GrabModeAsync,CurrentTime);
7940       cimg::X11_attr().wins[cimg::X11_attr().nb_wins++]=this;
7941       if (!_is_closed) _map_window(); else { _window_x = _window_y = cimg::type<int>::min(); }
7942       XUnlockDisplay(dpy);
7943     }
7944 
7945     CImgDisplay& assign() {
7946       if (is_empty()) return flush();
7947       Display *const dpy = cimg::X11_attr().display;
7948       XLockDisplay(dpy);
7949 
7950       // Remove display window from event thread list.
7951       unsigned int i;
7952       for (i = 0; i<cimg::X11_attr().nb_wins && cimg::X11_attr().wins[i]!=this; ++i) {}
7953       for (; i<cimg::X11_attr().nb_wins-1; ++i) cimg::X11_attr().wins[i] = cimg::X11_attr().wins[i+1];
7954       --cimg::X11_attr().nb_wins;
7955 
7956       // Destroy window, image, colormap and title.
7957       if (_is_fullscreen && !_is_closed) _desinit_fullscreen();
7958       XDestroyWindow(dpy,_window);
7959       _window = 0;
7960 #ifdef cimg_use_xshm
7961       if (_shminfo) {
7962         XShmDetach(dpy,_shminfo);
7963         XDestroyImage(_image);
7964         shmdt(_shminfo->shmaddr);
7965         shmctl(_shminfo->shmid,IPC_RMID,0);
7966         delete _shminfo;
7967         _shminfo = 0;
7968       } else
7969 #endif
7970         XDestroyImage(_image);
7971       _data = 0; _image = 0;
7972       if (cimg::X11_attr().nb_bits==8) XFreeColormap(dpy,_colormap);
7973       _colormap = 0;
7974       XSync(dpy,0);
7975 
7976       // Reset display variables
7977       delete[] _title;
7978       _width = _height = _normalization = _window_width = _window_height = 0;
7979       _window_x = _window_y = 0;
7980       _is_fullscreen = false;
7981       _is_closed = true;
7982       _min = _max = 0;
7983       _title = 0;
7984       flush();
7985 
7986       // End event thread and close display if necessary
7987       XUnlockDisplay(dpy);
7988       if (!cimg::X11_attr().nb_wins) {
7989         // Kill event thread
7990         //pthread_cancel(*cimg::X11_attr().event_thread);
7991         //XUnlockDisplay(cimg::X11_attr().display);
7992         //pthread_join(*cimg::X11_attr().event_thread,0);
7993         //delete cimg::X11_attr().event_thread;
7994         //cimg::X11_attr().event_thread = 0;
7995         // XUnlockDisplay(cimg::X11_attr().display); // <- This call make the library hang sometimes (fix required).
7996         // XCloseDisplay(cimg::X11_attr().display); // <- This call make the library hang sometimes (fix required).
7997         //cimg::X11_attr().display = 0;
7998       }
7999       return *this;
8000     }
8001 
8002     CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0,
8003                         const unsigned int normalization_type=3,
8004                         const bool fullscreen_flag=false, const bool closed_flag=false) {
8005       if (!dimw || !dimh) return assign();
8006       _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag);
8007       _min = _max = 0;
8008       std::memset(_data,0,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char):
8009                            (cimg::X11_attr().nb_bits==16?sizeof(unsigned short):sizeof(unsigned int)))*(unsigned long)_width*_height);
8010       return paint();
8011     }
8012 
8013     template<typename T>
8014     CImgDisplay& assign(const CImg<T>& img, const char *const title=0,
8015                         const unsigned int normalization_type=3,
8016                         const bool fullscreen_flag=false, const bool closed_flag=false) {
8017       if (!img) return assign();
8018       CImg<T> tmp;
8019       const CImg<T>& nimg = (img._depth==1)?img:(tmp=img.get_projections2d(img._width/2,img._height/2,img._depth/2));
8020       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
8021       if (_normalization==2) _min = (float)nimg.min_max(_max);
8022       return render(nimg).paint();
8023     }
8024 
8025     template<typename T>
8026     CImgDisplay& assign(const CImgList<T>& list, const char *const title=0,
8027                         const unsigned int normalization_type=3,
8028                         const bool fullscreen_flag=false, const bool closed_flag=false) {
8029       if (!list) return assign();
8030       CImg<T> tmp;
8031       const CImg<T> img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d(img._width/2,img._height/2,img._depth/2));
8032       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
8033       if (_normalization==2) _min = (float)nimg.min_max(_max);
8034       return render(nimg).paint();
8035     }
8036 
8037     CImgDisplay& assign(const CImgDisplay& disp) {
8038       if (!disp) return assign();
8039       _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed);
8040       std::memcpy(_data,disp._data,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char):
8041                                     cimg::X11_attr().nb_bits==16?sizeof(unsigned short):
8042                                     sizeof(unsigned int))*(unsigned long)_width*_height);
8043       return paint();
8044     }
8045 
8046     CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) {
8047       if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign();
8048       if (is_empty()) return assign(nwidth,nheight);
8049       Display *const dpy = cimg::X11_attr().display;
8050       const unsigned int
8051         tmpdimx = (nwidth>0)?nwidth:(-nwidth*width()/100),
8052         tmpdimy = (nheight>0)?nheight:(-nheight*height()/100),
8053         dimx = tmpdimx?tmpdimx:1,
8054         dimy = tmpdimy?tmpdimy:1;
8055       XLockDisplay(dpy);
8056       if (_window_width!=dimx || _window_height!=dimy) XResizeWindow(dpy,_window,dimx,dimy);
8057       if (_width!=dimx || _height!=dimy) switch (cimg::X11_attr().nb_bits) {
8058         case 8 :  { unsigned char pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break;
8059         case 16 : { unsigned short pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break;
8060         default : { unsigned int pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); }
8061         }
8062       _window_width = _width = dimx; _window_height = _height = dimy;
8063       _is_resized = false;
8064       XUnlockDisplay(dpy);
8065       if (_is_fullscreen) move((screen_width()-_width)/2,(screen_height()-_height)/2);
8066       if (force_redraw) return paint();
8067       return *this;
8068     }
8069 
8070     CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
8071       if (is_empty()) return *this;
8072       if (force_redraw) {
8073         const unsigned long buf_size = (unsigned long)_width*_height*(cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4));
8074         void *image_data = std::malloc(buf_size);
8075         std::memcpy(image_data,_data,buf_size);
8076         assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
8077         std::memcpy(_data,image_data,buf_size);
8078         std::free(image_data);
8079         return paint();
8080       }
8081       return assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
8082     }
8083 
8084     CImgDisplay& show() {
8085       if (is_empty() || !_is_closed) return *this;
8086       Display *const dpy = cimg::X11_attr().display;
8087       XLockDisplay(dpy);
8088       if (_is_fullscreen) _init_fullscreen();
8089       _map_window();
8090       _is_closed = false;
8091       XUnlockDisplay(dpy);
8092       return paint();
8093     }
8094 
8095     CImgDisplay& close() {
8096       if (is_empty() || _is_closed) return *this;
8097       Display *const dpy = cimg::X11_attr().display;
8098       XLockDisplay(dpy);
8099       if (_is_fullscreen) _desinit_fullscreen();
8100       XUnmapWindow(dpy,_window);
8101       _window_x = _window_y = -1;
8102       _is_closed = true;
8103       XUnlockDisplay(dpy);
8104       return *this;
8105     }
8106 
8107     CImgDisplay& move(const int posx, const int posy) {
8108       if (is_empty()) return *this;
8109       Display *const dpy = cimg::X11_attr().display;
8110       show();
8111       XLockDisplay(dpy);
8112       XMoveWindow(dpy,_window,posx,posy);
8113       _window_x = posx; _window_y = posy;
8114       _is_moved = false;
8115       XUnlockDisplay(dpy);
8116       return paint();
8117     }
8118 
8119     CImgDisplay& show_mouse() {
8120       if (is_empty()) return *this;
8121       Display *const dpy = cimg::X11_attr().display;
8122       XLockDisplay(dpy);
8123       XUndefineCursor(dpy,_window);
8124       XUnlockDisplay(dpy);
8125       return *this;
8126     }
8127 
8128     CImgDisplay& hide_mouse() {
8129       if (is_empty()) return *this;
8130       Display *const dpy = cimg::X11_attr().display;
8131       XLockDisplay(dpy);
8132       const char pix_data[8] = { 0 };
8133       XColor col;
8134       col.red = col.green = col.blue = 0;
8135       Pixmap pix = XCreateBitmapFromData(dpy,_window,pix_data,8,8);
8136       Cursor cur = XCreatePixmapCursor(dpy,pix,pix,&col,&col,0,0);
8137       XFreePixmap(dpy,pix);
8138       XDefineCursor(dpy,_window,cur);
8139       XUnlockDisplay(dpy);
8140       return *this;
8141     }
8142 
8143     CImgDisplay& set_mouse(const int posx, const int posy) {
8144       if (is_empty() || _is_closed) return *this;
8145       Display *const dpy = cimg::X11_attr().display;
8146       XLockDisplay(dpy);
8147       XWarpPointer(dpy,0L,_window,0,0,0,0,posx,posy);
8148       _mouse_x = posx; _mouse_y = posy;
8149       _is_moved = false;
8150       XSync(dpy,0);
8151       XUnlockDisplay(dpy);
8152       return *this;
8153     }
8154 
8155     CImgDisplay& set_title(const char *const format, ...) {
8156       if (is_empty()) return *this;
8157       char tmp[1024] = { 0 };
8158       va_list ap;
8159       va_start(ap, format);
8160       cimg_vsnprintf(tmp,sizeof(tmp),format,ap);
8161       va_end(ap);
8162       if (!std::strcmp(_title,tmp)) return *this;
8163       delete[] _title;
8164       const unsigned int s = std::strlen(tmp) + 1;
8165       _title = new char[s];
8166       std::memcpy(_title,tmp,s*sizeof(char));
8167       Display *const dpy = cimg::X11_attr().display;
8168       XLockDisplay(dpy);
8169       XStoreName(dpy,_window,tmp);
8170       XUnlockDisplay(dpy);
8171       return *this;
8172     }
8173 
8174     template<typename T>
8175     CImgDisplay& display(const CImg<T>& img) {
8176       if (!img)
8177         throw CImgArgumentException(_cimgdisplay_instance
8178                                     "display() : Empty specified image.",
8179                                     cimgdisplay_instance);
8180       if (is_empty()) return assign(img);
8181       return render(img).paint(false);
8182     }
8183 
8184     CImgDisplay& paint(const bool wait_expose=true) {
8185       if (is_empty()) return *this;
8186       Display *const dpy = cimg::X11_attr().display;
8187       XLockDisplay(dpy);
8188       _paint(wait_expose);
8189       XUnlockDisplay(dpy);
8190       return *this;
8191     }
8192 
8193     template<typename T>
8194     CImgDisplay& render(const CImg<T>& img, const bool flag8=false) {
8195       if (!img)
8196         throw CImgArgumentException(_cimgdisplay_instance
8197                                     "render() : Empty specified image.",
8198                                     cimgdisplay_instance);
8199       if (is_empty()) return *this;
8200       if (img._depth!=1) return render(img.get_projections2d(img._width/2,img._height/2,img._depth/2));
8201       if (cimg::X11_attr().nb_bits==8 && (img._width!=_width || img._height!=_height)) return render(img.get_resize(_width,_height,1,-100,1));
8202       if (cimg::X11_attr().nb_bits==8 && !flag8 && img._spectrum==3) {
8203         static const CImg<typename CImg<T>::ucharT> default_colormap = CImg<typename CImg<T>::ucharT>::default_LUT256();
8204         return render(img.get_index(default_colormap,1,false));
8205       }
8206 
8207       Display *const dpy = cimg::X11_attr().display;
8208       const T
8209         *data1 = img._data,
8210         *data2 = (img._spectrum>1)?img.data(0,0,0,1):data1,
8211         *data3 = (img._spectrum>2)?img.data(0,0,0,2):data1;
8212 
8213       if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3);
8214       XLockDisplay(dpy);
8215 
8216       if (!_normalization || (_normalization==3 && cimg::type<T>::string()==cimg::type<unsigned char>::string())) {
8217         _min = _max = 0;
8218         switch (cimg::X11_attr().nb_bits) {
8219         case 8 : { // 256 colormap, no normalization
8220           _set_colormap(_colormap,img._spectrum);
8221           unsigned char *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data:new unsigned char[(unsigned long)img._width*img._height];
8222           unsigned char *ptrd = (unsigned char*)ndata;
8223           switch (img._spectrum) {
8224           case 1 : for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) (*ptrd++) = (unsigned char)*(data1++);
8225             break;
8226           case 2 : for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8227               const unsigned char R = (unsigned char)*(data1++), G = (unsigned char)*(data2++);
8228               (*ptrd++) = (R&0xf0) | (G>>4);
8229             } break;
8230           default : for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8231               const unsigned char R = (unsigned char)*(data1++), G = (unsigned char)*(data2++), B = (unsigned char)*(data3++);
8232               (*ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6);
8233             }
8234           }
8235           if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height); delete[] ndata; }
8236         } break;
8237         case 16 : { // 16 bits colors, no normalization
8238           unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data:new unsigned short[(unsigned long)img._width*img._height];
8239           unsigned char *ptrd = (unsigned char*)ndata;
8240           const unsigned int M = 248;
8241           switch (img._spectrum) {
8242           case 1 :
8243             if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8244               const unsigned char val = (unsigned char)*(data1++), G = val>>2;
8245               *(ptrd++) = (val&M) | (G>>3);
8246               *(ptrd++) = (G<<5) | (G>>1);
8247               } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8248               const unsigned char val = (unsigned char)*(data1++), G = val>>2;
8249               *(ptrd++) = (G<<5) | (G>>1);
8250               *(ptrd++) = (val&M) | (G>>3);
8251             }
8252             break;
8253           case 2 :
8254             if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8255               const unsigned char G = (unsigned char)*(data2++)>>2;
8256               *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3);
8257               *(ptrd++) = (G<<5);
8258               } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8259               const unsigned char G = (unsigned char)*(data2++)>>2;
8260               *(ptrd++) = (G<<5);
8261               *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3);
8262             }
8263             break;
8264           default :
8265             if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8266               const unsigned char G = (unsigned char)*(data2++)>>2;
8267               *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3);
8268               *(ptrd++) = (G<<5) | ((unsigned char)*(data3++)>>3);
8269               } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8270               const unsigned char G = (unsigned char)*(data2++)>>2;
8271               *(ptrd++) = (G<<5) | ((unsigned char)*(data3++)>>3);
8272               *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3);
8273             }
8274           }
8275           if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height); delete[] ndata; }
8276         } break;
8277         default : { // 24 bits colors, no normalization
8278           unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data:new unsigned int[(unsigned long)img._width*img._height];
8279           if (sizeof(int)==4) { // 32 bits int uses optimized version
8280             unsigned int *ptrd = ndata;
8281             switch (img._spectrum) {
8282             case 1 :
8283               if (cimg::X11_attr().byte_order==cimg::endianness())
8284                 for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8285                   const unsigned char val = (unsigned char)*(data1++);
8286                   *(ptrd++) = (val<<16) | (val<<8) | val;
8287                 }
8288               else
8289                 for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8290                  const unsigned char val = (unsigned char)*(data1++);
8291                   *(ptrd++) = (val<<16) | (val<<8) | val;
8292                 }
8293               break;
8294             case 2 :
8295               if (cimg::X11_attr().byte_order==cimg::endianness())
8296                 for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
8297                   *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8);
8298               else
8299                 for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
8300                   *(ptrd++) = ((unsigned char)*(data2++)<<16) | ((unsigned char)*(data1++)<<8);
8301               break;
8302             default :
8303               if (cimg::X11_attr().byte_order==cimg::endianness())
8304                 for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
8305                   *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) | (unsigned char)*(data3++);
8306               else
8307                 for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
8308                   *(ptrd++) = ((unsigned char)*(data3++)<<24) | ((unsigned char)*(data2++)<<16) | ((unsigned char)*(data1++)<<8);
8309             }
8310           } else {
8311             unsigned char *ptrd = (unsigned char*)ndata;
8312             switch (img._spectrum) {
8313             case 1 :
8314               if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8315                 *(ptrd++) = 0;
8316                 *(ptrd++) = (unsigned char)*(data1++);
8317                 *(ptrd++) = 0;
8318                 *(ptrd++) = 0;
8319                 } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8320                 *(ptrd++) = 0;
8321                 *(ptrd++) = 0;
8322                 *(ptrd++) = (unsigned char)*(data1++);
8323                 *(ptrd++) = 0;
8324               }
8325               break;
8326             case 2 :
8327               if (cimg::X11_attr().byte_order) cimg::swap(data1,data2);
8328               for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8329                 *(ptrd++) = 0;
8330                 *(ptrd++) = (unsigned char)*(data2++);
8331                 *(ptrd++) = (unsigned char)*(data1++);
8332                 *(ptrd++) = 0;
8333               }
8334               break;
8335             default :
8336               if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8337                 *(ptrd++) = 0;
8338                 *(ptrd++) = (unsigned char)*(data1++);
8339                 *(ptrd++) = (unsigned char)*(data2++);
8340                 *(ptrd++) = (unsigned char)*(data3++);
8341                 } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8342                 *(ptrd++) = (unsigned char)*(data3++);
8343                 *(ptrd++) = (unsigned char)*(data2++);
8344                 *(ptrd++) = (unsigned char)*(data1++);
8345                 *(ptrd++) = 0;
8346               }
8347             }
8348           }
8349           if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height); delete[] ndata; }
8350         }
8351         }
8352       } else {
8353         if (_normalization==3) {
8354           if (cimg::type<T>::is_float()) _min = (float)img.min_max(_max);
8355           else { _min = (float)cimg::type<T>::min(); _max = (float)cimg::type<T>::max(); }
8356         } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max);
8357         const float delta = _max - _min, mm = 255/(delta?delta:1.0f);
8358         switch (cimg::X11_attr().nb_bits) {
8359         case 8 : { // 256 colormap, with normalization
8360           _set_colormap(_colormap,img._spectrum);
8361           unsigned char *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data:new unsigned char[(unsigned long)img._width*img._height];
8362           unsigned char *ptrd = (unsigned char*)ndata;
8363           switch (img._spectrum) {
8364           case 1 : for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8365               const unsigned char R = (unsigned char)((*(data1++)-_min)*mm);
8366               *(ptrd++) = R;
8367             } break;
8368           case 2 : for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8369               const unsigned char
8370                 R = (unsigned char)((*(data1++)-_min)*mm),
8371                 G = (unsigned char)((*(data2++)-_min)*mm);
8372             (*ptrd++) = (R&0xf0) | (G>>4);
8373           } break;
8374           default :
8375             for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8376               const unsigned char
8377                 R = (unsigned char)((*(data1++)-_min)*mm),
8378                 G = (unsigned char)((*(data2++)-_min)*mm),
8379                 B = (unsigned char)((*(data3++)-_min)*mm);
8380               *(ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6);
8381             }
8382           }
8383           if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height); delete[] ndata; }
8384         } break;
8385         case 16 : { // 16 bits colors, with normalization
8386           unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data:new unsigned short[(unsigned long)img._width*img._height];
8387           unsigned char *ptrd = (unsigned char*)ndata;
8388           const unsigned int M = 248;
8389           switch (img._spectrum) {
8390           case 1 :
8391             if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8392               const unsigned char val = (unsigned char)((*(data1++)-_min)*mm), G = val>>2;
8393               *(ptrd++) = (val&M) | (G>>3);
8394               *(ptrd++) = (G<<5) | (val>>3);
8395               } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8396               const unsigned char val = (unsigned char)((*(data1++)-_min)*mm), G = val>>2;
8397               *(ptrd++) = (G<<5) | (val>>3);
8398               *(ptrd++) = (val&M) | (G>>3);
8399             }
8400             break;
8401           case 2 :
8402             if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8403               const unsigned char G = (unsigned char)((*(data2++)-_min)*mm)>>2;
8404               *(ptrd++) = ((unsigned char)((*(data1++)-_min)*mm)&M) | (G>>3);
8405               *(ptrd++) = (G<<5);
8406               } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8407               const unsigned char G = (unsigned char)((*(data2++)-_min)*mm)>>2;
8408               *(ptrd++) = (G<<5);
8409               *(ptrd++) = ((unsigned char)((*(data1++)-_min)*mm)&M) | (G>>3);
8410             }
8411             break;
8412           default :
8413             if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8414               const unsigned char G = (unsigned char)((*(data2++)-_min)*mm)>>2;
8415               *(ptrd++) = ((unsigned char)((*(data1++)-_min)*mm)&M) | (G>>3);
8416               *(ptrd++) = (G<<5) | ((unsigned char)((*(data3++)-_min)*mm)>>3);
8417               } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8418               const unsigned char G = (unsigned char)((*(data2++)-_min)*mm)>>2;
8419               *(ptrd++) = (G<<5) | ((unsigned char)((*(data3++)-_min)*mm)>>3);
8420               *(ptrd++) = ((unsigned char)((*(data1++)-_min)*mm)&M) | (G>>3);
8421             }
8422           }
8423           if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height); delete[] ndata; }
8424         } break;
8425         default : { // 24 bits colors, with normalization
8426           unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data:new unsigned int[(unsigned long)img._width*img._height];
8427           if (sizeof(int)==4) { // 32 bits int uses optimized version
8428             unsigned int *ptrd = ndata;
8429             switch (img._spectrum) {
8430             case 1 :
8431               if (cimg::X11_attr().byte_order==cimg::endianness())
8432                 for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8433                   const unsigned char val = (unsigned char)((*(data1++)-_min)*mm);
8434                   *(ptrd++) = (val<<16) | (val<<8) | val;
8435                 }
8436               else
8437                 for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8438                   const unsigned char val = (unsigned char)((*(data1++)-_min)*mm);
8439                   *(ptrd++) = (val<<24) | (val<<16) | (val<<8);
8440                 }
8441               break;
8442             case 2 :
8443               if (cimg::X11_attr().byte_order==cimg::endianness())
8444                 for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
8445                   *(ptrd++) =
8446                     ((unsigned char)((*(data1++)-_min)*mm)<<16) |
8447                     ((unsigned char)((*(data2++)-_min)*mm)<<8);
8448               else
8449                 for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
8450                   *(ptrd++) =
8451                     ((unsigned char)((*(data2++)-_min)*mm)<<16) |
8452                     ((unsigned char)((*(data1++)-_min)*mm)<<8);
8453               break;
8454             default :
8455               if (cimg::X11_attr().byte_order==cimg::endianness())
8456                 for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
8457                   *(ptrd++) =
8458                     ((unsigned char)((*(data1++)-_min)*mm)<<16) |
8459                     ((unsigned char)((*(data2++)-_min)*mm)<<8) |
8460                     (unsigned char)((*(data3++)-_min)*mm);
8461               else
8462                 for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
8463                   *(ptrd++) =
8464                     ((unsigned char)((*(data3++)-_min)*mm)<<24) |
8465                     ((unsigned char)((*(data2++)-_min)*mm)<<16) |
8466                     ((unsigned char)((*(data1++)-_min)*mm)<<8);
8467             }
8468           } else {
8469             unsigned char *ptrd = (unsigned char*)ndata;
8470             switch (img._spectrum) {
8471             case 1 :
8472               if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8473                 const unsigned char val = (unsigned char)((*(data1++)-_min)*mm);
8474                 (*ptrd++) = 0;
8475                 (*ptrd++) = val;
8476                 (*ptrd++) = val;
8477                 (*ptrd++) = val;
8478                 } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8479                 const unsigned char val = (unsigned char)((*(data1++)-_min)*mm);
8480                 (*ptrd++) = val;
8481                 (*ptrd++) = val;
8482                 (*ptrd++) = val;
8483                 (*ptrd++) = 0;
8484               }
8485               break;
8486             case 2 :
8487               if (cimg::X11_attr().byte_order) cimg::swap(data1,data2);
8488               for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8489                 (*ptrd++) = 0;
8490                 (*ptrd++) = (unsigned char)((*(data2++)-_min)*mm);
8491                 (*ptrd++) = (unsigned char)((*(data1++)-_min)*mm);
8492                 (*ptrd++) = 0;
8493               }
8494               break;
8495             default :
8496               if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8497                 (*ptrd++) = 0;
8498                 (*ptrd++) = (unsigned char)((*(data1++)-_min)*mm);
8499                 (*ptrd++) = (unsigned char)((*(data2++)-_min)*mm);
8500                 (*ptrd++) = (unsigned char)((*(data3++)-_min)*mm);
8501                 } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8502                 (*ptrd++) = (unsigned char)((*(data3++)-_min)*mm);
8503                 (*ptrd++) = (unsigned char)((*(data2++)-_min)*mm);
8504                 (*ptrd++) = (unsigned char)((*(data1++)-_min)*mm);
8505                 (*ptrd++) = 0;
8506               }
8507             }
8508           }
8509           if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height); delete[] ndata; }
8510         }
8511         }
8512       }
8513       XUnlockDisplay(dpy);
8514       return *this;
8515     }
8516 
8517     template<typename T>
8518     const CImgDisplay& snapshot(CImg<T>& img) const {
8519       if (is_empty()) { img.assign(); return *this; }
8520       const unsigned char *ptrs = (unsigned char*)_data;
8521       img.assign(_width,_height,1,3);
8522       T
8523         *data1 = img.data(0,0,0,0),
8524         *data2 = img.data(0,0,0,1),
8525         *data3 = img.data(0,0,0,2);
8526       if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3);
8527       switch (cimg::X11_attr().nb_bits) {
8528       case 8 : {
8529         for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8530           const unsigned char val = *(ptrs++);
8531           *(data1++) = (T)(val&0xe0);
8532           *(data2++) = (T)((val&0x1c)<<3);
8533           *(data3++) = (T)(val<<6);
8534         }
8535       } break;
8536       case 16 : {
8537         if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8538           const unsigned char val0 = *(ptrs++), val1 = *(ptrs++);
8539           *(data1++) = (T)(val0&0xf8);
8540           *(data2++) = (T)((val0<<5) | ((val1&0xe0)>>5));
8541           *(data3++) = (T)(val1<<3);
8542           } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8543           const unsigned short val0 = *(ptrs++), val1 = *(ptrs++);
8544           *(data1++) = (T)(val1&0xf8);
8545           *(data2++) = (T)((val1<<5) | ((val0&0xe0)>>5));
8546           *(data3++) = (T)(val0<<3);
8547         }
8548       } break;
8549       default : {
8550         if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8551           ++ptrs;
8552           *(data1++) = (T)*(ptrs++);
8553           *(data2++) = (T)*(ptrs++);
8554           *(data3++) = (T)*(ptrs++);
8555           } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8556             *(data3++) = (T)*(ptrs++);
8557             *(data2++) = (T)*(ptrs++);
8558             *(data1++) = (T)*(ptrs++);
8559             ++ptrs;
8560           }
8561       }
8562       }
8563       return *this;
8564     }
8565 
8566     // Windows-based implementation.
8567     //-------------------------------
8568 #elif cimg_display==2
8569 
8570     bool _is_mouse_tracked, _is_cursor_visible;
8571     HANDLE _thread, _is_created, _mutex;
8572     HWND _window, _background_window;
8573     CLIENTCREATESTRUCT _ccs;
8574     unsigned int *_data;
8575     DEVMODE _curr_mode;
8576     BITMAPINFO _bmi;
8577     HDC _hdc;
8578 
8579     static int screen_width() {
8580       DEVMODE mode;
8581       mode.dmSize = sizeof(DEVMODE);
8582       mode.dmDriverExtra = 0;
8583       EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode);
8584       return mode.dmPelsWidth;
8585     }
8586 
8587     static int screen_height() {
8588       DEVMODE mode;
8589       mode.dmSize = sizeof(DEVMODE);
8590       mode.dmDriverExtra = 0;
8591       EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode);
8592       return mode.dmPelsHeight;
8593     }
8594 
8595     static void wait_all() {
8596       WaitForSingleObject(cimg::Win32_attr().wait_event,INFINITE);
8597     }
8598 
8599     static LRESULT APIENTRY _handle_events(HWND window,UINT msg,WPARAM wParam,LPARAM lParam) {
8600 #ifdef _WIN64
8601       CImgDisplay *const disp = (CImgDisplay*)GetWindowLongPtr(window,GWLP_USERDATA);
8602 #else
8603       CImgDisplay *const disp = (CImgDisplay*)GetWindowLong(window,GWL_USERDATA);
8604 #endif
8605       MSG st_msg;
8606       switch (msg) {
8607       case WM_CLOSE :
8608         disp->_mouse_x = disp->_mouse_y = -1;
8609         disp->_window_x = disp->_window_y = 0;
8610         disp->set_button().set_key(0).set_key(0,false)._is_closed = true;
8611         ReleaseMutex(disp->_mutex);
8612         ShowWindow(disp->_window,SW_HIDE);
8613         disp->_is_event = true;
8614         SetEvent(cimg::Win32_attr().wait_event);
8615         return 0;
8616       case WM_SIZE : {
8617         while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {}
8618         WaitForSingleObject(disp->_mutex,INFINITE);
8619         const unsigned int nw = LOWORD(lParam),nh = HIWORD(lParam);
8620         if (nw && nh && (nw!=disp->_width || nh!=disp->_height)) {
8621           disp->_window_width = nw;
8622           disp->_window_height = nh;
8623           disp->_mouse_x = disp->_mouse_y = -1;
8624           disp->_is_resized = disp->_is_event = true;
8625           SetEvent(cimg::Win32_attr().wait_event);
8626         }
8627         ReleaseMutex(disp->_mutex);
8628       } break;
8629       case WM_MOVE : {
8630         while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {}
8631         WaitForSingleObject(disp->_mutex,INFINITE);
8632         const int nx = (int)(short)(LOWORD(lParam)), ny = (int)(short)(HIWORD(lParam));
8633         if (nx!=disp->_window_x || ny!=disp->_window_y) {
8634           disp->_window_x = nx;
8635           disp->_window_y = ny;
8636           disp->_is_moved = disp->_is_event = true;
8637           SetEvent(cimg::Win32_attr().wait_event);
8638         }
8639         ReleaseMutex(disp->_mutex);
8640       } break;
8641       case WM_PAINT :
8642         disp->paint();
8643         break;
8644       case WM_KEYDOWN :
8645         disp->set_key((unsigned int)wParam);
8646         SetEvent(cimg::Win32_attr().wait_event);
8647         break;
8648       case WM_KEYUP :
8649         disp->set_key((unsigned int)wParam,false);
8650         SetEvent(cimg::Win32_attr().wait_event);
8651         break;
8652       case WM_MOUSEMOVE : {
8653         while (PeekMessage(&st_msg,window,WM_MOUSEMOVE,WM_MOUSEMOVE,PM_REMOVE)) {}
8654         disp->_mouse_x = LOWORD(lParam);
8655         disp->_mouse_y = HIWORD(lParam);
8656 #if (_WIN32_WINNT>=0x0400) && !defined(NOTRACKMOUSEEVENT)
8657         if (!disp->_is_mouse_tracked) {
8658           TRACKMOUSEEVENT tme;
8659           tme.cbSize = sizeof(TRACKMOUSEEVENT);
8660           tme.dwFlags = TME_LEAVE;
8661           tme.hwndTrack = disp->_window;
8662           if (TrackMouseEvent(&tme)) disp->_is_mouse_tracked = true;
8663         }
8664 #endif
8665         if (disp->_mouse_x<0 || disp->_mouse_y<0 || disp->_mouse_x>=disp->width() || disp->_mouse_y>=disp->height())
8666           disp->_mouse_x = disp->_mouse_y = -1;
8667         disp->_is_event = true;
8668         SetEvent(cimg::Win32_attr().wait_event);
8669       } break;
8670       case WM_MOUSELEAVE : {
8671         disp->_mouse_x = disp->_mouse_y = -1;
8672         disp->_is_mouse_tracked = false;
8673       } break;
8674       case WM_LBUTTONDOWN :
8675         disp->set_button(1);
8676         SetEvent(cimg::Win32_attr().wait_event);
8677         break;
8678       case WM_RBUTTONDOWN :
8679         disp->set_button(2);
8680         SetEvent(cimg::Win32_attr().wait_event);
8681         break;
8682       case WM_MBUTTONDOWN :
8683         disp->set_button(3);
8684         SetEvent(cimg::Win32_attr().wait_event);
8685         break;
8686       case WM_LBUTTONUP :
8687         disp->set_button(1,false);
8688         SetEvent(cimg::Win32_attr().wait_event);
8689         break;
8690       case WM_RBUTTONUP :
8691         disp->set_button(2,false);
8692         SetEvent(cimg::Win32_attr().wait_event);
8693         break;
8694       case WM_MBUTTONUP :
8695         disp->set_button(3,false);
8696         SetEvent(cimg::Win32_attr().wait_event);
8697         break;
8698       case 0x020A : // WM_MOUSEWHEEL:
8699         disp->set_wheel((int)((short)HIWORD(wParam))/120);
8700         SetEvent(cimg::Win32_attr().wait_event);
8701       case WM_SETCURSOR :
8702         if (disp->_is_cursor_visible) ShowCursor(TRUE);
8703         else ShowCursor(FALSE);
8704         break;
8705       }
8706       return DefWindowProc(window,msg,wParam,lParam);
8707     }
8708 
8709     static DWORD WINAPI _events_thread(void* arg) {
8710       CImgDisplay *const disp = (CImgDisplay*)(((void**)arg)[0]);
8711       const char *const title = (const char*)(((void**)arg)[1]);
8712       MSG msg;
8713       delete[] (void**)arg;
8714       disp->_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
8715       disp->_bmi.bmiHeader.biWidth = disp->width();
8716       disp->_bmi.bmiHeader.biHeight = -disp->height();
8717       disp->_bmi.bmiHeader.biPlanes = 1;
8718       disp->_bmi.bmiHeader.biBitCount = 32;
8719       disp->_bmi.bmiHeader.biCompression = BI_RGB;
8720       disp->_bmi.bmiHeader.biSizeImage = 0;
8721       disp->_bmi.bmiHeader.biXPelsPerMeter = 1;
8722       disp->_bmi.bmiHeader.biYPelsPerMeter = 1;
8723       disp->_bmi.bmiHeader.biClrUsed = 0;
8724       disp->_bmi.bmiHeader.biClrImportant = 0;
8725       disp->_data = new unsigned int[(unsigned long)disp->_width*disp->_height];
8726       if (!disp->_is_fullscreen) { // Normal window
8727         RECT rect;
8728         rect.left = rect.top = 0; rect.right = disp->_width-1; rect.bottom = disp->_height-1;
8729         AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
8730         const int
8731           border1 = (rect.right - rect.left + 1 - disp->_width)/2,
8732           border2 = rect.bottom - rect.top + 1 - disp->_height - border1;
8733         disp->_window = CreateWindowA("MDICLIENT",title?title:" ",
8734                                      WS_OVERLAPPEDWINDOW | (disp->_is_closed?0:WS_VISIBLE), CW_USEDEFAULT,CW_USEDEFAULT,
8735                                      disp->_width + 2*border1, disp->_height + border1 + border2,
8736                                      0,0,0,&(disp->_ccs));
8737         if (!disp->_is_closed) {
8738           GetWindowRect(disp->_window,&rect);
8739           disp->_window_x = rect.left + border1;
8740           disp->_window_y = rect.top + border2;
8741         } else disp->_window_x = disp->_window_y = 0;
8742       } else { // Fullscreen window
8743         const unsigned int sx = screen_width(), sy = screen_height();
8744         disp->_window = CreateWindowA("MDICLIENT",title?title:" ",
8745                                      WS_POPUP | (disp->_is_closed?0:WS_VISIBLE), (sx-disp->_width)/2, (sy-disp->_height)/2,
8746                                      disp->_width,disp->_height,0,0,0,&(disp->_ccs));
8747         disp->_window_x = disp->_window_y = 0;
8748       }
8749       SetForegroundWindow(disp->_window);
8750       disp->_hdc = GetDC(disp->_window);
8751       disp->_window_width = disp->_width;
8752       disp->_window_height = disp->_height;
8753       disp->flush();
8754 #ifdef _WIN64
8755       SetWindowLongPtr(disp->_window,GWLP_USERDATA,(LONG_PTR)disp);
8756       SetWindowLongPtr(disp->_window,GWLP_WNDPROC,(LONG_PTR)_handle_events);
8757 #else
8758       SetWindowLong(disp->_window,GWL_USERDATA,(LONG)disp);
8759       SetWindowLong(disp->_window,GWL_WNDPROC,(LONG)_handle_events);
8760 #endif
8761       SetEvent(disp->_is_created);
8762       while (GetMessage(&msg,0,0,0)) DispatchMessage(&msg);
8763       return 0;
8764     }
8765 
8766     CImgDisplay& _update_window_pos() {
8767       if (_is_closed) _window_x = _window_y = -1;
8768       else {
8769         RECT rect;
8770         rect.left = rect.top = 0; rect.right = _width-1; rect.bottom = _height-1;
8771         AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
8772         const int
8773           border1 = (rect.right - rect.left + 1 - _width)/2,
8774           border2 = rect.bottom - rect.top + 1 - _height - border1;
8775         GetWindowRect(_window,&rect);
8776         _window_x = rect.left + border1;
8777         _window_y = rect.top + border2;
8778       }
8779       return *this;
8780     }
8781 
8782     void _init_fullscreen() {
8783       _background_window = 0;
8784       if (!_is_fullscreen || _is_closed) _curr_mode.dmSize = 0;
8785       else {
8786         DEVMODE mode;
8787         unsigned int imode = 0, ibest = 0, bestbpp = 0, bw = ~0U, bh = ~0U;
8788         for (mode.dmSize = sizeof(DEVMODE), mode.dmDriverExtra = 0; EnumDisplaySettings(0,imode,&mode); ++imode) {
8789           const unsigned int nw = mode.dmPelsWidth, nh = mode.dmPelsHeight;
8790           if (nw>=_width && nh>=_height && mode.dmBitsPerPel>=bestbpp && nw<=bw && nh<=bh) {
8791             bestbpp = mode.dmBitsPerPel;
8792             ibest = imode;
8793             bw = nw; bh = nh;
8794           }
8795         }
8796         if (bestbpp) {
8797           _curr_mode.dmSize = sizeof(DEVMODE); _curr_mode.dmDriverExtra = 0;
8798           EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&_curr_mode);
8799           EnumDisplaySettings(0,ibest,&mode);
8800           ChangeDisplaySettings(&mode,0);
8801         } else _curr_mode.dmSize = 0;
8802 
8803         const unsigned int sx = screen_width(), sy = screen_height();
8804         if (sx!=_width || sy!=_height) {
8805           CLIENTCREATESTRUCT background_ccs;
8806           _background_window = CreateWindowA("MDICLIENT","",WS_POPUP | WS_VISIBLE, 0,0,sx,sy,0,0,0,&background_ccs);
8807           SetForegroundWindow(_background_window);
8808         }
8809       }
8810     }
8811 
8812     void _desinit_fullscreen() {
8813       if (!_is_fullscreen) return;
8814       if (_background_window) DestroyWindow(_background_window);
8815       _background_window = 0;
8816       if (_curr_mode.dmSize) ChangeDisplaySettings(&_curr_mode,0);
8817       _is_fullscreen = false;
8818     }
8819 
8820     CImgDisplay& _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0,
8821                          const unsigned int normalization_type=3,
8822                          const bool fullscreen_flag=false, const bool closed_flag=false) {
8823 
8824       // Allocate space for window title
8825       const char *const nptitle = ptitle?ptitle:"";
8826       const unsigned int s = (unsigned int)std::strlen(nptitle) + 1;
8827       char *const tmp_title = s?new char[s]:0;
8828       if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char));
8829 
8830       // Destroy previous window if existing
8831       if (!is_empty()) assign();
8832 
8833       // Set display variables
8834       _width = cimg::min(dimw,(unsigned int)screen_width());
8835       _height = cimg::min(dimh,(unsigned int)screen_height());
8836       _normalization = normalization_type<4?normalization_type:3;
8837       _is_fullscreen = fullscreen_flag;
8838       _window_x = _window_y = 0;
8839       _is_closed = closed_flag;
8840       _is_cursor_visible = true;
8841       _is_mouse_tracked = false;
8842       _title = tmp_title;
8843       flush();
8844       if (_is_fullscreen) _init_fullscreen();
8845 
8846       // Create event thread
8847       void *const arg = (void*)(new void*[2]);
8848       ((void**)arg)[0] = (void*)this;
8849       ((void**)arg)[1] = (void*)_title;
8850       unsigned long ThreadID = 0;
8851       _mutex = CreateMutex(0,FALSE,0);
8852       _is_created = CreateEvent(0,FALSE,FALSE,0);
8853       _thread = CreateThread(0,0,_events_thread,arg,0,&ThreadID);
8854       WaitForSingleObject(_is_created,INFINITE);
8855       return *this;
8856     }
8857 
8858     CImgDisplay& assign() {
8859       if (is_empty()) return flush();
8860       DestroyWindow(_window);
8861       TerminateThread(_thread,0);
8862       delete[] _data;
8863       delete[] _title;
8864       _data = 0;
8865       _title = 0;
8866       if (_is_fullscreen) _desinit_fullscreen();
8867       _width = _height = _normalization = _window_width = _window_height = 0;
8868       _window_x = _window_y = 0;
8869       _is_fullscreen = false;
8870       _is_closed = true;
8871       _min = _max = 0;
8872       _title = 0;
8873       flush();
8874       return *this;
8875     }
8876 
8877     CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0,
8878                         const unsigned int normalization_type=3,
8879                         const bool fullscreen_flag=false, const bool closed_flag=false) {
8880       if (!dimw || !dimh) return assign();
8881       _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag);
8882       _min = _max = 0;
8883       std::memset(_data,0,sizeof(unsigned int)*_width*_height);
8884       return paint();
8885     }
8886 
8887     template<typename T>
8888     CImgDisplay& assign(const CImg<T>& img, const char *const title=0,
8889                         const unsigned int normalization_type=3,
8890                         const bool fullscreen_flag=false, const bool closed_flag=false) {
8891       if (!img) return assign();
8892       CImg<T> tmp;
8893       const CImg<T>& nimg = (img._depth==1)?img:(tmp=img.get_projections2d(img._width/2,img._height/2,img._depth/2));
8894       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
8895       if (_normalization==2) _min = (float)nimg.min_max(_max);
8896       return display(nimg);
8897     }
8898 
8899     template<typename T>
8900     CImgDisplay& assign(const CImgList<T>& list, const char *const title=0,
8901                         const unsigned int normalization_type=3,
8902                         const bool fullscreen_flag=false, const bool closed_flag=false) {
8903       if (!list) return assign();
8904       CImg<T> tmp;
8905       const CImg<T> img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d(img._width/2,img._height/2,img._depth/2));
8906       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
8907       if (_normalization==2) _min = (float)nimg.min_max(_max);
8908       return display(nimg);
8909     }
8910 
8911     CImgDisplay& assign(const CImgDisplay& disp) {
8912       if (!disp) return assign();
8913       _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed);
8914       std::memcpy(_data,disp._data,sizeof(unsigned int)*_width*_height);
8915       return paint();
8916     }
8917 
8918     CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) {
8919       if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign();
8920       if (is_empty()) return assign(nwidth,nheight);
8921       const unsigned int
8922         tmpdimx = (nwidth>0)?nwidth:(-nwidth*_width/100),
8923         tmpdimy = (nheight>0)?nheight:(-nheight*_height/100),
8924         dimx = tmpdimx?tmpdimx:1,
8925         dimy = tmpdimy?tmpdimy:1;
8926       if (_window_width!=dimx || _window_height!=dimy) {
8927         RECT rect; rect.left = rect.top = 0; rect.right = dimx - 1; rect.bottom = dimy - 1;
8928         AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
8929         const int cwidth = rect.right - rect.left + 1, cheight = rect.bottom - rect.top + 1;
8930         SetWindowPos(_window,0,0,0,cwidth,cheight,SWP_NOMOVE | SWP_NOZORDER | SWP_NOCOPYBITS);
8931       }
8932       if (_width!=dimx || _height!=dimy) {
8933         unsigned int *const ndata = new unsigned int[dimx*dimy];
8934         if (force_redraw) _render_resize(_data,_width,_height,ndata,dimx,dimy);
8935         else std::memset(ndata,0x80,sizeof(unsigned int)*dimx*dimy);
8936         delete[] _data;
8937         _data = ndata;
8938         _bmi.bmiHeader.biWidth = dimx;
8939         _bmi.bmiHeader.biHeight = -(int)dimy;
8940         _width = dimx;
8941         _height = dimy;
8942       }
8943       _window_width = dimx; _window_height = dimy;
8944       _is_resized = false;
8945       if (_is_fullscreen) move((screen_width()-_width)/2,(screen_height()-_height)/2);
8946       if (force_redraw) return paint();
8947       return *this;
8948     }
8949 
8950     CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
8951       if (is_empty()) return *this;
8952       if (force_redraw) {
8953         const unsigned long buf_size = _width*_height*4UL;
8954         void *odata = std::malloc(buf_size);
8955         std::memcpy(odata,_data,buf_size);
8956         assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
8957         std::memcpy(_data,odata,buf_size);
8958         std::free(odata);
8959         return paint();
8960       }
8961       return assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
8962     }
8963 
8964     CImgDisplay& show() {
8965       if (is_empty() || !_is_closed) return *this;
8966       _is_closed = false;
8967       if (_is_fullscreen) _init_fullscreen();
8968       ShowWindow(_window,SW_SHOW);
8969       _update_window_pos();
8970       return paint();
8971     }
8972 
8973     CImgDisplay& close() {
8974       if (is_empty() || _is_closed) return *this;
8975       _is_closed = true;
8976       if (_is_fullscreen) _desinit_fullscreen();
8977       ShowWindow(_window,SW_HIDE);
8978       _window_x = _window_y = 0;
8979       return *this;
8980     }
8981 
8982     CImgDisplay& move(const int posx, const int posy) {
8983       if (is_empty()) return *this;
8984       if (!_is_fullscreen) {
8985         RECT rect; rect.left = rect.top = 0; rect.right = _window_width-1; rect.bottom = _window_height-1;
8986         AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
8987         const int border1 = (rect.right-rect.left+1-_width)/2, border2 = rect.bottom-rect.top+1-_height-border1;
8988         SetWindowPos(_window,0,posx-border1,posy-border2,0,0,SWP_NOSIZE | SWP_NOZORDER);
8989       } else SetWindowPos(_window,0,posx,posy,0,0,SWP_NOSIZE | SWP_NOZORDER);
8990       _window_x = posx;
8991       _window_y = posy;
8992       _is_moved = false;
8993       return show();
8994     }
8995 
8996     CImgDisplay& show_mouse() {
8997       if (is_empty()) return *this;
8998       _is_cursor_visible = true;
8999       ShowCursor(TRUE);
9000       SendMessage(_window,WM_SETCURSOR,0,0);
9001       return *this;
9002     }
9003 
9004     CImgDisplay& hide_mouse() {
9005       if (is_empty()) return *this;
9006       _is_cursor_visible = false;
9007       ShowCursor(FALSE);
9008       SendMessage(_window,WM_SETCURSOR,0,0);
9009       return *this;
9010     }
9011 
9012     CImgDisplay& set_mouse(const int posx, const int posy) {
9013       if (_is_closed || posx<0 || posy<0) return *this;
9014       _update_window_pos();
9015       const int res = (int)SetCursorPos(_window_x + posx,_window_y + posy);
9016       if (res) { _mouse_x = posx; _mouse_y = posy; }
9017       return *this;
9018     }
9019 
9020     CImgDisplay& set_title(const char *const format, ...) {
9021       if (is_empty()) return *this;
9022       char tmp[1024] = { 0 };
9023       va_list ap;
9024       va_start(ap, format);
9025       cimg_vsnprintf(tmp,sizeof(tmp),format,ap);
9026       va_end(ap);
9027       if (!std::strcmp(_title,tmp)) return *this;
9028       delete[] _title;
9029       const unsigned int s = (unsigned int)std::strlen(tmp) + 1;
9030       _title = new char[s];
9031       std::memcpy(_title,tmp,s*sizeof(char));
9032       SetWindowTextA(_window, tmp);
9033       return *this;
9034     }
9035 
9036     template<typename T>
9037     CImgDisplay& display(const CImg<T>& img) {
9038       if (!img)
9039         throw CImgArgumentException(_cimgdisplay_instance
9040                                     "display() : Empty specified image.",
9041                                     cimgdisplay_instance);
9042       if (is_empty()) return assign(img);
9043       return render(img).paint();
9044     }
9045 
9046     CImgDisplay& paint() {
9047       if (_is_closed) return *this;
9048       WaitForSingleObject(_mutex,INFINITE);
9049       SetDIBitsToDevice(_hdc,0,0,_width,_height,0,0,0,_height,_data,&_bmi,DIB_RGB_COLORS);
9050       ReleaseMutex(_mutex);
9051       return *this;
9052     }
9053 
9054     template<typename T>
9055     CImgDisplay& render(const CImg<T>& img) {
9056       if (!img)
9057         throw CImgArgumentException(_cimgdisplay_instance
9058                                     "render() : Empty specified image.",
9059                                     cimgdisplay_instance);
9060 
9061       if (is_empty()) return *this;
9062       if (img._depth!=1) return render(img.get_projections2d(img._width/2,img._height/2,img._depth/2));
9063 
9064       const T
9065         *data1 = img._data,
9066         *data2 = (img._spectrum>=2)?img.data(0,0,0,1):data1,
9067         *data3 = (img._spectrum>=3)?img.data(0,0,0,2):data1;
9068 
9069       WaitForSingleObject(_mutex,INFINITE);
9070       unsigned int
9071         *const ndata = (img._width==_width && img._height==_height)?_data:new unsigned int[(unsigned long)img._width*img._height],
9072         *ptrd = ndata;
9073 
9074       if (!_normalization || (_normalization==3 && cimg::type<T>::string()==cimg::type<unsigned char>::string())) {
9075         _min = _max = 0;
9076         switch (img._spectrum) {
9077         case 1 : {
9078           for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
9079             const unsigned char val = (unsigned char)*(data1++);
9080             *(ptrd++) = (val<<16) | (val<<8) | val;
9081           }
9082         } break;
9083         case 2 : {
9084           for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
9085             *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8);
9086         } break;
9087         default : {
9088           for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
9089             *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) | (unsigned char)*(data3++);
9090         }
9091         }
9092       } else {
9093         if (_normalization==3) {
9094           if (cimg::type<T>::is_float()) _min = (float)img.min_max(_max);
9095           else { _min = (float)cimg::type<T>::min(); _max = (float)cimg::type<T>::max(); }
9096         } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max);
9097         const float delta = _max - _min, mm = 255/(delta?delta:1.0f);
9098         switch (img._spectrum) {
9099         case 1 : {
9100           for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
9101             const unsigned char val = (unsigned char)((*(data1++)-_min)*mm);
9102             *(ptrd++) = (val<<16) | (val<<8) | val;
9103           }
9104         } break;
9105         case 2 : {
9106           for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
9107             const unsigned char
9108               R = (unsigned char)((*(data1++)-_min)*mm),
9109               G = (unsigned char)((*(data2++)-_min)*mm);
9110             *(ptrd++) = (R<<16) | (G<<8);
9111           }
9112         } break;
9113         default : {
9114           for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
9115             const unsigned char
9116               R = (unsigned char)((*(data1++)-_min)*mm),
9117               G = (unsigned char)((*(data2++)-_min)*mm),
9118               B = (unsigned char)((*(data3++)-_min)*mm);
9119             *(ptrd++) = (R<<16) | (G<<8) | B;
9120           }
9121         }
9122         }
9123       }
9124       if (ndata!=_data) { _render_resize(ndata,img._width,img._height,_data,_width,_height); delete[] ndata; }
9125       ReleaseMutex(_mutex);
9126       return *this;
9127     }
9128 
9129     template<typename T>
9130     const CImgDisplay& snapshot(CImg<T>& img) const {
9131       if (is_empty()) { img.assign(); return *this; }
9132       const unsigned int *ptrs = _data;
9133       img.assign(_width,_height,1,3);
9134       T
9135         *data1 = img.data(0,0,0,0),
9136         *data2 = img.data(0,0,0,1),
9137         *data3 = img.data(0,0,0,2);
9138       for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
9139         const unsigned int val = *(ptrs++);
9140         *(data1++) = (T)(unsigned char)(val>>16);
9141         *(data2++) = (T)(unsigned char)((val>>8)&0xFF);
9142         *(data3++) = (T)(unsigned char)(val&0xFF);
9143       }
9144       return *this;
9145     }
9146 #endif
9147 
9148     //@}
9149   };
9150 
9151   /*
9152    #--------------------------------------
9153    #
9154    #
9155    #
9156    # Definition of the CImg<T> structure
9157    #
9158    #
9159    #
9160    #--------------------------------------
9161    */
9162 
9163   //! Class representing an image (up to 4 dimensions wide), each pixel being of type \c T.
9164   /**
9165      This is the main class of the %CImg Library. It declares and constructs
9166      an image, allows access to its pixel values, and is able to perform various image operations.
9167 
9168      \par Image representation
9169 
9170      A %CImg image is defined as an instance of the container \c CImg<T>, which contains a regular grid of pixels,
9171      each pixel value being of type \c T. The image grid can have up to 4 dimensions : width, height, depth
9172      and number of channels.
9173      Usually, the three first dimensions are used to describe spatial coordinates <tt>(x,y,z)</tt>, while the number of channels
9174      is rather used as a vector-valued dimension (it may describe the R,G,B color channels for instance).
9175      If you need a fifth dimension, you can use image lists \c CImgList<T> rather than simple images \c CImg<T>.
9176 
9177      Thus, the \c CImg<T> class is able to represent volumetric images of vector-valued pixels,
9178      as well as images with less dimensions (1d scalar signal, 2d color images, ...).
9179      Most member functions of the class CImg<\c T> are designed to handle this maximum case of (3+1) dimensions.
9180 
9181      Concerning the pixel value type \c T :
9182      fully supported template types are the basic C++ types : <tt>unsigned char, char, short, unsigned int, int,
9183      unsigned long, long, float, double, ... </tt>.
9184      Typically, fast image display can be done using <tt>CImg<unsigned char></tt> images,
9185      while complex image processing algorithms may be rather coded using <tt>CImg<float></tt> or <tt>CImg<double></tt>
9186      images that have floating-point pixel values. The default value for the template T is \c float.
9187      Using your own template types may be possible. However, you will certainly have to define the complete set
9188      of arithmetic and logical operators for your class.
9189 
9190      \par Image structure
9191 
9192      The \c CImg<T> structure contains \e six fields :
9193      - \c _width defines the number of \a columns of the image (size along the X-axis).
9194      - \c _height defines the number of \a rows of the image (size along the Y-axis).
9195      - \c _depth defines the number of \a slices of the image (size along the Z-axis).
9196      - \c _spectrum defines the number of \a channels of the image (size along the C-axis).
9197      - \c _data defines a \a pointer to the \a pixel \a data (of type \c T).
9198      - \c _is_shared is a boolean that tells if the memory buffer \c data is shared with
9199        another image.
9200 
9201      You can access these fields publicly although it is recommended to use the dedicated functions
9202      width(), height(), depth(), spectrum() and ptr() to do so.
9203      Image dimensions are not limited to a specific range (as long as you got enough available memory).
9204      A value of \e 1 usually means that the corresponding dimension is \a flat.
9205      If one of the dimensions is \e 0, or if the data pointer is null, the image is considered as \e empty.
9206      Empty images should not contain any pixel data and thus, will not be processed by CImg member functions
9207      (a CImgInstanceException will be thrown instead).
9208      Pixel data are stored in memory, in a non interlaced mode (See \ref cimg_storage).
9209 
9210      \par Image declaration and construction
9211 
9212      Declaring an image can be done by using one of the several available constructors.
9213      Here is a list of the most used :
9214 
9215      - Construct images from arbitrary dimensions :
9216          - <tt>CImg<char> img;</tt> declares an empty image.
9217          - <tt>CImg<unsigned char> img(128,128);</tt> declares a 128x128 greyscale image with
9218          \c unsigned \c char pixel values.
9219          - <tt>CImg<double> img(3,3);</tt> declares a 3x3 matrix with \c double coefficients.
9220          - <tt>CImg<unsigned char> img(256,256,1,3);</tt> declares a 256x256x1x3 (color) image
9221          (colors are stored as an image with three channels).
9222          - <tt>CImg<double> img(128,128,128);</tt> declares a 128x128x128 volumetric and greyscale image
9223          (with \c double pixel values).
9224          - <tt>CImg<> img(128,128,128,3);</tt> declares a 128x128x128 volumetric color image
9225          (with \c float pixels, which is the default value of the template parameter \c T).
9226          - \b Note : images pixels are <b>not automatically initialized to 0</b>. You may use the function \c fill() to
9227          do it, or use the specific constructor taking 5 parameters like this :
9228          <tt>CImg<> img(128,128,128,3,0);</tt> declares a 128x128x128 volumetric color image with all pixel values to 0.
9229 
9230      - Construct images from filenames :
9231          - <tt>CImg<unsigned char> img("image.jpg");</tt> reads a JPEG color image from the file "image.jpg".
9232          - <tt>CImg<float> img("analyze.hdr");</tt> reads a volumetric image (ANALYZE7.5 format) from the file "analyze.hdr".
9233          - \b Note : You need to install <a href="http://www.imagemagick.org">ImageMagick</a>
9234          to be able to read common compressed image formats (JPG,PNG, ...) (See \ref cimg_files_io).
9235 
9236      - Construct images from C-style arrays :
9237          - <tt>CImg<int> img(data_buffer,256,256);</tt> constructs a 256x256 greyscale image from a \c int* buffer
9238          \c data_buffer (of size 256x256=65536).
9239          - <tt>CImg<unsigned char> img(data_buffer,256,256,1,3,false);</tt> constructs a 256x256 color image
9240          from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels follow each others).
9241          - <tt>CImg<unsigned char> img(data_buffer,256,256,1,3,true);</tt> constructs a 256x256 color image
9242          from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels are multiplexed).
9243 
9244          The complete list of constructors can be found <a href="#constructors">here</a>.
9245 
9246      \par Most useful functions
9247 
9248      The \c CImg<T> class contains a lot of functions that operates on images.
9249      Some of the most useful are :
9250 
9251      - operator()() : allows to access or write pixel values.
9252      - display() : displays the image in a new window.
9253   **/
9254   template<typename T>
9255   struct CImg {
9256 
9257     unsigned int _width, _height, _depth, _spectrum;
9258     bool _is_shared;
9259     T *_data;
9260 
9261     //! Simple iterator type, to loop through each pixel value of an image instance.
9262     /**
9263        \note
9264        - The \c CImg<T>::iterator type is defined to be a <tt>T*</tt>.
9265        - You will seldom have to use iterators in %CImg, most classical operations
9266          being achieved (often in a faster way) using methods of \c CImg<T>.
9267        \par Example
9268        \code
9269        CImg<float> img("reference.jpg");                                         // Load image from file.
9270        for (CImg<float>::iterator it = img.begin(), it<img.end(); ++it) *it = 0; // Set all pixels to '0', through a CImg iterator.
9271        img.fill(0);                                                              // Do the same with a built-in method.
9272        \endcode
9273    **/
9274     typedef T* iterator;
9275 
9276     //! Simple const iterator type, to loop through each pixel value of a \c const image instance.
9277     /**
9278        \note
9279        - The \c CImg<T>::const_iterator type is defined to be a \c const \c T*.
9280        - You will seldom have to use iterators in %CImg, most classical operations
9281          being achieved (often in a faster way) using methods of \c CImg<T>.
9282        \par Example
9283        \code
9284        const CImg<float> img("reference.jpg");                                    // Load image from file.
9285        float sum = 0;
9286        for (CImg<float>::iterator it = img.begin(), it<img.end(); ++it) sum+=*it; // Compute sum of all pixel values, through a CImg iterator.
9287        const float sum2 = img.sum();                                              // Do the same with a built-in method.
9288        \endcode
9289     **/
9290     typedef const T* const_iterator;
9291 
9292     //! Pixel value type.
9293     /**
9294        Refer to the type of the pixel values of an image instance.
9295        \note
9296        - The \c CImg<T>::value_type type of a \c CImg<T> is defined to be a \c T.
9297        - \c CImg<T>::value_type is actually not used in %CImg methods. It has been mainly defined for
9298          compatibility with STL naming conventions.
9299     **/
9300     typedef T value_type;
9301 
9302     // Define common types related to template type T.
9303     typedef typename cimg::superset<T,bool>::type Tbool;
9304     typedef typename cimg::superset<T,unsigned char>::type Tuchar;
9305     typedef typename cimg::superset<T,char>::type Tchar;
9306     typedef typename cimg::superset<T,unsigned short>::type Tushort;
9307     typedef typename cimg::superset<T,short>::type Tshort;
9308     typedef typename cimg::superset<T,unsigned int>::type Tuint;
9309     typedef typename cimg::superset<T,int>::type Tint;
9310     typedef typename cimg::superset<T,unsigned long>::type Tulong;
9311     typedef typename cimg::superset<T,long>::type Tlong;
9312     typedef typename cimg::superset<T,float>::type Tfloat;
9313     typedef typename cimg::superset<T,double>::type Tdouble;
9314     typedef typename cimg::last<T,bool>::type boolT;
9315     typedef typename cimg::last<T,unsigned char>::type ucharT;
9316     typedef typename cimg::last<T,char>::type charT;
9317     typedef typename cimg::last<T,unsigned short>::type ushortT;
9318     typedef typename cimg::last<T,short>::type shortT;
9319     typedef typename cimg::last<T,unsigned int>::type uintT;
9320     typedef typename cimg::last<T,int>::type intT;
9321     typedef typename cimg::last<T,unsigned long>::type ulongT;
9322     typedef typename cimg::last<T,long>::type longT;
9323     typedef typename cimg::last<T,float>::type floatT;
9324     typedef typename cimg::last<T,double>::type doubleT;
9325 
9326     //@}
9327     //---------------------------
9328     //
9329     //! \name Plugins
9330     //@{
9331     //---------------------------
9332 #ifdef cimg_plugin
9333 #include cimg_plugin
9334 #endif
9335 #ifdef cimg_plugin1
9336 #include cimg_plugin1
9337 #endif
9338 #ifdef cimg_plugin2
9339 #include cimg_plugin2
9340 #endif
9341 #ifdef cimg_plugin3
9342 #include cimg_plugin3
9343 #endif
9344 #ifdef cimg_plugin4
9345 #include cimg_plugin4
9346 #endif
9347 #ifdef cimg_plugin5
9348 #include cimg_plugin5
9349 #endif
9350 #ifdef cimg_plugin6
9351 #include cimg_plugin6
9352 #endif
9353 #ifdef cimg_plugin7
9354 #include cimg_plugin7
9355 #endif
9356 #ifdef cimg_plugin8
9357 #include cimg_plugin8
9358 #endif
9359 
9360     //@}
9361     //---------------------------------------------------------
9362     //
9363     //! \name Constructors / Destructor / Instance Management
9364     //@{
9365     //---------------------------------------------------------
9366 
9367     //! Destroy image.
9368     /**
9369        \note
9370        - The pixel buffer data() is deallocated if necessary, e.g. for non-empty and non-shared image instances.
9371        - Destroying an empty or shared image does nothing actually.
9372        \warning
9373        - When destroying a non-shared image, make sure that you will \e not operate on a remaining shared image
9374          that shares its buffer with the destroyed instance, in order to avoid further invalid memory access (to a deallocated buffer).
9375     **/
9376     ~CImg() {
9377       if (!_is_shared) delete[] _data;
9378     }
9379 
9380     //! Construct empty image.
9381     /**
9382        \note
9383        - An empty image has no pixel data and all of its dimensions width(), height(), depth(), spectrum()
9384          are set to \c 0, as well as its pixel buffer pointer data().
9385        - An empty image may be re-assigned afterwards, e.g. with the family of assign(unsigned int,unsigned int,unsigned int,unsigned int) methods,
9386          or by operator=(const CImg<t>&). In all cases, the type of pixels stays \c T.
9387        - An empty image is never shared.
9388        \par Example
9389        \code
9390        CImg<float> img1, img2;      // Construct two empty images.
9391        img1.assign(256,256,1,3);    // Re-assign 'img1' to be a 256x256x1x3 (color) image.
9392        img2 = img1.get_rand(0,255); // Re-assign 'img2' to be a random-valued version of 'img1'.
9393        img2.assign();               // Re-assign 'img2' to be an empty image again.
9394        \endcode
9395     **/
9396     CImg():_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {}
9397 
9398     //! Construct image with specified size.
9399     /**
9400        \param size_x Image width().
9401        \param size_y Image height().
9402        \param size_z Image depth().
9403        \param size_c Image spectrum() (number of channels).
9404        \note
9405        - It is able to create only \e non-shared images, and allocates thus a pixel buffer data() for each constructed image instance.
9406        - Setting one dimension \c size_x,\c size_y,\c size_z or \c size_c to \c 0 leads to the construction of an \e empty image.
9407        - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated (e.g. when requested size is too big for available memory).
9408        \warning
9409        - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values.
9410          In order to initialize pixel values during construction (e.g. with \c 0), use constructor
9411          CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) instead.
9412        \par Example
9413        \code
9414        CImg<float> img1(256,256,1,3);   // Construct a 256x256x1x3 (color) image, filled with garbage values.
9415        CImg<float> img2(256,256,1,3,0); // Construct a 256x256x1x3 (color) image, filled with value '0'.
9416        \endcode
9417     **/
9418     explicit CImg(const unsigned int size_x, const unsigned int size_y=1, const unsigned int size_z=1, const unsigned int size_c=1):
9419       _is_shared(false) {
9420       const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c;
9421       if (siz) {
9422         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
9423         try { _data = new T[siz]; } catch (...) {
9424           _width = _height = _depth = _spectrum = 0; _data = 0;
9425           throw CImgInstanceException(_cimg_instance
9426                                       "CImg() : Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9427                                       cimg_instance,
9428                                       cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),size_x,size_y,size_z,size_c);
9429         }
9430       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
9431     }
9432 
9433     //! Construct image with specified size and initialize pixel values.
9434     /**
9435        \param size_x Image width().
9436        \param size_y Image height().
9437        \param size_z Image depth().
9438        \param size_c Image spectrum() (number of channels).
9439        \param value Initialization value.
9440        \note
9441        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int),
9442          but it also fills the pixel buffer with the specified \c value.
9443        \warning
9444        - It cannot be used to construct a vector-valued image and initialize it with \e vector-valued pixels (e.g. RGB vector, for color images).
9445          For this task, you may use fillC() after construction.
9446     **/
9447     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, const T value):
9448       _is_shared(false) {
9449       const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c;
9450       if (siz) {
9451         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
9452         try { _data = new T[siz]; } catch (...) {
9453           _width = _height = _depth = _spectrum = 0; _data = 0;
9454           throw CImgInstanceException(_cimg_instance
9455                                       "CImg() : Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9456                                       cimg_instance,
9457                                       cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),size_x,size_y,size_z,size_c);
9458         }
9459         fill(value);
9460       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
9461     }
9462 
9463     //! Construct image with specified size and initialize pixel values from a sequence of integers.
9464     /**
9465        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, and initialize pixel
9466        values from the specified sequence of integers \c value0,\c value1,\c ...
9467        \param size_x Image width().
9468        \param size_y Image height().
9469        \param size_z Image depth().
9470        \param size_c Image spectrum() (number of channels).
9471        \param value0 First value of the initialization sequence (must be an \e integer).
9472        \param value1 Second value of the initialization sequence (must be an \e integer).
9473        \param ...
9474        \note
9475        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills
9476          the pixel buffer with a sequence of specified integer values.
9477        \warning
9478        - You must specify \e exactly \c size_x*\c size_y*\c size_z*\c size_c integers in the initialization sequence.
9479          Otherwise, the constructor may crash or fill your image pixels with garbage.
9480        \par Example
9481        \code
9482        const CImg<float> img(2,2,1,3,      // Construct a 2x2 color (RGB) image.
9483                              0,255,0,255,  // Set the 4 values for the red component.
9484                              0,0,255,255,  // Set the 4 values for the green component.
9485                              64,64,64,64); // Set the 4 values for the blue component.
9486        img.resize(150,150).display();
9487        \endcode
9488        \image html ref_constructor1.jpg
9489      **/
9490     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
9491          const int value0, const int value1, ...):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
9492 #define _CImg_stdarg(img,a0,a1,N,t) { \
9493         unsigned long _siz = (unsigned long)N; \
9494         if (_siz--) { \
9495           va_list ap; \
9496           va_start(ap,a1); \
9497           T *ptrd = (img)._data; \
9498           *(ptrd++) = (T)a0; \
9499           if (_siz--) { \
9500             *(ptrd++) = (T)a1; \
9501             for (; _siz; --_siz) *(ptrd++) = (T)va_arg(ap,t); \
9502           } \
9503           va_end(ap); \
9504         } \
9505       }
9506       assign(size_x,size_y,size_z,size_c);
9507       _CImg_stdarg(*this,value0,value1,(unsigned long)size_x*size_y*size_z*size_c,int);
9508     }
9509 
9510     //! Construct image with specified size and initialize pixel values from a sequence of doubles.
9511     /**
9512        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, and initialize pixel
9513        values from the specified sequence of doubles \c value0,\c value1,\c ...
9514        \param size_x Image width().
9515        \param size_y Image height().
9516        \param size_z Image depth().
9517        \param size_c Image spectrum() (number of channels).
9518        \param value0 First value of the initialization sequence (must be a \e double).
9519        \param value1 Second value of the initialization sequence (must be a \e double).
9520        \param ...
9521        \note
9522        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...), but
9523          takes a sequence of double values instead of integers.
9524        \warning
9525        - You must specify \e exactly \c dx*\c dy*\c dz*\c dc doubles in the initialization sequence.
9526          Otherwise, the constructor may crash or fill your image with garbage.
9527          For instance, the code below will probably crash on most platforms :
9528          \code
9529          const CImg<float> img(2,2,1,1, 0.5,0.5,255,255); // FAIL : The two last arguments are 'int', not 'double' !
9530          \endcode
9531      **/
9532     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
9533          const double value0, const double value1, ...):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
9534       assign(size_x,size_y,size_z,size_c);
9535       _CImg_stdarg(*this,value0,value1,(unsigned long)size_x*size_y*size_z*size_c,double);
9536     }
9537 
9538     //! Construct image with specified size and initialize pixel values from a value string.
9539     /**
9540        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, and initializes pixel
9541        values from the specified string \c values.
9542        \param size_x Image width().
9543        \param size_y Image height().
9544        \param size_z Image depth().
9545        \param size_c Image spectrum() (number of channels).
9546        \param values Value string describing the way pixel values are set.
9547        \param repeat_values Tells if the value filling process is repeated over the image.
9548        \note
9549        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills
9550          the pixel buffer with values described in the value string \c values.
9551        - Value string \c values may describe two different filling processes :
9552          - Either \c values is a sequences of values assigned to the image pixels, as in <tt>"1,2,3,7,8,2"</tt>.
9553            In this case, set \c repeat_values to \c true to periodically fill the image with the value sequence.
9554          - Either, \c values is a formula, as in <tt>"cos(x/10)*sin(y/20)"</tt>. In this case, parameter \c repeat_values is pointless.
9555        - For both cases, specifying \c repeat_values is mandatory. It disambiguates the possible overloading of constructor
9556          CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) with \c T being a <tt>const char*</tt>.
9557        - A \c CImgArgumentException is thrown when an invalid value string \c values is specified.
9558        \par Example
9559        \code
9560        const CImg<float> img1(129,129,1,3,"0,64,128,192,255",true),                   // Construct image filled from a value sequence.
9561                          img2(129,129,1,3,"if(c==0,255*abs(cos(x/10)),1.8*y)",false); // Construct image filled from a formula.
9562        (img1,img2).display();
9563        \endcode
9564        \image html ref_constructor2.jpg
9565      **/
9566     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
9567          const char *const values, const bool repeat_values):_is_shared(false) {
9568       const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c;
9569       if (siz) {
9570         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
9571         try { _data = new T[siz]; } catch (...) {
9572           _width = _height = _depth = _spectrum = 0; _data = 0;
9573           throw CImgInstanceException(_cimg_instance
9574                                       "CImg() : Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9575                                       cimg_instance,
9576                                       cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),size_x,size_y,size_z,size_c);
9577         }
9578         fill(values,repeat_values);
9579       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
9580     }
9581 
9582     //! Construct image with specified size and initialize pixel values from a memory buffer.
9583     /**
9584        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, and initializes pixel
9585        values from the specified \c t* memory buffer.
9586        \param values Pointer to the input memory buffer.
9587        \param size_x Image width().
9588        \param size_y Image height().
9589        \param size_z Image depth().
9590        \param size_c Image spectrum() (number of channels).
9591        \param is_shared Tells if input memory buffer must be shared by the current instance.
9592        \note
9593        - If \c is_shared is \c false, the image instance allocates its own pixel buffer, and values from the specified input buffer
9594          are copied to the instance buffer. If buffer types \c T and \c t are different, a regular static cast is performed during buffer copy.
9595        - Otherwise, the image instance does \e not allocate a new buffer, and uses the input memory buffer as its own pixel buffer. This case
9596          requires that types \c T and \c t are the same. Later, destroying such a shared image will not deallocate the pixel buffer,
9597          this task being obviously charged to the initial buffer allocator.
9598        - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated (e.g. when requested size is too big for available memory).
9599        \warning
9600        - You must take care when operating on a shared image, since it may have an invalid pixel buffer pointer data() (e.g. already deallocated).
9601        \par Example
9602        \code
9603        unsigned char tab[256*256] = { 0 };
9604        CImg<unsigned char> img1(tab,256,256,1,1,false), // Construct new non-shared image from buffer 'tab'.
9605                            img2(tab,256,256,1,1,true);  // Construct new shared-image from buffer 'tab'.
9606        tab[1024] = 255;                                 // Here, 'img2' is indirectly modified, but not 'img1'.
9607        \endcode
9608     **/
9609     template<typename t>
9610     CImg(const t *const values, const unsigned int size_x, const unsigned int size_y=1,
9611          const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false):_is_shared(false) {
9612       if (is_shared) {
9613         _width = _height = _depth = _spectrum = 0; _data = 0;
9614         throw CImgArgumentException(_cimg_instance
9615                                     "CImg() : Invalid construction request of a (%u,%u,%u,%u) shared instance from a (%s*) buffer "
9616                                     "(pixel types are different).",
9617                                     cimg_instance,
9618                                     size_x,size_y,size_z,size_c,CImg<t>::pixel_type());
9619       }
9620       const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c;
9621       if (values && siz) {
9622         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
9623         try { _data = new T[siz]; } catch (...) {
9624           _width = _height = _depth = _spectrum = 0; _data = 0;
9625           throw CImgInstanceException(_cimg_instance
9626                                       "CImg() : Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9627                                       cimg_instance,
9628                                       cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),size_x,size_y,size_z,size_c);
9629 
9630         }
9631         const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
9632       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
9633     }
9634 
9635     //! Construct image with specified size and initialize pixel values from a memory buffer \specialization.
9636     CImg(const T *const values, const unsigned int size_x, const unsigned int size_y=1,
9637          const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false) {
9638       const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c;
9639       if (values && siz) {
9640         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = is_shared;
9641         if (_is_shared) _data = const_cast<T*>(values);
9642         else {
9643           try { _data = new T[siz]; } catch (...) {
9644             _width = _height = _depth = _spectrum = 0; _data = 0;
9645             throw CImgInstanceException(_cimg_instance
9646                                         "CImg() : Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9647                                         cimg_instance,
9648                                         cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),size_x,size_y,size_z,size_c);
9649           }
9650           std::memcpy(_data,values,siz*sizeof(T)); }
9651       } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
9652     }
9653 
9654     //! Construct image from reading an image file.
9655     /**
9656        Construct a new image instance with pixels of type \c T, and initialize pixel values with the data read from an image file.
9657        \param filename Filename, as a C-string.
9658        \note
9659        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it reads the image
9660          dimensions and pixel values from the specified image file.
9661        - The recognition of the image file format by %CImg higly depends on the tools installed on your system
9662          and on the external libraries you used to link your code against.
9663        - Considered pixel type \c T should better fit the file format specification, or data loss may occur during file load
9664          (e.g. constructing a \c CImg<unsigned char> from a float-valued image file).
9665        - A \c CImgIOException is thrown when the specified \c filename cannot be read, or if the file format is not recognized.
9666        \par Example
9667        \code
9668        const CImg<float> img("reference.jpg");
9669        img.display();
9670        \endcode
9671        \image html ref_image.jpg
9672     **/
9673     explicit CImg(const char *const filename):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
9674       assign(filename);
9675     }
9676 
9677     //! Construct image copy.
9678     /**
9679        Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg<t> instance.
9680        \param img Input image to copy.
9681        \note
9682        - Constructed copy has the same size width() x height() x depth() x spectrum() and pixel values as the input image \c img.
9683        - If input image \c img is \e shared and if types \c T and \c t are the same, the constructed copy is also \e shared,
9684          and shares its pixel buffer with \c img.
9685          Modifying a pixel value in the constructed copy will thus also modifies it in the input image \c img.
9686          This behavior is needful to allow functions to return shared images.
9687        - Otherwise, the constructed copy allocates its own pixel buffer, and copies pixel values from the input image \c img
9688          into its buffer. The copied pixel values may be eventually statically casted if types \c T and \c t are different.
9689        - Constructing a copy from an image \c img when types \c t and \c T are the same is significantly faster than with different types.
9690        - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated (e.g. not enough available memory).
9691     **/
9692     template<typename t>
9693     CImg(const CImg<t>& img):_is_shared(false) {
9694       const unsigned long siz = img.size();
9695       if (img._data && siz) {
9696         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
9697         try { _data = new T[siz]; } catch (...) {
9698           _width = _height = _depth = _spectrum = 0; _data = 0;
9699           throw CImgInstanceException(_cimg_instance
9700                                       "CImg() : Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9701                                       cimg_instance,
9702                                       cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
9703                                       img._width,img._height,img._depth,img._spectrum);
9704         }
9705         const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
9706       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
9707     }
9708 
9709     //! Construct image copy \specialization.
9710     CImg(const CImg<T>& img) {
9711       const unsigned long siz = img.size();
9712       if (img._data && siz) {
9713         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; _is_shared = img._is_shared;
9714         if (_is_shared) _data = const_cast<T*>(img._data);
9715         else {
9716           try { _data = new T[siz]; } catch (...) {
9717             _width = _height = _depth = _spectrum = 0; _data = 0;
9718             throw CImgInstanceException(_cimg_instance
9719                                         "CImg() : Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9720                                         cimg_instance,
9721                                         cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
9722                                         img._width,img._height,img._depth,img._spectrum);
9723 
9724           }
9725           std::memcpy(_data,img._data,siz*sizeof(T));
9726         }
9727       } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
9728     }
9729 
9730     //! Advanced copy constructor.
9731     /**
9732        Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg<t> instance,
9733        while forcing the shared state of the constructed copy.
9734        \param img Input image to copy.
9735        \param is_shared Tells about the shared state of the constructed copy.
9736        \note
9737        - Similar to CImg(const CImg<t>&), except that it allows to decide the shared state of
9738          the constructed image, which does not depend anymore on the shared state of the input image \c img :
9739          - If \c is_shared is \c true, the constructed copy will share its pixel buffer with the input image \c img.
9740            For that case, the pixel types \c T and \c t \e must be the same.
9741          - If \c is_shared is \c false, the constructed copy will allocate its own pixel buffer, whether the input image \c img is
9742            shared or not.
9743        - A \c CImgArgumentException is thrown when a shared copy is requested with different pixel types \c T and \c t.
9744     **/
9745     template<typename t>
9746     CImg(const CImg<t>& img, const bool is_shared):_is_shared(false) {
9747       if (is_shared) {
9748         _width = _height = _depth = _spectrum = 0; _data = 0;
9749         throw CImgArgumentException(_cimg_instance
9750                                     "CImg() : Invalid construction request of a shared instance from a "
9751                                     "CImg<%s> image (%u,%u,%u,%u,%p) (pixel types are different).",
9752                                     cimg_instance,
9753                                     CImg<t>::pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data);
9754       }
9755       const unsigned long siz = img.size();
9756       if (img._data && siz) {
9757         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
9758         try { _data = new T[siz]; } catch (...) {
9759           _width = _height = _depth = _spectrum = 0; _data = 0;
9760           throw CImgInstanceException(_cimg_instance
9761                                       "CImg() : Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9762                                       cimg_instance,
9763                                       cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
9764                                       img._width,img._height,img._depth,img._spectrum);
9765         }
9766         const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
9767       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
9768     }
9769 
9770     //! Advanced copy constructor \specialization.
9771     CImg(const CImg<T>& img, const bool is_shared) {
9772       const unsigned long siz = img.size();
9773       if (img._data && siz) {
9774         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; _is_shared = is_shared;
9775         if (_is_shared) _data = const_cast<T*>(img._data);
9776         else {
9777           try { _data = new T[siz]; } catch (...) {
9778             _width = _height = _depth = _spectrum = 0; _data = 0;
9779             throw CImgInstanceException(_cimg_instance
9780                                         "CImg() : Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9781                                         cimg_instance,
9782                                         cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
9783                                         img._width,img._height,img._depth,img._spectrum);
9784           }
9785           std::memcpy(_data,img._data,siz*sizeof(T));
9786         }
9787       } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
9788     }
9789 
9790     //! Construct image with dimensions borrowed from another image.
9791     /**
9792        Construct a new image instance with pixels of type \c T, and size get from some dimensions of an existing \c CImg<t> instance.
9793        \param img Input image from which dimensions are borrowed.
9794        \param dimensions C-string describing the image size along the X,Y,Z and C-dimensions.
9795        \note
9796        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it takes the image dimensions
9797          (\e not its pixel values) from an existing \c CImg<t> instance.
9798        - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values.
9799          In order to initialize pixel values (e.g. with \c 0), use constructor CImg(const CImg<t>&,const char*,T) instead.
9800        \par Example
9801        \code
9802        const CImg<float> img1(256,128,1,3),      // 'img1' is a 256x128x1x3 image.
9803                          img2(img1,"xyzc"),      // 'img2' is a 256x128x1x3 image.
9804                          img3(img1,"y,x,z,c"),   // 'img3' is a 128x256x1x3 image.
9805                          img4(img1,"c,x,y,3",0), // 'img4' is a 3x128x256x3 image (with pixels initialized to '0').
9806        \endcode
9807      **/
9808     template<typename t>
9809     CImg(const CImg<t>& img, const char *const dimensions):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
9810       assign(img,dimensions);
9811     }
9812 
9813     //! Construct image with dimensions borrowed from another image and initialize pixel values.
9814     /**
9815        Construct a new image instance with pixels of type \c T, and size get from the dimensions of an existing \c CImg<t> instance,
9816        and set all pixel values to specified \c value.
9817        \param img Input image from which dimensions are borrowed.
9818        \param dimensions String describing the image size along the X,Y,Z and V-dimensions.
9819        \param value Value used for initialization.
9820        \note
9821        - Similar to CImg(const CImg<t>&,const char*), but it also fills the pixel buffer with the specified \c value.
9822      **/
9823     template<typename t>
9824     CImg(const CImg<t>& img, const char *const dimensions, const T value):
9825       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
9826       assign(img,dimensions).fill(value);
9827     }
9828 
9829     //! Construct image from a display window.
9830     /**
9831        Construct a new image instance with pixels of type \c T, as a snapshot of an existing \c CImgDisplay instance.
9832        \param disp Input display window.
9833        \note
9834        - The width() and height() of the constructed image instance are the same as the specified \c CImgDisplay.
9835        - The depth() and spectrum() of the constructed image instance are respectively set to \c 1 and \c 3 (i.e. a 2d color image).
9836        - The image pixels are read as 8-bits RGB values.
9837      **/
9838     explicit CImg(const CImgDisplay &disp):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
9839       disp.snapshot(*this);
9840     }
9841 
9842     //! Construct empty image \inplace.
9843     /**
9844        In-place version of the default constructor CImg(). It simply resets the instance to an empty image.
9845     **/
9846     CImg<T>& assign() {
9847       if (!_is_shared) delete[] _data;
9848       _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0;
9849       return *this;
9850     }
9851 
9852     //! Construct image with specified size \inplace.
9853     /**
9854        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int).
9855     **/
9856     CImg<T>& assign(const unsigned int size_x, const unsigned int size_y=1, const unsigned int size_z=1, const unsigned int size_c=1) {
9857       const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c;
9858       if (!siz) return assign();
9859       const unsigned long curr_siz = size();
9860       if (siz!=curr_siz) {
9861         if (_is_shared)
9862           throw CImgArgumentException(_cimg_instance
9863                                       "assign() : Invalid assignement request of shared instance from specified image (%u,%u,%u,%u).",
9864                                       cimg_instance,
9865                                       size_x,size_y,size_z,size_c);
9866         else {
9867           delete[] _data;
9868           try { _data = new T[siz]; } catch (...) {
9869             _width = _height = _depth = _spectrum = 0; _data = 0;
9870             throw CImgInstanceException(_cimg_instance
9871                                         "assign() : Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9872                                         cimg_instance,
9873                                         cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),size_x,size_y,size_z,size_c);
9874           }
9875         }
9876       }
9877       _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
9878       return *this;
9879     }
9880 
9881     //! Construct image with specified size and initialize pixel values \inplace.
9882     /**
9883        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,T).
9884     **/
9885     CImg<T>& assign(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, const T value) {
9886       return assign(size_x,size_y,size_z,size_c).fill(value);
9887     }
9888 
9889     //! Construct image with specified size and initialize pixel values from a sequence of integers \inplace.
9890     /**
9891        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...).
9892     **/
9893     CImg<T>& assign(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
9894                     const int value0, const int value1, ...) {
9895       assign(size_x,size_y,size_z,size_c);
9896       _CImg_stdarg(*this,value0,value1,(unsigned long)size_x*size_y*size_z*size_c,int);
9897       return *this;
9898     }
9899 
9900     //! Construct image with specified size and initialize pixel values from a sequence of doubles \inplace.
9901     /**
9902        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,double,double,...).
9903     **/
9904     CImg<T>& assign(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
9905                     const double value0, const double value1, ...) {
9906       assign(size_x,size_y,size_z,size_c);
9907       _CImg_stdarg(*this,value0,value1,(unsigned long)size_x*size_y*size_z*size_c,double);
9908       return *this;
9909     }
9910 
9911     //! Construct image with specified size and initialize pixel values from a value string \inplace.
9912     /**
9913        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,const char*,bool).
9914     **/
9915     CImg<T>& assign(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
9916                     const char *const values, const bool repeat_values) {
9917       return assign(size_x,size_y,size_z,size_c).fill(values,repeat_values);
9918     }
9919 
9920     //! Construct image with specified size and initialize pixel values from a memory buffer \inplace.
9921     /**
9922        In-place version of the constructor CImg(const t*,unsigned int,unsigned int,unsigned int,unsigned int).
9923     **/
9924     template<typename t>
9925     CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y=1,
9926                     const unsigned int size_z=1, const unsigned int size_c=1) {
9927       const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c;
9928       if (!values || !siz) return assign();
9929       assign(size_x,size_y,size_z,size_c);
9930       const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
9931       return *this;
9932     }
9933 
9934     //! Construct image with specified size and initialize pixel values from a memory buffer \specialization.
9935     CImg<T>& assign(const T *const values, const unsigned int size_x, const unsigned int size_y=1,
9936                     const unsigned int size_z=1, const unsigned int size_c=1) {
9937       const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c;
9938       if (!values || !siz) return assign();
9939       const unsigned long curr_siz = size();
9940       if (values==_data && siz==curr_siz) return assign(size_x,size_y,size_z,size_c);
9941       if (_is_shared || values+siz<_data || values>=_data+size()) {
9942         assign(size_x,size_y,size_z,size_c);
9943         if (_is_shared) std::memmove(_data,values,siz*sizeof(T));
9944         else std::memcpy(_data,values,siz*sizeof(T));
9945       } else {
9946         T *new_data = 0;
9947         try { new_data = new T[siz]; } catch (...) {
9948           _width = _height = _depth = _spectrum = 0; _data = 0;
9949           throw CImgInstanceException(_cimg_instance
9950                                       "assign() : Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9951                                       cimg_instance,
9952                                       cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),size_x,size_y,size_z,size_c);
9953         }
9954         std::memcpy(new_data,values,siz*sizeof(T));
9955         delete[] _data; _data = new_data; _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
9956       }
9957       return *this;
9958     }
9959 
9960     //! Construct image with specified size and initialize pixel values from a memory buffer \overloading.
9961     template<typename t>
9962     CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y,
9963                     const unsigned int size_z, const unsigned int size_c, const bool is_shared) {
9964       if (is_shared)
9965         throw CImgArgumentException(_cimg_instance
9966                                     "assign() : Invalid assignment request of shared instance from (%s*) buffer"
9967                                     "(pixel types are different).",
9968                                     cimg_instance,
9969                                     CImg<t>::pixel_type());
9970       return assign(values,size_x,size_y,size_z,size_c);
9971     }
9972 
9973     //! Construct image with specified size and initialize pixel values from a memory buffer \overloading.
9974     CImg<T>& assign(const T *const values, const unsigned int size_x, const unsigned int size_y,
9975                     const unsigned int size_z, const unsigned int size_c, const bool is_shared) {
9976       const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c;
9977       if (!values || !siz) {
9978         if (is_shared)
9979           throw CImgArgumentException(_cimg_instance
9980                                       "assign() : Invalid assignment request of shared instance from (null) or empty buffer.",
9981                                       cimg_instance);
9982         else return assign();
9983       }
9984       if (!is_shared) { if (_is_shared) assign(); assign(values,size_x,size_y,size_z,size_c); }
9985       else {
9986         if (!_is_shared) {
9987           if (values+siz<_data || values>=_data+size()) assign();
9988           else cimg::warn(_cimg_instance
9989                           "assign() : Shared image instance has overlapping memory.",
9990                           cimg_instance);
9991         }
9992         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = true;
9993         _data = const_cast<T*>(values);
9994       }
9995       return *this;
9996     }
9997 
9998     //! Construct image from reading an image file \inplace.
9999     /**
10000        In-place version of the constructor CImg(const char*).
10001     **/
10002     CImg<T>& assign(const char *const filename) {
10003       return load(filename);
10004     }
10005 
10006     //! Construct image copy \inplace.
10007     /**
10008        In-place version of the constructor CImg(const CImg<t>&).
10009     **/
10010     template<typename t>
10011     CImg<T>& assign(const CImg<t>& img) {
10012       return assign(img._data,img._width,img._height,img._depth,img._spectrum);
10013     }
10014 
10015     //! In-place version of the advanced copy constructor.
10016     /**
10017        In-place version of the constructor CImg(const CImg<t>&,bool).
10018      **/
10019     template<typename t>
10020     CImg<T>& assign(const CImg<t>& img, const bool is_shared) {
10021       return assign(img._data,img._width,img._height,img._depth,img._spectrum,is_shared);
10022     }
10023 
10024     //! Construct image with dimensions borrowed from another image \inplace.
10025     /**
10026        In-place version of the constructor CImg(const CImg<t>&,const char*).
10027     **/
10028     template<typename t>
10029     CImg<T>& assign(const CImg<t>& img, const char *const dimensions) {
10030       if (!dimensions || !*dimensions) return assign(img._width,img._height,img._depth,img._spectrum);
10031       unsigned int siz[4] = { 0,1,1,1 }, k = 0;
10032       for (const char *s = dimensions; *s && k<4; ++k) {
10033         char item[256] = { 0 };
10034         if (std::sscanf(s,"%255[^0-9%xyzvwhdcXYZVWHDC]",item)>0) s+=std::strlen(item);
10035         if (*s) {
10036           unsigned int val = 0; char sep = 0;
10037           if (std::sscanf(s,"%u%c",&val,&sep)>0) {
10038             if (sep=='%') siz[k] = val*(k==0?_width:k==1?_height:k==2?_depth:_spectrum)/100;
10039             else siz[k] = val;
10040             while (*s>='0' && *s<='9') ++s; if (sep=='%') ++s;
10041           } else switch (cimg::uncase(*s)) {
10042           case 'x' : case 'w' : siz[k] = img._width; ++s; break;
10043           case 'y' : case 'h' : siz[k] = img._height; ++s; break;
10044           case 'z' : case 'd' : siz[k] = img._depth; ++s; break;
10045           case 'c' : case 's' : siz[k] = img._spectrum; ++s; break;
10046           default :
10047             throw CImgArgumentException(_cimg_instance
10048                                         "assign() : Invalid character '%c' detected in specified dimension string '%s'.",
10049                                         cimg_instance,
10050                                         *s,dimensions);
10051           }
10052         }
10053       }
10054       return assign(siz[0],siz[1],siz[2],siz[3]);
10055     }
10056 
10057     //! Construct image with dimensions borrowed from another image and initialize pixel values \inplace.
10058     /**
10059        In-place version of the constructor CImg(const CImg<t>&,const char*,T).
10060     **/
10061     template<typename t>
10062     CImg<T>& assign(const CImg<t>& img, const char *const dimensions, const T value) {
10063       return assign(img,dimensions).fill(value);
10064     }
10065 
10066     //! Construct image from a display window \inplace.
10067     /**
10068        In-place version of the constructor CImg(const CImgDisplay&).
10069     **/
10070     CImg<T>& assign(const CImgDisplay &disp) {
10071       disp.snapshot(*this);
10072       return *this;
10073     }
10074 
10075     //! Construct empty image \inplace.
10076     /**
10077        Equivalent to assign().
10078        \note
10079        - It has been defined for compatibility with STL naming conventions.
10080     **/
10081     CImg<T>& clear() {
10082       return assign();
10083     }
10084 
10085     //! Transfer content of an image instance into another one.
10086     /**
10087        Transfer the dimensions and the pixel buffer content of an image instance into another one,
10088        and replace instance by an empty image. It avoids the copy of the pixel buffer
10089        when possible.
10090        \param img Destination image.
10091        \note
10092        - Pixel types \c T and \c t of source and destination images can be different, though the process is designed to be
10093          instantaneous when \c T and \c t are the same.
10094        \par Example
10095        \code
10096        CImg<float> src(256,256,1,3,0), // Construct a 256x256x1x3 (color) image filled with value '0'.
10097                    dest(16,16);        // Construct a 16x16x1x1 (scalar) image.
10098        src.move_to(dest);              // Now, 'src' is empty and 'dest' is the 256x256x1x3 image.
10099        \endcode
10100     **/
10101     template<typename t>
10102     CImg<t>& move_to(CImg<t>& img) {
10103       img.assign(*this);
10104       assign();
10105       return img;
10106     }
10107 
10108     //! Transfer content of an image instance into another one \specialization.
10109     CImg<T>& move_to(CImg<T>& img) {
10110       if (_is_shared || img._is_shared) img.assign(*this);
10111       else swap(img);
10112       assign();
10113       return img;
10114     }
10115 
10116     //! Transfer content of an image instance into a new image in an image list.
10117     /**
10118        Transfer the dimensions and the pixel buffer content of an image instance
10119        into a newly inserted image at position \c pos in specified \c CImgList<t> instance.
10120        \param list Destination list.
10121        \param pos Position of the newly inserted image in the list.
10122        \note
10123        - When optionnal parameter \c pos is ommited, the image instance is transfered as a new
10124          image at the end of the specified \c list.
10125        - It is convenient to sequentially insert new images into image lists, with no
10126          additional copies of memory buffer.
10127        \par Example
10128        \code
10129        CImgList<float> list;             // Construct an empty image list.
10130        CImg<float> img("reference.jpg"); // Read image from filename.
10131        img.move_to(list);                // Transfer image content as a new item in the list (no buffer copy).
10132        \endcode
10133     **/
10134     template<typename t>
10135     CImgList<t>& move_to(CImgList<t>& list, const unsigned int pos=~0U) {
10136       const unsigned int npos = pos>list._width?list._width:pos;
10137       move_to(list.insert(1,npos)[npos]);
10138       return list;
10139     }
10140 
10141     //! Swap fields of two image instances.
10142     /**
10143       \param img Image to swap fields with.
10144       \note
10145       - It can be used to interchange the content of two images in a very fast way. Can be convenient when dealing
10146         with algorithms requiring two swapping buffers.
10147       \par Example
10148       \code
10149       CImg<float> img1("lena.jpg"),
10150                   img2("milla.jpg");
10151       img1.swap(img2);               // Now, 'img1' is 'milla' and 'img2' is 'lena'.
10152       \endcode
10153     **/
10154     CImg<T>& swap(CImg<T>& img) {
10155       cimg::swap(_width,img._width);
10156       cimg::swap(_height,img._height);
10157       cimg::swap(_depth,img._depth);
10158       cimg::swap(_spectrum,img._spectrum);
10159       cimg::swap(_data,img._data);
10160       cimg::swap(_is_shared,img._is_shared);
10161       return img;
10162     }
10163 
10164     //! Return a reference to an empty image.
10165     /**
10166        \note
10167        This function is useful mainly to declare optional parameters having type \c CImg<T> in functions prototypes, e.g.
10168        \code
10169        void f(const int x=0, const int y=0, const CImg<float>& img=CImg<float>::empty());
10170        \endcode
10171      **/
10172     static CImg<T>& empty() {
10173       static CImg<T> _empty;
10174       return _empty.assign();
10175     }
10176 
10177     //@}
10178     //------------------------------------------
10179     //
10180     //! \name Overloaded Operators
10181     //@{
10182     //------------------------------------------
10183 
10184     //! Access to a pixel value.
10185     /**
10186        Return a reference to a located pixel value of the image instance,
10187        being possibly \e const, whether the image instance is \e const or not.
10188        This is the standard method to get/set pixel values in \c CImg<T> images.
10189        \param x X-coordinate of the pixel value.
10190        \param y Y-coordinate of the pixel value.
10191        \param z Z-coordinate of the pixel value.
10192        \param c C-coordinate of the pixel value.
10193        \note
10194        - Range of pixel coordinates start from <tt>(0,0,0,0)</tt> to <tt>(width()-1,height()-1,depth()-1,spectrum()-1)</tt>.
10195        - Due to the particular arrangement of the pixel buffers defined in %CImg, you can omit one coordinate if the corresponding dimension
10196          is equal to \c 1.
10197          For instance, pixels of a 2d image (depth() equal to \c 1) can be accessed by <tt>img(x,y,c)</tt> instead of <tt>img(x,y,0,c)</tt>.
10198        \warning
10199        - There is \e no boundary checking done in this operator, to make it as fast as possible.
10200          You \e must take care of out-of-bounds access by yourself, if necessary.
10201          For debuging purposes, you may want to define macro \c 'cimg_verbosity'>=3 to enable additional boundary checking operations
10202          in this operator. In that case, warning messages will be printed on the error output when accessing out-of-bounds pixels.
10203        \par Example
10204        \code
10205        CImg<float> img(100,100,1,3,0);                   // Construct a 100x100x1x3 (color) image with pixels set to '0'.
10206        const float
10207           valR = img(10,10,0,0),                         // Read red value at coordinates (10,10).
10208           valG = img(10,10,0,1),                         // Read green value at coordinates (10,10)
10209           valB = img(10,10,2),                           // Read blue value at coordinates (10,10) (Z-coordinate can be omitted).
10210           avg = (valR + valG + valB)/3;                  // Compute average pixel value.
10211        img(10,10,0) = img(10,10,1) = img(10,10,2) = avg; // Replace the color pixel (10,10) by the average grey value.
10212        \endcode
10213     **/
10214 #if cimg_verbosity>=3
10215     T& operator()(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
10216       const unsigned long off = (unsigned long)offset(x,y,z,c);
10217       if (!_data || off>=size()) {
10218         cimg::warn(_cimg_instance
10219                    "operator() : Invalid pixel request, at coordinates (%u,%u,%u,%u) [offset=%u].",
10220                    cimg_instance,
10221                    x,y,z,c,off);
10222         return *_data;
10223       }
10224       else return _data[off];
10225     }
10226 
10227     //! Access to a pixel value \const.
10228     const T& operator()(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
10229       return const_cast<CImg<T>*>(this)->operator()(x,y,z,c);
10230     }
10231 
10232     //! Access to a pixel value.
10233     /**
10234        \param x X-coordinate of the pixel value.
10235        \param y Y-coordinate of the pixel value.
10236        \param z Z-coordinate of the pixel value.
10237        \param c C-coordinate of the pixel value.
10238        \param wh Precomputed offset, must be equal to <tt>width()*\ref height()</tt>.
10239        \param whd Precomputed offset, must be equal to <tt>width()*\ref height()*\ref depth()</tt>.
10240        \note
10241        - Similar to (but faster than) operator()().
10242          It uses precomputed offsets to optimize memory access. You may use it to optimize
10243          the reading/writing of several pixel values in the same image (e.g. in a loop).
10244      **/
10245     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
10246                   const unsigned long wh, const unsigned long whd=0) {
10247       cimg::unused(wh,whd);
10248       return (*this)(x,y,z,c);
10249     }
10250 
10251     //! Access to a pixel value \const.
10252     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
10253                         const unsigned long wh, const unsigned long whd=0) const {
10254       cimg::unused(wh,whd);
10255       return (*this)(x,y,z,c);
10256     }
10257 #else
10258     T& operator()(const unsigned int x) {
10259       return _data[x];
10260     }
10261 
10262     const T& operator()(const unsigned int x) const {
10263       return _data[x];
10264     }
10265 
10266     T& operator()(const unsigned int x, const unsigned int y) {
10267       return _data[x + y*_width];
10268     }
10269 
10270     const T& operator()(const unsigned int x, const unsigned int y) const {
10271       return _data[x + y*_width];
10272     }
10273 
10274     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) {
10275       return _data[x + y*(unsigned long)_width + z*(unsigned long)_width*_height];
10276    }
10277 
10278     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) const {
10279       return _data[x + y*(unsigned long)_width + z*(unsigned long)_width*_height];
10280     }
10281 
10282     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) {
10283       return _data[x + y*(unsigned long)_width + z*(unsigned long)_width*_height + c*(unsigned long)_width*_height*_depth];
10284     }
10285 
10286     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) const {
10287       return _data[x + y*(unsigned long)_width + z*(unsigned long)_width*_height + c*(unsigned long)_width*_height*_depth];
10288     }
10289 
10290     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int,
10291                   const unsigned long wh) {
10292       return _data[x + y*_width + z*wh];
10293     }
10294 
10295     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int,
10296                         const unsigned long wh) const {
10297       return _data[x + y*_width + z*wh];
10298     }
10299 
10300     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
10301                   const unsigned long wh, const unsigned long whd) {
10302       return _data[x + y*_width + z*wh + c*whd];
10303     }
10304 
10305     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
10306                         const unsigned long wh, const unsigned long whd) const {
10307       return _data[x + y*_width + z*wh + c*whd];
10308     }
10309 #endif
10310 
10311     //! Implicitely cast an image into a \c T*.
10312     /**
10313        Implicitely cast a \c CImg<T> instance into a \c T* or \c const \c T* pointer, whether the image instance
10314        is \e const or not. The returned pointer points on the first value of the image pixel buffer.
10315        \note
10316        - It simply returns the pointer data() to the pixel buffer.
10317        - This implicit conversion is convenient to test the empty state of images (data() being \c 0 in this case), e.g.
10318        \code
10319        CImg<float> img1(100,100), img2; // 'img1' is a 100x100 image, 'img2' is an empty image.
10320        if (img1) {                      // Test succeeds, 'img1' is not an empty image.
10321          if (!img2) {                   // Test succeeds, 'img2' is an empty image.
10322            std::printf("'img1' is not empty, 'img2' is empty.");
10323          }
10324        }
10325        \endcode
10326        - It also allows to use brackets to access pixel values, without need for a \c CImg<T>::operator[](), e.g.
10327        \code
10328        CImg<float> img(100,100);
10329        const float value = img[99]; // Access to value of the last pixel on the first row.
10330        img[510] = 255;              // Set pixel value at (10,5).
10331        \endcode
10332     **/
10333     operator T*() {
10334       return _data;
10335     }
10336 
10337     //! Implicitely cast an image into a \c T* \const.
10338     operator const T*() const {
10339       return _data;
10340     }
10341 
10342     //! Assign a value to all image pixels.
10343     /**
10344        Assign specified \c value to each pixel value of the image instance.
10345        \param value Value that will be assigned to image pixels.
10346        \note
10347        - The image size is never modified.
10348        - The \c value may be casted to pixel type \c T if necessary.
10349        \par Example
10350        \code
10351        CImg<char> img(100,100); // Declare image (with garbage values).
10352        img = 0;                 // Set all pixel values to '0'.
10353        img = 1.2;               // Set all pixel values to '1' (cast of '1.2' as a 'char').
10354        \endcode
10355     **/
10356     CImg<T>& operator=(const T value) {
10357       return fill(value);
10358     }
10359 
10360     //! Assign pixels values from a specified expression.
10361     /**
10362        Initialize all pixel values from the specified string \c expression.
10363        \param expression Value string describing the way pixel values are set.
10364        \note
10365        - String parameter \c expression may describe different things :
10366          - 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"),
10367            the pixel values are set from specified \c expression and the image size is not modified.
10368          - If \c expression is a filename (as in \c "reference.jpg"), the corresponding image file is loaded and replace the image instance.
10369            The image size is modified if necessary.
10370        \par Example
10371        \code
10372        CImg<float> img1(100,100), img2(img1), img3(img1); // Declare three 100x100 scalar images with unitialized pixel values.
10373        img1 = "0,50,100,150,200,250,200,150,100,50";      // Set pixel values of 'img1' from a value sequence.
10374        img2 = "10*((x*y)%25)";                            // Set pixel values of 'img2' from a formula.
10375        img3 = "reference.jpg";                            // Set pixel values of 'img3' from a file (image size is modified).
10376        (img1,img2,img3).display();
10377        \endcode
10378        \image html ref_operator_eq.jpg
10379     **/
10380     CImg<T>& operator=(const char *const expression) {
10381       const unsigned int omode = cimg::exception_mode();
10382       cimg::exception_mode() = 0;
10383       try {
10384         fill(expression,true);
10385       } catch (CImgException&) {
10386         cimg::exception_mode() = omode;
10387         load(expression);
10388       }
10389       cimg::exception_mode() = omode;
10390       return *this;
10391     }
10392 
10393     //! Copy an image into the current image instance.
10394     /**
10395        Similar to the in-place copy constructor assign(const CImg<t>&).
10396     **/
10397     template<typename t>
10398     CImg<T>& operator=(const CImg<t>& img) {
10399       return assign(img);
10400     }
10401 
10402     //! Copy an image into the current image instance \specialization.
10403     CImg<T>& operator=(const CImg<T>& img) {
10404       return assign(img);
10405     }
10406 
10407     //! Copy the content of a display window to the current image instance.
10408     /**
10409        Similar to assign(const CImgDisplay&).
10410     **/
10411     CImg<T>& operator=(const CImgDisplay& disp) {
10412       disp.snapshot(*this);
10413       return *this;
10414     }
10415 
10416     //! In-place addition operator.
10417     /**
10418        Add specified \c value to all pixels of an image instance.
10419        \param value Value to add.
10420        \note
10421        - Resulting pixel values are casted to fit the pixel type \c T. For instance, adding \c 0.2 to a \c CImg<char> is possible but does nothing indeed.
10422        - Overflow values are treated as with standard C++ numeric types. For instance,
10423        \code
10424        CImg<unsigned char> img(100,100,1,1,255); // Construct a 100x100 image with pixel values '255'.
10425        img+=1;                                   // Add '1' to each pixels -> Overflow.
10426        // here all pixels of image 'img' are equal to '0'.
10427        \endcode
10428        - To prevent value overflow, you may want to consider pixel type \c T as \c float or \c double, and use cut() after addition.
10429        \par Example
10430        \code
10431        CImg<unsigned char> img1("reference.jpg");          // Load a 8-bits RGB image (values in [0,255]).
10432        CImg<float> img2(img1);                             // Construct a float-valued copy of 'img1'.
10433        img2+=100;                                          // Add '100' to pixel values -> goes out of [0,255] but no problems with floats.
10434        img2.cut(0,255);                                    // Cut values in [0,255] to fit the 'unsigned char' constraint.
10435        img1 = img2;                                        // Rewrite safe result in 'unsigned char' version 'img1'.
10436        const CImg<unsigned char> img3 = (img1 + 100).cut(0,255); // Do the same in a more simple and elegant way.
10437        (img1,img2,img3).display();
10438        \endcode
10439        \image html ref_operator_plus.jpg
10440      **/
10441     template<typename t>
10442     CImg<T>& operator+=(const t value) {
10443       cimg_for(*this,ptrd,T) *ptrd = (T)(*ptrd + value);
10444       return *this;
10445     }
10446 
10447     //! In-place addition operator.
10448     /**
10449        Add values to image pixels, according to the specified string \c expression.
10450        \param expression Value string describing the way pixel values are added.
10451        \note
10452        - Similar to operator=(const char*), except that it adds values to the pixels of the current image instance,
10453          instead of assigning them.
10454     **/
10455     CImg<T>& operator+=(const char *const expression) {
10456       const unsigned int omode = cimg::exception_mode();
10457       cimg::exception_mode() = 0;
10458       try {
10459         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
10460         _cimg_math_parser mp(base,expression,"operator+=");
10461         T *ptrd = _data;
10462         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd + mp.eval(x,y,z,c)); ++ptrd; }
10463       } catch (CImgException&) {
10464         cimg::exception_mode() = omode;
10465         *this+=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
10466       }
10467       cimg::exception_mode() = omode;
10468       return *this;
10469     }
10470 
10471     //! In-place addition operator.
10472     /**
10473        Add values to image pixels, according to the values of the input image \c img.
10474        \param img Input image to add.
10475        \note
10476        - The size of the image instance is never modified.
10477        - It is not mandatory that input image \c img has the same size as the image instance. If less values are available
10478          in \c img, then the values are added cyclically. For instance, adding one WxH scalar image (spectrum() equal to \c 1) to
10479          one WxH color image (spectrum() equal to \c 3) means each color channel will be incremented with the same values at the same
10480          locations.
10481        \par Example
10482        \code
10483        CImg<float> img1("reference.jpg");                                   // Load a RGB color image (img1.spectrum()==3)
10484        const CImg<float> img2(img1.width(),img.height(),1,1,"255*(x/w)^2"); // Construct a scalar shading (img2.spectrum()==1).
10485        img1+=img2;                                                          // Add shading to each channel of 'img1'.
10486        img1.cut(0,255);                                                     // Prevent [0,255] overflow.
10487        (img2,img1).display();
10488        \endcode
10489        \image html ref_operator_plus1.jpg
10490     **/
10491     template<typename t>
10492     CImg<T>& operator+=(const CImg<t>& img) {
10493       const unsigned long siz = size(), isiz = img.size();
10494       if (siz && isiz) {
10495         if (is_overlapped(img)) return *this+=+img;
10496         T *ptrd = _data, *const ptre = _data + siz;
10497         if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
10498           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)(*ptrd + *(ptrs++));
10499         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd + *(ptrs++));
10500       }
10501       return *this;
10502     }
10503 
10504     //! In-place increment operator (prefix).
10505     /**
10506        Add \c 1 to all image pixels, and return a reference to the current incremented image instance.
10507        \note
10508        - Writing \c ++img is equivalent to \c img+=1.
10509      **/
10510     CImg<T>& operator++() {
10511       cimg_for(*this,ptrd,T) ++*ptrd;
10512       return *this;
10513     }
10514 
10515     //! In-place increment operator (postfix).
10516     /**
10517        Add \c 1 to all image pixels, and return a new copy of the initial (pre-incremented) image instance.
10518        \note
10519        - Use the prefixed version operator++() if you don't need a copy of the initial (pre-incremented) image instance, since
10520          a useless image copy may be expensive in terms of memory usage.
10521      **/
10522     CImg<T> operator++(int) {
10523       const CImg<T> copy(*this,false);
10524       ++*this;
10525       return copy;
10526     }
10527 
10528     //! Return a non-shared copy of the image instance.
10529     /**
10530        \note
10531        - Use this operator to ensure you get a non-shared copy of an image instance with same pixel type \c T.
10532          Indeed, the usual copy constructor CImg<T>(const CImg<T>&) returns a shared copy of a shared input image, and it may be
10533          not desirable to work on a regular copy (e.g. for a resize operation) if you have no informations about the shared state
10534          of the input image.
10535        - Writing \c (+img) is equivalent to \c CImg<T>(img,false).
10536     **/
10537     CImg<T> operator+() const {
10538       return CImg<T>(*this,false);
10539     }
10540 
10541     //! Addition operator.
10542     /**
10543        Similar to operator+=(const t), except that it returns a new image instance instead of operating in-place.
10544        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10545      **/
10546     template<typename t>
10547     CImg<_cimg_Tt> operator+(const t value) const {
10548       return CImg<_cimg_Tt>(*this,false)+=value;
10549     }
10550 
10551     //! Addition operator.
10552     /**
10553        Similar to operator+=(const char*), except that it returns a new image instance instead of operating in-place.
10554        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10555      **/
10556     CImg<Tfloat> operator+(const char *const expression) const {
10557       return CImg<Tfloat>(*this,false)+=expression;
10558     }
10559 
10560     //! Addition operator.
10561     /**
10562        Similar to operator+=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
10563        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10564      **/
10565     template<typename t>
10566     CImg<_cimg_Tt> operator+(const CImg<t>& img) const {
10567       return CImg<_cimg_Tt>(*this,false)+=img;
10568     }
10569 
10570     //! In-place substraction operator.
10571     /**
10572        Similar to operator+=(const t), except that it performs a substraction instead of an addition.
10573      **/
10574     template<typename t>
10575     CImg<T>& operator-=(const t value) {
10576       cimg_for(*this,ptrd,T) *ptrd = (T)(*ptrd - value);
10577       return *this;
10578     }
10579 
10580     //! In-place substraction operator.
10581     /**
10582        Similar to operator+=(const char*), except that it performs a substraction instead of an addition.
10583      **/
10584     CImg<T>& operator-=(const char *const expression) {
10585       const unsigned int omode = cimg::exception_mode();
10586       cimg::exception_mode() = 0;
10587       try {
10588         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
10589         _cimg_math_parser mp(base,expression,"operator-=");
10590         T *ptrd = _data;
10591         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd - mp.eval(x,y,z,c)); ++ptrd; }
10592       } catch (CImgException&) {
10593         cimg::exception_mode() = omode;
10594         *this-=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
10595       }
10596       cimg::exception_mode() = omode;
10597       return *this;
10598     }
10599 
10600     //! In-place substraction operator.
10601     /**
10602        Similar to operator+=(const CImg<t>&), except that it performs a substraction instead of an addition.
10603      **/
10604     template<typename t>
10605     CImg<T>& operator-=(const CImg<t>& img) {
10606       const unsigned long siz = size(), isiz = img.size();
10607       if (siz && isiz) {
10608         if (is_overlapped(img)) return *this-=+img;
10609         T *ptrd = _data, *const ptre = _data + siz;
10610         if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
10611           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)(*ptrd - *(ptrs++));
10612         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd - *(ptrs++));
10613       }
10614       return *this;
10615     }
10616 
10617     //! In-place decrement operator (prefix).
10618     /**
10619        Similar to operator++(), except that it performs a decrement instead of an increment.
10620     **/
10621     CImg<T>& operator--() {
10622       cimg_for(*this,ptrd,T) *ptrd = *ptrd-(T)1;
10623       return *this;
10624     }
10625 
10626     //! In-place decrement operator (postfix).
10627     /**
10628        Similar to operator++(int), except that it performs a decrement instead of an increment.
10629     **/
10630     CImg<T> operator--(int) {
10631       const CImg<T> copy(*this,false);
10632       --*this;
10633       return copy;
10634     }
10635 
10636     //! Replace each pixel by its opposite value.
10637     /**
10638        \note
10639        - If the computed opposite values are out-of-range, they are treated as with standard C++ numeric types. For instance,
10640          the \c unsigned \c char opposite of \c 1 is \c 255.
10641        \par Example
10642        \code
10643        const CImg<unsigned char>
10644          img1("reference.jpg"),   // Load a RGB color image.
10645          img2 = -img1;            // Compute its opposite (in 'unsigned char').
10646        (img1,img2).display();
10647        \endcode
10648        \image html ref_operator_minus.jpg
10649      **/
10650     CImg<T> operator-() const {
10651       return CImg<T>(_width,_height,_depth,_spectrum,(T)0)-=*this;
10652     }
10653 
10654     //! Substraction operator.
10655     /**
10656        Similar to operator-=(const t), except that it returns a new image instance instead of operating in-place.
10657        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10658     **/
10659     template<typename t>
10660     CImg<_cimg_Tt> operator-(const t value) const {
10661       return CImg<_cimg_Tt>(*this,false)-=value;
10662     }
10663 
10664     //! Substraction operator.
10665     /**
10666        Similar to operator-=(const char*), except that it returns a new image instance instead of operating in-place.
10667        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10668     **/
10669     CImg<Tfloat> operator-(const char *const expression) const {
10670       return CImg<Tfloat>(*this,false)-=expression;
10671     }
10672 
10673     //! Substraction operator.
10674     /**
10675        Similar to operator-=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
10676        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10677     **/
10678     template<typename t>
10679     CImg<_cimg_Tt> operator-(const CImg<t>& img) const {
10680       return CImg<_cimg_Tt>(*this,false)-=img;
10681     }
10682 
10683     //! In-place multiplication operator.
10684     /**
10685        Similar to operator+=(const t), except that it performs a multiplication instead of an addition.
10686      **/
10687     template<typename t>
10688     CImg<T>& operator*=(const t value) {
10689       cimg_for(*this,ptrd,T) *ptrd = (T)(*ptrd * value);
10690       return *this;
10691     }
10692 
10693     //! In-place multiplication operator.
10694     /**
10695        Similar to operator+=(const char*), except that it performs a multiplication instead of an addition.
10696      **/
10697     CImg<T>& operator*=(const char *const expression) {
10698       const unsigned int omode = cimg::exception_mode();
10699       cimg::exception_mode() = 0;
10700       try {
10701         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
10702         _cimg_math_parser mp(base,expression,"operator*=");
10703         T *ptrd = _data;
10704         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd * mp.eval(x,y,z,c)); ++ptrd; }
10705       } catch (CImgException&) {
10706         cimg::exception_mode() = omode;
10707         mul(CImg<T>(_width,_height,_depth,_spectrum,expression,true));
10708       }
10709       cimg::exception_mode() = omode;
10710       return *this;
10711     }
10712 
10713     //! In-place multiplication operator.
10714     /**
10715        Replace the image instance by the matrix multiplication between the image instance and the specified matrix \c img.
10716        \param img Second operand of the matrix multiplication.
10717        \note
10718        - It does \e not compute a pointwise multiplication between two images. For this purpose, use mul(const CImg<t>&) instead.
10719        - The size of the image instance can be modified by this operator.
10720        \par Example
10721        \code
10722        CImg<float> A(2,2,1,1, 1,2,3,4);   // Construct 2x2 matrix A = [1,2;3,4].
10723        const CImg<float> X(1,2,1,1, 1,2); // Construct 1x2 vector X = [1;2].
10724        A*=X;                              // Assign matrix multiplication A*X to 'A'.
10725        // 'A' is now a 1x2 vector whose values are [5;11].
10726        \endcode
10727     **/
10728     template<typename t>
10729     CImg<T>& operator*=(const CImg<t>& img) {
10730       return ((*this)*img).move_to(*this);
10731     }
10732 
10733     //! Multiplication operator.
10734     /**
10735        Similar to operator*=(const t), except that it returns a new image instance instead of operating in-place.
10736        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10737     **/
10738     template<typename t>
10739     CImg<_cimg_Tt> operator*(const t value) const {
10740       return CImg<_cimg_Tt>(*this,false)*=value;
10741     }
10742 
10743     //! Multiplication operator.
10744     /**
10745        Similar to operator*=(const char*), except that it returns a new image instance instead of operating in-place.
10746        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10747     **/
10748     CImg<Tfloat> operator*(const char *const expression) const {
10749       return CImg<Tfloat>(*this,false)*=expression;
10750     }
10751 
10752     //! Multiplication operator.
10753     /**
10754        Similar to operator*=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
10755        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10756     **/
10757     template<typename t>
10758     CImg<_cimg_Tt> operator*(const CImg<t>& img) const {
10759       if (_width!=img._height || _depth!=1 || _spectrum!=1)
10760         throw CImgArgumentException(_cimg_instance
10761                                     "operator*() : Invalid multiplication of instance by specified matrix (%u,%u,%u,%u,%p)",
10762                                     cimg_instance,
10763                                     img._width,img._height,img._depth,img._spectrum,img._data);
10764 
10765       CImg<_cimg_Tt> res(img._width,_height);
10766       _cimg_Tt *ptrd = res._data;
10767       _cimg_Ttdouble value;
10768 #ifdef cimg_use_openmp
10769 #pragma omp parallel for if (size()>=1000 && img.size()>=1000) private(value)
10770 #endif
10771       cimg_forXY(res,i,j) { value = 0; cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k); *(ptrd++) = (_cimg_Tt)value; }
10772       return res;
10773     }
10774 
10775     //! In-place division operator.
10776     /**
10777        Similar to operator+=(const t), except that it performs a division instead of an addition.
10778      **/
10779     template<typename t>
10780     CImg<T>& operator/=(const t value) {
10781       cimg_for(*this,ptrd,T) *ptrd = (T)(*ptrd / value);
10782       return *this;
10783     }
10784 
10785     //! In-place division operator.
10786     /**
10787        Similar to operator+=(const char*), except that it performs a division instead of an addition.
10788      **/
10789     CImg<T>& operator/=(const char *const expression) {
10790       const unsigned int omode = cimg::exception_mode();
10791       cimg::exception_mode() = 0;
10792       try {
10793         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
10794         _cimg_math_parser mp(base,expression,"operator/=");
10795         T *ptrd = _data;
10796         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd / mp.eval(x,y,z,c)); ++ptrd; }
10797       } catch (CImgException&) {
10798         cimg::exception_mode() = omode;
10799         div(CImg<T>(_width,_height,_depth,_spectrum,expression,true));
10800       }
10801       cimg::exception_mode() = omode;
10802       return *this;
10803     }
10804 
10805     //! In-place division operator.
10806     /**
10807        Replace the image instance by the (right) matrix division between the image instance and the specified matrix \c img.
10808        \param img Second operand of the matrix division.
10809        \note
10810        - It does \e not compute a pointwise division between two images. For this purpose, use div(const CImg<t>&) instead.
10811        - It returns the matrix operation \c A*inverse(img).
10812        - The size of the image instance can be modified by this operator.
10813      **/
10814     template<typename t>
10815     CImg<T>& operator/=(const CImg<t>& img) {
10816       return (*this*img.get_invert()).move_to(*this);
10817     }
10818 
10819     //! Division operator.
10820     /**
10821        Similar to operator/=(const t), except that it returns a new image instance instead of operating in-place.
10822        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10823     **/
10824     template<typename t>
10825     CImg<_cimg_Tt> operator/(const t value) const {
10826       return CImg<_cimg_Tt>(*this,false)/=value;
10827     }
10828 
10829     //! Division operator.
10830     /**
10831        Similar to operator/=(const char*), except that it returns a new image instance instead of operating in-place.
10832        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10833     **/
10834     CImg<Tfloat> operator/(const char *const expression) const {
10835       return CImg<Tfloat>(*this,false)/=expression;
10836     }
10837 
10838     //! Division operator.
10839     /**
10840        Similar to operator/=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
10841        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10842     **/
10843     template<typename t>
10844     CImg<_cimg_Tt> operator/(const CImg<t>& img) const {
10845       return (*this)*img.get_invert();
10846     }
10847 
10848     //! In-place modulo operator.
10849     /**
10850        Similar to operator+=(const t), except that it performs a modulo operation instead of an addition.
10851     **/
10852     template<typename t>
10853     CImg<T>& operator%=(const t value) {
10854       cimg_for(*this,ptrd,T) *ptrd = (T)cimg::mod(*ptrd,(T)value);
10855       return *this;
10856     }
10857 
10858     //! In-place modulo operator.
10859     /**
10860        Similar to operator+=(const char*), except that it performs a modulo operation instead of an addition.
10861     **/
10862     CImg<T>& operator%=(const char *const expression) {
10863       const unsigned int omode = cimg::exception_mode();
10864       cimg::exception_mode() = 0;
10865       try {
10866         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
10867         _cimg_math_parser mp(base,expression,"operator%=");
10868         T *ptrd = _data;
10869         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::mod(*ptrd,(T)mp.eval(x,y,z,c)); ++ptrd; }
10870       } catch (CImgException&) {
10871         cimg::exception_mode() = omode;
10872         *this%=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
10873       }
10874       cimg::exception_mode() = omode;
10875       return *this;
10876     }
10877 
10878     //! In-place modulo operator.
10879     /**
10880        Similar to operator+=(const CImg<t>&), except that it performs a modulo operation instead of an addition.
10881     **/
10882     template<typename t>
10883     CImg<T>& operator%=(const CImg<t>& img) {
10884       const unsigned long siz = size(), isiz = img.size();
10885       if (siz && isiz) {
10886         if (is_overlapped(img)) return *this%=+img;
10887         T *ptrd = _data, *const ptre = _data + siz;
10888         if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
10889           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = cimg::mod(*ptrd,(T)*(ptrs++));
10890         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::mod(*ptrd,(T)*(ptrs++));
10891       }
10892       return *this;
10893     }
10894 
10895     //! Modulo operator.
10896     /**
10897        Similar to operator%=(const t), except that it returns a new image instance instead of operating in-place.
10898        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10899     **/
10900     template<typename t>
10901     CImg<_cimg_Tt> operator%(const t value) const {
10902       return CImg<_cimg_Tt>(*this,false)%=value;
10903     }
10904 
10905     //! Modulo operator.
10906     /**
10907        Similar to operator%=(const char*), except that it returns a new image instance instead of operating in-place.
10908        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10909     **/
10910     CImg<Tfloat> operator%(const char *const expression) const {
10911       return CImg<Tfloat>(*this,false)%=expression;
10912     }
10913 
10914     //! Modulo operator.
10915     /**
10916        Similar to operator%=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
10917        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10918     **/
10919     template<typename t>
10920     CImg<_cimg_Tt> operator%(const CImg<t>& img) const {
10921       return CImg<_cimg_Tt>(*this,false)%=img;
10922     }
10923 
10924     //! In-place bitwise AND operator.
10925     /**
10926        Similar to operator+=(const t), except that it performs a bitwise AND operation instead of an addition.
10927     **/
10928     template<typename t>
10929     CImg<T>& operator&=(const t value) {
10930       cimg_for(*this,ptrd,T) *ptrd = (T)((unsigned long)*ptrd & (unsigned long)value);
10931       return *this;
10932     }
10933 
10934     //! In-place bitwise AND operator.
10935     /**
10936        Similar to operator+=(const char*), except that it performs a bitwise AND operation instead of an addition.
10937     **/
10938     CImg<T>& operator&=(const char *const expression) {
10939       const unsigned int omode = cimg::exception_mode();
10940       cimg::exception_mode() = 0;
10941       try {
10942         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
10943         _cimg_math_parser mp(base,expression,"operator&=");
10944         T *ptrd = _data;
10945         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd & (unsigned long)mp.eval(x,y,z,c)); ++ptrd; }
10946       } catch (CImgException&) {
10947         cimg::exception_mode() = omode;
10948         *this&=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
10949       }
10950       cimg::exception_mode() = omode;
10951       return *this;
10952     }
10953 
10954     //! In-place bitwise AND operator.
10955     /**
10956        Similar to operator+=(const CImg<t>&), except that it performs a bitwise AND operation instead of an addition.
10957     **/
10958     template<typename t>
10959     CImg<T>& operator&=(const CImg<t>& img) {
10960       const unsigned long siz = size(), isiz = img.size();
10961       if (siz && isiz) {
10962         if (is_overlapped(img)) return *this&=+img;
10963         T *ptrd = _data, *const ptre = _data + siz;
10964         if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
10965           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)((unsigned long)*ptrd & (unsigned long)*(ptrs++));
10966         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((unsigned long)*ptrd & (unsigned long)*(ptrs++));
10967       }
10968       return *this;
10969     }
10970 
10971     //! Bitwise AND operator.
10972     /**
10973        Similar to operator&=(const t), except that it returns a new image instance instead of operating in-place.
10974        The pixel type of the returned image is \c T.
10975     **/
10976     template<typename t>
10977     CImg<T> operator&(const t value) const {
10978       return (+*this)&=value;
10979     }
10980 
10981     //! Bitwise AND operator.
10982     /**
10983        Similar to operator&=(const char*), except that it returns a new image instance instead of operating in-place.
10984        The pixel type of the returned image is \c T.
10985     **/
10986     CImg<T> operator&(const char *const expression) const {
10987       return (+*this)&=expression;
10988     }
10989 
10990     //! Bitwise AND operator.
10991     /**
10992        Similar to operator&=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
10993        The pixel type of the returned image is \c T.
10994     **/
10995     template<typename t>
10996     CImg<T> operator&(const CImg<t>& img) const {
10997       return (+*this)&=img;
10998     }
10999 
11000     //! In-place bitwise OR operator.
11001     /**
11002        Similar to operator+=(const t), except that it performs a bitwise OR operation instead of an addition.
11003     **/
11004     template<typename t>
11005     CImg<T>& operator|=(const t value) {
11006       cimg_for(*this,ptrd,T) *ptrd = (T)((unsigned long)*ptrd | (unsigned long)value);
11007       return *this;
11008     }
11009 
11010     //! In-place bitwise OR operator.
11011     /**
11012        Similar to operator+=(const char*), except that it performs a bitwise OR operation instead of an addition.
11013     **/
11014     CImg<T>& operator|=(const char *const expression) {
11015       const unsigned int omode = cimg::exception_mode();
11016       cimg::exception_mode() = 0;
11017       try {
11018         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
11019         _cimg_math_parser mp(base,expression,"operator|=");
11020         T *ptrd = _data;
11021         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd | (unsigned long)mp.eval(x,y,z,c)); ++ptrd; }
11022       } catch (CImgException&) {
11023         cimg::exception_mode() = omode;
11024         *this|=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
11025       }
11026       cimg::exception_mode() = omode;
11027       return *this;
11028     }
11029 
11030     //! In-place bitwise OR operator.
11031     /**
11032        Similar to operator+=(const CImg<t>&), except that it performs a bitwise OR operation instead of an addition.
11033     **/
11034     template<typename t>
11035     CImg<T>& operator|=(const CImg<t>& img) {
11036       const unsigned long siz = size(), isiz = img.size();
11037       if (siz && isiz) {
11038         if (is_overlapped(img)) return *this|=+img;
11039         T *ptrd = _data, *const ptre = _data + siz;
11040         if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
11041           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)((unsigned long)*ptrd | (unsigned long)*(ptrs++));
11042         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((unsigned long)*ptrd | (unsigned long)*(ptrs++));
11043       }
11044       return *this;
11045     }
11046 
11047     //! Bitwise OR operator.
11048     /**
11049        Similar to operator|=(const t), except that it returns a new image instance instead of operating in-place.
11050        The pixel type of the returned image is \c T.
11051     **/
11052     template<typename t>
11053     CImg<T> operator|(const t value) const {
11054       return (+*this)|=value;
11055     }
11056 
11057     //! Bitwise OR operator.
11058     /**
11059        Similar to operator|=(const char*), except that it returns a new image instance instead of operating in-place.
11060        The pixel type of the returned image is \c T.
11061     **/
11062     CImg<T> operator|(const char *const expression) const {
11063       return (+*this)|=expression;
11064     }
11065 
11066     //! Bitwise OR operator.
11067     /**
11068        Similar to operator|=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
11069        The pixel type of the returned image is \c T.
11070     **/
11071     template<typename t>
11072     CImg<T> operator|(const CImg<t>& img) const {
11073       return (+*this)|=img;
11074     }
11075 
11076     //! In-place bitwise XOR operator.
11077     /**
11078        Similar to operator+=(const t), except that it performs a bitwise XOR operation instead of an addition.
11079        \warning
11080        - It does \e not compute the \e power of pixel values. For this purpose, use pow(const t) instead.
11081     **/
11082     template<typename t>
11083     CImg<T>& operator^=(const t value) {
11084       cimg_for(*this,ptrd,T) *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)value);
11085       return *this;
11086     }
11087 
11088     //! In-place bitwise XOR operator.
11089     /**
11090        Similar to operator+=(const char*), except that it performs a bitwise XOR operation instead of an addition.
11091        \warning
11092        - It does \e not compute the \e power of pixel values. For this purpose, use pow(const char*) instead.
11093     **/
11094     CImg<T>& operator^=(const char *const expression) {
11095       const unsigned int omode = cimg::exception_mode();
11096       cimg::exception_mode() = 0;
11097       try {
11098         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
11099         _cimg_math_parser mp(base,expression,"operator^=");
11100         T *ptrd = _data;
11101         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)mp.eval(x,y,z,c)); ++ptrd; }
11102       } catch (CImgException&) {
11103         cimg::exception_mode() = omode;
11104         *this^=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
11105       }
11106       cimg::exception_mode() = omode;
11107       return *this;
11108     }
11109 
11110     //! In-place bitwise XOR operator.
11111     /**
11112        Similar to operator+=(const CImg<t>&), except that it performs a bitwise XOR operation instead of an addition.
11113        \warning
11114        - It does \e not compute the \e power of pixel values. For this purpose, use pow(const CImg<t>&) instead.
11115     **/
11116     template<typename t>
11117     CImg<T>& operator^=(const CImg<t>& img) {
11118       const unsigned long siz = size(), isiz = img.size();
11119       if (siz && isiz) {
11120         if (is_overlapped(img)) return *this^=+img;
11121         T *ptrd = _data, *const ptre = _data + siz;
11122         if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
11123           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)*(ptrs++));
11124         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)*(ptrs++));
11125       }
11126       return *this;
11127     }
11128 
11129     //! Bitwise XOR operator.
11130     /**
11131        Similar to operator^=(const t), except that it returns a new image instance instead of operating in-place.
11132        The pixel type of the returned image is \c T.
11133     **/
11134     template<typename t>
11135     CImg<T> operator^(const t value) const {
11136       return (+*this)^=value;
11137     }
11138 
11139     //! Bitwise XOR operator.
11140     /**
11141        Similar to operator^=(const char*), except that it returns a new image instance instead of operating in-place.
11142        The pixel type of the returned image is \c T.
11143     **/
11144     CImg<T> operator^(const char *const expression) const {
11145       return (+*this)^=expression;
11146     }
11147 
11148     //! Bitwise XOR operator.
11149     /**
11150        Similar to operator^=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
11151        The pixel type of the returned image is \c T.
11152     **/
11153     template<typename t>
11154     CImg<T> operator^(const CImg<t>& img) const {
11155       return (+*this)^=img;
11156     }
11157 
11158     //! In-place bitwise left shift operator.
11159     /**
11160        Similar to operator+=(const t), except that it performs a bitwise left shift instead of an addition.
11161     **/
11162     template<typename t>
11163     CImg<T>& operator<<=(const t value) {
11164       cimg_for(*this,ptrd,T) *ptrd = (T)(((long)*ptrd) << (int)value);
11165       return *this;
11166     }
11167 
11168     //! In-place bitwise left shift operator.
11169     /**
11170        Similar to operator+=(const char*), except that it performs a bitwise left shift instead of an addition.
11171     **/
11172     CImg<T>& operator<<=(const char *const expression) {
11173       const unsigned int omode = cimg::exception_mode();
11174       cimg::exception_mode() = 0;
11175       try {
11176         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
11177         _cimg_math_parser mp(base,expression,"operator<<=");
11178         T *ptrd = _data;
11179         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd << (int)mp.eval(x,y,z,c)); ++ptrd; }
11180       } catch (CImgException&) {
11181         cimg::exception_mode() = omode;
11182         *this<<=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
11183       }
11184       cimg::exception_mode() = omode;
11185       return *this;
11186     }
11187 
11188     //! In-place bitwise left shift operator.
11189     /**
11190        Similar to operator+=(const CImg<t>&), except that it performs a bitwise left shift instead of an addition.
11191     **/
11192     template<typename t>
11193     CImg<T>& operator<<=(const CImg<t>& img) {
11194       const unsigned long siz = size(), isiz = img.size();
11195       if (siz && isiz) {
11196         if (is_overlapped(img)) return *this^=+img;
11197         T *ptrd = _data, *const ptre = _data + siz;
11198         if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
11199           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)((long)*ptrd << (int)*(ptrs++));
11200         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((long)*ptrd << (int)*(ptrs++));
11201       }
11202       return *this;
11203     }
11204 
11205     //! Bitwise left shift operator.
11206     /**
11207        Similar to operator<<=(const t), except that it returns a new image instance instead of operating in-place.
11208        The pixel type of the returned image is \c T.
11209     **/
11210     template<typename t>
11211     CImg<T> operator<<(const t value) const {
11212       return (+*this)<<=value;
11213     }
11214 
11215     //! Bitwise left shift operator.
11216     /**
11217        Similar to operator<<=(const char*), except that it returns a new image instance instead of operating in-place.
11218        The pixel type of the returned image is \c T.
11219     **/
11220     CImg<T> operator<<(const char *const expression) const {
11221       return (+*this)<<=expression;
11222     }
11223 
11224     //! Bitwise left shift operator.
11225     /**
11226        Similar to operator<<=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
11227        The pixel type of the returned image is \c T.
11228     **/
11229     template<typename t>
11230     CImg<T> operator<<(const CImg<t>& img) const {
11231       return (+*this)<<=img;
11232     }
11233 
11234     //! In-place bitwise right shift operator.
11235     /**
11236        Similar to operator+=(const t), except that it performs a bitwise right shift instead of an addition.
11237     **/
11238     template<typename t>
11239     CImg<T>& operator>>=(const t value) {
11240       cimg_for(*this,ptrd,T) *ptrd = (T)(((long)*ptrd) >> (int)value);
11241       return *this;
11242     }
11243 
11244     //! In-place bitwise right shift operator.
11245     /**
11246        Similar to operator+=(const char*), except that it performs a bitwise right shift instead of an addition.
11247     **/
11248     CImg<T>& operator>>=(const char *const expression) {
11249       const unsigned int omode = cimg::exception_mode();
11250       cimg::exception_mode() = 0;
11251       try {
11252         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
11253         _cimg_math_parser mp(base,expression,"operator<<=");
11254         T *ptrd = _data;
11255         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd >> (int)mp.eval(x,y,z,c)); ++ptrd; }
11256       } catch (CImgException&) {
11257         cimg::exception_mode() = omode;
11258         *this>>=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
11259       }
11260       cimg::exception_mode() = omode;
11261       return *this;
11262     }
11263 
11264     //! In-place bitwise right shift operator.
11265     /**
11266        Similar to operator+=(const CImg<t>&), except that it performs a bitwise right shift instead of an addition.
11267     **/
11268     template<typename t>
11269     CImg<T>& operator>>=(const CImg<t>& img) {
11270       const unsigned long siz = size(), isiz = img.size();
11271       if (siz && isiz) {
11272         if (is_overlapped(img)) return *this^=+img;
11273         T *ptrd = _data, *const ptre = _data + siz;
11274         if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
11275           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)((long)*ptrd >> (int)*(ptrs++));
11276         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((long)*ptrd >> (int)*(ptrs++));
11277       }
11278       return *this;
11279     }
11280 
11281     //! Bitwise right shift operator.
11282     /**
11283        Similar to operator>>=(const t), except that it returns a new image instance instead of operating in-place.
11284        The pixel type of the returned image is \c T.
11285     **/
11286     template<typename t>
11287     CImg<T> operator>>(const t value) const {
11288       return (+*this)>>=value;
11289     }
11290 
11291     //! Bitwise right shift operator.
11292     /**
11293        Similar to operator>>=(const char*), except that it returns a new image instance instead of operating in-place.
11294        The pixel type of the returned image is \c T.
11295     **/
11296     CImg<T> operator>>(const char *const expression) const {
11297       return (+*this)>>=expression;
11298     }
11299 
11300     //! Bitwise right shift operator.
11301     /**
11302        Similar to operator>>=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
11303        The pixel type of the returned image is \c T.
11304     **/
11305     template<typename t>
11306     CImg<T> operator>>(const CImg<t>& img) const {
11307       return (+*this)>>=img;
11308     }
11309 
11310     //! Bitwise inversion operator.
11311     /**
11312        Similar to operator-(), except that it compute the bitwise inverse instead of the opposite value.
11313     **/
11314     CImg<T> operator~() const {
11315       CImg<T> res(_width,_height,_depth,_spectrum);
11316       const T *ptrs = _data;
11317       cimg_for(res,ptrd,T) { const unsigned long value = (unsigned long)*(ptrs++); *ptrd = (T)~value; }
11318       return res;
11319     }
11320 
11321     //! Test if all pixels of an image have the same value.
11322     /**
11323        Return \c true is all pixels of the image instance are equal to the specified \c value.
11324        \param value Reference value to compare with.
11325     **/
11326     template<typename t>
11327     bool operator==(const t value) const {
11328       if (is_empty()) return false;
11329       typedef _cimg_Tt Tt;
11330       bool is_equal = true;
11331       for (T *ptrd = _data + size(); is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)value)) {}
11332       return is_equal;
11333     }
11334 
11335     //! Test if all pixel values of an image follow a specified expression.
11336     /**
11337        Return \c true is all pixels of the image instance are equal to the specified \c expression.
11338        \param expression Value string describing the way pixel values are compared.
11339     **/
11340     bool operator==(const char *const expression) const {
11341       if (is_empty()) return !*expression;
11342       const unsigned int omode = cimg::exception_mode();
11343       cimg::exception_mode() = 0;
11344       bool is_equal = true;
11345       try {
11346         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
11347         _cimg_math_parser mp(base,expression,"operator<<=");
11348         const T *ptrs = _data;
11349         cimg_forXYZC(*this,x,y,z,c) { if (!is_equal) break; is_equal = ((double)*(ptrs++)==mp.eval(x,y,z,c)); }
11350       } catch (CImgException&) {
11351         cimg::exception_mode() = omode;
11352         is_equal = (*this==CImg<T>(_width,_height,_depth,_spectrum,expression,true));
11353       }
11354       cimg::exception_mode() = omode;
11355       return is_equal;
11356     }
11357 
11358     //! Test if two images have the same size and values.
11359     /**
11360        Return \c true if the image instance and the input image \c img have the same dimensions and pixel values, and \c false otherwise.
11361        \param img Input image to compare with.
11362        \note
11363        - The pixel buffer pointers data() of the two compared images do not have to be the same for operator==() to return \c true.
11364          Only the dimensions and the pixel values matter. Thus, the comparison can be \c true even for different pixel types \c T and \c t.
11365        \par Example
11366        \code
11367        const CImg<float> img1(1,3,1,1, 0,1,2); // Construct a 1x3 vector [0;1;2] (with 'float' pixel values).
11368        const CImg<char> img2(1,3,1,1, 0,1,2);  // Construct a 1x3 vector [0;1;2] (with 'char' pixel values).
11369        if (img1==img2) {                       // Test succeeds, image dimensions and values are the same.
11370          std::printf("'img1' and 'img2' have same dimensions and values.");
11371        }
11372        \endcode
11373     **/
11374     template<typename t>
11375     bool operator==(const CImg<t>& img) const {
11376       typedef _cimg_Tt Tt;
11377       const unsigned long siz = size();
11378       bool is_equal = true;
11379       if (siz!=img.size()) return false;
11380       t *ptrs = img._data + siz;
11381       for (T *ptrd = _data + siz; is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)*(--ptrs))) {}
11382       return is_equal;
11383     }
11384 
11385     //! Test if pixels of an image are all different from a value.
11386     /**
11387        Return \c true is all pixels of the image instance are different than the specified \c value.
11388        \param value Reference value to compare with.
11389     **/
11390     template<typename t>
11391     bool operator!=(const t value) const {
11392       return !((*this)==value);
11393     }
11394 
11395     //! Test if all pixel values of an image are different from a specified expression.
11396     /**
11397        Return \c true is all pixels of the image instance are different to the specified \c expression.
11398        \param expression Value string describing the way pixel values are compared.
11399     **/
11400     bool operator!=(const char *const expression) const {
11401       return !((*this)==expression);
11402     }
11403 
11404     //! Test if two images have different sizes or values.
11405     /**
11406        Return \c true if the image instance and the input image \c img have different dimensions or pixel values, and \c false otherwise.
11407        \param img Input image to compare with.
11408        \note
11409        - Writing \c img1!=img2 is equivalent to \c !(img1==img2).
11410     **/
11411     template<typename t>
11412     bool operator!=(const CImg<t>& img) const {
11413       return !((*this)==img);
11414     }
11415 
11416     //! Construct an image list from two images.
11417     /**
11418        Return a new list of image (\c CImgList instance) containing exactly two elements :
11419          - A copy of the image instance, at position [\c 0].
11420          - A copy of the specified image \c img, at position [\c 1].
11421 
11422        \param img Input image that will be the second image of the resulting list.
11423        \note
11424        - The family of operator,() is convenient to easily create list of images, but it is also \e quite \e slow in practice (see warning below).
11425        - Constructed lists contain no shared images. If image instance or input image \c img are shared, they are
11426          inserted as new non-shared copies in the resulting list.
11427        - The pixel type of the returned list may be a superset of the initial pixel type \c T, if necessary.
11428        \warning
11429        - Pipelining operator,() \c N times will perform \c N copies of the entire content of a (growing) image list.
11430          This may become very expensive in terms of speed and used memory. You should avoid using this technique to
11431          build a new CImgList instance from several images, if you are seeking for performance.
11432          Fast insertions of images in an image list are possible with CImgList<T>::insert(const CImg<t>&,unsigned int,bool) or
11433          move_to(CImgList<t>&,unsigned int).
11434        \par Example
11435        \code
11436        const CImg<float>
11437           img1("reference.jpg"),
11438           img2 = img1.get_mirror('x'),
11439           img3 = img2.get_blur(5);
11440        const CImgList<float> list = (img1,img2); // Create list of two elements from 'img1' and 'img2'.
11441        (list,img3).display();                    // Display image list containing copies of 'img1','img2' and 'img3'.
11442        \endcode
11443        \image html ref_operator_comma.jpg
11444     **/
11445     template<typename t>
11446     CImgList<_cimg_Tt> operator,(const CImg<t>& img) const {
11447       return CImgList<_cimg_Tt>(*this,img);
11448     }
11449 
11450     //! Construct an image list from image instance and an input image list.
11451     /**
11452        Return a new list of images (\c CImgList instance) containing exactly \c list.size() \c + \c 1 elements :
11453          - A copy of the image instance, at position [\c 0].
11454          - A copy of the specified image list \c list, from positions [\c 1] to [\c list.size()].
11455 
11456        \param list Input image list that will be appended to the image instance.
11457        \note
11458        - Similar to operator,(const CImg<t>&) const, except that it takes an image list as an argument.
11459     **/
11460     template<typename t>
11461     CImgList<_cimg_Tt> operator,(const CImgList<t>& list) const {
11462       return CImgList<_cimg_Tt>(list,false).insert(*this,0);
11463     }
11464 
11465     //! Split image along specified axis.
11466     /**
11467        Return a new list of images (\c CImgList instance) containing the splitted components
11468        of the instance image along the specified axis.
11469        \param axis Splitting axis (can be '\c x','\c y','\c z' or '\c c')
11470        \note
11471        - Similar to get_split(char,int) const, with default second argument.
11472        \par Example
11473        \code
11474        const CImg<unsigned char> img("reference.jpg"); // Load a RGB color image.
11475        const CImgList<unsigned char> list = (img<'c'); // Get a list of its three R,G,B channels.
11476        (img,list).display();
11477        \endcode
11478        \image html ref_operator_less.jpg
11479     **/
11480     CImgList<T> operator<(const char axis) const {
11481       return get_split(axis);
11482     }
11483 
11484     //@}
11485     //-------------------------------------
11486     //
11487     //! \name Instance Characteristics
11488     //@{
11489     //-------------------------------------
11490 
11491     //! Return the type of image pixel values as a C string.
11492     /**
11493        Return a \c char* string containing the usual type name of the image pixel values
11494        (i.e. a stringified version of the template parameter \c T).
11495        \note
11496        - The returned string may contain spaces (as in \c "unsigned char").
11497        - If the pixel type \c T does not correspond to a registered type, the string <tt>"unknown"</tt> is returned.
11498     **/
11499     static const char* pixel_type() {
11500       return cimg::type<T>::string();
11501     }
11502 
11503     //! Return the number of image columns.
11504     /**
11505        Return the image width, i.e. the image dimension along the X-axis.
11506        \note
11507        - The width() of an empty image is equal to \c 0.
11508        - width() is typically equal to \c 1 when considering images as \e vectors for matrix calculations.
11509        - width() returns an \c int, although the image width is internally stored as an \c unsigned \c int.
11510          Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
11511          \c unsigned \c int variables.
11512          Access to the initial \c unsigned \c int variable is possible (though not recommended) by <tt>(*this)._width</tt>.
11513     **/
11514     int width() const {
11515       return (int)_width;
11516     }
11517 
11518     //! Return the number of image rows.
11519     /**
11520        Return the image height, i.e. the image dimension along the Y-axis.
11521        \note
11522        - The height() of an empty image is equal to \c 0.
11523        - height() returns an \c int, although the image height is internally stored as an \c unsigned \c int.
11524          Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
11525          \c unsigned \c int variables.
11526          Access to the initial \c unsigned \c int variable is possible (though not recommended) by <tt>(*this)._height</tt>.
11527     **/
11528     int height() const {
11529       return (int)_height;
11530     }
11531 
11532     //! Return the number of image slices.
11533     /**
11534        Return the image depth, i.e. the image dimension along the Z-axis.
11535        \note
11536        - The depth() of an empty image is equal to \c 0.
11537        - depth() is typically equal to \c 1 when considering usual 2d images. When depth()\c > \c 1, the image
11538          is said to be \e volumetric.
11539        - depth() returns an \c int, although the image depth is internally stored as an \c unsigned \c int.
11540          Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
11541          \c unsigned \c int variables.
11542          Access to the initial \c unsigned \c int variable is possible (though not recommended) by <tt>(*this)._depth</tt>.
11543     **/
11544     int depth() const {
11545       return (int)_depth;
11546     }
11547 
11548     //! Return the number of image channels.
11549     /**
11550        Return the number of image channels, i.e. the image dimension along the C-axis.
11551        \note
11552        - The spectrum() of an empty image is equal to \c 0.
11553        - spectrum() is typically equal to \c 1 when considering scalar-valued images, to \c 3 for RGB-coded color images, and to
11554          \c 4 for RGBA-coded color images (with alpha-channel). The number of channels of an image instance
11555          is not limited. The meaning of the pixel values is not linked up to the number of channels
11556          (e.g. a 4-channel image may indifferently stands for a RGBA or CMYK color image).
11557        - spectrum() returns an \c int, although the image spectrum is internally stored as an \c unsigned \c int.
11558          Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
11559          \c unsigned \c int variables.
11560          Access to the initial \c unsigned \c int variable is possible (though not recommended) by <tt>(*this)._spectrum</tt>.
11561     **/
11562     int spectrum() const {
11563       return (int)_spectrum;
11564     }
11565 
11566     //! Return the total number of pixel values.
11567     /**
11568        Return <tt>width()*\ref height()*\ref depth()*\ref spectrum()</tt>,
11569        i.e. the total number of values of type \c T in the pixel buffer of the image instance.
11570        \note
11571        - The size() of an empty image is equal to \c 0.
11572        - The allocated memory size for a pixel buffer of a non-shared \c CImg<T> instance is equal to <tt>size()*sizeof(T)</tt>.
11573        \par Example
11574        \code
11575        const CImg<float> img(100,100,1,3);               // Construct new 100x100 color image.
11576        if (img.size()==30000)                            // Test succeeds.
11577          std::printf("Pixel buffer uses %lu bytes",
11578                      img.size()*sizeof(float));
11579        \endcode
11580     **/
11581     unsigned long size() const {
11582       return (unsigned long)_width*_height*_depth*_spectrum;
11583     }
11584 
11585     //! Return a pointer to the first pixel value.
11586     /**
11587        Return a \c T*, or a \c const \c T* pointer to the first value in the pixel buffer of the image instance,
11588        whether the instance is \c const or not.
11589        \note
11590        - The data() of an empty image is equal to \c 0 (null pointer).
11591        - The allocated pixel buffer for the image instance starts from \c data()
11592          and goes to <tt>data()+\ref size()-1</tt> (included).
11593        - To get the pointer to one particular location of the pixel buffer, use data(unsigned int,unsigned int,unsigned int,unsigned int) instead.
11594     **/
11595     T* data() {
11596       return _data;
11597     }
11598 
11599     //! Return a pointer to the first pixel value \const.
11600     const T* data() const {
11601       return _data;
11602     }
11603 
11604     //! Return a pointer to a located pixel value.
11605     /**
11606        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 of the image instance,
11607        whether the instance is \c const or not.
11608        \param x X-coordinate of the pixel value.
11609        \param y Y-coordinate of the pixel value.
11610        \param z Z-coordinate of the pixel value.
11611        \param c C-coordinate of the pixel value.
11612        \note
11613        - Writing \c img.data(x,y,z,c) is equivalent to <tt>&(img(x,y,z,c))</tt>. Thus, this method has the same properties as
11614          operator()(unsigned int,unsigned int,unsigned int,unsigned int).
11615      **/
11616 #if cimg_verbosity>=3
11617     T *data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
11618       const unsigned long off = (unsigned long)offset(x,y,z,c);
11619       if (off>=size()) {
11620         cimg::warn(_cimg_instance
11621                    "data() : Invalid pointer request, at coordinates (%u,%u,%u,%u) [offset=%u].",
11622                    cimg_instance,
11623                    x,y,z,c,off);
11624         return _data;
11625       }
11626       return _data + off;
11627     }
11628 
11629     //! Return a pointer to a located pixel value \const.
11630     const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
11631       return const_cast<CImg<T>*>(this)->data(x,y,z,c);
11632     }
11633 #else
11634     T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
11635       return _data + x + y*(unsigned long)_width + z*(unsigned long)_width*_height + c*(unsigned long)_width*_height*_depth;
11636     }
11637 
11638     const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
11639       return _data + x + y*_width + z*(unsigned long)_width*_height + c*(unsigned long)_width*_height*_depth;
11640     }
11641 #endif
11642 
11643     //! Return the offset to a located pixel value, with respect to the beginning of the pixel buffer.
11644     /**
11645        \param x X-coordinate of the pixel value.
11646        \param y Y-coordinate of the pixel value.
11647        \param z Z-coordinate of the pixel value.
11648        \param c C-coordinate of the pixel value.
11649        \note
11650        - Writing \c img.data(x,y,z,c) is equivalent to <tt>&(img(x,y,z,c)) - img.data()</tt>.
11651          Thus, this method has the same properties as operator()(unsigned int,unsigned int,unsigned int,unsigned int).
11652        \par Example
11653        \code
11654        const CImg<float> img(100,100,1,3);      // Define a 100x100 RGB-color image.
11655        const long off = img.offset(10,10,0,2);  // Get the offset of the blue value of the pixel located at (10,10).
11656        const float val = img[off];              // Get the blue value of this pixel.
11657        \endcode
11658     **/
11659     long offset(const int x, const int y=0, const int z=0, const int c=0) const {
11660       return x + y*(long)_width + z*(long)_width*_height + c*(long)_width*_height*_depth;
11661     }
11662 
11663     //! Return a CImg<T>::iterator pointing to the first pixel value.
11664     /**
11665        \note
11666        - Equivalent to data().
11667        - It has been mainly defined for compatibility with STL naming conventions.
11668      **/
11669     iterator begin() {
11670       return _data;
11671     }
11672 
11673     //! Return a CImg<T>::iterator pointing to the first value of the pixel buffer \const.
11674     const_iterator begin() const {
11675       return _data;
11676     }
11677 
11678     //! Return a CImg<T>::iterator pointing next to the last pixel value.
11679     /**
11680        \note
11681        - Writing \c img.end() is equivalent to <tt>img.data() + img.size()</tt>.
11682        - It has been mainly defined for compatibility with STL naming conventions.
11683        \warning
11684        - The returned iterator actually points to a value located \e outside the acceptable bounds of the pixel buffer. Trying
11685          to read or write the content of the returned iterator will probably result in a crash. Use it mainly as an
11686          strict upper bound for a CImg<T>::iterator.
11687        \par Example
11688        \code
11689        CImg<float> img(100,100,1,3);                                     // Define a 100x100 RGB color image.
11690        for (CImg<float>::iterator it = img.begin(); it<img.end(); ++it)  // 'img.end()' used here as an upper bound for the iterator.
11691          *it = 0;
11692        \endcode
11693     **/
11694     iterator end() {
11695       return _data + size();
11696     }
11697 
11698     //! Return a CImg<T>::iterator pointing next to the last pixel value \const.
11699     const_iterator end() const {
11700       return _data + size();
11701     }
11702 
11703     //! Return a reference to the first pixel value.
11704     /**
11705        \note
11706        - Writing \c img.front() is equivalent to <tt>img[0]</tt>, or <tt>img(0,0,0,0)</tt>.
11707        - It has been mainly defined for compatibility with STL naming conventions.
11708     **/
11709     T& front() {
11710       return *_data;
11711     }
11712 
11713     //! Return a reference to the first pixel value \const.
11714     const T& front() const {
11715       return *_data;
11716     }
11717 
11718     //! Return a reference to the last pixel value.
11719     /**
11720        \note
11721        - Writing \c img.end() is equivalent to <tt>img[img.size()-1]</tt>, or
11722          <tt>img(img.width()-1,img.height()-1,img.depth()-1,img.spectrum()-1)</tt>.
11723        - It has been mainly defined for compatibility with STL naming conventions.
11724     **/
11725     T& back() {
11726       return *(_data + size() - 1);
11727     }
11728 
11729     //! Return a reference to the last pixel value \const.
11730     const T& back() const {
11731       return *(_data + size() - 1);
11732     }
11733 
11734     //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions.
11735     /**
11736        Return a reference to the pixel value of the image instance located at a specified \c offset,
11737        or to a specified default value in case of out-of-bounds access.
11738        \param offset Offset to the desired pixel value.
11739        \param out_value Default value returned if \c offset is outside image bounds.
11740        \note
11741        - Writing \c img.at(offset,out_value) is similar to <tt>img[offset]</tt>, except that if \c offset
11742          is outside bounds (e.g. \c offset<0 or \c offset>=img.size()), a reference to a value \c out_value
11743          is safely returned instead.
11744        - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
11745          you are \e not sure about the validity of the specified pixel offset.
11746     **/
11747     T& at(const int offset, const T out_value) {
11748       return (offset<0 || offset>=(int)size())?(cimg::temporary(out_value)=out_value):(*this)[offset];
11749     }
11750 
11751     //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions \const.
11752     T at(const int offset, const T out_value) const {
11753       return (offset<0 || offset>=(int)size())?out_value:(*this)[offset];
11754     }
11755 
11756     //! Access to a pixel value at a specified offset, using Neumann boundary conditions.
11757     /**
11758        Return a reference to the pixel value of the image instance located at a specified \c offset,
11759        or to the nearest pixel location in the image instance in case of out-of-bounds access.
11760        \param offset Offset to the desired pixel value.
11761        \note
11762        - Similar to at(int,const T), except that an out-of-bounds access returns the value of the
11763          nearest pixel in the image instance, regarding the specified offset, i.e.
11764          - If \c offset<0, then \c img[0] is returned.
11765          - If \c offset>=img.size(), then \c img[img.size()-1] is returned.
11766        - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
11767          you are \e not sure about the validity of the specified pixel offset.
11768        - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _at(int).
11769      **/
11770     T& at(const int offset) {
11771       if (is_empty())
11772         throw CImgInstanceException(_cimg_instance
11773                                     "at() : Empty instance.",
11774                                     cimg_instance);
11775       return _at(offset);
11776     }
11777 
11778     T& _at(const int offset) {
11779       const unsigned int siz = (unsigned int)size();
11780       return (*this)[offset<0?0:(unsigned int)offset>=siz?siz-1:offset];
11781     }
11782 
11783     //! Access to a pixel value at a specified offset, using Neumann boundary conditions \const.
11784     T at(const int offset) const {
11785       if (is_empty())
11786         throw CImgInstanceException(_cimg_instance
11787                                     "at() : Empty instance.",
11788                                     cimg_instance);
11789       return _at(offset);
11790     }
11791 
11792     T _at(const int offset) const {
11793       const unsigned int siz = (unsigned int)size();
11794       return (*this)[offset<0?0:(unsigned int)offset>=siz?siz-1:offset];
11795     }
11796 
11797     //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate.
11798     /**
11799        Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c),
11800        or to a specified default value in case of out-of-bounds access along the X-axis.
11801        \param x X-coordinate of the pixel value.
11802        \param y Y-coordinate of the pixel value.
11803        \param z Z-coordinate of the pixel value.
11804        \param c C-coordinate of the pixel value.
11805        \param out_value Default value returned if \c (\c x,\c y,\c z,\c c) is outside image bounds.
11806        \note
11807        - Similar to operator()(), except that an out-of-bounds access along the X-axis returns the specified value \c out_value.
11808        - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
11809          you are \e not sure about the validity of the specified pixel coordinates.
11810        \warning
11811        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
11812     **/
11813     T& atX(const int x, const int y, const int z, const int c, const T out_value) {
11814       return (x<0 || x>=width())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
11815     }
11816 
11817     //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate \const.
11818     T atX(const int x, const int y, const int z, const int c, const T out_value) const {
11819       return (x<0 || x>=width())?out_value:(*this)(x,y,z,c);
11820     }
11821 
11822     //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate.
11823     /**
11824        Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c),
11825        or to the nearest pixel location in the image instance in case of out-of-bounds access along the X-axis.
11826        \param x X-coordinate of the pixel value.
11827        \param y Y-coordinate of the pixel value.
11828        \param z Z-coordinate of the pixel value.
11829        \param c C-coordinate of the pixel value.
11830        \note
11831        - Similar to at(int,int,int,int,const T), except that an out-of-bounds access returns the value of the
11832          nearest pixel in the image instance, regarding the specified X-coordinate.
11833        - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
11834          you are \e not sure about the validity of the specified pixel coordinates.
11835        - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _at(int,int,int,int).
11836        \warning
11837        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
11838      **/
11839     T& atX(const int x, const int y=0, const int z=0, const int c=0) {
11840       if (is_empty())
11841         throw CImgInstanceException(_cimg_instance
11842                                     "atX() : Empty instance.",
11843                                     cimg_instance);
11844       return _atX(x,y,z,c);
11845     }
11846 
11847     T& _atX(const int x, const int y=0, const int z=0, const int c=0) {
11848       return (*this)(x<0?0:(x>=width()?width()-1:x),y,z,c);
11849     }
11850 
11851     //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate \const.
11852     T atX(const int x, const int y=0, const int z=0, const int c=0) const {
11853       if (is_empty())
11854         throw CImgInstanceException(_cimg_instance
11855                                     "atX() : Empty instance.",
11856                                     cimg_instance);
11857       return _atX(x,y,z,c);
11858     }
11859 
11860     T _atX(const int x, const int y=0, const int z=0, const int c=0) const {
11861       return (*this)(x<0?0:(x>=width()?width()-1:x),y,z,c);
11862     }
11863 
11864     //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y-coordinates.
11865     /**
11866        Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on X and Y-coordinates.
11867     **/
11868     T& atXY(const int x, const int y, const int z, const int c, const T out_value) {
11869       return (x<0 || y<0 || x>=width() || y>=height())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
11870     }
11871 
11872     //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y coordinates \const.
11873     T atXY(const int x, const int y, const int z, const int c, const T out_value) const {
11874       return (x<0 || y<0 || x>=width() || y>=height())?out_value:(*this)(x,y,z,c);
11875     }
11876 
11877     //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates.
11878     /**
11879        Similar to atX(int,int,int,int), except that boundary checking is performed both on X and Y-coordinates.
11880        \note
11881        - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _atXY(int,int,int,int).
11882      **/
11883     T& atXY(const int x, const int y, const int z=0, const int c=0) {
11884       if (is_empty())
11885         throw CImgInstanceException(_cimg_instance
11886                                     "atXY() : Empty instance.",
11887                                     cimg_instance);
11888       return _atXY(x,y,z,c);
11889     }
11890 
11891     T& _atXY(const int x, const int y, const int z=0, const int c=0) {
11892       return (*this)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1:y),z,c);
11893     }
11894 
11895     //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates \const.
11896     T atXY(const int x, const int y, const int z=0, const int c=0) const {
11897       if (is_empty())
11898         throw CImgInstanceException(_cimg_instance
11899                                     "atXY() : Empty instance.",
11900                                     cimg_instance);
11901       return _atXY(x,y,z,c);
11902     }
11903 
11904     T _atXY(const int x, const int y, const int z=0, const int c=0) const {
11905       return (*this)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1:y),z,c);
11906     }
11907 
11908     //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates.
11909     /**
11910        Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on X,Y and Z-coordinates.
11911     **/
11912     T& atXYZ(const int x, const int y, const int z, const int c, const T out_value) {
11913       return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?
11914         (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
11915     }
11916 
11917     //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates \const.
11918     T atXYZ(const int x, const int y, const int z, const int c, const T out_value) const {
11919       return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?out_value:(*this)(x,y,z,c);
11920     }
11921 
11922     //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates.
11923     /**
11924        Similar to atX(int,int,int,int), except that boundary checking is performed both on X,Y and Z-coordinates.
11925        \note
11926        - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _atXYZ(int,int,int,int).
11927     **/
11928     T& atXYZ(const int x, const int y, const int z, const int c=0) {
11929       if (is_empty())
11930         throw CImgInstanceException(_cimg_instance
11931                                     "atXYZ() : Empty instance.",
11932                                     cimg_instance);
11933       return _atXYZ(x,y,z,c);
11934     }
11935 
11936     T& _atXYZ(const int x, const int y, const int z, const int c=0) {
11937       return (*this)(x<0?0:(x>=width()?width()-1:x),y<0?0:(y>=height()?height()-1:y),
11938                      z<0?0:(z>=depth()?depth()-1:z),c);
11939     }
11940 
11941     //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates \const.
11942     T atXYZ(const int x, const int y, const int z, const int c=0) const {
11943       if (is_empty())
11944         throw CImgInstanceException(_cimg_instance
11945                                     "atXYZ() : Empty instance.",
11946                                     cimg_instance);
11947       return _atXYZ(x,y,z,c);
11948     }
11949 
11950     T _atXYZ(const int x, const int y, const int z, const int c=0) const {
11951       return (*this)(x<0?0:(x>=width()?width()-1:x),y<0?0:(y>=height()?height()-1:y),
11952                      z<0?0:(z>=depth()?depth()-1:z),c);
11953     }
11954 
11955     //! Access to a pixel value, using Dirichlet boundary conditions.
11956     /**
11957        Similar to atX(int,int,int,int,const T), except that boundary checking is performed on all X,Y,Z and C-coordinates.
11958     **/
11959     T& atXYZC(const int x, const int y, const int z, const int c, const T out_value) {
11960       return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?
11961         (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
11962     }
11963 
11964     //! Access to a pixel value, using Dirichlet boundary conditions \const.
11965     T atXYZC(const int x, const int y, const int z, const int c, const T out_value) const {
11966       return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?out_value:(*this)(x,y,z,c);
11967     }
11968 
11969     //! Access to a pixel value, using Neumann boundary conditions.
11970     /**
11971        Similar to atX(int,int,int,int), except that boundary checking is performed on all X,Y,Z and C-coordinates.
11972        \note
11973        - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _atXYZC(int,int,int,int).
11974     **/
11975     T& atXYZC(const int x, const int y, const int z, const int c) {
11976       if (is_empty())
11977         throw CImgInstanceException(_cimg_instance
11978                                     "atXYZC() : Empty instance.",
11979                                     cimg_instance);
11980       return _atXYZC(x,y,z,c);
11981     }
11982 
11983     T& _atXYZC(const int x, const int y, const int z, const int c) {
11984       return (*this)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1:y),
11985                      z<0?0:(z>=depth()?depth()-1:z), c<0?0:(c>=spectrum()?spectrum()-1:c));
11986     }
11987 
11988     //! Access to a pixel value, using Neumann boundary conditions \const.
11989     T atXYZC(const int x, const int y, const int z, const int c) const {
11990       if (is_empty())
11991         throw CImgInstanceException(_cimg_instance
11992                                     "atXYZC() : Empty instance.",
11993                                     cimg_instance);
11994       return _atXYZC(x,y,z,c);
11995     }
11996 
11997     T _atXYZC(const int x, const int y, const int z, const int c) const {
11998       return (*this)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1:y),
11999                      z<0?0:(z>=depth()?depth()-1:z), c<0?0:(c>=spectrum()?spectrum()-1:c));
12000     }
12001 
12002     //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X-coordinate.
12003     /**
12004        Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
12005        or a specified default value in case of out-of-bounds access along the X-axis.
12006        \param fx X-coordinate of the pixel value (float-valued).
12007        \param y Y-coordinate of the pixel value.
12008        \param z Z-coordinate of the pixel value.
12009        \param c C-coordinate of the pixel value.
12010        \param out_value Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds.
12011        \note
12012        - Similar to atX(int,int,int,int,const T), except that the returned pixel value is approximated by a linear interpolation along the X-axis,
12013          if corresponding coordinates are not integers.
12014        - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued.
12015        \warning
12016        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
12017     **/
12018     Tfloat linear_atX(const float fx, const int y, const int z, const int c, const T out_value) const {
12019       const int
12020         x = (int)fx - (fx>=0?0:1), nx = x + 1;
12021       const float
12022         dx = fx - x;
12023       const Tfloat
12024         Ic = (Tfloat)atX(x,y,z,c,out_value), In = (Tfloat)atXY(nx,y,z,c,out_value);
12025       return Ic + dx*(In-Ic);
12026     }
12027 
12028     //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X-coordinate.
12029     /**
12030        Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
12031        or the value of the nearest pixel location in the image instance in case of out-of-bounds access along the X-axis.
12032        \param fx X-coordinate of the pixel value (float-valued).
12033        \param y Y-coordinate of the pixel value.
12034        \param z Z-coordinate of the pixel value.
12035        \param c C-coordinate of the pixel value.
12036        \note
12037        - Similar to linear_atX(float,int,int,int,const T) const, except that an out-of-bounds access returns the value of the
12038          nearest pixel in the image instance, regarding the specified X-coordinate.
12039        - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _linear_atX(float,int,int,int).
12040        \warning
12041        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
12042     **/
12043     Tfloat linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
12044       if (is_empty())
12045         throw CImgInstanceException(_cimg_instance
12046                                     "linear_atX() : Empty instance.",
12047                                     cimg_instance);
12048 
12049       return _linear_atX(fx,y,z,c);
12050     }
12051 
12052     Tfloat _linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
12053       const float
12054         nfx = fx<0?0:(fx>_width-1?_width-1:fx);
12055       const unsigned int
12056         x = (unsigned int)nfx;
12057       const float
12058         dx = nfx - x;
12059       const unsigned int
12060         nx = dx>0?x+1:x;
12061       const Tfloat
12062         Ic = (Tfloat)(*this)(x,y,z,c), In = (Tfloat)(*this)(nx,y,z,c);
12063       return Ic + dx*(In-Ic);
12064     }
12065 
12066     //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X and Y-coordinates.
12067     /**
12068        Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the boundary checking
12069        are achieved both for X and Y-coordinates.
12070     **/
12071     Tfloat linear_atXY(const float fx, const float fy, const int z, const int c, const T out_value) const {
12072       const int
12073         x = (int)fx - (fx>=0?0:1), nx = x + 1,
12074         y = (int)fy - (fy>=0?0:1), ny = y + 1;
12075       const float
12076         dx = fx - x,
12077         dy = fy - y;
12078       const Tfloat
12079         Icc = (Tfloat)atXY(x,y,z,c,out_value),  Inc = (Tfloat)atXY(nx,y,z,c,out_value),
12080         Icn = (Tfloat)atXY(x,ny,z,c,out_value), Inn = (Tfloat)atXY(nx,ny,z,c,out_value);
12081       return Icc + dx*(Inc-Icc + dy*(Icc+Inn-Icn-Inc)) + dy*(Icn-Icc);
12082     }
12083 
12084     //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X and Y-coordinates.
12085     /**
12086        Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking
12087        are achieved both for X and Y-coordinates.
12088        \note
12089        - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _linear_atXY(float,float,int,int).
12090     **/
12091     Tfloat linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
12092       if (is_empty())
12093         throw CImgInstanceException(_cimg_instance
12094                                     "linear_atXY() : Empty instance.",
12095                                     cimg_instance);
12096 
12097       return _linear_atXY(fx,fy,z,c);
12098     }
12099 
12100     Tfloat _linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
12101       const float
12102         nfx = fx<0?0:(fx>_width-1?_width-1:fx),
12103         nfy = fy<0?0:(fy>_height-1?_height-1:fy);
12104       const unsigned int
12105         x = (unsigned int)nfx,
12106         y = (unsigned int)nfy;
12107       const float
12108         dx = nfx - x,
12109         dy = nfy - y;
12110       const unsigned int
12111         nx = dx>0?x+1:x,
12112         ny = dy>0?y+1:y;
12113       const Tfloat
12114         Icc = (Tfloat)(*this)(x,y,z,c),  Inc = (Tfloat)(*this)(nx,y,z,c),
12115         Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c);
12116       return Icc + dx*(Inc-Icc + dy*(Icc+Inn-Icn-Inc)) + dy*(Icn-Icc);
12117     }
12118 
12119     //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates.
12120     /**
12121        Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the boundary checking
12122        are achieved both for X,Y and Z-coordinates.
12123     **/
12124     Tfloat linear_atXYZ(const float fx, const float fy, const float fz, const int c, const T out_value) const {
12125       const int
12126         x = (int)fx - (fx>=0?0:1), nx = x + 1,
12127         y = (int)fy - (fy>=0?0:1), ny = y + 1,
12128         z = (int)fz - (fz>=0?0:1), nz = z + 1;
12129       const float
12130         dx = fx - x,
12131         dy = fy - y,
12132         dz = fz - z;
12133       const Tfloat
12134         Iccc = (Tfloat)atXYZ(x,y,z,c,out_value), Incc = (Tfloat)atXYZ(nx,y,z,c,out_value),
12135         Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value), Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value),
12136         Iccn = (Tfloat)atXYZ(x,y,nz,c,out_value), Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value),
12137         Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value), Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value);
12138       return Iccc +
12139         dx*(Incc-Iccc +
12140             dy*(Iccc+Innc-Icnc-Incc +
12141                 dz*(Iccn+Innn+Icnc+Incc-Icnn-Incn-Iccc-Innc)) +
12142             dz*(Iccc+Incn-Iccn-Incc)) +
12143         dy*(Icnc-Iccc +
12144             dz*(Iccc+Icnn-Iccn-Icnc)) +
12145         dz*(Iccn-Iccc);
12146     }
12147 
12148     //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X,Y and Z-coordinates.
12149     /**
12150        Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking
12151        are achieved both for X,Y and Z-coordinates.
12152        \note
12153        - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _linear_atXYZ(float,float,float,int).
12154     **/
12155     Tfloat linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const {
12156       if (is_empty())
12157         throw CImgInstanceException(_cimg_instance
12158                                     "linear_atXYZ() : Empty instance.",
12159                                     cimg_instance);
12160 
12161       return _linear_atXYZ(fx,fy,fz,c);
12162     }
12163 
12164     Tfloat _linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const {
12165       const float
12166         nfx = fx<0?0:(fx>_width-1?_width-1:fx),
12167         nfy = fy<0?0:(fy>_height-1?_height-1:fy),
12168         nfz = fz<0?0:(fz>_depth-1?_depth-1:fz);
12169       const unsigned int
12170         x = (unsigned int)nfx,
12171         y = (unsigned int)nfy,
12172         z = (unsigned int)nfz;
12173       const float
12174         dx = nfx - x,
12175         dy = nfy - y,
12176         dz = nfz - z;
12177       const unsigned int
12178         nx = dx>0?x+1:x,
12179         ny = dy>0?y+1:y,
12180         nz = dz>0?z+1:z;
12181       const Tfloat
12182         Iccc = (Tfloat)(*this)(x,y,z,c), Incc = (Tfloat)(*this)(nx,y,z,c),
12183         Icnc = (Tfloat)(*this)(x,ny,z,c), Innc = (Tfloat)(*this)(nx,ny,z,c),
12184         Iccn = (Tfloat)(*this)(x,y,nz,c), Incn = (Tfloat)(*this)(nx,y,nz,c),
12185         Icnn = (Tfloat)(*this)(x,ny,nz,c), Innn = (Tfloat)(*this)(nx,ny,nz,c);
12186       return Iccc +
12187         dx*(Incc-Iccc +
12188             dy*(Iccc+Innc-Icnc-Incc +
12189                 dz*(Iccn+Innn+Icnc+Incc-Icnn-Incn-Iccc-Innc)) +
12190             dz*(Iccc+Incn-Iccn-Incc)) +
12191         dy*(Icnc-Iccc +
12192             dz*(Iccc+Icnn-Iccn-Icnc)) +
12193         dz*(Iccn-Iccc);
12194     }
12195 
12196     //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for all X,Y,Z and C-coordinates.
12197     /**
12198        Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the boundary checking
12199        are achieved for all X,Y,Z and C-coordinates.
12200     **/
12201     Tfloat linear_atXYZC(const float fx, const float fy, const float fz, const float fc, const T out_value) const {
12202       const int
12203         x = (int)fx - (fx>=0?0:1), nx = x + 1,
12204         y = (int)fy - (fy>=0?0:1), ny = y + 1,
12205         z = (int)fz - (fz>=0?0:1), nz = z + 1,
12206         c = (int)fc - (fc>=0?0:1), nc = c + 1;
12207       const float
12208         dx = fx - x,
12209         dy = fy - y,
12210         dz = fz - z,
12211         dc = fc - c;
12212       const Tfloat
12213         Icccc = (Tfloat)atXYZC(x,y,z,c,out_value), Inccc = (Tfloat)atXYZC(nx,y,z,c,out_value),
12214         Icncc = (Tfloat)atXYZC(x,ny,z,c,out_value), Inncc = (Tfloat)atXYZC(nx,ny,z,c,out_value),
12215         Iccnc = (Tfloat)atXYZC(x,y,nz,c,out_value), Incnc = (Tfloat)atXYZC(nx,y,nz,c,out_value),
12216         Icnnc = (Tfloat)atXYZC(x,ny,nz,c,out_value), Innnc = (Tfloat)atXYZC(nx,ny,nz,c,out_value),
12217         Icccn = (Tfloat)atXYZC(x,y,z,nc,out_value), Inccn = (Tfloat)atXYZC(nx,y,z,nc,out_value),
12218         Icncn = (Tfloat)atXYZC(x,ny,z,nc,out_value), Inncn = (Tfloat)atXYZC(nx,ny,z,nc,out_value),
12219         Iccnn = (Tfloat)atXYZC(x,y,nz,nc,out_value), Incnn = (Tfloat)atXYZC(nx,y,nz,nc,out_value),
12220         Icnnn = (Tfloat)atXYZC(x,ny,nz,nc,out_value), Innnn = (Tfloat)atXYZC(nx,ny,nz,nc,out_value);
12221       return Icccc +
12222         dx*(Inccc-Icccc +
12223             dy*(Icccc+Inncc-Icncc-Inccc +
12224                 dz*(Iccnc+Innnc+Icncc+Inccc-Icnnc-Incnc-Icccc-Inncc +
12225                     dc*(Iccnn+Innnn+Icncn+Inccn+Icnnc+Incnc+Icccc+Inncc-Icnnn-Incnn-Icccn-Inncn-Iccnc-Innnc-Icncc-Inccc)) +
12226                 dc*(Icccn+Inncn+Icncc+Inccc-Icncn-Inccn-Icccc-Inncc)) +
12227             dz*(Icccc+Incnc-Iccnc-Inccc +
12228                 dc*(Icccn+Incnn+Iccnc+Inccc-Iccnn-Inccn-Icccc-Incnc)) +
12229             dc*(Icccc+Inccn-Inccc-Icccn)) +
12230         dy*(Icncc-Icccc +
12231             dz*(Icccc+Icnnc-Iccnc-Icncc +
12232                 dc*(Icccn+Icnnn+Iccnc+Icncc-Iccnn-Icncn-Icccc-Icnnc)) +
12233             dc*(Icccc+Icncn-Icncc-Icccn)) +
12234         dz*(Iccnc-Icccc +
12235             dc*(Icccc+Iccnn-Iccnc-Icccn)) +
12236         dc*(Icccn-Icccc);
12237     }
12238 
12239     //! Return pixel value, using linear interpolation and Neumann boundary conditions for all X,Y,Z and C-coordinates.
12240     /**
12241        Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking
12242        are achieved for all X,Y,Z and C-coordinates.
12243        \note
12244        - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _linear_atXYZC(float,float,float,float).
12245     **/
12246     Tfloat linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
12247       if (is_empty())
12248         throw CImgInstanceException(_cimg_instance
12249                                     "linear_atXYZC() : Empty instance.",
12250                                     cimg_instance);
12251 
12252       return _linear_atXYZC(fx,fy,fz,fc);
12253     }
12254 
12255     Tfloat _linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
12256       const float
12257         nfx = fx<0?0:(fx>_width-1?_width-1:fx),
12258         nfy = fy<0?0:(fy>_height-1?_height-1:fy),
12259         nfz = fz<0?0:(fz>_depth-1?_depth-1:fz),
12260         nfc = fc<0?0:(fc>_spectrum-1?_spectrum-1:fc);
12261       const unsigned int
12262         x = (unsigned int)nfx,
12263         y = (unsigned int)nfy,
12264         z = (unsigned int)nfz,
12265         c = (unsigned int)nfc;
12266       const float
12267         dx = nfx - x,
12268         dy = nfy - y,
12269         dz = nfz - z,
12270         dc = nfc - c;
12271       const unsigned int
12272         nx = dx>0?x+1:x,
12273         ny = dy>0?y+1:y,
12274         nz = dz>0?z+1:z,
12275         nc = dc>0?c+1:c;
12276       const Tfloat
12277         Icccc = (Tfloat)(*this)(x,y,z,c), Inccc = (Tfloat)(*this)(nx,y,z,c),
12278         Icncc = (Tfloat)(*this)(x,ny,z,c), Inncc = (Tfloat)(*this)(nx,ny,z,c),
12279         Iccnc = (Tfloat)(*this)(x,y,nz,c), Incnc = (Tfloat)(*this)(nx,y,nz,c),
12280         Icnnc = (Tfloat)(*this)(x,ny,nz,c), Innnc = (Tfloat)(*this)(nx,ny,nz,c),
12281         Icccn = (Tfloat)(*this)(x,y,z,nc), Inccn = (Tfloat)(*this)(nx,y,z,nc),
12282         Icncn = (Tfloat)(*this)(x,ny,z,nc), Inncn = (Tfloat)(*this)(nx,ny,z,nc),
12283         Iccnn = (Tfloat)(*this)(x,y,nz,nc), Incnn = (Tfloat)(*this)(nx,y,nz,nc),
12284         Icnnn = (Tfloat)(*this)(x,ny,nz,nc), Innnn = (Tfloat)(*this)(nx,ny,nz,nc);
12285       return Icccc +
12286         dx*(Inccc-Icccc +
12287             dy*(Icccc+Inncc-Icncc-Inccc +
12288                 dz*(Iccnc+Innnc+Icncc+Inccc-Icnnc-Incnc-Icccc-Inncc +
12289                     dc*(Iccnn+Innnn+Icncn+Inccn+Icnnc+Incnc+Icccc+Inncc-Icnnn-Incnn-Icccn-Inncn-Iccnc-Innnc-Icncc-Inccc)) +
12290                 dc*(Icccn+Inncn+Icncc+Inccc-Icncn-Inccn-Icccc-Inncc)) +
12291             dz*(Icccc+Incnc-Iccnc-Inccc +
12292                 dc*(Icccn+Incnn+Iccnc+Inccc-Iccnn-Inccn-Icccc-Incnc)) +
12293             dc*(Icccc+Inccn-Inccc-Icccn)) +
12294         dy*(Icncc-Icccc +
12295             dz*(Icccc+Icnnc-Iccnc-Icncc +
12296                 dc*(Icccn+Icnnn+Iccnc+Icncc-Iccnn-Icncn-Icccc-Icnnc)) +
12297             dc*(Icccc+Icncn-Icncc-Icccn)) +
12298         dz*(Iccnc-Icccc +
12299             dc*(Icccc+Iccnn-Iccnc-Icccn)) +
12300         dc*(Icccn-Icccc);
12301     }
12302 
12303     //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate.
12304     /**
12305        Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
12306        or a specified default value in case of out-of-bounds access along the X-axis.
12307        \param fx d X-coordinate of the pixel value (float-valued).
12308        \param y Y-coordinate of the pixel value.
12309        \param z Z-coordinate of the pixel value.
12310        \param c C-coordinate of the pixel value.
12311        \param out_value Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds.
12312        \note
12313        - Similar to linear_atX(float,int,int,int,const T) const, except that the returned pixel value is approximated by a
12314          \e cubic interpolation along the X-axis.
12315        - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued.
12316        \warning
12317        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
12318     **/
12319     Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T out_value) const {
12320       const int
12321         x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2;
12322       const float
12323         dx = fx - x;
12324       const Tfloat
12325         Ip = (Tfloat)atX(px,y,z,c,out_value), Ic = (Tfloat)atX(x,y,z,c,out_value),
12326         In = (Tfloat)atX(nx,y,z,c,out_value), Ia = (Tfloat)atX(ax,y,z,c,out_value);
12327       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));
12328     }
12329 
12330     //! Return damped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate.
12331     /**
12332        Similar to cubic_atX(float,int,int,int,const T) const, except that you can specify the authorized minimum and maximum of the returned value.
12333     **/
12334     Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T out_value,
12335                      const Tfloat min_value, const Tfloat max_value) const {
12336       const Tfloat val = cubic_atX(fx,y,z,c,out_value);
12337       return val<min_value?min_value:val>max_value?max_value:val;
12338     }
12339 
12340     //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate.
12341     /**
12342        Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
12343        or the value of the nearest pixel location in the image instance in case of out-of-bounds access along the X-axis.
12344        \param fx X-coordinate of the pixel value (float-valued).
12345        \param y Y-coordinate of the pixel value.
12346        \param z Z-coordinate of the pixel value.
12347        \param c C-coordinate of the pixel value.
12348        \note
12349        - Similar to cubic_atX(float,int,int,int,const T) const, except that the returned pixel value is approximated by a cubic interpolation along the X-axis.
12350        - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _cubic_atX(float,int,int,int).
12351        \warning
12352        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
12353     **/
12354     Tfloat cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
12355       if (is_empty())
12356         throw CImgInstanceException(_cimg_instance
12357                                     "cubic_atX() : Empty instance.",
12358                                     cimg_instance);
12359       return _cubic_atX(fx,y,z,c);
12360     }
12361 
12362     Tfloat _cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
12363       const float
12364         nfx = fx<0?0:(fx>_width-1?_width-1:fx);
12365       const int
12366         x = (int)nfx;
12367       const float
12368         dx = nfx - x;
12369       const int
12370         px = x-1<0?0:x-1, nx = dx>0?x+1:x, ax = x+2>=width()?width()-1:x+2;
12371       const Tfloat
12372         Ip = (Tfloat)(*this)(px,y,z,c), Ic = (Tfloat)(*this)(x,y,z,c),
12373         In = (Tfloat)(*this)(nx,y,z,c), Ia = (Tfloat)(*this)(ax,y,z,c);
12374       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));
12375     }
12376 
12377     //! Return damped pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate.
12378     /**
12379        Similar to cubic_atX(float,int,int,int) const, except that you can specify the authorized minimum and maximum of the returned value.
12380     **/
12381     Tfloat cubic_atX(const float fx, const int y, const int z, const int c,
12382                      const Tfloat min_value, const Tfloat max_value) const {
12383       const Tfloat val = cubic_atX(fx,y,z,c);
12384       return val<min_value?min_value:val>max_value?max_value:val;
12385     }
12386 
12387     Tfloat _cubic_atX(const float fx, const int y, const int z, const int c,
12388                       const Tfloat min_value, const Tfloat max_value) const {
12389       const Tfloat val = _cubic_atX(fx,y,z,c);
12390       return val<min_value?min_value:val>max_value?max_value:val;
12391     }
12392 
12393     //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X and Y-coordinates.
12394     /**
12395        Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking
12396        are achieved both for X and Y-coordinates.
12397     **/
12398     Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T out_value) const {
12399       const int
12400         x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2,
12401         y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2;
12402       const float dx = fx - x, dy = fy - y;
12403       const Tfloat
12404         Ipp = (Tfloat)atXY(px,py,z,c,out_value), Icp = (Tfloat)atXY(x,py,z,c,out_value), Inp = (Tfloat)atXY(nx,py,z,c,out_value), Iap = (Tfloat)atXY(ax,py,z,c,out_value),
12405         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)),
12406         Ipc = (Tfloat)atXY(px,y,z,c,out_value),  Icc = (Tfloat)atXY(x, y,z,c,out_value), Inc = (Tfloat)atXY(nx,y,z,c,out_value),  Iac = (Tfloat)atXY(ax,y,z,c,out_value),
12407         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)),
12408         Ipn = (Tfloat)atXY(px,ny,z,c,out_value), Icn = (Tfloat)atXY(x,ny,z,c,out_value), Inn = (Tfloat)atXY(nx,ny,z,c,out_value), Ian = (Tfloat)atXY(ax,ny,z,c,out_value),
12409         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)),
12410         Ipa = (Tfloat)atXY(px,ay,z,c,out_value), Ica = (Tfloat)atXY(x,ay,z,c,out_value), Ina = (Tfloat)atXY(nx,ay,z,c,out_value), Iaa = (Tfloat)atXY(ax,ay,z,c,out_value),
12411         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));
12412       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));
12413     }
12414 
12415     //! Return damped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X and Y-coordinates.
12416     /**
12417        Similar to cubic_atXY(float,float,int,int,const T) const, except that you can specify the authorized minimum and maximum of the returned value.
12418     **/
12419     Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T out_value,
12420                       const Tfloat min_value, const Tfloat max_value) const {
12421       const Tfloat val = cubic_atXY(fx,fy,z,c,out_value);
12422       return val<min_value?min_value:val>max_value?max_value:val;
12423     }
12424 
12425     //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X and Y-coordinates.
12426     /**
12427        Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking
12428        are achieved for both X and Y-coordinates.
12429        \note
12430        - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _cubic_atXY(float,float,int,int).
12431     **/
12432     Tfloat cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
12433       if (is_empty())
12434         throw CImgInstanceException(_cimg_instance
12435                                     "cubic_atXY() : Empty instance.",
12436                                     cimg_instance);
12437       return _cubic_atXY(fx,fy,z,c);
12438     }
12439 
12440     Tfloat _cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
12441       const float
12442         nfx = fx<0?0:(fx>_width-1?_width-1:fx),
12443         nfy = fy<0?0:(fy>_height-1?_height-1:fy);
12444       const int x = (int)nfx, y = (int)nfy;
12445       const float dx = nfx - x, dy = nfy - y;
12446       const int
12447         px = x-1<0?0:x-1, nx = dx>0?x+1:x, ax = x+2>=width()?width()-1:x+2,
12448         py = y-1<0?0:y-1, ny = dy>0?y+1:y, ay = y+2>=height()?height()-1:y+2;
12449       const Tfloat
12450         Ipp = (Tfloat)(*this)(px,py,z,c), Icp = (Tfloat)(*this)(x,py,z,c), Inp = (Tfloat)(*this)(nx,py,z,c), Iap = (Tfloat)(*this)(ax,py,z,c),
12451         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)),
12452         Ipc = (Tfloat)(*this)(px,y,z,c),  Icc = (Tfloat)(*this)(x, y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c),  Iac = (Tfloat)(*this)(ax,y,z,c),
12453         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)),
12454         Ipn = (Tfloat)(*this)(px,ny,z,c), Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c), Ian = (Tfloat)(*this)(ax,ny,z,c),
12455         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)),
12456         Ipa = (Tfloat)(*this)(px,ay,z,c), Ica = (Tfloat)(*this)(x,ay,z,c), Ina = (Tfloat)(*this)(nx,ay,z,c), Iaa = (Tfloat)(*this)(ax,ay,z,c),
12457         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));
12458       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));
12459     }
12460 
12461     //! Return damped pixel value, using cubic interpolation and Neumann boundary conditions for the X and Y-coordinates.
12462     /**
12463        Similar to cubic_atXY(float,float,int,int) const, except that you can specify the authorized minimum and maximum of the returned value.
12464     **/
12465     Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c,
12466                       const Tfloat min_value, const Tfloat max_value) const {
12467       const Tfloat val = cubic_atXY(fx,fy,z,c);
12468       return val<min_value?min_value:val>max_value?max_value:val;
12469     }
12470 
12471     Tfloat _cubic_atXY(const float fx, const float fy, const int z, const int c,
12472                        const Tfloat min_value, const Tfloat max_value) const {
12473       const Tfloat val = _cubic_atXY(fx,fy,z,c);
12474       return val<min_value?min_value:val>max_value?max_value:val;
12475     }
12476 
12477     //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates.
12478     /**
12479        Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking
12480        are achieved both for X,Y and Z-coordinates.
12481     **/
12482     Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T out_value) const {
12483       const int
12484         x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2,
12485         y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2,
12486         z = (int)fz - (fz>=0?0:1), pz = z - 1, nz = z + 1, az = z + 2;
12487       const float dx = fx - x, dy = fy - y, dz = fz - z;
12488       const Tfloat
12489         Ippp = (Tfloat)atXYZ(px,py,pz,c,out_value), Icpp = (Tfloat)atXYZ(x,py,pz,c,out_value),
12490         Inpp = (Tfloat)atXYZ(nx,py,pz,c,out_value), Iapp = (Tfloat)atXYZ(ax,py,pz,c,out_value),
12491         Ipp = Icpp + 0.5f*(dx*(-Ippp+Inpp) + dx*dx*(2*Ippp-5*Icpp+4*Inpp-Iapp) + dx*dx*dx*(-Ippp+3*Icpp-3*Inpp+Iapp)),
12492         Ipcp = (Tfloat)atXYZ(px,y,pz,c,out_value),  Iccp = (Tfloat)atXYZ(x, y,pz,c,out_value),
12493         Incp = (Tfloat)atXYZ(nx,y,pz,c,out_value),  Iacp = (Tfloat)atXYZ(ax,y,pz,c,out_value),
12494         Icp = Iccp + 0.5f*(dx*(-Ipcp+Incp) + dx*dx*(2*Ipcp-5*Iccp+4*Incp-Iacp) + dx*dx*dx*(-Ipcp+3*Iccp-3*Incp+Iacp)),
12495         Ipnp = (Tfloat)atXYZ(px,ny,pz,c,out_value), Icnp = (Tfloat)atXYZ(x,ny,pz,c,out_value),
12496         Innp = (Tfloat)atXYZ(nx,ny,pz,c,out_value), Ianp = (Tfloat)atXYZ(ax,ny,pz,c,out_value),
12497         Inp = Icnp + 0.5f*(dx*(-Ipnp+Innp) + dx*dx*(2*Ipnp-5*Icnp+4*Innp-Ianp) + dx*dx*dx*(-Ipnp+3*Icnp-3*Innp+Ianp)),
12498         Ipap = (Tfloat)atXYZ(px,ay,pz,c,out_value), Icap = (Tfloat)atXYZ(x,ay,pz,c,out_value),
12499         Inap = (Tfloat)atXYZ(nx,ay,pz,c,out_value), Iaap = (Tfloat)atXYZ(ax,ay,pz,c,out_value),
12500         Iap = Icap + 0.5f*(dx*(-Ipap+Inap) + dx*dx*(2*Ipap-5*Icap+4*Inap-Iaap) + dx*dx*dx*(-Ipap+3*Icap-3*Inap+Iaap)),
12501         Ip = Icp + 0.5f*(dy*(-Ipp+Inp) + dy*dy*(2*Ipp-5*Icp+4*Inp-Iap) + dy*dy*dy*(-Ipp+3*Icp-3*Inp+Iap)),
12502         Ippc = (Tfloat)atXYZ(px,py,z,c,out_value), Icpc = (Tfloat)atXYZ(x,py,z,c,out_value),
12503         Inpc = (Tfloat)atXYZ(nx,py,z,c,out_value), Iapc = (Tfloat)atXYZ(ax,py,z,c,out_value),
12504         Ipc = Icpc + 0.5f*(dx*(-Ippc+Inpc) + dx*dx*(2*Ippc-5*Icpc+4*Inpc-Iapc) + dx*dx*dx*(-Ippc+3*Icpc-3*Inpc+Iapc)),
12505         Ipcc = (Tfloat)atXYZ(px,y,z,c,out_value),  Iccc = (Tfloat)atXYZ(x, y,z,c,out_value),
12506         Incc = (Tfloat)atXYZ(nx,y,z,c,out_value),  Iacc = (Tfloat)atXYZ(ax,y,z,c,out_value),
12507         Icc = Iccc + 0.5f*(dx*(-Ipcc+Incc) + dx*dx*(2*Ipcc-5*Iccc+4*Incc-Iacc) + dx*dx*dx*(-Ipcc+3*Iccc-3*Incc+Iacc)),
12508         Ipnc = (Tfloat)atXYZ(px,ny,z,c,out_value), Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value),
12509         Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value), Ianc = (Tfloat)atXYZ(ax,ny,z,c,out_value),
12510         Inc = Icnc + 0.5f*(dx*(-Ipnc+Innc) + dx*dx*(2*Ipnc-5*Icnc+4*Innc-Ianc) + dx*dx*dx*(-Ipnc+3*Icnc-3*Innc+Ianc)),
12511         Ipac = (Tfloat)atXYZ(px,ay,z,c,out_value), Icac = (Tfloat)atXYZ(x,ay,z,c,out_value),
12512         Inac = (Tfloat)atXYZ(nx,ay,z,c,out_value), Iaac = (Tfloat)atXYZ(ax,ay,z,c,out_value),
12513         Iac = Icac + 0.5f*(dx*(-Ipac+Inac) + dx*dx*(2*Ipac-5*Icac+4*Inac-Iaac) + dx*dx*dx*(-Ipac+3*Icac-3*Inac+Iaac)),
12514         Ic = Icc + 0.5f*(dy*(-Ipc+Inc) + dy*dy*(2*Ipc-5*Icc+4*Inc-Iac) + dy*dy*dy*(-Ipc+3*Icc-3*Inc+Iac)),
12515         Ippn = (Tfloat)atXYZ(px,py,nz,c,out_value), Icpn = (Tfloat)atXYZ(x,py,nz,c,out_value),
12516         Inpn = (Tfloat)atXYZ(nx,py,nz,c,out_value), Iapn = (Tfloat)atXYZ(ax,py,nz,c,out_value),
12517         Ipn = Icpn + 0.5f*(dx*(-Ippn+Inpn) + dx*dx*(2*Ippn-5*Icpn+4*Inpn-Iapn) + dx*dx*dx*(-Ippn+3*Icpn-3*Inpn+Iapn)),
12518         Ipcn = (Tfloat)atXYZ(px,y,nz,c,out_value),  Iccn = (Tfloat)atXYZ(x, y,nz,c,out_value),
12519         Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value),  Iacn = (Tfloat)atXYZ(ax,y,nz,c,out_value),
12520         Icn = Iccn + 0.5f*(dx*(-Ipcn+Incn) + dx*dx*(2*Ipcn-5*Iccn+4*Incn-Iacn) + dx*dx*dx*(-Ipcn+3*Iccn-3*Incn+Iacn)),
12521         Ipnn = (Tfloat)atXYZ(px,ny,nz,c,out_value), Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value),
12522         Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value), Iann = (Tfloat)atXYZ(ax,ny,nz,c,out_value),
12523         Inn = Icnn + 0.5f*(dx*(-Ipnn+Innn) + dx*dx*(2*Ipnn-5*Icnn+4*Innn-Iann) + dx*dx*dx*(-Ipnn+3*Icnn-3*Innn+Iann)),
12524         Ipan = (Tfloat)atXYZ(px,ay,nz,c,out_value), Ican = (Tfloat)atXYZ(x,ay,nz,c,out_value),
12525         Inan = (Tfloat)atXYZ(nx,ay,nz,c,out_value), Iaan = (Tfloat)atXYZ(ax,ay,nz,c,out_value),
12526         Ian = Ican + 0.5f*(dx*(-Ipan+Inan) + dx*dx*(2*Ipan-5*Ican+4*Inan-Iaan) + dx*dx*dx*(-Ipan+3*Ican-3*Inan+Iaan)),
12527         In = Icn + 0.5f*(dy*(-Ipn+Inn) + dy*dy*(2*Ipn-5*Icn+4*Inn-Ian) + dy*dy*dy*(-Ipn+3*Icn-3*Inn+Ian)),
12528         Ippa = (Tfloat)atXYZ(px,py,az,c,out_value), Icpa = (Tfloat)atXYZ(x,py,az,c,out_value),
12529         Inpa = (Tfloat)atXYZ(nx,py,az,c,out_value), Iapa = (Tfloat)atXYZ(ax,py,az,c,out_value),
12530         Ipa = Icpa + 0.5f*(dx*(-Ippa+Inpa) + dx*dx*(2*Ippa-5*Icpa+4*Inpa-Iapa) + dx*dx*dx*(-Ippa+3*Icpa-3*Inpa+Iapa)),
12531         Ipca = (Tfloat)atXYZ(px,y,az,c,out_value),  Icca = (Tfloat)atXYZ(x, y,az,c,out_value),
12532         Inca = (Tfloat)atXYZ(nx,y,az,c,out_value),  Iaca = (Tfloat)atXYZ(ax,y,az,c,out_value),
12533         Ica = Icca + 0.5f*(dx*(-Ipca+Inca) + dx*dx*(2*Ipca-5*Icca+4*Inca-Iaca) + dx*dx*dx*(-Ipca+3*Icca-3*Inca+Iaca)),
12534         Ipna = (Tfloat)atXYZ(px,ny,az,c,out_value), Icna = (Tfloat)atXYZ(x,ny,az,c,out_value),
12535         Inna = (Tfloat)atXYZ(nx,ny,az,c,out_value), Iana = (Tfloat)atXYZ(ax,ny,az,c,out_value),
12536         Ina = Icna + 0.5f*(dx*(-Ipna+Inna) + dx*dx*(2*Ipna-5*Icna+4*Inna-Iana) + dx*dx*dx*(-Ipna+3*Icna-3*Inna+Iana)),
12537         Ipaa = (Tfloat)atXYZ(px,ay,az,c,out_value), Icaa = (Tfloat)atXYZ(x,ay,az,c,out_value),
12538         Inaa = (Tfloat)atXYZ(nx,ay,az,c,out_value), Iaaa = (Tfloat)atXYZ(ax,ay,az,c,out_value),
12539         Iaa = Icaa + 0.5f*(dx*(-Ipaa+Inaa) + dx*dx*(2*Ipaa-5*Icaa+4*Inaa-Iaaa) + dx*dx*dx*(-Ipaa+3*Icaa-3*Inaa+Iaaa)),
12540         Ia = Ica + 0.5f*(dy*(-Ipa+Ina) + dy*dy*(2*Ipa-5*Ica+4*Ina-Iaa) + dy*dy*dy*(-Ipa+3*Ica-3*Ina+Iaa));
12541       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));
12542     }
12543 
12544     //! Return damped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates.
12545     /**
12546        Similar to cubic_atXYZ(float,float,float,int,const T) const, except that you can specify the authorized minimum and maximum of the returned value.
12547     **/
12548     Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T out_value,
12549                        const Tfloat min_value, const Tfloat max_value) const {
12550       const Tfloat val = cubic_atXYZ(fx,fy,fz,c,out_value);
12551       return val<min_value?min_value:val>max_value?max_value:val;
12552     }
12553 
12554     //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y and Z-coordinates.
12555     /**
12556        Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking
12557        are achieved both for X,Y and Z-coordinates.
12558        \note
12559        - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _cubic_atXYZ(float,float,float,int).
12560     **/
12561     Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const {
12562       if (is_empty())
12563         throw CImgInstanceException(_cimg_instance
12564                                     "cubic_atXYZ() : Empty instance.",
12565                                     cimg_instance);
12566       return _cubic_atXYZ(fx,fy,fz,c);
12567     }
12568 
12569     Tfloat _cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const {
12570       const float
12571         nfx = fx<0?0:(fx>_width-1?_width-1:fx),
12572         nfy = fy<0?0:(fy>_height-1?_height-1:fy),
12573         nfz = fz<0?0:(fz>_depth-1?_depth-1:fz);
12574       const int x = (int)nfx, y = (int)nfy, z = (int)nfz;
12575       const float dx = nfx - x, dy = nfy - y, dz = nfz - z;
12576       const int
12577         px = x-1<0?0:x-1, nx = dx>0?x+1:x, ax = x+2>=width()?width()-1:x+2,
12578         py = y-1<0?0:y-1, ny = dy>0?y+1:y, ay = y+2>=height()?height()-1:y+2,
12579         pz = z-1<0?0:z-1, nz = dz>0?z+1:z, az = z+2>=depth()?depth()-1:z+2;
12580       const Tfloat
12581         Ippp = (Tfloat)(*this)(px,py,pz,c), Icpp = (Tfloat)(*this)(x,py,pz,c),
12582         Inpp = (Tfloat)(*this)(nx,py,pz,c), Iapp = (Tfloat)(*this)(ax,py,pz,c),
12583         Ipp = Icpp + 0.5f*(dx*(-Ippp+Inpp) + dx*dx*(2*Ippp-5*Icpp+4*Inpp-Iapp) + dx*dx*dx*(-Ippp+3*Icpp-3*Inpp+Iapp)),
12584         Ipcp = (Tfloat)(*this)(px,y,pz,c),  Iccp = (Tfloat)(*this)(x, y,pz,c),
12585         Incp = (Tfloat)(*this)(nx,y,pz,c),  Iacp = (Tfloat)(*this)(ax,y,pz,c),
12586         Icp = Iccp + 0.5f*(dx*(-Ipcp+Incp) + dx*dx*(2*Ipcp-5*Iccp+4*Incp-Iacp) + dx*dx*dx*(-Ipcp+3*Iccp-3*Incp+Iacp)),
12587         Ipnp = (Tfloat)(*this)(px,ny,pz,c), Icnp = (Tfloat)(*this)(x,ny,pz,c),
12588         Innp = (Tfloat)(*this)(nx,ny,pz,c), Ianp = (Tfloat)(*this)(ax,ny,pz,c),
12589         Inp = Icnp + 0.5f*(dx*(-Ipnp+Innp) + dx*dx*(2*Ipnp-5*Icnp+4*Innp-Ianp) + dx*dx*dx*(-Ipnp+3*Icnp-3*Innp+Ianp)),
12590         Ipap = (Tfloat)(*this)(px,ay,pz,c), Icap = (Tfloat)(*this)(x,ay,pz,c),
12591         Inap = (Tfloat)(*this)(nx,ay,pz,c), Iaap = (Tfloat)(*this)(ax,ay,pz,c),
12592         Iap = Icap + 0.5f*(dx*(-Ipap+Inap) + dx*dx*(2*Ipap-5*Icap+4*Inap-Iaap) + dx*dx*dx*(-Ipap+3*Icap-3*Inap+Iaap)),
12593         Ip = Icp + 0.5f*(dy*(-Ipp+Inp) + dy*dy*(2*Ipp-5*Icp+4*Inp-Iap) + dy*dy*dy*(-Ipp+3*Icp-3*Inp+Iap)),
12594         Ippc = (Tfloat)(*this)(px,py,z,c), Icpc = (Tfloat)(*this)(x,py,z,c),
12595         Inpc = (Tfloat)(*this)(nx,py,z,c), Iapc = (Tfloat)(*this)(ax,py,z,c),
12596         Ipc = Icpc + 0.5f*(dx*(-Ippc+Inpc) + dx*dx*(2*Ippc-5*Icpc+4*Inpc-Iapc) + dx*dx*dx*(-Ippc+3*Icpc-3*Inpc+Iapc)),
12597         Ipcc = (Tfloat)(*this)(px,y,z,c),  Iccc = (Tfloat)(*this)(x, y,z,c),
12598         Incc = (Tfloat)(*this)(nx,y,z,c),  Iacc = (Tfloat)(*this)(ax,y,z,c),
12599         Icc = Iccc + 0.5f*(dx*(-Ipcc+Incc) + dx*dx*(2*Ipcc-5*Iccc+4*Incc-Iacc) + dx*dx*dx*(-Ipcc+3*Iccc-3*Incc+Iacc)),
12600         Ipnc = (Tfloat)(*this)(px,ny,z,c), Icnc = (Tfloat)(*this)(x,ny,z,c),
12601         Innc = (Tfloat)(*this)(nx,ny,z,c), Ianc = (Tfloat)(*this)(ax,ny,z,c),
12602         Inc = Icnc + 0.5f*(dx*(-Ipnc+Innc) + dx*dx*(2*Ipnc-5*Icnc+4*Innc-Ianc) + dx*dx*dx*(-Ipnc+3*Icnc-3*Innc+Ianc)),
12603         Ipac = (Tfloat)(*this)(px,ay,z,c), Icac = (Tfloat)(*this)(x,ay,z,c),
12604         Inac = (Tfloat)(*this)(nx,ay,z,c), Iaac = (Tfloat)(*this)(ax,ay,z,c),
12605         Iac = Icac + 0.5f*(dx*(-Ipac+Inac) + dx*dx*(2*Ipac-5*Icac+4*Inac-Iaac) + dx*dx*dx*(-Ipac+3*Icac-3*Inac+Iaac)),
12606         Ic = Icc + 0.5f*(dy*(-Ipc+Inc) + dy*dy*(2*Ipc-5*Icc+4*Inc-Iac) + dy*dy*dy*(-Ipc+3*Icc-3*Inc+Iac)),
12607         Ippn = (Tfloat)(*this)(px,py,nz,c), Icpn = (Tfloat)(*this)(x,py,nz,c),
12608         Inpn = (Tfloat)(*this)(nx,py,nz,c), Iapn = (Tfloat)(*this)(ax,py,nz,c),
12609         Ipn = Icpn + 0.5f*(dx*(-Ippn+Inpn) + dx*dx*(2*Ippn-5*Icpn+4*Inpn-Iapn) + dx*dx*dx*(-Ippn+3*Icpn-3*Inpn+Iapn)),
12610         Ipcn = (Tfloat)(*this)(px,y,nz,c),  Iccn = (Tfloat)(*this)(x, y,nz,c),
12611         Incn = (Tfloat)(*this)(nx,y,nz,c),  Iacn = (Tfloat)(*this)(ax,y,nz,c),
12612         Icn = Iccn + 0.5f*(dx*(-Ipcn+Incn) + dx*dx*(2*Ipcn-5*Iccn+4*Incn-Iacn) + dx*dx*dx*(-Ipcn+3*Iccn-3*Incn+Iacn)),
12613         Ipnn = (Tfloat)(*this)(px,ny,nz,c), Icnn = (Tfloat)(*this)(x,ny,nz,c),
12614         Innn = (Tfloat)(*this)(nx,ny,nz,c), Iann = (Tfloat)(*this)(ax,ny,nz,c),
12615         Inn = Icnn + 0.5f*(dx*(-Ipnn+Innn) + dx*dx*(2*Ipnn-5*Icnn+4*Innn-Iann) + dx*dx*dx*(-Ipnn+3*Icnn-3*Innn+Iann)),
12616         Ipan = (Tfloat)(*this)(px,ay,nz,c), Ican = (Tfloat)(*this)(x,ay,nz,c),
12617         Inan = (Tfloat)(*this)(nx,ay,nz,c), Iaan = (Tfloat)(*this)(ax,ay,nz,c),
12618         Ian = Ican + 0.5f*(dx*(-Ipan+Inan) + dx*dx*(2*Ipan-5*Ican+4*Inan-Iaan) + dx*dx*dx*(-Ipan+3*Ican-3*Inan+Iaan)),
12619         In = Icn + 0.5f*(dy*(-Ipn+Inn) + dy*dy*(2*Ipn-5*Icn+4*Inn-Ian) + dy*dy*dy*(-Ipn+3*Icn-3*Inn+Ian)),
12620         Ippa = (Tfloat)(*this)(px,py,az,c), Icpa = (Tfloat)(*this)(x,py,az,c),
12621         Inpa = (Tfloat)(*this)(nx,py,az,c), Iapa = (Tfloat)(*this)(ax,py,az,c),
12622         Ipa = Icpa + 0.5f*(dx*(-Ippa+Inpa) + dx*dx*(2*Ippa-5*Icpa+4*Inpa-Iapa) + dx*dx*dx*(-Ippa+3*Icpa-3*Inpa+Iapa)),
12623         Ipca = (Tfloat)(*this)(px,y,az,c),  Icca = (Tfloat)(*this)(x, y,az,c),
12624         Inca = (Tfloat)(*this)(nx,y,az,c),  Iaca = (Tfloat)(*this)(ax,y,az,c),
12625         Ica = Icca + 0.5f*(dx*(-Ipca+Inca) + dx*dx*(2*Ipca-5*Icca+4*Inca-Iaca) + dx*dx*dx*(-Ipca+3*Icca-3*Inca+Iaca)),
12626         Ipna = (Tfloat)(*this)(px,ny,az,c), Icna = (Tfloat)(*this)(x,ny,az,c),
12627         Inna = (Tfloat)(*this)(nx,ny,az,c), Iana = (Tfloat)(*this)(ax,ny,az,c),
12628         Ina = Icna + 0.5f*(dx*(-Ipna+Inna) + dx*dx*(2*Ipna-5*Icna+4*Inna-Iana) + dx*dx*dx*(-Ipna+3*Icna-3*Inna+Iana)),
12629         Ipaa = (Tfloat)(*this)(px,ay,az,c), Icaa = (Tfloat)(*this)(x,ay,az,c),
12630         Inaa = (Tfloat)(*this)(nx,ay,az,c), Iaaa = (Tfloat)(*this)(ax,ay,az,c),
12631         Iaa = Icaa + 0.5f*(dx*(-Ipaa+Inaa) + dx*dx*(2*Ipaa-5*Icaa+4*Inaa-Iaaa) + dx*dx*dx*(-Ipaa+3*Icaa-3*Inaa+Iaaa)),
12632         Ia = Ica + 0.5f*(dy*(-Ipa+Ina) + dy*dy*(2*Ipa-5*Ica+4*Ina-Iaa) + dy*dy*dy*(-Ipa+3*Ica-3*Ina+Iaa));
12633       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));
12634     }
12635 
12636     //! Return damped pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y and Z-coordinates.
12637     /**
12638        Similar to cubic_atXYZ(float,float,float,int) const, except that you can specify the authorized minimum and maximum of the returned value.
12639     **/
12640     Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c,
12641                        const Tfloat min_value, const Tfloat max_value) const {
12642       const Tfloat val = cubic_atXYZ(fx,fy,fz,c);
12643       return val<min_value?min_value:val>max_value?max_value:val;
12644     }
12645 
12646     Tfloat _cubic_atXYZ(const float fx, const float fy, const float fz, const int c,
12647                         const Tfloat min_value, const Tfloat max_value) const {
12648       const Tfloat val = _cubic_atXYZ(fx,fy,fz,c);
12649       return val<min_value?min_value:val>max_value?max_value:val;
12650     }
12651 
12652     //! Set pixel value, using linear interpolation for the X and Y-coordinates.
12653     /**
12654        Set pixel value at specified coordinates (\c fx,\c fy,\c z,\c c) in the image instance, in a way that the value is spread
12655        amongst several neighbors if the pixel coordinates are indeed float-valued.
12656        \param value Pixel value to set.
12657        \param fx X-coordinate of the pixel value (float-valued).
12658        \param fy Y-coordinate of the pixel value (float-valued).
12659        \param z Z-coordinate of the pixel value.
12660        \param c C-coordinate of the pixel value.
12661        \param is_added Tells if the pixel value is added to (\c true), or simply replace (\c false) the current image pixel(s).
12662        \return A reference to the current image instance.
12663        \note
12664        - If specified coordinates are outside image bounds, no operations are performed.
12665     **/
12666     CImg<T>& set_linear_atXY(const T& value, const float fx, const float fy=0, const int z=0, const int c=0,
12667                              const bool is_added=false) {
12668       const int
12669         x = (int)fx - (fx>=0?0:1), nx = x + 1,
12670         y = (int)fy - (fy>=0?0:1), ny = y + 1;
12671       const float
12672         dx = fx - x,
12673         dy = fy - y;
12674       if (z>=0 && z<depth() && c>=0 && c<spectrum()) {
12675         if (y>=0 && y<height()) {
12676           if (x>=0 && x<width()) {
12677             const float w1 = (1-dx)*(1-dy), w2 = is_added?1:(1-w1);
12678             (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
12679           }
12680           if (nx>=0 && nx<width()) {
12681             const float w1 = dx*(1-dy), w2 = is_added?1:(1-w1);
12682             (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
12683           }
12684         }
12685         if (ny>=0 && ny<height()) {
12686           if (x>=0 && x<width()) {
12687             const float w1 = (1-dx)*dy, w2 = is_added?1:(1-w1);
12688             (*this)(x,ny,z,c) = (T)(w1*value + w2*(*this)(x,ny,z,c));
12689           }
12690           if (nx>=0 && nx<width()) {
12691             const float w1 = dx*dy, w2 = is_added?1:(1-w1);
12692             (*this)(nx,ny,z,c) = (T)(w1*value + w2*(*this)(nx,ny,z,c));
12693           }
12694         }
12695       }
12696       return *this;
12697     }
12698 
12699     //! Set pixel value, using linear interpolation for the X,Y and Z-coordinates.
12700     /**
12701        Similar to set_linear_atXY(const T&,float,float,int,int,bool), except that the linear interpolation
12702        is achieved both for X,Y and Z-coordinates.
12703     **/
12704     CImg<T>& set_linear_atXYZ(const T& value, const float fx, const float fy=0, const float fz=0, const int c=0,
12705                               const bool is_added=false) {
12706       const int
12707         x = (int)fx - (fx>=0?0:1), nx = x + 1,
12708         y = (int)fy - (fy>=0?0:1), ny = y + 1,
12709         z = (int)fz - (fz>=0?0:1), nz = z + 1;
12710       const float
12711         dx = fx - x,
12712         dy = fy - y,
12713         dz = fz - z;
12714       if (c>=0 && c<spectrum()) {
12715         if (z>=0 && z<depth()) {
12716           if (y>=0 && y<height()) {
12717             if (x>=0 && x<width()) {
12718               const float w1 = (1-dx)*(1-dy)*(1-dz), w2 = is_added?1:(1-w1);
12719               (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
12720             }
12721             if (nx>=0 && nx<width()) {
12722               const float w1 = dx*(1-dy)*(1-dz), w2 = is_added?1:(1-w1);
12723               (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
12724             }
12725           }
12726           if (ny>=0 && ny<height()) {
12727             if (x>=0 && x<width()) {
12728               const float w1 = (1-dx)*dy*(1-dz), w2 = is_added?1:(1-w1);
12729               (*this)(x,ny,z,c) = (T)(w1*value + w2*(*this)(x,ny,z,c));
12730             }
12731             if (nx>=0 && nx<width()) {
12732               const float w1 = dx*dy*(1-dz), w2 = is_added?1:(1-w1);
12733               (*this)(nx,ny,z,c) = (T)(w1*value + w2*(*this)(nx,ny,z,c));
12734             }
12735           }
12736         }
12737         if (nz>=0 && nz<depth()) {
12738           if (y>=0 && y<height()) {
12739             if (x>=0 && x<width()) {
12740               const float w1 = (1-dx)*(1-dy)*dz, w2 = is_added?1:(1-w1);
12741               (*this)(x,y,nz,c) = (T)(w1*value + w2*(*this)(x,y,nz,c));
12742             }
12743             if (nx>=0 && nx<width()) {
12744               const float w1 = dx*(1-dy)*dz, w2 = is_added?1:(1-w1);
12745               (*this)(nx,y,nz,c) = (T)(w1*value + w2*(*this)(nx,y,nz,c));
12746             }
12747           }
12748           if (ny>=0 && ny<height()) {
12749             if (x>=0 && x<width()) {
12750               const float w1 = (1-dx)*dy*dz, w2 = is_added?1:(1-w1);
12751               (*this)(x,ny,nz,c) = (T)(w1*value + w2*(*this)(x,ny,nz,c));
12752             }
12753             if (nx>=0 && nx<width()) {
12754               const float w1 = dx*dy*dz, w2 = is_added?1:(1-w1);
12755               (*this)(nx,ny,nz,c) = (T)(w1*value + w2*(*this)(nx,ny,nz,c));
12756             }
12757           }
12758         }
12759       }
12760       return *this;
12761     }
12762 
12763     //! Return a C-string containing a list of all values of the image instance.
12764     /**
12765        Return a new \c CImg<char> image whose buffer data() is a \c char* string describing the list of all pixel values
12766        of the image instance (written in base 10), separated by specified \c separator character.
12767        \param separator A \c char character which specifies the separator between values in the returned C-string.
12768        \param max_size Maximum size of the returned image.
12769        \note
12770        - The returned image is never empty.
12771        - For an empty image instance, the returned string is <tt>""</tt>.
12772        - If \c max_size is equal to \c 0, there are no limits on the size of the returned string.
12773        - Otherwise, if the maximum number of string characters is exceeded, the value string is cut off
12774          and terminated by character \c '\0'. In that case, the returned image size is <tt>max_size + 1</tt>.
12775     **/
12776     CImg<charT> value_string(const char separator=',', const unsigned int max_size=0) const {
12777       if (is_empty()) return CImg<charT>::string("");
12778       CImgList<charT> items;
12779       char s_item[256] = { 0 };
12780       const T *ptrs = _data;
12781       unsigned int string_size = 0;
12782       for (unsigned long off = 0, siz = (unsigned int)size(); off<siz && string_size<=max_size; ++off) {
12783         const unsigned int printed_size = 1U + cimg_snprintf(s_item,sizeof(s_item),cimg::type<T>::format(),cimg::type<T>::format(*(ptrs++)));
12784         CImg<charT> item(s_item,printed_size);
12785         item[printed_size-1] = separator;
12786         item.move_to(items);
12787         if (max_size) string_size+=printed_size;
12788       }
12789       CImg<charT> res;
12790       (items>'x').move_to(res);
12791       if (max_size && res._width>max_size) res.crop(0,max_size);
12792       res.back() = 0;
12793       return res;
12794     }
12795 
12796     //@}
12797     //-------------------------------------
12798     //
12799     //! \name Instance Checking
12800     //@{
12801     //-------------------------------------
12802 
12803     //! Test shared state of the pixel buffer.
12804     /**
12805        Return \c true if image instance has a shared memory buffer, and \c false otherwise.
12806        \note
12807        - A shared image do not own his pixel buffer data() and will not deallocate it on destruction.
12808        - Most of the time, a \c CImg<T> image instance will \e not be shared.
12809        - A shared image can only be obtained by a limited set of constructors and methods (see list below).
12810     **/
12811     bool is_shared() const {
12812       return _is_shared;
12813     }
12814 
12815     //! Test if image instance is empty.
12816     /**
12817        Return \c true, if image instance is empty, i.e. does \e not contain any pixel values, has dimensions \c 0 x \c 0 x \c 0 x \c 0
12818        and a pixel buffer pointer set to \c 0 (null pointer), and \c false otherwise.
12819     **/
12820     bool is_empty() const {
12821       return !(_data && _width && _height && _depth && _spectrum);
12822     }
12823 
12824     //! Test if image instance contains a 'inf' value.
12825     /**
12826        Return \c true, if image instance contains a 'inf' value, and \c false otherwise.
12827     **/
12828     bool is_inf() const {
12829       if (cimg::type<T>::is_float()) cimg_for(*this,p,T) if (cimg::type<T>::is_inf((float)*p)) return true;
12830       return false;
12831     }
12832 
12833     //! Test if image instance contains a 'nan' value.
12834     /**
12835        Return \c true, if image instance contains a 'nan' value, and \c false otherwise.
12836     **/
12837     bool is_nan() const {
12838       if (cimg::type<T>::is_float()) cimg_for(*this,p,T) if (cimg::type<T>::is_nan((float)*p)) return true;
12839       return false;
12840     }
12841 
12842     //! Test if image width is equal to specified value.
12843     bool is_sameX(const unsigned int size_x) const {
12844       return _width==size_x;
12845     }
12846 
12847     //! Test if image width is equal to specified value.
12848     template<typename t>
12849     bool is_sameX(const CImg<t>& img) const {
12850       return is_sameX(img._width);
12851     }
12852 
12853     //! Test if image width is equal to specified value.
12854     bool is_sameX(const CImgDisplay& disp) const {
12855       return is_sameX(disp._width);
12856     }
12857 
12858     //! Test if image height is equal to specified value.
12859     bool is_sameY(const unsigned int size_y) const {
12860       return _height==size_y;
12861     }
12862 
12863     //! Test if image height is equal to specified value.
12864     template<typename t>
12865     bool is_sameY(const CImg<t>& img) const {
12866       return is_sameY(img._height);
12867     }
12868 
12869     //! Test if image height is equal to specified value.
12870     bool is_sameY(const CImgDisplay& disp) const {
12871       return is_sameY(disp._height);
12872     }
12873 
12874     //! Test if image depth is equal to specified value.
12875     bool is_sameZ(const unsigned int size_z) const {
12876       return _depth==size_z;
12877     }
12878 
12879     //! Test if image depth is equal to specified value.
12880     template<typename t>
12881     bool is_sameZ(const CImg<t>& img) const {
12882       return is_sameZ(img._depth);
12883     }
12884 
12885     //! Test if image spectrum is equal to specified value.
12886     bool is_sameC(const unsigned int size_c) const {
12887       return _spectrum==size_c;
12888     }
12889 
12890     //! Test if image spectrum is equal to specified value.
12891     template<typename t>
12892     bool is_sameC(const CImg<t>& img) const {
12893       return is_sameC(img._spectrum);
12894     }
12895 
12896     //! Test if image width and height are equal to specified values.
12897     /**
12898        Test if is_sameX(unsigned int) const and is_sameY(unsigned int) const are both verified.
12899     **/
12900     bool is_sameXY(const unsigned int size_x, const unsigned int size_y) const {
12901       return _width==size_x && _height==size_y;
12902     }
12903 
12904     //! Test if image width and height are the same as that of another image.
12905     /**
12906        Test if is_sameX(const CImg<t>&) const and is_sameY(const CImg<t>&) const are both verified.
12907     **/
12908     template<typename t>
12909     bool is_sameXY(const CImg<t>& img) const {
12910       return is_sameXY(img._width,img._height);
12911     }
12912 
12913     //! Test if image width and height are the same as that of an existing display window.
12914     /**
12915        Test if is_sameX(const CImgDisplay&) const and is_sameY(const CImgDisplay&) const are both verified.
12916     **/
12917     bool is_sameXY(const CImgDisplay& disp) const {
12918       return is_sameXY(disp._width,disp._height);
12919     }
12920 
12921     //! Test if image width and depth are equal to specified values.
12922     /**
12923        Test if is_sameX(unsigned int) const and is_sameZ(unsigned int) const are both verified.
12924     **/
12925     bool is_sameXZ(const unsigned int size_x, const unsigned int size_z) const {
12926       return _width==size_x && _depth==size_z;
12927     }
12928 
12929     //! Test if image width and depth are the same as that of another image.
12930     /**
12931        Test if is_sameX(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified.
12932     **/
12933     template<typename t>
12934     bool is_sameXZ(const CImg<t>& img) const {
12935       return is_sameXZ(img._width,img._depth);
12936     }
12937 
12938     //! Test if image width and spectrum are equal to specified values.
12939     /**
12940        Test if is_sameX(unsigned int) const and is_sameC(unsigned int) const are both verified.
12941     **/
12942     bool is_sameXC(const unsigned int size_x, const unsigned int size_c) const {
12943       return _width==size_x && _spectrum==size_c;
12944     }
12945 
12946     //! Test if image width and spectrum are the same as that of another image.
12947     /**
12948        Test if is_sameX(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
12949     **/
12950     template<typename t>
12951     bool is_sameXC(const CImg<t>& img) const {
12952       return is_sameXC(img._width,img._spectrum);
12953     }
12954 
12955     //! Test if image height and depth are equal to specified values.
12956     /**
12957        Test if is_sameY(unsigned int) const and is_sameZ(unsigned int) const are both verified.
12958     **/
12959     bool is_sameYZ(const unsigned int size_y, const unsigned int size_z) const {
12960       return _height==size_y && _depth==size_z;
12961     }
12962 
12963     //! Test if image height and depth are the same as that of another image.
12964     /**
12965        Test if is_sameY(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified.
12966     **/
12967     template<typename t>
12968     bool is_sameYZ(const CImg<t>& img) const {
12969       return is_sameYZ(img._height,img._depth);
12970     }
12971 
12972     //! Test if image height and spectrum are equal to specified values.
12973     /**
12974        Test if is_sameY(unsigned int) const and is_sameC(unsigned int) const are both verified.
12975     **/
12976     bool is_sameYC(const unsigned int size_y, const unsigned int size_c) const {
12977       return _height==size_y && _spectrum==size_c;
12978     }
12979 
12980     //! Test if image height and spectrum are the same as that of another image.
12981     /**
12982        Test if is_sameY(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
12983     **/
12984     template<typename t>
12985     bool is_sameYC(const CImg<t>& img) const {
12986       return is_sameYC(img._height,img._spectrum);
12987     }
12988 
12989     //! Test if image depth and spectrum are equal to specified values.
12990     /**
12991        Test if is_sameZ(unsigned int) const and is_sameC(unsigned int) const are both verified.
12992     **/
12993     bool is_sameZC(const unsigned int size_z, const unsigned int size_c) const {
12994       return _depth==size_z && _spectrum==size_c;
12995     }
12996 
12997     //! Test if image depth and spectrum are the same as that of another image.
12998     /**
12999        Test if is_sameZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
13000     **/
13001     template<typename t>
13002     bool is_sameZC(const CImg<t>& img) const {
13003       return is_sameZC(img._depth,img._spectrum);
13004     }
13005 
13006     //! Test if image width, height and depth are equal to specified values.
13007     /**
13008        Test if is_sameXY(unsigned int,unsigned int) const and is_sameZ(unsigned int) const are both verified.
13009     **/
13010     bool is_sameXYZ(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z) const {
13011       return is_sameXY(size_x,size_y) && _depth==size_z;
13012     }
13013 
13014     //! Test if image width, height and depth are the same as that of another image.
13015     /**
13016        Test if is_sameXY(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified.
13017     **/
13018     template<typename t>
13019     bool is_sameXYZ(const CImg<t>& img) const {
13020       return is_sameXYZ(img._width,img._height,img._depth);
13021     }
13022 
13023     //! Test if image width, height and spectrum are equal to specified values.
13024     /**
13025        Test if is_sameXY(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
13026     **/
13027     bool is_sameXYC(const unsigned int size_x, const unsigned int size_y, const unsigned int size_c) const {
13028       return is_sameXY(size_x,size_y) && _spectrum==size_c;
13029     }
13030 
13031     //! Test if image width, height and spectrum are the same as that of another image.
13032     /**
13033        Test if is_sameXY(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
13034     **/
13035     template<typename t>
13036     bool is_sameXYC(const CImg<t>& img) const {
13037       return is_sameXYC(img._width,img._height,img._spectrum);
13038     }
13039 
13040     //! Test if image width, depth and spectrum are equal to specified values.
13041     /**
13042        Test if is_sameXZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
13043     **/
13044     bool is_sameXZC(const unsigned int size_x, const unsigned int size_z, const unsigned int size_c) const {
13045       return is_sameXZ(size_x,size_z) && _spectrum==size_c;
13046     }
13047 
13048     //! Test if image width, depth and spectrum are the same as that of another image.
13049     /**
13050        Test if is_sameXZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
13051     **/
13052     template<typename t>
13053     bool is_sameXZC(const CImg<t>& img) const {
13054       return is_sameXZC(img._width,img._depth,img._spectrum);
13055     }
13056 
13057     //! Test if image height, depth and spectrum are equal to specified values.
13058     /**
13059        Test if is_sameYZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
13060     **/
13061     bool is_sameYZC(const unsigned int size_y, const unsigned int size_z, const unsigned int size_c) const {
13062       return is_sameYZ(size_y,size_z) && _spectrum==size_c;
13063     }
13064 
13065     //! Test if image height, depth and spectrum are the same as that of another image.
13066     /**
13067        Test if is_sameYZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
13068     **/
13069     template<typename t>
13070     bool is_sameYZC(const CImg<t>& img) const {
13071       return is_sameYZC(img._height,img._depth,img._spectrum);
13072     }
13073 
13074     //! Test if image width, height, depth and spectrum are equal to specified values.
13075     /**
13076        Test if is_sameXYZ(unsigned int,unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
13077     **/
13078     bool is_sameXYZC(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c) const {
13079       return is_sameXYZ(size_x,size_y,size_z) && _spectrum==size_c;
13080     }
13081 
13082     //! Test if image width, height, depth and spectrum are the same as that of another image.
13083     /**
13084        Test if is_sameXYZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
13085     **/
13086     template<typename t>
13087     bool is_sameXYZC(const CImg<t>& img) const {
13088       return is_sameXYZC(img._width,img._height,img._depth,img._spectrum);
13089     }
13090 
13091     //! Test if specified coordinates are inside image bounds.
13092     /**
13093        Return \c true if pixel located at (\c x,\c y,\c z,\c c) is inside bounds of the image instance, and \c false otherwise.
13094        \param x X-coordinate of the pixel value.
13095        \param y Y-coordinate of the pixel value.
13096        \param z Z-coordinate of the pixel value.
13097        \param c C-coordinate of the pixel value.
13098        \note
13099        - Return \c true only if all these conditions are verified :
13100          - The image instance is \e not empty.
13101          - <tt>0<=x<=\ref width()-1</tt>.
13102          - <tt>0<=y<=\ref height()-1</tt>.
13103          - <tt>0<=z<=\ref depth()-1</tt>.
13104          - <tt>0<=c<=\ref spectrum()-1</tt>.
13105     **/
13106     bool containsXYZC(const int x, const int y=0, const int z=0, const int c=0) const {
13107       return !is_empty() && x>=0 && x<width() && y>=0 && y<height() && z>=0 && z<depth() && c>=0 && c<spectrum();
13108     }
13109 
13110     //! Test if pixel value is inside image bounds and get its X,Y,Z and C-coordinates.
13111     /**
13112        Return \c true, if specified reference refers to a pixel value inside bounds of the image instance, and \c false otherwise.
13113        \param pixel Reference to pixel value to test.
13114        \param[out] x X-coordinate of the pixel value, if test succeeds.
13115        \param[out] y Y-coordinate of the pixel value, if test succeeds.
13116        \param[out] z Z-coordinate of the pixel value, if test succeeds.
13117        \param[out] c C-coordinate of the pixel value, if test succeeds.
13118        \note
13119        - Useful to convert an offset to a buffer value into pixel value coordinates :
13120        \code
13121        const CImg<float> img(100,100,1,3);      // Construct a 100x100 RGB color image.
13122        const unsigned long offset = 1249;       // Offset to the pixel (49,12,0,0).
13123        unsigned int x,y,z,c;
13124        if (img.contains(img[offset],x,y,z,c)) { // Convert offset to (x,y,z,c) coordinates.
13125          std::printf("Offset %u refers to pixel located at (%u,%u,%u,%u).\n",
13126                      offset,x,y,z,c);
13127        }
13128        \endcode
13129     **/
13130     template<typename t>
13131     bool contains(const T& pixel, t& x, t& y, t& z, t& c) const {
13132       const unsigned long wh = (unsigned long)_width*_height, whd = wh*_depth, siz = whd*_spectrum;
13133       const T *const ppixel = &pixel;
13134       if (is_empty() || ppixel<_data || ppixel>=_data+siz) return false;
13135       unsigned long off = (unsigned long)(ppixel - _data);
13136       const unsigned long nc = off/whd;
13137       off%=whd;
13138       const unsigned long nz = off/wh;
13139       off%=wh;
13140       const unsigned long ny = off/_width, nx = off%_width;
13141       x = (t)nx; y = (t)ny; z = (t)nz; c = (t)nc;
13142       return true;
13143     }
13144 
13145     //! Test if pixel value is inside image bounds and get its X,Y and Z-coordinates.
13146     /**
13147        Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X,Y and Z-coordinates are set.
13148     **/
13149     template<typename t>
13150     bool contains(const T& pixel, t& x, t& y, t& z) const {
13151       const unsigned long wh = (unsigned long)_width*_height, whd = wh*_depth, siz = whd*_spectrum;
13152       const T *const ppixel = &pixel;
13153       if (is_empty() || ppixel<_data || ppixel>=_data+siz) return false;
13154       unsigned long off = ((unsigned long)(ppixel - _data))%whd;
13155       const unsigned long nz = off/wh;
13156       off%=wh;
13157       const unsigned long ny = off/_width, nx = off%_width;
13158       x = (t)nx; y = (t)ny; z = (t)nz;
13159       return true;
13160     }
13161 
13162     //! Test if pixel value is inside image bounds and get its X and Y-coordinates.
13163     /**
13164        Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X and Y-coordinates are set.
13165     **/
13166     template<typename t>
13167     bool contains(const T& pixel, t& x, t& y) const {
13168       const unsigned long wh = (unsigned long)_width*_height, siz = wh*_depth*_spectrum;
13169       const T *const ppixel = &pixel;
13170       if (is_empty() || ppixel<_data || ppixel>=_data+siz) return false;
13171       unsigned long off = ((unsigned int)(ppixel - _data))%wh;
13172       const unsigned long ny = off/_width, nx = off%_width;
13173       x = (t)nx; y = (t)ny;
13174       return true;
13175     }
13176 
13177     //! Test if pixel value is inside image bounds and get its X-coordinate.
13178     /**
13179        Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X-coordinate is set.
13180     **/
13181     template<typename t>
13182     bool contains(const T& pixel, t& x) const {
13183       const T *const ppixel = &pixel;
13184       if (is_empty() || ppixel<_data || ppixel>=_data+size()) return false;
13185       x = (t)(((unsigned long)(ppixel - _data))%_width);
13186       return true;
13187     }
13188 
13189     //! Test if pixel value is inside image bounds.
13190     /**
13191        Similar to contains(const T&,t&,t&,t&,t&) const, except that no pixel coordinates are set.
13192     **/
13193     bool contains(const T& pixel) const {
13194       const T *const ppixel = &pixel;
13195       return !is_empty() && ppixel>=_data && ppixel<_data + size();
13196     }
13197 
13198     //! Test if pixel buffers of instance and input images overlap.
13199     /**
13200        Return \c true, if pixel buffers attached to image instance and input image \c img overlap, and \c false otherwise.
13201        \param img Input image to compare with.
13202        \note
13203        - Buffer overlapping may happen when manipulating \e shared images.
13204        - If two image buffers overlap, operating on one of the image will probably modify the other one.
13205        - Most of the time, \c CImg<T> instances are \e non-shared and do not overlap between each others.
13206        \par Example
13207        \code
13208        const CImg<float>
13209          img1("reference.jpg"),             // Load RGB-color image.
13210          img2 = img1.get_shared_channel(1); // Get shared version of the green channel.
13211        if (img1.is_overlapped(img2)) {      // Test succeeds, 'img1' and 'img2' overlaps.
13212          std::printf("Buffers overlap !\n");
13213        }
13214        \endcode
13215     **/
13216     template<typename t>
13217     bool is_overlapped(const CImg<t>& img) const {
13218       const unsigned long csiz = size(), isiz = img.size();
13219       return !((void*)(_data + csiz)<=(void*)img._data || (void*)_data>=(void*)(img._data + isiz));
13220     }
13221 
13222     //! Test if the set {\c *this,\c primitives,\c colors,\c opacities} defines a valid 3d object.
13223     /**
13224        Return \c true is the 3d object represented by the set {\c *this,\c primitives,\c colors,\c opacities} defines a
13225        valid 3d object, and \c false otherwise. The vertex coordinates are defined by the instance image.
13226        \param primitives List of primitives of the 3d object.
13227        \param colors List of colors of the 3d object.
13228        \param opacities List (or image) of opacities of the 3d object.
13229        \param is_full_check Tells if full checking of the 3d object must be performed.
13230        \param[out] error_message C-string to contain the error message, if the test does not succeed.
13231        \note
13232        - Set \c is_full_checking to \c false to speed-up the 3d object checking. In this case, only the size of
13233          each 3d object component is checked.
13234        - Size of the string \c error_message should be at least 128-bytes long, to be able to contain the error message.
13235     **/
13236     template<typename tp, typename tc, typename to>
13237     bool is_object3d(const CImgList<tp>& primitives,
13238                      const CImgList<tc>& colors,
13239                      const to& opacities,
13240                      const bool is_full_check=true,
13241                      char *const error_message=0) const {
13242       if (error_message) *error_message = 0;
13243 
13244       // Check consistency for the particular case of an empty 3d object.
13245       if (is_empty()) {
13246         if (primitives || colors || opacities) {
13247           if (error_message) std::sprintf(error_message,
13248                                           "3d object has no vertices but %u primitives, %u colors and %lu opacities",
13249                                           primitives._width,colors._width,(unsigned long)opacities.size());
13250           return false;
13251         }
13252         return true;
13253       }
13254 
13255       // Check consistency of vertices.
13256       if (_height!=3 || _depth>1 || _spectrum>1) { // Check vertices dimensions.
13257         if (error_message) std::sprintf(error_message,
13258                                         "3d object (%u,%u) has invalid vertices dimensions (%u,%u,%u,%u)",
13259                                         _width,primitives._width,_width,_height,_depth,_spectrum);
13260         return false;
13261       }
13262       if (colors._width>primitives._width+1) {
13263         if (error_message) std::sprintf(error_message,
13264                                         "3d object (%u,%u) defines %u colors",
13265                                         _width,primitives._width,colors._width);
13266         return false;
13267       }
13268       if (opacities.size()>primitives._width) {
13269         if (error_message) std::sprintf(error_message,
13270                                         "3d object (%u,%u) defines %lu opacities",
13271                                         _width,primitives._width,(unsigned long)opacities.size());
13272         return false;
13273       }
13274       if (!is_full_check) return true;
13275 
13276       // Check consistency of primitives.
13277       cimglist_for(primitives,l) {
13278         const CImg<tp>& primitive = primitives[l];
13279         const unsigned long psiz = primitive.size();
13280         switch (psiz) {
13281         case 1 : { // Point.
13282           const unsigned int i0 = (unsigned int)primitive(0);
13283           if (i0>=_width) {
13284             if (error_message) std::sprintf(error_message,
13285                                             "3d object (%u,%u) refers to invalid vertex indice %u in point primitive %u",
13286                                             _width,primitives._width,i0,l);
13287             return false;
13288           }
13289         } break;
13290         case 5 : { // Sphere.
13291           const unsigned int
13292             i0 = (unsigned int)primitive(0),
13293             i1 = (unsigned int)primitive(1);
13294           if (i0>=_width || i1>=_width) {
13295             if (error_message) std::sprintf(error_message,
13296                                             "3d object (%u,%u) refers to invalid vertex indices (%u,%u) in sphere primitive %u",
13297                                             _width,primitives._width,i0,i1,l);
13298             return false;
13299           }
13300         } break;
13301         case 2 : // Segment.
13302         case 6 : {
13303           const unsigned int
13304             i0 = (unsigned int)primitive(0),
13305             i1 = (unsigned int)primitive(1);
13306           if (i0>=_width || i1>=_width) {
13307             if (error_message) std::sprintf(error_message,
13308                                             "3d object (%u,%u) refers to invalid vertex indices (%u,%u) in segment primitive %u",
13309                                             _width,primitives._width,i0,i1,l);
13310             return false;
13311           }
13312         } break;
13313         case 3 : // Triangle.
13314         case 9 : {
13315           const unsigned int
13316             i0 = (unsigned int)primitive(0),
13317             i1 = (unsigned int)primitive(1),
13318             i2 = (unsigned int)primitive(2);
13319           if (i0>=_width || i1>=_width || i2>=_width) {
13320             if (error_message) std::sprintf(error_message,
13321                                             "3d object (%u,%u) refers to invalid vertex indices (%u,%u,%u) in triangle primitive %u",
13322                                             _width,primitives._width,i0,i1,i2,l);
13323             return false;
13324           }
13325         } break;
13326         case 4 : // Quadrangle.
13327         case 12 : {
13328           const unsigned int
13329             i0 = (unsigned int)primitive(0),
13330             i1 = (unsigned int)primitive(1),
13331             i2 = (unsigned int)primitive(2),
13332             i3 = (unsigned int)primitive(3);
13333           if (i0>=_width || i1>=_width || i2>=_width || i3>=_width) {
13334             if (error_message) std::sprintf(error_message,
13335                                             "3d object (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in quadrangle primitive %u",
13336                                             _width,primitives._width,i0,i1,i2,i3,l);
13337             return false;
13338           }
13339         } break;
13340         default :
13341           if (error_message) std::sprintf(error_message,
13342                                           "3d object has invalid primitive %u of size %u",
13343                                           l,(unsigned int)psiz);
13344           return false;
13345         }
13346       }
13347 
13348       // Check consistency of colors.
13349       cimglist_for(colors,c) {
13350         const CImg<tc>& color = colors[c];
13351         if (!color) {
13352           if (error_message) std::sprintf(error_message,
13353                                           "3d object has empty color for primitive %u",
13354                                           c);
13355           return false;
13356         }
13357       }
13358 
13359       // Check consistency of light texture.
13360       if (colors._width>primitives._width) {
13361         const CImg<tc> &light = colors.back();
13362         if (!light || light._depth>1) {
13363           if (error_message) std::sprintf(error_message,
13364                                           "3d object has invalid light texture (%u,%u,%u,%u)",
13365                                           light._width,light._height,light._depth,light._spectrum);
13366           return false;
13367         }
13368       }
13369 
13370       return true;
13371     }
13372 
13373     //! Test if image instance represents a valid serialization of a 3d object.
13374     /**
13375        Return \c true if the image instance represents a valid serialization of a 3d object, and \c false otherwise.
13376        \param is_full_check Tells if full checking of the instance must be performed.
13377        \param[out] error_message C-string to contain the error message, if the test does not succeed.
13378        \note
13379        - Set \c is_full_checking to \c false to speed-up the 3d object checking. In this case, only the size of
13380          each 3d object component is checked.
13381        - Size of the string \c error_message should be at least 128-bytes long, to be able to contain the error message.
13382     **/
13383     bool is_CImg3d(const bool is_full_check=true, char *const error_message=0) const {
13384       if (error_message) *error_message = 0;
13385 
13386       // Check instance dimension and header.
13387       if (_width!=1 || _height<8 || _depth!=1 || _spectrum!=1) {
13388         if (error_message) std::sprintf(error_message,
13389                                         "CImg3d has invalid dimensions (%u,%u,%u,%u)",
13390                                         _width,_height,_depth,_spectrum);
13391         return false;
13392       }
13393       const T *ptrs = _data, *const ptre = end();
13394       if (!_is_CImg3d(*(ptrs++),'C') || !_is_CImg3d(*(ptrs++),'I') || !_is_CImg3d(*(ptrs++),'m') ||
13395           !_is_CImg3d(*(ptrs++),'g') || !_is_CImg3d(*(ptrs++),'3') || !_is_CImg3d(*(ptrs++),'d')) {
13396         if (error_message) std::sprintf(error_message,
13397                                         "CImg3d header not found");
13398         return false;
13399       }
13400       const unsigned int
13401         nb_points = cimg::float2uint((float)*(ptrs++)),
13402         nb_primitives = cimg::float2uint((float)*(ptrs++));
13403 
13404       // Check consistency of vertex data.
13405       if (!nb_points) {
13406         if (nb_primitives) {
13407           if (error_message) std::sprintf(error_message,
13408                                           "CImg3d has no vertices but %u primitives",
13409                                           nb_primitives);
13410           return false;
13411         }
13412         if (ptrs!=ptre) {
13413           if (error_message) std::sprintf(error_message,
13414                                           "CImg3d (%u,%u) is empty but contains %u byte%s more than expected",
13415                                           nb_points,nb_primitives,(unsigned int)(ptre-ptrs),(ptre-ptrs)>1?"s":"");
13416           return false;
13417         }
13418         return true;
13419       }
13420       ptrs+=3*nb_points;
13421       if (ptrs>ptre) {
13422         if (error_message) std::sprintf(error_message,
13423                                         "CImg3d (%u,%u) has incomplete vertex data",
13424                                         nb_points,nb_primitives);
13425         return false;
13426       }
13427       if (!is_full_check) return true;
13428 
13429       // Check consistency of primitive data.
13430       if (ptrs==ptre) {
13431         if (error_message) std::sprintf(error_message,
13432                                         "CImg3d (%u,%u) has no primitive data",
13433                                         nb_points,nb_primitives);
13434         return false;
13435       }
13436       for (unsigned int p = 0; p<nb_primitives; ++p) {
13437         const unsigned int nb_inds = (unsigned int)*(ptrs++);
13438         switch (nb_inds) {
13439         case 1 : { // Point.
13440           const unsigned int i0 = cimg::float2uint((float)*(ptrs++));
13441           if (i0>=nb_points) {
13442             if (error_message) std::sprintf(error_message,
13443                                             "CImg3d (%u,%u) refers to invalid vertex indice %u in point primitive %u",
13444                                             nb_points,nb_primitives,i0,p);
13445             return false;
13446           }
13447         } break;
13448         case 5 : { // Sphere.
13449           const unsigned int
13450             i0 = cimg::float2uint((float)*(ptrs++)),
13451             i1 = cimg::float2uint((float)*(ptrs++));
13452           ptrs+=3;
13453           if (i0>=nb_points || i1>=nb_points) {
13454             if (error_message) std::sprintf(error_message,
13455                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in sphere primitive %u",
13456                                             nb_points,nb_primitives,i0,i1,p);
13457             return false;
13458           }
13459         } break;
13460         case 2 : case 6 : { // Segment.
13461           const unsigned int
13462             i0 = cimg::float2uint((float)*(ptrs++)),
13463             i1 = cimg::float2uint((float)*(ptrs++));
13464           if (nb_inds==6) ptrs+=4;
13465           if (i0>=nb_points || i1>=nb_points) {
13466             if (error_message) std::sprintf(error_message,
13467                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in segment primitive %u",
13468                                             nb_points,nb_primitives,i0,i1,p);
13469             return false;
13470           }
13471         } break;
13472         case 3 : case 9 : { // Triangle.
13473           const unsigned int
13474             i0 = cimg::float2uint((float)*(ptrs++)),
13475             i1 = cimg::float2uint((float)*(ptrs++)),
13476             i2 = cimg::float2uint((float)*(ptrs++));
13477           if (nb_inds==9) ptrs+=6;
13478           if (i0>=nb_points || i1>=nb_points || i2>=nb_points) {
13479             if (error_message) std::sprintf(error_message,
13480                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u) in triangle primitive %u",
13481                                             nb_points,nb_primitives,i0,i1,i2,p);
13482             return false;
13483           }
13484         } break;
13485         case 4 : case 12 : { // Quadrangle.
13486           const unsigned int
13487             i0 = cimg::float2uint((float)*(ptrs++)),
13488             i1 = cimg::float2uint((float)*(ptrs++)),
13489             i2 = cimg::float2uint((float)*(ptrs++)),
13490             i3 = cimg::float2uint((float)*(ptrs++));
13491           if (nb_inds==12) ptrs+=8;
13492           if (i0>=nb_points || i1>=nb_points || i2>=nb_points || i3>=nb_points) {
13493             if (error_message) std::sprintf(error_message,
13494                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in quadrangle primitive %u",
13495                                             nb_points,nb_primitives,i0,i1,i2,i3,p);
13496             return false;
13497           }
13498         } break;
13499         default :
13500           if (error_message) std::sprintf(error_message,
13501                                           "CImg3d (%u,%u) has invalid primitive %u of size %u",
13502                                           nb_points,nb_primitives,p,nb_inds);
13503           return false;
13504         }
13505         if (ptrs>ptre) {
13506           if (error_message) std::sprintf(error_message,
13507                                           "CImg3d (%u,%u) has incomplete primitive data for primitive %u",
13508                                           nb_points,nb_primitives,p);
13509           return false;
13510         }
13511       }
13512 
13513       // Check consistency of color data.
13514       if (ptrs==ptre) {
13515         if (error_message) std::sprintf(error_message,
13516                                         "CImg3d (%u,%u) has no color/texture data",
13517                                         nb_points,nb_primitives);
13518         return false;
13519       }
13520       for (unsigned int c = 0; c<nb_primitives; ++c) {
13521         if ((int)*(ptrs++)!=-128) ptrs+=2;
13522         else if ((ptrs+=3)<ptre) {
13523           const unsigned int w = (unsigned int)*(ptrs-3), h = (unsigned int)*(ptrs-2), s = (unsigned int)*(ptrs-1);
13524           if (!h && !s) {
13525             if (w>=c) {
13526               if (error_message) std::sprintf(error_message,
13527                                               "CImg3d (%u,%u) refers to invalid shared sprite/texture indice %u for primitive %u",
13528                                               nb_points,nb_primitives,w,c);
13529               return false;
13530             }
13531           } else ptrs+=w*h*s;
13532         }
13533         if (ptrs>ptre) {
13534           if (error_message) std::sprintf(error_message,
13535                                           "CImg3d (%u,%u) has incomplete color/texture data for primitive %u",
13536                                           nb_points,nb_primitives,c);
13537           return false;
13538         }
13539       }
13540 
13541       // Check consistency of opacity data.
13542       if (ptrs==ptre) {
13543         if (error_message) std::sprintf(error_message,
13544                                         "CImg3d (%u,%u) has no opacity data",
13545                                         nb_points,nb_primitives);
13546         return false;
13547       }
13548       for (unsigned int o = 0; o<nb_primitives; ++o) {
13549         if ((int)*(ptrs++)==-128 && (ptrs+=3)<ptre) {
13550           const unsigned int w = (unsigned int)*(ptrs-3), h = (unsigned int)*(ptrs-2), s = (unsigned int)*(ptrs-1);
13551           if (!h && !s) {
13552             if (w>=o) {
13553               if (error_message) std::sprintf(error_message,
13554                                               "CImg3d (%u,%u) refers to invalid shared opacity indice %u for primitive %u",
13555                                               nb_points,nb_primitives,w,o);
13556               return false;
13557             }
13558           } else ptrs+=w*h*s;
13559         }
13560         if (ptrs>ptre) {
13561           if (error_message) std::sprintf(error_message,
13562                                           "CImg3d (%u,%u) has incomplete opacity data for primitive %u",
13563                                           nb_points,nb_primitives,o);
13564           return false;
13565         }
13566       }
13567 
13568       // Check end of data.
13569       if (ptrs<ptre) {
13570         if (error_message) std::sprintf(error_message,
13571                                         "CImg3d (%u,%u) contains %u byte%s more than expected",
13572                                         nb_points,nb_primitives,(unsigned int)(ptre-ptrs),(ptre-ptrs)>1?"s":"");
13573         return false;
13574       }
13575       return true;
13576     }
13577 
13578     static bool _is_CImg3d(const T val, const char c) {
13579       return val>=(T)c && val<(T)(c+1);
13580     }
13581 
13582     //@}
13583     //-------------------------------------
13584     //
13585     //! \name Mathematical Functions
13586     //@{
13587     //-------------------------------------
13588 
13589     // Define the math formula parser/compiler and evaluator.
13590     struct _cimg_math_parser {
13591       CImgList<charT> label;
13592       CImgList<uintT> code;
13593       CImg<uintT> level, opcode;
13594       CImg<doubleT> mem;
13595       CImg<charT> expr;
13596       const CImg<T>& reference;
13597       CImg<Tdouble> reference_stats;
13598       unsigned int mempos, result;
13599       const char *const calling_function;
13600 #define _cimg_mp_return(x) { *se = saved_char; return x; }
13601 #define _cimg_mp_opcode0(op) _cimg_mp_return(opcode0(op));
13602 #define _cimg_mp_opcode1(op,i1) _cimg_mp_return(opcode1(op,i1));
13603 #define _cimg_mp_opcode2(op,i1,i2) { const unsigned int _i1 = i1, _i2 = i2; _cimg_mp_return(opcode2(op,_i1,_i2)); }
13604 #define _cimg_mp_opcode3(op,i1,i2,i3) { const unsigned int _i1 = i1, _i2 = i2, _i3 = i3; _cimg_mp_return(opcode3(op,_i1,_i2,_i3)); }
13605 #define _cimg_mp_opcode6(op,i1,i2,i3,i4,i5,i6) { const unsigned int _i1 = i1, _i2 = i2, _i3 = i3, _i4 = i4, _i5 = i5, _i6 = i6; \
13606         _cimg_mp_return(opcode6(op,_i1,_i2,_i3,_i4,_i5,_i6)); }
13607 
13608       // Constructor - Destructor.
13609       _cimg_math_parser(const CImg<T>& img, const char *const expression, const char *const funcname=0):
13610         reference(img),calling_function(funcname?funcname:"cimg_math_parser") {
13611         unsigned int l = 0;
13612         if (expression) {
13613           l = (unsigned int)std::strlen(expression);
13614           expr.assign(expression,l+1);
13615           if (*expr._data) {
13616             char *d = expr._data;
13617             for (const char *s = expr._data; *s || (bool)(*d=0); ++s) if (*s!=' ') *(d++) = *s;
13618             l = (unsigned int)(d - expr._data);
13619           }
13620         }
13621         if (!l) throw CImgArgumentException("[_cimg_math_parser] "
13622                                             "CImg<%s>::%s() : Empty specified expression.",
13623                                             pixel_type(),calling_function);
13624 
13625         int lv = 0; // Count parenthesis level of expression.
13626         level.assign(l);
13627         unsigned int *pd = level._data;
13628         for (const char *ps = expr._data; *ps && lv>=0; ++ps) *(pd++) = (unsigned int)(*ps=='('?lv++:*ps==')'?--lv:lv);
13629         if (lv!=0) {
13630           throw CImgArgumentException("[_cimg_math_parser] "
13631                                       "CImg<%s>::%s() : Unbalanced parentheses in specified expression '%s'.",
13632                                       pixel_type(),calling_function,
13633                                       expr._data);
13634         }
13635         // Init constant values.
13636         mem.assign(512);
13637         label.assign(512);
13638         mem[0] = 0;
13639         mem[1] = 1;
13640         mem[2] = (double)reference._width;
13641         mem[3] = (double)reference._height;
13642         mem[4] = (double)reference._depth;
13643         mem[5] = (double)reference._spectrum;
13644         mem[6] = cimg::PI;
13645         mem[7] = std::exp(1.0); // Then [8] = x, [9] = y, [10] = z, [11] = c
13646         mempos = 12;
13647         result = compile(expr._data,expr._data+l); // Compile formula into a serie of opcodes.
13648       }
13649 
13650       // Insert code instructions.
13651       unsigned int opcode0(const char op) {
13652         if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
13653         const unsigned int pos = mempos++;
13654         CImg<uintT>::vector(op,pos).move_to(code);
13655         return pos;
13656       }
13657 
13658       unsigned int opcode1(const char op, const unsigned int arg1) {
13659         if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
13660         const unsigned int pos = mempos++;
13661         CImg<uintT>::vector(op,pos,arg1).move_to(code);
13662         return pos;
13663       }
13664 
13665       unsigned int opcode2(const char op, const unsigned int arg1, const unsigned int arg2) {
13666         if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
13667         const unsigned int pos = mempos++;
13668         CImg<uintT>::vector(op,pos,arg1,arg2).move_to(code);
13669         return pos;
13670       }
13671 
13672       unsigned int opcode3(const char op, const unsigned int arg1, const unsigned int arg2, const unsigned int arg3) {
13673         if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
13674         const unsigned int pos = mempos++;
13675         CImg<uintT>::vector(op,pos,arg1,arg2,arg3).move_to(code);
13676         return pos;
13677       }
13678 
13679       unsigned int opcode6(const char op, const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
13680                            const unsigned int arg4, const unsigned int arg5, const unsigned int arg6) {
13681         if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
13682         const unsigned int pos = mempos++;
13683         CImg<uintT>::vector(op,pos,arg1,arg2,arg3,arg4,arg5,arg6).move_to(code);
13684         return pos;
13685       }
13686 
13687       // Compilation procedure.
13688       unsigned int compile(char *const ss, char *const se) {
13689         if (!ss || se<=ss || !*ss) {
13690           throw CImgArgumentException("[_cimg_math_parser] "
13691                                       "CImg<%s>::%s() : Missing item in specified expression '%s'.",
13692                                       pixel_type(),calling_function,
13693                                       expr._data);
13694         }
13695         char
13696           *const se1 = se-1, *const se2 = se-2, *const se3 = se-3, *const se4 = se-4,
13697           *const ss1 = ss+1, *const ss2 = ss+2, *const ss3 = ss+3, *const ss4 = ss+4,
13698           *const ss5 = ss+5, *const ss6 = ss+6, *const ss7 = ss+7;
13699         const char saved_char = *se; *se = 0;
13700         const unsigned int clevel = level[ss-expr._data], clevel1 = clevel+1;
13701         if (*se1==';') return compile(ss,se1);
13702 
13703         // Look for a single value, variable or variable assignment.
13704         char end = 0, sep = 0; double val = 0;
13705         const int nb = std::sscanf(ss,"%lf%c%c",&val,&sep,&end);
13706         if (nb==1) {
13707           if (val==0) _cimg_mp_return(0);
13708           if (val==1) _cimg_mp_return(1);
13709           if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
13710           const unsigned int pos = mempos++;
13711           mem[pos] = val;
13712           _cimg_mp_return(pos);
13713         }
13714         if (nb==2 && sep=='%') {
13715           if (val==0) _cimg_mp_return(0);
13716           if (val==100) _cimg_mp_return(1);
13717           if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
13718           const unsigned int pos = mempos++;
13719           mem[pos] = val/100;
13720           _cimg_mp_return(pos);
13721         }
13722         if (ss1==se) switch (*ss) {
13723           case 'w' : _cimg_mp_return(2); case 'h' : _cimg_mp_return(3); case 'd' : _cimg_mp_return(4); case 's' : _cimg_mp_return(5);
13724           case 'x' : _cimg_mp_return(8); case 'y' : _cimg_mp_return(9); case 'z' : _cimg_mp_return(10); case 'c' : _cimg_mp_return(11);
13725           case 'e' : _cimg_mp_return(7);
13726           case 'u' : case '?' : _cimg_mp_opcode2(0,0,1);
13727           case 'g' : _cimg_mp_opcode0(1);
13728           case 'i' : _cimg_mp_opcode0(2);
13729           }
13730         if (ss1==se1) {
13731           if (*ss=='p' && *ss1=='i') _cimg_mp_return(6); // pi
13732           if (*ss=='i') {
13733             if (*ss1=='m') _cimg_mp_opcode0(57); // im
13734             if (*ss1=='M') _cimg_mp_opcode0(58); // iM
13735             if (*ss1=='a') _cimg_mp_opcode0(59); // ia
13736             if (*ss1=='v') _cimg_mp_opcode0(60); // iv
13737           }
13738           if (*ss1=='m') {
13739             if (*ss=='x') _cimg_mp_opcode0(61); // xm
13740             if (*ss=='y') _cimg_mp_opcode0(62); // ym
13741             if (*ss=='z') _cimg_mp_opcode0(63); // zm
13742             if (*ss=='c') _cimg_mp_opcode0(64); // cm
13743           }
13744           if (*ss1=='M') {
13745             if (*ss=='x') _cimg_mp_opcode0(65); // xM
13746             if (*ss=='y') _cimg_mp_opcode0(66); // yM
13747             if (*ss=='z') _cimg_mp_opcode0(67); // zM
13748             if (*ss=='c') _cimg_mp_opcode0(68); // cM
13749           }
13750         }
13751         if (ss3==se) {
13752           if (*ss=='x' && *ss1=='/' && *ss2=='w') _cimg_mp_opcode0(3);
13753           if (*ss=='y' && *ss1=='/' && *ss2=='h') _cimg_mp_opcode0(4);
13754           if (*ss=='z' && *ss1=='/' && *ss2=='d') _cimg_mp_opcode0(5);
13755           if (*ss=='c' && *ss1=='/' && *ss2=='s') _cimg_mp_opcode0(6);
13756         }
13757 
13758         // Look for variable declarations.
13759         for (char *s = se2; s>ss; --s) if (*s==';' && level[s-expr._data]==clevel) { compile(ss,s); _cimg_mp_return(compile(s+1,se)); }
13760         for (char *s = ss1, *ps = ss, *ns = ss2; s<se1; ++s, ++ps, ++ns)
13761           if (*s=='=' && *ns!='=' && *ps!='=' && *ps!='>' && *ps!='<' && *ps!='!' && level[s-expr._data]==clevel) {
13762             CImg<charT> variable_name(ss,(unsigned int)(s-ss+1));
13763             variable_name.back() = 0;
13764             bool is_valid_name = true;
13765             if ((*ss>='0' && *ss<='9') ||
13766                 (s==ss+1 && (*ss=='x' || *ss=='y' || *ss=='z' || *ss=='c' ||
13767                              *ss=='w' || *ss=='h' || *ss=='d' || *ss=='s' ||
13768                              *ss=='e' || *ss=='u' || *ss=='g' || *ss=='i')) ||
13769                 (s==ss+2 && ((*ss=='p' && *(ss+1)=='i') ||
13770                              (*ss=='i' && (*(ss+1)=='m' || *(ss+1)=='M' || *(ss+1)=='a' || *(ss+1)=='v'))))) is_valid_name = false;
13771             for (const char *ns = ss; ns<s; ++ns)
13772               if ((*ns<'a' || *ns>'z') && (*ns<'A' || *ns>'Z') && (*ns<'0' || *ns>'9') && *ns!='_') {
13773                 is_valid_name = false; break;
13774               }
13775             if (!is_valid_name) {
13776               *se = saved_char;
13777               if (!std::strcmp(variable_name,"x") || !std::strcmp(variable_name,"y") || !std::strcmp(variable_name,"z") ||
13778                   !std::strcmp(variable_name,"c") || !std::strcmp(variable_name,"w") || !std::strcmp(variable_name,"h") ||
13779                   !std::strcmp(variable_name,"d") || !std::strcmp(variable_name,"s") || !std::strcmp(variable_name,"e") ||
13780                   !std::strcmp(variable_name,"u") || !std::strcmp(variable_name,"g") || !std::strcmp(variable_name,"i") ||
13781                   !std::strcmp(variable_name,"pi") || !std::strcmp(variable_name,"im") || !std::strcmp(variable_name,"iM") ||
13782                   !std::strcmp(variable_name,"ia") || !std::strcmp(variable_name,"iv"))
13783                 throw CImgArgumentException("[_cimg_math_parser] "
13784                                             "CImg<%s>::%s() : Invalid assignment of reserved variable name '%s' in specified expression '%s'.",
13785                                              pixel_type(),calling_function,
13786                                              variable_name._data,expr._data);
13787                else
13788                  throw CImgArgumentException("[_cimg_math_parser] "
13789                                              "CImg<%s>::%s() : Invalid variable name '%s' in specified expression '%s'.",
13790                                              pixel_type(),calling_function,
13791                                              variable_name._data,expr._data);
13792              }
13793              for (unsigned int i = 0; i<mempos; ++i) // Check for existing variable with same name.
13794                if (label[i]._data && !std::strcmp(variable_name,label[i])) {
13795                  *se = saved_char;
13796                  throw CImgArgumentException("[_cimg_math_parser] "
13797                                              "CImg<%s>::%s() : Invalid multiple assignments of variable '%s' in specified expression '%s'.",
13798                                              pixel_type(),calling_function,
13799                                              variable_name._data,expr._data);
13800                }
13801              const unsigned int src_pos = compile(s+1,se);
13802              if (mempos>=mem.size()) mem.resize(-200,1,1,1,0);
13803              const unsigned int dest_pos = mempos++;
13804              variable_name.move_to(label[dest_pos]);
13805              CImg<uintT>::vector(7,dest_pos,src_pos).move_to(code);
13806              _cimg_mp_return(dest_pos);
13807            }
13808 
13809         // Look for unary/binary operators. The operator precedences is defined as in C++.
13810         for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='|' && *ns=='|' && level[s-expr._data]==clevel) _cimg_mp_opcode2(8,compile(ss,s),compile(s+2,se));
13811         for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='&' && *ns=='&' && level[s-expr._data]==clevel) _cimg_mp_opcode2(9,compile(ss,s),compile(s+2,se));
13812         for (char *s = se2; s>ss; --s) if (*s=='|' && level[s-expr._data]==clevel) _cimg_mp_opcode2(10,compile(ss,s),compile(s+1,se));
13813         for (char *s = se2; s>ss; --s) if (*s=='&' && level[s-expr._data]==clevel) _cimg_mp_opcode2(11,compile(ss,s),compile(s+1,se));
13814         for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='!' && *ns=='=' && level[s-expr._data]==clevel) _cimg_mp_opcode2(12,compile(ss,s),compile(s+2,se));
13815         for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='=' && *ns=='=' && level[s-expr._data]==clevel) _cimg_mp_opcode2(13,compile(ss,s),compile(s+2,se));
13816         for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='<' && *ns=='=' && level[s-expr._data]==clevel) _cimg_mp_opcode2(14,compile(ss,s),compile(s+2,se));
13817         for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='>' && *ns=='=' && level[s-expr._data]==clevel) _cimg_mp_opcode2(15,compile(ss,s),compile(s+2,se));
13818         for (char *s = se2, *ns = se1, *ps = se3; s>ss; --s, --ns, --ps)
13819           if (*s=='<' && *ns!='<' && *ps!='<' && level[s-expr._data]==clevel) _cimg_mp_opcode2(16,compile(ss,s),compile(s+1,se));
13820         for (char *s = se2, *ns = se1, *ps = se3; s>ss; --s, --ns, --ps)
13821           if (*s=='>' && *ns!='>' && *ps!='>' && level[s-expr._data]==clevel) _cimg_mp_opcode2(17,compile(ss,s),compile(s+1,se));
13822         for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='<' && *ns=='<' && level[s-expr._data]==clevel) _cimg_mp_opcode2(18,compile(ss,s),compile(s+2,se));
13823         for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='>' && *ns=='>' && level[s-expr._data]==clevel) _cimg_mp_opcode2(19,compile(ss,s),compile(s+2,se));
13824         for (char *s = se2, *ps = se3; s>ss; --s, --ps)
13825           if (*s=='+' && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' && *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' &&
13826               (*ps!='e' || !(ps>ss && (*(ps-1)=='.' || (*(ps-1)>='0' && *(ps-1)<='9')))) && level[s-expr._data]==clevel)
13827             _cimg_mp_opcode2(21,compile(ss,s),compile(s+1,se));
13828         for (char *s = se2, *ps = se3; s>ss; --s, --ps)
13829           if (*s=='-' && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' && *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' &&
13830               (*ps!='e' || !(ps>ss && (*(ps-1)=='.' || (*(ps-1)>='0' && *(ps-1)<='9')))) && level[s-expr._data]==clevel)
13831             _cimg_mp_opcode2(20,compile(ss,s),compile(s+1,se));
13832         for (char *s = se2; s>ss; --s) if (*s=='*' && level[s-expr._data]==clevel) _cimg_mp_opcode2(22,compile(ss,s),compile(s+1,se));
13833         for (char *s = se2; s>ss; --s) if (*s=='/' && level[s-expr._data]==clevel) _cimg_mp_opcode2(23,compile(ss,s),compile(s+1,se));
13834         for (char *s = se2, *ns = se1; s>ss; --s, --ns)
13835           if (*s=='%' && *ns!='^' && level[s-expr._data]==clevel)
13836             _cimg_mp_opcode2(24,compile(ss,s),compile(s+1,se));
13837         if (ss<se1) {
13838           if (*ss=='+') _cimg_mp_return(compile(ss1,se));
13839           if (*ss=='-') _cimg_mp_opcode1(26,compile(ss1,se));
13840           if (*ss=='!') _cimg_mp_opcode1(27,compile(ss1,se));
13841           if (*ss=='~') _cimg_mp_opcode1(28,compile(ss1,se));
13842         }
13843         for (char *s = se2; s>ss; --s) if (*s=='^' && level[s-expr._data]==clevel) _cimg_mp_opcode2(25,compile(ss,s),compile(s+1,se));
13844 
13845         // Look for a function call or a parenthesis.
13846         if (*se1==')') {
13847           if (*ss=='(') _cimg_mp_return(compile(ss1,se1));
13848           if (!std::strncmp(ss,"sin(",4)) _cimg_mp_opcode1(29,compile(ss4,se1));
13849           if (!std::strncmp(ss,"cos(",4)) _cimg_mp_opcode1(30,compile(ss4,se1));
13850           if (!std::strncmp(ss,"tan(",4)) _cimg_mp_opcode1(31,compile(ss4,se1));
13851           if (!std::strncmp(ss,"asin(",5)) _cimg_mp_opcode1(32,compile(ss5,se1));
13852           if (!std::strncmp(ss,"acos(",5)) _cimg_mp_opcode1(33,compile(ss5,se1));
13853           if (!std::strncmp(ss,"atan(",5)) _cimg_mp_opcode1(34,compile(ss5,se1));
13854           if (!std::strncmp(ss,"sinh(",5)) _cimg_mp_opcode1(35,compile(ss5,se1));
13855           if (!std::strncmp(ss,"cosh(",5)) _cimg_mp_opcode1(36,compile(ss5,se1));
13856           if (!std::strncmp(ss,"tanh(",5)) _cimg_mp_opcode1(37,compile(ss5,se1));
13857           if (!std::strncmp(ss,"log10(",6)) _cimg_mp_opcode1(38,compile(ss6,se1));
13858           if (!std::strncmp(ss,"log2(",5)) _cimg_mp_opcode1(71,compile(ss5,se1));
13859           if (!std::strncmp(ss,"log(",4)) _cimg_mp_opcode1(39,compile(ss4,se1));
13860           if (!std::strncmp(ss,"exp(",4)) _cimg_mp_opcode1(40,compile(ss4,se1));
13861           if (!std::strncmp(ss,"sqrt(",5)) _cimg_mp_opcode1(41,compile(ss5,se1));
13862           if (!std::strncmp(ss,"sign(",5)) _cimg_mp_opcode1(42,compile(ss5,se1));
13863           if (!std::strncmp(ss,"abs(",4)) _cimg_mp_opcode1(43,compile(ss4,se1));
13864           if (!std::strncmp(ss,"atan2(",6)) {
13865             char *s1 = ss6; while (s1<se2 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
13866             _cimg_mp_opcode2(44,compile(ss6,s1),compile(s1+1,se1));
13867           }
13868           if (!std::strncmp(ss,"if(",3)) {
13869             char *s1 = ss3; while (s1<se4 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
13870             char *s2 = s1+1; while (s2<se2 && (*s2!=',' || level[s2-expr._data]!=clevel1)) ++s2;
13871             _cimg_mp_opcode3(45,compile(ss3,s1),compile(s1+1,s2),compile(s2+1,se1));
13872           }
13873           if (!std::strncmp(ss,"round(",6)) {
13874             unsigned int value = 0, round = 1, direction = 0;
13875             char *s1 = ss6; while (s1<se2 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
13876             value = compile(ss6,s1==se2?++s1:s1);
13877             if (s1<se1) {
13878               char *s2 = s1+1; while (s2<se2 && (*s2!=',' || level[s2-expr._data]!=clevel1)) ++s2;
13879               round = compile(s1+1,s2==se2?++s2:s2);
13880               if (s2<se1) direction = compile(s2+1,se1);
13881             }
13882             _cimg_mp_opcode3(46,value,round,direction);
13883           }
13884           if (!std::strncmp(ss,"?(",2) || !std::strncmp(ss,"u(",2)) {
13885             if (*ss2==')') _cimg_mp_opcode2(0,0,1);
13886             char *s1 = ss2; while (s1<se1 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
13887             if (s1<se1) _cimg_mp_opcode2(0,compile(ss2,s1),compile(s1+1,se1));
13888             _cimg_mp_opcode2(0,0,compile(ss2,s1));
13889           }
13890           if (!std::strncmp(ss,"i(",2)) {
13891             if (*ss2==')') _cimg_mp_return(0);
13892             unsigned int indx = 8, indy = 9, indz = 10, indc = 11, borders = 0, interpolation = 0;
13893             if (ss2!=se1) {
13894               char *s1 = ss2; while (s1<se2 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
13895               indx = compile(ss2,s1==se2?++s1:s1);
13896               if (s1<se1) {
13897                 char *s2 = s1+1; while (s2<se2 && (*s2!=',' || level[s2-expr._data]!=clevel1)) ++s2;
13898                 indy = compile(s1+1,s2==se2?++s2:s2);
13899                 if (s2<se1) {
13900                   char *s3 = s2+1; while (s3<se2 && (*s3!=',' || level[s3-expr._data]!=clevel1)) ++s3;
13901                   indz = compile(s2+1,s3==se2?++s3:s3);
13902                   if (s3<se1) {
13903                     char *s4 = s3+1; while (s4<se2 && (*s4!=',' || level[s4-expr._data]!=clevel1)) ++s4;
13904                     indc = compile(s3+1,s4==se2?++s4:s4);
13905                     if (s4<se1) {
13906                       char *s5 = s4+1; while (s5<se2 && (*s5!=',' || level[s5-expr._data]!=clevel1)) ++s5;
13907                       interpolation = compile(s4+1,s5==se2?++s5:s5);
13908                       if (s5<se1) borders = compile(s5+1,se1);
13909                     }
13910                   }
13911                 }
13912               }
13913             }
13914             _cimg_mp_opcode6(47,indx,indy,indz,indc,interpolation,borders);
13915           }
13916           if (!std::strncmp(ss,"min(",4) || !std::strncmp(ss,"max(",4)) {
13917             CImgList<uintT> opcode;
13918             if (mempos>=mem.size()) mem.resize(-200,1,1,1,0);
13919             const unsigned int pos = mempos++;
13920             CImg<uintT>::vector(ss[1]=='i'?48:49,pos).move_to(opcode);
13921             for (char *s = ss4; s<se; ++s) {
13922               char *ns = s; while (ns<se && (*ns!=',' || level[ns-expr._data]!=clevel1) && (*ns!=')' || level[ns-expr._data]!=clevel)) ++ns;
13923               CImg<uintT>::vector(compile(s,ns)).move_to(opcode);
13924               s = ns;
13925             }
13926             (opcode>'y').move_to(code);
13927             _cimg_mp_return(pos);
13928           }
13929           if (!std::strncmp(ss,"arg(",4)) {
13930             CImgList<uintT> opcode;
13931             if (mempos>=mem.size()) mem.resize(-200,1,1,1,0);
13932             const unsigned int pos = mempos++;
13933             CImg<uintT>::vector(69,pos).move_to(opcode);
13934             for (char *s = ss4; s<se; ++s) {
13935               char *ns = s; while (ns<se && (*ns!=',' || level[ns-expr._data]!=clevel1) && (*ns!=')' || level[ns-expr._data]!=clevel)) ++ns;
13936               CImg<uintT>::vector(compile(s,ns)).move_to(opcode);
13937               s = ns;
13938             }
13939             (opcode>'y').move_to(code);
13940             _cimg_mp_return(pos);
13941           }
13942           if (!std::strncmp(ss,"narg(",5)) {
13943             if (*ss5==')') _cimg_mp_return(0);
13944             unsigned int nb_args = 0;
13945             for (char *s = ss5; s<se; ++s) {
13946               char *ns = s; while (ns<se && (*ns!=',' || level[ns-expr._data]!=clevel1) && (*ns!=')' || level[ns-expr._data]!=clevel)) ++ns;
13947               ++nb_args; s = ns;
13948             }
13949             if (nb_args==0 || nb_args==1) _cimg_mp_return(nb_args);
13950             if (mempos>=mem.size()) mem.resize(-200,1,1,1,0);
13951             const unsigned int pos = mempos++;
13952             mem[pos] = nb_args;
13953             _cimg_mp_return(pos);
13954           }
13955           if (!std::strncmp(ss,"isval(",6)) {
13956             char sep = 0, end = 0; double val = 0;
13957             if (std::sscanf(ss6,"%lf%c%c",&val,&sep,&end)==2 && sep==')') _cimg_mp_return(1);
13958             _cimg_mp_return(0);
13959           }
13960           if (!std::strncmp(ss,"isnan(",6)) _cimg_mp_opcode1(50,compile(ss6,se1));
13961           if (!std::strncmp(ss,"isinf(",6)) _cimg_mp_opcode1(51,compile(ss6,se1));
13962           if (!std::strncmp(ss,"isint(",6)) _cimg_mp_opcode1(52,compile(ss6,se1));
13963           if (!std::strncmp(ss,"isbool(",7)) _cimg_mp_opcode1(53,compile(ss7,se1));
13964           if (!std::strncmp(ss,"rol(",4) || !std::strncmp(ss,"ror(",4)) {
13965             unsigned int value = 0, nb = 1;
13966             char *s1 = ss4; while (s1<se2 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
13967             value = compile(ss4,s1==se2?++s1:s1);
13968             if (s1<se1) {
13969               char *s2 = s1+1; while (s2<se2 && (*s2!=',' || level[s2-expr._data]!=clevel1)) ++s2;
13970               nb = compile(s1+1,se1);
13971             }
13972             _cimg_mp_opcode2(*ss2=='l'?54:55,value,nb);
13973           }
13974 
13975           if (!std::strncmp(ss,"sinc(",5)) _cimg_mp_opcode1(56,compile(ss5,se1));
13976           if (!std::strncmp(ss,"int(",4)) _cimg_mp_opcode1(70,compile(ss4,se1));
13977         }
13978 
13979         // No known item found, assuming this is an already initialized variable.
13980         CImg<charT> variable_name(ss,(unsigned int)(se-ss+1));
13981         variable_name.back() = 0;
13982         for (unsigned int i = 0; i<mempos; ++i) if (label[i]._data && !std::strcmp(variable_name,label[i])) _cimg_mp_return(i);
13983         *se = saved_char;
13984         throw CImgArgumentException("[_cimg_math_parser] "
13985                                     "CImg<%s>::%s() : Invalid item '%s' in specified expression '%s'.\n",
13986                                     pixel_type(),calling_function,
13987                                     variable_name._data,expr._data);
13988         return 0;
13989       }
13990 
13991       // Evaluation functions, known by the parser.
13992       double mp_u() {
13993         return mem[opcode(2)] + cimg::rand()*(mem[opcode(3)]-mem[opcode(2)]);
13994       }
13995       double mp_g() {
13996         return cimg::grand();
13997       }
13998       double mp_i() {
13999         return (double)reference.atXYZC((int)mem[8],(int)mem[9],(int)mem[10],(int)mem[11],0);
14000       }
14001       double mp_xw() {
14002         return mem[8]/reference.width();
14003       }
14004       double mp_yh() {
14005         return mem[9]/reference.height();
14006       }
14007       double mp_zd() {
14008         return mem[10]/reference.depth();
14009       }
14010       double mp_cs() {
14011         return mem[11]/reference.spectrum();
14012       }
14013       double mp_equal() {
14014         return mem[opcode[2]];
14015       }
14016       double mp_logical_and() {
14017         return (double)((bool)mem[opcode(2)] && (bool)mem[opcode(3)]);
14018       }
14019       double mp_logical_or() {
14020         return (double)((bool)mem[opcode(2)] || (bool)mem[opcode(3)]);
14021       }
14022       double mp_infeq() {
14023         return (double)(mem[opcode(2)]<=mem[opcode(3)]);
14024       }
14025       double mp_supeq() {
14026         return (double)(mem[opcode(2)]>=mem[opcode(3)]);
14027       }
14028       double mp_noteq() {
14029         return (double)(mem[opcode(2)]!=mem[opcode(3)]);
14030       }
14031       double mp_eqeq() {
14032         return (double)(mem[opcode(2)]==mem[opcode(3)]);
14033       }
14034       double mp_inf() {
14035         return (double)(mem[opcode(2)]<mem[opcode(3)]);
14036       }
14037       double mp_sup() {
14038         return (double)(mem[opcode(2)]>mem[opcode(3)]);
14039       }
14040       double mp_add() {
14041         return mem[opcode(2)] + mem[opcode(3)];
14042       }
14043       double mp_sub() {
14044         return mem[opcode(2)] - mem[opcode(3)];
14045       }
14046       double mp_mul() {
14047         return mem[opcode(2)] * mem[opcode(3)];
14048       }
14049       double mp_div() {
14050         return mem[opcode(2)] / mem[opcode(3)];
14051       }
14052       double mp_minus() {
14053         return -mem[opcode(2)];
14054       }
14055       double mp_not() {
14056         return !mem[opcode(2)];
14057       }
14058       double mp_logical_not() {
14059         return !mem[opcode(2)];
14060       }
14061       double mp_bitwise_not() {
14062         return ~(unsigned long)mem[opcode(2)];
14063       }
14064       double mp_modulo() {
14065         return cimg::mod(mem[opcode(2)],mem[opcode(3)]);
14066       }
14067       double mp_bitwise_and() {
14068         return ((unsigned long)mem[opcode(2)] & (unsigned long)mem[opcode(3)]);
14069       }
14070       double mp_bitwise_or() {
14071         return ((unsigned long)mem[opcode(2)] | (unsigned long)mem[opcode(3)]);
14072       }
14073       double mp_pow() {
14074         return std::pow(mem[opcode(2)],mem[opcode(3)]);
14075       }
14076       double mp_sin() {
14077         return std::sin(mem[opcode(2)]);
14078       }
14079       double mp_cos() {
14080         return std::cos(mem[opcode(2)]);
14081       }
14082       double mp_tan() {
14083         return std::tan(mem[opcode(2)]);
14084       }
14085       double mp_asin() {
14086         return std::asin(mem[opcode(2)]);
14087       }
14088       double mp_acos() {
14089         return std::acos(mem[opcode(2)]);
14090       }
14091       double mp_atan() {
14092         return std::atan(mem[opcode(2)]);
14093       }
14094       double mp_sinh() {
14095         return std::sinh(mem[opcode(2)]);
14096       }
14097       double mp_cosh() {
14098         return std::cosh(mem[opcode(2)]);
14099       }
14100       double mp_tanh() {
14101         return std::tanh(mem[opcode(2)]);
14102       }
14103       double mp_log10() {
14104         return std::log10(mem[opcode(2)]);
14105       }
14106       double mp_log2() {
14107         return cimg::log2(mem[opcode(2)]);
14108       }
14109       double mp_log() {
14110         return std::log(mem[opcode(2)]);
14111       }
14112       double mp_exp() {
14113         return std::exp(mem[opcode(2)]);
14114       }
14115       double mp_sqrt() {
14116         return std::sqrt(mem[opcode(2)]);
14117       }
14118       double mp_sign() {
14119         return cimg::sign(mem[opcode(2)]);
14120       }
14121       double mp_abs() {
14122         return cimg::abs(mem[opcode(2)]);
14123       }
14124       double mp_atan2() {
14125         return std::atan2(mem[opcode(2)],mem[opcode(3)]);
14126       }
14127       double mp_if() {
14128         return mem[opcode(2)]?mem[opcode(3)]:mem[opcode(4)];
14129       }
14130       double mp_round() {
14131         return cimg::round(mem[opcode(2)],mem[opcode(3)],(int)mem[opcode(4)]);
14132       }
14133       double mp_ixyzc() {
14134         const int i = (int)mem[opcode(6)], b = (int)mem[opcode(7)];
14135         if (i==0) { // Nearest neighbor interpolation.
14136           if (b==2) return (double)reference.atXYZC(cimg::mod((int)mem[opcode(2)],reference.width()),
14137                                                     cimg::mod((int)mem[opcode(3)],reference.height()),
14138                                                     cimg::mod((int)mem[opcode(4)],reference.depth()),
14139                                                     cimg::mod((int)mem[opcode(5)],reference.spectrum()));
14140           if (b==1) return (double)reference.atXYZC((int)mem[opcode(2)],
14141                                                     (int)mem[opcode(3)],
14142                                                     (int)mem[opcode(4)],
14143                                                     (int)mem[opcode(5)]);
14144           return (double)reference.atXYZC((int)mem[opcode(2)],
14145                                           (int)mem[opcode(3)],
14146                                           (int)mem[opcode(4)],
14147                                           (int)mem[opcode(5)],0);
14148         } else { // Linear interpolation.
14149           if (b==2) return (double)reference.linear_atXYZC(cimg::mod((float)mem[opcode(2)],(float)reference.width()),
14150                                                            cimg::mod((float)mem[opcode(3)],(float)reference.height()),
14151                                                            cimg::mod((float)mem[opcode(4)],(float)reference.depth()),
14152                                                            cimg::mod((float)mem[opcode(5)],(float)reference.spectrum()));
14153           if (b==1) return (double)reference.linear_atXYZC((float)mem[opcode(2)],
14154                                                            (float)mem[opcode(3)],
14155                                                            (float)mem[opcode(4)],
14156                                                            (float)mem[opcode(5)]);
14157           return (double)reference.linear_atXYZC((float)mem[opcode(2)],
14158                                                  (float)mem[opcode(3)],
14159                                                  (float)mem[opcode(4)],
14160                                                  (float)mem[opcode(5)],0);
14161         }
14162       }
14163       double mp_min() {
14164         double val = mem[opcode(2)];
14165         for (unsigned int i = 3; i<opcode._height; ++i) val = cimg::min(val,mem[opcode(i)]);
14166         return val;
14167       }
14168       double mp_max() {
14169         double val = mem[opcode(2)];
14170         for (unsigned int i = 3; i<opcode._height; ++i) val = cimg::max(val,mem[opcode(i)]);
14171         return val;
14172       }
14173       double mp_isnan() {
14174         const double val = mem[opcode(2)];
14175         return !(val==val);
14176       }
14177       double mp_isinf() {
14178         const double val = mem[opcode(2)];
14179         return val==(val+1);
14180       }
14181       double mp_isint() {
14182         const double val = mem[opcode(2)];
14183         return (double)(cimg::mod(val,1.0)==0);
14184       }
14185       double mp_isbool() {
14186         const double val = mem[opcode(2)];
14187         return (val==0.0 || val==1.0);
14188       }
14189       double mp_rol() {
14190         return cimg::rol(mem[opcode(2)],(unsigned int)mem[opcode(3)]);
14191       }
14192       double mp_ror() {
14193         return cimg::ror(mem[opcode(2)],(unsigned int)mem[opcode(3)]);
14194       }
14195       double mp_lsl() {
14196         return (long)mem[opcode(2)]<<(unsigned int)mem[opcode(3)];
14197       }
14198       double mp_lsr() {
14199         return (long)mem[opcode(2)]>>(unsigned int)mem[opcode(3)];
14200       }
14201       double mp_sinc() {
14202         return cimg::sinc(mem[opcode(2)]);
14203       }
14204       double mp_im() {
14205         if (!reference_stats) reference.get_stats().move_to(reference_stats);
14206         return reference_stats?reference_stats[0]:0;
14207       }
14208       double mp_iM() {
14209         if (!reference_stats) reference.get_stats().move_to(reference_stats);
14210         return reference_stats?reference_stats[1]:0;
14211       }
14212       double mp_ia() {
14213         if (!reference_stats) reference.get_stats().move_to(reference_stats);
14214         return reference_stats?reference_stats[2]:0;
14215       }
14216       double mp_iv() {
14217         if (!reference_stats) reference.get_stats().move_to(reference_stats);
14218         return reference_stats?reference_stats[3]:0;
14219       }
14220       double mp_xm() {
14221         if (!reference_stats) reference.get_stats().move_to(reference_stats);
14222         return reference_stats?reference_stats[4]:0;
14223       }
14224       double mp_ym() {
14225         if (!reference_stats) reference.get_stats().move_to(reference_stats);
14226         return reference_stats?reference_stats[5]:0;
14227       }
14228       double mp_zm() {
14229         if (!reference_stats) reference.get_stats().move_to(reference_stats);
14230         return reference_stats?reference_stats[6]:0;
14231       }
14232       double mp_cm() {
14233         if (!reference_stats) reference.get_stats().move_to(reference_stats);
14234         return reference_stats?reference_stats[7]:0;
14235       }
14236       double mp_xM() {
14237         if (!reference_stats) reference.get_stats().move_to(reference_stats);
14238         return reference_stats?reference_stats[8]:0;
14239       }
14240       double mp_yM() {
14241         if (!reference_stats) reference.get_stats().move_to(reference_stats);
14242         return reference_stats?reference_stats[9]:0;
14243       }
14244       double mp_zM() {
14245         if (!reference_stats) reference.get_stats().move_to(reference_stats);
14246         return reference_stats?reference_stats[10]:0;
14247       }
14248       double mp_cM() {
14249         if (!reference_stats) reference.get_stats().move_to(reference_stats);
14250         return reference_stats?reference_stats[11]:0;
14251       }
14252       double mp_arg() {
14253         const int _ind = (int)mem[opcode(2)];
14254         const unsigned int nb_args = opcode._height-2, ind = _ind<0?_ind+nb_args:(unsigned int)_ind;
14255         if (ind>=nb_args) return 0;
14256         return mem[opcode(ind+2)];
14257       }
14258       double mp_int() {
14259         return (double)(long)mem[opcode(2)];
14260       }
14261 
14262       // Evaluation procedure, with image data.
14263       double eval(const double x, const double y, const double z, const double c) {
14264         typedef double (_cimg_math_parser::*mp_func)();
14265         const mp_func mp_funcs[] = {
14266           &_cimg_math_parser::mp_u,            // 0
14267           &_cimg_math_parser::mp_g,            // 1
14268           &_cimg_math_parser::mp_i,            // 2
14269           &_cimg_math_parser::mp_xw,           // 3
14270           &_cimg_math_parser::mp_yh,           // 4
14271           &_cimg_math_parser::mp_zd,           // 5
14272           &_cimg_math_parser::mp_cs,           // 6
14273           &_cimg_math_parser::mp_equal,        // 7
14274           &_cimg_math_parser::mp_logical_or,   // 8
14275           &_cimg_math_parser::mp_logical_and,  // 9
14276           &_cimg_math_parser::mp_bitwise_or,   // 10
14277           &_cimg_math_parser::mp_bitwise_and,  // 11
14278           &_cimg_math_parser::mp_noteq,        // 12
14279           &_cimg_math_parser::mp_eqeq,         // 13
14280           &_cimg_math_parser::mp_infeq,        // 14
14281           &_cimg_math_parser::mp_supeq,        // 15
14282           &_cimg_math_parser::mp_inf,          // 16
14283           &_cimg_math_parser::mp_sup,          // 17
14284           &_cimg_math_parser::mp_lsl,          // 18
14285           &_cimg_math_parser::mp_lsr,          // 19
14286           &_cimg_math_parser::mp_sub,          // 20
14287           &_cimg_math_parser::mp_add,          // 21
14288           &_cimg_math_parser::mp_mul,          // 22
14289           &_cimg_math_parser::mp_div,          // 23
14290           &_cimg_math_parser::mp_modulo,       // 24
14291           &_cimg_math_parser::mp_pow,          // 25
14292           &_cimg_math_parser::mp_minus,        // 26
14293           &_cimg_math_parser::mp_logical_not,  // 27
14294           &_cimg_math_parser::mp_bitwise_not,  // 28
14295           &_cimg_math_parser::mp_sin,          // 29
14296           &_cimg_math_parser::mp_cos,          // 30
14297           &_cimg_math_parser::mp_tan,          // 31
14298           &_cimg_math_parser::mp_asin,         // 32
14299           &_cimg_math_parser::mp_acos,         // 33
14300           &_cimg_math_parser::mp_atan,         // 34
14301           &_cimg_math_parser::mp_sinh,         // 35
14302           &_cimg_math_parser::mp_cosh,         // 36
14303           &_cimg_math_parser::mp_tanh,         // 37
14304           &_cimg_math_parser::mp_log10,        // 38
14305           &_cimg_math_parser::mp_log,          // 39
14306           &_cimg_math_parser::mp_exp,          // 40
14307           &_cimg_math_parser::mp_sqrt,         // 41
14308           &_cimg_math_parser::mp_sign,         // 42
14309           &_cimg_math_parser::mp_abs,          // 43
14310           &_cimg_math_parser::mp_atan2,        // 44
14311           &_cimg_math_parser::mp_if,           // 45
14312           &_cimg_math_parser::mp_round,        // 46
14313           &_cimg_math_parser::mp_ixyzc,        // 47
14314           &_cimg_math_parser::mp_min,          // 48
14315           &_cimg_math_parser::mp_max,          // 49
14316           &_cimg_math_parser::mp_isnan,        // 50
14317           &_cimg_math_parser::mp_isinf,        // 51
14318           &_cimg_math_parser::mp_isint,        // 52
14319           &_cimg_math_parser::mp_isbool,       // 53
14320           &_cimg_math_parser::mp_rol,          // 54
14321           &_cimg_math_parser::mp_ror,          // 55
14322           &_cimg_math_parser::mp_sinc,         // 56
14323           &_cimg_math_parser::mp_im,           // 57
14324           &_cimg_math_parser::mp_iM,           // 58
14325           &_cimg_math_parser::mp_ia,           // 59
14326           &_cimg_math_parser::mp_iv,           // 60
14327           &_cimg_math_parser::mp_xm,           // 61
14328           &_cimg_math_parser::mp_ym,           // 62
14329           &_cimg_math_parser::mp_zm,           // 63
14330           &_cimg_math_parser::mp_cm,           // 64
14331           &_cimg_math_parser::mp_xM,           // 65
14332           &_cimg_math_parser::mp_yM,           // 66
14333           &_cimg_math_parser::mp_zM,           // 67
14334           &_cimg_math_parser::mp_cM,           // 68
14335           &_cimg_math_parser::mp_arg,          // 69
14336           &_cimg_math_parser::mp_int,          // 70
14337           &_cimg_math_parser::mp_log2          // 71
14338         };
14339 
14340         if (!mem) return 0;
14341         mem[8] = x; mem[9] = y; mem[10] = z; mem[11] = c;
14342         opcode._is_shared = true; opcode._width = opcode._depth = opcode._spectrum = 1;
14343         cimglist_for(code,l) {
14344           const CImg<uintT> &op = code[l];
14345           opcode._data = op._data; opcode._height = op._height;  // Allows to avoid parameter passing to evaluation functions.
14346           mem[opcode(1)] = (this->*mp_funcs[opcode[0]])();
14347         }
14348         return mem[result];
14349       }
14350     };
14351 
14352     //! Compute the square value of each pixel value.
14353     /**
14354        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$.
14355        \note
14356        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14357        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14358        \par Example
14359        \code
14360        const CImg<float> img("reference.jpg");
14361        (img,img.get_sqr().normalize(0,255)).display();
14362        \endcode
14363        \image html ref_sqr.jpg
14364     **/
14365     CImg<T>& sqr() {
14366       cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(val*val); };
14367       return *this;
14368     }
14369 
14370     //! Compute the square value of each pixel value \newinstance.
14371     CImg<Tfloat> get_sqr() const {
14372       return CImg<Tfloat>(*this,false).sqr();
14373     }
14374 
14375     //! Compute the square root of each pixel value.
14376     /**
14377        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$.
14378        \note
14379        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14380        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14381        \par Example
14382        \code
14383        const CImg<float> img("reference.jpg");
14384        (img,img.get_sqrt().normalize(0,255)).display();
14385        \endcode
14386        \image html ref_sqrt.jpg
14387     **/
14388     CImg<T>& sqrt() {
14389       cimg_for(*this,ptrd,T) *ptrd = (T)std::sqrt((double)*ptrd);
14390       return *this;
14391     }
14392 
14393     //! Compute the square root of each pixel value \newinstance.
14394     CImg<Tfloat> get_sqrt() const {
14395       return CImg<Tfloat>(*this,false).sqrt();
14396     }
14397 
14398     //! Compute the exponential of each pixel value.
14399     /**
14400        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$.
14401        \note
14402        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14403        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14404     **/
14405     CImg<T>& exp() {
14406       cimg_for(*this,ptrd,T) *ptrd = (T)std::exp((double)*ptrd);
14407       return *this;
14408     }
14409 
14410     //! Compute the exponential of each pixel value \newinstance.
14411     CImg<Tfloat> get_exp() const {
14412       return CImg<Tfloat>(*this,false).exp();
14413     }
14414 
14415     //! Compute the logarithm of each pixel value.
14416     /**
14417        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its logarithm \f$\mathrm{log}_{e}(I_{(x,y,z,c)})\f$.
14418        \note
14419        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14420        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14421     **/
14422     CImg<T>& log() {
14423       cimg_for(*this,ptrd,T) *ptrd = (T)std::log((double)*ptrd);
14424       return *this;
14425     }
14426 
14427     //! Compute the logarithm of each pixel value \newinstance.
14428     CImg<Tfloat> get_log() const {
14429       return CImg<Tfloat>(*this,false).log();
14430     }
14431 
14432     //! Compute the base-2 logarithm of each pixel value.
14433     /**
14434        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-2 logarithm \f$\mathrm{log}_{2}(I_{(x,y,z,c)})\f$.
14435        \note
14436        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14437        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14438     **/
14439     CImg<T>& log2() {
14440       cimg_for(*this,ptrd,T) *ptrd = (T)cimg::log2((double)*ptrd);
14441       return *this;
14442     }
14443 
14444     //! Compute the base-10 logarithm of each pixel value \newinstance.
14445     CImg<Tfloat> get_log2() const {
14446       return CImg<Tfloat>(*this,false).log2();
14447     }
14448 
14449     //! Compute the base-10 logarithm of each pixel value.
14450     /**
14451        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-10 logarithm \f$\mathrm{log}_{10}(I_{(x,y,z,c)})\f$.
14452        \note
14453        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14454        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14455     **/
14456     CImg<T>& log10() {
14457       cimg_for(*this,ptrd,T) *ptrd = (T)std::log10((double)*ptrd);
14458       return *this;
14459     }
14460 
14461     //! Compute the base-10 logarithm of each pixel value \newinstance.
14462     CImg<Tfloat> get_log10() const {
14463       return CImg<Tfloat>(*this,false).log10();
14464     }
14465 
14466     //! Compute the absolute value of each pixel value.
14467     /**
14468        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$.
14469        \note
14470        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14471        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14472     **/
14473     CImg<T>& abs() {
14474       cimg_for(*this,ptrd,T) *ptrd = cimg::abs(*ptrd);
14475       return *this;
14476     }
14477 
14478     //! Compute the absolute value of each pixel value \newinstance.
14479     CImg<Tfloat> get_abs() const {
14480       return CImg<Tfloat>(*this,false).abs();
14481     }
14482 
14483     //! Compute the sign of each pixel value.
14484     /**
14485        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sign \f$\mathrm{sign}(I_{(x,y,z,c)})\f$.
14486        \note
14487        - The sign is set to :
14488          - \c 1 if pixel value is strictly positive.
14489          - \c -1 if pixel value is strictly negative.
14490          - \c 0 if pixel value is equal to \c 0.
14491        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14492        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14493     **/
14494     CImg<T>& sign() {
14495       cimg_for(*this,ptrd,T) *ptrd = cimg::sign(*ptrd);
14496       return *this;
14497     }
14498 
14499     //! Compute the sign of each pixel value \newinstance.
14500     CImg<Tfloat> get_sign() const {
14501       return CImg<Tfloat>(*this,false).sign();
14502     }
14503 
14504     //! Compute the cosine of each pixel value.
14505     /**
14506        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$.
14507        \note
14508        - Pixel values are regarded as being in \e radian.
14509        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14510        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14511     **/
14512     CImg<T>& cos() {
14513       cimg_for(*this,ptrd,T) *ptrd = (T)std::cos((double)*ptrd);
14514       return *this;
14515     }
14516 
14517     //! Compute the cosine of each pixel value \newinstance.
14518     CImg<Tfloat> get_cos() const {
14519       return CImg<Tfloat>(*this,false).cos();
14520     }
14521 
14522     //! Compute the sine of each pixel value.
14523     /**
14524        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$.
14525        \note
14526        - Pixel values are regarded as being in \e radian.
14527        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14528        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14529     **/
14530     CImg<T>& sin() {
14531       cimg_for(*this,ptrd,T) *ptrd = (T)std::sin((double)*ptrd);
14532       return *this;
14533     }
14534 
14535     //! Compute the sine of each pixel value \newinstance.
14536     CImg<Tfloat> get_sin() const {
14537       return CImg<Tfloat>(*this,false).sin();
14538     }
14539 
14540     //! Compute the sinc of each pixel value.
14541     /**
14542        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sinc \f$\mathrm{sinc}(I_{(x,y,z,c)})\f$.
14543        \note
14544        - Pixel values are regarded as being exin \e radian.
14545        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14546        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14547     **/
14548     CImg<T>& sinc() {
14549       cimg_for(*this,ptrd,T) *ptrd = (T)cimg::sinc((double)*ptrd);
14550       return *this;
14551     }
14552 
14553     //! Compute the sinc of each pixel value \newinstance.
14554     CImg<Tfloat> get_sinc() const {
14555       return CImg<Tfloat>(*this,false).sinc();
14556     }
14557 
14558     //! Compute the tangent of each pixel value.
14559     /**
14560        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$.
14561        \note
14562        - Pixel values are regarded as being exin \e radian.
14563        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14564        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14565     **/
14566     CImg<T>& tan() {
14567       cimg_for(*this,ptrd,T) *ptrd = (T)std::tan((double)*ptrd);
14568       return *this;
14569     }
14570 
14571     //! Compute the tangent of each pixel value \newinstance.
14572     CImg<Tfloat> get_tan() const {
14573       return CImg<Tfloat>(*this,false).tan();
14574     }
14575 
14576     //! Compute the hyperbolic cosine of each pixel value.
14577     /**
14578        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic cosine \f$\mathrm{cosh}(I_{(x,y,z,c)})\f$.
14579        \note
14580        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14581        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14582     **/
14583     CImg<T>& cosh() {
14584       cimg_for(*this,ptrd,T) *ptrd = (T)std::cosh((double)*ptrd);
14585       return *this;
14586     }
14587 
14588     //! Compute the hyperbolic cosine of each pixel value \newinstance.
14589     CImg<Tfloat> get_cosh() const {
14590       return CImg<Tfloat>(*this,false).cosh();
14591     }
14592 
14593     //! Compute the hyperbolic sine of each pixel value.
14594     /**
14595        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic sine \f$\mathrm{sinh}(I_{(x,y,z,c)})\f$.
14596        \note
14597        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14598        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14599     **/
14600     CImg<T>& sinh() {
14601       cimg_for(*this,ptrd,T) *ptrd = (T)std::sinh((double)*ptrd);
14602       return *this;
14603     }
14604 
14605     //! Compute the hyperbolic sine of each pixel value \newinstance.
14606     CImg<Tfloat> get_sinh() const {
14607       return CImg<Tfloat>(*this,false).sinh();
14608     }
14609 
14610     //! Compute the hyperbolic tangent of each pixel value.
14611     /**
14612        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic tangent \f$\mathrm{tanh}(I_{(x,y,z,c)})\f$.
14613        \note
14614        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14615        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14616     **/
14617     CImg<T>& tanh() {
14618       cimg_for(*this,ptrd,T) *ptrd = (T)std::tanh((double)*ptrd);
14619       return *this;
14620     }
14621 
14622     //! Compute the hyperbolic tangent of each pixel value \newinstance.
14623     CImg<Tfloat> get_tanh() const {
14624       return CImg<Tfloat>(*this,false).tanh();
14625     }
14626 
14627     //! Compute the arccosine of each pixel value.
14628     /**
14629        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arccosine \f$\mathrm{acos}(I_{(x,y,z,c)})\f$.
14630        \note
14631        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14632        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14633     **/
14634     CImg<T>& acos() {
14635       cimg_for(*this,ptrd,T) *ptrd = (T)std::acos((double)*ptrd);
14636       return *this;
14637     }
14638 
14639     //! Compute the arccosine of each pixel value \newinstance.
14640     CImg<Tfloat> get_acos() const {
14641       return CImg<Tfloat>(*this,false).acos();
14642     }
14643 
14644     //! Compute the arcsine of each pixel value.
14645     /**
14646        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arcsine \f$\mathrm{asin}(I_{(x,y,z,c)})\f$.
14647        \note
14648        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14649        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14650     **/
14651     CImg<T>& asin() {
14652       cimg_for(*this,ptrd,T) *ptrd = (T)std::asin((double)*ptrd);
14653       return *this;
14654     }
14655 
14656     //! Compute the arcsine of each pixel value \newinstance.
14657     CImg<Tfloat> get_asin() const {
14658       return CImg<Tfloat>(*this,false).asin();
14659     }
14660 
14661     //! Compute the arctangent of each pixel value.
14662     /**
14663        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent \f$\mathrm{atan}(I_{(x,y,z,c)})\f$.
14664        \note
14665        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14666        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14667     **/
14668     CImg<T>& atan() {
14669       cimg_for(*this,ptrd,T) *ptrd = (T)std::atan((double)*ptrd);
14670       return *this;
14671     }
14672 
14673     //! Compute the arctangent of each pixel value \newinstance.
14674     CImg<Tfloat> get_atan() const {
14675       return CImg<Tfloat>(*this,false).atan();
14676     }
14677 
14678     //! Compute the arctangent2 of each pixel value.
14679     /**
14680        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent2 \f$\mathrm{atan2}(I_{(x,y,z,c)})\f$.
14681        \param img Image whose pixel values specify the second argument of the \c atan2() function.
14682        \note
14683        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14684        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14685        \par Example
14686        \code
14687        const CImg<float>
14688           img_x(100,100,1,1,"x-w/2",false),   // Define an horizontal centered gradient, from '-width/2' to 'width/2'.
14689           img_y(100,100,1,1,"y-h/2",false),   // Define a vertical centered gradient, from '-height/2' to 'height/2'.
14690           img_atan2 = img_y.get_atan2(img_x); // Compute atan2(y,x) for each pixel value.
14691        (img_x,img_y,img_atan2).display();
14692        \endcode
14693     **/
14694     template<typename t>
14695     CImg<T>& atan2(const CImg<t>& img) {
14696       const unsigned long siz = size(), isiz = img.size();
14697       if (siz && isiz) {
14698         if (is_overlapped(img)) return atan2(+img);
14699         T *ptrd = _data, *const ptre = _data + siz;
14700         if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
14701           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)std::atan2((double)*ptrd,(double)*(ptrs++));
14702         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)std::atan2((double)*ptrd,(double)*(ptrs++));
14703       }
14704       return *this;
14705     }
14706 
14707     //! Compute the arctangent2 of each pixel value \newinstance.
14708     template<typename t>
14709     CImg<Tfloat> get_atan2(const CImg<t>& img) const {
14710       return CImg<Tfloat>(*this,false).atan2(img);
14711     }
14712 
14713     //! In-place pointwise multiplication.
14714     /**
14715        Compute the pointwise multiplication between the image instance and the specified input image \c img.
14716        \param img Input image, as the second operand of the multiplication.
14717        \note
14718        - Similar to operator+=(const CImg<t>&), except that it performs a pointwise multiplication instead of an addition.
14719        - It does \e not perform a \e matrix multiplication. For this purpose, use operator*=(const CImg<t>&) instead.
14720        \par Example
14721        \code
14722        CImg<float>
14723          img("reference.jpg"),
14724          shade(img.width,img.height(),1,1,"-(x-w/2)^2-(y-h/2)^2",false);
14725        shade.normalize(0,1);
14726        (img,shade,img.get_mul(shade)).display();
14727        \endcode
14728     **/
14729     template<typename t>
14730     CImg<T>& mul(const CImg<t>& img) {
14731       const unsigned long siz = size(), isiz = img.size();
14732       if (siz && isiz) {
14733         if (is_overlapped(img)) return mul(+img);
14734         T *ptrd = _data, *const ptre = _data + siz;
14735         if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
14736           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)(*ptrd * *(ptrs++));
14737         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd * *(ptrs++));
14738       }
14739       return *this;
14740     }
14741 
14742     //! In-place pointwise multiplication \newinstance.
14743     template<typename t>
14744     CImg<_cimg_Tt> get_mul(const CImg<t>& img) const {
14745       return CImg<_cimg_Tt>(*this,false).mul(img);
14746     }
14747 
14748     //! In-place pointwise division.
14749     /**
14750        Similar to mul(const CImg<t>&), except that it performs a pointwise division instead of a multiplication.
14751     **/
14752     template<typename t>
14753     CImg<T>& div(const CImg<t>& img) {
14754       const unsigned long siz = size(), isiz = img.size();
14755       if (siz && isiz) {
14756         if (is_overlapped(img)) return div(+img);
14757         T *ptrd = _data, *const ptre = _data + siz;
14758         if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
14759           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)(*ptrd / *(ptrs++));
14760         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd / *(ptrs++));
14761       }
14762       return *this;
14763     }
14764 
14765     //! In-place pointwise division \newinstance.
14766     template<typename t>
14767     CImg<_cimg_Tt> get_div(const CImg<t>& img) const {
14768       return CImg<_cimg_Tt>(*this,false).div(img);
14769     }
14770 
14771     //! Raise each pixel value to a specified power.
14772     /**
14773        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$.
14774        \param p Exponent value.
14775        \note
14776        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14777        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14778        \par Example
14779        \code
14780        const CImg<float>
14781          img0("reference.jpg"),           // Load reference color image.
14782          img1 = (img0/255).pow(1.8)*=255, // Compute gamma correction, with gamma = 1.8.
14783          img2 = (img0/255).pow(0.5)*=255; // Compute gamma correction, with gamma = 0.5.
14784        (img0,img1,img2).display();
14785        \endcode
14786     **/
14787     CImg<T>& pow(const double p) {
14788       if (p==0) return fill(1);
14789       if (p==0.5) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)std::sqrt((double)val); } return *this; }
14790       if (p==1) return *this;
14791       if (p==2) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val; } return *this; }
14792       if (p==3) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val; } return *this; }
14793       if (p==4) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val*val; } return *this; }
14794       cimg_for(*this,ptrd,T) *ptrd = (T)std::pow((double)*ptrd,p);
14795       return *this;
14796     }
14797 
14798     //! Raise each pixel value to a specified power \newinstance.
14799     CImg<Tfloat> get_pow(const double p) const {
14800       return CImg<Tfloat>(*this,false).pow(p);
14801     }
14802 
14803     //! Raise each pixel value to a power, specified from an expression.
14804     /**
14805        Similar to operator+=(const char*), except it performs a pointwise exponentiation instead of an addition.
14806     **/
14807     CImg<T>& pow(const char *const expression) {
14808       const unsigned int omode = cimg::exception_mode();
14809       cimg::exception_mode() = 0;
14810       try {
14811         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
14812         _cimg_math_parser mp(base,expression,"pow");
14813         T *ptrd = _data;
14814         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)std::pow((double)*ptrd,mp.eval(x,y,z,c)); ++ptrd; }
14815       } catch (CImgException&) {
14816         CImg<Tfloat> values(_width,_height,_depth,_spectrum);
14817         try {
14818           values.fill(expression,true);
14819         } catch (CImgException&) {
14820           cimg::exception_mode() = omode;
14821           values.load(expression);
14822         }
14823         pow(values);
14824       }
14825       cimg::exception_mode() = omode;
14826       return *this;
14827     }
14828 
14829     //! Raise each pixel value to a power, specified from an expression \newinstance.
14830     CImg<Tfloat> get_pow(const char *const expression) const {
14831       return CImg<Tfloat>(*this,false).pow(expression);
14832     }
14833 
14834     //! Raise each pixel value to a power, pointwisely specified from another image.
14835     /**
14836        Similar to operator+=(const CImg<t>& img), except that it performs an exponentiation instead of an addition.
14837     **/
14838     template<typename t>
14839     CImg<T>& pow(const CImg<t>& img) {
14840       const unsigned long siz = size(), isiz = img.size();
14841       if (siz && isiz) {
14842         if (is_overlapped(img)) return pow(+img);
14843         T *ptrd = _data, *const ptre = _data + siz;
14844         if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
14845           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)std::pow((double)*ptrd,(double)(*(ptrs++)));
14846         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)std::pow((double)*ptrd,(double)(*(ptrs++)));
14847       }
14848       return *this;
14849     }
14850 
14851     //! Raise each pixel value to a power, pointwisely specified from another image \newinstance.
14852     template<typename t>
14853     CImg<Tfloat> get_pow(const CImg<t>& img) const {
14854       return CImg<Tfloat>(*this,false).pow(img);
14855     }
14856 
14857     //! Compute the bitwise left rotation of each pixel value.
14858     /**
14859        Similar to operator<<=(unsigned int), except that it performs a left rotation instead of a left shift.
14860     **/
14861     CImg<T>& rol(const unsigned int n=1) {
14862       cimg_for(*this,ptrd,T) *ptrd = (T)cimg::rol(*ptrd,n);
14863       return *this;
14864     }
14865 
14866     //! Compute the bitwise left rotation of each pixel value \newinstance.
14867     CImg<T> get_rol(const unsigned int n=1) const {
14868       return (+*this).rol(n);
14869     }
14870 
14871     //! Compute the bitwise left rotation of each pixel value.
14872     /**
14873        Similar to operator<<=(const char*), except that it performs a left rotation instead of a left shift.
14874     **/
14875     CImg<T>& rol(const char *const expression) {
14876       const unsigned int omode = cimg::exception_mode();
14877       cimg::exception_mode() = 0;
14878       try {
14879         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
14880         _cimg_math_parser mp(base,expression,"rol");
14881         T *ptrd = _data;
14882         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::rol(*ptrd,(unsigned int)mp.eval(x,y,z,c)); ++ptrd; }
14883       } catch (CImgException&) {
14884         CImg<Tfloat> values(_width,_height,_depth,_spectrum);
14885         try {
14886           values.fill(expression,true);
14887         } catch (CImgException&) {
14888           cimg::exception_mode() = omode;
14889           values.load(expression);
14890         }
14891         rol(values);
14892       }
14893       cimg::exception_mode() = omode;
14894       return *this;
14895     }
14896 
14897     //! Compute the bitwise left rotation of each pixel value \newinstance.
14898     CImg<T> get_rol(const char *const expression) const {
14899       return (+*this).rol(expression);
14900     }
14901 
14902     //! Compute the bitwise left rotation of each pixel value.
14903     /**
14904        Similar to operator<<=(const CImg<t>&), except that it performs a left rotation instead of a left shift.
14905     **/
14906     template<typename t>
14907     CImg<T>& rol(const CImg<t>& img) {
14908       const unsigned long siz = size(), isiz = img.size();
14909       if (siz && isiz) {
14910         if (is_overlapped(img)) return rol(+img);
14911         T *ptrd = _data, *const ptre = _data + siz;
14912         if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
14913           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)cimg::rol(*ptrd,(unsigned int)(*(ptrs++)));
14914         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)cimg::rol(*ptrd,(unsigned int)(*(ptrs++)));
14915       }
14916       return *this;
14917     }
14918 
14919     //! Compute the bitwise left rotation of each pixel value \newinstance.
14920     template<typename t>
14921     CImg<T> get_rol(const CImg<t>& img) const {
14922       return (+*this).rol(img);
14923     }
14924 
14925     //! Compute the bitwise right rotation of each pixel value.
14926     /**
14927        Similar to operator>>=(unsigned int), except that it performs a right rotation instead of a right shift.
14928     **/
14929     CImg<T>& ror(const unsigned int n=1) {
14930       cimg_for(*this,ptrd,T) *ptrd = (T)cimg::ror(*ptrd,n);
14931       return *this;
14932     }
14933 
14934     //! Compute the bitwise right rotation of each pixel value \newinstance.
14935     CImg<T> get_ror(const unsigned int n=1) const {
14936       return (+*this).ror(n);
14937     }
14938 
14939     //! Compute the bitwise right rotation of each pixel value.
14940     /**
14941        Similar to operator>>=(const char*), except that it performs a right rotation instead of a right shift.
14942     **/
14943     CImg<T>& ror(const char *const expression) {
14944       const unsigned int omode = cimg::exception_mode();
14945       cimg::exception_mode() = 0;
14946       try {
14947         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
14948         _cimg_math_parser mp(base,expression,"ror");
14949         T *ptrd = _data;
14950         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::ror(*ptrd,(unsigned int)mp.eval(x,y,z,c)); ++ptrd; }
14951       } catch (CImgException&) {
14952         CImg<Tfloat> values(_width,_height,_depth,_spectrum);
14953         try {
14954           values.fill(expression,true);
14955         } catch (CImgException&) {
14956           cimg::exception_mode() = omode;
14957           values.load(expression);
14958         }
14959         ror(values);
14960       }
14961       cimg::exception_mode() = omode;
14962       return *this;
14963     }
14964 
14965     //! Compute the bitwise right rotation of each pixel value \newinstance.
14966     CImg<T> get_ror(const char *const expression) const {
14967       return (+*this).ror(expression);
14968     }
14969 
14970     //! Compute the bitwise right rotation of each pixel value.
14971     /**
14972        Similar to operator>>=(const CImg<t>&), except that it performs a right rotation instead of a right shift.
14973     **/
14974     template<typename t>
14975     CImg<T>& ror(const CImg<t>& img) {
14976       const unsigned long siz = size(), isiz = img.size();
14977       if (siz && isiz) {
14978         if (is_overlapped(img)) return ror(+img);
14979         T *ptrd = _data, *const ptre = _data + siz;
14980         if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
14981           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)cimg::ror(*ptrd,(unsigned int)(*(ptrs++)));
14982         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)cimg::ror(*ptrd,(unsigned int)(*(ptrs++)));
14983       }
14984       return *this;
14985     }
14986 
14987     //! Compute the bitwise right rotation of each pixel value \newinstance.
14988     template<typename t>
14989     CImg<T> get_ror(const CImg<t>& img) const {
14990       return (+*this).ror(img);
14991     }
14992 
14993     //! Pointwise min operator between instance image and a value.
14994     /**
14995        \param val Value used as the reference argument of the min operator.
14996        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{val})\f$.
14997      **/
14998     CImg<T>& min(const T val) {
14999       cimg_for(*this,ptrd,T) *ptrd = cimg::min(*ptrd,val);
15000       return *this;
15001     }
15002 
15003     //! Pointwise min operator between instance image and a value \newinstance.
15004     CImg<T> get_min(const T val) const {
15005       return (+*this).min(val);
15006     }
15007 
15008     //! Pointwise min operator between two images.
15009     /**
15010        \param img Image used as the reference argument of the min operator.
15011        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
15012      **/
15013     template<typename t>
15014     CImg<T>& min(const CImg<t>& img) {
15015       const unsigned long siz = size(), isiz = img.size();
15016       if (siz && isiz) {
15017         if (is_overlapped(img)) return min(+img);
15018         T *ptrd = _data, *const ptre = _data + siz;
15019         if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
15020           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = cimg::min((T)*(ptrs++),*ptrd);
15021         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::min((T)*(ptrs++),*ptrd);
15022       }
15023       return *this;
15024     }
15025 
15026     //! Pointwise min operator between two images \newinstance.
15027     template<typename t>
15028     CImg<_cimg_Tt> get_min(const CImg<t>& img) const {
15029       return CImg<_cimg_Tt>(*this,false).min(img);
15030     }
15031 
15032     //! Pointwise min operator between an image and an expression.
15033     /**
15034        \param expression Math formula as a C-string.
15035        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
15036     **/
15037     CImg<T>& min(const char *const expression) {
15038       const unsigned int omode = cimg::exception_mode();
15039       cimg::exception_mode() = 0;
15040       try {
15041         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
15042         _cimg_math_parser mp(base,expression,"min");
15043         T *ptrd = _data;
15044         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::min(*ptrd,(T)mp.eval(x,y,z,c)); ++ptrd; }
15045       } catch (CImgException&) {
15046         CImg<T> values(_width,_height,_depth,_spectrum);
15047         try {
15048           values.fill(expression,true);
15049         } catch (CImgException&) {
15050           cimg::exception_mode() = omode;
15051           values.load(expression);
15052         }
15053         min(values);
15054       }
15055       cimg::exception_mode() = omode;
15056       return *this;
15057     }
15058 
15059     //! Pointwise min operator between an image and an expression \newinstance.
15060     CImg<Tfloat> get_min(const char *const expression) const {
15061       return CImg<Tfloat>(*this,false).min(expression);
15062     }
15063 
15064     //! Pointwise max operator between instance image and a value.
15065     /**
15066        \param val Value used as the reference argument of the max operator.
15067        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{val})\f$.
15068      **/
15069     CImg<T>& max(const T val) {
15070       cimg_for(*this,ptrd,T) *ptrd = cimg::max(*ptrd,val);
15071       return *this;
15072     }
15073 
15074     //! Pointwise max operator between instance image and a value \newinstance.
15075     CImg<T> get_max(const T val) const {
15076       return (+*this).max(val);
15077     }
15078 
15079     //! Pointwise max operator between two images.
15080     /**
15081        \param img Image used as the reference argument of the max operator.
15082        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
15083      **/
15084     template<typename t>
15085     CImg<T>& max(const CImg<t>& img) {
15086       const unsigned long siz = size(), isiz = img.size();
15087       if (siz && isiz) {
15088         if (is_overlapped(img)) return max(+img);
15089         T *ptrd = _data, *const ptre = _data + siz;
15090         if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
15091           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = cimg::max((T)*(ptrs++),*ptrd);
15092         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::max((T)*(ptrs++),*ptrd);
15093       }
15094       return *this;
15095     }
15096 
15097     //! Pointwise max operator between two images \newinstance.
15098     template<typename t>
15099     CImg<_cimg_Tt> get_max(const CImg<t>& img) const {
15100       return CImg<_cimg_Tt>(*this,false).max(img);
15101     }
15102 
15103     //! Pointwise max operator between an image and an expression.
15104     /**
15105        \param expression Math formula as a C-string.
15106        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
15107     **/
15108     CImg<T>& max(const char *const expression) {
15109       const unsigned int omode = cimg::exception_mode();
15110       cimg::exception_mode() = 0;
15111       try {
15112         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
15113         _cimg_math_parser mp(base,expression,"max");
15114         T *ptrd = _data;
15115         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::max(*ptrd,(T)mp.eval(x,y,z,c)); ++ptrd; }
15116       } catch (CImgException&) {
15117         CImg<T> values(_width,_height,_depth,_spectrum);
15118         try {
15119           values.fill(expression,true);
15120         } catch (CImgException&) {
15121           cimg::exception_mode() = omode;
15122           values.load(expression);
15123         }
15124         max(values);
15125       }
15126       cimg::exception_mode() = omode;
15127       return *this;
15128     }
15129 
15130     //! Pointwise max operator between an image and an expression \newinstance.
15131     CImg<Tfloat> get_max(const char *const expression) const {
15132       return CImg<Tfloat>(*this,false).max(expression);
15133     }
15134 
15135     //! Return a reference to the minimum pixel value.
15136     /**
15137      **/
15138     T& min() {
15139       if (is_empty())
15140         throw CImgInstanceException(_cimg_instance
15141                                     "min() : Empty instance.",
15142                                     cimg_instance);
15143       T *ptr_min = _data;
15144       T min_value = *ptr_min;
15145       cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
15146       return *ptr_min;
15147     }
15148 
15149     //! Return a reference to the minimum pixel value \const.
15150     const T& min() const {
15151       if (is_empty())
15152         throw CImgInstanceException(_cimg_instance
15153                                     "min() : Empty instance.",
15154                                     cimg_instance);
15155       const T *ptr_min = _data;
15156       T min_value = *ptr_min;
15157       cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
15158       return *ptr_min;
15159     }
15160 
15161     //! Return a reference to the maximum pixel value.
15162     /**
15163      **/
15164     T& max() {
15165       if (is_empty())
15166         throw CImgInstanceException(_cimg_instance
15167                                     "max() : Empty instance.",
15168                                     cimg_instance);
15169       T *ptr_max = _data;
15170       T max_value = *ptr_max;
15171       cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
15172       return *ptr_max;
15173     }
15174 
15175     //! Return a reference to the maximum pixel value \const.
15176     const T& max() const {
15177       if (is_empty())
15178         throw CImgInstanceException(_cimg_instance
15179                                     "max() : Empty instance.",
15180                                     cimg_instance);
15181       const T *ptr_max = _data;
15182       T max_value = *ptr_max;
15183       cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
15184       return *ptr_max;
15185     }
15186 
15187     //! Return a reference to the minimum pixel value as well as the maximum pixel value.
15188     /**
15189        \param[out] max_val Maximum pixel value.
15190     **/
15191     template<typename t>
15192     T& min_max(t& max_val) {
15193       if (is_empty())
15194         throw CImgInstanceException(_cimg_instance
15195                                     "min_max() : Empty instance.",
15196                                     cimg_instance);
15197       T *ptr_min = _data;
15198       T min_value = *ptr_min, max_value = min_value;
15199       cimg_for(*this,ptrs,T) {
15200         const T val = *ptrs;
15201         if (val<min_value) { min_value = val; ptr_min = ptrs; }
15202         if (val>max_value) max_value = val;
15203       }
15204       max_val = (t)max_value;
15205       return *ptr_min;
15206     }
15207 
15208     //! Return a reference to the minimum pixel value as well as the maximum pixel value \const.
15209     template<typename t>
15210     const T& min_max(t& max_val) const {
15211       if (is_empty())
15212         throw CImgInstanceException(_cimg_instance
15213                                     "min_max() : Empty instance.",
15214                                     cimg_instance);
15215       const T *ptr_min = _data;
15216       T min_value = *ptr_min, max_value = min_value;
15217       cimg_for(*this,ptrs,T) {
15218         const T val = *ptrs;
15219         if (val<min_value) { min_value = val; ptr_min = ptrs; }
15220         if (val>max_value) max_value = val;
15221       }
15222       max_val = (t)max_value;
15223       return *ptr_min;
15224     }
15225 
15226     //! Return a reference to the maximum pixel value as well as the minimum pixel value.
15227     /**
15228        \param[out] min_val Minimum pixel value.
15229     **/
15230     template<typename t>
15231     T& max_min(t& min_val) {
15232       if (is_empty())
15233         throw CImgInstanceException(_cimg_instance
15234                                     "max_min() : Empty instance.",
15235                                     cimg_instance);
15236       T *ptr_max = _data;
15237       T max_value = *ptr_max, min_value = max_value;
15238       cimg_for(*this,ptrs,T) {
15239         const T val = *ptrs;
15240         if (val>max_value) { max_value = val; ptr_max = ptrs; }
15241         if (val<min_value) min_value = val;
15242       }
15243       min_val = (t)min_value;
15244       return *ptr_max;
15245     }
15246 
15247     //! Return a reference to the maximum pixel value as well as the minimum pixel value \const.
15248     template<typename t>
15249     const T& max_min(t& min_val) const {
15250       if (is_empty())
15251         throw CImgInstanceException(_cimg_instance
15252                                     "max_min() : Empty instance.",
15253                                     cimg_instance);
15254       const T *ptr_max = _data;
15255       T max_value = *ptr_max, min_value = max_value;
15256       cimg_for(*this,ptrs,T) {
15257         const T val = *ptrs;
15258         if (val>max_value) { max_value = val; ptr_max = ptrs; }
15259         if (val<min_value) min_value = val;
15260       }
15261       min_val = (t)min_value;
15262       return *ptr_max;
15263     }
15264 
15265     //! Return the kth smallest pixel value.
15266     /**
15267        \param k Rank of the search smallest element.
15268     **/
15269     T kth_smallest(const unsigned int k) const {
15270       if (is_empty())
15271         throw CImgInstanceException(_cimg_instance
15272                                     "kth_smallest() : Empty instance.",
15273                                     cimg_instance);
15274       CImg<T> arr(*this);
15275       unsigned int l = 0, ir = size() - 1;
15276       for (;;) {
15277         if (ir<=l+1) {
15278           if (ir==l+1 && arr[ir]<arr[l]) cimg::swap(arr[l],arr[ir]);
15279           return arr[k];
15280         } else {
15281           const unsigned int mid = (l + ir)>>1;
15282           cimg::swap(arr[mid],arr[l+1]);
15283           if (arr[l]>arr[ir]) cimg::swap(arr[l],arr[ir]);
15284           if (arr[l+1]>arr[ir]) cimg::swap(arr[l+1],arr[ir]);
15285           if (arr[l]>arr[l+1]) cimg::swap(arr[l],arr[l+1]);
15286           unsigned int i = l + 1, j = ir;
15287           const T pivot = arr[l+1];
15288           for (;;) {
15289             do ++i; while (arr[i]<pivot);
15290             do --j; while (arr[j]>pivot);
15291             if (j<i) break;
15292             cimg::swap(arr[i],arr[j]);
15293           }
15294           arr[l+1] = arr[j];
15295           arr[j] = pivot;
15296           if (j>=k) ir = j - 1;
15297           if (j<=k) l = i;
15298         }
15299       }
15300       return 0;
15301     }
15302 
15303     //! Return the median pixel value.
15304     /**
15305      **/
15306     T median() const {
15307       if (is_empty())
15308         throw CImgInstanceException(_cimg_instance
15309                                     "median() : Empty instance.",
15310                                     cimg_instance);
15311       const unsigned int s = size();
15312       const T res = kth_smallest(s>>1);
15313       return (s%2)?res:((res+kth_smallest((s>>1)-1))/2);
15314     }
15315 
15316     //! Return the sum of all the pixel values.
15317     /**
15318      **/
15319     Tdouble sum() const {
15320       if (is_empty())
15321         throw CImgInstanceException(_cimg_instance
15322                                     "sum() : Empty instance.",
15323                                     cimg_instance);
15324       Tdouble res = 0;
15325       cimg_for(*this,ptrs,T) res+=(Tdouble)*ptrs;
15326       return res;
15327     }
15328 
15329     //! Return the average pixel value.
15330     /**
15331      **/
15332     Tdouble mean() const {
15333       if (is_empty())
15334         throw CImgInstanceException(_cimg_instance
15335                                     "mean() : Empty instance.",
15336                                     cimg_instance);
15337       Tdouble res = 0;
15338       cimg_for(*this,ptrs,T) res+=(Tdouble)*ptrs;
15339       return res/size();
15340     }
15341 
15342     //! Return the variance of the pixel values.
15343     /**
15344        \param variance_method Method used to estimate the variance. Can be :
15345        - \c 0 : Second moment, computed as
15346        \f$1/N \sum\limits_{k=1}^{N} (x_k - \bar x)^2 = 1/N \left( \sum\limits_{k=1}^N x_k^2 - \left( \sum\limits_{k=1}^N x_k \right)^2 / N \right)\f$
15347        with \f$ \bar x = 1/N \sum\limits_{k=1}^N x_k \f$.
15348        - \c 1 : Best unbiased estimator, computed as \f$\frac{1}{N-1} \sum\limits_{k=1}^{N} (x_k - \bar x)^2 \f$.
15349        - \c 2 : Least median of squares.
15350        - \c 3 : Least trimmed of squares.
15351     **/
15352     Tdouble variance(const unsigned int variance_method=1) const {
15353       Tdouble foo;
15354       return variance_mean(variance_method,foo);
15355     }
15356 
15357     //! Return the variance as well as the average of the pixel values.
15358     /**
15359        \param variance_method Method used to estimate the variance (see variance(const unsigned int) const).
15360        \param[out] mean Average pixel value.
15361     **/
15362     template<typename t>
15363     Tdouble variance_mean(const unsigned int variance_method, t& mean) const {
15364       if (is_empty())
15365         throw CImgInstanceException(_cimg_instance
15366                                     "variance_mean() : Empty instance.",
15367                                     cimg_instance);
15368 
15369       Tdouble variance = 0, average = 0;
15370       const unsigned long siz = size();
15371       switch (variance_method) {
15372       case 0 :{ // Least mean square (standard definition)
15373         Tdouble S = 0, S2 = 0;
15374         cimg_for(*this,ptrs,T) { const Tdouble val = (Tdouble)*ptrs; S+=val; S2+=val*val; }
15375         variance = (S2 - S*S/siz)/siz;
15376         average = S;
15377       } break;
15378       case 1 : { // Least mean square (robust definition)
15379         Tdouble S = 0, S2 = 0;
15380         cimg_for(*this,ptrs,T) { const Tdouble val = (Tdouble)*ptrs; S+=val; S2+=val*val; }
15381         variance = siz>1?(S2 - S*S/siz)/(siz - 1):0;
15382         average = S;
15383       } break;
15384       case 2 : { // Least Median of Squares (MAD)
15385         CImg<Tfloat> buf(*this);
15386         buf.sort();
15387         const unsigned long siz2 = siz>>1;
15388         const Tdouble med_i = (double)buf[siz2];
15389         cimg_for(buf,ptrs,Tfloat) { const Tdouble val = (Tdouble)*ptrs; *ptrs = (Tfloat)cimg::abs(val - med_i); average+=val; }
15390         buf.sort();
15391         const Tdouble sig = (Tdouble)(1.4828*buf[siz2]);
15392         variance = sig*sig;
15393       } break;
15394       default : { // Least trimmed of Squares
15395         CImg<Tfloat> buf(*this);
15396         const unsigned long siz2 = siz>>1;
15397         cimg_for(buf,ptrs,Tfloat) { const Tdouble val = (Tdouble)*ptrs; (*ptrs)=(Tfloat)((*ptrs)*val); average+=val; }
15398         buf.sort();
15399         Tdouble a = 0;
15400         const Tfloat *ptrs = buf._data;
15401         for (unsigned long j = 0; j<siz2; ++j) a+=(Tdouble)*(ptrs++);
15402         const Tdouble sig = (Tdouble)(2.6477*std::sqrt(a/siz2));
15403         variance = sig*sig;
15404       }
15405       }
15406       mean = (t)(average/siz);
15407       return variance>0?variance:0;
15408     }
15409 
15410     //! Return estimated variance of the noise.
15411     /**
15412        \param variance_method Method used to compute the variance (see variance(const unsigned int) const).
15413        \note Because of structures such as edges in images it is
15414        recommanded to use a robust variance estimation. The variance of the
15415        noise is estimated by computing the variance of the Laplacian \f$(\Delta
15416        I)^2 \f$ scaled by a factor \f$c\f$ insuring \f$ c E[(\Delta I)^2]=
15417        \sigma^2\f$ where \f$\sigma\f$ is the noise variance.
15418     **/
15419     Tdouble variance_noise(const unsigned int variance_method=2) const {
15420       if (is_empty())
15421         throw CImgInstanceException(_cimg_instance
15422                                     "variance_noise() : Empty instance.",
15423                                     cimg_instance);
15424 
15425       const unsigned long siz = size();
15426       if (!siz || !_data) return 0;
15427       if (variance_method>1) { // Compute a scaled version of the Laplacian.
15428         CImg<Tdouble> tmp(*this);
15429         if (_depth==1) {
15430           const Tdouble cste = 1.0/std::sqrt(20.0); // Depends on how the Laplacian is computed.
15431           CImg_3x3(I,T);
15432           cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) {
15433             tmp(x,y,c) = cste*((Tdouble)Inc + (Tdouble)Ipc + (Tdouble)Icn +
15434                                (Tdouble)Icp - 4*(Tdouble)Icc);
15435           }
15436         } else {
15437           const Tdouble cste = 1.0/std::sqrt(42.0); // Depends on how the Laplacian is computed.
15438           CImg_3x3x3(I,T);
15439           cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,T) {
15440             tmp(x,y,z,c) = cste*(
15441                                  (Tdouble)Incc + (Tdouble)Ipcc + (Tdouble)Icnc + (Tdouble)Icpc +
15442                                  (Tdouble)Iccn + (Tdouble)Iccp - 6*(Tdouble)Iccc);
15443           }
15444         }
15445         return tmp.variance(variance_method);
15446       }
15447 
15448       // Version that doesn't need intermediate images.
15449       Tdouble variance = 0, S = 0, S2 = 0;
15450       if (_depth==1) {
15451         const Tdouble cste = 1.0/std::sqrt(20.0);
15452         CImg_3x3(I,T);
15453         cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) {
15454           const Tdouble val = cste*((Tdouble)Inc + (Tdouble)Ipc +
15455                                     (Tdouble)Icn + (Tdouble)Icp - 4*(Tdouble)Icc);
15456           S+=val; S2+=val*val;
15457         }
15458       } else {
15459         const Tdouble cste = 1.0/std::sqrt(42.0);
15460         CImg_3x3x3(I,T);
15461         cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,T) {
15462           const Tdouble val = cste *
15463             ((Tdouble)Incc + (Tdouble)Ipcc + (Tdouble)Icnc +
15464              (Tdouble)Icpc +
15465              (Tdouble)Iccn + (Tdouble)Iccp - 6*(Tdouble)Iccc);
15466           S+=val; S2+=val*val;
15467         }
15468       }
15469       if (variance_method) variance = siz>1?(S2 - S*S/siz)/(siz - 1):0;
15470       else variance = (S2 - S*S/siz)/siz;
15471       return variance>0?variance:0;
15472     }
15473 
15474     //! Compute the MSE (Mean-Squared Error) between two images.
15475     /**
15476        \param img Image used as the second argument of the MSE operator.
15477     **/
15478     template<typename t>
15479     Tdouble MSE(const CImg<t>& img) const {
15480       if (img.size()!=size())
15481         throw CImgArgumentException(_cimg_instance
15482                                     "MSE() : Instance and specified image (%u,%u,%u,%u,%p) have different dimensions.",
15483                                     cimg_instance,
15484                                     img._width,img._height,img._depth,img._spectrum,img._data);
15485       Tdouble vMSE = 0;
15486       const t* ptr2 = img._data;
15487       cimg_for(*this,ptr1,T) {
15488         const Tdouble diff = (Tdouble)*ptr1 - (Tdouble)*(ptr2++);
15489         vMSE+=diff*diff;
15490       }
15491       const unsigned long siz = img.size();
15492       if (siz) vMSE/=siz;
15493       return vMSE;
15494     }
15495 
15496     //! Compute the PSNR (Peak Signal-to-Noise Ratio) between two images.
15497     /**
15498        \param img Image used as the second argument of the PSNR operator.
15499        \param max_value Maximum theoretical value of the signal.
15500      **/
15501     template<typename t>
15502     Tdouble PSNR(const CImg<t>& img, const Tdouble max_value=255) const {
15503       const Tdouble vMSE = (Tdouble)std::sqrt(MSE(img));
15504       return (vMSE!=0)?(Tdouble)(20*std::log10(max_value/vMSE)):(Tdouble)(cimg::type<Tdouble>::max());
15505     }
15506 
15507     //! Evaluate math formula.
15508     /**
15509        \param expression Math formula, as a C-string.
15510        \param x Value of the pre-defined variable \c x.
15511        \param y Value of the pre-defined variable \c y.
15512        \param z Value of the pre-defined variable \c z.
15513        \param c Value of the pre-defined variable \c c.
15514        \note Set \c expression to \c 0 to keep evaluating the last specified \c expression.
15515     **/
15516     double eval(const char *const expression, const double x=0, const double y=0, const double z=0, const double c=0) const {
15517       static _cimg_math_parser *mp = 0;
15518       if (expression) { delete mp; mp = 0; mp = new _cimg_math_parser(*this,expression,"eval"); }
15519       return mp?mp->eval(x,y,z,c):0;
15520     }
15521 
15522     //! Compute statistics vector from the pixel values.
15523     /**
15524        \param variance_method Method used to compute the variance (see variance(const unsigned int) const).
15525        \return Statistics vector as <tt>[min; max; mean; variance; xmin; ymin; zmin; cmin; xmax; ymax; zmax; cmax]</tt>.
15526     **/
15527     CImg<Tdouble> get_stats(const unsigned int variance_method=1) const {
15528       if (is_empty()) return CImg<doubleT>();
15529       const unsigned long siz = size();
15530       const T *const odata = _data;
15531       const T *pm = odata, *pM = odata;
15532       Tdouble S = 0, S2 = 0;
15533       T m = *pm, M = m;
15534       cimg_for(*this,ptrs,T) {
15535         const T val = *ptrs;
15536         const Tdouble _val = (Tdouble)val;
15537         if (val<m) { m = val; pm = ptrs; }
15538         if (val>M) { M = val; pM = ptrs; }
15539         S+=_val;
15540         S2+=_val*_val;
15541       }
15542       const Tdouble
15543         mean_value = S/siz,
15544         _variance_value = variance_method==0?(S2 - S*S/siz)/siz:
15545         (variance_method==1?(siz>1?(S2 - S*S/siz)/(siz - 1):0):
15546          variance(variance_method)),
15547         variance_value = _variance_value>0?_variance_value:0;
15548       int
15549         xm = 0, ym = 0, zm = 0, cm = 0,
15550         xM = 0, yM = 0, zM = 0, cM = 0;
15551       contains(*pm,xm,ym,zm,cm);
15552       contains(*pM,xM,yM,zM,cM);
15553       return CImg<Tdouble>(1,12).fill((Tdouble)m,(Tdouble)M,mean_value,variance_value,
15554                                       (Tdouble)xm,(Tdouble)ym,(Tdouble)zm,(Tdouble)cm,
15555                                       (Tdouble)xM,(Tdouble)yM,(Tdouble)zM,(Tdouble)cM);
15556     }
15557 
15558     //! Compute statistics vector from the pixel values \inplace.
15559     CImg<T>& stats(const unsigned int variance_method=1) {
15560       return get_stats(variance_method).move_to(*this);
15561     }
15562 
15563     //@}
15564     //-------------------------------------
15565     //
15566     //! \name Vector / Matrix Operations
15567     //@{
15568     //-------------------------------------
15569 
15570     //! Compute norm of the image, viewed as a matrix.
15571     /**
15572        \param magnitude_type Norm type. Can be :
15573        - \c -1 : Linf-norm
15574        - \c 0 : L2-norm
15575        - \c 1 : L1-norm
15576     **/
15577     Tdouble magnitude(const int magnitude_type=2) const {
15578       if (is_empty())
15579         throw CImgInstanceException(_cimg_instance
15580                                     "magnitude() : Empty instance.",
15581                                     cimg_instance);
15582       Tdouble res = 0;
15583       switch (magnitude_type) {
15584       case -1 : {
15585         cimg_for(*this,ptrs,T) { const Tdouble val = (Tdouble)cimg::abs(*ptrs); if (val>res) res = val; }
15586       } break;
15587       case 1 : {
15588         cimg_for(*this,ptrs,T) res+=(Tdouble)cimg::abs(*ptrs);
15589       } break;
15590       default : {
15591         cimg_for(*this,ptrs,T) res+=(Tdouble)cimg::sqr(*ptrs);
15592         res = (Tdouble)std::sqrt(res);
15593       }
15594       }
15595       return res;
15596     }
15597 
15598     //! Compute the trace of the image, viewed as a matrix.
15599     /**
15600      **/
15601     Tdouble trace() const {
15602       if (is_empty())
15603         throw CImgInstanceException(_cimg_instance
15604                                     "trace() : Empty instance.",
15605                                     cimg_instance);
15606       Tdouble res = 0;
15607       cimg_forX(*this,k) res+=(Tdouble)(*this)(k,k);
15608       return res;
15609     }
15610 
15611     //! Compute the determinant of the image, viewed as a matrix.
15612     /**
15613      **/
15614     Tdouble det() const {
15615       if (is_empty() || _width!=_height || _depth!=1 || _spectrum!=1)
15616         throw CImgInstanceException(_cimg_instance
15617                                     "det() : Instance is not a square matrix.",
15618                                     cimg_instance);
15619 
15620       switch (_width) {
15621       case 1 : return (Tdouble)((*this)(0,0));
15622       case 2 : return (Tdouble)((*this)(0,0))*(Tdouble)((*this)(1,1)) - (Tdouble)((*this)(0,1))*(Tdouble)((*this)(1,0));
15623       case 3 : {
15624         const Tdouble
15625           a = (Tdouble)_data[0], d = (Tdouble)_data[1], g = (Tdouble)_data[2],
15626           b = (Tdouble)_data[3], e = (Tdouble)_data[4], h = (Tdouble)_data[5],
15627           c = (Tdouble)_data[6], f = (Tdouble)_data[7], i = (Tdouble)_data[8];
15628         return i*a*e - a*h*f - i*b*d + b*g*f + c*d*h - c*g*e;
15629       }
15630       default : {
15631         CImg<Tfloat> lu(*this);
15632         CImg<uintT> indx;
15633         bool d;
15634         lu._LU(indx,d);
15635         Tdouble res = d?(Tdouble)1:(Tdouble)-1;
15636         cimg_forX(lu,i) res*=lu(i,i);
15637         return res;
15638       }
15639       }
15640       return 0;
15641     }
15642 
15643     //! Compute the dot product between instance and argument, viewed as matrices.
15644     /**
15645        \param img Image used as a second argument of the dot product.
15646     **/
15647     template<typename t>
15648     Tdouble dot(const CImg<t>& img) const {
15649       if (is_empty())
15650         throw CImgInstanceException(_cimg_instance
15651                                     "dot() : Empty instance.",
15652                                     cimg_instance);
15653       if (!img)
15654         throw CImgArgumentException(_cimg_instance
15655                                     "dot() : Empty specified image.",
15656                                     cimg_instance);
15657 
15658       const unsigned int nb = cimg::min(size(),img.size());
15659       Tdouble res = 0;
15660       for (unsigned int off = 0; off<nb; ++off) res+=(Tdouble)_data[off]*(Tdouble)img[off];
15661       return res;
15662     }
15663 
15664     //! Get vector-valued pixel located at specified position.
15665     /**
15666        \param x X-coordinate of the pixel value.
15667        \param y Y-coordinate of the pixel value.
15668        \param z Z-coordinate of the pixel value.
15669     **/
15670     CImg<T> get_vector_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const {
15671       _cimg_static CImg<T> res;
15672       if (res._height!=_spectrum) res.assign(1,_spectrum);
15673       const unsigned long whd = (unsigned long)_width*_height*_depth;
15674       const T *ptrs = data(x,y,z);
15675       T *ptrd = res._data;
15676       cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
15677       return res;
15678     }
15679 
15680     //! Get (square) matrix-valued pixel located at specified position.
15681     /**
15682        \param x X-coordinate of the pixel value.
15683        \param y Y-coordinate of the pixel value.
15684        \param z Z-coordinate of the pixel value.
15685        \note - The spectrum() of the image must be a square.
15686      **/
15687     CImg<T> get_matrix_at(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const {
15688       const int n = (int)std::sqrt((double)_spectrum);
15689       const T *ptrs = data(x,y,z,0);
15690       const unsigned long whd = (unsigned long)_width*_height*_depth;
15691       CImg<T> res(n,n);
15692       T *ptrd = res._data;
15693       cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
15694       return res;
15695     }
15696 
15697     //! Get tensor-valued pixel located at specified position.
15698     /**
15699        \param x X-coordinate of the pixel value.
15700        \param y Y-coordinate of the pixel value.
15701        \param z Z-coordinate of the pixel value.
15702     **/
15703     CImg<T> get_tensor_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const {
15704       const T *ptrs = data(x,y,z,0);
15705       const unsigned long whd = (unsigned long)_width*_height*_depth;
15706       if (_spectrum==6) return tensor(*ptrs,*(ptrs+whd),*(ptrs+2*whd),*(ptrs+3*whd),*(ptrs+4*whd),*(ptrs+5*whd));
15707       if (_spectrum==3) return tensor(*ptrs,*(ptrs+whd),*(ptrs+2*whd));
15708       return tensor(*ptrs);
15709     }
15710 
15711     //! Set vector-valued pixel at specified position.
15712     /**
15713        \param vec Vector to put on the instance image.
15714        \param x X-coordinate of the pixel value.
15715        \param y Y-coordinate of the pixel value.
15716        \param z Z-coordinate of the pixel value.
15717     **/
15718     template<typename t>
15719     CImg<T>& set_vector_at(const CImg<t>& vec, const unsigned int x, const unsigned int y=0, const unsigned int z=0) {
15720       if (x<_width && y<_height && z<_depth) {
15721         const t *ptrs = vec._data;
15722         const unsigned long whd = (unsigned long)_width*_height*_depth;
15723         T *ptrd = data(x,y,z);
15724         for (unsigned int k = cimg::min((unsigned int)vec.size(),_spectrum); k; --k) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
15725       }
15726       return *this;
15727     }
15728 
15729     //! Set (square) matrix-valued pixel at specified position.
15730     /**
15731        \param mat Matrix to put on the instance image.
15732        \param x X-coordinate of the pixel value.
15733        \param y Y-coordinate of the pixel value.
15734        \param z Z-coordinate of the pixel value.
15735     **/
15736     template<typename t>
15737     CImg<T>& set_matrix_at(const CImg<t>& mat, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
15738       return set_vector_at(mat,x,y,z);
15739     }
15740 
15741     //! Set tensor-valued pixel at specified position.
15742     /**
15743        \param ten Tensor to put on the instance image.
15744        \param x X-coordinate of the pixel value.
15745        \param y Y-coordinate of the pixel value.
15746        \param z Z-coordinate of the pixel value.
15747     **/
15748     template<typename t>
15749     CImg<T>& set_tensor_at(const CImg<t>& ten, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
15750       T *ptrd = data(x,y,z,0);
15751       const unsigned long siz = (unsigned long)_width*_height*_depth;
15752       if (ten._height==2) {
15753         *ptrd = (T)ten[0]; ptrd+=siz;
15754         *ptrd = (T)ten[1]; ptrd+=siz;
15755         *ptrd = (T)ten[3];
15756       }
15757       else {
15758         *ptrd = (T)ten[0]; ptrd+=siz;
15759         *ptrd = (T)ten[1]; ptrd+=siz;
15760         *ptrd = (T)ten[2]; ptrd+=siz;
15761         *ptrd = (T)ten[4]; ptrd+=siz;
15762         *ptrd = (T)ten[5]; ptrd+=siz;
15763         *ptrd = (T)ten[8];
15764       }
15765       return *this;
15766     }
15767 
15768     //! Unroll pixel values along axis \c y.
15769     /**
15770        \note Equivalent to \code unroll('y'); \endcode.
15771     **/
15772     CImg<T>& vector() {
15773       return unroll('y');
15774     }
15775 
15776     //! Unroll pixel values along axis \c y \newinstance.
15777     CImg<T> get_vector() const {
15778       return get_unroll('y');
15779     }
15780 
15781     //! Resize image to become a scalar square matrix.
15782     /**
15783      **/
15784     CImg<T>& matrix() {
15785       const unsigned long siz = size();
15786       switch (siz) {
15787       case 1 : break;
15788       case 4 : _width = _height = 2; break;
15789       case 9 : _width = _height = 3; break;
15790       case 16 : _width = _height = 4; break;
15791       case 25 : _width = _height = 5; break;
15792       case 36 : _width = _height = 6; break;
15793       case 49 : _width = _height = 7; break;
15794       case 64 : _width = _height = 8; break;
15795       case 81 : _width = _height = 9; break;
15796       case 100 : _width = _height = 10; break;
15797       default : {
15798         unsigned long i = 11, i2 = i*i;
15799         while (i2<siz) { i2+=2*i + 1; ++i; }
15800         if (i2==siz) _width = _height = i;
15801         else throw CImgInstanceException(_cimg_instance
15802                                          "matrix() : Invalid instance size %u (should be a square integer).",
15803                                          cimg_instance,
15804                                          siz);
15805       }
15806       }
15807       return *this;
15808     }
15809 
15810     //! Resize image to become a scalar square matrix \newinstance.
15811     CImg<T> get_matrix() const {
15812       return (+*this).matrix();
15813     }
15814 
15815     //! Resize image to become a symmetric tensor.
15816     /**
15817      **/
15818     CImg<T>& tensor() {
15819       return get_tensor().move_to(*this);
15820     }
15821 
15822     //! Resize image to become a symmetric tensor \newinstance.
15823     CImg<T> get_tensor() const {
15824       CImg<T> res;
15825       const unsigned long siz = size();
15826       switch (siz) {
15827       case 1 : break;
15828       case 3 :
15829         res.assign(2,2);
15830         res(0,0) = (*this)(0);
15831         res(1,0) = res(0,1) = (*this)(1);
15832         res(1,1) = (*this)(2);
15833         break;
15834       case 6 :
15835         res.assign(3,3);
15836         res(0,0) = (*this)(0);
15837         res(1,0) = res(0,1) = (*this)(1);
15838         res(2,0) = res(0,2) = (*this)(2);
15839         res(1,1) = (*this)(3);
15840         res(2,1) = res(1,2) = (*this)(4);
15841         res(2,2) = (*this)(5);
15842         break;
15843       default :
15844         throw CImgInstanceException(_cimg_instance
15845                                     "tensor() : Invalid instance size (does not define a 1x1, 2x2 or 3x3 tensor).",
15846                                     cimg_instance);
15847       }
15848       return res;
15849     }
15850 
15851     //! Resize image to become a diagonal matrix.
15852     /**
15853        \note Transform the image as a diagonal matrix so that each of its initial value becomes a diagonal coefficient.
15854     **/
15855     CImg<T>& diagonal() {
15856       return get_diagonal().move_to(*this);
15857     }
15858 
15859     //! Resize image to become a diagonal matrix \newinstance.
15860     CImg<T> get_diagonal() const {
15861       if (is_empty()) return *this;
15862       CImg<T> res(size(),size(),1,1,0);
15863       cimg_foroff(*this,off) res(off,off) = (*this)(off);
15864       return res;
15865     }
15866 
15867     //! Replace the image by an identity matrix.
15868     /**
15869        \note If the instance image is not square, it is resized to a square matrix using its maximum dimension as a reference.
15870     **/
15871     CImg<T>& identity_matrix() {
15872       return identity_matrix(cimg::max(_width,_height)).move_to(*this);
15873     }
15874 
15875     //! Replace the image by an identity matrix \newinstance.
15876     CImg<T> get_identity_matrix() const {
15877       return identity_matrix(cimg::max(_width,_height));
15878     }
15879 
15880     //! Fill image with a linear sequence of values.
15881     /**
15882        \param a0 Starting value of the sequence.
15883        \param a1 Ending value of the sequence.
15884     **/
15885     CImg<T>& sequence(const T a0, const T a1) {
15886       if (is_empty()) return *this;
15887       const unsigned int siz = size() - 1;
15888       T* ptr = _data;
15889       if (siz) {
15890         const Tdouble delta = (Tdouble)a1 - (Tdouble)a0;
15891         cimg_foroff(*this,l) *(ptr++) = (T)(a0 + delta*l/siz);
15892       } else *ptr = a0;
15893       return *this;
15894     }
15895 
15896     //! Fill image with a linear sequence of values \newinstance.
15897     CImg<T> get_sequence(const T a0, const T a1) const {
15898       return (+*this).sequence(a0,a1);
15899     }
15900 
15901     //! Transpose the image, viewed as a matrix.
15902     /**
15903        \note Equivalent to \code permute_axes("yxzc"); \endcode
15904     **/
15905     CImg<T>& transpose() {
15906       if (_width==1) { _width = _height; _height = 1; return *this; }
15907       if (_height==1) { _height = _width; _width = 1; return *this; }
15908       if (_width==_height) {
15909         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));
15910         return *this;
15911       }
15912       return get_transpose().move_to(*this);
15913     }
15914 
15915     //! Transpose the image, viewed as a matrix \newinstance.
15916     CImg<T> get_transpose() const {
15917       return get_permute_axes("yxzc");
15918     }
15919 
15920     //! Compute the cross product between two \c 1x3 images, viewed as 3d vectors.
15921     /**
15922        \param img Image used as the second argument of the cross product.
15923        \note The first argument of the cross product is \c *this.
15924      **/
15925     template<typename t>
15926     CImg<T>& cross(const CImg<t>& img) {
15927       if (_width!=1 || _height<3 || img._width!=1 || img._height<3)
15928         throw CImgInstanceException(_cimg_instance
15929                                     "cross() : Instance and/or specified image (%u,%u,%u,%u,%p) are not 3d vectors.",
15930                                     cimg_instance,
15931                                     img._width,img._height,img._depth,img._spectrum,img._data);
15932 
15933       const T x = (*this)[0], y = (*this)[1], z = (*this)[2];
15934       (*this)[0] = (T)(y*img[2] - z*img[1]);
15935       (*this)[1] = (T)(z*img[0] - x*img[2]);
15936       (*this)[2] = (T)(x*img[1] - y*img[0]);
15937       return *this;
15938     }
15939 
15940     //! Compute the cross product between two \c 1x3 images, viewed as 3d vectors \newinstance.
15941     template<typename t>
15942     CImg<_cimg_Tt> get_cross(const CImg<t>& img) const {
15943       return CImg<_cimg_Tt>(*this).cross(img);
15944     }
15945 
15946     //! Invert the instance image, viewed as a matrix.
15947     /**
15948        \param use_LU Choose the inverting algorithm. Can be :
15949        - \c true : LU-based matrix inversion.
15950        - \c false : SVD-based matrix inversion.
15951     **/
15952     CImg<T>& invert(const bool use_LU=true) {
15953       if (_width!=_height || _depth!=1 || _spectrum!=1)
15954         throw CImgInstanceException(_cimg_instance
15955                                     "invert() : Instance is not a square matrix.",
15956                                     cimg_instance);
15957 #ifdef cimg_use_lapack
15958       int INFO = (int)use_LU, N = _width, LWORK = 4*N, *const IPIV = new int[N];
15959       Tfloat
15960         *const lapA = new Tfloat[N*N],
15961         *const WORK = new Tfloat[LWORK];
15962       cimg_forXY(*this,k,l) lapA[k*N+l] = (Tfloat)((*this)(k,l));
15963       cimg::getrf(N,lapA,IPIV,INFO);
15964       if (INFO)
15965         cimg::warn(_cimg_instance
15966                    "invert() : LAPACK function dgetrf_() returned error code %d.",
15967                    cimg_instance,
15968                    INFO);
15969       else {
15970         cimg::getri(N,lapA,IPIV,WORK,LWORK,INFO);
15971         if (INFO)
15972           cimg::warn(_cimg_instance
15973                      "invert() : LAPACK function dgetri_() returned error code %d.",
15974                      cimg_instance,
15975                      INFO);
15976       }
15977       if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)(lapA[k*N+l]); else fill(0);
15978       delete[] IPIV; delete[] lapA; delete[] WORK;
15979 #else
15980       const double dete = _width>3?-1.0:det();
15981       if (dete!=0.0 && _width==2) {
15982         const double
15983           a = _data[0], c = _data[1],
15984           b = _data[2], d = _data[3];
15985         _data[0] = (T)(d/dete); _data[1] = (T)(-c/dete);
15986         _data[2] = (T)(-b/dete); _data[3] = (T)(a/dete);
15987       } else if (dete!=0.0 && _width==3) {
15988         const double
15989           a = _data[0], d = _data[1], g = _data[2],
15990           b = _data[3], e = _data[4], h = _data[5],
15991           c = _data[6], f = _data[7], i = _data[8];
15992         _data[0] = (T)((i*e-f*h)/dete), _data[1] = (T)((g*f-i*d)/dete), _data[2] = (T)((d*h-g*e)/dete);
15993         _data[3] = (T)((h*c-i*b)/dete), _data[4] = (T)((i*a-c*g)/dete), _data[5] = (T)((g*b-a*h)/dete);
15994         _data[6] = (T)((b*f-e*c)/dete), _data[7] = (T)((d*c-a*f)/dete), _data[8] = (T)((a*e-d*b)/dete);
15995       } else {
15996         if (use_LU) { // LU-based inverse computation
15997           CImg<Tfloat> A(*this), indx, col(1,_width);
15998           bool d;
15999           A._LU(indx,d);
16000           cimg_forX(*this,j) {
16001             col.fill(0);
16002             col(j) = 1;
16003             col._solve(A,indx);
16004             cimg_forX(*this,i) (*this)(j,i) = (T)col(i);
16005           }
16006         } else { // SVD-based inverse computation
16007           CImg<Tfloat> U(_width,_width), S(1,_width), V(_width,_width);
16008           SVD(U,S,V,false);
16009           U.transpose();
16010           cimg_forY(S,k) if (S[k]!=0) S[k]=1/S[k];
16011           S.diagonal();
16012           *this = V*S*U;
16013         }
16014       }
16015 #endif
16016       return *this;
16017     }
16018 
16019     //! Invert the instance image, viewed as a matrix \newinstance.
16020     CImg<Tfloat> get_invert(const bool use_LU=true) const {
16021       return CImg<Tfloat>(*this,false).invert(use_LU);
16022     }
16023 
16024     //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix.
16025     /**
16026     **/
16027     CImg<T>& pseudoinvert() {
16028       return get_pseudoinvert().move_to(*this);
16029     }
16030 
16031     //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix \newinstance.
16032     CImg<Tfloat> get_pseudoinvert() const {
16033       CImg<Tfloat> U, S, V;
16034       SVD(U,S,V);
16035       const Tfloat tolerance = (sizeof(Tfloat)<=4?5.96e-8f:1.11e-16f)*cimg::max(_width,_height)*S.max();
16036       cimg_forX(V,x) {
16037         const Tfloat s = S(x), invs = s>tolerance?1/s:(Tfloat)0;
16038         cimg_forY(V,y) V(x,y)*=invs;
16039       }
16040       return V*U.transpose();
16041     }
16042 
16043     //! Solve a system of linear equations.
16044     /**
16045        \param A Matrix of the linear system.
16046        \note Solve \c AX=B where \c B=*this.
16047     **/
16048     template<typename t>
16049     CImg<T>& solve(const CImg<t>& A) {
16050       if (_width!=1 || _depth!=1 || _spectrum!=1 || _height!=A._height || A._depth!=1 || A._spectrum!=1)
16051         throw CImgArgumentException(_cimg_instance
16052                                     "solve() : Instance and specified matrix (%u,%u,%u,%u,%p) have incompatible dimensions.",
16053                                     cimg_instance,
16054                                     A._width,A._height,A._depth,A._spectrum,A._data);
16055       typedef _cimg_Ttfloat Ttfloat;
16056       if (A._width==A._height) {
16057 #ifdef cimg_use_lapack
16058         char TRANS = 'N';
16059         int INFO, N = _height, LWORK = 4*N, one = 1, *const IPIV = new int[N];
16060         Ttfloat
16061           *const lapA = new Ttfloat[N*N],
16062           *const lapB = new Ttfloat[N],
16063           *const WORK = new Ttfloat[LWORK];
16064         cimg_forXY(A,k,l) lapA[k*N+l] = (Ttfloat)(A(k,l));
16065         cimg_forY(*this,i) lapB[i] = (Ttfloat)((*this)(i));
16066         cimg::getrf(N,lapA,IPIV,INFO);
16067         if (INFO)
16068           cimg::warn(_cimg_instance
16069                      "solve() : LAPACK library function dgetrf_() returned error code %d.",
16070                      cimg_instance,
16071                      INFO);
16072 
16073         if (!INFO) {
16074           cimg::getrs(TRANS,N,lapA,IPIV,lapB,INFO);
16075           if (INFO)
16076             cimg::warn(_cimg_instance
16077                        "solve() : LAPACK library function dgetrs_() returned error code %d.",
16078                        cimg_instance,
16079                        INFO);
16080         }
16081         if (!INFO) cimg_forY(*this,i) (*this)(i) = (T)(lapB[i]); else fill(0);
16082         delete[] IPIV; delete[] lapA; delete[] lapB; delete[] WORK;
16083 #else
16084         CImg<Ttfloat> lu(A,false);
16085         CImg<Ttfloat> indx;
16086         bool d;
16087         lu._LU(indx,d);
16088         _solve(lu,indx);
16089 #endif
16090       } else { // Least-square solution for non-square systems.
16091 #ifdef cimg_use_lapack
16092         char TRANS = 'N';
16093         int INFO, N = A._width, M = A._height, LWORK = -1, LDA = M, LDB = M, NRHS = _width;
16094         Ttfloat WORK_QUERY;
16095         Ttfloat
16096           * const lapA = new Ttfloat[M*N],
16097           * const lapB = new Ttfloat[M*NRHS];
16098         cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, &WORK_QUERY, LWORK, INFO);
16099         LWORK = (int) WORK_QUERY;
16100         Ttfloat *const WORK = new Ttfloat[LWORK];
16101         cimg_forXY(A,k,l) lapA[k*M+l] = (Ttfloat)(A(k,l));
16102         cimg_forXY(*this,k,l) lapB[k*M+l] = (Ttfloat)((*this)(k,l));
16103         cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, WORK, LWORK, INFO);
16104         if (INFO != 0)
16105           cimg::warn(_cimg_instance
16106                      "solve() : LAPACK library function sgels() returned error code %d.",
16107                      cimg_instance,
16108                      INFO);
16109         assign(NRHS, N);
16110         if (!INFO != 0)
16111           cimg_forXY(*this,k,l) (*this)(k,l) = (T) lapB[k*M+l];
16112         else
16113           assign(A.get_pseudoinvert()*(*this));
16114         delete[] lapA; delete[] lapB; delete[] WORK;
16115 #else
16116         assign(A.get_pseudoinvert()*(*this));
16117 #endif
16118       }
16119       return *this;
16120     }
16121 
16122     //! Solve a system of linear equations \newinstance.
16123     template<typename t>
16124     CImg<_cimg_Ttfloat> get_solve(const CImg<t>& A) const {
16125       return CImg<_cimg_Ttfloat>(*this,false).solve(A);
16126     }
16127 
16128     template<typename t, typename ti>
16129     CImg<T>& _solve(const CImg<t>& A, const CImg<ti>& indx) {
16130       typedef _cimg_Ttfloat Ttfloat;
16131       const int N = size();
16132       int ii = -1;
16133       Ttfloat sum;
16134       for (int i = 0; i<N; ++i) {
16135         const int ip = (int)indx[i];
16136         Ttfloat sum = (*this)(ip);
16137         (*this)(ip) = (*this)(i);
16138         if (ii>=0) for (int j = ii; j<=i-1; ++j) sum-=A(j,i)*(*this)(j);
16139         else if (sum!=0) ii = i;
16140         (*this)(i) = (T)sum;
16141       }
16142       for (int i = N - 1; i>=0; --i) {
16143         sum = (*this)(i);
16144         for (int j = i + 1; j<N; ++j) sum-=A(j,i)*(*this)(j);
16145         (*this)(i) = (T)(sum/A(i,i));
16146       }
16147       return *this;
16148     }
16149 
16150     //! Solve a tridiagonal system of linear equations.
16151     /**
16152        \param A Coefficients of the tridiagonal system.
16153        A is a tridiagonal matrix A = [ b0,c0,0,...; a1,b1,c1,0,... ; ... ; ...,0,aN,bN ],
16154        stored as a 3 columns matrix
16155        \note Solve AX=B where \c B=*this, using the Thomas algorithm.
16156     **/
16157     template<typename t>
16158     CImg<T>& solve_tridiagonal(const CImg<t>& A) {
16159       const unsigned int siz = (int)size();
16160       if (A._width!=3 || A._height!=siz)
16161         throw CImgArgumentException(_cimg_instance
16162                                     "solve_tridiagonal() : Instance and tridiagonal matrix "
16163                                     "(%u,%u,%u,%u,%p) have incompatible dimensions.",
16164                                     cimg_instance,
16165                                     A._width,A._height,A._depth,A._spectrum,A._data);
16166       typedef _cimg_Ttfloat Ttfloat;
16167       const Ttfloat epsilon = 1e-4;
16168       CImg<Ttfloat> B = A.get_column(1), V(*this,false);
16169       for (int i = 1; i<(int)siz; ++i) {
16170         const Ttfloat m = A(0,i)/(B[i-1]?B[i-1]:epsilon);
16171         B[i] -= m*A(2,i-1);
16172         V[i] -= m*V[i-1];
16173       }
16174       (*this)[siz-1] = (T)(V[siz-1]/(B[siz-1]?B[siz-1]:epsilon));
16175       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));
16176       return *this;
16177     }
16178 
16179     //! Solve a tridiagonal system of linear equations \newinstance.
16180     template<typename t>
16181     CImg<_cimg_Ttfloat> get_solve_tridiagonal(const CImg<t>& A) const {
16182       return CImg<_cimg_Ttfloat>(*this,false).solve_tridiagonal(A);
16183     }
16184 
16185     //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix.
16186     /**
16187        \param[out] val Vector of the estimated eigenvalues, in decreasing order.
16188        \param[out] vec Matrix of the estimated eigenvalues, sorted by columns.
16189     **/
16190     template<typename t>
16191     const CImg<T>& eigen(CImg<t>& val, CImg<t> &vec) const {
16192       if (is_empty()) { val.assign(); vec.assign(); }
16193       else {
16194         if (_width!=_height || _depth>1 || _spectrum>1)
16195           throw CImgInstanceException(_cimg_instance
16196                                       "eigen() : Instance is not a square matrix.",
16197                                       cimg_instance);
16198 
16199         if (val.size()<(unsigned long)_width) val.assign(1,_width);
16200         if (vec.size()<(unsigned long)_width*_width) vec.assign(_width,_width);
16201         switch (_width) {
16202         case 1 : { val[0] = (t)(*this)[0]; vec[0] = (t)1; } break;
16203         case 2 : {
16204           const double a = (*this)[0], b = (*this)[1], c = (*this)[2], d = (*this)[3], e = a + d;
16205           double f = e*e - 4*(a*d - b*c);
16206           if (f<0)
16207             cimg::warn(_cimg_instance
16208                        "eigen() : Complex eigenvalues found.",
16209                        cimg_instance);
16210 
16211           f = std::sqrt(f);
16212           const double l1 = 0.5*(e-f), l2 = 0.5*(e+f);
16213           const double theta1 = std::atan2(l2-a,b), theta2 = std::atan2(l1-a,b);
16214           val[0] = (t)l2;
16215           val[1] = (t)l1;
16216           vec(0,0) = (t)std::cos(theta1);
16217           vec(0,1) = (t)std::sin(theta1);
16218           vec(1,0) = (t)std::cos(theta2);
16219           vec(1,1) = (t)std::sin(theta2);
16220         } break;
16221         default :
16222           throw CImgInstanceException(_cimg_instance
16223                                       "eigen() : Eigenvalues computation of general matrices is limited to 2x2 matrices.",
16224                                       cimg_instance);
16225         }
16226       }
16227       return *this;
16228     }
16229 
16230     //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix.
16231     /**
16232        \return A list of two images <tt>[val; vec]</tt>, whose meaning is similar as in eigen(CImg<t>&,CImg<t>&) const.
16233     **/
16234     CImgList<Tfloat> get_eigen() const {
16235       CImgList<Tfloat> res(2);
16236       eigen(res[0],res[1]);
16237       return res;
16238     }
16239 
16240     //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix.
16241     /**
16242        \param[out] val Vector of the estimated eigenvalues, in decreasing order.
16243        \param[out] vec Matrix of the estimated eigenvalues, sorted by columns.
16244     **/
16245     template<typename t>
16246     const CImg<T>& symmetric_eigen(CImg<t>& val, CImg<t>& vec) const {
16247       if (is_empty()) { val.assign(); vec.assign(); }
16248       else {
16249 #ifdef cimg_use_lapack
16250         char JOB = 'V', UPLO = 'U';
16251         int N = _width, LWORK = 4*N, INFO;
16252         Tfloat
16253           *const lapA = new Tfloat[N*N],
16254           *const lapW = new Tfloat[N],
16255           *const WORK = new Tfloat[LWORK];
16256         cimg_forXY(*this,k,l) lapA[k*N+l] = (Tfloat)((*this)(k,l));
16257         cimg::syev(JOB,UPLO,N,lapA,lapW,WORK,LWORK,INFO);
16258         if (INFO)
16259           cimg::warn(_cimg_instance
16260                      "symmetric_eigen() : LAPACK library function dsyev_() returned error code %d.",
16261                      cimg_instance,
16262                      INFO);
16263 
16264         val.assign(1,N);
16265         vec.assign(N,N);
16266         if (!INFO) {
16267           cimg_forY(val,i) val(i) = (T)lapW[N-1-i];
16268           cimg_forXY(vec,k,l) vec(k,l) = (T)(lapA[(N-1-k)*N+l]);
16269         } else { val.fill(0); vec.fill(0); }
16270         delete[] lapA; delete[] lapW; delete[] WORK;
16271 #else
16272         if (_width!=_height || _depth>1 || _spectrum>1)
16273           throw CImgInstanceException(_cimg_instance
16274                                       "eigen() : Instance is not a square matrix.",
16275                                       cimg_instance);
16276 
16277         val.assign(1,_width);
16278         if (vec._data) vec.assign(_width,_width);
16279         if (_width<3) {
16280           eigen(val,vec);
16281           if (_width==2) { vec[1] = -vec[2]; vec[3] = vec[0]; } // Force orthogonality for 2x2 matrices.
16282           return *this;
16283         }
16284         CImg<t> V(_width,_width);
16285         SVD(vec,val,V,false);
16286         bool is_ambiguous = false;
16287         float eig = 0;
16288         cimg_forY(val,p) {       // check for ambiguous cases.
16289           if (val[p]>eig) eig = (float)val[p];
16290           t scal = 0;
16291           cimg_forY(vec,y) scal+=vec(p,y)*V(p,y);
16292           if (cimg::abs(scal)<0.9f) is_ambiguous = true;
16293           if (scal<0) val[p] = -val[p];
16294         }
16295         if (is_ambiguous) {
16296           ++(eig*=2);
16297           SVD(vec,val,V,false,40,eig);
16298           val-=eig;
16299         }
16300         CImg<intT> permutations;  // sort eigenvalues in decreasing order
16301         CImg<t> tmp(_width);
16302         val.sort(permutations,false);
16303         cimg_forY(vec,k) {
16304           cimg_forY(permutations,y) tmp(y) = vec(permutations(y),k);
16305           std::memcpy(vec.data(0,k),tmp._data,sizeof(t)*_width);
16306         }
16307 #endif
16308       }
16309       return *this;
16310     }
16311 
16312     //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix.
16313     /**
16314        \return A list of two images <tt>[val; vec]</tt>, whose meaning are similar as in symmetric_eigen(CImg<t>&,CImg<t>&) const.
16315     **/
16316     CImgList<Tfloat> get_symmetric_eigen() const {
16317       CImgList<Tfloat> res(2);
16318       symmetric_eigen(res[0],res[1]);
16319       return res;
16320     }
16321 
16322     //! Sort pixel values and get sorting permutations.
16323     /**
16324        \param[out] permutations Permutation map used for the sorting.
16325        \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way.
16326     **/
16327     template<typename t>
16328     CImg<T>& sort(CImg<t>& permutations, const bool is_increasing=true) {
16329       permutations.assign(_width,_height,_depth,_spectrum);
16330       if (is_empty()) return *this;
16331       cimg_foroff(permutations,off) permutations[off] = (t)off;
16332       return _quicksort(0,size()-1,permutations,is_increasing,true);
16333     }
16334 
16335     //! Sort pixel values and get sorting permutations \newinstance.
16336     template<typename t>
16337     CImg<T> get_sort(CImg<t>& permutations, const bool is_increasing=true) const {
16338       return (+*this).sort(permutations,is_increasing);
16339     }
16340 
16341     //! Sort pixel values.
16342     /**
16343        \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way.
16344        \param axis Tells if the value sorting must be done along a specific axis. Can be :
16345        - \c 0 : All pixel values are sorted, independently on their initial position.
16346        - \c 'x' : Image columns are sorted, according to the first value in each column.
16347        - \c 'y' : Image rows are sorted, according to the first value in each row.
16348        - \c 'z' : Image slices are sorted, according to the first value in each slice.
16349        - \c 'c' : Image channels are sorted, according to the first value in each channel.
16350     **/
16351     CImg<T>& sort(const bool is_increasing=true, const char axis=0) {
16352       if (is_empty()) return *this;
16353       CImg<uintT> perm;
16354       switch (cimg::uncase(axis)) {
16355       case 0 :
16356         _quicksort(0,size()-1,perm,is_increasing,false);
16357         break;
16358       case 'x' : {
16359         perm.assign(_width);
16360         get_crop(0,0,0,0,_width-1,0,0,0).sort(perm,is_increasing);
16361         CImg<T> img(*this,false);
16362         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(perm[x],y,z,c);
16363       } break;
16364       case 'y' : {
16365         perm.assign(_height);
16366         get_crop(0,0,0,0,0,_height-1,0,0).sort(perm,is_increasing);
16367         CImg<T> img(*this,false);
16368         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,perm[y],z,c);
16369       } break;
16370       case 'z' : {
16371         perm.assign(_depth);
16372         get_crop(0,0,0,0,0,0,_depth-1,0).sort(perm,is_increasing);
16373         CImg<T> img(*this,false);
16374         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,perm[z],c);
16375       } break;
16376       case 'c' : {
16377         perm.assign(_spectrum);
16378         get_crop(0,0,0,0,0,0,0,_spectrum-1).sort(perm,is_increasing);
16379         CImg<T> img(*this,false);
16380         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,z,perm[c]);
16381       } break;
16382       default :
16383         throw CImgArgumentException(_cimg_instance
16384                                     "sort() : Invalid specified axis '%c' "
16385                                     "(should be { x | y | z | c }).",
16386                                     cimg_instance,axis);
16387       }
16388       return *this;
16389     }
16390 
16391     //! Sort pixel values \newinstance.
16392     CImg<T> get_sort(const bool is_increasing=true, const char axis=0) const {
16393       return (+*this).sort(is_increasing,axis);
16394     }
16395 
16396     template<typename t>
16397     CImg<T>& _quicksort(const int indm, const int indM, CImg<t>& permutations, const bool is_increasing, const bool is_permutations) {
16398       if (indm<indM) {
16399         const int mid = (indm + indM)/2;
16400         if (is_increasing) {
16401           if ((*this)[indm]>(*this)[mid]) {
16402             cimg::swap((*this)[indm],(*this)[mid]); if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
16403           }
16404           if ((*this)[mid]>(*this)[indM]) {
16405             cimg::swap((*this)[indM],(*this)[mid]); if (is_permutations) cimg::swap(permutations[indM],permutations[mid]);
16406           }
16407           if ((*this)[indm]>(*this)[mid]) {
16408             cimg::swap((*this)[indm],(*this)[mid]); if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
16409           }
16410         } else {
16411           if ((*this)[indm]<(*this)[mid]) {
16412             cimg::swap((*this)[indm],(*this)[mid]); if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
16413           }
16414           if ((*this)[mid]<(*this)[indM]) {
16415             cimg::swap((*this)[indM],(*this)[mid]); if (is_permutations) cimg::swap(permutations[indM],permutations[mid]);
16416           }
16417           if ((*this)[indm]<(*this)[mid]) {
16418             cimg::swap((*this)[indm],(*this)[mid]); if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
16419           }
16420         }
16421         if (indM - indm>=3) {
16422           const T pivot = (*this)[mid];
16423           int i = indm, j = indM;
16424           if (is_increasing) {
16425             do {
16426               while ((*this)[i]<pivot) ++i;
16427               while ((*this)[j]>pivot) --j;
16428               if (i<=j) {
16429                 if (is_permutations) cimg::swap(permutations[i],permutations[j]);
16430                 cimg::swap((*this)[i++],(*this)[j--]);
16431               }
16432             } while (i<=j);
16433           } else {
16434             do {
16435               while ((*this)[i]>pivot) ++i;
16436               while ((*this)[j]<pivot) --j;
16437               if (i<=j) {
16438                 if (is_permutations) cimg::swap(permutations[i],permutations[j]);
16439                 cimg::swap((*this)[i++],(*this)[j--]);
16440               }
16441             } while (i<=j);
16442           }
16443           if (indm<j) _quicksort(indm,j,permutations,is_increasing,is_permutations);
16444           if (i<indM) _quicksort(i,indM,permutations,is_increasing,is_permutations);
16445         }
16446       }
16447       return *this;
16448     }
16449 
16450     //! Compute the SVD of the instance image, viewed as a general matrix.
16451     /**
16452        Compute the SVD decomposition \c *this=U*S*V' where \c U and \c V are orthogonal matrices
16453        and \c S is a diagonal matrix. \c V' denotes the matrix transpose of \c V.
16454        \param[out] U First matrix of the SVD product.
16455        \param[out] S Coefficients of the second (diagonal) matrix of the SVD product. These coefficients are stored as a vector.
16456        \param[out] V Third matrix of the SVD product.
16457        \param sorting Tells if the diagonal coefficients are sorted (in decreasing order).
16458        \param max_iteration Maximum number of iterations considered for the algorithm convergence.
16459        \param lambda Epsilon used for the algorithm convergence.
16460        \note The instance matrix can be computed from \c U,\c S and \c V by
16461        \code
16462        const CImg<> A;  // Input matrix (assumed to contain some values).
16463        CImg<> U,S,V;
16464        A.SVD(U,S,V)
16465        \endcode
16466     **/
16467     template<typename t>
16468     const CImg<T>& SVD(CImg<t>& U, CImg<t>& S, CImg<t>& V, const bool sorting=true,
16469                        const unsigned int max_iteration=40, const float lambda=0) const {
16470       if (is_empty()) { U.assign(); S.assign(); V.assign(); }
16471       else {
16472         U = *this;
16473         if (lambda!=0) {
16474           const unsigned int delta = cimg::min(U._width,U._height);
16475           for (unsigned int i = 0; i<delta; ++i) U(i,i) = (t)(U(i,i) + lambda);
16476         }
16477         if (S.size()<_width) S.assign(1,_width);
16478         if (V._width<_width || V._height<_height) V.assign(_width,_width);
16479         CImg<t> rv1(_width);
16480         t anorm = 0, c, f, g = 0, h, s, scale = 0;
16481         int l = 0, nm = 0;
16482 
16483         cimg_forX(U,i) {
16484           l = i+1; rv1[i] = scale*g; g = s = scale = 0;
16485           if (i<height()) {
16486             for (int k = i; k<height(); ++k) scale+= cimg::abs(U(i,k));
16487             if (scale) {
16488               for (int k = i; k<height(); ++k) { U(i,k)/=scale; s+= U(i,k)*U(i,k); }
16489               f = U(i,i); g = (t)((f>=0?-1:1)*std::sqrt(s)); h=f*g-s; U(i,i) = f-g;
16490               for (int j = l; j<width(); ++j) {
16491                 s = 0;
16492                 for (int k=i; k<height(); ++k) s+= U(i,k)*U(j,k);
16493                 f = s/h;
16494                 for (int k = i; k<height(); ++k) U(j,k)+= f*U(i,k);
16495               }
16496               for (int k = i; k<height(); ++k) U(i,k)*= scale;
16497             }
16498           }
16499           S[i]=scale*g;
16500 
16501           g = s = scale = 0;
16502           if (i<height() && i!=width()-1) {
16503             for (int k = l; k<width(); ++k) scale+=cimg::abs(U(k,i));
16504             if (scale) {
16505               for (int k = l; k<width(); ++k) { U(k,i)/= scale; s+= U(k,i)*U(k,i); }
16506               f = U(l,i); g = (t)((f>=0?-1:1)*std::sqrt(s)); h = f*g-s; U(l,i) = f-g;
16507               for (int k = l; k<width(); ++k) rv1[k]=U(k,i)/h;
16508               for (int j = l; j<height(); ++j) {
16509                 s = 0;
16510                 for (int k = l; k<width(); ++k) s+= U(k,j)*U(k,i);
16511                 for (int k = l; k<width(); ++k) U(k,j)+= s*rv1[k];
16512               }
16513               for (int k = l; k<width(); ++k) U(k,i)*= scale;
16514             }
16515           }
16516           anorm = (t)cimg::max((float)anorm,(float)(cimg::abs(S[i])+cimg::abs(rv1[i])));
16517         }
16518 
16519         for (int i = width()-1; i>=0; --i) {
16520           if (i<width()-1) {
16521             if (g) {
16522               for (int j = l; j<width(); ++j) V(i,j) =(U(j,i)/U(l,i))/g;
16523               for (int j = l; j<width(); ++j) {
16524                 s = 0;
16525                 for (int k = l; k<width(); ++k) s+= U(k,i)*V(j,k);
16526                 for (int k = l; k<width(); ++k) V(j,k)+= s*V(i,k);
16527               }
16528             }
16529             for (int j = l; j<width(); ++j) V(j,i) = V(i,j) = (t)0.0;
16530           }
16531           V(i,i) = (t)1.0; g = rv1[i]; l = i;
16532         }
16533 
16534         for (int i = cimg::min(width(),height())-1; i>=0; --i) {
16535           l = i+1; g = S[i];
16536           for (int j = l; j<width(); ++j) U(j,i) = 0;
16537           if (g) {
16538             g = 1/g;
16539             for (int j = l; j<width(); ++j) {
16540               s = 0; for (int k = l; k<height(); ++k) s+= U(i,k)*U(j,k);
16541               f = (s/U(i,i))*g;
16542               for (int k = i; k<height(); ++k) U(j,k)+= f*U(i,k);
16543             }
16544             for (int j = i; j<height(); ++j) U(i,j)*= g;
16545           } else for (int j = i; j<height(); ++j) U(i,j) = 0;
16546           ++U(i,i);
16547         }
16548 
16549         for (int k = width()-1; k>=0; --k) {
16550           for (unsigned int its = 0; its<max_iteration; ++its) {
16551             bool flag = true;
16552             for (l = k; l>=1; --l) {
16553               nm = l-1;
16554               if ((cimg::abs(rv1[l])+anorm)==anorm) { flag = false; break; }
16555               if ((cimg::abs(S[nm])+anorm)==anorm) break;
16556             }
16557             if (flag) {
16558               c = 0; s = 1;
16559               for (int i = l; i<=k; ++i) {
16560                 f = s*rv1[i]; rv1[i] = c*rv1[i];
16561                 if ((cimg::abs(f)+anorm)==anorm) break;
16562                 g = S[i]; h = (t)cimg::_pythagore(f,g); S[i] = h; h = 1/h; c = g*h; s = -f*h;
16563                 cimg_forY(U,j) { const t y = U(nm,j), z = U(i,j); U(nm,j) = y*c + z*s; U(i,j) = z*c - y*s; }
16564               }
16565             }
16566             const t z = S[k];
16567             if (l==k) { if (z<0) { S[k] = -z; cimg_forX(U,j) V(k,j) = -V(k,j); } break; }
16568             nm = k-1;
16569             t x = S[l], y = S[nm];
16570             g = rv1[nm]; h = rv1[k];
16571             f = ((y-z)*(y+z)+(g-h)*(g+h))/(2*h*y);
16572             g = (t)cimg::_pythagore(f,1.0);
16573             f = ((x-z)*(x+z)+h*((y/(f+ (f>=0?g:-g)))-h))/x;
16574             c = s = 1;
16575             for (int j = l; j<=nm; ++j) {
16576               const int i = j+1;
16577               g = rv1[i]; h = s*g; g = c*g;
16578               t y = S[i];
16579               t z = (t)cimg::_pythagore(f,h);
16580               rv1[j] = z; c = f/z; s = h/z;
16581               f = x*c+g*s; g = g*c-x*s; h = y*s; y*=c;
16582               cimg_forX(U,jj) { const t x = V(j,jj), z = V(i,jj); V(j,jj) = x*c + z*s; V(i,jj) = z*c - x*s; }
16583               z = (t)cimg::_pythagore(f,h); S[j] = z;
16584               if (z) { z = 1/z; c = f*z; s = h*z; }
16585               f = c*g+s*y; x = c*y-s*g;
16586               cimg_forY(U,jj) { const t y = U(j,jj); z = U(i,jj); U(j,jj) = y*c + z*s; U(i,jj) = z*c - y*s; }
16587             }
16588             rv1[l] = 0; rv1[k]=f; S[k]=x;
16589           }
16590         }
16591 
16592         if (sorting) {
16593           CImg<intT> permutations;
16594           CImg<t> tmp(_width);
16595           S.sort(permutations,false);
16596           cimg_forY(U,k) {
16597             cimg_forY(permutations,y) tmp(y) = U(permutations(y),k);
16598             std::memcpy(U.data(0,k),tmp._data,sizeof(t)*_width);
16599           }
16600           cimg_forY(V,k) {
16601             cimg_forY(permutations,y) tmp(y) = V(permutations(y),k);
16602             std::memcpy(V.data(0,k),tmp._data,sizeof(t)*_width);
16603           }
16604         }
16605       }
16606       return *this;
16607     }
16608 
16609     //! Compute the SVD of the instance image, viewed as a general matrix.
16610     /**
16611        \return A list of three images <tt>[U; S; V]</tt>, whose meaning is similar as in SVD(CImg<t>&,CImg<t>&,CImg<t>&,bool,unsigned int,float) const.
16612     **/
16613     CImgList<Tfloat> get_SVD(const bool sorting=true,
16614                              const unsigned int max_iteration=40, const float lambda=0) const {
16615       CImgList<Tfloat> res(3);
16616       SVD(res[0],res[1],res[2],sorting,max_iteration,lambda);
16617       return res;
16618     }
16619 
16620     // [internal] Compute the LU decomposition of a permuted matrix.
16621     template<typename t>
16622     CImg<T>& _LU(CImg<t>& indx, bool& d) {
16623       const int N = width();
16624       int imax = 0;
16625       CImg<Tfloat> vv(N);
16626       indx.assign(N);
16627       d = true;
16628       cimg_forX(*this,i) {
16629         Tfloat vmax = 0;
16630         cimg_forX(*this,j) {
16631           const Tfloat tmp = cimg::abs((*this)(j,i));
16632           if (tmp>vmax) vmax = tmp;
16633         }
16634         if (vmax==0) { indx.fill(0); return fill(0); }
16635         vv[i] = 1/vmax;
16636       }
16637       cimg_forX(*this,j) {
16638         for (int i = 0; i<j; ++i) {
16639           Tfloat sum=(*this)(j,i);
16640           for (int k = 0; k<i; ++k) sum-=(*this)(k,i)*(*this)(j,k);
16641           (*this)(j,i) = (T)sum;
16642         }
16643         Tfloat vmax = 0;
16644         for (int i = j; i<width(); ++i) {
16645           Tfloat sum=(*this)(j,i);
16646           for (int k = 0; k<j; ++k) sum-=(*this)(k,i)*(*this)(j,k);
16647           (*this)(j,i) = (T)sum;
16648           const Tfloat tmp = vv[i]*cimg::abs(sum);
16649           if (tmp>=vmax) { vmax=tmp; imax=i; }
16650         }
16651         if (j!=imax) {
16652           cimg_forX(*this,k) cimg::swap((*this)(k,imax),(*this)(k,j));
16653           d =!d;
16654           vv[imax] = vv[j];
16655         }
16656         indx[j] = (t)imax;
16657         if ((*this)(j,j)==0) (*this)(j,j) = (T)1e-20;
16658         if (j<N) {
16659           const Tfloat tmp = 1/(Tfloat)(*this)(j,j);
16660           for (int i=j+1; i<N; ++i) (*this)(j,i) = (T)((*this)(j,i)*tmp);
16661         }
16662       }
16663       return *this;
16664     }
16665 
16666     //! Compute minimal path in a graph, using the Dijkstra algorithm.
16667     /**
16668        \param distance An object having operator()(unsigned int i, unsigned int j) which returns distance between two nodes (i,j).
16669        \param nb_nodes Number of graph nodes.
16670        \param starting_node Indice of the starting node.
16671        \param ending_node Indice of the ending node (set to ~0U to ignore ending node).
16672        \param previous_node Array that gives the previous node indice in the path to the starting node (optional parameter).
16673        \return Array of distances of each node to the starting node.
16674     **/
16675     template<typename tf, typename t>
16676     static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
16677                             const unsigned int starting_node, const unsigned int ending_node,
16678                             CImg<t>& previous_node) {
16679       if (starting_node>=nb_nodes)
16680         throw CImgArgumentException("CImg<%s>::dijkstra() : Specified indice of starting node %u is higher than number of nodes %u.",
16681                                     pixel_type(),starting_node,nb_nodes);
16682       CImg<T> dist(1,nb_nodes,1,1,cimg::type<T>::max());
16683       dist(starting_node) = 0;
16684       previous_node.assign(1,nb_nodes,1,1,(t)-1);
16685       previous_node(starting_node) = (t)starting_node;
16686       CImg<uintT> Q(nb_nodes);
16687       cimg_forX(Q,u) Q(u) = u;
16688       cimg::swap(Q(starting_node),Q(0));
16689       unsigned int sizeQ = nb_nodes;
16690       while (sizeQ) {
16691         // Update neighbors from minimal vertex
16692         const unsigned int umin = Q(0);
16693         if (umin==ending_node) sizeQ = 0;
16694         else {
16695           const T dmin = dist(umin);
16696           const T infty = cimg::type<T>::max();
16697           for (unsigned int q = 1; q<sizeQ; ++q) {
16698             const unsigned int v = Q(q);
16699             const T d = (T)distance(v,umin);
16700             if (d<infty) {
16701               const T alt = dmin + d;
16702               if (alt<dist(v)) {
16703                 dist(v) = alt;
16704                 previous_node(v) = (t)umin;
16705                 const T distpos = dist(Q(q));
16706                 for (unsigned int pos = q, par = 0; pos && distpos<dist(Q(par=(pos+1)/2-1)); pos=par) cimg::swap(Q(pos),Q(par));
16707               }
16708             }
16709           }
16710           // Remove minimal vertex from queue
16711           Q(0) = Q(--sizeQ);
16712           const T distpos = dist(Q(0));
16713           for (unsigned int pos = 0, left = 0, right = 0;
16714                ((right=2*(pos+1),(left=right-1))<sizeQ && distpos>dist(Q(left))) || (right<sizeQ && distpos>dist(Q(right)));) {
16715             if (right<sizeQ) {
16716               if (dist(Q(left))<dist(Q(right))) { cimg::swap(Q(pos),Q(left)); pos = left; }
16717               else { cimg::swap(Q(pos),Q(right)); pos = right; }
16718             } else { cimg::swap(Q(pos),Q(left)); pos = left; }
16719           }
16720         }
16721       }
16722       return dist;
16723     }
16724 
16725     //! Return minimal path in a graph, using the Dijkstra algorithm.
16726     template<typename tf, typename t>
16727     static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
16728                             const unsigned int starting_node, const unsigned int ending_node=~0U) {
16729       CImg<uintT> foo;
16730       return dijkstra(distance,nb_nodes,starting_node,ending_node,foo);
16731     }
16732 
16733     //! Return minimal path in a graph, using the Dijkstra algorithm.
16734     /**
16735        \param starting_node Indice of the starting node.
16736        \param ending_node Indice of the ending node.
16737        \param previous_node Array that gives the previous node indice in the path to the starting node (optional parameter).
16738        \return Array of distances of each node to the starting node.
16739        \note image instance corresponds to the adjacency matrix of the graph.
16740     **/
16741     template<typename t>
16742     CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node, CImg<t>& previous_node) {
16743       return get_dijkstra(starting_node,ending_node,previous_node).move_to(*this);
16744     }
16745 
16746     //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance.
16747     template<typename t>
16748     CImg<T> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node, CImg<t>& previous_node) const {
16749       if (_width!=_height || _depth!=1 || _spectrum!=1)
16750         throw CImgInstanceException(_cimg_instance
16751                                     "dijkstra() : Instance is not a graph adjacency matrix.",
16752                                     cimg_instance);
16753 
16754       return dijkstra(*this,_width,starting_node,ending_node,previous_node);
16755     }
16756 
16757     //! Return minimal path in a graph, using the Dijkstra algorithm.
16758     CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) {
16759       return get_dijkstra(starting_node,ending_node).move_to(*this);
16760     }
16761 
16762     //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance.
16763     CImg<Tfloat> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) const {
16764       CImg<uintT> foo;
16765       return get_dijkstra(starting_node,ending_node,foo);
16766     }
16767 
16768     //! Return an image containing the ascii codes of the specified  string.
16769     /**
16770        \param str input C-string to encode as an image.
16771        \param is_last_zero Tells if the ending \c '0' character appear in the resulting image.
16772     **/
16773     static CImg<T> string(const char *const str, const bool is_last_zero=true) {
16774       if (!str) return CImg<T>();
16775       return CImg<T>(str,std::strlen(str)+(is_last_zero?1:0));
16776     }
16777 
16778     //! Return a \c 1x1 image containing specified value.
16779     /**
16780        \param a0 First vector value.
16781     **/
16782     static CImg<T> vector(const T& a0) {
16783       _cimg_static CImg<T> r(1,1);
16784       r[0] = a0;
16785       return r;
16786     }
16787 
16788     //! Return a \c 1x2 image containing specified values.
16789     /**
16790        \param a0 First vector value.
16791        \param a1 Second vector value.
16792     **/
16793     static CImg<T> vector(const T& a0, const T& a1) {
16794       _cimg_static CImg<T> r(1,2); T *ptr = r._data;
16795       *(ptr++) = a0; *(ptr++) = a1;
16796       return r;
16797     }
16798 
16799     //! Return a \c 1x3 image containing specified values.
16800     /**
16801        \param a0 First vector value.
16802        \param a1 Second vector value.
16803        \param a2 Third vector value.
16804     **/
16805     static CImg<T> vector(const T& a0, const T& a1, const T& a2) {
16806       _cimg_static CImg<T> r(1,3); T *ptr = r._data;
16807       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2;
16808       return r;
16809     }
16810 
16811     //! Return a \c 1x4 image containing specified values.
16812     /**
16813        \param a0 First vector value.
16814        \param a1 Second vector value.
16815        \param a2 Third vector value.
16816        \param a3 Fourth vector value.
16817     **/
16818     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3) {
16819       _cimg_static CImg<T> r(1,4); T *ptr = r._data;
16820       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16821       return r;
16822     }
16823 
16824     //! Return a \c 1x5 image containing specified values.
16825     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
16826       _cimg_static CImg<T> r(1,5); T *ptr = r._data;
16827       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4;
16828       return r;
16829     }
16830 
16831     //! Return a \c 1x6 image containing specified values.
16832     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
16833       _cimg_static CImg<T> r(1,6); T *ptr = r._data;
16834       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5;
16835       return r;
16836     }
16837 
16838     //! Return a \c 1x7 image containing specified values.
16839     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16840                           const T& a4, const T& a5, const T& a6) {
16841       _cimg_static CImg<T> r(1,7); T *ptr = r._data;
16842       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16843       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6;
16844       return r;
16845     }
16846 
16847     //! Return a \c 1x8 image containing specified values.
16848     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16849                           const T& a4, const T& a5, const T& a6, const T& a7) {
16850       _cimg_static CImg<T> r(1,8); T *ptr = r._data;
16851       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16852       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16853       return r;
16854     }
16855 
16856     //! Return a \c 1x9 image containing specified values.
16857     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16858                           const T& a4, const T& a5, const T& a6, const T& a7,
16859                           const T& a8) {
16860       _cimg_static CImg<T> r(1,9); T *ptr = r._data;
16861       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16862       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16863       *(ptr++) = a8;
16864       return r;
16865     }
16866 
16867     //! Return a \c 1x10 image containing specified values.
16868     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16869                           const T& a4, const T& a5, const T& a6, const T& a7,
16870                           const T& a8, const T& a9) {
16871       _cimg_static CImg<T> r(1,10); T *ptr = r._data;
16872       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16873       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16874       *(ptr++) = a8; *(ptr++) = a9;
16875       return r;
16876     }
16877 
16878     //! Return a \c 1x11 image containing specified values.
16879     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16880                           const T& a4, const T& a5, const T& a6, const T& a7,
16881                           const T& a8, const T& a9, const T& a10) {
16882       _cimg_static CImg<T> r(1,11); T *ptr = r._data;
16883       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16884       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16885       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10;
16886       return r;
16887     }
16888 
16889     //! Return a \c 1x12 image containing specified values.
16890     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16891                           const T& a4, const T& a5, const T& a6, const T& a7,
16892                           const T& a8, const T& a9, const T& a10, const T& a11) {
16893       _cimg_static CImg<T> r(1,12); T *ptr = r._data;
16894       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16895       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16896       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
16897       return r;
16898     }
16899 
16900     //! Return a \c 1x13 image containing specified values.
16901     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16902                           const T& a4, const T& a5, const T& a6, const T& a7,
16903                           const T& a8, const T& a9, const T& a10, const T& a11,
16904                           const T& a12) {
16905       _cimg_static CImg<T> r(1,13); T *ptr = r._data;
16906       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16907       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16908       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
16909       *(ptr++) = a12;
16910       return r;
16911     }
16912 
16913     //! Return a \c 1x14 image containing specified values.
16914     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16915                           const T& a4, const T& a5, const T& a6, const T& a7,
16916                           const T& a8, const T& a9, const T& a10, const T& a11,
16917                           const T& a12, const T& a13) {
16918       _cimg_static CImg<T> r(1,14); T *ptr = r._data;
16919       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16920       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16921       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
16922       *(ptr++) = a12; *(ptr++) = a13;
16923       return r;
16924     }
16925 
16926     //! Return a \c 1x15 image containing specified values.
16927     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16928                           const T& a4, const T& a5, const T& a6, const T& a7,
16929                           const T& a8, const T& a9, const T& a10, const T& a11,
16930                           const T& a12, const T& a13, const T& a14) {
16931       _cimg_static CImg<T> r(1,15); T *ptr = r._data;
16932       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16933       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16934       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
16935       *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14;
16936       return r;
16937     }
16938 
16939     //! Return a \c 1x16 image containing specified values.
16940     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16941                           const T& a4, const T& a5, const T& a6, const T& a7,
16942                           const T& a8, const T& a9, const T& a10, const T& a11,
16943                           const T& a12, const T& a13, const T& a14, const T& a15) {
16944       _cimg_static CImg<T> r(1,16); T *ptr = r._data;
16945       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16946       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16947       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
16948       *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15;
16949       return r;
16950     }
16951 
16952     //! Return a 1x1 matrix containing specified coefficients.
16953     /**
16954        \param a0 First matrix value.
16955        \note Equivalent to vector(const T&).
16956     **/
16957     static CImg<T> matrix(const T& a0) {
16958       return vector(a0);
16959     }
16960 
16961     //! Return a 2x2 matrix containing specified coefficients.
16962     /**
16963        \param a0 First matrix value.
16964        \param a1 Second matrix value.
16965        \param a2 Third matrix value.
16966        \param a3 Fourth matrix value.
16967     **/
16968     static CImg<T> matrix(const T& a0, const T& a1,
16969                           const T& a2, const T& a3) {
16970       _cimg_static CImg<T> r(2,2); T *ptr = r._data;
16971       *(ptr++) = a0; *(ptr++) = a1;
16972       *(ptr++) = a2; *(ptr++) = a3;
16973       return r;
16974     }
16975 
16976     //! Return a 3x3 matrix containing specified coefficients.
16977     /**
16978        \param a0 First matrix value.
16979        \param a1 Second matrix value.
16980        \param a2 Third matrix value.
16981        \param a3 Fourth matrix value.
16982        \param a4 Fifth matrix value.
16983        \param a5 Sixth matrix value.
16984        \param a6 Seventh matrix value.
16985        \param a7 Eighth matrix value.
16986        \param a8 Nineth matrix value.
16987     **/
16988     static CImg<T> matrix(const T& a0, const T& a1, const T& a2,
16989                           const T& a3, const T& a4, const T& a5,
16990                           const T& a6, const T& a7, const T& a8) {
16991       _cimg_static CImg<T> r(3,3); T *ptr = r._data;
16992       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2;
16993       *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5;
16994       *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8;
16995       return r;
16996     }
16997 
16998     //! Return a 4x4 matrix containing specified coefficients.
16999     static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3,
17000                           const T& a4, const T& a5, const T& a6, const T& a7,
17001                           const T& a8, const T& a9, const T& a10, const T& a11,
17002                           const T& a12, const T& a13, const T& a14, const T& a15) {
17003       _cimg_static CImg<T> r(4,4); T *ptr = r._data;
17004       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
17005       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
17006       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
17007       *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15;
17008       return r;
17009     }
17010 
17011     //! Return a 5x5 matrix containing specified coefficients.
17012     static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4,
17013                           const T& a5, const T& a6, const T& a7, const T& a8, const T& a9,
17014                           const T& a10, const T& a11, const T& a12, const T& a13, const T& a14,
17015                           const T& a15, const T& a16, const T& a17, const T& a18, const T& a19,
17016                           const T& a20, const T& a21, const T& a22, const T& a23, const T& a24) {
17017       _cimg_static CImg<T> r(5,5); T *ptr = r._data;
17018       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4;
17019       *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9;
17020       *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14;
17021       *(ptr++) = a15; *(ptr++) = a16; *(ptr++) = a17; *(ptr++) = a18; *(ptr++) = a19;
17022       *(ptr++) = a20; *(ptr++) = a21; *(ptr++) = a22; *(ptr++) = a23; *(ptr++) = a24;
17023       return r;
17024     }
17025 
17026     //! Return a 1x1 symmetric matrix containing specified coefficients.
17027     /**
17028        \param a0 First matrix value.
17029        \note Equivalent to vector(const T&).
17030     **/
17031     static CImg<T> tensor(const T& a0) {
17032       return matrix(a0);
17033     }
17034 
17035     //! Return a 2x2 symmetric matrix tensor containing specified coefficients.
17036     static CImg<T> tensor(const T& a0, const T& a1, const T& a2) {
17037       return matrix(a0,a1,a1,a2);
17038     }
17039 
17040     //! Return a 3x3 symmetric matrix containing specified coefficients.
17041     static CImg<T> tensor(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
17042       return matrix(a0,a1,a2,a1,a3,a4,a2,a4,a5);
17043     }
17044 
17045     //! Return a 1x1 diagonal matrix containing specified coefficients.
17046     static CImg<T> diagonal(const T& a0) {
17047       return matrix(a0);
17048     }
17049 
17050     //! Return a 2x2 diagonal matrix containing specified coefficients.
17051     static CImg<T> diagonal(const T& a0, const T& a1) {
17052       return matrix(a0,0,0,a1);
17053     }
17054 
17055     //! Return a 3x3 diagonal matrix containing specified coefficients.
17056     static CImg<T> diagonal(const T& a0, const T& a1, const T& a2) {
17057       return matrix(a0,0,0,0,a1,0,0,0,a2);
17058     }
17059 
17060     //! Return a 4x4 diagonal matrix containing specified coefficients.
17061     static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3) {
17062       return matrix(a0,0,0,0,0,a1,0,0,0,0,a2,0,0,0,0,a3);
17063     }
17064 
17065     //! Return a 5x5 diagonal matrix containing specified coefficients.
17066     static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
17067       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);
17068     }
17069 
17070     //! Return a NxN identity matrix.
17071     /**
17072        \param N Dimension of the matrix.
17073     **/
17074     static CImg<T> identity_matrix(const unsigned int N) {
17075       CImg<T> res(N,N,1,1,0);
17076       cimg_forX(res,x) res(x,x) = 1;
17077       return res;
17078     }
17079 
17080     //! Return a N-numbered sequence vector from \p a0 to \p a1.
17081     /**
17082        \param N Size of the resulting vector.
17083        \param a0 Starting value of the sequence.
17084        \param a1 Ending value of the sequence.
17085      **/
17086     static CImg<T> sequence(const unsigned int N, const T a0, const T a1) {
17087       if (N) return CImg<T>(1,N).sequence(a0,a1);
17088       return CImg<T>();
17089     }
17090 
17091     //! Return a 3x3 rotation matrix along the (x,y,z)-axis with an angle w.
17092     /**
17093        \param x X-coordinate of the rotation axis, or first quaternion coordinate.
17094        \param y Y-coordinate of the rotation axis, or second quaternion coordinate.
17095        \param z Z-coordinate of the rotation axis, or third quaternion coordinate.
17096        \param w Angle of the rotation axis, or fourth quaternion coordinate.
17097        \param is_quaternion Tell is the four arguments denotes a set { axis + angle } or a quaternion.
17098      **/
17099     static CImg<T> rotation_matrix(const float x, const float y, const float z, const float w, const bool is_quaternion=false) {
17100       float X,Y,Z,W;
17101       if (!is_quaternion) {
17102         const float norm = (float)std::sqrt(x*x + y*y + z*z),
17103           nx = norm>0?x/norm:0,
17104           ny = norm>0?y/norm:0,
17105           nz = norm>0?z/norm:1,
17106           nw = norm>0?w:0,
17107           sina = (float)std::sin(nw/2),
17108           cosa = (float)std::cos(nw/2);
17109         X = nx*sina;
17110         Y = ny*sina;
17111         Z = nz*sina;
17112         W = cosa;
17113       } else {
17114         const float norm = (float)std::sqrt(x*x + y*y + z*z + w*w);
17115         if (norm>0) { X = x/norm; Y = y/norm; Z = z/norm; W = w/norm; }
17116         else { X = Y = Z = 0; W = 1; }
17117       }
17118       const float xx = X*X, xy = X*Y, xz = X*Z, xw = X*W, yy = Y*Y, yz = Y*Z, yw = Y*W, zz = Z*Z, zw = Z*W;
17119       return CImg<T>::matrix((T)(1-2*(yy+zz)), (T)(2*(xy+zw)),   (T)(2*(xz-yw)),
17120                              (T)(2*(xy-zw)),   (T)(1-2*(xx+zz)), (T)(2*(yz+xw)),
17121                              (T)(2*(xz+yw)),   (T)(2*(yz-xw)),   (T)(1-2*(xx+yy)));
17122     }
17123 
17124     //@}
17125     //-----------------------------------
17126     //
17127     //! \name Value Manipulation
17128     //@{
17129     //-----------------------------------
17130 
17131     //! Fill all pixel values with specified value.
17132     /**
17133        \param val Fill value.
17134     **/
17135     CImg<T>& fill(const T val) {
17136       if (is_empty()) return *this;
17137       if (val && sizeof(T)!=1) cimg_for(*this,ptrd,T) *ptrd = val;
17138       else std::memset(_data,(int)val,sizeof(T)*size());
17139       return *this;
17140     }
17141 
17142     //! Fill all pixel values with specified value \newinstance.
17143     CImg<T> get_fill(const T val) const {
17144       return CImg<T>(_width,_height,_depth,_spectrum).fill(val);
17145     }
17146 
17147     //! Fill sequentially all pixel values with specified values.
17148     /**
17149        \param val0 First fill value.
17150        \param val1 Second fill value.
17151     **/
17152     CImg<T>& fill(const T val0, const T val1) {
17153       if (is_empty()) return *this;
17154       T *ptrd, *ptre = end()-1;
17155       for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; }
17156       if (ptrd!=ptre+1) *(ptrd++) = val0;
17157       return *this;
17158     }
17159 
17160     //! Fill sequentially all pixel values with specified values \newinstance.
17161     CImg<T> get_fill(const T val0, const T val1) const {
17162       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1);
17163     }
17164 
17165     //! Fill sequentially all pixel values with specified values \overloading.
17166     CImg<T>& fill(const T val0, const T val1, const T val2) {
17167       if (is_empty()) return *this;
17168       T *ptrd, *ptre = end()-2;
17169       for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; }
17170       ptre+=2;
17171       switch (ptre - ptrd) {
17172       case 2 : *(--ptre) = val1;
17173       case 1 : *(--ptre) = val0;
17174       }
17175       return *this;
17176     }
17177 
17178     //! Fill sequentially all pixel values with specified values \newinstance.
17179     CImg<T> get_fill(const T val0, const T val1, const T val2) const {
17180       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2);
17181     }
17182 
17183     //! Fill sequentially all pixel values with specified values \overloading.
17184     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3) {
17185       if (is_empty()) return *this;
17186       T *ptrd, *ptre = end()-3;
17187       for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; }
17188       ptre+=3;
17189       switch (ptre - ptrd) {
17190       case 3 : *(--ptre) = val2;
17191       case 2 : *(--ptre) = val1;
17192       case 1 : *(--ptre) = val0;
17193       }
17194       return *this;
17195     }
17196 
17197     //! Fill sequentially all pixel values with specified values \newinstance.
17198     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3) const {
17199       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3);
17200     }
17201 
17202     //! Fill sequentially all pixel values with specified values \overloading.
17203     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4) {
17204       if (is_empty()) return *this;
17205       T *ptrd, *ptre = end()-4;
17206       for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; }
17207       ptre+=4;
17208       switch (ptre - ptrd) {
17209       case 4 : *(--ptre) = val3;
17210       case 3 : *(--ptre) = val2;
17211       case 2 : *(--ptre) = val1;
17212       case 1 : *(--ptre) = val0;
17213       }
17214       return *this;
17215     }
17216 
17217     //! Fill sequentially all pixel values with specified values \newinstance.
17218     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4) const {
17219       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4);
17220     }
17221 
17222     //! Fill sequentially all pixel values with specified values \overloading.
17223     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5) {
17224       if (is_empty()) return *this;
17225       T *ptrd, *ptre = end()-5;
17226       for (ptrd = _data; ptrd<ptre; ) {
17227         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
17228       }
17229       ptre+=5;
17230       switch (ptre - ptrd) {
17231       case 5 : *(--ptre) = val4;
17232       case 4 : *(--ptre) = val3;
17233       case 3 : *(--ptre) = val2;
17234       case 2 : *(--ptre) = val1;
17235       case 1 : *(--ptre) = val0;
17236       }
17237       return *this;
17238     }
17239 
17240     //! Fill sequentially all pixel values with specified values \newinstance.
17241     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5) const {
17242       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5);
17243     }
17244 
17245     //! Fill sequentially all pixel values with specified values \overloading.
17246     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6) {
17247       if (is_empty()) return *this;
17248       T *ptrd, *ptre = end()-6;
17249       for (ptrd = _data; ptrd<ptre; ) {
17250         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5; *(ptrd++) = val6;
17251       }
17252       ptre+=6;
17253       switch (ptre - ptrd) {
17254       case 6 : *(--ptre) = val5;
17255       case 5 : *(--ptre) = val4;
17256       case 4 : *(--ptre) = val3;
17257       case 3 : *(--ptre) = val2;
17258       case 2 : *(--ptre) = val1;
17259       case 1 : *(--ptre) = val0;
17260       }
17261       return *this;
17262     }
17263 
17264     //! Fill sequentially all pixel values with specified values \newinstance.
17265     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6) const {
17266       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6);
17267     }
17268 
17269     //! Fill sequentially all pixel values with specified values \overloading.
17270     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17271                   const T val7) {
17272       if (is_empty()) return *this;
17273       T *ptrd, *ptre = end()-7;
17274       for (ptrd = _data; ptrd<ptre; ) {
17275         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3;
17276         *(ptrd++) = val4; *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7;
17277       }
17278       ptre+=7;
17279       switch (ptre - ptrd) {
17280       case 7 : *(--ptre) = val6;
17281       case 6 : *(--ptre) = val5;
17282       case 5 : *(--ptre) = val4;
17283       case 4 : *(--ptre) = val3;
17284       case 3 : *(--ptre) = val2;
17285       case 2 : *(--ptre) = val1;
17286       case 1 : *(--ptre) = val0;
17287       }
17288       return *this;
17289     }
17290 
17291     //! Fill sequentially all pixel values with specified values \newinstance.
17292     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17293                      const T val7) const {
17294       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7);
17295     }
17296 
17297     //! Fill sequentially all pixel values with specified values \overloading.
17298     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17299                   const T val7, const T val8) {
17300       if (is_empty()) return *this;
17301       T *ptrd, *ptre = end()-8;
17302       for (ptrd = _data; ptrd<ptre; ) {
17303         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2;
17304         *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
17305         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8;
17306       }
17307       ptre+=8;
17308       switch (ptre - ptrd) {
17309       case 8 : *(--ptre) = val7;
17310       case 7 : *(--ptre) = val6;
17311       case 6 : *(--ptre) = val5;
17312       case 5 : *(--ptre) = val4;
17313       case 4 : *(--ptre) = val3;
17314       case 3 : *(--ptre) = val2;
17315       case 2 : *(--ptre) = val1;
17316       case 1 : *(--ptre) = val0;
17317       }
17318       return *this;
17319     }
17320 
17321     //! Fill sequentially all pixel values with specified values \newinstance.
17322     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17323                      const T val7, const T val8) const {
17324       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8);
17325     }
17326 
17327     //! Fill sequentially all pixel values with specified values \overloading.
17328     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17329                   const T val7, const T val8, const T val9) {
17330       if (is_empty()) return *this;
17331       T *ptrd, *ptre = end()-9;
17332       for (ptrd = _data; ptrd<ptre; ) {
17333         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
17334         *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9;
17335       }
17336       ptre+=9;
17337       switch (ptre - ptrd) {
17338       case 9 : *(--ptre) = val8;
17339       case 8 : *(--ptre) = val7;
17340       case 7 : *(--ptre) = val6;
17341       case 6 : *(--ptre) = val5;
17342       case 5 : *(--ptre) = val4;
17343       case 4 : *(--ptre) = val3;
17344       case 3 : *(--ptre) = val2;
17345       case 2 : *(--ptre) = val1;
17346       case 1 : *(--ptre) = val0;
17347       }
17348       return *this;
17349     }
17350 
17351     //! Fill sequentially all pixel values with specified values \newinstance.
17352     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17353                      const T val7, const T val8, const T val9) const {
17354       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9);
17355     }
17356 
17357     //! Fill sequentially all pixel values with specified values \overloading.
17358     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17359                   const T val7, const T val8, const T val9, const T val10) {
17360       if (is_empty()) return *this;
17361       T *ptrd, *ptre = end()-10;
17362       for (ptrd = _data; ptrd<ptre; ) {
17363         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
17364         *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9;
17365         *(ptrd++) = val10;
17366       }
17367       ptre+=10;
17368       switch (ptre - ptrd) {
17369       case 10 : *(--ptre) = val9;
17370       case 9 : *(--ptre) = val8;
17371       case 8 : *(--ptre) = val7;
17372       case 7 : *(--ptre) = val6;
17373       case 6 : *(--ptre) = val5;
17374       case 5 : *(--ptre) = val4;
17375       case 4 : *(--ptre) = val3;
17376       case 3 : *(--ptre) = val2;
17377       case 2 : *(--ptre) = val1;
17378       case 1 : *(--ptre) = val0;
17379       }
17380       return *this;
17381     }
17382 
17383     //! Fill sequentially all pixel values with specified values \newinstance.
17384     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17385                      const T val7, const T val8, const T val9, const T val10) const {
17386       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10);
17387     }
17388 
17389     //! Fill sequentially all pixel values with specified values \overloading.
17390     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17391                   const T val7, const T val8, const T val9, const T val10, const T val11) {
17392       if (is_empty()) return *this;
17393       T *ptrd, *ptre = end()-11;
17394       for (ptrd = _data; ptrd<ptre; ) {
17395         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
17396         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
17397       }
17398       ptre+=11;
17399       switch (ptre - ptrd) {
17400       case 11 : *(--ptre) = val10;
17401       case 10 : *(--ptre) = val9;
17402       case 9 : *(--ptre) = val8;
17403       case 8 : *(--ptre) = val7;
17404       case 7 : *(--ptre) = val6;
17405       case 6 : *(--ptre) = val5;
17406       case 5 : *(--ptre) = val4;
17407       case 4 : *(--ptre) = val3;
17408       case 3 : *(--ptre) = val2;
17409       case 2 : *(--ptre) = val1;
17410       case 1 : *(--ptre) = val0;
17411       }
17412       return *this;
17413     }
17414 
17415     //! Fill sequentially all pixel values with specified values \newinstance.
17416     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17417                      const T val7, const T val8, const T val9, const T val10, const T val11) const {
17418       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11);
17419     }
17420 
17421     //! Fill sequentially all pixel values with specified values \overloading.
17422     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17423                   const T val7, const T val8, const T val9, const T val10, const T val11, const T val12) {
17424       if (is_empty()) return *this;
17425       T *ptrd, *ptre = end()-12;
17426       for (ptrd = _data; ptrd<ptre; ) {
17427         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
17428         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
17429         *(ptrd++) = val12;
17430       }
17431       ptre+=12;
17432       switch (ptre - ptrd) {
17433       case 12 : *(--ptre) = val11;
17434       case 11 : *(--ptre) = val10;
17435       case 10 : *(--ptre) = val9;
17436       case 9 : *(--ptre) = val8;
17437       case 8 : *(--ptre) = val7;
17438       case 7 : *(--ptre) = val6;
17439       case 6 : *(--ptre) = val5;
17440       case 5 : *(--ptre) = val4;
17441       case 4 : *(--ptre) = val3;
17442       case 3 : *(--ptre) = val2;
17443       case 2 : *(--ptre) = val1;
17444       case 1 : *(--ptre) = val0;
17445       }
17446       return *this;
17447     }
17448 
17449     //! Fill sequentially all pixel values with specified values \newinstance.
17450     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17451                      const T val7, const T val8, const T val9, const T val10, const T val11, const T val12) const {
17452       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12);
17453     }
17454 
17455     //! Fill sequentially all pixel values with specified values \overloading.
17456     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17457                   const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
17458                   const T val13) {
17459       if (is_empty()) return *this;
17460       T *ptrd, *ptre = end()-13;
17461       for (ptrd = _data; ptrd<ptre; ) {
17462         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
17463         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
17464         *(ptrd++) = val12; *(ptrd++) = val13;
17465       }
17466       ptre+=13;
17467       switch (ptre - ptrd) {
17468       case 13 : *(--ptre) = val12;
17469       case 12 : *(--ptre) = val11;
17470       case 11 : *(--ptre) = val10;
17471       case 10 : *(--ptre) = val9;
17472       case 9 : *(--ptre) = val8;
17473       case 8 : *(--ptre) = val7;
17474       case 7 : *(--ptre) = val6;
17475       case 6 : *(--ptre) = val5;
17476       case 5 : *(--ptre) = val4;
17477       case 4 : *(--ptre) = val3;
17478       case 3 : *(--ptre) = val2;
17479       case 2 : *(--ptre) = val1;
17480       case 1 : *(--ptre) = val0;
17481       }
17482       return *this;
17483     }
17484 
17485     //! Fill sequentially all pixel values with specified values \newinstance.
17486     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17487                      const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
17488                      const T val13) const {
17489       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,
17490                                                   val13);
17491     }
17492 
17493     //! Fill sequentially all pixel values with specified values \overloading.
17494     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17495                   const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
17496                   const T val13, const T val14) {
17497       if (is_empty()) return *this;
17498       T *ptrd, *ptre = end()-14;
17499       for (ptrd = _data; ptrd<ptre; ) {
17500         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
17501         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
17502         *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14;
17503       }
17504       ptre+=14;
17505       switch (ptre - ptrd) {
17506       case 14 : *(--ptre) = val13;
17507       case 13 : *(--ptre) = val12;
17508       case 12 : *(--ptre) = val11;
17509       case 11 : *(--ptre) = val10;
17510       case 10 : *(--ptre) = val9;
17511       case 9 : *(--ptre) = val8;
17512       case 8 : *(--ptre) = val7;
17513       case 7 : *(--ptre) = val6;
17514       case 6 : *(--ptre) = val5;
17515       case 5 : *(--ptre) = val4;
17516       case 4 : *(--ptre) = val3;
17517       case 3 : *(--ptre) = val2;
17518       case 2 : *(--ptre) = val1;
17519       case 1 : *(--ptre) = val0;
17520       }
17521       return *this;
17522     }
17523 
17524     //! Fill sequentially all pixel values with specified values \newinstance.
17525     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17526                      const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
17527                      const T val13, const T val14) const {
17528       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,
17529                                                   val13,val14);
17530     }
17531 
17532     //! Fill sequentially all pixel values with specified values \overloading.
17533     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17534                   const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
17535                   const T val13, const T val14, const T val15) {
17536       if (is_empty()) return *this;
17537       T *ptrd, *ptre = end()-15;
17538       for (ptrd = _data; ptrd<ptre; ) {
17539         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
17540         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
17541         *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14; *(ptrd++) = val15;
17542       }
17543       ptre+=15;
17544       switch (ptre - ptrd) {
17545       case 15 : *(--ptre) = val14;
17546       case 14 : *(--ptre) = val13;
17547       case 13 : *(--ptre) = val12;
17548       case 12 : *(--ptre) = val11;
17549       case 11 : *(--ptre) = val10;
17550       case 10 : *(--ptre) = val9;
17551       case 9 : *(--ptre) = val8;
17552       case 8 : *(--ptre) = val7;
17553       case 7 : *(--ptre) = val6;
17554       case 6 : *(--ptre) = val5;
17555       case 5 : *(--ptre) = val4;
17556       case 4 : *(--ptre) = val3;
17557       case 3 : *(--ptre) = val2;
17558       case 2 : *(--ptre) = val1;
17559       case 1 : *(--ptre) = val0;
17560       }
17561       return *this;
17562     }
17563 
17564     //! Fill sequentially all pixel values with specified values \newinstance.
17565     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17566                      const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
17567                      const T val13, const T val14, const T val15) const {
17568       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,
17569                                                   val13,val14,val15);
17570     }
17571 
17572     //! Fill sequentially pixel values according to a given expression.
17573     /**
17574        \param expression C-string describing a math formula, or a list of values.
17575        \param repeat_flag In case a list of values is provided, tells if this list must be repeated for the filling.
17576     **/
17577     CImg<T>& fill(const char *const expression, const bool repeat_flag) {
17578       if (is_empty() || !expression || !*expression) return *this;
17579       const unsigned int omode = cimg::exception_mode();
17580       cimg::exception_mode() = 0;
17581       try { // Try to fill values according to a formula.
17582         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
17583         _cimg_math_parser mp(base,expression,"fill");
17584         T *ptrd = _data;
17585         cimg_forXYZC(*this,x,y,z,c) *(ptrd++) = (T)mp.eval((double)x,(double)y,(double)z,(double)c);
17586       } catch (CImgException& e) { // If failed, try to recognize a list of values.
17587         char item[16384] = { 0 }, sep = 0;
17588         const char *nexpression = expression;
17589         unsigned long nb = 0;
17590         const unsigned long siz = size();
17591         T *ptrd = _data;
17592         for (double val = 0; *nexpression && nb<siz; ++nb) {
17593           sep = 0;
17594           const int err = std::sscanf(nexpression,"%4095[ \n\t0-9.e+-]%c",item,&sep);
17595           if (err>0 && std::sscanf(item,"%lf",&val)==1) {
17596             nexpression+=std::strlen(item) + (err>1?1:0);
17597             *(ptrd++) = (T)val;
17598           } else break;
17599         }
17600         cimg::exception_mode() = omode;
17601         if (nb<siz && (sep || *nexpression))
17602           throw CImgArgumentException(e.what(),pixel_type(),expression);
17603         if (repeat_flag && nb && nb<siz) for (T *ptrs = _data, *const ptre = _data + siz; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs;
17604       }
17605       cimg::exception_mode() = omode;
17606       return *this;
17607     }
17608 
17609     //! Fill sequentially pixel values according to a given expression \newinstance.
17610     CImg<T> get_fill(const char *const values, const bool repeat_values) const {
17611       return (+*this).fill(values,repeat_values);
17612     }
17613 
17614     //! Fill sequentially pixel values according to the values found in another image.
17615     /**
17616        \param values Image containing the values used for the filling.
17617        \param repeat_values In case there are less values than necessary in \c values, tells if these values must be repeated for the filling.
17618     **/
17619     template<typename t>
17620     CImg<T>& fill(const CImg<t>& values, const bool repeat_values=true) {
17621       if (is_empty() || !values) return *this;
17622       T *ptrd = _data, *ptre = ptrd + size();
17623       for (t *ptrs = values._data, *ptrs_end = ptrs + values.size(); ptrs<ptrs_end && ptrd<ptre; ++ptrs) *(ptrd++) = (T)*ptrs;
17624       if (repeat_values && ptrd<ptre) for (T *ptrs = _data; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs;
17625       return *this;
17626     }
17627 
17628     //! Fill sequentially pixel values according to the values found in another image \newinstance.
17629     template<typename t>
17630     CImg<T> get_fill(const CImg<t>& values, const bool repeat_values=true) const {
17631       return repeat_values?CImg<T>(_width,_height,_depth,_spectrum).fill(values,repeat_values):(+*this).fill(values,repeat_values);
17632     }
17633 
17634     //! Fill pixel values along the X-axis at a specified pixel position.
17635     /**
17636        \param y Y-coordinate of the filled column.
17637        \param z Z-coordinate of the filled column.
17638        \param c C-coordinate of the filled column.
17639        \param a0 First fill value.
17640     **/
17641     CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const int a0, ...) {
17642 #define _cimg_fill1(x,y,z,c,off,siz,t) { \
17643     va_list ap; va_start(ap,a0); T *ptrd = data(x,y,z,c); *ptrd = (T)a0; \
17644     for (unsigned long k = 1; k<siz; ++k) { ptrd+=off; *ptrd = (T)va_arg(ap,t); } \
17645     va_end(ap); }
17646       if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,int);
17647       return *this;
17648     }
17649 
17650     //! Fill pixel values along the X-axis at a specified pixel position \overloading.
17651     CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const double a0, ...) {
17652       if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,double);
17653       return *this;
17654     }
17655 
17656     //! Fill pixel values along the Y-axis at a specified pixel position.
17657     /**
17658        \param x X-coordinate of the filled row.
17659        \param z Z-coordinate of the filled row.
17660        \param c C-coordinate of the filled row.
17661        \param a0 First fill value.
17662     **/
17663     CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const int a0, ...) {
17664       if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,int);
17665       return *this;
17666     }
17667 
17668     //! Fill pixel values along the Y-axis at a specified pixel position \overloading.
17669     CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const double a0, ...) {
17670       if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,double);
17671       return *this;
17672     }
17673 
17674     //! Fill pixel values along the Z-axis at a specified pixel position.
17675     /**
17676        \param x X-coordinate of the filled slice.
17677        \param y Y-coordinate of the filled slice.
17678        \param c C-coordinate of the filled slice.
17679        \param a0 First fill value.
17680     **/
17681     CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const int a0, ...) {
17682       const unsigned long wh = (unsigned long)_width*_height;
17683       if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,int);
17684       return *this;
17685     }
17686 
17687     //! Fill pixel values along the Z-axis at a specified pixel position \overloading.
17688     CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const double a0, ...) {
17689       const unsigned long wh = (unsigned long)_width*_height;
17690       if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,double);
17691       return *this;
17692     }
17693 
17694     //! Fill pixel values along the C-axis at a specified pixel position.
17695     /**
17696        \param x X-coordinate of the filled channel.
17697        \param y Y-coordinate of the filled channel.
17698        \param z Z-coordinate of the filled channel.
17699        \param a0 First filling value.
17700     **/
17701     CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const int a0, ...) {
17702       const unsigned long whd = (unsigned long)_width*_height*_depth;
17703       if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,int);
17704       return *this;
17705     }
17706 
17707     //! Fill pixel values along the C-axis at a specified pixel position \overloading.
17708     CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const double a0, ...) {
17709       const unsigned long whd = (unsigned long)_width*_height*_depth;
17710       if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,double);
17711       return *this;
17712     }
17713 
17714     //! Discard specified value in the image buffer.
17715     /**
17716        \param value Value to discard.
17717        \note Discarded values will change the image geometry, so the resulting image
17718        is returned as a one-column vector.
17719     **/
17720     CImg<T>& discard(const T value) {
17721       return get_discard(value).move_to(*this);
17722     }
17723 
17724     //! Discard specified value in the image buffer \newinstance.
17725     CImg<T> get_discard(const T value) const {
17726       CImg<T> res(1,size());
17727       T *pd = res._data;
17728       for (const T *ps = _data, *const pse = end(); ps<pse; ++ps)
17729         if (*ps!=value) *(pd++) = *ps;
17730       if (pd==res._data) return CImg<T>();
17731       return res.resize(1,pd-res._data,1,1,-1);
17732     }
17733 
17734     //! Discard specified sequence of values in the image buffer.
17735     /**
17736        \param values Sequence of values to discard.
17737        \note Discarded values will change the image geometry, so the resulting image
17738        is returned as a one-column vector.
17739     **/
17740     template<typename t>
17741     CImg<T>& discard(const CImg<t>& values) {
17742       return get_discard(values).move_to(*this);
17743     }
17744 
17745     //! Discard specified sequence of values in the image buffer \newinstance.
17746     template<typename t>
17747     CImg<T> get_discard(const CImg<t>& values) const {
17748       if (!values) return *this;
17749       if (values.size()==1) return get_discard(*values);
17750       CImg<T> res(1,size());
17751       T *pd = res._data;
17752       const t *const pve = values.end();
17753       for (const T *ps = _data, *const pse = end(); ps<pse; ) {
17754         const T *_ps = ps;
17755         const t *pv = values._data;
17756         while (_ps<pse && pv<pve) { if (*(_ps++)!=(T)*pv) break; ++pv; }
17757         if (pv!=pve) {
17758           const unsigned int l = _ps - ps;
17759           if (l==1) *(pd++) = *ps; else { std::memcpy(pd,ps,sizeof(T)*l); pd+=l; }
17760         }
17761         ps = _ps;
17762       }
17763       if (pd==res._data) return CImg<T>();
17764       return res.resize(1,pd-res._data,1,1,-1);
17765     }
17766 
17767     //! Invert endianness of all pixel values.
17768     /**
17769      **/
17770     CImg<T>& invert_endianness() {
17771       cimg::invert_endianness(_data,size());
17772       return *this;
17773     }
17774 
17775     //! Invert endianness of all pixel values \newinstance.
17776     CImg<T> get_invert_endianness() const {
17777       return (+*this).invert_endianness();
17778     }
17779 
17780     //! Fill image with random values in specified range.
17781     /**
17782        \param val_min Minimal random value.
17783        \param val_max Maximal random value.
17784        \note Random samples are following a uniform distribution.
17785      **/
17786     CImg<T>& rand(const T val_min, const T val_max) {
17787       const float delta = (float)val_max - (float)val_min;
17788       cimg_for(*this,ptrd,T) *ptrd = (T)(val_min + cimg::rand()*delta);
17789       return *this;
17790     }
17791 
17792     //! Fill image with random values in specified range \newinstance.
17793     CImg<T> get_rand(const T val_min, const T val_max) const {
17794       return (+*this).rand(val_min,val_max);
17795     }
17796 
17797     //! Round pixel values.
17798     /**
17799        \param y Rounding precision.
17800        \param rounding_type Rounding type. Can be :
17801        - \c -1 : Backward.
17802        - \c 0 : Nearest.
17803        - \c 1 : Forward.
17804     **/
17805     CImg<T>& round(const double y=1, const int rounding_type=0) {
17806       if (y>0) cimg_for(*this,ptrd,T) *ptrd = cimg::round(*ptrd,y,rounding_type);
17807       return *this;
17808     }
17809 
17810     //! Round pixel values \newinstance.
17811     CImg<T> get_round(const double y=1, const unsigned int rounding_type=0) const {
17812       return (+*this).round(y,rounding_type);
17813     }
17814 
17815     //! Add random noise to pixel values.
17816     /**
17817        \param sigma Amplitude of the random additive noise. If \p sigma<0, it stands for a percentage of the global value range.
17818        \param noise_type Type of additive noise (can be \p 0=gaussian, \p 1=uniform, \p 2=Salt and Pepper, \p 3=Poisson or \p 4=Rician).
17819        \return A reference to the modified image instance.
17820        \note
17821        - For Poisson noise (\p noise_type=3), parameter \p sigma is ignored, as Poisson noise only depends on the image value itself.
17822        - Function \p CImg<T>::get_noise() is also defined. It returns a non-shared modified copy of the image instance.
17823        \par Example
17824        \code
17825        const CImg<float> img("reference.jpg"), res = img.get_noise(40);
17826        (img,res.normalize(0,255)).display();
17827        \endcode
17828        \image html ref_noise.jpg
17829     **/
17830     CImg<T>& noise(const double sigma, const unsigned int noise_type=0) {
17831       if (!is_empty()) {
17832         const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
17833         Tfloat nsigma = (Tfloat)sigma, m = 0, M = 0;
17834         if (nsigma==0 && noise_type!=3) return *this;
17835         if (nsigma<0 || noise_type==2) m = (Tfloat)min_max(M);
17836         if (nsigma<0) nsigma = (Tfloat)(-nsigma*(M-m)/100.0);
17837         switch (noise_type) {
17838         case 0 : { // Gaussian noise
17839           cimg_for(*this,ptrd,T) {
17840             Tfloat val = (Tfloat)(*ptrd + nsigma*cimg::grand());
17841             if (val>vmax) val = vmax;
17842             if (val<vmin) val = vmin;
17843             *ptrd = (T)val;
17844           }
17845         } break;
17846         case 1 : { // Uniform noise
17847           cimg_for(*this,ptrd,T) {
17848             Tfloat val = (Tfloat)(*ptrd + nsigma*cimg::crand());
17849             if (val>vmax) val = vmax;
17850             if (val<vmin) val = vmin;
17851             *ptrd = (T)val;
17852           }
17853         } break;
17854         case 2 : { // Salt & Pepper noise
17855           if (nsigma<0) nsigma = -nsigma;
17856           if (M==m) { m = 0; M = (Tfloat)(cimg::type<T>::is_float()?1:cimg::type<T>::max()); }
17857           cimg_for(*this,ptrd,T) if (cimg::rand()*100<nsigma) *ptrd = (T)(cimg::rand()<0.5?M:m);
17858         } break;
17859 
17860         case 3 : { // Poisson Noise
17861           cimg_for(*this,ptrd,T) *ptrd = (T)cimg::prand(*ptrd);
17862         } break;
17863 
17864         case 4 : { // Rice noise
17865           const Tfloat sqrt2 = (Tfloat)std::sqrt(2.0);
17866           cimg_for(*this,ptrd,T) {
17867             const Tfloat
17868               val0 = (Tfloat)*ptrd/sqrt2,
17869               re = (Tfloat)(val0 + nsigma*cimg::grand()),
17870               im = (Tfloat)(val0 + nsigma*cimg::grand());
17871             Tfloat val = (Tfloat)std::sqrt(re*re + im*im);
17872             if (val>vmax) val = vmax;
17873             if (val<vmin) val = vmin;
17874             *ptrd = (T)val;
17875           }
17876         } break;
17877         default :
17878           throw CImgArgumentException(_cimg_instance
17879                                       "noise() : Invalid specified noise type %d "
17880                                       "(should be { 0=gaussian | 1=uniform | 2=salt&Pepper | 3=poisson }).",
17881                                       cimg_instance,
17882                                       noise_type);
17883         }
17884       }
17885       return *this;
17886     }
17887 
17888     //! Add random noise to pixel values \newinstance.
17889     CImg<T> get_noise(const double sigma, const unsigned int noise_type=0) const {
17890       return (+*this).noise(sigma,noise_type);
17891     }
17892 
17893     //! Linearly normalize pixel values.
17894     /**
17895        \param min_value Minimum desired value of the resulting image.
17896        \param max_value Maximum desired value of the resulting image.
17897        \par Example
17898        \code
17899        const CImg<float> img("reference.jpg"), res = img.get_normalize(160,220);
17900        (img,res).display();
17901        \endcode
17902        \image html ref_normalize2.jpg
17903     **/
17904     CImg<T>& normalize(const T min_value, const T max_value) {
17905       if (is_empty()) return *this;
17906       const T a = min_value<max_value?min_value:max_value, b = min_value<max_value?max_value:min_value;
17907       T m, M = max_min(m);
17908       const Tfloat fm = (Tfloat)m, fM = (Tfloat)M;
17909       if (m==M) return fill(min_value);
17910       if (m!=a || M!=b) cimg_for(*this,ptrd,T) *ptrd = (T)((*ptrd-fm)/(fM-fm)*(b-a)+a);
17911       return *this;
17912     }
17913 
17914     //! Linearly normalize pixel values \newinstance.
17915     CImg<Tfloat> get_normalize(const T min_value, const T max_value) const {
17916       return CImg<Tfloat>(*this,false).normalize((Tfloat)min_value,(Tfloat)max_value);
17917     }
17918 
17919     //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm.
17920     /**
17921        \par Example
17922        \code
17923        const CImg<float> img("reference.jpg"), res = img.get_normalize();
17924        (img,res.normalize(0,255)).display();
17925        \endcode
17926        \image html ref_normalize.jpg
17927     **/
17928     CImg<T>& normalize() {
17929       T *ptrd = _data;
17930       const unsigned long whd = (unsigned long)_width*_height*_depth;
17931       cimg_forXYZ(*this,x,y,z) {
17932         const T *ptrs = ptrd;
17933         float n = 0;
17934         cimg_forC(*this,c) { n+=cimg::sqr((float)*ptrs); ptrs+=whd; }
17935         n = (float)std::sqrt(n);
17936         T *_ptrd = ptrd++;
17937         if (n>0) cimg_forC(*this,c) { *_ptrd = (T)(*_ptrd/n); _ptrd+=whd; }
17938         else cimg_forC(*this,c) { *_ptrd = (T)0; _ptrd+=whd; }
17939       }
17940       return *this;
17941     }
17942 
17943     //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm \newinstance.
17944     CImg<Tfloat> get_normalize() const {
17945       return CImg<Tfloat>(*this,false).normalize();
17946     }
17947 
17948     //! Compute L2-norm of each multi-valued pixel of the image instance.
17949     /**
17950        \param norm_type Type of computed vector norm (can be \p 0=Linf, \p 1=L1 or \p 2=L2).
17951        \par Example
17952        \code
17953        const CImg<float> img("reference.jpg"), res = img.get_norm();
17954        (img,res.normalize(0,255)).display();
17955        \endcode
17956        \image html ref_norm.jpg
17957     **/
17958     CImg<T>& norm(const int norm_type=2) {
17959       if (_spectrum==1) return abs();
17960       return get_norm(norm_type).move_to(*this);
17961     }
17962 
17963     //! Compute L2-norm of each multi-valued pixel of the image instance \newinstance.
17964     CImg<Tfloat> get_norm(const int norm_type=2) const {
17965       if (is_empty()) return *this;
17966       if (_spectrum==1) return get_abs();
17967       const T *ptrs = _data;
17968       const unsigned long whd = (unsigned long)_width*_height*_depth;
17969       CImg<Tfloat> res(_width,_height,_depth);
17970       Tfloat *ptrd = res._data;
17971       switch (norm_type) {
17972       case -1 : {             // Linf norm
17973         cimg_forXYZ(*this,x,y,z) {
17974           Tfloat n = 0;
17975           const T *_ptrs = ptrs++;
17976           cimg_forC(*this,c) { const Tfloat val = (Tfloat)cimg::abs(*_ptrs); if (val>n) n = val; _ptrs+=whd; }
17977           *(ptrd++) = n;
17978         }
17979       } break;
17980       case 1 : {              // L1 norm
17981         cimg_forXYZ(*this,x,y,z) {
17982           Tfloat n = 0;
17983           const T *_ptrs = ptrs++;
17984           cimg_forC(*this,c) { n+=cimg::abs(*_ptrs); _ptrs+=whd; }
17985           *(ptrd++) = n;
17986         }
17987       } break;
17988       default : {             // L2 norm
17989         cimg_forXYZ(*this,x,y,z) {
17990           Tfloat n = 0;
17991           const T *_ptrs = ptrs++;
17992           cimg_forC(*this,c) { n+=cimg::sqr((Tfloat)*_ptrs); _ptrs+=whd; }
17993           *(ptrd++) = (Tfloat)std::sqrt((Tfloat)n);
17994         }
17995       }
17996       }
17997       return res;
17998     }
17999 
18000     //! Cut pixel values in specified range.
18001     /**
18002        \param min_value Minimum desired value of the resulting image.
18003        \param max_value Maximum desired value of the resulting image.
18004        \par Example
18005        \code
18006        const CImg<float> img("reference.jpg"), res = img.get_cut(160,220);
18007        (img,res).display();
18008        \endcode
18009        \image html ref_cut.jpg
18010     **/
18011     CImg<T>& cut(const T min_value, const T max_value) {
18012       if (is_empty()) return *this;
18013       const T a = min_value<max_value?min_value:max_value, b = min_value<max_value?max_value:min_value;
18014       cimg_for(*this,ptrd,T) *ptrd = (*ptrd<a)?a:((*ptrd>b)?b:*ptrd);
18015       return *this;
18016     }
18017 
18018     //! Cut pixel values in specified range \newinstance.
18019     CImg<T> get_cut(const T min_value, const T max_value) const {
18020       return (+*this).cut(min_value,max_value);
18021     }
18022 
18023     //! Uniformly quantize pixel values.
18024     /**
18025        \param nb_levels Number of quantization levels.
18026        \param keep_range Tells if resulting values keep the same range as the original ones.
18027        \par Example
18028        \code
18029        const CImg<float> img("reference.jpg"), res = img.get_quantize(4);
18030        (img,res).display();
18031        \endcode
18032        \image html ref_quantize.jpg
18033     **/
18034     CImg<T>& quantize(const unsigned int nb_levels, const bool keep_range=true) {
18035       if (!nb_levels)
18036         throw CImgArgumentException(_cimg_instance
18037                                     "quantize() : Invalid quantization request with 0 values.",
18038                                     cimg_instance);
18039 
18040       if (is_empty()) return *this;
18041       Tfloat m, M = (Tfloat)max_min(m), range = M - m;
18042       if (range>0) {
18043         if (keep_range) cimg_for(*this,ptrd,T) {
18044           const unsigned int val = (unsigned int)((*ptrd-m)*nb_levels/range);
18045           *ptrd = (T)(m + cimg::min(val,nb_levels-1)*range/nb_levels);
18046         } else cimg_for(*this,ptrd,T) {
18047           const unsigned int val = (unsigned int)((*ptrd-m)*nb_levels/range);
18048           *ptrd = (T)cimg::min(val,nb_levels-1);
18049         }
18050       }
18051       return *this;
18052     }
18053 
18054     //! Uniformly quantize pixel values \newinstance.
18055     CImg<T> get_quantize(const unsigned int n, const bool keep_range=true) const {
18056       return (+*this).quantize(n,keep_range);
18057     }
18058 
18059     //! Threshold pixel values.
18060     /**
18061        \param value Threshold value
18062        \param soft_threshold Tells if soft thresholding must be applied (instead of hard one).
18063        \param strict_threshold Tells if threshold value is strict.
18064        \par Example
18065        \code
18066        const CImg<float> img("reference.jpg"), res = img.get_threshold(128);
18067        (img,res.normalize(0,255)).display();
18068        \endcode
18069        \image html ref_threshold.jpg
18070     **/
18071     CImg<T>& threshold(const T value, const bool soft_threshold=false, const bool strict_threshold=false) {
18072       if (is_empty()) return *this;
18073       if (strict_threshold) {
18074         if (soft_threshold) cimg_for(*this,ptrd,T) { const T v = *ptrd; *ptrd = v>value?(T)(v-value):v<-(float)value?(T)(v+value):(T)0; }
18075         else cimg_for(*this,ptrd,T) *ptrd = *ptrd>value?(T)1:(T)0;
18076       } else {
18077         if (soft_threshold) cimg_for(*this,ptrd,T) { const T v = *ptrd; *ptrd = v>=value?(T)(v-value):v<=-(float)value?(T)(v+value):(T)0; }
18078         else cimg_for(*this,ptrd,T) *ptrd = *ptrd>=value?(T)1:(T)0;
18079       }
18080       return *this;
18081     }
18082 
18083     //! Threshold pixel values \newinstance.
18084     CImg<T> get_threshold(const T value, const bool soft_threshold=false, const bool strict_threshold=false) const {
18085       return (+*this).threshold(value,soft_threshold,strict_threshold);
18086     }
18087 
18088     //! Compute the histogram of pixel values.
18089     /**
18090        \param nb_levels Number of desired histogram levels.
18091        \param min_value Minimum pixel value considered for the histogram computation. All pixel values lower than \p min_value will not be counted.
18092        \param max_value Maximum pixel value considered for the histogram computation. All pixel values higher than \p max_value will not be counted.
18093        \note
18094        - The histogram H of an image I is the 1d function where H(x) counts the number of occurences of the value x in the image I.
18095        - If \p min_value==max_value==0 (default behavior), the function first estimates the whole range of pixel values
18096        then uses it to compute the histogram.
18097        - The resulting histogram is always defined in 1d. Histograms of multi-valued images are not multi-dimensional.
18098        \par Example
18099        \code
18100        const CImg<float> img = CImg<float>("reference.jpg").histogram(256);
18101        img.display_graph(0,3);
18102        \endcode
18103        \image html ref_histogram.jpg
18104     **/
18105     CImg<T>& histogram(const unsigned int nb_levels, const T min_value=(T)0, const T max_value=(T)0) {
18106       return get_histogram(nb_levels,min_value,max_value).move_to(*this);
18107     }
18108 
18109     //! Compute the histogram of pixel values \newinstance.
18110     CImg<floatT> get_histogram(const unsigned int nb_levels, const T min_value=(T)0, const T max_value=(T)0) const {
18111       if (!nb_levels || is_empty()) return CImg<floatT>();
18112       T vmin = min_value<max_value?min_value:max_value, vmax = min_value<max_value?max_value:min_value;
18113       if (vmin==vmax && vmin==0) vmin = min_max(vmax);
18114       CImg<floatT> res(nb_levels,1,1,1,0);
18115       cimg_for(*this,ptrs,T) {
18116         const T val = *ptrs;
18117         if (val>=vmin && val<=vmax) ++res[val==vmax?nb_levels-1:(unsigned int)((val-vmin)*nb_levels/(vmax-vmin))];
18118       }
18119       return res;
18120     }
18121 
18122     //! Equalize histogram of pixel values.
18123     /**
18124        \param nb_levels Number of histogram levels used for the equalization.
18125        \param min_value Minimum pixel value considered for the histogram computation. All pixel values lower than \p min_value will not be counted.
18126        \param max_value Maximum pixel value considered for the histogram computation. All pixel values higher than \p max_value will not be counted.
18127        \note
18128        - If \p min_value==max_value==0 (default behavior), the function first estimates the whole range of pixel values
18129        then uses it to equalize the histogram.
18130        \par Example
18131        \code
18132        const CImg<float> img("reference.jpg"), res = img.get_equalize(256);
18133        (img,res).display();
18134        \endcode
18135        \image html ref_equalize.jpg
18136     **/
18137     CImg<T>& equalize(const unsigned int nb_levels, const T min_value=(T)0, const T max_value=(T)0) {
18138       if (is_empty()) return *this;
18139       T vmin = min_value, vmax = max_value;
18140       if (vmin==vmax && vmin==0) vmin = min_max(vmax);
18141       if (vmin<vmax) {
18142         CImg<floatT> hist = get_histogram(nb_levels,vmin,vmax);
18143         float cumul = 0;
18144         cimg_forX(hist,pos) { cumul+=hist[pos]; hist[pos] = cumul; }
18145         cimg_for(*this,ptrd,T) {
18146           const int pos = (unsigned int)((*ptrd-vmin)*(nb_levels-1)/(vmax-vmin));
18147           if (pos>=0 && pos<(int)nb_levels) *ptrd = (T)(vmin + (vmax-vmin)*hist[pos]/size());
18148         }
18149       }
18150       return *this;
18151     }
18152 
18153     //! Equalize histogram of pixel values \newinstance.
18154     CImg<T> get_equalize(const unsigned int nblevels, const T val_min=(T)0, const T val_max=(T)0) const {
18155       return (+*this).equalize(nblevels,val_min,val_max);
18156     }
18157 
18158     //! Index multi-valued pixels regarding to a specified colormap.
18159     /**
18160        \param colormap Multi-valued colormap used as the basis for multi-valued pixel indexing.
18161        \param dithering Level of dithering (0=disable, 1=standard level).
18162        \param map_indexes Tell if the values of the resulting image are the colormap indices or the colormap vectors.
18163        \note
18164        - \p img.index(colormap,dithering,1) is equivalent to <tt>img.index(colormap,dithering,0).map(colormap)</tt>.
18165        \par Example
18166        \code
18167        const CImg<float> img("reference.jpg"), colormap(3,1,1,3, 0,128,255, 0,128,255, 0,128,255);
18168        const CImg<float> res = img.get_index(colormap,1,true);
18169        (img,res).display();
18170        \endcode
18171        \image html ref_index.jpg
18172     **/
18173     template<typename t>
18174     CImg<T>& index(const CImg<t>& colormap, const float dithering=1, const bool map_indexes=false) {
18175       return get_index(colormap,dithering,map_indexes).move_to(*this);
18176     }
18177 
18178     //! Index multi-valued pixels regarding to a specified colormap \newinstance.
18179     template<typename t>
18180     CImg<typename CImg<t>::Tuint>
18181     get_index(const CImg<t>& colormap, const float dithering=1, const bool map_indexes=true) const {
18182       if (colormap._spectrum!=_spectrum)
18183         throw CImgArgumentException(_cimg_instance
18184                                     "index() : Instance and specified colormap (%u,%u,%u,%u,%p) "
18185                                     "have incompatible dimensions.",
18186                                     cimg_instance,
18187                                     colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data);
18188 
18189       typedef typename CImg<t>::Tuint tuint;
18190       if (is_empty()) return CImg<tuint>();
18191       const unsigned long whd = (unsigned long)_width*_height*_depth, pwhd = (unsigned long)colormap._width*colormap._height*colormap._depth;
18192       CImg<tuint> res(_width,_height,_depth,map_indexes?_spectrum:1);
18193       tuint *ptrd = res._data;
18194       if (dithering>0) { // Dithered versions.
18195         const float ndithering = (dithering<0?0:dithering>1?1:dithering)/16;
18196         Tfloat valm = 0, valM = (Tfloat)max_min(valm);
18197         if (valm==valM && valm>=0 && valM<=255) { valm = 0; valM = 255; }
18198         CImg<Tfloat> cache = get_crop(-1,0,0,0,_width,1,0,_spectrum-1);
18199         Tfloat *cache_current = cache.data(1,0,0,0), *cache_next = cache.data(1,1,0,0);
18200         const unsigned long cwhd = (unsigned long)cache._width*cache._height*cache._depth;
18201         switch (_spectrum) {
18202         case 1 : { // Optimized for scalars.
18203           cimg_forYZ(*this,y,z) {
18204             if (y<height()-2) {
18205               Tfloat *ptrc0 = cache_next; const T *ptrs0 = data(0,y+1,z,0);
18206               cimg_forX(*this,x) *(ptrc0++) = (Tfloat)*(ptrs0++);
18207             }
18208             Tfloat *ptrs0 = cache_current, *ptrsn0 = cache_next;
18209             cimg_forX(*this,x) {
18210               const Tfloat _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0;
18211               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
18212               for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) {
18213                 const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0;
18214                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
18215               }
18216               const Tfloat err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering;
18217               *ptrs0+=7*err0; *(ptrsn0-1)+=3*err0; *(ptrsn0++)+=5*err0; *ptrsn0+=err0;
18218               if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
18219             }
18220             cimg::swap(cache_current,cache_next);
18221           }
18222         } break;
18223         case 2 : { // Optimized for 2d vectors.
18224           tuint *ptrd1 = ptrd + whd;
18225           cimg_forYZ(*this,y,z) {
18226             if (y<height()-2) {
18227               Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd;
18228               const T *ptrs0 = data(0,y+1,z,0), *ptrs1 = ptrs0 + whd;
18229               cimg_forX(*this,x) { *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); }
18230             }
18231             Tfloat
18232               *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd,
18233               *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd;
18234             cimg_forX(*this,x) {
18235               const Tfloat
18236                 _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0,
18237                 _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1;
18238               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
18239               for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
18240                 const Tfloat
18241                   pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1,
18242                   dist = pval0*pval0 + pval1*pval1;
18243                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
18244               }
18245               const t *const ptrmin1 = ptrmin0 + pwhd;
18246               const Tfloat
18247                 err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering,
18248                 err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)*ndithering;
18249               *ptrs0+=7*err0; *ptrs1+=7*err1;
18250               *(ptrsn0-1)+=3*err0; *(ptrsn1-1)+=3*err1;
18251               *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1;
18252               *ptrsn0+=err0; *ptrsn1+=err1;
18253               if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; }
18254               else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
18255             }
18256             cimg::swap(cache_current,cache_next);
18257           }
18258         } break;
18259         case 3 : { // Optimized for 3d vectors (colors).
18260           tuint *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd;
18261           cimg_forYZ(*this,y,z) {
18262             if (y<height()-2) {
18263               Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd, *ptrc2 = ptrc1 + cwhd;
18264               const T *ptrs0 = data(0,y+1,z,0), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd;
18265               cimg_forX(*this,x) { *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); *(ptrc2++) = (Tfloat)*(ptrs2++); }
18266             }
18267             Tfloat
18268               *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd, *ptrs2 = ptrs1 + cwhd,
18269               *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd, *ptrsn2 = ptrsn1 + cwhd;
18270             cimg_forX(*this,x) {
18271               const Tfloat
18272                 _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0,
18273                 _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1,
18274                 _val2 = (Tfloat)*ptrs2, val2 = _val2<valm?valm:_val2>valM?valM:_val2;
18275               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
18276               for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
18277                 const Tfloat
18278                   pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1, pval2 = (Tfloat)*(ptrp2++) - val2,
18279                   dist = pval0*pval0 + pval1*pval1 + pval2*pval2;
18280                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
18281               }
18282               const t *const ptrmin1 = ptrmin0 + pwhd, *const ptrmin2 = ptrmin1 + pwhd;
18283               const Tfloat
18284                 err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering,
18285                 err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)*ndithering,
18286                 err2 = ((*(ptrs2++)=val2) - (Tfloat)*ptrmin2)*ndithering;
18287 
18288               *ptrs0+=7*err0; *ptrs1+=7*err1; *ptrs2+=7*err2;
18289               *(ptrsn0-1)+=3*err0; *(ptrsn1-1)+=3*err1; *(ptrsn2-1)+=3*err2;
18290               *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1; *(ptrsn2++)+=5*err2;
18291               *ptrsn0+=err0; *ptrsn1+=err1; *ptrsn2+=err2;
18292 
18293               if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; *(ptrd2++) = (tuint)*ptrmin2; }
18294               else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
18295             }
18296             cimg::swap(cache_current,cache_next);
18297           }
18298         } break;
18299         default : // Generic version
18300           cimg_forYZ(*this,y,z) {
18301             if (y<height()-2) {
18302               Tfloat *ptrc = cache_next;
18303               cimg_forC(*this,c) {
18304                 Tfloat *_ptrc = ptrc; const T *_ptrs = data(0,y+1,z,c);
18305                 cimg_forX(*this,x) *(_ptrc++) = (Tfloat)*(_ptrs++);
18306                 ptrc+=cwhd;
18307               }
18308             }
18309             Tfloat *ptrs = cache_current, *ptrsn = cache_next;
18310             cimg_forX(*this,x) {
18311               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = colormap._data;
18312               for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) {
18313                 Tfloat dist = 0; Tfloat *_ptrs = ptrs; const t *_ptrp = ptrp;
18314                 cimg_forC(*this,c) {
18315                   const Tfloat _val = *_ptrs, val = _val<valm?valm:_val>valM?valM:_val;
18316                   dist+=cimg::sqr((*_ptrs=val) - (Tfloat)*_ptrp); _ptrs+=cwhd; _ptrp+=pwhd;
18317                 }
18318                 if (dist<distmin) { ptrmin = ptrp; distmin = dist; }
18319               }
18320               const t *_ptrmin = ptrmin; Tfloat *_ptrs = ptrs++, *_ptrsn = (ptrsn++)-1;
18321               cimg_forC(*this,c) {
18322                 const Tfloat err = (*(_ptrs++) - (Tfloat)*_ptrmin)*ndithering;
18323                 *_ptrs+=7*err; *(_ptrsn++)+=3*err; *(_ptrsn++)+=5*err; *_ptrsn+=err;
18324                 _ptrmin+=pwhd; _ptrs+=cwhd-1; _ptrsn+=cwhd-2;
18325               }
18326               if (map_indexes) {
18327                 tuint *_ptrd = ptrd++;
18328                 cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; }
18329               }
18330               else *(ptrd++) = (tuint)(ptrmin - colormap._data);
18331             }
18332             cimg::swap(cache_current,cache_next);
18333           }
18334         }
18335       } else { // Non-dithered versions
18336         switch (_spectrum) {
18337         case 1 : { // Optimized for scalars.
18338           for (const T *ptrs0 = _data, *ptrs_end = ptrs0 + whd; ptrs0<ptrs_end; ) {
18339             const Tfloat val0 = (Tfloat)*(ptrs0++);
18340             Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
18341             for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) {
18342               const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0;
18343               if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
18344             }
18345             if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
18346           }
18347         } break;
18348         case 2 : { // Optimized for 2d vectors.
18349           tuint *ptrd1 = ptrd + whd;
18350           for (const T *ptrs0 = _data, *ptrs1 = ptrs0 + whd, *ptrs_end = ptrs1; ptrs0<ptrs_end; ) {
18351             const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++);
18352             Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
18353             for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
18354               const Tfloat
18355                 pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1,
18356                 dist = pval0*pval0 + pval1*pval1;
18357               if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
18358             }
18359             if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*(ptrmin0 + pwhd); }
18360             else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
18361           }
18362         } break;
18363         case 3 : { // Optimized for 3d vectors (colors).
18364           tuint *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd;
18365           for (const T *ptrs0 = _data, *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd, *ptrs_end = ptrs1; ptrs0<ptrs_end; ) {
18366             const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++), val2 = (Tfloat)*(ptrs2++);
18367             Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
18368             for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
18369               const Tfloat
18370                 pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1, pval2 = (Tfloat)*(ptrp2++) - val2,
18371                 dist = pval0*pval0 + pval1*pval1 + pval2*pval2;
18372               if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
18373             }
18374             if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*(ptrmin0 + pwhd); *(ptrd2++) = (tuint)*(ptrmin0 + 2*pwhd); }
18375             else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
18376           }
18377         } break;
18378         default : // Generic version.
18379           for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ++ptrs) {
18380             Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = colormap._data;
18381             for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) {
18382               Tfloat dist = 0; const T *_ptrs = ptrs; const t *_ptrp = ptrp;
18383               cimg_forC(*this,c) { dist+=cimg::sqr((Tfloat)*_ptrs - (Tfloat)*_ptrp); _ptrs+=whd; _ptrp+=pwhd; }
18384               if (dist<distmin) { ptrmin = ptrp; distmin = dist; }
18385             }
18386             if (map_indexes) {
18387               tuint *_ptrd = ptrd++;
18388               cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; }
18389             }
18390             else *(ptrd++) = (tuint)(ptrmin - colormap._data);
18391           }
18392         }
18393       }
18394       return res;
18395     }
18396 
18397     //! Map predefined colormap on the scalar (indexed) image instance.
18398     /**
18399        \param colormap Multi-valued colormap used for mapping the indexes.
18400        \par Example
18401        \code
18402        const CImg<float> img("reference.jpg"),
18403                          colormap1(3,1,1,3, 0,128,255, 0,128,255, 0,128,255),
18404                          colormap2(3,1,1,3, 255,0,0, 0,255,0, 0,0,255),
18405                          res = img.get_index(colormap1,0).map(colormap2);
18406        (img,res).display();
18407        \endcode
18408        \image html ref_map.jpg
18409     **/
18410     template<typename t>
18411     CImg<T>& map(const CImg<t>& colormap) {
18412       return get_map(colormap).move_to(*this);
18413     }
18414 
18415     //! Map predefined colormap on the scalar (indexed) image instance \newinstance.
18416     template<typename t>
18417     CImg<t> get_map(const CImg<t>& colormap) const {
18418       if (_spectrum!=1 && colormap._spectrum!=1)
18419         throw CImgArgumentException(_cimg_instance
18420                                     "map() : Instance and specified colormap (%u,%u,%u,%u,%p) "
18421                                     "have incompatible dimensions.",
18422                                     cimg_instance,
18423                                     colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data);
18424 
18425       const unsigned long whd = (unsigned long)_width*_height*_depth, pwhd = (unsigned long)colormap._width*colormap._height*colormap._depth;
18426       CImg<t> res(_width,_height,_depth,colormap._spectrum==1?_spectrum:colormap._spectrum);
18427       switch (colormap._spectrum) {
18428       case 1 : { // Optimized for scalars.
18429         const T *ptrs = _data;
18430         cimg_for(res,ptrd,t) {
18431           const unsigned long _ind = (unsigned long)*(ptrs++), ind = _ind<pwhd?_ind:0;
18432           *ptrd = colormap[ind];
18433         }
18434       } break;
18435       case 2 : { // Optimized for 2d vectors.
18436         const t *const ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd;
18437         t *ptrd0 = res._data, *ptrd1 = ptrd0 + whd;
18438         for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
18439           const unsigned long _ind = (unsigned long)*(ptrs++), ind = _ind<pwhd?_ind:0;
18440           *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind];
18441         }
18442       } break;
18443       case 3 : { // Optimized for 3d vectors (colors).
18444         const t *const ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd;
18445         t *ptrd0 = res._data, *ptrd1 = ptrd0 + whd, *ptrd2 = ptrd1 + whd;
18446         for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
18447           const unsigned long _ind = (unsigned long)*(ptrs++), ind = _ind<pwhd?_ind:0;
18448           *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind]; *(ptrd2++) = ptrp2[ind];
18449         }
18450       } break;
18451       default : { // Generic version.
18452         t *ptrd = res._data;
18453         for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
18454           const unsigned long _ind = (unsigned long)*(ptrs++), ind = _ind<pwhd?_ind:0;
18455           const t *ptrp = colormap._data + ind;
18456           t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = *ptrp; _ptrd+=whd; ptrp+=pwhd; }
18457         }
18458       }
18459       }
18460       return res;
18461     }
18462 
18463     //! Label connected components.
18464     /**
18465        \param is_high_connectivity Boolean that choose between 4(false)- or 8(true)-connectivity
18466        in 2d case, and between 6(false)- or 26(true)-connectivity in 3d case.
18467        \param tolerance Tolerance used to determine if two neighboring pixels belong to the same region.
18468        \note The algorithm of connected components computation has been primarily done
18469        by A. Meijster, according to the publication :
18470        'W.H. Hesselink, A. Meijster, C. Bron, "Concurrent Determination of Connected Components.",
18471        In: Science of Computer Programming 41 (2001), pp. 173--194'.
18472        The submitted code has then been modified to fit CImg coding style and constraints.
18473     **/
18474     CImg<T>& label(const bool is_high_connectivity=false, const Tfloat tolerance=0) {
18475       return get_label(is_high_connectivity,tolerance).move_to(*this);
18476     }
18477 
18478     //! Label connected components \newinstance.
18479     CImg<unsigned long> get_label(const bool is_high_connectivity=false,
18480                                   const Tfloat tolerance=0) const {
18481       if (is_empty()) return CImg<unsigned long>();
18482 
18483       // Create neighborhood tables.
18484       int dx[13], dy[13], dz[13], nb = 0;
18485       dx[nb]=1; dy[nb] = 0; dz[nb++]=0;
18486       dx[nb]=0; dy[nb] = 1; dz[nb++]=0;
18487       if (is_high_connectivity) {
18488         dx[nb]=1; dy[nb] = 1; dz[nb++]=0;
18489         dx[nb]=1; dy[nb] = -1; dz[nb++]=0;
18490       }
18491       if (_depth>1) { // 3d version.
18492         dx[nb]=0; dy[nb] = 0; dz[nb++]=1;
18493         if (is_high_connectivity) {
18494           dx[nb]=1; dy[nb] = 1; dz[nb++]=-1;
18495           dx[nb]=1; dy[nb] = 0; dz[nb++]=-1;
18496           dx[nb]=1; dy[nb] = -1; dz[nb++]=-1;
18497           dx[nb]=0; dy[nb] = 1; dz[nb++]=-1;
18498 
18499           dx[nb]=0; dy[nb] = 1; dz[nb++]=1;
18500           dx[nb]=1; dy[nb] = -1; dz[nb++]=1;
18501           dx[nb]=1; dy[nb] = 0; dz[nb++]=1;
18502           dx[nb]=1; dy[nb] = 1; dz[nb++]=1;
18503         }
18504       }
18505       return _get_label(nb,dx,dy,dz,tolerance);
18506     }
18507 
18508     //! Label connected components \overloading.
18509     /**
18510        \param connectivity_mask Mask of the neighboring pixels.
18511        \param tolerance Tolerance used to determine if two neighboring pixels belong to the same region.
18512     **/
18513     template<typename t>
18514     CImg<T>& label(const CImg<t>& connectivity_mask, const Tfloat tolerance=0) {
18515       return get_label(connectivity_mask,tolerance).move_to(*this);
18516     }
18517 
18518     //! Label connected components \newinstance.
18519     template<typename t>
18520     CImg<unsigned long> get_label(const CImg<t>& connectivity_mask,
18521                                   const Tfloat tolerance=0) const {
18522       int nb = 0;
18523       cimg_for(connectivity_mask,ptr,t) if (*ptr) ++nb;
18524       CImg<intT> dx(nb,1,1,1,0), dy(nb,1,1,1,0), dz(nb,1,1,1,0);
18525       nb = 0;
18526       cimg_forXYZ(connectivity_mask,x,y,z) if ((x || y || z) &&
18527                                                connectivity_mask(x,y,z)) {
18528         dx[nb] = x; dy[nb] = y; dz[nb++] = z;
18529       }
18530       return _get_label(nb,dx,dy,dz,tolerance);
18531     }
18532 
18533     CImg<unsigned long> _get_label(const unsigned int nb, const int
18534                                    *const dx, const int *const dy, const int *const dz,
18535                                    const Tfloat tolerance) const {
18536       CImg<unsigned long> res(_width,_height,_depth,_spectrum);
18537       cimg_forC(*this,c) {
18538         CImg<unsigned long> _res = res.get_shared_channel(c);
18539 
18540         // Init label numbers.
18541         unsigned long *ptr = _res.data();
18542         cimg_foroff(_res,p) *(ptr++) = p;
18543 
18544         // For each neighbour-direction, label.
18545         for (unsigned int n = 0; n<nb; ++n) {
18546           const int _dx = dx[n], _dy = dy[n], _dz = dz[n];
18547           if (_dx || _dy || _dz) {
18548             const int
18549               x0 = _dx<0?-_dx:0,
18550               x1 = _dx<0?_width:_width - _dx,
18551               y0 = _dy<0?-_dy:0,
18552               y1 = _dy<0?_height:_height - _dy,
18553               z0 = _dz<0?-_dz:0,
18554               z1 = _dz<0?_depth:_depth - _dz;
18555             const long wh = (long)_width*_height, offset = (long)_dz*wh + (long)_dy*_width + _dx;
18556             for (long z = z0, nz = z0 + _dz, pz = z0*wh; z<z1; ++z, ++nz, pz+=wh) {
18557               for (long y = y0, ny = y0 + _dy, py = y0*_width + pz; y<y1; ++y, ++ny, py+=_width) {
18558                 for (long x = x0, nx = x0 + _dx, p = x0 + py; x<x1; ++x, ++nx, ++p) {
18559                   if ((Tfloat)cimg::abs((*this)(x,y,z,c,wh)-(*this)(nx,ny,nz,c,wh))<=tolerance) {
18560                     const long q = p + offset;
18561                     unsigned long x, y;
18562                     for (x = p<q?q:p, y = p<q?p:q; x!=y && _res[x]!=x; ) { x = _res[x]; if (x<y) cimg::swap(x,y); }
18563                     if (x!=y) _res[x] = y;
18564                     for (unsigned long _p = p; _p!=y; ) { const unsigned long h = _res[_p]; _res[_p] = y; _p = h; }
18565                     for (unsigned long _q = q; _q!=y; ) { const unsigned long h = _res[_q]; _res[_q] = y; _q = h; }
18566                   }
18567                 }
18568               }
18569             }
18570           }
18571         }
18572 
18573         // Resolve equivalences.
18574         unsigned long counter = 0;
18575         ptr = _res.data();
18576         cimg_foroff(_res,p) { *ptr = *ptr==p?counter++:_res[*ptr]; ++ptr; }
18577       }
18578       return res;
18579     }
18580 
18581     //@}
18582     //---------------------------------
18583     //
18584     //! \name Color Base Management
18585     //@{
18586     //---------------------------------
18587 
18588     //! Return colormap \e "default", containing 256 colors entries in RGB.
18589     /**
18590        \return The following \c 256x1x1x3 colormap is returned :
18591        \image html ref_colormap_default.jpg
18592     **/
18593     static const CImg<Tuchar>& default_LUT256() {
18594       static CImg<Tuchar> colormap;
18595       if (!colormap) {
18596         colormap.assign(1,256,1,3);
18597         for (unsigned int index = 0, r = 16; r<256; r+=32)
18598           for (unsigned int g = 16; g<256; g+=32)
18599             for (unsigned int b = 32; b<256; b+=64) {
18600               colormap(0,index,0) = (Tuchar)r;
18601               colormap(0,index,1) = (Tuchar)g;
18602               colormap(0,index++,2) = (Tuchar)b;
18603             }
18604       }
18605       return colormap;
18606     }
18607 
18608     //! Return colormap \e "HSV", containing 256 colors entries in RGB.
18609     /**
18610        \return The following \c 256x1x1x3 colormap is returned :
18611        \image html ref_colormap_hsv.jpg
18612     **/
18613     static const CImg<Tuchar>& HSV_LUT256() {
18614       static CImg<Tuchar> colormap;
18615       if (!colormap) {
18616         CImg<Tint> tmp(1,256,1,3,1);
18617         tmp.get_shared_channel(0).sequence(0,359);
18618         colormap = tmp.HSVtoRGB();
18619       }
18620       return colormap;
18621     }
18622 
18623     //! Return colormap \e "lines", containing 256 colors entries in RGB.
18624     /**
18625        \return The following \c 256x1x1x3 colormap is returned :
18626        \image html ref_colormap_lines.jpg
18627     **/
18628     static const CImg<Tuchar>& lines_LUT256() {
18629       static const unsigned char pal[] = {
18630         217,62,88,75,1,237,240,12,56,160,165,116,1,1,204,2,15,248,148,185,133,141,46,246,222,116,16,5,207,226,
18631         17,114,247,1,214,53,238,0,95,55,233,235,109,0,17,54,33,0,90,30,3,0,94,27,19,0,68,212,166,130,0,15,7,119,
18632         238,2,246,198,0,3,16,10,13,2,25,28,12,6,2,99,18,141,30,4,3,140,12,4,30,233,7,10,0,136,35,160,168,184,20,
18633         233,0,1,242,83,90,56,180,44,41,0,6,19,207,5,31,214,4,35,153,180,75,21,76,16,202,218,22,17,2,136,71,74,
18634         81,251,244,148,222,17,0,234,24,0,200,16,239,15,225,102,230,186,58,230,110,12,0,7,129,249,22,241,37,219,
18635         1,3,254,210,3,212,113,131,197,162,123,252,90,96,209,60,0,17,0,180,249,12,112,165,43,27,229,77,40,195,12,
18636         87,1,210,148,47,80,5,9,1,137,2,40,57,205,244,40,8,252,98,0,40,43,206,31,187,0,180,1,69,70,227,131,108,0,
18637         223,94,228,35,248,243,4,16,0,34,24,2,9,35,73,91,12,199,51,1,249,12,103,131,20,224,2,70,32,
18638         233,1,165,3,8,154,246,233,196,5,0,6,183,227,247,195,208,36,0,0,226,160,210,198,69,153,210,1,23,8,192,2,4,
18639         137,1,0,52,2,249,241,129,0,0,234,7,238,71,7,32,15,157,157,252,158,2,250,6,13,30,11,162,0,199,21,11,27,224,
18640         4,157,20,181,111,187,218,3,0,11,158,230,196,34,223,22,248,135,254,210,157,219,0,117,239,3,255,4,227,5,247,
18641         11,4,3,188,111,11,105,195,2,0,14,1,21,219,192,0,183,191,113,241,1,12,17,248,0,48,7,19,1,254,212,0,239,246,
18642         0,23,0,250,165,194,194,17,3,253,0,24,6,0,141,167,221,24,212,2,235,243,0,0,205,1,251,133,204,28,4,6,1,10,
18643         141,21,74,12,236,254,228,19,1,0,214,1,186,13,13,6,13,16,27,209,6,216,11,207,251,59,32,9,155,23,19,235,143,
18644         116,6,213,6,75,159,23,6,0,228,4,10,245,249,1,7,44,234,4,102,174,0,19,239,103,16,15,18,8,214,22,4,47,244,
18645         255,8,0,251,173,1,212,252,250,251,252,6,0,29,29,222,233,246,5,149,0,182,180,13,151,0,203,183,0,35,149,0,
18646         235,246,254,78,9,17,203,73,11,195,0,3,5,44,0,0,237,5,106,6,130,16,214,20,168,247,168,4,207,11,5,1,232,251,
18647         129,210,116,231,217,223,214,27,45,38,4,177,186,249,7,215,172,16,214,27,249,230,236,2,34,216,217,0,175,30,
18648         243,225,244,182,20,212,2,226,21,255,20,0,2,13,62,13,191,14,76,64,20,121,4,118,0,216,1,147,0,2,210,1,215,
18649         95,210,236,225,184,46,0,248,24,11,1,9,141,250,243,9,221,233,160,11,147,2,55,8,23,12,253,9,0,54,0,231,6,3,
18650         141,8,2,246,9,180,5,11,8,227,8,43,110,242,1,130,5,97,36,10,6,219,86,133,11,108,6,1,5,244,67,19,28,0,174,
18651         154,16,127,149,252,188,196,196,228,244,9,249,0,0,0,37,170,32,250,0,73,255,23,3,224,234,38,195,198,0,255,87,
18652         33,221,174,31,3,0,189,228,6,153,14,144,14,108,197,0,9,206,245,254,3,16,253,178,248,0,95,125,8,0,3,168,21,
18653         23,168,19,50,240,244,185,0,1,144,10,168,31,82,1,13 };
18654       static const CImg<Tuchar> colormap(pal,1,256,1,3,false);
18655       return colormap;
18656     }
18657 
18658     //! Return colormap \e "hot", containing 256 colors entries in RGB.
18659     /**
18660        \return The following \c 256x1x1x3 colormap is returned :
18661        \image html ref_colormap_hot.jpg
18662     **/
18663     static const CImg<Tuchar>& hot_LUT256() {
18664       static CImg<Tuchar> colormap;
18665       if (!colormap) {
18666         colormap.assign(1,4,1,3,0);
18667         colormap[1] = colormap[2] = colormap[3] = colormap[6] = colormap[7] = colormap[11] = 255;
18668         colormap.resize(1,256,1,3,3);
18669       }
18670       return colormap;
18671     }
18672 
18673     //! Return colormap \e "cool", containing 256 colors entries in RGB.
18674     /**
18675        \return The following \c 256x1x1x3 colormap is returned :
18676        \image html ref_colormap_cool.jpg
18677     **/
18678     static const CImg<Tuchar>& cool_LUT256() {
18679       static CImg<Tuchar> colormap;
18680       if (!colormap) colormap.assign(1,2,1,3).fill(0,255,255,0,255,255).resize(1,256,1,3,3);
18681       return colormap;
18682     }
18683 
18684     //! Return colormap \e "jet", containing 256 colors entries in RGB.
18685     /**
18686        \return The following \c 256x1x1x3 colormap is returned :
18687        \image html ref_colormap_jet.jpg
18688     **/
18689     static const CImg<Tuchar>& jet_LUT256() {
18690       static CImg<Tuchar> colormap;
18691       if (!colormap) {
18692         colormap.assign(1,4,1,3,0);
18693         colormap[2] = colormap[3] = colormap[5] = colormap[6] = colormap[8] = colormap[9] = 255;
18694         colormap.resize(1,256,1,3,3);
18695       }
18696       return colormap;
18697     }
18698 
18699     //! Return colormap \e "flag", containing 256 colors entries in RGB.
18700     /**
18701        \return The following \c 256x1x1x3 colormap is returned :
18702        \image html ref_colormap_flag.jpg
18703     **/
18704     static const CImg<Tuchar>& flag_LUT256() {
18705       static CImg<Tuchar> colormap;
18706       if (!colormap) {
18707         colormap.assign(1,4,1,3,0);
18708         colormap[0] = colormap[1] = colormap[5] = colormap[9] = colormap[10] = 255;
18709         colormap.resize(1,256,1,3,0,2);
18710       }
18711       return colormap;
18712     }
18713 
18714     //! Return colormap \e "cube", containing 256 colors entries in RGB.
18715     /**
18716        \return The following \c 256x1x1x3 colormap is returned :
18717        \image html ref_colormap_cube.jpg
18718     **/
18719     static const CImg<Tuchar>& cube_LUT256() {
18720       static CImg<Tuchar> colormap;
18721       if (!colormap) {
18722         colormap.assign(1,8,1,3,0);
18723         colormap[1] = colormap[3] = colormap[5] = colormap[7] =
18724           colormap[10] = colormap[11] = colormap[12] = colormap[13] =
18725           colormap[20] = colormap[21] = colormap[22] = colormap[23] = 255;
18726         colormap.resize(1,256,1,3,3);
18727       }
18728       return colormap;
18729     }
18730 
18731     //! Convert pixel values from sRGB to RGB color spaces.
18732     CImg<T>& sRGBtoRGB() {
18733       cimg_for(*this,ptr,T) {
18734         const Tfloat
18735           sval = (Tfloat)*ptr,
18736           nsval = (sval<0?0:sval>255?255:sval)/255,
18737           val = (Tfloat)(nsval<=0.04045f?nsval/12.92f:std::pow((nsval+0.055f)/(1.055f),2.4f));
18738         *ptr = (T)(val*255);
18739       }
18740       return *this;
18741     }
18742 
18743     //! Convert pixel values from sRGB to RGB color spaces \newinstance.
18744     CImg<Tfloat> get_sRGBtoRGB() const {
18745       return CImg<Tfloat>(*this,false).sRGBtoRGB();
18746     }
18747 
18748     //! Convert pixel values from RGB to sRGB color spaces.
18749     CImg<T>& RGBtosRGB() {
18750       cimg_for(*this,ptr,T) {
18751         const Tfloat
18752           val = (Tfloat)*ptr,
18753           nval = (val<0?0:val>255?255:val)/255,
18754           sval = (Tfloat)(nval<=0.0031308f?nval*12.92f:1.055f*std::pow(nval,0.416667f)-0.055f);
18755         *ptr = (T)(sval*255);
18756       }
18757       return *this;
18758     }
18759 
18760     //! Convert pixel values from RGB to sRGB color spaces \newinstance.
18761     CImg<Tfloat> get_RGBtosRGB() const {
18762       return CImg<Tfloat>(*this,false).RGBtosRGB();
18763     }
18764 
18765     //! Convert pixel values from RGB to HSV color spaces.
18766     CImg<T>& RGBtoHSV() {
18767       if (_spectrum!=3)
18768         throw CImgInstanceException(_cimg_instance
18769                                     "RGBtoHSV() : Instance is not a RGB image.",
18770                                     cimg_instance);
18771 
18772       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18773       for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
18774         const Tfloat
18775           R = (Tfloat)*p1,
18776           G = (Tfloat)*p2,
18777           B = (Tfloat)*p3,
18778           nR = (R<0?0:(R>255?255:R))/255,
18779           nG = (G<0?0:(G>255?255:G))/255,
18780           nB = (B<0?0:(B>255?255:B))/255,
18781           m = cimg::min(nR,nG,nB),
18782           M = cimg::max(nR,nG,nB);
18783         Tfloat H = 0, S = 0;
18784         if (M!=m) {
18785           const Tfloat
18786             f = (nR==m)?(nG-nB):((nG==m)?(nB-nR):(nR-nG)),
18787             i = (Tfloat)((nR==m)?3:((nG==m)?5:1));
18788           H = (i-f/(M-m));
18789           if (H>=6) H-=6;
18790           H*=60;
18791           S = (M-m)/M;
18792         }
18793         *(p1++) = (T)H;
18794         *(p2++) = (T)S;
18795         *(p3++) = (T)M;
18796       }
18797       return *this;
18798     }
18799 
18800     //! Convert pixel values from RGB to HSV color spaces \newinstance.
18801     CImg<Tfloat> get_RGBtoHSV() const {
18802       return CImg<Tfloat>(*this,false).RGBtoHSV();
18803     }
18804 
18805     //! Convert pixel values from HSV to RGB color spaces.
18806     CImg<T>& HSVtoRGB() {
18807       if (_spectrum!=3)
18808         throw CImgInstanceException(_cimg_instance
18809                                     "HSVtoRGB() : Instance is not a HSV image.",
18810                                     cimg_instance);
18811 
18812       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18813       for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
18814         Tfloat
18815           H = (Tfloat)*p1,
18816           S = (Tfloat)*p2,
18817           V = (Tfloat)*p3,
18818           R = 0, G = 0, B = 0;
18819         if (H==0 && S==0) R = G = B = V;
18820         else {
18821           H/=60;
18822           const int i = (int)std::floor(H);
18823           const Tfloat
18824             f = (i&1)?(H - i):(1 - H + i),
18825             m = V*(1 - S),
18826             n = V*(1 - S*f);
18827           switch (i) {
18828           case 6 :
18829           case 0 : R = V; G = n; B = m; break;
18830           case 1 : R = n; G = V; B = m; break;
18831           case 2 : R = m; G = V; B = n; break;
18832           case 3 : R = m; G = n; B = V; break;
18833           case 4 : R = n; G = m; B = V; break;
18834           case 5 : R = V; G = m; B = n; break;
18835           }
18836         }
18837         R*=255; G*=255; B*=255;
18838         *(p1++) = (T)(R<0?0:(R>255?255:R));
18839         *(p2++) = (T)(G<0?0:(G>255?255:G));
18840         *(p3++) = (T)(B<0?0:(B>255?255:B));
18841       }
18842       return *this;
18843     }
18844 
18845     //! Convert pixel values from HSV to RGB color spaces \newinstance.
18846     CImg<Tuchar> get_HSVtoRGB() const {
18847       return CImg<Tuchar>(*this,false).HSVtoRGB();
18848     }
18849 
18850     //! Convert pixel values from RGB to HSL color spaces.
18851     CImg<T>& RGBtoHSL() {
18852       if (_spectrum!=3)
18853         throw CImgInstanceException(_cimg_instance
18854                                     "RGBtoHSL() : Instance is not a RGB image.",
18855                                     cimg_instance);
18856 
18857       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18858       for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
18859         const Tfloat
18860           R = (Tfloat)*p1,
18861           G = (Tfloat)*p2,
18862           B = (Tfloat)*p3,
18863           nR = (R<0?0:(R>255?255:R))/255,
18864           nG = (G<0?0:(G>255?255:G))/255,
18865           nB = (B<0?0:(B>255?255:B))/255,
18866           m = cimg::min(nR,nG,nB),
18867           M = cimg::max(nR,nG,nB),
18868           L = (m + M)/2;
18869         Tfloat H = 0, S = 0;
18870         if (M==m) H = S = 0;
18871         else {
18872           const Tfloat
18873             f = (nR==m)?(nG-nB):((nG==m)?(nB-nR):(nR-nG)),
18874             i = (nR==m)?3.0f:((nG==m)?5.0f:1.0f);
18875           H = (i-f/(M-m));
18876           if (H>=6) H-=6;
18877           H*=60;
18878           S = (2*L<=1)?((M-m)/(M+m)):((M-m)/(2-M-m));
18879         }
18880         *(p1++) = (T)H;
18881         *(p2++) = (T)S;
18882         *(p3++) = (T)L;
18883       }
18884       return *this;
18885     }
18886 
18887     //! Convert pixel values from RGB to HSL color spaces \newinstance.
18888     CImg<Tfloat> get_RGBtoHSL() const {
18889       return CImg< Tfloat>(*this,false).RGBtoHSL();
18890     }
18891 
18892     //! Convert pixel values from HSL to RGB color spaces.
18893     CImg<T>& HSLtoRGB() {
18894       if (_spectrum!=3)
18895         throw CImgInstanceException(_cimg_instance
18896                                     "HSLtoRGB() : Instance is not a HSL image.",
18897                                     cimg_instance);
18898 
18899       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18900       for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
18901         const Tfloat
18902           H = (Tfloat)*p1,
18903           S = (Tfloat)*p2,
18904           L = (Tfloat)*p3,
18905           q = 2*L<1?L*(1+S):(L+S-L*S),
18906           p = 2*L-q,
18907           h = H/360,
18908           tr = h + 1.0f/3,
18909           tg = h,
18910           tb = h - 1.0f/3,
18911           ntr = tr<0?tr+1:(tr>1?tr-1:tr),
18912           ntg = tg<0?tg+1:(tg>1?tg-1:tg),
18913           ntb = tb<0?tb+1:(tb>1?tb-1:tb),
18914           R = 255*(6*ntr<1?p+(q-p)*6*ntr:(2*ntr<1?q:(3*ntr<2?p+(q-p)*6*(2.0f/3-ntr):p))),
18915           G = 255*(6*ntg<1?p+(q-p)*6*ntg:(2*ntg<1?q:(3*ntg<2?p+(q-p)*6*(2.0f/3-ntg):p))),
18916           B = 255*(6*ntb<1?p+(q-p)*6*ntb:(2*ntb<1?q:(3*ntb<2?p+(q-p)*6*(2.0f/3-ntb):p)));
18917         *(p1++) = (T)(R<0?0:(R>255?255:R));
18918         *(p2++) = (T)(G<0?0:(G>255?255:G));
18919         *(p3++) = (T)(B<0?0:(B>255?255:B));
18920       }
18921       return *this;
18922     }
18923 
18924     //! Convert pixel values from HSL to RGB color spaces \newinstance.
18925     CImg<Tuchar> get_HSLtoRGB() const {
18926       return CImg<Tuchar>(*this,false).HSLtoRGB();
18927     }
18928 
18929     //! Convert pixel values from RGB to HSI color spaces.
18930     CImg<T>& RGBtoHSI() {
18931       if (_spectrum!=3)
18932         throw CImgInstanceException(_cimg_instance
18933                                     "RGBtoHSI() : Instance is not a RGB image.",
18934                                     cimg_instance);
18935 
18936       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18937       for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
18938         const Tfloat
18939           R = (Tfloat)*p1,
18940           G = (Tfloat)*p2,
18941           B = (Tfloat)*p3,
18942           nR = (R<0?0:(R>255?255:R))/255,
18943           nG = (G<0?0:(G>255?255:G))/255,
18944           nB = (B<0?0:(B>255?255:B))/255,
18945           m = cimg::min(nR,nG,nB),
18946           theta = (Tfloat)(std::acos(0.5f*((nR-nG)+(nR-nB))/std::sqrt(std::pow(nR-nG,2)+(nR-nB)*(nG-nB)))*180/cimg::PI),
18947           sum = nR + nG + nB;
18948         Tfloat H = 0, S = 0, I = 0;
18949         if (theta>0) H = (nB<=nG)?theta:360-theta;
18950         if (sum>0) S = 1 - 3/sum*m;
18951         I = sum/3;
18952         *(p1++) = (T)H;
18953         *(p2++) = (T)S;
18954         *(p3++) = (T)I;
18955       }
18956       return *this;
18957     }
18958 
18959     //! Convert pixel values from RGB to HSI color spaces \newinstance.
18960     CImg<Tfloat> get_RGBtoHSI() const {
18961       return CImg<Tfloat>(*this,false).RGBtoHSI();
18962     }
18963 
18964     //! Convert pixel values from HSI to RGB color spaces.
18965     CImg<T>& HSItoRGB() {
18966       if (_spectrum!=3)
18967         throw CImgInstanceException(_cimg_instance
18968                                     "HSItoRGB() : Instance is not a HSI image.",
18969                                     cimg_instance);
18970 
18971       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18972       for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
18973         Tfloat
18974           H = (Tfloat)*p1,
18975           S = (Tfloat)*p2,
18976           I = (Tfloat)*p3,
18977           a = I*(1-S),
18978           R = 0, G = 0, B = 0;
18979         if (H<120) {
18980           B = a;
18981           R = (Tfloat)(I*(1+S*std::cos(H*cimg::PI/180)/std::cos((60-H)*cimg::PI/180)));
18982           G = 3*I-(R+B);
18983         } else if (H<240) {
18984           H-=120;
18985           R = a;
18986           G = (Tfloat)(I*(1+S*std::cos(H*cimg::PI/180)/std::cos((60-H)*cimg::PI/180)));
18987           B = 3*I-(R+G);
18988         } else {
18989           H-=240;
18990           G = a;
18991           B = (Tfloat)(I*(1+S*std::cos(H*cimg::PI/180)/std::cos((60-H)*cimg::PI/180)));
18992           R = 3*I-(G+B);
18993         }
18994         R*=255; G*=255; B*=255;
18995         *(p1++) = (T)(R<0?0:(R>255?255:R));
18996         *(p2++) = (T)(G<0?0:(G>255?255:G));
18997         *(p3++) = (T)(B<0?0:(B>255?255:B));
18998       }
18999       return *this;
19000     }
19001 
19002     //! Convert pixel values from HSI to RGB color spaces \newinstance.
19003     CImg<Tfloat> get_HSItoRGB() const {
19004       return CImg< Tuchar>(*this,false).HSItoRGB();
19005     }
19006 
19007     //! Convert pixel values from RGB to YCbCr color spaces.
19008     CImg<T>& RGBtoYCbCr() {
19009       if (_spectrum!=3)
19010         throw CImgInstanceException(_cimg_instance
19011                                     "RGBtoYCbCr() : Instance is not a RGB image.",
19012                                     cimg_instance);
19013 
19014       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
19015       for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19016         const Tfloat
19017           R = (Tfloat)*p1,
19018           G = (Tfloat)*p2,
19019           B = (Tfloat)*p3,
19020           Y = (66*R + 129*G + 25*B + 128)/256 + 16,
19021           Cb = (-38*R - 74*G + 112*B + 128)/256 + 128,
19022           Cr = (112*R - 94*G - 18*B + 128)/256 + 128;
19023         *(p1++) = (T)(Y<0?0:(Y>255?255:Y));
19024         *(p2++) = (T)(Cb<0?0:(Cb>255?255:Cb));
19025         *(p3++) = (T)(Cr<0?0:(Cr>255?255:Cr));
19026       }
19027       return *this;
19028     }
19029 
19030     //! Convert pixel values from RGB to YCbCr color spaces \newinstance.
19031     CImg<Tuchar> get_RGBtoYCbCr() const {
19032       return CImg<Tuchar>(*this,false).RGBtoYCbCr();
19033     }
19034 
19035     //! Convert pixel values from RGB to YCbCr color spaces.
19036     CImg<T>& YCbCrtoRGB() {
19037       if (_spectrum!=3)
19038         throw CImgInstanceException(_cimg_instance
19039                                     "YCbCrtoRGB() : Instance is not a YCbCr image.",
19040                                     cimg_instance);
19041 
19042       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
19043       for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19044         const Tfloat
19045           Y = (Tfloat)*p1 - 16,
19046           Cb = (Tfloat)*p2 - 128,
19047           Cr = (Tfloat)*p3 - 128,
19048           R = (298*Y + 409*Cr + 128)/256,
19049           G = (298*Y - 100*Cb - 208*Cr + 128)/256,
19050           B = (298*Y + 516*Cb + 128)/256;
19051         *(p1++) = (T)(R<0?0:(R>255?255:R));
19052         *(p2++) = (T)(G<0?0:(G>255?255:G));
19053         *(p3++) = (T)(B<0?0:(B>255?255:B));
19054       }
19055       return *this;
19056     }
19057 
19058     //! Convert pixel values from RGB to YCbCr color spaces \newinstance.
19059     CImg<Tuchar> get_YCbCrtoRGB() const {
19060       return CImg<Tuchar>(*this,false).YCbCrtoRGB();
19061     }
19062 
19063     //! Convert pixel values from RGB to YUV color spaces.
19064     CImg<T>& RGBtoYUV() {
19065       if (_spectrum!=3)
19066         throw CImgInstanceException(_cimg_instance
19067                                     "RGBtoYUV() : Instance is not a RGB image.",
19068                                     cimg_instance);
19069 
19070       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
19071       for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19072         const Tfloat
19073           R = (Tfloat)*p1/255,
19074           G = (Tfloat)*p2/255,
19075           B = (Tfloat)*p3/255,
19076           Y = 0.299f*R + 0.587f*G + 0.114f*B;
19077         *(p1++) = (T)Y;
19078         *(p2++) = (T)(0.492f*(B-Y));
19079         *(p3++) = (T)(0.877*(R-Y));
19080       }
19081       return *this;
19082     }
19083 
19084     //! Convert pixel values from RGB to YUV color spaces \newinstance.
19085     CImg<Tfloat> get_RGBtoYUV() const {
19086       return CImg<Tfloat>(*this,false).RGBtoYUV();
19087     }
19088 
19089     //! Convert pixel values from YUV to RGB color spaces.
19090     CImg<T>& YUVtoRGB() {
19091       if (_spectrum!=3)
19092         throw CImgInstanceException(_cimg_instance
19093                                     "YUVtoRGB() : Instance is not a YUV image.",
19094                                     cimg_instance);
19095 
19096       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
19097       for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19098         const Tfloat
19099           Y = (Tfloat)*p1,
19100           U = (Tfloat)*p2,
19101           V = (Tfloat)*p3,
19102           R = (Y + 1.140f*V)*255,
19103           G = (Y - 0.395f*U - 0.581f*V)*255,
19104           B = (Y + 2.032f*U)*255;
19105         *(p1++) = (T)(R<0?0:(R>255?255:R));
19106         *(p2++) = (T)(G<0?0:(G>255?255:G));
19107         *(p3++) = (T)(B<0?0:(B>255?255:B));
19108       }
19109       return *this;
19110     }
19111 
19112     //! Convert pixel values from YUV to RGB color spaces \newinstance.
19113     CImg<Tuchar> get_YUVtoRGB() const {
19114       return CImg< Tuchar>(*this,false).YUVtoRGB();
19115     }
19116 
19117     //! Convert pixel values from RGB to CMY color spaces.
19118     CImg<T>& RGBtoCMY() {
19119       if (_spectrum!=3)
19120         throw CImgInstanceException(_cimg_instance
19121                                     "RGBtoCMY() : Instance is not a RGB image.",
19122                                     cimg_instance);
19123 
19124       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
19125       for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19126         const Tfloat
19127           R = (Tfloat)*p1,
19128           G = (Tfloat)*p2,
19129           B = (Tfloat)*p3,
19130           C = 255 - R,
19131           M = 255 - G,
19132           Y = 255 - B;
19133         *(p1++) = (T)(C<0?0:(C>255?255:C));
19134         *(p2++) = (T)(M<0?0:(M>255?255:M));
19135         *(p3++) = (T)(Y<0?0:(Y>255?255:Y));
19136       }
19137       return *this;
19138     }
19139 
19140     //! Convert pixel values from RGB to CMY color spaces \newinstance.
19141     CImg<Tuchar> get_RGBtoCMY() const {
19142       return CImg<Tfloat>(*this,false).RGBtoCMY();
19143     }
19144 
19145     //! Convert pixel values from CMY to RGB color spaces.
19146     CImg<T>& CMYtoRGB() {
19147       if (_spectrum!=3)
19148         throw CImgInstanceException(_cimg_instance
19149                                     "CMYtoRGB() : Instance is not a CMY image.",
19150                                     cimg_instance);
19151 
19152       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
19153       for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19154         const Tfloat
19155           C = (Tfloat)*p1,
19156           M = (Tfloat)*p2,
19157           Y = (Tfloat)*p3,
19158           R = 255 - C,
19159           G = 255 - M,
19160           B = 255 - Y;
19161         *(p1++) = (T)(R<0?0:(R>255?255:R));
19162         *(p2++) = (T)(G<0?0:(G>255?255:G));
19163         *(p3++) = (T)(B<0?0:(B>255?255:B));
19164       }
19165       return *this;
19166     }
19167 
19168     //! Convert pixel values from CMY to RGB color spaces \newinstance.
19169     CImg<Tuchar> get_CMYtoRGB() const {
19170       return CImg<Tuchar>(*this,false).CMYtoRGB();
19171     }
19172 
19173     //! Convert pixel values from CMY to CMYK color spaces.
19174     CImg<T>& CMYtoCMYK() {
19175       return get_CMYtoCMYK().move_to(*this);
19176     }
19177 
19178     //! Convert pixel values from CMY to CMYK color spaces \newinstance.
19179     CImg<Tuchar> get_CMYtoCMYK() const {
19180       if (_spectrum!=3)
19181         throw CImgInstanceException(_cimg_instance
19182                                     "CMYtoCMYK() : Instance is not a CMY image.",
19183                                     cimg_instance);
19184 
19185       CImg<Tfloat> res(_width,_height,_depth,4);
19186       const T *ps1 = data(0,0,0,0), *ps2 = data(0,0,0,1), *ps3 = data(0,0,0,2);
19187       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);
19188       for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19189         Tfloat
19190           C = (Tfloat)*(ps1++),
19191           M = (Tfloat)*(ps2++),
19192           Y = (Tfloat)*(ps3++),
19193           K = cimg::min(C,M,Y);
19194         if (K>=255) C = M = Y = 0;
19195         else { const Tfloat K1 = 255 - K; C = 255*(C - K)/K1; M = 255*(M - K)/K1; Y = 255*(Y - K)/K1; }
19196         *(pd1++) = (Tfloat)(C<0?0:(C>255?255:C));
19197         *(pd2++) = (Tfloat)(M<0?0:(M>255?255:M));
19198         *(pd3++) = (Tfloat)(Y<0?0:(Y>255?255:Y));
19199         *(pd4++) = (Tfloat)(K<0?0:(K>255?255:K));
19200       }
19201       return res;
19202     }
19203 
19204     //! Convert pixel values from CMYK to CMY color spaces.
19205     CImg<T>& CMYKtoCMY() {
19206       return get_CMYKtoCMY().move_to(*this);
19207     }
19208 
19209     //! Convert pixel values from CMYK to CMY color spaces \newinstance.
19210     CImg<Tfloat> get_CMYKtoCMY() const {
19211       if (_spectrum!=4)
19212         throw CImgInstanceException(_cimg_instance
19213                                     "CMYKtoCMY() : Instance is not a CMYK image.",
19214                                     cimg_instance);
19215 
19216       CImg<Tfloat> res(_width,_height,_depth,3);
19217       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);
19218       Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2);
19219       for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19220         const Tfloat
19221           C = (Tfloat)*(ps1++),
19222           M = (Tfloat)*(ps2++),
19223           Y = (Tfloat)*(ps3++),
19224           K = (Tfloat)*(ps4++),
19225           K1 = 1 - K/255,
19226           nC = C*K1 + K,
19227           nM = M*K1 + K,
19228           nY = Y*K1 + K;
19229         *(pd1++) = (Tfloat)(nC<0?0:(nC>255?255:nC));
19230         *(pd2++) = (Tfloat)(nM<0?0:(nM>255?255:nM));
19231         *(pd3++) = (Tfloat)(nY<0?0:(nY>255?255:nY));
19232       }
19233       return res;
19234     }
19235 
19236     //! Convert pixel values from RGB to XYZ_709 color spaces.
19237     /**
19238        \note Uses the standard D65 white point.
19239     **/
19240     CImg<T>& RGBtoXYZ() {
19241       if (_spectrum!=3)
19242         throw CImgInstanceException(_cimg_instance
19243                                     "RGBtoXYZ() : Instance is not a RGB image.",
19244                                     cimg_instance);
19245 
19246       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
19247       for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19248         const Tfloat
19249           R = (Tfloat)*p1/255,
19250           G = (Tfloat)*p2/255,
19251           B = (Tfloat)*p3/255;
19252         *(p1++) = (T)(0.412453f*R + 0.357580f*G + 0.180423f*B);
19253         *(p2++) = (T)(0.212671f*R + 0.715160f*G + 0.072169f*B);
19254         *(p3++) = (T)(0.019334f*R + 0.119193f*G + 0.950227f*B);
19255       }
19256       return *this;
19257     }
19258 
19259     //! Convert pixel values from RGB to XYZ_709 color spaces \newinstance.
19260     CImg<Tfloat> get_RGBtoXYZ() const {
19261       return CImg<Tfloat>(*this,false).RGBtoXYZ();
19262     }
19263 
19264     //! Convert pixel values from XYZ_709 to RGB color spaces.
19265     CImg<T>& XYZtoRGB() {
19266       if (_spectrum!=3)
19267         throw CImgInstanceException(_cimg_instance
19268                                     "XYZtoRGB() : Instance is not a XYZ image.",
19269                                     cimg_instance);
19270 
19271       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
19272       for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19273         const Tfloat
19274           X = (Tfloat)*p1*255,
19275           Y = (Tfloat)*p2*255,
19276           Z = (Tfloat)*p3*255,
19277           R = 3.240479f*X  - 1.537150f*Y - 0.498535f*Z,
19278           G = -0.969256f*X + 1.875992f*Y + 0.041556f*Z,
19279           B = 0.055648f*X  - 0.204043f*Y + 1.057311f*Z;
19280         *(p1++) = (T)(R<0?0:(R>255?255:R));
19281         *(p2++) = (T)(G<0?0:(G>255?255:G));
19282         *(p3++) = (T)(B<0?0:(B>255?255:B));
19283       }
19284       return *this;
19285     }
19286 
19287     //! Convert pixel values from XYZ_709 to RGB color spaces \newinstance.
19288     CImg<Tuchar> get_XYZtoRGB() const {
19289       return CImg<Tuchar>(*this,false).XYZtoRGB();
19290     }
19291 
19292     //! Convert pixel values from XYZ_709 to Lab color spaces.
19293     CImg<T>& XYZtoLab() {
19294 #define _cimg_Labf(x) ((x)>=0.008856f?(std::pow(x,(Tfloat)1/3)):(7.787f*(x)+16.0f/116))
19295 
19296       if (_spectrum!=3)
19297         throw CImgInstanceException(_cimg_instance
19298                                     "XYZtoLab() : Instance is not a XYZ image.",
19299                                     cimg_instance);
19300 
19301       const Tfloat
19302         Xn = (Tfloat)(0.412453f + 0.357580f + 0.180423f),
19303         Yn = (Tfloat)(0.212671f + 0.715160f + 0.072169f),
19304         Zn = (Tfloat)(0.019334f + 0.119193f + 0.950227f);
19305       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
19306       for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19307         const Tfloat
19308           X = (Tfloat)*p1,
19309           Y = (Tfloat)*p2,
19310           Z = (Tfloat)*p3,
19311           XXn = X/Xn, YYn = Y/Yn, ZZn = Z/Zn,
19312           fX = (Tfloat)_cimg_Labf(XXn),
19313           fY = (Tfloat)_cimg_Labf(YYn),
19314           fZ = (Tfloat)_cimg_Labf(ZZn);
19315         *(p1++) = (T)cimg::max(0.0f,116*fY - 16);
19316         *(p2++) = (T)(500*(fX - fY));
19317         *(p3++) = (T)(200*(fY - fZ));
19318       }
19319       return *this;
19320     }
19321 
19322     //! Convert pixel values from XYZ_709 to Lab color spaces \newinstance.
19323     CImg<Tfloat> get_XYZtoLab() const {
19324       return CImg<Tfloat>(*this,false).XYZtoLab();
19325     }
19326 
19327     //! Convert pixel values from Lab to XYZ_709 color spaces.
19328     CImg<T>& LabtoXYZ() {
19329 #define _cimg_Labfi(x) ((x)>=0.206893f?((x)*(x)*(x)):(((x)-16.0f/116)/7.787f))
19330 
19331       if (_spectrum!=3)
19332         throw CImgInstanceException(_cimg_instance
19333                                     "LabtoXYZ() : Instance is not a Lab image.",
19334                                     cimg_instance);
19335 
19336       const Tfloat
19337         Xn = (Tfloat)(0.412453f + 0.357580f + 0.180423f),
19338         Yn = (Tfloat)(0.212671f + 0.715160f + 0.072169f),
19339         Zn = (Tfloat)(0.019334f + 0.119193f + 0.950227f);
19340       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
19341       for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19342         const Tfloat
19343           L = (Tfloat)*p1,
19344           a = (Tfloat)*p2,
19345           b = (Tfloat)*p3,
19346           cY = (L + 16)/116,
19347           Y = (Tfloat)(Yn*_cimg_Labfi(cY)),
19348           pY = (Tfloat)std::pow(Y/Yn,(Tfloat)1/3),
19349           cX = a/500 + pY,
19350           X = Xn*cX*cX*cX,
19351           cZ = pY - b/200,
19352           Z = Zn*cZ*cZ*cZ;
19353         *(p1++) = (T)(X);
19354         *(p2++) = (T)(Y);
19355         *(p3++) = (T)(Z);
19356       }
19357       return *this;
19358     }
19359 
19360     //! Convert pixel values from Lab to XYZ_709 color spaces \newinstance.
19361     CImg<Tfloat> get_LabtoXYZ() const {
19362       return CImg<Tfloat>(*this,false).LabtoXYZ();
19363     }
19364 
19365     //! Convert pixel values from XYZ_709 to xyY color spaces.
19366     CImg<T>& XYZtoxyY() {
19367       if (_spectrum!=3)
19368         throw CImgInstanceException(_cimg_instance
19369                                     "XYZtoxyY() : Instance is not a XYZ image.",
19370                                     cimg_instance);
19371 
19372       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
19373       for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19374         const Tfloat
19375           X = (Tfloat)*p1,
19376           Y = (Tfloat)*p2,
19377           Z = (Tfloat)*p3,
19378           sum = (X+Y+Z),
19379           nsum = sum>0?sum:1;
19380         *(p1++) = (T)(X/nsum);
19381         *(p2++) = (T)(Y/nsum);
19382         *(p3++) = (T)Y;
19383       }
19384       return *this;
19385     }
19386 
19387     //! Convert pixel values from XYZ_709 to xyY color spaces \newinstance.
19388     CImg<Tfloat> get_XYZtoxyY() const {
19389       return CImg<Tfloat>(*this,false).XYZtoxyY();
19390     }
19391 
19392     //! Convert pixel values from xyY pixels to XYZ_709 color spaces.
19393     CImg<T>& xyYtoXYZ() {
19394       if (_spectrum!=3)
19395         throw CImgInstanceException(_cimg_instance
19396                                     "xyYtoXYZ() : Instance is not a xyY image.",
19397                                     cimg_instance);
19398 
19399       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
19400       for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19401         const Tfloat
19402          px = (Tfloat)*p1,
19403          py = (Tfloat)*p2,
19404          Y = (Tfloat)*p3,
19405          ny = py>0?py:1;
19406         *(p1++) = (T)(px*Y/ny);
19407         *(p2++) = (T)Y;
19408         *(p3++) = (T)((1-px-py)*Y/ny);
19409       }
19410       return *this;
19411     }
19412 
19413     //! Convert pixel values from xyY pixels to XYZ_709 color spaces \newinstance.
19414     CImg<Tfloat> get_xyYtoXYZ() const {
19415       return CImg<Tfloat>(*this,false).xyYtoXYZ();
19416     }
19417 
19418     //! Convert pixel values from RGB to Lab color spaces.
19419     CImg<T>& RGBtoLab() {
19420       return RGBtoXYZ().XYZtoLab();
19421     }
19422 
19423     //! Convert pixel values from RGB to Lab color spaces \newinstance.
19424     CImg<Tfloat> get_RGBtoLab() const {
19425       return CImg<Tfloat>(*this,false).RGBtoLab();
19426     }
19427 
19428     //! Convert pixel values from Lab to RGB color spaces.
19429     CImg<T>& LabtoRGB() {
19430       return LabtoXYZ().XYZtoRGB();
19431     }
19432 
19433     //! Convert pixel values from Lab to RGB color spaces \newinstance.
19434     CImg<Tuchar> get_LabtoRGB() const {
19435       return CImg<Tuchar>(*this,false).LabtoRGB();
19436     }
19437 
19438     //! Convert pixel values from RGB to xyY color spaces.
19439     CImg<T>& RGBtoxyY() {
19440       return RGBtoXYZ().XYZtoxyY();
19441     }
19442 
19443     //! Convert pixel values from RGB to xyY color spaces \newinstance.
19444     CImg<Tfloat> get_RGBtoxyY() const {
19445       return CImg<Tfloat>(*this,false).RGBtoxyY();
19446     }
19447 
19448     //! Convert pixel values from xyY to RGB color spaces.
19449     CImg<T>& xyYtoRGB() {
19450       return xyYtoXYZ().XYZtoRGB();
19451     }
19452 
19453     //! Convert pixel values from xyY to RGB color spaces \newinstance.
19454     CImg<Tuchar> get_xyYtoRGB() const {
19455       return CImg<Tuchar>(*this,false).xyYtoRGB();
19456     }
19457 
19458     //! Convert pixel values from RGB to CMYK color spaces.
19459     CImg<T>& RGBtoCMYK() {
19460       return RGBtoCMY().CMYtoCMYK();
19461     }
19462 
19463     //! Convert pixel values from RGB to CMYK color spaces \newinstance.
19464     CImg<Tfloat> get_RGBtoCMYK() const {
19465       return CImg<Tfloat>(*this,false).RGBtoCMYK();
19466     }
19467 
19468     //! Convert pixel values from CMYK to RGB color spaces.
19469     CImg<T>& CMYKtoRGB() {
19470       return CMYKtoCMY().CMYtoRGB();
19471     }
19472 
19473     //! Convert pixel values from CMYK to RGB color spaces \newinstance.
19474     CImg<Tuchar> get_CMYKtoRGB() const {
19475       return CImg<Tuchar>(*this,false).CMYKtoRGB();
19476     }
19477 
19478     //! Convert RGB color image to a Bayer-coded scalar image.
19479     /**
19480        \note First (upper-left) pixel if the red component of the pixel color.
19481     **/
19482     CImg<T>& RGBtoBayer() {
19483       return get_RGBtoBayer().move_to(*this);
19484     }
19485 
19486     //! Convert RGB color image to a Bayer-coded scalar image \newinstance.
19487     CImg<T> get_RGBtoBayer() const {
19488       if (_spectrum!=3)
19489         throw CImgInstanceException(_cimg_instance
19490                                     "RGBtoBayer() : Instance is not a RGB image.",
19491                                     cimg_instance);
19492 
19493       CImg<T> res(_width,_height,_depth,1);
19494       const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
19495       T *ptrd = res._data;
19496       cimg_forXYZ(*this,x,y,z) {
19497         if (y%2) {
19498           if (x%2) *(ptrd++) = *ptr_b;
19499           else *(ptrd++) = *ptr_g;
19500         } else {
19501           if (x%2) *(ptrd++) = *ptr_g;
19502           else *(ptrd++) = *ptr_r;
19503         }
19504         ++ptr_r; ++ptr_g; ++ptr_b;
19505       }
19506       return res;
19507     }
19508 
19509     //! Convert Bayer-coded scalar image to a RGB color image.
19510     CImg<T>& BayertoRGB(const unsigned int interpolation_type=3) {
19511       return get_BayertoRGB(interpolation_type).move_to(*this);
19512     }
19513 
19514     //! Convert Bayer-coded scalar image to a RGB color image \newinstance.
19515     CImg<Tuchar> get_BayertoRGB(const unsigned int interpolation_type=3) const {
19516       if (_spectrum!=1)
19517         throw CImgInstanceException(_cimg_instance
19518                                     "BayertoRGB() : Instance is not a Bayer image.",
19519                                     cimg_instance);
19520 
19521       CImg<Tuchar> res(_width,_height,_depth,3);
19522       CImg_3x3(I,T);
19523       Tuchar *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1), *ptr_b = res.data(0,0,0,2);
19524       switch (interpolation_type) {
19525       case 3 : { // Edge-directed
19526         CImg_3x3(R,T);
19527         CImg_3x3(G,T);
19528         CImg_3x3(B,T);
19529         cimg_forXYZ(*this,x,y,z) {
19530           const int _p1x = x?x-1:1, _p1y = y?y-1:1, _n1x = x<width()-1?x+1:x-1, _n1y = y<height()-1?y+1:y-1;
19531           cimg_get3x3(*this,x,y,z,0,I,T);
19532           if (y%2) {
19533             if (x%2) {
19534               const Tfloat alpha = cimg::sqr((Tfloat)Inc - Ipc), beta = cimg::sqr((Tfloat)Icn - Icp), cx = 1/(1+alpha), cy = 1/(1+beta);
19535               *ptr_g = (Tuchar)((cx*(Inc+Ipc) + cy*(Icn+Icp))/(2*(cx+cy)));
19536             } else *ptr_g = (Tuchar)Icc;
19537           } else {
19538             if (x%2) *ptr_g = (Tuchar)Icc;
19539             else {
19540               const Tfloat alpha = cimg::sqr((Tfloat)Inc - Ipc), beta = cimg::sqr((Tfloat)Icn - Icp), cx = 1/(1+alpha), cy = 1/(1+beta);
19541               *ptr_g = (Tuchar)((cx*(Inc+Ipc) + cy*(Icn+Icp))/(2*(cx+cy)));
19542             }
19543           }
19544           ++ptr_g;
19545         }
19546         cimg_forXYZ(*this,x,y,z) {
19547           const int _p1x = x?x-1:1, _p1y = y?y-1:1, _n1x = x<width()-1?x+1:x-1, _n1y = y<height()-1?y+1:y-1;
19548           cimg_get3x3(*this,x,y,z,0,I,T);
19549           cimg_get3x3(res,x,y,z,1,G,T);
19550           if (y%2) {
19551             if (x%2) *ptr_b = (Tuchar)Icc;
19552             else { *ptr_r = (Tuchar)((Icn+Icp)/2); *ptr_b = (Tuchar)((Inc+Ipc)/2); }
19553           } else {
19554             if (x%2) { *ptr_r = (Tuchar)((Inc+Ipc)/2); *ptr_b = (Tuchar)((Icn+Icp)/2); }
19555             else *ptr_r = (Tuchar)Icc;
19556           }
19557           ++ptr_r; ++ptr_b;
19558         }
19559         ptr_r = res.data(0,0,0,0);
19560         ptr_g = res.data(0,0,0,1);
19561         ptr_b = res.data(0,0,0,2);
19562         cimg_forXYZ(*this,x,y,z) {
19563           const int _p1x = x?x-1:1, _p1y = y?y-1:1, _n1x = x<width()-1?x+1:x-1, _n1y = y<height()-1?y+1:y-1;
19564           cimg_get3x3(res,x,y,z,0,R,T);
19565           cimg_get3x3(res,x,y,z,1,G,T);
19566           cimg_get3x3(res,x,y,z,2,B,T);
19567           if (y%2) {
19568             if (x%2) {
19569               const float alpha = (float)cimg::sqr(Rnc-Rpc), beta = (float)cimg::sqr(Rcn-Rcp), cx = 1/(1+alpha), cy = 1/(1+beta);
19570               *ptr_r = (Tuchar)((cx*(Rnc+Rpc) + cy*(Rcn+Rcp))/(2*(cx+cy)));
19571             }
19572           } else {
19573             if (!(x%2)) {
19574               const float alpha = (float)cimg::sqr(Bnc-Bpc), beta = (float)cimg::sqr(Bcn-Bcp), cx = 1/(1+alpha), cy = 1/(1+beta);
19575               *ptr_b = (Tuchar)((cx*(Bnc+Bpc) + cy*(Bcn+Bcp))/(2*(cx+cy)));
19576             }
19577           }
19578           ++ptr_r; ++ptr_g; ++ptr_b;
19579         }
19580       } break;
19581       case 2 : { // Linear interpolation
19582         cimg_forXYZ(*this,x,y,z) {
19583           const int _p1x = x?x-1:1, _p1y = y?y-1:1, _n1x = x<width()-1?x+1:x-1, _n1y = y<height()-1?y+1:y-1;
19584           cimg_get3x3(*this,x,y,z,0,I,T);
19585           if (y%2) {
19586             if (x%2) { *ptr_r = (Tuchar)((Ipp+Inn+Ipn+Inp)/4); *ptr_g = (Tuchar)((Inc+Ipc+Icn+Icp)/4); *ptr_b = (Tuchar)Icc; }
19587             else { *ptr_r = (Tuchar)((Icp+Icn)/2); *ptr_g = (Tuchar)Icc; *ptr_b = (Tuchar)((Inc+Ipc)/2); }
19588           } else {
19589             if (x%2) { *ptr_r = (Tuchar)((Ipc+Inc)/2); *ptr_g = (Tuchar)Icc; *ptr_b = (Tuchar)((Icn+Icp)/2); }
19590             else { *ptr_r = (Tuchar)Icc; *ptr_g = (Tuchar)((Inc+Ipc+Icn+Icp)/4); *ptr_b = (Tuchar)((Ipp+Inn+Ipn+Inp)/4); }
19591           }
19592           ++ptr_r; ++ptr_g; ++ptr_b;
19593         }
19594       } break;
19595       case 1 : { // Nearest neighbor interpolation
19596         cimg_forXYZ(*this,x,y,z) {
19597           const int _p1x = x?x-1:1, _p1y = y?y-1:1, _n1x = x<width()-1?x+1:x-1, _n1y = y<height()-1?y+1:y-1;
19598           cimg_get3x3(*this,x,y,z,0,I,T);
19599           if (y%2) {
19600             if (x%2) { *ptr_r = (Tuchar)cimg::min(Ipp,Inn,Ipn,Inp); *ptr_g = (Tuchar)cimg::min(Inc,Ipc,Icn,Icp); *ptr_b = (Tuchar)Icc; }
19601             else { *ptr_r = (Tuchar)cimg::min(Icn,Icp); *ptr_g = (Tuchar)Icc; *ptr_b = (Tuchar)cimg::min(Inc,Ipc); }
19602           } else {
19603             if (x%2) { *ptr_r = (Tuchar)cimg::min(Inc,Ipc); *ptr_g = (Tuchar)Icc; *ptr_b = (Tuchar)cimg::min(Icn,Icp); }
19604             else { *ptr_r = (Tuchar)Icc; *ptr_g = (Tuchar)cimg::min(Inc,Ipc,Icn,Icp); *ptr_b = (Tuchar)cimg::min(Ipp,Inn,Ipn,Inp); }
19605           }
19606           ++ptr_r; ++ptr_g; ++ptr_b;
19607         }
19608       } break;
19609       default : { // 0-filling interpolation
19610         const T *ptrs = _data;
19611         res.fill(0);
19612         cimg_forXYZ(*this,x,y,z) {
19613           const T val = *(ptrs++);
19614           if (y%2) { if (x%2) *ptr_b = val; else *ptr_g = val; } else { if (x%2) *ptr_g = val; else *ptr_r = val; }
19615           ++ptr_r; ++ptr_g; ++ptr_b;
19616         }
19617       }
19618       }
19619       return res;
19620     }
19621 
19622     //@}
19623     //------------------------------------------
19624     //
19625     //! \name Geometric / Spatial Manipulation
19626     //@{
19627     //------------------------------------------
19628 
19629     static float _cimg_lanczos(const float x) {
19630       if (x<=-2 || x>=2) return 0;
19631       const float a = (float)cimg::PI*x, b = 0.5f*a;
19632       return (float)(x?std::sin(a)*std::sin(b)/(a*b):1);
19633     }
19634 
19635     //! Resize image to new dimensions.
19636     /**
19637        \param size_x Number of columns (new size along the X-axis).
19638        \param size_y Number of rows (new size along the Y-axis).
19639        \param size_z Number of slices (new size along the Z-axis).
19640        \param size_c Number of vector-channels (new size along the C-axis).
19641        \param interpolation_type Method of interpolation :
19642        - -1 = no interpolation : raw memory resizing.
19643        - 0 = no interpolation : additional space is filled according to \p boundary_conditions.
19644        - 1 = nearest-neighbor interpolation.
19645        - 2 = moving average interpolation.
19646        - 3 = linear interpolation.
19647        - 4 = grid interpolation.
19648        - 5 = bicubic interpolation.
19649        - 6 = lanczos interpolation.
19650        \param boundary_conditions Border condition type.
19651        \param centering_x Set centering type (only if \p interpolation_type=0).
19652        \param centering_y Set centering type (only if \p interpolation_type=0).
19653        \param centering_z Set centering type (only if \p interpolation_type=0).
19654        \param centering_c Set centering type (only if \p interpolation_type=0).
19655        \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100).
19656     **/
19657     CImg<T>& resize(const int size_x, const int size_y=-100,
19658                     const int size_z=-100, const int size_c=-100,
19659                     const int interpolation_type=1, const unsigned int boundary_conditions=0,
19660                     const float centering_x = 0, const float centering_y = 0,
19661                     const float centering_z = 0, const float centering_c = 0) {
19662       if (!size_x || !size_y || !size_z || !size_c) return assign();
19663       const unsigned int
19664         _sx = (unsigned int)(size_x<0?-size_x*width()/100:size_x),
19665         _sy = (unsigned int)(size_y<0?-size_y*height()/100:size_y),
19666         _sz = (unsigned int)(size_z<0?-size_z*depth()/100:size_z),
19667         _sc = (unsigned int)(size_c<0?-size_c*spectrum()/100:size_c),
19668         sx = _sx?_sx:1, sy = _sy?_sy:1, sz = _sz?_sz:1, sc = _sc?_sc:1;
19669       if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return *this;
19670       if (is_empty()) return assign(sx,sy,sz,sc,0);
19671       if (interpolation_type==-1 && sx*sy*sz*sc==size()) {
19672         _width = sx; _height = sy; _depth = sz; _spectrum = sc;
19673         return *this;
19674       }
19675       return get_resize(sx,sy,sz,sc,interpolation_type,boundary_conditions,centering_x,centering_y,centering_z,centering_c).move_to(*this);
19676     }
19677 
19678     //! Resize image to new dimensions \newinstance.
19679     CImg<T> get_resize(const int size_x, const int size_y = -100,
19680                        const int size_z = -100, const int size_c = -100,
19681                        const int interpolation_type=1, const unsigned int boundary_conditions=0,
19682                        const float centering_x = 0, const float centering_y = 0,
19683                        const float centering_z = 0, const float centering_c = 0) const {
19684       if (centering_x<0 || centering_x>1 || centering_y<0 || centering_y>1 ||
19685           centering_z<0 || centering_z>1 || centering_c<0 || centering_c>1)
19686         throw CImgArgumentException(_cimg_instance
19687                                     "resize() : Specified centering arguments (%g,%g,%g,%g) are outside range [0,1].",
19688                                     cimg_instance,
19689                                     centering_x,centering_y,centering_z,centering_c);
19690 
19691       if (!size_x || !size_y || !size_z || !size_c) return CImg<T>();
19692       const unsigned int
19693         _sx = (unsigned int)(size_x<0?-size_x*width()/100:size_x),
19694         _sy = (unsigned int)(size_y<0?-size_y*height()/100:size_y),
19695         _sz = (unsigned int)(size_z<0?-size_z*depth()/100:size_z),
19696         _sc = (unsigned int)(size_c<0?-size_c*spectrum()/100:size_c),
19697         sx = _sx?_sx:1, sy = _sy?_sy:1, sz = _sz?_sz:1, sc = _sc?_sc:1;
19698       if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return +*this;
19699       if (is_empty()) return CImg<T>(sx,sy,sz,sc,0);
19700 
19701       CImg<T> res;
19702       switch (interpolation_type) {
19703 
19704         // Raw resizing.
19705         //
19706       case -1 :
19707         std::memcpy(res.assign(sx,sy,sz,sc,0)._data,_data,sizeof(T)*cimg::min(size(),sx*sy*sz*sc));
19708         break;
19709 
19710         // No interpolation.
19711         //
19712       case 0 : {
19713         const int
19714           xc = (int)(centering_x*((int)sx - width())),
19715           yc = (int)(centering_y*((int)sy - height())),
19716           zc = (int)(centering_z*((int)sz - depth())),
19717           cc = (int)(centering_c*((int)sc - spectrum()));
19718 
19719         switch (boundary_conditions) {
19720         case 2 : { // Cyclic borders.
19721           res.assign(sx,sy,sz,sc);
19722           const int
19723             x0 = ((int)xc%width()) - width(),
19724             y0 = ((int)yc%height()) - height(),
19725             z0 = ((int)zc%depth()) - depth(),
19726             c0 = ((int)cc%spectrum()) - spectrum();
19727           for (int c = c0; c<(int)sc; c+=spectrum())
19728             for (int z = z0; z<(int)sz; z+=depth())
19729               for (int y = y0; y<(int)sy; y+=height())
19730                 for (int x = x0; x<(int)sx; x+=width())
19731                   res.draw_image(x,y,z,c,*this);
19732         } break;
19733         case 1 : { // Neumann borders.
19734           res.assign(sx,sy,sz,sc).draw_image(xc,yc,zc,cc,*this);
19735           CImg<T> sprite;
19736           if (xc>0) {  // X-backward
19737             res.get_crop(xc,yc,zc,cc,xc,yc+height()-1,zc+depth()-1,cc+spectrum()-1).move_to(sprite);
19738             for (int x = xc-1; x>=0; --x) res.draw_image(x,yc,zc,cc,sprite);
19739           }
19740           if (xc+width()<(int)sx) { // X-forward
19741             res.get_crop(xc+width()-1,yc,zc,cc,xc+width()-1,yc+height()-1,zc+depth()-1,cc+spectrum()-1).move_to(sprite);
19742             for (int x = xc+width(); x<(int)sx; ++x) res.draw_image(x,yc,zc,cc,sprite);
19743           }
19744           if (yc>0) {  // Y-backward
19745             res.get_crop(0,yc,zc,cc,sx-1,yc,zc+depth()-1,cc+spectrum()-1).move_to(sprite);
19746             for (int y = yc-1; y>=0; --y) res.draw_image(0,y,zc,cc,sprite);
19747           }
19748           if (yc+height()<(int)sy) { // Y-forward
19749             res.get_crop(0,yc+height()-1,zc,cc,sx-1,yc+height()-1,zc+depth()-1,cc+spectrum()-1).move_to(sprite);
19750             for (int y = yc+height(); y<(int)sy; ++y) res.draw_image(0,y,zc,cc,sprite);
19751           }
19752           if (zc>0) {  // Z-backward
19753             res.get_crop(0,0,zc,cc,sx-1,sy-1,zc,cc+spectrum()-1).move_to(sprite);
19754             for (int z = zc-1; z>=0; --z) res.draw_image(0,0,z,cc,sprite);
19755           }
19756           if (zc+depth()<(int)sz) { // Z-forward
19757             res.get_crop(0,0,zc+depth()-1,cc,sx-1,sy-1,zc+depth()-1,cc+spectrum()-1).move_to(sprite);
19758             for (int z = zc+depth(); z<(int)sz; ++z) res.draw_image(0,0,z,cc,sprite);
19759           }
19760           if (cc>0) {  // C-backward
19761             res.get_crop(0,0,0,cc,sx-1,sy-1,sz-1,cc).move_to(sprite);
19762             for (int c = cc-1; c>=0; --c) res.draw_image(0,0,0,c,sprite);
19763           }
19764           if (cc+spectrum()<(int)sc) { // C-forward
19765             res.get_crop(0,0,0,cc+spectrum()-1,sx-1,sy-1,sz-1,cc+spectrum()-1).move_to(sprite);
19766             for (int c = cc+spectrum(); c<(int)sc; ++c) res.draw_image(0,0,0,c,sprite);
19767           }
19768         } break;
19769         default : // Dirichlet borders.
19770           res.assign(sx,sy,sz,sc,0).draw_image(xc,yc,zc,cc,*this);
19771         }
19772         break;
19773       } break;
19774 
19775         // Nearest neighbor interpolation.
19776         //
19777       case 1 : {
19778         res.assign(sx,sy,sz,sc);
19779         CImg<ulongT> off_x(sx), off_y(sy+1), off_z(sz+1), off_c(sc+1);
19780         unsigned long *poff_x, *poff_y, *poff_z, *poff_c, curr, old;
19781         const unsigned long
19782           wh = (unsigned long)_width*_height,
19783           whd = (unsigned long)_width*_height*_depth,
19784           sxy = (unsigned long)sx*sy,
19785           sxyz = (unsigned long)sx*sy*sz;
19786         if (sx==_width) off_x.fill(1);
19787         else {
19788           poff_x = off_x._data; curr = 0;
19789           cimg_forX(res,x) { old = curr; curr = ((x+1LU)*_width/sx); *(poff_x++) = curr - old; }
19790         }
19791         if (sy==_height) off_y.fill(_width);
19792         else {
19793           poff_y = off_y._data; curr = 0;
19794           cimg_forY(res,y) { old = curr; curr = ((y+1LU)*_height/sy); *(poff_y++) = _width*(curr - old); } *poff_y = 0;
19795         }
19796         if (sz==_depth) off_z.fill(wh);
19797         else {
19798           poff_z = off_z._data; curr = 0;
19799           cimg_forZ(res,z) { old = curr; curr = ((z+1LU)*_depth/sz); *(poff_z++) = wh*(curr - old); } *poff_z = 0;
19800         }
19801         if (sc==_spectrum) off_c.fill(whd);
19802         else {
19803           poff_c = off_c._data; curr = 0;
19804           cimg_forC(res,c) { old = curr; curr = ((c+1LU)*_spectrum/sc); *(poff_c++) = whd*(curr - old); } *poff_c = 0;
19805         }
19806 
19807         T *ptrd = res._data;
19808         const T* ptrv = _data;
19809         poff_c = off_c._data;
19810         for (unsigned int c = 0; c<sc; ) {
19811           const T *ptrz = ptrv;
19812           poff_z = off_z._data;
19813           for (unsigned int z = 0; z<sz; ) {
19814             const T *ptry = ptrz;
19815             poff_y = off_y._data;
19816             for (unsigned int y = 0; y<sy; ) {
19817               const T *ptrx = ptry;
19818               poff_x = off_x._data;
19819               cimg_forX(res,x) { *(ptrd++) = *ptrx; ptrx+=*(poff_x++); }
19820               ++y;
19821               unsigned long dy = *(poff_y++);
19822               for (;!dy && y<dy; std::memcpy(ptrd,ptrd - sx,sizeof(T)*sx), ++y, ptrd+=sx, dy = *(poff_y++)) {}
19823               ptry+=dy;
19824             }
19825             ++z;
19826             unsigned long dz = *(poff_z++);
19827             for (;!dz && z<dz; std::memcpy(ptrd,ptrd-sxy,sizeof(T)*sxy), ++z, ptrd+=sxy, dz = *(poff_z++)) {}
19828             ptrz+=dz;
19829           }
19830           ++c;
19831           unsigned long dc = *(poff_c++);
19832           for (;!dc && c<dc; std::memcpy(ptrd,ptrd-sxyz,sizeof(T)*sxyz), ++c, ptrd+=sxyz, dc = *(poff_c++)) {}
19833           ptrv+=dc;
19834         }
19835       } break;
19836 
19837         // Moving average.
19838         //
19839       case 2 : {
19840         bool instance_first = true;
19841         if (sx!=_width) {
19842           CImg<Tfloat> tmp(sx,_height,_depth,_spectrum,0);
19843           for (unsigned int a = _width*sx, b = _width, c = sx, s = 0, t = 0; a; ) {
19844             const unsigned int d = cimg::min(b,c);
19845             a-=d; b-=d; c-=d;
19846             cimg_forYZC(tmp,y,z,v) tmp(t,y,z,v)+=(Tfloat)(*this)(s,y,z,v)*d;
19847             if (!b) { cimg_forYZC(tmp,y,z,v) tmp(t,y,z,v)/=_width; ++t; b = _width; }
19848             if (!c) { ++s; c = sx; }
19849           }
19850           tmp.move_to(res);
19851           instance_first = false;
19852         }
19853         if (sy!=_height) {
19854           CImg<Tfloat> tmp(sx,sy,_depth,_spectrum,0);
19855           for (unsigned int a = _height*sy, b = _height, c = sy, s = 0, t = 0; a; ) {
19856             const unsigned int d = cimg::min(b,c);
19857             a-=d; b-=d; c-=d;
19858             if (instance_first) cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)(*this)(x,s,z,v)*d;
19859             else cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)res(x,s,z,v)*d;
19860             if (!b) { cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)/=_height; ++t; b = _height; }
19861             if (!c) { ++s; c = sy; }
19862           }
19863           tmp.move_to(res);
19864           instance_first = false;
19865         }
19866         if (sz!=_depth) {
19867           CImg<Tfloat> tmp(sx,sy,sz,_spectrum,0);
19868           for (unsigned int a = _depth*sz, b = _depth, c = sz, s = 0, t = 0; a; ) {
19869             const unsigned int d = cimg::min(b,c);
19870             a-=d; b-=d; c-=d;
19871             if (instance_first) cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)(*this)(x,y,s,v)*d;
19872             else cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)res(x,y,s,v)*d;
19873             if (!b) { cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)/=_depth; ++t; b = _depth; }
19874             if (!c) { ++s; c = sz; }
19875           }
19876           tmp.move_to(res);
19877           instance_first = false;
19878         }
19879         if (sc!=_spectrum) {
19880           CImg<Tfloat> tmp(sx,sy,sz,sc,0);
19881           for (unsigned int a = _spectrum*sc, b = _spectrum, c = sc, s = 0, t = 0; a; ) {
19882             const unsigned int d = cimg::min(b,c);
19883             a-=d; b-=d; c-=d;
19884             if (instance_first) cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(Tfloat)(*this)(x,y,z,s)*d;
19885             else cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(Tfloat)res(x,y,z,s)*d;
19886             if (!b) { cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)/=_spectrum; ++t; b = _spectrum; }
19887             if (!c) { ++s; c = sc; }
19888           }
19889           tmp.move_to(res);
19890           instance_first = false;
19891         }
19892       } break;
19893 
19894         // Linear interpolation.
19895         //
19896       case 3 : {
19897         CImg<uintT> off(cimg::max(sx,sy,sz,sc));
19898         CImg<floatT> foff(off._width);
19899         unsigned int *poff;
19900         float *pfoff, old, curr;
19901         CImg<T> resx, resy, resz, resc;
19902         T *ptrd;
19903 
19904         if (sx!=_width) {
19905           if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
19906           else {
19907             const float fx = (!boundary_conditions && sx>_width)?(sx>1?(_width-1.0f)/(sx-1):0):(float)_width/sx;
19908             resx.assign(sx,_height,_depth,_spectrum);
19909             curr = old = 0; poff = off._data; pfoff = foff._data;
19910             cimg_forX(resx,x) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fx; *(poff++) = (unsigned int)curr - (unsigned int)old; }
19911             ptrd = resx._data;
19912             const T *ptrs0 = _data;
19913             cimg_forYZC(resx,y,z,c) {
19914               poff = off._data; pfoff = foff._data;
19915               const T *ptrs = ptrs0, *const ptrsmax = ptrs0 + (_width-1);
19916               cimg_forX(resx,x) {
19917                 const float alpha = *(pfoff++);
19918                 const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs+1):val1;
19919                 *(ptrd++) = (T)((1-alpha)*val1 + alpha*val2);
19920                 ptrs+=*(poff++);
19921               }
19922               ptrs0+=_width;
19923             }
19924           }
19925         } else resx.assign(*this,true);
19926 
19927         if (sy!=_height) {
19928           if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
19929           else {
19930             const float fy = (!boundary_conditions && sy>_height)?(sy>1?(_height-1.0f)/(sy-1):0):(float)_height/sy;
19931             resy.assign(sx,sy,_depth,_spectrum);
19932             curr = old = 0; poff = off._data; pfoff = foff._data;
19933             cimg_forY(resy,y) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fy; *(poff++) = sx*((unsigned int)curr-(unsigned int)old); }
19934             cimg_forXZC(resy,x,z,c) {
19935               ptrd = resy.data(x,0,z,c);
19936               const T *ptrs = resx.data(x,0,z,c), *const ptrsmax = ptrs + (_height-1)*sx;
19937               poff = off._data; pfoff = foff._data;
19938               cimg_forY(resy,y) {
19939                 const float alpha = *(pfoff++);
19940                 const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs+sx):val1;
19941                 *ptrd = (T)((1-alpha)*val1 + alpha*val2);
19942                 ptrd+=sx;
19943                 ptrs+=*(poff++);
19944               }
19945             }
19946           }
19947           resx.assign();
19948         } else resy.assign(resx,true);
19949 
19950         if (sz!=_depth) {
19951           if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
19952           else {
19953             const float fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth-1.0f)/(sz-1):0):(float)_depth/sz;
19954             const unsigned int sxy = sx*sy;
19955             resz.assign(sx,sy,sz,_spectrum);
19956             curr = old = 0; poff = off._data; pfoff = foff._data;
19957             cimg_forZ(resz,z) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fz; *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); }
19958             cimg_forXYC(resz,x,y,c) {
19959               ptrd = resz.data(x,y,0,c);
19960               const T *ptrs = resy.data(x,y,0,c), *const ptrsmax = ptrs + (_depth-1)*sxy;
19961               poff = off._data; pfoff = foff._data;
19962               cimg_forZ(resz,z) {
19963                 const float alpha = *(pfoff++);
19964                 const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs+sxy):val1;
19965                 *ptrd = (T)((1-alpha)*val1 + alpha*val2);
19966                 ptrd+=sxy;
19967                 ptrs+=*(poff++);
19968               }
19969             }
19970           }
19971           resy.assign();
19972         } else resz.assign(resy,true);
19973 
19974         if (sc!=_spectrum) {
19975           if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
19976           else {
19977             const float fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum-1.0f)/(sc-1):0):(float)_spectrum/sc;
19978             const unsigned int sxyz = sx*sy*sz;
19979             resc.assign(sx,sy,sz,sc);
19980             curr = old = 0; poff = off._data; pfoff = foff._data;
19981             cimg_forC(resc,c) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fc; *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); }
19982             cimg_forXYZ(resc,x,y,z) {
19983               ptrd = resc.data(x,y,z,0);
19984               const T *ptrs = resz.data(x,y,z,0), *const ptrsmax = ptrs + (_spectrum-1)*sxyz;
19985               poff = off._data; pfoff = foff._data;
19986               cimg_forC(resc,c) {
19987                 const float alpha = *(pfoff++);
19988                 const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs+sxyz):val1;
19989                 *ptrd = (T)((1-alpha)*val1 + alpha*val2);
19990                 ptrd+=sxyz;
19991                 ptrs+=*(poff++);
19992               }
19993             }
19994           }
19995           resz.assign();
19996         } else resc.assign(resz,true);
19997         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
19998       } break;
19999 
20000         // Grid interpolation.
20001         //
20002       case 4 : {
20003         CImg<T> resx, resy, resz, resc;
20004         const unsigned int sxy = sx*sy, sxyz = sx*sy*sz;
20005         if (sx!=_width) {
20006           if (sx<_width) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
20007           else {
20008             resx.assign(sx,_height,_depth,_spectrum,0);
20009             const T *ptrs = _data;
20010             T *ptrd = resx._data + (int)(centering_x*(sx-1)/_width);
20011             cimg_forYZC(*this,y,z,c) {
20012               cimg_forX(*this,x) ptrd[x*sx/_width] = *(ptrs++);
20013               ptrd+=sx;
20014             }
20015           }
20016         } else resx.assign(*this,true);
20017 
20018         if (sy!=_height) {
20019           if (sy<_height) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
20020           else {
20021             resy.assign(sx,sy,_depth,_spectrum,0);
20022             const T *ptrs = resx._data;
20023             T *ptrd = resy._data + (int)(centering_y*(sy-1)/_height)*sx;
20024             cimg_forZC(*this,z,c) {
20025               cimg_forY(*this,y) { std::memcpy(ptrd + (y*sy/_height)*sx,ptrs,sizeof(T)*sx); ptrs+=sx; }
20026               ptrd+=sxy;
20027             }
20028           }
20029           resx.assign();
20030         } else resy.assign(resx,true);
20031 
20032         if (sz!=_depth) {
20033           if (sz<_depth) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
20034           else {
20035             resz.assign(sx,sy,sz,_spectrum,0);
20036             const T *ptrs = resy._data;
20037             T *ptrd = resz._data + (int)(centering_z*(sz-1)/_depth)*sxy;
20038             cimg_forC(*this,c) {
20039               cimg_forZ(*this,z) { std::memcpy(ptrd + (z*sz/_depth)*sxy,ptrs,sizeof(T)*sxy); ptrs+=sxy; }
20040               ptrd+=sxyz;
20041             }
20042           }
20043           resy.assign();
20044         } else resz.assign(resy,true);
20045 
20046         if (sc!=_spectrum) {
20047           if (sc<_spectrum) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
20048           else {
20049             resc.assign(sx,sy,sz,sc,0);
20050             const T *ptrs = resz._data;
20051             T *ptrd = resc._data + (int)(centering_c*(sc-1)/_spectrum)*sxyz;
20052             cimg_forC(*this,c) { std::memcpy(ptrd + (c*sc/_spectrum)*sxyz,ptrs,sizeof(T)*sxyz); ptrs+=sxyz; }
20053           }
20054           resz.assign();
20055         } else resc.assign(resz,true);
20056 
20057         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
20058       } break;
20059 
20060         // Bicubic interpolation.
20061         //
20062       case 5 : {
20063         const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
20064         CImg<uintT> off(cimg::max(sx,sy,sz,sc));
20065         CImg<floatT> foff(off._width);
20066         unsigned int *poff;
20067         float *pfoff, old, curr;
20068         CImg<T> resx, resy, resz, resc;
20069         T *ptrd;
20070 
20071         if (sx!=_width) {
20072           if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
20073           else {
20074             const float fx = (!boundary_conditions && sx>_width)?(sx>1?(_width-1.0f)/(sx-1):0):(float)_width/sx;
20075             resx.assign(sx,_height,_depth,_spectrum);
20076             curr = old = 0; poff = off._data; pfoff = foff._data;
20077             cimg_forX(resx,x) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fx; *(poff++) = (unsigned int)curr - (unsigned int)old; }
20078             ptrd = resx._data;
20079             const T *ptrs0 = _data;
20080             cimg_forYZC(resx,y,z,c) {
20081               poff = off._data; pfoff = foff._data;
20082               const T *ptrs = ptrs0, *const ptrsmax = ptrs0 + (_width-2);
20083               cimg_forX(resx,x) {
20084                 const float t = *(pfoff++);
20085                 const Tfloat
20086                   val1 = (Tfloat)*ptrs,
20087                   val0 = ptrs>ptrs0?(Tfloat)*(ptrs-1):val1,
20088                   val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+1):val1,
20089                   val3 = ptrs<ptrsmax?(Tfloat)*(ptrs+2):val2,
20090                   val = val1 + 0.5f*(t*(-val0+val2) + t*t*(2*val0-5*val1+4*val2-val3) + t*t*t*(-val0+3*val1-3*val2+val3));
20091                 *(ptrd++) = (T)(val<vmin?vmin:val>vmax?vmax:val);
20092                 ptrs+=*(poff++);
20093               }
20094               ptrs0+=_width;
20095             }
20096           }
20097         } else resx.assign(*this,true);
20098 
20099         if (sy!=_height) {
20100           if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
20101           else {
20102             const float fy = (!boundary_conditions && sy>_height)?(sy>1?(_height-1.0f)/(sy-1):0):(float)_height/sy;
20103             resy.assign(sx,sy,_depth,_spectrum);
20104             curr = old = 0; poff = off._data; pfoff = foff._data;
20105             cimg_forY(resy,y) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fy; *(poff++) = sx*((unsigned int)curr-(unsigned int)old); }
20106             cimg_forXZC(resy,x,z,c) {
20107               ptrd = resy.data(x,0,z,c);
20108               const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs0 + (_height-2)*sx;
20109               poff = off._data; pfoff = foff._data;
20110               cimg_forY(resy,y) {
20111                 const float t = *(pfoff++);
20112                 const Tfloat
20113                   val1 = (Tfloat)*ptrs,
20114                   val0 = ptrs>ptrs0?(Tfloat)*(ptrs-sx):val1,
20115                   val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sx):val1,
20116                   val3 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sx):val2,
20117                   val = val1 + 0.5f*(t*(-val0+val2) + t*t*(2*val0-5*val1+4*val2-val3) + t*t*t*(-val0+3*val1-3*val2+val3));
20118                 *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
20119                 ptrd+=sx;
20120                 ptrs+=*(poff++);
20121               }
20122             }
20123           }
20124           resx.assign();
20125         } else resy.assign(resx,true);
20126 
20127         if (sz!=_depth) {
20128           if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
20129           else {
20130             const float fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth-1.0f)/(sz-1):0):(float)_depth/sz;
20131             const unsigned int sxy = sx*sy;
20132             resz.assign(sx,sy,sz,_spectrum);
20133             curr = old = 0; poff = off._data; pfoff = foff._data;
20134             cimg_forZ(resz,z) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fz; *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); }
20135             cimg_forXYC(resz,x,y,c) {
20136               ptrd = resz.data(x,y,0,c);
20137               const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmax = ptrs0 + (_depth-2)*sxy;
20138               poff = off._data; pfoff = foff._data;
20139               cimg_forZ(resz,z) {
20140                 const float t = *(pfoff++);
20141                 const Tfloat
20142                   val1 = (Tfloat)*ptrs,
20143                   val0 = ptrs>ptrs0?(Tfloat)*(ptrs-sxy):val1,
20144                   val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxy):val1,
20145                   val3 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sxy):val2,
20146                   val = val1 + 0.5f*(t*(-val0+val2) + t*t*(2*val0-5*val1+4*val2-val3) + t*t*t*(-val0+3*val1-3*val2+val3));
20147                 *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
20148                 ptrd+=sxy;
20149                 ptrs+=*(poff++);
20150               }
20151             }
20152           }
20153           resy.assign();
20154         } else resz.assign(resy,true);
20155 
20156         if (sc!=_spectrum) {
20157           if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
20158           else {
20159             const float fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum-1.0f)/(sc-1):0):(float)_spectrum/sc;
20160             const unsigned int sxyz = sx*sy*sz;
20161             resc.assign(sx,sy,sz,sc);
20162             curr = old = 0; poff = off._data; pfoff = foff._data;
20163             cimg_forC(resc,c) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fc; *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); }
20164             cimg_forXYZ(resc,x,y,z) {
20165               ptrd = resc.data(x,y,z,0);
20166               const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmax = ptrs + (_spectrum-2)*sxyz;
20167               poff = off._data; pfoff = foff._data;
20168               cimg_forC(resc,c) {
20169                 const float t = *(pfoff++);
20170                 const Tfloat
20171                   val1 = (Tfloat)*ptrs,
20172                   val0 = ptrs>ptrs0?(Tfloat)*(ptrs-sxyz):val1,
20173                   val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxyz):val1,
20174                   val3 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sxyz):val2,
20175                   val = val1 + 0.5f*(t*(-val0+val2) + t*t*(2*val0-5*val1+4*val2-val3) + t*t*t*(-val0+3*val1-3*val2+val3));
20176                 *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
20177                 ptrd+=sxyz;
20178                 ptrs+=*(poff++);
20179               }
20180             }
20181           }
20182           resz.assign();
20183         } else resc.assign(resz,true);
20184 
20185         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
20186       } break;
20187 
20188         // Lanczos interpolation.
20189         //
20190       case 6 : {
20191         const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
20192         CImg<uintT> off(cimg::max(sx,sy,sz,sc));
20193         CImg<floatT> foff(off._width);
20194         unsigned int *poff;
20195         float *pfoff, old, curr;
20196         CImg<T> resx, resy, resz, resc;
20197         T *ptrd;
20198 
20199         if (sx!=_width) {
20200           if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
20201           else {
20202             const float fx = (!boundary_conditions && sx>_width)?(sx>1?(_width-1.0f)/(sx-1):0):(float)_width/sx;
20203             resx.assign(sx,_height,_depth,_spectrum);
20204             curr = old = 0; poff = off._data; pfoff = foff._data;
20205             cimg_forX(resx,x) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fx; *(poff++) = (unsigned int)curr - (unsigned int)old; }
20206             ptrd = resx._data;
20207             const T *ptrs0 = _data;
20208             cimg_forYZC(resx,y,z,c) {
20209               poff = off._data; pfoff = foff._data;
20210               const T *ptrs = ptrs0, *const ptrsmin = ptrs0 + 1, *const ptrsmax = ptrs0 + (_width-2);
20211               cimg_forX(resx,x) {
20212                 const float
20213                   t = *(pfoff++),
20214                   w0 = _cimg_lanczos(t+2),
20215                   w1 = _cimg_lanczos(t+1),
20216                   w2 = _cimg_lanczos(t),
20217                   w3 = _cimg_lanczos(t-1),
20218                   w4 = _cimg_lanczos(t-2);
20219                 const Tfloat
20220                   val2 = (Tfloat)*ptrs,
20221                   val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-1):val2,
20222                   val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2):val1,
20223                   val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+1):val2,
20224                   val4 = ptrs<ptrsmax?(Tfloat)*(ptrs+2):val3,
20225                   val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
20226                 *(ptrd++) = (T)(val<vmin?vmin:val>vmax?vmax:val);
20227                 ptrs+=*(poff++);
20228               }
20229               ptrs0+=_width;
20230             }
20231           }
20232         } else resx.assign(*this,true);
20233 
20234         if (sy!=_height) {
20235           if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
20236           else {
20237             const float fy = (!boundary_conditions && sy>_height)?(sy>1?(_height-1.0f)/(sy-1):0):(float)_height/sy;
20238             resy.assign(sx,sy,_depth,_spectrum);
20239             curr = old = 0; poff = off._data; pfoff = foff._data;
20240             cimg_forY(resy,y) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fy; *(poff++) = sx*((unsigned int)curr-(unsigned int)old); }
20241             cimg_forXZC(resy,x,z,c) {
20242               ptrd = resy.data(x,0,z,c);
20243               const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sx, *const ptrsmax = ptrs0 + (_height-2)*sx;
20244               poff = off._data; pfoff = foff._data;
20245               cimg_forY(resy,y) {
20246                 const float
20247                   t = *(pfoff++),
20248                   w0 = _cimg_lanczos(t+2),
20249                   w1 = _cimg_lanczos(t+1),
20250                   w2 = _cimg_lanczos(t),
20251                   w3 = _cimg_lanczos(t-1),
20252                   w4 = _cimg_lanczos(t-2);
20253                 const Tfloat
20254                   val2 = (Tfloat)*ptrs,
20255                   val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-sx):val2,
20256                   val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2*sx):val1,
20257                   val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sx):val2,
20258                   val4 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sx):val3,
20259                   val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
20260                 *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
20261                 ptrd+=sx;
20262                 ptrs+=*(poff++);
20263               }
20264             }
20265           }
20266           resx.assign();
20267         } else resy.assign(resx,true);
20268 
20269         if (sz!=_depth) {
20270           if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
20271           else {
20272             const float fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth-1.0f)/(sz-1):0):(float)_depth/sz;
20273             const unsigned int sxy = sx*sy;
20274             resz.assign(sx,sy,sz,_spectrum);
20275             curr = old = 0; poff = off._data; pfoff = foff._data;
20276             cimg_forZ(resz,z) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fz; *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); }
20277             cimg_forXYC(resz,x,y,c) {
20278               ptrd = resz.data(x,y,0,c);
20279               const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxy, *const ptrsmax = ptrs0 + (_depth-2)*sxy;
20280               poff = off._data; pfoff = foff._data;
20281               cimg_forZ(resz,z) {
20282                 const float
20283                   t = *(pfoff++),
20284                   w0 = _cimg_lanczos(t+2),
20285                   w1 = _cimg_lanczos(t+1),
20286                   w2 = _cimg_lanczos(t),
20287                   w3 = _cimg_lanczos(t-1),
20288                   w4 = _cimg_lanczos(t-2);
20289                 const Tfloat
20290                   val2 = (Tfloat)*ptrs,
20291                   val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-sxy):val2,
20292                   val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2*sxy):val1,
20293                   val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxy):val2,
20294                   val4 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sxy):val3,
20295                   val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
20296                 *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
20297                 ptrd+=sxy;
20298                 ptrs+=*(poff++);
20299               }
20300             }
20301           }
20302           resy.assign();
20303         } else resz.assign(resy,true);
20304 
20305         if (sc!=_spectrum) {
20306           if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
20307           else {
20308             const float fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum-1.0f)/(sc-1):0):(float)_spectrum/sc;
20309             const unsigned int sxyz = sx*sy*sz;
20310             resc.assign(sx,sy,sz,sc);
20311             curr = old = 0; poff = off._data; pfoff = foff._data;
20312             cimg_forC(resc,c) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fc; *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); }
20313             cimg_forXYZ(resc,x,y,z) {
20314               ptrd = resc.data(x,y,z,0);
20315               const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxyz, *const ptrsmax = ptrs + (_spectrum-2)*sxyz;
20316               poff = off._data; pfoff = foff._data;
20317               cimg_forC(resc,c) {
20318                 const float
20319                   t = *(pfoff++),
20320                   w0 = _cimg_lanczos(t+2),
20321                   w1 = _cimg_lanczos(t+1),
20322                   w2 = _cimg_lanczos(t),
20323                   w3 = _cimg_lanczos(t-1),
20324                   w4 = _cimg_lanczos(t-2);
20325                 const Tfloat
20326                   val2 = (Tfloat)*ptrs,
20327                   val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-sxyz):val2,
20328                   val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2*sxyz):val1,
20329                   val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxyz):val2,
20330                   val4 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sxyz):val3,
20331                   val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
20332                 *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
20333                 ptrd+=sxyz;
20334                 ptrs+=*(poff++);
20335               }
20336             }
20337           }
20338           resz.assign();
20339         } else resc.assign(resz,true);
20340 
20341         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
20342       } break;
20343 
20344         // Unknow interpolation.
20345         //
20346       default :
20347         throw CImgArgumentException(_cimg_instance
20348                                     "resize() : Invalid specified interpolation %d "
20349                                     "(should be { -1=raw | 0=none | 1=nearest | 2=average | 3=linear | 4=grid | 5=bicubic | 6=lanczos }).",
20350                                     cimg_instance,
20351                                     interpolation_type);
20352       }
20353       return res;
20354     }
20355 
20356     //! Resize image to dimensions of another image.
20357     /**
20358        \param src Reference image used for dimensions.
20359        \param interpolation_type Interpolation method.
20360        \param boundary_conditions Boundary conditions.
20361        \param centering_x Set centering type (only if \p interpolation_type=0).
20362        \param centering_y Set centering type (only if \p interpolation_type=0).
20363        \param centering_z Set centering type (only if \p interpolation_type=0).
20364        \param centering_c Set centering type (only if \p interpolation_type=0).
20365      **/
20366     template<typename t>
20367     CImg<T>& resize(const CImg<t>& src,
20368                     const int interpolation_type=1, const unsigned int boundary_conditions=0,
20369                     const float centering_x = 0, const float centering_y = 0,
20370                     const float centering_z = 0, const float centering_c = 0) {
20371       return resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions,
20372                     centering_x,centering_y,centering_z,centering_c);
20373     }
20374 
20375     //! Resize image to dimensions of another image \newinstance.
20376     template<typename t>
20377     CImg<T> get_resize(const CImg<t>& src,
20378                        const int interpolation_type=1, const unsigned int boundary_conditions=0,
20379                        const float centering_x = 0, const float centering_y = 0,
20380                        const float centering_z = 0, const float centering_c = 0) const {
20381       return get_resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions,
20382                         centering_x,centering_y,centering_z,centering_c);
20383     }
20384 
20385     //! Resize image to dimensions of a display window.
20386     /**
20387        \param disp Reference display window used for dimensions.
20388        \param interpolation_type Interpolation method.
20389        \param boundary_conditions Boundary conditions.
20390        \param centering_x Set centering type (only if \p interpolation_type=0).
20391        \param centering_y Set centering type (only if \p interpolation_type=0).
20392        \param centering_z Set centering type (only if \p interpolation_type=0).
20393        \param centering_c Set centering type (only if \p interpolation_type=0).
20394      **/
20395     CImg<T>& resize(const CImgDisplay& disp,
20396                     const int interpolation_type=1, const unsigned int boundary_conditions=0,
20397                     const float centering_x = 0, const float centering_y = 0,
20398                     const float centering_z = 0, const float centering_c = 0) {
20399       return resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions,
20400                     centering_x,centering_y,centering_z,centering_c);
20401     }
20402 
20403     //! Resize image to dimensions of a display window \newinstance.
20404     CImg<T> get_resize(const CImgDisplay& disp,
20405                        const int interpolation_type=1, const unsigned int boundary_conditions=0,
20406                        const float centering_x = 0, const float centering_y = 0,
20407                        const float centering_z = 0, const float centering_c = 0) const {
20408       return get_resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions,
20409                         centering_x,centering_y,centering_z,centering_c);
20410     }
20411 
20412     //! Resize image to half-size along XY axes, using an optimized filter.
20413     CImg<T>& resize_halfXY() {
20414       return get_resize_halfXY().move_to(*this);
20415     }
20416 
20417     //! Resize image to half-size along XY axes, using an optimized filter \newinstance.
20418     CImg<T> get_resize_halfXY() const {
20419       if (is_empty()) return *this;
20420       const Tfloat mask[9] = { 0.07842776544f, 0.1231940459f, 0.07842776544f,
20421                               0.1231940459f,  0.1935127547f, 0.1231940459f,
20422                               0.07842776544f, 0.1231940459f, 0.07842776544f };
20423       T I[9] = { 0 };
20424       CImg<T> res(_width/2,_height/2,_depth,_spectrum);
20425       T *ptrd = res._data;
20426       cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,T)
20427         if (x%2 && y%2) *(ptrd++) = (T)
20428                           (I[0]*mask[0] + I[1]*mask[1] + I[2]*mask[2] +
20429                            I[3]*mask[3] + I[4]*mask[4] + I[5]*mask[5] +
20430                            I[6]*mask[6] + I[7]*mask[7] + I[8]*mask[8]);
20431       return res;
20432     }
20433 
20434     //! Resize image to double-size, using the Scale2X algorithm.
20435     /**
20436        \note Use anisotropic upscaling algorithm <a href="http://scale2x.sourceforge.net/algorithm.html">described here</a>.
20437     **/
20438     CImg<T>& resize_doubleXY() {
20439       return get_resize_doubleXY().move_to(*this);
20440     }
20441 
20442     //! Resize image to double-size, using the Scale2X algorithm \newinstance.
20443     CImg<T> get_resize_doubleXY() const {
20444 #define _cimg_gs2x_for3(bound,i) \
20445  for (int i = 0, _p1##i = 0, \
20446       _n1##i = 1>=(bound)?(int)(bound)-1:1; \
20447       _n1##i<(int)(bound) || i==--_n1##i; \
20448       _p1##i = i++, ++_n1##i, ptrd1+=(res)._width, ptrd2+=(res)._width)
20449 
20450 #define _cimg_gs2x_for3x3(img,x,y,z,c,I,T) \
20451   _cimg_gs2x_for3((img)._height,y) for (int x = 0, \
20452    _p1##x = 0, \
20453    _n1##x = (int)( \
20454    (I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
20455    (I[3] = I[4] = (T)(img)(0,y,z,c)), \
20456    (I[7] = (T)(img)(0,_n1##y,z,c)),     \
20457    1>=(img)._width?(img).width()-1:1); \
20458    (_n1##x<(img).width() && ( \
20459    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
20460    (I[5] = (T)(img)(_n1##x,y,z,c)), \
20461    (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
20462    x==--_n1##x; \
20463    I[1] = I[2], \
20464    I[3] = I[4], I[4] = I[5], \
20465    I[7] = I[8], \
20466    _p1##x = x++, ++_n1##x)
20467 
20468       if (is_empty()) return *this;
20469       CImg<T> res(_width<<1,_height<<1,_depth,_spectrum);
20470       CImg_3x3(I,T);
20471       cimg_forZC(*this,z,c) {
20472         T
20473           *ptrd1 = res.data(0,0,z,c),
20474           *ptrd2 = ptrd1 + res._width;
20475         _cimg_gs2x_for3x3(*this,x,y,z,c,I,T) {
20476           if (Icp!=Icn && Ipc!=Inc) {
20477             *(ptrd1++) = Ipc==Icp?Ipc:Icc;
20478             *(ptrd1++) = Icp==Inc?Inc:Icc;
20479             *(ptrd2++) = Ipc==Icn?Ipc:Icc;
20480             *(ptrd2++) = Icn==Inc?Inc:Icc;
20481           } else { *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc; }
20482         }
20483       }
20484       return res;
20485     }
20486 
20487     //! Resize image to triple-size, using the Scale3X algorithm.
20488     /**
20489        \note Use anisotropic upscaling algorithm <a href="http://scale2x.sourceforge.net/algorithm.html">described here</a>.
20490     **/
20491     CImg<T>& resize_tripleXY() {
20492       return get_resize_tripleXY().move_to(*this);
20493     }
20494 
20495     //! Resize image to triple-size, using the Scale3X algorithm \newinstance.
20496     CImg<T> get_resize_tripleXY() const {
20497 #define _cimg_gs3x_for3(bound,i) \
20498  for (int i = 0, _p1##i = 0, \
20499       _n1##i = 1>=(bound)?(int)(bound)-1:1; \
20500       _n1##i<(int)(bound) || i==--_n1##i; \
20501       _p1##i = i++, ++_n1##i, ptrd1+=2*(res)._width, ptrd2+=2*(res)._width, ptrd3+=2*(res)._width)
20502 
20503 #define _cimg_gs3x_for3x3(img,x,y,z,c,I,T) \
20504   _cimg_gs3x_for3((img)._height,y) for (int x = 0, \
20505    _p1##x = 0, \
20506    _n1##x = (int)( \
20507    (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
20508    (I[3] = I[4] = (T)(img)(0,y,z,c)), \
20509    (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)),      \
20510    1>=(img)._width?(img).width()-1:1); \
20511    (_n1##x<(img).width() && ( \
20512    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
20513    (I[5] = (T)(img)(_n1##x,y,z,c)), \
20514    (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
20515    x==--_n1##x; \
20516    I[0] = I[1], I[1] = I[2], \
20517    I[3] = I[4], I[4] = I[5], \
20518    I[6] = I[7], I[7] = I[8], \
20519    _p1##x = x++, ++_n1##x)
20520 
20521       if (is_empty()) return *this;
20522       CImg<T> res(3*_width,3*_height,_depth,_spectrum);
20523       CImg_3x3(I,T);
20524       cimg_forZC(*this,z,c) {
20525         T
20526           *ptrd1 = res.data(0,0,z,c),
20527           *ptrd2 = ptrd1 + res._width,
20528           *ptrd3 = ptrd2 + res._width;
20529         _cimg_gs3x_for3x3(*this,x,y,z,c,I,T) {
20530           if (Icp != Icn && Ipc != Inc) {
20531             *(ptrd1++) = Ipc==Icp?Ipc:Icc;
20532             *(ptrd1++) = (Ipc==Icp && Icc!=Inp) || (Icp==Inc && Icc!=Ipp)?Icp:Icc;
20533             *(ptrd1++) = Icp==Inc?Inc:Icc;
20534             *(ptrd2++) = (Ipc==Icp && Icc!=Ipn) || (Ipc==Icn && Icc!=Ipp)?Ipc:Icc;
20535             *(ptrd2++) = Icc;
20536             *(ptrd2++) = (Icp==Inc && Icc!=Inn) || (Icn==Inc && Icc!=Inp)?Inc:Icc;
20537             *(ptrd3++) = Ipc==Icn?Ipc:Icc;
20538             *(ptrd3++) = (Ipc==Icn && Icc!=Inn) || (Icn==Inc && Icc!=Ipn)?Icn:Icc;
20539             *(ptrd3++) = Icn==Inc?Inc:Icc;
20540           } else {
20541             *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd1++) = Icc;
20542             *(ptrd2++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc;
20543             *(ptrd3++) = Icc; *(ptrd3++) = Icc; *(ptrd3++) = Icc;
20544           }
20545         }
20546       }
20547       return res;
20548     }
20549 
20550     //! Mirror image content along specified axis.
20551     /**
20552        \param axis Mirror axis
20553     **/
20554     CImg<T>& mirror(const char axis) {
20555       if (is_empty()) return *this;
20556       T *pf, *pb, *buf = 0;
20557       switch (cimg::uncase(axis)) {
20558       case 'x' : {
20559         pf = _data; pb = data(_width-1);
20560         const unsigned int width2 = _width/2;
20561         for (unsigned int yzv = 0; yzv<_height*_depth*_spectrum; ++yzv) {
20562           for (unsigned int x = 0; x<width2; ++x) { const T val = *pf; *(pf++) = *pb; *(pb--) = val; }
20563           pf+=_width - width2;
20564           pb+=_width + width2;
20565         }
20566       } break;
20567       case 'y' : {
20568         buf = new T[_width];
20569         pf = _data; pb = data(0,_height-1);
20570         const unsigned int height2 = _height/2;
20571         for (unsigned int zv = 0; zv<_depth*_spectrum; ++zv) {
20572           for (unsigned int y = 0; y<height2; ++y) {
20573             std::memcpy(buf,pf,_width*sizeof(T));
20574             std::memcpy(pf,pb,_width*sizeof(T));
20575             std::memcpy(pb,buf,_width*sizeof(T));
20576             pf+=_width;
20577             pb-=_width;
20578           }
20579           pf+=(unsigned long)_width*(_height - height2);
20580           pb+=(unsigned long)_width*(_height + height2);
20581         }
20582       } break;
20583       case 'z' : {
20584         buf = new T[(unsigned long)_width*_height];
20585         pf = _data; pb = data(0,0,_depth-1);
20586         const unsigned int depth2 = _depth/2;
20587         cimg_forC(*this,c) {
20588           for (unsigned int z = 0; z<depth2; ++z) {
20589             std::memcpy(buf,pf,_width*_height*sizeof(T));
20590             std::memcpy(pf,pb,_width*_height*sizeof(T));
20591             std::memcpy(pb,buf,_width*_height*sizeof(T));
20592             pf+=(unsigned long)_width*_height;
20593             pb-=(unsigned long)_width*_height;
20594           }
20595           pf+=(unsigned long)_width*_height*(_depth - depth2);
20596           pb+=(unsigned long)_width*_height*(_depth + depth2);
20597         }
20598       } break;
20599       case 'c' : {
20600         buf = new T[(unsigned long)_width*_height*_depth];
20601         pf = _data; pb = data(0,0,0,_spectrum-1);
20602         const unsigned int _spectrum2 = _spectrum/2;
20603         for (unsigned int v = 0; v<_spectrum2; ++v) {
20604           std::memcpy(buf,pf,_width*_height*_depth*sizeof(T));
20605           std::memcpy(pf,pb,_width*_height*_depth*sizeof(T));
20606           std::memcpy(pb,buf,_width*_height*_depth*sizeof(T));
20607           pf+=(unsigned long)_width*_height*_depth;
20608           pb-=(unsigned long)_width*_height*_depth;
20609         }
20610       } break;
20611       default :
20612         throw CImgArgumentException(_cimg_instance
20613                                     "mirror() : Invalid specified axis '%c'.",
20614                                     cimg_instance,
20615                                     axis);
20616       }
20617       delete[] buf;
20618       return *this;
20619     }
20620 
20621     //! Mirror image content along specified axis \newinstance.
20622     CImg<T> get_mirror(const char axis) const {
20623       return (+*this).mirror(axis);
20624     }
20625 
20626     //! Mirror image content along specified axes.
20627     /**
20628        \param axes Mirror axes, as a C-string.
20629        \note \c axes may contains multiple character, e.g. \c "xyz"
20630     **/
20631     CImg<T>& mirror(const char *const axes) {
20632       for (const char *s = axes; *s; s++) mirror(*s);
20633       return *this;
20634     }
20635 
20636     //! Mirror image content along specified axes \newinstance.
20637     CImg<T> get_mirror(const char *const axes) const {
20638       return (+*this).mirror(axes);
20639     }
20640 
20641     //! Shift image content.
20642     /**
20643        \param delta_x Amount of displacement along the X-axis.
20644        \param delta_y Amount of displacement along the Y-axis.
20645        \param delta_z Amount of displacement along the Z-axis.
20646        \param delta_c Amount of displacement along the C-axis.
20647        \param boundary_conditions Border condition.
20648 
20649        - \c boundary_conditions can be :
20650           - 0 : Zero border condition (Dirichlet).
20651           - 1 : Nearest neighbors (Neumann).
20652           - 2 : Repeat Pattern (Fourier style).
20653     **/
20654     CImg<T>& shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0,
20655                    const int boundary_conditions=0) {
20656       if (is_empty()) return *this;
20657       if (delta_x) // Shift along X-axis
20658         switch (boundary_conditions) {
20659         case 0 :
20660           if (cimg::abs(delta_x)>=width()) return fill(0);
20661           if (delta_x<0) cimg_forYZC(*this,y,z,c) {
20662             std::memmove(data(0,y,z,c),data(-delta_x,y,z,c),(_width+delta_x)*sizeof(T));
20663             std::memset(data(_width+delta_x,y,z,c),0,-delta_x*sizeof(T));
20664           } else cimg_forYZC(*this,y,z,c) {
20665             std::memmove(data(delta_x,y,z,c),data(0,y,z,c),(_width-delta_x)*sizeof(T));
20666             std::memset(data(0,y,z,c),0,delta_x*sizeof(T));
20667           }
20668           break;
20669         case 1 :
20670           if (delta_x<0) {
20671             const int ndelta_x = (-delta_x>=width())?width()-1:-delta_x;
20672             if (!ndelta_x) return *this;
20673             cimg_forYZC(*this,y,z,c) {
20674               std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T));
20675               T *ptrd = data(_width-1,y,z,c);
20676               const T val = *ptrd;
20677               for (int l = 0; l<ndelta_x-1; ++l) *(--ptrd) = val;
20678             }
20679           } else {
20680             const int ndelta_x = (delta_x>=width())?width()-1:delta_x;
20681             if (!ndelta_x) return *this;
20682             cimg_forYZC(*this,y,z,c) {
20683               std::memmove(data(ndelta_x,y,z,c),data(0,y,z,c),(_width-ndelta_x)*sizeof(T));
20684               T *ptrd = data(0,y,z,c);
20685               const T val = *ptrd;
20686               for (int l = 0; l<ndelta_x-1; ++l) *(++ptrd) = val;
20687             }
20688           }
20689           break;
20690         default : {
20691           const int ml = cimg::mod(-delta_x,width()), ndelta_x = (ml<=width()/2)?ml:(ml-width());
20692           if (!ndelta_x) return *this;
20693           T *const buf = new T[cimg::abs(ndelta_x)];
20694           if (ndelta_x>0) cimg_forYZC(*this,y,z,c) {
20695             std::memcpy(buf,data(0,y,z,c),ndelta_x*sizeof(T));
20696             std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T));
20697             std::memcpy(data(_width-ndelta_x,y,z,c),buf,ndelta_x*sizeof(T));
20698           } else cimg_forYZC(*this,y,z,c) {
20699             std::memcpy(buf,data(_width+ndelta_x,y,z,c),-ndelta_x*sizeof(T));
20700             std::memmove(data(-ndelta_x,y,z,c),data(0,y,z,c),(_width+ndelta_x)*sizeof(T));
20701             std::memcpy(data(0,y,z,c),buf,-ndelta_x*sizeof(T));
20702           }
20703           delete[] buf;
20704         }
20705         }
20706 
20707       if (delta_y) // Shift along Y-axis
20708         switch (boundary_conditions) {
20709         case 0 :
20710           if (cimg::abs(delta_y)>=height()) return fill(0);
20711           if (delta_y<0) cimg_forZC(*this,z,c) {
20712             std::memmove(data(0,0,z,c),data(0,-delta_y,z,c),_width*(_height+delta_y)*sizeof(T));
20713             std::memset(data(0,_height+delta_y,z,c),0,-delta_y*_width*sizeof(T));
20714           } else cimg_forZC(*this,z,c) {
20715             std::memmove(data(0,delta_y,z,c),data(0,0,z,c),_width*(_height-delta_y)*sizeof(T));
20716             std::memset(data(0,0,z,c),0,delta_y*_width*sizeof(T));
20717           }
20718           break;
20719         case 1 :
20720           if (delta_y<0) {
20721             const int ndelta_y = (-delta_y>=height())?height()-1:-delta_y;
20722             if (!ndelta_y) return *this;
20723             cimg_forZC(*this,z,c) {
20724               std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T));
20725               T *ptrd = data(0,_height-ndelta_y,z,c), *ptrs = data(0,_height-1,z,c);
20726               for (int l = 0; l<ndelta_y-1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; }
20727             }
20728           } else {
20729             const int ndelta_y = (delta_y>=height())?height()-1:delta_y;
20730             if (!ndelta_y) return *this;
20731             cimg_forZC(*this,z,c) {
20732               std::memmove(data(0,ndelta_y,z,c),data(0,0,z,c),_width*(_height-ndelta_y)*sizeof(T));
20733               T *ptrd = data(0,1,z,c), *ptrs = data(0,0,z,c);
20734               for (int l = 0; l<ndelta_y-1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; }
20735             }
20736           }
20737           break;
20738         default : {
20739           const int ml = cimg::mod(-delta_y,height()), ndelta_y = (ml<=height()/2)?ml:(ml-height());
20740           if (!ndelta_y) return *this;
20741           T *const buf = new T[(unsigned long)_width*cimg::abs(ndelta_y)];
20742           if (ndelta_y>0) cimg_forZC(*this,z,c) {
20743             std::memcpy(buf,data(0,0,z,c),_width*ndelta_y*sizeof(T));
20744             std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T));
20745             std::memcpy(data(0,_height-ndelta_y,z,c),buf,_width*ndelta_y*sizeof(T));
20746           } else cimg_forZC(*this,z,c) {
20747             std::memcpy(buf,data(0,_height+ndelta_y,z,c),-ndelta_y*_width*sizeof(T));
20748             std::memmove(data(0,-ndelta_y,z,c),data(0,0,z,c),_width*(_height+ndelta_y)*sizeof(T));
20749             std::memcpy(data(0,0,z,c),buf,-ndelta_y*_width*sizeof(T));
20750           }
20751           delete[] buf;
20752         }
20753         }
20754 
20755       if (delta_z) // Shift along Z-axis
20756         switch (boundary_conditions) {
20757         case 0 :
20758           if (cimg::abs(delta_z)>=depth()) return fill(0);
20759           if (delta_z<0) cimg_forC(*this,c) {
20760             std::memmove(data(0,0,0,c),data(0,0,-delta_z,c),_width*_height*(_depth+delta_z)*sizeof(T));
20761             std::memset(data(0,0,_depth+delta_z,c),0,_width*_height*(-delta_z)*sizeof(T));
20762           } else cimg_forC(*this,c) {
20763             std::memmove(data(0,0,delta_z,c),data(0,0,0,c),_width*_height*(_depth-delta_z)*sizeof(T));
20764             std::memset(data(0,0,0,c),0,delta_z*_width*_height*sizeof(T));
20765           }
20766           break;
20767         case 1 :
20768           if (delta_z<0) {
20769             const int ndelta_z = (-delta_z>=depth())?depth()-1:-delta_z;
20770             if (!ndelta_z) return *this;
20771             cimg_forC(*this,c) {
20772               std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
20773               T *ptrd = data(0,0,_depth-ndelta_z,c), *ptrs = data(0,0,_depth-1,c);
20774               for (int l = 0; l<ndelta_z-1; ++l) { std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=(unsigned long)_width*_height; }
20775             }
20776           } else {
20777             const int ndelta_z = (delta_z>=depth())?depth()-1:delta_z;
20778             if (!ndelta_z) return *this;
20779             cimg_forC(*this,c) {
20780               std::memmove(data(0,0,ndelta_z,c),data(0,0,0,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
20781               T *ptrd = data(0,0,1,c), *ptrs = data(0,0,0,c);
20782               for (int l = 0; l<ndelta_z-1; ++l) { std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=(unsigned long)_width*_height; }
20783             }
20784           }
20785           break;
20786         default : {
20787           const int ml = cimg::mod(-delta_z,depth()), ndelta_z = (ml<=depth()/2)?ml:(ml-depth());
20788           if (!ndelta_z) return *this;
20789           T *const buf = new T[(unsigned long)_width*_height*cimg::abs(ndelta_z)];
20790           if (ndelta_z>0) cimg_forC(*this,c) {
20791             std::memcpy(buf,data(0,0,0,c),_width*_height*ndelta_z*sizeof(T));
20792             std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
20793             std::memcpy(data(0,0,_depth-ndelta_z,c),buf,_width*_height*ndelta_z*sizeof(T));
20794           } else cimg_forC(*this,c) {
20795             std::memcpy(buf,data(0,0,_depth+ndelta_z,c),-ndelta_z*_width*_height*sizeof(T));
20796             std::memmove(data(0,0,-ndelta_z,c),data(0,0,0,c),_width*_height*(_depth+ndelta_z)*sizeof(T));
20797             std::memcpy(data(0,0,0,c),buf,-ndelta_z*_width*_height*sizeof(T));
20798           }
20799           delete[] buf;
20800         }
20801         }
20802 
20803       if (delta_c) // Shift along C-axis
20804         switch (boundary_conditions) {
20805         case 0 :
20806           if (cimg::abs(delta_c)>=spectrum()) return fill(0);
20807           if (delta_c<0) {
20808             std::memmove(_data,data(0,0,0,-delta_c),_width*_height*_depth*(_spectrum+delta_c)*sizeof(T));
20809             std::memset(data(0,0,0,_spectrum+delta_c),0,_width*_height*_depth*(-delta_c)*sizeof(T));
20810           } else {
20811             std::memmove(data(0,0,0,delta_c),_data,_width*_height*_depth*(_spectrum-delta_c)*sizeof(T));
20812             std::memset(_data,0,delta_c*_width*_height*_depth*sizeof(T));
20813           }
20814           break;
20815         case 1 :
20816           if (delta_c<0) {
20817             const int ndelta_c = (-delta_c>=spectrum())?spectrum()-1:-delta_c;
20818             if (!ndelta_c) return *this;
20819             std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
20820             T *ptrd = data(0,0,0,_spectrum-ndelta_c), *ptrs = data(0,0,0,_spectrum-1);
20821             for (int l = 0; l<ndelta_c-1; ++l) { std::memcpy(ptrd,ptrs,_width*_height*_depth*sizeof(T)); ptrd+=(unsigned long)_width*_height*_depth; }
20822           } else {
20823             const int ndelta_c = (delta_c>=spectrum())?spectrum()-1:delta_c;
20824             if (!ndelta_c) return *this;
20825             std::memmove(data(0,0,0,ndelta_c),_data,_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
20826             T *ptrd = data(0,0,0,1);
20827             for (int l = 0; l<ndelta_c-1; ++l) { std::memcpy(ptrd,_data,_width*_height*_depth*sizeof(T)); ptrd+=(unsigned long)_width*_height*_depth; }
20828           }
20829           break;
20830         default : {
20831           const int ml = cimg::mod(-delta_c,spectrum()), ndelta_c = (ml<=spectrum()/2)?ml:(ml-spectrum());
20832           if (!ndelta_c) return *this;
20833           T *const buf = new T[(unsigned long)_width*_height*_depth*cimg::abs(ndelta_c)];
20834           if (ndelta_c>0) {
20835             std::memcpy(buf,_data,_width*_height*_depth*ndelta_c*sizeof(T));
20836             std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
20837             std::memcpy(data(0,0,0,_spectrum-ndelta_c),buf,_width*_height*_depth*ndelta_c*sizeof(T));
20838           } else {
20839             std::memcpy(buf,data(0,0,0,_spectrum+ndelta_c),-ndelta_c*_width*_height*_depth*sizeof(T));
20840             std::memmove(data(0,0,0,-ndelta_c),_data,_width*_height*_depth*(_spectrum+ndelta_c)*sizeof(T));
20841             std::memcpy(_data,buf,-ndelta_c*_width*_height*_depth*sizeof(T));
20842           }
20843           delete[] buf;
20844         }
20845         }
20846       return *this;
20847     }
20848 
20849     //! Shift image content \newinstance.
20850     CImg<T> get_shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0,
20851                           const int boundary_conditions=0) const {
20852       return (+*this).shift(delta_x,delta_y,delta_z,delta_c,boundary_conditions);
20853     }
20854 
20855     //! Permute axes order.
20856     /**
20857        \param order Axes permutations, as a C-string of 4 characters.
20858        This function permutes image content regarding the specified axes permutation.
20859     **/
20860     CImg<T>& permute_axes(const char *const order) {
20861       return get_permute_axes(order).move_to(*this);
20862     }
20863 
20864     //! Permute axes order \newinstance.
20865     CImg<T> get_permute_axes(const char *const order) const {
20866       const T foo = (T)0;
20867       return _get_permute_axes(order,foo);
20868     }
20869 
20870     template<typename t>
20871     CImg<t> _get_permute_axes(const char *const permut, const t&) const {
20872       if (is_empty() || !permut) return CImg<t>(*this,false);
20873       CImg<t> res;
20874       const T* ptrs = _data;
20875       if (!cimg::strncasecmp(permut,"xyzc",4)) return +*this;
20876       if (!cimg::strncasecmp(permut,"xycz",4)) {
20877         res.assign(_width,_height,_spectrum,_depth);
20878         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20879         cimg_forXYZC(*this,x,y,z,c) res(x,y,c,z,wh,whd) = (t)*(ptrs++);
20880       }
20881       if (!cimg::strncasecmp(permut,"xzyc",4)) {
20882         res.assign(_width,_depth,_height,_spectrum);
20883         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20884         cimg_forXYZC(*this,x,y,z,c) res(x,z,y,c,wh,whd) = (t)*(ptrs++);
20885       }
20886       if (!cimg::strncasecmp(permut,"xzcy",4)) {
20887         res.assign(_width,_depth,_spectrum,_height);
20888         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20889         cimg_forXYZC(*this,x,y,z,c) res(x,z,c,y,wh,whd) = (t)*(ptrs++);
20890       }
20891       if (!cimg::strncasecmp(permut,"xcyz",4)) {
20892         res.assign(_width,_spectrum,_height,_depth);
20893         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20894         cimg_forXYZC(*this,x,y,z,c) res(x,c,y,z,wh,whd) = (t)*(ptrs++);
20895       }
20896       if (!cimg::strncasecmp(permut,"xczy",4)) {
20897         res.assign(_width,_spectrum,_depth,_height);
20898         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20899         cimg_forXYZC(*this,x,y,z,c) res(x,c,z,y,wh,whd) = (t)*(ptrs++);
20900       }
20901       if (!cimg::strncasecmp(permut,"yxzc",4)) {
20902         res.assign(_height,_width,_depth,_spectrum);
20903         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20904         cimg_forXYZC(*this,x,y,z,c) res(y,x,z,c,wh,whd) = (t)*(ptrs++);
20905       }
20906       if (!cimg::strncasecmp(permut,"yxcz",4)) {
20907         res.assign(_height,_width,_spectrum,_depth);
20908         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20909         cimg_forXYZC(*this,x,y,z,c) res(y,x,c,z,wh,whd) = (t)*(ptrs++);
20910       }
20911       if (!cimg::strncasecmp(permut,"yzxc",4)) {
20912         res.assign(_height,_depth,_width,_spectrum);
20913         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20914         cimg_forXYZC(*this,x,y,z,c) res(y,z,x,c,wh,whd) = (t)*(ptrs++);
20915       }
20916       if (!cimg::strncasecmp(permut,"yzcx",4)) {
20917         res.assign(_height,_depth,_spectrum,_width);
20918         switch (_width) {
20919         case 1 : {
20920           t *ptr_r = res.data(0,0,0,0);
20921           for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
20922             *(ptr_r++) = (t)*(ptrs++);
20923           }
20924         } break;
20925         case 2 : {
20926           t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1);
20927           for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
20928             *(ptr_r++) = (t)*(ptrs++); *(ptr_g++) = (t)*(ptrs++);
20929           }
20930         } break;
20931         case 3 : { // Optimization for the classical conversion from interleaved RGB to planar RGB
20932           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);
20933           for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
20934             *(ptr_r++) = (t)*(ptrs++); *(ptr_g++) = (t)*(ptrs++); *(ptr_b++) = (t)*(ptrs++);
20935           }
20936         } break;
20937         case 4 : { // Optimization for the classical conversion from interleaved RGBA to planar RGBA
20938           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), *ptr_a = res.data(0,0,0,3);
20939           for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
20940             *(ptr_r++) = (t)*(ptrs++); *(ptr_g++) = (t)*(ptrs++); *(ptr_b++) = (t)*(ptrs++); *(ptr_a++) = (t)*(ptrs++);
20941           }
20942         } break;
20943         default : {
20944           const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20945           cimg_forXYZC(*this,x,y,z,c) res(y,z,c,x,wh,whd) = *(ptrs++);
20946           return res;
20947         }
20948         }
20949       }
20950       if (!cimg::strncasecmp(permut,"ycxz",4)) {
20951         res.assign(_height,_spectrum,_width,_depth);
20952         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20953         cimg_forXYZC(*this,x,y,z,c) res(y,c,x,z,wh,whd) = (t)*(ptrs++);
20954       }
20955       if (!cimg::strncasecmp(permut,"yczx",4)) {
20956         res.assign(_height,_spectrum,_depth,_width);
20957         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20958         cimg_forXYZC(*this,x,y,z,c) res(y,c,z,x,wh,whd) = (t)*(ptrs++);
20959       }
20960       if (!cimg::strncasecmp(permut,"zxyc",4)) {
20961         res.assign(_depth,_width,_height,_spectrum);
20962         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20963         cimg_forXYZC(*this,x,y,z,c) res(z,x,y,c,wh,whd) = (t)*(ptrs++);
20964       }
20965       if (!cimg::strncasecmp(permut,"zxcy",4)) {
20966         res.assign(_depth,_width,_spectrum,_height);
20967         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20968         cimg_forXYZC(*this,x,y,z,c) res(z,x,c,y,wh,whd) = (t)*(ptrs++);
20969       }
20970       if (!cimg::strncasecmp(permut,"zyxc",4)) {
20971         res.assign(_depth,_height,_width,_spectrum);
20972         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20973         cimg_forXYZC(*this,x,y,z,c) res(z,y,x,c,wh,whd) = (t)*(ptrs++);
20974       }
20975       if (!cimg::strncasecmp(permut,"zycx",4)) {
20976         res.assign(_depth,_height,_spectrum,_width);
20977         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20978         cimg_forXYZC(*this,x,y,z,c) res(z,y,c,x,wh,whd) = (t)*(ptrs++);
20979       }
20980       if (!cimg::strncasecmp(permut,"zcxy",4)) {
20981         res.assign(_depth,_spectrum,_width,_height);
20982         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20983         cimg_forXYZC(*this,x,y,z,c) res(z,c,x,y,wh,whd) = (t)*(ptrs++);
20984       }
20985       if (!cimg::strncasecmp(permut,"zcyx",4)) {
20986         res.assign(_depth,_spectrum,_height,_width);
20987         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20988         cimg_forXYZC(*this,x,y,z,c) res(z,c,y,x,wh,whd) = (t)*(ptrs++);
20989       }
20990       if (!cimg::strncasecmp(permut,"cxyz",4)) {
20991         res.assign(_spectrum,_width,_height,_depth);
20992         switch (_spectrum) {
20993         case 1 : {
20994           const T *ptr_r = data(0,0,0,0);
20995           t *ptrd = res._data;
20996           for (unsigned long siz = (unsigned long)_width*_height*_depth; siz; --siz) *(ptrd++) = (t)*(ptr_r++);
20997         } break;
20998         case 2 : {
20999           const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1);
21000           t *ptrd = res._data;
21001           for (unsigned long siz = (unsigned long)_width*_height*_depth; siz; --siz) {
21002             *(ptrd++) = (t)*(ptr_r++); *(ptrd++) = (t)*(ptr_g++);
21003           }
21004         } break;
21005         case 3 : { // Optimization for the classical conversion from planar RGB to interleaved RGB
21006           const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
21007           t *ptrd = res._data;
21008           for (unsigned long siz = (unsigned long)_width*_height*_depth; siz; --siz) {
21009             *(ptrd++) = (t)*(ptr_r++); *(ptrd++) = (t)*(ptr_g++); *(ptrd++) = (t)*(ptr_b++);
21010           }
21011         } break;
21012         case 4 : { // Optimization for the classical conversion from planar RGBA to interleaved RGBA
21013           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);
21014           t *ptrd = res._data;
21015           for (unsigned long siz = (unsigned long)_width*_height*_depth; siz; --siz) {
21016             *(ptrd++) = (t)*(ptr_r++); *(ptrd++) = (t)*(ptr_g++); *(ptrd++) = (t)*(ptr_b++); *(ptrd++) = (t)*(ptr_a++);
21017           }
21018         } break;
21019         default : {
21020           const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
21021           cimg_forXYZC(*this,x,y,z,c) res(c,x,y,z,wh,whd) = (t)*(ptrs++);
21022         }
21023         }
21024       }
21025       if (!cimg::strncasecmp(permut,"cxzy",4)) {
21026         res.assign(_spectrum,_width,_depth,_height);
21027         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
21028         cimg_forXYZC(*this,x,y,z,c) res(c,x,z,y,wh,whd) = (t)*(ptrs++);
21029       }
21030       if (!cimg::strncasecmp(permut,"cyxz",4)) {
21031         res.assign(_spectrum,_height,_width,_depth);
21032         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
21033         cimg_forXYZC(*this,x,y,z,c) res(c,y,x,z,wh,whd) = (t)*(ptrs++);
21034       }
21035       if (!cimg::strncasecmp(permut,"cyzx",4)) {
21036         res.assign(_spectrum,_height,_depth,_width);
21037         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
21038         cimg_forXYZC(*this,x,y,z,c) res(c,y,z,x,wh,whd) = (t)*(ptrs++);
21039       }
21040       if (!cimg::strncasecmp(permut,"czxy",4)) {
21041         res.assign(_spectrum,_depth,_width,_height);
21042         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
21043         cimg_forXYZC(*this,x,y,z,c) res(c,z,x,y,wh,whd) = (t)*(ptrs++);
21044       }
21045       if (!cimg::strncasecmp(permut,"czyx",4)) {
21046         res.assign(_spectrum,_depth,_height,_width);
21047         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
21048         cimg_forXYZC(*this,x,y,z,c) res(c,z,y,x,wh,whd) = (t)*(ptrs++);
21049       }
21050       if (!res)
21051         throw CImgArgumentException(_cimg_instance
21052                                     "permute_axes() : Invalid specified permutation '%s'.",
21053                                     cimg_instance,
21054                                     permut);
21055       return res;
21056     }
21057 
21058     //! Unroll pixel values along specified axis.
21059     /**
21060        \param axis Unroll axis (can be \c 'x', \c 'y', \c 'z' or c 'c').
21061     **/
21062     CImg<T>& unroll(const char axis) {
21063       const unsigned int siz = size();
21064       if (siz) switch (axis) {
21065       case 'x' : _width = siz; _height = _depth = _spectrum = 1; break;
21066       case 'y' : _height = siz; _width = _depth = _spectrum = 1; break;
21067       case 'z' : _depth = siz; _width = _height = _spectrum = 1; break;
21068       default : _spectrum = siz; _width = _height = _depth = 1;
21069       }
21070       return *this;
21071     }
21072 
21073     //! Unroll pixel values along specified axis \newinstance.
21074     CImg<T> get_unroll(const char axis) const {
21075       return (+*this).unroll(axis);
21076     }
21077 
21078     //! Rotate image with arbitrary angle.
21079     /**
21080        \param angle Rotation angle, in degrees.
21081        \param boundary_conditions Boundary conditions. Can be <tt>{  0=dirichlet | 1=neumann | 2=cyclic }</tt>.
21082        \param interpolation_type Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
21083        \note Most of the time, size of the image is modified.
21084     **/
21085     CImg<T>& rotate(const float angle, const unsigned int boundary_conditions=0, const unsigned int interpolation_type=1) {
21086       return get_rotate(angle,boundary_conditions,interpolation_type).move_to(*this);
21087     }
21088 
21089     //! Rotate image with arbitrary angle \newinstance.
21090     CImg<T> get_rotate(const float angle, const unsigned int boundary_conditions=0, const unsigned int interpolation_type=1) const {
21091       if (is_empty()) return *this;
21092       CImg<T> res;
21093       const float nangle = cimg::mod(angle,360.0f);
21094       if (boundary_conditions!=1 && cimg::mod(nangle,90.0f)==0) { // optimized version for orthogonal angles
21095         const int wm1 = width() - 1, hm1 = height() - 1;
21096         const int iangle = (int)nangle/90;
21097         switch (iangle) {
21098         case 1 : {
21099           res.assign(_height,_width,_depth,_spectrum);
21100           T *ptrd = res._data;
21101           cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(y,hm1-x,z,c);
21102         } break;
21103         case 2 : {
21104           res.assign(_width,_height,_depth,_spectrum);
21105           T *ptrd = res._data;
21106           cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1-x,hm1-y,z,c);
21107         } break;
21108         case 3 : {
21109           res.assign(_height,_width,_depth,_spectrum);
21110           T *ptrd = res._data;
21111           cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1-y,x,z,c);
21112         } break;
21113         default :
21114           return *this;
21115         }
21116       } else { // generic version
21117         const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
21118         const float
21119           rad = (float)(nangle*cimg::PI/180.0),
21120           ca = (float)std::cos(rad),
21121           sa = (float)std::sin(rad),
21122           ux = cimg::abs(_width*ca), uy = cimg::abs(_width*sa),
21123           vx = cimg::abs(_height*sa), vy = cimg::abs(_height*ca),
21124           w2 = 0.5f*_width, h2 = 0.5f*_height,
21125           dw2 = 0.5f*(ux+vx), dh2 = 0.5f*(uy+vy);
21126         res.assign((int)(ux+vx),(int)(uy+vy),_depth,_spectrum);
21127         switch (boundary_conditions) {
21128         case 0 : {
21129           switch (interpolation_type) {
21130           case 2 : {
21131             cimg_forXY(res,x,y) cimg_forZC(*this,z,c) {
21132               const Tfloat val = cubic_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,c,0);
21133               res(x,y,z,c) = (T)(val<vmin?vmin:val>vmax?vmax:val);
21134             }
21135           } break;
21136           case 1 : {
21137             cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
21138               res(x,y,z,c) = (T)linear_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,c,0);
21139           } break;
21140           default : {
21141             cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
21142               res(x,y,z,c) = atXY((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),(int)(h2 - (x-dw2)*sa + (y-dh2)*ca),z,c,0);
21143           }
21144           }
21145         } break;
21146         case 1 : {
21147           switch (interpolation_type) {
21148           case 2 : {
21149             cimg_forXY(res,x,y) cimg_forZC(*this,z,c) {
21150               const Tfloat val = _cubic_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,c);
21151               res(x,y,z,c) = (T)(val<vmin?vmin:val>vmax?vmax:val);
21152             }
21153           } break;
21154           case 1 : {
21155             cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
21156               res(x,y,z,c) = (T)_linear_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,c);
21157           } break;
21158           default : {
21159             cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
21160               res(x,y,z,c) = _atXY((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),(int)(h2 - (x-dw2)*sa + (y-dh2)*ca),z,c);
21161           }
21162           }
21163         } break;
21164         case 2 : {
21165           switch (interpolation_type) {
21166           case 2 : {
21167             cimg_forXY(res,x,y) cimg_forZC(*this,z,c) {
21168               const Tfloat val = _cubic_atXY(cimg::mod(w2 + (x-dw2)*ca + (y-dh2)*sa,(float)width()),
21169                                              cimg::mod(h2 - (x-dw2)*sa + (y-dh2)*ca,(float)height()),z,c);
21170               res(x,y,z,c) = (T)(val<vmin?vmin:val>vmax?vmax:val);
21171             }
21172           } break;
21173           case 1 : {
21174             cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
21175               res(x,y,z,c) = (T)_linear_atXY(cimg::mod(w2 + (x-dw2)*ca + (y-dh2)*sa,(float)width()),
21176                                              cimg::mod(h2 - (x-dw2)*sa + (y-dh2)*ca,(float)height()),z,c);
21177           } break;
21178           default : {
21179             cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
21180               res(x,y,z,c) = (*this)(cimg::mod((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),width()),
21181                                       cimg::mod((int)(h2 - (x-dw2)*sa + (y-dh2)*ca),height()),z,c);
21182           }
21183           }
21184         } break;
21185         default :
21186           throw CImgArgumentException(_cimg_instance
21187                                       "rotate() : Invalid specified border conditions %d "
21188                                       "(should be { 0=dirichlet | 1=neumann | 2=cyclic }).",
21189                                       cimg_instance,
21190                                       boundary_conditions);
21191         }
21192       }
21193       return res;
21194     }
21195 
21196     //! Rotate image with arbitrary angle, around a center point.
21197     /**
21198        \param angle Rotation angle, in degrees.
21199        \param cx X-coordinate of the rotation center.
21200        \param cy Y-coordinate of the rotation center.
21201        \param zoom Zoom factor.
21202        \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann | 2=cyclic }</tt>.
21203        \param interpolation_type Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
21204     **/
21205     CImg<T>& rotate(const float angle, const float cx, const float cy, const float zoom,
21206                     const unsigned int boundary_conditions=3, const unsigned int interpolation_type=1) {
21207       return get_rotate(angle,cx,cy,zoom,boundary_conditions,interpolation_type).move_to(*this);
21208     }
21209 
21210     //! Rotate image with arbitrary angle, around a center point \newinstance.
21211     CImg<T> get_rotate(const float angle, const float cx, const float cy, const float zoom,
21212                        const unsigned int boundary_conditions=3, const unsigned int interpolation_type=1) const {
21213       if (interpolation_type>2)
21214         throw CImgArgumentException(_cimg_instance
21215                                     "rotate() : Invalid specified interpolation type %d "
21216                                     "(should be { 0=none | 1=linear | 2=bicubic }).",
21217                                     cimg_instance,
21218                                     interpolation_type);
21219 
21220       if (is_empty()) return *this;
21221       CImg<T> res(_width,_height,_depth,_spectrum);
21222       const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
21223       const float
21224         rad = (float)((angle*cimg::PI)/180.0),
21225         ca = (float)std::cos(rad)/zoom,
21226         sa = (float)std::sin(rad)/zoom;
21227       switch (boundary_conditions) {
21228       case 0 : {
21229         switch (interpolation_type) {
21230         case 2 : {
21231           cimg_forXY(res,x,y) cimg_forZC(*this,z,c) {
21232             const Tfloat val = cubic_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,c,0);
21233             res(x,y,z,c) = (T)(val<vmin?vmin:val>vmax?vmax:val);
21234           }
21235         } break;
21236         case 1 : {
21237           cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
21238             res(x,y,z,c) = (T)linear_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,c,0);
21239         } break;
21240         default : {
21241           cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
21242             res(x,y,z,c) = atXY((int)(cx + (x-cx)*ca + (y-cy)*sa),(int)(cy - (x-cx)*sa + (y-cy)*ca),z,c,0);
21243         }
21244         }
21245       } break;
21246       case 1 : {
21247         switch (interpolation_type) {
21248         case 2 : {
21249           cimg_forXY(res,x,y) cimg_forZC(*this,z,c) {
21250             const Tfloat val = _cubic_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,c);
21251             res(x,y,z,c) = (T)(val<vmin?vmin:val>vmax?vmax:val);
21252           }
21253         } break;
21254         case 1 : {
21255           cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
21256             res(x,y,z,c) = (T)_linear_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,c);
21257         } break;
21258         default : {
21259           cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
21260             res(x,y,z,c) = _atXY((int)(cx + (x-cx)*ca + (y-cy)*sa),(int)(cy - (x-cx)*sa + (y-cy)*ca),z,c);
21261         }
21262         }
21263       } break;
21264       case 2 : {
21265         switch (interpolation_type) {
21266         case 2 : {
21267           cimg_forXY(res,x,y) cimg_forZC(*this,z,c) {
21268             const Tfloat val = _cubic_atXY(cimg::mod(cx + (x-cx)*ca + (y-cy)*sa,(float)width()),
21269                                            cimg::mod(cy - (x-cx)*sa + (y-cy)*ca,(float)height()),z,c);
21270             res(x,y,z,c) = (T)(val<vmin?vmin:val>vmax?vmax:val);
21271           }
21272         } break;
21273         case 1 : {
21274           cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
21275             res(x,y,z,c) = (T)_linear_atXY(cimg::mod(cx + (x-cx)*ca + (y-cy)*sa,(float)width()),
21276                                            cimg::mod(cy - (x-cx)*sa + (y-cy)*ca,(float)height()),z,c);
21277         } break;
21278         default : {
21279           cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
21280             res(x,y,z,c) = (*this)(cimg::mod((int)(cx + (x-cx)*ca + (y-cy)*sa),width()),
21281                                     cimg::mod((int)(cy - (x-cx)*sa + (y-cy)*ca),height()),z,c);
21282         }
21283         }
21284       } break;
21285       default :
21286         throw CImgArgumentException(_cimg_instance
21287                                     "rotate() : Invalid specified border conditions %d "
21288                                     "(should be { 0=dirichlet | 1=neumann | 2=cyclic }).",
21289                                     cimg_instance,
21290                                     boundary_conditions);
21291       }
21292       return res;
21293     }
21294 
21295     //! Warp image content by a warping field.
21296     /**
21297        \param warp Warping field.
21298        \param is_relative Tells if warping field gives absolute or relative warping coordinates.
21299        \param is_linear_interpolation Tells if linear interpolation must be used (instead of nearest neighbors).
21300        \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann | 2=cyclic }</tt>.
21301      **/
21302     template<typename t>
21303     CImg<T>& warp(const CImg<t>& warp, const bool is_relative=false,
21304                   const bool is_linear_interpolation=true, const unsigned int boundary_conditions=0) {
21305       return get_warp(warp,is_relative,is_linear_interpolation,boundary_conditions).move_to(*this);
21306     }
21307 
21308     //! Warp image content by a warping field \newinstance
21309     template<typename t>
21310     CImg<T> get_warp(const CImg<t>& warp, const bool is_relative=false,
21311                      const bool is_linear_interpolation=true, const unsigned int boundary_conditions=0) const {
21312       if (is_empty() || !warp) return *this;
21313       if (is_relative && !is_sameXYZ(warp))
21314         throw CImgArgumentException(_cimg_instance
21315                                     "warp() : Instance and specified relative warping field (%u,%u,%u,%u,%p) "
21316                                     "have different XYZ dimensions.",
21317                                     cimg_instance,
21318                                     warp._width,warp._height,warp._depth,warp._spectrum,warp._data);
21319 
21320       CImg<T> res(warp._width,warp._height,warp._depth,_spectrum);
21321       T *ptrd = res._data;
21322       switch (warp._spectrum) {
21323       case 1 : // 1d warping.
21324         if (is_relative) { // Relative warp coordinates
21325           if (is_linear_interpolation) switch (boundary_conditions) {
21326             case 2 : {
21327               cimg_forC(res,c) {
21328                 const t *ptrs0 = warp._data;
21329                 cimg_forXYZ(res,x,y,z)
21330                   *(ptrd++) = (T)_linear_atX(cimg::mod(x - (float)*(ptrs0++),(float)_width),y,z,c);
21331               }
21332             } break;
21333             case 1 : {
21334               cimg_forC(res,c) {
21335                 const t *ptrs0 = warp._data;
21336                 cimg_forXYZ(res,x,y,z)
21337                   *(ptrd++) = (T)_linear_atX(x - (float)*(ptrs0++),y,z,c);
21338               }
21339             } break;
21340             default : {
21341               cimg_forC(res,c) {
21342                 const t *ptrs0 = warp._data;
21343                 cimg_forXYZ(res,x,y,z)
21344                   *(ptrd++) = (T)linear_atX(x - (float)*(ptrs0++),y,z,c,0);
21345               }
21346             }
21347             } else switch (boundary_conditions) {
21348             case 2 : {
21349               cimg_forC(res,c) {
21350                 const t *ptrs0 = warp._data;
21351                 cimg_forXYZ(res,x,y,z)
21352                   *(ptrd++) = (*this)(cimg::mod(x - (int)*(ptrs0++),(int)_width),y,z,c);
21353               }
21354             } break;
21355             case 1 : {
21356               cimg_forC(res,c) {
21357                 const t *ptrs0 = warp._data;
21358                 cimg_forXYZ(res,x,y,z)
21359                   *(ptrd++) = _atX(x - (int)*(ptrs0++),y,z,c);
21360               }
21361             } break;
21362             default : {
21363               cimg_forC(res,c) {
21364                 const t *ptrs0 = warp._data;
21365                 cimg_forXYZ(res,x,y,z)
21366                   *(ptrd++) = atX(x - (int)*(ptrs0++),y,z,c,0);
21367               }
21368             }
21369             }
21370         } else { // Absolute warp coordinates
21371           if (is_linear_interpolation) switch (boundary_conditions) {
21372             case 2 : {
21373               cimg_forC(res,c) {
21374                 const t *ptrs0 = warp._data;
21375                 cimg_forXYZ(res,x,y,z)
21376                   *(ptrd++) = (T)_linear_atX(cimg::mod((float)*(ptrs0++),(float)_width),0,0,c);
21377               }
21378             } break;
21379             case 1 : {
21380               cimg_forC(res,c) {
21381                 const t *ptrs0 = warp._data;
21382                 cimg_forXYZ(res,x,y,z)
21383                   *(ptrd++) = (T)_linear_atX((float)*(ptrs0++),0,0,c);
21384               }
21385             } break;
21386             default : {
21387               cimg_forC(res,c) {
21388                 const t *ptrs0 = warp._data;
21389                 cimg_forXYZ(res,x,y,z)
21390                   *(ptrd++) = (T)linear_atX((float)*(ptrs0++),0,0,c,0);
21391               }
21392             }
21393             } else switch (boundary_conditions) {
21394             case 2 : {
21395               cimg_forC(res,c) {
21396                 const t *ptrs0 = warp._data;
21397                 cimg_forXYZ(res,x,y,z)
21398                   *(ptrd++) = (*this)(cimg::mod((int)*(ptrs0++),(int)_width),0,0,c);
21399               }
21400             } break;
21401             case 1 : {
21402               cimg_forC(res,c) {
21403                 const t *ptrs0 = warp._data;
21404                 cimg_forXYZ(res,x,y,z)
21405                   *(ptrd++) = _atX((int)*(ptrs0++),0,0,c);
21406               }
21407             } break;
21408             default : {
21409               cimg_forC(res,c) {
21410                 const t *ptrs0 = warp._data;
21411                 cimg_forXYZ(res,x,y,z)
21412                   *(ptrd++) = atX((int)*(ptrs0++),0,0,c,0);
21413               }
21414             }
21415             }
21416         }
21417         break;
21418 
21419       case 2 : // 2d warping
21420         if (is_relative) { // Relative warp coordinates
21421           if (is_linear_interpolation) switch (boundary_conditions) {
21422             case 2 : {
21423               cimg_forC(res,c) {
21424                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21425                 cimg_forXYZ(res,x,y,z)
21426                   *(ptrd++) = (T)_linear_atXY(cimg::mod(x - (float)*(ptrs0++),(float)_width),
21427                                               cimg::mod(y - (float)*(ptrs1++),(float)_height),z,c);
21428               }
21429             } break;
21430             case 1 : {
21431               cimg_forC(res,c) {
21432                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21433                 cimg_forXYZ(res,x,y,z)
21434                   *(ptrd++) = (T)_linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
21435               }
21436             } break;
21437             default : {
21438               cimg_forC(res,c) {
21439                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21440                 cimg_forXYZ(res,x,y,z)
21441                   *(ptrd++) = (T)linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,0);
21442               }
21443             }
21444             } else switch (boundary_conditions) {
21445             case 2 : {
21446               cimg_forC(res,c) {
21447                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21448                 cimg_forXYZ(res,x,y,z)
21449                   *(ptrd++) = (*this)(cimg::mod(x - (int)*(ptrs0++),(int)_width),
21450                                       cimg::mod(y - (int)*(ptrs1++),(int)_height),z,c);
21451               }
21452             } break;
21453             case 1 : {
21454               cimg_forC(res,c) {
21455                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21456                 cimg_forXYZ(res,x,y,z)
21457                   *(ptrd++) = _atXY(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z,c);
21458               }
21459             } break;
21460             default : {
21461               cimg_forC(res,c) {
21462                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21463                 cimg_forXYZ(res,x,y,z)
21464                   *(ptrd++) = atXY(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z,c,0);
21465               }
21466             }
21467             }
21468         } else { // Absolute warp coordinates
21469           if (is_linear_interpolation) switch (boundary_conditions) {
21470             case 2 : {
21471               cimg_forC(res,c) {
21472                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21473                 cimg_forXYZ(res,x,y,z)
21474                   *(ptrd++) = (T)_linear_atXY(cimg::mod((float)*(ptrs0++),(float)_width),
21475                                               cimg::mod((float)*(ptrs1++),(float)_height),0,c);
21476               }
21477             } break;
21478             case 1 : {
21479               cimg_forC(res,c) {
21480                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21481                 cimg_forXYZ(res,x,y,z)
21482                   *(ptrd++) = (T)_linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c);
21483               }
21484             } break;
21485             default : {
21486               cimg_forC(res,c) {
21487                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21488                 cimg_forXYZ(res,x,y,z)
21489                   *(ptrd++) = (T)linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c,0);
21490               }
21491             }
21492             } else switch (boundary_conditions) {
21493             case 2 : {
21494               cimg_forC(res,c) {
21495                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21496                 cimg_forXYZ(res,x,y,z)
21497                   *(ptrd++) = (*this)(cimg::mod((int)*(ptrs0++),(int)_width),
21498                                       cimg::mod((int)*(ptrs1++),(int)_height),0,c);
21499               }
21500             } break;
21501             case 1 : {
21502               cimg_forC(res,c) {
21503                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21504                 cimg_forXYZ(res,x,y,z)
21505                   *(ptrd++) = _atXY((int)*(ptrs0++),(int)*(ptrs1++),0,c);
21506               }
21507             } break;
21508             default : {
21509               cimg_forC(res,c) {
21510                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21511                 cimg_forXYZ(res,x,y,z)
21512                   *(ptrd++) = atXY((int)*(ptrs0++),(int)*(ptrs1++),0,c,0);
21513               }
21514             }
21515             }
21516         }
21517         break;
21518 
21519       default : // 3d warping
21520         if (is_relative) { // Relative warp coordinates
21521           if (is_linear_interpolation) switch (boundary_conditions) {
21522             case 2 : {
21523               cimg_forC(res,c) {
21524                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21525                 cimg_forXYZ(res,x,y,z)
21526                   *(ptrd++) = (T)_linear_atXYZ(cimg::mod(x - (float)*(ptrs0++),(float)_width),
21527                                                cimg::mod(y - (float)*(ptrs1++),(float)_height),
21528                                                cimg::mod(z - (float)*(ptrs2++),(float)_depth),c);
21529               }
21530             } break;
21531             case 1 : {
21532               cimg_forC(res,c) {
21533                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21534                 cimg_forXYZ(res,x,y,z)
21535                   *(ptrd++) = (T)_linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c);
21536               }
21537             } break;
21538             default : {
21539               cimg_forC(res,c) {
21540                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21541                 cimg_forXYZ(res,x,y,z)
21542                   *(ptrd++) = (T)linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c,0);
21543               }
21544             }
21545             } else switch (boundary_conditions) {
21546             case 2 : {
21547               cimg_forC(res,c) {
21548                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21549                 cimg_forXYZ(res,x,y,z)
21550                   *(ptrd++) = (*this)(cimg::mod(x - (int)*(ptrs0++),(int)_width),
21551                                       cimg::mod(y - (int)*(ptrs1++),(int)_height),
21552                                       cimg::mod(z - (int)*(ptrs2++),(int)_depth),c);
21553               }
21554             } break;
21555             case 1 : {
21556               cimg_forC(res,c) {
21557                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21558                 cimg_forXYZ(res,x,y,z)
21559                   *(ptrd++) = _atXYZ(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z - (int)*(ptrs2++),c);
21560               }
21561             } break;
21562             default : {
21563               cimg_forC(res,c) {
21564                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21565                 cimg_forXYZ(res,x,y,z)
21566                   *(ptrd++) = atXYZ(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z - (int)*(ptrs2++),c,0);
21567               }
21568             }
21569             }
21570         } else { // Absolute warp coordinates
21571           if (is_linear_interpolation) switch (boundary_conditions) {
21572             case 2 : {
21573               cimg_forC(res,c) {
21574                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21575                 cimg_forXYZ(res,x,y,z)
21576                   *(ptrd++) = (T)_linear_atXYZ(cimg::mod((float)*(ptrs0++),(float)_width),
21577                                                cimg::mod((float)*(ptrs1++),(float)_height),
21578                                                cimg::mod((float)*(ptrs2++),(float)_depth),c);
21579               }
21580             } break;
21581             case 1 : {
21582               cimg_forC(res,c) {
21583                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21584                 cimg_forXYZ(res,x,y,z)
21585                   *(ptrd++) = (T)_linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
21586               }
21587             } break;
21588             default : {
21589               cimg_forC(res,c) {
21590                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21591                 cimg_forXYZ(res,x,y,z)
21592                   *(ptrd++) = (T)linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c,0);
21593               }
21594             }
21595             } else switch (boundary_conditions) {
21596             case 2 : {
21597               cimg_forC(res,c) {
21598                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21599                 cimg_forXYZ(res,x,y,z)
21600                   *(ptrd++) = (*this)(cimg::mod((int)*(ptrs0++),(int)_width),
21601                                       cimg::mod((int)*(ptrs1++),(int)_height),
21602                                       cimg::mod((int)*(ptrs2++),(int)_depth),c);
21603               }
21604             } break;
21605             case 1 : {
21606               cimg_forC(res,c) {
21607                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21608                 cimg_forXYZ(res,x,y,z)
21609                   *(ptrd++) = _atXYZ((int)*(ptrs0++),(int)*(ptrs1++),(int)*(ptrs2++),c);
21610               }
21611             } break;
21612             default : {
21613               cimg_forC(res,c) {
21614                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21615                 cimg_forXYZ(res,x,y,z)
21616                   *(ptrd++) = atXYZ((int)*(ptrs0++),(int)*(ptrs1++),(int)*(ptrs2++),c,0);
21617               }
21618             }
21619             }
21620         }
21621       }
21622       return res;
21623     }
21624 
21625     //! Generate a 2d representation of a 3d image, with XY,XZ and YZ views.
21626     /**
21627        \param x0 X-coordinate of the projection point.
21628        \param y0 Y-coordinate of the projection point.
21629        \param z0 Z-coordinate of the projection point.
21630     **/
21631     CImg<T> get_projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) const {
21632       if (is_empty() || _depth<2) return +*this;
21633       const unsigned int
21634         _x0 = (x0>=_width)?_width - 1:x0,
21635         _y0 = (y0>=_height)?_height - 1:y0,
21636         _z0 = (z0>=_depth)?_depth - 1:z0;
21637       const CImg<T>
21638         img_xy = get_crop(0,0,_z0,0,_width-1,_height-1,_z0,_spectrum-1),
21639         img_zy = get_crop(_x0,0,0,0,_x0,_height-1,_depth-1,_spectrum-1).permute_axes("xzyc").resize(_depth,_height,1,-100,-1),
21640         img_xz = get_crop(0,_y0,0,0,_width-1,_y0,_depth-1,_spectrum-1).resize(_width,_depth,1,-100,-1);
21641       return CImg<T>(_width + _depth,_height + _depth,1,_spectrum,cimg::min(img_xy.min(),img_zy.min(),img_xz.min())).
21642         draw_image(0,0,img_xy).draw_image(img_xy._width,0,img_zy).
21643         draw_image(0,img_xy._height,img_xz);
21644     }
21645 
21646     //! Construct a 2d representation of a 3d image, with XY,XZ and YZ views \inplace.
21647     CImg<T>& projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) {
21648       if (_depth<2) return *this;
21649       return get_projections2d(x0,y0,z0).move_to(*this);
21650     }
21651 
21652     //! Crop image region.
21653     /**
21654        \param x0 = X-coordinate of the upper-left crop rectangle corner.
21655        \param y0 = Y-coordinate of the upper-left crop rectangle corner.
21656        \param z0 = Z-coordinate of the upper-left crop rectangle corner.
21657        \param c0 = C-coordinate of the upper-left crop rectangle corner.
21658        \param x1 = X-coordinate of the lower-right crop rectangle corner.
21659        \param y1 = Y-coordinate of the lower-right crop rectangle corner.
21660        \param z1 = Z-coordinate of the lower-right crop rectangle corner.
21661        \param c1 = C-coordinate of the lower-right crop rectangle corner.
21662        \param boundary_conditions = Dirichlet (false) or Neumann border conditions.
21663     **/
21664     CImg<T>& crop(const int x0, const int y0, const int z0, const int c0,
21665                   const int x1, const int y1, const int z1, const int c1,
21666                   const bool boundary_conditions=false) {
21667       return get_crop(x0,y0,z0,c0,x1,y1,z1,c1,boundary_conditions).move_to(*this);
21668     }
21669 
21670     //! Crop image region \newinstance.
21671     CImg<T> get_crop(const int x0, const int y0, const int z0, const int c0,
21672                      const int x1, const int y1, const int z1, const int c1,
21673                      const bool boundary_conditions=false) const {
21674       if (is_empty())
21675         throw CImgInstanceException(_cimg_instance
21676                                     "crop() : Empty instance.",
21677                                     cimg_instance);
21678       const int
21679         nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0,
21680         ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0,
21681         nz0 = z0<z1?z0:z1, nz1 = z0^z1^nz0,
21682         nc0 = c0<c1?c0:c1, nc1 = c0^c1^nc0;
21683       CImg<T> res(1U + nx1 - nx0,1U + ny1 - ny0,1U + nz1 - nz0,1U + nc1 - nc0);
21684       if (nx0<0 || nx1>=width() || ny0<0 || ny1>=height() || nz0<0 || nz1>=depth() || nc0<0 || nc1>=spectrum()) {
21685         if (boundary_conditions) cimg_forXYZC(res,x,y,z,c) res(x,y,z,c) = _atXYZC(nx0+x,ny0+y,nz0+z,nc0+c);
21686         else res.fill(0).draw_image(-nx0,-ny0,-nz0,-nc0,*this);
21687       } else res.draw_image(-nx0,-ny0,-nz0,-nc0,*this);
21688       return res;
21689     }
21690 
21691     //! Crop image region \overloading.
21692     CImg<T>& crop(const int x0, const int y0, const int z0,
21693                   const int x1, const int y1, const int z1,
21694                   const bool boundary_conditions=false) {
21695       return crop(x0,y0,z0,0,x1,y1,z1,_spectrum-1,boundary_conditions);
21696     }
21697 
21698     //! Crop image region \newinstance.
21699     CImg<T> get_crop(const int x0, const int y0, const int z0,
21700                      const int x1, const int y1, const int z1,
21701                      const bool boundary_conditions=false) const {
21702       return get_crop(x0,y0,z0,0,x1,y1,z1,_spectrum-1,boundary_conditions);
21703     }
21704 
21705     //! Crop image region \overloading.
21706     CImg<T>& crop(const int x0, const int y0,
21707                   const int x1, const int y1,
21708                   const bool boundary_conditions=false) {
21709       return crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions);
21710     }
21711 
21712     //! Crop image region \newinstance.
21713     CImg<T> get_crop(const int x0, const int y0,
21714                      const int x1, const int y1,
21715                      const bool boundary_conditions=false) const {
21716       return get_crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions);
21717     }
21718 
21719     //! Crop image region \overloading.
21720     CImg<T>& crop(const int x0, const int x1, const bool boundary_conditions=false) {
21721       return crop(x0,0,0,0,x1,_height-1,_depth-1,_spectrum-1,boundary_conditions);
21722     }
21723 
21724     //! Crop image region \newinstance.
21725     CImg<T> get_crop(const int x0, const int x1, const bool boundary_conditions=false) const {
21726       return get_crop(x0,0,0,0,x1,_height-1,_depth-1,_spectrum-1,boundary_conditions);
21727     }
21728 
21729     //! Autocrop image region, regarding the specified background value.
21730     CImg<T>& autocrop(const T value, const char *const axes="czyx") {
21731       if (is_empty()) return *this;
21732       for (const char *s = axes; *s; ++s) {
21733         const char axis = cimg::uncase(*s);
21734         const CImg<intT> coords = _autocrop(value,axis);
21735         if (coords[0]==-1 && coords[1]==-1) return assign(); // Image has only 'value' pixels.
21736         else switch (axis) {
21737         case 'x' : {
21738           const int x0 = coords[0], x1 = coords[1];
21739           if (x0>=0 && x1>=0) crop(x0,x1);
21740         } break;
21741         case 'y' : {
21742           const int y0 = coords[0], y1 = coords[1];
21743           if (y0>=0 && y1>=0) crop(0,y0,_width-1,y1);
21744         } break;
21745         case 'z' : {
21746           const int z0 = coords[0], z1 = coords[1];
21747           if (z0>=0 && z1>=0) crop(0,0,z0,_width-1,_height-1,z1);
21748         } break;
21749         default : {
21750           const int c0 = coords[0], c1 = coords[1];
21751           if (c0>=0 && c1>=0) crop(0,0,0,c0,_width-1,_height-1,_depth-1,c1);
21752         }
21753         }
21754       }
21755       return *this;
21756     }
21757 
21758     //! Autocrop image region, regarding the specified background value \newinstance.
21759     CImg<T> get_autocrop(const T value, const char *const axes="czyx") const {
21760       return (+*this).autocrop(value,axes);
21761     }
21762 
21763     //! Autocrop image region, regarding the specified background color.
21764     CImg<T>& autocrop(const T *const color, const char *const axes="zyx") {
21765       if (is_empty()) return *this;
21766       for (const char *s = axes; *s; ++s) {
21767         const char axis = cimg::uncase(*s);
21768         switch (axis) {
21769         case 'x' : {
21770           int x0 = width(), x1 = -1;
21771           cimg_forC(*this,c) {
21772             const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'x');
21773             const int nx0 = coords[0], nx1 = coords[1];
21774             if (nx0>=0 && nx1>=0) { x0 = cimg::min(x0,nx0); x1 = cimg::max(x1,nx1); }
21775           }
21776           if (x0==width() && x1==-1) return assign(); else crop(x0,x1);
21777         } break;
21778         case 'y' : {
21779           int y0 = height(), y1 = -1;
21780           cimg_forC(*this,c) {
21781             const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'y');
21782             const int ny0 = coords[0], ny1 = coords[1];
21783             if (ny0>=0 && ny1>=0) { y0 = cimg::min(y0,ny0); y1 = cimg::max(y1,ny1); }
21784           }
21785           if (y0==height() && y1==-1) return assign(); else crop(0,y0,_width-1,y1);
21786         } break;
21787         default : {
21788           int z0 = depth(), z1 = -1;
21789           cimg_forC(*this,c) {
21790             const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'z');
21791             const int nz0 = coords[0], nz1 = coords[1];
21792             if (nz0>=0 && nz1>=0) { z0 = cimg::min(z0,nz0); z1 = cimg::max(z1,nz1); }
21793           }
21794           if (z0==depth() && z1==-1) return assign(); else crop(0,0,z0,_width-1,_height-1,z1);
21795         }
21796         }
21797       }
21798       return *this;
21799     }
21800 
21801     //! Autocrop image region, regarding the specified background color \newinstance.
21802     CImg<T> get_autocrop(const T *const color, const char *const axes="zyx") const {
21803       return (+*this).autocrop(color,axes);
21804     }
21805 
21806     //! Autocrop image region, regarding the specified background color \overloading.
21807     template<typename t> CImg<T>& autocrop(const CImg<t>& color, const char *const axes="zyx") {
21808       return get_autocrop(color,axes).move_to(*this);
21809     }
21810 
21811     //! Autocrop image region, regarding the specified background color \newinstance.
21812     template<typename t> CImg<T> get_autocrop(const CImg<t>& color, const char *const axes="zyx") const {
21813       return get_autocrop(color._data,axes);
21814     }
21815 
21816     CImg<intT> _autocrop(const T value, const char axis) const {
21817       CImg<intT> res;
21818       switch (cimg::uncase(axis)) {
21819       case 'x' : {
21820         int x0 = -1, x1 = -1;
21821         cimg_forX(*this,x) cimg_forYZC(*this,y,z,c)
21822           if ((*this)(x,y,z,c)!=value) { x0 = x; x = width(); y = height(); z = depth(); c = spectrum(); }
21823         if (x0>=0) {
21824           for (int x = width()-1; x>=0; --x) cimg_forYZC(*this,y,z,c)
21825             if ((*this)(x,y,z,c)!=value) { x1 = x; x = 0; y = height(); z = depth(); c = spectrum(); }
21826         }
21827         res = CImg<intT>::vector(x0,x1);
21828       } break;
21829       case 'y' : {
21830         int y0 = -1, y1 = -1;
21831         cimg_forY(*this,y) cimg_forXZC(*this,x,z,c)
21832           if ((*this)(x,y,z,c)!=value) { y0 = y; x = width(); y = height(); z = depth(); c = spectrum(); }
21833         if (y0>=0) {
21834           for (int y = height()-1; y>=0; --y) cimg_forXZC(*this,x,z,c)
21835             if ((*this)(x,y,z,c)!=value) { y1 = y; x = width(); y = 0; z = depth(); c = spectrum(); }
21836         }
21837         res = CImg<intT>::vector(y0,y1);
21838       } break;
21839       case 'z' : {
21840         int z0 = -1, z1 = -1;
21841         cimg_forZ(*this,z) cimg_forXYC(*this,x,y,c)
21842           if ((*this)(x,y,z,c)!=value) { z0 = z; x = width(); y = height(); z = depth(); c = spectrum(); }
21843         if (z0>=0) {
21844           for (int z = depth()-1; z>=0; --z) cimg_forXYC(*this,x,y,c)
21845             if ((*this)(x,y,z,c)!=value) { z1 = z; x = width(); y = height(); z = 0; c = spectrum(); }
21846         }
21847         res = CImg<intT>::vector(z0,z1);
21848       } break;
21849       default : {
21850         int c0 = -1, c1 = -1;
21851         cimg_forC(*this,c) cimg_forXYZ(*this,x,y,z)
21852           if ((*this)(x,y,z,c)!=value) { c0 = c; x = width(); y = height(); z = depth(); c = spectrum(); }
21853         if (c0>=0) {
21854           for (int c = spectrum()-1; c>=0; --c) cimg_forXYZ(*this,x,y,z)
21855             if ((*this)(x,y,z,c)!=value) { c1 = c; x = width(); y = height(); z = depth(); c = 0; }
21856         }
21857         res = CImg<intT>::vector(c0,c1);
21858       }
21859       }
21860       return res;
21861     }
21862 
21863     //! Return specified image column.
21864     /**
21865        \param x0 Image column.
21866     **/
21867     CImg<T> get_column(const int x0) const {
21868       return get_columns(x0,x0);
21869     }
21870 
21871     //! Return specified image column \inplace.
21872     CImg<T>& column(const int x0) {
21873       return columns(x0,x0);
21874     }
21875 
21876     //! Return specified range of image columns.
21877     /**
21878        \param x0 Starting image column.
21879        \param x1 Ending image column.
21880     **/
21881     CImg<T>& columns(const int x0, const int x1) {
21882       return get_columns(x0,x1).move_to(*this);
21883     }
21884 
21885     //! Return specified range of image columns \inplace.
21886     CImg<T> get_columns(const int x0, const int x1) const {
21887       return get_crop(x0,0,0,0,x1,height()-1,depth()-1,spectrum()-1);
21888     }
21889 
21890     //! Return specified image row.
21891     CImg<T> get_row(const int y0) const {
21892       return get_rows(y0,y0);
21893     }
21894 
21895     //! Return specified image row \inplace.
21896     /**
21897        \param y0 Image row.
21898     **/
21899     CImg<T>& row(const int y0) {
21900       return rows(y0,y0);
21901     }
21902 
21903     //! Return specified range of image rows.
21904     /**
21905        \param y0 Starting image row.
21906        \param y1 Ending image row.
21907     **/
21908     CImg<T> get_rows(const int y0, const int y1) const {
21909       return get_crop(0,y0,0,0,width()-1,y1,depth()-1,spectrum()-1);
21910     }
21911 
21912     //! Return specified range of image rows \inplace.
21913     CImg<T>& rows(const int y0, const int y1) {
21914       return get_rows(y0,y1).move_to(*this);
21915     }
21916 
21917     //! Return specified image slice.
21918     /**
21919        \param z0 Image slice.
21920     **/
21921     CImg<T> get_slice(const int z0) const {
21922       return get_slices(z0,z0);
21923     }
21924 
21925     //! Return specified image slice \inplace.
21926     CImg<T>& slice(const int z0) {
21927       return slices(z0,z0);
21928     }
21929 
21930     //! Return specified range of image slices.
21931     /**
21932        \param z0 Starting image slice.
21933        \param z1 Ending image slice.
21934     **/
21935     CImg<T> get_slices(const int z0, const int z1) const {
21936       return get_crop(0,0,z0,0,width()-1,height()-1,z1,spectrum()-1);
21937     }
21938 
21939     //! Return specified range of image slices \inplace.
21940     CImg<T>& slices(const int z0, const int z1) {
21941       return get_slices(z0,z1).move_to(*this);
21942     }
21943 
21944     //! Return specified image channel.
21945     /**
21946        \param c0 Image channel.
21947     **/
21948     CImg<T> get_channel(const int c0) const {
21949       return get_channels(c0,c0);
21950     }
21951 
21952     //! Return specified image channel \inplace.
21953     CImg<T>& channel(const int c0) {
21954       return channels(c0,c0);
21955     }
21956 
21957     //! Return specified range of image channels.
21958     /**
21959        \param c0 Starting image channel.
21960        \param c1 Ending image channel.
21961     **/
21962     CImg<T> get_channels(const int c0, const int c1) const {
21963       return get_crop(0,0,0,c0,width()-1,height()-1,depth()-1,c1);
21964     }
21965 
21966     //! Return specified range of image channels \inplace.
21967     CImg<T>& channels(const int c0, const int c1) {
21968       return get_channels(c0,c1).move_to(*this);
21969     }
21970 
21971     //! Return stream line of a 2d or 3d vector field.
21972     CImg<floatT> get_streamline(const float x, const float y, const float z,
21973                                 const float L=256, const float dl=0.1f,
21974                                 const unsigned int interpolation_type=2, const bool is_backward_tracking=false,
21975                                 const bool is_oriented_only=false) const {
21976       if (_spectrum!=2 && _spectrum!=3)
21977         throw CImgInstanceException(_cimg_instance
21978                                     "streamline() : Instance is not a 2d or 3d vector field.",
21979                                     cimg_instance);
21980       if (_spectrum==2) {
21981         if (is_oriented_only) {
21982           typename CImg<T>::_functor4d_streamline2d_oriented func(*this);
21983           return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,0,0,0,_width-1.0f,_height-1.0f,0.0f);
21984         } else {
21985           typename CImg<T>::_functor4d_streamline2d_directed func(*this);
21986           return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,0,0,0,_width-1.0f,_height-1.0f,0.0f);
21987         }
21988       }
21989       if (is_oriented_only) {
21990         typename CImg<T>::_functor4d_streamline3d_oriented func(*this);
21991         return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,0,0,0,_width-1.0f,_height-1.0f,_depth-1.0f);
21992       }
21993       typename CImg<T>::_functor4d_streamline3d_directed func(*this);
21994       return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,0,0,0,_width-1.0f,_height-1.0f,_depth-1.0f);
21995     }
21996 
21997     //! Return stream line of a 3d vector field.
21998     /**
21999        \param func Vector field function.
22000        \param x X-coordinate of the starting point of the streamline.
22001        \param y Y-coordinate of the starting point of the streamline.
22002        \param z Z-coordinate of the starting point of the streamline.
22003        \param L Streamline length.
22004        \param dl Streamline length increment.
22005        \param interpolation_type Type of interpolation. Can be <tt>{ 0=nearest int | 1=linear | 2=2nd-order RK | 3=4th-order RK. }</tt>.
22006        \param is_backward_tracking Tells if the streamline is estimated forward or backward.
22007        \param is_oriented_only Tells if the direction of the vectors must be ignored.
22008        \param x0 X-coordinate of the first bounding-box vertex.
22009        \param y0 Y-coordinate of the first bounding-box vertex.
22010        \param z0 Z-coordinate of the first bounding-box vertex.
22011        \param x1 X-coordinate of the second bounding-box vertex.
22012        \param y1 Y-coordinate of the second bounding-box vertex.
22013        \param z1 Z-coordinate of the second bounding-box vertex.
22014     **/
22015     template<typename tfunc>
22016     static CImg<floatT> streamline(const tfunc& func,
22017                                    const float x, const float y, const float z,
22018                                    const float L=256, const float dl=0.1f,
22019                                    const unsigned int interpolation_type=2, const bool is_backward_tracking=false,
22020                                    const bool is_oriented_only=false,
22021                                    const float x0=0, const float y0=0, const float z0=0,
22022                                    const float x1=0, const float y1=0, const float z1=0) {
22023       if (dl<=0)
22024         throw CImgArgumentException("CImg<%s>::streamline() : Invalid specified integration length %g "
22025                                     "(should be >0).",
22026                                     pixel_type(),
22027                                     dl);
22028 
22029       const bool is_bounded = (x0!=x1 || y0!=y1 || z0!=z1);
22030       if (L<=0 || (is_bounded && (x<x0 || x>x1 || y<y0 || y>y1 || z<z0 || z>z1))) return CImg<floatT>();
22031       const unsigned int size_L = (unsigned int)cimg::round(L/dl+1);
22032       CImg<floatT> coordinates(size_L,3);
22033       const float dl2 = dl/2;
22034       float
22035         *ptr_x = coordinates.data(0,0),
22036         *ptr_y = coordinates.data(0,1),
22037         *ptr_z = coordinates.data(0,2),
22038         pu = (float)(dl*func(x,y,z,0)),
22039         pv = (float)(dl*func(x,y,z,1)),
22040         pw = (float)(dl*func(x,y,z,2)),
22041         X = x, Y = y, Z = z;
22042 
22043       switch (interpolation_type) {
22044       case 0 : { // Nearest integer interpolation.
22045         cimg_forX(coordinates,l) {
22046           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
22047           const int
22048             xi = (int)(X>0?X+0.5f:X-0.5f),
22049             yi = (int)(Y>0?Y+0.5f:Y-0.5f),
22050             zi = (int)(Z>0?Z+0.5f:Z-0.5f);
22051           float
22052             u = (float)(dl*func((float)xi,(float)yi,(float)zi,0)),
22053             v = (float)(dl*func((float)xi,(float)yi,(float)zi,1)),
22054             w = (float)(dl*func((float)xi,(float)yi,(float)zi,2));
22055           if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
22056           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
22057           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
22058         }
22059       } break;
22060       case 1 : { // First-order interpolation.
22061         cimg_forX(coordinates,l) {
22062           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
22063           float
22064             u = (float)(dl*func(X,Y,Z,0)),
22065             v = (float)(dl*func(X,Y,Z,1)),
22066             w = (float)(dl*func(X,Y,Z,2));
22067           if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
22068           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
22069           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
22070         }
22071       } break;
22072       case 2 : { // Second order interpolation.
22073         cimg_forX(coordinates,l) {
22074           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
22075           float
22076             u0 = (float)(dl2*func(X,Y,Z,0)),
22077             v0 = (float)(dl2*func(X,Y,Z,1)),
22078             w0 = (float)(dl2*func(X,Y,Z,2));
22079           if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
22080           float
22081             u = (float)(dl*func(X+u0,Y+v0,Z+w0,0)),
22082             v = (float)(dl*func(X+u0,Y+v0,Z+w0,1)),
22083             w = (float)(dl*func(X+u0,Y+v0,Z+w0,2));
22084           if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
22085           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
22086           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
22087         }
22088       } break;
22089       default : { // Fourth order interpolation.
22090         cimg_forX(coordinates,x) {
22091           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
22092           float
22093             u0 = (float)(dl2*func(X,Y,Z,0)),
22094             v0 = (float)(dl2*func(X,Y,Z,1)),
22095             w0 = (float)(dl2*func(X,Y,Z,2));
22096           if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
22097           float
22098             u1 = (float)(dl2*func(X+u0,Y+v0,Z+w0,0)),
22099             v1 = (float)(dl2*func(X+u0,Y+v0,Z+w0,1)),
22100             w1 = (float)(dl2*func(X+u0,Y+v0,Z+w0,2));
22101           if (is_oriented_only && u1*pu + v1*pv + w1*pw<0) { u1 = -u1; v1 = -v1; w1 = -w1; }
22102           float
22103             u2 = (float)(dl2*func(X+u1,Y+v1,Z+w1,0)),
22104             v2 = (float)(dl2*func(X+u1,Y+v1,Z+w1,1)),
22105             w2 = (float)(dl2*func(X+u1,Y+v1,Z+w1,2));
22106           if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u2 = -u2; v2 = -v2; w2 = -w2; }
22107           float
22108             u3 = (float)(dl2*func(X+u2,Y+v2,Z+w2,0)),
22109             v3 = (float)(dl2*func(X+u2,Y+v2,Z+w2,1)),
22110             w3 = (float)(dl2*func(X+u2,Y+v2,Z+w2,2));
22111           if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u3 = -u3; v3 = -v3; w3 = -w3; }
22112           const float
22113             u = (u0 + u3)/3 + (u1 + u2)/1.5f,
22114             v = (v0 + v3)/3 + (v1 + v2)/1.5f,
22115             w = (w0 + w3)/3 + (w1 + w2)/1.5f;
22116           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
22117           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
22118         }
22119       }
22120       }
22121       if (ptr_x!=coordinates.data(0,1)) coordinates.resize((int)(ptr_x-coordinates.data()),3,1,1,0);
22122       return coordinates;
22123     }
22124 
22125     //! Return stream line of a 3d vector field \overloading.
22126     static CImg<floatT> streamline(const char *const expression,
22127                                    const float x, const float y, const float z,
22128                                    const float L=256, const float dl=0.1f,
22129                                    const unsigned int interpolation_type=2, const bool is_backward_tracking=true,
22130                                    const bool is_oriented_only=false,
22131                                    const float x0=0, const float y0=0, const float z0=0,
22132                                    const float x1=0, const float y1=0, const float z1=0) {
22133       _functor4d_streamline_expr func(expression);
22134       return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,is_oriented_only,x0,y0,z0,x1,y1,z1);
22135     }
22136 
22137     struct _functor4d_streamline2d_directed {
22138       const CImg<T>& ref;
22139       _functor4d_streamline2d_directed(const CImg<T>& pref):ref(pref) {}
22140       float operator()(const float x, const float y, const float z, const unsigned int c) const {
22141         return c<2?(float)ref._linear_atXY(x,y,(int)z,c):0;
22142       }
22143     };
22144 
22145     struct _functor4d_streamline3d_directed {
22146       const CImg<T>& ref;
22147       _functor4d_streamline3d_directed(const CImg<T>& pref):ref(pref) {}
22148       float operator()(const float x, const float y, const float z, const unsigned int c) const {
22149         return (float)ref._linear_atXYZ(x,y,z,c);
22150       }
22151     };
22152 
22153     struct _functor4d_streamline2d_oriented {
22154       const CImg<T>& ref;
22155       CImg<floatT> *pI;
22156       _functor4d_streamline2d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,1,2); }
22157       ~_functor4d_streamline2d_oriented() { delete pI; }
22158       float operator()(const float x, const float y, const float z, const unsigned int c) const {
22159 #define _cimg_vecalign2d(i,j) 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); }
22160         int
22161           xi = (int)x - (x>=0?0:1), nxi = xi + 1,
22162           yi = (int)y - (y>=0?0:1), nyi = yi + 1,
22163           zi = (int)z;
22164         const float
22165           dx = x - xi,
22166           dy = y - yi;
22167         if (c==0) {
22168           CImg<floatT>& I = *pI;
22169           if (xi<0) xi = 0; if (nxi<0) nxi = 0;
22170           if (xi>=ref.width()) xi = ref.width()-1; if (nxi>=ref.width()) nxi = ref.width()-1;
22171           if (yi<0) yi = 0; if (nyi<0) nyi = 0;
22172           if (yi>=ref.height()) yi = ref.height()-1; if (nyi>=ref.height()) nyi = ref.height()-1;
22173           I(0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,1) = (float)ref(xi,yi,zi,1);
22174           I(1,0,0) = (float)ref(nxi,yi,zi,0); I(1,0,1) = (float)ref(nxi,yi,zi,1);
22175           I(1,1,0) = (float)ref(nxi,nyi,zi,0); I(1,1,1) = (float)ref(nxi,nyi,zi,1);
22176           I(0,1,0) = (float)ref(xi,nyi,zi,0); I(0,1,1) = (float)ref(xi,nyi,zi,1);
22177           _cimg_vecalign2d(1,0); _cimg_vecalign2d(1,1); _cimg_vecalign2d(0,1);
22178         }
22179         return c<2?(float)pI->_linear_atXY(dx,dy,0,c):0;
22180       }
22181     };
22182 
22183     struct _functor4d_streamline3d_oriented {
22184       const CImg<T>& ref;
22185       CImg<floatT> *pI;
22186       _functor4d_streamline3d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,2,3); }
22187       ~_functor4d_streamline3d_oriented() { delete pI; }
22188       float operator()(const float x, const float y, const float z, const unsigned int c) const {
22189 #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) { \
22190   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); }
22191         int
22192           xi = (int)x - (x>=0?0:1), nxi = xi + 1,
22193           yi = (int)y - (y>=0?0:1), nyi = yi + 1,
22194           zi = (int)z - (z>=0?0:1), nzi = zi + 1;
22195         const float
22196           dx = x - xi,
22197           dy = y - yi,
22198           dz = z - zi;
22199         if (c==0) {
22200           CImg<floatT>& I = *pI;
22201           if (xi<0) xi = 0; if (nxi<0) nxi = 0;
22202           if (xi>=ref.width()) xi = ref.width()-1; if (nxi>=ref.width()) nxi = ref.width()-1;
22203           if (yi<0) yi = 0; if (nyi<0) nyi = 0;
22204           if (yi>=ref.height()) yi = ref.height()-1; if (nyi>=ref.height()) nyi = ref.height()-1;
22205           if (zi<0) zi = 0; if (nzi<0) nzi = 0;
22206           if (zi>=ref.depth()) zi = ref.depth()-1; if (nzi>=ref.depth()) nzi = ref.depth()-1;
22207           I(0,0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,0,1) = (float)ref(xi,yi,zi,1); I(0,0,0,2) = (float)ref(xi,yi,zi,2);
22208           I(1,0,0,0) = (float)ref(nxi,yi,zi,0); I(1,0,0,1) = (float)ref(nxi,yi,zi,1); I(1,0,0,2) = (float)ref(nxi,yi,zi,2);
22209           I(1,1,0,0) = (float)ref(nxi,nyi,zi,0); I(1,1,0,1) = (float)ref(nxi,nyi,zi,1); I(1,1,0,2) = (float)ref(nxi,nyi,zi,2);
22210           I(0,1,0,0) = (float)ref(xi,nyi,zi,0); I(0,1,0,1) = (float)ref(xi,nyi,zi,1); I(0,1,0,2) = (float)ref(xi,yi,zi,2);
22211           I(0,0,0,1) = (float)ref(xi,yi,nzi,0); I(0,0,0,1) = (float)ref(xi,yi,nzi,1); I(0,0,0,2) = (float)ref(xi,yi,nzi,2);
22212           I(1,0,0,1) = (float)ref(nxi,yi,nzi,0); I(1,0,0,1) = (float)ref(nxi,yi,nzi,1); I(1,0,0,2) = (float)ref(nxi,yi,nzi,2);
22213           I(1,1,0,1) = (float)ref(nxi,nyi,nzi,0); I(1,1,0,1) = (float)ref(nxi,nyi,nzi,1); I(1,1,0,2) = (float)ref(nxi,nyi,nzi,2);
22214           I(0,1,0,1) = (float)ref(xi,nyi,nzi,0); I(0,1,0,1) = (float)ref(xi,nyi,nzi,1); I(0,1,0,2) = (float)ref(xi,yi,nzi,2);
22215           _cimg_vecalign3d(1,0,0); _cimg_vecalign3d(1,1,0); _cimg_vecalign3d(0,1,0);
22216           _cimg_vecalign3d(0,0,1); _cimg_vecalign3d(1,0,1); _cimg_vecalign3d(1,1,1); _cimg_vecalign3d(0,1,1);
22217         }
22218         return (float)pI->_linear_atXYZ(dx,dy,dz,c);
22219       }
22220     };
22221 
22222     struct _functor4d_streamline_expr {
22223       _cimg_math_parser *mp;
22224       ~_functor4d_streamline_expr() { delete mp; }
22225       _functor4d_streamline_expr(const char *const expr):mp(0) { mp = new _cimg_math_parser(CImg<T>::empty(),expr,"streamline"); }
22226       float operator()(const float x, const float y, const float z, const unsigned int c) const {
22227         return (float)mp->eval(x,y,z,c);
22228       }
22229     };
22230 
22231     //! Return a shared-memory image referencing a range of pixels of the image instance.
22232     /**
22233        \param x0 X-coordinate of the starting pixel.
22234        \param x1 X-coordinate of the ending pixel.
22235        \param y0 Y-coordinate.
22236        \param z0 Z-coordinate.
22237        \param c0 C-coordinate.
22238      **/
22239     CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
22240                               const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) {
22241       const unsigned int beg = (unsigned int)offset(x0,y0,z0,c0), end = offset(x1,y0,z0,c0);
22242       if (beg>end || beg>=size() || end>=size())
22243         throw CImgArgumentException(_cimg_instance
22244                                     "get_shared_points() : Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).",
22245                                     cimg_instance,
22246                                     x0,x1,y0,z0,c0);
22247 
22248       return CImg<T>(_data+beg,x1-x0+1,1,1,1,true);
22249     }
22250 
22251     //! Return a shared-memory image referencing a range of pixels of the image instance \const.
22252     const CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
22253                                     const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) const {
22254       const unsigned int beg = (unsigned int)offset(x0,y0,z0,c0), end = offset(x1,y0,z0,c0);
22255       if (beg>end || beg>=size() || end>=size())
22256         throw CImgArgumentException(_cimg_instance
22257                                     "get_shared_points() : Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).",
22258                                     cimg_instance,
22259                                     x0,x1,y0,z0,c0);
22260 
22261       return CImg<T>(_data+beg,x1-x0+1,1,1,1,true);
22262     }
22263 
22264     //! Return a shared-memory image referencing a range of rows of the image instance.
22265     /**
22266        \param y0 Y-coordinate of the starting row.
22267        \param y1 Y-coordinate of the ending row.
22268        \param z0 Z-coordinate.
22269        \param c0 C-coordinate.
22270     **/
22271     CImg<T> get_shared_rows(const unsigned int y0, const unsigned int y1,
22272                              const unsigned int z0=0, const unsigned int c0=0) {
22273       const unsigned int beg = offset(0,y0,z0,c0), end = offset(0,y1,z0,c0);
22274       if (beg>end || beg>=size() || end>=size())
22275         throw CImgArgumentException(_cimg_instance
22276                                     "get_shared_rows() : Invalid request of a shared-memory subset (0->%u,%u->%u,%u,%u).",
22277                                     cimg_instance,
22278                                     _width-1,y0,y1,z0,c0);
22279 
22280       return CImg<T>(_data+beg,_width,y1-y0+1,1,1,true);
22281     }
22282 
22283     //! Return a shared-memory image referencing a range of rows of the image instance \const.
22284     const CImg<T> get_shared_rows(const unsigned int y0, const unsigned int y1,
22285                                    const unsigned int z0=0, const unsigned int c0=0) const {
22286       const unsigned int beg = offset(0,y0,z0,c0), end = offset(0,y1,z0,c0);
22287       if (beg>end || beg>=size() || end>=size())
22288         throw CImgArgumentException(_cimg_instance
22289                                     "get_shared_rows() : Invalid request of a shared-memory subset (0->%u,%u->%u,%u,%u).",
22290                                     cimg_instance,
22291                                     _width-1,y0,y1,z0,c0);
22292 
22293       return CImg<T>(_data+beg,_width,y1-y0+1,1,1,true);
22294     }
22295 
22296     //! Return a shared-memory image referencing one row of the image instance.
22297     /**
22298        \param y0 Y-coordinate.
22299        \param z0 Z-coordinate.
22300        \param c0 C-coordinate.
22301     **/
22302     CImg<T> get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) {
22303       return get_shared_rows(y0,y0,z0,c0);
22304     }
22305 
22306     //! Return a shared-memory image referencing one row of the image instance \const.
22307     const CImg<T> get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) const {
22308       return get_shared_rows(y0,y0,z0,c0);
22309     }
22310 
22311     //! Return a shared memory image referencing a range of slices of the image instance.
22312     /**
22313        \param z0 Z-coordinate of the starting slice.
22314        \param z1 Z-coordinate of the ending slice.
22315        \param c0 C-coordinate.
22316     **/
22317     CImg<T> get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) {
22318       const unsigned int beg = offset(0,0,z0,c0), end = offset(0,0,z1,c0);
22319       if (beg>end || beg>=size() || end>=size())
22320         throw CImgArgumentException(_cimg_instance
22321                                     "get_shared_slices() : Invalid request of a shared-memory subset (0->%u,0->%u,%u->%u,%u).",
22322                                     cimg_instance,
22323                                     _width-1,_height-1,z0,z1,c0);
22324 
22325       return CImg<T>(_data+beg,_width,_height,z1-z0+1,1,true);
22326     }
22327 
22328     //! Return a shared memory image referencing a range of slices of the image instance \const.
22329     const CImg<T> get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) const {
22330       const unsigned int beg = offset(0,0,z0,c0), end = offset(0,0,z1,c0);
22331       if (beg>end || beg>=size() || end>=size())
22332         throw CImgArgumentException(_cimg_instance
22333                                     "get_shared_slices() : Invalid request of a shared-memory subset (0->%u,0->%u,%u->%u,%u).",
22334                                     cimg_instance,
22335                                     _width-1,_height-1,z0,z1,c0);
22336 
22337       return CImg<T>(_data+beg,_width,_height,z1-z0+1,1,true);
22338     }
22339 
22340     //! Return a shared-memory image referencing one slice of the image instance.
22341     /**
22342        \param z0 Z-coordinate.
22343        \param c0 C-coordinate.
22344     **/
22345     CImg<T> get_shared_slice(const unsigned int z0, const unsigned int c0=0) {
22346       return get_shared_slices(z0,z0,c0);
22347     }
22348 
22349     //! Return a shared-memory image referencing one slice of the image instance \const.
22350     const CImg<T> get_shared_slice(const unsigned int z0, const unsigned int c0=0) const {
22351       return get_shared_slices(z0,z0,c0);
22352     }
22353 
22354     //! Return a shared-memory image referencing a range of channels of the image instance.
22355     /**
22356        \param c0 C-coordinate of the starting channel.
22357        \param c1 C-coordinate of the ending channel.
22358     **/
22359     CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) {
22360       const unsigned int beg = offset(0,0,0,c0), end = offset(0,0,0,c1);
22361       if (beg>end || beg>=size() || end>=size())
22362         throw CImgArgumentException(_cimg_instance
22363                                     "get_shared_channels() : Invalid request of a shared-memory subset (0->%u,0->%u,0->%u,%u->%u).",
22364                                     cimg_instance,
22365                                     _width-1,_height-1,_depth-1,c0,c1);
22366 
22367       return CImg<T>(_data+beg,_width,_height,_depth,c1-c0+1,true);
22368     }
22369 
22370     //! Return a shared-memory image referencing a range of channels of the image instance \const.
22371     const CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) const {
22372       const unsigned int beg = offset(0,0,0,c0), end = offset(0,0,0,c1);
22373       if (beg>end || beg>=size() || end>=size())
22374         throw CImgArgumentException(_cimg_instance
22375                                     "get_shared_channels() : Invalid request of a shared-memory subset (0->%u,0->%u,0->%u,%u->%u).",
22376                                     cimg_instance,
22377                                     _width-1,_height-1,_depth-1,c0,c1);
22378 
22379       return CImg<T>(_data+beg,_width,_height,_depth,c1-c0+1,true);
22380     }
22381 
22382     //! Return a shared-memory image referencing one channel of the image instance.
22383     /**
22384        \param c0 C-coordinate.
22385     **/
22386     CImg<T> get_shared_channel(const unsigned int c0) {
22387       return get_shared_channels(c0,c0);
22388     }
22389 
22390     //! Return a shared-memory image referencing one channel of the image instance \const.
22391     const CImg<T> get_shared_channel(const unsigned int c0) const {
22392       return get_shared_channels(c0,c0);
22393     }
22394 
22395     //! Return a shared-memory version of the image instance.
22396     CImg<T> get_shared() {
22397       return CImg<T>(_data,_width,_height,_depth,_spectrum,true);
22398     }
22399 
22400     //! Return a shared-memory version of the image instance \const.
22401     const CImg<T> get_shared() const {
22402       return CImg<T>(_data,_width,_height,_depth,_spectrum,true);
22403     }
22404 
22405     //! Split image into a list along specified axis.
22406     /**
22407        \param axis Splitting axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
22408        \param nb Number of splitted parts.
22409        \note
22410        - If \c nb==0, there are as much splitted parts as the image size along the specified axis.
22411        - If \c nb<=0, instance image is splitted into blocs of -\c nb pixel wide.
22412        - If \c nb>0, instance image is splitted into \c nb blocs.
22413     **/
22414     CImgList<T> get_split(const char axis, const int nb=0) const {
22415       CImgList<T> res;
22416       const char _axis = cimg::uncase(axis);
22417 
22418       if (nb<=0) { // Split by bloc size.
22419         const unsigned int dp = (unsigned int)(nb?-nb:1);
22420         int p = 0;
22421         switch (_axis) {
22422         case 'x': {
22423           for (int pe=_width-dp; p<pe; p+=dp) get_crop(p,0,0,0,p+dp-1,_height-1,_depth-1,_spectrum-1).move_to(res);
22424           get_crop(p,0,0,0,_width-1,_height-1,_depth-1,_spectrum-1).move_to(res);
22425         } break;
22426         case 'y': {
22427           for (int pe=_height-dp; p<pe; p+=dp) get_crop(0,p,0,0,_width-1,p+dp-1,_depth-1,_spectrum-1).move_to(res);
22428           get_crop(0,p,0,0,_width-1,_height-1,_depth-1,_spectrum-1).move_to(res);
22429         } break;
22430         case 'z': {
22431           for (int pe=_depth-dp; p<pe; p+=dp) get_crop(0,0,p,0,_width-1,_height-1,p+dp-1,_spectrum-1).move_to(res);
22432           get_crop(0,0,p,0,_width-1,_height-1,_depth-1,_spectrum-1).move_to(res);
22433         } break;
22434         default: {
22435           for (int pe=_spectrum-dp; p<pe; p+=dp) get_crop(0,0,0,p,_width-1,_height-1,_depth-1,p+dp-1).move_to(res);
22436           get_crop(0,0,0,p,_width-1,_height-1,_depth-1,_spectrum-1).move_to(res);
22437         }
22438         }
22439 
22440       } else { // Split by number of (non-homogeneous) blocs.
22441         const unsigned int siz = _axis=='x'?_width:_axis=='y'?_height:_axis=='z'?_depth:_axis=='c'?_spectrum:0;
22442         if ((unsigned int)nb>siz)
22443           throw CImgArgumentException(_cimg_instance
22444                                       "get_split() : Instance cannot be split along %c-axis into %u blocs.",
22445                                       cimg_instance,
22446                                       axis,nb);
22447         int err = (int)siz;
22448         unsigned int _p = 0;
22449         switch (_axis) {
22450         case 'x' : {
22451           cimg_forX(*this,p) if ((err-=nb)<=0) {
22452             get_crop(_p,0,0,0,p,_height-1,_depth-1,_spectrum-1).move_to(res);
22453             err+=(int)siz;
22454             _p=p+1;
22455           }
22456         } break;
22457         case 'y' : {
22458           cimg_forY(*this,p) if ((err-=nb)<=0) {
22459             get_crop(0,_p,0,0,_width-1,p,_depth-1,_spectrum-1).move_to(res);
22460             err+=(int)siz;
22461             _p=p+1;
22462           }
22463         } break;
22464         case 'z' : {
22465           cimg_forZ(*this,p) if ((err-=nb)<=0) {
22466             get_crop(0,0,_p,0,_width-1,_height-1,p,_spectrum-1).move_to(res);
22467             err+=(int)siz;
22468             _p=p+1;
22469           }
22470         } break;
22471         default : {
22472           cimg_forC(*this,p) if ((err-=nb)<=0) {
22473             get_crop(0,0,0,_p,_width-1,_height-1,_depth-1,p).move_to(res);
22474             err+=(int)siz;
22475             _p=p+1;
22476           }
22477         }
22478         }
22479       }
22480       return res;
22481     }
22482 
22483     //! Split image into a list of one-column vectors, according to a specified splitting value.
22484     /**
22485        \param value Splitting value.
22486        \param keep_values Tells if the splitting value must be kept in the splitted blocs.
22487        \param is_shared Tells if the splitted blocs have shared memory buffers.
22488     **/
22489     CImgList<T> get_split(const T value, const bool keep_values, const bool is_shared) const {
22490       CImgList<T> res;
22491       for (const T *ps = _data, *_ps = ps, *const pe = end(); ps<pe; ) {
22492         while (_ps<pe && *_ps==value) ++_ps;
22493         unsigned int siz = _ps - ps;
22494         if (siz && keep_values) res.insert(CImg<T>(ps,1,siz,1,1,is_shared),~0U,is_shared);
22495         ps = _ps;
22496         while (_ps<pe && *_ps!=value) ++_ps;
22497         siz = _ps - ps;
22498         if (siz) res.insert(CImg<T>(ps,1,siz,1,1,is_shared),~0U,is_shared);
22499         ps = _ps;
22500       }
22501       return res;
22502     }
22503 
22504     //! Split image into a list of one-column vectors, according to a specified splitting value sequence.
22505     /**
22506        \param values Splitting value sequence.
22507        \param keep_values Tells if the splitting sequence must be kept in the splitted blocs.
22508        \param is_shared Tells if the splitted blocs have shared memory buffers.
22509      **/
22510     template<typename t>
22511     CImgList<T> get_split(const CImg<t>& values, const bool keep_values, const bool is_shared) const {
22512       if (!values) return CImgList<T>(*this);
22513       if (values.size()==1) return get_split(*values,keep_values,is_shared);
22514       CImgList<T> res;
22515       const t *pve = values.end();
22516       for (const T *ps = _data, *_ps = ps, *const pe = end(); ps<pe; ) {
22517 
22518         // Try to find match from current position.
22519         const t *pv = 0;
22520         do {
22521           pv = values._data;
22522           const T *__ps = _ps;
22523           while (__ps<pe && pv<pve && *__ps==(T)*pv) { ++__ps; ++pv; }
22524           if (pv==pve) _ps = __ps;
22525         } while (pv==pve);
22526         unsigned int siz = _ps - ps;
22527         if (siz && keep_values) res.insert(CImg<T>(ps,1,siz,1,1,is_shared),~0U,is_shared); // If match found.
22528         ps = _ps;
22529 
22530         // Try to find non-match from current position.
22531         do {
22532           pv = values._data;
22533           while (_ps<pe && *_ps!=(T)*pv) ++_ps;
22534           if (_ps<pe) {
22535             const T *__ps = _ps + 1;
22536             ++pv;
22537             while (__ps<pe && pv<pve && *__ps==(T)*pv) { ++__ps; ++pv; }
22538             if (pv!=pve) _ps = __ps;
22539           }
22540         } while (_ps<pe && pv!=pve);
22541 
22542         // Here, EOF of match found.
22543         siz = _ps - ps;
22544         if (siz) res.insert(CImg<T>(ps,1,siz,1,1,is_shared),~0U,is_shared);
22545         ps = _ps;
22546       }
22547       return res;
22548     }
22549 
22550     //! Append two images along specified axis.
22551     /**
22552        \param img Image to append with instance image.
22553        \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
22554        \param align Append alignment in \c [0,1].
22555     **/
22556     template<typename t>
22557     CImg<T>& append(const CImg<t>& img, const char axis='x', const float align=0) {
22558       if (is_empty()) return assign(img,false);
22559       if (!img) return *this;
22560       return CImgList<T>(*this,true).insert(img).get_append(axis,align).move_to(*this);
22561     }
22562 
22563     //! Append two images along specified axis \specialization.
22564     CImg<T>& append(const CImg<T>& img, const char axis='x', const float align=0) {
22565       if (is_empty()) return assign(img,false);
22566       if (!img) return *this;
22567       return CImgList<T>(*this,img,true).get_append(axis,align).move_to(*this);
22568     }
22569 
22570     //! Append two images along specified axis \const.
22571     template<typename t>
22572     CImg<_cimg_Tt> get_append(const CImg<T>& img, const char axis='x', const float align=0) const {
22573       if (is_empty()) return +img;
22574       if (!img) return +*this;
22575       return CImgList<_cimg_Tt>(*this,true).insert(img).get_append(axis,align);
22576     }
22577 
22578     //! Append two images along specified axis \specialization.
22579     CImg<T> get_append(const CImg<T>& img, const char axis='x', const float align=0) const {
22580       if (is_empty()) return +img;
22581       if (!img) return +*this;
22582       return CImgList<T>(*this,img,true).get_append(axis,align);
22583     }
22584 
22585     //@}
22586     //---------------------------------------
22587     //
22588     //! \name Filtering / Transforms
22589     //@{
22590     //---------------------------------------
22591 
22592     //! Correlate image by a mask.
22593     /**
22594        \param mask = the correlation kernel.
22595        \param boundary_conditions = the border condition type (0=zero, 1=dirichlet)
22596        \param is_normalized = enable local normalization.
22597        \note The correlation of the image instance \p *this by the mask \p mask is defined to be :
22598        res(x,y,z) = sum_{i,j,k} (*this)(x+i,y+j,z+k)*mask(i,j,k)
22599     **/
22600     template<typename t>
22601     CImg<T>& correlate(const CImg<t>& mask, const unsigned int boundary_conditions=1, const bool is_normalized=false) {
22602       if (is_empty() || !mask) return *this;
22603       return get_correlate(mask,boundary_conditions,is_normalized).move_to(*this);
22604     }
22605 
22606     //! Correlate image by a mask \newinstance.
22607     template<typename t>
22608     CImg<_cimg_Ttfloat> get_correlate(const CImg<t>& mask, const unsigned int boundary_conditions=1,
22609                                       const bool is_normalized=false) const {
22610       if (is_empty() || !mask) return *this;
22611       typedef _cimg_Ttfloat Ttfloat;
22612       CImg<Ttfloat> res(_width,_height,_depth,cimg::max(_spectrum,mask._spectrum));
22613       if (boundary_conditions && mask._width==mask._height && ((mask._depth==1 && mask._width<=5) || (mask._depth==mask._width && mask._width<=3))) {
22614         // A special optimization is done for 2x2, 3x3, 4x4, 5x5, 2x2x2 and 3x3x3 mask (with boundary_conditions=1)
22615         Ttfloat *ptrd = res._data;
22616         switch (mask._depth) {
22617         case 3 : {
22618           T I[27] = { 0 };
22619           cimg_forC(res,c) {
22620             const CImg<T> _img = get_shared_channel(c%_spectrum);
22621             const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
22622             if (is_normalized) {
22623               const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
22624               cimg_for3x3x3(_img,x,y,z,0,I,T) {
22625                 const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] +
22626                                      I[ 3]*I[ 3] + I[ 4]*I[ 4] + I[ 5]*I[ 5] +
22627                                      I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] +
22628                                      I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] +
22629                                      I[12]*I[12] + I[13]*I[13] + I[14]*I[14] +
22630                                      I[15]*I[15] + I[16]*I[16] + I[17]*I[17] +
22631                                      I[18]*I[18] + I[19]*I[19] + I[20]*I[20] +
22632                                      I[21]*I[21] + I[22]*I[22] + I[23]*I[23] +
22633                                      I[24]*I[24] + I[25]*I[25] + I[26]*I[26]);
22634                 *(ptrd++) = (Ttfloat)(N?(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] +
22635                                          I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] +
22636                                          I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] +
22637                                          I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] +
22638                                          I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] +
22639                                          I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] +
22640                                          I[18]*_mask[18] + I[19]*_mask[19] + I[20]*_mask[20] +
22641                                          I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] +
22642                                          I[24]*_mask[24] + I[25]*_mask[25] + I[26]*_mask[26])/std::sqrt(N):0);
22643               }
22644             } else cimg_for3x3x3(_img,x,y,z,0,I,T)
22645                      *(ptrd++) = (Ttfloat)(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] +
22646                                            I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] +
22647                                            I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] +
22648                                            I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] +
22649                                            I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] +
22650                                            I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] +
22651                                            I[18]*_mask[18] + I[19]*_mask[19] + I[20]*_mask[20] +
22652                                            I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] +
22653                                            I[24]*_mask[24] + I[25]*_mask[25] + I[26]*_mask[26]);
22654           }
22655         } break;
22656         case 2 : {
22657           T I[8] = { 0 };
22658           cimg_forC(res,c) {
22659             const CImg<T> _img = get_shared_channel(c%_spectrum);
22660             const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
22661             if (is_normalized) {
22662               const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
22663               cimg_for2x2x2(_img,x,y,z,0,I,T) {
22664                 const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] +
22665                                      I[2]*I[2] + I[3]*I[3] +
22666                                      I[4]*I[4] + I[5]*I[5] +
22667                                      I[6]*I[6] + I[7]*I[7]);
22668                 *(ptrd++) = (Ttfloat)(N?(I[0]*_mask[0] + I[1]*_mask[1] +
22669                                          I[2]*_mask[2] + I[3]*_mask[3] +
22670                                          I[4]*_mask[4] + I[5]*_mask[5] +
22671                                          I[6]*_mask[6] + I[7]*_mask[7])/std::sqrt(N):0);
22672               }
22673             } else cimg_for2x2x2(_img,x,y,z,0,I,T)
22674                      *(ptrd++) = (Ttfloat)(I[0]*_mask[0] + I[1]*_mask[1] +
22675                                            I[2]*_mask[2] + I[3]*_mask[3] +
22676                                            I[4]*_mask[4] + I[5]*_mask[5] +
22677                                            I[6]*_mask[6] + I[7]*_mask[7]);
22678           }
22679         } break;
22680         default :
22681         case 1 :
22682           switch (mask._width) {
22683           case 6 : {
22684             T I[36] = { 0 };
22685             cimg_forC(res,c) {
22686               const CImg<T> _img = get_shared_channel(c%_spectrum);
22687               const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
22688               if (is_normalized) {
22689                 const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
22690                 cimg_forZ(_img,z) cimg_for6x6(_img,x,y,z,0,I,T) {
22691                   const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] + I[ 5]*I[ 5] +
22692                                        I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] +
22693                                        I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + I[15]*I[15] + I[16]*I[16] + I[17]*I[17] +
22694                                        I[18]*I[18] + I[19]*I[19] + I[20]*I[20] + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] +
22695                                        I[24]*I[24] + I[25]*I[25] + I[26]*I[26] + I[27]*I[27] + I[28]*I[28] + I[29]*I[29] +
22696                                        I[30]*I[30] + I[31]*I[31] + I[32]*I[32] + I[33]*I[33] + I[34]*I[34] + I[35]*I[35]);
22697                   *(ptrd++) = (Ttfloat)(N?(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] +
22698                                            I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] +
22699                                            I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] + I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] +
22700                                            I[18]*_mask[18] + I[19]*_mask[19] + I[20]*_mask[20] + I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] +
22701                                            I[24]*_mask[24] + I[25]*_mask[25] + I[26]*_mask[26] + I[27]*_mask[27] + I[28]*_mask[28] + I[29]*_mask[29] +
22702                                            I[30]*_mask[30] + I[31]*_mask[31] + I[32]*_mask[32] + I[33]*_mask[33] + I[34]*_mask[34] + I[35]*_mask[35])/std::sqrt(N):0);
22703                 }
22704               } else cimg_forZ(_img,z) cimg_for6x6(_img,x,y,z,0,I,T)
22705                        *(ptrd++) = (Ttfloat)(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] +
22706                                              I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] +
22707                                              I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] + I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] +
22708                                              I[18]*_mask[18] + I[19]*_mask[19] + I[20]*_mask[20] + I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] +
22709                                              I[24]*_mask[24] + I[25]*_mask[25] + I[26]*_mask[26] + I[27]*_mask[27] + I[28]*_mask[28] + I[29]*_mask[29] +
22710                                              I[30]*_mask[30] + I[31]*_mask[31] + I[32]*_mask[32] + I[33]*_mask[33] + I[34]*_mask[34] + I[35]*_mask[35]);
22711             }
22712           } break;
22713           case 5 : {
22714             T I[25] = { 0 };
22715             cimg_forC(res,c) {
22716               const CImg<T> _img = get_shared_channel(c%_spectrum);
22717               const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
22718               if (is_normalized) {
22719                 const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
22720                 cimg_forZ(_img,z) cimg_for5x5(_img,x,y,z,0,I,T) {
22721                   const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] +
22722                                        I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] +
22723                                        I[10]*I[10] + I[11]*I[11] + I[12]*I[12] + I[13]*I[13] + I[14]*I[14] +
22724                                        I[15]*I[15] + I[16]*I[16] + I[17]*I[17] + I[18]*I[18] + I[19]*I[19] +
22725                                        I[20]*I[20] + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] + I[24]*I[24]);
22726                   *(ptrd++) = (Ttfloat)(N?(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] +
22727                                            I[ 5]*_mask[ 5] + I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] +
22728                                            I[10]*_mask[10] + I[11]*_mask[11] + I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] +
22729                                            I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] + I[18]*_mask[18] + I[19]*_mask[19] +
22730                                            I[20]*_mask[20] + I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] + I[24]*_mask[24])/std::sqrt(N):0);
22731                 }
22732               } else cimg_forZ(_img,z) cimg_for5x5(_img,x,y,z,0,I,T)
22733                        *(ptrd++) = (Ttfloat)(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] +
22734                                              I[ 5]*_mask[ 5] + I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] +
22735                                              I[10]*_mask[10] + I[11]*_mask[11] + I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] +
22736                                              I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] + I[18]*_mask[18] + I[19]*_mask[19] +
22737                                              I[20]*_mask[20] + I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] + I[24]*_mask[24]);
22738             }
22739           } break;
22740           case 4 : {
22741             T I[16] = { 0 };
22742             cimg_forC(res,c) {
22743               const CImg<T> _img = get_shared_channel(c%_spectrum);
22744               const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
22745               if (is_normalized) {
22746                 const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
22747                 cimg_forZ(_img,z) cimg_for4x4(_img,x,y,z,0,I,T) {
22748                   const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] +
22749                                        I[ 4]*I[ 4] + I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] +
22750                                        I[ 8]*I[ 8] + I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] +
22751                                        I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + I[15]*I[15]);
22752                   *(ptrd++) = (Ttfloat)(N?(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] +
22753                                            I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] + I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] +
22754                                            I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] +
22755                                            I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] + I[15]*_mask[15])/std::sqrt(N):0);
22756                 }
22757               } else cimg_forZ(_img,z) cimg_for4x4(_img,x,y,z,0,I,T)
22758                        *(ptrd++) = (Ttfloat)(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] +
22759                                              I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] + I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] +
22760                                              I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] +
22761                                              I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] + I[15]*_mask[15]);
22762             }
22763           } break;
22764           case 3 : {
22765             T I[9] = { 0 };
22766             cimg_forC(res,c) {
22767               const CImg<T> _img = get_shared_channel(c%_spectrum);
22768               const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
22769               if (is_normalized) {
22770                 const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
22771                 cimg_forZ(_img,z) cimg_for3x3(_img,x,y,z,0,I,T) {
22772                   const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] + I[2]*I[2] +
22773                                        I[3]*I[3] + I[4]*I[4] + I[5]*I[5] +
22774                                        I[6]*I[6] + I[7]*I[7] + I[8]*I[8]);
22775                   *(ptrd++) = (Ttfloat)(N?(I[0]*_mask[0] + I[1]*_mask[1] + I[2]*_mask[2] +
22776                                            I[3]*_mask[3] + I[4]*_mask[4] + I[5]*_mask[5] +
22777                                            I[6]*_mask[6] + I[7]*_mask[7] + I[8]*_mask[8])/std::sqrt(N):0);
22778                 }
22779               } else cimg_forZ(_img,z) cimg_for3x3(_img,x,y,z,0,I,T)
22780                        *(ptrd++) = (Ttfloat)(I[0]*_mask[0] + I[1]*_mask[1] + I[2]*_mask[2] +
22781                                              I[3]*_mask[3] + I[4]*_mask[4] + I[5]*_mask[5] +
22782                                              I[6]*_mask[6] + I[7]*_mask[7] + I[8]*_mask[8]);
22783             }
22784           } break;
22785           case 2 : {
22786             T I[4] = { 0 };
22787             cimg_forC(res,c) {
22788               const CImg<T> _img = get_shared_channel(c%_spectrum);
22789               const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
22790               if (is_normalized) {
22791                 const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
22792                 cimg_forZ(_img,z) cimg_for2x2(_img,x,y,z,0,I,T) {
22793                   const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] +
22794                                        I[2]*I[2] + I[3]*I[3]);
22795                   *(ptrd++) = (Ttfloat)(N?(I[0]*_mask[0] + I[1]*_mask[1] +
22796                                            I[2]*_mask[2] + I[3]*_mask[3])/std::sqrt(N):0);
22797                 }
22798               } else cimg_forZ(_img,z) cimg_for2x2(_img,x,y,z,0,I,T)
22799                        *(ptrd++) = (Ttfloat)(I[0]*_mask[0] + I[1]*_mask[1] +
22800                                              I[2]*_mask[2] + I[3]*_mask[3]);
22801             }
22802           } break;
22803           case 1 :
22804             if (is_normalized) res.fill(1);
22805             else cimg_forC(res,c) {
22806                 const CImg<T> _img = get_shared_channel(c%_spectrum);
22807                 const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
22808                 res.get_shared_channel(c).assign(_img)*=_mask[0];
22809               }
22810             break;
22811           }
22812         }
22813       } else { // Generic version for other masks and borders conditions.
22814         const int
22815           mx2 = mask.width()/2, my2 = mask.height()/2, mz2 = mask.depth()/2,
22816           mx1 = mx2 - 1 + (mask.width()%2), my1 = my2 - 1 + (mask.height()%2), mz1 = mz2 - 1 + (mask.depth()%2),
22817           mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
22818         cimg_forC(res,c) {
22819           const CImg<T> _img = get_shared_channel(c%_spectrum);
22820           const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
22821           if (is_normalized) { // Normalized correlation.
22822             const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
22823             for (int z = mz1; z<mze; ++z)
22824               for (int y = my1; y<mye; ++y)
22825                 for (int x = mx1; x<mxe; ++x) {
22826                   Ttfloat val = 0, N = 0;
22827                   for (int zm = -mz1; zm<=mz2; ++zm)
22828                     for (int ym = -my1; ym<=my2; ++ym)
22829                       for (int xm = -mx1; xm<=mx2; ++xm) {
22830                         const Ttfloat _val = (Ttfloat)_img(x+xm,y+ym,z+zm);
22831                         val+=_val*_mask(mx1+xm,my1+ym,mz1+zm);
22832                         N+=_val*_val;
22833                       }
22834                   N*=M;
22835                   res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0);
22836                 }
22837             if (boundary_conditions)
22838               cimg_forYZ(res,y,z)
22839                 for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
22840                   Ttfloat val = 0, N = 0;
22841                   for (int zm = -mz1; zm<=mz2; ++zm)
22842                     for (int ym = -my1; ym<=my2; ++ym)
22843                       for (int xm = -mx1; xm<=mx2; ++xm) {
22844                         const Ttfloat _val = (Ttfloat)_img._atXYZ(x+xm,y+ym,z+zm);
22845                         val+=_val*_mask(mx1+xm,my1+ym,mz1+zm);
22846                         N+=_val*_val;
22847                       }
22848                   N*=M;
22849                   res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0);
22850                 }
22851             else
22852               cimg_forYZ(res,y,z)
22853                 for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
22854                   Ttfloat val = 0, N = 0;
22855                   for (int zm = -mz1; zm<=mz2; ++zm)
22856                     for (int ym = -my1; ym<=my2; ++ym)
22857                       for (int xm = -mx1; xm<=mx2; ++xm) {
22858                         const Ttfloat _val = (Ttfloat)_img.atXYZ(x+xm,y+ym,z+zm,0,0);
22859                         val+=_val*_mask(mx1+xm,my1+ym,mz1+zm);
22860                         N+=_val*_val;
22861                       }
22862                   N*=M;
22863                   res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0);
22864                 }
22865           } else { // Classical correlation.
22866             for (int z = mz1; z<mze; ++z)
22867               for (int y = my1; y<mye; ++y)
22868                 for (int x = mx1; x<mxe; ++x) {
22869                   Ttfloat val = 0;
22870                   for (int zm = -mz1; zm<=mz2; ++zm)
22871                     for (int ym = -my1; ym<=my2; ++ym)
22872                       for (int xm = -mx1; xm<=mx2; ++xm)
22873                         val+=_img(x+xm,y+ym,z+zm)*_mask(mx1+xm,my1+ym,mz1+zm);
22874                   res(x,y,z,c) = (Ttfloat)val;
22875                 }
22876             if (boundary_conditions)
22877               cimg_forYZ(res,y,z)
22878                 for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
22879                   Ttfloat val = 0;
22880                   for (int zm = -mz1; zm<=mz2; ++zm)
22881                     for (int ym = -my1; ym<=my2; ++ym)
22882                       for (int xm = -mx1; xm<=mx2; ++xm)
22883                         val+=_img._atXYZ(x+xm,y+ym,z+zm)*_mask(mx1+xm,my1+ym,mz1+zm);
22884                   res(x,y,z,c) = (Ttfloat)val;
22885                 }
22886             else
22887               cimg_forYZ(res,y,z)
22888                 for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
22889                   Ttfloat val = 0;
22890                   for (int zm = -mz1; zm<=mz2; ++zm)
22891                     for (int ym = -my1; ym<=my2; ++ym)
22892                       for (int xm = -mx1; xm<=mx2; ++xm)
22893                         val+=_img.atXYZ(x+xm,y+ym,z+zm,0,0)*_mask(mx1+xm,my1+ym,mz1+zm);
22894                   res(x,y,z,c) = (Ttfloat)val;
22895                 }
22896           }
22897         }
22898       }
22899       return res;
22900     }
22901 
22902     //! Convolve image by a mask.
22903     /**
22904        \param mask = the correlation kernel.
22905        \param boundary_conditions = the border condition type (0=zero, 1=dirichlet)
22906        \param is_normalized = enable local normalization.
22907        \note The result \p res of the convolution of an image \p img by a mask \p mask is defined to be :
22908        res(x,y,z) = sum_{i,j,k} img(x-i,y-j,z-k)*mask(i,j,k)
22909     **/
22910     template<typename t>
22911     CImg<T>& convolve(const CImg<t>& mask, const unsigned int boundary_conditions=1, const bool is_normalized=false) {
22912       if (is_empty() || !mask) return *this;
22913       return get_convolve(mask,boundary_conditions,is_normalized).move_to(*this);
22914     }
22915 
22916     //! Convolve image by a mask \newinstance.
22917     template<typename t>
22918     CImg<_cimg_Ttfloat> get_convolve(const CImg<t>& mask, const unsigned int boundary_conditions=1,
22919                                      const bool is_normalized=false) const {
22920       if (is_empty() || !mask) return *this;
22921       return get_correlate(CImg<t>(mask._data,mask.size(),1,1,1,true).get_mirror('x').resize(mask,-1),boundary_conditions,is_normalized);
22922     }
22923 
22924     //! Erode image by a structuring element.
22925     /**
22926        \param mask Structuring element.
22927        \param boundary_conditions Boundary conditions.
22928        \param is_normalized Tells if the erosion is locally normalized.
22929     **/
22930     template<typename t>
22931     CImg<T>& erode(const CImg<t>& mask, const unsigned int boundary_conditions=1, const bool is_normalized=false) {
22932       if (is_empty() || !mask) return *this;
22933       return get_erode(mask,boundary_conditions,is_normalized).move_to(*this);
22934     }
22935 
22936     //! Erode image by a structuring element \newinstance.
22937     template<typename t>
22938     CImg<_cimg_Tt> get_erode(const CImg<t>& mask, const unsigned int boundary_conditions=1,
22939                              const bool is_normalized=false) const {
22940       if (is_empty() || !mask) return *this;
22941       typedef _cimg_Tt Tt;
22942       CImg<Tt> res(_width,_height,_depth,cimg::max(_spectrum,mask._spectrum));
22943       const int
22944         mx2 = mask.width()/2, my2 = mask.height()/2, mz2 = mask.depth()/2,
22945         mx1 = mx2 - 1 + (mask.width()%2), my1 = my2 - 1 + (mask.height()%2), mz1 = mz2 - 1 + (mask.depth()%2),
22946         mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
22947       cimg_forC(*this,c) {
22948         const CImg<T> _img = get_shared_channel(c%_spectrum);
22949         const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
22950         if (is_normalized) { // Normalized erosion.
22951           for (int z = mz1; z<mze; ++z)
22952             for (int y = my1; y<mye; ++y)
22953               for (int x = mx1; x<mxe; ++x) {
22954                 Tt min_val = cimg::type<Tt>::max();
22955                 for (int zm = -mz1; zm<=mz2; ++zm)
22956                   for (int ym = -my1; ym<=my2; ++ym)
22957                     for (int xm = -mx1; xm<=mx2; ++xm) {
22958                       const t mval = _mask(mx1+xm,my1+ym,mz1+zm);
22959                       const Tt cval = (Tt)(_img(x+xm,y+ym,z+zm) + mval);
22960                       if (mval && cval<min_val) min_val = cval;
22961                     }
22962                 res(x,y,z,c) = min_val;
22963               }
22964           if (boundary_conditions)
22965             cimg_forYZ(res,y,z)
22966               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
22967                 Tt min_val = cimg::type<Tt>::max();
22968                 for (int zm = -mz1; zm<=mz2; ++zm)
22969                   for (int ym = -my1; ym<=my2; ++ym)
22970                     for (int xm = -mx1; xm<=mx2; ++xm) {
22971                       const t mval = _mask(mx1+xm,my1+ym,mz1+zm);
22972                       const Tt cval = (Tt)(_img._atXYZ(x+xm,y+ym,z+zm) + mval);
22973                       if (mval && cval<min_val) min_val = cval;
22974                     }
22975                 res(x,y,z,c) = min_val;
22976               }
22977           else
22978             cimg_forYZ(res,y,z)
22979               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
22980                 Tt min_val = cimg::type<Tt>::max();
22981                 for (int zm = -mz1; zm<=mz2; ++zm)
22982                   for (int ym = -my1; ym<=my2; ++ym)
22983                     for (int xm = -mx1; xm<=mx2; ++xm) {
22984                       const t mval = _mask(mx1+xm,my1+ym,mz1+zm);
22985                       const Tt cval = (Tt)(_img.atXYZ(x+xm,y+ym,z+zm,0,0) + mval);
22986                       if (mval && cval<min_val) min_val = cval;
22987                     }
22988                 res(x,y,z,c) = min_val;
22989               }
22990         } else { // Classical erosion.
22991           for (int z = mz1; z<mze; ++z)
22992             for (int y = my1; y<mye; ++y)
22993               for (int x = mx1; x<mxe; ++x) {
22994                 Tt min_val = cimg::type<Tt>::max();
22995                 for (int zm = -mz1; zm<=mz2; ++zm)
22996                   for (int ym = -my1; ym<=my2; ++ym)
22997                     for (int xm = -mx1; xm<=mx2; ++xm) {
22998                       const Tt cval = (Tt)_img(x+xm,y+ym,z+zm);
22999                       if (_mask(mx1+xm,my1+ym,mz1+zm) && cval<min_val) min_val = cval;
23000                     }
23001                 res(x,y,z,c) = min_val;
23002               }
23003           if (boundary_conditions)
23004             cimg_forYZ(res,y,z)
23005               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
23006                 Tt min_val = cimg::type<Tt>::max();
23007                 for (int zm = -mz1; zm<=mz2; ++zm)
23008                   for (int ym = -my1; ym<=my2; ++ym)
23009                     for (int xm = -mx1; xm<=mx2; ++xm) {
23010                       const T cval = (Tt)_img._atXYZ(x+xm,y+ym,z+zm);
23011                       if (_mask(mx1+xm,my1+ym,mz1+zm) && cval<min_val) min_val = cval;
23012                     }
23013                 res(x,y,z,c) = min_val;
23014               }
23015           else
23016             cimg_forYZ(res,y,z)
23017               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
23018                 Tt min_val = cimg::type<Tt>::max();
23019                 for (int zm = -mz1; zm<=mz2; ++zm)
23020                   for (int ym = -my1; ym<=my2; ++ym)
23021                     for (int xm = -mx1; xm<=mx2; ++xm) {
23022                       const T cval = (Tt)_img.atXYZ(x+xm,y+ym,z+zm,0,0);
23023                       if (_mask(mx1+xm,my1+ym,mz1+zm) && cval<min_val) min_val = cval;
23024                     }
23025                 res(x,y,z,c) = min_val;
23026               }
23027         }
23028       }
23029       return res;
23030     }
23031 
23032     //! Erode image by a rectangular structuring element of specified size.
23033     /**
23034        \param sx Width of the structuring element.
23035        \param sy Height of the structuring element.
23036        \param sz Depth of the structuring element.
23037     **/
23038     CImg<T>& erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
23039       if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this;
23040       if (sx>1 && _width>1) { // Along X-axis.
23041         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;
23042         CImg<T> buf(L);
23043         T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
23044         cimg_forYZC(*this,y,z,c) {
23045           const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
23046           ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true;
23047           for (int p = s2-1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }} *(ptrd++) = cur;
23048           for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; } *(ptrd++) = cur; }
23049           for (int p = L - s - 1; p>0; --p) {
23050             const T val = *ptrs; ptrs+=off;
23051             if (is_first) {
23052               const T *nptrs = ptrs - off; cur = val;
23053               for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
23054               nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
23055             } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
23056             *(ptrd++) = cur;
23057           }
23058           ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
23059           for (int p = s1; p>0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val<cur) cur = val; } *(ptrd--) = cur;
23060           for (int p = s2-1; p>0 && ptrd>=ptrdb; --p) { const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur; }
23061           T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
23062         }
23063       }
23064 
23065       if (sy>1 && _height>1) { // Along Y-axis.
23066         const int L = height(), off = width(), s = (int)sy, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2;
23067         CImg<T> buf(L);
23068         T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
23069         cimg_forXZC(*this,x,z,c) {
23070           const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
23071           ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true;
23072           for (int p = s2-1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }} *(ptrd++) = cur;
23073           for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; } *(ptrd++) = cur; }
23074           for (int p = L - s - 1; p>0; --p) {
23075             const T val = *ptrs; ptrs+=off;
23076             if (is_first) {
23077               const T *nptrs = ptrs - off; cur = val;
23078               for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
23079               nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
23080             } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
23081             *(ptrd++) = cur;
23082           }
23083           ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
23084           for (int p = s1; p>0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val<cur) cur = val; } *(ptrd--) = cur;
23085           for (int p = s2-1; p>0 && ptrd>=ptrdb; --p) { const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur; }
23086           T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
23087         }
23088       }
23089 
23090       if (sz>1 && _depth>1) { // Along Z-axis.
23091         const int L = depth(), off = width()*height(), s = (int)sz, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2;
23092         CImg<T> buf(L);
23093         T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
23094         cimg_forXYC(*this,x,y,c) {
23095           const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
23096           ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true;
23097           for (int p = s2-1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }} *(ptrd++) = cur;
23098           for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; } *(ptrd++) = cur; }
23099           for (int p = L - s - 1; p>0; --p) {
23100             const T val = *ptrs; ptrs+=off;
23101             if (is_first) {
23102               const T *nptrs = ptrs - off; cur = val;
23103               for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
23104               nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
23105             } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
23106             *(ptrd++) = cur;
23107           }
23108           ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
23109           for (int p = s1; p>0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val<cur) cur = val; } *(ptrd--) = cur;
23110           for (int p = s2-1; p>0 && ptrd>=ptrdb; --p) { const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur; }
23111           T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
23112         }
23113       }
23114       return *this;
23115     }
23116 
23117     //! Erode image by a rectangular structuring element of specified size \newinstance.
23118     CImg<T> get_erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
23119       return (+*this).erode(sx,sy,sz);
23120     }
23121 
23122     //! Erode the image by a square structuring element of specified size.
23123     /**
23124        \param s Size of the structuring element.
23125     **/
23126     CImg<T>& erode(const unsigned int s) {
23127       return erode(s,s,s);
23128     }
23129 
23130     //! Erode the image by a square structuring element of specified size \newinstance.
23131     CImg<T> get_erode(const unsigned int s) const {
23132       return (+*this).erode(s);
23133     }
23134 
23135     //! Dilate image by a structuring element.
23136     /**
23137        \param mask Structuring element.
23138        \param boundary_conditions Boundary conditions.
23139        \param is_normalized Tells if the erosion is locally normalized.
23140     **/
23141     template<typename t>
23142     CImg<T>& dilate(const CImg<t>& mask, const unsigned int boundary_conditions=1, const bool is_normalized=false) {
23143       if (is_empty() || !mask) return *this;
23144       return get_dilate(mask,boundary_conditions,is_normalized).move_to(*this);
23145     }
23146 
23147     //! Dilate image by a structuring element \newinstance.
23148     template<typename t>
23149     CImg<_cimg_Tt> get_dilate(const CImg<t>& mask, const unsigned int boundary_conditions=1,
23150                               const bool is_normalized=false) const {
23151       if (is_empty() || !mask) return *this;
23152       typedef _cimg_Tt Tt;
23153       CImg<Tt> res(_width,_height,_depth,_spectrum);
23154       const int
23155         mx2 = mask.width()/2, my2 = mask.height()/2, mz2 = mask.depth()/2,
23156         mx1 = mx2 - 1 + (mask.width()%2), my1 = my2 - 1 + (mask.height()%2), mz1 = mz2 - 1 + (mask.depth()%2),
23157         mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
23158       cimg_forC(*this,c) {
23159         const CImg<T> _img = get_shared_channel(c%_spectrum);
23160         const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
23161         if (is_normalized) { // Normalized dilation.
23162           for (int z = mz1; z<mze; ++z)
23163             for (int y = my1; y<mye; ++y)
23164               for (int x = mx1; x<mxe; ++x) {
23165                 Tt max_val = cimg::type<Tt>::min();
23166                 for (int zm = -mz1; zm<=mz2; ++zm)
23167                   for (int ym = -my1; ym<=my2; ++ym)
23168                     for (int xm = -mx1; xm<=mx2; ++xm) {
23169                       const t mval = _mask(mx1+xm,my1+ym,mz1+zm);
23170                       const Tt cval = (Tt)(_img(x+xm,y+ym,z+zm) - mval);
23171                       if (mval && cval>max_val) max_val = cval;
23172                     }
23173                 res(x,y,z,c) = max_val;
23174               }
23175           if (boundary_conditions)
23176             cimg_forYZ(res,y,z)
23177               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
23178                 Tt max_val = cimg::type<Tt>::min();
23179                 for (int zm = -mz1; zm<=mz2; ++zm)
23180                   for (int ym = -my1; ym<=my2; ++ym)
23181                     for (int xm = -mx1; xm<=mx2; ++xm) {
23182                       const t mval = _mask(mx1+xm,my1+ym,mz1+zm);
23183                       const Tt cval = (Tt)(_img._atXYZ(x+xm,y+ym,z+zm) - mval);
23184                       if (mval && cval>max_val) max_val = cval;
23185                     }
23186                 res(x,y,z,c) = max_val;
23187               }
23188           else
23189             cimg_forYZ(*this,y,z)
23190               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
23191                 Tt max_val = cimg::type<Tt>::min();
23192                 for (int zm = -mz1; zm<=mz2; ++zm)
23193                   for (int ym = -my1; ym<=my2; ++ym)
23194                     for (int xm = -mx1; xm<=mx2; ++xm) {
23195                       const t mval = _mask(mx1+xm,my1+ym,mz1+zm);
23196                       const Tt cval = (Tt)(_img.atXYZ(x+xm,y+ym,z+zm,0,0) - mval);
23197                       if (mval && cval>max_val) max_val = cval;
23198                     }
23199                 res(x,y,z,c) = max_val;
23200               }
23201         } else { // Classical dilation.
23202           for (int z = mz1; z<mze; ++z)
23203             for (int y = my1; y<mye; ++y)
23204               for (int x = mx1; x<mxe; ++x) {
23205                 Tt max_val = cimg::type<Tt>::min();
23206                 for (int zm = -mz1; zm<=mz2; ++zm)
23207                   for (int ym = -my1; ym<=my2; ++ym)
23208                     for (int xm = -mx1; xm<=mx2; ++xm) {
23209                       const Tt cval = (Tt)_img(x+xm,y+ym,z+zm);
23210                       if (_mask(mx1+xm,my1+ym,mz1+zm) && cval>max_val) max_val = cval;
23211                     }
23212                 res(x,y,z,c) = max_val;
23213               }
23214           if (boundary_conditions)
23215             cimg_forYZ(res,y,z)
23216               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
23217                 Tt max_val = cimg::type<Tt>::min();
23218                 for (int zm = -mz1; zm<=mz2; ++zm)
23219                   for (int ym = -my1; ym<=my2; ++ym)
23220                     for (int xm = -mx1; xm<=mx2; ++xm) {
23221                       const T cval = (Tt)_img._atXYZ(x+xm,y+ym,z+zm);
23222                       if (_mask(mx1+xm,my1+ym,mz1+zm) && cval>max_val) max_val = cval;
23223                     }
23224                 res(x,y,z,c) = max_val;
23225               }
23226           else
23227             cimg_forYZ(res,y,z)
23228               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
23229                 Tt max_val = cimg::type<Tt>::min();
23230                 for (int zm = -mz1; zm<=mz2; ++zm)
23231                   for (int ym = -my1; ym<=my2; ++ym)
23232                     for (int xm = -mx1; xm<=mx2; ++xm) {
23233                       const T cval = (Tt)_img.atXYZ(x+xm,y+ym,z+zm,0,0);
23234                       if (_mask(mx1+xm,my1+ym,mz1+zm) && cval>max_val) max_val = cval;
23235                     }
23236                 res(x,y,z,c) = max_val;
23237               }
23238         }
23239       }
23240       return res;
23241     }
23242 
23243     //! Dilate image by a rectangular structuring element of specified size.
23244     /**
23245        \param sx Width of the structuring element.
23246        \param sy Height of the structuring element.
23247        \param sz Depth of the structuring element.
23248     **/
23249     CImg<T>& dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
23250       if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this;
23251       if (sx>1 && _width>1) { // Along X-axis.
23252         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;
23253         CImg<T> buf(L);
23254         T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
23255         cimg_forYZC(*this,y,z,c) {
23256           const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
23257           ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true;
23258           for (int p = s2-1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }} *(ptrd++) = cur;
23259           for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; } *(ptrd++) = cur; }
23260           for (int p = L - s - 1; p>0; --p) {
23261             const T val = *ptrs; ptrs+=off;
23262             if (is_first) {
23263               const T *nptrs = ptrs - off; cur = val;
23264               for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
23265               nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
23266             } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
23267             *(ptrd++) = cur;
23268           }
23269           ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
23270           for (int p = s1; p>0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val>cur) cur = val; } *(ptrd--) = cur;
23271           for (int p = s2-1; p>0 && ptrd>=ptrdb; --p) { const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur; }
23272           T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
23273         }
23274       }
23275 
23276       if (sy>1 && _height>1) { // Along Y-axis.
23277         const int L = height(), off = width(), s = (int)sy, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2;
23278         CImg<T> buf(L);
23279         T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
23280         cimg_forXZC(*this,x,z,c) {
23281           const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
23282           ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true;
23283           for (int p = s2-1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }} *(ptrd++) = cur;
23284           for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; } *(ptrd++) = cur; }
23285           for (int p = L - s - 1; p>0; --p) {
23286             const T val = *ptrs; ptrs+=off;
23287             if (is_first) {
23288               const T *nptrs = ptrs - off; cur = val;
23289               for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
23290               nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
23291             } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
23292             *(ptrd++) = cur;
23293           }
23294           ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
23295           for (int p = s1; p>0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val>cur) cur = val; } *(ptrd--) = cur;
23296           for (int p = s2-1; p>0 && ptrd>=ptrdb; --p) { const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur; }
23297           T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
23298         }
23299       }
23300 
23301       if (sz>1 && _depth>1) { // Along Z-axis.
23302         const int L = depth(), off = width()*height(), s = (int)sz, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2;
23303         CImg<T> buf(L);
23304         T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
23305         cimg_forXYC(*this,x,y,c) {
23306           const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
23307           ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true;
23308           for (int p = s2-1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }} *(ptrd++) = cur;
23309           for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; } *(ptrd++) = cur; }
23310           for (int p = L - s - 1; p>0; --p) {
23311             const T val = *ptrs; ptrs+=off;
23312             if (is_first) {
23313               const T *nptrs = ptrs - off; cur = val;
23314               for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
23315               nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
23316             } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
23317             *(ptrd++) = cur;
23318           }
23319           ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
23320           for (int p = s1; p>0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val>cur) cur = val; } *(ptrd--) = cur;
23321           for (int p = s2-1; p>0 && ptrd>=ptrdb; --p) { const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur; }
23322           T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
23323         }
23324       }
23325       return *this;
23326     }
23327 
23328     //! Dilate image by a rectangular structuring element of specified size \newinstance.
23329     CImg<T> get_dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
23330       return (+*this).dilate(sx,sy,sz);
23331     }
23332 
23333     //! Dilate image by a square structuring element of specified size.
23334     /**
23335        \param s Size of the structuring element.
23336     **/
23337     CImg<T>& dilate(const unsigned int s) {
23338       return dilate(s,s,s);
23339     }
23340 
23341     //! Dilate image by a square structuring element of specified size \newinstance.
23342     CImg<T> get_dilate(const unsigned int s) const {
23343       return (+*this).dilate(s);
23344     }
23345 
23346     //! Compute watershed transform.
23347     /**
23348        \param priority Priority map.
23349        \param fill_lines Tells if watershed lines must be filled or not.
23350        \note Non-zero values of the instance instance are propagated to zero-valued ones according to specified the priority map.
23351     **/
23352     template<typename t>
23353     CImg<T>& watershed(const CImg<t>& priority, const bool fill_lines=true) {
23354       if (is_empty()) return *this;
23355       if (!is_sameXYZ(priority))
23356         throw CImgArgumentException(_cimg_instance
23357                                     "watershed() : image instance and specified priority (%u,%u,%u,%u,%p) have different dimensions.",
23358                                     cimg_instance,priority._width,priority._height,priority._depth,priority._spectrum,priority._data);
23359       if (_spectrum!=1) { cimg_forC(*this,c) get_shared_channel(c).watershed(priority.get_shared_channel(c%priority._spectrum),fill_lines); return *this; }
23360 
23361       CImg<boolT> in_queue(_width,_height,_depth,1,0);
23362       CImg<typename cimg::superset2<T,t,int>::type> Q;
23363       unsigned int sizeQ = 0;
23364 
23365       // Find seed points and insert them in priority queue.
23366       const T *ptrs = _data;
23367       cimg_forXYZ(*this,x,y,z) if (*(ptrs++)) {
23368         if (x-1>=0 && !(*this)(x-1,y,z))       Q._priority_queue_insert(in_queue,sizeQ,priority(x-1,y,z),x-1,y,z);
23369         if (x+1<width() && !(*this)(x+1,y,z))  Q._priority_queue_insert(in_queue,sizeQ,priority(x+1,y,z),x+1,y,z);
23370         if (y-1>=0 && !(*this)(x,y-1,z))       Q._priority_queue_insert(in_queue,sizeQ,priority(x,y-1,z),x,y-1,z);
23371         if (y+1<height() && !(*this)(x,y+1,z)) Q._priority_queue_insert(in_queue,sizeQ,priority(x,y+1,z),x,y+1,z);
23372         if (z-1>=0 && !(*this)(x,y,z-1))       Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z-1),x,y,z-1);
23373         if (z+1<depth() && !(*this)(x,y,z+1))  Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z+1),x,y,z+1);
23374       }
23375 
23376       // Start watershed computation.
23377       while (sizeQ) {
23378 
23379         // Get and remove point with maximal priority from the queue.
23380         const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
23381         Q._priority_queue_remove(sizeQ);
23382 
23383         // Check labels of the neighbors.
23384         bool is_same_label = true;
23385         unsigned int label = 0;
23386         if (x-1>=0) {
23387           if ((*this)(x-1,y,z)) { if (!label) label = (unsigned int)(*this)(x-1,y,z); else if (label!=(*this)(x-1,y,z)) is_same_label = false; }
23388           else Q._priority_queue_insert(in_queue,sizeQ,priority(x-1,y,z),x-1,y,z);
23389         }
23390         if (x+1<width()) {
23391           if ((*this)(x+1,y,z)) { if (!label) label = (unsigned int)(*this)(x+1,y,z); else if (label!=(*this)(x+1,y,z)) is_same_label = false; }
23392           else Q._priority_queue_insert(in_queue,sizeQ,priority(x+1,y,z),x+1,y,z);
23393         }
23394         if (y-1>=0) {
23395           if ((*this)(x,y-1,z)) { if (!label) label = (unsigned int)(*this)(x,y-1,z); else if (label!=(*this)(x,y-1,z)) is_same_label = false; }
23396           else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y-1,z),x,y-1,z);
23397         }
23398         if (y+1<height()) {
23399           if ((*this)(x,y+1,z)) { if (!label) label = (unsigned int)(*this)(x,y+1,z); else if (label!=(*this)(x,y+1,z)) is_same_label = false; }
23400           else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y+1,z),x,y+1,z);
23401         }
23402         if (z-1>=0) {
23403           if ((*this)(x,y,z-1)) { if (!label) label = (unsigned int)(*this)(x,y,z-1); else if (label!=(*this)(x,y,z-1)) is_same_label = false; }
23404           else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z-1),x,y,z-1);
23405         }
23406         if (z+1<depth()) {
23407           if ((*this)(x,y,z+1)) { if (!label) label = (unsigned int)(*this)(x,y,z+1); else if (label!=(*this)(x,y,z+1)) is_same_label = false; }
23408           else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z+1),x,y,z+1);
23409         }
23410         if (is_same_label) (*this)(x,y,z) = label;
23411       }
23412 
23413       // Fill lines.
23414       if (fill_lines) {
23415 
23416         // Sort all non-labeled pixels with labeled neighbors.
23417         in_queue = false;
23418         const T *ptrs = _data;
23419         cimg_forXYZ(*this,x,y,z) if (!*(ptrs++) &&
23420                                      ((x-1>=0 && (*this)(x-1,y,z)) || (x+1<width() && (*this)(x+1,y,z)) ||
23421                                       (y-1>=0 && (*this)(x,y-1,z)) || (y+1<height() && (*this)(x,y+1,z)) ||
23422                                       (z-1>=0 && (*this)(x,y,z-1)) || (z+1>depth() && (*this)(x,y,z+1))))
23423           Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z),x,y,z);
23424 
23425         // Start line filling process.
23426         while (sizeQ) {
23427           const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
23428           Q._priority_queue_remove(sizeQ);
23429           t pmax = cimg::type<t>::min();
23430           int xmax = 0, ymax = 0, zmax = 0;
23431           if (x-1>=0) {
23432             if ((*this)(x-1,y,z)) { if (priority(x-1,y,z)>pmax) { pmax = priority(x-1,y,z); xmax = x-1; ymax = y; zmax = z; }}
23433             else Q._priority_queue_insert(in_queue,sizeQ,priority(x-1,y,z),x-1,y,z);
23434           }
23435           if (x+1<width()) {
23436             if ((*this)(x+1,y,z)) { if (priority(x+1,y,z)>pmax) { pmax = priority(x+1,y,z); xmax = x+1; ymax = y; zmax = z; }}
23437             else Q._priority_queue_insert(in_queue,sizeQ,priority(x+1,y,z),x+1,y,z);
23438           }
23439           if (y-1>=0) {
23440             if ((*this)(x,y-1,z)) { if (priority(x,y-1,z)>pmax) { pmax = priority(x,y-1,z); xmax = x; ymax = y-1; zmax = z; }}
23441             else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y-1,z),x,y-1,z);
23442           }
23443           if (y+1<height()) {
23444             if ((*this)(x,y+1,z)) { if (priority(x,y+1,z)>pmax) { pmax = priority(x,y+1,z); xmax = x; ymax = y+1; zmax = z; }}
23445             else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y+1,z),x,y+1,z);
23446           }
23447           if (z-1>=0) {
23448             if ((*this)(x,y,z-1)) { if (priority(x,y,z-1)>pmax) { pmax = priority(x,y,z-1); xmax = x; ymax = y; zmax = z-1; }}
23449             else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z-1),x,y,z-1);
23450           }
23451           if (z+1<depth()) {
23452             if ((*this)(x,y,z+1)) { if (priority(x,y,z+1)>pmax) { pmax = priority(x,y,z+1); xmax = x; ymax = y; zmax = z+1; }}
23453             else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z+1),x,y,z+1);
23454           }
23455           (*this)(x,y,z) = (*this)(xmax,ymax,zmax);
23456         }
23457       }
23458       return *this;
23459     }
23460 
23461     //! Compute watershed transform \newinstance.
23462     template<typename t>
23463     CImg<T> get_watershed(const CImg<t>& priority, const bool fill_lines=true) const {
23464       return (+*this).watershed(priority,fill_lines);
23465     }
23466 
23467     // [internal] Insert/Remove items in priority queue, for watershed/distance transforms.
23468     template<typename t>
23469     bool _priority_queue_insert(CImg<boolT>& in_queue, unsigned int& siz, const t value, const unsigned int x, const unsigned int y, const unsigned int z) {
23470       if (in_queue(x,y,z)) return false;
23471       in_queue(x,y,z) = true;
23472       if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); }
23473       (*this)(siz-1,0) = (T)value; (*this)(siz-1,1) = (T)x; (*this)(siz-1,2) = (T)y; (*this)(siz-1,3) = (T)z;
23474       for (unsigned int pos = siz - 1, par = 0; pos && value>(*this)(par=(pos+1)/2-1,0); pos = par) {
23475         cimg::swap((*this)(pos,0),(*this)(par,0)); cimg::swap((*this)(pos,1),(*this)(par,1));
23476         cimg::swap((*this)(pos,2),(*this)(par,2)); cimg::swap((*this)(pos,3),(*this)(par,3));
23477       }
23478       return true;
23479     }
23480 
23481     CImg<T>& _priority_queue_remove(unsigned int& siz) {
23482       (*this)(0,0) = (*this)(--siz,0); (*this)(0,1) = (*this)(siz,1); (*this)(0,2) = (*this)(siz,2); (*this)(0,3) = (*this)(siz,3);
23483       const float value = (*this)(0,0);
23484       for (unsigned int pos = 0, left = 0, right = 0;
23485            ((right=2*(pos+1),(left=right-1))<siz && value<(*this)(left,0)) || (right<siz && value<(*this)(right,0));) {
23486         if (right<siz) {
23487           if ((*this)(left,0)>(*this)(right,0)) {
23488             cimg::swap((*this)(pos,0),(*this)(left,0)); cimg::swap((*this)(pos,1),(*this)(left,1));
23489             cimg::swap((*this)(pos,2),(*this)(left,2)); cimg::swap((*this)(pos,3),(*this)(left,3));
23490             pos = left;
23491           } else {
23492             cimg::swap((*this)(pos,0),(*this)(right,0)); cimg::swap((*this)(pos,1),(*this)(right,1));
23493             cimg::swap((*this)(pos,2),(*this)(right,2)); cimg::swap((*this)(pos,3),(*this)(right,3));
23494             pos = right;
23495           }
23496         } else {
23497           cimg::swap((*this)(pos,0),(*this)(left,0)); cimg::swap((*this)(pos,1),(*this)(left,1));
23498           cimg::swap((*this)(pos,2),(*this)(left,2)); cimg::swap((*this)(pos,3),(*this)(left,3));
23499           pos = left;
23500         }
23501       }
23502       return *this;
23503     }
23504 
23505     //! Apply recursive Deriche filter.
23506     /**
23507        \param sigma Standard deviation of the filter.
23508        \param order Order of the filter. Can be <tt>{ 0=smooth-filter | 1=1st-derivative | 2=2nd-derivative }</tt>.
23509        \param axis Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
23510        \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
23511     **/
23512     CImg<T>& deriche(const float sigma, const int order=0, const char axis='x', const bool boundary_conditions=true) {
23513 #define _cimg_deriche_apply \
23514   Tfloat *ptrY = Y._data, yb = 0, yp = 0; \
23515   T xp = (T)0; \
23516   if (boundary_conditions) { xp = *ptrX; yb = yp = (Tfloat)(coefp*xp); } \
23517   for (int m = 0; m<N; ++m) { \
23518     const T xc = *ptrX; ptrX+=off; \
23519     const Tfloat yc = *(ptrY++) = (Tfloat)(a0*xc + a1*xp - b1*yp - b2*yb); \
23520     xp = xc; yb = yp; yp = yc; \
23521   } \
23522   T xn = (T)0, xa = (T)0; \
23523   Tfloat yn = 0, ya = 0; \
23524   if (boundary_conditions) { xn = xa = *(ptrX-off); yn = ya = (Tfloat)coefn*xn; } \
23525   for (int n = N-1; n>=0; --n) { \
23526     const T xc = *(ptrX-=off); \
23527     const Tfloat yc = (Tfloat)(a2*xn + a3*xa - b1*yn - b2*ya); \
23528     xa = xn; xn = xc; ya = yn; yn = yc; \
23529     *ptrX = (T)(*(--ptrY)+yc); \
23530   }
23531       const char naxis = cimg::uncase(axis);
23532       const float nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100;
23533       if (is_empty() || (nsigma<0.1 && !order)) return *this;
23534       const float
23535         nnsigma = nsigma<0.1f?0.1f:nsigma,
23536         alpha = 1.695f/nnsigma,
23537         ema = (float)std::exp(-alpha),
23538         ema2 = (float)std::exp(-2*alpha),
23539         b1 = -2*ema,
23540         b2 = ema2;
23541       float a0 = 0, a1 = 0, a2 = 0, a3 = 0, coefp = 0, coefn = 0;
23542       switch (order) {
23543       case 0 : {
23544         const float k = (1-ema)*(1-ema)/(1+2*alpha*ema-ema2);
23545         a0 = k;
23546         a1 = k*(alpha-1)*ema;
23547         a2 = k*(alpha+1)*ema;
23548         a3 = -k*ema2;
23549       } break;
23550       case 1 : {
23551         const float k = -(1-ema)*(1-ema)*(1-ema)/(2*(ema+1)*ema);
23552         a0 = a3 = 0;
23553         a1 = k*ema;
23554         a2 = -a1;
23555       } break;
23556       case 2 : {
23557         const float
23558           ea = (float)std::exp(-alpha),
23559           k = -(ema2-1)/(2*alpha*ema),
23560           kn = (-2*(-1+3*ea-3*ea*ea+ea*ea*ea)/(3*ea+1+3*ea*ea+ea*ea*ea));
23561         a0 = kn;
23562         a1 = -kn*(1+k*alpha)*ema;
23563         a2 = kn*(1-k*alpha)*ema;
23564         a3 = -kn*ema2;
23565       } break;
23566       default :
23567         throw CImgArgumentException(_cimg_instance
23568                                     "deriche() : Invalid specified filter order %u "
23569                                     "(should be { 0=smoothing | 1=1st-derivative | 2=2nd-derivative }).",
23570                                     cimg_instance,
23571                                     order);
23572       }
23573       coefp = (a0+a1)/(1+b1+b2);
23574       coefn = (a2+a3)/(1+b1+b2);
23575       switch (naxis) {
23576       case 'x' : {
23577         const int N = _width;
23578         const unsigned long off = 1U;
23579         CImg<Tfloat> Y(N);
23580         cimg_forYZC(*this,y,z,c) { T *ptrX = data(0,y,z,c); _cimg_deriche_apply; }
23581       } break;
23582       case 'y' : {
23583         const int N = _height;
23584         const unsigned long off = (unsigned long)_width;
23585         CImg<Tfloat> Y(N);
23586         cimg_forXZC(*this,x,z,c) { T *ptrX = data(x,0,z,c); _cimg_deriche_apply; }
23587       } break;
23588       case 'z' : {
23589         const int N = _depth;
23590         const unsigned long off = (unsigned long)_width*_height;
23591         CImg<Tfloat> Y(N);
23592         cimg_forXYC(*this,x,y,c) { T *ptrX = data(x,y,0,c); _cimg_deriche_apply; }
23593       } break;
23594       default : {
23595         const int N = _spectrum;
23596         const unsigned long off = (unsigned long)_width*_height*_depth;
23597         CImg<Tfloat> Y(N);
23598         cimg_forXYZ(*this,x,y,z) { T *ptrX = data(x,y,z,0); _cimg_deriche_apply; }
23599       }
23600       }
23601       return *this;
23602     }
23603 
23604     //! Apply recursive Deriche filter \newinstance.
23605     CImg<Tfloat> get_deriche(const float sigma, const int order=0, const char axis='x', const bool boundary_conditions=true) const {
23606       return CImg<Tfloat>(*this,false).deriche(sigma,order,axis,boundary_conditions);
23607     }
23608 
23609     //! Blur image.
23610     /**
23611        \param sigma_x Standard deviation of the blur, along the X-axis.
23612        \param sigma_y Standard deviation of the blur, along the Y-axis.
23613        \param sigma_z Standard deviation of the blur, along the Z-axis.
23614        \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
23615        \note
23616        - The blur is computed as a 0-order Deriche filter. This is not a gaussian blur.
23617        - This is a recursive algorithm, not depending on the values of the standard deviations.
23618     **/
23619     CImg<T>& blur(const float sigma_x, const float sigma_y, const float sigma_z, const bool boundary_conditions=true) {
23620       if (!is_empty()) {
23621         if (_width>1) deriche(sigma_x,0,'x',boundary_conditions);
23622         if (_height>1) deriche(sigma_y,0,'y',boundary_conditions);
23623         if (_depth>1) deriche(sigma_z,0,'z',boundary_conditions);
23624       }
23625       return *this;
23626     }
23627 
23628     //! Blur image \newinstance.
23629     CImg<Tfloat> get_blur(const float sigma_x, const float sigma_y, const float sigma_z, const bool boundary_conditions=true) const {
23630       return CImg<Tfloat>(*this,false).blur(sigma_x,sigma_y,sigma_z,boundary_conditions);
23631     }
23632 
23633     //! Blur image isotropically.
23634     /**
23635        \param sigma Standard deviation of the blur.
23636        \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.a
23637     **/
23638     CImg<T>& blur(const float sigma, const bool boundary_conditions=true) {
23639       const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100;
23640       return blur(nsigma,nsigma,nsigma,boundary_conditions);
23641     }
23642 
23643     //! Blur image isotropically \newinstance.
23644     CImg<Tfloat> get_blur(const float sigma, const bool boundary_conditions=true) const {
23645       return CImg<Tfloat>(*this,false).blur(sigma,boundary_conditions);
23646     }
23647 
23648     //! Blur image anisotropically, directed by a field of diffusion tensors.
23649     /**
23650        \param G Field of square roots of diffusion tensors/vectors used to drive the smoothing.
23651        \param amplitude Amplitude of the smoothing.
23652        \param dl Spatial discretization.
23653        \param da Angular discretization.
23654        \param gauss_prec Precision of the diffusion process.
23655        \param interpolation_type Interpolation scheme. Can be <tt>{ 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }</tt>.
23656        \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not.
23657     **/
23658     template<typename t>
23659     CImg<T>& blur_anisotropic(const CImg<t>& G,
23660                               const float amplitude=60, const float dl=0.8f, const float da=30,
23661                               const float gauss_prec=2, const unsigned int interpolation_type=0,
23662                               const bool is_fast_approx=1) {
23663 
23664       // Check arguments and init variables
23665       if (!is_sameXYZ(G) || (G._spectrum!=3 && G._spectrum!=6))
23666         throw CImgArgumentException(_cimg_instance
23667                                     "blur_anisotropic() : Invalid specified diffusion tensor field (%u,%u,%u,%u,%p).",
23668                                     cimg_instance,
23669                                     G._width,G._height,G._depth,G._spectrum,G._data);
23670 
23671       if (is_empty() || amplitude<=0 || dl<0) return *this;
23672       const bool is_3d = (G._spectrum==6);
23673       T val_min, val_max = max_min(val_min);
23674 
23675       if (da<=0) {  // Iterated oriented Laplacians
23676         CImg<Tfloat> velocity(_width,_height,_depth,_spectrum);
23677         for (unsigned int iteration = 0; iteration<(unsigned int)amplitude; ++iteration) {
23678           Tfloat *ptrd = velocity._data, veloc_max = 0;
23679           if (is_3d) { // 3d version
23680             CImg_3x3x3(I,Tfloat);
23681             cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
23682               const Tfloat
23683                 ixx = Incc + Ipcc - 2*Iccc,
23684                 ixy = (Innc + Ippc - Inpc - Ipnc)/4,
23685                 ixz = (Incn + Ipcp - Incp - Ipcn)/4,
23686                 iyy = Icnc + Icpc - 2*Iccc,
23687                 iyz = (Icnn + Icpp - Icnp - Icpn)/4,
23688                 izz = Iccn + Iccp - 2*Iccc,
23689                 veloc = (Tfloat)(G(x,y,z,0)*ixx + 2*G(x,y,z,1)*ixy + 2*G(x,y,z,2)*ixz + G(x,y,z,3)*iyy + 2*G(x,y,z,4)*iyz + G(x,y,z,5)*izz);
23690               *(ptrd++) = veloc;
23691               if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
23692             }
23693           } else { // 2d version
23694             CImg_3x3(I,Tfloat);
23695             cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
23696               const Tfloat
23697                 ixx = Inc + Ipc - 2*Icc,
23698                 ixy = (Inn + Ipp - Inp - Ipn)/4,
23699                 iyy = Icn + Icp - 2*Icc,
23700                 veloc = (Tfloat)(G(x,y,0,0)*ixx + 2*G(x,y,0,1)*ixy + G(x,y,0,2)*iyy);
23701               *(ptrd++) = veloc;
23702               if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
23703             }
23704           }
23705           if (veloc_max>0) *this+=(velocity*=dl/veloc_max);
23706         }
23707       } else { // LIC-based smoothing.
23708         const unsigned long whd = (unsigned long)_width*_height*_depth;
23709         const float sqrt2amplitude = (float)std::sqrt(2*amplitude);
23710         const int dx1 = width() - 1, dy1 = height() - 1, dz1 = depth() - 1;
23711         CImg<Tfloat> res(_width,_height,_depth,_spectrum,0), W(_width,_height,_depth,is_3d?4:3), val(_spectrum);
23712         int N = 0;
23713         if (is_3d) { // 3d version
23714           for (float phi = (180%(int)da)/2.0f; phi<=180; phi+=da) {
23715             const float phir = (float)(phi*cimg::PI/180), datmp = (float)(da/std::cos(phir)), da2 = datmp<1?360.0f:datmp;
23716             for (float theta = 0; theta<360; (theta+=da2),++N) {
23717               const float
23718                 thetar = (float)(theta*cimg::PI/180),
23719                 vx = (float)(std::cos(thetar)*std::cos(phir)),
23720                 vy = (float)(std::sin(thetar)*std::cos(phir)),
23721                 vz = (float)std::sin(phir);
23722               const t
23723                 *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2),
23724                 *pd = G.data(0,0,0,3), *pe = G.data(0,0,0,4), *pf = G.data(0,0,0,5);
23725               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);
23726               cimg_forXYZ(G,xg,yg,zg) {
23727                 const t a = *(pa++), b = *(pb++), c = *(pc++), d = *(pd++), e = *(pe++), f = *(pf++);
23728                 const float
23729                   u = (float)(a*vx + b*vy + c*vz),
23730                   v = (float)(b*vx + d*vy + e*vz),
23731                   w = (float)(c*vx + e*vy + f*vz),
23732                   n = (float)std::sqrt(1e-5+u*u+v*v+w*w),
23733                   dln = dl/n;
23734                 *(pd0++) = (Tfloat)(u*dln);
23735                 *(pd1++) = (Tfloat)(v*dln);
23736                 *(pd2++) = (Tfloat)(w*dln);
23737                 *(pd3++) = (Tfloat)n;
23738               }
23739 
23740               Tfloat *ptrd = res._data;
23741               cimg_forXYZ(*this,x,y,z) {
23742                 val.fill(0);
23743                 const float
23744                   n = (float)W(x,y,z,3),
23745                   fsigma = (float)(n*sqrt2amplitude),
23746                   fsigma2 = 2*fsigma*fsigma,
23747                   length = gauss_prec*fsigma;
23748                 float
23749                   S = 0,
23750                   X = (float)x,
23751                   Y = (float)y,
23752                   Z = (float)z;
23753                 switch (interpolation_type) {
23754                 case 0 : { // Nearest neighbor
23755                   for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
23756                     const int
23757                       cx = (int)(X+0.5f),
23758                       cy = (int)(Y+0.5f),
23759                       cz = (int)(Z+0.5f);
23760                     const float
23761                       u = (float)W(cx,cy,cz,0),
23762                       v = (float)W(cx,cy,cz,1),
23763                       w = (float)W(cx,cy,cz,2);
23764                     if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,cz,c); ++S; }
23765                     else {
23766                       const float coef = (float)std::exp(-l*l/fsigma2);
23767                       cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,cz,c));
23768                       S+=coef;
23769                     }
23770                     X+=u; Y+=v; Z+=w;
23771                   }
23772                 } break;
23773                 case 1 : { // Linear interpolation
23774                   for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
23775                     const float
23776                       u = (float)(W._linear_atXYZ(X,Y,Z,0)),
23777                       v = (float)(W._linear_atXYZ(X,Y,Z,1)),
23778                       w = (float)(W._linear_atXYZ(X,Y,Z,2));
23779                     if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
23780                     else {
23781                       const float coef = (float)std::exp(-l*l/fsigma2);
23782                       cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
23783                       S+=coef;
23784                     }
23785                     X+=u; Y+=v; Z+=w;
23786                   }
23787                 } break;
23788                 default : { // 2nd order Runge Kutta
23789                   for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
23790                     const float
23791                       u0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,0)),
23792                       v0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,1)),
23793                       w0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,2)),
23794                       u = (float)(W._linear_atXYZ(X+u0,Y+v0,Z+w0,0)),
23795                       v = (float)(W._linear_atXYZ(X+u0,Y+v0,Z+w0,1)),
23796                       w = (float)(W._linear_atXYZ(X+u0,Y+v0,Z+w0,2));
23797                     if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
23798                     else {
23799                       const float coef = (float)std::exp(-l*l/fsigma2);
23800                       cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
23801                       S+=coef;
23802                     }
23803                     X+=u; Y+=v; Z+=w;
23804                   }
23805                 } break;
23806                 }
23807                 Tfloat *_ptrd = ptrd++;
23808                 if (S>0) cimg_forC(res,c) { *_ptrd+=val[c]/S; _ptrd+=whd; }
23809                 else cimg_forC(res,c) { *_ptrd+=(Tfloat)((*this)(x,y,z,c)); _ptrd+=whd; }
23810               }
23811             }
23812           }
23813         } else { // 2d LIC algorithm
23814           for (float theta = (360%(int)da)/2.0f; theta<360; (theta+=da),++N) {
23815             const float thetar = (float)(theta*cimg::PI/180), vx = (float)(std::cos(thetar)), vy = (float)(std::sin(thetar));
23816             const t *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2);
23817             Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(0,0,0,2);
23818             cimg_forXY(G,xg,yg) {
23819               const t a = *(pa++), b = *(pb++), c = *(pc++);
23820               const float
23821                 u = (float)(a*vx + b*vy),
23822                 v = (float)(b*vx + c*vy),
23823                 n = (float)std::sqrt(1e-5+u*u+v*v),
23824                 dln = dl/n;
23825               *(pd0++) = (Tfloat)(u*dln);
23826               *(pd1++) = (Tfloat)(v*dln);
23827               *(pd2++) = (Tfloat)n;
23828             }
23829             Tfloat *ptrd = res._data;
23830             cimg_forXY(*this,x,y) {
23831               val.fill(0);
23832               const float
23833                 n = (float)W(x,y,0,2),
23834                 fsigma = (float)(n*sqrt2amplitude),
23835                 fsigma2 = 2*fsigma*fsigma,
23836                 length = gauss_prec*fsigma;
23837               float
23838                 S = 0,
23839                 X = (float)x,
23840                 Y = (float)y;
23841               switch (interpolation_type) {
23842               case 0 : { // Nearest-neighbor
23843                 for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
23844                   const int
23845                     cx = (int)(X+0.5f),
23846                     cy = (int)(Y+0.5f);
23847                   const float
23848                     u = (float)W(cx,cy,0,0),
23849                     v = (float)W(cx,cy,0,1);
23850                   if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,0,c); ++S; }
23851                   else {
23852                     const float coef = (float)std::exp(-l*l/fsigma2);
23853                     cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,0,c));
23854                     S+=coef;
23855                   }
23856                   X+=u; Y+=v;
23857                 }
23858               } break;
23859               case 1 : { // Linear interpolation
23860                 for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
23861                   const float
23862                     u = (float)(W._linear_atXY(X,Y,0,0)),
23863                     v = (float)(W._linear_atXY(X,Y,0,1));
23864                   if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
23865                   else {
23866                     const float coef = (float)std::exp(-l*l/fsigma2);
23867                     cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
23868                     S+=coef;
23869                   }
23870                   X+=u; Y+=v;
23871                 }
23872               } break;
23873               default : { // 2nd-order Runge-kutta interpolation
23874                 for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
23875                   const float
23876                     u0 = (float)(0.5f*W._linear_atXY(X,Y,0,0)),
23877                     v0 = (float)(0.5f*W._linear_atXY(X,Y,0,1)),
23878                     u = (float)(W._linear_atXY(X+u0,Y+v0,0,0)),
23879                     v = (float)(W._linear_atXY(X+u0,Y+v0,0,1));
23880                   if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
23881                   else {
23882                     const float coef = (float)std::exp(-l*l/fsigma2);
23883                     cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
23884                     S+=coef;
23885                   }
23886                   X+=u; Y+=v;
23887                 }
23888               }
23889               }
23890               Tfloat *_ptrd = ptrd++;
23891               if (S>0) cimg_forC(res,c) { *_ptrd+=val[c]/S; _ptrd+=whd; }
23892               else cimg_forC(res,c) { *_ptrd+=(Tfloat)((*this)(x,y,0,c)); _ptrd+=whd; }
23893             }
23894           }
23895         }
23896         const Tfloat *ptrs = res._data;
23897         cimg_for(*this,ptrd,T) { const Tfloat val = *(ptrs++)/N; *ptrd = val<val_min?val_min:(val>val_max?val_max:(T)val); }
23898       }
23899       return *this;
23900     }
23901 
23902     //! Blur image anisotropically, directed by a field of diffusion tensors \newinstance.
23903     template<typename t>
23904     CImg<T> get_blur_anisotropic(const CImg<t>& G,
23905                                  const float amplitude=60, const float dl=0.8f, const float da=30,
23906                                  const float gauss_prec=2, const unsigned int interpolation_type=0,
23907                                  const bool is_fast_approx=true) const {
23908       return (+*this).blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx);
23909     }
23910 
23911     //! Blur image anisotropically, in an edge-preserving way.
23912     /**
23913        \param amplitude Amplitude of the smoothing.
23914        \param sharpness Sharpness.
23915        \param anisotropy Anisotropy.
23916        \param alpha Standard deviation of the gradient blur.
23917        \param sigma Standard deviation of the structure tensor blur.
23918        \param dl Spatial discretization.
23919        \param da Angular discretization.
23920        \param gauss_prec Precision of the diffusion process.
23921        \param interpolation_type Interpolation scheme. Can be <tt>{ 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }</tt>.
23922        \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not.
23923      **/
23924     CImg<T>& blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f,
23925                               const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30,
23926                               const float gauss_prec=2, const unsigned int interpolation_type=0,
23927                               const bool is_fast_approx=true) {
23928       return blur_anisotropic(get_diffusion_tensors(sharpness,anisotropy,alpha,sigma,interpolation_type!=3),
23929                               amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx);
23930     }
23931 
23932     //! Blur image anisotropically, in an edge-preserving way \newinstance.
23933     CImg<T> get_blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f,
23934                                  const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f,
23935                                  const float da=30, const float gauss_prec=2, const unsigned int interpolation_type=0,
23936                                  const bool is_fast_approx=true) const {
23937       return (+*this).blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,interpolation_type,is_fast_approx);
23938     }
23939 
23940     //! Blur image, with the bilateral filter.
23941     /**
23942        \param sigma_x Amount of blur along the X-axis.
23943        \param sigma_y Amount of blur along the Y-axis.
23944        \param sigma_z Amount of blur along the Z-axis.
23945        \param sigma_r Amount of blur along the value axis.
23946        \param bgrid_x Size of the bilateral grid along the X-axis.
23947        \param bgrid_y Size of the bilateral grid along the Y-axis.
23948        \param bgrid_z Size of the bilateral grid along the Z-axis.
23949        \param bgrid_r Size of the bilateral grid along the value axis.
23950        \param interpolation_type Use interpolation for image slicing.
23951        \note This algorithm uses the optimisation technique proposed by S. Paris and F. Durand, in ECCV'2006
23952        (extended for 3d volumetric images).
23953     **/
23954     CImg<T>& blur_bilateral(const float sigma_x, const float sigma_y, const float sigma_z, const float sigma_r,
23955                             const int bgrid_x, const int bgrid_y, const int bgrid_z, const int bgrid_r,
23956                             const bool interpolation_type=true) {
23957       if (is_empty()) return *this;
23958       T m, M = max_min(m);
23959       const float range = (float)(1.0f + M - m);
23960       const unsigned int
23961         bx0 = bgrid_x>=0?bgrid_x:_width*(-bgrid_x)/100,
23962         by0 = bgrid_y>=0?bgrid_y:_height*(-bgrid_y)/100,
23963         bz0 = bgrid_z>=0?bgrid_z:_depth*(-bgrid_z)/100,
23964         br0 = bgrid_r>=0?bgrid_r:(int)(-range*bgrid_r/100),
23965         bx = bx0>0?bx0:1,
23966         by = by0>0?by0:1,
23967         bz = bz0>0?bz0:1,
23968         br = br0>0?br0:1;
23969       const float
23970         _sigma_x = sigma_x>=0?sigma_x:-sigma_x*_width/100,
23971         _sigma_y = sigma_y>=0?sigma_y:-sigma_y*_height/100,
23972         _sigma_z = sigma_z>=0?sigma_z:-sigma_z*_depth/100,
23973         nsigma_x = _sigma_x*bx/_width,
23974         nsigma_y = _sigma_y*by/_height,
23975         nsigma_z = _sigma_z*bz/_depth,
23976         nsigma_r = sigma_r*br/range;
23977       if (nsigma_x>0 || nsigma_y>0 || nsigma_z>0 || nsigma_r>0) {
23978         const bool is_3d = (_depth>1);
23979         if (is_3d) { // 3d version of the algorithm
23980           CImg<floatT> bgrid(bx,by,bz,br), bgridw(bx,by,bz,br);
23981           cimg_forC(*this,c) {
23982             bgrid.fill(0); bgridw.fill(0);
23983             cimg_forXYZ(*this,x,y,z) {
23984               const T val = (*this)(x,y,z,c);
23985               const int X = x*bx/_width, Y = y*by/_height, Z = z*bz/_depth, R = (int)((val-m)*br/range);
23986               bgrid(X,Y,Z,R) += (float)val;
23987               bgridw(X,Y,Z,R) += 1;
23988             }
23989             bgrid.blur(nsigma_x,nsigma_y,nsigma_z,true).deriche(nsigma_r,0,'c',false);
23990             bgridw.blur(nsigma_x,nsigma_y,nsigma_z,true).deriche(nsigma_r,0,'c',false);
23991             if (interpolation_type) cimg_forXYZ(*this,x,y,z) {
23992               const T val = (*this)(x,y,z,c);
23993               const float X = (float)x*bx/_width, Y = (float)y*by/_height, Z = (float)z*bz/_depth, R = (float)((val-m)*br/range),
23994                 bval0 = bgrid._linear_atXYZC(X,Y,Z,R), bval1 = bgridw._linear_atXYZC(X,Y,Z,R);
23995               (*this)(x,y,z,c) = (T)(bval0/bval1);
23996             } else cimg_forXYZ(*this,x,y,z) {
23997               const T val = (*this)(x,y,z,c);
23998               const int X = x*bx/_width, Y = y*by/_height, Z = z*bz/_depth, R = (int)((val-m)*br/range);
23999               const float bval0 = bgrid(X,Y,Z,R), bval1 = bgridw(X,Y,Z,R);
24000               (*this)(x,y,z,c) = (T)(bval0/bval1);
24001             }
24002           }
24003         } else { // 2d version of the algorithm
24004           CImg<floatT> bgrid(bx,by,br,2);
24005           cimg_forC(*this,c) {
24006             bgrid.fill(0);
24007             cimg_forXY(*this,x,y) {
24008               const T val = (*this)(x,y,c);
24009               const int X = x*bx/_width, Y = y*by/_height, R = (int)((val-m)*br/range);
24010               bgrid(X,Y,R,0) += (float)val;
24011               bgrid(X,Y,R,1) += 1;
24012             }
24013             bgrid.blur(nsigma_x,nsigma_y,0,true).blur(0,0,nsigma_r,false);
24014             if (interpolation_type) cimg_forXY(*this,x,y) {
24015               const T val = (*this)(x,y,c);
24016               const float X = (float)x*bx/_width, Y = (float)y*by/_height, R = (float)((val-m)*br/range),
24017                 bval0 = bgrid._linear_atXYZ(X,Y,R,0), bval1 = bgrid._linear_atXYZ(X,Y,R,1);
24018               (*this)(x,y,c) = (T)(bval0/bval1);
24019             } else cimg_forXY(*this,x,y) {
24020               const T val = (*this)(x,y,c);
24021               const int X = x*bx/_width, Y = y*by/_height, R = (int)((val-m)*br/range);
24022               const float bval0 = bgrid(X,Y,R,0), bval1 = bgrid(X,Y,R,1);
24023               (*this)(x,y,c) = (T)(bval0/bval1);
24024             }
24025           }
24026         }
24027       }
24028       return *this;
24029     }
24030 
24031     //! Blur image, with the bilateral filter \newinstance.
24032     CImg<T> get_blur_bilateral(const float sigma_x, const float sigma_y, const float sigma_z, const float sigma_r,
24033                                const int bgrid_x, const int bgrid_y, const int bgrid_z, const int bgrid_r,
24034                                const bool interpolation_type=true) const {
24035       return (+*this).blur_bilateral(sigma_x,sigma_y,sigma_z,sigma_r,bgrid_x,bgrid_y,bgrid_z,bgrid_r,interpolation_type);
24036     }
24037 
24038     //! Blur image using the bilateral filter.
24039     /**
24040        \param sigma_s Amount of blur along the XYZ-axes.
24041        \param sigma_r Amount of blur along the value axis.
24042        \param bgrid_s Size of the bilateral grid along the XYZ-axes.
24043        \param bgrid_r Size of the bilateral grid along the value axis.
24044        \param interpolation_type Use interpolation for image slicing.
24045     **/
24046     CImg<T>& blur_bilateral(const float sigma_s, const float sigma_r, const int bgrid_s=-33, const int bgrid_r=32,
24047                             const bool interpolation_type=true) {
24048       const float nsigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100;
24049       return blur_bilateral(nsigma_s,nsigma_s,nsigma_s,sigma_r,bgrid_s,bgrid_s,bgrid_s,bgrid_r,interpolation_type);
24050     }
24051 
24052     //! Blur image using the bilateral filter \newinstance.
24053     CImg<T> get_blur_bilateral(const float sigma_s, const float sigma_r, const int bgrid_s=-33, const int bgrid_r=32,
24054                                const bool interpolation_type=true) const {
24055       return (+*this).blur_bilateral(sigma_s,sigma_s,sigma_s,sigma_r,bgrid_s,bgrid_s,bgrid_s,bgrid_r,interpolation_type);
24056     }
24057 
24058     //! Blur image using patch-based space.
24059     /**
24060        \param sigma_s Amount of blur along the XYZ-axes.
24061        \param sigma_p Amount of blur along the value axis.
24062        \param patch_size Size of the patchs.
24063        \param lookup_size Size of the window to search similar patchs.
24064        \param smoothness Smoothness for the patch comparison.
24065        \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not.
24066     **/
24067     CImg<T>& blur_patch(const float sigma_s, const float sigma_p, const unsigned int patch_size=3,
24068                         const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true) {
24069       if (is_empty() || !patch_size || !lookup_size) return *this;
24070       return get_blur_patch(sigma_s,sigma_p,patch_size,lookup_size,smoothness,is_fast_approx).move_to(*this);
24071     }
24072 
24073     //! Blur image using patch-based space \newinstance.
24074     CImg<T> get_blur_patch(const float sigma_s, const float sigma_p, const unsigned int patch_size=3,
24075                            const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true) const {
24076 
24077 #define _cimg_blur_patch3d_fast(N) \
24078       cimg_for##N##XYZ(res,x,y,z) { \
24079         T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,x,y,z,c,pP,T); pP+=N3; } \
24080         const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \
24081         float sum_weights = 0; \
24082         cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (cimg::abs(img(x,y,z,0) - img(p,q,r,0))<sigma_p3) { \
24083           T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,p,q,r,c,pQ,T); pQ+=N3; } \
24084           float distance2 = 0; \
24085           pQ = Q._data; cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(pQ++); distance2+=dI*dI; } \
24086           distance2/=Pnorm; \
24087           const float dx = (float)p - x, dy = (float)q - y, dz = (float)r - z, \
24088             alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = alldist>3?0.0f:1.0f; \
24089           sum_weights+=weight; \
24090           cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c); \
24091         } \
24092         if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights; \
24093         else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \
24094     }
24095 
24096 #define _cimg_blur_patch3d(N) \
24097       cimg_for##N##XYZ(res,x,y,z) { \
24098         T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,x,y,z,c,pP,T); pP+=N3; } \
24099         const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \
24100         float sum_weights = 0, weight_max = 0; \
24101         cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) { \
24102           T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,p,q,r,c,pQ,T); pQ+=N3; } \
24103           float distance2 = 0; \
24104           pQ = Q._data; cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(pQ++); distance2+=dI*dI; } \
24105           distance2/=Pnorm; \
24106           const float dx = (float)p - x, dy = (float)q - y, dz = (float)r - z, \
24107             alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = (float)std::exp(-alldist); \
24108           if (weight>weight_max) weight_max = weight; \
24109           sum_weights+=weight; \
24110           cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c); \
24111         } \
24112         sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=weight_max*(*this)(x,y,z,c); \
24113         if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights; \
24114         else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \
24115       }
24116 
24117 #define _cimg_blur_patch2d_fast(N) \
24118         cimg_for##N##XY(res,x,y) { \
24119           T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N(img,x,y,0,c,pP,T); pP+=N2; } \
24120           const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \
24121           float sum_weights = 0; \
24122           cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) if (cimg::abs(img(x,y,0,0) - img(p,q,0,0))<sigma_p3) { \
24123             T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N(img,p,q,0,c,pQ,T); pQ+=N2; } \
24124             float distance2 = 0; \
24125             pQ = Q._data; cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(pQ++); distance2+=dI*dI; } \
24126             distance2/=Pnorm; \
24127             const float dx = (float)p - x, dy = (float)q - y, \
24128               alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = alldist>3?0.0f:1.0f; \
24129             sum_weights+=weight; \
24130             cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c); \
24131           } \
24132           if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights; \
24133           else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \
24134         }
24135 
24136 #define _cimg_blur_patch2d(N) \
24137         cimg_for##N##XY(res,x,y) { \
24138           T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N(img,x,y,0,c,pP,T); pP+=N2; } \
24139           const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \
24140           float sum_weights = 0, weight_max = 0; \
24141           cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) { \
24142             T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N(img,p,q,0,c,pQ,T); pQ+=N2; } \
24143             float distance2 = 0; \
24144             pQ = Q._data; cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(pQ++); distance2+=dI*dI; } \
24145             distance2/=Pnorm; \
24146             const float dx = (float)p - x, dy = (float)q - y, \
24147               alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = (float)std::exp(-alldist); \
24148             if (weight>weight_max) weight_max = weight; \
24149             sum_weights+=weight; \
24150             cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c); \
24151           } \
24152           sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=weight_max*(*this)(x,y,c); \
24153           if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights; \
24154           else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \
24155     }
24156 
24157       if (is_empty() || !patch_size || !lookup_size) return +*this;
24158       CImg<Tfloat> res(_width,_height,_depth,_spectrum,0);
24159       const CImg<T> _img = smoothness>0?get_blur(smoothness):CImg<Tfloat>(),&img = smoothness>0?_img:*this;
24160       CImg<T> P(patch_size*patch_size*_spectrum), Q(P);
24161       const float
24162         nsigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100,
24163         sigma_s2 = nsigma_s*nsigma_s, sigma_p2 = sigma_p*sigma_p, sigma_p3 = 3*sigma_p,
24164         Pnorm = P.size()*sigma_p2;
24165       const int rsize2 = (int)lookup_size/2, rsize1 = (int)lookup_size - rsize2 - 1;
24166       const unsigned int N2 = patch_size*patch_size, N3 = N2*patch_size;
24167       if (_depth>1) switch (patch_size) { // 3d
24168       case 2 : if (is_fast_approx) _cimg_blur_patch3d_fast(2) else _cimg_blur_patch3d(2) break;
24169       case 3 : if (is_fast_approx) _cimg_blur_patch3d_fast(3) else _cimg_blur_patch3d(3) break;
24170       default : {
24171         const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1;
24172         if (is_fast_approx) cimg_forXYZ(res,x,y,z) { // Fast
24173           P = img.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true);
24174           const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2;
24175           float sum_weights = 0;
24176           cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (cimg::abs(img(x,y,z,0)-img(p,q,r,0))<sigma_p3) {
24177             (Q = img.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P;
24178             const float
24179               dx = (float)x - p, dy = (float)y - q, dz = (float)z - r,
24180               distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2),
24181               weight = distance2>3?0.0f:1.0f;
24182             sum_weights+=weight;
24183             cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c);
24184           }
24185           if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights;
24186           else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c));
24187         } else cimg_forXYZ(res,x,y,z) { // Exact
24188           P = img.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true);
24189           const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2;
24190           float sum_weights = 0, weight_max = 0;
24191           cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) {
24192             (Q = img.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P;
24193             const float
24194               dx = (float)x - p, dy = (float)y - q, dz = (float)z - r,
24195               distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2),
24196               weight = (float)std::exp(-distance2);
24197             if (weight>weight_max) weight_max = weight;
24198             sum_weights+=weight;
24199             cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c);
24200           }
24201           sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=weight_max*(*this)(x,y,z,c);
24202           if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights;
24203           else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c));
24204         }
24205       }
24206       } else switch (patch_size) { // 2d
24207       case 2 : if (is_fast_approx) _cimg_blur_patch2d_fast(2) else _cimg_blur_patch2d(2) break;
24208       case 3 : if (is_fast_approx) _cimg_blur_patch2d_fast(3) else _cimg_blur_patch2d(3) break;
24209       case 4 : if (is_fast_approx) _cimg_blur_patch2d_fast(4) else _cimg_blur_patch2d(4) break;
24210       case 5 : if (is_fast_approx) _cimg_blur_patch2d_fast(5) else _cimg_blur_patch2d(5) break;
24211       case 6 : if (is_fast_approx) _cimg_blur_patch2d_fast(6) else _cimg_blur_patch2d(6) break;
24212       case 7 : if (is_fast_approx) _cimg_blur_patch2d_fast(7) else _cimg_blur_patch2d(7) break;
24213       case 8 : if (is_fast_approx) _cimg_blur_patch2d_fast(8) else _cimg_blur_patch2d(8) break;
24214       case 9 : if (is_fast_approx) _cimg_blur_patch2d_fast(9) else _cimg_blur_patch2d(9) break;
24215       default : { // Fast
24216         const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1;
24217         if (is_fast_approx) cimg_forXY(res,x,y) { // 2d fast approximation.
24218           P = img.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true);
24219           const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2;
24220           float sum_weights = 0;
24221           cimg_for_inXY(res,x0,y0,x1,y1,p,q) if (cimg::abs(img(x,y,0)-img(p,q,0))<sigma_p3) {
24222             (Q = img.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P;
24223             const float
24224               dx = (float)x - p, dy = (float)y - q,
24225               distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2),
24226               weight = distance2>3?0.0f:1.0f;
24227             sum_weights+=weight;
24228             cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c);
24229           }
24230           if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights;
24231           else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c));
24232         } else cimg_forXY(res,x,y) { // 2d exact algorithm.
24233           P = img.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true);
24234           const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2;
24235           float sum_weights = 0, weight_max = 0;
24236           cimg_for_inXY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) {
24237             (Q = img.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P;
24238             const float
24239               dx = (float)x - p, dy = (float)y - q,
24240               distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2),
24241               weight = (float)std::exp(-distance2);
24242             if (weight>weight_max) weight_max = weight;
24243             sum_weights+=weight;
24244             cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c);
24245           }
24246           sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=weight_max*(*this)(x,y,c);
24247           if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights;
24248           else cimg_forC(res,c) res(x,y,0,c) = (Tfloat)((*this)(x,y,c));
24249         }
24250       }
24251       }
24252       return res;
24253     }
24254 
24255     //! Blur image with the median filter.
24256     /**
24257        \param n Size of the median filter.
24258     **/
24259     CImg<T>& blur_median(const unsigned int n) {
24260       if (!n) return *this;
24261       return get_blur_median(n).move_to(*this);
24262     }
24263 
24264     //! Blur image with the median filter \newinstance.
24265     CImg<T> get_blur_median(const unsigned int n) const {
24266       if (is_empty() || n<=1) return +*this;
24267       CImg<T> res(_width,_height,_depth,_spectrum);
24268       T *ptrd = res._data;
24269       const int hl = n/2, hr = hl - 1 + n%2;
24270       if (res._depth!=1) cimg_forXYZC(*this,x,y,z,c) { // 3d
24271         const int
24272           x0 = x - hl, y0 = y - hl, z0 = z-hl, x1 = x + hr, y1 = y + hr, z1 = z+hr,
24273           nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0,
24274           nx1 = x1>=width()?width()-1:x1, ny1 = y1>=height()?height()-1:y1, nz1 = z1>=depth()?depth()-1:z1;
24275         *(ptrd++) = get_crop(nx0,ny0,nz0,c,nx1,ny1,nz1,c).median();
24276       } else {
24277 #define _cimg_median_sort(a,b) if ((a)>(b)) cimg::swap(a,b)
24278         if (res._height!=1) switch (n) { // 2d
24279         case 3 : {
24280           T I[9] = { 0 };
24281           CImg_3x3(J,T);
24282           cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) {
24283             std::memcpy(J,I,9*sizeof(T));
24284             _cimg_median_sort(Jcp, Jnp); _cimg_median_sort(Jcc, Jnc); _cimg_median_sort(Jcn, Jnn);
24285             _cimg_median_sort(Jpp, Jcp); _cimg_median_sort(Jpc, Jcc); _cimg_median_sort(Jpn, Jcn);
24286             _cimg_median_sort(Jcp, Jnp); _cimg_median_sort(Jcc, Jnc); _cimg_median_sort(Jcn, Jnn);
24287             _cimg_median_sort(Jpp, Jpc); _cimg_median_sort(Jnc, Jnn); _cimg_median_sort(Jcc, Jcn);
24288             _cimg_median_sort(Jpc, Jpn); _cimg_median_sort(Jcp, Jcc); _cimg_median_sort(Jnp, Jnc);
24289             _cimg_median_sort(Jcc, Jcn); _cimg_median_sort(Jcc, Jnp); _cimg_median_sort(Jpn, Jcc);
24290             _cimg_median_sort(Jcc, Jnp);
24291             *(ptrd++) = Jcc;
24292           }
24293         } break;
24294         case 5 : {
24295           T I[25] = { 0 };
24296           CImg_5x5(J,T);
24297           cimg_forC(*this,c) cimg_for5x5(*this,x,y,0,c,I,T) {
24298             std::memcpy(J,I,25*sizeof(T));
24299             _cimg_median_sort(Jbb, Jpb); _cimg_median_sort(Jnb, Jab); _cimg_median_sort(Jcb, Jab); _cimg_median_sort(Jcb, Jnb);
24300             _cimg_median_sort(Jpp, Jcp); _cimg_median_sort(Jbp, Jcp); _cimg_median_sort(Jbp, Jpp); _cimg_median_sort(Jap, Jbc);
24301             _cimg_median_sort(Jnp, Jbc); _cimg_median_sort(Jnp, Jap); _cimg_median_sort(Jcc, Jnc); _cimg_median_sort(Jpc, Jnc);
24302             _cimg_median_sort(Jpc, Jcc); _cimg_median_sort(Jbn, Jpn); _cimg_median_sort(Jac, Jpn); _cimg_median_sort(Jac, Jbn);
24303             _cimg_median_sort(Jnn, Jan); _cimg_median_sort(Jcn, Jan); _cimg_median_sort(Jcn, Jnn); _cimg_median_sort(Jpa, Jca);
24304             _cimg_median_sort(Jba, Jca); _cimg_median_sort(Jba, Jpa); _cimg_median_sort(Jna, Jaa); _cimg_median_sort(Jcb, Jbp);
24305             _cimg_median_sort(Jnb, Jpp); _cimg_median_sort(Jbb, Jpp); _cimg_median_sort(Jbb, Jnb); _cimg_median_sort(Jab, Jcp);
24306             _cimg_median_sort(Jpb, Jcp); _cimg_median_sort(Jpb, Jab); _cimg_median_sort(Jpc, Jac); _cimg_median_sort(Jnp, Jac);
24307             _cimg_median_sort(Jnp, Jpc); _cimg_median_sort(Jcc, Jbn); _cimg_median_sort(Jap, Jbn); _cimg_median_sort(Jap, Jcc);
24308             _cimg_median_sort(Jnc, Jpn); _cimg_median_sort(Jbc, Jpn); _cimg_median_sort(Jbc, Jnc); _cimg_median_sort(Jba, Jna);
24309             _cimg_median_sort(Jcn, Jna); _cimg_median_sort(Jcn, Jba); _cimg_median_sort(Jpa, Jaa); _cimg_median_sort(Jnn, Jaa);
24310             _cimg_median_sort(Jnn, Jpa); _cimg_median_sort(Jan, Jca); _cimg_median_sort(Jnp, Jcn); _cimg_median_sort(Jap, Jnn);
24311             _cimg_median_sort(Jbb, Jnn); _cimg_median_sort(Jbb, Jap); _cimg_median_sort(Jbc, Jan); _cimg_median_sort(Jpb, Jan);
24312             _cimg_median_sort(Jpb, Jbc); _cimg_median_sort(Jpc, Jba); _cimg_median_sort(Jcb, Jba); _cimg_median_sort(Jcb, Jpc);
24313             _cimg_median_sort(Jcc, Jpa); _cimg_median_sort(Jnb, Jpa); _cimg_median_sort(Jnb, Jcc); _cimg_median_sort(Jnc, Jca);
24314             _cimg_median_sort(Jab, Jca); _cimg_median_sort(Jab, Jnc); _cimg_median_sort(Jac, Jna); _cimg_median_sort(Jbp, Jna);
24315             _cimg_median_sort(Jbp, Jac); _cimg_median_sort(Jbn, Jaa); _cimg_median_sort(Jpp, Jaa); _cimg_median_sort(Jpp, Jbn);
24316             _cimg_median_sort(Jcp, Jpn); _cimg_median_sort(Jcp, Jan); _cimg_median_sort(Jnc, Jpa); _cimg_median_sort(Jbn, Jna);
24317             _cimg_median_sort(Jcp, Jnc); _cimg_median_sort(Jcp, Jbn); _cimg_median_sort(Jpb, Jap); _cimg_median_sort(Jnb, Jpc);
24318             _cimg_median_sort(Jbp, Jcn); _cimg_median_sort(Jpc, Jcn); _cimg_median_sort(Jap, Jcn); _cimg_median_sort(Jab, Jbc);
24319             _cimg_median_sort(Jpp, Jcc); _cimg_median_sort(Jcp, Jac); _cimg_median_sort(Jab, Jpp); _cimg_median_sort(Jab, Jcp);
24320             _cimg_median_sort(Jcc, Jac); _cimg_median_sort(Jbc, Jac); _cimg_median_sort(Jpp, Jcp); _cimg_median_sort(Jbc, Jcc);
24321             _cimg_median_sort(Jpp, Jbc); _cimg_median_sort(Jpp, Jcn); _cimg_median_sort(Jcc, Jcn); _cimg_median_sort(Jcp, Jcn);
24322             _cimg_median_sort(Jcp, Jbc); _cimg_median_sort(Jcc, Jnn); _cimg_median_sort(Jcp, Jcc); _cimg_median_sort(Jbc, Jnn);
24323             _cimg_median_sort(Jcc, Jba); _cimg_median_sort(Jbc, Jba); _cimg_median_sort(Jbc, Jcc);
24324             *(ptrd++) = Jcc;
24325           }
24326         } break;
24327         default : {
24328           cimg_forXYC(*this,x,y,c) {
24329             const int
24330               x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr,
24331               nx0 = x0<0?0:x0, ny0 = y0<0?0:y0,
24332               nx1 = x1>=width()?width()-1:x1, ny1 = y1>=height()?height()-1:y1;
24333             *(ptrd++) = get_crop(nx0,ny0,0,c,nx1,ny1,0,c).median();
24334           }
24335         }
24336         } else switch (n) { // 1d
24337         case 2 : {
24338           T I[4] = { 0 };
24339           cimg_forC(*this,c) cimg_for2x2(*this,x,y,0,c,I,T) *(ptrd++) = (T)(0.5f*(I[0]+I[1]));
24340         } break;
24341         case 3 : {
24342           T I[9] = { 0 };
24343           cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T)
24344             *(ptrd++) = I[3]<I[4]?(I[4]<I[5]?I[4]:(I[3]<I[5]?I[5]:I[3])):(I[3]<I[5]?I[3]:(I[4]<I[5]?I[5]:I[4]));
24345         } break;
24346         default : {
24347           cimg_forXC(*this,x,c) {
24348             const int
24349               x0 = x - hl, x1 = x + hr,
24350               nx0 = x0<0?0:x0, nx1 = x1>=width()?width()-1:x1;
24351             *(ptrd++) = get_crop(nx0,0,0,c,nx1,0,0,c).median();
24352           }
24353         }
24354         }
24355       }
24356       return res;
24357     }
24358 
24359     //! Sharpen image.
24360     /**
24361        \param amplitude Sharpening amplitude
24362        \param sharpen_type Select sharpening method. Can be <tt>{ false=inverse diffusion | true=shock filters }</tt>.
24363        \param edge Edge threshold (shock filters only).
24364        \param alpha Gradient smoothness (shock filters only).
24365        \param sigma Tensor smoothness (shock filters only).
24366     **/
24367     CImg<T>& sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1, const float alpha=0, const float sigma=0) {
24368       if (is_empty()) return *this;
24369       T val_min, val_max = max_min(val_min);
24370       const float nedge = edge/2;
24371       CImg<Tfloat> val, vec, velocity(_width,_height,_depth,_spectrum);
24372       Tfloat *ptrd = velocity._data, veloc_max = 0;
24373 
24374       if (_depth>1) { // 3d
24375         CImg_3x3x3(I,Tfloat);
24376         if (sharpen_type) { // Shock filters.
24377           CImg<Tfloat> G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors());
24378           if (sigma>0) G.blur(sigma);
24379           Tfloat *ptrG0 = G.data(0,0,0,0), *ptrG1 = G.data(0,0,0,1), *ptrG2 = G.data(0,0,0,2), *ptrG3 = G.data(0,0,0,3);
24380           cimg_forXYZ(G,x,y,z) {
24381             G.get_tensor_at(x,y,z).symmetric_eigen(val,vec);
24382             if (val[0]<0) val[0] = 0;
24383             if (val[1]<0) val[1] = 0;
24384             if (val[2]<0) val[2] = 0;
24385             *(ptrG0++) = vec(0,0);
24386             *(ptrG1++) = vec(0,1);
24387             *(ptrG2++) = vec(0,2);
24388             *(ptrG3++) = 1 - (Tfloat)std::pow(1+val[0]+val[1]+val[2],-(Tfloat)nedge);
24389           }
24390           cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
24391             const Tfloat
24392               u = G(x,y,z,0),
24393               v = G(x,y,z,1),
24394               w = G(x,y,z,2),
24395               amp = G(x,y,z,3),
24396               ixx = Incc + Ipcc - 2*Iccc,
24397               ixy = (Innc + Ippc - Inpc - Ipnc)/4,
24398               ixz = (Incn + Ipcp - Incp - Ipcn)/4,
24399               iyy = Icnc + Icpc - 2*Iccc,
24400               iyz = (Icnn + Icpp - Icnp - Icpn)/4,
24401               izz = Iccn + Iccp - 2*Iccc,
24402               ixf = Incc - Iccc,
24403               ixb = Iccc - Ipcc,
24404               iyf = Icnc - Iccc,
24405               iyb = Iccc - Icpc,
24406               izf = Iccn - Iccc,
24407               izb = Iccc - Iccp,
24408               itt = u*u*ixx + v*v*iyy + w*w*izz + 2*u*v*ixy + 2*u*w*ixz + 2*v*w*iyz,
24409               it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb) + w*cimg::minmod(izf,izb),
24410               veloc = -amp*cimg::sign(itt)*cimg::abs(it);
24411             *(ptrd++) = veloc;
24412             if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
24413           }
24414         } else cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { // Inverse diffusion.
24415           const Tfloat veloc = -Ipcc - Incc - Icpc - Icnc - Iccp - Iccn + 6*Iccc;
24416           *(ptrd++) = veloc;
24417           if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
24418         }
24419       } else {
24420         CImg_3x3(I,Tfloat);
24421         if (sharpen_type) { // Shock filters.
24422           CImg<Tfloat> G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors());
24423           if (sigma>0) G.blur(sigma);
24424           Tfloat *ptrG0 = G.data(0,0,0,0), *ptrG1 = G.data(0,0,0,1), *ptrG2 = G.data(0,0,0,2);
24425           cimg_forXY(G,x,y) {
24426             G.get_tensor_at(x,y).symmetric_eigen(val,vec);
24427             if (val[0]<0) val[0] = 0;
24428             if (val[1]<0) val[1] = 0;
24429             *(ptrG0++) = vec(0,0);
24430             *(ptrG1++) = vec(0,1);
24431             *(ptrG2++) = 1 - (Tfloat)std::pow(1 + val[0] + val[1],-(Tfloat)nedge);
24432           }
24433           cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
24434             const Tfloat
24435               u = G(x,y,0),
24436               v = G(x,y,1),
24437               amp = G(x,y,2),
24438               ixx = Inc + Ipc - 2*Icc,
24439               ixy = (Inn + Ipp - Inp - Ipn)/4,
24440               iyy = Icn + Icp - 2*Icc,
24441               ixf = Inc - Icc,
24442               ixb = Icc - Ipc,
24443               iyf = Icn - Icc,
24444               iyb = Icc - Icp,
24445               itt = u*u*ixx + v*v*iyy + 2*u*v*ixy,
24446               it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb),
24447               veloc = -amp*cimg::sign(itt)*cimg::abs(it);
24448             *(ptrd++) = veloc;
24449             if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
24450           }
24451         } else cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) { // Inverse diffusion.
24452           const Tfloat veloc = -Ipc - Inc - Icp - Icn + 4*Icc;
24453           *(ptrd++) = veloc;
24454           if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
24455         }
24456       }
24457       if (veloc_max<=0) return *this;
24458       return ((velocity*=amplitude/veloc_max)+=*this).cut(val_min,val_max).move_to(*this);
24459     }
24460 
24461     //! Sharpen image \newinstance.
24462     CImg<T> get_sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1, const float alpha=0, const float sigma=0) const {
24463       return (+*this).sharpen(amplitude,sharpen_type,edge,alpha,sigma);
24464     }
24465 
24466     //! Return image gradient.
24467     /**
24468        \param axes Axes considered for the gradient computation, as a C-string (e.g "xy").
24469        \param scheme = Numerical scheme used for the gradient computation :
24470        - -1 = Backward finite differences
24471        - 0 = Centered finite differences
24472        - 1 = Forward finite differences
24473        - 2 = Using Sobel masks
24474        - 3 = Using rotation invariant masks
24475        - 4 = Using Deriche recusrsive filter.
24476     **/
24477     CImgList<Tfloat> get_gradient(const char *const axes=0, const int scheme=3) const {
24478       CImgList<Tfloat> grad(2,_width,_height,_depth,_spectrum);
24479       Tfloat *ptrd0 = grad[0]._data, *ptrd1 = grad[1]._data;
24480       bool is_3d = false;
24481       if (axes) {
24482         for (unsigned int a = 0; axes[a]; ++a) {
24483           const char axis = cimg::uncase(axes[a]);
24484           switch (axis) {
24485           case 'x' : case 'y' : break;
24486           case 'z' : is_3d = true; break;
24487           default :
24488             throw CImgArgumentException(_cimg_instance
24489                                         "get_gradient() : Invalid specified axis '%c'.",
24490                                         cimg_instance,
24491                                         axis);
24492           }
24493         }
24494       } else is_3d = (_depth>1);
24495       if (is_3d) {
24496         CImg<Tfloat>(_width,_height,_depth,_spectrum).move_to(grad);
24497         Tfloat *ptrd2 = grad[2]._data;
24498         switch (scheme) { // 3d.
24499         case -1 : { // Backward finite differences.
24500           CImg_3x3x3(I,Tfloat);
24501           cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
24502             *(ptrd0++) = Iccc - Ipcc;
24503             *(ptrd1++) = Iccc - Icpc;
24504             *(ptrd2++) = Iccc - Iccp;
24505           }
24506         } break;
24507         case 1 : { // Forward finite differences.
24508           CImg_2x2x2(I,Tfloat);
24509           cimg_forC(*this,c) cimg_for2x2x2(*this,x,y,z,c,I,Tfloat) {
24510             *(ptrd0++) = Incc - Iccc;
24511             *(ptrd1++) = Icnc - Iccc;
24512             *(ptrd2++) = Iccn - Iccc;
24513           }
24514         } break;
24515         case 4 : { // Using Deriche filter with low standard variation.
24516           grad[0] = get_deriche(0,1,'x');
24517           grad[1] = get_deriche(0,1,'y');
24518           grad[2] = get_deriche(0,1,'z');
24519         } break;
24520         default : { // Central finite differences.
24521           CImg_3x3x3(I,Tfloat);
24522           cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
24523             *(ptrd0++) = (Incc - Ipcc)/2;
24524             *(ptrd1++) = (Icnc - Icpc)/2;
24525             *(ptrd2++) = (Iccn - Iccp)/2;
24526           }
24527         }
24528         }
24529       } else switch (scheme) { // 2d.
24530       case -1 : { // Backward finite differences.
24531         CImg_3x3(I,Tfloat);
24532         cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
24533           *(ptrd0++) = Icc - Ipc;
24534           *(ptrd1++) = Icc - Icp;
24535         }
24536       } break;
24537       case 1 : { // Forward finite differences.
24538         CImg_2x2(I,Tfloat);
24539         cimg_forZC(*this,z,c) cimg_for2x2(*this,x,y,z,c,I,Tfloat) {
24540           *(ptrd0++) = Inc - Icc;
24541           *(ptrd1++) = Icn - Icc;
24542         }
24543       } break;
24544       case 2 : { // Sobel scheme.
24545         CImg_3x3(I,Tfloat);
24546         const Tfloat a = 1, b = 2;
24547         cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
24548           *(ptrd0++) = -a*Ipp - b*Ipc - a*Ipn + a*Inp + b*Inc + a*Inn;
24549           *(ptrd1++) = -a*Ipp - b*Icp - a*Inp + a*Ipn + b*Icn + a*Inn;
24550         }
24551       } break;
24552       case 3 : { // Rotation invariant mask.
24553         CImg_3x3(I,Tfloat);
24554         const Tfloat a = (Tfloat)(0.25f*(2-std::sqrt(2.0f))), b = (Tfloat)(0.5f*(std::sqrt(2.0f)-1));
24555         cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
24556           *(ptrd0++) = -a*Ipp - b*Ipc - a*Ipn + a*Inp + b*Inc + a*Inn;
24557           *(ptrd1++) = -a*Ipp - b*Icp - a*Inp + a*Ipn + b*Icn + a*Inn;
24558         }
24559       } break;
24560       case 4 : { // using Deriche filter with low standard variation
24561         grad[0] = get_deriche(0,1,'x');
24562         grad[1] = get_deriche(0,1,'y');
24563       } break;
24564       default : { // central finite differences
24565         CImg_3x3(I,Tfloat);
24566         cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
24567           *(ptrd0++) = (Inc - Ipc)/2;
24568           *(ptrd1++) = (Icn - Icp)/2;
24569         }
24570       }
24571       }
24572       if (!axes) return grad;
24573       CImgList<Tfloat> res;
24574       for (unsigned int l = 0; axes[l]; ++l) {
24575         const char axis = cimg::uncase(axes[l]);
24576         switch (axis) {
24577         case 'x' : res.insert(grad[0]); break;
24578         case 'y' : res.insert(grad[1]); break;
24579         case 'z' : res.insert(grad[2]); break;
24580         }
24581       }
24582       grad.assign();
24583       return res;
24584     }
24585 
24586     //! Return image hessian.
24587     /**
24588        \param axes Axes considered for the hessian computation, as a C-string (e.g "xy").
24589     **/
24590     CImgList<Tfloat> get_hessian(const char *const axes=0) const {
24591       CImgList<Tfloat> res;
24592       const char *naxes = axes, *const def_axes2d = "xxxyyy", *const def_axes3d = "xxxyxzyyyzzz";
24593       if (!axes) naxes = _depth>1?def_axes3d:def_axes2d;
24594       const unsigned int lmax = std::strlen(naxes);
24595       if (lmax%2)
24596         throw CImgArgumentException(_cimg_instance
24597                                     "get_hessian() : Invalid specified axes '%s'.",
24598                                     cimg_instance,
24599                                     naxes);
24600 
24601       res.assign(lmax/2,_width,_height,_depth,_spectrum);
24602       if (!cimg::strcasecmp(naxes,def_axes3d)) { // 3d
24603         Tfloat
24604           *ptrd0 = res[0]._data, *ptrd1 = res[1]._data, *ptrd2 = res[2]._data,
24605           *ptrd3 = res[3]._data, *ptrd4 = res[4]._data, *ptrd5 = res[5]._data;
24606         CImg_3x3x3(I,Tfloat);
24607         cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
24608           *(ptrd0++) = Ipcc + Incc - 2*Iccc;          // Ixx
24609           *(ptrd1++) = (Ippc + Innc - Ipnc - Inpc)/4; // Ixy
24610           *(ptrd2++) = (Ipcp + Incn - Ipcn - Incp)/4; // Ixz
24611           *(ptrd3++) = Icpc + Icnc - 2*Iccc;          // Iyy
24612           *(ptrd4++) = (Icpp + Icnn - Icpn - Icnp)/4; // Iyz
24613           *(ptrd5++) = Iccn + Iccp - 2*Iccc;          // Izz
24614         }
24615       } else if (!cimg::strcasecmp(naxes,def_axes2d)) { // 2d
24616         Tfloat *ptrd0 = res[0]._data, *ptrd1 = res[1]._data, *ptrd2 = res[2]._data;
24617         CImg_3x3(I,Tfloat);
24618         cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
24619           *(ptrd0++) = Ipc + Inc - 2*Icc;         // Ixx
24620           *(ptrd1++) = (Ipp + Inn - Ipn - Inp)/4; // Ixy
24621           *(ptrd2++) = Icp + Icn - 2*Icc;         // Iyy
24622         }
24623       } else for (unsigned int l = 0; l<lmax; ) { // Version with custom axes.
24624         const unsigned int l2 = l/2;
24625           char axis1 = naxes[l++], axis2 = naxes[l++];
24626           if (axis1>axis2) cimg::swap(axis1,axis2);
24627           bool valid_axis = false;
24628           Tfloat *ptrd = res[l2]._data;
24629           if (axis1=='x' && axis2=='x') { // Ixx
24630             valid_axis = true; CImg_3x3(I,Tfloat);
24631             cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Ipc + Inc - 2*Icc;
24632           }
24633           else if (axis1=='x' && axis2=='y') { // Ixy
24634             valid_axis = true; CImg_3x3(I,Tfloat);
24635             cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Ipp + Inn - Ipn - Inp)/4;
24636           }
24637           else if (axis1=='x' && axis2=='z') { // Ixz
24638             valid_axis = true; CImg_3x3x3(I,Tfloat);
24639             cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Ipcp + Incn - Ipcn - Incp)/4;
24640           }
24641           else if (axis1=='y' && axis2=='y') { // Iyy
24642             valid_axis = true; CImg_3x3(I,Tfloat);
24643             cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Icp + Icn - 2*Icc;
24644           }
24645           else if (axis1=='y' && axis2=='z') { // Iyz
24646             valid_axis = true; CImg_3x3x3(I,Tfloat);
24647             cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Icpp + Icnn - Icpn - Icnp)/4;
24648           }
24649           else if (axis1=='z' && axis2=='z') { // Izz
24650             valid_axis = true; CImg_3x3x3(I,Tfloat);
24651             cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Iccn + Iccp - 2*Iccc;
24652           }
24653           else if (!valid_axis)
24654             throw CImgArgumentException(_cimg_instance
24655                                         "get_hessian() : Invalid specified axes '%s'.",
24656                                         cimg_instance,
24657                                         naxes);
24658         }
24659       return res;
24660     }
24661 
24662     //! Compute image laplacian.
24663     CImg<T>& laplacian() {
24664       return get_laplacian().move_to(*this);
24665     }
24666 
24667     //! Compute image laplacian \newinstance.
24668     CImg<Tfloat> get_laplacian() const {
24669       if (is_empty()) return CImg<Tfloat>();
24670       CImg<Tfloat> res(_width,_height,_depth,_spectrum);
24671       Tfloat *ptrd = res._data;
24672       if (_depth>1) { // 3d
24673         CImg_3x3x3(I,Tfloat);
24674         cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat)
24675           *(ptrd++) = Incc + Ipcc + Icnc + Icpc + Iccn + Iccp - 6*Iccc;
24676       } else if (_height>1) { // 2d
24677         CImg_3x3(I,Tfloat);
24678         cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat)
24679           *(ptrd++) = Inc + Ipc + Icn + Icp - 4*Icc;
24680       } else { // 1d
24681         CImg_3x3(I,Tfloat);
24682         cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat)
24683           *(ptrd++) = Inc + Ipc - 2*Icc;
24684       }
24685       return res;
24686     }
24687 
24688     //! Compute the structure tensor field of an image.
24689     /**
24690        \param scheme Numerical scheme. Can be <tt>{ 0=central | 1=fwd/bwd1 | 2=fwd/bwd2 }</tt>
24691     **/
24692     CImg<T>& structure_tensors(const unsigned int scheme=2) {
24693       return get_structure_tensors(scheme).move_to(*this);
24694     }
24695 
24696     //! Compute the structure tensor field of an image \newinstance.
24697     CImg<Tfloat> get_structure_tensors(const unsigned int scheme=2) const {
24698       if (is_empty()) return *this;
24699       CImg<Tfloat> res;
24700       if (_depth>1) { // 3d
24701         res.assign(_width,_height,_depth,6,0);
24702         CImg_3x3x3(I,Tfloat);
24703         switch (scheme) {
24704         case 0 : { // classical central finite differences
24705           cimg_forC(*this,c) {
24706             Tfloat
24707               *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
24708               *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
24709             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
24710               const Tfloat
24711                 ix = (Incc - Ipcc)/2,
24712                 iy = (Icnc - Icpc)/2,
24713                 iz = (Iccn - Iccp)/2;
24714               *(ptrd0++)+=ix*ix;
24715               *(ptrd1++)+=ix*iy;
24716               *(ptrd2++)+=ix*iz;
24717               *(ptrd3++)+=iy*iy;
24718               *(ptrd4++)+=iy*iz;
24719               *(ptrd5++)+=iz*iz;
24720             }
24721           }
24722         } break;
24723         case 1 : { // Forward/backward finite differences (version 1).
24724           cimg_forC(*this,c) {
24725             Tfloat
24726               *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
24727               *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
24728             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
24729               const Tfloat
24730                 ixf = Incc - Iccc, ixb = Iccc - Ipcc,
24731                 iyf = Icnc - Iccc, iyb = Iccc - Icpc,
24732                 izf = Iccn - Iccc, izb = Iccc - Iccp;
24733               *(ptrd0++)+=(ixf*ixf + ixf*ixb + ixb*ixf + ixb*ixb)/4;
24734               *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
24735               *(ptrd2++)+=(ixf*izf + ixf*izb + ixb*izf + ixb*izb)/4;
24736               *(ptrd3++)+=(iyf*iyf + iyf*iyb + iyb*iyf + iyb*iyb)/4;
24737               *(ptrd4++)+=(iyf*izf + iyf*izb + iyb*izf + iyb*izb)/4;
24738               *(ptrd5++)+=(izf*izf + izf*izb + izb*izf + izb*izb)/4;
24739             }
24740           }
24741         } break;
24742         default : { // Forward/backward finite differences (version 2).
24743           cimg_forC(*this,c) {
24744             Tfloat
24745               *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
24746               *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
24747             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
24748               const Tfloat
24749                 ixf = Incc - Iccc, ixb = Iccc - Ipcc,
24750                 iyf = Icnc - Iccc, iyb = Iccc - Icpc,
24751                 izf = Iccn - Iccc, izb = Iccc - Iccp;
24752               *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2;
24753               *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
24754               *(ptrd2++)+=(ixf*izf + ixf*izb + ixb*izf + ixb*izb)/4;
24755               *(ptrd3++)+=(iyf*iyf + iyb*iyb)/2;
24756               *(ptrd4++)+=(iyf*izf + iyf*izb + iyb*izf + iyb*izb)/4;
24757               *(ptrd5++)+=(izf*izf + izb*izb)/2;
24758             }
24759           }
24760         } break;
24761         }
24762       } else { // 2d
24763         res.assign(_width,_height,_depth,3,0);
24764         CImg_3x3(I,Tfloat);
24765         switch (scheme) {
24766         case 0 : { // classical central finite differences
24767           cimg_forC(*this,c) {
24768             Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
24769             cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
24770               const Tfloat
24771                 ix = (Inc - Ipc)/2,
24772                 iy = (Icn - Icp)/2;
24773               *(ptrd0++)+=ix*ix;
24774               *(ptrd1++)+=ix*iy;
24775               *(ptrd2++)+=iy*iy;
24776             }
24777           }
24778         } break;
24779         case 1 : { // Forward/backward finite differences (version 1).
24780           cimg_forC(*this,c) {
24781             Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
24782             cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
24783               const Tfloat
24784                 ixf = Inc - Icc, ixb = Icc - Ipc,
24785                 iyf = Icn - Icc, iyb = Icc - Icp;
24786               *(ptrd0++)+=(ixf*ixf + ixf*ixb + ixb*iyf + ixb*ixb)/4;
24787               *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
24788               *(ptrd2++)+=(iyf*iyf + iyf*iyb + iyb*iyf + iyb*iyb)/4;
24789             }
24790           }
24791         } break;
24792         default : { // Forward/backward finite differences (version 2).
24793           cimg_forC(*this,c) {
24794             Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
24795             cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
24796               const Tfloat
24797                 ixf = Inc - Icc, ixb = Icc - Ipc,
24798                 iyf = Icn - Icc, iyb = Icc - Icp;
24799               *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2;
24800               *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
24801               *(ptrd2++)+=(iyf*iyf + iyb*iyb)/2;
24802             }
24803           }
24804         } break;
24805         }
24806       }
24807       return res;
24808     }
24809 
24810     //! Compute field of diffusion tensors for edge-preserving smoothing.
24811     /**
24812        \param sharpness Sharpness
24813        \param anisotropy Anisotropy
24814        \param alpha Standard deviation of the gradient blur.
24815        \param sigma Standard deviation of the structure tensor blur.
24816        \param is_sqrt Tells if the square root of the tensor field is computed instead.
24817     **/
24818     CImg<T>& diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f,
24819                                const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) {
24820       CImg<Tfloat> res;
24821       const float nsharpness = cimg::max(sharpness,1e-5f), power1 = (is_sqrt?0.5f:1)*nsharpness, power2 = power1/(1e-7f+1-anisotropy);
24822       blur(alpha).normalize(0,(T)255);
24823 
24824       if (_depth>1) { // 3d
24825         CImg<floatT> val(3), vec(3,3);
24826         get_structure_tensors().move_to(res).blur(sigma);
24827         Tfloat
24828           *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
24829           *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
24830         cimg_forXYZ(*this,x,y,z) {
24831           res.get_tensor_at(x,y,z).symmetric_eigen(val,vec);
24832           const float
24833             _l1 = val[2], _l2 = val[1], _l3 = val[0],
24834             l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0, l3 = _l3>0?_l3:0,
24835             ux = vec(0,0), uy = vec(0,1), uz = vec(0,2),
24836             vx = vec(1,0), vy = vec(1,1), vz = vec(1,2),
24837             wx = vec(2,0), wy = vec(2,1), wz = vec(2,2),
24838             n1 = (float)std::pow(1+l1+l2+l3,-power1),
24839             n2 = (float)std::pow(1+l1+l2+l3,-power2);
24840           *(ptrd0++) = n1*(ux*ux + vx*vx) + n2*wx*wx;
24841           *(ptrd1++) = n1*(ux*uy + vx*vy) + n2*wx*wy;
24842           *(ptrd2++) = n1*(ux*uz + vx*vz) + n2*wx*wz;
24843           *(ptrd3++) = n1*(uy*uy + vy*vy) + n2*wy*wy;
24844           *(ptrd4++) = n1*(uy*uz + vy*vz) + n2*wy*wz;
24845           *(ptrd5++) = n1*(uz*uz + vz*vz) + n2*wz*wz;
24846         }
24847       } else { // for 2d images
24848         CImg<floatT> val(2), vec(2,2);
24849         get_structure_tensors().move_to(res).blur(sigma);
24850         Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
24851         cimg_forXY(*this,x,y) {
24852           res.get_tensor_at(x,y).symmetric_eigen(val,vec);
24853           const float
24854             _l1 = val[1], _l2 = val[0],
24855             l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0,
24856             ux = vec(1,0), uy = vec(1,1),
24857             vx = vec(0,0), vy = vec(0,1),
24858             n1 = (float)std::pow(1+l1+l2,-power1),
24859             n2 = (float)std::pow(1+l1+l2,-power2);
24860           *(ptrd0++) = n1*ux*ux + n2*vx*vx;
24861           *(ptrd1++) = n1*ux*uy + n2*vx*vy;
24862           *(ptrd2++) = n1*uy*uy + n2*vy*vy;
24863         }
24864       }
24865       return res.move_to(*this);
24866     }
24867 
24868     //! Compute field of diffusion tensors for edge-preserving smoothing \newinstance.
24869     CImg<Tfloat> get_diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f,
24870                                        const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) const {
24871       return CImg<Tfloat>(*this,false).diffusion_tensors(sharpness,anisotropy,alpha,sigma,is_sqrt);
24872     }
24873 
24874     //! Estimate displacement field between two images.
24875     /**
24876        \param source Reference image.
24877        \param smoothness Smoothness of estimated displacement field.
24878        \param precision Precision required for algorithm convergence.
24879        \param nb_scales Number of scales used to estimate the displacement field.
24880        \param iteration_max Maximum number of iterations allowed for one scale.
24881        \param is_backward : if false, match I2(X+U(X)) = I1(X), else match I2(X) = I1(X-U(X)).
24882     **/
24883     CImg<T>& displacement(const CImg<T>& source, const float smoothness=0.1f, const float precision=5.0f,
24884                           const unsigned int nb_scales=0, const unsigned int iteration_max=10000,
24885                           const bool is_backward=false) {
24886       return get_displacement(source,smoothness,precision,nb_scales,iteration_max,is_backward).move_to(*this);
24887     }
24888 
24889     //! Estimate displacement field between two images \newinstance.
24890     CImg<Tfloat> get_displacement(const CImg<T>& source,
24891                                   const float smoothness=0.1f, const float precision=5.0f,
24892                                   const unsigned int nb_scales=0, const unsigned int iteration_max=10000,
24893                                   const bool is_backward=false) const {
24894       if (is_empty() || !source) return +*this;
24895       if (!is_sameXYZC(source))
24896         throw CImgArgumentException(_cimg_instance
24897                                     "displacement() : Instance and source image (%u,%u,%u,%u,%p) have different dimensions.",
24898                                     cimg_instance,
24899                                     source._width,source._height,source._depth,source._spectrum,source._data);
24900       if (precision<0)
24901         throw CImgArgumentException(_cimg_instance
24902                                     "displacement() : Invalid specified precision %g "
24903                                     "(should be >=0)",
24904                                     cimg_instance,
24905                                     precision);
24906       const unsigned int _nb_scales = nb_scales>0?nb_scales:(unsigned int)(2*std::log((double)(cimg::max(_width,_height))));
24907       const float _precision = (float)std::pow(10.0,-(double)precision);
24908       float sm, sM = source.max_min(sm), tm, tM = max_min(tm);
24909       const float sdelta = sm==sM?1:(sM - sm), tdelta = tm==tM?1:(tM - tm);
24910       const bool is_3d = source._depth>1;
24911       CImg<floatT> U;
24912 
24913       for (int scale = _nb_scales-1; scale>=0; --scale) {
24914         const float factor = (float)std::pow(1.5,(double)scale);
24915         const unsigned int
24916           _sw = (unsigned int)(_width/factor), sw = _sw?_sw:1,
24917           _sh = (unsigned int)(_height/factor), sh = _sh?_sh:1,
24918           _sd = (unsigned int)(_depth/factor), sd = _sd?_sd:1;
24919         if (sw<5 && sh<5 && (!is_3d || sd<5)) continue;  // skip too small scales.
24920         const CImg<Tfloat>
24921           I1 = (source.get_resize(sw,sh,sd,-100,2)-=sm)/=sdelta,
24922           I2 = (get_resize(I1,2)-=tm)/=tdelta;
24923         if (U) (U*=1.5f).resize(I2._width,I2._height,I2._depth,-100,3);
24924         else U.assign(I2._width,I2._height,I2._depth,is_3d?3:2,0);
24925         float dt = 2, energy = cimg::type<float>::max();
24926         const CImgList<Tfloat> dI = is_backward?I1.get_gradient():I2.get_gradient();
24927 
24928         for (unsigned int iteration = 0; iteration<iteration_max; ++iteration) {
24929           float _energy = 0;
24930           if (is_3d) { // 3d version.
24931             if (smoothness>=0) cimg_for3XYZ(U,x,y,z) { // Isotropic regularization.
24932                 const float
24933                   X = is_backward?x - U(x,y,z,0):x + U(x,y,z,0),
24934                   Y = is_backward?y - U(x,y,z,1):y + U(x,y,z,1),
24935                   Z = is_backward?z - U(x,y,z,2):z + U(x,y,z,2);
24936                 float delta_I = 0, _energy_regul = 0;
24937                 if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1.linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c));
24938                 else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,z,c) - I2.linear_atXYZ(X,Y,Z,c));
24939                 cimg_forC(U,c) {
24940                   const float
24941                     Ux = 0.5f*(U(_n1x,y,z,c) - U(_p1x,y,z,c)),
24942                     Uy = 0.5f*(U(x,_n1y,z,c) - U(x,_p1y,z,c)),
24943                     Uz = 0.5f*(U(x,y,_n1z,c) - U(x,y,_p1z,c)),
24944                     Uxx = U(_n1x,y,z,c) + U(_p1x,y,z,c),
24945                     Uyy = U(x,_n1y,z,c) + U(x,_p1y,z,c),
24946                     Uzz = U(x,y,_n1z,c) + U(x,y,_p1z,c);
24947                   U(x,y,z,c) = (float)(U(x,y,z,c) + dt*(delta_I*dI[c].linear_atXYZ(X,Y,Z) + smoothness* ( Uxx + Uyy + Uzz)))/(1+6*smoothness*dt);
24948                   _energy_regul+=Ux*Ux + Uy*Uy + Uz*Uz;
24949                 }
24950                 _energy+=delta_I*delta_I + smoothness*_energy_regul;
24951               } else {
24952               const float nsmoothness = -smoothness;
24953               cimg_for3XYZ(U,x,y,z) { // Anisotropic regularization.
24954                 const float
24955                   X = is_backward?x - U(x,y,z,0):x + U(x,y,z,0),
24956                   Y = is_backward?y - U(x,y,z,1):y + U(x,y,z,1),
24957                   Z = is_backward?z - U(x,y,z,2):z + U(x,y,z,2);
24958                 float delta_I = 0, _energy_regul = 0;
24959                 if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1.linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c));
24960                 else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,z,c) - I2.linear_atXYZ(X,Y,Z,c));
24961                 cimg_forC(U,c) {
24962                   const float
24963                     Ux = 0.5f*(U(_n1x,y,z,c) - U(_p1x,y,z,c)),
24964                     Uy = 0.5f*(U(x,_n1y,z,c) - U(x,_p1y,z,c)),
24965                     Uz = 0.5f*(U(x,y,_n1z,c) - U(x,y,_p1z,c)),
24966                     N2 = Ux*Ux + Uy*Uy + Uz*Uz,
24967                     N = std::sqrt(N2),
24968                     N3 = 1e-5 + N2*N,
24969                     coef_a = (1 - Ux*Ux/N2)/N,
24970                     coef_b = -2*Ux*Uy/N3,
24971                     coef_c = -2*Ux*Uz/N3,
24972                     coef_d = (1 - Uy*Uy/N2)/N,
24973                     coef_e = -2*Uy*Uz/N3,
24974                     coef_f = (1 - Uz*Uz/N2)/N,
24975                     Uxx = U(_n1x,y,z,c) + U(_p1x,y,z,c),
24976                     Uyy = U(x,_n1y,z,c) + U(x,_p1y,z,c),
24977                     Uzz = U(x,y,_n1z,c) + U(x,y,_p1z,c),
24978                     Uxy = 0.25f*(U(_n1x,_n1y,z,c) + U(_p1x,_p1y,z,c) - U(_n1x,_p1y,z,c) - U(_n1x,_p1y,z,c)),
24979                     Uxz = 0.25f*(U(_n1x,y,_n1z,c) + U(_p1x,y,_p1z,c) - U(_n1x,y,_p1z,c) - U(_n1x,y,_p1z,c)),
24980                     Uyz = 0.25f*(U(x,_n1y,_n1z,c) + U(x,_p1y,_p1z,c) - U(x,_n1y,_p1z,c) - U(x,_n1y,_p1z,c));
24981                   U(x,y,z,c) = (float)(U(x,y,z,c) + dt*(delta_I*dI[c].linear_atXYZ(X,Y,Z) +
24982                                                         nsmoothness* ( coef_a*Uxx + coef_b*Uxy + coef_c*Uxz + coef_d*Uyy + coef_e*Uyz + coef_f*Uzz ))
24983                                        )/(1+2*(coef_a+coef_d+coef_f)*nsmoothness*dt);
24984                   _energy_regul+=N;
24985                 }
24986                 _energy+=delta_I*delta_I + nsmoothness*_energy_regul;
24987               }
24988             }
24989           } else { // 2d version.
24990             if (smoothness>=0) cimg_for3XY(U,x,y) { // Isotropic regularization.
24991                 const float
24992                   X = is_backward?x - U(x,y,0):x + U(x,y,0),
24993                   Y = is_backward?y - U(x,y,1):y + U(x,y,1);
24994                 float delta_I = 0, _energy_regul = 0;
24995                 if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1.linear_atXY(X,Y,c) - I2(x,y,c));
24996                 else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,c) - I2.linear_atXY(X,Y,c));
24997                 cimg_forC(U,c) {
24998                   const float
24999                     Ux = 0.5f*(U(_n1x,y,c) - U(_p1x,y,c)),
25000                     Uy = 0.5f*(U(x,_n1y,c) - U(x,_p1y,c)),
25001                     Uxx = U(_n1x,y,c) + U(_p1x,y,c),
25002                     Uyy = U(x,_n1y,c) + U(x,_p1y,c);
25003                   U(x,y,c) = (float)(U(x,y,c) + dt*(delta_I*dI[c].linear_atXY(X,Y) + smoothness*( Uxx + Uyy )))/(1+4*smoothness*dt);
25004                   _energy_regul+=Ux*Ux + Uy*Uy;
25005                 }
25006                 _energy+=delta_I*delta_I + smoothness*_energy_regul;
25007               } else {
25008               const float nsmoothness = -smoothness;
25009               cimg_for3XY(U,x,y) { // Anisotropic regularization.
25010                 const float
25011                   X = is_backward?x - U(x,y,0):x + U(x,y,0),
25012                   Y = is_backward?y - U(x,y,1):y + U(x,y,1);
25013                 float delta_I = 0, _energy_regul = 0;
25014                 if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1.linear_atXY(X,Y,c) - I2(x,y,c));
25015                 else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,c) - I2.linear_atXY(X,Y,c));
25016                 cimg_forC(U,c) {
25017                   const float
25018                     Ux = 0.5f*(U(_n1x,y,c) - U(_p1x,y,c)),
25019                     Uy = 0.5f*(U(x,_n1y,c) - U(x,_p1y,c)),
25020                     N2 = Ux*Ux + Uy*Uy,
25021                     N = std::sqrt(N2),
25022                     N3 = 1e-5 + N2*N,
25023                     coef_a = Uy*Uy/N3,
25024                     coef_b = -2*Ux*Uy/N3,
25025                     coef_c = Ux*Ux/N3,
25026                     Uxx = U(_n1x,y,c) + U(_p1x,y,c),
25027                     Uyy = U(x,_n1y,c) + U(x,_p1y,c),
25028                     Uxy = 0.25f*(U(_n1x,_n1y,c) + U(_p1x,_p1y,c) - U(_n1x,_p1y,c) - U(_n1x,_p1y,c));
25029                   U(x,y,c) = (float)(U(x,y,c) + dt*(delta_I*dI[c].linear_atXY(X,Y) + nsmoothness*( coef_a*Uxx + coef_b*Uxy + coef_c*Uyy )))/(1+2*(coef_a+coef_c)*nsmoothness*dt);
25030                   _energy_regul+=N;
25031                 }
25032                 _energy+=delta_I*delta_I + nsmoothness*_energy_regul;
25033               }
25034             }
25035           }
25036           const float d_energy = (_energy - energy)/(sw*sh*sd);
25037           if (d_energy<=0 && -d_energy<_precision) break;
25038           if (d_energy>0) dt*=0.5f;
25039           energy = _energy;
25040         }
25041       }
25042       return U;
25043     }
25044 
25045     //! Compute distance to a specified value.
25046     /**
25047         \param value Reference value.
25048         \param metric Type of metric. Can be <tt>{ 0=Chebyshev | 1=Manhattan | 2=Euclidean | 3=Squared-euclidean }</tt>.
25049         \note
25050         The distance transform implementation has been submitted by A. Meijster, and implements
25051         the article 'W.H. Hesselink, A. Meijster, J.B.T.M. Roerdink,
25052                      "A general algorithm for computing distance transforms in linear time.",
25053                      In: Mathematical Morphology and its Applications to Image and Signal Processing,
25054                      J. Goutsias, L. Vincent, and D.S. Bloomberg (eds.), Kluwer, 2000, pp. 331-340.'
25055          The submitted code has then been modified to fit CImg coding style and constraints.
25056     **/
25057     CImg<T>& distance(const T value, const unsigned int metric=2) {
25058       if (is_empty()) return *this;
25059       bool is_value = false;
25060       cimg_for(*this,ptr,T) *ptr = (T)(*ptr==value?is_value=true,0:999999999);
25061       if (!is_value) return fill(cimg::type<T>::max());
25062       switch (metric) {
25063       case 0 : return _distance_core(_distance_sep_cdt,_distance_dist_cdt);          // Chebyshev.
25064       case 1 : return _distance_core(_distance_sep_mdt,_distance_dist_mdt);          // Manhattan.
25065       case 3 : return _distance_core(_distance_sep_edt,_distance_dist_edt);          // Euclidean.
25066       default : return _distance_core(_distance_sep_edt,_distance_dist_edt).sqrt();  // Squared Euclidean.
25067       }
25068       return *this;
25069     }
25070 
25071     //! Compute distance to a specified value \newinstance.
25072     CImg<Tfloat> get_distance(const T value, const unsigned int metric=2) const {
25073       return CImg<Tfloat>(*this,false).distance((Tfloat)value,metric);
25074     }
25075 
25076     static long _distance_sep_edt(const long i, const long u, const long *const g) {
25077       return (u*u-i*i+g[u]-g[i])/(2*(u-i));
25078     }
25079 
25080     static long _distance_dist_edt(const long x, const long i, const long *const g) {
25081       return (x-i)*(x-i) + g[i];
25082     }
25083 
25084     static long _distance_sep_mdt(const long i, const long u, const long *const g) {
25085       return (u-i<=g[u]-g[i]?999999999:(g[u]-g[i]+u+i)/2);
25086     }
25087 
25088     static long _distance_dist_mdt(const long x, const long i, const long *const g) {
25089       return (x<i?i-x:x-i) + g[i];
25090     }
25091 
25092     static long _distance_sep_cdt(const long i, const long u, const long *const g) {
25093       const long h = (i+u)/2;
25094       if (g[i]<=g[u]) { return h<i+g[u]?i+g[u]:h; }
25095       return h<u-g[i]?h:u-g[i];
25096     }
25097 
25098     static long _distance_dist_cdt(const long x, const long i, const long *const g) {
25099       const long d = x<i?i-x:x-i;
25100       return d<g[i]?g[i]:d;
25101     }
25102 
25103     static void _distance_scan(const unsigned int len,
25104                                const long *const g,
25105                                long (*const sep)(const long, const long, const long *const),
25106                                long (*const f)(const long, const long, const long *const),
25107                                long *const s,
25108                                long *const t,
25109                                long *const dt) {
25110       long q = s[0] = t[0] = 0;
25111       for (int u = 1; u<(int)len; ++u) { // Forward scan.
25112         while ((q>=0) && f(t[q],s[q],g)>f(t[q],u,g)) { --q; }
25113         if (q<0) { q = 0; s[0] = u; }
25114         else { const long w = 1 + sep(s[q], u, g); if (w<(long)len) { ++q; s[q] = u; t[q] = w; }}
25115       }
25116       for (int u = (int)len-1; u>=0; --u) { dt[u] = f(u,s[q],g); if (u==t[q]) --q; } // Backward scan.
25117     }
25118 
25119     CImg<T>& _distance_core(long (*const sep)(const long, const long, const long *const),
25120                             long (*const f)(const long, const long, const long *const)) {
25121       const unsigned long wh = (unsigned long)_width*_height;
25122       cimg_forC(*this,c) {
25123         CImg<longT> g(_width), dt(_width), s(_width), t(_width);
25124         CImg<T> img = get_shared_channel(c);
25125         cimg_forYZ(*this,y,z) { // Over X-direction.
25126           cimg_forX(*this,x) g[x] = (long)img(x,y,z,0,wh);
25127           _distance_scan(_width,g,sep,f,s,t,dt);
25128           cimg_forX(*this,x) img(x,y,z,0,wh) = (T)dt[x];
25129         }
25130         g.assign(_height); dt.assign(_height); s.assign(_height); t.assign(_height);
25131         cimg_forXZ(*this,x,z) { // Over Y-direction.
25132           cimg_forY(*this,y) g[y] = (long)img(x,y,z,0,wh);
25133           _distance_scan(_height,g,sep,f,s,t,dt);
25134           cimg_forY(*this,y) img(x,y,z,0,wh) = (T)dt[y];
25135         }
25136         if (_depth>1) {
25137           g.assign(_depth); dt.assign(_depth); s.assign(_depth); t.assign(_depth);
25138           cimg_forXY(*this,x,y) { // Over Z-direction.
25139             cimg_forZ(*this,z) g[z] = (long)img(x,y,z,0,wh);
25140             _distance_scan(_depth,g,sep,f,s,t,dt);
25141             cimg_forZ(*this,z) img(x,y,z,0,wh) = (T)dt[z];
25142           }
25143         }
25144       }
25145       return *this;
25146     }
25147 
25148     //! Compute chamfer distance to a specified value, with a custom metric.
25149     /**
25150        \param value Reference value.
25151        \param metric_mask Metric mask.
25152        \note The algorithm code has been initially proposed by A. Meijster, and modified by D. Tschumperlé.
25153     **/
25154     template<typename t>
25155     CImg<T>& distance(const T value, const CImg<t>& metric_mask) {
25156       if (is_empty()) return *this;
25157       bool is_value = false;
25158       cimg_for(*this,ptr,T) *ptr = (T)(*ptr==value?is_value=true,0:999999999);
25159       if (!is_value) return fill(cimg::type<T>::max());
25160       const unsigned long wh = (unsigned long)_width*_height;
25161       cimg_forC(*this,c) {
25162         CImg<T> img = get_shared_channel(c);
25163         cimg_forXYZ(metric_mask,dx,dy,dz) {
25164           const t weight = metric_mask(dx,dy,dz);
25165           if (weight) {
25166             for (int z = dz, nz = 0; z<depth(); ++z,++nz) { // Forward scan.
25167               for (int y = dy , ny = 0; y<height(); ++y,++ny) {
25168                 for (int x = dx, nx = 0; x<width(); ++x,++nx) {
25169                   const T dd = img(nx,ny,nz,0,wh) + weight;
25170                   if (dd<img(x,y,z,0,wh)) img(x,y,z,0,wh) = dd;
25171                 }
25172               }
25173             }
25174             for (int z = depth() - 1 - dz, nz = depth() - 1; z>=0; --z,--nz) { // Backward scan.
25175               for (int y = height() - 1 - dy, ny = height() - 1; y>=0; --y,--ny) {
25176                 for (int x = width() - 1 - dx, nx = width() - 1; x>=0; --x,--nx) {
25177                   const T dd = img(nx,ny,nz,0,wh) + weight;
25178                   if (dd<img(x,y,z,0,wh)) img(x,y,z,0,wh) = dd;
25179                 }
25180               }
25181             }
25182           }
25183         }
25184       }
25185       return *this;
25186     }
25187 
25188     //! Compute chamfer distance to a specified value, with a custom metric \newinstance.
25189     template<typename t>
25190     CImg<Tfloat> get_distance(const T value, const CImg<t>& metric_mask) const {
25191       return CImg<Tfloat>(*this,false).distance(value,metric_mask);
25192     }
25193 
25194     //! Compute distance map to one source point.
25195     /**
25196        \param x X-coordinate of the source point.
25197        \param y Y-coordinate of the source point.
25198        \param z Z-coordinate of the source point.
25199        \note At input, image instance represents a field of potentials.
25200     **/
25201     CImg<T>& distance_dijkstra(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
25202       return get_distance_dijkstra(x,y,z).move_to(*this);
25203     }
25204 
25205     //! Compute distance map to one source point \newinstance.
25206     CImg<Tfloat> get_distance_dijkstra(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const {
25207       if (is_empty()) return *this;
25208       if (!containsXYZC(x,y,z,0))
25209         throw CImgArgumentException(_cimg_instance
25210                                     "distance_dijkstra() : image instance does not contain specified starting point (%u,%u,%u).",
25211                                     cimg_instance,
25212                                     x,y,z);
25213       if (_spectrum!=1)
25214         throw CImgInstanceException(_cimg_instance
25215                                     "distance_dijkstra() : image instance is not a scalar image.",
25216                                     cimg_instance);
25217       CImg<Tfloat> res(_width,_height,_depth,2);
25218       CImg<boolT> in_queue(_width,_height,_depth,1,0);
25219       CImg<Tint> Q;
25220       unsigned int sizeQ = 0;
25221 
25222       // Put specified point in priority queue.
25223       Q._priority_queue_insert(in_queue,sizeQ,0,x,y,z);
25224       res(x,y,z) = 0; res(x,y,z,1) = 0;
25225 
25226       // Start distance propagation.
25227       while (sizeQ) {
25228 
25229         // Get and remove point with minimal potential from the queue.
25230         const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
25231         const Tfloat potential = (Tfloat)-Q(0,0);
25232         Q._priority_queue_remove(sizeQ);
25233 
25234         // Update neighbors.
25235         Tfloat npot = 0;
25236         if (x-1>=0 && Q._priority_queue_insert(in_queue,sizeQ,-(npot=(*this)(x-1,y,z)+potential),x-1,y,z)) {
25237           res(x-1,y,z) = npot; res(x-1,y,z,1) = 2;
25238         }
25239         if (x+1<width() && Q._priority_queue_insert(in_queue,sizeQ,-(npot=(*this)(x+1,y,z)+potential),x+1,y,z)) {
25240           res(x+1,y,z) = npot; res(x+1,y,z,1) = 1;
25241         }
25242         if (y-1>=0 && Q._priority_queue_insert(in_queue,sizeQ,-(npot=(*this)(x,y-1,z)+potential),x,y-1,z)) {
25243           res(x,y-1,z) = npot; res(x,y-1,z,1) = 4;
25244         }
25245         if (y+1<height() && Q._priority_queue_insert(in_queue,sizeQ,-(npot=(*this)(x,y+1,z)+potential),x,y+1,z)) {
25246           res(x,y+1,z) = npot; res(x,y+1,z,1) = 3;
25247         }
25248         if (z-1>=0 && Q._priority_queue_insert(in_queue,sizeQ,-(npot=(*this)(x,y,z-1)+potential),x,y,z-1)) {
25249           res(x,y,z-1) = npot; res(x,y,z+1,1) = 6;
25250         }
25251         if (z+1<depth() && Q._priority_queue_insert(in_queue,sizeQ,-(npot=(*this)(x,y,z+1)+potential),x,y,z+1)) {
25252           res(x,y,z+1) = npot; res(x,y,z+1,1) = 5;
25253         }
25254       }
25255       return res;
25256     }
25257 
25258     //! Compute distance function to 0-valued isophotes, using the Eikonal PDE.
25259     /**
25260        \param nb_iterations Number of PDE iterations.
25261        \param band_size Size of the narrow band.
25262        \param time_step Time step of the PDE iterations.
25263     **/
25264     CImg<T>& distance_eikonal(const unsigned int nb_iterations, const float band_size=0, const float time_step=0.5f) {
25265       if (is_empty()) return *this;
25266       CImg<Tfloat> velocity(*this);
25267       for (unsigned int iteration = 0; iteration<nb_iterations; ++iteration) {
25268         Tfloat *ptrd = velocity._data, veloc_max = 0;
25269         if (_depth>1) { // 3d
25270           CImg_3x3x3(I,Tfloat);
25271           cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) if (band_size<=0 || cimg::abs(Iccc)<band_size) {
25272             const Tfloat
25273               gx = (Incc - Ipcc)/2,
25274               gy = (Icnc - Icpc)/2,
25275               gz = (Iccn - Iccp)/2,
25276               sgn = -cimg::sign(Iccc),
25277               ix = gx*sgn>0?(Incc - Iccc):(Iccc - Ipcc),
25278               iy = gy*sgn>0?(Icnc - Iccc):(Iccc - Icpc),
25279               iz = gz*sgn>0?(Iccn - Iccc):(Iccc - Iccp),
25280               ng = (Tfloat)(1e-5f + std::sqrt(gx*gx + gy*gy + gz*gz)),
25281               ngx = gx/ng,
25282               ngy = gy/ng,
25283               ngz = gz/ng,
25284               veloc = sgn*(ngx*ix + ngy*iy + ngz*iz - 1);
25285             *(ptrd++) = veloc;
25286             if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
25287           } else *(ptrd++) = 0;
25288         } else { // 2d version
25289           CImg_3x3(I,Tfloat);
25290           cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) if (band_size<=0 || cimg::abs(Icc)<band_size) {
25291             const Tfloat
25292               gx = (Inc - Ipc)/2,
25293               gy = (Icn - Icp)/2,
25294               sgn = -cimg::sign(Icc),
25295               ix = gx*sgn>0?(Inc - Icc):(Icc - Ipc),
25296               iy = gy*sgn>0?(Icn - Icc):(Icc - Icp),
25297               ng = (Tfloat)(1e-5f + std::sqrt(gx*gx + gy*gy)),
25298               ngx = gx/ng,
25299               ngy = gy/ng,
25300               veloc = sgn*(ngx*ix + ngy*iy - 1);
25301             *(ptrd++) = veloc;
25302             if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
25303           } else *(ptrd++) = 0;
25304         }
25305         if (veloc_max>0) *this+=(velocity*=time_step/veloc_max);
25306       }
25307       return *this;
25308     }
25309 
25310     //! Compute distance function to 0-valued isophotes, using the Eikonal PDE \newinstance.
25311     CImg<Tfloat> get_distance_eikonal(const unsigned int nb_iterations, const float band_size=0, const float time_step=0.5f) const {
25312       return CImg<Tfloat>(*this,false).distance_eikonal(nb_iterations,band_size,time_step);
25313     }
25314 
25315     //! Compute Haar multiscale wavelet transform.
25316     /**
25317        \param axis Axis considered for the transform.
25318        \param invert Set inverse of direct transform.
25319        \param nb_scales Number of scales used for the transform.
25320     **/
25321     CImg<T>& haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) {
25322       return get_haar(axis,invert,nb_scales).move_to(*this);
25323     }
25324 
25325     //! Compute Haar multiscale wavelet transform \newinstance.
25326     CImg<Tfloat> get_haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) const {
25327       if (is_empty() || !nb_scales) return +*this;
25328       CImg<Tfloat> res;
25329       const Tfloat sqrt2 = std::sqrt(2);
25330       if (nb_scales==1) {
25331         switch (cimg::uncase(axis)) { // Single scale transform
25332         case 'x' : {
25333           const unsigned int w = _width/2;
25334           if (w) {
25335             if (w%2)
25336               throw CImgInstanceException(_cimg_instance
25337                                           "haar() : Sub-image width %u is not even at scale %u.",
25338                                           cimg_instance,
25339                                           w);
25340 
25341             res.assign(_width,_height,_depth,_spectrum);
25342             if (invert) cimg_forYZC(*this,y,z,c) { // Inverse transform along X
25343               for (unsigned int x = 0, xw = w, x2 = 0; x<w; ++x, ++xw) {
25344                 const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(xw,y,z,c);
25345                 res(x2++,y,z,c) = val0 - val1;
25346                 res(x2++,y,z,c) = val0 + val1;
25347               }
25348             } else cimg_forYZC(*this,y,z,c) { // Direct transform along X
25349               for (unsigned int x = 0, xw = w, x2 = 0; x<w; ++x, ++xw) {
25350                 const Tfloat val0 = (Tfloat)(*this)(x2++,y,z,c), val1 = (Tfloat)(*this)(x2++,y,z,c);
25351                 res(x,y,z,c) = (val0 + val1)/2;
25352                 res(xw,y,z,c) = (val1 - val0)/2;
25353               }
25354             }
25355           } else return *this;
25356         } break;
25357         case 'y' : {
25358           const unsigned int h = _height/2;
25359           if (h) {
25360             if (h%2)
25361               throw CImgInstanceException(_cimg_instance
25362                                           "haar() : Sub-image height %u is not even at scale %u.",
25363                                           cimg_instance,
25364                                           h);
25365 
25366             res.assign(_width,_height,_depth,_spectrum);
25367             if (invert) cimg_forXZC(*this,x,z,c) { // Inverse transform along Y
25368               for (unsigned int y = 0, yh = h, y2 = 0; y<h; ++y, ++yh) {
25369                 const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(x,yh,z,c);
25370                 res(x,y2++,z,c) = (val0 - val1)/sqrt2;
25371                 res(x,y2++,z,c) = (val0 + val1)/sqrt2;
25372               }
25373             } else cimg_forXZC(*this,x,z,c) {
25374               for (unsigned int y = 0, yh = h, y2 = 0; y<h; ++y, ++yh) { // Direct transform along Y
25375                 const Tfloat val0 = (Tfloat)(*this)(x,y2++,z,c), val1 = (Tfloat)(*this)(x,y2++,z,c);
25376                 res(x,y,z,c)  = (val0 + val1)/sqrt2;
25377                 res(x,yh,z,c) = (val1 - val0)/sqrt2;
25378               }
25379             }
25380           } else return *this;
25381         } break;
25382         case 'z' : {
25383           const unsigned int d = _depth/2;
25384           if (d) {
25385             if (d%2)
25386               throw CImgInstanceException(_cimg_instance
25387                                           "haar() : Sub-image depth %u is not even at scale %u.",
25388                                           cimg_instance,
25389                                           d);
25390 
25391             res.assign(_width,_height,_depth,_spectrum);
25392             if (invert) cimg_forXYC(*this,x,y,c) { // Inverse transform along Z
25393               for (unsigned int z = 0, zd = d, z2 = 0; z<d; ++z, ++zd) {
25394                 const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(x,y,zd,c);
25395                 res(x,y,z2++,c) = (val0 - val1)/sqrt2;
25396                 res(x,y,z2++,c) = (val0 + val1)/sqrt2;
25397               }
25398             } else cimg_forXYC(*this,x,y,c) {
25399               for (unsigned int z = 0, zd = d, z2 = 0; z<d; ++z, ++zd) { // Direct transform along Z
25400                 const Tfloat val0 = (Tfloat)(*this)(x,y,z2++,c), val1 = (Tfloat)(*this)(x,y,z2++,c);
25401                 res(x,y,z,c)  = (val0 + val1)/sqrt2;
25402                 res(x,y,zd,c) = (val1 - val0)/sqrt2;
25403               }
25404             }
25405           } else return *this;
25406         } break;
25407         default :
25408           throw CImgArgumentException(_cimg_instance
25409                                       "haar() : Invalid specified axis '%c' "
25410                                       "(should be { x | y | z }).",
25411                                       cimg_instance,
25412                                       axis);
25413         }
25414       } else { // Multi-scale version
25415         if (invert) {
25416           res.assign(*this);
25417           switch (cimg::uncase(axis)) {
25418           case 'x' : {
25419             unsigned int w = _width;
25420             for (unsigned int s = 1; w && s<nb_scales; ++s) w/=2;
25421             for (w = w?w:1; w<=_width; w*=2) res.draw_image(res.get_crop(0,w-1).get_haar('x',true,1));
25422           } break;
25423           case 'y' : {
25424             unsigned int h = _width;
25425             for (unsigned int s = 1; h && s<nb_scales; ++s) h/=2;
25426             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));
25427           } break;
25428           case 'z' : {
25429             unsigned int d = _depth;
25430             for (unsigned int s = 1; d && s<nb_scales; ++s) d/=2;
25431             for (d = d?d:1; d<=_depth; d*=2) res.draw_image(res.get_crop(0,0,0,_width-1,_height-1,d-1).get_haar('z',true,1));
25432           } break;
25433           default :
25434             throw CImgArgumentException(_cimg_instance
25435                                         "haar() : Invalid specified axis '%c' "
25436                                         "(should be { x | y | z }).",
25437                                         cimg_instance,
25438                                         axis);
25439           }
25440         } else { // Direct transform
25441           res = get_haar(axis,false,1);
25442           switch (cimg::uncase(axis)) {
25443           case 'x' : {
25444             for (unsigned int s = 1, w = _width/2; w && s<nb_scales; ++s, w/=2) res.draw_image(res.get_crop(0,w-1).get_haar('x',false,1));
25445           } break;
25446           case 'y' : {
25447             for (unsigned int s = 1, h = _height/2; h && s<nb_scales; ++s, h/=2) res.draw_image(res.get_crop(0,0,_width-1,h-1).get_haar('y',false,1));
25448           } break;
25449           case 'z' : {
25450             for (unsigned int s = 1, d = _depth/2; d && s<nb_scales; ++s, d/=2) res.draw_image(res.get_crop(0,0,0,_width-1,_height-1,d-1).get_haar('z',false,1));
25451           } break;
25452           default :
25453             throw CImgArgumentException(_cimg_instance
25454                                         "haar() : Invalid specified axis '%c' "
25455                                         "(should be { x | y | z }).",
25456                                         cimg_instance,
25457                                         axis);
25458           }
25459         }
25460       }
25461       return res;
25462     }
25463 
25464     //! Compute Haar multiscale wavelet transform \overloading.
25465     /**
25466        \param invert Set inverse of direct transform.
25467        \param nb_scales Number of scales used for the transform.
25468     **/
25469     CImg<T>& haar(const bool invert=false, const unsigned int nb_scales=1) {
25470       return get_haar(invert,nb_scales).move_to(*this);
25471     }
25472 
25473     //! Compute Haar multiscale wavelet transform \newinstance.
25474     CImg<Tfloat> get_haar(const bool invert=false, const unsigned int nb_scales=1) const {
25475       CImg<Tfloat> res;
25476 
25477       if (nb_scales==1) { // Single scale transform
25478         if (_width>1) get_haar('x',invert,1).move_to(res);
25479         if (_height>1) { if (res) res.haar('y',invert,1); else get_haar('y',invert,1).move_to(res); }
25480         if (_depth>1) { if (res) res.haar('z',invert,1); else get_haar('z',invert,1).move_to(res); }
25481         if (res) return res;
25482       } else { // Multi-scale transform
25483         if (invert) { // Inverse transform
25484           res.assign(*this);
25485           if (_width>1) {
25486             if (_height>1) {
25487               if (_depth>1) {
25488                 unsigned int w = _width, h = _height, d = _depth; for (unsigned int s = 1; w && h && d && s<nb_scales; ++s) { w/=2; h/=2; d/=2; }
25489                 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)
25490                   res.draw_image(res.get_crop(0,0,0,w-1,h-1,d-1).get_haar(true,1));
25491               } else {
25492                 unsigned int w = _width, h = _height; for (unsigned int s = 1; w && h && s<nb_scales; ++s) { w/=2; h/=2; }
25493                 for (w = w?w:1, h = h?h:1; w<=_width && h<=_height; w*=2, h*=2)
25494                   res.draw_image(res.get_crop(0,0,0,w-1,h-1,0).get_haar(true,1));
25495               }
25496             } else {
25497               if (_depth>1) {
25498                 unsigned int w = _width, d = _depth; for (unsigned int s = 1; w && d && s<nb_scales; ++s) { w/=2; d/=2; }
25499                 for (w = w?w:1, d = d?d:1; w<=_width && d<=_depth; w*=2, d*=2)
25500                   res.draw_image(res.get_crop(0,0,0,w-1,0,d-1).get_haar(true,1));
25501               } else {
25502                 unsigned int w = _width; for (unsigned int s = 1; w && s<nb_scales; ++s) w/=2;
25503                 for (w = w?w:1; w<=_width; w*=2)
25504                   res.draw_image(res.get_crop(0,0,0,w-1,0,0).get_haar(true,1));
25505               }
25506             }
25507           } else {
25508             if (_height>1) {
25509               if (_depth>1) {
25510                 unsigned int h = _height, d = _depth; for (unsigned int s = 1; h && d && s<nb_scales; ++s) { h/=2; d/=2; }
25511                 for (h = h?h:1, d = d?d:1; h<=_height && d<=_depth; h*=2, d*=2)
25512                   res.draw_image(res.get_crop(0,0,0,0,h-1,d-1).get_haar(true,1));
25513               } else {
25514                 unsigned int h = _height; for (unsigned int s = 1; h && s<nb_scales; ++s) h/=2;
25515                 for (h = h?h:1; h<=_height; h*=2)
25516                   res.draw_image(res.get_crop(0,0,0,0,h-1,0).get_haar(true,1));
25517               }
25518             } else {
25519               if (_depth>1) {
25520                 unsigned int d = _depth; for (unsigned int s = 1; d && s<nb_scales; ++s) d/=2;
25521                 for (d = d?d:1; d<=_depth; d*=2)
25522                   res.draw_image(res.get_crop(0,0,0,0,0,d-1).get_haar(true,1));
25523               } else return *this;
25524             }
25525           }
25526         } else { // Direct transform
25527           res = get_haar(false,1);
25528           if (_width>1) {
25529             if (_height>1) {
25530               if (_depth>1) for (unsigned int s = 1, w = _width/2, h = _height/2, d = _depth/2; w && h && d && s<nb_scales; ++s, w/=2, h/=2, d/=2)
25531                 res.draw_image(res.get_crop(0,0,0,w-1,h-1,d-1).haar(false,1));
25532               else for (unsigned int s = 1, w = _width/2, h = _height/2; w && h && s<nb_scales; ++s, w/=2, h/=2)
25533                 res.draw_image(res.get_crop(0,0,0,w-1,h-1,0).haar(false,1));
25534             } else {
25535               if (_depth>1) for (unsigned int s = 1, w = _width/2, d = _depth/2; w && d && s<nb_scales; ++s, w/=2, d/=2)
25536                 res.draw_image(res.get_crop(0,0,0,w-1,0,d-1).haar(false,1));
25537               else for (unsigned int s = 1, w = _width/2; w && s<nb_scales; ++s, w/=2)
25538                 res.draw_image(res.get_crop(0,0,0,w-1,0,0).haar(false,1));
25539             }
25540           } else {
25541             if (_height>1) {
25542               if (_depth>1) for (unsigned int s = 1, h = _height/2, d = _depth/2; h && d && s<nb_scales; ++s, h/=2, d/=2)
25543                 res.draw_image(res.get_crop(0,0,0,0,h-1,d-1).haar(false,1));
25544               else for (unsigned int s = 1, h = _height/2; h && s<nb_scales; ++s, h/=2)
25545                 res.draw_image(res.get_crop(0,0,0,0,h-1,0).haar(false,1));
25546             } else {
25547               if (_depth>1) for (unsigned int s = 1, d = _depth/2; d && s<nb_scales; ++s, d/=2)
25548                 res.draw_image(res.get_crop(0,0,0,0,0,d-1).haar(false,1));
25549               else return *this;
25550             }
25551           }
25552         }
25553         return res;
25554       }
25555       return *this;
25556     }
25557 
25558     //! Compute 1d Fast Fourier Transform, along a specified axis.
25559     /**
25560        \param axis Axis along which the FFT is computed.
25561        \param is_invert Tells if the forward (\c false) or inverse (\c true) FFT is computed.
25562     **/
25563     CImgList<Tfloat> get_FFT(const char axis, const bool is_invert=false) const {
25564       CImgList<Tfloat> res(*this,CImg<Tfloat>());
25565       CImg<Tfloat>::FFT(res[0],res[1],axis,is_invert);
25566       return res;
25567     }
25568 
25569     //! Compute n-d Fast Fourier Transform.
25570     /*
25571       \param is_invert Tells if the forward (\c false) or inverse (\c true) FFT is computed.
25572     **/
25573     CImgList<Tfloat> get_FFT(const bool is_invert=false) const {
25574       CImgList<Tfloat> res(*this,CImg<Tfloat>());
25575       CImg<Tfloat>::FFT(res[0],res[1],is_invert);
25576       return res;
25577     }
25578 
25579     //! Compute 1d Fast Fourier Transform, along a specified axis.
25580     /**
25581        \param[in,out] real Real part of the pixel values.
25582        \param[in,out] imag Imaginary part of the pixel values.
25583        \param axis Axis along which the FFT is computed.
25584        \param is_invert Tells if the forward (\c false) or inverse (\c true) FFT is computed.
25585     **/
25586     static void FFT(CImg<T>& real, CImg<T>& imag, const char axis, const bool is_invert=false) {
25587       if (!real)
25588         throw CImgInstanceException("CImg<%s>::FFT() : Specified real part is empty.",
25589                                     pixel_type());
25590 
25591       if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,0);
25592       if (!real.is_sameXYZC(imag))
25593         throw CImgInstanceException("CImg<%s>::FFT() : Specified real part (%u,%u,%u,%u,%p) and imaginary part (%u,%u,%u,%u,%p) have different dimensions.",
25594                                     pixel_type(),
25595                                     real._width,real._height,real._depth,real._spectrum,real._data,
25596                                     imag._width,imag._height,imag._depth,imag._spectrum,imag._data);
25597 #ifdef cimg_use_fftw3
25598       fftw_complex *data_in;
25599       fftw_plan data_plan;
25600 
25601       switch (cimg::uncase(axis)) {
25602       case 'x' : { // Fourier along X, using FFTW library.
25603         data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width);
25604         if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT() : Failed to allocate memory (%s) for computing FFT of image (%u,%u,%u,%u) along the X-axis.",
25605                                                   pixel_type(),
25606                                                   cimg::strbuffersize(sizeof(fftw_complex)*real._width),
25607                                                   real._width,real._height,real._depth,real._spectrum);
25608 
25609         data_plan = fftw_plan_dft_1d(real._width,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
25610         cimg_forYZC(real,y,z,c) {
25611           T *ptrr = real.data(0,y,z,c), *ptri = imag.data(0,y,z,c);
25612           double *ptrd = (double*)data_in;
25613           cimg_forX(real,x) { *(ptrd++) = (double)*(ptrr++); *(ptrd++) = (double)*(ptri++); }
25614           fftw_execute(data_plan);
25615           const unsigned int fact = real._width;
25616           if (is_invert) cimg_forX(real,x) { *(--ptri) = (T)(*(--ptrd)/fact); *(--ptrr) = (T)(*(--ptrd)/fact); }
25617           else cimg_forX(real,x) { *(--ptri) = (T)*(--ptrd); *(--ptrr) = (T)*(--ptrd); }
25618         }
25619       } break;
25620       case 'y' : { // Fourier along Y, using FFTW library.
25621         data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * real._height);
25622         if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT() : Failed to allocate memory (%s) for computing FFT of image (%u,%u,%u,%u) along the Y-axis.",
25623                                                   pixel_type(),
25624                                                   cimg::strbuffersize(sizeof(fftw_complex)*real._height),
25625                                                   real._width,real._height,real._depth,real._spectrum);
25626 
25627         data_plan = fftw_plan_dft_1d(real._height,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
25628         const unsigned int off = real._width;
25629         cimg_forXZC(real,x,z,c) {
25630           T *ptrr = real.data(x,0,z,c), *ptri = imag.data(x,0,z,c);
25631           double *ptrd = (double*)data_in;
25632           cimg_forY(real,y) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; }
25633           fftw_execute(data_plan);
25634           const unsigned int fact = real._height;
25635           if (is_invert) cimg_forY(real,y) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); }
25636           else cimg_forY(real,y) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); }
25637         }
25638       } break;
25639       case 'z' : { // Fourier along Z, using FFTW library.
25640         data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * real._depth);
25641         if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT() : Failed to allocate memory (%s) for computing FFT of image (%u,%u,%u,%u) along the Z-axis.",
25642                                                   pixel_type(),
25643                                                   cimg::strbuffersize(sizeof(fftw_complex)*real._depth),
25644                                                   real._width,real._height,real._depth,real._spectrum);
25645 
25646         data_plan = fftw_plan_dft_1d(real._depth,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
25647         const unsigned long off = (unsigned long)real._width*real._height;
25648         cimg_forXYC(real,x,y,c) {
25649           T *ptrr = real.data(x,y,0,c), *ptri = imag.data(x,y,0,c);
25650           double *ptrd = (double*)data_in;
25651           cimg_forZ(real,z) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; }
25652           fftw_execute(data_plan);
25653           const unsigned int fact = real._depth;
25654           if (is_invert) cimg_forZ(real,z) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); }
25655           else cimg_forZ(real,z) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); }
25656         }
25657       } break;
25658       default : { // Fourier along C, using FFTW library.
25659         data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * real._spectrum);
25660         if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT() : Failed to allocate memory (%s) for computing FFT of image (%u,%u,%u,%u) along the C-axis.",
25661                                                   pixel_type(),
25662                                                   cimg::strbuffersize(sizeof(fftw_complex)*real._spectrum),
25663                                                   real._width,real._height,real._depth,real._spectrum);
25664 
25665         data_plan = fftw_plan_dft_1d(real._spectrum,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
25666         const unsigned long off = (unsigned long)real._width*real._height*real._depth;
25667         cimg_forXYZ(real,x,y,z) {
25668           T *ptrr = real.data(x,y,z,0), *ptri = imag.data(x,y,z,0);
25669           double *ptrd = (double*)data_in;
25670           cimg_forC(real,c) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; }
25671           fftw_execute(data_plan);
25672           const unsigned int fact = real._spectrum;
25673           if (is_invert) cimg_forC(real,c) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); }
25674           else cimg_forC(real,c) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); }
25675         }
25676       }
25677       }
25678       fftw_destroy_plan(data_plan);
25679       fftw_free(data_in);
25680 #else
25681       switch (cimg::uncase(axis)) {
25682       case 'x' : { // Fourier along X, using built-in functions.
25683         const unsigned int N = real._width, N2 = (N>>1);
25684         if (((N-1)&N) && N!=1)
25685           throw CImgInstanceException("CImgList<%s>::FFT() : Specified real and imaginary parts (%u,%u,%u,%u) have non 2^N dimension along the X-axis.",
25686                                       pixel_type(),
25687                                       real._width,real._height,real._depth,real._spectrum);
25688 
25689         for (unsigned int i = 0, j = 0; i<N2; ++i) {
25690           if (j>i) cimg_forYZC(real,y,z,c) {
25691             cimg::swap(real(i,y,z,c),real(j,y,z,c)); cimg::swap(imag(i,y,z,c),imag(j,y,z,c));
25692             if (j<N2) {
25693               const unsigned int ri = N-1-i, rj = N-1-j;
25694               cimg::swap(real(ri,y,z,c),real(rj,y,z,c)); cimg::swap(imag(ri,y,z,c),imag(rj,y,z,c));
25695             }
25696           }
25697           for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
25698         }
25699         for (unsigned int delta = 2; delta<=N; delta<<=1) {
25700           const unsigned int delta2 = (delta>>1);
25701           for (unsigned int i = 0; i<N; i+=delta) {
25702             float wr = 1, wi = 0;
25703             const float angle = (float)((is_invert?+1:-1)*2*cimg::PI/delta),
25704                         ca = (float)std::cos(angle),
25705                         sa = (float)std::sin(angle);
25706             for (unsigned int k = 0; k<delta2; ++k) {
25707               const unsigned int j = i + k, nj = j + delta2;
25708               cimg_forYZC(real,y,z,c) {
25709                 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);
25710                 const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
25711                 nir = (T)(ir - tmpr);
25712                 nii = (T)(ii - tmpi);
25713                 ir+=(T)tmpr;
25714                 ii+=(T)tmpi;
25715               }
25716               const float nwr = wr*ca-wi*sa;
25717               wi = wi*ca + wr*sa;
25718               wr = nwr;
25719             }
25720           }
25721         }
25722         if (is_invert) { real/=N; imag/=N; }
25723       } break;
25724       case 'y' : { // Fourier along Y, using built-in functions.
25725         const unsigned int N = real._height, N2 = (N>>1);
25726         if (((N-1)&N) && N!=1)
25727           throw CImgInstanceException("CImgList<%s>::FFT() : Specified real and imaginary parts (%u,%u,%u,%u) have non 2^N dimension along the Y-axis.",
25728                                       pixel_type(),
25729                                       real._width,real._height,real._depth,real._spectrum);
25730 
25731         for (unsigned int i = 0, j = 0; i<N2; ++i) {
25732           if (j>i) cimg_forXZC(real,x,z,c) {
25733             cimg::swap(real(x,i,z,c),real(x,j,z,c)); cimg::swap(imag(x,i,z,c),imag(x,j,z,c));
25734             if (j<N2) {
25735               const unsigned int ri = N - 1 - i, rj = N - 1 - j;
25736               cimg::swap(real(x,ri,z,c),real(x,rj,z,c)); cimg::swap(imag(x,ri,z,c),imag(x,rj,z,c));
25737             }
25738           }
25739           for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
25740         }
25741         for (unsigned int delta = 2; delta<=N; delta<<=1) {
25742           const unsigned int delta2 = (delta>>1);
25743           for (unsigned int i = 0; i<N; i+=delta) {
25744             float wr = 1, wi = 0;
25745             const float angle = (float)((is_invert?+1:-1)*2*cimg::PI/delta),
25746                         ca = (float)std::cos(angle), sa = (float)std::sin(angle);
25747             for (unsigned int k = 0; k<delta2; ++k) {
25748               const unsigned int j = i + k, nj = j + delta2;
25749               cimg_forXZC(real,x,z,c) {
25750                 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);
25751                 const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
25752                 nir = (T)(ir - tmpr);
25753                 nii = (T)(ii - tmpi);
25754                 ir+=(T)tmpr;
25755                 ii+=(T)tmpi;
25756               }
25757               const float nwr = wr*ca-wi*sa;
25758               wi = wi*ca + wr*sa;
25759               wr = nwr;
25760             }
25761           }
25762         }
25763         if (is_invert) { real/=N; imag/=N; }
25764       } break;
25765       case 'z' : { // Fourier along Z, using built-in functions.
25766         const unsigned int N = real._depth, N2 = (N>>1);
25767         if (((N-1)&N) && N!=1)
25768           throw CImgInstanceException("CImgList<%s>::FFT() : Specified real and imaginary parts (%u,%u,%u,%u) have non 2^N dimension along the Z-axis.",
25769                                       pixel_type(),
25770                                       real._width,real._height,real._depth,real._spectrum);
25771 
25772         for (unsigned int i = 0, j = 0; i<N2; ++i) {
25773           if (j>i) cimg_forXYC(real,x,y,c) {
25774             cimg::swap(real(x,y,i,c),real(x,y,j,c)); cimg::swap(imag(x,y,i,c),imag(x,y,j,c));
25775             if (j<N2) {
25776               const unsigned int ri = N - 1 - i, rj = N - 1 - j;
25777               cimg::swap(real(x,y,ri,c),real(x,y,rj,c)); cimg::swap(imag(x,y,ri,c),imag(x,y,rj,c));
25778             }
25779           }
25780           for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
25781         }
25782         for (unsigned int delta = 2; delta<=N; delta<<=1) {
25783           const unsigned int delta2 = (delta>>1);
25784           for (unsigned int i = 0; i<N; i+=delta) {
25785             float wr = 1, wi = 0;
25786             const float angle = (float)((is_invert?+1:-1)*2*cimg::PI/delta),
25787                         ca = (float)std::cos(angle), sa = (float)std::sin(angle);
25788             for (unsigned int k = 0; k<delta2; ++k) {
25789               const unsigned int j = i + k, nj = j + delta2;
25790               cimg_forXYC(real,x,y,c) {
25791                 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);
25792                 const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
25793                 nir = (T)(ir - tmpr);
25794                 nii = (T)(ii - tmpi);
25795                 ir+=(T)tmpr;
25796                 ii+=(T)tmpi;
25797               }
25798               const float nwr = wr*ca-wi*sa;
25799               wi = wi*ca + wr*sa;
25800               wr = nwr;
25801             }
25802           }
25803         }
25804         if (is_invert) { real/=N; imag/=N; }
25805       } break;
25806       default :
25807         throw CImgArgumentException("CImgList<%s>::FFT() : Invalid specified axis '%c' for real and imaginary parts (%u,%u,%u,%u) "
25808                                     "(should be { x | y | z }).",
25809                                     pixel_type(),axis,
25810                                     real._width,real._height,real._depth,real._spectrum);
25811       }
25812 #endif
25813     }
25814 
25815     //! Compute n-d Fast Fourier Transform.
25816     /**
25817        \param[in,out] real Real part of the pixel values.
25818        \param[in,out] imag Imaginary part of the pixel values.
25819        \param is_invert Tells if the forward (\c false) or inverse (\c true) FFT is computed.
25820     **/
25821     static void FFT(CImg<T>& real, CImg<T>& imag, const bool is_invert=false) {
25822       if (!real)
25823         throw CImgInstanceException("CImgList<%s>::FFT() : Empty specified real part.",
25824                                     pixel_type());
25825 
25826       if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,0);
25827       if (!real.is_sameXYZC(imag))
25828         throw CImgInstanceException("CImgList<%s>::FFT() : Specified real part (%u,%u,%u,%u,%p) and imaginary part (%u,%u,%u,%u,%p) have different dimensions.",
25829                                     pixel_type(),
25830                                     real._width,real._height,real._depth,real._spectrum,real._data,
25831                                     imag._width,imag._height,imag._depth,imag._spectrum,imag._data);
25832 
25833 #ifdef cimg_use_fftw3
25834       fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width*real._height*real._depth);
25835       if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT() : Failed to allocate memory (%s) for computing FFT of image (%u,%u,%u,%u).",
25836                                                 pixel_type(),
25837                                                 cimg::strbuffersize(sizeof(fftw_complex)*real._width*real._height*real._depth*real._spectrum),
25838                                                 real._width,real._height,real._depth,real._spectrum);
25839 
25840       fftw_plan data_plan;
25841       const unsigned long w = (unsigned long)real._width, wh = w*real._height, whd = wh*real._depth;
25842       data_plan = fftw_plan_dft_3d(real._width,real._height,real._depth,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
25843       cimg_forC(real,c) {
25844         T *ptrr = real.data(0,0,0,c), *ptri = imag.data(0,0,0,c);
25845         double *ptrd = (double*)data_in;
25846         for (unsigned int x = 0; x<real._width; ++x, ptrr-=wh-1, ptri-=wh-1)
25847           for (unsigned int y = 0; y<real._height; ++y, ptrr-=whd-w, ptri-=whd-w)
25848             for (unsigned int z = 0; z<real._depth; ++z, ptrr+=wh, ptri+=wh) {
25849               *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri;
25850             }
25851         fftw_execute(data_plan);
25852         ptrd = (double*)data_in;
25853         ptrr = real.data(0,0,0,c);
25854         ptri = imag.data(0,0,0,c);
25855         if (!is_invert) for (unsigned int x = 0; x<real._width; ++x, ptrr-=wh-1, ptri-=wh-1)
25856           for (unsigned int y = 0; y<real._height; ++y, ptrr-=whd-w, ptri-=whd-w)
25857             for (unsigned int z = 0; z<real._depth; ++z, ptrr+=wh, ptri+=wh) {
25858               *ptrr = (T)*(ptrd++); *ptri = (T)*(ptrd++);
25859             }
25860         else for (unsigned int x = 0; x<real._width; ++x, ptrr-=wh-1, ptri-=wh-1)
25861           for (unsigned int y = 0; y<real._height; ++y, ptrr-=whd-w, ptri-=whd-w)
25862             for (unsigned int z = 0; z<real._depth; ++z, ptrr+=wh, ptri+=wh) {
25863               *ptrr = (T)(*(ptrd++)/whd); *ptri = (T)(*(ptrd++)/whd);
25864             }
25865       }
25866       fftw_destroy_plan(data_plan);
25867       fftw_free(data_in);
25868 #else
25869       if (real._depth>1) FFT(real,imag,'z',is_invert);
25870       if (real._height>1) FFT(real,imag,'y',is_invert);
25871       if (real._width>1) FFT(real,imag,'x',is_invert);
25872 #endif
25873     }
25874 
25875     //@}
25876     //-------------------------------------
25877     //
25878     //! \name 3d Objects Management
25879     //@{
25880     //-------------------------------------
25881 
25882     //! Shift 3d object's vertices.
25883     /**
25884        \param tx X-coordinate of the 3d displacement vector.
25885        \param ty Y-coordinate of the 3d displacement vector.
25886        \param tz Z-coordinate of the 3d displacement vector.
25887     **/
25888     CImg<T>& shift_object3d(const float tx, const float ty=0, const float tz=0) {
25889       if (_height!=3 || _depth>1 || _spectrum>1)
25890         throw CImgInstanceException(_cimg_instance
25891                                     "shift_object3d() : Instance is not a set of 3d vertices.",
25892                                     cimg_instance);
25893 
25894       get_shared_row(0)+=tx; get_shared_row(1)+=ty; get_shared_row(2)+=tz;
25895       return *this;
25896     }
25897 
25898     //! Shift 3d object's vertices \newinstance.
25899     CImg<Tfloat> get_shift_object3d(const float tx, const float ty=0, const float tz=0) const {
25900       return CImg<Tfloat>(*this,false).shift_object3d(tx,ty,tz);
25901     }
25902 
25903     //! Shift 3d object's vertices, so that it becomes centered.
25904     /**
25905        \note The object center is computed as its barycenter.
25906     **/
25907     CImg<T>& shift_object3d() {
25908       if (_height!=3 || _depth>1 || _spectrum>1)
25909         throw CImgInstanceException(_cimg_instance
25910                                     "shift_object3d() : Instance is not a set of 3d vertices.",
25911                                     cimg_instance);
25912 
25913       CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
25914       float xm, xM = (float)xcoords.max_min(xm), ym, yM = (float)ycoords.max_min(ym), zm, zM = (float)zcoords.max_min(zm);
25915       xcoords-=(xm + xM)/2; ycoords-=(ym + yM)/2; zcoords-=(zm + zM)/2;
25916       return *this;
25917     }
25918 
25919     //! Shift 3d object's vertices, so that it becomes centered \newinstance.
25920     CImg<Tfloat> get_shift_object3d() const {
25921       return CImg<Tfloat>(*this,false).shift_object3d();
25922     }
25923 
25924     //! Resize 3d object.
25925     /**
25926        \param sx Width of the 3d object's bounding box.
25927        \param sy Height of the 3d object's bounding box.
25928        \param sz Depth of the 3d object's bounding box.
25929     **/
25930     CImg<T>& resize_object3d(const float sx, const float sy=-100, const float sz=-100) {
25931       if (_height!=3 || _depth>1 || _spectrum>1)
25932         throw CImgInstanceException(_cimg_instance
25933                                     "resize_object3d() : Instance is not a set of 3d vertices.",
25934                                     cimg_instance);
25935 
25936       CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
25937       float xm, xM = (float)xcoords.max_min(xm), ym, yM = (float)ycoords.max_min(ym), zm, zM = (float)zcoords.max_min(zm);
25938       if (xm<xM) { if (sx>0) xcoords*=sx/(xM-xm); else xcoords*=-sx/100; }
25939       if (ym<yM) { if (sy>0) ycoords*=sy/(yM-ym); else ycoords*=-sy/100; }
25940       if (zm<zM) { if (sz>0) zcoords*=sz/(zM-zm); else zcoords*=-sz/100; }
25941       return *this;
25942     }
25943 
25944     //! Resize 3d object \newinstance.
25945     CImg<Tfloat> get_resize_object3d(const float sx, const float sy=-100, const float sz=-100) const {
25946       return CImg<Tfloat>(*this,false).resize_object3d(sx,sy,sz);
25947     }
25948 
25949     //! Resize 3d object to unit size.
25950     CImg<T> resize_object3d() {
25951       if (_height!=3 || _depth>1 || _spectrum>1)
25952         throw CImgInstanceException(_cimg_instance
25953                                     "resize_object3d() : Instance is not a set of 3d vertices.",
25954                                     cimg_instance);
25955 
25956       CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
25957       float xm, xM = (float)xcoords.max_min(xm), ym, yM = (float)ycoords.max_min(ym), zm, zM = (float)zcoords.max_min(zm);
25958       const float dx = xM - xm, dy = yM - ym, dz = zM - zm, dmax = cimg::max(dx,dy,dz);
25959       if (dmax>0) { xcoords/=dmax; ycoords/=dmax; zcoords/=dmax; }
25960       return *this;
25961     }
25962 
25963     //! Resize 3d object to unit size \newinstance.
25964     CImg<Tfloat> get_resize_object3d() const {
25965       return CImg<Tfloat>(*this,false).resize_object3d();
25966     }
25967 
25968     //! Merge two 3d objects together.
25969     /**
25970        \param[in,out] primitives Primitives data of the current 3d object.
25971        \param obj_vertices Vertices data of the additional 3d object.
25972        \param obj_primitives Primitives data of the additional 3d object.
25973     **/
25974     template<typename tf, typename tp, typename tff>
25975     CImg<T>& append_object3d(CImgList<tf>& primitives, const CImg<tp>& obj_vertices, const CImgList<tff>& obj_primitives) {
25976       if (!obj_vertices || !obj_primitives) return *this;
25977       if (obj_vertices._height!=3 || obj_vertices._depth>1 || obj_vertices._spectrum>1)
25978         throw CImgInstanceException(_cimg_instance
25979                                     "append_object3d() : Specified vertice image (%u,%u,%u,%u,%p) is not a set of 3d vertices.",
25980                                     cimg_instance,
25981                                     obj_vertices._width,obj_vertices._height,obj_vertices._depth,obj_vertices._spectrum,obj_vertices._data);
25982 
25983       if (is_empty()) { primitives.assign(obj_primitives); return assign(obj_vertices); }
25984       if (_height!=3 || _depth>1 || _spectrum>1)
25985         throw CImgInstanceException(_cimg_instance
25986                                     "append_object3d() : Instance is not a set of 3d vertices.",
25987                                     cimg_instance);
25988 
25989       const unsigned int P = _width;
25990       append(obj_vertices,'x');
25991       const unsigned int N = primitives._width;
25992       primitives.insert(obj_primitives);
25993       for (unsigned int i = N; i<primitives._width; ++i) {
25994         CImg<tf> &p = primitives[i];
25995         switch (p.size()) {
25996         case 1 : p[0]+=P; break; // Point.
25997         case 5 : p[0]+=P; p[1]+=P; break; // Sphere.
25998         case 2 : case 6 : p[0]+=P; p[1]+=P; break; // Segment.
25999         case 3 : case 9 : p[0]+=P; p[1]+=P; p[2]+=P; break; // Triangle.
26000         case 4 : case 12 : p[0]+=P; p[1]+=P; p[2]+=P; p[3]+=P; break; // Rectangle.
26001         }
26002       }
26003       return *this;
26004     }
26005 
26006     //! Texturize primitives of a 3d object.
26007     /**
26008        \param[in,out] primitives Primitives data of the 3d object.
26009        \param[in,out] colors Colors data of the 3d object.
26010        \param texture Texture image to map to 3d object.
26011        \param coords Texture-mapping coordinates.
26012     **/
26013     template<typename tp, typename tc, typename tt, typename tx>
26014     const CImg<T>& texturize_object3d(CImgList<tp>& primitives, CImgList<tc>& colors,
26015                                       const CImg<tt>& texture, const CImg<tx>& coords=CImg<tx>::empty()) const {
26016       if (is_empty()) return *this;
26017       if (_height!=3)
26018         throw CImgInstanceException(_cimg_instance
26019                                     "texturize_object3d() : image instance is not a set of 3d points.",
26020                                     cimg_instance);
26021       if (coords && (coords._width!=_width || coords._height!=2))
26022         throw CImgArgumentException(_cimg_instance
26023                                     "texturize_object3d() : Invalid specified texture coordinates (%u,%u,%u,%u,%p).",
26024                                     cimg_instance,
26025                                     coords._width,coords._height,coords._depth,coords._spectrum,coords._data);
26026       CImg<unsigned int> _coords;
26027       if (!coords) { // If no texture coordinates specified, do a default XY-projection.
26028         _coords.assign(_width,2);
26029         float
26030           xmin, xmax = (float)get_shared_row(0).max_min(xmin),
26031           ymin, ymax = (float)get_shared_row(1).max_min(ymin),
26032           dx = xmax>xmin?xmax-xmin:1,
26033           dy = ymax>ymin?ymax-ymin:1;
26034         cimg_forX(*this,p) {
26035           _coords(p,0) = (unsigned int)(((*this)(p,0)-xmin)*(texture._width-1)/dx);
26036           _coords(p,1) = (unsigned int)(((*this)(p,1)-ymin)*(texture._height-1)/dy);
26037         }
26038       } else _coords = coords;
26039 
26040       int texture_ind = -1;
26041       cimglist_for(primitives,l) {
26042         CImg<tp> &p = primitives[l];
26043         const unsigned int siz = p.size();
26044         switch (siz) {
26045         case 1 : { // Point.
26046           const unsigned int
26047             i0 = (unsigned int)p[0],
26048             x0 = (unsigned int)_coords(i0,0), y0 = (unsigned int)_coords(i0,1);
26049           texture.get_vector_at(x0,y0).move_to(colors[l]);
26050         } break;
26051         case 2 : case 6 : { // Line.
26052           const unsigned int
26053             i0 = (unsigned int)p[0], i1 = (unsigned int)p[1],
26054             x0 = (unsigned int)_coords(i0,0), y0 = (unsigned int)_coords(i0,1),
26055             x1 = (unsigned int)_coords(i1,0), y1 = (unsigned int)_coords(i1,1);
26056           if (texture_ind<0) colors[texture_ind=l] = texture; else colors[l].assign(colors[texture_ind],true);
26057           CImg<tp>::vector(i0,i1,x0,y0,x1,y1).move_to(p);
26058         } break;
26059         case 3 : case 9 : { // Triangle.
26060           const unsigned int
26061             i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2],
26062             x0 = (unsigned int)_coords(i0,0), y0 = (unsigned int)_coords(i0,1),
26063             x1 = (unsigned int)_coords(i1,0), y1 = (unsigned int)_coords(i1,1),
26064             x2 = (unsigned int)_coords(i2,0), y2 = (unsigned int)_coords(i2,1);
26065           if (texture_ind<0) colors[texture_ind=l] = texture; else colors[l].assign(colors[texture_ind],true);
26066           CImg<tp>::vector(i0,i1,i2,x0,y0,x1,y1,x2,y2).move_to(p);
26067         } break;
26068         case 4 : case 12 : { // Quadrangle.
26069           const unsigned int
26070             i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2], i3 = (unsigned int)p[3],
26071             x0 = (unsigned int)_coords(i0,0), y0 = (unsigned int)_coords(i0,1),
26072             x1 = (unsigned int)_coords(i1,0), y1 = (unsigned int)_coords(i1,1),
26073             x2 = (unsigned int)_coords(i2,0), y2 = (unsigned int)_coords(i2,1),
26074             x3 = (unsigned int)_coords(i3,0), y3 = (unsigned int)_coords(i3,1);
26075           if (texture_ind<0) colors[texture_ind=l] = texture; else colors[l].assign(colors[texture_ind],true);
26076           CImg<tp>::vector(i0,i1,i2,i3,x0,y0,x1,y1,x2,y2,x3,y3).move_to(p);
26077         } break;
26078         }
26079       }
26080       return *this;
26081     }
26082 
26083     //! Generate a 3d elevation of the image instance.
26084     /**
26085        \param[out] primitives The returned list of the 3d object primitives
26086                               (template type \e tf should be at least \e unsigned \e int).
26087        \param[out] colors The returned list of the 3d object colors.
26088        \param elevation The input elevation map.
26089        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
26090        \par Example
26091        \code
26092        const CImg<float> img("reference.jpg");
26093        CImgList<unsigned int> faces3d;
26094        CImgList<unsigned char> colors3d;
26095        const CImg<float> points3d = img.get_elevation3d(faces3d,colors,img.get_norm()*0.2);
26096        CImg<unsigned char>().display_object3d("Elevation3d",points3d,faces3d,colors3d);
26097        \endcode
26098        \image html ref_elevation3d.jpg
26099     **/
26100     template<typename tf, typename tc, typename te>
26101     CImg<floatT> get_elevation3d(CImgList<tf>& primitives, CImgList<tc>& colors, const CImg<te>& elevation) const {
26102       if (!is_sameXY(elevation) || elevation._depth>1 || elevation._spectrum>1)
26103         throw CImgArgumentException(_cimg_instance
26104                                     "get_elevation3d() : Instance and specified elevation (%u,%u,%u,%u,%p) "
26105                                     "have incompatible dimensions.",
26106                                     cimg_instance,
26107                                     elevation._width,elevation._height,elevation._depth,elevation._spectrum,elevation._data);
26108       if (is_empty()) return *this;
26109       float m, M = (float)max_min(m);
26110       if (M==m) ++M;
26111       colors.assign();
26112       const unsigned int size_x1 = _width - 1, size_y1 = _height - 1;
26113       for (unsigned int y = 0; y<size_y1; ++y)
26114         for (unsigned int x = 0; x<size_x1; ++x) {
26115           const unsigned char
26116             r = (unsigned char)(((*this)(x,y,0) - m)*255/(M-m)),
26117             g = _spectrum>1?(unsigned char)(((*this)(x,y,1) - m)*255/(M-m)):r,
26118             b = _spectrum>2?(unsigned char)(((*this)(x,y,2) - m)*255/(M-m)):(_spectrum>1?0:r);
26119           CImg<tc>::vector((tc)r,(tc)g,(tc)b).move_to(colors);
26120         }
26121       const typename CImg<te>::_functor2d_int func(elevation);
26122       return elevation3d(primitives,func,0,0,_width-1.0f,_height-1.0f,_width,_height);
26123     }
26124 
26125     //! Generate the 3d projection planes of the image instance.
26126     /**
26127        \param[out] primitives Primitives data of the returned 3d object.
26128        \param[out] colors Colors data of the returned 3d object.
26129        \param x0 X-coordinate of the projection point.
26130        \param y0 Y-coordinate of the projection point.
26131        \param z0 Z-coordinate of the projection point.
26132        \param normalize_colors Tells if the created textures have normalized colors.
26133     **/
26134     template<typename tf, typename tc>
26135     CImg<floatT> get_projections3d(CImgList<tf>& primitives, CImgList<tc>& colors,
26136                                    const unsigned int x0, const unsigned int y0, const unsigned int z0,
26137                                    const bool normalize_colors=false) const {
26138       float m = 0, M = 0, delta = 1;
26139       if (normalize_colors) { m = (float)min_max(M); delta = 255/(m==M?1:M-m); }
26140       const unsigned int
26141         _x0 = (x0>=_width)?_width - 1:x0,
26142         _y0 = (y0>=_height)?_height - 1:y0,
26143         _z0 = (z0>=_depth)?_depth - 1:z0;
26144       CImg<tc> img_xy, img_xz, img_yz;
26145       if (normalize_colors) {
26146         ((get_crop(0,0,_z0,0,_width-1,_height-1,_z0,_spectrum-1)-=m)*=delta).move_to(img_xy);
26147         ((get_crop(0,_y0,0,0,_width-1,_y0,_depth-1,_spectrum-1)-=m)*=delta).resize(_width,_depth,1,-100,-1).move_to(img_xz);
26148         ((get_crop(_x0,0,0,0,_x0,_height-1,_depth-1,_spectrum-1)-=m)*=delta).resize(_height,_depth,1,-100,-1).move_to(img_yz);
26149       } else {
26150         get_crop(0,0,_z0,0,_width-1,_height-1,_z0,_spectrum-1).move_to(img_xy);
26151         get_crop(0,_y0,0,0,_width-1,_y0,_depth-1,_spectrum-1).resize(_width,_depth,1,-100,-1).move_to(img_xz);
26152         get_crop(_x0,0,0,0,_x0,_height-1,_depth-1,_spectrum-1).resize(_height,_depth,1,-100,-1).move_to(img_yz);
26153       }
26154       CImg<floatT> points(12,3,1,1,
26155                           0,_width-1,_width-1,0,   0,_width-1,_width-1,0, _x0,_x0,_x0,_x0,
26156                           0,0,_height-1,_height-1, _y0,_y0,_y0,_y0,       0,_height-1,_height-1,0,
26157                           _z0,_z0,_z0,_z0,         0,0,_depth-1,_depth-1, 0,0,_depth-1,_depth-1);
26158       primitives.assign();
26159       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).move_to(primitives);
26160       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).move_to(primitives);
26161       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).move_to(primitives);
26162       colors.assign();
26163       img_xy.move_to(colors);
26164       img_xz.move_to(colors);
26165       img_yz.move_to(colors);
26166       return points;
26167     }
26168 
26169     //! Generate a isoline of the image instance as a 3d object.
26170     /**
26171        \param[out] primitives The returned list of the 3d object primitives
26172                               (template type \e tf should be at least \e unsigned \e int).
26173        \param isovalue The returned list of the 3d object colors.
26174        \param size_x The number of subdivisions along the X-axis.
26175        \param size_y The number of subdisivions along the Y-axis.
26176        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
26177        \par Example
26178        \code
26179        const CImg<float> img("reference.jpg");
26180        CImgList<unsigned int> faces3d;
26181        const CImg<float> points3d = img.get_isoline3d(faces3d,100);
26182        CImg<unsigned char>().display_object3d("Isoline3d",points3d,faces3d,colors3d);
26183        \endcode
26184        \image html ref_isoline3d.jpg
26185     **/
26186     template<typename tf>
26187     CImg<floatT> get_isoline3d(CImgList<tf>& primitives, const float isovalue,
26188                                const int size_x=-100, const int size_y=-100) const {
26189       if (_spectrum>1)
26190         throw CImgInstanceException(_cimg_instance
26191                                     "get_isoline3d() : Instance is not a scalar image.",
26192                                     cimg_instance);
26193       if (_depth>1)
26194         throw CImgInstanceException(_cimg_instance
26195                                     "get_isoline3d() : Instance is not a 2d image.",
26196                                     cimg_instance);
26197       primitives.assign();
26198       if (is_empty()) return *this;
26199       CImg<floatT> vertices;
26200       if ((size_x==-100 && size_y==-100) || (size_x==width() && size_y==height())) {
26201         const _functor2d_int func(*this);
26202         vertices = isoline3d(primitives,func,isovalue,0,0,width()-1.0f,height()-1.0f,width(),height());
26203       } else {
26204         const _functor2d_float func(*this);
26205         vertices = isoline3d(primitives,func,isovalue,0,0,width()-1.0f,height()-1.0f,size_x,size_y);
26206       }
26207       return vertices;
26208     }
26209 
26210     //! Generate an isosurface of the image instance as a 3d object.
26211     /**
26212        \param[out] primitives The returned list of the 3d object primitives
26213                               (template type \e tf should be at least \e unsigned \e int).
26214        \param isovalue The returned list of the 3d object colors.
26215        \param size_x Number of subdivisions along the X-axis.
26216        \param size_y Number of subdisivions along the Y-axis.
26217        \param size_z Number of subdisivions along the Z-axis.
26218        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
26219        \par Example
26220        \code
26221        const CImg<float> img = CImg<unsigned char>("reference.jpg").resize(-100,-100,20);
26222        CImgList<unsigned int> faces3d;
26223        const CImg<float> points3d = img.get_isosurface3d(faces3d,100);
26224        CImg<unsigned char>().display_object3d("Isosurface3d",points3d,faces3d,colors3d);
26225        \endcode
26226        \image html ref_isosurface3d.jpg
26227     **/
26228     template<typename tf>
26229     CImg<floatT> get_isosurface3d(CImgList<tf>& primitives, const float isovalue,
26230                                   const int size_x=-100, const int size_y=-100, const int size_z=-100) const {
26231       if (_spectrum>1)
26232         throw CImgInstanceException(_cimg_instance
26233                                     "get_isosurface3d() : Instance is not a scalar image.",
26234                                     cimg_instance);
26235       primitives.assign();
26236       if (is_empty()) return *this;
26237       CImg<floatT> vertices;
26238       if ((size_x==-100 && size_y==-100 && size_z==-100) || (size_x==width() && size_y==height() && size_z==depth())) {
26239         const _functor3d_int func(*this);
26240         vertices = isosurface3d(primitives,func,isovalue,0,0,0,width()-1.0f,height()-1.0f,depth()-1.0f,width(),height(),depth());
26241       } else {
26242         const _functor3d_float func(*this);
26243         vertices = isosurface3d(primitives,func,isovalue,0,0,0,width()-1.0f,height()-1.0f,depth()-1.0f,size_x,size_y,size_z);
26244       }
26245       return vertices;
26246     }
26247 
26248     //! Compute 3d elevation of a function as a 3d object.
26249     /**
26250        \param[out] primitives Primitives data of the resulting 3d object.
26251        \param func Elevation function. Is of type <tt>float (*func)(const float x,const float y)</tt>.
26252        \param x0 X-coordinate of the starting point.
26253        \param y0 Y-coordinate of the starting point.
26254        \param x1 X-coordinate of the ending point.
26255        \param y1 Y-coordinate of the ending point.
26256        \param size_x Resolution of the function along the X-axis.
26257        \param size_y Resolution of the function along the Y-axis.
26258     **/
26259     template<typename tf, typename tfunc>
26260     static CImg<floatT> elevation3d(CImgList<tf>& primitives, const tfunc& func,
26261                                     const float x0, const float y0, const float x1, const float y1,
26262                                     const int size_x=256, const int size_y=256) {
26263       const float
26264         nx0 = x0<x1?x0:x1, ny0 = y0<y1?y0:y1,
26265         nx1 = x0<x1?x1:x0, ny1 = y0<y1?y1:y0;
26266       const unsigned int
26267         _nsize_x = (unsigned int)(size_x>=0?size_x:(nx1-nx0)*-size_x/100), nsize_x = _nsize_x?_nsize_x:1, nsize_x1 = nsize_x - 1,
26268         _nsize_y = (unsigned int)(size_y>=0?size_y:(ny1-ny0)*-size_y/100), nsize_y = _nsize_y?_nsize_y:1, nsize_y1 = nsize_y - 1;
26269       if (nsize_x<2 || nsize_y<2)
26270         throw CImgArgumentException("CImg<%s>::elevation3d() : Invalid specified size (%d,%d).",
26271                                     pixel_type(),
26272                                     nsize_x,nsize_y);
26273 
26274       CImg<floatT> vertices(nsize_x*nsize_y,3);
26275       floatT *ptr_x = vertices.data(0,0), *ptr_y = vertices.data(0,1), *ptr_z = vertices.data(0,2);
26276       for (unsigned int y = 0; y<nsize_y; ++y) {
26277         const float Y = ny0 + y*(ny1-ny0)/nsize_y1;
26278         for (unsigned int x = 0; x<nsize_x; ++x) {
26279           const float X = nx0 + x*(nx1-nx0)/nsize_x1;
26280           *(ptr_x++) = (float)x;
26281           *(ptr_y++) = (float)y;
26282           *(ptr_z++) = (float)func(X,Y);
26283         }
26284       }
26285       primitives.assign(nsize_x1*nsize_y1,1,4);
26286       for (unsigned int p = 0, y = 0; y<nsize_y1; ++y) {
26287         const unsigned int yw = y*nsize_x;
26288         for (unsigned int x = 0; x<nsize_x1; ++x) {
26289           const unsigned int xpyw = x + yw, xpyww = xpyw + nsize_x;
26290           primitives[p++].fill(xpyw,xpyww,xpyww+1,xpyw+1);
26291         }
26292       }
26293       return vertices;
26294     }
26295 
26296     //! Compute 3d elevation of a function, as a 3d object \overloading.
26297     template<typename tf>
26298     static CImg<floatT> elevation3d(CImgList<tf>& primitives, const char *const expression,
26299                                     const float x0, const float y0, const float x1, const float y1,
26300                                     const int size_x=256, const int size_y=256) {
26301       const _functor2d_expr func(expression);
26302       return elevation3d(primitives,func,x0,y0,x1,y1,size_x,size_y);
26303     }
26304 
26305     //! Compute 0-isolines of a function, as a 3d object.
26306     /**
26307        \param[out] primitives Primitives data of the resulting 3d object.
26308        \param func Elevation function. Is of type <tt>float (*func)(const float x,const float y)</tt>.
26309        \param isovalue Isovalue to extract from function.
26310        \param x0 X-coordinate of the starting point.
26311        \param y0 Y-coordinate of the starting point.
26312        \param x1 X-coordinate of the ending point.
26313        \param y1 Y-coordinate of the ending point.
26314        \param size_x Resolution of the function along the X-axis.
26315        \param size_y Resolution of the function along the Y-axis.
26316        \note Use the marching squares algorithm for extracting the isolines.
26317      **/
26318     template<typename tf, typename tfunc>
26319     static CImg<floatT> isoline3d(CImgList<tf>& primitives, const tfunc& func, const float isovalue,
26320                                   const float x0, const float y0, const float x1, const float y1,
26321                                   const int size_x=256, const int size_y=256) {
26322       static const unsigned int edges[16] = { 0x0, 0x9, 0x3, 0xa, 0x6, 0xf, 0x5, 0xc, 0xc, 0x5, 0xf, 0x6, 0xa, 0x3, 0x9, 0x0 };
26323       static const int segments[16][4] = { { -1,-1,-1,-1 }, { 0,3,-1,-1 }, { 0,1,-1,-1 }, { 1,3,-1,-1 },
26324                                            { 1,2,-1,-1 },   { 0,1,2,3 },   { 0,2,-1,-1 }, { 2,3,-1,-1 },
26325                                            { 2,3,-1,-1 },   { 0,2,-1,-1},  { 0,3,1,2 },   { 1,2,-1,-1 },
26326                                            { 1,3,-1,-1 },   { 0,1,-1,-1},  { 0,3,-1,-1},  { -1,-1,-1,-1 } };
26327       const unsigned int
26328         _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)),
26329         _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)),
26330         nx = _nx?_nx:1,
26331         ny = _ny?_ny:1,
26332         nxm1 = nx - 1,
26333         nym1 = ny - 1;
26334       primitives.assign();
26335       if (!nxm1 || !nym1) return CImg<floatT>();
26336       const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1;
26337       CImgList<floatT> vertices;
26338       CImg<intT> indices1(nx,1,1,2,-1), indices2(nx,1,1,2);
26339       CImg<floatT> values1(nx), values2(nx);
26340       float X = x0, Y = y0, nX = X + dx, nY = Y + dy;
26341 
26342       // Fill first line with values
26343       cimg_forX(values1,x) { values1(x) = (float)func(X,Y); X+=dx; }
26344 
26345       // Run the marching squares algorithm
26346       for (unsigned int yi = 0, nyi = 1; yi<nym1; ++yi, ++nyi, Y=nY, nY+=dy) {
26347         X = x0; nX = X + dx;
26348         indices2.fill(-1);
26349         for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X=nX, nX+=dx) {
26350 
26351           // Determine square configuration
26352           const float
26353             val0 = values1(xi),
26354             val1 = values1(nxi),
26355             val2 = values2(nxi) = (float)func(nX,nY),
26356             val3 = values2(xi) = (float)func(X,nY);
26357           const unsigned int
26358             configuration = (val0<isovalue?1:0)  | (val1<isovalue?2:0)  | (val2<isovalue?4:0)  | (val3<isovalue?8:0),
26359             edge = edges[configuration];
26360 
26361           // Compute intersection vertices
26362           if (edge) {
26363             if ((edge&1) && indices1(xi,0)<0) {
26364               const float Xi = X + (isovalue-val0)*dx/(val1-val0);
26365               indices1(xi,0) = vertices._width;
26366               CImg<floatT>::vector(Xi,Y,0).move_to(vertices);
26367             }
26368             if ((edge&2) && indices1(nxi,1)<0) {
26369               const float Yi = Y + (isovalue-val1)*dy/(val2-val1);
26370               indices1(nxi,1) = vertices._width;
26371               CImg<floatT>::vector(nX,Yi,0).move_to(vertices);
26372             }
26373             if ((edge&4) && indices2(xi,0)<0) {
26374               const float Xi = X + (isovalue-val3)*dx/(val2-val3);
26375               indices2(xi,0) = vertices._width;
26376               CImg<floatT>::vector(Xi,nY,0).move_to(vertices);
26377             }
26378             if ((edge&8) && indices1(xi,1)<0) {
26379               const float Yi = Y + (isovalue-val0)*dy/(val3-val0);
26380               indices1(xi,1) = vertices._width;
26381               CImg<floatT>::vector(X,Yi,0).move_to(vertices);
26382             }
26383 
26384             // Create segments
26385             for (const int *segment = segments[configuration]; *segment!=-1; ) {
26386               const unsigned int p0 = *(segment++), p1 = *(segment++);
26387               const tf
26388                 i0 = (tf)(_isoline3d_indice(p0,indices1,indices2,xi,nxi)),
26389                 i1 = (tf)(_isoline3d_indice(p1,indices1,indices2,xi,nxi));
26390               CImg<tf>::vector(i0,i1).move_to(primitives);
26391             }
26392           }
26393         }
26394         values1.swap(values2);
26395         indices1.swap(indices2);
26396       }
26397       return vertices>'x';
26398     }
26399 
26400     //! Compute isolines of a function, as a 3d object \overloading.
26401     template<typename tf>
26402     static CImg<floatT> isoline3d(CImgList<tf>& primitives, const char *const expression, const float isovalue,
26403                                   const float x0, const float y0, const float x1, const float y1,
26404                                   const int size_x=256, const int size_y=256) {
26405       const _functor2d_expr func(expression);
26406       return isoline3d(primitives,func,isovalue,x0,y0,x1,y1,size_x,size_y);
26407     }
26408 
26409     template<typename t>
26410     static int _isoline3d_indice(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& indices2,
26411                                  const unsigned int x, const unsigned int nx) {
26412       switch (edge) {
26413       case 0 : return (int)indices1(x,0);
26414       case 1 : return (int)indices1(nx,1);
26415       case 2 : return (int)indices2(x,0);
26416       case 3 : return (int)indices1(x,1);
26417       }
26418       return 0;
26419     }
26420 
26421     //! Compute isosurface of a function, as a 3d object.
26422     /**
26423        \param[out] primitives Primitives data of the resulting 3d object.
26424        \param func Implicit function. Is of type <tt>float (*func)(const float x, const float y, const float z)</tt>.
26425        \param isovalue Isovalue to extract.
26426        \param x0 X-coordinate of the starting point.
26427        \param y0 Y-coordinate of the starting point.
26428        \param z0 Z-coordinate of the starting point.
26429        \param x1 X-coordinate of the ending point.
26430        \param y1 Y-coordinate of the ending point.
26431        \param z1 Z-coordinate of the ending point.
26432        \param size_x Resolution of the elevation function along the X-axis.
26433        \param size_y Resolution of the elevation function along the Y-axis.
26434        \param size_z Resolution of the elevation function along the Z-axis.
26435        \note Use the marching cubes algorithm for extracting the isosurface.
26436      **/
26437     template<typename tf, typename tfunc>
26438     static CImg<floatT> isosurface3d(CImgList<tf>& primitives, const tfunc& func, const float isovalue,
26439                                      const float x0, const float y0, const float z0,
26440                                      const float x1, const float y1, const float z1,
26441                                      const int size_x=32, const int size_y=32, const int size_z=32) {
26442       static unsigned int edges[256] = {
26443         0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
26444         0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
26445         0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
26446         0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
26447         0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
26448         0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
26449         0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
26450         0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
26451         0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
26452         0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
26453         0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
26454         0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460,
26455         0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0,
26456         0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230,
26457         0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190,
26458         0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000 };
26459 
26460       static int triangles[256][16] = {
26461         { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26462         { 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26463         { 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26464         { 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1 },
26465         { 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26466         { 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1 },
26467         { 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1 },
26468         { 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, { 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26469         { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26470         { 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1 },
26471         { 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1 },
26472         { 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, { 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1 },
26473         { 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1 },
26474         { 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, { 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1 },
26475         { 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1 }, { 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1 },
26476         { 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, { 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1 },
26477         { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26478         { 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1 },
26479         { 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 },
26480         { 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, { 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1 },
26481         { 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 },
26482         { 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, { 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1 },
26483         { 10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1 }, { 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1 },
26484         { 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, { 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1 },
26485         { 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1 },
26486         { 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, { 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26487         { 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1 }, { 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1 },
26488         { 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1 }, { 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1 },
26489         { 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1 }, { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1 },
26490         { 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1 }, { 11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1 },
26491         { 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1 }, { 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1 },
26492         { 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1 }, { 11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26493         { 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26494         { 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 },
26495         { 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1 },
26496         { 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1 },
26497         { 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 },
26498         { 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, { 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1 },
26499         { 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1 },
26500         { 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1 }, { 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1 },
26501         { 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1 },
26502         { 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, { 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1 },
26503         { 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1 },
26504         { 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1 }, { 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1 },
26505         { 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, { 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1 },
26506         { 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1 }, { 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1 },
26507         { 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, { 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1 },
26508         { 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1 }, { 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1 },
26509         { 10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1 },
26510         { 10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1 },
26511         { 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1 },
26512         { 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1 },
26513         { 10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1 },
26514         { 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, { 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1 },
26515         { 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1 }, { 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1 },
26516         { 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, { 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26517         { 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, { 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1 },
26518         { 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1 }, { 10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1 },
26519         { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, { 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1 },
26520         { 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, { 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26521         { 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, { 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1 },
26522         { 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1 }, { 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1 },
26523         { 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1 }, { 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26524         { 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1 }, { 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26525         { 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26526         { 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 },
26527         { 10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 },
26528         { 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, { 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1 },
26529         { 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1 },
26530         { 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1 }, { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1 },
26531         { 10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, { 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1 },
26532         { 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1 }, { 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1 },
26533         { 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1 },
26534         { 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1 },
26535         { 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1 },
26536         { 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1 }, { 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1 },
26537         { 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, { 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26538         { 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1 }, { 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1 },
26539         { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1 }, { 10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1 },
26540         { 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1 }, { 10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26541         { 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 },
26542         { 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, { 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1 },
26543         { 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, { 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1 },
26544         { 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1 }, { 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1 },
26545         { 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1 }, { 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1 },
26546         { 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1 }, { 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1 },
26547         { 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1 }, { 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1 },
26548         { 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1 }, { 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1 },
26549         { 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, { 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1 },
26550         { 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1 }, { 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1 },
26551         { 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1 }, { 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1 },
26552         { 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1 }, { 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1 },
26553         { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1 }, { 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1 },
26554         { 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1 }, { 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26555         { 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1 }, { 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1 },
26556         { 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26557         { 11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1 },
26558         { 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1 }, { 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1 },
26559         { 11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1 },
26560         { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1 }, { 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1 },
26561         { 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, { 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1 },
26562         { 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1 }, { 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1 },
26563         { 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1 },
26564         { 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, { 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26565         { 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, { 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1 },
26566         { 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1 }, { 10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1 },
26567         { 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1 }, { 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1 },
26568         { 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1 }, { 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26569         { 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1 }, { 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1 },
26570         { 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1 }, { 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1 },
26571         { 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26572         { 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1 }, { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26573         { 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1 },
26574         { 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1 }, { 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1 },
26575         { 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1 }, { 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1 },
26576         { 11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, { 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1 },
26577         { 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1 }, { 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1 },
26578         { 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1 }, { 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26579         { 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, { 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1 },
26580         { 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26581         { 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1 },
26582         { 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, { 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26583         { 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1 },
26584         { 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26585         { 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, { 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26586         { 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1 }, { 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26587         { 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26588         { 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }
26589       };
26590 
26591       const unsigned int
26592         _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)),
26593         _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)),
26594         _nz = (unsigned int)(size_z>=0?size_z:cimg::round((z1-z0)*-size_z/100 + 1)),
26595         nx = _nx?_nx:1,
26596         ny = _ny?_ny:1,
26597         nz = _nz?_nz:1,
26598         nxm1 = nx - 1,
26599         nym1 = ny - 1,
26600         nzm1 = nz - 1;
26601       primitives.assign();
26602       if (!nxm1 || !nym1 || !nzm1) return CImg<floatT>();
26603       const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1, dz = (z1 - z0)/nzm1;
26604       CImgList<floatT> vertices;
26605       CImg<intT> indices1(nx,ny,1,3,-1), indices2(indices1);
26606       CImg<floatT> values1(nx,ny), values2(nx,ny);
26607       float X = 0, Y = 0, Z = 0, nX = 0, nY = 0, nZ = 0;
26608 
26609       // Fill the first plane with function values
26610       Y = y0;
26611       cimg_forY(values1,y) {
26612         X = x0;
26613         cimg_forX(values1,x) { values1(x,y) = (float)func(X,Y,z0); X+=dx; }
26614         Y+=dy;
26615       }
26616 
26617       // Run Marching Cubes algorithm
26618       Z = z0; nZ = Z + dz;
26619       for (unsigned int zi = 0; zi<nzm1; ++zi, Z = nZ, nZ+=dz) {
26620         Y = y0; nY = Y + dy;
26621         indices2.fill(-1);
26622         for (unsigned int yi = 0, nyi = 1; yi<nym1; ++yi, ++nyi, Y = nY, nY+=dy) {
26623           X = x0; nX = X + dx;
26624           for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X = nX, nX+=dx) {
26625 
26626             // Determine cube configuration
26627             const float
26628               val0 = values1(xi,yi),
26629               val1 = values1(nxi,yi),
26630               val2 = values1(nxi,nyi),
26631               val3 = values1(xi,nyi),
26632               val4 = values2(xi,yi) = (float)func(X,Y,nZ),
26633               val5 = values2(nxi,yi) = (float)func(nX,Y,nZ),
26634               val6 = values2(nxi,nyi) = (float)func(nX,nY,nZ),
26635               val7 = values2(xi,nyi) = (float)func(X,nY,nZ);
26636 
26637             const unsigned int configuration =
26638               (val0<isovalue?1:0)  | (val1<isovalue?2:0)  | (val2<isovalue?4:0)  | (val3<isovalue?8:0) |
26639               (val4<isovalue?16:0) | (val5<isovalue?32:0) | (val6<isovalue?64:0) | (val7<isovalue?128:0),
26640               edge = edges[configuration];
26641 
26642             // Compute intersection vertices
26643             if (edge) {
26644               if ((edge&1) && indices1(xi,yi,0)<0) {
26645                 const float Xi = X + (isovalue-val0)*dx/(val1-val0);
26646                 indices1(xi,yi,0) = vertices._width;
26647                 CImg<floatT>::vector(Xi,Y,Z).move_to(vertices);
26648               }
26649               if ((edge&2) && indices1(nxi,yi,1)<0) {
26650                 const float Yi = Y + (isovalue-val1)*dy/(val2-val1);
26651                 indices1(nxi,yi,1) = vertices._width;
26652                 CImg<floatT>::vector(nX,Yi,Z).move_to(vertices);
26653               }
26654               if ((edge&4) && indices1(xi,nyi,0)<0) {
26655                 const float Xi = X + (isovalue-val3)*dx/(val2-val3);
26656                 indices1(xi,nyi,0) = vertices._width;
26657                 CImg<floatT>::vector(Xi,nY,Z).move_to(vertices);
26658               }
26659               if ((edge&8) && indices1(xi,yi,1)<0) {
26660                 const float Yi = Y + (isovalue-val0)*dy/(val3-val0);
26661                 indices1(xi,yi,1) = vertices._width;
26662                 CImg<floatT>::vector(X,Yi,Z).move_to(vertices);
26663               }
26664               if ((edge&16) && indices2(xi,yi,0)<0) {
26665                 const float Xi = X + (isovalue-val4)*dx/(val5-val4);
26666                 indices2(xi,yi,0) = vertices._width;
26667                 CImg<floatT>::vector(Xi,Y,nZ).move_to(vertices);
26668               }
26669               if ((edge&32) && indices2(nxi,yi,1)<0) {
26670                 const float Yi = Y + (isovalue-val5)*dy/(val6-val5);
26671                 indices2(nxi,yi,1) = vertices._width;
26672                 CImg<floatT>::vector(nX,Yi,nZ).move_to(vertices);
26673               }
26674               if ((edge&64) && indices2(xi,nyi,0)<0) {
26675                 const float Xi = X + (isovalue-val7)*dx/(val6-val7);
26676                 indices2(xi,nyi,0) = vertices._width;
26677                 CImg<floatT>::vector(Xi,nY,nZ).move_to(vertices);
26678               }
26679               if ((edge&128) && indices2(xi,yi,1)<0)  {
26680                 const float Yi = Y + (isovalue-val4)*dy/(val7-val4);
26681                 indices2(xi,yi,1) = vertices._width;
26682                 CImg<floatT>::vector(X,Yi,nZ).move_to(vertices);
26683               }
26684               if ((edge&256) && indices1(xi,yi,2)<0) {
26685                 const float Zi = Z+ (isovalue-val0)*dz/(val4-val0);
26686                 indices1(xi,yi,2) = vertices._width;
26687                 CImg<floatT>::vector(X,Y,Zi).move_to(vertices);
26688               }
26689               if ((edge&512) && indices1(nxi,yi,2)<0)  {
26690                 const float Zi = Z + (isovalue-val1)*dz/(val5-val1);
26691                 indices1(nxi,yi,2) = vertices._width;
26692                 CImg<floatT>::vector(nX,Y,Zi).move_to(vertices);
26693               }
26694               if ((edge&1024) && indices1(nxi,nyi,2)<0) {
26695                 const float Zi = Z + (isovalue-val2)*dz/(val6-val2);
26696                 indices1(nxi,nyi,2) = vertices._width;
26697                 CImg<floatT>::vector(nX,nY,Zi).move_to(vertices);
26698               }
26699               if ((edge&2048) && indices1(xi,nyi,2)<0) {
26700                 const float Zi = Z + (isovalue-val3)*dz/(val7-val3);
26701                 indices1(xi,nyi,2) = vertices._width;
26702                 CImg<floatT>::vector(X,nY,Zi).move_to(vertices);
26703               }
26704 
26705               // Create triangles
26706               for (const int *triangle = triangles[configuration]; *triangle!=-1; ) {
26707                 const unsigned int p0 = *(triangle++), p1 = *(triangle++), p2 = *(triangle++);
26708                 const tf
26709                   i0 = (tf)(_isosurface3d_indice(p0,indices1,indices2,xi,yi,nxi,nyi)),
26710                   i1 = (tf)(_isosurface3d_indice(p1,indices1,indices2,xi,yi,nxi,nyi)),
26711                   i2 = (tf)(_isosurface3d_indice(p2,indices1,indices2,xi,yi,nxi,nyi));
26712                 CImg<tf>::vector(i0,i2,i1).move_to(primitives);
26713               }
26714             }
26715           }
26716         }
26717         cimg::swap(values1,values2);
26718         cimg::swap(indices1,indices2);
26719       }
26720       return vertices>'x';
26721     }
26722 
26723     //! Compute isosurface of a function, as a 3d object \overloading.
26724     template<typename tf>
26725     static CImg<floatT> isosurface3d(CImgList<tf>& primitives, const char *const expression, const float isovalue,
26726                                      const float x0, const float y0, const float z0,
26727                                      const float x1, const float y1, const float z1,
26728                                      const int dx=32, const int dy=32, const int dz=32) {
26729       const _functor3d_expr func(expression);
26730       return isosurface3d(primitives,func,isovalue,x0,y0,z0,x1,y1,z1,dx,dy,dz);
26731     }
26732 
26733     template<typename t>
26734     static int _isosurface3d_indice(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& indices2,
26735                                     const unsigned int x, const unsigned int y, const unsigned int nx, const unsigned int ny) {
26736       switch (edge) {
26737       case 0 : return indices1(x,y,0);
26738       case 1 : return indices1(nx,y,1);
26739       case 2 : return indices1(x,ny,0);
26740       case 3 : return indices1(x,y,1);
26741       case 4 : return indices2(x,y,0);
26742       case 5 : return indices2(nx,y,1);
26743       case 6 : return indices2(x,ny,0);
26744       case 7 : return indices2(x,y,1);
26745       case 8 : return indices1(x,y,2);
26746       case 9 : return indices1(nx,y,2);
26747       case 10 : return indices1(nx,ny,2);
26748       case 11 : return indices1(x,ny,2);
26749       }
26750       return 0;
26751     }
26752 
26753     // Define functors for accessing image values (used in previous functions).
26754     struct _functor2d_int {
26755       const CImg<T>& ref;
26756       _functor2d_int(const CImg<T>& pref):ref(pref) {}
26757       float operator()(const float x, const float y) const {
26758         return (float)ref((int)x,(int)y);
26759       }
26760     };
26761 
26762     struct _functor2d_float {
26763       const CImg<T>& ref;
26764       _functor2d_float(const CImg<T>& pref):ref(pref) {}
26765       float operator()(const float x, const float y) const {
26766         return (float)ref._linear_atXY(x,y);
26767       }
26768     };
26769 
26770     struct _functor2d_expr {
26771       _cimg_math_parser *mp;
26772       _functor2d_expr(const char *const expr):mp(0) { mp = new _cimg_math_parser(CImg<T>::empty(),expr,0); }
26773       ~_functor2d_expr() { delete mp; }
26774       float operator()(const float x, const float y) const {
26775         return (float)mp->eval(x,y,0,0);
26776       }
26777     };
26778 
26779     struct _functor3d_int {
26780       const CImg<T>& ref;
26781       _functor3d_int(const CImg<T>& pref):ref(pref) {}
26782       float operator()(const float x, const float y, const float z) const {
26783         return (float)ref((int)x,(int)y,(int)z);
26784       }
26785     };
26786 
26787     struct _functor3d_float {
26788       const CImg<T>& ref;
26789       _functor3d_float(const CImg<T>& pref):ref(pref) {}
26790       float operator()(const float x, const float y, const float z) const {
26791         return (float)ref._linear_atXYZ(x,y,z);
26792       }
26793     };
26794 
26795     struct _functor3d_expr {
26796       _cimg_math_parser *mp;
26797       ~_functor3d_expr() { delete mp; }
26798       _functor3d_expr(const char *const expr):mp(0) { mp = new _cimg_math_parser(CImg<T>::empty(),expr,0); }
26799       float operator()(const float x, const float y, const float z) const {
26800         return (float)mp->eval(x,y,z,0);
26801       }
26802     };
26803 
26804     struct _functor4d_int {
26805       const CImg<T>& ref;
26806       _functor4d_int(const CImg<T>& pref):ref(pref) {}
26807       float operator()(const float x, const float y, const float z, const unsigned int c) const {
26808         return (float)ref((int)x,(int)y,(int)z,c);
26809       }
26810     };
26811 
26812     //! Generate a 3d box object.
26813     /**
26814        \param[out] primitives The returned list of the 3d object primitives
26815                               (template type \e tf should be at least \e unsigned \e int).
26816        \param size_x The width of the box (dimension along the X-axis).
26817        \param size_y The height of the box (dimension along the Y-axis).
26818        \param size_z The depth of the box (dimension along the Z-axis).
26819        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
26820        \par Example
26821        \code
26822        CImgList<unsigned int> faces3d;
26823        const CImg<float> points3d = CImg<float>::box3d(faces3d,10,20,30);
26824        CImg<unsigned char>().display_object3d("Box3d",points3d,faces3d);
26825        \endcode
26826        \image html ref_box3d.jpg
26827     **/
26828     template<typename tf>
26829     static CImg<floatT> box3d(CImgList<tf>& primitives,
26830                               const float size_x=200, const float size_y=100, const float size_z=100) {
26831       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);
26832       return CImg<floatT>(8,3,1,1,
26833                           0.,size_x,size_x,    0.,    0.,size_x,size_x,    0.,
26834                           0.,    0.,size_y,size_y,    0.,    0.,size_y,size_y,
26835                           0.,    0.,    0.,    0.,size_z,size_z,size_z,size_z);
26836     }
26837 
26838     //! Generate a 3d cone.
26839     /**
26840        \param[out] primitives The returned list of the 3d object primitives
26841                               (template type \e tf should be at least \e unsigned \e int).
26842        \param radius The radius of the cone basis.
26843        \param size_z The cone's height.
26844        \param subdivisions The number of basis angular subdivisions.
26845        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
26846        \par Example
26847        \code
26848        CImgList<unsigned int> faces3d;
26849        const CImg<float> points3d = CImg<float>::cone3d(faces3d,50);
26850        CImg<unsigned char>().display_object3d("Cone3d",points3d,faces3d);
26851        \endcode
26852        \image html ref_cone3d.jpg
26853     **/
26854     template<typename tf>
26855     static CImg<floatT> cone3d(CImgList<tf>& primitives,
26856                                const float radius=50, const float size_z=100, const unsigned int subdivisions=24) {
26857       primitives.assign();
26858       if (!subdivisions) return CImg<floatT>();
26859       CImgList<floatT> vertices(2,1,3,1,1,
26860                                 0.,0.,size_z,
26861                                 0.,0.,0.);
26862       for (float delta = 360.0f/subdivisions, angle = 0; angle<360; angle+=delta) {
26863         const float a = (float)(angle*cimg::PI/180);
26864         CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0).move_to(vertices);
26865       }
26866       const unsigned int nbr = vertices._width - 2;
26867       for (unsigned int p = 0; p<nbr; ++p) {
26868         const unsigned int curr = 2 + p, next = 2 + ((p+1)%nbr);
26869         CImg<tf>::vector(1,next,curr).move_to(primitives);
26870         CImg<tf>::vector(0,curr,next).move_to(primitives);
26871       }
26872       return vertices>'x';
26873     }
26874 
26875     //! Generate a 3d cylinder.
26876     /**
26877        \param[out] primitives The returned list of the 3d object primitives
26878                               (template type \e tf should be at least \e unsigned \e int).
26879        \param radius The radius of the cylinder basis.
26880        \param size_z The cylinder's height.
26881        \param subdivisions The number of basis angular subdivisions.
26882        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
26883        \par Example
26884        \code
26885        CImgList<unsigned int> faces3d;
26886        const CImg<float> points3d = CImg<float>::cylinder3d(faces3d,50);
26887        CImg<unsigned char>().display_object3d("Cylinder3d",points3d,faces3d);
26888        \endcode
26889        \image html ref_cylinder3d.jpg
26890     **/
26891     template<typename tf>
26892     static CImg<floatT> cylinder3d(CImgList<tf>& primitives,
26893                                    const float radius=50, const float size_z=100, const unsigned int subdivisions=24) {
26894       primitives.assign();
26895       if (!subdivisions) return CImg<floatT>();
26896       CImgList<floatT> vertices(2,1,3,1,1,
26897                                 0.,0.,0.,
26898                                 0.,0.,size_z);
26899       for (float delta = 360.0f/subdivisions, angle = 0; angle<360; angle+=delta) {
26900         const float a = (float)(angle*cimg::PI/180);
26901         CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0.0f).move_to(vertices);
26902         CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),size_z).move_to(vertices);
26903       }
26904       const unsigned int nbr = (vertices._width - 2)/2;
26905       for (unsigned int p = 0; p<nbr; ++p) {
26906         const unsigned int curr = 2+2*p, next = 2+(2*((p+1)%nbr));
26907         CImg<tf>::vector(0,next,curr).move_to(primitives);
26908         CImg<tf>::vector(1,curr+1,next+1).move_to(primitives);
26909         CImg<tf>::vector(curr,next,next+1,curr+1).move_to(primitives);
26910       }
26911       return vertices>'x';
26912     }
26913 
26914     //! Generate a 3d torus.
26915     /**
26916        \param[out] primitives The returned list of the 3d object primitives
26917                               (template type \e tf should be at least \e unsigned \e int).
26918        \param radius1 The large radius.
26919        \param radius2 The small radius.
26920        \param subdivisions1 The number of angular subdivisions for the large radius.
26921        \param subdivisions2 The number of angular subdivisions for the small radius.
26922        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
26923        \par Example
26924        \code
26925        CImgList<unsigned int> faces3d;
26926        const CImg<float> points3d = CImg<float>::torus3d(faces3d,20,4);
26927        CImg<unsigned char>().display_object3d("Torus3d",points3d,faces3d);
26928        \endcode
26929        \image html ref_torus3d.jpg
26930     **/
26931     template<typename tf>
26932     static CImg<floatT> torus3d(CImgList<tf>& primitives,
26933                                 const float radius1=100, const float radius2=30,
26934                                 const unsigned int subdivisions1=24, const unsigned int subdivisions2=12) {
26935       primitives.assign();
26936       if (!subdivisions1 || !subdivisions2) return CImg<floatT>();
26937       CImgList<floatT> vertices;
26938       for (unsigned int v = 0; v<subdivisions1; ++v) {
26939         const float
26940           beta = (float)(v*2*cimg::PI/subdivisions1),
26941           xc = radius1*(float)std::cos(beta),
26942           yc = radius1*(float)std::sin(beta);
26943         for (unsigned int u = 0; u<subdivisions2; ++u) {
26944           const float
26945             alpha = (float)(u*2*cimg::PI/subdivisions2),
26946             x = xc + radius2*(float)(std::cos(alpha)*std::cos(beta)),
26947             y = yc + radius2*(float)(std::cos(alpha)*std::sin(beta)),
26948             z = radius2*(float)std::sin(alpha);
26949           CImg<floatT>::vector(x,y,z).move_to(vertices);
26950         }
26951       }
26952       for (unsigned int vv = 0; vv<subdivisions1; ++vv) {
26953         const unsigned int nv = (vv+1)%subdivisions1;
26954         for (unsigned int uu = 0; uu<subdivisions2; ++uu) {
26955           const unsigned int nu = (uu+1)%subdivisions2, svv = subdivisions2*vv, snv = subdivisions2*nv;
26956           CImg<tf>::vector(svv+nu,svv+uu,snv+uu,snv+nu).move_to(primitives);
26957         }
26958       }
26959       return vertices>'x';
26960     }
26961 
26962     //! Generate a 3d XY-plane.
26963     /**
26964        \param[out] primitives The returned list of the 3d object primitives
26965                               (template type \e tf should be at least \e unsigned \e int).
26966        \param size_x The width of the plane (dimension along the X-axis).
26967        \param size_y The height of the plane (dimensions along the Y-axis).
26968        \param subdivisions_x The number of planar subdivisions along the X-axis.
26969        \param subdivisions_y The number of planar subdivisions along the Y-axis.
26970        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
26971        \par Example
26972        \code
26973        CImgList<unsigned int> faces3d;
26974        const CImg<float> points3d = CImg<float>::plane3d(faces3d,100,50);
26975        CImg<unsigned char>().display_object3d("Plane3d",points3d,faces3d);
26976        \endcode
26977        \image html ref_plane3d.jpg
26978     **/
26979     template<typename tf>
26980     static CImg<floatT> plane3d(CImgList<tf>& primitives,
26981                                 const float size_x=100, const float size_y=100,
26982                                 const unsigned int subdivisions_x=10, const unsigned int subdivisions_y=10) {
26983       primitives.assign();
26984       if (!subdivisions_x || !subdivisions_y) return CImg<floatT>();
26985       CImgList<floatT> vertices;
26986       const unsigned int w = subdivisions_x + 1, h = subdivisions_y + 1;
26987       const float fx = (float)size_x/w, fy = (float)size_y/h;
26988       for (unsigned int y = 0; y<h; ++y) for (unsigned int x = 0; x<w; ++x)
26989         CImg<floatT>::vector(fx*x,fy*y,0).move_to(vertices);
26990       for (unsigned int y = 0; y<subdivisions_y; ++y) for (unsigned int x = 0; x<subdivisions_x; ++x) {
26991         const int off1 = x+y*w, off2 = x+1+y*w, off3 = x+1+(y+1)*w, off4 = x+(y+1)*w;
26992         CImg<tf>::vector(off1,off4,off3,off2).move_to(primitives);
26993       }
26994       return vertices>'x';
26995     }
26996 
26997     //! Generate a 3d sphere.
26998     /**
26999        \param[out] primitives The returned list of the 3d object primitives
27000                               (template type \e tf should be at least \e unsigned \e int).
27001        \param radius The radius of the sphere (dimension along the X-axis).
27002        \param subdivisions The number of recursive subdivisions from an initial icosahedron.
27003        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
27004        \par Example
27005        \code
27006        CImgList<unsigned int> faces3d;
27007        const CImg<float> points3d = CImg<float>::sphere3d(faces3d,100,4);
27008        CImg<unsigned char>().display_object3d("Sphere3d",points3d,faces3d);
27009        \endcode
27010        \image html ref_sphere3d.jpg
27011     **/
27012     template<typename tf>
27013     static CImg<floatT> sphere3d(CImgList<tf>& primitives,
27014                                  const float radius=50, const unsigned int subdivisions=3) {
27015 
27016       // Create initial icosahedron
27017       primitives.assign();
27018       const double tmp = (1+std::sqrt(5.0f))/2, a = 1.0/std::sqrt(1+tmp*tmp), b = tmp*a;
27019       CImgList<floatT> vertices(12,1,3,1,1, b,a,0.0, -b,a,0.0, -b,-a,0.0, b,-a,0.0, a,0.0,b, a,0.0,-b,
27020                                 -a,0.0,-b, -a,0.0,b, 0.0,b,a, 0.0,-b,a, 0.0,-b,-a, 0.0,b,-a);
27021       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,
27022                         8,0,11, 8,11,1, 9,10,3, 9,2,10, 8,4,0, 11,0,5, 4,9,3,
27023                         5,3,10, 7,8,1, 6,1,11, 7,2,9, 6,10,2);
27024       // edge - length/2
27025       float he = (float)a;
27026 
27027       // Recurse subdivisions
27028       for (unsigned int i = 0; i<subdivisions; ++i) {
27029         const unsigned int L = primitives._width;
27030         he/=2;
27031         const float he2 = he*he;
27032         for (unsigned int l = 0; l<L; ++l) {
27033           const unsigned int
27034             p0 = (unsigned int)primitives(0,0), p1 = (unsigned int)primitives(0,1), p2 = (unsigned int)primitives(0,2);
27035           const float
27036             x0 = vertices(p0,0), y0 = vertices(p0,1), z0 = vertices(p0,2),
27037             x1 = vertices(p1,0), y1 = vertices(p1,1), z1 = vertices(p1,2),
27038             x2 = vertices(p2,0), y2 = vertices(p2,1), z2 = vertices(p2,2),
27039             tnx0 = (x0+x1)/2, tny0 = (y0+y1)/2, tnz0 = (z0+z1)/2, nn0 = (float)std::sqrt(tnx0*tnx0+tny0*tny0+tnz0*tnz0),
27040             tnx1 = (x0+x2)/2, tny1 = (y0+y2)/2, tnz1 = (z0+z2)/2, nn1 = (float)std::sqrt(tnx1*tnx1+tny1*tny1+tnz1*tnz1),
27041             tnx2 = (x1+x2)/2, tny2 = (y1+y2)/2, tnz2 = (z1+z2)/2, nn2 = (float)std::sqrt(tnx2*tnx2+tny2*tny2+tnz2*tnz2),
27042             nx0 = tnx0/nn0, ny0 = tny0/nn0, nz0 = tnz0/nn0,
27043             nx1 = tnx1/nn1, ny1 = tny1/nn1, nz1 = tnz1/nn1,
27044             nx2 = tnx2/nn2, ny2 = tny2/nn2, nz2 = tnz2/nn2;
27045           int i0 = -1, i1 = -1, i2 = -1;
27046           cimglist_for(vertices,p) {
27047             const float x = (float)vertices(p,0), y = (float)vertices(p,1), z = (float)vertices(p,2);
27048             if (cimg::sqr(x-nx0) + cimg::sqr(y-ny0) + cimg::sqr(z-nz0)<he2) i0 = p;
27049             if (cimg::sqr(x-nx1) + cimg::sqr(y-ny1) + cimg::sqr(z-nz1)<he2) i1 = p;
27050             if (cimg::sqr(x-nx2) + cimg::sqr(y-ny2) + cimg::sqr(z-nz2)<he2) i2 = p;
27051           }
27052           if (i0<0) { CImg<floatT>::vector(nx0,ny0,nz0).move_to(vertices); i0 = vertices._width - 1; }
27053           if (i1<0) { CImg<floatT>::vector(nx1,ny1,nz1).move_to(vertices); i1 = vertices._width - 1; }
27054           if (i2<0) { CImg<floatT>::vector(nx2,ny2,nz2).move_to(vertices); i2 = vertices._width - 1; }
27055           primitives.remove(0);
27056           CImg<tf>::vector(p0,i0,i1).move_to(primitives);
27057           CImg<tf>::vector((tf)i0,(tf)p1,(tf)i2).move_to(primitives);
27058           CImg<tf>::vector((tf)i1,(tf)i2,(tf)p2).move_to(primitives);
27059           CImg<tf>::vector((tf)i1,(tf)i0,(tf)i2).move_to(primitives);
27060         }
27061       }
27062       return (vertices>'x')*=radius;
27063     }
27064 
27065     //! Generate a 3d ellipsoid.
27066     /**
27067        \param[out] primitives The returned list of the 3d object primitives
27068                               (template type \e tf should be at least \e unsigned \e int).
27069        \param tensor The tensor which gives the shape and size of the ellipsoid.
27070        \param subdivisions The number of recursive subdivisions from an initial stretched icosahedron.
27071        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
27072        \par Example
27073        \code
27074        CImgList<unsigned int> faces3d;
27075        const CImg<float> tensor = CImg<float>::diagonal(10,7,3),
27076                          points3d = CImg<float>::ellipsoid3d(faces3d,tensor,4);
27077        CImg<unsigned char>().display_object3d("Ellipsoid3d",points3d,faces3d);
27078        \endcode
27079        \image html ref_ellipsoid3d.jpg
27080     **/
27081     template<typename tf, typename t>
27082     static CImg<floatT> ellipsoid3d(CImgList<tf>& primitives,
27083                                     const CImg<t>& tensor, const unsigned int subdivisions=3) {
27084       primitives.assign();
27085       if (!subdivisions) return CImg<floatT>();
27086       CImg<floatT> S, V;
27087       tensor.symmetric_eigen(S,V);
27088       const float orient =
27089         (V(0,1)*V(1,2) - V(0,2)*V(1,1))*V(2,0) +
27090         (V(0,2)*V(1,0) - V(0,0)*V(1,2))*V(2,1) +
27091         (V(0,0)*V(1,1) - V(0,1)*V(1,0))*V(2,2);
27092       if (orient<0) { V(2,0) = -V(2,0); V(2,1) = -V(2,1); V(2,2) = -V(2,2); }
27093       const float l0 = S[0], l1 = S[1], l2 = S[2];
27094       CImg<floatT> vertices = sphere3d(primitives,1.0,subdivisions);
27095       vertices.get_shared_row(0)*=l0;
27096       vertices.get_shared_row(1)*=l1;
27097       vertices.get_shared_row(2)*=l2;
27098       return V*vertices;
27099     }
27100 
27101     //! Convert 3d object into a CImg3d representation.
27102     /**
27103        \param primitives Primitives data of the 3d object.
27104        \param colors Colors data of the 3d object.
27105        \param opacities Opacities data of the 3d object.
27106     **/
27107     template<typename tp, typename tc, typename to>
27108     CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives,
27109                               const CImgList<tc>& colors,
27110                               const to& opacities) {
27111       return get_object3dtoCImg3d(primitives,colors,opacities).move_to(*this);
27112     }
27113 
27114     //! Convert 3d object into a CImg3d representation \overloading.
27115     template<typename tp, typename tc>
27116     CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives,
27117                               const CImgList<tc>& colors) {
27118       return get_object3dtoCImg3d(primitives,colors).move_to(*this);
27119     }
27120 
27121     //! Convert 3d object into a CImg3d representation \overloading.
27122     template<typename tp>
27123     CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives) {
27124       return get_object3dtoCImg3d(primitives).move_to(*this);
27125     }
27126 
27127     //! Convert 3d object into a CImg3d representation \overloading.
27128     CImg<T>& object3dtoCImg3d() {
27129       return get_object3dtoCImg3d().move_to(*this);
27130     }
27131 
27132     //! Convert 3d object into a CImg3d representation \newinstance.
27133     template<typename tp, typename tc, typename to>
27134     CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives,
27135                                       const CImgList<tc>& colors,
27136                                       const to& opacities) const {
27137       char error_message[1024] = { 0 };
27138       if (!is_object3d(primitives,colors,opacities,true,error_message))
27139         throw CImgInstanceException(_cimg_instance
27140                                     "object3dtoCImg3d() : Invalid specified 3d object (%u,%u) (%s).",
27141                                     cimg_instance,_width,primitives._width,error_message);
27142       CImg<floatT> res(1,_size_object3dtoCImg3d(primitives,colors,opacities));
27143       float *ptrd = res._data;
27144 
27145       // Put magick number.
27146       *(ptrd++) = 'C' + 0.5f; *(ptrd++) = 'I' + 0.5f; *(ptrd++) = 'm' + 0.5f;
27147       *(ptrd++) = 'g' + 0.5f; *(ptrd++) = '3' + 0.5f; *(ptrd++) = 'd' + 0.5f;
27148 
27149       // Put number of vertices and primitives.
27150       *(ptrd++) = cimg::uint2float(_width);
27151       *(ptrd++) = cimg::uint2float(primitives._width);
27152 
27153       // Put vertex data.
27154       if (is_empty() || !primitives) return res;
27155       const T *ptrx = data(0,0), *ptry = data(0,1), *ptrz = data(0,2);
27156       cimg_forX(*this,p) {
27157         *(ptrd++) = (float)*(ptrx++);
27158         *(ptrd++) = (float)*(ptry++);
27159         *(ptrd++) = (float)*(ptrz++);
27160       }
27161 
27162       // Put primitive data.
27163       cimglist_for(primitives,p) {
27164         *(ptrd++) = (float)primitives[p].size();
27165         const tp *ptrp = primitives[p]._data;
27166         cimg_foroff(primitives[p],i) *(ptrd++) = cimg::uint2float((unsigned int)*(ptrp++));
27167       }
27168 
27169       // Put color/texture data.
27170       const unsigned int csiz = cimg::min(colors._width,primitives._width);
27171       for (int c = 0; c<(int)csiz; ++c) {
27172         const CImg<tc>& color = colors[c];
27173         const tc *ptrc = color._data;
27174         if (color.size()==3) { *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*ptrc; }
27175         else {
27176           *(ptrd++) = -128.0f;
27177           int shared_ind = -1;
27178           if (color.is_shared()) for (int i = 0; i<c; ++i) if (ptrc==colors[i]._data) { shared_ind = i; break; }
27179           if (shared_ind<0) {
27180             *(ptrd++) = (float)color._width;
27181             *(ptrd++) = (float)color._height;
27182             *(ptrd++) = (float)color._spectrum;
27183             cimg_foroff(color,l) *(ptrd++) = (float)*(ptrc++);
27184           } else {
27185             *(ptrd++) = (float)shared_ind;
27186             *(ptrd++) = 0;
27187             *(ptrd++) = 0;
27188           }
27189         }
27190       }
27191       const int csiz2 = primitives._width - colors._width;
27192       for (int c = 0; c<csiz2; ++c) { *(ptrd++) = 200.0f; *(ptrd++) = 200.0f; *(ptrd++) = 200.0f; }
27193 
27194       // Put opacity data.
27195       ptrd = _object3dtoCImg3d(opacities,ptrd);
27196       const float *ptre = res.end();
27197       while (ptrd<ptre) *(ptrd++) = 1.0f;
27198       return res;
27199     }
27200 
27201     template<typename to>
27202     float* _object3dtoCImg3d(const CImgList<to>& opacities, float *ptrd) const {
27203       cimglist_for(opacities,o) {
27204         const CImg<to>& opacity = opacities[o];
27205         const to *ptro = opacity._data;
27206         if (opacity.size()==1) *(ptrd++) = (float)*ptro;
27207         else {
27208           *(ptrd++) = -128.0f;
27209           int shared_ind = -1;
27210           if (opacity.is_shared()) for (int i = 0; i<o; ++i) if (ptro==opacities[i]._data) { shared_ind = i; break; }
27211           if (shared_ind<0) {
27212             *(ptrd++) = (float)opacity._width;
27213             *(ptrd++) = (float)opacity._height;
27214             *(ptrd++) = (float)opacity._spectrum;
27215             cimg_foroff(opacity,l) *(ptrd++) = (float)*(ptro++);
27216           } else {
27217             *(ptrd++) = (float)shared_ind;
27218             *(ptrd++) = 0;
27219             *(ptrd++) = 0;
27220           }
27221         }
27222       }
27223       return ptrd;
27224     }
27225 
27226     template<typename to>
27227     float* _object3dtoCImg3d(const CImg<to>& opacities, float *ptrd) const {
27228       const to *ptro = opacities._data;
27229       cimg_foroff(opacities,o) *(ptrd++) = (float)*(ptro++);
27230       return ptrd;
27231     }
27232 
27233     template<typename tp, typename tc, typename to>
27234     unsigned int _size_object3dtoCImg3d(const CImgList<tp>& primitives,
27235                                         const CImgList<tc>& colors,
27236                                         const CImgList<to>& opacities) const {
27237       unsigned int siz = 8 + 3*width();
27238       cimglist_for(primitives,p) siz+=primitives[p].size() + 1;
27239       for (int c = cimg::min(primitives._width,colors._width)-1; c>=0; --c) {
27240         if (colors[c].is_shared()) siz+=4;
27241         else { const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4+csiz:3; }
27242       }
27243       if (colors._width<primitives._width) siz+=3*(primitives._width - colors._width);
27244       cimglist_for(opacities,o) {
27245         if (opacities[o].is_shared()) siz+=4;
27246         else { const unsigned int osiz = opacities[o].size(); siz+=(osiz!=1)?4+osiz:1; }
27247       }
27248       siz+=primitives._width - opacities._width;
27249       return siz;
27250     }
27251 
27252     template<typename tp, typename tc, typename to>
27253     unsigned int _size_object3dtoCImg3d(const CImgList<tp>& primitives,
27254                                         const CImgList<tc>& colors,
27255                                         const CImg<to>& opacities) const {
27256       unsigned int siz = 8 + 3*width();
27257       cimglist_for(primitives,p) siz+=primitives[p].size() + 1;
27258       for (int c = cimg::min(primitives._width,colors._width)-1; c>=0; --c) {
27259         const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4+csiz:3;
27260       }
27261       if (colors._width<primitives._width) siz+=3*(primitives._width - colors._width);
27262       siz+=primitives.size();
27263       cimg::unused(opacities);
27264       return siz;
27265     }
27266 
27267     //! Convert 3d object into a CImg3d representation \overloading.
27268     template<typename tp, typename tc>
27269     CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives,
27270                                       const CImgList<tc>& colors) const {
27271       CImgList<T> opacities;
27272       return get_object3dtoCImg3d(primitives,colors,opacities);
27273     }
27274 
27275     //! Convert 3d object into a CImg3d representation \overloading.
27276     template<typename tp>
27277     CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives) const {
27278       CImgList<T> colors, opacities;
27279       return get_object3dtoCImg3d(primitives,colors,opacities);
27280     }
27281 
27282     //! Convert 3d object into a CImg3d representation \overloading.
27283     CImg<floatT> get_object3dtoCImg3d() const {
27284       CImgList<T> opacities, colors;
27285       CImgList<uintT> primitives(width(),1,1,1,1);
27286       cimglist_for(primitives,p) primitives(p,0) = p;
27287       return get_object3dtoCImg3d(primitives,colors,opacities);
27288     }
27289 
27290     //! Convert CImg3d representation into a 3d object.
27291     /**
27292        \param[out] primitives Primitives data of the 3d object.
27293        \param[out] colors Colors data of the 3d object.
27294        \param[out] opacities Opacities data of the 3d object.
27295     **/
27296     template<typename tp, typename tc, typename to>
27297     CImg<T>& CImg3dtoobject3d(CImgList<tp>& primitives, CImgList<tc>& colors, CImgList<to>& opacities) {
27298       return get_CImg3dtoobject3d(primitives,colors,opacities).move_to(*this);
27299     }
27300 
27301     //! Convert CImg3d representation into a 3d object \newinstance.
27302     template<typename tp, typename tc, typename to>
27303     CImg<T> get_CImg3dtoobject3d(CImgList<tp>& primitives, CImgList<tc>& colors, CImgList<to>& opacities) const {
27304       char error_message[1024] = { 0 };
27305       if (!is_CImg3d(true,error_message))
27306         throw CImgInstanceException(_cimg_instance
27307                                     "CImg3dtoobject3d() : image instance is not a CImg3d (%s).",
27308                                     cimg_instance,error_message);
27309       const T *ptrs = _data + 6;
27310       const unsigned int
27311         nb_points = cimg::float2uint((float)*(ptrs++)),
27312         nb_primitives = cimg::float2uint((float)*(ptrs++));
27313       const CImg<T> points = CImg<T>(ptrs,3,nb_points,1,1,true).get_transpose();
27314       ptrs+=3*nb_points;
27315       primitives.assign(nb_primitives);
27316       cimglist_for(primitives,p) {
27317         const unsigned int nb_inds = (unsigned int)*(ptrs++);
27318         primitives[p].assign(1,nb_inds);
27319         tp *ptrp = primitives[p]._data;
27320         for (unsigned int i = 0; i<nb_inds; ++i) *(ptrp++) = (tp)cimg::float2uint((float)*(ptrs++));
27321       }
27322       colors.assign(nb_primitives);
27323       cimglist_for(colors,c) {
27324         if ((int)*ptrs==-128) {
27325           ++ptrs;
27326           const unsigned int w = (unsigned int)*(ptrs++), h = (unsigned int)*(ptrs++), s = (unsigned int)*(ptrs++);
27327           if (!h && !s) colors[c].assign(colors[w],true);
27328           else { colors[c].assign(ptrs,w,h,1,s,false); ptrs+=w*h*s; }
27329         } else { colors[c].assign(ptrs,1,1,1,3,false); ptrs+=3; }
27330       }
27331       opacities.assign(nb_primitives);
27332       cimglist_for(opacities,o) {
27333         if ((int)*ptrs==-128) {
27334           ++ptrs;
27335           const unsigned int w = (unsigned int)*(ptrs++), h = (unsigned int)*(ptrs++), s = (unsigned int)*(ptrs++);
27336           if (!h && !s) opacities[o].assign(opacities[w],true);
27337           else { opacities[o].assign(ptrs,w,h,1,s,false); ptrs+=w*h*s; }
27338         } else opacities[o].assign(1,1,1,1,*(ptrs++));
27339       }
27340       return points;
27341     }
27342 
27343     //@}
27344     //---------------------------
27345     //
27346     //! \name Drawing Functions
27347     //@{
27348     //---------------------------
27349 
27350     // [internal] The following _draw_scanline() routines are *non user-friendly functions*, used only for internal purpose.
27351     // Pre-requisites : x0<x1, y-coordinate is valid, col is valid.
27352     template<typename tc>
27353     CImg<T>& _draw_scanline(const int x0, const int x1, const int y,
27354                             const tc *const color, const float opacity=1,
27355                             const float brightness=1, const bool init=false) {
27356       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
27357       static float nopacity = 0, copacity = 0;
27358       static unsigned long whd = 0;
27359       static const tc *col = 0;
27360       if (init) {
27361         nopacity = cimg::abs(opacity);
27362         copacity = 1 - cimg::max(opacity,0);
27363         whd = (unsigned long)_width*_height*_depth;
27364       } else {
27365         const int nx0 = x0>0?x0:0, nx1 = x1<width()?x1:width()-1, dx = nx1 - nx0;
27366         if (dx>=0) {
27367           col = color;
27368           const unsigned long off = whd - dx - 1;
27369           T *ptrd = data(nx0,y);
27370           if (opacity>=1) { // ** Opaque drawing **
27371             if (brightness==1) { // Brightness==1
27372               if (sizeof(T)!=1) cimg_forC(*this,c) {
27373                 const T val = (T)*(col++);
27374                 for (int x = dx; x>=0; --x) *(ptrd++) = val;
27375                 ptrd+=off;
27376               } else cimg_forC(*this,c) {
27377                 const T val = (T)*(col++);
27378                 std::memset(ptrd,(int)val,dx+1);
27379                 ptrd+=whd;
27380               }
27381             } else if (brightness<1) { // Brightness<1
27382               if (sizeof(T)!=1) cimg_forC(*this,c) {
27383                 const T val = (T)(*(col++)*brightness);
27384                 for (int x = dx; x>=0; --x) *(ptrd++) = val;
27385                 ptrd+=off;
27386               } else cimg_forC(*this,c) {
27387                 const T val = (T)(*(col++)*brightness);
27388                 std::memset(ptrd,(int)val,dx+1);
27389                 ptrd+=whd;
27390               }
27391             } else { // Brightness>1
27392               if (sizeof(T)!=1) cimg_forC(*this,c) {
27393                 const T val = (T)((2-brightness)**(col++) + (brightness-1)*maxval);
27394                 for (int x = dx; x>=0; --x) *(ptrd++) = val;
27395                 ptrd+=off;
27396               } else cimg_forC(*this,c) {
27397                 const T val = (T)((2-brightness)**(col++) + (brightness-1)*maxval);
27398                 std::memset(ptrd,(int)val,dx+1);
27399                 ptrd+=whd;
27400               }
27401             }
27402           } else { // ** Transparent drawing **
27403             if (brightness==1) { // Brightness==1
27404               cimg_forC(*this,c) {
27405                 const T val = (T)*(col++);
27406                 for (int x = dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ++ptrd; }
27407                 ptrd+=off;
27408               }
27409             } else if (brightness<=1) { // Brightness<1
27410               cimg_forC(*this,c) {
27411                 const T val = (T)(*(col++)*brightness);
27412                 for (int x = dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ++ptrd; }
27413                 ptrd+=off;
27414               }
27415             } else { // Brightness>1
27416               cimg_forC(*this,c) {
27417                 const T val = (T)((2-brightness)**(col++) + (brightness-1)*maxval);
27418                 for (int x = dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ++ptrd; }
27419                 ptrd+=off;
27420               }
27421             }
27422           }
27423         }
27424       }
27425       return *this;
27426     }
27427 
27428     template<typename tc>
27429     CImg<T>& _draw_scanline(const tc *const color, const float opacity=1) {
27430       return _draw_scanline(0,0,0,color,opacity,0,true);
27431     }
27432 
27433     //! Draw a 3d point.
27434     /**
27435        \param x0 X-coordinate of the point.
27436        \param y0 Y-coordinate of the point.
27437        \param z0 Z-coordinate of the point.
27438        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
27439        \param opacity Drawing opacity.
27440        \note
27441        - To set pixel values without clipping needs, you should use the faster CImg::operator()() function.
27442        \par Example:
27443        \code
27444        CImg<unsigned char> img(100,100,1,3,0);
27445        const unsigned char color[] = { 255,128,64 };
27446        img.draw_point(50,50,color);
27447        \endcode
27448     **/
27449     template<typename tc>
27450     CImg<T>& draw_point(const int x0, const int y0, const int z0,
27451                         const tc *const color, const float opacity=1) {
27452       if (is_empty()) return *this;
27453       if (!color)
27454         throw CImgArgumentException(_cimg_instance
27455                                     "draw_point() : Specified color is (null).",
27456                                     cimg_instance);
27457       if (x0>=0 && y0>=0 && z0>=0 && x0<width() && y0<height() && z0<depth()) {
27458         const unsigned long whd = (unsigned long)_width*_height*_depth;
27459         const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
27460         T *ptrd = data(x0,y0,z0,0);
27461         const tc *col = color;
27462         if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; }
27463         else cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; }
27464       }
27465       return *this;
27466     }
27467 
27468     //! Draw a 2d point \simplification.
27469     template<typename tc>
27470     CImg<T>& draw_point(const int x0, const int y0,
27471                         const tc *const color, const float opacity=1) {
27472       return draw_point(x0,y0,0,color,opacity);
27473     }
27474 
27475     // Draw a points cloud.
27476     /**
27477        \param points Image of vertices coordinates.
27478        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
27479        \param opacity Drawing opacity.
27480     **/
27481     template<typename t, typename tc>
27482     CImg<T>& draw_point(const CImg<t>& points,
27483                         const tc *const color, const float opacity=1) {
27484       if (is_empty() || !points) return *this;
27485       switch (points._height) {
27486       case 0 : case 1 :
27487         throw CImgArgumentException(_cimg_instance
27488                                     "draw_point() : Invalid specified point set (%u,%u,%u,%u,%p).",
27489                                     cimg_instance,
27490                                     points._width,points._height,points._depth,points._spectrum,points._data);
27491       case 2 : {
27492         cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),color,opacity);
27493       } break;
27494       default : {
27495         cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),(int)points(i,2),color,opacity);
27496       }
27497       }
27498       return *this;
27499     }
27500 
27501     //! Draw a 2d line.
27502     /**
27503        \param x0 X-coordinate of the starting line point.
27504        \param y0 Y-coordinate of the starting line point.
27505        \param x1 X-coordinate of the ending line point.
27506        \param y1 Y-coordinate of the ending line point.
27507        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
27508        \param opacity Drawing opacity.
27509        \param pattern An integer whose bits describe the line pattern.
27510        \param init_hatch Tells if a reinitialization of the hash state must be done.
27511        \note
27512        - Line routine uses Bresenham's algorithm.
27513        - Set \p init_hatch = false to draw consecutive hatched segments without breaking the line pattern.
27514        \par Example:
27515        \code
27516        CImg<unsigned char> img(100,100,1,3,0);
27517        const unsigned char color[] = { 255,128,64 };
27518         img.draw_line(40,40,80,70,color);
27519        \endcode
27520     **/
27521     template<typename tc>
27522     CImg<T>& draw_line(const int x0, const int y0,
27523                        const int x1, const int y1,
27524                        const tc *const color, const float opacity=1,
27525                        const unsigned int pattern=~0U, const bool init_hatch=true) {
27526       if (is_empty()) return *this;
27527       if (!color)
27528         throw CImgArgumentException(_cimg_instance
27529                                     "draw_line() : Specified color is (null).",
27530                                     cimg_instance);
27531       static unsigned int hatch = ~0U - (~0U>>1);
27532       if (init_hatch) hatch = ~0U - (~0U>>1);
27533       const bool xdir = x0<x1, ydir = y0<y1;
27534       int
27535         nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
27536         &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
27537         &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
27538         &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
27539         &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
27540       if (xright<0 || xleft>=width()) return *this;
27541       if (xleft<0) { yleft-=(int)((float)xleft*((float)yright - yleft)/((float)xright - xleft)); xleft = 0; }
27542       if (xright>=width()) { yright-=(int)(((float)xright - width())*((float)yright - yleft)/((float)xright - xleft)); xright = width() - 1; }
27543       if (ydown<0 || yup>=height()) return *this;
27544       if (yup<0) { xup-=(int)((float)yup*((float)xdown - xup)/((float)ydown - yup)); yup = 0; }
27545       if (ydown>=height()) { xdown-=(int)(((float)ydown - height())*((float)xdown - xup)/((float)ydown - yup)); ydown = height() - 1; }
27546       T *ptrd0 = data(nx0,ny0);
27547       int dx = xright - xleft, dy = ydown - yup;
27548       const bool steep = dy>dx;
27549       if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
27550       const long
27551         offx = (nx0<nx1?1:-1)*(steep?width():1),
27552         offy = (ny0<ny1?1:-1)*(steep?1:width());
27553       const unsigned long wh = (unsigned long)_width*_height;
27554       if (opacity>=1) {
27555         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
27556           if (pattern&hatch) {
27557             T *ptrd = ptrd0; const tc* col = color;
27558             cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
27559           }
27560           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
27561           ptrd0+=offx;
27562           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
27563         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
27564           T *ptrd = ptrd0; const tc* col = color; cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
27565           ptrd0+=offx;
27566           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
27567         }
27568       } else {
27569         const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
27570         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
27571           if (pattern&hatch) {
27572             T *ptrd = ptrd0; const tc* col = color;
27573             cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
27574           }
27575           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
27576           ptrd0+=offx;
27577           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
27578         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
27579           T *ptrd = ptrd0; const tc* col = color; cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
27580           ptrd0+=offx;
27581           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
27582         }
27583       }
27584       return *this;
27585     }
27586 
27587     //! Draw a 2d line, with z-buffering.
27588     /**
27589        \param zbuffer Zbuffer image.
27590        \param x0 X-coordinate of the starting point.
27591        \param y0 Y-coordinate of the starting point.
27592        \param z0 Z-coordinate of the starting point
27593        \param x1 X-coordinate of the ending point.
27594        \param y1 Y-coordinate of the ending point.
27595        \param z1 Z-coordinate of the ending point.
27596        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
27597        \param opacity Drawing opacity.
27598        \param pattern An integer whose bits describe the line pattern.
27599        \param init_hatch Tells if a reinitialization of the hash state must be done.
27600     **/
27601     template<typename tz,typename tc>
27602     CImg<T>& draw_line(CImg<tz>& zbuffer,
27603                        const int x0, const int y0, const float z0,
27604                        const int x1, const int y1, const float z1,
27605                        const tc *const color, const float opacity=1,
27606                        const unsigned int pattern=~0U, const bool init_hatch=true) {
27607       typedef typename cimg::superset<tz,float>::type tzfloat;
27608       if (is_empty() || z0<=0 || z1<=0) return *this;
27609       if (!color)
27610         throw CImgArgumentException(_cimg_instance
27611                                     "draw_line() : Specified color is (null).",
27612                                     cimg_instance);
27613       if (!is_sameXY(zbuffer))
27614         throw CImgArgumentException(_cimg_instance
27615                                     "draw_line() : Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
27616                                     cimg_instance,
27617                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
27618       static unsigned int hatch = ~0U - (~0U>>1);
27619       if (init_hatch) hatch = ~0U - (~0U>>1);
27620       const bool xdir = x0<x1, ydir = y0<y1;
27621       int
27622         nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
27623         &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
27624         &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
27625         &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
27626         &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
27627       tzfloat
27628         Z0 = 1/(tzfloat)z0, Z1 = 1/(tzfloat)z1, nz0 = Z0, nz1 = Z1, dz = Z1 - Z0,
27629         &zleft = xdir?nz0:nz1,
27630         &zright = xdir?nz1:nz0,
27631         &zup = ydir?nz0:nz1,
27632         &zdown = ydir?nz1:nz0;
27633       if (xright<0 || xleft>=width()) return *this;
27634       if (xleft<0) {
27635         const float D = (float)xright - xleft;
27636         yleft-=(int)((float)xleft*((float)yright - yleft)/D);
27637         zleft-=(tzfloat)xleft*(zright - zleft)/D;
27638         xleft = 0;
27639       }
27640       if (xright>=width()) {
27641         const float d = (float)xright - width(), D = (float)xright - xleft;
27642         yright-=(int)(d*((float)yright - yleft)/D);
27643         zright-=(tzfloat)d*(zright - zleft)/D;
27644         xright = width() - 1;
27645       }
27646       if (ydown<0 || yup>=height()) return *this;
27647       if (yup<0) {
27648         const float D = (float)ydown - yup;
27649         xup-=(int)((float)yup*((float)xdown - xup)/D);
27650         zup-=(tzfloat)yup*(zdown - zup)/D;
27651         yup = 0;
27652       }
27653       if (ydown>=height()) {
27654         const float d = (float)ydown - height(), D = (float)ydown - yup;
27655         xdown-=(int)(d*((float)xdown - xup)/D);
27656         zdown-=(tzfloat)d*(zdown - zup)/D;
27657         ydown = height() - 1;
27658       }
27659       T *ptrd0 = data(nx0,ny0);
27660       tz *ptrz = zbuffer.data(nx0,ny0);
27661       int dx = xright - xleft, dy = ydown - yup;
27662       const bool steep = dy>dx;
27663       if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
27664       const long
27665         offx = (nx0<nx1?1:-1)*(steep?width():1),
27666         offy = (ny0<ny1?1:-1)*(steep?1:width());
27667       const unsigned long wh = (unsigned long)_width*_height,
27668         ndx = dx>0?dx:1;
27669       if (opacity>=1) {
27670         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
27671           const tzfloat z = Z0 + x*dz/ndx;
27672           if (z>=(tzfloat)*ptrz && pattern&hatch) {
27673             *ptrz = (tz)z;
27674             T *ptrd = ptrd0; const tc *col = color;
27675             cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
27676           }
27677           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
27678           ptrd0+=offx; ptrz+=offx;
27679           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
27680         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
27681           const tzfloat z = Z0 + x*dz/ndx;
27682           if (z>=(tzfloat)*ptrz) {
27683             *ptrz = (tz)z;
27684             T *ptrd = ptrd0; const tc *col = color;
27685             cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
27686           }
27687           ptrd0+=offx; ptrz+=offx;
27688           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
27689         }
27690       } else {
27691         const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
27692         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
27693           const tzfloat z = Z0 + x*dz/ndx;
27694           if (z>=(tzfloat)*ptrz && pattern&hatch) {
27695             *ptrz = (tz)z;
27696             T *ptrd = ptrd0; const tc *col = color;
27697             cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
27698           }
27699           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
27700           ptrd0+=offx; ptrz+=offx;
27701           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
27702         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
27703           const tzfloat z = Z0 + x*dz/ndx;
27704           if (z>=(tzfloat)*ptrz) {
27705             *ptrz = (tz)z;
27706             T *ptrd = ptrd0; const tc *col = color;
27707             cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
27708           }
27709           ptrd0+=offx; ptrz+=offx;
27710           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
27711         }
27712       }
27713       return *this;
27714     }
27715 
27716     //! Draw a 3d line.
27717     /**
27718        \param x0 X-coordinate of the starting point.
27719        \param y0 Y-coordinate of the starting point.
27720        \param z0 Z-coordinate of the starting point
27721        \param x1 X-coordinate of the ending point.
27722        \param y1 Y-coordinate of the ending point.
27723        \param z1 Z-coordinate of the ending point.
27724        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
27725        \param opacity Drawing opacity.
27726        \param pattern An integer whose bits describe the line pattern.
27727        \param init_hatch Tells if a reinitialization of the hash state must be done.
27728     **/
27729     template<typename tc>
27730     CImg<T>& draw_line(const int x0, const int y0, const int z0,
27731                        const int x1, const int y1, const int z1,
27732                        const tc *const color, const float opacity=1,
27733                        const unsigned int pattern=~0U, const bool init_hatch=true) {
27734       if (is_empty()) return *this;
27735       if (!color)
27736         throw CImgArgumentException(_cimg_instance
27737                                     "draw_line() : Specified color is (null).",
27738                                     cimg_instance);
27739       static unsigned int hatch = ~0U - (~0U>>1);
27740       if (init_hatch) hatch = ~0U - (~0U>>1);
27741       int nx0 = x0, ny0 = y0, nz0 = z0, nx1 = x1, ny1 = y1, nz1 = z1;
27742       if (nx0>nx1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
27743       if (nx1<0 || nx0>=width()) return *this;
27744       if (nx0<0) { const float D = 1.0f + nx1 - nx0; ny0-=(int)((float)nx0*(1.0f + ny1 - ny0)/D); nz0-=(int)((float)nx0*(1.0f + nz1 - nz0)/D); nx0 = 0; }
27745       if (nx1>=width()) { const float d = (float)nx1 - width(), D = 1.0f + nx1 - nx0; ny1+=(int)(d*(1.0f + ny0 - ny1)/D); nz1+=(int)(d*(1.0f + nz0 - nz1)/D); nx1 = width() - 1; }
27746       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
27747       if (ny1<0 || ny0>=height()) return *this;
27748       if (ny0<0) { const float D = 1.0f + ny1 - ny0; nx0-=(int)((float)ny0*(1.0f + nx1 - nx0)/D); nz0-=(int)((float)ny0*(1.0f + nz1 - nz0)/D); ny0 = 0; }
27749       if (ny1>=height()) { const float d = (float)ny1 - height(), D = 1.0f + ny1 - ny0; nx1+=(int)(d*(1.0f + nx0 - nx1)/D); nz1+=(int)(d*(1.0f + nz0 - nz1)/D); ny1 = height() - 1; }
27750       if (nz0>nz1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
27751       if (nz1<0 || nz0>=depth()) return *this;
27752       if (nz0<0) { const float D = 1.0f + nz1 - nz0; nx0-=(int)((float)nz0*(1.0f + nx1 - nx0)/D); ny0-=(int)((float)nz0*(1.0f + ny1 - ny0)/D); nz0 = 0; }
27753       if (nz1>=depth()) { const float d = (float)nz1 - depth(), D = 1.0f + nz1 - nz0; nx1+=(int)(d*(1.0f + nx0 - nx1)/D); ny1+=(int)(d*(1.0f + ny0 - ny1)/D); nz1 = depth() - 1; }
27754       const unsigned int dmax = cimg::max(cimg::abs(nx1 - nx0),cimg::abs(ny1 - ny0),nz1 - nz0);
27755       const unsigned long whd = (unsigned long)_width*_height*_depth;
27756       const float px = (nx1 - nx0)/(float)dmax, py = (ny1 - ny0)/(float)dmax, pz = (nz1 - nz0)/(float)dmax;
27757       float x = (float)nx0, y = (float)ny0, z = (float)nz0;
27758       if (opacity>=1) for (unsigned int t = 0; t<=dmax; ++t) {
27759         if (!(~pattern) || (~pattern && pattern&hatch)) {
27760           T* ptrd = data((unsigned int)x,(unsigned int)y,(unsigned int)z);
27761           const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; }
27762         }
27763         x+=px; y+=py; z+=pz; if (pattern) { hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); }
27764       } else {
27765         const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
27766         for (unsigned int t = 0; t<=dmax; ++t) {
27767           if (!(~pattern) || (~pattern && pattern&hatch)) {
27768             T* ptrd = data((unsigned int)x,(unsigned int)y,(unsigned int)z);
27769             const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; }
27770           }
27771           x+=px; y+=py; z+=pz; if (pattern) { hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); }
27772         }
27773       }
27774       return *this;
27775     }
27776 
27777     //! Draw a textured 2d line.
27778     /**
27779        \param x0 X-coordinate of the starting line point.
27780        \param y0 Y-coordinate of the starting line point.
27781        \param x1 X-coordinate of the ending line point.
27782        \param y1 Y-coordinate of the ending line point.
27783        \param texture Texture image defining the pixel colors.
27784        \param tx0 X-coordinate of the starting texture point.
27785        \param ty0 Y-coordinate of the starting texture point.
27786        \param tx1 X-coordinate of the ending texture point.
27787        \param ty1 Y-coordinate of the ending texture point.
27788        \param opacity Drawing opacity.
27789        \param pattern An integer whose bits describe the line pattern.
27790        \param init_hatch Tells if the hash variable must be reinitialized.
27791        \note
27792        - Line routine uses the well known Bresenham's algorithm.
27793        \par Example:
27794        \code
27795        CImg<unsigned char> img(100,100,1,3,0), texture("texture256x256.ppm");
27796        const unsigned char color[] = { 255,128,64 };
27797        img.draw_line(40,40,80,70,texture,0,0,255,255);
27798        \endcode
27799     **/
27800     template<typename tc>
27801     CImg<T>& draw_line(const int x0, const int y0,
27802                        const int x1, const int y1,
27803                        const CImg<tc>& texture,
27804                        const int tx0, const int ty0,
27805                        const int tx1, const int ty1,
27806                        const float opacity=1,
27807                        const unsigned int pattern=~0U, const bool init_hatch=true) {
27808       if (is_empty()) return *this;
27809       if (texture._depth>1 || texture._spectrum<_spectrum)
27810         throw CImgArgumentException(_cimg_instance
27811                                     "draw_line() : Invalid specified texture (%u,%u,%u,%u,%p).",
27812                                     cimg_instance,
27813                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
27814       if (is_overlapped(texture)) return draw_line(x0,y0,x1,y1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
27815       static unsigned int hatch = ~0U - (~0U>>1);
27816       if (init_hatch) hatch = ~0U - (~0U>>1);
27817       const bool xdir = x0<x1, ydir = y0<y1;
27818       int
27819         dtx = tx1-tx0, dty = ty1-ty0,
27820         nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
27821         tnx0 = tx0, tnx1 = tx1, tny0 = ty0, tny1 = ty1,
27822         &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1, &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
27823         &txleft = xdir?tnx0:tnx1, &tyleft = xdir?tny0:tny1, &txright = xdir?tnx1:tnx0, &tyright = xdir?tny1:tny0,
27824         &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1, &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0,
27825         &txup = ydir?tnx0:tnx1, &tyup = ydir?tny0:tny1, &txdown = ydir?tnx1:tnx0, &tydown = ydir?tny1:tny0;
27826       if (xright<0 || xleft>=width()) return *this;
27827       if (xleft<0) {
27828         const float D = (float)xright - xleft;
27829         yleft-=(int)((float)xleft*((float)yright - yleft)/D);
27830         txleft-=(int)((float)xleft*((float)txright - txleft)/D);
27831         tyleft-=(int)((float)xleft*((float)tyright - tyleft)/D);
27832         xleft = 0;
27833       }
27834       if (xright>=width()) {
27835         const float d = (float)xright - width(), D = (float)xright - xleft;
27836         yright-=(int)(d*((float)yright - yleft)/D);
27837         txright-=(int)(d*((float)txright - txleft)/D);
27838         tyright-=(int)(d*((float)tyright - tyleft)/D);
27839         xright = width() - 1;
27840       }
27841       if (ydown<0 || yup>=height()) return *this;
27842       if (yup<0) {
27843         const float D = (float)ydown - yup;
27844         xup-=(int)((float)yup*((float)xdown - xup)/D);
27845         txup-=(int)((float)yup*((float)txdown - txup)/D);
27846         tyup-=(int)((float)yup*((float)tydown - tyup)/D);
27847         yup = 0;
27848       }
27849       if (ydown>=height()) {
27850         const float d = (float)ydown - height(), D = (float)ydown - yup;
27851         xdown-=(int)(d*((float)xdown - xup)/D);
27852         txdown-=(int)(d*((float)txdown - txup)/D);
27853         tydown-=(int)(d*((float)tydown - tyup)/D);
27854         ydown = height() - 1;
27855       }
27856       T *ptrd0 = data(nx0,ny0);
27857       int dx = xright - xleft, dy = ydown - yup;
27858       const bool steep = dy>dx;
27859       if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
27860       const long
27861         offx = (nx0<nx1?1:-1)*(steep?width():1),
27862         offy = (ny0<ny1?1:-1)*(steep?1:width()),
27863         ndx = dx>0?dx:1;
27864       const unsigned long wh = (unsigned long)_width*_height;
27865 
27866       if (opacity>=1) {
27867         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
27868           if (pattern&hatch) {
27869             T *ptrd = ptrd0;
27870             const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
27871             cimg_forC(*this,c) { *ptrd = (T)texture(tx,ty,0,c); ptrd+=wh; }
27872           }
27873           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
27874           ptrd0+=offx;
27875           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
27876         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
27877           T *ptrd = ptrd0;
27878           const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
27879           cimg_forC(*this,c) { *ptrd = (T)texture(tx,ty,0,c); ptrd+=wh; }
27880           ptrd0+=offx;
27881           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
27882         }
27883       } else {
27884         const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
27885         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
27886           T *ptrd = ptrd0;
27887           if (pattern&hatch) {
27888             const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
27889             cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture(tx,ty,0,c) + *ptrd*copacity); ptrd+=wh; }
27890           }
27891           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
27892           ptrd0+=offx;
27893           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
27894         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
27895           T *ptrd = ptrd0;
27896           const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
27897           cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture(tx,ty,0,c) + *ptrd*copacity); ptrd+=wh; }
27898           ptrd0+=offx;
27899           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
27900         }
27901       }
27902       return *this;
27903     }
27904 
27905     //! Draw a textured 2d line, with perspective correction.
27906     /**
27907        \param x0 X-coordinate of the starting point.
27908        \param y0 Y-coordinate of the starting point.
27909        \param z0 Z-coordinate of the starting point
27910        \param x1 X-coordinate of the ending point.
27911        \param y1 Y-coordinate of the ending point.
27912        \param z1 Z-coordinate of the ending point.
27913        \param texture Texture image defining the pixel colors.
27914        \param tx0 X-coordinate of the starting texture point.
27915        \param ty0 Y-coordinate of the starting texture point.
27916        \param tx1 X-coordinate of the ending texture point.
27917        \param ty1 Y-coordinate of the ending texture point.
27918        \param opacity Drawing opacity.
27919        \param pattern An integer whose bits describe the line pattern.
27920        \param init_hatch Tells if the hash variable must be reinitialized.
27921     **/
27922     template<typename tc>
27923     CImg<T>& draw_line(const int x0, const int y0, const float z0,
27924                        const int x1, const int y1, const float z1,
27925                        const CImg<tc>& texture,
27926                        const int tx0, const int ty0,
27927                        const int tx1, const int ty1,
27928                        const float opacity=1,
27929                        const unsigned int pattern=~0U, const bool init_hatch=true) {
27930       if (is_empty() && z0<=0 && z1<=0) return *this;
27931       if (texture._depth>1 || texture._spectrum<_spectrum)
27932         throw CImgArgumentException(_cimg_instance
27933                                     "draw_line() : Invalid specified texture (%u,%u,%u,%u,%p).",
27934                                     cimg_instance,
27935                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
27936       if (is_overlapped(texture)) return draw_line(x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
27937       static unsigned int hatch = ~0U - (~0U>>1);
27938       if (init_hatch) hatch = ~0U - (~0U>>1);
27939       const bool xdir = x0<x1, ydir = y0<y1;
27940       int
27941         nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
27942         &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
27943         &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
27944         &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
27945         &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
27946       float
27947         Tx0 = tx0/z0, Tx1 = tx1/z1,
27948         Ty0 = ty0/z0, Ty1 = ty1/z1,
27949         Z0 = 1/z0, Z1 = 1/z1,
27950         dz = Z1 - Z0, dtx = Tx1 - Tx0, dty = Ty1 - Ty0,
27951         tnx0 = Tx0, tnx1 = Tx1, tny0 = Ty0, tny1 = Ty1, nz0 = Z0, nz1 = Z1,
27952         &zleft = xdir?nz0:nz1, &txleft = xdir?tnx0:tnx1, &tyleft = xdir?tny0:tny1,
27953         &zright = xdir?nz1:nz0, &txright = xdir?tnx1:tnx0, &tyright = xdir?tny1:tny0,
27954         &zup = ydir?nz0:nz1, &txup = ydir?tnx0:tnx1, &tyup = ydir?tny0:tny1,
27955         &zdown = ydir?nz1:nz0, &txdown = ydir?tnx1:tnx0, &tydown = ydir?tny1:tny0;
27956       if (xright<0 || xleft>=width()) return *this;
27957       if (xleft<0) {
27958         const float D = (float)xright - xleft;
27959         yleft-=(int)((float)xleft*((float)yright - yleft)/D);
27960         zleft-=(float)xleft*(zright - zleft)/D;
27961         txleft-=(float)xleft*(txright - txleft)/D;
27962         tyleft-=(float)xleft*(tyright - tyleft)/D;
27963         xleft = 0;
27964       }
27965       if (xright>=width()) {
27966         const float d = (float)xright - width(), D = (float)xright - xleft;
27967         yright-=(int)(d*((float)yright - yleft)/D);
27968         zright-=d*(zright - zleft)/D;
27969         txright-=d*(txright - txleft)/D;
27970         tyright-=d*(tyright - tyleft)/D;
27971         xright = width() - 1;
27972       }
27973       if (ydown<0 || yup>=height()) return *this;
27974       if (yup<0) {
27975         const float D = (float)ydown - yup;
27976         xup-=(int)((float)yup*((float)xdown - xup)/D);
27977         zup-=(float)yup*(zdown - zup)/D;
27978         txup-=(float)yup*(txdown - txup)/D;
27979         tyup-=(float)yup*(tydown - tyup)/D;
27980         yup = 0;
27981       }
27982       if (ydown>=height()) {
27983         const float d = (float)ydown - height(), D = (float)ydown - yup;
27984         xdown-=(int)(d*((float)xdown - xup)/D);
27985         zdown-=d*(zdown - zup)/D;
27986         txdown-=d*(txdown - txup)/D;
27987         tydown-=d*(tydown - tyup)/D;
27988         ydown = height() - 1;
27989       }
27990       T *ptrd0 = data(nx0,ny0);
27991       int dx = xright - xleft, dy = ydown - yup;
27992       const bool steep = dy>dx;
27993       if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
27994       const long
27995         offx = (nx0<nx1?1:-1)*(steep?width():1),
27996         offy = (ny0<ny1?1:-1)*(steep?1:width()),
27997         ndx = dx>0?dx:1;
27998       const unsigned long wh = (unsigned long)_width*_height;
27999 
28000       if (opacity>=1) {
28001         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
28002           if (pattern&hatch) {
28003             const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
28004             T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); ptrd+=wh; }
28005           }
28006           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
28007           ptrd0+=offx;
28008           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
28009         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
28010           const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
28011           T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); ptrd+=wh; }
28012           ptrd0+=offx;
28013           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
28014         }
28015       } else {
28016         const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
28017         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
28018           if (pattern&hatch) {
28019             const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
28020             T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,c) + *ptrd*copacity); ptrd+=wh; }
28021           }
28022           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
28023           ptrd0+=offx;
28024           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
28025         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
28026           const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
28027           T *ptrd = ptrd0;
28028           cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,c) + *ptrd*copacity); ptrd+=wh; }
28029           ptrd0+=offx;
28030           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
28031         }
28032       }
28033       return *this;
28034     }
28035 
28036     //! Draw a textured 2d line, with perspective correction and z-buffering.
28037     /**
28038        \param zbuffer Z-buffer image.
28039        \param x0 X-coordinate of the starting point.
28040        \param y0 Y-coordinate of the starting point.
28041        \param z0 Z-coordinate of the starting point
28042        \param x1 X-coordinate of the ending point.
28043        \param y1 Y-coordinate of the ending point.
28044        \param z1 Z-coordinate of the ending point.
28045        \param texture Texture image defining the pixel colors.
28046        \param tx0 X-coordinate of the starting texture point.
28047        \param ty0 Y-coordinate of the starting texture point.
28048        \param tx1 X-coordinate of the ending texture point.
28049        \param ty1 Y-coordinate of the ending texture point.
28050        \param opacity Drawing opacity.
28051        \param pattern An integer whose bits describe the line pattern.
28052        \param init_hatch Tells if the hash variable must be reinitialized.
28053     **/
28054     template<typename tz, typename tc>
28055     CImg<T>& draw_line(CImg<tz>& zbuffer,
28056                        const int x0, const int y0, const float z0,
28057                        const int x1, const int y1, const float z1,
28058                        const CImg<tc>& texture,
28059                        const int tx0, const int ty0,
28060                        const int tx1, const int ty1,
28061                        const float opacity=1,
28062                        const unsigned int pattern=~0U, const bool init_hatch=true) {
28063       typedef typename cimg::superset<tz,float>::type tzfloat;
28064       if (is_empty() || z0<=0 || z1<=0) return *this;
28065       if (!is_sameXY(zbuffer))
28066         throw CImgArgumentException(_cimg_instance
28067                                     "draw_line() : Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
28068                                     cimg_instance,
28069                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
28070       if (texture._depth>1 || texture._spectrum<_spectrum)
28071         throw CImgArgumentException(_cimg_instance
28072                                     "draw_line() : Invalid specified texture (%u,%u,%u,%u,%p).",
28073                                     cimg_instance,
28074                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
28075       if (is_overlapped(texture)) return draw_line(zbuffer,x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
28076       static unsigned int hatch = ~0U - (~0U>>1);
28077       if (init_hatch) hatch = ~0U - (~0U>>1);
28078       const bool xdir = x0<x1, ydir = y0<y1;
28079       int
28080         nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
28081         &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
28082         &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
28083         &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
28084         &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
28085       float
28086         Tx0 = tx0/z0, Tx1 = tx1/z1,
28087         Ty0 = ty0/z0, Ty1 = ty1/z1,
28088         dtx = Tx1 - Tx0, dty = Ty1 - Ty0,
28089         tnx0 = Tx0, tnx1 = Tx1, tny0 = Ty0, tny1 = Ty1,
28090         &txleft = xdir?tnx0:tnx1, &tyleft = xdir?tny0:tny1,
28091         &txright = xdir?tnx1:tnx0, &tyright = xdir?tny1:tny0,
28092         &txup = ydir?tnx0:tnx1, &tyup = ydir?tny0:tny1,
28093         &txdown = ydir?tnx1:tnx0, &tydown = ydir?tny1:tny0;
28094       tzfloat
28095         Z0 = 1/(tzfloat)z0, Z1 = 1/(tzfloat)z1,
28096         dz = Z1 - Z0,  nz0 = Z0, nz1 = Z1,
28097         &zleft = xdir?nz0:nz1,
28098         &zright = xdir?nz1:nz0,
28099         &zup = ydir?nz0:nz1,
28100         &zdown = ydir?nz1:nz0;
28101       if (xright<0 || xleft>=width()) return *this;
28102       if (xleft<0) {
28103         const float D = (float)xright - xleft;
28104         yleft-=(int)((float)xleft*((float)yright - yleft)/D);
28105         zleft-=(float)xleft*(zright - zleft)/D;
28106         txleft-=(float)xleft*(txright - txleft)/D;
28107         tyleft-=(float)xleft*(tyright - tyleft)/D;
28108         xleft = 0;
28109       }
28110       if (xright>=width()) {
28111         const float d = (float)xright - width(), D = (float)xright - xleft;
28112         yright-=(int)(d*((float)yright - yleft)/D);
28113         zright-=d*(zright - zleft)/D;
28114         txright-=d*(txright - txleft)/D;
28115         tyright-=d*(tyright - tyleft)/D;
28116         xright = width()-1;
28117       }
28118       if (ydown<0 || yup>=height()) return *this;
28119       if (yup<0) {
28120         const float D = (float)ydown - yup;
28121         xup-=(int)((float)yup*((float)xdown - xup)/D);
28122         zup-=yup*(zdown - zup)/D;
28123         txup-=yup*(txdown - txup)/D;
28124         tyup-=yup*(tydown - tyup)/D;
28125         yup = 0;
28126       }
28127       if (ydown>=height()) {
28128         const float d = (float)ydown - height(), D = (float)ydown - yup;
28129         xdown-=(int)(d*((float)xdown - xup)/D);
28130         zdown-=d*(zdown - zup)/D;
28131         txdown-=d*(txdown - txup)/D;
28132         tydown-=d*(tydown - tyup)/D;
28133         ydown = height()-1;
28134       }
28135       T *ptrd0 = data(nx0,ny0);
28136       tz *ptrz = zbuffer.data(nx0,ny0);
28137       int dx = xright - xleft, dy = ydown - yup;
28138       const bool steep = dy>dx;
28139       if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
28140       const long
28141         offx = (nx0<nx1?1:-1)*(steep?width():1),
28142         offy = (ny0<ny1?1:-1)*(steep?1:width()),
28143         ndx = dx>0?dx:1;
28144       const unsigned long wh = (unsigned long)_width*_height;
28145 
28146       if (opacity>=1) {
28147         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
28148           if (pattern&hatch) {
28149             const tzfloat z = Z0 + x*dz/ndx;
28150             if (z>=(tzfloat)*ptrz) {
28151               *ptrz = (tz)z;
28152               const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
28153               T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); ptrd+=wh; }
28154             }
28155           }
28156           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
28157           ptrd0+=offx; ptrz+=offx;
28158           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
28159         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
28160           const tzfloat z = Z0 + x*dz/ndx;
28161           if (z>=(tzfloat)*ptrz) {
28162             *ptrz = (tz)z;
28163             const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
28164             T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); ptrd+=wh; }
28165           }
28166           ptrd0+=offx; ptrz+=offx;
28167           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
28168         }
28169       } else {
28170         const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
28171         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
28172           if (pattern&hatch) {
28173             const tzfloat z = Z0 + x*dz/ndx;
28174             if (z>=(tzfloat)*ptrz) {
28175               *ptrz = (tz)z;
28176               const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
28177               T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,c) + *ptrd*copacity); ptrd+=wh; }
28178             }
28179           }
28180           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
28181           ptrd0+=offx; ptrz+=offx;
28182           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
28183         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
28184           const tzfloat z = Z0 + x*dz/ndx;
28185           if (z>=(tzfloat)*ptrz) {
28186             *ptrz = (tz)z;
28187             const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
28188             T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,c) + *ptrd*copacity); ptrd+=wh; }
28189           }
28190           ptrd0+=offx; ptrz+=offx;
28191           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offx; error+=dx; }
28192         }
28193       }
28194       return *this;
28195     }
28196 
28197     //! Draw a set of consecutive lines.
28198     /**
28199        \param points Coordinates of vertices, stored as a list of vectors.
28200        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
28201        \param opacity Drawing opacity.
28202        \param pattern An integer whose bits describe the line pattern.
28203        \param init_hatch If set to true, init hatch motif.
28204        \note
28205        - This function uses several call to the single CImg::draw_line() procedure,
28206        depending on the vectors size in \p points.
28207        \par Example:
28208        \code
28209        CImg<unsigned char> img(100,100,1,3,0);
28210        const unsigned char color[] = { 255,128,64 };
28211        CImgList<int> points;
28212        points.insert(CImg<int>::vector(0,0)).
28213              .insert(CImg<int>::vector(70,10)).
28214              .insert(CImg<int>::vector(80,60)).
28215              .insert(CImg<int>::vector(10,90));
28216        img.draw_line(points,color);
28217        \endcode
28218     **/
28219     template<typename t, typename tc>
28220     CImg<T>& draw_line(const CImg<t>& points,
28221                        const tc *const color, const float opacity=1,
28222                        const unsigned int pattern=~0U, const bool init_hatch=true) {
28223       if (is_empty() || !points || points._width<2) return *this;
28224       bool ninit_hatch = init_hatch;
28225       switch (points._height) {
28226       case 0 : case 1 :
28227         throw CImgArgumentException(_cimg_instance
28228                                     "draw_line() : Invalid specified point set (%u,%u,%u,%u,%p).",
28229                                     cimg_instance,
28230                                     points._width,points._height,points._depth,points._spectrum,points._data);
28231 
28232       case 2 : {
28233         const int x0 = (int)points(0,0), y0 = (int)points(0,1);
28234         int ox = x0, oy = y0;
28235         for (unsigned int i = 1; i<points._width; ++i) {
28236           const int x = (int)points(i,0), y = (int)points(i,1);
28237           draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch);
28238           ninit_hatch = false;
28239           ox = x; oy = y;
28240         }
28241       } break;
28242       default : {
28243         const int x0 = (int)points(0,0), y0 = (int)points(0,1), z0 = (int)points(0,2);
28244         int ox = x0, oy = y0, oz = z0;
28245         for (unsigned int i = 1; i<points._width; ++i) {
28246           const int x = (int)points(i,0), y = (int)points(i,1), z = (int)points(i,2);
28247           draw_line(ox,oy,oz,x,y,z,color,opacity,pattern,ninit_hatch);
28248           ninit_hatch = false;
28249           ox = x; oy = y; oz = z;
28250         }
28251       }
28252       }
28253       return *this;
28254     }
28255 
28256     //! Draw a 2d arrow.
28257     /**
28258        \param x0 X-coordinate of the starting arrow point (tail).
28259        \param y0 Y-coordinate of the starting arrow point (tail).
28260        \param x1 X-coordinate of the ending arrow point (head).
28261        \param y1 Y-coordinate of the ending arrow point (head).
28262        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
28263        \param angle Aperture angle of the arrow head.
28264        \param length Length of the arrow head. If negative, describes a percentage of the arrow length.
28265        \param opacity Drawing opacity.
28266        \param pattern An integer whose bits describe the line pattern.
28267     **/
28268     template<typename tc>
28269     CImg<T>& draw_arrow(const int x0, const int y0,
28270                         const int x1, const int y1,
28271                         const tc *const color, const float opacity=1,
28272                         const float angle=30, const float length=-10,
28273                         const unsigned int pattern=~0U) {
28274       if (is_empty()) return *this;
28275       const float u = (float)(x0 - x1), v = (float)(y0 - y1), sq = u*u + v*v,
28276         deg = (float)(angle*cimg::PI/180), ang = (sq>0)?(float)std::atan2(v,u):0.0f,
28277         l = (length>=0)?length:-length*(float)std::sqrt(sq)/100;
28278       if (sq>0) {
28279         const float
28280             cl = (float)std::cos(ang - deg), sl = (float)std::sin(ang - deg),
28281             cr = (float)std::cos(ang + deg), sr = (float)std::sin(ang + deg);
28282         const int
28283           xl = x1 + (int)(l*cl), yl = y1 + (int)(l*sl),
28284           xr = x1 + (int)(l*cr), yr = y1 + (int)(l*sr),
28285           xc = x1 + (int)((l+1)*(cl+cr))/2, yc = y1 + (int)((l+1)*(sl+sr))/2;
28286         draw_line(x0,y0,xc,yc,color,opacity,pattern).draw_triangle(x1,y1,xl,yl,xr,yr,color,opacity);
28287       } else draw_point(x0,y0,color,opacity);
28288       return *this;
28289     }
28290 
28291     //! Draw a 2d spline.
28292     /**
28293        \param x0 X-coordinate of the starting curve point
28294        \param y0 Y-coordinate of the starting curve point
28295        \param u0 X-coordinate of the starting velocity
28296        \param v0 Y-coordinate of the starting velocity
28297        \param x1 X-coordinate of the ending curve point
28298        \param y1 Y-coordinate of the ending curve point
28299        \param u1 X-coordinate of the ending velocity
28300        \param v1 Y-coordinate of the ending velocity
28301        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
28302        \param precision Curve drawing precision.
28303        \param opacity Drawing opacity.
28304        \param pattern An integer whose bits describe the line pattern.
28305        \param init_hatch If \c true, init hatch motif.
28306        \note
28307        - The curve is a 2d cubic Bezier spline, from the set of specified starting/ending points
28308        and corresponding velocity vectors.
28309        - The spline is drawn as a serie of connected segments. The \p precision parameter sets the
28310        average number of pixels in each drawn segment.
28311        - A cubic Bezier curve is sometimes defined by a set of 4 points { (\p x0,\p y0), (\p xa,\p ya), (\p xb,\p yb), (\p x1,\p y1) }
28312        where (\p x0,\p y0) is the starting point, (\p x1,\p y1) is the ending point and (\p xa,\p ya), (\p xb,\p yb) are two
28313        \e control points.
28314        The starting and ending velocities (\p u0,\p v0) and (\p u1,\p v1) can be deduced easily from the control points as
28315        \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).
28316        \par Example:
28317        \code
28318        CImg<unsigned char> img(100,100,1,3,0);
28319        const unsigned char color[] = { 255,255,255 };
28320        img.draw_spline(30,30,0,100,90,40,0,-100,color);
28321        \endcode
28322     **/
28323     template<typename tc>
28324     CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0,
28325                          const int x1, const int y1, const float u1, const float v1,
28326                          const tc *const color, const float opacity=1,
28327                          const float precision=0.25, const unsigned int pattern=~0U,
28328                          const bool init_hatch=true) {
28329       if (is_empty()) return *this;
28330       if (!color)
28331         throw CImgArgumentException(_cimg_instance
28332                                     "draw_spline() : Specified color is (null).",
28333                                     cimg_instance);
28334       if (x0==x1 && y0==y1) return draw_point(x0,y0,color,opacity);
28335       bool ninit_hatch = init_hatch;
28336       const float
28337         ax = u0 + u1 + 2*(x0 - x1),
28338         bx = 3*(x1 - x0) - 2*u0 - u1,
28339         ay = v0 + v1 + 2*(y0 - y1),
28340         by = 3*(y1 - y0) - 2*v0 - v1,
28341         _precision = 1/(std::sqrt(cimg::sqr((float)x0-x1)+cimg::sqr((float)y0-y1))*(precision>0?precision:1));
28342       int ox = x0, oy = y0;
28343       for (float t = 0; t<1; t+=_precision) {
28344         const float t2 = t*t, t3 = t2*t;
28345         const int
28346           nx = (int)(ax*t3 + bx*t2 + u0*t + x0),
28347           ny = (int)(ay*t3 + by*t2 + v0*t + y0);
28348         draw_line(ox,oy,nx,ny,color,opacity,pattern,ninit_hatch);
28349         ninit_hatch = false;
28350         ox = nx; oy = ny;
28351       }
28352       return draw_line(ox,oy,x1,y1,color,opacity,pattern,false);
28353     }
28354 
28355     //! Draw a 3d spline \overloading.
28356     /**
28357        \note
28358        - Similar to CImg::draw_spline() for a 3d spline in a volumetric image.
28359     **/
28360     template<typename tc>
28361     CImg<T>& draw_spline(const int x0, const int y0, const int z0, const float u0, const float v0, const float w0,
28362                          const int x1, const int y1, const int z1, const float u1, const float v1, const float w1,
28363                          const tc *const color, const float opacity=1,
28364                          const float precision=4, const unsigned int pattern=~0U,
28365                          const bool init_hatch=true) {
28366       if (is_empty()) return *this;
28367       if (!color)
28368         throw CImgArgumentException(_cimg_instance
28369                                     "draw_spline() : Specified color is (null).",
28370                                     cimg_instance);
28371       if (x0==x1 && y0==y1 && z0==z1) return draw_point(x0,y0,z0,color,opacity);
28372       bool ninit_hatch = init_hatch;
28373       const float
28374         ax = u0 + u1 + 2*(x0 - x1),
28375         bx = 3*(x1 - x0) - 2*u0 - u1,
28376         ay = v0 + v1 + 2*(y0 - y1),
28377         by = 3*(y1 - y0) - 2*v0 - v1,
28378         az = w0 + w1 + 2*(z0 - z1),
28379         bz = 3*(z1 - z0) - 2*w0 - w1,
28380         _precision = 1/(std::sqrt(cimg::sqr(x0-x1)+cimg::sqr(y0-y1))*(precision>0?precision:1));
28381       int ox = x0, oy = y0, oz = z0;
28382       for (float t = 0; t<1; t+=_precision) {
28383         const float t2 = t*t, t3 = t2*t;
28384         const int
28385           nx = (int)(ax*t3 + bx*t2 + u0*t + x0),
28386           ny = (int)(ay*t3 + by*t2 + v0*t + y0),
28387           nz = (int)(az*t3 + bz*t2 + w0*t + z0);
28388         draw_line(ox,oy,oz,nx,ny,nz,color,opacity,pattern,ninit_hatch);
28389         ninit_hatch = false;
28390         ox = nx; oy = ny; oz = nz;
28391       }
28392       return draw_line(ox,oy,oz,x1,y1,z1,color,opacity,pattern,false);
28393     }
28394 
28395     //! Draw a textured 2d spline.
28396     /**
28397        \param x0 X-coordinate of the starting curve point
28398        \param y0 Y-coordinate of the starting curve point
28399        \param u0 X-coordinate of the starting velocity
28400        \param v0 Y-coordinate of the starting velocity
28401        \param x1 X-coordinate of the ending curve point
28402        \param y1 Y-coordinate of the ending curve point
28403        \param u1 X-coordinate of the ending velocity
28404        \param v1 Y-coordinate of the ending velocity
28405        \param texture Texture image defining line pixel colors.
28406        \param tx0 X-coordinate of the starting texture point.
28407        \param ty0 Y-coordinate of the starting texture point.
28408        \param tx1 X-coordinate of the ending texture point.
28409        \param ty1 Y-coordinate of the ending texture point.
28410        \param precision Curve drawing precision.
28411        \param opacity Drawing opacity.
28412        \param pattern An integer whose bits describe the line pattern.
28413        \param init_hatch if \c true, reinit hatch motif.
28414     **/
28415     template<typename t>
28416     CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0,
28417                          const int x1, const int y1, const float u1, const float v1,
28418                          const CImg<t>& texture,
28419                          const int tx0, const int ty0, const int tx1, const int ty1,
28420                          const float opacity=1,
28421                          const float precision=4, const unsigned int pattern=~0U,
28422                          const bool init_hatch=true) {
28423       if (texture._depth>1 || texture._spectrum<_spectrum)
28424         throw CImgArgumentException(_cimg_instance
28425                                     "draw_spline() : Invalid specified texture (%u,%u,%u,%u,%p).",
28426                                     cimg_instance,
28427                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
28428       if (is_empty()) return *this;
28429       if (is_overlapped(texture)) return draw_spline(x0,y0,u0,v0,x1,y1,u1,v1,+texture,tx0,ty0,tx1,ty1,precision,opacity,pattern,init_hatch);
28430       if (x0==x1 && y0==y1) return draw_point(x0,y0,texture.get_vector_at(x0,y0),opacity);
28431       bool ninit_hatch = init_hatch;
28432       const float
28433         ax = u0 + u1 + 2*(x0 - x1),
28434         bx = 3*(x1 - x0) - 2*u0 - u1,
28435         ay = v0 + v1 + 2*(y0 - y1),
28436         by = 3*(y1 - y0) - 2*v0 - v1,
28437         _precision = 1/(std::sqrt(cimg::sqr(x0-x1)+cimg::sqr(y0-y1))*(precision>0?precision:1));
28438       int ox = x0, oy = y0, otx = tx0, oty = ty0;
28439       for (float t1 = 0; t1<1; t1+=_precision) {
28440         const float t2 = t1*t1, t3 = t2*t1;
28441         const int
28442           nx = (int)(ax*t3 + bx*t2 + u0*t1 + x0),
28443           ny = (int)(ay*t3 + by*t2 + v0*t1 + y0),
28444           ntx = tx0 + (int)((tx1-tx0)*t1),
28445           nty = ty0 + (int)((ty1-ty0)*t1);
28446         draw_line(ox,oy,nx,ny,texture,otx,oty,ntx,nty,opacity,pattern,ninit_hatch);
28447         ninit_hatch = false;
28448         ox = nx; oy = ny; otx = ntx; oty = nty;
28449       }
28450       return draw_line(ox,oy,x1,y1,texture,otx,oty,tx1,ty1,opacity,pattern,false);
28451     }
28452 
28453     //! Draw a set of consecutive splines.
28454     /**
28455        \param points Vertices data.
28456        \param tangents Tangents data.
28457        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
28458        \param opacity Drawing opacity.
28459        \param is_closed_set Tells if the drawn spline set is closed.
28460        \param precision Precision of the drawing.
28461        \param pattern An integer whose bits describe the line pattern.
28462        \param init_hatch If \c true, init hatch motif.
28463     **/
28464     template<typename tp, typename tt, typename tc>
28465     CImg<T>& draw_spline(const CImg<tp>& points, const CImg<tt>& tangents,
28466                          const tc *const color, const float opacity=1,
28467                          const bool is_closed_set=false, const float precision=4,
28468                          const unsigned int pattern=~0U, const bool init_hatch=true) {
28469       if (is_empty() || !points || !tangents || points._width<2 || tangents._width<2) return *this;
28470       bool ninit_hatch = init_hatch;
28471       switch (points._height) {
28472       case 0 : case 1 :
28473         throw CImgArgumentException(_cimg_instance
28474                                     "draw_spline() : Invalid specified point set (%u,%u,%u,%u,%p).",
28475                                     cimg_instance,
28476                                     points._width,points._height,points._depth,points._spectrum,points._data);
28477 
28478       case 2 : {
28479         const int x0 = (int)points(0,0), y0 = (int)points(0,1);
28480         const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1);
28481         int ox = x0, oy = y0;
28482         float ou = u0, ov = v0;
28483         for (unsigned int i = 1; i<points._width; ++i) {
28484           const int x = (int)points(i,0), y = (int)points(i,1);
28485           const float u = (float)tangents(i,0), v = (float)tangents(i,1);
28486           draw_spline(ox,oy,ou,ov,x,y,u,v,color,precision,opacity,pattern,ninit_hatch);
28487           ninit_hatch = false;
28488           ox = x; oy = y; ou = u; ov = v;
28489         }
28490         if (is_closed_set) draw_spline(ox,oy,ou,ov,x0,y0,u0,v0,color,precision,opacity,pattern,false);
28491       } break;
28492       default : {
28493         const int x0 = (int)points(0,0), y0 = (int)points(0,1), z0 = (int)points(0,2);
28494         const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1), w0 = (float)tangents(0,2);
28495         int ox = x0, oy = y0, oz = z0;
28496         float ou = u0, ov = v0, ow = w0;
28497         for (unsigned int i = 1; i<points._width; ++i) {
28498           const int x = (int)points(i,0), y = (int)points(i,1), z = (int)points(i,2);
28499           const float u = (float)tangents(i,0), v = (float)tangents(i,1), w = (float)tangents(i,2);
28500           draw_spline(ox,oy,oz,ou,ov,ow,x,y,z,u,v,w,color,opacity,pattern,ninit_hatch);
28501           ninit_hatch = false;
28502           ox = x; oy = y; oz = z; ou = u; ov = v; ow = w;
28503         }
28504         if (is_closed_set) draw_spline(ox,oy,oz,ou,ov,ow,x0,y0,z0,u0,v0,w0,color,precision,opacity,pattern,false);
28505       }
28506       }
28507       return *this;
28508     }
28509 
28510     //! Draw a set of consecutive splines \overloading.
28511     /**
28512        Similar to previous function, with the point tangents automatically estimated from the given points set.
28513     **/
28514     template<typename tp, typename tc>
28515     CImg<T>& draw_spline(const CImg<tp>& points,
28516                          const tc *const color, const float opacity=1,
28517                          const bool is_closed_set=false, const float precision=4,
28518                          const unsigned int pattern=~0U, const bool init_hatch=true) {
28519       if (is_empty() || !points || points._width<2) return *this;
28520       CImg<Tfloat> tangents;
28521       switch (points._height) {
28522       case 0 : case 1 :
28523         throw CImgArgumentException(_cimg_instance
28524                                     "draw_spline() : Invalid specified point set (%u,%u,%u,%u,%p).",
28525                                     cimg_instance,
28526                                     points._width,points._height,points._depth,points._spectrum,points._data);
28527       case 2 : {
28528         tangents.assign(points._width,points._height);
28529         cimg_forX(points,p) {
28530           const unsigned int
28531             p0 = is_closed_set?(p+points._width-1)%points._width:(p?p-1:0),
28532             p1 = is_closed_set?(p+1)%points._width:(p+1<points._width?p+1:p);
28533           const float
28534             x = (float)points(p,0),
28535             y = (float)points(p,1),
28536             x0 = (float)points(p0,0),
28537             y0 = (float)points(p0,1),
28538             x1 = (float)points(p1,0),
28539             y1 = (float)points(p1,1),
28540             u0 = x - x0,
28541             v0 = y - y0,
28542             n0 = 1e-8f + (float)std::sqrt(u0*u0 + v0*v0),
28543             u1 = x1 - x,
28544             v1 = y1 - y,
28545             n1 = 1e-8f + (float)std::sqrt(u1*u1 + v1*v1),
28546             u = u0/n0 + u1/n1,
28547             v = v0/n0 + v1/n1,
28548             n = 1e-8f + (float)std::sqrt(u*u + v*v),
28549             fact = 0.5f*(n0 + n1);
28550           tangents(p,0) = (Tfloat)(fact*u/n);
28551           tangents(p,1) = (Tfloat)(fact*v/n);
28552         }
28553       } break;
28554       default : {
28555         tangents.assign(points._width,points._height);
28556         cimg_forX(points,p) {
28557           const unsigned int
28558             p0 = is_closed_set?(p+points._width-1)%points._width:(p?p-1:0),
28559             p1 = is_closed_set?(p+1)%points._width:(p+1<points._width?p+1:p);
28560           const float
28561             x = (float)points(p,0),
28562             y = (float)points(p,1),
28563             z = (float)points(p,2),
28564             x0 = (float)points(p0,0),
28565             y0 = (float)points(p0,1),
28566             z0 = (float)points(p0,2),
28567             x1 = (float)points(p1,0),
28568             y1 = (float)points(p1,1),
28569             z1 = (float)points(p1,2),
28570             u0 = x - x0,
28571             v0 = y - y0,
28572             w0 = z - z0,
28573             n0 = 1e-8f + (float)std::sqrt(u0*u0 + v0*v0 + w0*w0),
28574             u1 = x1 - x,
28575             v1 = y1 - y,
28576             w1 = z1 - z,
28577             n1 = 1e-8f + (float)std::sqrt(u1*u1 + v1*v1 + w1*w1),
28578             u = u0/n0 + u1/n1,
28579             v = v0/n0 + v1/n1,
28580             w = w0/n0 + w1/n1,
28581             n = 1e-8f + (float)std::sqrt(u*u + v*v + w*w),
28582             fact = 0.5f*(n0 + n1);
28583           tangents(p,0) = (Tfloat)(fact*u/n);
28584           tangents(p,1) = (Tfloat)(fact*v/n);
28585           tangents(p,2) = (Tfloat)(fact*w/n);
28586         }
28587       }
28588       }
28589       return draw_spline(points,tangents,color,opacity,is_closed_set,precision,pattern,init_hatch);
28590     }
28591 
28592     // Inner macro for drawing triangles.
28593 #define _cimg_for_triangle1(img,xl,xr,y,x0,y0,x1,y1,x2,y2) \
28594         for (int y = y0<0?0:y0, \
28595                xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \
28596                xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \
28597                _sxn=1, \
28598                _sxr=1, \
28599                _sxl=1, \
28600                _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \
28601                _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \
28602                _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \
28603                _dyn = y2-y1, \
28604                _dyr = y2-y0, \
28605                _dyl = y1-y0, \
28606                _counter = (_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
28607                            _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
28608                            _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
28609                            cimg::min((int)(img)._height-y-1,y2-y)), \
28610                _errn = _dyn/2, \
28611                _errr = _dyr/2, \
28612                _errl = _dyl/2, \
28613                _rxn = _dyn?(x2-x1)/_dyn:0, \
28614                _rxr = _dyr?(x2-x0)/_dyr:0, \
28615                _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
28616                                        (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn); \
28617              _counter>=0; --_counter, ++y, \
28618                xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
28619                xl+=(y!=y1)?_rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0): \
28620                            (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
28621 
28622 #define _cimg_for_triangle2(img,xl,cl,xr,cr,y,x0,y0,c0,x1,y1,c1,x2,y2,c2) \
28623         for (int y = y0<0?0:y0, \
28624                xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \
28625                cr = y0>=0?c0:(c0-y0*(c2-c0)/(y2-y0)), \
28626                xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \
28627                cl = y1>=0?(y0>=0?(y0==y1?c1:c0):(c0-y0*(c1-c0)/(y1-y0))):(c1-y1*(c2-c1)/(y2-y1)), \
28628                _sxn=1, _scn=1, \
28629                _sxr=1, _scr=1, \
28630                _sxl=1, _scl=1, \
28631                _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \
28632                _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \
28633                _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \
28634                _dcn = c2>c1?c2-c1:(_scn=-1,c1-c2), \
28635                _dcr = c2>c0?c2-c0:(_scr=-1,c0-c2), \
28636                _dcl = c1>c0?c1-c0:(_scl=-1,c0-c1), \
28637                _dyn = y2-y1, \
28638                _dyr = y2-y0, \
28639                _dyl = y1-y0, \
28640                _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
28641                           _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
28642                           _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
28643                           _dcn-=_dyn?_dyn*(_dcn/_dyn):0, \
28644                           _dcr-=_dyr?_dyr*(_dcr/_dyr):0, \
28645                           _dcl-=_dyl?_dyl*(_dcl/_dyl):0, \
28646                           cimg::min((int)(img)._height-y-1,y2-y)), \
28647                _errn = _dyn/2, _errcn = _errn, \
28648                _errr = _dyr/2, _errcr = _errr, \
28649                _errl = _dyl/2, _errcl = _errl, \
28650                _rxn = _dyn?(x2-x1)/_dyn:0, \
28651                _rcn = _dyn?(c2-c1)/_dyn:0, \
28652                _rxr = _dyr?(x2-x0)/_dyr:0, \
28653                _rcr = _dyr?(c2-c0)/_dyr:0, \
28654                _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
28655                                        (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
28656                _rcl = (y0!=y1 && y1>0)?(_dyl?(c1-c0)/_dyl:0): \
28657                                        (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcn ); \
28658              _counter>=0; --_counter, ++y, \
28659                xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
28660                cr+=_rcr+((_errcr-=_dcr)<0?_errcr+=_dyr,_scr:0), \
28661                xl+=(y!=y1)?(cl+=_rcl+((_errcl-=_dcl)<0?(_errcl+=_dyl,_scl):0), \
28662                            _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
28663                (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcl=_rcn, cl=c1, \
28664                 _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
28665 
28666 #define _cimg_for_triangle3(img,xl,txl,tyl,xr,txr,tyr,y,x0,y0,tx0,ty0,x1,y1,tx1,ty1,x2,y2,tx2,ty2) \
28667         for (int y = y0<0?0:y0, \
28668                xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \
28669                txr = y0>=0?tx0:(tx0-y0*(tx2-tx0)/(y2-y0)), \
28670                tyr = y0>=0?ty0:(ty0-y0*(ty2-ty0)/(y2-y0)), \
28671                xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \
28672                txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0-y0*(tx1-tx0)/(y1-y0))):(tx1-y1*(tx2-tx1)/(y2-y1)), \
28673                tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0-y0*(ty1-ty0)/(y1-y0))):(ty1-y1*(ty2-ty1)/(y2-y1)), \
28674                _sxn=1, _stxn=1, _styn=1, \
28675                _sxr=1, _stxr=1, _styr=1, \
28676                _sxl=1, _stxl=1, _styl=1, \
28677                _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \
28678                _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \
28679                _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \
28680                _dtxn = tx2>tx1?tx2-tx1:(_stxn=-1,tx1-tx2), \
28681                _dtxr = tx2>tx0?tx2-tx0:(_stxr=-1,tx0-tx2), \
28682                _dtxl = tx1>tx0?tx1-tx0:(_stxl=-1,tx0-tx1), \
28683                _dtyn = ty2>ty1?ty2-ty1:(_styn=-1,ty1-ty2), \
28684                _dtyr = ty2>ty0?ty2-ty0:(_styr=-1,ty0-ty2), \
28685                _dtyl = ty1>ty0?ty1-ty0:(_styl=-1,ty0-ty1), \
28686                _dyn = y2-y1, \
28687                _dyr = y2-y0, \
28688                _dyl = y1-y0, \
28689                _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
28690                           _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
28691                           _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
28692                           _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \
28693                           _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \
28694                           _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \
28695                           _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \
28696                           _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \
28697                           _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \
28698                           cimg::min((int)(img)._height-y-1,y2-y)), \
28699                _errn = _dyn/2, _errtxn = _errn, _errtyn = _errn, \
28700                _errr = _dyr/2, _errtxr = _errr, _errtyr = _errr, \
28701                _errl = _dyl/2, _errtxl = _errl, _errtyl = _errl, \
28702                _rxn = _dyn?(x2-x1)/_dyn:0, \
28703                _rtxn = _dyn?(tx2-tx1)/_dyn:0, \
28704                _rtyn = _dyn?(ty2-ty1)/_dyn:0, \
28705                _rxr = _dyr?(x2-x0)/_dyr:0, \
28706                _rtxr = _dyr?(tx2-tx0)/_dyr:0, \
28707                _rtyr = _dyr?(ty2-ty0)/_dyr:0, \
28708                _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
28709                                        (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
28710                _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1-tx0)/_dyl:0): \
28711                                        (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \
28712                _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1-ty0)/_dyl:0): \
28713                                        (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ); \
28714              _counter>=0; --_counter, ++y, \
28715                xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
28716                txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \
28717                tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \
28718                xl+=(y!=y1)?(txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \
28719                             tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \
28720                            _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
28721                (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \
28722                 _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1,\
28723                 _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
28724 
28725 #define _cimg_for_triangle4(img,xl,cl,txl,tyl,xr,cr,txr,tyr,y,x0,y0,c0,tx0,ty0,x1,y1,c1,tx1,ty1,x2,y2,c2,tx2,ty2) \
28726         for (int y = y0<0?0:y0, \
28727                xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \
28728                cr = y0>=0?c0:(c0-y0*(c2-c0)/(y2-y0)), \
28729                txr = y0>=0?tx0:(tx0-y0*(tx2-tx0)/(y2-y0)), \
28730                tyr = y0>=0?ty0:(ty0-y0*(ty2-ty0)/(y2-y0)), \
28731                xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \
28732                cl = y1>=0?(y0>=0?(y0==y1?c1:c0):(c0-y0*(c1-c0)/(y1-y0))):(c1-y1*(c2-c1)/(y2-y1)), \
28733                txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0-y0*(tx1-tx0)/(y1-y0))):(tx1-y1*(tx2-tx1)/(y2-y1)), \
28734                tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0-y0*(ty1-ty0)/(y1-y0))):(ty1-y1*(ty2-ty1)/(y2-y1)), \
28735                _sxn=1, _scn=1, _stxn=1, _styn=1, \
28736                _sxr=1, _scr=1, _stxr=1, _styr=1, \
28737                _sxl=1, _scl=1, _stxl=1, _styl=1, \
28738                _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \
28739                _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \
28740                _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \
28741                _dcn = c2>c1?c2-c1:(_scn=-1,c1-c2), \
28742                _dcr = c2>c0?c2-c0:(_scr=-1,c0-c2), \
28743                _dcl = c1>c0?c1-c0:(_scl=-1,c0-c1), \
28744                _dtxn = tx2>tx1?tx2-tx1:(_stxn=-1,tx1-tx2), \
28745                _dtxr = tx2>tx0?tx2-tx0:(_stxr=-1,tx0-tx2), \
28746                _dtxl = tx1>tx0?tx1-tx0:(_stxl=-1,tx0-tx1), \
28747                _dtyn = ty2>ty1?ty2-ty1:(_styn=-1,ty1-ty2), \
28748                _dtyr = ty2>ty0?ty2-ty0:(_styr=-1,ty0-ty2), \
28749                _dtyl = ty1>ty0?ty1-ty0:(_styl=-1,ty0-ty1), \
28750                _dyn = y2-y1, \
28751                _dyr = y2-y0, \
28752                _dyl = y1-y0, \
28753                _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
28754                           _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
28755                           _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
28756                           _dcn-=_dyn?_dyn*(_dcn/_dyn):0, \
28757                           _dcr-=_dyr?_dyr*(_dcr/_dyr):0, \
28758                           _dcl-=_dyl?_dyl*(_dcl/_dyl):0, \
28759                           _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \
28760                           _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \
28761                           _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \
28762                           _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \
28763                           _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \
28764                           _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \
28765                           cimg::min((int)(img)._height-y-1,y2-y)), \
28766                _errn = _dyn/2, _errcn = _errn, _errtxn = _errn, _errtyn = _errn, \
28767                _errr = _dyr/2, _errcr = _errr, _errtxr = _errr, _errtyr = _errr, \
28768                _errl = _dyl/2, _errcl = _errl, _errtxl = _errl, _errtyl = _errl, \
28769                _rxn = _dyn?(x2-x1)/_dyn:0, \
28770                _rcn = _dyn?(c2-c1)/_dyn:0, \
28771                _rtxn = _dyn?(tx2-tx1)/_dyn:0, \
28772                _rtyn = _dyn?(ty2-ty1)/_dyn:0, \
28773                _rxr = _dyr?(x2-x0)/_dyr:0, \
28774                _rcr = _dyr?(c2-c0)/_dyr:0, \
28775                _rtxr = _dyr?(tx2-tx0)/_dyr:0, \
28776                _rtyr = _dyr?(ty2-ty0)/_dyr:0, \
28777                _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
28778                                        (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
28779                _rcl = (y0!=y1 && y1>0)?(_dyl?(c1-c0)/_dyl:0): \
28780                                        (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcn ), \
28781                _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1-tx0)/_dyl:0): \
28782                                         (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \
28783                _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1-ty0)/_dyl:0): \
28784                                         (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ); \
28785              _counter>=0; --_counter, ++y, \
28786                xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
28787                cr+=_rcr+((_errcr-=_dcr)<0?_errcr+=_dyr,_scr:0), \
28788                txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \
28789                tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \
28790                xl+=(y!=y1)?(cl+=_rcl+((_errcl-=_dcl)<0?(_errcl+=_dyl,_scl):0), \
28791                             txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \
28792                             tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \
28793                             _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
28794                (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcl=_rcn, cl=c1, \
28795                 _errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \
28796                 _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1, \
28797                 _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
28798 
28799 #define _cimg_for_triangle5(img,xl,txl,tyl,lxl,lyl,xr,txr,tyr,lxr,lyr,y,x0,y0,tx0,ty0,lx0,ly0,x1,y1,tx1,ty1,lx1,ly1,x2,y2,tx2,ty2,lx2,ly2) \
28800         for (int y = y0<0?0:y0, \
28801                xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \
28802                txr = y0>=0?tx0:(tx0-y0*(tx2-tx0)/(y2-y0)), \
28803                tyr = y0>=0?ty0:(ty0-y0*(ty2-ty0)/(y2-y0)), \
28804                lxr = y0>=0?lx0:(lx0-y0*(lx2-lx0)/(y2-y0)), \
28805                lyr = y0>=0?ly0:(ly0-y0*(ly2-ly0)/(y2-y0)), \
28806                xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \
28807                txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0-y0*(tx1-tx0)/(y1-y0))):(tx1-y1*(tx2-tx1)/(y2-y1)), \
28808                tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0-y0*(ty1-ty0)/(y1-y0))):(ty1-y1*(ty2-ty1)/(y2-y1)), \
28809                lxl = y1>=0?(y0>=0?(y0==y1?lx1:lx0):(lx0-y0*(lx1-lx0)/(y1-y0))):(lx1-y1*(lx2-lx1)/(y2-y1)), \
28810                lyl = y1>=0?(y0>=0?(y0==y1?ly1:ly0):(ly0-y0*(ly1-ly0)/(y1-y0))):(ly1-y1*(ly2-ly1)/(y2-y1)), \
28811                _sxn=1, _stxn=1, _styn=1, _slxn=1, _slyn=1, \
28812                _sxr=1, _stxr=1, _styr=1, _slxr=1, _slyr=1, \
28813                _sxl=1, _stxl=1, _styl=1, _slxl=1, _slyl=1, \
28814                _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), _dyn = y2-y1, \
28815                _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), _dyr = y2-y0, \
28816                _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), _dyl = y1-y0, \
28817                _dtxn = tx2>tx1?tx2-tx1:(_stxn=-1,tx1-tx2), \
28818                _dtxr = tx2>tx0?tx2-tx0:(_stxr=-1,tx0-tx2), \
28819                _dtxl = tx1>tx0?tx1-tx0:(_stxl=-1,tx0-tx1), \
28820                _dtyn = ty2>ty1?ty2-ty1:(_styn=-1,ty1-ty2), \
28821                _dtyr = ty2>ty0?ty2-ty0:(_styr=-1,ty0-ty2), \
28822                _dtyl = ty1>ty0?ty1-ty0:(_styl=-1,ty0-ty1), \
28823                _dlxn = lx2>lx1?lx2-lx1:(_slxn=-1,lx1-lx2), \
28824                _dlxr = lx2>lx0?lx2-lx0:(_slxr=-1,lx0-lx2), \
28825                _dlxl = lx1>lx0?lx1-lx0:(_slxl=-1,lx0-lx1), \
28826                _dlyn = ly2>ly1?ly2-ly1:(_slyn=-1,ly1-ly2), \
28827                _dlyr = ly2>ly0?ly2-ly0:(_slyr=-1,ly0-ly2), \
28828                _dlyl = ly1>ly0?ly1-ly0:(_slyl=-1,ly0-ly1), \
28829                _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
28830                           _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
28831                           _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
28832                           _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \
28833                           _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \
28834                           _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \
28835                           _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \
28836                           _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \
28837                           _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \
28838                           _dlxn-=_dyn?_dyn*(_dlxn/_dyn):0, \
28839                           _dlxr-=_dyr?_dyr*(_dlxr/_dyr):0, \
28840                           _dlxl-=_dyl?_dyl*(_dlxl/_dyl):0, \
28841                           _dlyn-=_dyn?_dyn*(_dlyn/_dyn):0, \
28842                           _dlyr-=_dyr?_dyr*(_dlyr/_dyr):0, \
28843                           _dlyl-=_dyl?_dyl*(_dlyl/_dyl):0, \
28844                           cimg::min((int)(img)._height-y-1,y2-y)), \
28845                _errn = _dyn/2, _errtxn = _errn, _errtyn = _errn, _errlxn = _errn, _errlyn = _errn, \
28846                _errr = _dyr/2, _errtxr = _errr, _errtyr = _errr, _errlxr = _errr, _errlyr = _errr, \
28847                _errl = _dyl/2, _errtxl = _errl, _errtyl = _errl, _errlxl = _errl, _errlyl = _errl, \
28848                _rxn = _dyn?(x2-x1)/_dyn:0, \
28849                _rtxn = _dyn?(tx2-tx1)/_dyn:0, \
28850                _rtyn = _dyn?(ty2-ty1)/_dyn:0, \
28851                _rlxn = _dyn?(lx2-lx1)/_dyn:0, \
28852                _rlyn = _dyn?(ly2-ly1)/_dyn:0, \
28853                _rxr = _dyr?(x2-x0)/_dyr:0, \
28854                _rtxr = _dyr?(tx2-tx0)/_dyr:0, \
28855                _rtyr = _dyr?(ty2-ty0)/_dyr:0, \
28856                _rlxr = _dyr?(lx2-lx0)/_dyr:0, \
28857                _rlyr = _dyr?(ly2-ly0)/_dyr:0, \
28858                _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
28859                                        (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
28860                _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1-tx0)/_dyl:0): \
28861                                         (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \
28862                _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1-ty0)/_dyl:0): \
28863                                         (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ), \
28864                _rlxl = (y0!=y1 && y1>0)?(_dyl?(lx1-lx0)/_dyl:0): \
28865                                         (_errlxl=_errlxn, _dlxl=_dlxn, _dyl=_dyn, _slxl=_slxn, _rlxn ), \
28866                _rlyl = (y0!=y1 && y1>0)?(_dyl?(ly1-ly0)/_dyl:0): \
28867                                         (_errlyl=_errlyn, _dlyl=_dlyn, _dyl=_dyn, _slyl=_slyn, _rlyn ); \
28868              _counter>=0; --_counter, ++y, \
28869                xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
28870                txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \
28871                tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \
28872                lxr+=_rlxr+((_errlxr-=_dlxr)<0?_errlxr+=_dyr,_slxr:0), \
28873                lyr+=_rlyr+((_errlyr-=_dlyr)<0?_errlyr+=_dyr,_slyr:0), \
28874                xl+=(y!=y1)?(txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \
28875                             tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \
28876                             lxl+=_rlxl+((_errlxl-=_dlxl)<0?(_errlxl+=_dyl,_slxl):0), \
28877                             lyl+=_rlyl+((_errlyl-=_dlyl)<0?(_errlyl+=_dyl,_slyl):0), \
28878                             _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
28879                (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \
28880                 _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1, \
28881                 _errlxl=_errlxn, _dlxl=_dlxn, _dyl=_dyn, _slxl=_slxn, _rlxl=_rlxn, lxl=lx1, \
28882                 _errlyl=_errlyn, _dlyl=_dlyn, _dyl=_dyn, _slyl=_slyn, _rlyl=_rlyn, lyl=ly1, \
28883                 _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
28884 
28885     // [internal] Draw a filled triangle.
28886     template<typename tc>
28887     CImg<T>& _draw_triangle(const int x0, const int y0,
28888                             const int x1, const int y1,
28889                             const int x2, const int y2,
28890                             const tc *const color, const float opacity,
28891                             const float brightness) {
28892       _draw_scanline(color,opacity);
28893       const float nbrightness = brightness<0?0:(brightness>2?2:brightness);
28894       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
28895       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1);
28896       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2);
28897       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2);
28898       if (ny0<height() && ny2>=0) {
28899         if ((nx1 - nx0)*(ny2 - ny0) - (nx2 - nx0)*(ny1 - ny0)<0)
28900           _cimg_for_triangle1(*this,xl,xr,y,nx0,ny0,nx1,ny1,nx2,ny2) _draw_scanline(xl,xr,y,color,opacity,nbrightness);
28901         else
28902           _cimg_for_triangle1(*this,xl,xr,y,nx0,ny0,nx1,ny1,nx2,ny2) _draw_scanline(xr,xl,y,color,opacity,nbrightness);
28903       }
28904       return *this;
28905     }
28906 
28907     //! Draw a filled 2d triangle.
28908     /**
28909        \param x0 X-coordinate of the first vertex.
28910        \param y0 Y-coordinate of the first vertex.
28911        \param x1 X-coordinate of the second vertex.
28912        \param y1 Y-coordinate of the second vertex.
28913        \param x2 X-coordinate of the third vertex.
28914        \param y2 Y-coordinate of the third vertex.
28915        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
28916        \param opacity Drawing opacity.
28917      **/
28918     template<typename tc>
28919     CImg<T>& draw_triangle(const int x0, const int y0,
28920                            const int x1, const int y1,
28921                            const int x2, const int y2,
28922                            const tc *const color, const float opacity=1) {
28923       if (is_empty()) return *this;
28924       if (!color)
28925         throw CImgArgumentException(_cimg_instance
28926                                     "draw_triangle : Specified color is (null).",
28927                                     cimg_instance);
28928       _draw_triangle(x0,y0,x1,y1,x2,y2,color,opacity,1);
28929       return *this;
28930     }
28931 
28932     //! Draw a outlined 2d triangle.
28933     /**
28934        \param x0 X-coordinate of the first vertex.
28935        \param y0 Y-coordinate of the first vertex.
28936        \param x1 X-coordinate of the second vertex.
28937        \param y1 Y-coordinate of the second vertex.
28938        \param x2 X-coordinate of the third vertex.
28939        \param y2 Y-coordinate of the third vertex.
28940        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
28941        \param opacity Drawing opacity.
28942        \param pattern An integer whose bits describe the outline pattern.
28943      **/
28944     template<typename tc>
28945     CImg<T>& draw_triangle(const int x0, const int y0,
28946                            const int x1, const int y1,
28947                            const int x2, const int y2,
28948                            const tc *const color, const float opacity,
28949                            const unsigned int pattern) {
28950       if (is_empty()) return *this;
28951       if (!color)
28952         throw CImgArgumentException(_cimg_instance
28953                                     "draw_triangle : Specified color is (null).",
28954                                     cimg_instance);
28955       draw_line(x0,y0,x1,y1,color,opacity,pattern,true).
28956         draw_line(x1,y1,x2,y2,color,opacity,pattern,false).
28957         draw_line(x2,y2,x0,y0,color,opacity,pattern,false);
28958       return *this;
28959     }
28960 
28961     //! Draw a filled 2d triangle, with z-buffering.
28962     /**
28963        \param zbuffer Z-buffer image.
28964        \param x0 X-coordinate of the first vertex.
28965        \param y0 Y-coordinate of the first vertex.
28966        \param z0 Z-coordinate of the first vertex.
28967        \param x1 X-coordinate of the second vertex.
28968        \param y1 Y-coordinate of the second vertex.
28969        \param z1 Z-coordinate of the second vertex.
28970        \param x2 X-coordinate of the third vertex.
28971        \param y2 Y-coordinate of the third vertex.
28972        \param z2 Z-coordinate of the third vertex.
28973        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
28974        \param opacity Drawing opacity.
28975        \param brightness Brightness factor.
28976     **/
28977     template<typename tz, typename tc>
28978     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
28979                            const int x0, const int y0, const float z0,
28980                            const int x1, const int y1, const float z1,
28981                            const int x2, const int y2, const float z2,
28982                            const tc *const color, const float opacity=1,
28983                            const float brightness=1) {
28984       typedef typename cimg::superset<tz,float>::type tzfloat;
28985       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
28986       if (!color)
28987         throw CImgArgumentException(_cimg_instance
28988                                     "draw_triangle() : Specified color is (null).",
28989                                     cimg_instance);
28990       if (!is_sameXY(zbuffer))
28991         throw CImgArgumentException(_cimg_instance
28992                                     "draw_triangle() : Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
28993                                     cimg_instance,
28994                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
28995       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
28996       const float
28997         nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0),
28998         nbrightness = brightness<0?0:(brightness>2?2:brightness);
28999       const long whd = (long)_width*_height*_depth, offx = _spectrum*whd;
29000       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
29001       tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
29002       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
29003       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nz0,nz2);
29004       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nz1,nz2);
29005       if (ny0>=height() || ny2<0) return *this;
29006       tzfloat
29007         pzl = (nz1 - nz0)/(ny1 - ny0),
29008         pzr = (nz2 - nz0)/(ny2 - ny0),
29009         pzn = (nz2 - nz1)/(ny2 - ny1),
29010         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
29011         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
29012       _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) {
29013         if (y==ny1) { zl = nz1; pzl = pzn; }
29014         int xleft = xleft0, xright = xright0;
29015         tzfloat zleft = zl, zright = zr;
29016         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright);
29017         const int dx = xright - xleft;
29018         const tzfloat pentez = (zright - zleft)/dx;
29019         if (xleft<0 && dx) zleft-=xleft*(zright - zleft)/dx;
29020         if (xleft<0) xleft = 0;
29021         if (xright>=width()-1) xright = width() - 1;
29022         T* ptrd = data(xleft,y,0,0);
29023         tz *ptrz = zbuffer.data(xleft,y);
29024         if (opacity>=1) {
29025           if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29026               if (zleft>=(tzfloat)*ptrz) {
29027                 *ptrz = (tz)zleft;
29028               const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; }
29029               ptrd-=offx;
29030             }
29031             zleft+=pentez;
29032           } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29033               if (zleft>=(tzfloat)*ptrz) {
29034                 *ptrz = (tz)zleft;
29035               const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(nbrightness*(*col++)); ptrd+=whd; }
29036               ptrd-=offx;
29037             }
29038             zleft+=pentez;
29039           } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29040               if (zleft>=(tzfloat)*ptrz) {
29041                 *ptrz = (tz)zleft;
29042               const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval); ptrd+=whd; }
29043               ptrd-=offx;
29044             }
29045             zleft+=pentez;
29046           }
29047         } else {
29048           if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29049               if (zleft>=(tzfloat)*ptrz) {
29050                 *ptrz = (tz)zleft;
29051               const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=whd; }
29052               ptrd-=offx;
29053             }
29054             zleft+=pentez;
29055           } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29056               if (zleft>=(tzfloat)*ptrz) {
29057                 *ptrz = (tz)zleft;
29058               const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(nopacity*nbrightness**(col++) + *ptrd*copacity); ptrd+=whd; }
29059               ptrd-=offx;
29060             }
29061             zleft+=pentez;
29062           } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29063               if (zleft>=(tzfloat)*ptrz) {
29064                 *ptrz = (tz)zleft;
29065               const tc *col = color;
29066               cimg_forC(*this,c) {
29067                 const T val = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval);
29068                 *ptrd = (T)(nopacity*val + *ptrd*copacity);
29069                 ptrd+=whd;
29070               }
29071               ptrd-=offx;
29072             }
29073             zleft+=pentez;
29074           }
29075         }
29076         zr+=pzr; zl+=pzl;
29077       }
29078       return *this;
29079     }
29080 
29081     //! Draw a Gouraud-shaded 2d triangle.
29082     /**
29083        \param x0 X-coordinate of the first vertex in the image instance.
29084        \param y0 Y-coordinate of the first vertex in the image instance.
29085        \param x1 X-coordinate of the second vertex in the image instance.
29086        \param y1 Y-coordinate of the second vertex in the image instance.
29087        \param x2 X-coordinate of the third vertex in the image instance.
29088        \param y2 Y-coordinate of the third vertex in the image instance.
29089        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
29090        \param brightness0 Brightness factor of the first vertex (in [0,2]).
29091        \param brightness1 brightness factor of the second vertex (in [0,2]).
29092        \param brightness2 brightness factor of the third vertex (in [0,2]).
29093        \param opacity Drawing opacity.
29094     **/
29095     template<typename tc>
29096     CImg<T>& draw_triangle(const int x0, const int y0,
29097                            const int x1, const int y1,
29098                            const int x2, const int y2,
29099                            const tc *const color,
29100                            const float brightness0,
29101                            const float brightness1,
29102                            const float brightness2,
29103                            const float opacity=1) {
29104       if (is_empty()) return *this;
29105       if (!color)
29106         throw CImgArgumentException(_cimg_instance
29107                                     "draw_triangle : Specified color is (null).",
29108                                     cimg_instance);
29109       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
29110       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
29111       const long whd = (long)_width*_height*_depth, offx = _spectrum*whd-1;
29112       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
29113         nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
29114         nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
29115         nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
29116       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nc0,nc1);
29117       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nc0,nc2);
29118       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nc1,nc2);
29119       if (ny0>=height() || ny2<0) return *this;
29120       _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
29121         int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0;
29122         if (xright<xleft) cimg::swap(xleft,xright,cleft,cright);
29123         const int
29124           dx = xright - xleft,
29125           dc = cright>cleft?cright - cleft:cleft - cright,
29126           rc = dx?(cright - cleft)/dx:0,
29127           sc = cright>cleft?1:-1,
29128           ndc = dc-(dx?dx*(dc/dx):0);
29129         int errc = dx>>1;
29130         if (xleft<0 && dx) cleft-=xleft*(cright - cleft)/dx;
29131         if (xleft<0) xleft = 0;
29132         if (xright>=width()-1) xright = width() - 1;
29133         T* ptrd = data(xleft,y);
29134         if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
29135           const tc *col = color;
29136           cimg_forC(*this,c) {
29137             *ptrd = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256);
29138             ptrd+=whd;
29139           }
29140           ptrd-=offx;
29141           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
29142         } else for (int x = xleft; x<=xright; ++x) {
29143           const tc *col = color;
29144           cimg_forC(*this,c) {
29145             const T val = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256);
29146             *ptrd = (T)(nopacity*val + *ptrd*copacity);
29147             ptrd+=whd;
29148           }
29149           ptrd-=offx;
29150           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
29151         }
29152       }
29153       return *this;
29154     }
29155 
29156     //! Draw a Gouraud-shaded 2d triangle, with z-buffering \overloading.
29157     template<typename tz, typename tc>
29158     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
29159                            const int x0, const int y0, const float z0,
29160                            const int x1, const int y1, const float z1,
29161                            const int x2, const int y2, const float z2,
29162                            const tc *const color,
29163                            const float brightness0,
29164                            const float brightness1,
29165                            const float brightness2,
29166                            const float opacity=1) {
29167       typedef typename cimg::superset<tz,float>::type tzfloat;
29168       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
29169       if (!color)
29170         throw CImgArgumentException(_cimg_instance
29171                                     "draw_triangle() : Specified color is (null).",
29172                                     cimg_instance);
29173       if (!is_sameXY(zbuffer))
29174         throw CImgArgumentException(_cimg_instance
29175                                     "draw_triangle() : Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
29176                                     cimg_instance,
29177                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
29178       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
29179       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
29180       const long whd = (long)_width*_height*_depth, offx = _spectrum*whd;
29181       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
29182         nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
29183         nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
29184         nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
29185       tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
29186       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1,nc0,nc1);
29187       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nz0,nz2,nc0,nc2);
29188       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nz1,nz2,nc1,nc2);
29189       if (ny0>=height() || ny2<0) return *this;
29190       tzfloat
29191         pzl = (nz1 - nz0)/(ny1 - ny0),
29192         pzr = (nz2 - nz0)/(ny2 - ny0),
29193         pzn = (nz2 - nz1)/(ny2 - ny1),
29194         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
29195         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
29196       _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
29197         if (y==ny1) { zl = nz1; pzl = pzn; }
29198         int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0;
29199         tzfloat zleft = zl, zright = zr;
29200         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,cleft,cright);
29201         const int
29202           dx = xright - xleft,
29203           dc = cright>cleft?cright - cleft:cleft - cright,
29204           rc = dx?(cright-cleft)/dx:0,
29205           sc = cright>cleft?1:-1,
29206           ndc = dc-(dx?dx*(dc/dx):0);
29207         const tzfloat pentez = (zright - zleft)/dx;
29208         int errc = dx>>1;
29209         if (xleft<0 && dx) {
29210           cleft-=xleft*(cright - cleft)/dx;
29211           zleft-=xleft*(zright - zleft)/dx;
29212         }
29213         if (xleft<0) xleft = 0;
29214         if (xright>=width()-1) xright = width()-1;
29215         T *ptrd = data(xleft,y);
29216         tz *ptrz = zbuffer.data(xleft,y);
29217         if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
29218             if (zleft>=(tzfloat)*ptrz) {
29219               *ptrz = (tz)zleft;
29220               const tc *col = color;
29221               cimg_forC(*this,c) {
29222                 *ptrd = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256);
29223                 ptrd+=whd;
29224               }
29225               ptrd-=offx;
29226             }
29227             zleft+=pentez;
29228             cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
29229           } else for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
29230             if (zleft>=(tzfloat)*ptrz) {
29231               *ptrz = (tz)zleft;
29232               const tc *col = color;
29233               cimg_forC(*this,c) {
29234                 const T val = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256);
29235                 *ptrd = (T)(nopacity*val + *ptrd*copacity);
29236                 ptrd+=whd;
29237               }
29238               ptrd-=offx;
29239             }
29240             zleft+=pentez;
29241             cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
29242           }
29243         zr+=pzr; zl+=pzl;
29244       }
29245       return *this;
29246     }
29247 
29248     //! Draw a color-interpolated 2d triangle.
29249     /**
29250        \param x0 X-coordinate of the first vertex in the image instance.
29251        \param y0 Y-coordinate of the first vertex in the image instance.
29252        \param x1 X-coordinate of the second vertex in the image instance.
29253        \param y1 Y-coordinate of the second vertex in the image instance.
29254        \param x2 X-coordinate of the third vertex in the image instance.
29255        \param y2 Y-coordinate of the third vertex in the image instance.
29256        \param color1 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the first vertex.
29257        \param color2 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the seconf vertex.
29258        \param color3 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the third vertex.
29259        \param opacity Drawing opacity.
29260      **/
29261     template<typename tc1, typename tc2, typename tc3>
29262     CImg<T>& draw_triangle(const int x0, const int y0,
29263                            const int x1, const int y1,
29264                            const int x2, const int y2,
29265                            const tc1 *const color1,
29266                            const tc2 *const color2,
29267                            const tc3 *const color3,
29268                            const float opacity=1) {
29269       const unsigned char one = 1;
29270       cimg_forC(*this,c) get_shared_channel(c).draw_triangle(x0,y0,x1,y1,x2,y2,&one,color1[c],color2[c],color3[c],opacity);
29271       return *this;
29272     }
29273 
29274     //! Draw a textured 2d triangle.
29275     /**
29276        \param x0 X-coordinate of the first vertex in the image instance.
29277        \param y0 Y-coordinate of the first vertex in the image instance.
29278        \param x1 X-coordinate of the second vertex in the image instance.
29279        \param y1 Y-coordinate of the second vertex in the image instance.
29280        \param x2 X-coordinate of the third vertex in the image instance.
29281        \param y2 Y-coordinate of the third vertex in the image instance.
29282        \param texture Texture image used to fill the triangle.
29283        \param tx0 X-coordinate of the first vertex in the texture image.
29284        \param ty0 Y-coordinate of the first vertex in the texture image.
29285        \param tx1 X-coordinate of the second vertex in the texture image.
29286        \param ty1 Y-coordinate of the second vertex in the texture image.
29287        \param tx2 X-coordinate of the third vertex in the texture image.
29288        \param ty2 Y-coordinate of the third vertex in the texture image.
29289        \param opacity Drawing opacity.
29290        \param brightness Brightness factor of the drawing (in [0,2]).
29291     **/
29292     template<typename tc>
29293     CImg<T>& draw_triangle(const int x0, const int y0,
29294                            const int x1, const int y1,
29295                            const int x2, const int y2,
29296                            const CImg<tc>& texture,
29297                            const int tx0, const int ty0,
29298                            const int tx1, const int ty1,
29299                            const int tx2, const int ty2,
29300                            const float opacity=1,
29301                            const float brightness=1) {
29302       if (is_empty()) return *this;
29303       if (texture._depth>1 || texture._spectrum<_spectrum)
29304         throw CImgArgumentException(_cimg_instance
29305                                     "draw_triangle() : Invalid specified texture (%u,%u,%u,%u,%p).",
29306                                     cimg_instance,
29307                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
29308       if (is_overlapped(texture)) return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
29309       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
29310       const float
29311         nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0),
29312         nbrightness = brightness<0?0:(brightness>2?2:brightness);
29313       const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth, offx = _spectrum*whd-1;
29314       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
29315         ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2;
29316       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1);
29317       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2);
29318       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2);
29319       if (ny0>=height() || ny2<0) return *this;
29320       _cimg_for_triangle3(*this,xleft0,txleft0,tyleft0,xright0,txright0,tyright0,y,
29321                           nx0,ny0,ntx0,nty0,nx1,ny1,ntx1,nty1,nx2,ny2,ntx2,nty2) {
29322         int
29323           xleft = xleft0, xright = xright0,
29324           txleft = txleft0, txright = txright0,
29325           tyleft = tyleft0, tyright = tyright0;
29326         if (xright<xleft) cimg::swap(xleft,xright,txleft,txright,tyleft,tyright);
29327         const int
29328           dx = xright - xleft,
29329           dtx = txright>txleft?txright - txleft:txleft - txright,
29330           dty = tyright>tyleft?tyright - tyleft:tyleft - tyright,
29331           rtx = dx?(txright - txleft)/dx:0,
29332           rty = dx?(tyright - tyleft)/dx:0,
29333           stx = txright>txleft?1:-1,
29334           sty = tyright>tyleft?1:-1,
29335           ndtx = dtx - (dx?dx*(dtx/dx):0),
29336           ndty = dty - (dx?dx*(dty/dx):0);
29337         int errtx = dx>>1, errty = errtx;
29338         if (xleft<0 && dx) {
29339           txleft-=xleft*(txright - txleft)/dx;
29340           tyleft-=xleft*(tyright - tyleft)/dx;
29341         }
29342         if (xleft<0) xleft = 0;
29343         if (xright>=width()-1) xright = width()-1;
29344         T* ptrd = data(xleft,y,0,0);
29345         if (opacity>=1) {
29346           if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
29347             const tc *col = texture.data(txleft,tyleft);
29348             cimg_forC(*this,c) {
29349               *ptrd = (T)*col;
29350               ptrd+=whd; col+=twhd;
29351             }
29352             ptrd-=offx;
29353             txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
29354             tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
29355           } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) {
29356             const tc *col = texture.data(txleft,tyleft);
29357             cimg_forC(*this,c) {
29358               *ptrd = (T)(nbrightness**col);
29359               ptrd+=whd; col+=twhd;
29360             }
29361             ptrd-=offx;
29362             txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
29363             tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
29364           } else for (int x = xleft; x<=xright; ++x) {
29365             const tc *col = texture.data(txleft,tyleft);
29366             cimg_forC(*this,c) {
29367               *ptrd = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval);
29368               ptrd+=whd; col+=twhd;
29369             }
29370             ptrd-=offx;
29371             txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
29372             tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
29373           }
29374         } else {
29375           if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
29376             const tc *col = texture.data(txleft,tyleft);
29377             cimg_forC(*this,c) {
29378               *ptrd = (T)(nopacity**col + *ptrd*copacity);
29379               ptrd+=whd; col+=twhd;
29380             }
29381             ptrd-=offx;
29382             txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
29383             tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
29384           } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) {
29385             const tc *col = texture.data(txleft,tyleft);
29386             cimg_forC(*this,c) {
29387               *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity);
29388               ptrd+=whd; col+=twhd;
29389             }
29390             ptrd-=offx;
29391             txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
29392             tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
29393           } else for (int x = xleft; x<=xright; ++x) {
29394             const tc *col = texture.data(txleft,tyleft);
29395             cimg_forC(*this,c) {
29396               const T val = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval);
29397               *ptrd = (T)(nopacity*val + *ptrd*copacity);
29398               ptrd+=whd; col+=twhd;
29399             }
29400             ptrd-=offx;
29401             txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
29402             tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
29403           }
29404         }
29405       }
29406       return *this;
29407     }
29408 
29409     //! Draw a 2d textured triangle, with perspective correction.
29410     template<typename tc>
29411     CImg<T>& draw_triangle(const int x0, const int y0, const float z0,
29412                            const int x1, const int y1, const float z1,
29413                            const int x2, const int y2, const float z2,
29414                            const CImg<tc>& texture,
29415                            const int tx0, const int ty0,
29416                            const int tx1, const int ty1,
29417                            const int tx2, const int ty2,
29418                            const float opacity=1,
29419                            const float brightness=1) {
29420       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
29421       if (texture._depth>1 || texture._spectrum<_spectrum)
29422         throw CImgArgumentException(_cimg_instance
29423                                     "draw_triangle() : Invalid specified texture (%u,%u,%u,%u,%p).",
29424                                     cimg_instance,
29425                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
29426       if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
29427       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
29428       const float
29429         nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0),
29430         nbrightness = brightness<0?0:(brightness>2?2:brightness);
29431       const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth, offx = _spectrum*whd-1;
29432       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
29433       float
29434         ntx0 = tx0/z0, nty0 = ty0/z0,
29435         ntx1 = tx1/z1, nty1 = ty1/z1,
29436         ntx2 = tx2/z2, nty2 = ty2/z2,
29437         nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2;
29438       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1);
29439       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2);
29440       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2);
29441       if (ny0>=height() || ny2<0) return *this;
29442       float
29443         ptxl = (ntx1 - ntx0)/(ny1 - ny0),
29444         ptxr = (ntx2 - ntx0)/(ny2 - ny0),
29445         ptxn = (ntx2 - ntx1)/(ny2 - ny1),
29446         ptyl = (nty1 - nty0)/(ny1 - ny0),
29447         ptyr = (nty2 - nty0)/(ny2 - ny0),
29448         ptyn = (nty2 - nty1)/(ny2 - ny1),
29449         pzl = (nz1 - nz0)/(ny1 - ny0),
29450         pzr = (nz2 - nz0)/(ny2 - ny0),
29451         pzn = (nz2 - nz1)/(ny2 - ny1),
29452         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
29453         txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
29454         tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
29455         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))),
29456         txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
29457         tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
29458       _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) {
29459         if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
29460         int xleft = xleft0, xright = xright0;
29461         float
29462           zleft = zl, zright = zr,
29463           txleft = txl, txright = txr,
29464           tyleft = tyl, tyright = tyr;
29465         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright);
29466         const int dx = xright - xleft;
29467         const float
29468           pentez = (zright - zleft)/dx,
29469           pentetx = (txright - txleft)/dx,
29470           pentety = (tyright - tyleft)/dx;
29471         if (xleft<0 && dx) {
29472           zleft-=xleft*(zright - zleft)/dx;
29473           txleft-=xleft*(txright - txleft)/dx;
29474           tyleft-=xleft*(tyright - tyleft)/dx;
29475         }
29476         if (xleft<0) xleft = 0;
29477         if (xright>=width()-1) xright = width()-1;
29478         T* ptrd = data(xleft,y,0,0);
29479         if (opacity>=1) {
29480           if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
29481             const float invz = 1/zleft;
29482             const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29483             cimg_forC(*this,c) {
29484               *ptrd = (T)*col;
29485               ptrd+=whd; col+=twhd;
29486             }
29487             ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29488           } else if (nbrightness<1) for (int x=xleft; x<=xright; ++x) {
29489             const float invz = 1/zleft;
29490             const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29491             cimg_forC(*this,c) {
29492               *ptrd = (T)(nbrightness**col);
29493               ptrd+=whd; col+=twhd;
29494             }
29495             ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29496           } else for (int x = xleft; x<=xright; ++x) {
29497             const float invz = 1/zleft;
29498             const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29499             cimg_forC(*this,c) {
29500               *ptrd = (T)((2-nbrightness)**col + (nbrightness-1)*maxval);
29501               ptrd+=whd; col+=twhd;
29502             }
29503             ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29504           }
29505         } else {
29506           if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
29507             const float invz = 1/zleft;
29508             const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29509             cimg_forC(*this,c) {
29510               *ptrd = (T)(nopacity**col + *ptrd*copacity);
29511               ptrd+=whd; col+=twhd;
29512             }
29513             ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29514           } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) {
29515             const float invz = 1/zleft;
29516             const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29517             cimg_forC(*this,c) {
29518               *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity);
29519               ptrd+=whd; col+=twhd;
29520             }
29521             ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29522           } else for (int x = xleft; x<=xright; ++x) {
29523             const float invz = 1/zleft;
29524             const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29525             cimg_forC(*this,c) {
29526               const T val = (T)((2-nbrightness)**col + (nbrightness-1)*maxval);
29527               *ptrd = (T)(nopacity*val + *ptrd*copacity);
29528               ptrd+=whd; col+=twhd;
29529             }
29530             ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29531           }
29532         }
29533         zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
29534       }
29535       return *this;
29536     }
29537 
29538     //! Draw a textured 2d triangle, with perspective correction and z-buffering.
29539     template<typename tz, typename tc>
29540     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
29541                            const int x0, const int y0, const float z0,
29542                            const int x1, const int y1, const float z1,
29543                            const int x2, const int y2, const float z2,
29544                            const CImg<tc>& texture,
29545                            const int tx0, const int ty0,
29546                            const int tx1, const int ty1,
29547                            const int tx2, const int ty2,
29548                            const float opacity=1,
29549                            const float brightness=1) {
29550       typedef typename cimg::superset<tz,float>::type tzfloat;
29551       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
29552       if (!is_sameXY(zbuffer))
29553         throw CImgArgumentException(_cimg_instance
29554                                     "draw_triangle() : Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
29555                                     cimg_instance,
29556                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
29557 
29558       if (texture._depth>1 || texture._spectrum<_spectrum)
29559         throw CImgArgumentException(_cimg_instance
29560                                     "draw_triangle() : Invalid specified texture (%u,%u,%u,%u,%p).",
29561                                     cimg_instance,
29562                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
29563       if (is_overlapped(texture)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
29564       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
29565       const float
29566         nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0),
29567         nbrightness = brightness<0?0:(brightness>2?2:brightness);
29568       const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth, offx = _spectrum*whd;
29569       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
29570       float
29571         ntx0 = tx0/z0, nty0 = ty0/z0,
29572         ntx1 = tx1/z1, nty1 = ty1/z1,
29573         ntx2 = tx2/z2, nty2 = ty2/z2;
29574       tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
29575       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1);
29576       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2);
29577       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2);
29578       if (ny0>=height() || ny2<0) return *this;
29579       float
29580         ptxl = (ntx1 - ntx0)/(ny1 - ny0),
29581         ptxr = (ntx2 - ntx0)/(ny2 - ny0),
29582         ptxn = (ntx2 - ntx1)/(ny2 - ny1),
29583         ptyl = (nty1 - nty0)/(ny1 - ny0),
29584         ptyr = (nty2 - nty0)/(ny2 - ny0),
29585         ptyn = (nty2 - nty1)/(ny2 - ny1),
29586         txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
29587         tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
29588         txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
29589         tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
29590       tzfloat
29591         pzl = (nz1 - nz0)/(ny1 - ny0),
29592         pzr = (nz2 - nz0)/(ny2 - ny0),
29593         pzn = (nz2 - nz1)/(ny2 - ny1),
29594         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
29595         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
29596       _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) {
29597         if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
29598         int xleft = xleft0, xright = xright0;
29599         float txleft = txl, txright = txr, tyleft = tyl, tyright = tyr;
29600         tzfloat zleft = zl, zright = zr;
29601         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright);
29602         const int dx = xright - xleft;
29603         const float pentetx = (txright - txleft)/dx, pentety = (tyright - tyleft)/dx;
29604         const tzfloat pentez = (zright - zleft)/dx;
29605         if (xleft<0 && dx) {
29606           zleft-=xleft*(zright - zleft)/dx;
29607           txleft-=xleft*(txright - txleft)/dx;
29608           tyleft-=xleft*(tyright - tyleft)/dx;
29609         }
29610         if (xleft<0) xleft = 0;
29611         if (xright>=width()-1) xright = width()-1;
29612         T *ptrd = data(xleft,y,0,0);
29613         tz *ptrz = zbuffer.data(xleft,y);
29614         if (opacity>=1) {
29615           if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29616               if (zleft>=(tzfloat)*ptrz) {
29617                 *ptrz = (tz)zleft;
29618                 const tzfloat invz = 1/zleft;
29619                 const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29620                 cimg_forC(*this,c) {
29621                   *ptrd = (T)*col;
29622                   ptrd+=whd; col+=twhd;
29623                 }
29624                 ptrd-=offx;
29625               }
29626               zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29627             } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29628               if (zleft>=(tzfloat)*ptrz) {
29629                 *ptrz = (tz)zleft;
29630                 const tzfloat invz = 1/zleft;
29631                 const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29632                 cimg_forC(*this,c) {
29633                   *ptrd = (T)(nbrightness**col);
29634                   ptrd+=whd; col+=twhd;
29635                 }
29636                 ptrd-=offx;
29637               }
29638               zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29639             } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29640               if (zleft>=(tzfloat)*ptrz) {
29641                 *ptrz = (tz)zleft;
29642                 const tzfloat invz = 1/zleft;
29643                 const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29644                 cimg_forC(*this,c) {
29645                   *ptrd = (T)((2-nbrightness)**col + (nbrightness-1)*maxval);
29646                   ptrd+=whd; col+=twhd;
29647                 }
29648                 ptrd-=offx;
29649               }
29650               zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29651             }
29652         } else {
29653           if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29654               if (zleft>=(tzfloat)*ptrz) {
29655                 *ptrz = (tz)zleft;
29656                 const tzfloat invz = 1/zleft;
29657                 const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29658                 cimg_forC(*this,c) {
29659                   *ptrd = (T)(nopacity**col + *ptrd*copacity);
29660                   ptrd+=whd; col+=twhd;
29661                 }
29662                 ptrd-=offx;
29663               }
29664               zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29665             } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29666               if (zleft>=(tzfloat)*ptrz) {
29667                 *ptrz = (tz)zleft;
29668                 const tzfloat invz = 1/zleft;
29669                 const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29670                 cimg_forC(*this,c) {
29671                   *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity);
29672                   ptrd+=whd; col+=twhd;
29673                 }
29674                 ptrd-=offx;
29675               }
29676               zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29677             } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29678               if (zleft>=(tzfloat)*ptrz) {
29679                 *ptrz = (tz)zleft;
29680                 const tzfloat invz = 1/zleft;
29681                 const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29682                 cimg_forC(*this,c) {
29683                   const T val = (T)((2-nbrightness)**col + (nbrightness-1)*maxval);
29684                   *ptrd = (T)(nopacity*val + *ptrd*copacity);
29685                   ptrd+=whd; col+=twhd;
29686                 }
29687                 ptrd-=offx;
29688               }
29689               zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29690             }
29691         }
29692         zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
29693       }
29694       return *this;
29695     }
29696 
29697     //! Draw a Phong-shaded 2d triangle.
29698     /**
29699        \param x0 X-coordinate of the first vertex in the image instance.
29700        \param y0 Y-coordinate of the first vertex in the image instance.
29701        \param x1 X-coordinate of the second vertex in the image instance.
29702        \param y1 Y-coordinate of the second vertex in the image instance.
29703        \param x2 X-coordinate of the third vertex in the image instance.
29704        \param y2 Y-coordinate of the third vertex in the image instance.
29705        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
29706        \param light Light image.
29707        \param lx0 X-coordinate of the first vertex in the light image.
29708        \param ly0 Y-coordinate of the first vertex in the light image.
29709        \param lx1 X-coordinate of the second vertex in the light image.
29710        \param ly1 Y-coordinate of the second vertex in the light image.
29711        \param lx2 X-coordinate of the third vertex in the light image.
29712        \param ly2 Y-coordinate of the third vertex in the light image.
29713        \param opacity Drawing opacity.
29714     **/
29715     template<typename tc, typename tl>
29716     CImg<T>& draw_triangle(const int x0, const int y0,
29717                            const int x1, const int y1,
29718                            const int x2, const int y2,
29719                            const tc *const color,
29720                            const CImg<tl>& light,
29721                            const int lx0, const int ly0,
29722                            const int lx1, const int ly1,
29723                            const int lx2, const int ly2,
29724                            const float opacity=1) {
29725       if (is_empty()) return *this;
29726       if (!color)
29727         throw CImgArgumentException(_cimg_instance
29728                                     "draw_triangle : Specified color is (null).",
29729                                     cimg_instance);
29730       if (light._depth>1 || light._spectrum<_spectrum)
29731         throw CImgArgumentException(_cimg_instance
29732                                     "draw_triangle() : Invalid specified light texture (%u,%u,%u,%u,%p).",
29733                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
29734       if (is_overlapped(light)) return draw_triangle(x0,y0,x1,y1,x2,y2,color,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
29735       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
29736       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
29737       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
29738         nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
29739       const long whd = (long)_width*_height*_depth, offx = _spectrum*whd-1;
29740       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nlx0,nlx1,nly0,nly1);
29741       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nlx0,nlx2,nly0,nly2);
29742       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nlx1,nlx2,nly1,nly2);
29743       if (ny0>=height() || ny2<0) return *this;
29744       _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
29745                           nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
29746         int
29747           xleft = xleft0, xright = xright0,
29748           lxleft = lxleft0, lxright = lxright0,
29749           lyleft = lyleft0, lyright = lyright0;
29750         if (xright<xleft) cimg::swap(xleft,xright,lxleft,lxright,lyleft,lyright);
29751         const int
29752           dx = xright - xleft,
29753           dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
29754           dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
29755           rlx = dx?(lxright - lxleft)/dx:0,
29756           rly = dx?(lyright - lyleft)/dx:0,
29757           slx = lxright>lxleft?1:-1,
29758           sly = lyright>lyleft?1:-1,
29759           ndlx = dlx - (dx?dx*(dlx/dx):0),
29760           ndly = dly - (dx?dx*(dly/dx):0);
29761         int errlx = dx>>1, errly = errlx;
29762         if (xleft<0 && dx) {
29763           lxleft-=xleft*(lxright - lxleft)/dx;
29764           lyleft-=xleft*(lyright - lyleft)/dx;
29765         }
29766         if (xleft<0) xleft = 0;
29767         if (xright>=width()-1) xright = width()-1;
29768         T* ptrd = data(xleft,y,0,0);
29769         if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
29770           const tc *col = color;
29771           cimg_forC(*this,c) {
29772             const tl l = light(lxleft,lyleft,c);
29773             *ptrd = (T)(l<1?l**(col++):((2-l)**(col++)+(l-1)*maxval));
29774             ptrd+=whd;
29775           }
29776           ptrd-=offx;
29777           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
29778           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
29779         } else  for (int x = xleft; x<=xright; ++x) {
29780           const tc *col = color;
29781           cimg_forC(*this,c) {
29782             const tl l = light(lxleft,lyleft,c);
29783             const T val = (T)(l<1?l**(col++):((2-l)**(col++)+(l-1)*maxval));
29784             *ptrd = (T)(nopacity*val + *ptrd*copacity);
29785             ptrd+=whd;
29786           }
29787           ptrd-=offx;
29788           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
29789           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
29790         }
29791       }
29792       return *this;
29793     }
29794 
29795     //! Draw a Phong-shaded 2d triangle, with z-buffering.
29796     template<typename tz, typename tc, typename tl>
29797     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
29798                            const int x0, const int y0, const float z0,
29799                            const int x1, const int y1, const float z1,
29800                            const int x2, const int y2, const float z2,
29801                            const tc *const color,
29802                            const CImg<tl>& light,
29803                            const int lx0, const int ly0,
29804                            const int lx1, const int ly1,
29805                            const int lx2, const int ly2,
29806                            const float opacity=1) {
29807       typedef typename cimg::superset<tz,float>::type tzfloat;
29808       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
29809       if (!color)
29810         throw CImgArgumentException(_cimg_instance
29811                                     "draw_triangle() : Specified color is (null).",
29812                                     cimg_instance);
29813       if (light._depth>1 || light._spectrum<_spectrum)
29814         throw CImgArgumentException(_cimg_instance
29815                                     "draw_triangle() : Invalid specified light texture (%u,%u,%u,%u,%p).",
29816                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
29817       if (!is_sameXY(zbuffer))
29818         throw CImgArgumentException(_cimg_instance
29819                                     "draw_triangle() : Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
29820                                     cimg_instance,
29821                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
29822       if (is_overlapped(light)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,
29823                                                      +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
29824       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
29825       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
29826       const long whd = (long)_width*_height*_depth, offx = _spectrum*whd;
29827       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
29828         nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
29829       tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
29830       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nlx0,nlx1,nly0,nly1,nz0,nz1);
29831       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nlx0,nlx2,nly0,nly2,nz0,nz2);
29832       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nlx1,nlx2,nly1,nly2,nz1,nz2);
29833       if (ny0>=height() || ny2<0) return *this;
29834       tzfloat
29835         pzl = (nz1 - nz0)/(ny1 - ny0),
29836         pzr = (nz2 - nz0)/(ny2 - ny0),
29837         pzn = (nz2 - nz1)/(ny2 - ny1),
29838         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
29839         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
29840       _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
29841                           nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
29842         if (y==ny1) { zl = nz1; pzl = pzn; }
29843         int
29844           xleft = xleft0, xright = xright0,
29845           lxleft = lxleft0, lxright = lxright0,
29846           lyleft = lyleft0, lyright = lyright0;
29847         tzfloat zleft = zl, zright = zr;
29848         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,lxleft,lxright,lyleft,lyright);
29849         const int
29850           dx = xright - xleft,
29851           dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
29852           dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
29853           rlx = dx?(lxright - lxleft)/dx:0,
29854           rly = dx?(lyright - lyleft)/dx:0,
29855           slx = lxright>lxleft?1:-1,
29856           sly = lyright>lyleft?1:-1,
29857           ndlx = dlx - (dx?dx*(dlx/dx):0),
29858           ndly = dly - (dx?dx*(dly/dx):0);
29859         const tzfloat pentez = (zright - zleft)/dx;
29860         int errlx = dx>>1, errly = errlx;
29861         if (xleft<0 && dx) {
29862           zleft-=xleft*(zright - zleft)/dx;
29863           lxleft-=xleft*(lxright - lxleft)/dx;
29864           lyleft-=xleft*(lyright - lyleft)/dx;
29865         }
29866         if (xleft<0) xleft = 0;
29867         if (xright>=width()-1) xright = width()-1;
29868         T *ptrd = data(xleft,y,0,0);
29869         tz *ptrz = zbuffer.data(xleft,y);
29870         if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29871             if (zleft>=(tzfloat)*ptrz) {
29872               *ptrz = (tz)zleft;
29873               const tc *col = color;
29874               cimg_forC(*this,c) {
29875                 const tl l = light(lxleft,lyleft,c);
29876                 const tc cval = *(col++);
29877                 *ptrd = (T)(l<1?l*cval:(2-l)*cval+(l-1)*maxval);
29878                 ptrd+=whd;
29879               }
29880               ptrd-=offx;
29881             }
29882             zleft+=pentez;
29883             lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
29884             lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
29885           } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29886             if (zleft>=(tzfloat)*ptrz) {
29887               *ptrz = (tz)zleft;
29888               const tc *col = color;
29889               cimg_forC(*this,c) {
29890                 const tl l = light(lxleft,lyleft,c);
29891                 const tc cval = *(col++);
29892                 const T val = (T)(l<1?l*cval:(2-l)*cval+(l-1)*maxval);
29893                 *ptrd = (T)(nopacity*val + *ptrd*copacity);
29894                 ptrd+=whd;
29895               }
29896               ptrd-=offx;
29897             }
29898             zleft+=pentez;
29899             lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
29900             lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
29901           }
29902         zr+=pzr; zl+=pzl;
29903       }
29904       return *this;
29905     }
29906 
29907     //! Draw a textured Gouraud-shaded 2d triangle.
29908     /**
29909        \param x0 X-coordinate of the first vertex in the image instance.
29910        \param y0 Y-coordinate of the first vertex in the image instance.
29911        \param x1 X-coordinate of the second vertex in the image instance.
29912        \param y1 Y-coordinate of the second vertex in the image instance.
29913        \param x2 X-coordinate of the third vertex in the image instance.
29914        \param y2 Y-coordinate of the third vertex in the image instance.
29915        \param texture Texture image used to fill the triangle.
29916        \param tx0 X-coordinate of the first vertex in the texture image.
29917        \param ty0 Y-coordinate of the first vertex in the texture image.
29918        \param tx1 X-coordinate of the second vertex in the texture image.
29919        \param ty1 Y-coordinate of the second vertex in the texture image.
29920        \param tx2 X-coordinate of the third vertex in the texture image.
29921        \param ty2 Y-coordinate of the third vertex in the texture image.
29922        \param brightness0 Brightness factor of the first vertex.
29923        \param brightness1 Brightness factor of the second vertex.
29924        \param brightness2 Brightness factor of the third vertex.
29925        \param opacity Drawing opacity.
29926     **/
29927     template<typename tc>
29928     CImg<T>& draw_triangle(const int x0, const int y0,
29929                            const int x1, const int y1,
29930                            const int x2, const int y2,
29931                            const CImg<tc>& texture,
29932                            const int tx0, const int ty0,
29933                            const int tx1, const int ty1,
29934                            const int tx2, const int ty2,
29935                            const float brightness0,
29936                            const float brightness1,
29937                            const float brightness2,
29938                            const float opacity=1) {
29939       if (is_empty()) return *this;
29940       if (texture._depth>1 || texture._spectrum<_spectrum)
29941         throw CImgArgumentException(_cimg_instance
29942                                     "draw_triangle() : Invalid specified texture (%u,%u,%u,%u,%p).",
29943                                     cimg_instance,
29944                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
29945       if (is_overlapped(texture))
29946         return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,brightness0,brightness1,brightness2,opacity);
29947       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
29948       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
29949       const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth, offx = _spectrum*whd-1;
29950       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
29951         ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2,
29952         nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
29953         nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
29954         nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
29955       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nc0,nc1);
29956       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nc0,nc2);
29957       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nc1,nc2);
29958       if (ny0>=height() || ny2<0) return *this;
29959       _cimg_for_triangle4(*this,xleft0,cleft0,txleft0,tyleft0,xright0,cright0,txright0,tyright0,y,
29960                           nx0,ny0,nc0,ntx0,nty0,nx1,ny1,nc1,ntx1,nty1,nx2,ny2,nc2,ntx2,nty2) {
29961         int
29962           xleft = xleft0, xright = xright0,
29963           cleft = cleft0, cright = cright0,
29964           txleft = txleft0, txright = txright0,
29965           tyleft = tyleft0, tyright = tyright0;
29966         if (xright<xleft) cimg::swap(xleft,xright,cleft,cright,txleft,txright,tyleft,tyright);
29967         const int
29968           dx = xright - xleft,
29969           dc = cright>cleft?cright - cleft:cleft - cright,
29970           dtx = txright>txleft?txright - txleft:txleft - txright,
29971           dty = tyright>tyleft?tyright - tyleft:tyleft - tyright,
29972           rc = dx?(cright - cleft)/dx:0,
29973           rtx = dx?(txright - txleft)/dx:0,
29974           rty = dx?(tyright - tyleft)/dx:0,
29975           sc = cright>cleft?1:-1,
29976           stx = txright>txleft?1:-1,
29977           sty = tyright>tyleft?1:-1,
29978           ndc = dc - (dx?dx*(dc/dx):0),
29979           ndtx = dtx - (dx?dx*(dtx/dx):0),
29980           ndty = dty - (dx?dx*(dty/dx):0);
29981         int errc = dx>>1, errtx = errc, errty = errc;
29982         if (xleft<0 && dx) {
29983           cleft-=xleft*(cright - cleft)/dx;
29984           txleft-=xleft*(txright - txleft)/dx;
29985           tyleft-=xleft*(tyright - tyleft)/dx;
29986         }
29987         if (xleft<0) xleft = 0;
29988         if (xright>=width()-1) xright = width()-1;
29989         T* ptrd = data(xleft,y,0,0);
29990         if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
29991           const tc *col = texture.data(txleft,tyleft);
29992           cimg_forC(*this,c) {
29993             *ptrd = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
29994             ptrd+=whd; col+=twhd;
29995           }
29996           ptrd-=offx;
29997           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
29998           txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
29999           tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
30000         } else for (int x = xleft; x<=xright; ++x) {
30001           const tc *col = texture.data(txleft,tyleft);
30002           cimg_forC(*this,c) {
30003             const T val = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
30004             *ptrd = (T)(nopacity*val + *ptrd*copacity);
30005             ptrd+=whd; col+=twhd;
30006           }
30007           ptrd-=offx;
30008           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
30009           txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
30010           tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
30011         }
30012       }
30013       return *this;
30014     }
30015 
30016     //! Draw a textured Gouraud-shaded 2d triangle, with perspective correction \overloading.
30017     template<typename tc>
30018     CImg<T>& draw_triangle(const int x0, const int y0, const float z0,
30019                            const int x1, const int y1, const float z1,
30020                            const int x2, const int y2, const float z2,
30021                            const CImg<tc>& texture,
30022                            const int tx0, const int ty0,
30023                            const int tx1, const int ty1,
30024                            const int tx2, const int ty2,
30025                            const float brightness0,
30026                            const float brightness1,
30027                            const float brightness2,
30028                            const float opacity=1) {
30029       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
30030       if (texture._depth>1 || texture._spectrum<_spectrum)
30031         throw CImgArgumentException(_cimg_instance
30032                                     "draw_triangle() : Invalid specified texture (%u,%u,%u,%u,%p).",
30033                                     cimg_instance,
30034                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
30035       if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
30036                                                        brightness0,brightness1,brightness2,opacity);
30037       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
30038       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
30039       const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth, offx = _spectrum*whd-1;
30040       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
30041         nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
30042         nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
30043         nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
30044       float
30045         ntx0 = tx0/z0, nty0 = ty0/z0,
30046         ntx1 = tx1/z1, nty1 = ty1/z1,
30047         ntx2 = tx2/z2, nty2 = ty2/z2,
30048         nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2;
30049       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1,nc0,nc1);
30050       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2,nc0,nc2);
30051       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2,nc1,nc2);
30052       if (ny0>=height() || ny2<0) return *this;
30053       float
30054         ptxl = (ntx1 - ntx0)/(ny1 - ny0),
30055         ptxr = (ntx2 - ntx0)/(ny2 - ny0),
30056         ptxn = (ntx2 - ntx1)/(ny2 - ny1),
30057         ptyl = (nty1 - nty0)/(ny1 - ny0),
30058         ptyr = (nty2 - nty0)/(ny2 - ny0),
30059         ptyn = (nty2 - nty1)/(ny2 - ny1),
30060         pzl = (nz1 - nz0)/(ny1 - ny0),
30061         pzr = (nz2 - nz0)/(ny2 - ny0),
30062         pzn = (nz2 - nz1)/(ny2 - ny1),
30063         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
30064         txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
30065         tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
30066         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))),
30067         txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
30068         tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
30069       _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
30070         if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
30071         int
30072           xleft = xleft0, xright = xright0,
30073           cleft = cleft0, cright = cright0;
30074         float
30075           zleft = zl, zright = zr,
30076           txleft = txl, txright = txr,
30077           tyleft = tyl, tyright = tyr;
30078         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,cleft,cright);
30079         const int
30080           dx = xright - xleft,
30081           dc = cright>cleft?cright - cleft:cleft - cright,
30082           rc = dx?(cright - cleft)/dx:0,
30083           sc = cright>cleft?1:-1,
30084           ndc = dc - (dx?dx*(dc/dx):0);
30085         const float
30086           pentez = (zright - zleft)/dx,
30087           pentetx = (txright - txleft)/dx,
30088           pentety = (tyright - tyleft)/dx;
30089         int errc = dx>>1;
30090         if (xleft<0 && dx) {
30091           cleft-=xleft*(cright - cleft)/dx;
30092           zleft-=xleft*(zright - zleft)/dx;
30093           txleft-=xleft*(txright - txleft)/dx;
30094           tyleft-=xleft*(tyright - tyleft)/dx;
30095         }
30096         if (xleft<0) xleft = 0;
30097         if (xright>=width()-1) xright = width()-1;
30098         T* ptrd = data(xleft,y,0,0);
30099         if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
30100           const float invz = 1/zleft;
30101           const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
30102           cimg_forC(*this,c) {
30103             *ptrd = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
30104             ptrd+=whd; col+=twhd;
30105           }
30106           ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
30107           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
30108         } else for (int x = xleft; x<=xright; ++x) {
30109           const float invz = 1/zleft;
30110           const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
30111           cimg_forC(*this,c) {
30112             const T val = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
30113             *ptrd = (T)(nopacity*val + *ptrd*copacity);
30114             ptrd+=whd; col+=twhd;
30115           }
30116           ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
30117           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
30118         }
30119         zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
30120       }
30121       return *this;
30122     }
30123 
30124     //! Draw a textured Gouraud-shaded 2d triangle, with perspective correction and z-buffering \overloading.
30125     template<typename tz, typename tc>
30126     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
30127                            const int x0, const int y0, const float z0,
30128                            const int x1, const int y1, const float z1,
30129                            const int x2, const int y2, const float z2,
30130                            const CImg<tc>& texture,
30131                            const int tx0, const int ty0,
30132                            const int tx1, const int ty1,
30133                            const int tx2, const int ty2,
30134                            const float brightness0,
30135                            const float brightness1,
30136                            const float brightness2,
30137                            const float opacity=1) {
30138       typedef typename cimg::superset<tz,float>::type tzfloat;
30139       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
30140       if (!is_sameXY(zbuffer))
30141         throw CImgArgumentException(_cimg_instance
30142                                     "draw_triangle() : Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
30143                                     cimg_instance,
30144                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
30145       if (texture._depth>1 || texture._spectrum<_spectrum)
30146         throw CImgArgumentException(_cimg_instance
30147                                     "draw_triangle() : Invalid specified texture (%u,%u,%u,%u,%p).",
30148                                     cimg_instance,
30149                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
30150       if (is_overlapped(texture)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
30151                                                        brightness0,brightness1,brightness2,opacity);
30152       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
30153       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
30154       const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth, offx = _spectrum*whd;
30155       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
30156         nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
30157         nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
30158         nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
30159       float
30160         ntx0 = tx0/z0, nty0 = ty0/z0,
30161         ntx1 = tx1/z1, nty1 = ty1/z1,
30162         ntx2 = tx2/z2, nty2 = ty2/z2;
30163       tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
30164       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1,nc0,nc1);
30165       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2,nc0,nc2);
30166       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2,nc1,nc2);
30167       if (ny0>=height() || ny2<0) return *this;
30168       float
30169         ptxl = (ntx1 - ntx0)/(ny1 - ny0),
30170         ptxr = (ntx2 - ntx0)/(ny2 - ny0),
30171         ptxn = (ntx2 - ntx1)/(ny2 - ny1),
30172         ptyl = (nty1 - nty0)/(ny1 - ny0),
30173         ptyr = (nty2 - nty0)/(ny2 - ny0),
30174         ptyn = (nty2 - nty1)/(ny2 - ny1),
30175         txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
30176         tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
30177         txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
30178         tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
30179       tzfloat
30180         pzl = (nz1 - nz0)/(ny1 - ny0),
30181         pzr = (nz2 - nz0)/(ny2 - ny0),
30182         pzn = (nz2 - nz1)/(ny2 - ny1),
30183         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
30184         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
30185       _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
30186         if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
30187         int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0;
30188         float txleft = txl, txright = txr, tyleft = tyl, tyright = tyr;
30189         tzfloat zleft = zl, zright = zr;
30190         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,cleft,cright);
30191         const int
30192           dx = xright - xleft,
30193           dc = cright>cleft?cright - cleft:cleft - cright,
30194           rc = dx?(cright - cleft)/dx:0,
30195           sc = cright>cleft?1:-1,
30196           ndc = dc - (dx?dx*(dc/dx):0);
30197         float pentetx = (txright - txleft)/dx, pentety = (tyright - tyleft)/dx;
30198         const tzfloat pentez = (zright - zleft)/dx;
30199         int errc = dx>>1;
30200         if (xleft<0 && dx) {
30201           cleft-=xleft*(cright - cleft)/dx;
30202           zleft-=xleft*(zright - zleft)/dx;
30203           txleft-=xleft*(txright - txleft)/dx;
30204           tyleft-=xleft*(tyright - tyleft)/dx;
30205         }
30206         if (xleft<0) xleft = 0;
30207         if (xright>=width()-1) xright = width()-1;
30208         T* ptrd = data(xleft,y);
30209         tz *ptrz = zbuffer.data(xleft,y);
30210         if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
30211             if (zleft>=(tzfloat)*ptrz) {
30212               *ptrz = (tz)zleft;
30213               const tzfloat invz = 1/zleft;
30214               const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
30215               cimg_forC(*this,c) {
30216                 *ptrd = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
30217                 ptrd+=whd; col+=twhd;
30218               }
30219               ptrd-=offx;
30220             }
30221             zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
30222             cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
30223           } else for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
30224             if (zleft>=(tzfloat)*ptrz) {
30225               *ptrz = (tz)zleft;
30226               const tzfloat invz = 1/zleft;
30227               const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
30228               cimg_forC(*this,c) {
30229                 const T val = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
30230                 *ptrd = (T)(nopacity*val + *ptrd*copacity);
30231                 ptrd+=whd; col+=twhd;
30232               }
30233               ptrd-=offx;
30234             }
30235             zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
30236             cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
30237           }
30238         zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
30239       }
30240       return *this;
30241     }
30242 
30243     //! Draw a textured Phong-shaded 2d triangle.
30244     /**
30245        \param x0 X-coordinate of the first vertex in the image instance.
30246        \param y0 Y-coordinate of the first vertex in the image instance.
30247        \param x1 X-coordinate of the second vertex in the image instance.
30248        \param y1 Y-coordinate of the second vertex in the image instance.
30249        \param x2 X-coordinate of the third vertex in the image instance.
30250        \param y2 Y-coordinate of the third vertex in the image instance.
30251        \param texture Texture image used to fill the triangle.
30252        \param tx0 X-coordinate of the first vertex in the texture image.
30253        \param ty0 Y-coordinate of the first vertex in the texture image.
30254        \param tx1 X-coordinate of the second vertex in the texture image.
30255        \param ty1 Y-coordinate of the second vertex in the texture image.
30256        \param tx2 X-coordinate of the third vertex in the texture image.
30257        \param ty2 Y-coordinate of the third vertex in the texture image.
30258        \param light Light image.
30259        \param lx0 X-coordinate of the first vertex in the light image.
30260        \param ly0 Y-coordinate of the first vertex in the light image.
30261        \param lx1 X-coordinate of the second vertex in the light image.
30262        \param ly1 Y-coordinate of the second vertex in the light image.
30263        \param lx2 X-coordinate of the third vertex in the light image.
30264        \param ly2 Y-coordinate of the third vertex in the light image.
30265        \param opacity Drawing opacity.
30266     **/
30267     template<typename tc, typename tl>
30268     CImg<T>& draw_triangle(const int x0, const int y0,
30269                            const int x1, const int y1,
30270                            const int x2, const int y2,
30271                            const CImg<tc>& texture,
30272                            const int tx0, const int ty0,
30273                            const int tx1, const int ty1,
30274                            const int tx2, const int ty2,
30275                            const CImg<tl>& light,
30276                            const int lx0, const int ly0,
30277                            const int lx1, const int ly1,
30278                            const int lx2, const int ly2,
30279                            const float opacity=1) {
30280       if (is_empty()) return *this;
30281       if (texture._depth>1 || texture._spectrum<_spectrum)
30282         throw CImgArgumentException(_cimg_instance
30283                                     "draw_triangle() : Invalid specified texture (%u,%u,%u,%u,%p).",
30284                                     cimg_instance,
30285                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
30286       if (light._depth>1 || light._spectrum<_spectrum)
30287         throw CImgArgumentException(_cimg_instance
30288                                     "draw_triangle() : Invalid specified light texture (%u,%u,%u,%u,%p).",
30289                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
30290       if (is_overlapped(texture)) return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
30291       if (is_overlapped(light))   return draw_triangle(x0,y0,x1,y1,x2,y2,texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
30292       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
30293       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
30294       const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth, offx = _spectrum*whd-1;
30295       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
30296         ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2,
30297         nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
30298       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1);
30299       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2);
30300       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2);
30301       if (ny0>=height() || ny2<0) return *this;
30302       _cimg_for_triangle5(*this,xleft0,lxleft0,lyleft0,txleft0,tyleft0,xright0,lxright0,lyright0,txright0,tyright0,y,
30303                           nx0,ny0,nlx0,nly0,ntx0,nty0,nx1,ny1,nlx1,nly1,ntx1,nty1,nx2,ny2,nlx2,nly2,ntx2,nty2) {
30304         int
30305           xleft = xleft0, xright = xright0,
30306           lxleft = lxleft0, lxright = lxright0,
30307           lyleft = lyleft0, lyright = lyright0,
30308           txleft = txleft0, txright = txright0,
30309           tyleft = tyleft0, tyright = tyright0;
30310         if (xright<xleft) cimg::swap(xleft,xright,lxleft,lxright,lyleft,lyright,txleft,txright,tyleft,tyright);
30311         const int
30312           dx = xright - xleft,
30313           dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
30314           dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
30315           dtx = txright>txleft?txright - txleft:txleft - txright,
30316           dty = tyright>tyleft?tyright - tyleft:tyleft - tyright,
30317           rlx = dx?(lxright - lxleft)/dx:0,
30318           rly = dx?(lyright - lyleft)/dx:0,
30319           rtx = dx?(txright - txleft)/dx:0,
30320           rty = dx?(tyright - tyleft)/dx:0,
30321           slx = lxright>lxleft?1:-1,
30322           sly = lyright>lyleft?1:-1,
30323           stx = txright>txleft?1:-1,
30324           sty = tyright>tyleft?1:-1,
30325           ndlx = dlx - (dx?dx*(dlx/dx):0),
30326           ndly = dly - (dx?dx*(dly/dx):0),
30327           ndtx = dtx - (dx?dx*(dtx/dx):0),
30328           ndty = dty - (dx?dx*(dty/dx):0);
30329         int errlx = dx>>1, errly = errlx, errtx = errlx, errty = errlx;
30330         if (xleft<0 && dx) {
30331           lxleft-=xleft*(lxright - lxleft)/dx;
30332           lyleft-=xleft*(lyright - lyleft)/dx;
30333           txleft-=xleft*(txright - txleft)/dx;
30334           tyleft-=xleft*(tyright - tyleft)/dx;
30335         }
30336         if (xleft<0) xleft = 0;
30337         if (xright>=width()-1) xright = width()-1;
30338         T* ptrd = data(xleft,y,0,0);
30339         if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
30340           const tc *col = texture.data(txleft,tyleft);
30341           cimg_forC(*this,c) {
30342             const tl l = light(lxleft,lyleft,c);
30343             *ptrd = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
30344             ptrd+=whd; col+=twhd;
30345           }
30346           ptrd-=offx;
30347           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
30348           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
30349           txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
30350           tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
30351         } else for (int x = xleft; x<=xright; ++x) {
30352           const tc *col = texture.data(txleft,tyleft);
30353           cimg_forC(*this,c) {
30354             const tl l = light(lxleft,lyleft,c);
30355             const T val = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
30356             *ptrd = (T)(nopacity*val + *ptrd*copacity);
30357             ptrd+=whd; col+=twhd;
30358           }
30359           ptrd-=offx;
30360           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
30361           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
30362           txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
30363           tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
30364         }
30365       }
30366       return *this;
30367     }
30368 
30369     //! Draw a textured Phong-shaded 2d triangle, with perspective correction.
30370     template<typename tc, typename tl>
30371     CImg<T>& draw_triangle(const int x0, const int y0, const float z0,
30372                            const int x1, const int y1, const float z1,
30373                            const int x2, const int y2, const float z2,
30374                            const CImg<tc>& texture,
30375                            const int tx0, const int ty0,
30376                            const int tx1, const int ty1,
30377                            const int tx2, const int ty2,
30378                            const CImg<tl>& light,
30379                            const int lx0, const int ly0,
30380                            const int lx1, const int ly1,
30381                            const int lx2, const int ly2,
30382                            const float opacity=1) {
30383       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
30384       if (texture._depth>1 || texture._spectrum<_spectrum)
30385         throw CImgArgumentException(_cimg_instance
30386                                     "draw_triangle() : Invalid specified texture (%u,%u,%u,%u,%p).",
30387                                     cimg_instance,
30388                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
30389       if (light._depth>1 || light._spectrum<_spectrum)
30390         throw CImgArgumentException(_cimg_instance
30391                                     "draw_triangle() : Invalid specified light texture (%u,%u,%u,%u,%p).",
30392                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
30393       if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
30394       if (is_overlapped(light)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
30395       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
30396       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
30397       const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth, offx = _spectrum*whd-1;
30398       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
30399         nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
30400       float
30401         ntx0 = tx0/z0, nty0 = ty0/z0,
30402         ntx1 = tx1/z1, nty1 = ty1/z1,
30403         ntx2 = tx2/z2, nty2 = ty2/z2,
30404         nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2;
30405       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1,nz0,nz1);
30406       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2,nz0,nz2);
30407       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2,nz1,nz2);
30408       if (ny0>=height() || ny2<0) return *this;
30409       float
30410         ptxl = (ntx1 - ntx0)/(ny1 - ny0),
30411         ptxr = (ntx2 - ntx0)/(ny2 - ny0),
30412         ptxn = (ntx2 - ntx1)/(ny2 - ny1),
30413         ptyl = (nty1 - nty0)/(ny1 - ny0),
30414         ptyr = (nty2 - nty0)/(ny2 - ny0),
30415         ptyn = (nty2 - nty1)/(ny2 - ny1),
30416         pzl = (nz1 - nz0)/(ny1 - ny0),
30417         pzr = (nz2 - nz0)/(ny2 - ny0),
30418         pzn = (nz2 - nz1)/(ny2 - ny1),
30419         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
30420         txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
30421         tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
30422         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))),
30423         txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
30424         tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
30425       _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
30426                           nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
30427         if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
30428         int
30429           xleft = xleft0, xright = xright0,
30430           lxleft = lxleft0, lxright = lxright0,
30431           lyleft = lyleft0, lyright = lyright0;
30432         float
30433           zleft = zl, zright = zr,
30434           txleft = txl, txright = txr,
30435           tyleft = tyl, tyright = tyr;
30436         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,lxleft,lxright,lyleft,lyright);
30437         const int
30438           dx = xright - xleft,
30439           dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
30440           dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
30441           rlx = dx?(lxright - lxleft)/dx:0,
30442           rly = dx?(lyright - lyleft)/dx:0,
30443           slx = lxright>lxleft?1:-1,
30444           sly = lyright>lyleft?1:-1,
30445           ndlx = dlx - (dx?dx*(dlx/dx):0),
30446           ndly = dly - (dx?dx*(dly/dx):0);
30447         const float
30448           pentez = (zright - zleft)/dx,
30449           pentetx = (txright - txleft)/dx,
30450           pentety = (tyright - tyleft)/dx;
30451         int errlx = dx>>1, errly = errlx;
30452         if (xleft<0 && dx) {
30453           zleft-=xleft*(zright - zleft)/dx;
30454           lxleft-=xleft*(lxright - lxleft)/dx;
30455           lyleft-=xleft*(lyright - lyleft)/dx;
30456           txleft-=xleft*(txright - txleft)/dx;
30457           tyleft-=xleft*(tyright - tyleft)/dx;
30458         }
30459         if (xleft<0) xleft = 0;
30460         if (xright>=width()-1) xright = width()-1;
30461         T* ptrd = data(xleft,y,0,0);
30462         if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
30463           const float invz = 1/zleft;
30464           const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
30465           cimg_forC(*this,c) {
30466             const tl l = light(lxleft,lyleft,c);
30467             *ptrd = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
30468             ptrd+=whd; col+=twhd;
30469           }
30470           ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
30471           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
30472           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
30473         } else for (int x = xleft; x<=xright; ++x) {
30474           const float invz = 1/zleft;
30475           const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
30476           cimg_forC(*this,c) {
30477             const tl l = light(lxleft,lyleft,c);
30478             const T val = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
30479             *ptrd = (T)(nopacity*val + *ptrd*copacity);
30480             ptrd+=whd; col+=twhd;
30481           }
30482           ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
30483           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
30484           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
30485         }
30486         zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
30487       }
30488       return *this;
30489     }
30490 
30491     //! Draw a textured Phong-shaded 2d triangle, with perspective correction and z-buffering.
30492     template<typename tz, typename tc, typename tl>
30493     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
30494                            const int x0, const int y0, const float z0,
30495                            const int x1, const int y1, const float z1,
30496                            const int x2, const int y2, const float z2,
30497                            const CImg<tc>& texture,
30498                            const int tx0, const int ty0,
30499                            const int tx1, const int ty1,
30500                            const int tx2, const int ty2,
30501                            const CImg<tl>& light,
30502                            const int lx0, const int ly0,
30503                            const int lx1, const int ly1,
30504                            const int lx2, const int ly2,
30505                            const float opacity=1) {
30506       typedef typename cimg::superset<tz,float>::type tzfloat;
30507       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
30508       if (!is_sameXY(zbuffer))
30509         throw CImgArgumentException(_cimg_instance
30510                                     "draw_triangle() : Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
30511                                     cimg_instance,
30512                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
30513       if (texture._depth>1 || texture._spectrum<_spectrum)
30514         throw CImgArgumentException(_cimg_instance
30515                                     "draw_triangle() : Invalid specified texture (%u,%u,%u,%u,%p).",
30516                                     cimg_instance,
30517                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
30518       if (light._depth>1 || light._spectrum<_spectrum)
30519         throw CImgArgumentException(_cimg_instance
30520                                     "draw_triangle() : Invalid specified light texture (%u,%u,%u,%u,%p).",
30521                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
30522       if (is_overlapped(texture)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,
30523                                                        +texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
30524       if (is_overlapped(light)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,
30525                                                      texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
30526       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
30527       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
30528       const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth, offx = _spectrum*whd;
30529       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
30530         nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
30531       float
30532         ntx0 = tx0/z0, nty0 = ty0/z0,
30533         ntx1 = tx1/z1, nty1 = ty1/z1,
30534         ntx2 = tx2/z2, nty2 = ty2/z2;
30535       tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
30536       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1,nz0,nz1);
30537       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2,nz0,nz2);
30538       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2,nz1,nz2);
30539       if (ny0>=height() || ny2<0) return *this;
30540       float
30541         ptxl = (ntx1 - ntx0)/(ny1 - ny0),
30542         ptxr = (ntx2 - ntx0)/(ny2 - ny0),
30543         ptxn = (ntx2 - ntx1)/(ny2 - ny1),
30544         ptyl = (nty1 - nty0)/(ny1 - ny0),
30545         ptyr = (nty2 - nty0)/(ny2 - ny0),
30546         ptyn = (nty2 - nty1)/(ny2 - ny1),
30547         txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
30548         tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
30549         txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
30550         tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
30551       tzfloat
30552         pzl = (nz1 - nz0)/(ny1 - ny0),
30553         pzr = (nz2 - nz0)/(ny2 - ny0),
30554         pzn = (nz2 - nz1)/(ny2 - ny1),
30555         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
30556         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
30557       _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
30558                           nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
30559         if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
30560         int
30561           xleft = xleft0, xright = xright0,
30562           lxleft = lxleft0, lxright = lxright0,
30563           lyleft = lyleft0, lyright = lyright0;
30564         float txleft = txl, txright = txr, tyleft = tyl, tyright = tyr;
30565         tzfloat zleft = zl, zright = zr;
30566         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,lxleft,lxright,lyleft,lyright);
30567         const int
30568           dx = xright - xleft,
30569           dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
30570           dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
30571           rlx = dx?(lxright - lxleft)/dx:0,
30572           rly = dx?(lyright - lyleft)/dx:0,
30573           slx = lxright>lxleft?1:-1,
30574           sly = lyright>lyleft?1:-1,
30575           ndlx = dlx - (dx?dx*(dlx/dx):0),
30576           ndly = dly - (dx?dx*(dly/dx):0);
30577         float pentetx = (txright - txleft)/dx, pentety = (tyright - tyleft)/dx;
30578         const tzfloat pentez = (zright - zleft)/dx;
30579         int errlx = dx>>1, errly = errlx;
30580         if (xleft<0 && dx) {
30581           zleft-=xleft*(zright - zleft)/dx;
30582           lxleft-=xleft*(lxright - lxleft)/dx;
30583           lyleft-=xleft*(lyright - lyleft)/dx;
30584           txleft-=xleft*(txright - txleft)/dx;
30585           tyleft-=xleft*(tyright - tyleft)/dx;
30586         }
30587         if (xleft<0) xleft = 0;
30588         if (xright>=width()-1) xright = width()-1;
30589         T* ptrd = data(xleft,y);
30590         tz *ptrz = zbuffer.data(xleft,y);
30591         if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
30592             if (zleft>=(tzfloat)*ptrz) {
30593               *ptrz = (tz)zleft;
30594               const tzfloat invz = 1/zleft;
30595               const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
30596               cimg_forC(*this,c) {
30597                 const tl l = light(lxleft,lyleft,c);
30598                 *ptrd = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
30599                 ptrd+=whd; col+=twhd;
30600               }
30601               ptrd-=offx;
30602             }
30603             zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
30604             lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
30605             lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
30606           } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
30607             if (zleft>=(tzfloat)*ptrz) {
30608               *ptrz = (tz)zleft;
30609               const tzfloat invz = 1/zleft;
30610               const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
30611               cimg_forC(*this,c) {
30612                 const tl l = light(lxleft,lyleft,c);
30613                 const T val = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
30614                 *ptrd = (T)(nopacity*val + *ptrd*copacity);
30615                 ptrd+=whd; col+=twhd;
30616               }
30617               ptrd-=offx;
30618             }
30619             zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
30620             lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
30621             lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
30622           }
30623         zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
30624       }
30625       return *this;
30626     }
30627 
30628     //! Draw a filled 4d rectangle.
30629     /**
30630        \param x0 X-coordinate of the upper-left rectangle corner.
30631        \param y0 Y-coordinate of the upper-left rectangle corner.
30632        \param z0 Z-coordinate of the upper-left rectangle corner.
30633        \param c0 C-coordinate of the upper-left rectangle corner.
30634        \param x1 X-coordinate of the lower-right rectangle corner.
30635        \param y1 Y-coordinate of the lower-right rectangle corner.
30636        \param z1 Z-coordinate of the lower-right rectangle corner.
30637        \param c1 C-coordinate of the lower-right rectangle corner.
30638        \param val Scalar value used to fill the rectangle area.
30639        \param opacity Drawing opacity.
30640     **/
30641     CImg<T>& draw_rectangle(const int x0, const int y0, const int z0, const int c0,
30642                             const int x1, const int y1, const int z1, const int c1,
30643                             const T val, const float opacity=1) {
30644       if (is_empty()) return *this;
30645       const bool bx = (x0<x1), by = (y0<y1), bz = (z0<z1), bc = (c0<c1);
30646       const int
30647         nx0 = bx?x0:x1, nx1 = bx?x1:x0,
30648         ny0 = by?y0:y1, ny1 = by?y1:y0,
30649         nz0 = bz?z0:z1, nz1 = bz?z1:z0,
30650         nc0 = bc?c0:c1, nc1 = bc?c1:c0;
30651       const int
30652         lX = (1 + nx1 - nx0) + (nx1>=width()?width() - 1 - nx1:0) + (nx0<0?nx0:0),
30653         lY = (1 + ny1 - ny0) + (ny1>=height()?height() - 1 - ny1:0) + (ny0<0?ny0:0),
30654         lZ = (1 + nz1 - nz0) + (nz1>=depth()?depth() - 1 - nz1:0) + (nz0<0?nz0:0),
30655         lC = (1 + nc1 - nc0) + (nc1>=spectrum()?spectrum() - 1 - nc1:0) + (nc0<0?nc0:0);
30656       const unsigned long
30657         offX = (unsigned long)_width - lX,
30658         offY = (unsigned long)_width*(_height - lY),
30659         offZ = (unsigned long)_width*_height*(_depth - lZ);
30660       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
30661       T *ptrd = data(nx0<0?0:nx0,ny0<0?0:ny0,nz0<0?0:nz0,nc0<0?0:nc0);
30662       if (lX>0 && lY>0 && lZ>0 && lC>0)
30663         for (int v = 0; v<lC; ++v) {
30664           for (int z = 0; z<lZ; ++z) {
30665             for (int y = 0; y<lY; ++y) {
30666               if (opacity>=1) {
30667                 if (sizeof(T)!=1) { for (int x = 0; x<lX; ++x) *(ptrd++) = val; ptrd+=offX; }
30668                 else { std::memset(ptrd,(int)val,lX); ptrd+=_width; }
30669               } else { for (int x = 0; x<lX; ++x) { *ptrd = (T)(nopacity*val + *ptrd*copacity); ++ptrd; } ptrd+=offX; }
30670             }
30671             ptrd+=offY;
30672           }
30673           ptrd+=offZ;
30674         }
30675       return *this;
30676     }
30677 
30678     //! Draw a filled 3d rectangle.
30679     /**
30680        \param x0 X-coordinate of the upper-left rectangle corner.
30681        \param y0 Y-coordinate of the upper-left rectangle corner.
30682        \param z0 Z-coordinate of the upper-left rectangle corner.
30683        \param x1 X-coordinate of the lower-right rectangle corner.
30684        \param y1 Y-coordinate of the lower-right rectangle corner.
30685        \param z1 Z-coordinate of the lower-right rectangle corner.
30686        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
30687        \param opacity Drawing opacity.
30688     **/
30689     template<typename tc>
30690     CImg<T>& draw_rectangle(const int x0, const int y0, const int z0,
30691                             const int x1, const int y1, const int z1,
30692                             const tc *const color, const float opacity=1) {
30693       if (is_empty()) return *this;
30694       if (!color)
30695         throw CImgArgumentException(_cimg_instance
30696                                     "draw_rectangle : Specified color is (null).",
30697                                     cimg_instance);
30698       cimg_forC(*this,c) draw_rectangle(x0,y0,z0,c,x1,y1,z1,c,(T)color[c],opacity);
30699       return *this;
30700     }
30701 
30702     //! Draw an outlined 3d rectangle \overloading.
30703     template<typename tc>
30704     CImg<T>& draw_rectangle(const int x0, const int y0, const int z0,
30705                             const int x1, const int y1, const int z1,
30706                             const tc *const color, const float opacity,
30707                             const unsigned int pattern) {
30708       return draw_line(x0,y0,z0,x1,y0,z0,color,opacity,pattern,true).
30709         draw_line(x1,y0,z0,x1,y1,z0,color,opacity,pattern,false).
30710         draw_line(x1,y1,z0,x0,y1,z0,color,opacity,pattern,false).
30711         draw_line(x0,y1,z0,x0,y0,z0,color,opacity,pattern,false).
30712         draw_line(x0,y0,z1,x1,y0,z1,color,opacity,pattern,true).
30713         draw_line(x1,y0,z1,x1,y1,z1,color,opacity,pattern,false).
30714         draw_line(x1,y1,z1,x0,y1,z1,color,opacity,pattern,false).
30715         draw_line(x0,y1,z1,x0,y0,z1,color,opacity,pattern,false).
30716         draw_line(x0,y0,z0,x0,y0,z1,color,opacity,pattern,true).
30717         draw_line(x1,y0,z0,x1,y0,z1,color,opacity,pattern,true).
30718         draw_line(x1,y1,z0,x1,y1,z1,color,opacity,pattern,true).
30719         draw_line(x0,y1,z0,x0,y1,z1,color,opacity,pattern,true);
30720     }
30721 
30722     //! Draw a filled 2d rectangle.
30723     /**
30724        \param x0 X-coordinate of the upper-left rectangle corner.
30725        \param y0 Y-coordinate of the upper-left rectangle corner.
30726        \param x1 X-coordinate of the lower-right rectangle corner.
30727        \param y1 Y-coordinate of the lower-right rectangle corner.
30728        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
30729        \param opacity Drawing opacity.
30730     **/
30731     template<typename tc>
30732     CImg<T>& draw_rectangle(const int x0, const int y0,
30733                             const int x1, const int y1,
30734                             const tc *const color, const float opacity=1) {
30735       return draw_rectangle(x0,y0,0,x1,y1,_depth-1,color,opacity);
30736     }
30737 
30738     //! Draw a outlined 2d rectangle \overloading.
30739     template<typename tc>
30740     CImg<T>& draw_rectangle(const int x0, const int y0,
30741                             const int x1, const int y1,
30742                             const tc *const color, const float opacity,
30743                             const unsigned int pattern) {
30744       if (is_empty()) return *this;
30745       if (y0==y1) return draw_line(x0,y0,x1,y0,color,opacity,pattern,true);
30746       if (x0==x1) return draw_line(x0,y0,x0,y1,color,opacity,pattern,true);
30747       const bool bx = (x0<x1), by = (y0<y1);
30748       const int
30749         nx0 = bx?x0:x1, nx1 = bx?x1:x0,
30750         ny0 = by?y0:y1, ny1 = by?y1:y0;
30751       if (ny1==ny0+1) return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true).
30752                       draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false);
30753       return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true).
30754         draw_line(nx1,ny0+1,nx1,ny1-1,color,opacity,pattern,false).
30755         draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false).
30756         draw_line(nx0,ny1-1,nx0,ny0+1,color,opacity,pattern,false);
30757     }
30758 
30759     //! Draw a filled 2d polygon.
30760     /**
30761        \param points Set of polygon vertices.
30762        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
30763        \param opacity Drawing opacity.
30764      **/
30765     template<typename t, typename tc>
30766     CImg<T>& draw_polygon(const CImg<t>& points,
30767                           const tc *const color, const float opacity=1) {
30768       if (is_empty() || !points || points._width<3) return *this;
30769       if (!color)
30770         throw CImgArgumentException(_cimg_instance
30771                                     "draw_polygon() : Specified color is (null).",
30772                                     cimg_instance);
30773 
30774       // Normalize 2d input coordinates.
30775       CImg<intT> npoints(points._width,2);
30776       int x = npoints(0,0) = (int)points(0,0), y = npoints(0,1) = (int)points(0,1);
30777       unsigned int nb_points = 1;
30778       for (unsigned int p = 1; p<points._width; ++p) {
30779         const int nx = (int)points(p,0), ny = (int)points(p,1);
30780         if (nx!=x || ny!=y) { npoints(nb_points,0) = nx; npoints(nb_points++,1) = ny; x = nx; y = ny; }
30781       }
30782 
30783       if (nb_points==3) return draw_triangle((int)npoints(0,0),(int)npoints(0,1),
30784                                              (int)npoints(1,0),(int)npoints(1,1),
30785                                              (int)npoints(2,0),(int)npoints(2,1),color,opacity);
30786       // Draw polygon segments.
30787       _draw_scanline(color,opacity);
30788       int
30789         xmax = 0, xmin = (int)npoints.get_shared_points(0,nb_points-1,0).min_max(xmax),
30790         ymax = 0, ymin = (int)npoints.get_shared_points(0,nb_points-1,1).min_max(ymax);
30791       if (xmax<0 || xmin>=width() || ymax<0 || ymin>=height()) return *this;
30792       if (ymin==ymax) return _draw_scanline(xmin,xmax,ymin,color,opacity);
30793       const unsigned int
30794         nymin = ymin<0?0:(unsigned int)ymin,
30795         nymax = ymax>=height()?_height-1:(unsigned int)ymax,
30796         dy = 1 + nymax - nymin;
30797       CImg<intT> X(1+2*nb_points,dy,1,1,0), tmp;
30798       int cx = (int)npoints(0,0), cy = (int)npoints(0,1);
30799       unsigned int cp = 0;
30800       for (unsigned int p = 0; p<nb_points; ++p) {
30801         const unsigned int np = (p!=nb_points-1)?p+1:0, ap = (np!=nb_points-1)?np+1:0;
30802         const int
30803           nx = (int)npoints(np,0), ny = (int)npoints(np,1), ay = (int)npoints(ap,1),
30804           y0 = cy - nymin, y1 = ny - nymin;
30805         if (y0!=y1) {
30806           const int countermin = ((ny<ay && cy<ny) || (ny>ay && cy>ny))?1:0;
30807           for (int x = cx, y = y0, _sx = 1, _sy = 1,
30808                  _dx = nx>cx?nx-cx:((_sx=-1),cx-nx),
30809                  _dy = y1>y0?y1-y0:((_sy=-1),y0-y1),
30810                  _counter = ((_dx-=_dy?_dy*(_dx/_dy):0),_dy),
30811                  _err = _dx>>1,
30812                  _rx = _dy?(nx-cx)/_dy:0;
30813                _counter>=countermin;
30814                --_counter, y+=_sy, x+=_rx + ((_err-=_dx)<0?_err+=_dy,_sx:0))
30815             if (y>=0 && y<(int)dy) X(++X(0,y),y) = x;
30816           cp = np; cx = nx; cy = ny;
30817         } else {
30818           const int pp = (cp?cp-1:nb_points-1), py = (int)npoints(pp,1);
30819           if (y0>=0 && y0<(int)dy && (!p || (cy>py && ay>cy) || (cy<py && ay<cy))) X(++X(0,y0),y0) = nx;
30820           if (cy!=ay) { cp = np; cx = nx; cy = ny; }
30821         }
30822       }
30823 
30824       // Draw polygon scanlines.
30825       for (int y = 0; y<(int)dy; ++y) {
30826         tmp.assign(X.data(1,y),X(0,y),1,1,1,true).sort();
30827         for (int i = 1; i<=X(0,y); ) {
30828           const int xb = X(i++,y), xe = X(i++,y);
30829           _draw_scanline(xb,xe,nymin+y,color,opacity);
30830         }
30831       }
30832 
30833       return *this;
30834     }
30835 
30836     //! Draw a outlined 2d polygon \overloading.
30837     template<typename t, typename tc>
30838     CImg<T>& draw_polygon(const CImg<t>& points,
30839                           const tc *const color, const float opacity, const unsigned int pattern) {
30840       if (is_empty() || !points || points._width<3) return *this;
30841       bool ninit_hatch = true;
30842       switch (points._height) {
30843       case 0 : case 1 :
30844         throw CImgArgumentException(_cimg_instance
30845                                     "draw_polygon() : Invalid specified point set.",
30846                                     cimg_instance);
30847       case 2 : { // 2d version.
30848         CImg<intT> npoints(points._width,2);
30849         int x = npoints(0,0) = (int)points(0,0), y = npoints(0,1) = (int)points(0,1);
30850         unsigned int nb_points = 1;
30851         for (unsigned int p = 1; p<points._width; ++p) {
30852           const int nx = (int)points(p,0), ny = (int)points(p,1);
30853           if (nx!=x || ny!=y) { npoints(nb_points,0) = nx; npoints(nb_points++,1) = ny; x = nx; y = ny; }
30854         }
30855         const int x0 = (int)npoints(0,0), y0 = (int)npoints(0,1);
30856         int ox = x0, oy = y0;
30857         for (unsigned int i = 1; i<nb_points; ++i) {
30858           const int x = (int)npoints(i,0), y = (int)npoints(i,1);
30859           draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch);
30860           ninit_hatch = false;
30861           ox = x; oy = y;
30862         }
30863         draw_line(ox,oy,x0,y0,color,opacity,pattern,false);
30864       } break;
30865       default : { // 3d version.
30866         CImg<intT> npoints(points._width,3);
30867         int x = npoints(0,0) = (int)points(0,0), y = npoints(0,1) = (int)points(0,1), z = npoints(0,2) = (int)points(0,2);
30868         unsigned int nb_points = 1;
30869         for (unsigned int p = 1; p<points._width; ++p) {
30870           const int nx = (int)points(p,0), ny = (int)points(p,1), nz = (int)points(p,2);
30871           if (nx!=x || ny!=y || nz!=z) { npoints(nb_points,0) = nx; npoints(nb_points,1) = ny; npoints(nb_points++,2) = nz; x = nx; y = ny; z = nz; }
30872         }
30873         const int x0 = (int)npoints(0,0), y0 = (int)npoints(0,1), z0 = (int)npoints(0,2);
30874         int ox = x0, oy = y0, oz = z0;
30875         for (unsigned int i = 1; i<nb_points; ++i) {
30876           const int x = (int)npoints(i,0), y = (int)npoints(i,1), z = (int)npoints(i,2);
30877           draw_line(ox,oy,oz,x,y,z,color,opacity,pattern,ninit_hatch);
30878           ninit_hatch = false;
30879           ox = x; oy = y; oz = z;
30880         }
30881         draw_line(ox,oy,oz,x0,y0,z0,color,opacity,pattern,false);
30882       }
30883       }
30884       return *this;
30885     }
30886 
30887     //! Draw a filled 2d ellipse.
30888     /**
30889        \param x0 X-coordinate of the ellipse center.
30890        \param y0 Y-coordinate of the ellipse center.
30891        \param r1 First radius of the ellipse.
30892        \param r2 Second radius of the ellipse.
30893        \param angle Angle of the first radius.
30894        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
30895        \param opacity Drawing opacity.
30896     **/
30897     template<typename tc>
30898     CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
30899                           const tc *const color, const float opacity=1) {
30900       return _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,0U);
30901     }
30902 
30903     //! Draw a filled 2d ellipse \overloading.
30904     /**
30905        \param x0 X-coordinate of the ellipse center.
30906        \param y0 Y-coordinate of the ellipse center.
30907        \param tensor Diffusion tensor describing the ellipse.
30908        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
30909        \param opacity Drawing opacity.
30910     **/
30911     template<typename t, typename tc>
30912     CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
30913                           const tc *const color, const float opacity=1) {
30914       CImgList<t> eig = tensor.get_symmetric_eigen();
30915       const CImg<t> &val = eig[0], &vec = eig[1];
30916       return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)),
30917                           std::atan2(vec(0,1),vec(0,0))*180/cimg::PI,
30918                           color,opacity);
30919     }
30920 
30921     //! Draw an outlined 2d ellipse.
30922     /**
30923        \param x0 X-coordinate of the ellipse center.
30924        \param y0 Y-coordinate of the ellipse center.
30925        \param r1 First radius of the ellipse.
30926        \param r2 Second radius of the ellipse.
30927        \param angle Angle of the first radius.
30928        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
30929        \param opacity Drawing opacity.
30930        \param pattern An integer whose bits describe the outline pattern.
30931     **/
30932     template<typename tc>
30933     CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
30934                           const tc *const color, const float opacity, const unsigned int pattern) {
30935       if (pattern) _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,pattern);
30936       return *this;
30937     }
30938 
30939     //! Draw an outlined 2d ellipse \overloading.
30940     /**
30941        \param x0 X-coordinate of the ellipse center.
30942        \param y0 Y-coordinate of the ellipse center.
30943        \param tensor Diffusion tensor describing the ellipse.
30944        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
30945        \param opacity Drawing opacity.
30946        \param pattern An integer whose bits describe the outline pattern.
30947     **/
30948     template<typename t, typename tc>
30949     CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
30950                           const tc *const color, const float opacity,
30951                           const unsigned int pattern) {
30952       CImgList<t> eig = tensor.get_symmetric_eigen();
30953       const CImg<t> &val = eig[0], &vec = eig[1];
30954       return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)),
30955                           std::atan2(vec(0,1),vec(0,0))*180/cimg::PI,
30956                           color,opacity,pattern);
30957     }
30958 
30959     template<typename tc>
30960     CImg<T>& _draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
30961                            const tc *const color, const float opacity,
30962                            const unsigned int pattern) {
30963       if (is_empty()) return *this;
30964       if (!color)
30965         throw CImgArgumentException(_cimg_instance
30966                                     "draw_ellipse : Specified color is (null).",
30967                                     cimg_instance);
30968       if (r1<=0 || r2<=0) return draw_point(x0,y0,color,opacity);
30969       _draw_scanline(color,opacity);
30970       const float
30971         nr1 = cimg::abs(r1), nr2 = cimg::abs(r2),
30972         nangle = (float)(angle*cimg::PI/180),
30973         u = (float)std::cos(nangle),
30974         v = (float)std::sin(nangle),
30975         rmax = cimg::max(nr1,nr2),
30976         l1 = (float)std::pow(rmax/(nr1>0?nr1:1e-6),2),
30977         l2 = (float)std::pow(rmax/(nr2>0?nr2:1e-6),2),
30978         a = l1*u*u + l2*v*v,
30979         b = u*v*(l1-l2),
30980         c = l1*v*v + l2*u*u;
30981       const int
30982         yb = (int)std::sqrt(a*rmax*rmax/(a*c - b*b)),
30983         tymin = y0 - yb - 1,
30984         tymax = y0 + yb + 1,
30985         ymin = tymin<0?0:tymin,
30986         ymax = tymax>=height()?height()-1:tymax;
30987       int oxmin = 0, oxmax = 0;
30988       bool first_line = true;
30989       for (int y = ymin; y<=ymax; ++y) {
30990         const float
30991           Y = y - y0 + (y<y0?0.5f:-0.5f),
30992           delta = b*b*Y*Y - a*(c*Y*Y - rmax*rmax),
30993           sdelta = delta>0?(float)std::sqrt(delta)/a:0.0f,
30994           bY = b*Y/a,
30995           fxmin = x0 - 0.5f - bY - sdelta,
30996           fxmax = x0 + 0.5f - bY + sdelta;
30997         const int xmin = (int)fxmin, xmax = (int)fxmax;
30998         if (!pattern) _draw_scanline(xmin,xmax,y,color,opacity);
30999         else {
31000           if (first_line) {
31001             if (y0-yb>=0) _draw_scanline(xmin,xmax,y,color,opacity);
31002             else draw_point(xmin,y,color,opacity).draw_point(xmax,y,color,opacity);
31003             first_line = false;
31004           } else {
31005             if (xmin<oxmin) _draw_scanline(xmin,oxmin-1,y,color,opacity);
31006             else _draw_scanline(oxmin+(oxmin==xmin?0:1),xmin,y,color,opacity);
31007             if (xmax<oxmax) _draw_scanline(xmax,oxmax-1,y,color,opacity);
31008             else _draw_scanline(oxmax+(oxmax==xmax?0:1),xmax,y,color,opacity);
31009             if (y==tymax) _draw_scanline(xmin+1,xmax-1,y,color,opacity);
31010           }
31011         }
31012         oxmin = xmin; oxmax = xmax;
31013       }
31014       return *this;
31015     }
31016 
31017     //! Draw a filled 2d circle.
31018     /**
31019        \param x0 X-coordinate of the circle center.
31020        \param y0 Y-coordinate of the circle center.
31021        \param radius  Circle radius.
31022        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
31023        \param opacity Drawing opacity.
31024        \note
31025        - Circle version of the Bresenham's algorithm is used.
31026     **/
31027     template<typename tc>
31028     CImg<T>& draw_circle(const int x0, const int y0, int radius,
31029                          const tc *const color, const float opacity=1) {
31030       if (is_empty()) return *this;
31031       if (!color)
31032         throw CImgArgumentException(_cimg_instance
31033                                     "draw_circle : Specified color is (null).",
31034                                     cimg_instance);
31035       _draw_scanline(color,opacity);
31036       if (radius<0 || x0-radius>=width() || y0+radius<0 || y0-radius>=height()) return *this;
31037       if (y0>=0 && y0<height()) _draw_scanline(x0-radius,x0+radius,y0,color,opacity);
31038       for (int f = 1-radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x<y; ) {
31039         if (f>=0) {
31040           const int x1 = x0-x, x2 = x0+x, y1 = y0-y, y2 = y0+y;
31041           if (y1>=0 && y1<height()) _draw_scanline(x1,x2,y1,color,opacity);
31042           if (y2>=0 && y2<height()) _draw_scanline(x1,x2,y2,color,opacity);
31043           f+=(ddFy+=2); --y;
31044         }
31045         const bool no_diag = y!=(x++);
31046         ++(f+=(ddFx+=2));
31047         const int x1 = x0-y, x2 = x0+y, y1 = y0-x, y2 = y0+x;
31048         if (no_diag) {
31049           if (y1>=0 && y1<height()) _draw_scanline(x1,x2,y1,color,opacity);
31050           if (y2>=0 && y2<height()) _draw_scanline(x1,x2,y2,color,opacity);
31051         }
31052       }
31053       return *this;
31054     }
31055 
31056     //! Draw an outlined 2d circle.
31057     /**
31058        \param x0 X-coordinate of the circle center.
31059        \param y0 Y-coordinate of the circle center.
31060        \param radius Circle radius.
31061        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
31062        \param opacity Drawing opacity.
31063        \param pattern An integer whose bits describe the outline pattern.
31064     **/
31065     template<typename tc>
31066     CImg<T>& draw_circle(const int x0, const int y0, int radius,
31067                          const tc *const color, const float opacity,
31068                          const unsigned int pattern) {
31069       cimg::unused(pattern);
31070       if (is_empty()) return *this;
31071       if (!color)
31072         throw CImgArgumentException(_cimg_instance
31073                                     "draw_circle : Specified color is (null).",
31074                                     cimg_instance);
31075       if (radius<0 || x0-radius>=width() || y0+radius<0 || y0-radius>=height()) return *this;
31076       if (!radius) return draw_point(x0,y0,color,opacity);
31077       draw_point(x0-radius,y0,color,opacity).draw_point(x0+radius,y0,color,opacity).
31078         draw_point(x0,y0-radius,color,opacity).draw_point(x0,y0+radius,color,opacity);
31079       if (radius==1) return *this;
31080       for (int f = 1-radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x<y; ) {
31081         if (f>=0) { f+=(ddFy+=2); --y; }
31082         ++x; ++(f+=(ddFx+=2));
31083         if (x!=y+1) {
31084           const int x1 = x0-y, x2 = x0+y, y1 = y0-x, y2 = y0+x, x3 = x0-x, x4 = x0+x, y3 = y0-y, y4 = y0+y;
31085           draw_point(x1,y1,color,opacity).draw_point(x1,y2,color,opacity).
31086             draw_point(x2,y1,color,opacity).draw_point(x2,y2,color,opacity);
31087           if (x!=y)
31088             draw_point(x3,y3,color,opacity).draw_point(x4,y4,color,opacity).
31089               draw_point(x4,y3,color,opacity).draw_point(x3,y4,color,opacity);
31090         }
31091       }
31092       return *this;
31093     }
31094 
31095     //! Draw an image.
31096     /**
31097        \param sprite Sprite image.
31098        \param x0 X-coordinate of the sprite position.
31099        \param y0 Y-coordinate of the sprite position.
31100        \param z0 Z-coordinate of the sprite position.
31101        \param c0 C-coordinate of the sprite position.
31102        \param opacity Drawing opacity.
31103     **/
31104     template<typename t>
31105     CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
31106                         const CImg<t>& sprite, const float opacity=1) {
31107       if (is_empty() || !sprite) return *this;
31108       if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity);
31109       if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1) return assign(sprite,false);
31110       const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bc = (c0<0);
31111       const int
31112         lX = sprite.width() - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0) + (bx?x0:0),
31113         lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0),
31114         lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0),
31115         lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0);
31116       const t
31117         *ptrs = sprite._data -
31118         (bx?x0:0) -
31119         (by?y0*sprite.width():0) -
31120         (bz?z0*sprite.width()*sprite.height():0) -
31121         (bc?c0*sprite.width()*sprite.height()*sprite.depth():0);
31122       const unsigned long
31123         offX = (unsigned long)_width - lX,
31124         soffX = (unsigned long)sprite._width - lX,
31125         offY = (unsigned long)_width*(_height - lY),
31126         soffY = (unsigned long)sprite._width*(sprite._height - lY),
31127         offZ = (unsigned long)_width*_height*(_depth - lZ),
31128         soffZ = (unsigned long)sprite._width*sprite._height*(sprite._depth - lZ);
31129       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
31130       if (lX>0 && lY>0 && lZ>0 && lC>0) {
31131         T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0);
31132         for (int v = 0; v<lC; ++v) {
31133           for (int z = 0; z<lZ; ++z) {
31134             for (int y = 0; y<lY; ++y) {
31135               if (opacity>=1) for (int x = 0; x<lX; ++x) *(ptrd++) = (T)*(ptrs++);
31136               else for (int x = 0; x<lX; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; }
31137               ptrd+=offX; ptrs+=soffX;
31138             }
31139             ptrd+=offY; ptrs+=soffY;
31140           }
31141           ptrd+=offZ; ptrs+=soffZ;
31142         }
31143       }
31144       return *this;
31145     }
31146 
31147     //! Draw an image \specialization.
31148     CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
31149                         const CImg<T>& sprite, const float opacity=1) {
31150       if (is_empty() || !sprite) return *this;
31151       if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity);
31152       if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1) return assign(sprite,false);
31153       const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bc = (c0<0);
31154       const int
31155         lX = sprite.width() - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0) + (bx?x0:0),
31156         lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0),
31157         lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0),
31158         lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0);
31159       const T
31160         *ptrs = sprite._data -
31161         (bx?x0:0) -
31162         (by?y0*sprite.width():0) -
31163         (bz?z0*sprite.width()*sprite.height():0) -
31164         (bc?c0*sprite.width()*sprite.height()*sprite.depth():0);
31165       const unsigned long
31166         offX = (unsigned long)_width - lX,
31167         soffX = (unsigned long)sprite._width - lX,
31168         offY = (unsigned long)_width*(_height - lY),
31169         soffY = (unsigned long)sprite._width*(sprite._height - lY),
31170         offZ = (unsigned long)_width*_height*(_depth - lZ),
31171         soffZ = (unsigned long)sprite._width*sprite._height*(sprite._depth - lZ),
31172         slX = lX*sizeof(T);
31173       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
31174       if (lX>0 && lY>0 && lZ>0 && lC>0) {
31175         T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0);
31176         for (int v = 0; v<lC; ++v) {
31177           for (int z = 0; z<lZ; ++z) {
31178             if (opacity>=1) for (int y = 0; y<lY; ++y) { std::memcpy(ptrd,ptrs,slX); ptrd+=_width; ptrs+=sprite._width; }
31179             else for (int y = 0; y<lY; ++y) {
31180                 for (int x = 0; x<lX; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; }
31181                 ptrd+=offX; ptrs+=soffX;
31182               }
31183             ptrd+=offY; ptrs+=soffY;
31184           }
31185           ptrd+=offZ; ptrs+=soffZ;
31186         }
31187       }
31188       return *this;
31189     }
31190 
31191     //! Draw an image \overloading.
31192     template<typename t>
31193     CImg<T>& draw_image(const int x0, const int y0, const int z0,
31194                         const CImg<t>& sprite, const float opacity=1) {
31195       return draw_image(x0,y0,z0,0,sprite,opacity);
31196     }
31197 
31198     //! Draw an image \overloading.
31199     template<typename t>
31200     CImg<T>& draw_image(const int x0, const int y0,
31201                         const CImg<t>& sprite, const float opacity=1) {
31202       return draw_image(x0,y0,0,sprite,opacity);
31203     }
31204 
31205     //! Draw an image \overloading.
31206     template<typename t>
31207     CImg<T>& draw_image(const int x0,
31208                         const CImg<t>& sprite, const float opacity=1) {
31209       return draw_image(x0,0,sprite,opacity);
31210     }
31211 
31212     //! Draw an image \overloading.
31213     template<typename t>
31214     CImg<T>& draw_image(const CImg<t>& sprite, const float opacity=1) {
31215       return draw_image(0,sprite,opacity);
31216     }
31217 
31218     //! Draw a masked image.
31219     /**
31220        \param sprite Sprite image.
31221        \param mask Mask image.
31222        \param x0 X-coordinate of the sprite position in the image instance.
31223        \param y0 Y-coordinate of the sprite position in the image instance.
31224        \param z0 Z-coordinate of the sprite position in the image instance.
31225        \param c0 C-coordinate of the sprite position in the image instance.
31226        \param mask_max_value Maximum pixel value of the mask image \c mask.
31227        \param opacity Drawing opacity.
31228        \note
31229        - Pixel values of \c mask set the opacity of the corresponding pixels in \c sprite.
31230        - Dimensions along x,y and z of \p sprite and \p mask must be the same.
31231     **/
31232     template<typename ti, typename tm>
31233     CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
31234                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
31235                         const float mask_max_value=1) {
31236       if (is_empty() || !sprite || !mask) return *this;
31237       if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,mask,opacity,mask_max_value);
31238       if (is_overlapped(mask))   return draw_image(x0,y0,z0,c0,sprite,+mask,opacity,mask_max_value);
31239       if (mask._width!=sprite._width || mask._height!=sprite._height || mask._depth!=sprite._depth)
31240         throw CImgArgumentException(_cimg_instance
31241                                     "draw_image() : Sprite (%u,%u,%u,%u,%p) and mask (%u,%u,%u,%u,%p) have incompatible dimensions.",
31242                                     cimg_instance,
31243                                     sprite._width,sprite._height,sprite._depth,sprite._spectrum,sprite._data,
31244                                     mask._width,mask._height,mask._depth,mask._spectrum,mask._data);
31245 
31246       const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bc = (c0<0);
31247       const int
31248         lX = sprite.width() - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0) + (bx?x0:0),
31249         lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0),
31250         lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0),
31251         lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0);
31252       const int
31253         coff = -(bx?x0:0)-(by?y0*mask.width():0)-(bz?z0*mask.width()*mask.height():0)-(bc?c0*mask.width()*mask.height()*mask.depth():0),
31254         ssize = mask.width()*mask.height()*mask.depth();
31255       const ti *ptrs = sprite._data + coff;
31256       const tm *ptrm = mask._data   + coff;
31257       const unsigned long
31258         offX = (unsigned long)_width - lX,
31259         soffX = (unsigned long)sprite._width - lX,
31260         offY = (unsigned long)_width*(_height - lY),
31261         soffY = (unsigned long)sprite._width*(sprite._height - lY),
31262         offZ = (unsigned long)_width*_height*(_depth - lZ),
31263         soffZ = (unsigned long)sprite._width*sprite._height*(sprite._depth - lZ);
31264       if (lX>0 && lY>0 && lZ>0 && lC>0) {
31265         T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0);
31266         for (int c = 0; c<lC; ++c) {
31267           ptrm = mask._data + (ptrm - mask._data)%ssize;
31268           for (int z = 0; z<lZ; ++z) {
31269             for (int y = 0; y<lY; ++y) {
31270               for (int x = 0; x<lX; ++x) {
31271                 const float mopacity = (float)(*(ptrm++)*opacity),
31272                   nopacity = cimg::abs(mopacity), copacity = mask_max_value - cimg::max(mopacity,0);
31273                 *ptrd = (T)((nopacity*(*(ptrs++)) + *ptrd*copacity)/mask_max_value);
31274                 ++ptrd;
31275               }
31276               ptrd+=offX; ptrs+=soffX; ptrm+=soffX;
31277             }
31278             ptrd+=offY; ptrs+=soffY; ptrm+=soffY;
31279           }
31280           ptrd+=offZ; ptrs+=soffZ; ptrm+=soffZ;
31281         }
31282       }
31283       return *this;
31284     }
31285 
31286     //! Draw a masked image \overloading.
31287     template<typename ti, typename tm>
31288     CImg<T>& draw_image(const int x0, const int y0, const int z0,
31289                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
31290                         const float mask_max_value=1) {
31291       return draw_image(x0,y0,z0,0,sprite,mask,opacity,mask_max_value);
31292     }
31293 
31294     //! Draw a image \overloading.
31295     template<typename ti, typename tm>
31296     CImg<T>& draw_image(const int x0, const int y0,
31297                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
31298                         const float mask_max_value=1) {
31299       return draw_image(x0,y0,0,sprite,mask,opacity,mask_max_value);
31300     }
31301 
31302     //! Draw a image \overloading.
31303     template<typename ti, typename tm>
31304     CImg<T>& draw_image(const int x0,
31305                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
31306                         const float mask_max_value=1) {
31307       return draw_image(x0,0,sprite,mask,opacity,mask_max_value);
31308     }
31309 
31310     //! Draw an image.
31311     template<typename ti, typename tm>
31312     CImg<T>& draw_image(const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
31313                         const float mask_max_value=1) {
31314       return draw_image(0,sprite,mask,opacity,mask_max_value);
31315     }
31316 
31317     //! Draw a text string.
31318     /**
31319        \param x0 X-coordinate of the text in the image instance.
31320        \param y0 Y-coordinate of the text in the image instance.
31321        \param text Format of the text ('printf'-style format string).
31322        \param foreground_color Pointer to \c spectrum() consecutive values, defining the foreground drawing color.
31323        \param background_color Pointer to \c spectrum() consecutive values, defining the background drawing color.
31324        \param opacity Drawing opacity.
31325        \param font Font used for drawing text.
31326     **/
31327     template<typename tc1, typename tc2, typename t>
31328     CImg<T>& draw_text(const int x0, const int y0,
31329                        const char *const text,
31330                        const tc1 *const foreground_color, const tc2 *const background_color,
31331                        const float opacity, const CImgList<t>& font, ...) {
31332       if (!font) return *this;
31333       char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font);
31334       cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap);
31335       return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font);
31336     }
31337 
31338     //! Draw a text string \overloading.
31339     /**
31340        \note A transparent background is used for the text.
31341     **/
31342     template<typename tc, typename t>
31343     CImg<T>& draw_text(const int x0, const int y0,
31344                        const char *const text,
31345                        const tc *const foreground_color, const int,
31346                        const float opacity, const CImgList<t>& font, ...) {
31347       if (!font) return *this;
31348       char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font);
31349       cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap);
31350       return _draw_text(x0,y0,tmp,foreground_color,(tc*)0,opacity,font);
31351     }
31352 
31353     //! Draw a text string \overloading.
31354     /**
31355        \note A transparent foreground is used for the text.
31356     **/
31357     template<typename tc, typename t>
31358     CImg<T>& draw_text(const int x0, const int y0,
31359                        const char *const text,
31360                        const int, const tc *const background_color,
31361                        const float opacity, const CImgList<t>& font, ...) {
31362       if (!font) return *this;
31363       char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font);
31364       cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap);
31365       return _draw_text(x0,y0,tmp,(tc*)0,background_color,opacity,font);
31366     }
31367 
31368     //! Draw a text string \overloading.
31369     /**
31370        \param x0 X-coordinate of the text in the image instance.
31371        \param y0 Y-coordinate of the text in the image instance.
31372        \param text Format of the text ('printf'-style format string).
31373        \param foreground_color Array of spectrum() values of type \c T, defining the foreground color (0 means 'transparent').
31374        \param background_color Array of spectrum() values of type \c T, defining the background color (0 means 'transparent').
31375        \param opacity Drawing opacity.
31376        \param font_height Height of the text font (exact match for 13,24,32,57, interpolated otherwise).
31377     **/
31378     template<typename tc1, typename tc2>
31379     CImg<T>& draw_text(const int x0, const int y0,
31380                        const char *const text,
31381                        const tc1 *const foreground_color, const tc2 *const background_color,
31382                        const float opacity=1, const unsigned int font_height=13, ...) {
31383       if (!font_height) return *this;
31384       char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font_height); cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap);
31385       static CImgList<floatT> font;
31386       const unsigned int
31387         ref_height = font_height<=13?13:font_height<=28?24:font_height<=32?32:57,
31388         padding_x = font_height<=18?1:font_height<=32?2:3;
31389       if (!font || font[0]._height!=font_height) {
31390         font = CImgList<floatT>::font(ref_height,true);
31391         font[0].assign(1,font_height);
31392         if (ref_height==font_height) cimglist_for(font,l) font[l].resize(font[l]._width + padding_x,-100,-100,-100,0);
31393       }
31394       if (is_empty()) {
31395         if (font[0]._spectrum!=1) cimglist_for_in(font,0,255,l) font[l].channel(0);
31396       } else if (font[0]._spectrum<_spectrum) cimglist_for_in(font,0,255,l) font[l].resize(-100,-100,1,_spectrum);
31397       if (ref_height!=font_height) for (const char *ptrs = tmp; *ptrs; ++ptrs) {
31398           const unsigned int __c = (unsigned int)(unsigned char)*ptrs, _c = (__c=='\t')?' ':__c;
31399           if (_c<font._width) {
31400             CImg<floatT> &c = font[_c];
31401             if (c._height!=font_height) {
31402               c.resize(cimg::max(1U,c._width*font_height/c._height),font_height,-100,-100,c._height>font_height?2:3);
31403               c.resize(c._width + padding_x,-100,-100,-100,0);
31404             }
31405           }
31406           if (_c+256U<font._width) {
31407             CImg<floatT> &c = font[_c+256];
31408             if (c._height!=font_height) {
31409               c.resize(cimg::max(1U,c._width*font_height/c._height),font_height,-100,-100,c._height>font_height?2:3);
31410               c.resize(c._width + padding_x,-100,-100,-100,0);
31411             }
31412           }
31413         }
31414       return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font);
31415     }
31416 
31417     //! Draw a text string \overloading.
31418     template<typename tc>
31419     CImg<T>& draw_text(const int x0, const int y0,
31420                        const char *const text,
31421                        const tc *const foreground_color, const int background_color=0,
31422                        const float opacity=1, const unsigned int font_height=13, ...) {
31423       if (!font_height) return *this;
31424       cimg::unused(background_color);
31425       char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font_height); cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap);
31426       return draw_text(x0,y0,"%s",foreground_color,(const tc*)0,opacity,font_height,tmp);
31427     }
31428 
31429     //! Draw a text string \overloading.
31430     template<typename tc>
31431     CImg<T>& draw_text(const int x0, const int y0,
31432                        const char *const text,
31433                        const int, const tc *const background_color,
31434                        const float opacity=1, const unsigned int font_height=13, ...) {
31435       if (!font_height) return *this;
31436       char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font_height); cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap);
31437       return draw_text(x0,y0,"%s",(tc*)0,background_color,opacity,font_height,tmp);
31438     }
31439 
31440     template<typename tc1, typename tc2, typename t>
31441     CImg<T>& _draw_text(const int x0, const int y0,
31442                         const char *const text,
31443                         const tc1 *const foreground_color, const tc2 *const background_color,
31444                         const float opacity, const CImgList<t>& font) {
31445       if (!text) return *this;
31446       if (!font)
31447         throw CImgArgumentException(_cimg_instance
31448                                     "draw_text() : Empty specified font.",
31449                                     cimg_instance);
31450       const unsigned int text_length = (unsigned int)std::strlen(text);
31451       if (is_empty()) {
31452         // If needed, pre-compute necessary size of the image
31453         int x = 0, y = 0, w = 0;
31454         unsigned char c = 0;
31455         for (unsigned int i = 0; i<text_length; ++i) {
31456           c = text[i];
31457           switch (c) {
31458           case '\n' : y+=font[0]._height; if (x>w) w = x; x = 0; break;
31459           case '\t' : x+=4*font[' ']._width; break;
31460           default : if (c<font._width) x+=font[c]._width;
31461           }
31462         }
31463         if (x!=0 || c=='\n') {
31464           if (x>w) w=x;
31465           y+=font[0]._height;
31466         }
31467         assign(x0+w,y0+y,1,font[0]._spectrum,0);
31468         if (background_color) cimg_forC(*this,c) get_shared_channel(c).fill((T)background_color[c]);
31469       }
31470 
31471       int x = x0, y = y0;
31472       CImg<t> letter;
31473       for (unsigned int i = 0; i<text_length; ++i) {
31474         const unsigned char c = text[i];
31475         switch (c) {
31476         case '\n' : y+=font[' ']._height; x = x0; break;
31477         case '\t' : x+=4*font[' ']._width; break;
31478         default : if (c<font._width) {
31479           letter = font[c];
31480           const unsigned int cmin = cimg::min(_spectrum,letter._spectrum);
31481           const CImg<t>& mask = (c+256)<(int)font._width?font[c+256]:font[c];
31482           if (foreground_color)
31483             for (unsigned long p = 0; p<(unsigned long)letter._width*letter._height; ++p)
31484               if (mask(p)) for (unsigned int c = 0; c<cmin; ++c) letter(p,0,0,c) = (t)(letter(p,0,0,c)*foreground_color[c]);
31485           if (background_color)
31486             for (unsigned long p = 0; p<(unsigned long)letter._width*letter._height; ++p)
31487               if (!mask(p)) for (unsigned int c = 0; c<cmin; ++c) letter(p,0,0,c) = (t)background_color[c];
31488           if (!background_color && font._width>=512) draw_image(x,y,letter,mask,opacity,(T)1);
31489           else draw_image(x,y,letter,opacity);
31490           x+=letter._width;
31491           }
31492         }
31493       }
31494       return *this;
31495     }
31496 
31497     //! Draw a 2d vector field.
31498     /**
31499        \param flow Image of 2d vectors used as input data.
31500        \param color Image of spectrum()-D vectors corresponding to the color of each arrow.
31501        \param opacity Drawing opacity.
31502        \param sampling Length (in pixels) between each arrow.
31503        \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length).
31504        \param is_arrow Tells if arrows must be drawn, instead of oriented segments.
31505        \param pattern Used pattern to draw lines.
31506        \note Clipping is supported.
31507     **/
31508     template<typename t1, typename t2>
31509     CImg<T>& draw_quiver(const CImg<t1>& flow,
31510                          const t2 *const color, const float opacity=1,
31511                          const unsigned int sampling=25, const float factor=-20,
31512                          const bool is_arrow=true, const unsigned int pattern=~0U) {
31513       return draw_quiver(flow,CImg<t2>(color,_spectrum,1,1,1,true),opacity,sampling,factor,is_arrow,pattern);
31514     }
31515 
31516     //! Draw a 2d vector field, using a field of colors.
31517     /**
31518        \param flow Image of 2d vectors used as input data.
31519        \param color Image of spectrum()-D vectors corresponding to the color of each arrow.
31520        \param opacity Opacity of the drawing.
31521        \param sampling Length (in pixels) between each arrow.
31522        \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length).
31523        \param is_arrow Tells if arrows must be drawn, instead of oriented segments.
31524        \param pattern Used pattern to draw lines.
31525        \note Clipping is supported.
31526     **/
31527     template<typename t1, typename t2>
31528     CImg<T>& draw_quiver(const CImg<t1>& flow,
31529                          const CImg<t2>& color, const float opacity=1,
31530                          const unsigned int sampling=25, const float factor=-20,
31531                          const bool is_arrow=true, const unsigned int pattern=~0U) {
31532       if (is_empty()) return *this;
31533       if (!flow || flow._spectrum!=2)
31534         throw CImgArgumentException(_cimg_instance
31535                                     "draw_quiver() : Invalid dimensions of specified flow (%u,%u,%u,%u,%p).",
31536                                     cimg_instance,
31537                                     flow._width,flow._height,flow._depth,flow._spectrum,flow._data);
31538       if (sampling<=0)
31539         throw CImgArgumentException(_cimg_instance
31540                                     "draw_quiver() : Invalid sampling value %g "
31541                                     "(should be >0)",
31542                                     cimg_instance,
31543                                     sampling);
31544       const bool colorfield = (color._width==flow._width && color._height==flow._height && color._depth==1 && color._spectrum==_spectrum);
31545       if (is_overlapped(flow)) return draw_quiver(+flow,color,opacity,sampling,factor,is_arrow,pattern);
31546       float vmax,fact;
31547       if (factor<=0) {
31548         float m, M = (float)flow.get_norm(2).max_min(m);
31549         vmax = (float)cimg::max(cimg::abs(m),cimg::abs(M));
31550         if (!vmax) vmax = 1;
31551         fact = -factor;
31552       } else { fact = factor; vmax = 1; }
31553 
31554       for (unsigned int y = sampling/2; y<_height; y+=sampling)
31555         for (unsigned int x = sampling/2; x<_width; x+=sampling) {
31556           const unsigned int X = x*flow._width/_width, Y = y*flow._height/_height;
31557           float u = (float)flow(X,Y,0,0)*fact/vmax, v = (float)flow(X,Y,0,1)*fact/vmax;
31558           if (is_arrow) {
31559             const int xx = x+(int)u, yy = y+(int)v;
31560             if (colorfield) draw_arrow(x,y,xx,yy,color.get_vector_at(X,Y)._data,opacity,45,sampling/5.0f,pattern);
31561             else draw_arrow(x,y,xx,yy,color._data,opacity,45,sampling/5.0f,pattern);
31562           } else {
31563             if (colorfield) draw_line((int)(x-0.5*u),(int)(y-0.5*v),(int)(x+0.5*u),(int)(y+0.5*v),color.get_vector_at(X,Y)._data,opacity,pattern);
31564             else draw_line((int)(x-0.5*u),(int)(y-0.5*v),(int)(x+0.5*u),(int)(y+0.5*v),color._data,opacity,pattern);
31565           }
31566         }
31567 
31568       return *this;
31569     }
31570 
31571     //! Draw a labeled horizontal axis.
31572     /**
31573        \param values_x Values along the horizontal axis.
31574        \param y Y-coordinate of the horizontal axis in the image instance.
31575        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
31576        \param opacity Drawing opacity.
31577        \param pattern Drawing pattern.
31578        \param font_height Height of the labels (exact match for 13,24,32,57, interpolated otherwise).
31579        \param allow_zero Enable/disable the drawing of label '0' if found.
31580     **/
31581     template<typename t, typename tc>
31582     CImg<T>& draw_axis(const CImg<t>& values_x, const int y,
31583                        const tc *const color, const float opacity=1,
31584                        const unsigned int pattern=~0U, const unsigned int font_height=13,
31585                        const bool allow_zero=true) {
31586       if (is_empty()) return *this;
31587       const int yt = (y+3+font_height)<_height?(y+3):(y-2-font_height);
31588       const int siz = (int)values_x.size()-1;
31589       char txt[32] = { 0 };
31590       CImg<T> label;
31591       if (siz<=0) { // Degenerated case.
31592         draw_line(0,y,_width-1,y,color,opacity,pattern);
31593         if (!siz) {
31594           cimg_snprintf(txt,sizeof(txt),"%g",(double)*values_x);
31595           label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
31596           const int
31597             _xt = (width() - label.width())/2,
31598             xt = _xt<3?3:_xt+label.width()>=width()-2?width()-3-label.width():_xt;
31599           draw_point(width()/2,y-1,color,opacity).draw_point(width()/2,y+1,color,opacity);
31600           if (allow_zero || txt[0]!='0' || txt[1]!=0)
31601             draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
31602         }
31603       } else { // Regular case.
31604         if (values_x[0]<values_x[siz]) draw_arrow(0,y,_width-1,y,color,opacity,30,5,pattern);
31605         else draw_arrow(_width-1,y,0,y,color,opacity,30,5,pattern);
31606         cimg_foroff(values_x,x) {
31607           cimg_snprintf(txt,sizeof(txt),"%g",(double)values_x(x));
31608           label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
31609           const int
31610             xi = (int)(x*(_width-1)/siz),
31611             _xt = xi - label.width()/2,
31612             xt = _xt<3?3:_xt+label.width()>=width()-2?width()-3-label.width():_xt;
31613           draw_point(xi,y-1,color,opacity).draw_point(xi,y+1,color,opacity);
31614           if (allow_zero || txt[0]!='0' || txt[1]!=0)
31615             draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
31616         }
31617       }
31618       return *this;
31619     }
31620 
31621     //! Draw a labeled vertical axis.
31622     /**
31623        \param x X-coordinate of the vertical axis in the image instance.
31624        \param values_y Values along the Y-axis.
31625        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
31626        \param opacity Drawing opacity.
31627        \param pattern Drawing pattern.
31628        \param font_height Height of the labels (exact match for 13,24,32,57, interpolated otherwise).
31629        \param allow_zero Enable/disable the drawing of label '0' if found.
31630     **/
31631     template<typename t, typename tc>
31632     CImg<T>& draw_axis(const int x, const CImg<t>& values_y,
31633                        const tc *const color, const float opacity=1,
31634                        const unsigned int pattern=~0U, const unsigned int font_height=13,
31635                        const bool allow_zero=true) {
31636       if (is_empty()) return *this;
31637       int siz = (int)values_y.size()-1;
31638       char txt[32] = { 0 };
31639       CImg<T> label;
31640       if (siz<=0) { // Degenerated case.
31641         draw_line(x,0,x,_height-1,color,opacity,pattern);
31642         if (!siz) {
31643           cimg_snprintf(txt,sizeof(txt),"%g",(double)*values_y);
31644           label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
31645           const int
31646             _yt = (height() - label.height())/2,
31647             yt = _yt<0?0:_yt+label.height()>=height()?height()-1-label.height():_yt,
31648             _xt = x - 2 - label.width(),
31649             xt = _xt>=0?_xt:x+3;
31650           draw_point(x-1,height()/2,color,opacity).draw_point(x+1,height()/2,color,opacity);
31651           if (allow_zero || txt[0]!='0' || txt[1]!=0)
31652             draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
31653         }
31654       } else { // Regular case.
31655         if (values_y[0]<values_y[siz]) draw_arrow(x,0,x,_height-1,color,opacity,30,5,pattern);
31656         else draw_arrow(x,_height-1,x,0,color,opacity,30,5,pattern);
31657         cimg_foroff(values_y,y) {
31658           cimg_snprintf(txt,sizeof(txt),"%g",(double)values_y(y));
31659           label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
31660           const int
31661             yi = (int)(y*(_height-1)/siz),
31662             _yt = yi - label.height()/2,
31663             yt = _yt<0?0:_yt+label.height()>=height()?height()-1-label.height():_yt,
31664             _xt = x - 2 - label.width(),
31665             xt = _xt>=0?_xt:x+3;
31666           draw_point(x-1,yi,color,opacity).draw_point(x+1,yi,color,opacity);
31667           if (allow_zero || txt[0]!='0' || txt[1]!=0)
31668             draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
31669         }
31670       }
31671       return *this;
31672     }
31673 
31674     //! Draw labeled horizontal and vertical axes.
31675     /**
31676        \param values_x Values along the X-axis.
31677        \param values_y Values along the Y-axis.
31678        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
31679        \param opacity Drawing opacity.
31680        \param pattern_x Drawing pattern for the X-axis.
31681        \param pattern_y Drawing pattern for the Y-axis.
31682        \param font_height Height of the labels (exact match for 13,24,32,57, interpolated otherwise).
31683        \param allow_zero Enable/disable the drawing of label '0' if found.
31684     **/
31685     template<typename tx, typename ty, typename tc>
31686     CImg<T>& draw_axes(const CImg<tx>& values_x, const CImg<ty>& values_y,
31687                        const tc *const color, const float opacity=1,
31688                        const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U,
31689                        const unsigned int font_height=13, const bool allow_zero=true) {
31690       if (is_empty()) return *this;
31691       const CImg<tx> nvalues_x(values_x._data,values_x.size(),1,1,1,true);
31692       const int sizx = (int)values_x.size()-1, wm1 = width()-1;
31693       if (sizx>=0) {
31694         float ox = (float)*nvalues_x;
31695         for (unsigned int x = sizx?1:0; x<_width; ++x) {
31696           const float nx = (float)nvalues_x._linear_atX((float)x*sizx/wm1);
31697           if (nx*ox<=0) { draw_axis(nx==0?x:x-1,values_y,color,opacity,pattern_y,font_height,allow_zero); break; }
31698           ox = nx;
31699         }
31700       }
31701       const CImg<ty> nvalues_y(values_y._data,values_y.size(),1,1,1,true);
31702       const int sizy = (int)values_y.size()-1, hm1 = height()-1;
31703       if (sizy>0) {
31704         float oy = (float)nvalues_y[0];
31705         for (unsigned int y = sizy?1:0; y<_height; ++y) {
31706           const float ny = (float)nvalues_y._linear_atX((float)y*sizy/hm1);
31707           if (ny*oy<=0) { draw_axis(values_x,ny==0?y:y-1,color,opacity,pattern_x,font_height,allow_zero); break; }
31708           oy = ny;
31709         }
31710       }
31711       return *this;
31712     }
31713 
31714     //! Draw labeled horizontal and vertical axes \overloading.
31715     template<typename tc>
31716     CImg<T>& draw_axes(const float x0, const float x1, const float y0, const float y1,
31717                        const tc *const color, const float opacity=1,
31718                        const int subdivisionx=-60, const int subdivisiony=-60,
31719                        const float precisionx=0, const float precisiony=0,
31720                        const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U,
31721                        const unsigned int font_height=13) {
31722       if (is_empty()) return *this;
31723       const bool allow_zero = (x0*x1>0) || (y0*y1>0);
31724       const float
31725         dx = cimg::abs(x1-x0), dy = cimg::abs(y1-y0),
31726         px = dx<=0?1:precisionx==0?(float)std::pow(10.0,(int)std::log10(dx)-2.0):precisionx,
31727         py = dy<=0?1:precisiony==0?(float)std::pow(10.0,(int)std::log10(dy)-2.0):precisiony;
31728       if (x0!=x1 && y0!=y1)
31729         draw_axes(CImg<floatT>::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1).round(px),
31730                   CImg<floatT>::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1).round(py),
31731                   color,opacity,pattern_x,pattern_y,font_height,allow_zero);
31732       else if (x0==x1 && y0!=y1)
31733         draw_axis((int)x0,CImg<floatT>::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1).round(py),
31734                   color,opacity,pattern_y,font_height);
31735       else if (x0!=x1 && y0==y1)
31736         draw_axis(CImg<floatT>::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1).round(px),(int)y0,
31737                   color,opacity,pattern_x,font_height);
31738       return *this;
31739     }
31740 
31741     //! Draw 2d grid.
31742     /**
31743        \param values_x X-coordinates of the vertical lines.
31744        \param values_y Y-coordinates of the horizontal lines.
31745        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
31746        \param opacity Drawing opacity.
31747        \param pattern_x Drawing pattern for vertical lines.
31748        \param pattern_y Drawing pattern for horizontal lines.
31749     **/
31750     template<typename tx, typename ty, typename tc>
31751     CImg<T>& draw_grid(const CImg<tx>& values_x, const CImg<ty>& values_y,
31752                        const tc *const color, const float opacity=1,
31753                        const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) {
31754       if (is_empty()) return *this;
31755       if (values_x) cimg_foroff(values_x,x) {
31756           const int xi = (int)values_x[x];
31757           if (xi>=0 && xi<width()) draw_line(xi,0,xi,_height-1,color,opacity,pattern_x);
31758         }
31759       if (values_y) cimg_foroff(values_y,y) {
31760           const int yi = (int)values_y[y];
31761           if (yi>=0 && yi<height()) draw_line(0,yi,_width-1,yi,color,opacity,pattern_y);
31762         }
31763       return *this;
31764     }
31765 
31766     //! Draw 2d grid \simplification.
31767     template<typename tc>
31768     CImg<T>& draw_grid(const float delta_x,  const float delta_y,
31769                        const float offsetx, const float offsety,
31770                        const bool invertx, const bool inverty,
31771                        const tc *const color, const float opacity=1,
31772                        const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) {
31773       if (is_empty()) return *this;
31774       CImg<uintT> seqx, seqy;
31775       if (delta_x!=0) {
31776         const float dx = delta_x>0?delta_x:_width*-delta_x/100;
31777         const unsigned int nx = (unsigned int)(_width/dx);
31778         seqx = CImg<uintT>::sequence(1+nx,0,(unsigned int)(dx*nx));
31779         if (offsetx) cimg_foroff(seqx,x) seqx(x) = (unsigned int)cimg::mod(seqx(x)+offsetx,(float)_width);
31780         if (invertx) cimg_foroff(seqx,x) seqx(x) = _width - 1 - seqx(x);
31781       }
31782       if (delta_y!=0) {
31783         const float dy = delta_y>0?delta_y:_height*-delta_y/100;
31784         const unsigned int ny = (unsigned int)(_height/dy);
31785         seqy = CImg<uintT>::sequence(1+ny,0,(unsigned int)(dy*ny));
31786         if (offsety) cimg_foroff(seqy,y) seqy(y) = (unsigned int)cimg::mod(seqy(y)+offsety,(float)_height);
31787         if (inverty) cimg_foroff(seqy,y) seqy(y) = _height - 1 - seqy(y);
31788      }
31789       return draw_grid(seqx,seqy,color,opacity,pattern_x,pattern_y);
31790     }
31791 
31792     //! Draw 1d graph.
31793     /**
31794        \param data Image containing the graph values I = f(x).
31795        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
31796        \param opacity Drawing opacity.
31797 
31798        \param plot_type Define the type of the plot :
31799                       - 0 = No plot.
31800                       - 1 = Plot using segments.
31801                       - 2 = Plot using cubic splines.
31802                       - 3 = Plot with bars.
31803        \param vertex_type Define the type of points :
31804                       - 0 = No points.
31805                       - 1 = Point.
31806                       - 2 = Straight cross.
31807                       - 3 = Diagonal cross.
31808                       - 4 = Filled circle.
31809                       - 5 = Outlined circle.
31810                       - 6 = Square.
31811                       - 7 = Diamond.
31812        \param ymin Lower bound of the y-range.
31813        \param ymax Upper bound of the y-range.
31814        \param pattern Drawing pattern.
31815        \note
31816          - if \c ymin==ymax==0, the y-range is computed automatically from the input samples.
31817     **/
31818     template<typename t, typename tc>
31819     CImg<T>& draw_graph(const CImg<t>& data,
31820                         const tc *const color, const float opacity=1,
31821                         const unsigned int plot_type=1, const int vertex_type=1,
31822                         const double ymin=0, const double ymax=0, const unsigned int pattern=~0U) {
31823       if (is_empty() || _height<=1) return *this;
31824       if (!color)
31825         throw CImgArgumentException(_cimg_instance
31826                                     "draw_graph() : Specified color is (null).",
31827                                     cimg_instance);
31828 
31829       // Create shaded colors for displaying bar plots.
31830       CImg<tc> color1, color2;
31831       if (plot_type==3) {
31832         color1.assign(_spectrum); color2.assign(_spectrum);
31833         cimg_forC(*this,c) { color1[c] = (tc)cimg::min((float)cimg::type<tc>::max(),color[c]*1.2f); color2[c] = (tc)(color[c]*0.4f); }
31834       }
31835 
31836       // Compute min/max and normalization factors.
31837       const unsigned long
31838         siz = data.size(),
31839         _siz1 = siz - (plot_type!=3?1:0),
31840         siz1 = _siz1?_siz1:1;
31841       const unsigned int
31842         _width1 = _width - (plot_type!=3?1:0),
31843         width1 = _width1?_width1:1;
31844       double m = ymin, M = ymax;
31845       if (ymin==ymax) m = (double)data.max_min(M);
31846       if (m==M) { --m; ++M; }
31847       const float ca = (float)(M-m)/(_height-1);
31848       bool init_hatch = true;
31849 
31850       // Draw graph edges
31851       switch (plot_type%4) {
31852       case 1 : { // Segments
31853         int oX = 0, oY = (int)((data[0]-m)/ca);
31854         if (siz==1) {
31855           const int Y = (int)((*data-m)/ca);
31856           draw_line(0,Y,width()-1,Y,color,opacity,pattern);
31857         } else for (unsigned long off = 1; off<siz; ++off) {
31858             const int
31859               X = off*_width1/siz1,
31860               Y = (int)((data[off]-m)/ca);
31861             draw_line(oX,oY,X,Y,color,opacity,pattern,init_hatch);
31862             oX = X; oY = Y;
31863             init_hatch = false;
31864           }
31865       } break;
31866       case 2 : { // Spline
31867         const CImg<t> ndata(data._data,siz,1,1,1,true);
31868         int oY = (int)((data[0]-m)/ca);
31869         cimg_forX(*this,x) {
31870           const int Y = (int)((ndata._cubic_atX((float)x*siz1/width1)-m)/ca);
31871           if (x>0) draw_line(x,oY,x+1,Y,color,opacity,pattern,init_hatch);
31872           init_hatch = false;
31873           oY = Y;
31874         }
31875       } break;
31876       case 3 : { // Bars
31877         const int Y0 = (int)(-m/ca);
31878         int oX = 0;
31879         cimg_foroff(data,off) {
31880           const int
31881             X = (off+1)*_width/siz-1,
31882             Y = (int)((data[off]-m)/ca);
31883           draw_rectangle(oX,Y0,X,Y,color,opacity).
31884             draw_line(oX,Y,oX,Y0,color2.data(),opacity).
31885             draw_line(oX,Y0,X,Y0,Y<=Y0?color2.data():color1.data(),opacity).
31886             draw_line(X,Y,X,Y0,color1.data(),opacity).
31887             draw_line(oX,Y,X,Y,Y<=Y0?color1.data():color2.data(),opacity);
31888           oX = X+1;
31889         }
31890       } break;
31891       default : break; // No edges
31892       }
31893 
31894       // Draw graph points
31895       const unsigned int wb2 = plot_type==3?_width1/(2*siz):0;
31896       switch (vertex_type%8) {
31897       case 1 : { // Point
31898         cimg_foroff(data,off) {
31899           const int
31900             X = off*_width1/siz1 + wb2,
31901             Y = (int)((data[off]-m)/ca);
31902           draw_point(X,Y,color,opacity);
31903         }
31904       } break;
31905       case 2 : { // Straight Cross
31906         cimg_foroff(data,off) {
31907           const int
31908             X = off*_width1/siz1 + wb2,
31909             Y = (int)((data[off]-m)/ca);
31910           draw_line(X-3,Y,X+3,Y,color,opacity).draw_line(X,Y-3,X,Y+3,color,opacity);
31911         }
31912       } break;
31913       case 3 : { // Diagonal Cross
31914         cimg_foroff(data,off) {
31915           const int
31916             X = off*_width1/siz1 + wb2,
31917             Y = (int)((data[off]-m)/ca);
31918           draw_line(X-3,Y-3,X+3,Y+3,color,opacity).draw_line(X-3,Y+3,X+3,Y-3,color,opacity);
31919         }
31920       } break;
31921       case 4 : { // Filled Circle
31922         cimg_foroff(data,off) {
31923           const int
31924             X = off*_width1/siz1 + wb2,
31925             Y = (int)((data[off]-m)/ca);
31926           draw_circle(X,Y,3,color,opacity);
31927         }
31928       } break;
31929       case 5 : { // Outlined circle
31930         cimg_foroff(data,off) {
31931           const int
31932             X = off*_width1/siz1 + wb2,
31933             Y = (int)((data[off]-m)/ca);
31934           draw_circle(X,Y,3,color,opacity,0U);
31935         }
31936       } break;
31937       case 6 : { // Square
31938         cimg_foroff(data,off) {
31939           const int
31940             X = off*_width1/siz1 + wb2,
31941             Y = (int)((data[off]-m)/ca);
31942           draw_rectangle(X-3,Y-3,X+3,Y+3,color,opacity,~0U);
31943         }
31944       } break;
31945       case 7 : { // Diamond
31946         cimg_foroff(data,off) {
31947           const int
31948             X = off*_width1/siz1 + wb2,
31949             Y = (int)((data[off]-m)/ca);
31950           draw_line(X,Y-4,X+4,Y,color,opacity).
31951             draw_line(X+4,Y,X,Y+4,color,opacity).
31952             draw_line(X,Y+4,X-4,Y,color,opacity).
31953             draw_line(X-4,Y,X,Y-4,color,opacity);
31954         }
31955       } break;
31956       default : break; // No points
31957       }
31958       return *this;
31959     }
31960 
31961     //! Draw filled 3d region with the flood fill algorithm.
31962     /**
31963        \param x X-coordinate of the starting point of the region to fill.
31964        \param y Y-coordinate of the starting point of the region to fill.
31965        \param z Z-coordinate of the starting point of the region to fill.
31966        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
31967        \param[out] region Image that will contain the mask of the filled region mask, as an output.
31968        \param sigma Tolerance concerning neighborhood values.
31969        \param opacity Opacity of the drawing.
31970        \param is_high_connexity Tells if 8-connexity must be used (only for 2d images).
31971        \return \c region is initialized with the binary mask of the filled region.
31972     **/
31973     template<typename tc, typename t>
31974     CImg<T>& draw_fill(const int x, const int y, const int z,
31975                        const tc *const color, const float opacity,
31976                        CImg<t>& region, const float sigma=0,
31977                        const bool is_high_connexity=false) {
31978 
31979 #define _cimg_draw_fill_test(x,y,z,res) if (region(x,y,z)) res = false; else { \
31980   res = true; \
31981   const T *reference_col = reference_color._data + _spectrum, *ptrs = data(x,y,z) + siz; \
31982   for (unsigned int i = _spectrum; res && i; --i) { ptrs-=whd; res = (cimg::abs(*ptrs - *(--reference_col))<=sigma); } \
31983   region(x,y,z) = (t)(res?1:noregion); \
31984 }
31985 
31986 #define _cimg_draw_fill_set(x,y,z) { \
31987   const tc *col = color; \
31988   T *ptrd = data(x,y,z); \
31989   if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; } \
31990   else cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; } \
31991 }
31992 
31993 #define _cimg_draw_fill_insert(x,y,z) { \
31994   if (posr1>=remaining._height) remaining.resize(3,remaining._height<<1,1,1,0); \
31995   unsigned int *ptrr = remaining.data(0,posr1); \
31996   *(ptrr++) = x; *(ptrr++) = y; *(ptrr++) = z; ++posr1; \
31997 }
31998 
31999 #define _cimg_draw_fill_test_neighbor(x,y,z,cond) if (cond) { \
32000   const unsigned int tx = x, ty = y, tz = z; \
32001   _cimg_draw_fill_test(tx,ty,tz,res); if (res) _cimg_draw_fill_insert(tx,ty,tz); \
32002 }
32003 
32004       if (!color)
32005         throw CImgArgumentException(_cimg_instance
32006                                     "draw_fill() : Specified color is (null).",
32007                                     cimg_instance);
32008 
32009       region.assign(_width,_height,_depth,1,(t)0);
32010       if (x>=0 && x<width() && y>=0 && y<height() && z>=0 && z<depth()) {
32011         const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
32012         const unsigned long whd = (unsigned long)_width*_height*_depth, siz = (unsigned long)_spectrum*whd;
32013         const unsigned int W1 = _width-1, H1 = _height-1, D1 = _depth-1;
32014         const bool is_3d = (_depth>1);
32015         const CImg<T> reference_color = get_vector_at(x,y,z);
32016         CImg<uintT> remaining(3,512,1,1,0);
32017         remaining(0,0) = x; remaining(1,0) = y; remaining(2,0) = z;
32018         unsigned int posr0 = 0, posr1 = 1;
32019         region(x,y,z) = (t)1;
32020         const t noregion = ((t)1==(t)2)?(t)0:(t)(-1);
32021         if (is_3d) do { // 3d version of the filling algorithm
32022           const unsigned int *pcurr = remaining.data(0,posr0++), xc = *(pcurr++), yc = *(pcurr++), zc = *(pcurr++);
32023           if (posr0>=512) { remaining.shift(0,-(int)posr0); posr1-=posr0; posr0 = 0; }
32024           bool cont, res;
32025           unsigned int nxc = xc;
32026           do { // X-backward
32027             _cimg_draw_fill_set(nxc,yc,zc);
32028             _cimg_draw_fill_test_neighbor(nxc,yc-1,zc,yc!=0);
32029             _cimg_draw_fill_test_neighbor(nxc,yc+1,zc,yc<H1);
32030             _cimg_draw_fill_test_neighbor(nxc,yc,zc-1,zc!=0);
32031             _cimg_draw_fill_test_neighbor(nxc,yc,zc+1,zc<D1);
32032             if (nxc) { --nxc; _cimg_draw_fill_test(nxc,yc,zc,cont); } else cont = false;
32033           } while (cont);
32034           nxc = xc;
32035           do { // X-forward
32036             if ((++nxc)<=W1) { _cimg_draw_fill_test(nxc,yc,zc,cont); } else cont = false;
32037             if (cont) {
32038               _cimg_draw_fill_set(nxc,yc,zc);
32039               _cimg_draw_fill_test_neighbor(nxc,yc-1,zc,yc!=0);
32040               _cimg_draw_fill_test_neighbor(nxc,yc+1,zc,yc<H1);
32041               _cimg_draw_fill_test_neighbor(nxc,yc,zc-1,zc!=0);
32042               _cimg_draw_fill_test_neighbor(nxc,yc,zc+1,zc<D1);
32043             }
32044           } while (cont);
32045           unsigned int nyc = yc;
32046           do { // Y-backward
32047             if (nyc) { --nyc; _cimg_draw_fill_test(xc,nyc,zc,cont); } else cont = false;
32048             if (cont) {
32049               _cimg_draw_fill_set(xc,nyc,zc);
32050               _cimg_draw_fill_test_neighbor(xc-1,nyc,zc,xc!=0);
32051               _cimg_draw_fill_test_neighbor(xc+1,nyc,zc,xc<W1);
32052               _cimg_draw_fill_test_neighbor(xc,nyc,zc-1,zc!=0);
32053               _cimg_draw_fill_test_neighbor(xc,nyc,zc+1,zc<D1);
32054             }
32055           } while (cont);
32056           nyc = yc;
32057           do { // Y-forward
32058             if ((++nyc)<=H1) { _cimg_draw_fill_test(xc,nyc,zc,cont); } else cont = false;
32059             if (cont) {
32060               _cimg_draw_fill_set(xc,nyc,zc);
32061               _cimg_draw_fill_test_neighbor(xc-1,nyc,zc,xc!=0);
32062               _cimg_draw_fill_test_neighbor(xc+1,nyc,zc,xc<W1);
32063               _cimg_draw_fill_test_neighbor(xc,nyc,zc-1,zc!=0);
32064               _cimg_draw_fill_test_neighbor(xc,nyc,zc+1,zc<D1);
32065             }
32066           } while (cont);
32067           unsigned int nzc = zc;
32068           do { // Z-backward
32069             if (nzc) { --nzc; _cimg_draw_fill_test(xc,yc,nzc,cont); } else cont = false;
32070             if (cont) {
32071               _cimg_draw_fill_set(xc,yc,nzc);
32072               _cimg_draw_fill_test_neighbor(xc-1,yc,nzc,xc!=0);
32073               _cimg_draw_fill_test_neighbor(xc+1,yc,nzc,xc<W1);
32074               _cimg_draw_fill_test_neighbor(xc,yc-1,nzc,yc!=0);
32075               _cimg_draw_fill_test_neighbor(xc,yc+1,nzc,yc<H1);
32076             }
32077           } while (cont);
32078           nzc = zc;
32079           do { // Z-forward
32080             if ((++nzc)<=D1) { _cimg_draw_fill_test(xc,yc,nzc,cont); } else cont = false;
32081             if (cont) {
32082               _cimg_draw_fill_set(xc,nyc,zc);
32083               _cimg_draw_fill_test_neighbor(xc-1,yc,nzc,xc!=0);
32084               _cimg_draw_fill_test_neighbor(xc+1,yc,nzc,xc<W1);
32085               _cimg_draw_fill_test_neighbor(xc,yc-1,nzc,yc!=0);
32086               _cimg_draw_fill_test_neighbor(xc,yc+1,nzc,yc<H1);
32087             }
32088           } while (cont);
32089         } while (posr1>posr0);
32090         else do { // 2d version of the filling algorithm
32091           const unsigned int *pcurr = remaining.data(0,posr0++), xc = *(pcurr++), yc = *(pcurr++);
32092           if (posr0>=512) { remaining.shift(0,-(int)posr0); posr1-=posr0; posr0 = 0; }
32093           bool cont, res;
32094           unsigned int nxc = xc;
32095           do { // X-backward
32096             _cimg_draw_fill_set(nxc,yc,0);
32097             _cimg_draw_fill_test_neighbor(nxc,yc-1,0,yc!=0);
32098             _cimg_draw_fill_test_neighbor(nxc,yc+1,0,yc<H1);
32099             if (is_high_connexity) {
32100               _cimg_draw_fill_test_neighbor(nxc-1,yc-1,0,(nxc!=0 && yc!=0));
32101               _cimg_draw_fill_test_neighbor(nxc+1,yc-1,0,(nxc<W1 && yc!=0));
32102               _cimg_draw_fill_test_neighbor(nxc-1,yc+1,0,(nxc!=0 && yc<H1));
32103               _cimg_draw_fill_test_neighbor(nxc+1,yc+1,0,(nxc<W1 && yc<H1));
32104             }
32105             if (nxc) { --nxc; _cimg_draw_fill_test(nxc,yc,0,cont); } else cont = false;
32106           } while (cont);
32107           nxc = xc;
32108           do { // X-forward
32109             if ((++nxc)<=W1) { _cimg_draw_fill_test(nxc,yc,0,cont); } else cont = false;
32110             if (cont) {
32111               _cimg_draw_fill_set(nxc,yc,0);
32112               _cimg_draw_fill_test_neighbor(nxc,yc-1,0,yc!=0);
32113               _cimg_draw_fill_test_neighbor(nxc,yc+1,0,yc<H1);
32114               if (is_high_connexity) {
32115                 _cimg_draw_fill_test_neighbor(nxc-1,yc-1,0,(nxc!=0 && yc!=0));
32116                 _cimg_draw_fill_test_neighbor(nxc+1,yc-1,0,(nxc<W1 && yc!=0));
32117                 _cimg_draw_fill_test_neighbor(nxc-1,yc+1,0,(nxc!=0 && yc<H1));
32118                 _cimg_draw_fill_test_neighbor(nxc+1,yc+1,0,(nxc<W1 && yc<H1));
32119               }
32120             }
32121           } while (cont);
32122           unsigned int nyc = yc;
32123           do { // Y-backward
32124             if (nyc) { --nyc; _cimg_draw_fill_test(xc,nyc,0,cont); } else cont = false;
32125             if (cont) {
32126               _cimg_draw_fill_set(xc,nyc,0);
32127               _cimg_draw_fill_test_neighbor(xc-1,nyc,0,xc!=0);
32128               _cimg_draw_fill_test_neighbor(xc+1,nyc,0,xc<W1);
32129               if (is_high_connexity) {
32130                 _cimg_draw_fill_test_neighbor(xc-1,nyc-1,0,(xc!=0 && nyc!=0));
32131                 _cimg_draw_fill_test_neighbor(xc+1,nyc-1,0,(xc<W1 && nyc!=0));
32132                 _cimg_draw_fill_test_neighbor(xc-1,nyc+1,0,(xc!=0 && nyc<H1));
32133                 _cimg_draw_fill_test_neighbor(xc+1,nyc+1,0,(xc<W1 && nyc<H1));
32134               }
32135             }
32136           } while (cont);
32137           nyc = yc;
32138           do { // Y-forward
32139             if ((++nyc)<=H1) { _cimg_draw_fill_test(xc,nyc,0,cont); } else cont = false;
32140             if (cont) {
32141               _cimg_draw_fill_set(xc,nyc,0);
32142               _cimg_draw_fill_test_neighbor(xc-1,nyc,0,xc!=0);
32143               _cimg_draw_fill_test_neighbor(xc+1,nyc,0,xc<W1);
32144               if (is_high_connexity) {
32145                 _cimg_draw_fill_test_neighbor(xc-1,nyc-1,0,(xc!=0 && nyc!=0));
32146                 _cimg_draw_fill_test_neighbor(xc+1,nyc-1,0,(xc<W1 && nyc!=0));
32147                 _cimg_draw_fill_test_neighbor(xc-1,nyc+1,0,(xc!=0 && nyc<H1));
32148                 _cimg_draw_fill_test_neighbor(xc+1,nyc+1,0,(xc<W1 && nyc<H1));
32149               }
32150             }
32151           } while (cont);
32152         } while (posr1>posr0);
32153         if (noregion) cimg_for(region,ptrd,t) if (*ptrd==noregion) *ptrd = (t)0;
32154       }
32155       return *this;
32156     }
32157 
32158     //! Draw filled 3d region with the flood fill algorithm \simplification.
32159     template<typename tc>
32160     CImg<T>& draw_fill(const int x, const int y, const int z,
32161                        const tc *const color, const float opacity=1,
32162                        const float sigma=0, const bool is_high_connexity=false) {
32163       CImg<boolT> tmp;
32164       return draw_fill(x,y,z,color,opacity,tmp,sigma,is_high_connexity);
32165     }
32166 
32167     //! Draw filled 2d region with the flood fill algorithm \simplification.
32168     template<typename tc>
32169     CImg<T>& draw_fill(const int x, const int y,
32170                        const tc *const color, const float opacity=1,
32171                        const float sigma=0, const bool is_high_connexity=false) {
32172       CImg<boolT> tmp;
32173       return draw_fill(x,y,0,color,opacity,tmp,sigma,is_high_connexity);
32174     }
32175 
32176     //! Draw a random plasma texture.
32177     /**
32178        \param alpha Alpha-parameter.
32179        \param beta Beta-parameter.
32180        \param scale Scale-parameter.
32181        \note Use the mid-point algorithm to render.
32182     **/
32183     CImg<T>& draw_plasma(const float alpha=1, const float beta=0, const unsigned int scale=8) {
32184       if (is_empty()) return *this;
32185       const int w = width(), h = height();
32186       const Tfloat m = (Tfloat)cimg::type<T>::min(), M = (Tfloat)cimg::type<T>::max();
32187       cimg_forZC(*this,z,c) {
32188         CImg<T> ref = get_shared_slice(z,c);
32189         for (int d=1<<cimg::min(scale,31U); d>1; d>>=1) {
32190           const int d2 = d>>1;
32191           const float r = alpha*d + beta;
32192           for (int y0=0; y0<h; y0+=d)
32193             for (int x0=0; x0<w; x0+=d) {
32194               const int x1 = (x0 + d)%w, y1 = (y0 + d)%h, xc = (x0 + d2)%w, yc = (y0 + d2)%h;
32195               const Tfloat val = (Tfloat)(0.25f*(ref(x0,y0) + ref(x0,y1) + ref(x0,y1) + ref(x1,y1)) + r*cimg::crand());
32196               ref(xc,yc) = (T)(val<m?m:val>M?M:val);
32197             }
32198           for (int y=-d2; y<h; y+=d)
32199             for (int x0=0; x0<w; x0+=d) {
32200               const int y0 = cimg::mod(y,h), x1 = (x0 + d)%w, y1 = (y + d)%h, xc = (x0 + d2)%w, yc = (y + d2)%h;
32201               const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) + r*cimg::crand());
32202               ref(xc,yc) = (T)(val<m?m:val>M?M:val);
32203             }
32204           for (int y0=0; y0<h; y0+=d)
32205             for (int x=-d2; x<w; x+=d) {
32206               const int x0 = cimg::mod(x,w), x1 = (x + d)%w, y1 = (y0 + d)%h, xc = (x + d2)%w, yc = (y0 + d2)%h;
32207               const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) + r*cimg::crand());
32208               ref(xc,yc) = (T)(val<m?m:val>M?M:val);
32209             }
32210           for (int y=-d2; y<h; y+=d)
32211             for (int x=-d2; x<w; x+=d) {
32212               const int x0 = cimg::mod(x,w), y0 = cimg::mod(y,h), x1 = (x + d)%w, y1 = (y + d)%h, xc = (x + d2)%w, yc = (y + d2)%h;
32213               const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) + r*cimg::crand());
32214               ref(xc,yc) = (T)(val<m?m:val>M?M:val);
32215             }
32216         }
32217       }
32218       return *this;
32219     }
32220 
32221     //! Draw a quadratic Mandelbrot or Julia 2d fractal.
32222     /**
32223        \param x0 X-coordinate of the upper-left pixel.
32224        \param y0 Y-coordinate of the upper-left pixel.
32225        \param x1 X-coordinate of the lower-right pixel.
32226        \param y1 Y-coordinate of the lower-right pixel.
32227        \param colormap Colormap.
32228        \param opacity Drawing opacity.
32229        \param z0r Real part of the upper-left fractal vertex.
32230        \param z0i Imaginary part of the upper-left fractal vertex.
32231        \param z1r Real part of the lower-right fractal vertex.
32232        \param z1i Imaginary part of the lower-right fractal vertex.
32233        \param iteration_max Maximum number of iterations for each estimated point.
32234        \param is_normalized_iteration Tells if iterations are normalized.
32235        \param is_julia_set Tells if the Mandelbrot or Julia set is rendered.
32236        \param param_r Real part of the Julia set parameter.
32237        \param param_i Imaginary part of the Julia set parameter.
32238        \note Fractal rendering is done by the Escape Time Algorithm.
32239     **/
32240     template<typename tc>
32241     CImg<T>& draw_mandelbrot(const int x0, const int y0, const int x1, const int y1,
32242                              const CImg<tc>& colormap, const float opacity=1,
32243                              const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2,
32244                              const unsigned int iteration_max=255,
32245                              const bool is_normalized_iteration=false,
32246                              const bool is_julia_set=false,
32247                              const double param_r=0, const double param_i=0) {
32248       if (is_empty()) return *this;
32249       CImg<tc> palette;
32250       if (colormap) palette.assign(colormap._data,colormap.size()/colormap._spectrum,1,1,colormap._spectrum,true);
32251       if (palette && palette._spectrum!=_spectrum)
32252         throw CImgArgumentException(_cimg_instance
32253                                     "draw_mandelbrot() : Instance and specified colormap (%u,%u,%u,%u,%p) have incompatible dimensions.",
32254                                     cimg_instance,
32255                                     colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data);
32256 
32257       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0), ln2 = (float)std::log(2.0);
32258       unsigned int iteration = 0;
32259       cimg_for_inXY(*this,x0,y0,x1,y1,p,q) {
32260         const double x = z0r + p*(z1r-z0r)/_width, y = z0i + q*(z1i-z0i)/_height;
32261         double zr, zi, cr, ci;
32262         if (is_julia_set) { zr = x; zi = y; cr = param_r; ci = param_i; }
32263         else { zr = param_r; zi = param_i; cr = x; ci = y; }
32264         for (iteration=1; zr*zr + zi*zi<=4 && iteration<=iteration_max; ++iteration) {
32265           const double temp = zr*zr - zi*zi + cr;
32266           zi = 2*zr*zi + ci;
32267           zr = temp;
32268         }
32269         if (iteration>iteration_max) {
32270           if (palette) {
32271             if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette(0,c);
32272             else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(0,c)*nopacity + (*this)(p,q,0,c)*copacity);
32273           } else {
32274             if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)0;
32275             else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)((*this)(p,q,0,c)*copacity);
32276           }
32277         } else if (is_normalized_iteration) {
32278           const float
32279             normz = (float)cimg::abs(zr*zr+zi*zi),
32280             niteration = (float)(iteration + 1 - std::log(std::log(normz))/ln2);
32281           if (palette) {
32282             if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._linear_atX(niteration,c);
32283             else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette._linear_atX(niteration,c)*nopacity + (*this)(p,q,0,c)*copacity);
32284           } else {
32285             if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)niteration;
32286             else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(niteration*nopacity + (*this)(p,q,0,c)*copacity);
32287           }
32288         } else {
32289           if (palette) {
32290             if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._atX(iteration,c);
32291             else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(iteration,c)*nopacity + (*this)(p,q,0,c)*copacity);
32292           } else {
32293             if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)iteration;
32294             else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(iteration*nopacity + (*this)(p,q,0,c)*copacity);
32295           }
32296         }
32297       }
32298       return *this;
32299     }
32300 
32301     //! Draw a quadratic Mandelbrot or Julia 2d fractal \overloading.
32302     template<typename tc>
32303     CImg<T>& draw_mandelbrot(const CImg<tc>& colormap, const float opacity=1,
32304                              const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2,
32305                              const unsigned int iteration_max=255,
32306                              const bool is_normalized_iteration=false,
32307                              const bool is_julia_set=false,
32308                              const double param_r=0, const double param_i=0) {
32309       return draw_mandelbrot(0,0,_width-1,_height-1,colormap,opacity,
32310                              z0r,z0i,z1r,z1i,iteration_max,is_normalized_iteration,is_julia_set,param_r,param_i);
32311     }
32312 
32313     //! Draw a 1d gaussian function.
32314     /**
32315        \param xc X-coordinate of the gaussian center.
32316        \param sigma Standard variation of the gaussian distribution.
32317        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
32318        \param opacity Drawing opacity.
32319     **/
32320     template<typename tc>
32321     CImg<T>& draw_gaussian(const float xc, const float sigma,
32322                            const tc *const color, const float opacity=1) {
32323       if (is_empty()) return *this;
32324       if (!color)
32325         throw CImgArgumentException(_cimg_instance
32326                                     "draw_gaussian() : Specified color is (null).",
32327                                     cimg_instance);
32328       const float sigma2 = 2*sigma*sigma, nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
32329       const unsigned long whd = (unsigned long)_width*_height*_depth;
32330       const tc *col = color;
32331       cimg_forX(*this,x) {
32332         const float dx = (x - xc), val = (float)std::exp(-dx*dx/sigma2);
32333         T *ptrd = data(x,0,0,0);
32334         if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
32335         else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
32336         col-=_spectrum;
32337       }
32338       return *this;
32339     }
32340 
32341     //! Draw a 2d gaussian function.
32342     /**
32343        \param xc X-coordinate of the gaussian center.
32344        \param yc Y-coordinate of the gaussian center.
32345        \param tensor Covariance matrix (must be 2x2).
32346        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
32347        \param opacity Drawing opacity.
32348     **/
32349     template<typename t, typename tc>
32350     CImg<T>& draw_gaussian(const float xc, const float yc, const CImg<t>& tensor,
32351                            const tc *const color, const float opacity=1) {
32352       if (is_empty()) return *this;
32353       if (tensor._width!=2 || tensor._height!=2 || tensor._depth!=1 || tensor._spectrum!=1)
32354         throw CImgArgumentException(_cimg_instance
32355                                     "draw_gaussian() : Specified tensor (%u,%u,%u,%u,%p) is not a 2x2 matrix.",
32356                                     cimg_instance,
32357                                     tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data);
32358       if (!color)
32359         throw CImgArgumentException(_cimg_instance
32360                                     "draw_gaussian() : Specified color is (null).",
32361                                     cimg_instance);
32362       typedef typename CImg<t>::Tfloat tfloat;
32363       const CImg<tfloat> invT = tensor.get_invert(), invT2 = (invT*invT)/(-2.0);
32364       const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = invT2(1,1);
32365       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
32366       const unsigned long whd = (unsigned long)_width*_height*_depth;
32367       const tc *col = color;
32368       float dy = -yc;
32369       cimg_forY(*this,y) {
32370         float dx = -xc;
32371         cimg_forX(*this,x) {
32372           const float val = (float)std::exp(a*dx*dx + b*dx*dy + c*dy*dy);
32373           T *ptrd = data(x,y,0,0);
32374           if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
32375           else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
32376           col-=_spectrum;
32377           ++dx;
32378         }
32379         ++dy;
32380       }
32381       return *this;
32382     }
32383 
32384     //! Draw a 2d gaussian function \overloading.
32385     template<typename tc>
32386     CImg<T>& draw_gaussian(const int xc, const int yc, const float r1, const float r2, const float ru, const float rv,
32387                            const tc *const color, const float opacity=1) {
32388       const double
32389         a = r1*ru*ru + r2*rv*rv,
32390         b = (r1-r2)*ru*rv,
32391         c = r1*rv*rv + r2*ru*ru;
32392       const CImg<Tfloat> tensor(2,2,1,1, a,b,b,c);
32393       return draw_gaussian(xc,yc,tensor,color,opacity);
32394     }
32395 
32396     //! Draw a 2d gaussian function \overloading.
32397     template<typename tc>
32398     CImg<T>& draw_gaussian(const float xc, const float yc, const float sigma,
32399                            const tc *const color, const float opacity=1) {
32400       return draw_gaussian(xc,yc,CImg<floatT>::diagonal(sigma,sigma),color,opacity);
32401     }
32402 
32403     //! Draw a 3d gaussian function \overloading.
32404     template<typename t, typename tc>
32405     CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const CImg<t>& tensor,
32406                            const tc *const color, const float opacity=1) {
32407       if (is_empty()) return *this;
32408       typedef typename CImg<t>::Tfloat tfloat;
32409       if (tensor._width!=3 || tensor._height!=3 || tensor._depth!=1 || tensor._spectrum!=1)
32410         throw CImgArgumentException(_cimg_instance
32411                                     "draw_gaussian() : Specified tensor (%u,%u,%u,%u,%p) is not a 3x3 matrix.",
32412                                     cimg_instance,
32413                                     tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data);
32414 
32415       const CImg<tfloat> invT = tensor.get_invert(), invT2 = (invT*invT)/(-2.0);
32416       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);
32417       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
32418       const unsigned long whd = (unsigned long)_width*_height*_depth;
32419       const tc *col = color;
32420       cimg_forXYZ(*this,x,y,z) {
32421         const float
32422           dx = (x - xc), dy = (y - yc), dz = (z - zc),
32423           val = (float)std::exp(a*dx*dx + b*dx*dy + c*dx*dz + d*dy*dy + e*dy*dz + f*dz*dz);
32424         T *ptrd = data(x,y,z,0);
32425         if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
32426         else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
32427         col-=_spectrum;
32428       }
32429       return *this;
32430     }
32431 
32432     //! Draw a 3d gaussian function \overloading.
32433     template<typename tc>
32434     CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const float sigma,
32435                            const tc *const color, const float opacity=1) {
32436       return draw_gaussian(xc,yc,zc,CImg<floatT>::diagonal(sigma,sigma,sigma),color,opacity);
32437     }
32438 
32439     //! Draw a 3d object.
32440     /**
32441        \param x0 X-coordinate of the 3d object position
32442        \param y0 Y-coordinate of the 3d object position
32443        \param z0 Z-coordinate of the 3d object position
32444        \param vertices Image Nx3 describing 3d point coordinates
32445        \param primitives List of P primitives
32446        \param colors List of P color (or textures)
32447        \param opacities Image or list of P opacities
32448        \param render_type d Render type (0=Points, 1=Lines, 2=Faces (no light), 3=Faces (flat), 4=Faces(Gouraud)
32449        \param is_double_sided Tells if object faces have two sides or are oriented.
32450        \param focale length of the focale (0 for parallel projection)
32451        \param lightx X-coordinate of the light
32452        \param lighty Y-coordinate of the light
32453        \param lightz Z-coordinate of the light
32454        \param specular_light Amount of specular light.
32455        \param specular_shine Shininess of the object
32456     **/
32457     template<typename tp, typename tf, typename tc, typename to>
32458     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
32459                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
32460                            const CImgList<tc>& colors, const CImg<to>& opacities,
32461                            const unsigned int render_type=4,
32462                            const bool is_double_sided=false, const float focale=500,
32463                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
32464                            const float specular_light=0.2f, const float specular_shine=0.1f) {
32465       return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type,is_double_sided,focale,lightx,lighty,lightz,
32466                            specular_light,specular_shine,CImg<floatT>::empty());
32467     }
32468 
32469     //! Draw a 3d object \simplification.
32470     template<typename tp, typename tf, typename tc, typename to, typename tz>
32471     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
32472                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
32473                            const CImgList<tc>& colors, const CImg<to>& opacities,
32474                            const unsigned int render_type,
32475                            const bool is_double_sided, const float focale,
32476                            const float lightx, const float lighty, const float lightz,
32477                            const float specular_light, const float specular_shine,
32478                            CImg<tz>& zbuffer) {
32479       return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
32480                             render_type,is_double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,1);
32481     }
32482 
32483 #ifdef cimg_use_board
32484     template<typename tp, typename tf, typename tc, typename to>
32485     CImg<T>& draw_object3d(LibBoard::Board& board,
32486                            const float x0, const float y0, const float z0,
32487                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
32488                            const CImgList<tc>& colors, const CImg<to>& opacities,
32489                            const unsigned int render_type=4,
32490                            const bool is_double_sided=false, const float focale=500,
32491                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
32492                            const float specular_light=0.2f, const float specular_shine=0.1f) {
32493       return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type,is_double_sided,focale,lightx,lighty,lightz,
32494                            specular_light,specular_shine,CImg<floatT>::empty());
32495     }
32496 
32497     template<typename tp, typename tf, typename tc, typename to, typename tz>
32498     CImg<T>& draw_object3d(LibBoard::Board& board,
32499                            const float x0, const float y0, const float z0,
32500                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
32501                            const CImgList<tc>& colors, const CImg<to>& opacities,
32502                            const unsigned int render_type,
32503                            const bool is_double_sided, const float focale,
32504                            const float lightx, const float lighty, const float lightz,
32505                            const float specular_light, const float specular_shine,
32506                            CImg<tz>& zbuffer) {
32507       return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
32508                             render_type,is_double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,1);
32509     }
32510 #endif
32511 
32512     //! Draw a 3d object \simplification.
32513     template<typename tp, typename tf, typename tc, typename to>
32514     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
32515                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
32516                            const CImgList<tc>& colors, const CImgList<to>& opacities,
32517                            const unsigned int render_type=4,
32518                            const bool is_double_sided=false, const float focale=500,
32519                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
32520                            const float specular_light=0.2f, const float specular_shine=0.1f) {
32521       return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type,is_double_sided,focale,lightx,lighty,lightz,
32522                            specular_light,specular_shine,CImg<floatT>::empty());
32523     }
32524 
32525     //! Draw a 3d object \simplification.
32526     template<typename tp, typename tf, typename tc, typename to, typename tz>
32527     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
32528                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
32529                            const CImgList<tc>& colors, const CImgList<to>& opacities,
32530                            const unsigned int render_type,
32531                            const bool is_double_sided, const float focale,
32532                            const float lightx, const float lighty, const float lightz,
32533                            const float specular_light, const float specular_shine,
32534                            CImg<tz>& zbuffer) {
32535       return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
32536                             render_type,is_double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,1);
32537     }
32538 
32539 #ifdef cimg_use_board
32540     template<typename tp, typename tf, typename tc, typename to>
32541     CImg<T>& draw_object3d(LibBoard::Board& board,
32542                            const float x0, const float y0, const float z0,
32543                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
32544                            const CImgList<tc>& colors, const CImgList<to>& opacities,
32545                            const unsigned int render_type=4,
32546                            const bool is_double_sided=false, const float focale=500,
32547                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
32548                            const float specular_light=0.2f, const float specular_shine=0.1f) {
32549       return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type,is_double_sided,focale,lightx,lighty,lightz,
32550                            specular_light,specular_shine,CImg<floatT>::empty());
32551     }
32552 
32553     template<typename tp, typename tf, typename tc, typename to, typename tz>
32554     CImg<T>& draw_object3d(LibBoard::Board& board,
32555                            const float x0, const float y0, const float z0,
32556                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
32557                            const CImgList<tc>& colors, const CImgList<to>& opacities,
32558                            const unsigned int render_type,
32559                            const bool is_double_sided, const float focale,
32560                            const float lightx, const float lighty, const float lightz,
32561                            const float specular_light, const float specular_shine,
32562                            CImg<tz>& zbuffer) {
32563       return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
32564                             render_type,is_double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,1);
32565     }
32566 #endif
32567 
32568     //! Draw a 3d object \simplification.
32569     template<typename tp, typename tf, typename tc>
32570     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
32571                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
32572                            const CImgList<tc>& colors,
32573                            const unsigned int render_type=4,
32574                            const bool is_double_sided=false, const float focale=500,
32575                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
32576                            const float specular_light=0.2f, const float specular_shine=0.1f) {
32577       return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::empty(),
32578                            render_type,is_double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,CImg<floatT>::empty());
32579     }
32580 
32581     //! Draw a 3d object \simplification.
32582     template<typename tp, typename tf, typename tc, typename tz>
32583     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
32584                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
32585                            const CImgList<tc>& colors,
32586                            const unsigned int render_type,
32587                            const bool is_double_sided, const float focale,
32588                            const float lightx, const float lighty, const float lightz,
32589                            const float specular_light, const float specular_shine,
32590                            CImg<tz>& zbuffer) {
32591       return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::empty(),
32592                            render_type,is_double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,zbuffer);
32593     }
32594 
32595 #ifdef cimg_use_board
32596     template<typename tp, typename tf, typename tc, typename to>
32597     CImg<T>& draw_object3d(LibBoard::Board& board,
32598                            const float x0, const float y0, const float z0,
32599                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
32600                            const CImgList<tc>& colors,
32601                            const unsigned int render_type=4,
32602                            const bool is_double_sided=false, const float focale=500,
32603                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
32604                            const float specular_light=0.2f, const float specular_shine=0.1f) {
32605       return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::empty(),
32606                            render_type,is_double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,CImg<floatT>::empty());
32607     }
32608 
32609     template<typename tp, typename tf, typename tc, typename to, typename tz>
32610     CImg<T>& draw_object3d(LibBoard::Board& board,
32611                            const float x0, const float y0, const float z0,
32612                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
32613                            const CImgList<tc>& colors,
32614                            const unsigned int render_type,
32615                            const bool is_double_sided, const float focale,
32616                            const float lightx, const float lighty, const float lightz,
32617                            const float specular_light, const float specular_shine,
32618                            CImg<tz>& zbuffer) {
32619       return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::empty(),
32620                            render_type,is_double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,zbuffer);
32621     }
32622 #endif
32623 
32624     template<typename t>
32625     unsigned int ___draw_object3d(const CImgList<t>& opacities, const unsigned int n_primitive) const {
32626       return (n_primitive>=opacities._width || opacities[n_primitive].is_empty())?1:opacities[n_primitive].size();
32627     }
32628 
32629     template<typename t>
32630     unsigned int ___draw_object3d(const CImg<t>&, const unsigned int) const {
32631       return 1;
32632     }
32633 
32634     template<typename tc, typename to>
32635     void __draw_object3d(const unsigned int n_primitive, const CImgList<to>& opacities, const CImg<tc>& color,
32636                          const int nx0, const int ny0, const CImg<T>& sprite, const float opac, const float factor) {
32637       if (n_primitive<opacities._width && opacities[n_primitive]) {
32638         const CImg<to>& opacity = opacities[n_primitive];
32639         if (opacity.size()==1) draw_image(nx0,ny0,sprite,opac);
32640         else if (opacity.is_sameXY(sprite)) draw_image(nx0,ny0,sprite,opacity);
32641         else if (opacity.is_sameXY(color)) draw_image(nx0,ny0,sprite,opacity.get_resize(sprite._width,sprite._height));
32642         else {
32643           const unsigned int
32644             W = cimg::max(sprite._width,(unsigned int)(opacity._width*factor)),
32645             H = cimg::max(sprite._height,(unsigned int)(opacity._height*factor));
32646           const CImg<T> __sprite = (sprite._width==W && sprite._height==H)?CImg<T>():sprite.get_resize(W,H), &_sprite = __sprite?__sprite:sprite;
32647           const CImg<to> __opacity = (opacity._width==W && opacity._height==H)?CImg<to>():opacity.get_resize(W,H), &_opacity = __opacity?__opacity:opacity;
32648           draw_image(nx0,ny0,_sprite,_opacity);
32649         }
32650       } else draw_image(nx0,ny0,sprite,opac);
32651     }
32652 
32653     template<typename tc, typename to>
32654     void __draw_object3d(const unsigned int, const CImg<to>&, const CImg<tc>&,
32655                          const int nx0, const int ny0, const CImg<T>& sprite, const float opac, const float) {
32656       draw_image(nx0,ny0,sprite,opac);
32657     }
32658 
32659     template<typename tz, typename tp, typename tf, typename tc, typename to>
32660     CImg<T>& _draw_object3d(void *const pboard, CImg<tz>& zbuffer,
32661                             const float X, const float Y, const float Z,
32662                             const CImg<tp>& vertices,
32663                             const CImgList<tf>& primitives,
32664                             const CImgList<tc>& colors,
32665                             const to& opacities,
32666                             const unsigned int render_type,
32667                             const bool is_double_sided, const float focale,
32668                             const float lightx, const float lighty, const float lightz,
32669                             const float specular_light, const float specular_shine,
32670                             const float sprite_scale) {
32671       typedef typename cimg::superset2<tp,tz,float>::type tpfloat;
32672       if (is_empty() || !vertices || !primitives) return *this;
32673       char error_message[1024] = { 0 };
32674       if (!vertices.is_object3d(primitives,colors,opacities,false,error_message))
32675         throw CImgArgumentException(_cimg_instance
32676                                     "draw_object3d() : Invalid specified 3d object (%u,%u) (%s).",
32677                                     cimg_instance,vertices._width,primitives._width,error_message);
32678 #ifndef cimg_use_board
32679       if (pboard) return *this;
32680 #endif
32681       const float
32682         nspec = 1 - (specular_light<0.0f?0.0f:(specular_light>1.0f?1.0f:specular_light)),
32683         nspec2 = 1 + (specular_shine<0.0f?0.0f:specular_shine),
32684         nsl1 = (nspec2 - 1)/cimg::sqr(nspec - 1),
32685         nsl2 = 1 - 2*nsl1*nspec,
32686         nsl3 = nspec2 - nsl1 - nsl2;
32687 
32688       // Create light texture for phong-like rendering.
32689       CImg<floatT> light_texture;
32690       if (render_type==5) {
32691         if (colors._width>primitives._width) {
32692           static CImg<floatT> default_light_texture;
32693           static const tc *lptr = 0;
32694           static tc ref_values[64] = { 0 };
32695           const CImg<tc>& img = colors.back();
32696           bool is_same_texture = (lptr==img._data);
32697           if (is_same_texture)
32698             for (unsigned int r = 0, j = 0; j<8; ++j)
32699               for (unsigned int i = 0; i<8; ++i)
32700                 if (ref_values[r++]!=img(i*img._width/9,j*img._height/9,0,(i+j)%img._spectrum)) { is_same_texture = false; break; }
32701           if (!is_same_texture || default_light_texture._spectrum<_spectrum) {
32702             (default_light_texture.assign(img,false)/=255).resize(-100,-100,1,_spectrum);
32703             lptr = colors.back().data();
32704             for (unsigned int r = 0, j = 0; j<8; ++j)
32705               for (unsigned int i = 0; i<8; ++i)
32706                 ref_values[r++] = img(i*img._width/9,j*img._height/9,0,(i+j)%img._spectrum);
32707           }
32708           light_texture.assign(default_light_texture,true);
32709         } else {
32710           static CImg<floatT> default_light_texture;
32711           static float olightx = 0, olighty = 0, olightz = 0, ospecular_shine = 0;
32712           if (!default_light_texture ||
32713               lightx!=olightx || lighty!=olighty || lightz!=olightz ||
32714               specular_shine!=ospecular_shine || default_light_texture._spectrum<_spectrum) {
32715             default_light_texture.assign(512,512);
32716             const float
32717               dlx = lightx - X,
32718               dly = lighty - Y,
32719               dlz = lightz - Z,
32720               nl = (float)std::sqrt(dlx*dlx + dly*dly + dlz*dlz),
32721               nlx = default_light_texture._width/2*(1 + dlx/nl),
32722               nly = default_light_texture._height/2*(1 + dly/nl),
32723               white[] = { 1 };
32724             default_light_texture.draw_gaussian(nlx,nly,default_light_texture._width/3.0f,white);
32725             cimg_forXY(default_light_texture,x,y) {
32726               const float factor = default_light_texture(x,y);
32727               if (factor>nspec) default_light_texture(x,y) = cimg::min(2,nsl1*factor*factor + nsl2*factor + nsl3);
32728             }
32729             default_light_texture.resize(-100,-100,1,_spectrum);
32730             olightx = lightx; olighty = lighty; olightz = lightz; ospecular_shine = specular_shine;
32731           }
32732           light_texture.assign(default_light_texture,true);
32733         }
32734       }
32735 
32736       // Compute 3d to 2d projection.
32737       CImg<tpfloat> projections(vertices._width,2);
32738       tpfloat parallzmin = cimg::type<tpfloat>::max();
32739       const float
32740         absfocale = focale?cimg::abs(focale):0,
32741         _focale = absfocale?absfocale:(1-parallzmin);
32742       if (absfocale) cimg_forX(projections,l) { // Perspective projection
32743           const tpfloat
32744             x = (tpfloat)vertices(l,0),
32745             y = (tpfloat)vertices(l,1),
32746             z = (tpfloat)vertices(l,2);
32747           const tpfloat projectedz = z + Z + absfocale;
32748           projections(l,1) = Y + absfocale*y/projectedz;
32749           projections(l,0) = X + absfocale*x/projectedz;
32750         } else cimg_forX(projections,l) { // Parallel projection
32751           const tpfloat
32752             x = (tpfloat)vertices(l,0),
32753             y = (tpfloat)vertices(l,1),
32754             z = (tpfloat)vertices(l,2);
32755           if (z<parallzmin) parallzmin = z;
32756           projections(l,1) = Y + y;
32757           projections(l,0) = X + x;
32758         }
32759 
32760       // Compute and sort visible primitives.
32761       CImg<uintT> visibles(primitives._width);
32762       CImg<tpfloat> zrange(primitives._width);
32763       unsigned int nb_visibles = 0;
32764       const tpfloat zmin = absfocale?(tpfloat)(1.5f - absfocale):cimg::type<tpfloat>::min();
32765       cimglist_for(primitives,l) {
32766         const CImg<tf>& primitive = primitives[l];
32767         switch (primitive.size()) {
32768         case 1 : { // Point
32769           const unsigned int i0 = (unsigned int)primitive(0);
32770           const tpfloat z0 = Z + vertices(i0,2);
32771           if (z0>zmin) {
32772             visibles(nb_visibles) = (unsigned int)l;
32773             zrange(nb_visibles++) = z0;
32774           }
32775         } break;
32776         case 5 : { // Sphere
32777           const unsigned int
32778             i0 = (unsigned int)primitive(0),
32779             i1 = (unsigned int)primitive(1);
32780           const tpfloat
32781             Xc = 0.5f*((float)vertices(i0,0) + (float)vertices(i1,0)),
32782             Yc = 0.5f*((float)vertices(i0,1) + (float)vertices(i1,1)),
32783             Zc = 0.5f*((float)vertices(i0,2) + (float)vertices(i1,2)),
32784             _zc = Z + Zc,
32785             zc = _zc + _focale,
32786             xc = X + Xc*(absfocale?absfocale/zc:1),
32787             yc = Y + Yc*(absfocale?absfocale/zc:1),
32788             radius = 0.5f*std::sqrt(cimg::sqr(vertices(i1,0) - vertices(i0,0)) +
32789                                     cimg::sqr(vertices(i1,1) - vertices(i0,1)) +
32790                                     cimg::sqr(vertices(i1,2) - vertices(i0,2)))*(absfocale?absfocale/zc:1),
32791             xm = xc - radius,
32792             ym = yc - radius,
32793             xM = xc + radius,
32794             yM = yc + radius;
32795           if (xM>=0 && xm<_width && yM>=0 && ym<_height && _zc>zmin) {
32796             visibles(nb_visibles) = (unsigned int)l;
32797             zrange(nb_visibles++) = _zc;
32798           }
32799         } break;
32800         case 2 : // Segment
32801         case 6 : {
32802           const unsigned int
32803             i0 = (unsigned int)primitive(0),
32804             i1 = (unsigned int)primitive(1);
32805           const tpfloat
32806             x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
32807             x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2);
32808           tpfloat xm, xM, ym, yM;
32809           if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
32810           if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
32811           if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin) {
32812             visibles(nb_visibles) = (unsigned int)l;
32813             zrange(nb_visibles++) = (z0 + z1)/2;
32814           }
32815         } break;
32816         case 3 :  // Triangle
32817         case 9 : {
32818           const unsigned int
32819             i0 = (unsigned int)primitive(0),
32820             i1 = (unsigned int)primitive(1),
32821             i2 = (unsigned int)primitive(2);
32822           const tpfloat
32823             x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
32824             x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2),
32825             x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2);
32826           tpfloat xm, xM, ym, yM;
32827           if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
32828           if (x2<xm) xm = x2;
32829           if (x2>xM) xM = x2;
32830           if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
32831           if (y2<ym) ym = y2;
32832           if (y2>yM) yM = y2;
32833           if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin) {
32834             const tpfloat d = (x1-x0)*(y2-y0) - (x2-x0)*(y1-y0);
32835             if (is_double_sided || d<0) {
32836               visibles(nb_visibles) = (unsigned int)l;
32837               zrange(nb_visibles++) = (z0 + z1 + z2)/3;
32838             }
32839           }
32840         } break;
32841         case 4 : // Rectangle
32842         case 12 : {
32843           const unsigned int
32844             i0 = (unsigned int)primitive(0),
32845             i1 = (unsigned int)primitive(1),
32846             i2 = (unsigned int)primitive(2),
32847             i3 = (unsigned int)primitive(3);
32848           const tpfloat
32849             x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
32850             x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2),
32851             x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2),
32852             x3 = projections(i3,0), y3 = projections(i3,1), z3 = Z + vertices(i3,2);
32853           tpfloat xm, xM, ym, yM;
32854           if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
32855           if (x2<xm) xm = x2;
32856           if (x2>xM) xM = x2;
32857           if (x3<xm) xm = x3;
32858           if (x3>xM) xM = x3;
32859           if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
32860           if (y2<ym) ym = y2;
32861           if (y2>yM) yM = y2;
32862           if (y3<ym) ym = y3;
32863           if (y3>yM) yM = y3;
32864           if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin) {
32865             const float d = (x1 - x0)*(y2 - y0) - (x2 - x0)*(y1 - y0);
32866             if (is_double_sided || d<0) {
32867               visibles(nb_visibles) = (unsigned int)l;
32868               zrange(nb_visibles++) = (z0 + z1 + z2 + z3)/4;
32869             }
32870           }
32871         } break;
32872         default :
32873           throw CImgArgumentException(_cimg_instance
32874                                       "draw_object3d() : Invalid primitive[%u] with size %u "
32875                                       "(should have size 1,2,3,4,5,6,9 or 12).",
32876                                       cimg_instance,
32877                                       l,primitive.size());
32878         }
32879       }
32880       if (nb_visibles<=0) return *this;
32881       CImg<uintT> permutations;
32882       CImg<tpfloat>(zrange._data,nb_visibles,1,1,1,true).sort(permutations,false);
32883 
32884       // Compute light properties
32885       CImg<floatT> lightprops;
32886       switch (render_type) {
32887       case 3 : { // Flat Shading
32888         lightprops.assign(nb_visibles);
32889         cimg_forX(lightprops,l) {
32890           const CImg<tf>& primitive = primitives(visibles(permutations(l)));
32891           const unsigned int psize = primitive.size();
32892           if (psize==3 || psize==4 || psize==9 || psize==12) {
32893             const unsigned int
32894               i0 = (unsigned int)primitive(0),
32895               i1 = (unsigned int)primitive(1),
32896               i2 = (unsigned int)primitive(2);
32897             const tpfloat
32898               x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2),
32899               x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2),
32900               x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2),
32901               dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0,
32902               dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0,
32903               nx = dy1*dz2 - dz1*dy2,
32904               ny = dz1*dx2 - dx1*dz2,
32905               nz = dx1*dy2 - dy1*dx2,
32906               norm = (tpfloat)std::sqrt(1e-5f + nx*nx + ny*ny + nz*nz),
32907               lx = X + (x0 + x1 + x2)/3 - lightx,
32908               ly = Y + (y0 + y1 + y2)/3 - lighty,
32909               lz = Z + (z0 + z1 + z2)/3 - lightz,
32910               nl = (tpfloat)std::sqrt(1e-5f + lx*lx + ly*ly + lz*lz),
32911               factor = cimg::max(cimg::abs(-lx*nx-ly*ny-lz*nz)/(norm*nl),0);
32912             lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3);
32913           } else lightprops[l] = 1;
32914         }
32915       } break;
32916 
32917       case 4 : // Gouraud Shading
32918       case 5 : { // Phong-Shading
32919         CImg<tpfloat> vertices_normals(vertices._width,3,1,1,0);
32920         for (unsigned int l = 0; l<nb_visibles; ++l) {
32921           const CImg<tf>& primitive = primitives[visibles(l)];
32922           const unsigned int psize = primitive.size();
32923           const bool
32924             triangle_flag = (psize==3) || (psize==9),
32925             rectangle_flag = (psize==4) || (psize==12);
32926           if (triangle_flag || rectangle_flag) {
32927             const unsigned int
32928               i0 = (unsigned int)primitive(0),
32929               i1 = (unsigned int)primitive(1),
32930               i2 = (unsigned int)primitive(2),
32931               i3 = rectangle_flag?(unsigned int)primitive(3):0;
32932             const tpfloat
32933               x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2),
32934               x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2),
32935               x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2),
32936               dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0,
32937               dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0,
32938               nnx = dy1*dz2 - dz1*dy2,
32939               nny = dz1*dx2 - dx1*dz2,
32940               nnz = dx1*dy2 - dy1*dx2,
32941               norm = (tpfloat)(1e-5f + std::sqrt(nnx*nnx + nny*nny + nnz*nnz)),
32942               nx = nnx/norm,
32943               ny = nny/norm,
32944               nz = nnz/norm;
32945             vertices_normals(i0,0)+=nx; vertices_normals(i0,1)+=ny; vertices_normals(i0,2)+=nz;
32946             vertices_normals(i1,0)+=nx; vertices_normals(i1,1)+=ny; vertices_normals(i1,2)+=nz;
32947             vertices_normals(i2,0)+=nx; vertices_normals(i2,1)+=ny; vertices_normals(i2,2)+=nz;
32948             if (rectangle_flag) { vertices_normals(i3,0)+=nx; vertices_normals(i3,1)+=ny; vertices_normals(i3,2)+=nz; }
32949           }
32950         }
32951 
32952         if (is_double_sided) cimg_forX(vertices_normals,p) if (vertices_normals(p,2)>0) {
32953           vertices_normals(p,0) = -vertices_normals(p,0);
32954           vertices_normals(p,1) = -vertices_normals(p,1);
32955           vertices_normals(p,2) = -vertices_normals(p,2);
32956         }
32957 
32958         if (render_type==4) {
32959           lightprops.assign(vertices._width);
32960           cimg_forX(lightprops,l) {
32961             const tpfloat
32962               nx = vertices_normals(l,0),
32963               ny = vertices_normals(l,1),
32964               nz = vertices_normals(l,2),
32965               norm = (tpfloat)std::sqrt(1e-5f + nx*nx + ny*ny + nz*nz),
32966               lx = X + vertices(l,0) - lightx,
32967               ly = Y + vertices(l,1) - lighty,
32968               lz = Z + vertices(l,2) - lightz,
32969               nl = (tpfloat)std::sqrt(1e-5f + lx*lx + ly*ly + lz*lz),
32970               factor = cimg::max((-lx*nx-ly*ny-lz*nz)/(norm*nl),0);
32971             lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3);
32972           }
32973         } else {
32974           const unsigned int
32975             lw2 = light_texture._width/2 - 1,
32976             lh2 = light_texture._height/2 - 1;
32977           lightprops.assign(vertices._width,2);
32978           cimg_forX(lightprops,l) {
32979             const tpfloat
32980               nx = vertices_normals(l,0),
32981               ny = vertices_normals(l,1),
32982               nz = vertices_normals(l,2),
32983               norm = (tpfloat)std::sqrt(1e-5f + nx*nx + ny*ny + nz*nz),
32984               nnx = nx/norm,
32985               nny = ny/norm;
32986             lightprops(l,0) = lw2*(1 + nnx);
32987             lightprops(l,1) = lh2*(1 + nny);
32988           }
32989         }
32990       } break;
32991       }
32992 
32993       // Draw visible primitives
32994       const CImg<tc> default_color(1,_spectrum,1,1,(tc)200);
32995       for (unsigned int l = 0; l<nb_visibles; ++l) {
32996         const unsigned int n_primitive = visibles(permutations(l));
32997         const CImg<tf>& primitive = primitives[n_primitive];
32998         const CImg<tc>
32999           &__color = n_primitive<colors._width?colors[n_primitive]:CImg<tc>(),
33000           _color = (__color && __color.size()!=_spectrum && __color._spectrum<_spectrum)?colors[n_primitive].get_resize(-100,-100,-100,_spectrum,0):CImg<tc>(),
33001           &color = _color?_color:(__color?__color:default_color);
33002         const tc *const pcolor = color._data;
33003         const unsigned int siz_opac = ___draw_object3d(opacities,n_primitive);
33004         const float opac = (n_primitive>=opacities._width || !siz_opac)?1.0f:opacities(n_primitive,0);
33005 
33006 #ifdef cimg_use_board
33007         LibBoard::Board &board = *(LibBoard::Board*)pboard;
33008 #endif
33009 
33010         switch (primitive.size()) {
33011         case 1 : { // Colored point or sprite
33012           const unsigned int n0 = (unsigned int)primitive[0];
33013           const int x0 = (int)projections(n0,0), y0 = (int)projections(n0,1);
33014           if (color.size()==_spectrum && siz_opac==1) { // Colored point.
33015             draw_point(x0,y0,pcolor,opac);
33016 #ifdef cimg_use_board
33017             if (pboard) {
33018               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
33019               board.fillCircle((float)x0,height()-(float)y0,0);
33020             }
33021 #endif
33022           } else { // Colored sprite.
33023             if (!__color)
33024               throw CImgArgumentException(_cimg_instance
33025                                           "draw_object3d() : Undefined texture for sprite primitive [%u].",
33026                                           cimg_instance,n_primitive);
33027             const tpfloat z = Z + vertices(n0,2);
33028             const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1);
33029             const unsigned int
33030               _sw = (unsigned int)(color._width*factor),
33031               _sh = (unsigned int)(color._height*factor),
33032               sw = _sw?_sw:1, sh = _sh?_sh:1;
33033             const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2;
33034             if (sw<_width && sh<_height && (nx0+(int)sw/2>=0 || nx0-(int)sw/2<width() || ny0+(int)sh/2>=0 || ny0-(int)sh/2<height())) {
33035               const CImg<tc>
33036                 _sprite = (sw!=color._width || sh!=color._height)?color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<tc>(),
33037                 &sprite = _sprite?_sprite:color;
33038               __draw_object3d(n_primitive,opacities,color,nx0,ny0,sprite,opac,factor);
33039 #ifdef cimg_use_board
33040               if (pboard) {
33041                 board.setPenColorRGBi(128,128,128);
33042                 board.setFillColor(LibBoard::Color::None);
33043                 board.drawRectangle((float)nx0,height()-(float)ny0,sw,sh);
33044               }
33045 #endif
33046             }
33047           }
33048         } break;
33049         case 2 : { // Colored line
33050           const unsigned int
33051             n0 = (unsigned int)primitive[0],
33052             n1 = (unsigned int)primitive[1];
33053           const int
33054             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
33055             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1);
33056           const float
33057             z0 = vertices(n0,2) + Z + _focale,
33058             z1 = vertices(n1,2) + Z + _focale;
33059           if (render_type) {
33060             if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opac);
33061             else draw_line(x0,y0,x1,y1,pcolor,opac);
33062 #ifdef cimg_use_board
33063             if (pboard) {
33064               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
33065               board.drawLine((float)x0,height()-(float)y0,x1,height()-(float)y1);
33066             }
33067 #endif
33068           } else {
33069             draw_point(x0,y0,pcolor,opac).draw_point(x1,y1,pcolor,opac);
33070 #ifdef cimg_use_board
33071             if (pboard) {
33072               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
33073               board.drawCircle((float)x0,height()-(float)y0,0);
33074               board.drawCircle((float)x1,height()-(float)y1,0);
33075             }
33076 #endif
33077           }
33078         } break;
33079         case 5 : { // Colored sphere
33080           const unsigned int
33081             n0 = (unsigned int)primitive[0],
33082             n1 = (unsigned int)primitive[1];
33083           const float
33084             Xc = 0.5f*((float)vertices(n0,0) + (float)vertices(n1,0)),
33085             Yc = 0.5f*((float)vertices(n0,1) + (float)vertices(n1,1)),
33086             Zc = 0.5f*((float)vertices(n0,2) + (float)vertices(n1,2)),
33087             zc = Z + Zc + _focale,
33088             xc = X + Xc*(absfocale?absfocale/zc:1),
33089             yc = Y + Yc*(absfocale?absfocale/zc:1),
33090             radius = 0.5f*std::sqrt(cimg::sqr(vertices(n1,0) - vertices(n0,0)) +
33091                                     cimg::sqr(vertices(n1,1) - vertices(n0,1)) +
33092                                     cimg::sqr(vertices(n1,2) - vertices(n0,2)))*(absfocale?absfocale/zc:1);
33093           switch (render_type) {
33094           case 0 :
33095             draw_point((int)xc,(int)yc,pcolor,opac);
33096 #ifdef cimg_use_board
33097             if (pboard) {
33098               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
33099               board.fillCircle(xc,height()-yc,0);
33100             }
33101 #endif
33102             break;
33103           case 1 :
33104             draw_circle((int)xc,(int)yc,(int)radius,pcolor,opac,~0U);
33105 #ifdef cimg_use_board
33106             if (pboard) {
33107               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
33108               board.setFillColor(LibBoard::Color::None);
33109               board.drawCircle(xc,height()-yc,radius);
33110             }
33111 #endif
33112             break;
33113           default :
33114             draw_circle((int)xc,(int)yc,(int)radius,pcolor,opac);
33115 #ifdef cimg_use_board
33116             if (pboard) {
33117               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
33118               board.fillCircle(xc,height()-yc,radius);
33119             }
33120 #endif
33121             break;
33122           }
33123         } break;
33124         case 6 : { // Textured line
33125           if (!__color)
33126             throw CImgArgumentException(_cimg_instance
33127                                         "draw_object3d() : Undefined texture for line primitive [%u].",
33128                                         cimg_instance,n_primitive);
33129           const unsigned int
33130             n0 = (unsigned int)primitive[0],
33131             n1 = (unsigned int)primitive[1],
33132             tx0 = (unsigned int)primitive[2],
33133             ty0 = (unsigned int)primitive[3],
33134             tx1 = (unsigned int)primitive[4],
33135             ty1 = (unsigned int)primitive[5];
33136           const int
33137             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
33138             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1);
33139           const float
33140             z0 = vertices(n0,2) + Z + _focale,
33141             z1 = vertices(n1,2) + Z + _focale;
33142           if (render_type) {
33143             if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac);
33144             else draw_line(x0,y0,x1,y1,color,tx0,ty0,tx1,ty1,opac);
33145 #ifdef cimg_use_board
33146             if (pboard) {
33147               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
33148               board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1);
33149             }
33150 #endif
33151           } else {
33152             draw_point(x0,y0,color.get_vector_at(tx0,ty0)._data,opac).
33153               draw_point(x1,y1,color.get_vector_at(tx1,ty1)._data,opac);
33154 #ifdef cimg_use_board
33155             if (pboard) {
33156               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
33157               board.drawCircle((float)x0,height()-(float)y0,0);
33158               board.drawCircle((float)x1,height()-(float)y1,0);
33159             }
33160 #endif
33161           }
33162         } break;
33163         case 3 : { // Colored triangle
33164           const unsigned int
33165             n0 = (unsigned int)primitive[0],
33166             n1 = (unsigned int)primitive[1],
33167             n2 = (unsigned int)primitive[2];
33168           const int
33169             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
33170             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
33171             x2 = (int)projections(n2,0), y2 = (int)projections(n2,1);
33172           const float
33173             z0 = vertices(n0,2) + Z + _focale,
33174             z1 = vertices(n1,2) + Z + _focale,
33175             z2 = vertices(n2,2) + Z + _focale;
33176           switch (render_type) {
33177           case 0 :
33178             draw_point(x0,y0,pcolor,opac).draw_point(x1,y1,pcolor,opac).draw_point(x2,y2,pcolor,opac);
33179 #ifdef cimg_use_board
33180             if (pboard) {
33181               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
33182               board.drawCircle((float)x0,height()-(float)y0,0);
33183               board.drawCircle((float)x1,height()-(float)y1,0);
33184               board.drawCircle((float)x2,height()-(float)y2,0);
33185             }
33186 #endif
33187             break;
33188           case 1 :
33189             if (zbuffer)
33190               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opac).draw_line(zbuffer,x0,y0,z0,x2,y2,z2,pcolor,opac).
33191                 draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opac);
33192             else
33193               draw_line(x0,y0,x1,y1,pcolor,opac).draw_line(x0,y0,x2,y2,pcolor,opac).
33194                 draw_line(x1,y1,x2,y2,pcolor,opac);
33195 #ifdef cimg_use_board
33196             if (pboard) {
33197               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
33198               board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1);
33199               board.drawLine((float)x0,height()-(float)y0,(float)x2,height()-(float)y2);
33200               board.drawLine((float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
33201             }
33202 #endif
33203             break;
33204           case 2 :
33205             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opac);
33206             else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opac);
33207 #ifdef cimg_use_board
33208             if (pboard) {
33209               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
33210               board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
33211             }
33212 #endif
33213             break;
33214           case 3 :
33215             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opac,lightprops(l));
33216             else _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opac,lightprops(l));
33217 #ifdef cimg_use_board
33218             if (pboard) {
33219               const float lp = cimg::min(lightprops(l),1);
33220               board.setPenColorRGBi((unsigned char)(color[0]*lp),
33221                                      (unsigned char)(color[1]*lp),
33222                                      (unsigned char)(color[2]*lp),
33223                                      (unsigned char)(opac*255));
33224               board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
33225             }
33226 #endif
33227             break;
33228           case 4 :
33229             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,lightprops(n0),lightprops(n1),lightprops(n2),opac);
33230             else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprops(n0),lightprops(n1),lightprops(n2),opac);
33231 #ifdef cimg_use_board
33232             if (pboard) {
33233               board.setPenColorRGBi((unsigned char)(color[0]),
33234                                      (unsigned char)(color[1]),
33235                                      (unsigned char)(color[2]),
33236                                      (unsigned char)(opac*255));
33237               board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprops(n0),
33238                                          (float)x1,height()-(float)y1,lightprops(n1),
33239                                          (float)x2,height()-(float)y2,lightprops(n2));
33240             }
33241 #endif
33242             break;
33243           case 5 : {
33244             const unsigned int
33245               lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1),
33246               lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1),
33247               lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1);
33248             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac);
33249             else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac);
33250 #ifdef cimg_use_board
33251             if (pboard) {
33252               const float
33253                 l0 = light_texture((int)(light_texture.width()/2*(1+lightprops(n0,0))), (int)(light_texture.height()/2*(1+lightprops(n0,1)))),
33254                 l1 = light_texture((int)(light_texture.width()/2*(1+lightprops(n1,0))), (int)(light_texture.height()/2*(1+lightprops(n1,1)))),
33255                 l2 = light_texture((int)(light_texture.width()/2*(1+lightprops(n2,0))), (int)(light_texture.height()/2*(1+lightprops(n2,1))));
33256               board.setPenColorRGBi((unsigned char)(color[0]),
33257                                      (unsigned char)(color[1]),
33258                                      (unsigned char)(color[2]),
33259                                      (unsigned char)(opac*255));
33260               board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,
33261                                          (float)x1,height()-(float)y1,l1,
33262                                          (float)x2,height()-(float)y2,l2);
33263             }
33264 #endif
33265           } break;
33266           }
33267         } break;
33268         case 4 : { // Colored rectangle
33269           const unsigned int
33270             n0 = (unsigned int)primitive[0],
33271             n1 = (unsigned int)primitive[1],
33272             n2 = (unsigned int)primitive[2],
33273             n3 = (unsigned int)primitive[3];
33274           const int
33275             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
33276             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
33277             x2 = (int)projections(n2,0), y2 = (int)projections(n2,1),
33278             x3 = (int)projections(n3,0), y3 = (int)projections(n3,1);
33279           const float
33280             z0 = vertices(n0,2) + Z + _focale,
33281             z1 = vertices(n1,2) + Z + _focale,
33282             z2 = vertices(n2,2) + Z + _focale,
33283             z3 = vertices(n3,2) + Z + _focale;
33284           switch (render_type) {
33285           case 0 :
33286             draw_point(x0,y0,pcolor,opac).draw_point(x1,y1,pcolor,opac).
33287               draw_point(x2,y2,pcolor,opac).draw_point(x3,y3,pcolor,opac);
33288 #ifdef cimg_use_board
33289             if (pboard) {
33290               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
33291               board.drawCircle((float)x0,height()-(float)y0,0);
33292               board.drawCircle((float)x1,height()-(float)y1,0);
33293               board.drawCircle((float)x2,height()-(float)y2,0);
33294               board.drawCircle((float)x3,height()-(float)y3,0);
33295             }
33296 #endif
33297             break;
33298           case 1 :
33299             if (zbuffer)
33300               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opac).draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opac).
33301                 draw_line(zbuffer,x2,y2,z2,x3,y3,z3,pcolor,opac).draw_line(zbuffer,x3,y3,z3,x0,y0,z0,pcolor,opac);
33302             else
33303               draw_line(x0,y0,x1,y1,pcolor,opac).draw_line(x1,y1,x2,y2,pcolor,opac).
33304                 draw_line(x2,y2,x3,y3,pcolor,opac).draw_line(x3,y3,x0,y0,pcolor,opac);
33305 #ifdef cimg_use_board
33306             if (pboard) {
33307               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
33308               board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1);
33309               board.drawLine((float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
33310               board.drawLine((float)x2,height()-(float)y2,(float)x3,height()-(float)y3);
33311               board.drawLine((float)x3,height()-(float)y3,(float)x0,height()-(float)y0);
33312             }
33313 #endif
33314             break;
33315           case 2 :
33316             if (zbuffer)
33317               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opac).draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opac);
33318             else
33319               draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opac).draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opac);
33320 #ifdef cimg_use_board
33321             if (pboard) {
33322               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
33323               board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
33324               board.fillTriangle((float)x0,height()-(float)y0,(float)x2,height()-(float)y2,(float)x3,height()-(float)y3);
33325             }
33326 #endif
33327             break;
33328           case 3 :
33329             if (zbuffer)
33330               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opac,lightprops(l)).
33331                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opac,lightprops(l));
33332             else
33333               _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opac,lightprops(l)).
33334                 _draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opac,lightprops(l));
33335 #ifdef cimg_use_board
33336             if (pboard) {
33337               const float lp = cimg::min(lightprops(l),1);
33338               board.setPenColorRGBi((unsigned char)(color[0]*lp),
33339                                      (unsigned char)(color[1]*lp),
33340                                      (unsigned char)(color[2]*lp),(unsigned char)(opac*255));
33341               board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
33342               board.fillTriangle((float)x0,height()-(float)y0,(float)x2,height()-(float)y2,(float)x3,height()-(float)y3);
33343             }
33344 #endif
33345             break;
33346           case 4 : {
33347             const float
33348               lightprop0 = lightprops(n0), lightprop1 = lightprops(n1),
33349               lightprop2 = lightprops(n2), lightprop3 = lightprops(n3);
33350             if (zbuffer)
33351               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,lightprop0,lightprop1,lightprop2,opac).
33352                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,lightprop0,lightprop2,lightprop3,opac);
33353             else
33354               draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprop0,lightprop1,lightprop2,opac).
33355                 draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,lightprop0,lightprop2,lightprop3,opac);
33356 #ifdef cimg_use_board
33357             if (pboard) {
33358               board.setPenColorRGBi((unsigned char)(color[0]),
33359                                      (unsigned char)(color[1]),
33360                                      (unsigned char)(color[2]),
33361                                      (unsigned char)(opac*255));
33362               board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprop0,
33363                                          (float)x1,height()-(float)y1,lightprop1,
33364                                          (float)x2,height()-(float)y2,lightprop2);
33365               board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprop0,
33366                                          (float)x2,height()-(float)y2,lightprop2,
33367                                          (float)x3,height()-(float)y3,lightprop3);
33368             }
33369 #endif
33370           } break;
33371           case 5 : {
33372             const unsigned int
33373               lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1),
33374               lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1),
33375               lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1),
33376               lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1);
33377             if (zbuffer)
33378               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac).
33379                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac);
33380             else
33381               draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac).
33382                 draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac);
33383 #ifdef cimg_use_board
33384             if (pboard) {
33385               const float
33386                 l0 = light_texture((int)(light_texture.width()/2*(1+lx0)), (int)(light_texture.height()/2*(1+ly0))),
33387                 l1 = light_texture((int)(light_texture.width()/2*(1+lx1)), (int)(light_texture.height()/2*(1+ly1))),
33388                 l2 = light_texture((int)(light_texture.width()/2*(1+lx2)), (int)(light_texture.height()/2*(1+ly2))),
33389                 l3 = light_texture((int)(light_texture.width()/2*(1+lx3)), (int)(light_texture.height()/2*(1+ly3)));
33390               board.setPenColorRGBi((unsigned char)(color[0]),
33391                                      (unsigned char)(color[1]),
33392                                      (unsigned char)(color[2]),
33393                                      (unsigned char)(opac*255));
33394               board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,
33395                                          (float)x1,height()-(float)y1,l1,
33396                                          (float)x2,height()-(float)y2,l2);
33397               board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,
33398                                          (float)x2,height()-(float)y2,l2,
33399                                          (float)x3,height()-(float)y3,l3);
33400             }
33401 #endif
33402           } break;
33403           }
33404         } break;
33405         case 9 : { // Textured triangle
33406           if (!__color)
33407             throw CImgArgumentException(_cimg_instance
33408                                         "draw_object3d() : Undefined texture for triangle primitive [%u].",
33409                                         cimg_instance,n_primitive);
33410           const unsigned int
33411             n0 = (unsigned int)primitive[0],
33412             n1 = (unsigned int)primitive[1],
33413             n2 = (unsigned int)primitive[2],
33414             tx0 = (unsigned int)primitive[3],
33415             ty0 = (unsigned int)primitive[4],
33416             tx1 = (unsigned int)primitive[5],
33417             ty1 = (unsigned int)primitive[6],
33418             tx2 = (unsigned int)primitive[7],
33419             ty2 = (unsigned int)primitive[8];
33420           const int
33421             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
33422             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
33423             x2 = (int)projections(n2,0), y2 = (int)projections(n2,1);
33424           const float
33425             z0 = vertices(n0,2) + Z + _focale,
33426             z1 = vertices(n1,2) + Z + _focale,
33427             z2 = vertices(n2,2) + Z + _focale;
33428           switch (render_type) {
33429           case 0 :
33430             draw_point(x0,y0,color.get_vector_at(tx0,ty0)._data,opac).
33431               draw_point(x1,y1,color.get_vector_at(tx1,ty1)._data,opac).
33432               draw_point(x2,y2,color.get_vector_at(tx2,ty2)._data,opac);
33433 #ifdef cimg_use_board
33434             if (pboard) {
33435               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
33436               board.drawCircle((float)x0,height()-(float)y0,0);
33437               board.drawCircle((float)x1,height()-(float)y1,0);
33438               board.drawCircle((float)x2,height()-(float)y2,0);
33439             }
33440 #endif
33441             break;
33442           case 1 :
33443             if (zbuffer)
33444               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac).
33445                 draw_line(zbuffer,x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opac).
33446                 draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac);
33447             else
33448               draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac).
33449                 draw_line(x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opac).
33450                 draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac);
33451 #ifdef cimg_use_board
33452             if (pboard) {
33453               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
33454               board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1);
33455               board.drawLine((float)x0,height()-(float)y0,(float)x2,height()-(float)y2);
33456               board.drawLine((float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
33457             }
33458 #endif
33459             break;
33460           case 2 :
33461             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac);
33462             else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac);
33463 #ifdef cimg_use_board
33464             if (pboard) {
33465               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
33466               board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
33467             }
33468 #endif
33469             break;
33470           case 3 :
33471             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l));
33472             else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l));
33473 #ifdef cimg_use_board
33474             if (pboard) {
33475               const float lp = cimg::min(lightprops(l),1);
33476               board.setPenColorRGBi((unsigned char)(128*lp),
33477                                     (unsigned char)(128*lp),
33478                                     (unsigned char)(128*lp),
33479                                     (unsigned char)(opac*255));
33480               board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
33481             }
33482 #endif
33483             break;
33484           case 4 :
33485             if (zbuffer)
33486               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprops(n0),lightprops(n1),lightprops(n2),opac);
33487             else
33488               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprops(n0),lightprops(n1),lightprops(n2),opac);
33489 #ifdef cimg_use_board
33490             if (pboard) {
33491               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
33492               board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprops(n0),
33493                                         (float)x1,height()-(float)y1,lightprops(n1),
33494                                         (float)x2,height()-(float)y2,lightprops(n2));
33495             }
33496 #endif
33497             break;
33498           case 5 :
33499             if (zbuffer)
33500               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,
33501                             (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1),
33502                             (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1),
33503                             (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1),
33504                             opac);
33505             else
33506               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,
33507                             (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1),
33508                             (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1),
33509                             (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1),
33510                             opac);
33511 #ifdef cimg_use_board
33512             if (pboard) {
33513               const float
33514                 l0 = light_texture((int)(light_texture.width()/2*(1+lightprops(n0,0))), (int)(light_texture.height()/2*(1+lightprops(n0,1)))),
33515                 l1 = light_texture((int)(light_texture.width()/2*(1+lightprops(n1,0))), (int)(light_texture.height()/2*(1+lightprops(n1,1)))),
33516                 l2 = light_texture((int)(light_texture.width()/2*(1+lightprops(n2,0))), (int)(light_texture.height()/2*(1+lightprops(n2,1))));
33517               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
33518               board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,(float)x1,height()-(float)y1,l1,(float)x2,height()-(float)y2,l2);
33519             }
33520 #endif
33521             break;
33522           }
33523         } break;
33524         case 12 : { // Textured quadrangle
33525           if (!__color)
33526             throw CImgArgumentException(_cimg_instance
33527                                         "draw_object3d() : Undefined texture for quadrangle primitive [%u].",
33528                                         cimg_instance,n_primitive);
33529           const unsigned int
33530             n0 = (unsigned int)primitive[0],
33531             n1 = (unsigned int)primitive[1],
33532             n2 = (unsigned int)primitive[2],
33533             n3 = (unsigned int)primitive[3],
33534             tx0 = (unsigned int)primitive[4],
33535             ty0 = (unsigned int)primitive[5],
33536             tx1 = (unsigned int)primitive[6],
33537             ty1 = (unsigned int)primitive[7],
33538             tx2 = (unsigned int)primitive[8],
33539             ty2 = (unsigned int)primitive[9],
33540             tx3 = (unsigned int)primitive[10],
33541             ty3 = (unsigned int)primitive[11];
33542           const int
33543             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
33544             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
33545             x2 = (int)projections(n2,0), y2 = (int)projections(n2,1),
33546             x3 = (int)projections(n3,0), y3 = (int)projections(n3,1);
33547           const float
33548             z0 = vertices(n0,2) + Z + _focale,
33549             z1 = vertices(n1,2) + Z + _focale,
33550             z2 = vertices(n2,2) + Z + _focale,
33551             z3 = vertices(n3,2) + Z + _focale;
33552 
33553           switch (render_type) {
33554           case 0 :
33555             draw_point(x0,y0,color.get_vector_at(tx0,ty0)._data,opac).
33556               draw_point(x1,y1,color.get_vector_at(tx1,ty1)._data,opac).
33557               draw_point(x2,y2,color.get_vector_at(tx2,ty2)._data,opac).
33558               draw_point(x3,y3,color.get_vector_at(tx3,ty3)._data,opac);
33559 #ifdef cimg_use_board
33560             if (pboard) {
33561               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
33562               board.drawCircle((float)x0,height()-(float)y0,0);
33563               board.drawCircle((float)x1,height()-(float)y1,0);
33564               board.drawCircle((float)x2,height()-(float)y2,0);
33565               board.drawCircle((float)x3,height()-(float)y3,0);
33566             }
33567 #endif
33568             break;
33569           case 1 :
33570             if (zbuffer)
33571               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac).
33572                 draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac).
33573                 draw_line(zbuffer,x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opac).
33574                 draw_line(zbuffer,x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opac);
33575             else
33576               draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac).
33577                 draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac).
33578                 draw_line(x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opac).
33579                 draw_line(x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opac);
33580 #ifdef cimg_use_board
33581             if (pboard) {
33582               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
33583               board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1);
33584               board.drawLine((float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
33585               board.drawLine((float)x2,height()-(float)y2,(float)x3,height()-(float)y3);
33586               board.drawLine((float)x3,height()-(float)y3,(float)x0,height()-(float)y0);
33587             }
33588 #endif
33589             break;
33590           case 2 :
33591             if (zbuffer)
33592               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac).
33593                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac);
33594             else
33595               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac).
33596                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac);
33597 #ifdef cimg_use_board
33598             if (pboard) {
33599               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
33600               board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
33601               board.fillTriangle((float)x0,height()-(float)y0,(float)x2,height()-(float)y2,(float)x3,height()-(float)y3);
33602             }
33603 #endif
33604             break;
33605           case 3 :
33606             if (zbuffer)
33607               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l)).
33608                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac,lightprops(l));
33609             else
33610               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l)).
33611                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac,lightprops(l));
33612 #ifdef cimg_use_board
33613             if (pboard) {
33614               const float lp = cimg::min(lightprops(l),1);
33615               board.setPenColorRGBi((unsigned char)(128*lp),
33616                                      (unsigned char)(128*lp),
33617                                      (unsigned char)(128*lp),
33618                                      (unsigned char)(opac*255));
33619               board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
33620               board.fillTriangle((float)x0,height()-(float)y0,(float)x2,height()-(float)y2,(float)x3,height()-(float)y3);
33621             }
33622 #endif
33623             break;
33624           case 4 : {
33625             const float
33626               lightprop0 = lightprops(n0), lightprop1 = lightprops(n1),
33627               lightprop2 = lightprops(n2), lightprop3 = lightprops(n3);
33628             if (zbuffer)
33629               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprop0,lightprop1,lightprop2,opac).
33630                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,lightprop0,lightprop2,lightprop3,opac);
33631             else
33632               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprop0,lightprop1,lightprop2,opac).
33633                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,lightprop0,lightprop2,lightprop3,opac);
33634 #ifdef cimg_use_board
33635             if (pboard) {
33636               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
33637               board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprop0,
33638                                          (float)x1,height()-(float)y1,lightprop1,
33639                                          (float)x2,height()-(float)y2,lightprop2);
33640               board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprop0,
33641                                          (float)x2,height()-(float)y2,lightprop2,
33642                                          (float)x3,height()-(float)y3,lightprop3);
33643             }
33644 #endif
33645           } break;
33646           case 5 : {
33647             const unsigned int
33648               lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1),
33649               lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1),
33650               lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1),
33651               lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1);
33652             if (zbuffer)
33653               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac).
33654                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac);
33655             else
33656               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac).
33657                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac);
33658 #ifdef cimg_use_board
33659             if (pboard) {
33660               const float
33661                 l0 = light_texture((int)(light_texture.width()/2*(1+lx0)), (int)(light_texture.height()/2*(1+ly0))),
33662                 l1 = light_texture((int)(light_texture.width()/2*(1+lx1)), (int)(light_texture.height()/2*(1+ly1))),
33663                 l2 = light_texture((int)(light_texture.width()/2*(1+lx2)), (int)(light_texture.height()/2*(1+ly2))),
33664                 l3 = light_texture((int)(light_texture.width()/2*(1+lx3)), (int)(light_texture.height()/2*(1+ly3)));
33665               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
33666               board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,
33667                                          (float)x1,height()-(float)y1,l1,
33668                                          (float)x2,height()-(float)y2,l2);
33669               board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,
33670                                          (float)x2,height()-(float)y2,l2,
33671                                          (float)x3,height()-(float)y3,l3);
33672             }
33673 #endif
33674           } break;
33675           }
33676         } break;
33677         }
33678       }
33679       return *this;
33680     }
33681 
33682     //@}
33683     //---------------------------
33684     //
33685     //! \name Data Input
33686     //@{
33687     //---------------------------
33688 
33689     //! Launch simple interface to select a shape from an image.
33690     /**
33691        \param disp Display window to use.
33692        \param feature_type Type of feature to select. Can be <tt>{ 0=point | 1=line | 2=rectangle | 3=ellipse }</tt>.
33693        \param XYZ Pointer to 3 values X,Y,Z which tells about the projection point coordinates, for volumetric images.
33694     **/
33695     CImg<T>& select(CImgDisplay &disp,
33696                     const unsigned int feature_type=2, unsigned int *const XYZ=0) {
33697       return get_select(disp,feature_type,XYZ).move_to(*this);
33698     }
33699 
33700     //! Simple interface to select a shape from an image \overloading.
33701     CImg<T>& select(const char *const title,
33702                     const unsigned int feature_type=2, unsigned int *const XYZ=0) {
33703       return get_select(title,feature_type,XYZ).move_to(*this);
33704     }
33705 
33706     //! Simple interface to select a shape from an image \newinstance.
33707     CImg<intT> get_select(CImgDisplay &disp,
33708                           const unsigned int feature_type=2, unsigned int *const XYZ=0) const {
33709       return _get_select(disp,0,feature_type,XYZ,0,0,0,true);
33710     }
33711 
33712     //! Simple interface to select a shape from an image \newinstance.
33713     CImg<intT> get_select(const char *const title,
33714                           const unsigned int feature_type=2, unsigned int *const XYZ=0) const {
33715       CImgDisplay disp;
33716       return _get_select(disp,title,feature_type,XYZ,0,0,0,true);
33717     }
33718 
33719     CImg<intT> _get_select(CImgDisplay &disp, const char *const title,
33720                            const unsigned int feature_type, unsigned int *const XYZ,
33721                            const int origX, const int origY, const int origZ,
33722                            const bool reset_view3d=true) const {
33723       if (is_empty()) return CImg<intT>(1,feature_type==0?3:6,1,1,-1);
33724       if (!disp) {
33725         disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1);
33726         if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum);
33727       } else if (title) disp.set_title("%s",title);
33728 
33729       const unsigned int old_normalization = disp.normalization();
33730       bool old_is_resized = disp.is_resized();
33731       disp._normalization = 0;
33732       disp.show().set_key(0).set_wheel();
33733 
33734       unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 };
33735 
33736       int area = 0, starting_area = 0, clicked_area = 0, phase = 0,
33737         X0 = (int)((XYZ?XYZ[0]:_width/2)%_width), Y0 = (int)((XYZ?XYZ[1]:_height/2)%_height), Z0 = (int)((XYZ?XYZ[2]:_depth/2)%_depth),
33738         X1 =-1, Y1 = -1, Z1 = -1,
33739         X = -1, Y = -1, Z = -1, X3d = -1, Y3d = -1,
33740         oX = X, oY = Y, oZ = Z, oX3d = X3d, oY3d = -1;
33741       unsigned int old_button = 0, key = 0;
33742 
33743       bool shape_selected = false, text_down = false;
33744       static CImg<floatT> pose3d;
33745       static bool is_view3d = false;
33746       if (reset_view3d) { pose3d.assign(); is_view3d = false; }
33747       CImg<floatT> points3d, opacities3d, sel_opacities3d;
33748       CImgList<uintT> primitives3d, sel_primitives3d;
33749       CImgList<ucharT> colors3d, sel_colors3d;
33750       CImg<ucharT> visu, visu0, view3d;
33751       char text[1024] = { 0 };
33752 
33753       while (!key && !disp.is_closed() && !shape_selected) {
33754 
33755         // Handle mouse motion and selection
33756         oX = X; oY = Y; oZ = Z;
33757         int
33758           mx = disp.mouse_x(),
33759           my = disp.mouse_y();
33760         const int
33761           mX = mx<0?-1:mx*(width()+(depth()>1?depth():0))/disp.width(),
33762           mY = my<0?-1:my*(height()+(depth()>1?depth():0))/disp.height();
33763         area = 0;
33764         if (mX>=0 && mY>=0 && mX<width() && mY<height())  { area = 1; X = mX; Y = mY; Z = phase?Z1:Z0; }
33765         if (mX>=0 && mX<width() && mY>=height()) { area = 2; X = mX; Z = mY - _height; Y = phase?Y1:Y0; }
33766         if (mY>=0 && mX>=width() && mY<height()) { area = 3; Y = mY; Z = mX - _width; X = phase?X1:X0; }
33767         if (mX>=width() && mY>=height()) area = 4;
33768         if (disp.button()) { if (!clicked_area) clicked_area = area; } else clicked_area = 0;
33769 
33770         switch (key = disp.key()) {
33771 #if cimg_OS!=2
33772         case cimg::keyCTRLRIGHT :
33773 #endif
33774         case 0 : case cimg::keyCTRLLEFT : key = 0; break;
33775         case cimg::keyPAGEUP : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(1); key = 0; } break;
33776         case cimg::keyPAGEDOWN : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(-1); key = 0; } break;
33777         case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
33778           disp.set_fullscreen(false).resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
33779                                             CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
33780             _is_resized = true;
33781           disp.set_key(key,false); key = 0; visu0.assign();
33782         } break;
33783         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
33784           disp.set_fullscreen(false).resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
33785           disp.set_key(key,false); key = 0; visu0.assign();
33786         } break;
33787         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
33788           disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true;
33789           disp.set_key(key,false); key = 0; visu0.assign();
33790         } break;
33791         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
33792           disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
33793           disp.set_key(key,false); key = 0; visu0.assign();
33794         } break;
33795         case cimg::keyV : is_view3d = !is_view3d; disp.set_key(key,false); key = 0; visu0.assign(); break;
33796         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
33797           static unsigned int snap_number = 0;
33798           char filename[32] = { 0 };
33799           std::FILE *file;
33800           do {
33801             cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++);
33802             if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
33803           } while (file);
33804           if (visu0) {
33805             visu.draw_text(0,0," Saving snapshot... ",foreground_color,background_color,1,13).display(disp);
33806             visu0.save(filename);
33807             visu.draw_text(0,0," Snapshot '%s' saved. ",foreground_color,background_color,1,13,filename).display(disp);
33808           }
33809           disp.set_key(key,false); key = 0;
33810         } break;
33811         case cimg::keyO :
33812           if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
33813             static unsigned int snap_number = 0;
33814             char filename[32] = { 0 };
33815             std::FILE *file;
33816             do {
33817 #ifdef cimg_use_zlib
33818               cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++);
33819 #else
33820               cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++);
33821 #endif
33822               if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
33823             } while (file);
33824             visu.draw_text(0,0," Saving instance... ",foreground_color,background_color,1,13).display(disp);
33825             save(filename);
33826             visu.draw_text(0,0," Instance '%s' saved. ",foreground_color,background_color,1,13,filename).display(disp);
33827             disp.set_key(key,false); key = 0;
33828           } break;
33829         }
33830 
33831         switch (area) {
33832 
33833         case 0 : // When mouse is out of image range.
33834           mx = my = X = Y = Z = -1;
33835           break;
33836 
33837         case 1 : case 2 : case 3 : // When mouse is over the XY,XZ or YZ projections.
33838           if (disp.button()&1 && phase<2 && clicked_area==area) { // When selection has been started (1st step).
33839             if (_depth>1 && (X1!=X || Y1!=Y || Z1!=Z)) visu0.assign();
33840             X1 = X; Y1 = Y; Z1 = Z;
33841           }
33842           if (!(disp.button()&1) && phase>=2 && clicked_area!=area) { // When selection is at 2nd step (for volumes).
33843             switch (starting_area) {
33844             case 1 : if (Z1!=Z) visu0.assign(); Z1 = Z; break;
33845             case 2 : if (Y1!=Y) visu0.assign(); Y1 = Y; break;
33846             case 3 : if (X1!=X) visu0.assign(); X1 = X; break;
33847             }
33848           }
33849           if (disp.button()&2 && clicked_area==area) { // When moving through the image/volume.
33850             if (phase) {
33851               if (_depth>1 && (X1!=X || Y1!=Y || Z1!=Z)) visu0.assign();
33852               X1 = X; Y1 = Y; Z1 = Z;
33853             } else {
33854               if (_depth>1 && (X0!=X || Y0!=Y || Z0!=Z)) visu0.assign();
33855               X0 = X; Y0 = Y; Z0 = Z;
33856             }
33857           }
33858           if (disp.button()&4) { // Reset positions.
33859             oX = X = X0; oY = Y = Y0; oZ = Z = Z0; phase = area = clicked_area = starting_area = 0; visu0.assign();
33860           }
33861           if (disp.wheel()) { // When moving through the slices of the volume (with mouse wheel).
33862             if (_depth>1 && !disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT() && !disp.is_keySHIFTLEFT() && !disp.is_keySHIFTRIGHT() &&
33863                 !disp.is_keyALT() && !disp.is_keyALTGR()) {
33864               switch (area) {
33865               case 1 :
33866                 if (phase) Z = (Z1+=disp.wheel()); else Z = (Z0+=disp.wheel());
33867                 visu0.assign(); break;
33868               case 2 :
33869                 if (phase) Y = (Y1+=disp.wheel()); else Y = (Y0+=disp.wheel());
33870                 visu0.assign(); break;
33871               case 3 :
33872                 if (phase) X = (X1+=disp.wheel()); else X = (X0+=disp.wheel());
33873                 visu0.assign(); break;
33874               }
33875               disp.set_wheel();
33876             } else key = ~0U;
33877           }
33878           if ((disp.button()&1)!=old_button) { // When left button has just been pressed or released.
33879             switch (phase) {
33880             case 0 :
33881               if (area==clicked_area) {
33882                 X0 = X1 = X; Y0 = Y1 = Y; Z0 = Z1 = Z; starting_area = area; ++phase;
33883               } break;
33884             case 1 :
33885               if (area==starting_area) {
33886                 X1 = X; Y1 = Y; Z1 = Z; ++phase;
33887               } else if (!(disp.button()&1)) { oX = X = X0; oY = Y = Y0; oZ = Z = Z0; phase = 0; visu0.assign(); }
33888               break;
33889             case 2 : ++phase; break;
33890             }
33891             old_button = disp.button()&1;
33892           }
33893           break;
33894 
33895         case 4 : // When mouse is over the 3d view.
33896           if (is_view3d && points3d) {
33897             X3d = mx - _width*disp.width()/(_width+(_depth>1?_depth:0));
33898             Y3d = my - _height*disp.height()/(_height+(_depth>1?_depth:0));
33899             if (oX3d<0) { oX3d = X3d; oY3d = Y3d; }
33900             if ((disp.button()&3)==3) { pose3d.assign(); view3d.assign(); oX3d = oY3d = X3d = Y3d = -1; } // Left + right buttons : reset.
33901             else if (disp.button()&1 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Left button : rotate.
33902               const float
33903                 R = 0.45f*cimg::min(view3d._width,view3d._height),
33904                 R2 = R*R,
33905                 u0 = (float)(oX3d-view3d.width()/2),
33906                 v0 = (float)(oY3d-view3d.height()/2),
33907                 u1 = (float)(X3d-view3d.width()/2),
33908                 v1 = (float)(Y3d-view3d.height()/2),
33909                 n0 = (float)std::sqrt(u0*u0+v0*v0),
33910                 n1 = (float)std::sqrt(u1*u1+v1*v1),
33911                 nu0 = n0>R?(u0*R/n0):u0,
33912                 nv0 = n0>R?(v0*R/n0):v0,
33913                 nw0 = (float)std::sqrt(cimg::max(0,R2-nu0*nu0-nv0*nv0)),
33914                 nu1 = n1>R?(u1*R/n1):u1,
33915                 nv1 = n1>R?(v1*R/n1):v1,
33916                 nw1 = (float)std::sqrt(cimg::max(0,R2-nu1*nu1-nv1*nv1)),
33917                 u = nv0*nw1 - nw0*nv1,
33918                 v = nw0*nu1 - nu0*nw1,
33919                 w = nv0*nu1 - nu0*nv1,
33920                 n = (float)std::sqrt(u*u+v*v+w*w),
33921                 alpha = (float)std::asin(n/R2);
33922               pose3d.draw_image(CImg<floatT>::rotation_matrix(u,v,w,alpha)*pose3d.get_crop(0,0,2,2));
33923               view3d.assign();
33924             } else if (disp.button()&2 && pose3d && oY3d!=Y3d) {  // Right button : zoom.
33925               pose3d(3,2)-=(oY3d - Y3d)*1.5f; view3d.assign();
33926             }
33927             if (disp.wheel()) { // Wheel : zoom
33928               pose3d(3,2)-=disp.wheel()*15; view3d.assign(); disp.set_wheel();
33929             }
33930             if (disp.button()&4 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Middle button : shift.
33931               pose3d(3,0)-=oX3d - X3d; pose3d(3,1)-=oY3d - Y3d; view3d.assign();
33932             }
33933             oX3d = X3d; oY3d = Y3d;
33934           }
33935           mx = my = X = Y = Z = -1;
33936           break;
33937         }
33938 
33939         if (phase) {
33940           if (!feature_type) shape_selected = phase?true:false;
33941           else {
33942             if (_depth>1) shape_selected = (phase==3)?true:false;
33943             else shape_selected = (phase==2)?true:false;
33944           }
33945         }
33946 
33947         if (X0<0) X0 = 0; if (X0>=width()) X0 = width() - 1;
33948         if (Y0<0) Y0 = 0; if (Y0>=height()) Y0 = height() - 1;
33949         if (Z0<0) Z0 = 0; if (Z0>=depth()) Z0 = depth() - 1;
33950         if (X1<1) X1 = 0; if (X1>=width()) X1 = width() - 1;
33951         if (Y1<0) Y1 = 0; if (Y1>=height()) Y1 = height() - 1;
33952         if (Z1<0) Z1 = 0; if (Z1>=depth()) Z1 = depth() - 1;
33953 
33954         // Draw visualization image on the display
33955         if (oX!=X || oY!=Y || oZ!=Z || !visu0 || (_depth>1 && !view3d)) {
33956 
33957           if (!visu0) { // Create image of projected planes.
33958             CImg<Tuchar> tmp, tmp0;
33959             if (_depth!=1) {
33960               tmp0 = get_projections2d(phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0);
33961               tmp = tmp0.get_channels(0,cimg::min(2,spectrum() - 1));
33962             } else tmp = get_channels(0,cimg::min(2,spectrum() - 1));
33963             switch (old_normalization) {
33964             case 0 : tmp.move_to(visu0); break;
33965             case 1 : tmp.normalize(0,255).move_to(visu0); break;
33966             case 2 : {
33967               const float m = disp._min, M = disp._max;
33968               ((tmp-=m)*=255.0f/(M-m>0?M-m:1)).move_to(visu0);
33969             }
33970             case 3 :
33971               if (cimg::type<T>::is_float()) (tmp.normalize(0,255)).move_to(visu0);
33972               else {
33973                 const float m = (float)cimg::type<T>::min(), M = (float)cimg::type<T>::max();
33974                 ((tmp-=m)*=255.0f/(M-m)).move_to(visu0);
33975               } break;
33976             }
33977             visu0.resize(disp);
33978             view3d.assign();
33979             points3d.assign();
33980           }
33981 
33982           if (is_view3d && _depth>1 && !view3d) { // Create 3d view for volumetric images.
33983             const unsigned int
33984               _x3d = (unsigned int)cimg::round((float)_width*visu0._width/(_width+_depth),1,1),
33985               _y3d = (unsigned int)cimg::round((float)_height*visu0._height/(_height+_depth),1,1),
33986               x3d = _x3d>=visu0._width?visu0._width-1:_x3d,
33987               y3d = _y3d>=visu0._height?visu0._height-1:_y3d;
33988             CImg<ucharT>(1,2,1,1,64,128).resize(visu0._width-x3d,visu0._height-y3d,1,visu0._spectrum,3).move_to(view3d);
33989             if (!points3d) {
33990               get_projections3d(primitives3d,colors3d,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0,true).move_to(points3d);
33991               points3d.append(CImg<floatT>(8,3,1,1,
33992                                            0,_width-1,_width-1,0,0,_width-1,_width-1,0,
33993                                            0,0,_height-1,_height-1,0,0,_height-1,_height-1,
33994                                            0,0,0,0,_depth-1,_depth-1,_depth-1,_depth-1),'x');
33995               CImg<uintT>::vector(12,13).move_to(primitives3d); CImg<uintT>::vector(13,14).move_to(primitives3d);
33996               CImg<uintT>::vector(14,15).move_to(primitives3d); CImg<uintT>::vector(15,12).move_to(primitives3d);
33997               CImg<uintT>::vector(16,17).move_to(primitives3d); CImg<uintT>::vector(17,18).move_to(primitives3d);
33998               CImg<uintT>::vector(18,19).move_to(primitives3d); CImg<uintT>::vector(19,16).move_to(primitives3d);
33999               CImg<uintT>::vector(12,16).move_to(primitives3d); CImg<uintT>::vector(13,17).move_to(primitives3d);
34000               CImg<uintT>::vector(14,18).move_to(primitives3d); CImg<uintT>::vector(15,19).move_to(primitives3d);
34001               colors3d.insert(12,CImg<ucharT>::vector(255,255,255));
34002               opacities3d.assign(primitives3d.width(),1,1,1,0.5f);
34003               if (!phase) {
34004                 opacities3d[0] = opacities3d[1] = opacities3d[2] = 0.8f;
34005                 sel_primitives3d.assign();
34006                 sel_colors3d.assign();
34007                 sel_opacities3d.assign();
34008               } else {
34009                 if (feature_type==2) {
34010                   points3d.append(CImg<floatT>(8,3,1,1,
34011                                                X0,X1,X1,X0,X0,X1,X1,X0,
34012                                                Y0,Y0,Y1,Y1,Y0,Y0,Y1,Y1,
34013                                                Z0,Z0,Z0,Z0,Z1,Z1,Z1,Z1),'x');
34014                   sel_primitives3d.assign();
34015                   CImg<uintT>::vector(20,21).move_to(sel_primitives3d); CImg<uintT>::vector(21,22).move_to(sel_primitives3d);
34016                   CImg<uintT>::vector(22,23).move_to(sel_primitives3d); CImg<uintT>::vector(23,20).move_to(sel_primitives3d);
34017                   CImg<uintT>::vector(24,25).move_to(sel_primitives3d); CImg<uintT>::vector(25,26).move_to(sel_primitives3d);
34018                   CImg<uintT>::vector(26,27).move_to(sel_primitives3d); CImg<uintT>::vector(27,24).move_to(sel_primitives3d);
34019                   CImg<uintT>::vector(20,24).move_to(sel_primitives3d); CImg<uintT>::vector(21,25).move_to(sel_primitives3d);
34020                   CImg<uintT>::vector(22,26).move_to(sel_primitives3d); CImg<uintT>::vector(23,27).move_to(sel_primitives3d);
34021                 } else {
34022                   points3d.append(CImg<floatT>(2,3,1,1,
34023                                                X0,X1,
34024                                                Y0,Y1,
34025                                                Z0,Z1),'x');
34026                   sel_primitives3d.assign(CImg<uintT>::vector(20,21));
34027                 }
34028                 sel_colors3d.assign(sel_primitives3d._width,CImg<ucharT>::vector(255,255,255));
34029                 sel_opacities3d.assign(sel_primitives3d._width,1,1,1,0.8f);
34030               }
34031               points3d.shift_object3d(-0.5f*_width,-0.5f*_height,-0.5f*_depth).resize_object3d();
34032               points3d*=0.75f*cimg::min(view3d._width,view3d._height);
34033             }
34034 
34035             if (!pose3d) CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose3d);
34036             CImg<floatT> zbuffer3d(view3d._width,view3d._height,1,1,0);
34037             const CImg<floatT> rotated_points3d = pose3d.get_crop(0,0,2,2)*points3d;
34038             if (sel_primitives3d)
34039               view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width,
34040                                    pose3d(3,1) + 0.5f*view3d._height,
34041                                    pose3d(3,2),
34042                                    rotated_points3d,sel_primitives3d,sel_colors3d,sel_opacities3d,
34043                                    2,true,500,0,0,0,0,0,zbuffer3d);
34044             view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width,
34045                                  pose3d(3,1) + 0.5f*view3d._height,
34046                                  pose3d(3,2),
34047                                  rotated_points3d,primitives3d,colors3d,opacities3d,
34048                                  2,true,500,0,0,0,0,0,zbuffer3d);
34049             visu0.draw_image(x3d,y3d,view3d);
34050           }
34051           visu = visu0;
34052 
34053           const int d = (_depth>1)?_depth:0;
34054           if (phase) switch (feature_type) {
34055           case 1 : {
34056             const int
34057               x0 = (int)((X0+0.5f)*disp.width()/(_width+d)),
34058               y0 = (int)((Y0+0.5f)*disp.height()/(_height+d)),
34059               x1 = (int)((X1+0.5f)*disp.width()/(_width+d)),
34060               y1 = (int)((Y1+0.5f)*disp.height()/(_height+d));
34061             visu.draw_arrow(x0,y0,x1,y1,background_color,0.9f,30,5,0x55555555).
34062               draw_arrow(x0,y0,x1,y1,foreground_color,0.9f,30,5,0xAAAAAAAA);
34063             if (d) {
34064               const int
34065                 zx0 = (int)((_width+Z0+0.5f)*disp.width()/(_width+d)),
34066                 zx1 = (int)((_width+Z1+0.5f)*disp.width()/(_width+d)),
34067                 zy0 = (int)((_height+Z0+0.5f)*disp.height()/(_height+d)),
34068                 zy1 = (int)((_height+Z1+0.5f)*disp.height()/(_height+d));
34069               visu.draw_arrow(zx0,y0,zx1,y1,foreground_color,0.9f,30,5,0x55555555).
34070                 draw_arrow(x0,zy0,x1,zy1,foreground_color,0.9f,30,5,0x55555555).
34071                 draw_arrow(zx0,y0,zx1,y1,foreground_color,0.9f,30,5,0xAAAAAAAA).
34072                 draw_arrow(x0,zy0,x1,zy1,foreground_color,0.9f,30,5,0xAAAAAAAA);
34073             }
34074           } break;
34075           case 2 : {
34076             const int
34077               x0 = (X0<X1?X0:X1)*disp.width()/(_width+d),
34078               y0 = (Y0<Y1?Y0:Y1)*disp.height()/(_height+d),
34079               x1 = ((X0<X1?X1:X0)+1)*disp.width()/(_width+d)-1,
34080               y1 = ((Y0<Y1?Y1:Y0)+1)*disp.height()/(_height+d)-1;
34081             visu.draw_rectangle(x0,y0,x1,y1,background_color,0.2f).draw_rectangle(x0,y0,x1,y1,foreground_color,0.6f,0x55555555);
34082             if (d) {
34083               const int
34084                 zx0 = (int)((_width+(Z0<Z1?Z0:Z1))*disp.width()/(_width+d)),
34085                 zy0 = (int)((_height+(Z0<Z1?Z0:Z1))*disp.height()/(_height+d)),
34086                 zx1 = (int)((_width+(Z0<Z1?Z1:Z0)+1)*disp.width()/(_width+d))-1,
34087                 zy1 = (int)((_height+(Z0<Z1?Z1:Z0)+1)*disp.height()/(_height+d))-1;
34088               visu.draw_rectangle(zx0,y0,zx1,y1,background_color,0.2f).draw_rectangle(zx0,y0,zx1,y1,foreground_color,0.6f,0x55555555).
34089                 draw_rectangle(x0,zy0,x1,zy1,background_color,0.2f).draw_rectangle(x0,zy0,x1,zy1,foreground_color,0.6f,0x55555555);
34090             }
34091           } break;
34092           case 3 : {
34093             const int
34094               x0 = X0*disp.width()/(_width+d),
34095               y0 = Y0*disp.height()/(_height+d),
34096               x1 = X1*disp.width()/(_width+d)-1,
34097               y1 = Y1*disp.height()/(_height+d)-1;
34098             visu.draw_ellipse(x0,y0,(float)cimg::abs(x1-x0),(float)cimg::abs(y1-y0),0,background_color,0.2f).
34099               draw_ellipse(x0,y0,(float)cimg::abs(x1-x0),(float)cimg::abs(y1-y0),0,foreground_color,0.6f,0x55555555).
34100               draw_point(x0,y0,foreground_color,0.6f);
34101             if (d) {
34102               const int
34103                 zx0 = (int)((_width+Z0)*disp.width()/(_width+d)),
34104                 zy0 = (int)((_height+Z0)*disp.height()/(_height+d)),
34105                 zx1 = (int)((_width+Z1+1)*disp.width()/(_width+d))-1,
34106                 zy1 = (int)((_height+Z1+1)*disp.height()/(_height+d))-1;
34107               visu.draw_ellipse(zx0,y0,(float)cimg::abs(zx1-zx0),(float)cimg::abs(y1-y0),0,background_color,0.2f).
34108                 draw_ellipse(zx0,y0,(float)cimg::abs(zx1-zx0),(float)cimg::abs(y1-y0),0,foreground_color,0.6f,0x55555555).
34109                 draw_point(zx0,y0,foreground_color,0.6f).
34110                 draw_ellipse(x0,zy0,(float)cimg::abs(x1-x0),(float)cimg::abs(zy1-zy0),0,background_color,0.2f).
34111                 draw_ellipse(x0,zy0,(float)cimg::abs(x1-x0),(float)cimg::abs(zy1-zy0),0,foreground_color,0.6f,0x55555555).
34112                 draw_point(x0,zy0,foreground_color,0.6f);
34113             }
34114           } break;
34115           } else {
34116             const int
34117               x0 = X*disp.width()/(_width+d),
34118               y0 = Y*disp.height()/(_height+d),
34119               x1 = (X+1)*disp.width()/(_width+d)-1,
34120               y1 = (Y+1)*disp.height()/(_height+d)-1,
34121               zx0 = (Z+_width)*disp.width()/(_width+d),
34122               zx1 = (Z+_width+1)*disp.width()/(_width+d),
34123               zy0 = (Z+_height)*disp.height()/(_height+d),
34124               zy1 = (Z+_height+1)*disp.height()/(_height+d);
34125 
34126             if (x1-x0>=4 && y1-y0>=4) visu.draw_rectangle(x0,y0,x1,y1,background_color,0.2f).
34127                                         draw_rectangle(x0,y0,x1,y1,foreground_color,0.6f,~0U);
34128 
34129             if (_depth>1) {
34130               if (y1-y0>=4 && zx1-zx0>=4) visu.draw_rectangle(zx0,y0,zx1,y1,background_color,0.2f).
34131                                             draw_rectangle(zx0,y0,zx1,y1,foreground_color,0.6f,~0U);
34132               if (x1-x0>=4 && zy1-zy0>=4) visu.draw_rectangle(x0,zy0,x1,zy1,background_color,0.2f).
34133                                             draw_rectangle(x0,zy0,x1,zy1,foreground_color,0.6f,~0U);
34134             }
34135           }
34136 
34137           if (my>=0 && my<13) text_down = true; else if (my>=visu.height()-13) text_down = false;
34138           if (!feature_type || !phase) {
34139             if (X>=0 && Y>=0 && Z>=0 && X<width() && Y<height() && Z<depth()) {
34140               if (_depth>1) cimg_snprintf(text,sizeof(text)," Point (%d,%d,%d) = [ ",origX+X,origY+Y,origZ+Z);
34141               else cimg_snprintf(text,sizeof(text)," Point (%d,%d) = [ ",origX+X,origY+Y);
34142               char *ctext = text + std::strlen(text), *const ltext = text + 512;
34143               for (unsigned int c = 0; c<_spectrum && ctext<ltext; ++c) {
34144                 cimg_snprintf(ctext,sizeof(text)/2,cimg::type<T>::format(),cimg::type<T>::format((*this)(X,Y,Z,c)));
34145                 ctext = text + std::strlen(text);
34146                 *(ctext++) = ' '; *ctext = 0;
34147               }
34148               std::strcpy(text + std::strlen(text),"] ");
34149             }
34150           } else switch (feature_type) {
34151           case 1 : {
34152             const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1), norm = std::sqrt(dX*dX+dY*dY+dZ*dZ);
34153             if (_depth>1) cimg_snprintf(text,sizeof(text)," Vect (%d,%d,%d)-(%d,%d,%d), Norm = %g ",
34154                                         origX+X0,origY+Y0,origZ+Z0,origX+X1,origY+Y1,origZ+Z1,norm);
34155             else cimg_snprintf(text,sizeof(text)," Vect (%d,%d)-(%d,%d), Norm = %g ",
34156                                origX+X0,origY+Y0,origX+X1,origY+Y1,norm);
34157           } break;
34158           case 2 :
34159             if (_depth>1) cimg_snprintf(text,sizeof(text)," Box (%d,%d,%d)-(%d,%d,%d), Size = (%d,%d,%d) ",
34160                                         origX+(X0<X1?X0:X1),origY+(Y0<Y1?Y0:Y1),origZ+(Z0<Z1?Z0:Z1),
34161                                         origX+(X0<X1?X1:X0),origY+(Y0<Y1?Y1:Y0),origZ+(Z0<Z1?Z1:Z0),
34162                                         1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1),1+cimg::abs(Z0-Z1));
34163             else cimg_snprintf(text,sizeof(text)," Box (%d,%d)-(%d,%d), Size = (%d,%d) ",
34164                                origX+(X0<X1?X0:X1),origY+(Y0<Y1?Y0:Y1),origX+(X0<X1?X1:X0),origY+(Y0<Y1?Y1:Y0),
34165                                1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1));
34166             break;
34167           default :
34168             if (_depth>1) cimg_snprintf(text,sizeof(text)," Ellipse (%d,%d,%d)-(%d,%d,%d), Radii = (%d,%d,%d) ",
34169                                         origX+X0,origY+Y0,origZ+Z0,origX+X1,origY+Y1,origZ+Z1,
34170                                         1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1),1+cimg::abs(Z0-Z1));
34171             else cimg_snprintf(text,sizeof(text)," Ellipse (%d,%d)-(%d,%d), Radii = (%d,%d) ",
34172                                origX+X0,origY+Y0,origX+X1,origY+Y1,1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1));
34173             }
34174           if (phase || (mx>=0 && my>=0)) visu.draw_text(0,text_down?visu.height()-13:0,text,foreground_color,background_color,0.7f,13);
34175           disp.display(visu).wait();
34176         } else if (!shape_selected) disp.wait();
34177         if (disp.is_resized()) { disp.resize(false)._is_resized = false; old_is_resized = true; visu0.assign(); }
34178       }
34179 
34180       // Return result
34181       CImg<intT> res(1,feature_type==0?3:6,1,1,-1);
34182       if (XYZ) { XYZ[0] = (unsigned int)X0; XYZ[1] = (unsigned int)Y0; XYZ[2] = (unsigned int)Z0; }
34183       if (shape_selected) {
34184         if (feature_type==2) {
34185           if (X0>X1) cimg::swap(X0,X1);
34186           if (Y0>Y1) cimg::swap(Y0,Y1);
34187           if (Z0>Z1) cimg::swap(Z0,Z1);
34188         }
34189         if (X1<0 || Y1<0 || Z1<0) X0 = Y0 = Z0 = X1 = Y1 = Z1 = -1;
34190         switch (feature_type) {
34191         case 1 : case 2 : res[0] = X0; res[1] = Y0; res[2] = Z0; res[3] = X1; res[4] = Y1; res[5] = Z1; break;
34192         case 3 : res[3] = cimg::abs(X1-X0); res[4] = cimg::abs(Y1-Y0); res[5] = cimg::abs(Z1-Z0);  // keep no break here !
34193         default : res[0] = X0; res[1] = Y0; res[2] = Z0;
34194         }
34195       }
34196       disp.set_button();
34197       disp._normalization = old_normalization;
34198       disp._is_resized = old_is_resized;
34199       if (key!=~0U) disp.set_key(key);
34200       return res;
34201     }
34202 
34203     //! Select sub-graph in a graph.
34204     CImg<intT> get_select_graph(CImgDisplay &disp,
34205                                 const unsigned int plot_type=1, const unsigned int vertex_type=1,
34206                                 const char *const labelx=0, const double xmin=0, const double xmax=0,
34207                                 const char *const labely=0, const double ymin=0, const double ymax=0) const {
34208       if (is_empty())
34209         throw CImgInstanceException(_cimg_instance
34210                                     "select_graph() : Empty instance.",
34211                                     cimg_instance);
34212       if (!disp) disp.assign(cimg_fitscreen(640,480,1),0,0).set_title("CImg<%s>",pixel_type());
34213       const unsigned long siz = (unsigned long)_width*_height*_depth;
34214       const unsigned int old_normalization = disp.normalization();
34215       disp.show().set_button().set_wheel()._normalization = 0;
34216 
34217       double nymin = ymin, nymax = ymax, nxmin = xmin, nxmax = xmax;
34218       if (nymin==nymax) { nymin = (Tfloat)min_max(nymax); const double dy = nymax - nymin; nymin-=dy/20; nymax+=dy/20; }
34219       if (nymin==nymax) { --nymin; ++nymax; }
34220       if (nxmin==nxmax && nxmin==0) { nxmin = 0; nxmax = siz - 1.0; }
34221 
34222       const unsigned char black[] = { 0, 0, 0 }, white[] = { 255, 255, 255 }, gray[] = { 220, 220, 220 };
34223       const unsigned char gray2[] = { 110, 110, 110 }, ngray[] = { 35, 35, 35 };
34224       static unsigned int odimv = 0;
34225       static CImg<ucharT> colormap;
34226       if (odimv!=_spectrum) {
34227         odimv = _spectrum;
34228         colormap = CImg<ucharT>(3,_spectrum,1,1,120).noise(70,1);
34229         if (_spectrum==1) { colormap[0] = colormap[1] = 120; colormap[2] = 200; }
34230         else {
34231           colormap(0,0) = 220; colormap(1,0) = 10; colormap(2,0) = 10;
34232           if (_spectrum>1) { colormap(0,1) = 10; colormap(1,1) = 220; colormap(2,1) = 10; }
34233           if (_spectrum>2) { colormap(0,2) = 10; colormap(1,2) = 10; colormap(2,2) = 220; }
34234         }
34235       }
34236 
34237       CImg<ucharT> visu0, visu, graph, text, axes;
34238       int x0 = -1, x1 = -1, y0 = -1, y1 = -1, omouse_x = -2, omouse_y = -2;
34239       const unsigned int one = plot_type==3?0:1;
34240       unsigned int okey = 0, obutton = 0;
34241       char message[1024] = { 0 };
34242       CImg_3x3(I,unsigned char);
34243 
34244       for (bool selected = false; !selected && !disp.is_closed() && !okey && !disp.wheel(); ) {
34245         const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y();
34246         const unsigned int key = disp.key(), button = disp.button();
34247 
34248         // Generate graph representation.
34249         if (!visu0) {
34250           visu0.assign(disp.width(),disp.height(),1,3,220);
34251           const int gdimx = disp.width() - 32, gdimy = disp.height() - 32;
34252           if (gdimx>0 && gdimy>0) {
34253             graph.assign(gdimx,gdimy,1,3,255);
34254             if (siz<32) { if (siz>1) graph.draw_grid(gdimx/(float)(siz - one),gdimy/(float)(siz - one),0,0,false,true,black,0.2f,0x33333333,0x33333333); }
34255             else graph.draw_grid(-10,-10,0,0,false,true,black,0.2f,0x33333333,0x33333333);
34256             cimg_forC(*this,c) graph.draw_graph(get_shared_channel(c),&colormap(0,c),(plot_type!=3 || _spectrum==1)?1:0.6f,
34257                                                 plot_type,vertex_type,nymax,nymin);
34258 
34259             axes.assign(gdimx,gdimy,1,1,0);
34260             const float
34261               dx = (float)cimg::abs(nxmax-nxmin), dy = (float)cimg::abs(nymax-nymin),
34262               px = (float)std::pow(10.0,(int)std::log10(dx?dx:1)-2.0),
34263               py = (float)std::pow(10.0,(int)std::log10(dy?dy:1)-2.0);
34264             const CImg<Tdouble>
34265               seqx = dx<=0?CImg<Tdouble>::vector(nxmin):CImg<Tdouble>::sequence(1 + gdimx/60,nxmin,one?nxmax:nxmin+(nxmax-nxmin)*(siz+1)/siz).round(px),
34266               seqy = CImg<Tdouble>::sequence(1 + gdimy/60,nymax,nymin).round(py);
34267 
34268             const bool allow_zero = (nxmin*nxmax>0) || (nymin*nymax>0);
34269             axes.draw_axes(seqx,seqy,white,1,~0U,~0U,13,allow_zero);
34270             if (nymin>0) axes.draw_axis(seqx,gdimy-1,gray,1,~0U,13,allow_zero);
34271             if (nymax<0) axes.draw_axis(seqx,0,gray,1,~0U,13,allow_zero);
34272             if (nxmin>0) axes.draw_axis(0,seqy,gray,1,~0U,13,allow_zero);
34273             if (nxmax<0) axes.draw_axis(gdimx-1,seqy,gray,1,~0U,13,allow_zero);
34274 
34275             cimg_for3x3(axes,x,y,0,0,I,unsigned char)
34276               if (Icc) {
34277                 if (Icc==255) cimg_forC(graph,c) graph(x,y,c) = 0;
34278                 else cimg_forC(graph,c) graph(x,y,c) = (unsigned char)(2*graph(x,y,c)/3);
34279               }
34280               else if (Ipc || Inc || Icp || Icn || Ipp || Inn || Ipn || Inp) cimg_forC(graph,c) graph(x,y,c) = (graph(x,y,c)+511)/3;
34281 
34282             visu0.draw_image(16,16,graph);
34283             visu0.draw_line(15,15,16+gdimx,15,gray2).draw_line(16+gdimx,15,16+gdimx,16+gdimy,gray2).
34284               draw_line(16+gdimx,16+gdimy,15,16+gdimy,white).draw_line(15,16+gdimy,15,15,white);
34285           } else graph.assign();
34286           text.assign().draw_text(0,0,labelx?labelx:"X-axis",white,ngray,1,13).resize(-100,-100,1,3);
34287           visu0.draw_image((visu0.width()-text.width())/2,visu0.height()-14,~text);
34288           text.assign().draw_text(0,0,labely?labely:"Y-axis",white,ngray,1,13).rotate(-90).resize(-100,-100,1,3);
34289           visu0.draw_image(1,(visu0.height()-text.height())/2,~text);
34290           visu.assign();
34291         }
34292 
34293         // Generate and display current view.
34294         if (!visu) {
34295           visu.assign(visu0);
34296           if (graph && x0>=0 && x1>=0) {
34297             const int
34298               nx0 = x0<=x1?x0:x1,
34299               nx1 = x0<=x1?x1:x0,
34300               ny0 = y0<=y1?y0:y1,
34301               ny1 = y0<=y1?y1:y0,
34302               sx0 = 16 + nx0*(visu.width()-32)/cimg::max(1U,siz-one),
34303               sx1 = 15 + (nx1+1)*(visu.width()-32)/cimg::max(1U,siz-one),
34304               sy0 = 16 + ny0,
34305               sy1 = 16 + ny1;
34306             if (y0>=0 && y1>=0)
34307               visu.draw_rectangle(sx0,sy0,sx1,sy1,gray,0.5f).draw_rectangle(sx0,sy0,sx1,sy1,black,0.5f,0xCCCCCCCCU);
34308             else visu.draw_rectangle(sx0,0,sx1,visu.height()-17,gray,0.5f).
34309                    draw_line(sx0,16,sx0,visu.height()-17,black,0.5f,0xCCCCCCCCU).
34310                    draw_line(sx1,16,sx1,visu.height()-17,black,0.5f,0xCCCCCCCCU);
34311           }
34312           if (mouse_x>=16 && mouse_y>=16 && mouse_x<visu.width()-16 && mouse_y<visu.height()-16) {
34313             if (graph) visu.draw_line(mouse_x,16,mouse_x,visu.height()-17,black,0.5f,0x55555555U);
34314             const unsigned int x = (unsigned int)cimg::round((mouse_x-16.0f)*(siz-one)/(disp.width()-32),1,one?0:-1);
34315             const double cx = nxmin + x*(nxmax-nxmin)/cimg::max(1U,siz-1);
34316             if (_spectrum>=7)
34317               cimg_snprintf(message,sizeof(message),"Value[%u:%g] = ( %g %g %g ... %g %g %g )",x,cx,
34318                             (double)(*this)(x,0,0,0),(double)(*this)(x,0,0,1),(double)(*this)(x,0,0,2),
34319                             (double)(*this)(x,0,0,_spectrum-4),(double)(*this)(x,0,0,_spectrum-3),(double)(*this)(x,0,0,_spectrum-1));
34320             else {
34321               cimg_snprintf(message,sizeof(message),"Value[%u:%g] = ( ",x,cx);
34322               cimg_forC(*this,c) std::sprintf(message + std::strlen(message),"%g ",(double)(*this)(x,0,0,c));
34323               std::sprintf(message + std::strlen(message),")");
34324             }
34325             if (x0>=0 && x1>=0) {
34326               const unsigned int
34327                 nx0 = x0<=x1?x0:x1,
34328                 nx1 = x0<=x1?x1:x0,
34329                 ny0 = y0<=y1?y0:y1,
34330                 ny1 = y0<=y1?y1:y0;
34331               const double
34332                 cx0 = nxmin + nx0*(nxmax-nxmin)/cimg::max(1U,siz-1),
34333                 cx1 = nxmin + (nx1+one)*(nxmax-nxmin)/cimg::max(1U,siz-1),
34334                 cy0 = nymax - ny0*(nymax-nymin)/(visu._height-32),
34335                 cy1 = nymax - ny1*(nymax-nymin)/(visu._height-32);
34336               if (y0>=0 && y1>=0)
34337                 std::sprintf(message + std::strlen(message)," - Range ( %u:%g, %g ) - ( %u:%g, %g )",x0,cx0,cy0,x1+one,cx1,cy1);
34338               else
34339                 std::sprintf(message + std::strlen(message)," - Range [ %u:%g - %u:%g ]",x0,cx0,x1+one,cx1);
34340             }
34341             text.assign().draw_text(0,0,message,white,ngray,1,13).resize(-100,-100,1,3);
34342             visu.draw_image((visu.width()-text.width())/2,1,~text);
34343           }
34344           visu.display(disp);
34345         }
34346 
34347         // Test keys.
34348         switch (okey = key) {
34349 #if cimg_OS!=2
34350         case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT :
34351 #endif
34352         case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : okey = 0; break;
34353         case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
34354           disp.set_fullscreen(false).resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
34355                                             CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
34356             _is_resized = true;
34357           disp.set_key(key,false); okey = 0;
34358         } break;
34359         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
34360           disp.set_fullscreen(false).resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
34361           disp.set_key(key,false); okey = 0;
34362         } break;
34363         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
34364           disp.set_fullscreen(false).resize(cimg_fitscreen(640,480,1),false)._is_resized = true;
34365           disp.set_key(key,false); okey = 0;
34366         } break;
34367         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
34368           disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
34369           disp.set_key(key,false); okey = 0;
34370         } break;
34371         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
34372           static unsigned int snap_number = 0;
34373           if (visu || visu0) {
34374             CImg<ucharT> &screen = visu?visu:visu0;
34375             char filename[32] = { 0 };
34376             std::FILE *file;
34377             do {
34378               cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++);
34379               if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
34380             } while (file);
34381             (+screen).draw_text(0,0," Saving snapshot... ",black,gray,1,13).display(disp);
34382             screen.save(filename);
34383             screen.draw_text(0,0," Snapshot '%s' saved. ",black,gray,1,13,filename).display(disp);
34384           }
34385           disp.set_key(key,false); okey = 0;
34386         } break;
34387         case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
34388             static unsigned int snap_number = 0;
34389             if (visu || visu0) {
34390               CImg<ucharT> &screen = visu?visu:visu0;
34391               char filename[32] = { 0 };
34392               std::FILE *file;
34393               do {
34394 #ifdef cimg_use_zlib
34395                 cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++);
34396 #else
34397                 cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++);
34398 #endif
34399                 if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
34400               } while (file);
34401               (+screen).draw_text(0,0," Saving instance... ",black,gray,1,13).display(disp);
34402               save(filename);
34403               screen.draw_text(0,0," Instance '%s' saved. ",black,gray,1,13,filename).display(disp);
34404             }
34405             disp.set_key(key,false); okey = 0;
34406           } break;
34407         }
34408 
34409         // Handle mouse motion and mouse buttons
34410         if (obutton!=button || omouse_x!=mouse_x || omouse_y!=mouse_y) {
34411           visu.assign();
34412           if (disp.mouse_x()>=0 && disp.mouse_y()>=0) {
34413             const int
34414               mx = (mouse_x -16)*(int)(siz-one)/(disp.width()-32),
34415               cx = mx<0?0:(mx>=(int)(siz-one)?(int)(siz-1-one):mx),
34416               my = mouse_y - 16,
34417               cy = my<=0?0:(my>=(disp.height()-32)?(disp.height()-32):my);
34418             if (button&1) {
34419               if (!obutton) { x0 = cx; y0 = -1; } else { x1 = cx; y1 = -1; }
34420             }
34421             else if (button&2) {
34422               if (!obutton) { x0 = cx; y0 = cy; } else { x1 = cx; y1 = cy; }
34423             }
34424             else if (obutton) { x1 = x1>=0?cx:-1; y1 = y1>=0?cy:-1; selected = true; }
34425           } else if (!button && obutton) selected = true;
34426           obutton = button; omouse_x = mouse_x; omouse_y = mouse_y;
34427         }
34428         if (disp.is_resized()) { disp.resize(false); visu0.assign(); }
34429         if (visu && visu0) disp.wait();
34430       }
34431 
34432       disp._normalization = old_normalization;
34433       if (x1>=0 && x1<x0) cimg::swap(x0,x1);
34434       if (y1<y0) cimg::swap(y0,y1);
34435       disp.set_key(okey);
34436       return CImg<intT>(4,1,1,1,x0,y0,x1>=0?x1+(int)one:-1,y1);
34437     }
34438 
34439     //! Load image from a file.
34440     /**
34441        \param filename Filename, as a C-string.
34442        \note The extension of \c filename defines the file format. If no filename
34443        extension is provided, CImg<T>::get_load() will try to load the file as a .cimg or .cimgz file.
34444     **/
34445     CImg<T>& load(const char *const filename) {
34446       if (!filename)
34447         throw CImgArgumentException(_cimg_instance
34448                                     "load() : Specified filename is (null).",
34449                                     cimg_instance);
34450 
34451       if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) {
34452         char filename_local[1024] = { 0 };
34453         load(cimg::load_network_external(filename,filename_local));
34454         std::remove(filename_local);
34455         return *this;
34456       }
34457 
34458       const char *const ext = cimg::split_filename(filename);
34459       const unsigned int omode = cimg::exception_mode();
34460       cimg::exception_mode() = 0;
34461       try {
34462 #ifdef cimg_load_plugin
34463         cimg_load_plugin(filename);
34464 #endif
34465 #ifdef cimg_load_plugin1
34466         cimg_load_plugin1(filename);
34467 #endif
34468 #ifdef cimg_load_plugin2
34469         cimg_load_plugin2(filename);
34470 #endif
34471 #ifdef cimg_load_plugin3
34472         cimg_load_plugin3(filename);
34473 #endif
34474 #ifdef cimg_load_plugin4
34475         cimg_load_plugin4(filename);
34476 #endif
34477 #ifdef cimg_load_plugin5
34478         cimg_load_plugin5(filename);
34479 #endif
34480 #ifdef cimg_load_plugin6
34481         cimg_load_plugin6(filename);
34482 #endif
34483 #ifdef cimg_load_plugin7
34484         cimg_load_plugin7(filename);
34485 #endif
34486 #ifdef cimg_load_plugin8
34487         cimg_load_plugin8(filename);
34488 #endif
34489         // Ascii formats
34490         if (!cimg::strcasecmp(ext,"asc")) load_ascii(filename);
34491         else if (!cimg::strcasecmp(ext,"dlm") ||
34492                  !cimg::strcasecmp(ext,"txt")) load_dlm(filename);
34493 
34494         // 2d binary formats
34495         else if (!cimg::strcasecmp(ext,"bmp")) load_bmp(filename);
34496         else if (!cimg::strcasecmp(ext,"jpg") ||
34497                  !cimg::strcasecmp(ext,"jpeg") ||
34498                  !cimg::strcasecmp(ext,"jpe") ||
34499                  !cimg::strcasecmp(ext,"jfif") ||
34500                  !cimg::strcasecmp(ext,"jif")) load_jpeg(filename);
34501         else if (!cimg::strcasecmp(ext,"png")) load_png(filename);
34502         else if (!cimg::strcasecmp(ext,"ppm") ||
34503                  !cimg::strcasecmp(ext,"pgm") ||
34504                  !cimg::strcasecmp(ext,"pnm") ||
34505                  !cimg::strcasecmp(ext,"pbm") ||
34506                  !cimg::strcasecmp(ext,"pnk")) load_pnm(filename);
34507         else if (!cimg::strcasecmp(ext,"pfm")) load_pfm(filename);
34508         else if (!cimg::strcasecmp(ext,"tif") ||
34509                  !cimg::strcasecmp(ext,"tiff")) load_tiff(filename);
34510         else if (!cimg::strcasecmp(ext,"exr")) load_exr(filename);
34511         else if (!cimg::strcasecmp(ext,"cr2") ||
34512                  !cimg::strcasecmp(ext,"crw") ||
34513                  !cimg::strcasecmp(ext,"dcr") ||
34514                  !cimg::strcasecmp(ext,"mrw") ||
34515                  !cimg::strcasecmp(ext,"nef") ||
34516                  !cimg::strcasecmp(ext,"orf") ||
34517                  !cimg::strcasecmp(ext,"pix") ||
34518                  !cimg::strcasecmp(ext,"ptx") ||
34519                  !cimg::strcasecmp(ext,"raf") ||
34520                  !cimg::strcasecmp(ext,"srf")) load_dcraw_external(filename);
34521 
34522         // 3d binary formats
34523         else if (!cimg::strcasecmp(ext,"dcm") ||
34524                  !cimg::strcasecmp(ext,"dicom")) load_medcon_external(filename);
34525         else if (!cimg::strcasecmp(ext,"hdr") ||
34526                  !cimg::strcasecmp(ext,"nii")) load_analyze(filename);
34527         else if (!cimg::strcasecmp(ext,"par") ||
34528                  !cimg::strcasecmp(ext,"rec")) load_parrec(filename);
34529         else if (!cimg::strcasecmp(ext,"mnc")) load_minc2(filename);
34530         else if (!cimg::strcasecmp(ext,"inr")) load_inr(filename);
34531         else if (!cimg::strcasecmp(ext,"pan")) load_pandore(filename);
34532         else if (!cimg::strcasecmp(ext,"cimg") ||
34533                  !cimg::strcasecmp(ext,"cimgz") ||
34534                  !*ext)  return load_cimg(filename);
34535 
34536         // Archive files
34537         else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename);
34538 
34539         // Image sequences
34540         else if (!cimg::strcasecmp(ext,"avi") ||
34541                  !cimg::strcasecmp(ext,"mov") ||
34542                  !cimg::strcasecmp(ext,"asf") ||
34543                  !cimg::strcasecmp(ext,"divx") ||
34544                  !cimg::strcasecmp(ext,"flv") ||
34545                  !cimg::strcasecmp(ext,"mpg") ||
34546                  !cimg::strcasecmp(ext,"m1v") ||
34547                  !cimg::strcasecmp(ext,"m2v") ||
34548                  !cimg::strcasecmp(ext,"m4v") ||
34549                  !cimg::strcasecmp(ext,"mjp") ||
34550                  !cimg::strcasecmp(ext,"mkv") ||
34551                  !cimg::strcasecmp(ext,"mpe") ||
34552                  !cimg::strcasecmp(ext,"movie") ||
34553                  !cimg::strcasecmp(ext,"ogm") ||
34554                  !cimg::strcasecmp(ext,"ogg") ||
34555                  !cimg::strcasecmp(ext,"qt") ||
34556                  !cimg::strcasecmp(ext,"rm") ||
34557                  !cimg::strcasecmp(ext,"vob") ||
34558                  !cimg::strcasecmp(ext,"wmv") ||
34559                  !cimg::strcasecmp(ext,"xvid") ||
34560                  !cimg::strcasecmp(ext,"mpeg")) load_ffmpeg(filename);
34561         else throw CImgIOException("CImg<%s>::load()",
34562                                    pixel_type());
34563       } catch (CImgIOException&) {
34564         std::FILE *file = 0;
34565         try {
34566           file = cimg::fopen(filename,"rb");
34567         } catch (CImgIOException&) {
34568           cimg::exception_mode() = omode;
34569           throw CImgIOException(_cimg_instance
34570                                 "load() : Failed to open file '%s'.",
34571                                 cimg_instance,
34572                                 filename);
34573         }
34574         try {
34575           const char *const f_type = cimg::file_type(file,filename);
34576           std::fclose(file);
34577           if (!cimg::strcasecmp(f_type,"pnm")) load_pnm(filename);
34578           else if (!cimg::strcasecmp(f_type,"pfm")) load_pfm(filename);
34579           else if (!cimg::strcasecmp(f_type,"bmp")) load_bmp(filename);
34580           else if (!cimg::strcasecmp(f_type,"jpg")) load_jpeg(filename);
34581           else if (!cimg::strcasecmp(f_type,"pan")) load_pandore(filename);
34582           else if (!cimg::strcasecmp(f_type,"png")) load_png(filename);
34583           else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename);
34584           else if (!cimg::strcasecmp(f_type,"inr")) load_inr(filename);
34585           else if (!cimg::strcasecmp(f_type,"dcm")) load_medcon_external(filename);
34586           else throw CImgIOException("CImg<%s>::load()",
34587                                      pixel_type());
34588         } catch (CImgIOException&) {
34589           try {
34590             load_other(filename);
34591           } catch (CImgIOException&) {
34592             cimg::exception_mode() = omode;
34593             throw CImgIOException(_cimg_instance
34594                                   "load() : Failed to recognize format of file '%s'.",
34595                                   cimg_instance,
34596                                   filename);
34597           }
34598         }
34599       }
34600       cimg::exception_mode() = omode;
34601       return *this;
34602     }
34603 
34604     //! Load image from a file \newinstance.
34605     static CImg<T> get_load(const char *const filename) {
34606       return CImg<T>().load(filename);
34607     }
34608 
34609     //! Load image from an ascii file.
34610     /**
34611        \param filename Filename, as a C -string.
34612     **/
34613     CImg<T>& load_ascii(const char *const filename) {
34614       return _load_ascii(0,filename);
34615     }
34616 
34617     //! Load image from an ascii file \inplace.
34618     static CImg<T> get_load_ascii(const char *const filename) {
34619       return CImg<T>().load_ascii(filename);
34620     }
34621 
34622     //! Load image from an ascii file \overloading.
34623     CImg<T>& load_ascii(std::FILE *const file) {
34624       return _load_ascii(file,0);
34625     }
34626 
34627     //! Loadimage from an ascii file \newinstance.
34628     static CImg<T> get_load_ascii(std::FILE *const file) {
34629       return CImg<T>().load_ascii(file);
34630     }
34631 
34632     CImg<T>& _load_ascii(std::FILE *const file, const char *const filename) {
34633       if (!file && !filename)
34634         throw CImgArgumentException(_cimg_instance
34635                                     "load_ascii() : Specified filename is (null).",
34636                                     cimg_instance);
34637 
34638       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
34639       char line[256] = { 0 };
34640       int err = std::fscanf(nfile,"%255[^\n]",line);
34641       unsigned int dx = 0, dy = 1, dz = 1, dc = 1;
34642       std::sscanf(line,"%u%*c%u%*c%u%*c%u",&dx,&dy,&dz,&dc);
34643       err = std::fscanf(nfile,"%*[^0-9.eE+-]");
34644       if (!dx || !dy || !dz || !dc) {
34645         if (!file) cimg::fclose(nfile);
34646         throw CImgIOException(_cimg_instance
34647                               "load_ascii() : Invalid ascii header in file '%s', image dimensions are set to (%u,%u,%u,%u).",
34648                               cimg_instance,
34649                               filename?filename:"(FILE*)",dx,dy,dz,dc);
34650       }
34651       assign(dx,dy,dz,dc);
34652       const unsigned long siz = size();
34653       unsigned long off = 0;
34654       double val;
34655       T *ptr = _data;
34656       for (err = 1, off = 0; off<siz && err==1; ++off) {
34657         err = std::fscanf(nfile,"%lf%*[^0-9.eE+-]",&val);
34658         *(ptr++) = (T)val;
34659       }
34660       if (err!=1)
34661         cimg::warn(_cimg_instance
34662                    "load_ascii() : Only %lu/%lu values read from file '%s'.",
34663                    cimg_instance,
34664                    off-1,siz,filename?filename:"(FILE*)");
34665 
34666       if (!file) cimg::fclose(nfile);
34667       return *this;
34668     }
34669 
34670     //! Load image from a DLM file.
34671     /**
34672       \param filename Filename, as a C-string.
34673     **/
34674     CImg<T>& load_dlm(const char *const filename) {
34675       return _load_dlm(0,filename);
34676     }
34677 
34678     //! Load image from a DLM file \newinstance.
34679     static CImg<T> get_load_dlm(const char *const filename) {
34680       return CImg<T>().load_dlm(filename);
34681     }
34682 
34683     //! Load image from a DLM file \overloading.
34684     CImg<T>& load_dlm(std::FILE *const file) {
34685       return _load_dlm(file,0);
34686     }
34687 
34688     //! Load image from a DLM file \newinstance.
34689     static CImg<T> get_load_dlm(std::FILE *const file) {
34690       return CImg<T>().load_dlm(file);
34691     }
34692 
34693     CImg<T>& _load_dlm(std::FILE *const file, const char *const filename) {
34694       if (!file && !filename)
34695         throw CImgArgumentException(_cimg_instance
34696                                     "load_dlm() : Specified filename is (null).",
34697                                     cimg_instance);
34698 
34699       std::FILE *const nfile = file?file:cimg::fopen(filename,"r");
34700       char delimiter[256] = { 0 }, tmp[256] = { 0 };
34701       unsigned int cdx = 0, dx = 0, dy = 0;
34702       int err = 0;
34703       double val;
34704       assign(256,256);
34705       while ((err = std::fscanf(nfile,"%lf%255[^0-9.+-]",&val,delimiter))>0) {
34706         if (err>0) (*this)(cdx++,dy) = (T)val;
34707         if (cdx>=_width) resize(3*_width/2,_height,1,1,0);
34708         char c = 0;
34709         if (!std::sscanf(delimiter,"%255[^\n]%c",tmp,&c) || c=='\n') {
34710           dx = cimg::max(cdx,dx);
34711           if (++dy>=_height) resize(_width,3*_height/2,1,1,0);
34712           cdx = 0;
34713         }
34714       }
34715       if (cdx && err==1) { dx = cdx; ++dy; }
34716       if (!dx || !dy) {
34717         if (!file) cimg::fclose(nfile);
34718         throw CImgIOException(_cimg_instance
34719                               "load_dlm() : Invalid DLM file '%s'.",
34720                               cimg_instance,
34721                               filename?filename:"(FILE*)");
34722       }
34723       resize(dx,dy,1,1,0);
34724       if (!file) cimg::fclose(nfile);
34725       return *this;
34726     }
34727 
34728     //! Load image from a BMP file.
34729     /**
34730        \param filename Filename, as a C-string.
34731     **/
34732     CImg<T>& load_bmp(const char *const filename) {
34733       return _load_bmp(0,filename);
34734     }
34735 
34736     //! Load image from a BMP file \newinstance.
34737     static CImg<T> get_load_bmp(const char *const filename) {
34738       return CImg<T>().load_bmp(filename);
34739     }
34740 
34741     //! Load image from a BMP file \overloading.
34742     CImg<T>& load_bmp(std::FILE *const file) {
34743       return _load_bmp(file,0);
34744     }
34745 
34746     //! Load image from a BMP file \newinstance.
34747     static CImg<T> get_load_bmp(std::FILE *const file) {
34748       return CImg<T>().load_bmp(file);
34749     }
34750 
34751     CImg<T>& _load_bmp(std::FILE *const file, const char *const filename) {
34752       if (!file && !filename)
34753         throw CImgArgumentException(_cimg_instance
34754                                     "load_bmp() : Specified filename is (null).",
34755                                     cimg_instance);
34756 
34757       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
34758       unsigned char header[64] = { 0 };
34759       cimg::fread(header,54,nfile);
34760       if (*header!='B' || header[1]!='M') {
34761         if (!file) cimg::fclose(nfile);
34762         throw CImgIOException(_cimg_instance
34763                               "load_bmp() : Invalid BMP file '%s'.",
34764                               cimg_instance,
34765                               filename?filename:"(FILE*)");
34766       }
34767 
34768       // Read header and pixel buffer
34769       int
34770         file_size = header[0x02] + (header[0x03]<<8) + (header[0x04]<<16) + (header[0x05]<<24),
34771         offset = header[0x0A] + (header[0x0B]<<8) + (header[0x0C]<<16) + (header[0x0D]<<24),
34772         dx = header[0x12] + (header[0x13]<<8) + (header[0x14]<<16) + (header[0x15]<<24),
34773         dy = header[0x16] + (header[0x17]<<8) + (header[0x18]<<16) + (header[0x19]<<24),
34774         compression = header[0x1E] + (header[0x1F]<<8) + (header[0x20]<<16) + (header[0x21]<<24),
34775         nb_colors = header[0x2E] + (header[0x2F]<<8) + (header[0x30]<<16) + (header[0x31]<<24),
34776         bpp = header[0x1C] + (header[0x1D]<<8);
34777 
34778       if (!file_size || file_size==offset) {
34779         std::fseek(nfile,0,SEEK_END);
34780         file_size = (int)std::ftell(nfile);
34781         std::fseek(nfile,54,SEEK_SET);
34782       }
34783 
34784       const int
34785         cimg_iobuffer = 12*1024*1024,
34786         dx_bytes = (bpp==1)?(dx/8+(dx%8?1:0)):((bpp==4)?(dx/2+(dx%2?1:0)):(dx*bpp/8)),
34787         align_bytes = (4-dx_bytes%4)%4,
34788         buf_size = cimg::min(cimg::abs(dy)*(dx_bytes + align_bytes),file_size - offset);
34789 
34790       CImg<intT> colormap;
34791       if (bpp<16) { if (!nb_colors) nb_colors = 1<<bpp; } else nb_colors = 0;
34792       if (nb_colors) { colormap.assign(nb_colors); cimg::fread(colormap._data,nb_colors,nfile); }
34793       const int xoffset = offset - 54 - 4*nb_colors;
34794       if (xoffset>0) std::fseek(nfile,xoffset,SEEK_CUR);
34795 
34796       CImg<ucharT> buffer;
34797       if (buf_size<cimg_iobuffer) { buffer.assign(buf_size); cimg::fread(buffer._data,buf_size,nfile); }
34798       else buffer.assign(dx_bytes + align_bytes);
34799       unsigned char *ptrs = buffer;
34800 
34801       // Decompress buffer (if necessary)
34802       if (compression) {
34803         if (file)
34804           throw CImgIOException(_cimg_instance
34805                                 "load_bmp() : Unable to load compressed data from '(*FILE)' inputs.",
34806                                 cimg_instance);
34807         else {
34808           if (!file) cimg::fclose(nfile);
34809           return load_other(filename);
34810         }
34811       }
34812 
34813       // Read pixel data
34814       assign(dx,cimg::abs(dy),1,3);
34815       switch (bpp) {
34816       case 1 : { // Monochrome
34817         for (int y = height()-1; y>=0; --y) {
34818           if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); }
34819           unsigned char mask = 0x80, val = 0;
34820           cimg_forX(*this,x) {
34821             if (mask==0x80) val = *(ptrs++);
34822             const unsigned char *col = (unsigned char*)(colormap._data + (val&mask?1:0));
34823             (*this)(x,y,2) = (T)*(col++);
34824             (*this)(x,y,1) = (T)*(col++);
34825             (*this)(x,y,0) = (T)*(col++);
34826             mask = cimg::ror(mask);
34827           }
34828           ptrs+=align_bytes;
34829         }
34830       } break;
34831       case 4 : { // 16 colors
34832         for (int y = height()-1; y>=0; --y) {
34833           if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); }
34834           unsigned char mask = 0xF0, val = 0;
34835           cimg_forX(*this,x) {
34836             if (mask==0xF0) val = *(ptrs++);
34837             const unsigned char color = (unsigned char)((mask<16)?(val&mask):((val&mask)>>4));
34838             const unsigned char *col = (unsigned char*)(colormap._data + color);
34839             (*this)(x,y,2) = (T)*(col++);
34840             (*this)(x,y,1) = (T)*(col++);
34841             (*this)(x,y,0) = (T)*(col++);
34842             mask = cimg::ror(mask,4);
34843           }
34844           ptrs+=align_bytes;
34845         }
34846       } break;
34847       case 8 : { //  256 colors
34848         for (int y = height()-1; y>=0; --y) {
34849           if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); }
34850           cimg_forX(*this,x) {
34851             const unsigned char *col = (unsigned char*)(colormap._data + *(ptrs++));
34852             (*this)(x,y,2) = (T)*(col++);
34853             (*this)(x,y,1) = (T)*(col++);
34854             (*this)(x,y,0) = (T)*(col++);
34855           }
34856           ptrs+=align_bytes;
34857         }
34858       } break;
34859       case 16 : { // 16 bits colors
34860         for (int y = height()-1; y>=0; --y) {
34861           if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); }
34862           cimg_forX(*this,x) {
34863             const unsigned char c1 = *(ptrs++), c2 = *(ptrs++);
34864             const unsigned short col = (unsigned short)(c1|(c2<<8));
34865             (*this)(x,y,2) = (T)(col&0x1F);
34866             (*this)(x,y,1) = (T)((col>>5)&0x1F);
34867             (*this)(x,y,0) = (T)((col>>10)&0x1F);
34868           }
34869           ptrs+=align_bytes;
34870         }
34871       } break;
34872       case 24 : { // 24 bits colors
34873         for (int y = height()-1; y>=0; --y) {
34874           if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); }
34875           cimg_forX(*this,x) {
34876             (*this)(x,y,2) = (T)*(ptrs++);
34877             (*this)(x,y,1) = (T)*(ptrs++);
34878             (*this)(x,y,0) = (T)*(ptrs++);
34879           }
34880           ptrs+=align_bytes;
34881         }
34882       } break;
34883       case 32 : { // 32 bits colors
34884         for (int y = height()-1; y>=0; --y) {
34885           if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); }
34886           cimg_forX(*this,x) {
34887             (*this)(x,y,2) = (T)*(ptrs++);
34888             (*this)(x,y,1) = (T)*(ptrs++);
34889             (*this)(x,y,0) = (T)*(ptrs++);
34890             ++ptrs;
34891           }
34892           ptrs+=align_bytes;
34893         }
34894       } break;
34895       }
34896       if (dy<0) mirror('y');
34897       if (!file) cimg::fclose(nfile);
34898       return *this;
34899     }
34900 
34901     //! Load image from a JPEG file.
34902     /**
34903        \param filename Filename, as a C-string.
34904     **/
34905     CImg<T>& load_jpeg(const char *const filename) {
34906       return _load_jpeg(0,filename);
34907     }
34908 
34909     //! Load image from a JPEG file \newinstance.
34910     static CImg<T> get_load_jpeg(const char *const filename) {
34911       return CImg<T>().load_jpeg(filename);
34912     }
34913 
34914     //! Load image from a JPEG file \overloading.
34915     CImg<T>& load_jpeg(std::FILE *const file) {
34916       return _load_jpeg(file,0);
34917     }
34918 
34919     //! Load image from a JPEG file \newinstance.
34920     static CImg<T> get_load_jpeg(std::FILE *const file) {
34921       return CImg<T>().load_jpeg(file);
34922     }
34923 
34924     // Custom error handler for libjpeg.
34925 #ifdef cimg_use_jpeg
34926     struct _cimg_error_mgr {
34927       struct jpeg_error_mgr original;
34928       jmp_buf setjmp_buffer;
34929       char message[JMSG_LENGTH_MAX];
34930     };
34931 
34932     typedef struct _cimg_error_mgr *_cimg_error_ptr;
34933 
34934     METHODDEF(void) _cimg_jpeg_error_exit(j_common_ptr cinfo) {
34935       _cimg_error_ptr c_err = (_cimg_error_ptr) cinfo->err;  // Return control to the setjmp point
34936       (*cinfo->err->format_message)(cinfo,c_err->message);
34937       jpeg_destroy(cinfo);  // Clean memory and temp files.
34938       longjmp(c_err->setjmp_buffer,1);
34939     }
34940 #endif
34941 
34942     CImg<T>& _load_jpeg(std::FILE *const file, const char *const filename) {
34943       if (!file && !filename)
34944         throw CImgArgumentException(_cimg_instance
34945                                     "load_jpeg() : Specified filename is (null).",
34946                                     cimg_instance);
34947 
34948 #ifndef cimg_use_jpeg
34949       if (file)
34950         throw CImgIOException(_cimg_instance
34951                               "load_jpeg() : Unable to load data from '(FILE*)' unless libjpeg is enabled.",
34952                               cimg_instance);
34953       else return load_other(filename);
34954 #else
34955 
34956       struct jpeg_decompress_struct cinfo;
34957       struct _cimg_error_mgr jerr;
34958       cinfo.err = jpeg_std_error(&jerr.original);
34959       jerr.original.error_exit = _cimg_jpeg_error_exit;
34960 
34961       if (setjmp(jerr.setjmp_buffer)) { // JPEG error
34962         throw CImgIOException(_cimg_instance
34963                              "load_jpeg() : Error message returned by libjpeg : %s.",
34964                              cimg_instance,jerr.message);
34965       }
34966 
34967       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
34968       jpeg_create_decompress(&cinfo);
34969       jpeg_stdio_src(&cinfo,nfile);
34970       jpeg_read_header(&cinfo,TRUE);
34971       jpeg_start_decompress(&cinfo);
34972 
34973       if (cinfo.output_components!=1 && cinfo.output_components!=3 && cinfo.output_components!=4) {
34974         if (!file) return load_other(filename);
34975         else
34976           throw CImgIOException(_cimg_instance
34977                                 "load_jpeg() : Failed to load JPEG data from file '%s'.",
34978                                 cimg_instance,filename?filename:"(FILE*)");
34979       }
34980       CImg<ucharT> buffer(cinfo.output_width*cinfo.output_components);
34981       JSAMPROW row_pointer[1];
34982       assign(cinfo.output_width,cinfo.output_height,1,cinfo.output_components);
34983       T *ptr_r = _data, *ptr_g = _data + 1UL*_width*_height, *ptr_b = _data + 2UL*_width*_height, *ptr_a = _data + 3UL*_width*_height;
34984       while (cinfo.output_scanline<cinfo.output_height) {
34985         *row_pointer = buffer._data;
34986         if (jpeg_read_scanlines(&cinfo,row_pointer,1)!=1) {
34987           cimg::warn(_cimg_instance
34988                      "load_jpeg() : Incomplete data in file '%s'.",
34989                      cimg_instance,filename?filename:"(FILE*)");
34990           break;
34991         }
34992         const unsigned char *ptrs = buffer._data;
34993         switch (_spectrum) {
34994         case 1 : {
34995           cimg_forX(*this,x) *(ptr_r++) = (T)*(ptrs++);
34996         } break;
34997         case 3 : {
34998           cimg_forX(*this,x) {
34999             *(ptr_r++) = (T)*(ptrs++);
35000             *(ptr_g++) = (T)*(ptrs++);
35001             *(ptr_b++) = (T)*(ptrs++);
35002           }
35003         } break;
35004         case 4 : {
35005           cimg_forX(*this,x) {
35006             *(ptr_r++) = (T)*(ptrs++);
35007             *(ptr_g++) = (T)*(ptrs++);
35008             *(ptr_b++) = (T)*(ptrs++);
35009             *(ptr_a++) = (T)*(ptrs++);
35010           }
35011         } break;
35012         }
35013       }
35014       jpeg_finish_decompress(&cinfo);
35015       jpeg_destroy_decompress(&cinfo);
35016       if (!file) cimg::fclose(nfile);
35017       return *this;
35018 #endif
35019     }
35020 
35021     //! Load image from a file, using Magick++ library.
35022     /**
35023        \param filename Filename, as a C-string.
35024     **/
35025     // Added April/may 2006 by Christoph Hormann <chris_hormann@gmx.de>
35026     //   This is experimental code, not much tested, use with care.
35027     CImg<T>& load_magick(const char *const filename) {
35028       if (!filename)
35029         throw CImgArgumentException(_cimg_instance
35030                                     "load_magick() : Specified filename is (null).",
35031                                     cimg_instance);
35032 
35033 #ifdef cimg_use_magick
35034       Magick::Image image(filename);
35035       const unsigned int W = image.size().width(), H = image.size().height();
35036       switch (image.type()) {
35037       case Magick::PaletteMatteType :
35038       case Magick::TrueColorMatteType :
35039       case Magick::ColorSeparationType : {
35040         assign(W,H,1,4);
35041         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);
35042         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
35043         for (unsigned long off = (unsigned long)W*H; off; --off) {
35044           *(ptr_r++) = (T)(pixels->red);
35045           *(ptr_g++) = (T)(pixels->green);
35046           *(ptr_b++) = (T)(pixels->blue);
35047           *(ptr_a++) = (T)(pixels->opacity);
35048           ++pixels;
35049         }
35050       } break;
35051       case Magick::PaletteType :
35052       case Magick::TrueColorType : {
35053         assign(W,H,1,3);
35054         T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
35055         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
35056         for (unsigned long off = (unsigned long)W*H; off; --off) {
35057           *(ptr_r++) = (T)(pixels->red);
35058           *(ptr_g++) = (T)(pixels->green);
35059           *(ptr_b++) = (T)(pixels->blue);
35060           ++pixels;
35061         }
35062       } break;
35063       case Magick::GrayscaleMatteType : {
35064         assign(W,H,1,2);
35065         T *ptr_r = data(0,0,0,0), *ptr_a = data(0,0,0,1);
35066         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
35067         for (unsigned long off = (unsigned long)W*H; off; --off) {
35068           *(ptr_r++) = (T)(pixels->red);
35069           *(ptr_a++) = (T)(pixels->opacity);
35070           ++pixels;
35071         }
35072       } break;
35073       default : {
35074         assign(W,H,1,1);
35075         T *ptr_r = data(0,0,0,0);
35076         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
35077         for (unsigned long off = (unsigned long)W*H; off; --off) {
35078           *(ptr_r++) = (T)(pixels->red);
35079           ++pixels;
35080         }
35081       }
35082       }
35083 #else
35084       throw CImgIOException(_cimg_instance
35085                             "load_magick() : Unable to load file '%s' unless libMagick++ is enabled.",
35086                             cimg_instance,
35087                             filename);
35088 #endif
35089       return *this;
35090     }
35091 
35092     //! Load image from a file, using Magick++ library \newinstance.
35093     static CImg<T> get_load_magick(const char *const filename) {
35094       return CImg<T>().load_magick(filename);
35095     }
35096 
35097     //! Load image from a PNG file.
35098     /**
35099        \param filename Filename, as a C-string.
35100     **/
35101     CImg<T>& load_png(const char *const filename) {
35102       return _load_png(0,filename);
35103     }
35104 
35105     //! Load image from a PNG file \newinstance.
35106     static CImg<T> get_load_png(const char *const filename) {
35107       return CImg<T>().load_png(filename);
35108     }
35109 
35110     //! Load image from a PNG file \overloading.
35111     CImg<T>& load_png(std::FILE *const file) {
35112       return _load_png(file,0);
35113     }
35114 
35115     //! Load image from a PNG file \newinstance.
35116     static CImg<T> get_load_png(std::FILE *const file) {
35117       return CImg<T>().load_png(file);
35118     }
35119 
35120     // (Note : Most of this function has been written by Eric Fausett)
35121     CImg<T>& _load_png(std::FILE *const file, const char *const filename) {
35122       if (!file && !filename)
35123         throw CImgArgumentException(_cimg_instance
35124                                     "load_png() : Specified filename is (null).",
35125                                     cimg_instance);
35126 
35127 #ifndef cimg_use_png
35128       if (file)
35129         throw CImgIOException(_cimg_instance
35130                               "load_png() : Unable to load data from '(FILE*)' unless libpng is enabled.",
35131                               cimg_instance);
35132 
35133       else return load_other(filename);
35134 #else
35135       // Open file and check for PNG validity
35136       const char *volatile nfilename = filename; // two 'volatile' here to remove a g++ warning due to 'setjmp'.
35137       std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"rb");
35138 
35139       unsigned char pngCheck[8] = { 0 };
35140       cimg::fread(pngCheck,8,(std::FILE*)nfile);
35141       if (png_sig_cmp(pngCheck,0,8)) {
35142         if (!file) cimg::fclose(nfile);
35143         throw CImgIOException(_cimg_instance
35144                               "load_png() : Invalid PNG file '%s'.",
35145                               cimg_instance,
35146                               nfilename?nfilename:"(FILE*)");
35147       }
35148 
35149       // Setup PNG structures for read
35150       png_voidp user_error_ptr = 0;
35151       png_error_ptr user_error_fn = 0, user_warning_fn = 0;
35152       png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,user_error_ptr,user_error_fn,user_warning_fn);
35153       if (!png_ptr) {
35154         if (!file) cimg::fclose(nfile);
35155         throw CImgIOException(_cimg_instance
35156                               "load_png() : Failed to initialize 'png_ptr' structure for file '%s'.",
35157                               cimg_instance,
35158                               nfilename?nfilename:"(FILE*)");
35159       }
35160       png_infop info_ptr = png_create_info_struct(png_ptr);
35161       if (!info_ptr) {
35162         if (!file) cimg::fclose(nfile);
35163         png_destroy_read_struct(&png_ptr,(png_infopp)0,(png_infopp)0);
35164         throw CImgIOException(_cimg_instance
35165                               "load_png() : Failed to initialize 'info_ptr' structure for file '%s'.",
35166                               cimg_instance,
35167                               nfilename?nfilename:"(FILE*)");
35168       }
35169       png_infop end_info = png_create_info_struct(png_ptr);
35170       if (!end_info) {
35171         if (!file) cimg::fclose(nfile);
35172         png_destroy_read_struct(&png_ptr,&info_ptr,(png_infopp)0);
35173         throw CImgIOException(_cimg_instance
35174                               "load_png() : Failed to initialize 'end_info' structure for file '%s'.",
35175                               cimg_instance,
35176                               nfilename?nfilename:"(FILE*)");
35177       }
35178 
35179       // Error handling callback for png file reading
35180       if (setjmp(png_jmpbuf(png_ptr))) {
35181         if (!file) cimg::fclose((std::FILE*)nfile);
35182         png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0);
35183         throw CImgIOException(_cimg_instance
35184                               "load_png() : Encountered unknown fatal error in libpng for file '%s'.",
35185                               cimg_instance,
35186                               nfilename?nfilename:"(FILE*)");
35187       }
35188       png_init_io(png_ptr, nfile);
35189       png_set_sig_bytes(png_ptr, 8);
35190 
35191       // Get PNG Header Info up to data block
35192       png_read_info(png_ptr,info_ptr);
35193       png_uint_32 W, H;
35194       int bit_depth, color_type, interlace_type;
35195       bool is_gray = false;
35196       png_get_IHDR(png_ptr,info_ptr,&W,&H,&bit_depth,&color_type,&interlace_type,(int*)0,(int*)0);
35197 
35198       // Transforms to unify image data
35199       if (color_type==PNG_COLOR_TYPE_PALETTE) {
35200         png_set_palette_to_rgb(png_ptr);
35201         color_type = PNG_COLOR_TYPE_RGB;
35202         bit_depth = 8;
35203       }
35204       if (color_type==PNG_COLOR_TYPE_GRAY && bit_depth<8) {
35205         png_set_expand_gray_1_2_4_to_8(png_ptr);
35206         is_gray = true;
35207         bit_depth = 8;
35208       }
35209       if (png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS)) {
35210         png_set_tRNS_to_alpha(png_ptr);
35211         color_type |= PNG_COLOR_MASK_ALPHA;
35212       }
35213       if (color_type==PNG_COLOR_TYPE_GRAY || color_type==PNG_COLOR_TYPE_GRAY_ALPHA) {
35214         png_set_gray_to_rgb(png_ptr);
35215         color_type |= PNG_COLOR_MASK_COLOR;
35216         is_gray = true;
35217       }
35218       if (color_type==PNG_COLOR_TYPE_RGB)
35219         png_set_filler(png_ptr,0xffffU,PNG_FILLER_AFTER);
35220 
35221       png_read_update_info(png_ptr,info_ptr);
35222       if (bit_depth!=8 && bit_depth!=16) {
35223         if (!file) cimg::fclose(nfile);
35224         png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0);
35225         throw CImgIOException(_cimg_instance
35226                               "load_png() : Invalid bit depth %u in file '%s'.",
35227                               cimg_instance,
35228                               bit_depth,nfilename?nfilename:"(FILE*)");
35229       }
35230       const int byte_depth = bit_depth>>3;
35231 
35232       // Allocate Memory for Image Read
35233       png_bytep *const imgData = new png_bytep[H];
35234       for (unsigned int row = 0; row<H; ++row) imgData[row] = new png_byte[byte_depth*4*W];
35235       png_read_image(png_ptr,imgData);
35236       png_read_end(png_ptr,end_info);
35237 
35238       // Read pixel data
35239       if (color_type!=PNG_COLOR_TYPE_RGB && color_type!=PNG_COLOR_TYPE_RGB_ALPHA) {
35240         if (!file) cimg::fclose(nfile);
35241         png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0);
35242         throw CImgIOException(_cimg_instance
35243                               "load_png() : Invalid color coding type %u in file '%s'.",
35244                               cimg_instance,
35245                               color_type,nfilename?nfilename:"(FILE*)");
35246       }
35247       const bool is_alpha = (color_type==PNG_COLOR_TYPE_RGBA);
35248       assign(W,H,1,(is_gray?1:3) + (is_alpha?1:0));
35249       T
35250         *ptr_r = data(0,0,0,0),
35251         *ptr_g = is_gray?0:data(0,0,0,1),
35252         *ptr_b = is_gray?0:data(0,0,0,2),
35253         *ptr_a = !is_alpha?0:data(0,0,0,is_gray?1:3);
35254       switch (bit_depth) {
35255       case 8 : {
35256         cimg_forY(*this,y) {
35257           const unsigned char *ptrs = (unsigned char*)imgData[y];
35258           cimg_forX(*this,x) {
35259             *(ptr_r++) = (T)*(ptrs++);
35260             if (ptr_g) *(ptr_g++) = (T)*(ptrs++); else ++ptrs;
35261             if (ptr_b) *(ptr_b++) = (T)*(ptrs++); else ++ptrs;
35262             if (ptr_a) *(ptr_a++) = (T)*(ptrs++); else ++ptrs;
35263           }
35264         }
35265       } break;
35266       case 16 : {
35267         cimg_forY(*this,y) {
35268           const unsigned short *ptrs = (unsigned short*)(imgData[y]);
35269           if (!cimg::endianness()) cimg::invert_endianness(ptrs,4*_width);
35270           cimg_forX(*this,x) {
35271             *(ptr_r++) = (T)*(ptrs++);
35272             if (ptr_g) *(ptr_g++) = (T)*(ptrs++); else ++ptrs;
35273             if (ptr_b) *(ptr_b++) = (T)*(ptrs++); else ++ptrs;
35274             if (ptr_a) *(ptr_a++) = (T)*(ptrs++); else ++ptrs;
35275           }
35276         }
35277       } break;
35278       }
35279       png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
35280 
35281       // Deallocate Image Read Memory
35282       cimg_forY(*this,n) delete[] imgData[n];
35283       delete[] imgData;
35284       if (!file) cimg::fclose(nfile);
35285       return *this;
35286 #endif
35287     }
35288 
35289     //! Load image from a PNM file.
35290     /**
35291       \param filename Filename, as a C-string.
35292     **/
35293     CImg<T>& load_pnm(const char *const filename) {
35294       return _load_pnm(0,filename);
35295     }
35296 
35297     //! Load image from a PNM file \newinstance.
35298     static CImg<T> get_load_pnm(const char *const filename) {
35299       return CImg<T>().load_pnm(filename);
35300     }
35301 
35302     //! Load image from a PNM file \overloading.
35303     CImg<T>& load_pnm(std::FILE *const file) {
35304       return _load_pnm(file,0);
35305     }
35306 
35307     //! Load image from a PNM file \newinstance.
35308     static CImg<T> get_load_pnm(std::FILE *const file) {
35309       return CImg<T>().load_pnm(file);
35310     }
35311 
35312     CImg<T>& _load_pnm(std::FILE *const file, const char *const filename) {
35313       if (!file && !filename)
35314         throw CImgArgumentException(_cimg_instance
35315                                     "load_pnm() : Specified filename is (null).",
35316                                     cimg_instance);
35317 
35318       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
35319       unsigned int ppm_type, W, H, D = 1, colormax = 255;
35320       char item[1024] = { 0 };
35321       int err, rval, gval, bval;
35322       const long cimg_iobuffer = 12*1024*1024;
35323       while ((err=std::fscanf(nfile,"%1023[^\n]",item))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
35324       if (std::sscanf(item," P%u",&ppm_type)!=1) {
35325         if (!file) cimg::fclose(nfile);
35326         throw CImgIOException(_cimg_instance
35327                               "load_pnm() : PNM header not found in file '%s'.",
35328                               cimg_instance,
35329                               filename?filename:"(FILE*)");
35330       }
35331       while ((err=std::fscanf(nfile," %1023[^\n]",item))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
35332       if ((err=std::sscanf(item," %u %u %u %u",&W,&H,&D,&colormax))<2) {
35333         if (!file) cimg::fclose(nfile);
35334         throw CImgIOException(_cimg_instance
35335                               "load_pnm() : WIDTH and HEIGHT fields undefined in file '%s'.",
35336                               cimg_instance,
35337                               filename?filename:"(FILE*)");
35338       }
35339       if (ppm_type!=1 && ppm_type!=4) {
35340         if (err==2 || (err==3 && (ppm_type==5 || ppm_type==7 || ppm_type==8 || ppm_type==9))) {
35341           while ((err=std::fscanf(nfile," %1023[^\n]",item))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
35342           if (std::sscanf(item,"%u",&colormax)!=1)
35343             cimg::warn(_cimg_instance
35344                        "load_pnm() : COLORMAX field is undefined in file '%s'.",
35345                        cimg_instance,
35346                        filename?filename:"(FILE*)");
35347         } else { colormax = D; D = 1; }
35348       }
35349       std::fgetc(nfile);
35350 
35351       switch (ppm_type) {
35352       case 1 : { // 2d b&w ascii.
35353         assign(W,H,1,1);
35354         T* ptrd = _data;
35355         cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)(rval?0:255); else break; }
35356       } break;
35357       case 2 : { // 2d grey ascii.
35358         assign(W,H,1,1);
35359         T* ptrd = _data;
35360         cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)rval; else break; }
35361       } break;
35362       case 3 : { // 2d color ascii.
35363         assign(W,H,1,3);
35364         T *ptrd = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
35365         cimg_forXY(*this,x,y) {
35366           if (std::fscanf(nfile,"%d %d %d",&rval,&gval,&bval)==3) { *(ptrd++) = (T)rval; *(ptr_g++) = (T)gval; *(ptr_b++) = (T)bval; }
35367           else break;
35368         }
35369       } break;
35370       case 4 : { // 2d b&w binary (support 3D PINK extension).
35371         CImg<ucharT> raw;
35372         assign(W,H,D,1);
35373         T *ptrd = data(0,0,0,0);
35374         unsigned int w = 0, h = 0, d = 0;
35375         for (long to_read = (long)((W/8 + (W%8?1:0))*H*D); to_read>0; ) {
35376           raw.assign(cimg::min(to_read,cimg_iobuffer));
35377           cimg::fread(raw._data,raw._width,nfile);
35378           to_read-=raw._width;
35379           const unsigned char *ptrs = raw._data;
35380           unsigned char mask = 0, val = 0;
35381           for (unsigned long off = (unsigned long)raw._width; off || mask; mask>>=1) {
35382             if (!mask) { if (off--) val = *(ptrs++); mask = 128; }
35383             *(ptrd++) = (T)((val&mask)?0:255);
35384             if (++w==W) { w = 0; mask = 0; if (++h==H) { h = 0; if (++d==D) break; }}
35385           }
35386         }
35387       } break;
35388       case 5 : case 7 : { // 2d/3d grey binary (support 3D PINK extension).
35389         if (colormax<256) { // 8 bits.
35390           CImg<ucharT> raw;
35391           assign(W,H,D,1);
35392           T *ptrd = data(0,0,0,0);
35393           for (long to_read = (long)size(); to_read>0; ) {
35394             raw.assign(cimg::min(to_read,cimg_iobuffer));
35395             cimg::fread(raw._data,raw._width,nfile);
35396             to_read-=raw._width;
35397             const unsigned char *ptrs = raw._data;
35398             for (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
35399           }
35400         } else { // 16 bits.
35401           CImg<ushortT> raw;
35402           assign(W,H,D,1);
35403           T *ptrd = data(0,0,0,0);
35404           for (long to_read = (long)size(); to_read>0; ) {
35405             raw.assign(cimg::min(to_read,cimg_iobuffer/2));
35406             cimg::fread(raw._data,raw._width,nfile);
35407             if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width);
35408             to_read-=raw._width;
35409             const unsigned short *ptrs = raw._data;
35410             for (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
35411           }
35412         }
35413       } break;
35414       case 6 : { // 2d color binary.
35415         if (colormax<256) { // 8 bits.
35416           CImg<ucharT> raw;
35417           assign(W,H,1,3);
35418           T
35419             *ptr_r = data(0,0,0,0),
35420             *ptr_g = data(0,0,0,1),
35421             *ptr_b = data(0,0,0,2);
35422           for (long to_read = (long)size(); to_read>0; ) {
35423             raw.assign(cimg::min(to_read,cimg_iobuffer));
35424             cimg::fread(raw._data,raw._width,nfile);
35425             to_read-=raw._width;
35426             const unsigned char *ptrs = raw._data;
35427             for (unsigned long off = (unsigned long)raw._width/3; off; --off) {
35428               *(ptr_r++) = (T)*(ptrs++);
35429               *(ptr_g++) = (T)*(ptrs++);
35430               *(ptr_b++) = (T)*(ptrs++);
35431             }
35432           }
35433         } else { // 16 bits.
35434           CImg<ushortT> raw;
35435           assign(W,H,1,3);
35436           T
35437             *ptr_r = data(0,0,0,0),
35438             *ptr_g = data(0,0,0,1),
35439             *ptr_b = data(0,0,0,2);
35440           for (long to_read = (int)size(); to_read>0; ) {
35441             raw.assign(cimg::min(to_read,cimg_iobuffer/2));
35442             cimg::fread(raw._data,raw._width,nfile);
35443             if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width);
35444             to_read-=raw._width;
35445             const unsigned short *ptrs = raw._data;
35446             for (unsigned long off = (unsigned long)raw._width/3; off; --off) {
35447               *(ptr_r++) = (T)*(ptrs++);
35448               *(ptr_g++) = (T)*(ptrs++);
35449               *(ptr_b++) = (T)*(ptrs++);
35450             }
35451           }
35452         }
35453       } break;
35454       case 8 : { // 2d/3d grey binary with int32 integers (PINK extension).
35455         CImg<intT> raw;
35456         assign(W,H,D,1);
35457         T *ptrd = data(0,0,0,0);
35458         for (long to_read = (long)size(); to_read>0; ) {
35459           raw.assign(cimg::min(to_read,cimg_iobuffer));
35460           cimg::fread(raw._data,raw._width,nfile);
35461           to_read-=raw._width;
35462           const int *ptrs = raw._data;
35463           for (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
35464         }
35465       } break;
35466       case 9 : { // 2d/3d grey binary with float values (PINK extension).
35467         CImg<floatT> raw;
35468         assign(W,H,D,1);
35469         T *ptrd = data(0,0,0,0);
35470         for (long to_read = (long)size(); to_read>0; ) {
35471           raw.assign(cimg::min(to_read,cimg_iobuffer));
35472           cimg::fread(raw._data,raw._width,nfile);
35473           to_read-=raw._width;
35474           const float *ptrs = raw._data;
35475           for (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
35476         }
35477       } break;
35478       default :
35479         assign();
35480         if (!file) cimg::fclose(nfile);
35481         throw CImgIOException(_cimg_instance
35482                               "load_pnm() : PNM type 'P%d' found, but type is not supported.",
35483                               cimg_instance,
35484                               filename?filename:"(FILE*)",ppm_type);
35485       }
35486       if (!file) cimg::fclose(nfile);
35487       return *this;
35488     }
35489 
35490     //! Load image from a PFM file.
35491     /**
35492       \param filename Filename, as a C-string.
35493     **/
35494     CImg<T>& load_pfm(const char *const filename) {
35495       return _load_pfm(0,filename);
35496     }
35497 
35498     //! Load image from a PFM file \newinstance.
35499     static CImg<T> get_load_pfm(const char *const filename) {
35500       return CImg<T>().load_pfm(filename);
35501     }
35502 
35503     //! Load image from a PFM file \overloading.
35504     CImg<T>& load_pfm(std::FILE *const file) {
35505       return _load_pfm(file,0);
35506     }
35507 
35508     //! Load image from a PFM file \newinstance.
35509     static CImg<T> get_load_pfm(std::FILE *const file) {
35510       return CImg<T>().load_pfm(file);
35511     }
35512 
35513     CImg<T>& _load_pfm(std::FILE *const file, const char *const filename) {
35514       if (!file && !filename)
35515         throw CImgArgumentException(_cimg_instance
35516                                     "load_pfm() : Specified filename is (null).",
35517                                     cimg_instance);
35518 
35519       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
35520       char pfm_type, item[1024] = { 0 };
35521       int W = 0, H = 0, err = 0;
35522       double scale = 0;
35523       while ((err=std::fscanf(nfile,"%1023[^\n]",item))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
35524       if (std::sscanf(item," P%c",&pfm_type)!=1) {
35525         if (!file) cimg::fclose(nfile);
35526         throw CImgIOException(_cimg_instance
35527                               "load_pfm() : PFM header not found in file '%s'.",
35528                               cimg_instance,
35529                               filename?filename:"(FILE*)");
35530       }
35531       while ((err=std::fscanf(nfile," %1023[^\n]",item))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
35532       if ((err=std::sscanf(item," %d %d",&W,&H))<2) {
35533         if (!file) cimg::fclose(nfile);
35534         throw CImgIOException(_cimg_instance
35535                               "load_pfm() : WIDTH and HEIGHT fields are undefined in file '%s'.",
35536                               cimg_instance,
35537                               filename?filename:"(FILE*)");
35538       }
35539       if (err==2) {
35540         while ((err=std::fscanf(nfile," %1023[^\n]",item))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
35541         if (std::sscanf(item,"%lf",&scale)!=1)
35542           cimg::warn(_cimg_instance
35543                      "load_pfm() : SCALE field is undefined in file '%s'.",
35544                      cimg_instance,
35545                      filename?filename:"(FILE*)");
35546       }
35547       std::fgetc(nfile);
35548       const bool is_color = (pfm_type=='F'), is_inverted = (scale>0)!=cimg::endianness();
35549       if (is_color) {
35550         assign(W,H,1,3,0);
35551         CImg<floatT> buf(3*W);
35552         T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
35553         cimg_forY(*this,y) {
35554           cimg::fread(buf._data,3*W,nfile);
35555           if (is_inverted) cimg::invert_endianness(buf._data,3*W);
35556           const float *ptrs = buf._data;
35557           cimg_forX(*this,x) {
35558             *(ptr_r++) = (T)*(ptrs++);
35559             *(ptr_g++) = (T)*(ptrs++);
35560             *(ptr_b++) = (T)*(ptrs++);
35561           }
35562         }
35563       } else {
35564         assign(W,H,1,1,0);
35565         CImg<floatT> buf(W);
35566         T *ptrd = data(0,0,0,0);
35567         cimg_forY(*this,y) {
35568           cimg::fread(buf._data,W,nfile);
35569           if (is_inverted) cimg::invert_endianness(buf._data,W);
35570           const float *ptrs = buf._data;
35571           cimg_forX(*this,x) *(ptrd++) = (T)*(ptrs++);
35572         }
35573       }
35574       if (!file) cimg::fclose(nfile);
35575       return mirror('y');  // Most of the .pfm files are flipped along the y-axis.
35576     }
35577 
35578     //! Load image from a RGB file.
35579     /**
35580       \param filename Filename, as a C-string.
35581       \param dimw Width of the image buffer.
35582       \param dimh Height of the image buffer.
35583     **/
35584     CImg<T>& load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
35585       return _load_rgb(0,filename,dimw,dimh);
35586     }
35587 
35588     //! Load image from a RGB file \newinstance.
35589     static CImg<T> get_load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
35590       return CImg<T>().load_rgb(filename,dimw,dimh);
35591     }
35592 
35593     //! Load image from a RGB file \overloading.
35594     CImg<T>& load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
35595       return _load_rgb(file,0,dimw,dimh);
35596     }
35597 
35598     //! Load image from a RGB file \newinstance.
35599     static CImg<T> get_load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
35600       return CImg<T>().load_rgb(file,dimw,dimh);
35601     }
35602 
35603     CImg<T>& _load_rgb(std::FILE *const file, const char *const filename, const unsigned int dimw, const unsigned int dimh) {
35604       if (!file && !filename)
35605         throw CImgArgumentException(_cimg_instance
35606                                     "load_rgb() : Specified filename is (null).",
35607                                     cimg_instance);
35608 
35609       if (!dimw || !dimh) return assign();
35610       const long cimg_iobuffer = 12*1024*1024;
35611       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
35612       CImg<ucharT> raw;
35613       assign(dimw,dimh,1,3);
35614       T
35615         *ptr_r = data(0,0,0,0),
35616         *ptr_g = data(0,0,0,1),
35617         *ptr_b = data(0,0,0,2);
35618       for (long to_read = (long)size(); to_read>0; ) {
35619         raw.assign(cimg::min(to_read,cimg_iobuffer));
35620         cimg::fread(raw._data,raw._width,nfile);
35621         to_read-=raw._width;
35622         const unsigned char *ptrs = raw._data;
35623         for (unsigned long off = raw._width/3UL; off; --off) {
35624           *(ptr_r++) = (T)*(ptrs++);
35625           *(ptr_g++) = (T)*(ptrs++);
35626           *(ptr_b++) = (T)*(ptrs++);
35627         }
35628       }
35629       if (!file) cimg::fclose(nfile);
35630       return *this;
35631     }
35632 
35633     //! Load image from a RGBA file.
35634     /**
35635        \param filename Filename, as a C-string.
35636        \param dimw Width of the image buffer.
35637        \param dimh Height of the image buffer.
35638     **/
35639     CImg<T>& load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
35640       return _load_rgba(0,filename,dimw,dimh);
35641     }
35642 
35643     //! Load image from a RGBA file \newinstance.
35644     static CImg<T> get_load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
35645       return CImg<T>().load_rgba(filename,dimw,dimh);
35646     }
35647 
35648     //! Load image from a RGBA file \overloading.
35649     CImg<T>& load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
35650       return _load_rgba(file,0,dimw,dimh);
35651     }
35652 
35653     //! Load image from a RGBA file \newinstance.
35654     static CImg<T> get_load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
35655       return CImg<T>().load_rgba(file,dimw,dimh);
35656     }
35657 
35658     CImg<T>& _load_rgba(std::FILE *const file, const char *const filename, const unsigned int dimw, const unsigned int dimh) {
35659       if (!file && !filename)
35660         throw CImgArgumentException(_cimg_instance
35661                                     "load_rgba() : Specified filename is (null).",
35662                                     cimg_instance);
35663 
35664       if (!dimw || !dimh) return assign();
35665       const long cimg_iobuffer = 12*1024*1024;
35666       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
35667       CImg<ucharT> raw;
35668       assign(dimw,dimh,1,4);
35669       T
35670         *ptr_r = data(0,0,0,0),
35671         *ptr_g = data(0,0,0,1),
35672         *ptr_b = data(0,0,0,2),
35673         *ptr_a = data(0,0,0,3);
35674       for (long to_read = (long)size(); to_read>0; ) {
35675         raw.assign(cimg::min(to_read,cimg_iobuffer));
35676         cimg::fread(raw._data,raw._width,nfile);
35677         to_read-=raw._width;
35678         const unsigned char *ptrs = raw._data;
35679         for (unsigned long off = raw._width/4UL; off; --off) {
35680           *(ptr_r++) = (T)*(ptrs++);
35681           *(ptr_g++) = (T)*(ptrs++);
35682           *(ptr_b++) = (T)*(ptrs++);
35683           *(ptr_a++) = (T)*(ptrs++);
35684         }
35685       }
35686       if (!file) cimg::fclose(nfile);
35687       return *this;
35688     }
35689 
35690     //! Load image from a TIFF file.
35691     /**
35692        \param filename Filename, as a C-string.
35693        \param first_frame First frame to read (for multi-pages tiff).
35694        \param last_frame Last frame to read (for multi-pages tiff).
35695        \param step_frame Step value of frame reading.
35696        \note
35697        - libtiff support is enabled by defining the precompilation
35698         directive \c cimg_use_tif.
35699        - When libtiff is enabled, 2D and 3D (multipage) several
35700         channel per pixel are supported for
35701         <tt>char,uchar,short,ushort,float</tt> and \c double pixel types.
35702        - If \c cimg_use_tif is not defined at compilation time the
35703         function uses CImg<T>& load_other(const char*).
35704      **/
35705     CImg<T>& load_tiff(const char *const filename,
35706                        const unsigned int first_frame=0, const unsigned int last_frame=~0U,
35707                        const unsigned int step_frame=1) {
35708       if (!filename)
35709         throw CImgArgumentException(_cimg_instance
35710                                     "load_tiff() : Specified filename is (null).",
35711                                     cimg_instance);
35712 
35713       const unsigned int
35714         nfirst_frame = first_frame<last_frame?first_frame:last_frame,
35715         nstep_frame = step_frame?step_frame:1;
35716       unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
35717 
35718 #ifndef cimg_use_tiff
35719       if (nfirst_frame || nlast_frame!=~0U || nstep_frame>1)
35720         throw CImgArgumentException(_cimg_instance
35721                                     "load_tiff() : Unable to read sub-images from file '%s' unless libtiff is enabled.",
35722                                     cimg_instance,
35723                                     filename);
35724       return load_other(filename);
35725 #else
35726       TIFF *tif = TIFFOpen(filename,"r");
35727       if (tif) {
35728         unsigned int nb_images = 0;
35729         do ++nb_images; while (TIFFReadDirectory(tif));
35730         if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images))
35731           cimg::warn(_cimg_instance
35732                      "load_tiff() : File '%s' contains %u image(s) while specified frame range is [%u,%u] (step %u).",
35733                      cimg_instance,
35734                      filename,nb_images,nfirst_frame,nlast_frame,nstep_frame);
35735 
35736         if (nfirst_frame>=nb_images) return assign();
35737         if (nlast_frame>=nb_images) nlast_frame = nb_images-1;
35738         TIFFSetDirectory(tif,0);
35739         CImg<T> frame;
35740         for (unsigned int l = nfirst_frame; l<=nlast_frame; l+=nstep_frame) {
35741           frame._load_tiff(tif,l);
35742           if (l==nfirst_frame) assign(frame._width,frame._height,1+(nlast_frame-nfirst_frame)/nstep_frame,frame._spectrum);
35743           if (frame._width>_width || frame._height>_height || frame._spectrum>_spectrum)
35744             resize(cimg::max(frame._width,_width),cimg::max(frame._height,_height),-100,cimg::max(frame._spectrum,_spectrum),0);
35745           draw_image(0,0,(l-nfirst_frame)/nstep_frame,frame);
35746         }
35747         TIFFClose(tif);
35748       } else throw CImgIOException(_cimg_instance
35749                                    "load_tiff() : Failed to open file '%s'.",
35750                                    cimg_instance,
35751                                    filename);
35752       return *this;
35753 #endif
35754     }
35755 
35756     //! Load image from a TIFF file \newinstance.
35757     static CImg<T> get_load_tiff(const char *const filename,
35758                                  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
35759                                  const unsigned int step_frame=1) {
35760       return CImg<T>().load_tiff(filename,first_frame,last_frame,step_frame);
35761     }
35762 
35763     // (Original contribution by Jerome Boulanger).
35764 #ifdef cimg_use_tiff
35765     template<typename t>
35766     void _load_tiff_tiled_contig(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) {
35767       t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif));
35768       if (buf) {
35769         for (unsigned int row = 0; row<ny; row+=th)
35770           for (unsigned int col = 0; col<nx; col+=tw) {
35771             if (TIFFReadTile(tif,buf,col,row,0,0)<0) {
35772               _TIFFfree(buf); TIFFClose(tif);
35773               throw CImgIOException(_cimg_instance
35774                                     "load_tiff() : Invalid tile in file '%s'.",
35775                                     cimg_instance,
35776                                     TIFFFileName(tif));
35777             }
35778             const t *ptr = buf;
35779             for (unsigned int rr = row; rr<cimg::min((unsigned int)(row+th),(unsigned int)ny); ++rr)
35780               for (unsigned int cc = col; cc<cimg::min((unsigned int)(col+tw),(unsigned int)nx); ++cc)
35781                 for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
35782                   (*this)(cc,rr,vv) = (T)(ptr[(rr-row)*th*samplesperpixel + (cc-col)*samplesperpixel + vv]);
35783           }
35784         _TIFFfree(buf);
35785       }
35786     }
35787 
35788     template<typename t>
35789     void _load_tiff_tiled_separate(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) {
35790       t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif));
35791       if (buf) {
35792         for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
35793           for (unsigned int row = 0; row<ny; row+=th)
35794             for (unsigned int col = 0; col<nx; col+=tw) {
35795               if (TIFFReadTile(tif,buf,col,row,0,vv)<0) {
35796                 _TIFFfree(buf); TIFFClose(tif);
35797                 throw CImgIOException(_cimg_instance
35798                                       "load_tiff() : Invalid tile in file '%s'.",
35799                                       cimg_instance,
35800                                       TIFFFileName(tif));
35801               }
35802               const t *ptr = buf;
35803               for (unsigned int rr = row; rr<cimg::min((unsigned int)(row+th),(unsigned int)ny); ++rr)
35804                 for (unsigned int cc = col; cc<cimg::min((unsigned int)(col+tw),(unsigned int)nx); ++cc)
35805                   (*this)(cc,rr,vv) = (T)*(ptr++);
35806             }
35807         _TIFFfree(buf);
35808       }
35809     }
35810 
35811     template<typename t>
35812     void _load_tiff_contig(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) {
35813       t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
35814       if (buf) {
35815         uint32 row, rowsperstrip = (uint32)-1;
35816         TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
35817         for (row = 0; row<ny; row+= rowsperstrip) {
35818           uint32 nrow = (row+rowsperstrip>ny?ny-row:rowsperstrip);
35819           tstrip_t strip = TIFFComputeStrip(tif, row, 0);
35820           if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) {
35821             _TIFFfree(buf); TIFFClose(tif);
35822             throw CImgIOException(_cimg_instance
35823                                   "load_tiff() : Invalid strip in file '%s'.",
35824                                   cimg_instance,
35825                                   TIFFFileName(tif));
35826           }
35827           const t *ptr = buf;
35828           for (unsigned int rr = 0; rr<nrow; ++rr)
35829             for (unsigned int cc = 0; cc<nx; ++cc)
35830               for (unsigned int vv = 0; vv<samplesperpixel; ++vv) (*this)(cc,row+rr,vv) = (T)*(ptr++);
35831         }
35832         _TIFFfree(buf);
35833       }
35834     }
35835 
35836     template<typename t>
35837     void _load_tiff_separate(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) {
35838       t *buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
35839       if (buf) {
35840         uint32 row, rowsperstrip = (uint32)-1;
35841         TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
35842         for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
35843           for (row = 0; row<ny; row+= rowsperstrip) {
35844             uint32 nrow = (row+rowsperstrip>ny?ny-row:rowsperstrip);
35845             tstrip_t strip = TIFFComputeStrip(tif, row, vv);
35846             if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) {
35847               _TIFFfree(buf); TIFFClose(tif);
35848               throw CImgIOException(_cimg_instance
35849                                     "load_tiff() : Invalid strip in file '%s'.",
35850                                     cimg_instance,
35851                                     TIFFFileName(tif));
35852             }
35853             const t *ptr = buf;
35854             for (unsigned int rr = 0;rr<nrow; ++rr)
35855               for (unsigned int cc = 0; cc<nx; ++cc)
35856                 (*this)(cc,row+rr,vv) = (T)*(ptr++);
35857           }
35858         _TIFFfree(buf);
35859       }
35860     }
35861 
35862     CImg<T>& _load_tiff(TIFF *const tif, const unsigned int directory) {
35863       if (!TIFFSetDirectory(tif,directory)) return assign();
35864       uint16 samplesperpixel, bitspersample, photo;
35865       uint16 sampleformat = SAMPLEFORMAT_UINT;
35866       uint32 nx,ny;
35867       const char *const filename = TIFFFileName(tif);
35868       TIFFGetField(tif,TIFFTAG_IMAGEWIDTH,&nx);
35869       TIFFGetField(tif,TIFFTAG_IMAGELENGTH,&ny);
35870       TIFFGetField(tif,TIFFTAG_SAMPLESPERPIXEL,&samplesperpixel);
35871       TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sampleformat);
35872       TIFFGetFieldDefaulted(tif,TIFFTAG_BITSPERSAMPLE,&bitspersample);
35873       TIFFGetField(tif,TIFFTAG_PHOTOMETRIC,&photo);
35874       int spectrum = samplesperpixel;
35875       if (photo == 3) spectrum = 3;
35876       assign(nx,ny,1,spectrum);
35877       if ((photo < 3)  && ( bitspersample!=8 || !(samplesperpixel==3 || samplesperpixel==4))) {
35878         uint16 config;
35879         TIFFGetField(tif,TIFFTAG_PLANARCONFIG,&config);
35880         if (TIFFIsTiled(tif)) {
35881           uint32 tw, th;
35882           TIFFGetField(tif,TIFFTAG_TILEWIDTH,&tw);
35883           TIFFGetField(tif,TIFFTAG_TILELENGTH,&th);
35884           if (config==PLANARCONFIG_CONTIG) switch (bitspersample) {
35885             case 8 : {
35886               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_contig<unsigned char>(tif,samplesperpixel,nx,ny,tw,th);
35887               else _load_tiff_tiled_contig<signed char>(tif,samplesperpixel,nx,ny,tw,th);
35888             } break;
35889             case 16 :
35890               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_contig<unsigned short>(tif,samplesperpixel,nx,ny,tw,th);
35891               else _load_tiff_tiled_contig<short>(tif,samplesperpixel,nx,ny,tw,th);
35892               break;
35893             case 32 :
35894               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_contig<unsigned int>(tif,samplesperpixel,nx,ny,tw,th);
35895               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_tiled_contig<int>(tif,samplesperpixel,nx,ny,tw,th);
35896               else _load_tiff_tiled_contig<float>(tif,samplesperpixel,nx,ny,tw,th);
35897               break;
35898             } else switch (bitspersample) {
35899             case 8 :
35900               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_separate<unsigned char>(tif,samplesperpixel,nx,ny,tw,th);
35901               else _load_tiff_tiled_separate<signed char>(tif,samplesperpixel,nx,ny,tw,th);
35902               break;
35903             case 16 :
35904               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_separate<unsigned short>(tif,samplesperpixel,nx,ny,tw,th);
35905               else _load_tiff_tiled_separate<short>(tif,samplesperpixel,nx,ny,tw,th);
35906               break;
35907             case 32 :
35908               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_separate<unsigned int>(tif,samplesperpixel,nx,ny,tw,th);
35909               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_tiled_separate<int>(tif,samplesperpixel,nx,ny,tw,th);
35910               else _load_tiff_tiled_separate<float>(tif,samplesperpixel,nx,ny,tw,th);
35911               break;
35912             }
35913         } else {
35914           if (config==PLANARCONFIG_CONTIG) switch (bitspersample) {
35915             case 8 :
35916               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned char>(tif,samplesperpixel,nx,ny);
35917               else _load_tiff_contig<signed char>(tif,samplesperpixel,nx,ny);
35918               break;
35919             case 16 :
35920               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned short>(tif,samplesperpixel,nx,ny);
35921               else _load_tiff_contig<short>(tif,samplesperpixel,nx,ny);
35922               break;
35923             case 32 :
35924               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned int>(tif,samplesperpixel,nx,ny);
35925               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig<int>(tif,samplesperpixel,nx,ny);
35926               else _load_tiff_contig<float>(tif,samplesperpixel,nx,ny);
35927               break;
35928             } else switch (bitspersample){
35929             case 8 :
35930               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned char>(tif,samplesperpixel,nx,ny);
35931               else _load_tiff_separate<signed char>(tif,samplesperpixel,nx,ny);
35932               break;
35933             case 16 :
35934               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned short>(tif,samplesperpixel,nx,ny);
35935               else _load_tiff_separate<short>(tif,samplesperpixel,nx,ny);
35936               break;
35937             case 32 :
35938               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned int>(tif,samplesperpixel,nx,ny);
35939               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate<int>(tif,samplesperpixel,nx,ny);
35940               else _load_tiff_separate<float>(tif,samplesperpixel,nx,ny);
35941               break;
35942             }
35943         }
35944       } else {
35945         uint32 *const raster = (uint32*)_TIFFmalloc(nx*ny*sizeof(uint32));
35946         if (!raster) {
35947           _TIFFfree(raster); TIFFClose(tif);
35948           throw CImgException(_cimg_instance
35949                               "load_tiff() : Failed to allocate memory (%s) for file '%s'.",
35950                               cimg_instance,
35951                               cimg::strbuffersize(nx*ny*sizeof(uint32)),filename);
35952         }
35953         TIFFReadRGBAImage(tif,nx,ny,raster,0);
35954         switch (spectrum) {
35955         case 1 : {
35956           cimg_forXY(*this,x,y) (*this)(x,y) = (T)(float)((raster[nx*(ny-1-y)+x] + 128)/257);
35957         } break;
35958         case 3 : {
35959           cimg_forXY(*this,x,y) {
35960             (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny-1-y)+x]);
35961             (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny-1-y)+x]);
35962             (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny-1-y)+x]);
35963           }
35964         } break;
35965         case 4 : {
35966           cimg_forXY(*this,x,y) {
35967             (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny-1-y)+x]);
35968             (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny-1-y)+x]);
35969             (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny-1-y)+x]);
35970             (*this)(x,y,3) = (T)(float)TIFFGetA(raster[nx*(ny-1-y)+x]);
35971           }
35972         } break;
35973         }
35974         _TIFFfree(raster);
35975       }
35976       return *this;
35977     }
35978 #endif
35979 
35980     //! Load image from a MINC2 file.
35981     /**
35982         \param filename Filename, as a C-string.
35983     **/
35984     // (Original code by Haz-Edine Assemlal).
35985     CImg<T>& load_minc2(const char *const filename) {
35986       if (!filename)
35987         throw CImgArgumentException(_cimg_instance
35988                                     "load_minc2() : Specified filename is (null).",
35989                                     cimg_instance);
35990 #ifndef cimg_use_minc2
35991       return load_other(filename);
35992 #else
35993       minc::minc_1_reader rdr;
35994       rdr.open(filename);
35995       assign(rdr.ndim(1) ? rdr.ndim(1) : 1,
35996              rdr.ndim(2) ? rdr.ndim(2) : 1,
35997              rdr.ndim(3) ? rdr.ndim(3) : 1,
35998              rdr.ndim(4) ? rdr.ndim(4) : 1);
35999       if(typeid(T)==typeid(unsigned char))
36000         rdr.setup_read_byte();
36001       else if(typeid(T)==typeid(int))
36002         rdr.setup_read_int();
36003       else if(typeid(T)==typeid(double))
36004         rdr.setup_read_double();
36005       else
36006         rdr.setup_read_float();
36007       minc::load_standard_volume(rdr, this->_data);
36008       return *this;
36009 #endif
36010     }
36011 
36012     //! Load image from a MINC2 file \newinstance.
36013     static CImg<T> get_load_minc2(const char *const filename) {
36014       return CImg<T>().load_analyze(filename);
36015     }
36016 
36017     //! Load image from an ANALYZE7.5/NIFTI file.
36018     /**
36019        \param filename Filename, as a C-string.
36020        \param[out] voxel_size Pointer to the three voxel sizes read from the file.
36021     **/
36022     CImg<T>& load_analyze(const char *const filename, float *const voxel_size=0) {
36023       return _load_analyze(0,filename,voxel_size);
36024     }
36025 
36026     //! Load image from an ANALYZE7.5/NIFTI file \newinstance.
36027     static CImg<T> get_load_analyze(const char *const filename, float *const voxel_size=0) {
36028       return CImg<T>().load_analyze(filename,voxel_size);
36029     }
36030 
36031     //! Load image from an ANALYZE7.5/NIFTI file \overloading.
36032     CImg<T>& load_analyze(std::FILE *const file, float *const voxel_size=0) {
36033       return _load_analyze(file,0,voxel_size);
36034     }
36035 
36036     //! Load image from an ANALYZE7.5/NIFTI file \newinstance.
36037     static CImg<T> get_load_analyze(std::FILE *const file, float *const voxel_size=0) {
36038       return CImg<T>().load_analyze(file,voxel_size);
36039     }
36040 
36041     CImg<T>& _load_analyze(std::FILE *const file, const char *const filename, float *const voxel_size=0) {
36042       if (!file && !filename)
36043         throw CImgArgumentException(_cimg_instance
36044                                     "load_analyze() : Specified filename is (null).",
36045                                     cimg_instance);
36046 
36047       std::FILE *nfile_header = 0, *nfile = 0;
36048       if (!file) {
36049         char body[1024] = { 0 };
36050         const char *const ext = cimg::split_filename(filename,body);
36051         if (!cimg::strcasecmp(ext,"hdr")) { // File is an Analyze header file.
36052           nfile_header = cimg::fopen(filename,"rb");
36053           std::sprintf(body + std::strlen(body),".img");
36054           nfile = cimg::fopen(body,"rb");
36055         } else if (!cimg::strcasecmp(ext,"img")) { // File is an Analyze data file.
36056           nfile = cimg::fopen(filename,"rb");
36057           std::sprintf(body + std::strlen(body),".hdr");
36058           nfile_header = cimg::fopen(body,"rb");
36059         } else nfile_header = nfile = cimg::fopen(filename,"rb"); // File is a Niftii file.
36060       } else nfile_header = nfile = file; // File is a Niftii file.
36061       if (!nfile || !nfile_header)
36062         throw CImgIOException(_cimg_instance
36063                               "load_analyze() : Invalid Analyze7.5 or NIFTI header in file '%s'.",
36064                               cimg_instance,
36065                               filename?filename:"(FILE*)");
36066 
36067       // Read header.
36068       bool endian = false;
36069       unsigned int header_size;
36070       cimg::fread(&header_size,1,nfile_header);
36071       if (!header_size)
36072         throw CImgIOException(_cimg_instance
36073                               "load_analyze() : Invalid zero-sized header in file '%s'.",
36074                               cimg_instance,
36075                               filename?filename:"(FILE*)");
36076 
36077       if (header_size>=4096) { endian = true; cimg::invert_endianness(header_size); }
36078       unsigned char *const header = new unsigned char[header_size];
36079       cimg::fread(header+4,header_size-4,nfile_header);
36080       if (!file && nfile_header!=nfile) cimg::fclose(nfile_header);
36081       if (endian) {
36082         cimg::invert_endianness((short*)(header+40),5);
36083         cimg::invert_endianness((short*)(header+70),1);
36084         cimg::invert_endianness((short*)(header+72),1);
36085         cimg::invert_endianness((float*)(header+76),4);
36086         cimg::invert_endianness((float*)(header+112),1);
36087       }
36088       unsigned short *dim = (unsigned short*)(header+40), dimx = 1, dimy = 1, dimz = 1, dimv = 1;
36089       if (!dim[0])
36090         cimg::warn(_cimg_instance
36091                    "load_analyze() : File '%s' defines an image with zero dimensions.",
36092                    cimg_instance,
36093                    filename?filename:"(FILE*)");
36094 
36095       if (dim[0]>4)
36096         cimg::warn(_cimg_instance
36097                    "load_analyze() : File '%s' defines an image with %u dimensions, reading only the 4 first.",
36098                    cimg_instance,
36099                    filename?filename:"(FILE*)",dim[0]);
36100 
36101       if (dim[0]>=1) dimx = dim[1];
36102       if (dim[0]>=2) dimy = dim[2];
36103       if (dim[0]>=3) dimz = dim[3];
36104       if (dim[0]>=4) dimv = dim[4];
36105       float scalefactor = *(float*)(header+112); if (scalefactor==0) scalefactor=1;
36106       const unsigned short datatype = *(short*)(header+70);
36107       if (voxel_size) {
36108         const float *vsize = (float*)(header+76);
36109         voxel_size[0] = vsize[1]; voxel_size[1] = vsize[2]; voxel_size[2] = vsize[3];
36110       }
36111       delete[] header;
36112 
36113       // Read pixel data.
36114       assign(dimx,dimy,dimz,dimv);
36115       switch (datatype) {
36116       case 2 : {
36117         unsigned char *const buffer = new unsigned char[dimx*dimy*dimz*dimv];
36118         cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile);
36119         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
36120         delete[] buffer;
36121       } break;
36122       case 4 : {
36123         short *const buffer = new short[dimx*dimy*dimz*dimv];
36124         cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile);
36125         if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv);
36126         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
36127         delete[] buffer;
36128       } break;
36129       case 8 : {
36130         int *const buffer = new int[dimx*dimy*dimz*dimv];
36131         cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile);
36132         if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv);
36133         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
36134         delete[] buffer;
36135       } break;
36136       case 16 : {
36137         float *const buffer = new float[dimx*dimy*dimz*dimv];
36138         cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile);
36139         if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv);
36140         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
36141         delete[] buffer;
36142       } break;
36143       case 64 : {
36144         double *const buffer = new double[dimx*dimy*dimz*dimv];
36145         cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile);
36146         if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv);
36147         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
36148         delete[] buffer;
36149       } break;
36150       default :
36151         if (!file) cimg::fclose(nfile);
36152         throw CImgIOException(_cimg_instance
36153                               "load_analyze() : Unable to load datatype %d in file '%s'",
36154                               cimg_instance,
36155                               datatype,filename?filename:"(FILE*)");
36156       }
36157       if (!file) cimg::fclose(nfile);
36158       return *this;
36159     }
36160 
36161     //! Load image from a .cimg[z] file.
36162     /**
36163       \param filename Filename, as a C-string.
36164       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
36165       \param align Appending alignment.
36166     **/
36167     CImg<T>& load_cimg(const char *const filename, const char axis='z', const float align=0) {
36168       CImgList<T> list;
36169       list.load_cimg(filename);
36170       if (list._width==1) return list[0].move_to(*this);
36171       return assign(list.get_append(axis,align));
36172     }
36173 
36174     //! Load image from a .cimg[z] file \newinstance
36175     static CImg<T> get_load_cimg(const char *const filename, const char axis='z', const float align=0) {
36176       return CImg<T>().load_cimg(filename,axis,align);
36177     }
36178 
36179     //! Load image from a .cimg[z] file \overloading.
36180     CImg<T>& load_cimg(std::FILE *const file, const char axis='z', const float align=0) {
36181       CImgList<T> list;
36182       list.load_cimg(file);
36183       if (list._width==1) return list[0].move_to(*this);
36184       return assign(list.get_append(axis,align));
36185     }
36186 
36187     //! Load image from a .cimg[z] file \newinstance
36188     static CImg<T> get_load_cimg(std::FILE *const file, const char axis='z', const float align=0) {
36189       return CImg<T>().load_cimg(file,axis,align);
36190     }
36191 
36192     //! Load sub-images of a .cimg file.
36193     /**
36194       \param filename Filename, as a C-string.
36195       \param n0 Starting frame.
36196       \param n1 Ending frame.
36197       \param x0 X-coordinate of the starting sub-image vertex.
36198       \param y0 Y-coordinate of the starting sub-image vertex.
36199       \param z0 Z-coordinate of the starting sub-image vertex.
36200       \param c0 C-coordinate of the starting sub-image vertex.
36201       \param x1 X-coordinate of the ending sub-image vertex.
36202       \param y1 Y-coordinate of the ending sub-image vertex.
36203       \param z1 Z-coordinate of the ending sub-image vertex.
36204       \param c1 C-coordinate of the ending sub-image vertex.
36205       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
36206       \param align Appending alignment.
36207     **/
36208     CImg<T>& load_cimg(const char *const filename,
36209                        const unsigned int n0, const unsigned int n1,
36210                        const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
36211                        const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1,
36212                        const char axis='z', const float align=0) {
36213       CImgList<T> list;
36214       list.load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
36215       if (list._width==1) return list[0].move_to(*this);
36216       return assign(list.get_append(axis,align));
36217     }
36218 
36219     //! Load sub-images of a .cimg file \newinstance.
36220     static CImg<T> get_load_cimg(const char *const filename,
36221                                  const unsigned int n0, const unsigned int n1,
36222                                  const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
36223                                  const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1,
36224                                  const char axis='z', const float align=0) {
36225       return CImg<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align);
36226     }
36227 
36228     //! Load sub-images of a .cimg file \overloading.
36229     CImg<T>& load_cimg(std::FILE *const file,
36230                        const unsigned int n0, const unsigned int n1,
36231                        const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
36232                        const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1,
36233                        const char axis='z', const float align=0) {
36234       CImgList<T> list;
36235       list.load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
36236       if (list._width==1) return list[0].move_to(*this);
36237       return assign(list.get_append(axis,align));
36238     }
36239 
36240     //! Load sub-images of a .cimg file \newinstance.
36241     static CImg<T> get_load_cimg(std::FILE *const file,
36242                                  const unsigned int n0, const unsigned int n1,
36243                                  const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
36244                                  const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1,
36245                                  const char axis='z', const float align=0) {
36246       return CImg<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align);
36247     }
36248 
36249     //! Load image from an INRIMAGE-4 file.
36250     /**
36251        \param filename Filename, as a C-string.
36252        \param[out] voxel_size Pointer to the three voxel sizes read from the file.
36253     **/
36254     CImg<T>& load_inr(const char *const filename, float *const voxel_size=0) {
36255       return _load_inr(0,filename,voxel_size);
36256     }
36257 
36258     //! Load image from an INRIMAGE-4 file \newinstance.
36259     static CImg<T> get_load_inr(const char *const filename, float *const voxel_size=0) {
36260       return CImg<T>().load_inr(filename,voxel_size);
36261     }
36262 
36263     //! Load image from an INRIMAGE-4 file \overloading.
36264     CImg<T>& load_inr(std::FILE *const file, float *const voxel_size=0) {
36265       return _load_inr(file,0,voxel_size);
36266     }
36267 
36268     //! Load image from an INRIMAGE-4 file \newinstance.
36269     static CImg<T> get_load_inr(std::FILE *const file, float *voxel_size=0) {
36270       return CImg<T>().load_inr(file,voxel_size);
36271     }
36272 
36273     static void _load_inr_header(std::FILE *file, int out[8], float *const voxel_size) {
36274       char item[1024] = { 0 }, tmp1[64] = { 0 }, tmp2[64] = { 0 };
36275       out[0] = std::fscanf(file,"%63s",item);
36276       out[0] = out[1] = out[2] = out[3] = out[5] = 1; out[4] = out[6] = out[7] = -1;
36277       if(cimg::strncasecmp(item,"#INRIMAGE-4#{",13)!=0)
36278         throw CImgIOException("CImg<%s>::load_inr() : INRIMAGE-4 header not found.",
36279                               pixel_type());
36280 
36281       while (std::fscanf(file," %63[^\n]%*c",item)!=EOF && std::strncmp(item,"##}",3)) {
36282         std::sscanf(item," XDIM%*[^0-9]%d",out);
36283         std::sscanf(item," YDIM%*[^0-9]%d",out+1);
36284         std::sscanf(item," ZDIM%*[^0-9]%d",out+2);
36285         std::sscanf(item," VDIM%*[^0-9]%d",out+3);
36286         std::sscanf(item," PIXSIZE%*[^0-9]%d",out+6);
36287         if (voxel_size) {
36288           std::sscanf(item," VX%*[^0-9.+-]%f",voxel_size);
36289           std::sscanf(item," VY%*[^0-9.+-]%f",voxel_size+1);
36290           std::sscanf(item," VZ%*[^0-9.+-]%f",voxel_size+2);
36291         }
36292         if (std::sscanf(item," CPU%*[ =]%s",tmp1)) out[7]=cimg::strncasecmp(tmp1,"sun",3)?0:1;
36293         switch (std::sscanf(item," TYPE%*[ =]%s %s",tmp1,tmp2)) {
36294         case 0 : break;
36295         case 2 : out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0; std::strncpy(tmp1,tmp2,sizeof(tmp1)-1);
36296         case 1 :
36297           if (!cimg::strncasecmp(tmp1,"int",3)   || !cimg::strncasecmp(tmp1,"fixed",5))  out[4] = 0;
36298           if (!cimg::strncasecmp(tmp1,"float",5) || !cimg::strncasecmp(tmp1,"double",6)) out[4] = 1;
36299           if (!cimg::strncasecmp(tmp1,"packed",6))                                       out[4] = 2;
36300           if (out[4]>=0) break;
36301         default :
36302           throw CImgIOException("CImg<%s>::load_inr() : Invalid pixel type '%s' defined in header.",
36303                                 pixel_type(),
36304                                 tmp2);
36305         }
36306       }
36307       if(out[0]<0 || out[1]<0 || out[2]<0 || out[3]<0)
36308         throw CImgIOException("CImg<%s>::load_inr() : Invalid dimensions (%d,%d,%d,%d) defined in header.",
36309                               pixel_type(),
36310                               out[0],out[1],out[2],out[3]);
36311       if(out[4]<0 || out[5]<0)
36312         throw CImgIOException("CImg<%s>::load_inr() : Incomplete pixel type defined in header.",
36313                               pixel_type());
36314       if(out[6]<0)
36315         throw CImgIOException("CImg<%s>::load_inr() : Incomplete PIXSIZE field defined in header.",
36316                               pixel_type());
36317       if(out[7]<0)
36318         throw CImgIOException("CImg<%s>::load_inr() : Big/Little Endian coding type undefined in header.",
36319                               pixel_type());
36320     }
36321 
36322     CImg<T>& _load_inr(std::FILE *const file, const char *const filename, float *const voxel_size) {
36323 #define _cimg_load_inr_case(Tf,sign,pixsize,Ts) \
36324      if (!loaded && fopt[6]==pixsize && fopt[4]==Tf && fopt[5]==sign) { \
36325         Ts *xval, *const val = new Ts[fopt[0]*fopt[3]]; \
36326         cimg_forYZ(*this,y,z) { \
36327             cimg::fread(val,fopt[0]*fopt[3],nfile); \
36328             if (fopt[7]!=endian) cimg::invert_endianness(val,fopt[0]*fopt[3]); \
36329             xval = val; cimg_forX(*this,x) cimg_forC(*this,c) (*this)(x,y,z,c) = (T)*(xval++); \
36330           } \
36331         delete[] val; \
36332         loaded = true; \
36333       }
36334 
36335       if (!file && !filename)
36336         throw CImgArgumentException(_cimg_instance
36337                                     "load_inr() : Specified filename is (null).",
36338                                     cimg_instance);
36339 
36340       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
36341       int fopt[8], endian=cimg::endianness()?1:0;
36342       bool loaded = false;
36343       if (voxel_size) voxel_size[0] = voxel_size[1] = voxel_size[2] = 1;
36344       _load_inr_header(nfile,fopt,voxel_size);
36345       assign(fopt[0],fopt[1],fopt[2],fopt[3]);
36346       _cimg_load_inr_case(0,0,8,unsigned char);
36347       _cimg_load_inr_case(0,1,8,char);
36348       _cimg_load_inr_case(0,0,16,unsigned short);
36349       _cimg_load_inr_case(0,1,16,short);
36350       _cimg_load_inr_case(0,0,32,unsigned int);
36351       _cimg_load_inr_case(0,1,32,int);
36352       _cimg_load_inr_case(1,0,32,float);
36353       _cimg_load_inr_case(1,1,32,float);
36354       _cimg_load_inr_case(1,0,64,double);
36355       _cimg_load_inr_case(1,1,64,double);
36356       if (!loaded) {
36357         if (!file) cimg::fclose(nfile);
36358         throw CImgIOException(_cimg_instance
36359                               "load_inr() : Unknown pixel type defined in file '%s'.",
36360                               cimg_instance,
36361                               filename?filename:"(FILE*)");
36362       }
36363       if (!file) cimg::fclose(nfile);
36364       return *this;
36365     }
36366 
36367     //! Load image from a EXR file.
36368     /**
36369       \param filename Filename, as a C-string.
36370     **/
36371     CImg<T>& load_exr(const char *const filename) {
36372       if (!filename)
36373         throw CImgArgumentException(_cimg_instance
36374                                     "load_exr() : Specified filename is (null).",
36375                                     cimg_instance);
36376 
36377 #ifndef cimg_use_openexr
36378       return load_other(filename);
36379 #else
36380       Imf::RgbaInputFile file(filename);
36381       Imath::Box2i dw = file.dataWindow();
36382       const int
36383         inwidth = dw.max.x - dw.min.x + 1,
36384         inheight = dw.max.y - dw.min.y + 1;
36385       Imf::Array2D<Imf::Rgba> pixels;
36386       pixels.resizeErase(inheight,inwidth);
36387       file.setFrameBuffer(&pixels[0][0] - dw.min.x - dw.min.y*inwidth, 1, inwidth);
36388       file.readPixels(dw.min.y, dw.max.y);
36389       assign(inwidth,inheight,1,4);
36390       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);
36391       cimg_forXY(*this,x,y) {
36392         *(ptr_r++) = (T)pixels[y][x].r;
36393         *(ptr_g++) = (T)pixels[y][x].g;
36394         *(ptr_b++) = (T)pixels[y][x].b;
36395         *(ptr_a++) = (T)pixels[y][x].a;
36396       }
36397       return *this;
36398 #endif
36399     }
36400 
36401     //! Load image from a EXR file \newinstance.
36402     static CImg<T> get_load_exr(const char *const filename) {
36403       return CImg<T>().load_exr(filename);
36404     }
36405 
36406     //! Load image from a PANDORE-5 file.
36407     /**
36408       \param filename Filename, as a C-string.
36409     **/
36410     CImg<T>& load_pandore(const char *const filename) {
36411       return _load_pandore(0,filename);
36412     }
36413 
36414     //! Load image from a PANDORE-5 file \newinstance.
36415     static CImg<T> get_load_pandore(const char *const filename) {
36416       return CImg<T>().load_pandore(filename);
36417     }
36418 
36419     //! Load image from a PANDORE-5 file \overloading.
36420     CImg<T>& load_pandore(std::FILE *const file) {
36421       return _load_pandore(file,0);
36422     }
36423 
36424     //! Load image from a PANDORE-5 file \newinstance.
36425     static CImg<T> get_load_pandore(std::FILE *const file) {
36426       return CImg<T>().load_pandore(file);
36427     }
36428 
36429     CImg<T>& _load_pandore(std::FILE *const file, const char *const filename) {
36430 #define __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,ndim,stype) \
36431         cimg::fread(dims,nbdim,nfile); \
36432         if (endian) cimg::invert_endianness(dims,nbdim); \
36433         assign(nwidth,nheight,ndepth,ndim); \
36434         const unsigned int siz = size(); \
36435         stype *buffer = new stype[siz]; \
36436         cimg::fread(buffer,siz,nfile); \
36437         if (endian) cimg::invert_endianness(buffer,siz); \
36438         T *ptrd = _data; \
36439         cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); \
36440         buffer-=siz; \
36441         delete[] buffer
36442 
36443 #define _cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1,stype2,stype3,ltype) { \
36444         if (sizeof(stype1)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1); } \
36445         else if (sizeof(stype2)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype2); } \
36446         else if (sizeof(stype3)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype3); } \
36447         else throw CImgIOException(_cimg_instance \
36448                                    "load_pandore() : Unknown pixel datatype in file '%s'.", \
36449                                    cimg_instance, \
36450                                    filename?filename:"(FILE*)"); }
36451 
36452       if (!file && !filename)
36453         throw CImgArgumentException(_cimg_instance
36454                                     "load_pandore() : Specified filename is (null).",
36455                                     cimg_instance);
36456 
36457       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
36458       char header[32] = { 0 };
36459       cimg::fread(header,12,nfile);
36460       if (cimg::strncasecmp("PANDORE",header,7)) {
36461         if (!file) cimg::fclose(nfile);
36462         throw CImgIOException(_cimg_instance
36463                               "load_pandore() : PANDORE header not found in file '%s'.",
36464                               cimg_instance,
36465                               filename?filename:"(FILE*)");
36466       }
36467       unsigned int imageid, dims[8] = { 0 };
36468       cimg::fread(&imageid,1,nfile);
36469       const bool endian = (imageid>255);
36470       if (endian) cimg::invert_endianness(imageid);
36471       cimg::fread(header,20,nfile);
36472 
36473       switch (imageid) {
36474       case 2: _cimg_load_pandore_case(2,dims[1],1,1,1,unsigned char,unsigned char,unsigned char,1); break;
36475       case 3: _cimg_load_pandore_case(2,dims[1],1,1,1,long,int,short,4); break;
36476       case 4: _cimg_load_pandore_case(2,dims[1],1,1,1,double,float,float,4); break;
36477       case 5: _cimg_load_pandore_case(3,dims[2],dims[1],1,1,unsigned char,unsigned char,unsigned char,1); break;
36478       case 6: _cimg_load_pandore_case(3,dims[2],dims[1],1,1,long,int,short,4); break;
36479       case 7: _cimg_load_pandore_case(3,dims[2],dims[1],1,1,double,float,float,4); break;
36480       case 8: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,unsigned char,unsigned char,unsigned char,1); break;
36481       case 9: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,long,int,short,4); break;
36482       case 10: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,double,float,float,4); break;
36483       case 11 : { // Region 1d
36484         cimg::fread(dims,3,nfile);
36485         if (endian) cimg::invert_endianness(dims,3);
36486         assign(dims[1],1,1,1);
36487         const unsigned siz = size();
36488         if (dims[2]<256) {
36489           unsigned char *buffer = new unsigned char[siz];
36490           cimg::fread(buffer,siz,nfile);
36491           T *ptrd = _data;
36492           cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
36493           buffer-=siz;
36494           delete[] buffer;
36495         } else {
36496           if (dims[2]<65536) {
36497             unsigned short *buffer = new unsigned short[siz];
36498             cimg::fread(buffer,siz,nfile);
36499             if (endian) cimg::invert_endianness(buffer,siz);
36500             T *ptrd = _data;
36501             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
36502             buffer-=siz;
36503             delete[] buffer;
36504           } else {
36505             unsigned int *buffer = new unsigned int[siz];
36506             cimg::fread(buffer,siz,nfile);
36507             if (endian) cimg::invert_endianness(buffer,siz);
36508             T *ptrd = _data;
36509             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
36510             buffer-=siz;
36511             delete[] buffer;
36512           }
36513         }
36514       }
36515         break;
36516       case 12 : { // Region 2d
36517         cimg::fread(dims,4,nfile);
36518         if (endian) cimg::invert_endianness(dims,4);
36519         assign(dims[2],dims[1],1,1);
36520         const unsigned int siz = size();
36521         if (dims[3]<256) {
36522           unsigned char *buffer = new unsigned char[siz];
36523           cimg::fread(buffer,siz,nfile);
36524           T *ptrd = _data;
36525           cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
36526           buffer-=siz;
36527           delete[] buffer;
36528         } else {
36529           if (dims[3]<65536) {
36530             unsigned short *buffer = new unsigned short[siz];
36531             cimg::fread(buffer,siz,nfile);
36532             if (endian) cimg::invert_endianness(buffer,siz);
36533             T *ptrd = _data;
36534             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
36535             buffer-=siz;
36536             delete[] buffer;
36537           } else {
36538             unsigned long *buffer = new unsigned long[siz];
36539             cimg::fread(buffer,siz,nfile);
36540             if (endian) cimg::invert_endianness(buffer,siz);
36541             T *ptrd = _data;
36542             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
36543             buffer-=siz;
36544             delete[] buffer;
36545           }
36546         }
36547       }
36548         break;
36549       case 13 : { // Region 3d
36550         cimg::fread(dims,5,nfile);
36551         if (endian) cimg::invert_endianness(dims,5);
36552         assign(dims[3],dims[2],dims[1],1);
36553         const unsigned int siz = size();
36554         if (dims[4]<256) {
36555           unsigned char *buffer = new unsigned char[siz];
36556           cimg::fread(buffer,siz,nfile);
36557           T *ptrd = _data;
36558           cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
36559           buffer-=siz;
36560           delete[] buffer;
36561         } else {
36562           if (dims[4]<65536) {
36563             unsigned short *buffer = new unsigned short[siz];
36564             cimg::fread(buffer,siz,nfile);
36565             if (endian) cimg::invert_endianness(buffer,siz);
36566             T *ptrd = _data;
36567             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
36568             buffer-=siz;
36569             delete[] buffer;
36570           } else {
36571             unsigned int *buffer = new unsigned int[siz];
36572             cimg::fread(buffer,siz,nfile);
36573             if (endian) cimg::invert_endianness(buffer,siz);
36574             T *ptrd = _data;
36575             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
36576             buffer-=siz;
36577             delete[] buffer;
36578           }
36579         }
36580       }
36581         break;
36582       case 16: _cimg_load_pandore_case(4,dims[2],dims[1],1,3,unsigned char,unsigned char,unsigned char,1); break;
36583       case 17: _cimg_load_pandore_case(4,dims[2],dims[1],1,3,long,int,short,4); break;
36584       case 18: _cimg_load_pandore_case(4,dims[2],dims[1],1,3,double,float,float,4); break;
36585       case 19: _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,unsigned char,unsigned char,unsigned char,1); break;
36586       case 20: _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,long,int,short,4); break;
36587       case 21: _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,double,float,float,4); break;
36588       case 22: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned char,unsigned char,unsigned char,1); break;
36589       case 23: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],long,int,short,4);
36590       case 24: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned long,unsigned int,unsigned short,4); break;
36591       case 25: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],double,float,float,4); break;
36592       case 26: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned char,unsigned char,unsigned char,1); break;
36593       case 27: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],long,int,short,4); break;
36594       case 28: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned long,unsigned int,unsigned short,4); break;
36595       case 29: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],double,float,float,4); break;
36596       case 30: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned char,unsigned char,unsigned char,1); break;
36597       case 31: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],long,int,short,4); break;
36598       case 32: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned long,unsigned int,unsigned short,4); break;
36599       case 33: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],double,float,float,4); break;
36600       case 34 : { // Points 1d
36601         int ptbuf[4] = { 0 };
36602         cimg::fread(ptbuf,1,nfile);
36603         if (endian) cimg::invert_endianness(ptbuf,1);
36604         assign(1); (*this)(0) = (T)ptbuf[0];
36605       } break;
36606       case 35 : { // Points 2d
36607         int ptbuf[4] = { 0 };
36608         cimg::fread(ptbuf,2,nfile);
36609         if (endian) cimg::invert_endianness(ptbuf,2);
36610         assign(2); (*this)(0) = (T)ptbuf[1]; (*this)(1) = (T)ptbuf[0];
36611       } break;
36612       case 36 : { // Points 3d
36613         int ptbuf[4] = { 0 };
36614         cimg::fread(ptbuf,3,nfile);
36615         if (endian) cimg::invert_endianness(ptbuf,3);
36616         assign(3); (*this)(0) = (T)ptbuf[2]; (*this)(1) = (T)ptbuf[1]; (*this)(2) = (T)ptbuf[0];
36617       } break;
36618       default :
36619         if (!file) cimg::fclose(nfile);
36620         throw CImgIOException(_cimg_instance
36621                               "load_pandore() : Unable to load data with ID_type %u in file '%s'.",
36622                               cimg_instance,
36623                               imageid,filename?filename:"(FILE*)");
36624       }
36625       if (!file) cimg::fclose(nfile);
36626       return *this;
36627     }
36628 
36629     //! Load image from a PAR-REC (Philips) file.
36630     /**
36631       \param filename Filename, as a C-string.
36632       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
36633       \param align Appending alignment.
36634     **/
36635     CImg<T>& load_parrec(const char *const filename, const char axis='c', const float align=0) {
36636       CImgList<T> list;
36637       list.load_parrec(filename);
36638       if (list._width==1) return list[0].move_to(*this);
36639       return assign(list.get_append(axis,align));
36640     }
36641 
36642     //! Load image from a PAR-REC (Philips) file \newinstance.
36643     static CImg<T> get_load_parrec(const char *const filename, const char axis='c', const float align=0) {
36644       return CImg<T>().load_parrec(filename,axis,align);
36645     }
36646 
36647     //! Load image from a raw binary file.
36648     /**
36649       \param filename Filename, as a C-string.
36650       \param size_x Width of the image buffer.
36651       \param size_y Height of the image buffer.
36652       \param size_z Depth of the image buffer.
36653       \param size_c Spectrum of the image buffer.
36654       \param is_multiplexed Tells if the image values are multiplexed along the C-axis.
36655       \param invert_endianness Tells if the endianness of the image buffer must be inverted.
36656     **/
36657     CImg<T>& load_raw(const char *const filename,
36658                       const unsigned int size_x=0, const unsigned int size_y=1,
36659                       const unsigned int size_z=1, const unsigned int size_c=1,
36660                       const bool is_multiplexed=false, const bool invert_endianness=false) {
36661       return _load_raw(0,filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness);
36662     }
36663 
36664     //! Load image from a raw binary file \newinstance.
36665     static CImg<T> get_load_raw(const char *const filename,
36666                                 const unsigned int size_x=0, const unsigned int size_y=1,
36667                                 const unsigned int size_z=1, const unsigned int size_c=1,
36668                                 const bool is_multiplexed=false, const bool invert_endianness=false) {
36669       return CImg<T>().load_raw(filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness);
36670     }
36671 
36672     //! Load image from a raw binary file \overloading.
36673     CImg<T>& load_raw(std::FILE *const file,
36674                       const unsigned int size_x=0, const unsigned int size_y=1,
36675                       const unsigned int size_z=1, const unsigned int size_c=1,
36676                       const bool is_multiplexed=false, const bool invert_endianness=false) {
36677       return _load_raw(file,0,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness);
36678     }
36679 
36680     //! Load image from a raw binary file \newinstance.
36681     static CImg<T> get_load_raw(std::FILE *const file,
36682                                 const unsigned int size_x=0, const unsigned int size_y=1,
36683                                 const unsigned int size_z=1, const unsigned int size_c=1,
36684                                 const bool is_multiplexed=false, const bool invert_endianness=false) {
36685       return CImg<T>().load_raw(file,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness);
36686     }
36687 
36688     CImg<T>& _load_raw(std::FILE *const file, const char *const filename,
36689                        const unsigned int size_x, const unsigned int size_y,
36690                        const unsigned int size_z, const unsigned int size_c,
36691                        const bool is_multiplexed, const bool invert_endianness) {
36692       if (!file && !filename)
36693         throw CImgArgumentException(_cimg_instance
36694                                     "load_raw() : Specified filename is (null).",
36695                                     cimg_instance);
36696       unsigned int siz = size_x*size_y*size_z*size_c, _size_x = size_x, _size_y = size_y, _size_z = size_z, _size_c = size_c;
36697       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
36698       if (!siz) {  // Retrieve file size.
36699         const long fpos = std::ftell(nfile);
36700         if (fpos<0) throw CImgArgumentException(_cimg_instance
36701                                                 "load_raw() : Cannot determine size of input file '%s'.",
36702                                                 cimg_instance,filename?filename:"(FILE*)");
36703         std::fseek(nfile,0,SEEK_END);
36704         siz = _size_y = (unsigned int)std::ftell(nfile)/sizeof(T);
36705         _size_x = _size_z = _size_c = 1;
36706         std::fseek(nfile,fpos,SEEK_SET);
36707       }
36708       assign(_size_x,_size_y,_size_z,_size_c,0);
36709       if (!is_multiplexed || size_c==1) {
36710         cimg::fread(_data,siz,nfile);
36711         if (invert_endianness) cimg::invert_endianness(_data,siz);
36712       } else {
36713         CImg<T> buf(1,1,1,_size_c);
36714         cimg_forXYZ(*this,x,y,z) {
36715           cimg::fread(buf._data,_size_c,nfile);
36716           if (invert_endianness) cimg::invert_endianness(buf._data,_size_c);
36717           set_vector_at(buf,x,y,z);
36718         }
36719       }
36720       if (!file) cimg::fclose(nfile);
36721       return *this;
36722     }
36723 
36724     //! Load image sequence using FFMPEG av's libraries.
36725     /**
36726       \param filename Filename, as a C-string.
36727       \param first_frame Index of the first frame to read.
36728       \param last_frame Index of the last frame to read.
36729       \param step_frame Step value for frame reading.
36730       \param pixel_format To be documented.
36731       \param resume To be documented.
36732       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
36733       \param align Appending alignment.
36734     **/
36735     CImg<T>& load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
36736                          const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false,
36737                          const char axis='z', const float align=0) {
36738       return get_load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format,resume,axis,align).move_to(*this);
36739     }
36740 
36741     //! Load image sequence using FFMPEG av's libraries \newinstance.
36742     static CImg<T> get_load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
36743                                    const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false,
36744                                    const char axis='z', const float align=0) {
36745       return CImgList<T>().load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format,resume).get_append(axis,align);
36746     }
36747 
36748     //! Load image sequence from a YUV file.
36749     /**
36750       \param filename Filename, as a C-string.
36751       \param size_x Width of the frames.
36752       \param size_y Height of the frames.
36753       \param first_frame Index of the first frame to read.
36754       \param last_frame Index of the last frame to read.
36755       \param step_frame Step value for frame reading.
36756       \param yuv2rgb Tells if the YUV to RGB transform must be applied.
36757       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
36758     **/
36759     CImg<T>& load_yuv(const char *const filename,
36760                       const unsigned int size_x, const unsigned int size_y=1,
36761                       const unsigned int first_frame=0, const unsigned int last_frame=~0U,
36762                       const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
36763       return get_load_yuv(filename,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this);
36764     }
36765 
36766     //! Load image sequence from a YUV file \newinstance.
36767     static CImg<T> get_load_yuv(const char *const filename,
36768                                 const unsigned int size_x, const unsigned int size_y=1,
36769                                 const unsigned int first_frame=0, const unsigned int last_frame=~0U,
36770                                 const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
36771       return CImgList<T>().load_yuv(filename,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb).get_append(axis);
36772     }
36773 
36774     //! Load image sequence from a YUV file \overloading.
36775     CImg<T>& load_yuv(std::FILE *const file,
36776                       const unsigned int size_x, const unsigned int size_y=1,
36777                       const unsigned int first_frame=0, const unsigned int last_frame=~0U,
36778                       const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
36779       return get_load_yuv(file,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this);
36780     }
36781 
36782     //! Load image sequence from a YUV file \newinstance.
36783     static CImg<T> get_load_yuv(std::FILE *const file,
36784                                 const unsigned int size_x, const unsigned int size_y=1,
36785                                 const unsigned int first_frame=0, const unsigned int last_frame=~0U,
36786                                 const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
36787       return CImgList<T>().load_yuv(file,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb).get_append(axis);
36788     }
36789 
36790     //! Load 3d object from a .OFF file.
36791     /**
36792         \param[out] primitives Primitives data of the 3d object.
36793         \param[out] colors Colors data of the 3d object.
36794         \param filename Filename, as a C-string.
36795     **/
36796     template<typename tf, typename tc>
36797     CImg<T>& load_off(CImgList<tf>& primitives, CImgList<tc>& colors, const char *const filename) {
36798       return _load_off(primitives,colors,0,filename);
36799     }
36800 
36801     //! Load 3d object from a .OFF file \newinstance.
36802     template<typename tf, typename tc>
36803     static CImg<T> get_load_off(CImgList<tf>& primitives, CImgList<tc>& colors, const char *const filename) {
36804       return CImg<T>().load_off(primitives,colors,filename);
36805     }
36806 
36807     //! Load 3d object from a .OFF file \overloading.
36808     template<typename tf, typename tc>
36809     CImg<T>& load_off(CImgList<tf>& primitives, CImgList<tc>& colors, std::FILE *const file) {
36810       return _load_off(primitives,colors,file,0);
36811     }
36812 
36813     //! Load 3d object from a .OFF file \newinstance.
36814     template<typename tf, typename tc>
36815     static CImg<T> get_load_off(CImgList<tf>& primitives, CImgList<tc>& colors, std::FILE *const file) {
36816       return CImg<T>().load_off(primitives,colors,file);
36817     }
36818 
36819     template<typename tf, typename tc>
36820     CImg<T>& _load_off(CImgList<tf>& primitives, CImgList<tc>& colors,
36821                        std::FILE *const file, const char *const filename) {
36822       if (!file && !filename)
36823         throw CImgArgumentException(_cimg_instance
36824                                     "load_off() : Specified filename is (null).",
36825                                     cimg_instance);
36826 
36827       std::FILE *const nfile = file?file:cimg::fopen(filename,"r");
36828       unsigned int nb_points = 0, nb_primitives = 0, nb_read = 0;
36829       char line[256] = { 0 };
36830       int err;
36831 
36832       // Skip comments, and read magic string OFF
36833       do { err = std::fscanf(nfile,"%255[^\n] ",line); } while (!err || (err==1 && *line=='#'));
36834       if (cimg::strncasecmp(line,"OFF",3) && cimg::strncasecmp(line,"COFF",4)) {
36835         if (!file) cimg::fclose(nfile);
36836         throw CImgIOException(_cimg_instance
36837                               "load_off() : OFF header not found in file '%s'.",
36838                               cimg_instance,
36839                               filename?filename:"(FILE*)");
36840       }
36841       do { err = std::fscanf(nfile,"%255[^\n] ",line); } while (!err || (err==1 && *line=='#'));
36842       if ((err = std::sscanf(line,"%u%u%*[^\n] ",&nb_points,&nb_primitives))!=2) {
36843         if (!file) cimg::fclose(nfile);
36844         throw CImgIOException(_cimg_instance
36845                               "load_off() : Invalid number of vertices or primitives specified in file '%s'.",
36846                               cimg_instance,
36847                               filename?filename:"(FILE*)");
36848       }
36849 
36850       // Read points data
36851       assign(nb_points,3);
36852       float X = 0, Y = 0, Z = 0;
36853       cimg_forX(*this,l) {
36854         do { err = std::fscanf(nfile,"%255[^\n] ",line); } while (!err || (err==1 && *line=='#'));
36855         if ((err = std::sscanf(line,"%f%f%f%*[^\n] ",&X,&Y,&Z))!=3) {
36856           if (!file) cimg::fclose(nfile);
36857           throw CImgIOException(_cimg_instance
36858                                 "load_off() : Failed to read vertex %u/%u in file '%s'.",
36859                                 cimg_instance,
36860                                 l+1,nb_points,filename?filename:"(FILE*)");
36861         }
36862         (*this)(l,0) = (T)X; (*this)(l,1) = (T)Y; (*this)(l,2) = (T)Z;
36863       }
36864 
36865       // Read primitive data
36866       primitives.assign();
36867       colors.assign();
36868       bool stopflag = false;
36869       while (!stopflag) {
36870         float c0 = 0.7f, c1 = 0.7f, c2 = 0.7f;
36871         unsigned int prim = 0, i0 = 0, i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, i6 = 0, i7 = 0;
36872         *line = 0;
36873         if ((err = std::fscanf(nfile,"%u",&prim))!=1) stopflag=true;
36874         else {
36875           ++nb_read;
36876           switch (prim) {
36877           case 1 : {
36878             if ((err = std::fscanf(nfile,"%u%255[^\n] ",&i0,line))<2) {
36879               cimg::warn(_cimg_instance
36880                          "load_off() : Failed to read primitive %u/%u from file '%s'.",
36881                          cimg_instance,
36882                          nb_read,nb_primitives,filename?filename:"(FILE*)");
36883 
36884               err = std::fscanf(nfile,"%*[^\n] ");
36885             } else {
36886               err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
36887               CImg<tf>::vector(i0).move_to(primitives);
36888               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
36889             }
36890           } break;
36891           case 2 : {
36892             if ((err = std::fscanf(nfile,"%u%u%255[^\n] ",&i0,&i1,line))<2) {
36893               cimg::warn(_cimg_instance
36894                          "load_off() : Failed to read primitive %u/%u from file '%s'.",
36895                          cimg_instance,
36896                          nb_read,nb_primitives,filename?filename:"(FILE*)");
36897 
36898               err = std::fscanf(nfile,"%*[^\n] ");
36899             } else {
36900               err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
36901               CImg<tf>::vector(i0,i1).move_to(primitives);
36902               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
36903             }
36904           } break;
36905           case 3 : {
36906             if ((err = std::fscanf(nfile,"%u%u%u%255[^\n] ",&i0,&i1,&i2,line))<3) {
36907               cimg::warn(_cimg_instance
36908                          "load_off() : Failed to read primitive %u/%u from file '%s'.",
36909                          cimg_instance,
36910                          nb_read,nb_primitives,filename?filename:"(FILE*)");
36911 
36912               err = std::fscanf(nfile,"%*[^\n] ");
36913             } else {
36914               err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
36915               CImg<tf>::vector(i0,i2,i1).move_to(primitives);
36916               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
36917             }
36918           } break;
36919           case 4 : {
36920             if ((err = std::fscanf(nfile,"%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,line))<4) {
36921               cimg::warn(_cimg_instance
36922                          "load_off() : Failed to read primitive %u/%u from file '%s'.",
36923                          cimg_instance,
36924                          nb_read,nb_primitives,filename?filename:"(FILE*)");
36925 
36926               err = std::fscanf(nfile,"%*[^\n] ");
36927             } else {
36928               err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
36929               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
36930               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
36931             }
36932           } break;
36933           case 5 : {
36934             if ((err = std::fscanf(nfile,"%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,line))<5) {
36935               cimg::warn(_cimg_instance
36936                          "load_off() : Failed to read primitive %u/%u from file '%s'.",
36937                          cimg_instance,
36938                          nb_read,nb_primitives,filename?filename:"(FILE*)");
36939 
36940               err = std::fscanf(nfile,"%*[^\n] ");
36941             } else {
36942               err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
36943               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
36944               CImg<tf>::vector(i0,i4,i3).move_to(primitives);
36945               colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
36946               ++nb_primitives;
36947             }
36948           } break;
36949           case 6 : {
36950             if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,line))<6) {
36951               cimg::warn(_cimg_instance
36952                          "load_off() : Failed to read primitive %u/%u from file '%s'.",
36953                          cimg_instance,
36954                          nb_read,nb_primitives,filename?filename:"(FILE*)");
36955 
36956               err = std::fscanf(nfile,"%*[^\n] ");
36957             } else {
36958               err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
36959               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
36960               CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives);
36961               colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
36962               ++nb_primitives;
36963             }
36964           } break;
36965           case 7 : {
36966             if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,line))<7) {
36967               cimg::warn(_cimg_instance
36968                          "load_off() : Failed to read primitive %u/%u from file '%s'.",
36969                          cimg_instance,
36970                          nb_read,nb_primitives,filename?filename:"(FILE*)");
36971 
36972               err = std::fscanf(nfile,"%*[^\n] ");
36973             } else {
36974               err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
36975               CImg<tf>::vector(i0,i4,i3,i1).move_to(primitives);
36976               CImg<tf>::vector(i0,i6,i5,i4).move_to(primitives);
36977               CImg<tf>::vector(i3,i2,i1).move_to(primitives);
36978               colors.insert(3,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
36979               ++(++nb_primitives);
36980             }
36981           } break;
36982           case 8 : {
36983             if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,&i7,line))<7) {
36984               cimg::warn(_cimg_instance
36985                          "load_off() : Failed to read primitive %u/%u from file '%s'.",
36986                          cimg_instance,
36987                          nb_read,nb_primitives,filename?filename:"(FILE*)");
36988 
36989               err = std::fscanf(nfile,"%*[^\n] ");
36990             } else {
36991               err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
36992               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
36993               CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives);
36994               CImg<tf>::vector(i0,i7,i6,i5).move_to(primitives);
36995               colors.insert(3,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
36996               ++(++nb_primitives);
36997             }
36998           } break;
36999           default :
37000             cimg::warn(_cimg_instance
37001                        "load_off() : Failed to read primitive %u/%u (%u vertices) from file '%s'.",
37002                        cimg_instance,
37003                        nb_read,nb_primitives,prim,filename?filename:"(FILE*)");
37004 
37005             err = std::fscanf(nfile,"%*[^\n] ");
37006           }
37007         }
37008       }
37009       if (!file) cimg::fclose(nfile);
37010       if (primitives._width!=nb_primitives)
37011         cimg::warn(_cimg_instance
37012                    "load_off() : Only %u/%u primitives read from file '%s'.",
37013                    cimg_instance,
37014                    primitives._width,nb_primitives,filename?filename:"(FILE*)");
37015       return *this;
37016     }
37017 
37018     //! Load image sequence using FFMPEG's external tool 'ffmpeg'.
37019     /**
37020       \param filename Filename, as a C-string.
37021       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
37022       \param align Appending alignment.
37023     **/
37024     CImg<T>& load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) {
37025       return get_load_ffmpeg_external(filename,axis,align).move_to(*this);
37026     }
37027 
37028     //! Load image sequence using FFMPEG's external tool 'ffmpeg' \newinstance.
37029     static CImg<T> get_load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) {
37030       return CImgList<T>().load_ffmpeg_external(filename).get_append(axis,align);
37031     }
37032 
37033     //! Load image using GraphicsMagick's external tool 'gm'.
37034     /**
37035        \param filename Filename, as a C-string.
37036     **/
37037     CImg<T>& load_graphicsmagick_external(const char *const filename) {
37038       if (!filename)
37039         throw CImgArgumentException(_cimg_instance
37040                                     "load_graphicsmagick_external() : Specified filename is (null).",
37041                                     cimg_instance);
37042       std::fclose(cimg::fopen(filename,"rb"));            // Check if file exists.
37043       char command[1024] = { 0 }, filetmp[512] = { 0 };
37044       std::FILE *file = 0;
37045 #if cimg_OS==1
37046       cimg_snprintf(command,sizeof(command),"%s convert \"%s\" pnm:-",cimg::graphicsmagick_path(),filename);
37047       file = popen(command,"r");
37048       if (file) {
37049         try { load_pnm(file); } catch (...) {
37050           pclose(file);
37051           throw CImgIOException(_cimg_instance
37052                                 "load_graphicsmagick_external() : Failed to load file '%s' with external command 'gm'.",
37053                                 cimg_instance,
37054                                 filename);
37055         }
37056         pclose(file);
37057         return *this;
37058       }
37059 #endif
37060       do {
37061         cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.pnm",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
37062         if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
37063       } while (file);
37064       cimg_snprintf(command,sizeof(command),"%s convert \"%s\" \"%s\"",cimg::graphicsmagick_path(),filename,filetmp);
37065       cimg::system(command,cimg::graphicsmagick_path());
37066       if (!(file = std::fopen(filetmp,"rb"))) {
37067         cimg::fclose(cimg::fopen(filename,"r"));
37068         throw CImgIOException(_cimg_instance
37069                               "load_graphicsmagick_external() : Failed to load file '%s' with external command 'gm'.",
37070                               cimg_instance,
37071                               filename);
37072 
37073       } else cimg::fclose(file);
37074       load_pnm(filetmp);
37075       std::remove(filetmp);
37076       return *this;
37077     }
37078 
37079     //! Load image using GraphicsMagick's external tool 'gm' \newinstance.
37080     static CImg<T> get_load_graphicsmagick_external(const char *const filename) {
37081       return CImg<T>().load_graphicsmagick_external(filename);
37082     }
37083 
37084     //! Load gzipped image file, using external tool 'gunzip'.
37085     /**
37086        \param filename Filename, as a C-string.
37087     **/
37088     CImg<T>& load_gzip_external(const char *const filename) {
37089       if (!filename)
37090         throw CImgIOException(_cimg_instance
37091                               "load_gzip_external() : Specified filename is (null).",
37092                               cimg_instance);
37093       std::fclose(cimg::fopen(filename,"rb"));            // Check if file exists.
37094       char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
37095       const char
37096         *const ext = cimg::split_filename(filename,body),
37097         *const ext2 = cimg::split_filename(body,0);
37098 
37099       std::FILE *file = 0;
37100       do {
37101         if (!cimg::strcasecmp(ext,"gz")) {
37102           if (*ext2) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
37103           else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
37104         } else {
37105           if (*ext) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
37106           else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
37107         }
37108         if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
37109       } while (file);
37110 
37111       cimg_snprintf(command,sizeof(command),"%s -c \"%s\" > %s",cimg::gunzip_path(),filename,filetmp);
37112       cimg::system(command);
37113       if (!(file = std::fopen(filetmp,"rb"))) {
37114         cimg::fclose(cimg::fopen(filename,"r"));
37115         throw CImgIOException(_cimg_instance
37116                               "load_gzip_external() : Failed to load file '%s' with external command 'gunzip'.",
37117                               cimg_instance,
37118                               filename);
37119 
37120       } else cimg::fclose(file);
37121       load(filetmp);
37122       std::remove(filetmp);
37123       return *this;
37124     }
37125 
37126     //! Load gzipped image file, using external tool 'gunzip' \newinstance.
37127     static CImg<T> get_load_gzip_external(const char *const filename) {
37128       return CImg<T>().load_gzip_external(filename);
37129     }
37130 
37131     //! Load image using ImageMagick's external tool 'convert'.
37132     /**
37133        \param filename Filename, as a C-string.
37134     **/
37135     CImg<T>& load_imagemagick_external(const char *const filename) {
37136       if (!filename)
37137         throw CImgArgumentException(_cimg_instance
37138                                     "load_imagemagick_external() : Specified filename is (null).",
37139                                     cimg_instance);
37140       std::fclose(cimg::fopen(filename,"rb"));            // Check if file exists.
37141       char command[1024] = { 0 }, filetmp[512] = { 0 };
37142       std::FILE *file = 0;
37143 #if cimg_OS==1
37144       cimg_snprintf(command,sizeof(command),"%s \"%s\" pnm:-",cimg::imagemagick_path(),filename);
37145       file = popen(command,"r");
37146       if (file) {
37147         try { load_pnm(file); } catch (...) {
37148           pclose(file);
37149           throw CImgIOException(_cimg_instance
37150                                 "load_imagemagick_external() : Failed to load file '%s' with external command 'convert'.",
37151                                 cimg_instance,
37152                                 filename);
37153         }
37154         pclose(file);
37155         return *this;
37156       }
37157 #endif
37158       do {
37159         cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.pnm",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
37160         if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
37161       } while (file);
37162       cimg_snprintf(command,sizeof(command),"%s \"%s\" \"%s\"",cimg::imagemagick_path(),filename,filetmp);
37163       cimg::system(command,cimg::imagemagick_path());
37164       if (!(file = std::fopen(filetmp,"rb"))) {
37165         cimg::fclose(cimg::fopen(filename,"r"));
37166         throw CImgIOException(_cimg_instance
37167                               "load_imagemagick_external() : Failed to load file '%s' with external command 'convert'.",
37168                               cimg_instance,
37169                               filename);
37170 
37171       } else cimg::fclose(file);
37172       load_pnm(filetmp);
37173       std::remove(filetmp);
37174       return *this;
37175     }
37176 
37177     //! Load image using ImageMagick's external tool 'convert' \newinstance.
37178     static CImg<T> get_load_imagemagick_external(const char *const filename) {
37179       return CImg<T>().load_imagemagick_external(filename);
37180     }
37181 
37182     //! Load image from a DICOM file, using XMedcon's external tool 'medcon'.
37183     /**
37184        \param filename Filename, as a C-string.
37185     **/
37186     CImg<T>& load_medcon_external(const char *const filename) {
37187       if (!filename)
37188         throw CImgArgumentException(_cimg_instance
37189                                     "load_medcon_external() : Specified filename is (null).",
37190                                     cimg_instance);
37191       std::fclose(cimg::fopen(filename,"rb"));            // Check if file exists.
37192       char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
37193       cimg::fclose(cimg::fopen(filename,"r"));
37194       std::FILE *file = 0;
37195       do {
37196         cimg_snprintf(filetmp,sizeof(filetmp),"%s.hdr",cimg::filenamerand());
37197         if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
37198       } while (file);
37199       cimg_snprintf(command,sizeof(command),"%s -w -c anlz -o %s -f %s",cimg::medcon_path(),filetmp,filename);
37200       cimg::system(command);
37201       cimg::split_filename(filetmp,body);
37202 
37203       cimg_snprintf(command,sizeof(command),"%s.hdr",body);
37204       file = std::fopen(command,"rb");
37205       if (!file) {
37206         cimg_snprintf(command,sizeof(command),"m000-%s.hdr",body);
37207         file = std::fopen(command,"rb");
37208         if (!file) {
37209           throw CImgIOException(_cimg_instance
37210                                 "load_medcon_external() : Failed to load file '%s' with external command 'medcon'.",
37211                                 cimg_instance,
37212                                 filename);
37213         }
37214       }
37215       cimg::fclose(file);
37216       load_analyze(command);
37217       std::remove(command);
37218       cimg::split_filename(command,body);
37219       cimg_snprintf(command,sizeof(command),"%s.img",body);
37220       std::remove(command);
37221       return *this;
37222     }
37223 
37224     //! Load image from a DICOM file, using XMedcon's external tool 'medcon' \newinstance.
37225     static CImg<T> get_load_medcon_external(const char *const filename) {
37226       return CImg<T>().load_medcon_external(filename);
37227     }
37228 
37229     //! Load image from a RAW Color Camera file, using external tool 'dcraw'.
37230     /**
37231        \param filename Filename, as a C-string.
37232     **/
37233     CImg<T>& load_dcraw_external(const char *const filename) {
37234       if (!filename)
37235         throw CImgArgumentException(_cimg_instance
37236                                     "load_dcraw_external() : Specified filename is (null).",
37237                                     cimg_instance);
37238       std::fclose(cimg::fopen(filename,"rb"));            // Check if file exists.
37239       char command[1024] = { 0 }, filetmp[512] = { 0 };
37240       std::FILE *file = 0;
37241 #if cimg_OS==1
37242       cimg_snprintf(command,sizeof(command),"%s -w -4 -c \"%s\"",cimg::dcraw_path(),filename);
37243       file = popen(command,"r");
37244       if (file) {
37245         try { load_pnm(file); } catch (...) {
37246           pclose(file);
37247           throw CImgIOException(_cimg_instance
37248                                 "load_dcraw_external() : Failed to load file '%s' with external command 'dcraw'.",
37249                                 cimg_instance,
37250                                 filename);
37251         }
37252         pclose(file);
37253         return *this;
37254       }
37255 #endif
37256       do {
37257         cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.ppm",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
37258         if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
37259       } while (file);
37260       cimg_snprintf(command,sizeof(command),"%s -w -4 -c \"%s\" > %s",cimg::dcraw_path(),filename,filetmp);
37261       cimg::system(command,cimg::dcraw_path());
37262       if (!(file = std::fopen(filetmp,"rb"))) {
37263         cimg::fclose(cimg::fopen(filename,"r"));
37264         throw CImgIOException(_cimg_instance
37265                               "load_dcraw_external() : Failed to load file '%s' with external command 'dcraw'.",
37266                               cimg_instance,
37267                               filename);
37268 
37269       } else cimg::fclose(file);
37270       load_pnm(filetmp);
37271       std::remove(filetmp);
37272       return *this;
37273     }
37274 
37275     //! Load image from a RAW Color Camera file, using external tool 'dcraw' \newinstance.
37276     static CImg<T> get_load_dcraw_external(const char *const filename) {
37277       return CImg<T>().load_dcraw_external(filename);
37278     }
37279 
37280     //! Load image from a camera stream, using OpenCV.
37281     /**
37282        \param camera_index Index of the camera to capture images from.
37283        \param skip_frames Number of frames to skip before the capture.
37284        \param release_camera Tells if the camera ressource must be released at the end of the method.
37285     **/
37286     CImg<T>& load_camera(const int camera_index=-1, const unsigned int skip_frames=0, const bool release_camera=false) {
37287 #ifdef cimg_use_opencv
37288       const int ind = camera_index + 1;
37289       if (ind<0 || ind>255)
37290         throw CImgArgumentException(_cimg_instance
37291                                     "load_camera() : Invalid request for camera #%d.",
37292                                     cimg_instance,
37293                                     camera_index);
37294       static CvCapture *capture[256] = { 0 };
37295       if (release_camera) {
37296         if (capture[ind]) cvReleaseCapture(&(capture[ind]));
37297         capture[ind] = 0;
37298         return *this;
37299       }
37300       if (!capture[ind]) {
37301         capture[ind] = cvCreateCameraCapture(camera_index);
37302         if (!capture[ind]) {
37303           if (camera_index>=0)
37304             throw CImgIOException(_cimg_instance
37305                                   "load_camera() : Failed to initialize camera #%d.",
37306                                   cimg_instance,
37307                                   camera_index);
37308           else
37309             throw CImgIOException(_cimg_instance
37310                                   "load_camera() : Failed to initialize default camera.",
37311                                   cimg_instance);
37312         }
37313       }
37314       const IplImage *img = 0;
37315       for (unsigned int i = 0; i<skip_frames; ++i) img = cvQueryFrame(capture[ind]);
37316       img = cvQueryFrame(capture[ind]);
37317       if (img) {
37318         const int step = (int)(img->widthStep - 3*img->width);
37319         assign(img->width,img->height,1,3);
37320         const unsigned char* ptrs = (unsigned char*)img->imageData;
37321         T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
37322         if (step>0) cimg_forY(*this,y) {
37323             cimg_forX(*this,x) { *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++); }
37324             ptrs+=step;
37325           } else for (unsigned long siz = (unsigned long)img->width*img->height; siz; --siz) {
37326             *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++);
37327           }
37328       }
37329 #else
37330       cimg::unused(camera_index,skip_frames,release_camera);
37331       throw CImgIOException(_cimg_instance
37332                             "load_camera() : This function requires the OpenCV library to run "
37333                             "(macro 'cimg_use_opencv' must be defined).",
37334                             cimg_instance);
37335 #endif
37336       return *this;
37337     }
37338 
37339     //! Load image from a camera stream, using OpenCV \newinstance.
37340     static CImg<T> get_load_camera(const int camera_index=-1, const unsigned int skip_frames=0, const bool release_camera=false) {
37341       return CImg<T>().load_camera(camera_index,skip_frames,release_camera);
37342     }
37343 
37344     //! Load image using various non-native ways.
37345     /**
37346        \param filename Filename, as a C-string.
37347     **/
37348     CImg<T>& load_other(const char *const filename) {
37349       if (!filename)
37350         throw CImgArgumentException(_cimg_instance
37351                                     "load_other() : Specified filename is (null).",
37352                                     cimg_instance);
37353 
37354       const unsigned int omode = cimg::exception_mode();
37355       cimg::exception_mode() = 0;
37356       try { load_magick(filename); }
37357       catch (CImgException&) {
37358         try { load_imagemagick_external(filename); }
37359         catch (CImgException&) {
37360           try { load_graphicsmagick_external(filename); }
37361           catch (CImgException&) {
37362             try { load_cimg(filename); }
37363             catch (CImgException&) {
37364               try {
37365                 std::fclose(cimg::fopen(filename,"rb"));
37366                 cimg::exception_mode() = omode;
37367                 throw CImgIOException(_cimg_instance
37368                                       "load_other() : Failed to recognize format of file '%s'.",
37369                                       cimg_instance,
37370                                       filename);
37371               } catch (CImgException&) {
37372                 cimg::exception_mode() = omode;
37373                 throw CImgIOException(_cimg_instance
37374                                       "load_other() : Failed to open file '%s'.",
37375                                       cimg_instance,
37376                                       filename);
37377               }
37378             }
37379           }
37380         }
37381       }
37382       cimg::exception_mode() = omode;
37383       return *this;
37384     }
37385 
37386     //! Load image using various non-native ways \newinstance.
37387     static CImg<T> get_load_other(const char *const filename) {
37388       return CImg<T>().load_other(filename);
37389     }
37390 
37391     //@}
37392     //---------------------------
37393     //
37394     //! \name Data Output
37395     //@{
37396     //---------------------------
37397 
37398     //! Display informations about the image data on the standard error output.
37399     /**
37400        \param title Name for the considered image.
37401        \param display_stats Tells to compute and display image statistics.
37402     **/
37403     const CImg<T>& print(const char *const title=0, const bool display_stats=true) const {
37404       int xm = 0, ym = 0, zm = 0, vm = 0, xM = 0, yM = 0, zM = 0, vM = 0;
37405       static CImg<doubleT> st;
37406       if (!is_empty() && display_stats) {
37407         st = get_stats();
37408         xm = (int)st[4]; ym = (int)st[5], zm = (int)st[6], vm = (int)st[7];
37409         xM = (int)st[8]; yM = (int)st[9], zM = (int)st[10], vM = (int)st[11];
37410       }
37411       const unsigned long siz = size(), msiz = siz*sizeof(T), siz1 = siz-1, mdisp = msiz<8*1024?0:(msiz<8*1024*1024?1:2), width1 = _width-1;
37412 
37413       char _title[64] = { 0 };
37414       if (!title) cimg_snprintf(_title,sizeof(_title),"CImg<%s>",pixel_type());
37415 
37416       std::fprintf(cimg::output(),"%s%s%s%s : %sthis%s = %p, %ssize%s = (%u,%u,%u,%u) [%lu %s], %sdata%s = (%s*)%p",
37417                    cimg::t_magenta,cimg::t_bold,title?title:_title,cimg::t_normal,
37418                    cimg::t_bold,cimg::t_normal,(void*)this,
37419                    cimg::t_bold,cimg::t_normal,_width,_height,_depth,_spectrum,
37420                    mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)),
37421                    mdisp==0?"b":(mdisp==1?"Kio":"Mio"),
37422                    cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin());
37423       if (_data) std::fprintf(cimg::output(),"..%p (%s) = [ ",(void*)((char*)end()-1),_is_shared?"shared":"non-shared");
37424       else std::fprintf(cimg::output()," (%s) = [ ",_is_shared?"shared":"non-shared");
37425 
37426       if (!is_empty()) cimg_foroff(*this,off) {
37427         std::fprintf(cimg::output(),cimg::type<T>::format(),cimg::type<T>::format(_data[off]));
37428         if (off!=siz1) std::fprintf(cimg::output(),"%s",off%_width==width1?" ; ":" ");
37429         if (off==7 && siz>16) { off = siz1-8; if (off!=7) std::fprintf(cimg::output(),"... "); }
37430       }
37431       if (!is_empty() && display_stats)
37432         std::fprintf(cimg::output()," ], %smin%s = %g, %smax%s = %g, %smean%s = %g, %sstd%s = %g, %scoords_min%s = (%u,%u,%u,%u), %scoords_max%s = (%u,%u,%u,%u).\n",
37433                      cimg::t_bold,cimg::t_normal,st[0],
37434                      cimg::t_bold,cimg::t_normal,st[1],
37435                      cimg::t_bold,cimg::t_normal,st[2],
37436                      cimg::t_bold,cimg::t_normal,std::sqrt(st[3]),
37437                      cimg::t_bold,cimg::t_normal,xm,ym,zm,vm,
37438                      cimg::t_bold,cimg::t_normal,xM,yM,zM,vM);
37439       else std::fprintf(cimg::output(),"%s].\n",is_empty()?"":" ");
37440       std::fflush(cimg::output());
37441       return *this;
37442     }
37443 
37444     //! Display image into a CImgDisplay window.
37445     /**
37446        \param disp Display window.
37447     **/
37448     const CImg<T>& display(CImgDisplay& disp) const {
37449       disp.display(*this);
37450       return *this;
37451     }
37452 
37453     //! Display image into a CImgDisplay window, in an interactive way.
37454     /**
37455         \param disp Display window.
37456         \param display_info Tells if image informations are displayed on the standard output.
37457     **/
37458     const CImg<T>& display(CImgDisplay &disp, const bool display_info) const {
37459       return _display(disp,0,display_info,false);
37460     }
37461 
37462     //! Display image into an interactive window.
37463     /**
37464         \param title Window title
37465         \param display_info Tells if image informations are displayed on the standard output.
37466     **/
37467     const CImg<T>& display(const char *const title=0, const bool display_info=true) const {
37468       CImgDisplay disp;
37469       return _display(disp,title,display_info,false);
37470     }
37471 
37472     const CImg<T>& _display(CImgDisplay &disp, const char *const title,
37473                             const bool display_info, const bool exit_on_simpleclick) const {
37474       if (is_empty())
37475         throw CImgInstanceException(_cimg_instance
37476                                     "display() : Empty instance.",
37477                                     cimg_instance);
37478 
37479       unsigned int oldw = 0, oldh = 0, XYZ[3], key = 0;
37480       int x0 = 0, y0 = 0, z0 = 0, x1 = width() - 1, y1 = height() - 1, z1 = depth() - 1;
37481 
37482       if (!disp) {
37483         disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1);
37484         if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum);
37485       } else if (title) disp.set_title("%s",title);
37486       disp.show().flush();
37487 
37488       const CImg<char> dtitle = CImg<char>::string(disp.title());
37489       if (display_info) print(dtitle);
37490 
37491       CImg<T> zoom;
37492       for (bool reset_view = true, resize_disp = false, is_first_select = true; !key && !disp.is_closed(); ) {
37493         if (reset_view) {
37494           XYZ[0] = (x0 + x1)/2; XYZ[1] = (y0 + y1)/2; XYZ[2] = (z0 + z1)/2;
37495           x0 = 0; y0 = 0; z0 = 0; x1 = _width - 1; y1 = _height-1; z1 = _depth-1;
37496           oldw = disp.width(); oldh = disp.height();
37497           reset_view = false;
37498         }
37499         if (!x0 && !y0 && !z0 && x1==width()-1 && y1==height()-1 && z1==depth()-1) zoom.assign();
37500         else zoom = get_crop(x0,y0,z0,x1,y1,z1);
37501 
37502         const unsigned int
37503           dx = 1 + x1 - x0, dy = 1 + y1 - y0, dz = 1 + z1 - z0,
37504           tw = dx + (dz>1?dz:0), th = dy + (dz>1?dz:0);
37505         if (!disp.is_fullscreen() && resize_disp) {
37506           const unsigned int
37507             ttw = tw*disp.width()/oldw, tth = th*disp.height()/oldh,
37508             dM = cimg::max(ttw,tth), diM = (unsigned int)cimg::max(disp.width(),disp.height()),
37509             imgw = cimg::max(16U,ttw*diM/dM), imgh = cimg::max(16U,tth*diM/dM);
37510           disp.set_fullscreen(false).resize(cimg_fitscreen(imgw,imgh,1),false);
37511           resize_disp = false;
37512         }
37513         oldw = tw; oldh = th;
37514 
37515         bool
37516           go_up = false, go_down = false, go_left = false, go_right = false,
37517           go_inc = false, go_dec = false, go_in = false, go_out = false,
37518           go_in_center = false;
37519         const CImg<T>& visu = zoom?zoom:*this;
37520         const CImg<intT> selection = visu._get_select(disp,0,2,XYZ,x0,y0,z0,is_first_select);
37521         is_first_select = false;
37522 
37523         if (disp.wheel()) {
37524           if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { go_out = !(go_in = disp.wheel()>0); go_in_center = false; }
37525           else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) { go_right = !(go_left = disp.wheel()>0); }
37526           else if (disp.is_keyALT() || disp.is_keyALTGR() || _depth==1) { go_down = !(go_up = disp.wheel()>0); }
37527           disp.set_wheel();
37528         }
37529 
37530         const int
37531           sx0 = selection(0), sy0 = selection(1), sz0 = selection(2),
37532           sx1 = selection(3), sy1 = selection(4), sz1 = selection(5);
37533         if (sx0>=0 && sy0>=0 && sz0>=0 && sx1>=0 && sy1>=0 && sz1>=0) {
37534           x1 = x0 + sx1; y1 = y0 + sy1; z1 = z0 + sz1; x0+=sx0; y0+=sy0; z0+=sz0;
37535           if (sx0==sx1 && sy0==sy1 && sz0==sz1) {
37536             if (exit_on_simpleclick && !zoom) break; else reset_view = true;
37537           }
37538           resize_disp = true;
37539         } else switch (key = disp.key()) {
37540 #if cimg_OS!=2
37541           case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT :
37542 #endif
37543           case 0 : case cimg::keyCTRLLEFT : case cimg::keyPAD5 : case cimg::keySHIFTLEFT :
37544 #if cimg_OS!=2
37545           case cimg::keyALTGR :
37546 #endif
37547           case cimg::keyALT : key = 0; break;
37548           case cimg::keyP : if (visu._depth>1 && (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT())) { // Special mode : play stack of frames
37549               const unsigned int
37550                 w1 = visu._width*disp.width()/(visu._width+(visu._depth>1?visu._depth:0)),
37551                 h1 = visu._height*disp.height()/(visu._height+(visu._depth>1?visu._depth:0));
37552               float frame_timing = 5;
37553               bool is_stopped = false;
37554               disp.set_key(key,false).set_wheel().resize(cimg_fitscreen(w1,h1,1),false); key = 0;
37555               for (unsigned int timer = 0; !key && !disp.is_closed() && !disp.button(); ) {
37556                 if (disp.is_resized()) disp.resize(false);
37557                 if (!timer) {
37558                   visu.get_slice((int)XYZ[2]).display(disp.set_title("%s | z=%d",dtitle.data(),XYZ[2]));
37559                   (++XYZ[2])%=visu._depth;
37560                 }
37561                 if (!is_stopped) { if (++timer>(unsigned int)frame_timing) timer = 0; } else timer = ~0U;
37562                 if (disp.wheel()) { frame_timing-=disp.wheel()/3.0f; disp.set_wheel(); }
37563                 switch (key = disp.key()) {
37564 #if cimg_OS!=2
37565                 case cimg::keyCTRLRIGHT :
37566 #endif
37567                 case cimg::keyCTRLLEFT : key = 0; break;
37568                 case cimg::keyPAGEUP : frame_timing-=0.3f; key = 0; break;
37569                 case cimg::keyPAGEDOWN : frame_timing+=0.3f; key = 0; break;
37570                 case cimg::keySPACE : is_stopped = !is_stopped; disp.set_key(key,false); key = 0; break;
37571                 case cimg::keyARROWLEFT : case cimg::keyARROWUP : is_stopped = true; timer = 0; key = 0; break;
37572                 case cimg::keyARROWRIGHT : case cimg::keyARROWDOWN : is_stopped = true; (XYZ[2]+=visu._depth-2)%=visu._depth; timer = 0; key = 0; break;
37573                 case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
37574                     disp.set_fullscreen(false).resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
37575                                                       CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false);
37576                     disp.set_key(key,false); key = 0;
37577                   } break;
37578                 case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
37579                     disp.set_fullscreen(false).resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false).set_key(key,false); key = 0;
37580                   } break;
37581                 case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
37582                     disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false).set_key(key,false); key = 0;
37583                   } break;
37584                 case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
37585                     disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen().set_key(key,false); key = 0;
37586                   } break;
37587                 }
37588                 frame_timing = frame_timing<1?1:(frame_timing>39?39:frame_timing);
37589                 disp.wait(20);
37590               }
37591               const unsigned int
37592                 w2 = (visu._width + (visu._depth>1?visu._depth:0))*disp.width()/visu._width,
37593                 h2 = (visu._height + (visu._depth>1?visu._depth:0))*disp.height()/visu._height;
37594               disp.resize(cimg_fitscreen(w2,h2,1),false).set_title(dtitle.data()).set_key().set_button().set_wheel();
37595               key = 0;
37596             } break;
37597           case cimg::keyHOME : reset_view = resize_disp = true; key = 0; break;
37598           case cimg::keyPADADD : go_in = true; go_in_center = true; key = 0; break;
37599           case cimg::keyPADSUB : go_out = true; key = 0; break;
37600           case cimg::keyARROWLEFT : case cimg::keyPAD4: go_left = true; key = 0; break;
37601           case cimg::keyARROWRIGHT : case cimg::keyPAD6: go_right = true; key = 0; break;
37602           case cimg::keyARROWUP : case cimg::keyPAD8: go_up = true; key = 0; break;
37603           case cimg::keyARROWDOWN : case cimg::keyPAD2: go_down = true; key = 0; break;
37604           case cimg::keyPAD7 : go_up = go_left = true; key = 0; break;
37605           case cimg::keyPAD9 : go_up = go_right = true; key = 0; break;
37606           case cimg::keyPAD1 : go_down = go_left = true; key = 0; break;
37607           case cimg::keyPAD3 : go_down = go_right = true; key = 0; break;
37608           case cimg::keyPAGEUP : go_inc = true; key = 0; break;
37609           case cimg::keyPAGEDOWN : go_dec = true; key = 0; break;
37610           }
37611         if (go_in) {
37612           const int
37613             mx = go_in_center?disp.width()/2:disp.mouse_x(),
37614             my = go_in_center?disp.height()/2:disp.mouse_y(),
37615             mX = mx*(_width+(_depth>1?_depth:0))/disp.width(),
37616             mY = my*(_height+(_depth>1?_depth:0))/disp.height();
37617           int X = XYZ[0], Y = XYZ[1], Z = XYZ[2];
37618           if (mX<width() && mY<height())  { X = x0 + mX*(1+x1-x0)/_width; Y = y0 + mY*(1+y1-y0)/_height; Z = XYZ[2]; }
37619           if (mX<width() && mY>=height()) { X = x0 + mX*(1+x1-x0)/_width; Z = z0 + (mY-_height)*(1+z1-z0)/_depth; Y = XYZ[1]; }
37620           if (mX>=width() && mY<height()) { Y = y0 + mY*(1+y1-y0)/_height; Z = z0 + (mX-_width)*(1+z1-z0)/_depth; X = XYZ[0]; }
37621           if (x1-x0>4) { x0 = X - 7*(X-x0)/8; x1 = X + 7*(x1-X)/8; }
37622           if (y1-y0>4) { y0 = Y - 7*(Y-y0)/8; y1 = Y + 7*(y1-Y)/8; }
37623           if (z1-z0>4) { z0 = Z - 7*(Z-z0)/8; z1 = Z + 7*(z1-Z)/8; }
37624         }
37625         if (go_out) {
37626           const int
37627             delta_x = (x1-x0)/8, delta_y = (y1-y0)/8, delta_z = (z1-z0)/8,
37628             ndelta_x = delta_x?delta_x:(_width>1?1:0),
37629             ndelta_y = delta_y?delta_y:(_height>1?1:0),
37630             ndelta_z = delta_z?delta_z:(_depth>1?1:0);
37631           x0-=ndelta_x; y0-=ndelta_y; z0-=ndelta_z;
37632           x1+=ndelta_x; y1+=ndelta_y; z1+=ndelta_z;
37633           if (x0<0) { x1-=x0; x0 = 0; if (x1>=width()) x1 = width() - 1; }
37634           if (y0<0) { y1-=y0; y0 = 0; if (y1>=height()) y1 = height() - 1; }
37635           if (z0<0) { z1-=z0; z0 = 0; if (z1>=depth()) z1 = depth() - 1; }
37636           if (x1>=width()) { x0-=(x1-width()+1); x1 = width()-1; if (x0<0) x0 = 0; }
37637           if (y1>=height()) { y0-=(y1-height()+1); y1 = height()-1; if (y0<0) y0 = 0; }
37638           if (z1>=depth()) { z0-=(z1-depth()+1); z1 = depth()-1; if (z0<0) z0 = 0; }
37639         }
37640         if (go_left) {
37641           const int delta = (x1-x0)/5, ndelta = delta?delta:(_width>1?1:0);
37642           if (x0-ndelta>=0) { x0-=ndelta; x1-=ndelta; }
37643           else { x1-=x0; x0 = 0; }
37644         }
37645         if (go_right) {
37646           const int delta = (x1-x0)/5, ndelta = delta?delta:(_width>1?1:0);
37647           if (x1+ndelta<width()) { x0+=ndelta; x1+=ndelta; }
37648           else { x0+=(width()-1-x1); x1 = width()-1; }
37649         }
37650         if (go_up) {
37651           const int delta = (y1-y0)/5, ndelta = delta?delta:(_height>1?1:0);
37652           if (y0-ndelta>=0) { y0-=ndelta; y1-=ndelta; }
37653           else { y1-=y0; y0 = 0; }
37654         }
37655         if (go_down) {
37656           const int delta = (y1-y0)/5, ndelta = delta?delta:(_height>1?1:0);
37657           if (y1+ndelta<height()) { y0+=ndelta; y1+=ndelta; }
37658           else { y0+=(height()-1-y1); y1 = height()-1; }
37659         }
37660         if (go_inc) {
37661           const int delta = (z1-z0)/5, ndelta = delta?delta:(_depth>1?1:0);
37662           if (z0-ndelta>=0) { z0-=ndelta; z1-=ndelta; }
37663           else { z1-=z0; z0 = 0; }
37664         }
37665         if (go_dec) {
37666           const int delta = (z1-z0)/5, ndelta = delta?delta:(_depth>1?1:0);
37667           if (z1+ndelta<depth()) { z0+=ndelta; z1+=ndelta; }
37668           else { z0+=(depth()-1-z1); z1 = depth()-1; }
37669         }
37670         disp.wait(100);
37671       }
37672       disp.set_key(key);
37673       return *this;
37674     }
37675 
37676     //! Display object 3d in an interactive window.
37677     /**
37678        \param disp Display window.
37679        \param vertices Vertices data of the 3d object.
37680        \param primitives Primitives data of the 3d object.
37681        \param colors Colors data of the 3d object.
37682        \param opacities Opacities data of the 3d object.
37683        \param centering Tells if the 3d object must be centered for the display.
37684        \param render_static Rendering mode.
37685        \param render_motion Rendering mode, when the 3d object is moved.
37686        \param is_double_sided Tells if the object primitives are double-sided.
37687        \param focale Focale
37688        \param light_x X-coordinate of the light source.
37689        \param light_y Y-coordinate of the light source.
37690        \param light_z Z-coordinate of the light source.
37691        \param specular_light Amount of specular light.
37692        \param specular_shine Shininess of the object material.
37693        \param display_axes Tells if the 3d axes are displayed.
37694        \param pose_matrix Pointer to 12 values, defining a 3d pose (as a 4x3 matrix).
37695     **/
37696     template<typename tp, typename tf, typename tc, typename to>
37697     const CImg<T>& display_object3d(CImgDisplay& disp,
37698                                     const CImg<tp>& vertices,
37699                                     const CImgList<tf>& primitives,
37700                                     const CImgList<tc>& colors,
37701                                     const to& opacities,
37702                                     const bool centering=true,
37703                                     const int render_static=4, const int render_motion=1,
37704                                     const bool is_double_sided=true, const float focale=500,
37705                                     const float light_x=0, const float light_y=0, const float light_z=-5000,
37706                                     const float specular_light=0.2f, const float specular_shine=0.1f,
37707                                     const bool display_axes=true, float *const pose_matrix=0) const {
37708       return _display_object3d(disp,0,vertices,primitives,colors,opacities,centering,render_static,
37709                                render_motion,is_double_sided,focale,
37710                                light_x,light_y,light_z,specular_light,specular_shine,
37711                                display_axes,pose_matrix);
37712     }
37713 
37714     //! Display object 3d in an interactive window \simplification.
37715     template<typename tp, typename tf, typename tc, typename to>
37716     const CImg<T>& display_object3d(const char *const title,
37717                                     const CImg<tp>& vertices,
37718                                     const CImgList<tf>& primitives,
37719                                     const CImgList<tc>& colors,
37720                                     const to& opacities,
37721                                     const bool centering=true,
37722                                     const int render_static=4, const int render_motion=1,
37723                                     const bool is_double_sided=true, const float focale=500,
37724                                     const float light_x=0, const float light_y=0, const float light_z=-5000,
37725                                     const float specular_light=0.2f, const float specular_shine=0.1f,
37726                                     const bool display_axes=true, float *const pose_matrix=0) const {
37727       CImgDisplay disp;
37728       return _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,render_static,
37729                                render_motion,is_double_sided,focale,
37730                                light_x,light_y,light_z,specular_light,specular_shine,
37731                                display_axes,pose_matrix);
37732     }
37733 
37734     //! Display object 3d in an interactive window \simplification.
37735     template<typename tp, typename tf, typename tc>
37736     const CImg<T>& display_object3d(CImgDisplay &disp,
37737                                     const CImg<tp>& vertices,
37738                                     const CImgList<tf>& primitives,
37739                                     const CImgList<tc>& colors,
37740                                     const bool centering=true,
37741                                     const int render_static=4, const int render_motion=1,
37742                                     const bool is_double_sided=true, const float focale=500,
37743                                     const float light_x=0, const float light_y=0, const float light_z=-5000,
37744                                     const float specular_light=0.2f, const float specular_shine=0.1f,
37745                                     const bool display_axes=true, float *const pose_matrix=0) const {
37746       return display_object3d(disp,vertices,primitives,colors,CImgList<floatT>(),centering,
37747                               render_static,render_motion,is_double_sided,focale,
37748                               light_x,light_y,light_z,specular_light,specular_shine,
37749                               display_axes,pose_matrix);
37750     }
37751 
37752     //! Display object 3d in an interactive window \simplification.
37753     template<typename tp, typename tf, typename tc>
37754     const CImg<T>& display_object3d(const char *const title,
37755                                     const CImg<tp>& vertices,
37756                                     const CImgList<tf>& primitives,
37757                                     const CImgList<tc>& colors,
37758                                     const bool centering=true,
37759                                     const int render_static=4, const int render_motion=1,
37760                                     const bool is_double_sided=true, const float focale=500,
37761                                     const float light_x=0, const float light_y=0, const float light_z=-5000,
37762                                     const float specular_light=0.2f, const float specular_shine=0.1f,
37763                                     const bool display_axes=true, float *const pose_matrix=0) const {
37764       return display_object3d(title,vertices,primitives,colors,CImgList<floatT>(),centering,
37765                               render_static,render_motion,is_double_sided,focale,
37766                               light_x,light_y,light_z,specular_light,specular_shine,
37767                               display_axes,pose_matrix);
37768     }
37769 
37770     //! Display object 3d in an interactive window \simplification.
37771     template<typename tp, typename tf>
37772     const CImg<T>& display_object3d(CImgDisplay &disp,
37773                                     const CImg<tp>& vertices,
37774                                     const CImgList<tf>& primitives,
37775                                     const bool centering=true,
37776                                     const int render_static=4, const int render_motion=1,
37777                                     const bool is_double_sided=true, const float focale=500,
37778                                     const float light_x=0, const float light_y=0, const float light_z=-5000,
37779                                     const float specular_light=0.2f, const float specular_shine=0.1f,
37780                                     const bool display_axes=true, float *const pose_matrix=0) const {
37781       return display_object3d(disp,vertices,primitives,CImgList<T>(),centering,
37782                               render_static,render_motion,is_double_sided,focale,
37783                               light_x,light_y,light_z,specular_light,specular_shine,
37784                               display_axes,pose_matrix);
37785     }
37786 
37787 
37788     //! Display object 3d in an interactive window \simplification.
37789     template<typename tp, typename tf>
37790     const CImg<T>& display_object3d(const char *const title,
37791                                     const CImg<tp>& vertices,
37792                                     const CImgList<tf>& primitives,
37793                                     const bool centering=true,
37794                                     const int render_static=4, const int render_motion=1,
37795                                     const bool is_double_sided=true, const float focale=500,
37796                                     const float light_x=0, const float light_y=0, const float light_z=-5000,
37797                                     const float specular_light=0.2f, const float specular_shine=0.1f,
37798                                     const bool display_axes=true, float *const pose_matrix=0) const {
37799       return display_object3d(title,vertices,primitives,CImgList<T>(),centering,
37800                               render_static,render_motion,is_double_sided,focale,
37801                               light_x,light_y,light_z,specular_light,specular_shine,
37802                               display_axes,pose_matrix);
37803     }
37804 
37805     //! Display object 3d in an interactive window \simplification.
37806     template<typename tp>
37807     const CImg<T>& display_object3d(CImgDisplay &disp,
37808                                     const CImg<tp>& vertices,
37809                                     const bool centering=true,
37810                                     const int render_static=4, const int render_motion=1,
37811                                     const bool is_double_sided=true, const float focale=500,
37812                                     const float light_x=0, const float light_y=0, const float light_z=-5000,
37813                                     const float specular_light=0.2f, const float specular_shine=0.1f,
37814                                     const bool display_axes=true, float *const pose_matrix=0) const {
37815       return display_object3d(disp,vertices,CImgList<uintT>(),centering,
37816                               render_static,render_motion,is_double_sided,focale,
37817                               light_x,light_y,light_z,specular_light,specular_shine,
37818                               display_axes,pose_matrix);
37819     }
37820 
37821     //! Display object 3d in an interactive window \simplification.
37822     template<typename tp>
37823     const CImg<T>& display_object3d(const char *const title,
37824                                     const CImg<tp>& vertices,
37825                                     const bool centering=true,
37826                                     const int render_static=4, const int render_motion=1,
37827                                     const bool is_double_sided=true, const float focale=500,
37828                                     const float light_x=0, const float light_y=0, const float light_z=-5000,
37829                                     const float specular_light=0.2f, const float specular_shine=0.1f,
37830                                     const bool display_axes=true, float *const pose_matrix=0) const {
37831       return display_object3d(title,vertices,CImgList<uintT>(),centering,
37832                               render_static,render_motion,is_double_sided,focale,
37833                               light_x,light_y,light_z,specular_light,specular_shine,
37834                               display_axes,pose_matrix);
37835     }
37836 
37837     template<typename tp, typename tf, typename tc, typename to>
37838     const CImg<T>& _display_object3d(CImgDisplay& disp, const char *const title,
37839                                      const CImg<tp>& vertices,
37840                                      const CImgList<tf>& primitives,
37841                                      const CImgList<tc>& colors,
37842                                      const to& opacities,
37843                                      const bool centering,
37844                                      const int render_static, const int render_motion,
37845                                      const bool is_double_sided, const float focale,
37846                                      const float light_x, const float light_y, const float light_z,
37847                                      const float specular_light, const float specular_shine,
37848                                      const bool display_axes, float *const pose_matrix) const {
37849       typedef typename cimg::superset<tp,float>::type tpfloat;
37850 
37851       // Check input arguments
37852       if (is_empty()) {
37853         if (disp) return CImg<T>(disp.width(),disp.height(),1,(colors && colors[0].size()==1)?1:3,0).
37854                     _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,
37855                                       render_static,render_motion,is_double_sided,focale,
37856                                       light_x,light_y,light_z,specular_light,specular_shine,
37857                                       display_axes,pose_matrix);
37858         else return CImg<T>(1,2,1,1,64,128).resize(cimg_fitscreen(640,480,1),1,(colors && colors[0].size()==1)?1:3,3).
37859                _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,
37860                                  render_static,render_motion,is_double_sided,focale,
37861                                  light_x,light_y,light_z,specular_light,specular_shine,
37862                                  display_axes,pose_matrix);
37863       } else { if (disp) disp.resize(*this,false); }
37864       char error_message[1024] = { 0 };
37865       if (!vertices.is_object3d(primitives,colors,opacities,true,error_message))
37866         throw CImgArgumentException(_cimg_instance
37867                                     "display_object3d() : Invalid specified 3d object (%u,%u) (%s).",
37868                                     cimg_instance,vertices._width,primitives._width,error_message);
37869       if (vertices._width && !primitives) {
37870         CImgList<tf> nprimitives(vertices._width,1,1,1,1);
37871         cimglist_for(nprimitives,l) nprimitives(l,0) = l;
37872         return _display_object3d(disp,title,vertices,nprimitives,colors,opacities,centering,
37873                                  render_static,render_motion,is_double_sided,focale,
37874                                  light_x,light_y,light_z,specular_light,specular_shine,
37875                                  display_axes,pose_matrix);
37876       }
37877       if (!disp) {
37878         disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,3);
37879         if (!title) disp.set_title("CImg<%s> (%u vertices, %u primitives)",pixel_type(),vertices._width,primitives._width);
37880       } else if (title) disp.set_title("%s",title);
37881 
37882       // Init 3d objects and compute object statistics
37883       CImg<floatT>
37884         pose,
37885         rotated_vertices(vertices._width,3),
37886         bbox_vertices, rotated_bbox_vertices,
37887         axes_vertices, rotated_axes_vertices,
37888         bbox_opacities, axes_opacities;
37889       CImgList<uintT> bbox_primitives, axes_primitives;
37890       CImgList<tf> reverse_primitives;
37891       CImgList<T> bbox_colors, bbox_colors2, axes_colors;
37892       unsigned int ns_width = 0, ns_height = 0;
37893       int _is_double_sided = (int)is_double_sided;
37894       bool ndisplay_axes = display_axes;
37895       const CImg<T>
37896         background_color(1,1,1,_spectrum,0),
37897         foreground_color(1,1,1,_spectrum,255);
37898       float
37899         Xoff = 0, Yoff = 0, Zoff = 0, sprite_scale = 1,
37900         xm = 0, xM = vertices?vertices.get_shared_row(0).max_min(xm):0,
37901         ym = 0, yM = vertices?vertices.get_shared_row(1).max_min(ym):0,
37902         zm = 0, zM = vertices?vertices.get_shared_row(2).max_min(zm):0;
37903       const float delta = cimg::max(xM-xm,yM-ym,zM-zm);
37904 
37905       rotated_bbox_vertices = bbox_vertices.assign(8,3,1,1,
37906                                                    xm,xM,xM,xm,xm,xM,xM,xm,
37907                                                    ym,ym,yM,yM,ym,ym,yM,yM,
37908                                                    zm,zm,zm,zm,zM,zM,zM,zM);
37909       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);
37910       bbox_colors.assign(6,_spectrum,1,1,1,background_color[0]);
37911       bbox_colors2.assign(6,_spectrum,1,1,1,foreground_color[0]);
37912       bbox_opacities.assign(bbox_colors._width,1,1,1,0.3f);
37913 
37914       rotated_axes_vertices = axes_vertices.assign(7,3,1,1,
37915                                                    0,20,0,0,22,-6,-6,
37916                                                    0,0,20,0,-6,22,-6,
37917                                                    0,0,0,20,0,0,22);
37918       axes_opacities.assign(3,1,1,1,1);
37919       axes_colors.assign(3,_spectrum,1,1,1,foreground_color[0]);
37920       axes_primitives.assign(3,1,2,1,1, 0,1, 0,2, 0,3);
37921 
37922       // Begin user interaction loop
37923       CImg<T> visu0(*this), visu;
37924       CImg<tpfloat> zbuffer(visu0.width(),visu0.height(),1,1,0);
37925       bool init_pose = true, clicked = false, redraw = true;
37926       unsigned int key = 0;
37927       int
37928         x0 = 0, y0 = 0, x1 = 0, y1 = 0,
37929         nrender_static = render_static,
37930         nrender_motion = render_motion;
37931       disp.show().flush();
37932 
37933       while (!disp.is_closed() && !key) {
37934 
37935         // Init object pose
37936         if (init_pose) {
37937           const float
37938             ratio = delta>0?(2.0f*cimg::min(disp.width(),disp.height())/(3.0f*delta)):1,
37939             dx = (xM + xm)/2, dy = (yM + ym)/2, dz = (zM + zm)/2;
37940           if (centering)
37941             CImg<floatT>(4,3,1,1, ratio,0.,0.,-ratio*dx, 0.,ratio,0.,-ratio*dy, 0.,0.,ratio,-ratio*dz).move_to(pose);
37942           else CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose);
37943           if (pose_matrix) {
37944             CImg<floatT> pose0(pose_matrix,4,3,1,1,false);
37945             pose0.resize(4,4,1,1,0); pose.resize(4,4,1,1,0);
37946             pose0(3,3) = pose(3,3) = 1;
37947             (pose0*pose).get_crop(0,0,3,2).move_to(pose);
37948             Xoff = pose_matrix[12]; Yoff = pose_matrix[13]; Zoff = pose_matrix[14]; sprite_scale = pose_matrix[15];
37949           } else { Xoff = Yoff = Zoff = 0; sprite_scale = 1; }
37950           init_pose = false;
37951           redraw = true;
37952         }
37953 
37954         // Rotate and draw 3d object
37955         if (redraw) {
37956           const float
37957             r00 = pose(0,0), r10 = pose(1,0), r20 = pose(2,0), r30 = pose(3,0),
37958             r01 = pose(0,1), r11 = pose(1,1), r21 = pose(2,1), r31 = pose(3,1),
37959             r02 = pose(0,2), r12 = pose(1,2), r22 = pose(2,2), r32 = pose(3,2);
37960           if ((clicked && nrender_motion>=0) || (!clicked && nrender_static>=0))
37961             cimg_forX(vertices,l) {
37962               const float x = (float)vertices(l,0), y = (float)vertices(l,1), z = (float)vertices(l,2);
37963               rotated_vertices(l,0) = r00*x + r10*y + r20*z + r30;
37964               rotated_vertices(l,1) = r01*x + r11*y + r21*z + r31;
37965               rotated_vertices(l,2) = r02*x + r12*y + r22*z + r32;
37966             }
37967           else cimg_forX(bbox_vertices,l) {
37968               const float x = bbox_vertices(l,0), y = bbox_vertices(l,1), z = bbox_vertices(l,2);
37969               rotated_bbox_vertices(l,0) = r00*x + r10*y + r20*z + r30;
37970               rotated_bbox_vertices(l,1) = r01*x + r11*y + r21*z + r31;
37971               rotated_bbox_vertices(l,2) = r02*x + r12*y + r22*z + r32;
37972             }
37973 
37974           // Draw object
37975           visu = visu0;
37976           if ((clicked && nrender_motion<0) || (!clicked && nrender_static<0))
37977             visu.draw_object3d(Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
37978                                rotated_bbox_vertices,bbox_primitives,bbox_colors,bbox_opacities,2,false,focale).
37979               draw_object3d(Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
37980                             rotated_bbox_vertices,bbox_primitives,bbox_colors2,1,false,focale);
37981           else visu._draw_object3d((void*)0,(!clicked && nrender_static>0)?zbuffer.fill(0):CImg<tpfloat>::empty(),
37982                                    Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
37983                                    rotated_vertices,reverse_primitives?reverse_primitives:primitives,
37984                                    colors,opacities,clicked?nrender_motion:nrender_static,_is_double_sided==1,focale,
37985                                    width()/2.0f+light_x,height()/2.0f+light_y,light_z,specular_light,specular_shine,
37986                                    sprite_scale);
37987           // Draw axes
37988           if (ndisplay_axes) {
37989             const float
37990               n = (float)std::sqrt(1e-8 + r00*r00 + r01*r01 + r02*r02),
37991               _r00 = r00/n, _r10 = r10/n, _r20 = r20/n,
37992               _r01 = r01/n, _r11 = r11/n, _r21 = r21/n,
37993               _r02 = r01/n, _r12 = r12/n, _r22 = r22/n,
37994               Xaxes = 25, Yaxes = visu._height - 38.0f;
37995             cimg_forX(axes_vertices,l) {
37996               const float
37997                 x = axes_vertices(l,0),
37998                 y = axes_vertices(l,1),
37999                 z = axes_vertices(l,2);
38000               rotated_axes_vertices(l,0) = _r00*x + _r10*y + _r20*z;
38001               rotated_axes_vertices(l,1) = _r01*x + _r11*y + _r21*z;
38002               rotated_axes_vertices(l,2) = _r02*x + _r12*y + _r22*z;
38003             }
38004             axes_opacities(0,0) = (rotated_axes_vertices(1,2)>0)?0.5f:1.0f;
38005             axes_opacities(1,0) = (rotated_axes_vertices(2,2)>0)?0.5f:1.0f;
38006             axes_opacities(2,0) = (rotated_axes_vertices(3,2)>0)?0.5f:1.0f;
38007             visu.draw_object3d(Xaxes,Yaxes,0,rotated_axes_vertices,axes_primitives,axes_colors,axes_opacities,1,false,focale).
38008               draw_text((int)(Xaxes+rotated_axes_vertices(4,0)),
38009                         (int)(Yaxes+rotated_axes_vertices(4,1)),
38010                         "X",axes_colors[0]._data,0,axes_opacities(0,0),13).
38011               draw_text((int)(Xaxes+rotated_axes_vertices(5,0)),
38012                         (int)(Yaxes+rotated_axes_vertices(5,1)),
38013                         "Y",axes_colors[1]._data,0,axes_opacities(1,0),13).
38014               draw_text((int)(Xaxes+rotated_axes_vertices(6,0)),
38015                         (int)(Yaxes+rotated_axes_vertices(6,1)),
38016                         "Z",axes_colors[2]._data,0,axes_opacities(2,0),13);
38017           }
38018           visu.display(disp);
38019           if (!clicked || nrender_motion==nrender_static) redraw = false;
38020         }
38021 
38022         // Handle user interaction
38023         disp.wait();
38024         if ((disp.button() || disp.wheel()) && disp.mouse_x()>=0 && disp.mouse_y()>=0) {
38025           redraw = true;
38026           if (!clicked) { x0 = x1 = disp.mouse_x(); y0 = y1 = disp.mouse_y(); if (!disp.wheel()) clicked = true; }
38027           else { x1 = disp.mouse_x(); y1 = disp.mouse_y(); }
38028           if (disp.button()&1) {
38029             const float
38030               R = 0.45f*cimg::min(disp.width(),disp.height()),
38031               R2 = R*R,
38032               u0 = (float)(x0-disp.width()/2),
38033               v0 = (float)(y0-disp.height()/2),
38034               u1 = (float)(x1-disp.width()/2),
38035               v1 = (float)(y1-disp.height()/2),
38036               n0 = (float)std::sqrt(u0*u0+v0*v0),
38037               n1 = (float)std::sqrt(u1*u1+v1*v1),
38038               nu0 = n0>R?(u0*R/n0):u0,
38039               nv0 = n0>R?(v0*R/n0):v0,
38040               nw0 = (float)std::sqrt(cimg::max(0,R2-nu0*nu0-nv0*nv0)),
38041               nu1 = n1>R?(u1*R/n1):u1,
38042               nv1 = n1>R?(v1*R/n1):v1,
38043               nw1 = (float)std::sqrt(cimg::max(0,R2-nu1*nu1-nv1*nv1)),
38044               u = nv0*nw1-nw0*nv1,
38045               v = nw0*nu1-nu0*nw1,
38046               w = nv0*nu1-nu0*nv1,
38047               n = (float)std::sqrt(u*u+v*v+w*w),
38048               alpha = (float)std::asin(n/R2);
38049             (CImg<floatT>::rotation_matrix(u,v,w,alpha)*pose).move_to(pose);
38050             x0 = x1; y0 = y1;
38051           }
38052           if (disp.button()&2) {
38053             if (focale>0) Zoff-=(y0-y1)*focale/400;
38054             else { const float s = std::exp((y0-y1)/400.0f); pose*=s; sprite_scale*=s; }
38055             x0 = x1; y0 = y1;
38056           }
38057           if (disp.wheel()) {
38058             if (focale>0) Zoff-=disp.wheel()*focale/20;
38059             else { const float s = std::exp(disp.wheel()/20.0f); pose*=s; sprite_scale*=s; }
38060             disp.set_wheel();
38061           }
38062           if (disp.button()&4) { Xoff+=(x1-x0); Yoff+=(y1-y0); x0 = x1; y0 = y1; }
38063           if ((disp.button()&1) && (disp.button()&2)) {
38064             init_pose = true; disp.set_button(); x0 = x1; y0 = y1;
38065             pose = CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0);
38066           }
38067         } else if (clicked) { x0 = x1; y0 = y1; clicked = false; redraw = true; }
38068 
38069         switch (key = disp.key()) {
38070 #if cimg_OS!=2
38071         case cimg::keyCTRLRIGHT :
38072 #endif
38073         case 0 : case cimg::keyCTRLLEFT : key = 0; break;
38074         case cimg::keyD: if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
38075             disp.set_fullscreen(false).resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
38076                                               CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
38077               _is_resized = true;
38078             disp.set_key(key,false); key = 0;
38079           } break;
38080         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
38081             disp.set_fullscreen(false).resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
38082             disp.set_key(key,false); key = 0;
38083           } break;
38084         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
38085             disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true;
38086             disp.set_key(key,false); key = 0;
38087           } break;
38088         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
38089             if (!ns_width || !ns_height ||
38090                 ns_width>(unsigned int)disp.screen_width() || ns_height>(unsigned int)disp.screen_height()) {
38091               ns_width = disp.screen_width()*3U/4;
38092               ns_height = disp.screen_height()*3U/4;
38093             }
38094             if (disp.is_fullscreen()) disp.resize(ns_width,ns_height,false);
38095             else {
38096               ns_width = (unsigned int)disp.width(); ns_height = disp.height();
38097               disp.resize(disp.screen_width(),disp.screen_height(),false);
38098             }
38099             disp.toggle_fullscreen()._is_resized = true;
38100             disp.set_key(key,false); key = 0;
38101           } break;
38102         case cimg::keyT : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Switch single/double-sided primitives.
38103             if (--_is_double_sided==-2) _is_double_sided = 1;
38104             if (_is_double_sided>=0) reverse_primitives.assign();
38105             else primitives.get_reverse_object3d().move_to(reverse_primitives);
38106             disp.set_key(key,false); key = 0; redraw = true;
38107           } break;
38108         case cimg::keyZ : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Enable/disable Z-buffer
38109             if (zbuffer) zbuffer.assign();
38110             else zbuffer.assign(visu0.width(),visu0.height(),1,1,0);
38111             disp.set_key(key,false); key = 0; redraw = true;
38112           } break;
38113         case cimg::keyA : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Show/hide 3d axes.
38114             ndisplay_axes = !ndisplay_axes;
38115             disp.set_key(key,false); key = 0; redraw = true;
38116           } break;
38117         case cimg::keyF1 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to points.
38118             nrender_motion = (nrender_static==0 && nrender_motion!=0)?0:-1; nrender_static = 0;
38119             disp.set_key(key,false); key = 0; redraw = true;
38120           } break;
38121         case cimg::keyF2 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to lines.
38122             nrender_motion = (nrender_static==1 && nrender_motion!=1)?1:-1; nrender_static = 1;
38123             disp.set_key(key,false); key = 0; redraw = true;
38124           } break;
38125         case cimg::keyF3 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat.
38126             nrender_motion = (nrender_static==2 && nrender_motion!=2)?2:-1; nrender_static = 2;
38127             disp.set_key(key,false); key = 0; redraw = true;
38128           } break;
38129         case cimg::keyF4 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat-shaded.
38130             nrender_motion = (nrender_static==3 && nrender_motion!=3)?3:-1; nrender_static = 3;
38131             disp.set_key(key,false); key = 0; redraw = true;
38132           } break;
38133         case cimg::keyF5 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to gouraud-shaded.
38134             nrender_motion = (nrender_static==4 && nrender_motion!=4)?4:-1; nrender_static = 4;
38135             disp.set_key(key,false); key = 0; redraw = true;
38136           } break;
38137         case cimg::keyF6 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to phong-shaded.
38138             nrender_motion = (nrender_static==5 && nrender_motion!=5)?5:-1; nrender_static = 5;
38139             disp.set_key(key,false); key = 0; redraw = true;
38140           } break;
38141         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save snapshot
38142             static unsigned int snap_number = 0;
38143             char filename[32] = { 0 };
38144             std::FILE *file;
38145             do {
38146               cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++);
38147               if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
38148             } while (file);
38149             (+visu).draw_text(0,0," Saving snapshot... ",foreground_color._data,background_color._data,1,13).display(disp);
38150             visu.save(filename);
38151             visu.draw_text(0,0," Snapshot '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp);
38152             disp.set_key(key,false); key = 0;
38153           } break;
38154         case cimg::keyG : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .off file
38155             static unsigned int snap_number = 0;
38156             char filename[32] = { 0 };
38157             std::FILE *file;
38158             do {
38159               cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.off",snap_number++);
38160               if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
38161             } while (file);
38162             visu.draw_text(0,0," Saving object... ",foreground_color._data,background_color._data,1,13).display(disp);
38163             vertices.save_off(reverse_primitives?reverse_primitives:primitives,colors,filename);
38164             visu.draw_text(0,0," Object '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp);
38165             disp.set_key(key,false); key = 0;
38166           } break;
38167         case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .cimg file
38168             static unsigned int snap_number = 0;
38169             char filename[32] = { 0 };
38170             std::FILE *file;
38171             do {
38172 #ifdef cimg_use_zlib
38173               cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++);
38174 #else
38175               cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++);
38176 #endif
38177               if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
38178             } while (file);
38179             visu.draw_text(0,0," Saving object... ",foreground_color._data,background_color._data,1,13).display(disp);
38180             vertices.get_object3dtoCImg3d(reverse_primitives?reverse_primitives:primitives,colors,opacities).save(filename);
38181             visu.draw_text(0,0," Object '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp);
38182             disp.set_key(key,false); key = 0;
38183           } break;
38184 #ifdef cimg_use_board
38185         case cimg::keyP : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .EPS file
38186             static unsigned int snap_number = 0;
38187             char filename[32] = { 0 };
38188             std::FILE *file;
38189             do {
38190               cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.eps",snap_number++);
38191               if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
38192             } while (file);
38193             visu.draw_text(0,0," Saving EPS snapshot... ",foreground_color._data,background_color._data,1,13).display(disp);
38194             LibBoard::Board board;
38195             (+visu)._draw_object3d(&board,zbuffer.fill(0),
38196                                    Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
38197                                    rotated_vertices,reverse_primitives?reverse_primitives:primitives,
38198                                    colors,opacities,clicked?nrender_motion:nrender_static,
38199                                    _is_double_sided==1,focale,
38200                                    visu.width()/2.0f+light_x,visu.height()/2.0f+light_y,light_z,specular_light,specular_shine,
38201                                    sprite_scale);
38202             board.saveEPS(filename);
38203             visu.draw_text(0,0," Object '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp);
38204             disp.set_key(key,false); key = 0;
38205           } break;
38206         case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .SVG file
38207             static unsigned int snap_number = 0;
38208             char filename[32] = { 0 };
38209             std::FILE *file;
38210             do {
38211               cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.svg",snap_number++);
38212               if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
38213             } while (file);
38214             visu.draw_text(0,0," Saving SVG snapshot... ",foreground_color._data,background_color._data,1,13).display(disp);
38215             LibBoard::Board board;
38216             (+visu)._draw_object3d(&board,zbuffer.fill(0),
38217                                    Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
38218                                    rotated_vertices,reverse_primitives?reverse_primitives:primitives,
38219                                    colors,opacities,clicked?nrender_motion:nrender_static,
38220                                    _is_double_sided==1,focale,
38221                                    visu.width()/2.0f+light_x,visu.height()/2.0f+light_y,light_z,specular_light,specular_shine,
38222                                    sprite_scale);
38223             board.saveSVG(filename);
38224             visu.draw_text(0,0," Object '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp);
38225             disp.set_key(key,false); key = 0;
38226           } break;
38227 #endif
38228         }
38229         if (disp.is_resized()) {
38230           disp.resize(false); visu0 = get_resize(disp,1);
38231           if (zbuffer) zbuffer.assign(disp.width(),disp.height());
38232           redraw = true;
38233         }
38234       }
38235       if (pose_matrix) {
38236         std::memcpy(pose_matrix,pose._data,12*sizeof(float));
38237         pose_matrix[12] = Xoff; pose_matrix[13] = Yoff; pose_matrix[14] = Zoff; pose_matrix[15] = sprite_scale;
38238       }
38239       disp.set_button().set_key(key);
38240       return *this;
38241     }
38242 
38243     //! Display 1d graph in an interactive window.
38244     /**
38245        \param disp Display window.
38246        \param plot_type Plot type. Can be <tt>{ 0=points | 1=segments | 2=splines | 3=bars }</tt>.
38247        \param vertex_type Vertex type.
38248        \param labelx Title for the horizontal axis, as a C-string.
38249        \param xmin Minimum value along the X-axis.
38250        \param xmax Maximum value along the X-axis.
38251        \param labely Title for the vertical axis, as a C-string.
38252        \param ymin Minimum value along the X-axis.
38253        \param ymax Maximum value along the X-axis.
38254     **/
38255     const CImg<T>& display_graph(CImgDisplay &disp,
38256                                  const unsigned int plot_type=1, const unsigned int vertex_type=1,
38257                                  const char *const labelx=0, const double xmin=0, const double xmax=0,
38258                                  const char *const labely=0, const double ymin=0, const double ymax=0) const {
38259       if (is_empty())
38260         throw CImgInstanceException(_cimg_instance
38261                                     "display_graph() : Empty instance.",
38262                                     cimg_instance);
38263       if (!disp) disp.assign(cimg_fitscreen(640,480,1),0,0).set_title("CImg<%s>",pixel_type());
38264       const unsigned long siz = (unsigned long)_width*_height*_depth, siz1 = cimg::max(1U,siz-1);
38265       const unsigned int old_normalization = disp.normalization();
38266       disp.show().flush()._normalization = 0;
38267 
38268       double y0 = ymin, y1 = ymax, nxmin = xmin, nxmax = xmax;
38269       if (nxmin==nxmax) { nxmin = 0; nxmax = siz1; }
38270       int x0 = 0, x1 = width()*height()*depth() - 1, key = 0;
38271 
38272       for (bool reset_view = true, resize_disp = false; !key && !disp.is_closed(); ) {
38273         if (reset_view) { x0 = 0; x1 = width()*height()*depth()-1; y0 = ymin; y1 = ymax; reset_view = false; }
38274         CImg<T> zoom(x1-x0+1,1,1,spectrum());
38275         cimg_forC(*this,c) zoom.get_shared_channel(c) = CImg<T>(data(x0,0,0,c),x1-x0+1,1,1,1,true);
38276 
38277         if (y0==y1) { y0 = zoom.min_max(y1); const double dy = y1 - y0; y0-=dy/20; y1+=dy/20; }
38278         if (y0==y1) { --y0; ++y1; }
38279         const CImg<intT> selection = zoom.get_select_graph(disp,plot_type,vertex_type,
38280                                                            labelx,
38281                                                            nxmin + x0*(nxmax-nxmin)/siz1,
38282                                                            nxmin + x1*(nxmax-nxmin)/siz1,
38283                                                            labely,y0,y1);
38284         const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y();
38285         if (selection[0]>=0) {
38286           if (selection[2]<0) reset_view = true;
38287           else {
38288             x1 = x0 + selection[2]; x0+=selection[0];
38289             if (selection[1]>=0 && selection[3]>=0) {
38290               y0 = y1 - selection[3]*(y1-y0)/(disp.height()-32);
38291               y1-=selection[1]*(y1-y0)/(disp.height()-32);
38292             }
38293           }
38294         } else {
38295           bool go_in = false, go_out = false, go_left = false, go_right = false, go_up = false, go_down = false;
38296           switch (key = disp.key()) {
38297           case cimg::keyHOME : reset_view = resize_disp = true; key = 0; disp.set_key(); break;
38298           case cimg::keyPADADD : go_in = true; go_out = false; key = 0; disp.set_key(); break;
38299           case cimg::keyPADSUB : go_out = true; go_in = false; key = 0; disp.set_key(); break;
38300           case cimg::keyARROWLEFT : case cimg::keyPAD4 : go_left = true; go_right = false; key = 0; disp.set_key(); break;
38301           case cimg::keyARROWRIGHT : case cimg::keyPAD6 : go_right = true; go_left = false; key = 0; disp.set_key(); break;
38302           case cimg::keyARROWUP : case cimg::keyPAD8 : go_up = true; go_down = false; key = 0; disp.set_key(); break;
38303           case cimg::keyARROWDOWN : case cimg::keyPAD2 : go_down = true; go_up = false; key = 0; disp.set_key(); break;
38304           case cimg::keyPAD7 : go_left = true; go_up = true; key = 0; disp.set_key(); break;
38305           case cimg::keyPAD9 : go_right = true; go_up = true; key = 0; disp.set_key(); break;
38306           case cimg::keyPAD1 : go_left = true; go_down = true; key = 0; disp.set_key(); break;
38307           case cimg::keyPAD3 : go_right = true; go_down = true; key = 0; disp.set_key(); break;
38308           }
38309           if (disp.wheel()) {
38310             if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) go_out = !(go_in = disp.wheel()>0);
38311             else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) go_left = !(go_right = disp.wheel()>0);
38312             else go_up = !(go_down = disp.wheel()<0);
38313             key = 0;
38314           }
38315 
38316           if (go_in) {
38317             const int
38318               xsiz = x1 - x0,
38319               mx = (mouse_x-16)*xsiz/(disp.width()-32),
38320               cx = x0 + (mx<0?0:(mx>=xsiz?xsiz:mx));
38321             if (x1-x0>4) {
38322               x0 = cx - 7*(cx-x0)/8; x1 = cx + 7*(x1-cx)/8;
38323               if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
38324                 const double
38325                   ysiz = y1 - y0,
38326                   my = (mouse_y-16)*ysiz/(disp.height()-32),
38327                   cy = y1 - (my<0?0:(my>=ysiz?ysiz:my));
38328                 y0 = cy - 7*(cy-y0)/8; y1 = cy + 7*(y1-cy)/8;
38329               } else y0 = y1 = 0;
38330             }
38331           }
38332           if (go_out) {
38333             if (x0>0 || x1<(int)siz1) {
38334               const int delta_x = (x1-x0)/8, ndelta_x = delta_x?delta_x:(siz>1?1:0);
38335               const double ndelta_y = (y1-y0)/8;
38336               x0-=ndelta_x; x1+=ndelta_x;
38337               y0-=ndelta_y; y1+=ndelta_y;
38338               if (x0<0) { x1-=x0; x0 = 0; if (x1>=(int)siz) x1 = (int)siz1; }
38339               if (x1>=(int)siz) { x0-=(x1-siz1); x1 = (int)siz1; if (x0<0) x0 = 0; }
38340             }
38341           }
38342           if (go_left) {
38343             const int delta = (x1-x0)/5, ndelta = delta?delta:1;
38344             if (x0-ndelta>=0) { x0-=ndelta; x1-=ndelta; }
38345             else { x1-=x0; x0 = 0; }
38346             go_left = false;
38347           }
38348           if (go_right) {
38349             const int delta = (x1-x0)/5, ndelta = delta?delta:1;
38350             if (x1+ndelta<(int)siz) { x0+=ndelta; x1+=ndelta; }
38351             else { x0+=(siz1-x1); x1 = siz1; }
38352             go_right = false;
38353           }
38354           if (go_up) {
38355             const double delta = (y1-y0)/10, ndelta = delta?delta:1;
38356             y0+=ndelta; y1+=ndelta;
38357             go_up = false;
38358           }
38359           if (go_down) {
38360             const double delta = (y1-y0)/10, ndelta = delta?delta:1;
38361             y0-=ndelta; y1-=ndelta;
38362             go_down = false;
38363           }
38364         }
38365       }
38366       disp._normalization = old_normalization;
38367       return *this;
38368     }
38369 
38370     //! Display 1d graph in an interactive window \overloading.
38371     const CImg<T>& display_graph(const char *const title=0,
38372                                  const unsigned int plot_type=1, const unsigned int vertex_type=1,
38373                                  const char *const labelx=0, const double xmin=0, const double xmax=0,
38374                                  const char *const labely=0, const double ymin=0, const double ymax=0) const {
38375       if (is_empty())
38376         throw CImgInstanceException(_cimg_instance
38377                                     "display_graph() : Empty instance.",
38378                                     cimg_instance);
38379       CImgDisplay disp;
38380       return display_graph(disp.set_title("%s",title),plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax);
38381     }
38382 
38383     //! Save image as a file.
38384     /**
38385        \param filename Filename, as a C-string.
38386        \param number When positive, represents an index added to the filename.
38387        \note
38388        - The used file format is defined by the file extension in the filename \p filename.
38389        - Parameter \p number can be used to add a 6-digit number to the filename before saving.
38390     **/
38391     const CImg<T>& save(const char *const filename, const int number=-1) const {
38392       if (!filename)
38393         throw CImgArgumentException(_cimg_instance
38394                                     "save() : Specified filename is (null).",
38395                                     cimg_instance);
38396       // Do not test for empty instances, since .cimg format is able to manage empty instances.
38397       const char *const ext = cimg::split_filename(filename);
38398       char nfilename[1024] = { 0 };
38399       const char *const fn = (number>=0)?cimg::number_filename(filename,number,6,nfilename):filename;
38400 #ifdef cimg_save_plugin
38401       cimg_save_plugin(fn);
38402 #endif
38403 #ifdef cimg_save_plugin1
38404       cimg_save_plugin1(fn);
38405 #endif
38406 #ifdef cimg_save_plugin2
38407       cimg_save_plugin2(fn);
38408 #endif
38409 #ifdef cimg_save_plugin3
38410       cimg_save_plugin3(fn);
38411 #endif
38412 #ifdef cimg_save_plugin4
38413       cimg_save_plugin4(fn);
38414 #endif
38415 #ifdef cimg_save_plugin5
38416       cimg_save_plugin5(fn);
38417 #endif
38418 #ifdef cimg_save_plugin6
38419       cimg_save_plugin6(fn);
38420 #endif
38421 #ifdef cimg_save_plugin7
38422       cimg_save_plugin7(fn);
38423 #endif
38424 #ifdef cimg_save_plugin8
38425       cimg_save_plugin8(fn);
38426 #endif
38427       // Ascii formats
38428       if (!cimg::strcasecmp(ext,"asc")) return save_ascii(fn);
38429       else if (!cimg::strcasecmp(ext,"dlm") ||
38430                !cimg::strcasecmp(ext,"txt")) return save_dlm(fn);
38431       else if (!cimg::strcasecmp(ext,"cpp") ||
38432                !cimg::strcasecmp(ext,"hpp") ||
38433                !cimg::strcasecmp(ext,"h") ||
38434                !cimg::strcasecmp(ext,"c")) return save_cpp(fn);
38435 
38436       // 2d binary formats
38437       else if (!cimg::strcasecmp(ext,"bmp")) return save_bmp(fn);
38438       else if (!cimg::strcasecmp(ext,"jpg") ||
38439                !cimg::strcasecmp(ext,"jpeg") ||
38440                !cimg::strcasecmp(ext,"jpe") ||
38441                !cimg::strcasecmp(ext,"jfif") ||
38442                !cimg::strcasecmp(ext,"jif")) return save_jpeg(fn);
38443       else if (!cimg::strcasecmp(ext,"rgb")) return save_rgb(fn);
38444       else if (!cimg::strcasecmp(ext,"rgba")) return save_rgba(fn);
38445       else if (!cimg::strcasecmp(ext,"png")) return save_png(fn);
38446       else if (!cimg::strcasecmp(ext,"pgm") ||
38447                !cimg::strcasecmp(ext,"ppm") ||
38448                !cimg::strcasecmp(ext,"pnm")) return save_pnm(fn);
38449       else if (!cimg::strcasecmp(ext,"pnk")) return save_pnk(fn);
38450       else if (!cimg::strcasecmp(ext,"pfm")) return save_pfm(fn);
38451       else if (!cimg::strcasecmp(ext,"exr")) return save_exr(fn);
38452       else if (!cimg::strcasecmp(ext,"tif") ||
38453                !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn);
38454 
38455       // 3d binary formats
38456       else if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true);
38457       else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false);
38458       else if (!cimg::strcasecmp(ext,"dcm")) return save_medcon_external(fn);
38459       else if (!cimg::strcasecmp(ext,"hdr") ||
38460                !cimg::strcasecmp(ext,"nii")) return save_analyze(fn);
38461       else if (!cimg::strcasecmp(ext,"inr")) return save_inr(fn);
38462       else if (!cimg::strcasecmp(ext,"mnc")) return save_minc2(fn);
38463       else if (!cimg::strcasecmp(ext,"pan")) return save_pandore(fn);
38464       else if (!cimg::strcasecmp(ext,"raw")) return save_raw(fn);
38465 
38466       // Archive files
38467       else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn);
38468 
38469       // Image sequences
38470       else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,true);
38471       else if (!cimg::strcasecmp(ext,"avi") ||
38472                !cimg::strcasecmp(ext,"mov") ||
38473                !cimg::strcasecmp(ext,"asf") ||
38474                !cimg::strcasecmp(ext,"divx") ||
38475                !cimg::strcasecmp(ext,"flv") ||
38476                !cimg::strcasecmp(ext,"mpg") ||
38477                !cimg::strcasecmp(ext,"m1v") ||
38478                !cimg::strcasecmp(ext,"m2v") ||
38479                !cimg::strcasecmp(ext,"m4v") ||
38480                !cimg::strcasecmp(ext,"mjp") ||
38481                !cimg::strcasecmp(ext,"mkv") ||
38482                !cimg::strcasecmp(ext,"mpe") ||
38483                !cimg::strcasecmp(ext,"movie") ||
38484                !cimg::strcasecmp(ext,"ogm") ||
38485                !cimg::strcasecmp(ext,"ogg") ||
38486                !cimg::strcasecmp(ext,"qt") ||
38487                !cimg::strcasecmp(ext,"rm") ||
38488                !cimg::strcasecmp(ext,"vob") ||
38489                !cimg::strcasecmp(ext,"wmv") ||
38490                !cimg::strcasecmp(ext,"xvid") ||
38491                !cimg::strcasecmp(ext,"mpeg")) return save_ffmpeg(fn);
38492       return save_other(fn);
38493     }
38494 
38495     //! Save image as an ascii file.
38496     /**
38497       \param filename Filename, as a C-string.
38498     **/
38499     const CImg<T>& save_ascii(const char *const filename) const {
38500       return _save_ascii(0,filename);
38501     }
38502 
38503     //! Save image as an ascii file \overloading.
38504     const CImg<T>& save_ascii(std::FILE *const file) const {
38505       return _save_ascii(file,0);
38506     }
38507 
38508     const CImg<T>& _save_ascii(std::FILE *const file, const char *const filename) const {
38509       if (!file && !filename)
38510         throw CImgArgumentException(_cimg_instance
38511                                     "save_ascii() : Specified filename is (null).",
38512                                     cimg_instance);
38513       if (is_empty())
38514         throw CImgInstanceException(_cimg_instance
38515                                     "save_ascii() : Empty instance, for file '%s'.",
38516                                     cimg_instance,
38517                                     filename?filename:"(FILE*)");
38518 
38519       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
38520       std::fprintf(nfile,"%u %u %u %u\n",_width,_height,_depth,_spectrum);
38521       const T* ptrs = _data;
38522       cimg_forYZC(*this,y,z,c) {
38523         cimg_forX(*this,x) std::fprintf(nfile,"%.16g ",(double)*(ptrs++));
38524         std::fputc('\n',nfile);
38525       }
38526       if (!file) cimg::fclose(nfile);
38527       return *this;
38528     }
38529 
38530     //! Save image as a .cpp source file.
38531     /**
38532       \param filename Filename, as a C-string.
38533     **/
38534     const CImg<T>& save_cpp(const char *const filename) const {
38535       return _save_cpp(0,filename);
38536     }
38537 
38538     //! Save image as a .cpp source file \overloading.
38539     const CImg<T>& save_cpp(std::FILE *const file) const {
38540       return _save_cpp(file,0);
38541     }
38542 
38543     const CImg<T>& _save_cpp(std::FILE *const file, const char *const filename) const {
38544       if (!file && !filename)
38545         throw CImgArgumentException(_cimg_instance
38546                                     "save_cpp() : Specified filename is (null).",
38547                                     cimg_instance);
38548       if (is_empty())
38549         throw CImgInstanceException(_cimg_instance
38550                                     "save_cpp() : Empty instance, for file '%s'.",
38551                                     cimg_instance,
38552                                     filename?filename:"(FILE*)");
38553 
38554       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
38555       char varname[1024] = { 0 };
38556       if (filename) std::sscanf(cimg::basename(filename),"%1023[a-zA-Z0-9_]",varname);
38557       if (!*varname) cimg_snprintf(varname,sizeof(varname),"unnamed");
38558       std::fprintf(nfile,
38559                    "/* Define image '%s' of size %ux%ux%ux%u and type '%s' */\n"
38560                    "%s data_%s[] = { \n  ",
38561                    varname,_width,_height,_depth,_spectrum,pixel_type(),pixel_type(),varname);
38562       for (unsigned long off = 0, siz = size()-1; off<=siz; ++off) {
38563         std::fprintf(nfile,cimg::type<T>::format(),cimg::type<T>::format((*this)[off]));
38564         if (off==siz) std::fprintf(nfile," };\n");
38565         else if (!((off+1)%16)) std::fprintf(nfile,",\n  ");
38566         else std::fprintf(nfile,", ");
38567       }
38568       if (!file) cimg::fclose(nfile);
38569       return *this;
38570     }
38571 
38572     //! Save image as a DLM file.
38573     /**
38574        \param filename Filename, as a C-string.
38575     **/
38576     const CImg<T>& save_dlm(const char *const filename) const {
38577       return _save_dlm(0,filename);
38578     }
38579 
38580     //! Save image as a DLM file \overloading.
38581     const CImg<T>& save_dlm(std::FILE *const file) const {
38582       return _save_dlm(file,0);
38583     }
38584 
38585     const CImg<T>& _save_dlm(std::FILE *const file, const char *const filename) const {
38586       if (!file && !filename)
38587         throw CImgArgumentException(_cimg_instance
38588                                     "save_dlm() : Specified filename is (null).",
38589                                     cimg_instance);
38590       if (is_empty())
38591         throw CImgInstanceException(_cimg_instance
38592                                     "save_dlm() : Empty instance, for file '%s'.",
38593                                     cimg_instance,
38594                                     filename?filename:"(FILE*)");
38595       if (_depth>1)
38596         cimg::warn(_cimg_instance
38597                    "save_dlm() : Instance is volumetric, values along Z will be unrolled in file '%s'.",
38598                    cimg_instance,
38599                    filename?filename:"(FILE*)");
38600 
38601       if (_spectrum>1)
38602         cimg::warn(_cimg_instance
38603                    "save_dlm() : Instance is multispectral, values along C will be unrolled in file '%s'.",
38604                    cimg_instance,
38605                    filename?filename:"(FILE*)");
38606 
38607       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
38608       const T* ptrs = _data;
38609       cimg_forYZC(*this,y,z,c) {
38610         cimg_forX(*this,x) std::fprintf(nfile,"%.16g%s",(double)*(ptrs++),(x==width()-1)?"":",");
38611         std::fputc('\n',nfile);
38612       }
38613       if (!file) cimg::fclose(nfile);
38614       return *this;
38615     }
38616 
38617     //! Save image as a BMP file.
38618     /**
38619       \param filename Filename, as a C-string.
38620     **/
38621     const CImg<T>& save_bmp(const char *const filename) const {
38622       return _save_bmp(0,filename);
38623     }
38624 
38625     //! Save image as a BMP file \overloading.
38626     const CImg<T>& save_bmp(std::FILE *const file) const {
38627       return _save_bmp(file,0);
38628     }
38629 
38630     const CImg<T>& _save_bmp(std::FILE *const file, const char *const filename) const {
38631       if (!file && !filename)
38632         throw CImgArgumentException(_cimg_instance
38633                                     "save_bmp() : Specified filename is (null).",
38634                                     cimg_instance);
38635       if (is_empty())
38636         throw CImgInstanceException(_cimg_instance
38637                                     "save_bmp() : Empty instance, for file '%s'.",
38638                                     cimg_instance,
38639                                     filename?filename:"(FILE*)");
38640       if (_depth>1)
38641         cimg::warn(_cimg_instance
38642                    "save_bmp() : Instance is volumetric, only the first slice will be saved in file '%s'.",
38643                    cimg_instance,
38644                    filename?filename:"(FILE*)");
38645 
38646       if (_spectrum>3)
38647         cimg::warn(_cimg_instance
38648                    "save_bmp() : Instance is multispectral, only the three first channels will be saved in file '%s'.",
38649                    cimg_instance,
38650                    filename?filename:"(FILE*)");
38651 
38652       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
38653       unsigned char header[54] = { 0 }, align_buf[4] = { 0 };
38654       const unsigned int
38655         align = (4 - (3*_width)%4)%4,
38656         buf_size = (3*_width + align)*height(),
38657         file_size = 54 + buf_size;
38658       header[0] = 'B'; header[1] = 'M';
38659       header[0x02] = file_size&0xFF;
38660       header[0x03] = (file_size>>8)&0xFF;
38661       header[0x04] = (file_size>>16)&0xFF;
38662       header[0x05] = (file_size>>24)&0xFF;
38663       header[0x0A] = 0x36;
38664       header[0x0E] = 0x28;
38665       header[0x12] = _width&0xFF;
38666       header[0x13] = (_width>>8)&0xFF;
38667       header[0x14] = (_width>>16)&0xFF;
38668       header[0x15] = (_width>>24)&0xFF;
38669       header[0x16] = _height&0xFF;
38670       header[0x17] = (_height>>8)&0xFF;
38671       header[0x18] = (_height>>16)&0xFF;
38672       header[0x19] = (_height>>24)&0xFF;
38673       header[0x1A] = 1;
38674       header[0x1B] = 0;
38675       header[0x1C] = 24;
38676       header[0x1D] = 0;
38677       header[0x22] = buf_size&0xFF;
38678       header[0x23] = (buf_size>>8)&0xFF;
38679       header[0x24] = (buf_size>>16)&0xFF;
38680       header[0x25] = (buf_size>>24)&0xFF;
38681       header[0x27] = 0x1;
38682       header[0x2B] = 0x1;
38683       cimg::fwrite(header,54,nfile);
38684 
38685       const T
38686         *ptr_r = data(0,_height-1,0,0),
38687         *ptr_g = (_spectrum>=2)?data(0,_height-1,0,1):0,
38688         *ptr_b = (_spectrum>=3)?data(0,_height-1,0,2):0;
38689 
38690       switch (_spectrum) {
38691       case 1 : {
38692         cimg_forY(*this,y) {
38693           cimg_forX(*this,x) {
38694             const unsigned char val = (unsigned char)*(ptr_r++);
38695             std::fputc(val,nfile); std::fputc(val,nfile); std::fputc(val,nfile);
38696           }
38697           cimg::fwrite(align_buf,align,nfile);
38698           ptr_r-=2*_width;
38699         }
38700       } break;
38701       case 2 : {
38702         cimg_forY(*this,y) {
38703           cimg_forX(*this,x) {
38704             std::fputc(0,nfile);
38705             std::fputc((unsigned char)(*(ptr_g++)),nfile);
38706             std::fputc((unsigned char)(*(ptr_r++)),nfile);
38707           }
38708           cimg::fwrite(align_buf,align,nfile);
38709           ptr_r-=2*_width; ptr_g-=2*_width;
38710         }
38711       } break;
38712       default : {
38713         cimg_forY(*this,y) {
38714           cimg_forX(*this,x) {
38715             std::fputc((unsigned char)(*(ptr_b++)),nfile);
38716             std::fputc((unsigned char)(*(ptr_g++)),nfile);
38717             std::fputc((unsigned char)(*(ptr_r++)),nfile);
38718           }
38719           cimg::fwrite(align_buf,align,nfile);
38720           ptr_r-=2*_width; ptr_g-=2*_width; ptr_b-=2*_width;
38721         }
38722       }
38723       }
38724       if (!file) cimg::fclose(nfile);
38725       return *this;
38726     }
38727 
38728     //! Save image as a JPEG file.
38729     /**
38730       \param filename Filename, as a C-string.
38731       \param quality Image quality (in %)
38732     **/
38733     const CImg<T>& save_jpeg(const char *const filename, const unsigned int quality=100) const {
38734       return _save_jpeg(0,filename,quality);
38735     }
38736 
38737     //! Save image as a JPEG file \overloading.
38738     const CImg<T>& save_jpeg(std::FILE *const file, const unsigned int quality=100) const {
38739       return _save_jpeg(file,0,quality);
38740     }
38741 
38742     const CImg<T>& _save_jpeg(std::FILE *const file, const char *const filename, const unsigned int quality) const {
38743       if (!file && !filename)
38744         throw CImgArgumentException(_cimg_instance
38745                                     "save_jpeg() : Specified filename is (null).",
38746                                     cimg_instance);
38747       if (is_empty())
38748         throw CImgInstanceException(_cimg_instance
38749                                     "save_jpeg() : Empty instance, for file '%s'.",
38750                                     cimg_instance,
38751                                     filename?filename:"(FILE*)");
38752       if (_depth>1)
38753         cimg::warn(_cimg_instance
38754                    "save_jpeg() : Instance is volumetric, only the first slice will be saved in file '%s'.",
38755                    cimg_instance,
38756                    filename?filename:"(FILE*)");
38757 
38758 #ifndef cimg_use_jpeg
38759       if (!file) return save_other(filename,quality);
38760       else throw CImgIOException(_cimg_instance
38761                                  "save_jpeg() : Unable to save data in '(*FILE)' unless libjpeg is enabled.",
38762                                  cimg_instance);
38763 #else
38764       unsigned int dimbuf = 0;
38765       J_COLOR_SPACE colortype = JCS_RGB;
38766 
38767       switch(_spectrum) {
38768       case 1 : dimbuf = 1; colortype = JCS_GRAYSCALE; break;
38769       case 2 : dimbuf = 3; colortype = JCS_RGB; break;
38770       case 3 : dimbuf = 3; colortype = JCS_RGB; break;
38771       default: dimbuf = 4; colortype = JCS_CMYK; break;
38772       }
38773 
38774       // Call libjpeg functions
38775       struct jpeg_compress_struct cinfo;
38776       struct jpeg_error_mgr jerr;
38777       cinfo.err = jpeg_std_error(&jerr);
38778       jpeg_create_compress(&cinfo);
38779       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
38780       jpeg_stdio_dest(&cinfo,nfile);
38781       cinfo.image_width = _width;
38782       cinfo.image_height = _height;
38783       cinfo.input_components = dimbuf;
38784       cinfo.in_color_space = colortype;
38785       jpeg_set_defaults(&cinfo);
38786       jpeg_set_quality(&cinfo,quality<100?quality:100,TRUE);
38787       jpeg_start_compress(&cinfo,TRUE);
38788 
38789       JSAMPROW row_pointer[1];
38790       CImg<ucharT> buffer((unsigned long)_width*dimbuf);
38791 
38792       while (cinfo.next_scanline < cinfo.image_height) {
38793         unsigned char *ptrd = buffer._data;
38794 
38795         // Fill pixel buffer
38796         switch (_spectrum) {
38797         case 1 : { // Greyscale images
38798           const T *ptr_g = data(0, cinfo.next_scanline);
38799           for(unsigned int b = 0; b < cinfo.image_width; b++)
38800             *(ptrd++) = (unsigned char)*(ptr_g++);
38801         } break;
38802         case 2 : { // RG images
38803           const T *ptr_r = data(0,cinfo.next_scanline,0,0),
38804             *ptr_g = data(0,cinfo.next_scanline,0,1);
38805           for(unsigned int b = 0; b < cinfo.image_width; ++b) {
38806             *(ptrd++) = (unsigned char)*(ptr_r++);
38807             *(ptrd++) = (unsigned char)*(ptr_g++);
38808             *(ptrd++) = 0;
38809           }
38810         } break;
38811         case 3 : { // RGB images
38812           const T *ptr_r = data(0,cinfo.next_scanline,0,0),
38813             *ptr_g = data(0,cinfo.next_scanline,0,1),
38814             *ptr_b = data(0,cinfo.next_scanline,0,2);
38815           for(unsigned int b = 0; b < cinfo.image_width; ++b) {
38816             *(ptrd++) = (unsigned char)*(ptr_r++);
38817             *(ptrd++) = (unsigned char)*(ptr_g++);
38818             *(ptrd++) = (unsigned char)*(ptr_b++);
38819           }
38820         } break;
38821         default : { // CMYK images
38822           const T *ptr_r = data(0,cinfo.next_scanline,0,0),
38823             *ptr_g = data(0,cinfo.next_scanline,0,1),
38824             *ptr_b = data(0,cinfo.next_scanline,0,2),
38825             *ptr_a = data(0,cinfo.next_scanline,0,3);
38826           for(unsigned int b = 0; b < cinfo.image_width; ++b) {
38827             *(ptrd++) = (unsigned char)*(ptr_r++);
38828             *(ptrd++) = (unsigned char)*(ptr_g++);
38829             *(ptrd++) = (unsigned char)*(ptr_b++);
38830             *(ptrd++) = (unsigned char)*(ptr_a++);
38831           }
38832         }
38833         }
38834         *row_pointer = buffer._data;
38835         jpeg_write_scanlines(&cinfo,row_pointer,1);
38836       }
38837       jpeg_finish_compress(&cinfo);
38838       if (!file) cimg::fclose(nfile);
38839       jpeg_destroy_compress(&cinfo);
38840       return *this;
38841 #endif
38842     }
38843 
38844     //! Save image, using built-in ImageMagick++ library.
38845     /**
38846       \param filename Filename, as a C-string.
38847       \param bytes_per_pixel Force the number of bytes per pixel for the saving, when possible.
38848     **/
38849     const CImg<T>& save_magick(const char *const filename, const unsigned int bytes_per_pixel=0) const {
38850       if (!filename)
38851         throw CImgArgumentException(_cimg_instance
38852                                     "save_magick() : Specified filename is (null).",
38853                                     cimg_instance);
38854       if (is_empty())
38855         throw CImgInstanceException(_cimg_instance
38856                                     "save_magick() : Empty instance, for file '%s'.",
38857                                     cimg_instance,
38858                                     filename);
38859 #ifdef cimg_use_magick
38860       double stmin, stmax = (double)max_min(stmin);
38861       if (_depth>1)
38862         cimg::warn(_cimg_instance
38863                    "save_magick() : Instance is volumetric, only the first slice will be saved in file '%s'.",
38864                    cimg_instance,
38865                    filename);
38866 
38867       if (_spectrum>3)
38868         cimg::warn(_cimg_instance
38869                    "save_magick() : Instance is multispectral, only the three first channels will be saved in file '%s'.",
38870                    cimg_instance,
38871                    filename);
38872 
38873       if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
38874         cimg::warn(_cimg_instance
38875                    "save_magick() : Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
38876                    cimg_instance,
38877                    filename,stmin,stmax);
38878 
38879       Magick::Image image(Magick::Geometry(_width,_height),"black");
38880       image.type(Magick::TrueColorType);
38881       image.depth(bytes_per_pixel?(8*bytes_per_pixel):(stmax>=256?16:8));
38882       const T
38883         *ptr_r = data(0,0,0,0),
38884         *ptr_g = _spectrum>1?data(0,0,0,1):0,
38885         *ptr_b = _spectrum>2?data(0,0,0,2):0;
38886       Magick::PixelPacket *pixels = image.getPixels(0,0,_width,_height);
38887       switch (_spectrum) {
38888       case 1 : // Scalar images
38889         for (unsigned long off = (unsigned long)_width*_height; off; --off) {
38890           pixels->red = pixels->green = pixels->blue = (Magick::Quantum)*(ptr_r++);
38891           ++pixels;
38892         }
38893         break;
38894       case 2 : // RG images
38895         for (unsigned long off = (unsigned long)_width*_height; off; --off) {
38896           pixels->red = (Magick::Quantum)*(ptr_r++);
38897           pixels->green = (Magick::Quantum)*(ptr_g++);
38898           pixels->blue = 0; ++pixels;
38899         }
38900         break;
38901       default : // RGB images
38902         for (unsigned long off = (unsigned long)_width*_height; off; --off) {
38903           pixels->red = (Magick::Quantum)*(ptr_r++);
38904           pixels->green = (Magick::Quantum)*(ptr_g++);
38905           pixels->blue = (Magick::Quantum)*(ptr_b++);
38906           ++pixels;
38907         }
38908       }
38909       image.syncPixels();
38910       image.write(filename);
38911 #else
38912       cimg::unused(bytes_per_pixel);
38913       throw CImgIOException(_cimg_instance
38914                             "save_magick() : Unable to save file '%s' unless libMagick++ is enabled.",
38915                             cimg_instance,
38916                             filename);
38917 #endif
38918       return *this;
38919     }
38920 
38921     //! Save image as a PNG file.
38922     /**
38923        \param filename Filename, as a C-string.
38924        \param bytes_per_pixel Force the number of bytes per pixels for the saving, when possible.
38925     **/
38926     const CImg<T>& save_png(const char *const filename, const unsigned int bytes_per_pixel=0) const {
38927       return _save_png(0,filename,bytes_per_pixel);
38928     }
38929 
38930     //! Save image as a PNG file \overloading.
38931     const CImg<T>& save_png(std::FILE *const file, const unsigned int bytes_per_pixel=0) const {
38932       return _save_png(file,0,bytes_per_pixel);
38933     }
38934 
38935     const CImg<T>& _save_png(std::FILE *const file, const char *const filename, const unsigned int bytes_per_pixel=0) const {
38936       if (!file && !filename)
38937         throw CImgArgumentException(_cimg_instance
38938                                     "save_png() : Specified filename is (null).",
38939                                     cimg_instance);
38940       if (is_empty())
38941         throw CImgInstanceException(_cimg_instance
38942                                     "save_png() : Empty image, for file '%s'.",
38943                                     cimg_instance,
38944                                     filename?filename:"(FILE*)");
38945 #ifndef cimg_use_png
38946       cimg::unused(bytes_per_pixel);
38947       if (!file) return save_other(filename);
38948       else throw CImgIOException(_cimg_instance
38949                                  "save_png() : Unable to save data in '(*FILE)' unless libpng is enabled.",
38950                                  cimg_instance);
38951 #else
38952       const char *volatile nfilename = filename; // two 'volatile' here to remove a g++ warning due to 'setjmp'.
38953       std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"wb");
38954 
38955       double stmin, stmax = (double)max_min(stmin);
38956       if (_depth>1)
38957         cimg::warn(_cimg_instance
38958                    "save_png() : Instance is volumetric, only the first slice will be saved in file '%s'.",
38959                    cimg_instance,
38960                    filename);
38961 
38962       if (_spectrum>4)
38963         cimg::warn(_cimg_instance
38964                    "save_png() : Instance is multispectral, only the three first channels will be saved in file '%s'.",
38965                    cimg_instance,
38966                    filename);
38967 
38968       if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
38969         cimg::warn(_cimg_instance
38970                    "save_png() : Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
38971                    cimg_instance,
38972                    filename,stmin,stmax);
38973 
38974       // Setup PNG structures for write
38975       png_voidp user_error_ptr = 0;
38976       png_error_ptr user_error_fn = 0, user_warning_fn = 0;
38977       png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,user_error_ptr, user_error_fn, user_warning_fn);
38978       if(!png_ptr){
38979         if (!file) cimg::fclose(nfile);
38980         throw CImgIOException(_cimg_instance
38981                               "save_png() : Failed to initialize 'png_ptr' structure when saving file '%s'.",
38982                               cimg_instance,
38983                               nfilename?nfilename:"(FILE*)");
38984       }
38985       png_infop info_ptr = png_create_info_struct(png_ptr);
38986       if (!info_ptr) {
38987         png_destroy_write_struct(&png_ptr,(png_infopp)0);
38988         if (!file) cimg::fclose(nfile);
38989         throw CImgIOException(_cimg_instance
38990                               "save_png() : Failed to initialize 'info_ptr' structure when saving file '%s'.",
38991                               cimg_instance,
38992                               nfilename?nfilename:"(FILE*)");
38993       }
38994       if (setjmp(png_jmpbuf(png_ptr))) {
38995         png_destroy_write_struct(&png_ptr, &info_ptr);
38996         if (!file) cimg::fclose(nfile);
38997         throw CImgIOException(_cimg_instance
38998                               "save_png() : Encountered unknown fatal error in libpng when saving file '%s'.",
38999                               cimg_instance,
39000                               nfilename?nfilename:"(FILE*)");
39001       }
39002       png_init_io(png_ptr, nfile);
39003       const int bit_depth = bytes_per_pixel?(bytes_per_pixel*8):(stmax>=256?16:8);
39004       int color_type;
39005       switch (spectrum()) {
39006       case 1 : color_type = PNG_COLOR_TYPE_GRAY; break;
39007       case 2 : color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break;
39008       case 3 : color_type = PNG_COLOR_TYPE_RGB; break;
39009       default : color_type = PNG_COLOR_TYPE_RGB_ALPHA;
39010       }
39011       const int interlace_type = PNG_INTERLACE_NONE;
39012       const int compression_type = PNG_COMPRESSION_TYPE_DEFAULT;
39013       const int filter_method = PNG_FILTER_TYPE_DEFAULT;
39014       png_set_IHDR(png_ptr,info_ptr,_width,_height,bit_depth,color_type,interlace_type,compression_type,filter_method);
39015       png_write_info(png_ptr,info_ptr);
39016       const int byte_depth = bit_depth>>3;
39017       const int numChan = spectrum()>4?4:spectrum();
39018       const int pixel_bit_depth_flag = numChan * (bit_depth-1);
39019 
39020       // Allocate Memory for Image Save and Fill pixel data
39021       png_bytep *const imgData = new png_byte*[_height];
39022       for (unsigned int row = 0; row<_height; ++row) imgData[row] = new png_byte[byte_depth*numChan*_width];
39023       const T *pC0 = data(0,0,0,0);
39024       switch (pixel_bit_depth_flag) {
39025       case 7 :  { // Gray 8-bit
39026         cimg_forY(*this,y) {
39027           unsigned char *ptrd = imgData[y];
39028           cimg_forX(*this,x) *(ptrd++) = (unsigned char)*(pC0++);
39029         }
39030       } break;
39031       case 14 : { // Gray w/ Alpha 8-bit
39032         const T *pC1 = data(0,0,0,1);
39033         cimg_forY(*this,y) {
39034           unsigned char *ptrd = imgData[y];
39035           cimg_forX(*this,x) {
39036             *(ptrd++) = (unsigned char)*(pC0++);
39037             *(ptrd++) = (unsigned char)*(pC1++);
39038           }
39039         }
39040       } break;
39041       case 21 :  { // RGB 8-bit
39042         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2);
39043         cimg_forY(*this,y) {
39044           unsigned char *ptrd = imgData[y];
39045           cimg_forX(*this,x) {
39046             *(ptrd++) = (unsigned char)*(pC0++);
39047             *(ptrd++) = (unsigned char)*(pC1++);
39048             *(ptrd++) = (unsigned char)*(pC2++);
39049           }
39050         }
39051       } break;
39052       case 28 : { // RGB x/ Alpha 8-bit
39053         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3);
39054         cimg_forY(*this,y){
39055           unsigned char *ptrd = imgData[y];
39056           cimg_forX(*this,x){
39057             *(ptrd++) = (unsigned char)*(pC0++);
39058             *(ptrd++) = (unsigned char)*(pC1++);
39059             *(ptrd++) = (unsigned char)*(pC2++);
39060             *(ptrd++) = (unsigned char)*(pC3++);
39061           }
39062         }
39063       } break;
39064       case 15 : { // Gray 16-bit
39065         cimg_forY(*this,y){
39066           unsigned short *ptrd = (unsigned short*)(imgData[y]);
39067           cimg_forX(*this,x) *(ptrd++) = (unsigned short)*(pC0++);
39068           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],_width);
39069         }
39070       } break;
39071       case 30 : { // Gray w/ Alpha 16-bit
39072         const T *pC1 = data(0,0,0,1);
39073         cimg_forY(*this,y){
39074           unsigned short *ptrd = (unsigned short*)(imgData[y]);
39075           cimg_forX(*this,x) {
39076             *(ptrd++) = (unsigned short)*(pC0++);
39077             *(ptrd++) = (unsigned short)*(pC1++);
39078           }
39079           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],2*_width);
39080         }
39081       } break;
39082       case 45 : { // RGB 16-bit
39083         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2);
39084         cimg_forY(*this,y) {
39085           unsigned short *ptrd = (unsigned short*)(imgData[y]);
39086           cimg_forX(*this,x) {
39087             *(ptrd++) = (unsigned short)*(pC0++);
39088             *(ptrd++) = (unsigned short)*(pC1++);
39089             *(ptrd++) = (unsigned short)*(pC2++);
39090           }
39091           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],3*_width);
39092         }
39093       } break;
39094       case 60 : { // RGB w/ Alpha 16-bit
39095         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3);
39096         cimg_forY(*this,y) {
39097           unsigned short *ptrd = (unsigned short*)(imgData[y]);
39098           cimg_forX(*this,x) {
39099             *(ptrd++) = (unsigned short)*(pC0++);
39100             *(ptrd++) = (unsigned short)*(pC1++);
39101             *(ptrd++) = (unsigned short)*(pC2++);
39102             *(ptrd++) = (unsigned short)*(pC3++);
39103           }
39104           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],4*_width);
39105         }
39106       } break;
39107       default :
39108         if (!file) cimg::fclose(nfile);
39109         throw CImgIOException(_cimg_instance
39110                               "save_png() : Encountered unknown fatal error in libpng when saving file '%s'.",
39111                               cimg_instance,
39112                               nfilename?nfilename:"(FILE*)");
39113       }
39114       png_write_image(png_ptr,imgData);
39115       png_write_end(png_ptr,info_ptr);
39116       png_destroy_write_struct(&png_ptr, &info_ptr);
39117 
39118       // Deallocate Image Write Memory
39119       cimg_forY(*this,n) delete[] imgData[n];
39120       delete[] imgData;
39121       if (!file) cimg::fclose(nfile);
39122       return *this;
39123 #endif
39124     }
39125 
39126     //! Save image as a PNM file.
39127     /**
39128       \param filename Filename, as a C-string.
39129       \param bytes_per_pixel Force the number of bytes per pixels for the saving.
39130     **/
39131     const CImg<T>& save_pnm(const char *const filename, const unsigned int bytes_per_pixel=0) const {
39132       return _save_pnm(0,filename,bytes_per_pixel);
39133     }
39134 
39135     //! Save image as a PNM file \overloading.
39136     const CImg<T>& save_pnm(std::FILE *const file, const unsigned int bytes_per_pixel=0) const {
39137       return _save_pnm(file,0,bytes_per_pixel);
39138     }
39139 
39140     const CImg<T>& _save_pnm(std::FILE *const file, const char *const filename, const unsigned int bytes_per_pixel=0) const {
39141       if (!file && !filename)
39142         throw CImgArgumentException(_cimg_instance
39143                                     "save_pnm() : Specified filename is (null).",
39144                                     cimg_instance);
39145       if (is_empty())
39146         throw CImgInstanceException(_cimg_instance
39147                                     "save_pnm() : Empty instance, for file '%s'.",
39148                                     cimg_instance,
39149                                     filename?filename:"(FILE*)");
39150 
39151       double stmin, stmax = (double)max_min(stmin);
39152       if (_depth>1)
39153         cimg::warn(_cimg_instance
39154                    "save_pnm() : Instance is volumetric, only the first slice will be saved in file '%s'.",
39155                    cimg_instance,
39156                    filename?filename:"(FILE*)");
39157 
39158       if (_spectrum>3)
39159         cimg::warn(_cimg_instance
39160                    "save_pnm() : Instance is multispectral, only the three first channels will be saved in file '%s'.",
39161                    cimg_instance,
39162                    filename?filename:"(FILE*)");
39163 
39164       if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
39165         cimg::warn(_cimg_instance
39166                    "save_pnm() : Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
39167                    cimg_instance,
39168                    stmin,stmax,filename?filename:"(FILE*)");
39169 
39170       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
39171       const T
39172         *ptr_r = data(0,0,0,0),
39173         *ptr_g = (_spectrum>=2)?data(0,0,0,1):0,
39174         *ptr_b = (_spectrum>=3)?data(0,0,0,2):0;
39175       const unsigned long buf_size = cimg::min(1024*1024UL,_width*_height*(_spectrum==1?1UL:3UL));
39176 
39177       std::fprintf(nfile,"P%c\n%u %u\n%u\n",
39178                    (_spectrum==1?'5':'6'),_width,_height,stmax<256?255:(stmax<4096?4095:65535));
39179 
39180       switch (_spectrum) {
39181       case 1 : { // Scalar image
39182         if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PGM 8 bits
39183           CImg<ucharT> buf(buf_size);
39184           for (long to_write = (long)_width*_height; to_write>0; ) {
39185             const unsigned long N = cimg::min((unsigned long)to_write,buf_size);
39186             unsigned char *ptrd = buf._data;
39187             for (unsigned long i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr_r++);
39188             cimg::fwrite(buf._data,N,nfile);
39189             to_write-=N;
39190           }
39191         } else { // Binary PGM 16 bits
39192           CImg<ushortT> buf(buf_size);
39193           for (long to_write = (long)_width*_height; to_write>0; ) {
39194             const unsigned long N = cimg::min((unsigned long)to_write,buf_size);
39195             unsigned short *ptrd = buf._data;
39196             for (unsigned long i = N; i>0; --i) *(ptrd++) = (unsigned short)*(ptr_r++);
39197             if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
39198             cimg::fwrite(buf._data,N,nfile);
39199             to_write-=N;
39200           }
39201         }
39202       } break;
39203       case 2 : { // RG image
39204         if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits
39205           CImg<ucharT> buf(buf_size);
39206           for (long to_write = (long)_width*_height; to_write>0; ) {
39207             const unsigned long N = cimg::min((unsigned long)to_write,buf_size/3);
39208             unsigned char *ptrd = buf._data;
39209             for (unsigned long i = N; i>0; --i) {
39210               *(ptrd++) = (unsigned char)*(ptr_r++);
39211               *(ptrd++) = (unsigned char)*(ptr_g++);
39212               *(ptrd++) = 0;
39213             }
39214             cimg::fwrite(buf._data,3*N,nfile);
39215             to_write-=N;
39216           }
39217         } else {             // Binary PPM 16 bits
39218           CImg<ushortT> buf(buf_size);
39219           for (long to_write = (long)_width*_height; to_write>0; ) {
39220             const unsigned long N = cimg::min((unsigned long)to_write,buf_size/3);
39221             unsigned short *ptrd = buf._data;
39222             for (unsigned long i = N; i>0; --i) {
39223               *(ptrd++) = (unsigned short)*(ptr_r++);
39224               *(ptrd++) = (unsigned short)*(ptr_g++);
39225               *(ptrd++) = 0;
39226             }
39227             if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
39228             cimg::fwrite(buf._data,3*N,nfile);
39229             to_write-=N;
39230           }
39231         }
39232       } break;
39233       default : { // RGB image
39234         if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits
39235           CImg<ucharT> buf(buf_size);
39236           for (long to_write = (long)_width*_height; to_write>0; ) {
39237             const unsigned long N = cimg::min((unsigned long)to_write,buf_size/3);
39238             unsigned char *ptrd = buf._data;
39239             for (unsigned long i = N; i>0; --i) {
39240               *(ptrd++) = (unsigned char)*(ptr_r++);
39241               *(ptrd++) = (unsigned char)*(ptr_g++);
39242               *(ptrd++) = (unsigned char)*(ptr_b++);
39243             }
39244             cimg::fwrite(buf._data,3*N,nfile);
39245             to_write-=N;
39246           }
39247         } else {             // Binary PPM 16 bits
39248           CImg<ushortT> buf(buf_size);
39249           for (long to_write = (long)_width*_height; to_write>0; ) {
39250             const unsigned long N = cimg::min((unsigned long)to_write,buf_size/3);
39251             unsigned short *ptrd = buf._data;
39252             for (unsigned long i = N; i>0; --i) {
39253               *(ptrd++) = (unsigned short)*(ptr_r++);
39254               *(ptrd++) = (unsigned short)*(ptr_g++);
39255               *(ptrd++) = (unsigned short)*(ptr_b++);
39256             }
39257             if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
39258             cimg::fwrite(buf._data,3*N,nfile);
39259             to_write-=N;
39260           }
39261         }
39262       }
39263       }
39264       if (!file) cimg::fclose(nfile);
39265       return *this;
39266     }
39267 
39268     //! Save image as a PNK file.
39269     /**
39270       \param filename Filename, as a C-string.
39271     **/
39272     const CImg<T>& save_pnk(const char *const filename) const {
39273       return _save_pnk(0,filename);
39274     }
39275 
39276     //! Save image as a PNK file \overloading.
39277     const CImg<T>& save_pnk(std::FILE *const file) const {
39278       return _save_pnk(file,0);
39279     }
39280 
39281     const CImg<T>& _save_pnk(std::FILE *const file, const char *const filename) const {
39282       if (!file && !filename)
39283         throw CImgArgumentException(_cimg_instance
39284                                     "save_pnk() : Specified filename is (null).",
39285                                     cimg_instance);
39286       if (is_empty())
39287         throw CImgInstanceException(_cimg_instance
39288                                     "save_pnk() : Empty instance, for file '%s'.",
39289                                     cimg_instance,
39290                                     filename?filename:"(FILE*)");
39291       if (_spectrum>1)
39292         cimg::warn(_cimg_instance
39293                    "save_pnk() : Instance is multispectral, only the first channel will be saved in file '%s'.",
39294                    cimg_instance,
39295                    filename?filename:"(FILE*)");
39296 
39297       const unsigned long buf_size = cimg::min(1024*1024LU,_width*_height*_depth);
39298       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
39299       const T *ptr = data(0,0,0,0);
39300 
39301       if (!cimg::type<T>::is_float() && sizeof(T)==1 && _depth<2) _save_pnm(file,filename,0); // Can be saved as regular PNM file.
39302       else if (!cimg::type<T>::is_float() && sizeof(T)==1) { // Save as extended P5 file : Binary byte-valued 3d.
39303         std::fprintf(nfile,"P5\n%u %u %u\n255\n",_width,_height,_depth);
39304         CImg<ucharT> buf(buf_size);
39305         for (long to_write = (long)_width*_height*_depth; to_write>0; ) {
39306           const unsigned long N = cimg::min((unsigned long)to_write,buf_size);
39307           unsigned char *ptrd = buf._data;
39308           for (unsigned long i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr++);
39309           cimg::fwrite(buf._data,N,nfile);
39310           to_write-=N;
39311         }
39312       } else if (!cimg::type<T>::is_float()) { // Save as P8 : Binary int32-valued 3d.
39313         if (_depth>1) std::fprintf(nfile,"P8\n%u %u %u\n%d\n",_width,_height,_depth,(int)max());
39314         else std::fprintf(nfile,"P8\n%u %u\n%d\n",_width,_height,(int)max());
39315         CImg<intT> buf(buf_size);
39316         for (long to_write = (long)_width*_height*_depth; to_write>0; ) {
39317           const unsigned long N = cimg::min((unsigned long)to_write,buf_size);
39318           int *ptrd = buf._data;
39319           for (unsigned long i = N; i>0; --i) *(ptrd++) = (int)*(ptr++);
39320           cimg::fwrite(buf._data,N,nfile);
39321           to_write-=N;
39322         }
39323       } else { // Save as P9 : Binary float-valued 3d.
39324         if (_depth>1) std::fprintf(nfile,"P9\n%u %u %u\n%g\n",_width,_height,_depth,(double)max());
39325         else std::fprintf(nfile,"P9\n%u %u\n%g\n",_width,_height,(double)max());
39326         CImg<floatT> buf(buf_size);
39327         for (long to_write = (long)_width*_height*_depth; to_write>0; ) {
39328           const unsigned long N = cimg::min((unsigned long)to_write,buf_size);
39329           float *ptrd = buf._data;
39330           for (unsigned long i = N; i>0; --i) *(ptrd++) = (float)*(ptr++);
39331           cimg::fwrite(buf._data,N,nfile);
39332           to_write-=N;
39333         }
39334       }
39335 
39336       if (!file) cimg::fclose(nfile);
39337       return *this;
39338     }
39339 
39340     //! Save image as a PFM file.
39341     /**
39342       \param filename Filename, as a C-string.
39343     **/
39344     const CImg<T>& save_pfm(const char *const filename) const {
39345       return get_mirror('y')._save_pfm(0,filename);
39346     }
39347 
39348     //! Save image as a PFM file \overloading.
39349     const CImg<T>& save_pfm(std::FILE *const file) const {
39350       return get_mirror('y')._save_pfm(file,0);
39351     }
39352 
39353     const CImg<T>& _save_pfm(std::FILE *const file, const char *const filename) const {
39354       if (!file && !filename)
39355         throw CImgArgumentException(_cimg_instance
39356                                     "save_pfm() : Specified filename is (null).",
39357                                     cimg_instance);
39358       if (is_empty())
39359         throw CImgInstanceException(_cimg_instance
39360                                     "save_pfm() : Empty instance, for file '%s'.",
39361                                     cimg_instance,
39362                                     filename?filename:"(FILE*)");
39363       if (_depth>1)
39364         cimg::warn(_cimg_instance
39365                    "save_pfm() : Instance is volumetric, only the first slice will be saved in file '%s'.",
39366                    cimg_instance,
39367                    filename?filename:"(FILE*)");
39368 
39369       if (_spectrum>3)
39370         cimg::warn(_cimg_instance
39371                    "save_pfm() : image instance is multispectral, only the three first channels will be saved in file '%s'.",
39372                    cimg_instance,
39373                    filename?filename:"(FILE*)");
39374 
39375       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
39376       const T
39377         *ptr_r = data(0,0,0,0),
39378         *ptr_g = (_spectrum>=2)?data(0,0,0,1):0,
39379         *ptr_b = (_spectrum>=3)?data(0,0,0,2):0;
39380       const unsigned int buf_size = cimg::min(1024*1024U,_width*_height*(_spectrum==1?1:3));
39381 
39382       std::fprintf(nfile,"P%c\n%u %u\n1.0\n",
39383                    (_spectrum==1?'f':'F'),_width,_height);
39384 
39385       switch (_spectrum) {
39386       case 1 : { // Scalar image
39387         CImg<floatT> buf(buf_size);
39388         for (long to_write = (long)_width*_height; to_write>0; ) {
39389           const unsigned long N = cimg::min((unsigned long)to_write,buf_size);
39390           float *ptrd = buf._data;
39391           for (unsigned long i = N; i>0; --i) *(ptrd++) = (float)*(ptr_r++);
39392           if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
39393           cimg::fwrite(buf._data,N,nfile);
39394           to_write-=N;
39395         }
39396       } break;
39397       case 2 : { // RG image
39398         CImg<floatT> buf(buf_size);
39399         for (long to_write = (long)_width*_height; to_write>0; ) {
39400           const unsigned int N = cimg::min((unsigned int)to_write,buf_size/3);
39401           float *ptrd = buf._data;
39402           for (unsigned long i = N; i>0; --i) {
39403             *(ptrd++) = (float)*(ptr_r++);
39404             *(ptrd++) = (float)*(ptr_g++);
39405             *(ptrd++) = 0;
39406           }
39407           if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
39408           cimg::fwrite(buf._data,3*N,nfile);
39409           to_write-=N;
39410         }
39411       } break;
39412       default : { // RGB image
39413         CImg<floatT> buf(buf_size);
39414         for (long to_write = (long)_width*_height; to_write>0; ) {
39415           const unsigned int N = cimg::min((unsigned int)to_write,buf_size/3);
39416           float *ptrd = buf._data;
39417           for (unsigned long i = N; i>0; --i) {
39418             *(ptrd++) = (float)*(ptr_r++);
39419             *(ptrd++) = (float)*(ptr_g++);
39420             *(ptrd++) = (float)*(ptr_b++);
39421           }
39422           if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
39423           cimg::fwrite(buf._data,3*N,nfile);
39424           to_write-=N;
39425         }
39426       }
39427       }
39428       if (!file) cimg::fclose(nfile);
39429       return *this;
39430     }
39431 
39432     //! Save image as a RGB file.
39433     /**
39434       \param filename Filename, as a C-string.
39435     **/
39436     const CImg<T>& save_rgb(const char *const filename) const {
39437       return _save_rgb(0,filename);
39438     }
39439 
39440     //! Save image as a RGB file \overloading.
39441     const CImg<T>& save_rgb(std::FILE *const file) const {
39442       return _save_rgb(file,0);
39443     }
39444 
39445     const CImg<T>& _save_rgb(std::FILE *const file, const char *const filename) const {
39446       if (!file && !filename)
39447         throw CImgArgumentException(_cimg_instance
39448                                     "save_rgb() : Specified filename is (null).",
39449                                     cimg_instance);
39450       if (is_empty())
39451         throw CImgInstanceException(_cimg_instance
39452                                     "save_rgb() : Empty instance, for file '%s'.",
39453                                     cimg_instance,
39454                                     filename?filename:"(FILE*)");
39455       if (_spectrum!=3)
39456         cimg::warn(_cimg_instance
39457                    "save_rgb() : image instance has not exactly 3 channels, for file '%s'.",
39458                    cimg_instance,
39459                    filename?filename:"(FILE*)");
39460 
39461       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
39462       const unsigned long wh = (unsigned long)_width*_height;
39463       unsigned char *const buffer = new unsigned char[3*wh], *nbuffer = buffer;
39464       const T
39465         *ptr1 = data(0,0,0,0),
39466         *ptr2 = _spectrum>1?data(0,0,0,1):0,
39467         *ptr3 = _spectrum>2?data(0,0,0,2):0;
39468       switch (_spectrum) {
39469       case 1 : { // Scalar image
39470         for (unsigned long k = 0; k<wh; ++k) {
39471           const unsigned char val = (unsigned char)*(ptr1++);
39472           *(nbuffer++) = val;
39473           *(nbuffer++) = val;
39474           *(nbuffer++) = val;
39475         }
39476       } break;
39477       case 2 : { // RG image
39478         for (unsigned long k = 0; k<wh; ++k) {
39479           *(nbuffer++) = (unsigned char)(*(ptr1++));
39480           *(nbuffer++) = (unsigned char)(*(ptr2++));
39481           *(nbuffer++) = 0;
39482         }
39483       } break;
39484       default : { // RGB image
39485         for (unsigned long k = 0; k<wh; ++k) {
39486           *(nbuffer++) = (unsigned char)(*(ptr1++));
39487           *(nbuffer++) = (unsigned char)(*(ptr2++));
39488           *(nbuffer++) = (unsigned char)(*(ptr3++));
39489         }
39490       }
39491       }
39492       cimg::fwrite(buffer,3*wh,nfile);
39493       if (!file) cimg::fclose(nfile);
39494       delete[] buffer;
39495       return *this;
39496     }
39497 
39498     //! Save image as a RGBA file.
39499     /**
39500        \param filename Filename, as a C-string.
39501     **/
39502     const CImg<T>& save_rgba(const char *const filename) const {
39503       return _save_rgba(0,filename);
39504     }
39505 
39506     //! Save image as a RGBA file \overloading.
39507     const CImg<T>& save_rgba(std::FILE *const file) const {
39508       return _save_rgba(file,0);
39509     }
39510 
39511     const CImg<T>& _save_rgba(std::FILE *const file, const char *const filename) const {
39512       if (!file && !filename)
39513         throw CImgArgumentException(_cimg_instance
39514                                     "save_rgba() : Specified filename is (null).",
39515                                     cimg_instance);
39516       if (is_empty())
39517         throw CImgInstanceException(_cimg_instance
39518                                     "save_rgba() : Empty instance, for file '%s'.",
39519                                     cimg_instance,
39520                                     filename?filename:"(FILE*)");
39521       if (_spectrum!=4)
39522         cimg::warn(_cimg_instance
39523                    "save_rgba() : image instance has not exactly 4 channels, for file '%s'.",
39524                    cimg_instance,
39525                    filename?filename:"(FILE*)");
39526 
39527       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
39528       const unsigned long wh = (unsigned long)_width*_height;
39529       unsigned char *const buffer = new unsigned char[4*wh], *nbuffer = buffer;
39530       const T
39531         *ptr1 = data(0,0,0,0),
39532         *ptr2 = _spectrum>1?data(0,0,0,1):0,
39533         *ptr3 = _spectrum>2?data(0,0,0,2):0,
39534         *ptr4 = _spectrum>3?data(0,0,0,3):0;
39535       switch (_spectrum) {
39536       case 1 : { // Scalar images
39537         for (unsigned long k = 0; k<wh; ++k) {
39538           const unsigned char val = (unsigned char)*(ptr1++);
39539           *(nbuffer++) = val;
39540           *(nbuffer++) = val;
39541           *(nbuffer++) = val;
39542           *(nbuffer++) = 255;
39543         }
39544       } break;
39545       case 2 : { // RG images
39546         for (unsigned long k = 0; k<wh; ++k) {
39547           *(nbuffer++) = (unsigned char)(*(ptr1++));
39548           *(nbuffer++) = (unsigned char)(*(ptr2++));
39549           *(nbuffer++) = 0;
39550           *(nbuffer++) = 255;
39551         }
39552       } break;
39553       case 3 : { // RGB images
39554         for (unsigned long k = 0; k<wh; ++k) {
39555           *(nbuffer++) = (unsigned char)(*(ptr1++));
39556           *(nbuffer++) = (unsigned char)(*(ptr2++));
39557           *(nbuffer++) = (unsigned char)(*(ptr3++));
39558           *(nbuffer++) = 255;
39559         }
39560       } break;
39561       default : { // RGBA images
39562         for (unsigned long k = 0; k<wh; ++k) {
39563           *(nbuffer++) = (unsigned char)(*(ptr1++));
39564           *(nbuffer++) = (unsigned char)(*(ptr2++));
39565           *(nbuffer++) = (unsigned char)(*(ptr3++));
39566           *(nbuffer++) = (unsigned char)(*(ptr4++));
39567         }
39568       }
39569       }
39570       cimg::fwrite(buffer,4*wh,nfile);
39571       if (!file) cimg::fclose(nfile);
39572       delete[] buffer;
39573       return *this;
39574     }
39575 
39576     //! Save image as a TIFF file.
39577     /**
39578        \param filename Filename, as a C-string.
39579        \param compression_type Type of data compression. Can be <tt>{ 1=None | 2=CCITTRLE | 3=CCITTFAX3 | 4=CCITTFAX4 | 5=LZW | 6=JPEG }</tt>.
39580        \note
39581        - libtiff support is enabled by defining the precompilation
39582         directive \c cimg_use_tif.
39583        - When libtiff is enabled, 2D and 3D (multipage) several
39584         channel per pixel are supported for
39585         <tt>char,uchar,short,ushort,float</tt> and \c double pixel types.
39586        - If \c cimg_use_tif is not defined at compilation time the
39587         function uses CImg<T>&save_other(const char*).
39588      **/
39589     const CImg<T>& save_tiff(const char *const filename, const unsigned int compression_type=0) const {
39590       if (!filename)
39591         throw CImgArgumentException(_cimg_instance
39592                                     "save_tiff() : Specified filename is (null).",
39593                                     cimg_instance);
39594       if (is_empty())
39595         throw CImgInstanceException(_cimg_instance
39596                                     "save_tiff() : Empty instance, for file '%s'.",
39597                                     cimg_instance,
39598                                     filename);
39599 
39600 #ifdef cimg_use_tiff
39601       TIFF *tif = TIFFOpen(filename,"w");
39602       if (tif) {
39603         cimg_forZ(*this,z) get_slice(z)._save_tiff(tif,z,compression_type);
39604         TIFFClose(tif);
39605       } else throw CImgIOException(_cimg_instance
39606                                    "save_tiff() : Failed to open file '%s' for writing.",
39607                                    cimg_instance,
39608                                    filename);
39609 #else
39610       cimg::unused(compression_type);
39611       return save_other(filename);
39612 #endif
39613       return *this;
39614     }
39615 
39616 #ifdef cimg_use_tiff
39617 
39618 #define _cimg_save_tiff(types,typed,compression_type) \
39619     if (!std::strcmp(types,pixel_type())) { const typed foo = (typed)0; return _save_tiff(tif,directory,foo,compression_type); }
39620 
39621     // [internal] Save a plane into a tiff file
39622     template<typename t>
39623     const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory, const t& pixel_t, const unsigned int compression_type) const {
39624       if (is_empty() || !tif || pixel_t) return *this;
39625       const char *const filename = TIFFFileName(tif);
39626       uint32 rowsperstrip = (uint32)-1;
39627       uint16 spp = _spectrum, bpp = sizeof(t)*8, photometric;
39628       if (spp==3 || spp==4) photometric = PHOTOMETRIC_RGB;
39629       else photometric = PHOTOMETRIC_MINISBLACK;
39630       TIFFSetDirectory(tif,directory);
39631       TIFFSetField(tif,TIFFTAG_IMAGEWIDTH,_width);
39632       TIFFSetField(tif,TIFFTAG_IMAGELENGTH,_height);
39633       TIFFSetField(tif,TIFFTAG_ORIENTATION,ORIENTATION_TOPLEFT);
39634       TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,spp);
39635       if (cimg::type<t>::is_float()) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,3);
39636       else if (cimg::type<t>::min()==0) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,1);
39637       else TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,2);
39638       TIFFSetField(tif,TIFFTAG_BITSPERSAMPLE,bpp);
39639       TIFFSetField(tif,TIFFTAG_PLANARCONFIG,PLANARCONFIG_CONTIG);
39640       TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,photometric);
39641       TIFFSetField(tif,TIFFTAG_COMPRESSION,compression_type?(compression_type-1):COMPRESSION_NONE);
39642       rowsperstrip = TIFFDefaultStripSize(tif,rowsperstrip);
39643       TIFFSetField(tif,TIFFTAG_ROWSPERSTRIP,rowsperstrip);
39644       TIFFSetField(tif,TIFFTAG_FILLORDER,FILLORDER_MSB2LSB);
39645       TIFFSetField(tif,TIFFTAG_SOFTWARE,"CImg");
39646       t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
39647       if (buf) {
39648         for (unsigned int row = 0; row<_height; row+=rowsperstrip) {
39649           uint32 nrow = (row + rowsperstrip>_height?_height-row:rowsperstrip);
39650           tstrip_t strip = TIFFComputeStrip(tif,row,0);
39651           tsize_t i = 0;
39652           for (unsigned int rr = 0; rr<nrow; ++rr)
39653             for (unsigned int cc = 0; cc<_width; ++cc)
39654               for (unsigned int vv = 0; vv<spp; ++vv)
39655                 buf[i++] = (t)(*this)(cc,row + rr,0,vv);
39656           if (TIFFWriteEncodedStrip(tif,strip,buf,i*sizeof(t))<0)
39657             throw CImgIOException(_cimg_instance
39658                                   "save_tiff() : Invalid strip writing when saving file '%s'.",
39659                                   cimg_instance,
39660                                   filename?filename:"(FILE*)");
39661         }
39662         _TIFFfree(buf);
39663       }
39664       TIFFWriteDirectory(tif);
39665       return (*this);
39666     }
39667 
39668     const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int compression_type) const {
39669       _cimg_save_tiff("bool",unsigned char,compression_type);
39670       _cimg_save_tiff("char",char,compression_type);
39671       _cimg_save_tiff("unsigned char",unsigned char,compression_type);
39672       _cimg_save_tiff("short",short,compression_type);
39673       _cimg_save_tiff("unsigned short",unsigned short,compression_type);
39674       _cimg_save_tiff("int",int,compression_type);
39675       _cimg_save_tiff("unsigned int",unsigned int,compression_type);
39676       _cimg_save_tiff("long",int,compression_type);
39677       _cimg_save_tiff("unsigned long",unsigned int,compression_type);
39678       _cimg_save_tiff("float",float,compression_type);
39679       _cimg_save_tiff("double",float,compression_type);
39680       const char *const filename = TIFFFileName(tif);
39681       throw CImgInstanceException(_cimg_instance
39682                                   "save_tiff() : Unsupported pixel type '%s' for file '%s'.",
39683                                   cimg_instance,
39684                                   pixel_type(),filename?filename:"(FILE*)");
39685       return *this;
39686     }
39687 #endif
39688 
39689     //! Save image as a MINC2 file.
39690     /**
39691        \param filename Filename, as a C-string.
39692        \param imitate_file If non-zero, reference filename, as a C-string, to borrow header from.
39693     **/
39694     const CImg<T>& save_minc2(const char *const filename,
39695                               const char *const imitate_file=0) const {
39696       if (!filename)
39697         throw CImgArgumentException(_cimg_instance
39698                                    "save_minc2() : Specified filename is (null).",
39699                                    cimg_instance);
39700      if (is_empty())
39701        throw CImgInstanceException(_cimg_instance
39702                                    "save_minc2() : Empty instance, for file '%s'.",
39703                                    cimg_instance,
39704                                    filename);
39705 #ifndef cimg_use_minc2
39706      cimg::unused(imitate_file);
39707      return save_other(filename);
39708 #else
39709      minc::minc_1_writer wtr;
39710      if (imitate_file)
39711        wtr.open(filename, imitate_file);
39712      else {
39713        minc::minc_info di;
39714        if(width()) di.push_back(minc::dim_info(width(), width()*0.5, -1, minc::dim_info::DIM_X));
39715        if(height()) di.push_back(minc::dim_info(height(), height()*0.5, -1, minc::dim_info::DIM_Y));
39716        if(depth()) di.push_back(minc::dim_info(depth(), depth()*0.5, -1, minc::dim_info::DIM_Z));
39717        if(spectrum()) di.push_back(minc::dim_info(spectrum(), spectrum()*0.5, -1, minc::dim_info::DIM_TIME));
39718        wtr.open(filename, di, 1, NC_FLOAT, 0);
39719      }
39720      if(typeid(T)==typeid(unsigned char))
39721        wtr.setup_write_byte();
39722      else if(typeid(T)==typeid(int))
39723        wtr.setup_write_int();
39724      else if(typeid(T)==typeid(double))
39725        wtr.setup_write_double();
39726      else
39727        wtr.setup_write_float();
39728      minc::save_standard_volume(wtr, this->_data);
39729      return *this;
39730 #endif
39731     }
39732 
39733     //! Save image as an ANALYZE7.5 or NIFTI file.
39734     /**
39735       \param filename Filename, as a C-string.
39736       \param voxel_size Pointer to 3 consecutive values that tell about the voxel sizes along the X,Y and Z dimensions.
39737     **/
39738     const CImg<T>& save_analyze(const char *const filename, const float *const voxel_size=0) const {
39739       if (!filename)
39740         throw CImgArgumentException(_cimg_instance
39741                                     "save_analyze() : Specified filename is (null).",
39742                                     cimg_instance);
39743       if (is_empty())
39744         throw CImgInstanceException(_cimg_instance
39745                                     "save_analyze() : Empty instance, for file '%s'.",
39746                                     cimg_instance,
39747                                     filename);
39748 
39749       std::FILE *file;
39750       char header[348] = { 0 }, hname[1024] = { 0 }, iname[1024] = { 0 };
39751       const char *const ext = cimg::split_filename(filename);
39752       short datatype=-1;
39753       std::memset(header,0,348);
39754       if (!*ext) { cimg_snprintf(hname,sizeof(hname),"%s.hdr",filename); cimg_snprintf(iname,sizeof(iname),"%s.img",filename); }
39755       if (!cimg::strncasecmp(ext,"hdr",3)) {
39756         std::strcpy(hname,filename); std::strncpy(iname,filename,sizeof(iname)-1); std::sprintf(iname + std::strlen(iname)-3,"img");
39757       }
39758       if (!cimg::strncasecmp(ext,"img",3)) {
39759         std::strcpy(hname,filename); std::strncpy(iname,filename,sizeof(iname)-1); std::sprintf(hname + std::strlen(iname)-3,"hdr");
39760       }
39761       if (!cimg::strncasecmp(ext,"nii",3)) {
39762         std::strncpy(hname,filename,sizeof(hname)-1); *iname = 0;
39763       }
39764       int *const iheader = (int*)header;
39765       *iheader = 348;
39766       std::strcpy(header + 4,"CImg");
39767       std::strcpy(header + 14," ");
39768       ((short*)(header + 36))[0] = 4096;
39769       ((char*)(header + 38))[0] = 114;
39770       ((short*)(header + 40))[0] = 4;
39771       ((short*)(header + 40))[1] = _width;
39772       ((short*)(header + 40))[2] = _height;
39773       ((short*)(header + 40))[3] = _depth;
39774       ((short*)(header + 40))[4] = _spectrum;
39775       if (!cimg::strcasecmp(pixel_type(),"bool")) datatype = 2;
39776       if (!cimg::strcasecmp(pixel_type(),"unsigned char")) datatype = 2;
39777       if (!cimg::strcasecmp(pixel_type(),"char")) datatype = 2;
39778       if (!cimg::strcasecmp(pixel_type(),"unsigned short")) datatype = 4;
39779       if (!cimg::strcasecmp(pixel_type(),"short")) datatype = 4;
39780       if (!cimg::strcasecmp(pixel_type(),"unsigned int")) datatype = 8;
39781       if (!cimg::strcasecmp(pixel_type(),"int")) datatype = 8;
39782       if (!cimg::strcasecmp(pixel_type(),"unsigned long")) datatype = 8;
39783       if (!cimg::strcasecmp(pixel_type(),"long")) datatype = 8;
39784       if (!cimg::strcasecmp(pixel_type(),"float")) datatype = 16;
39785       if (!cimg::strcasecmp(pixel_type(),"double")) datatype = 64;
39786       if (datatype<0)
39787         throw CImgIOException(_cimg_instance
39788                               "save_analyze() : Unsupported pixel type '%s' for file '%s'.",
39789                               cimg_instance,
39790                               pixel_type(),filename);
39791 
39792       ((short*)(header+70))[0] = datatype;
39793       ((short*)(header+72))[0] = sizeof(T);
39794       ((float*)(header+112))[0] = 1;
39795       ((float*)(header+76))[0] = 0;
39796       if (voxel_size) {
39797         ((float*)(header+76))[1] = voxel_size[0];
39798         ((float*)(header+76))[2] = voxel_size[1];
39799         ((float*)(header+76))[3] = voxel_size[2];
39800       } else ((float*)(header+76))[1] = ((float*)(header+76))[2] = ((float*)(header+76))[3] = 1;
39801       file = cimg::fopen(hname,"wb");
39802       cimg::fwrite(header,348,file);
39803       if (*iname) { cimg::fclose(file); file = cimg::fopen(iname,"wb"); }
39804       cimg::fwrite(_data,size(),file);
39805       cimg::fclose(file);
39806       return *this;
39807     }
39808 
39809     //! Save image as a .cimg file.
39810     /**
39811       \param filename Filename, as a C-string.
39812       \param is_compressed Tells if the file contains compressed image data.
39813     **/
39814     const CImg<T>& save_cimg(const char *const filename, const bool is_compressed=false) const {
39815       CImgList<T>(*this,true).save_cimg(filename,is_compressed);
39816       return *this;
39817     }
39818 
39819     //! Save image as a .cimg file \overloading.
39820     const CImg<T>& save_cimg(std::FILE *const file, const bool is_compressed=false) const {
39821       CImgList<T>(*this,true).save_cimg(file,is_compressed);
39822       return *this;
39823     }
39824 
39825     //! Save image as a sub-image into an existing .cimg file.
39826     /**
39827       \param filename Filename, as a C-string.
39828       \param n0 Index of the image inside the file.
39829       \param x0 X-coordinate of the sub-image location.
39830       \param y0 Y-coordinate of the sub-image location.
39831       \param z0 Z-coordinate of the sub-image location.
39832       \param c0 C-coordinate of the sub-image location.
39833     **/
39834     const CImg<T>& save_cimg(const char *const filename,
39835                              const unsigned int n0,
39836                              const unsigned int x0, const unsigned int y0,
39837                              const unsigned int z0, const unsigned int c0) const {
39838       CImgList<T>(*this,true).save_cimg(filename,n0,x0,y0,z0,c0);
39839       return *this;
39840     }
39841 
39842     //! Save image as a sub-image into an existing .cimg file \overloading.
39843     const CImg<T>& save_cimg(std::FILE *const file,
39844                              const unsigned int n0,
39845                              const unsigned int x0, const unsigned int y0,
39846                              const unsigned int z0, const unsigned int c0) const {
39847       CImgList<T>(*this,true).save_cimg(file,n0,x0,y0,z0,c0);
39848       return *this;
39849     }
39850 
39851     //! Save blank image as a .cimg file.
39852     /**
39853         \param filename Filename, as a C-string.
39854         \param dx Width of the image.
39855         \param dy Height of the image.
39856         \param dz Depth of the image.
39857         \param dc Number of channels of the image.
39858         \note
39859         - All pixel values of the saved image are set to \c 0.
39860         - Use this method to save large images without having to instanciate and allocate them.
39861     **/
39862     static void save_empty_cimg(const char *const filename,
39863                                 const unsigned int dx, const unsigned int dy=1,
39864                                 const unsigned int dz=1, const unsigned int dc=1) {
39865       return CImgList<T>::save_empty_cimg(filename,1,dx,dy,dz,dc);
39866     }
39867 
39868     //! Save blank image as a .cimg file \overloading.
39869     /**
39870        Same as save_empty_cimg(const char *,unsigned int,unsigned int,unsigned int,unsigned int)
39871        with a file stream argument instead of a filename string.
39872     **/
39873     static void save_empty_cimg(std::FILE *const file,
39874                                 const unsigned int dx, const unsigned int dy=1,
39875                                 const unsigned int dz=1, const unsigned int dc=1) {
39876       return CImgList<T>::save_empty_cimg(file,1,dx,dy,dz,dc);
39877     }
39878 
39879     //! Save image as an INRIMAGE-4 file.
39880     /**
39881       \param filename Filename, as a C-string.
39882       \param voxel_size Pointer to 3 values specifying the voxel sizes along the X,Y and Z dimensions.
39883     **/
39884     const CImg<T>& save_inr(const char *const filename, const float *const voxel_size=0) const {
39885       return _save_inr(0,filename,voxel_size);
39886     }
39887 
39888     //! Save image as an INRIMAGE-4 file \overloading.
39889     const CImg<T>& save_inr(std::FILE *const file, const float *const voxel_size=0) const {
39890       return _save_inr(file,0,voxel_size);
39891     }
39892 
39893     const CImg<T>& _save_inr(std::FILE *const file, const char *const filename, const float *const voxel_size) const {
39894       if (!file && !filename)
39895         throw CImgArgumentException(_cimg_instance
39896                                     "save_inr() : Specified filename is (null).",
39897                                     cimg_instance);
39898       if (is_empty())
39899         throw CImgInstanceException(_cimg_instance
39900                                     "save_inr() : Empty instance, for file '%s'.",
39901                                     cimg_instance,
39902                                     filename?filename:"(FILE*)");
39903 
39904       int inrpixsize=-1;
39905       const char *inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0";
39906       if (!cimg::strcasecmp(pixel_type(),"unsigned char"))  { inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1; }
39907       if (!cimg::strcasecmp(pixel_type(),"char"))           { inrtype = "fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1; }
39908       if (!cimg::strcasecmp(pixel_type(),"unsigned short")) { inrtype = "unsigned fixed\nPIXSIZE=16 bits\nSCALE=2**0";inrpixsize = 2; }
39909       if (!cimg::strcasecmp(pixel_type(),"short"))          { inrtype = "fixed\nPIXSIZE=16 bits\nSCALE=2**0"; inrpixsize = 2; }
39910       if (!cimg::strcasecmp(pixel_type(),"unsigned int"))   { inrtype = "unsigned fixed\nPIXSIZE=32 bits\nSCALE=2**0";inrpixsize = 4; }
39911       if (!cimg::strcasecmp(pixel_type(),"int"))            { inrtype = "fixed\nPIXSIZE=32 bits\nSCALE=2**0"; inrpixsize = 4; }
39912       if (!cimg::strcasecmp(pixel_type(),"float"))          { inrtype = "float\nPIXSIZE=32 bits"; inrpixsize = 4; }
39913       if (!cimg::strcasecmp(pixel_type(),"double"))         { inrtype = "float\nPIXSIZE=64 bits"; inrpixsize = 8; }
39914       if (inrpixsize<=0)
39915         throw CImgIOException(_cimg_instance
39916                               "save_inr() : Unsupported pixel type '%s' for file '%s'",
39917                               cimg_instance,
39918                               pixel_type(),filename?filename:"(FILE*)");
39919 
39920       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
39921       char header[257] = { 0 };
39922       int err = cimg_snprintf(header,sizeof(header),"#INRIMAGE-4#{\nXDIM=%u\nYDIM=%u\nZDIM=%u\nVDIM=%u\n",_width,_height,_depth,_spectrum);
39923       if (voxel_size) err+=std::sprintf(header + err,"VX=%g\nVY=%g\nVZ=%g\n",voxel_size[0],voxel_size[1],voxel_size[2]);
39924       err+=std::sprintf(header + err,"TYPE=%s\nCPU=%s\n",inrtype,cimg::endianness()?"sun":"decm");
39925       std::memset(header + err,'\n',252 - err);
39926       std::memcpy(header + 252,"##}\n",4);
39927       cimg::fwrite(header,256,nfile);
39928       cimg_forXYZ(*this,x,y,z) cimg_forC(*this,c) cimg::fwrite(&((*this)(x,y,z,c)),1,nfile);
39929       if (!file) cimg::fclose(nfile);
39930       return *this;
39931     }
39932 
39933     //! Save image as an OpenEXR file.
39934     /**
39935        \param filename Filename, as a C-string.
39936        \note The OpenEXR file format is <a href="http://en.wikipedia.org/wiki/OpenEXR">described here</a>.
39937     **/
39938     const CImg<T>& save_exr(const char *const filename) const {
39939       if (!filename)
39940         throw CImgArgumentException(_cimg_instance
39941                                     "save_exr() : Specified filename is (null).",
39942                                     cimg_instance);
39943       if (is_empty())
39944         throw CImgInstanceException(_cimg_instance
39945                                     "save_exr() : Empty instance, for file '%s'.",
39946                                     cimg_instance,
39947                                     filename);
39948       if (_depth>1)
39949         cimg::warn(_cimg_instance
39950                    "save_exr() : Instance is volumetric, only the first slice will be saved in file '%s'.",
39951                    cimg_instance,
39952                    filename);
39953 
39954 #ifndef cimg_use_openexr
39955       return save_other(filename);
39956 #else
39957       Imf::Rgba *const ptrd0 = new Imf::Rgba[(unsigned long)_width*_height], *ptrd = ptrd0, rgba;
39958       switch (_spectrum) {
39959       case 1 : { // Grayscale image.
39960         for (const T *ptr_r = data(), *const ptr_e = ptr_r + (unsigned long)_width*_height; ptr_r<ptr_e;) {
39961           rgba.r = rgba.g = rgba.b = (half)(*(ptr_r++));
39962           rgba.a = (half)1;
39963           *(ptrd++) = rgba;
39964         }
39965       } break;
39966       case 2 : { // RG image.
39967         for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1), *const ptr_e = ptr_r + (unsigned long)_width*_height; ptr_r<ptr_e; ) {
39968           rgba.r = (half)(*(ptr_r++));
39969           rgba.g = (half)(*(ptr_g++));
39970           rgba.b = (half)0;
39971           rgba.a = (half)1;
39972           *(ptrd++) = rgba;
39973         }
39974       } break;
39975       case 3 : { // RGB image.
39976         for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *const ptr_e = ptr_r + (unsigned long)_width*_height; ptr_r<ptr_e;) {
39977           rgba.r = (half)(*(ptr_r++));
39978           rgba.g = (half)(*(ptr_g++));
39979           rgba.b = (half)(*(ptr_b++));
39980           rgba.a = (half)1;
39981           *(ptrd++) = rgba;
39982         }
39983       } break;
39984       default : { // RGBA image.
39985         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),
39986                *const ptr_e = ptr_r + (unsigned long)_width*_height; ptr_r<ptr_e;) {
39987           rgba.r = (half)(*(ptr_r++));
39988           rgba.g = (half)(*(ptr_g++));
39989           rgba.b = (half)(*(ptr_b++));
39990           rgba.a = (half)(*(ptr_a++));
39991           *(ptrd++) = rgba;
39992         }
39993       } break;
39994       }
39995       Imf::RgbaOutputFile outFile(filename,_width,_height,
39996                                   _spectrum==1?Imf::WRITE_Y:_spectrum==2?Imf::WRITE_YA:_spectrum==3?Imf::WRITE_RGB:Imf::WRITE_RGBA);
39997       outFile.setFrameBuffer(ptrd0,1,_width);
39998       outFile.writePixels(_height);
39999       delete[] ptrd0;
40000       return *this;
40001 #endif
40002     }
40003 
40004     //! Save image as a Pandore-5 file.
40005     /**
40006        \param filename Filename, as a C-string.
40007        \param colorspace Colorspace data field in output file
40008        (see <a href="http://www.greyc.ensicaen.fr/~regis/Pandore/#documentation">Pandore file specifications</a> for more informations).
40009     **/
40010     const CImg<T>& save_pandore(const char *const filename, const unsigned int colorspace=0) const {
40011       return _save_pandore(0,filename,colorspace);
40012     }
40013 
40014     //! Save image as a Pandore-5 file \overloading.
40015     /**
40016         Same as save_pandore(const char *,unsigned int) const
40017         with a file stream argument instead of a filename string.
40018     **/
40019     const CImg<T>& save_pandore(std::FILE *const file, const unsigned int colorspace=0) const {
40020       return _save_pandore(file,0,colorspace);
40021     }
40022 
40023     unsigned int _save_pandore_header_length(unsigned int id, unsigned int *dims, const unsigned int colorspace) const {
40024       unsigned int nbdims = 0;
40025       if (id==2 || id==3 || id==4) { dims[0] = 1; dims[1] = _width;  nbdims = 2; }
40026       if (id==5 || id==6 || id==7) { dims[0] = 1; dims[1] = _height; dims[2] = _width;  nbdims=3; }
40027       if (id==8 || id==9 || id==10) { dims[0] = _spectrum; dims[1] = _depth;  dims[2] = _height; dims[3] = _width; nbdims = 4; }
40028       if (id==16 || id==17 || id==18) { dims[0] = 3; dims[1] = _height; dims[2] = _width;  dims[3] = colorspace; nbdims = 4; }
40029       if (id==19 || id==20 || id==21) { dims[0] = 3; dims[1] = _depth;  dims[2] = _height; dims[3] = _width; dims[4] = colorspace; nbdims = 5; }
40030       if (id==22 || id==23 || id==25) { dims[0] = _spectrum; dims[1] = _width;  nbdims = 2; }
40031       if (id==26 || id==27 || id==29) { dims[0] = _spectrum; dims[1] = _height; dims[2] = _width;  nbdims=3; }
40032       if (id==30 || id==31 || id==33) { dims[0] = _spectrum; dims[1] = _depth;  dims[2] = _height; dims[3] = _width; nbdims = 4; }
40033       return nbdims;
40034     }
40035 
40036     const CImg<T>& _save_pandore(std::FILE *const file, const char *const filename, const unsigned int colorspace) const {
40037 
40038 #define __cimg_save_pandore_case(dtype) \
40039        dtype *buffer = new dtype[size()]; \
40040        const T *ptrs = _data; \
40041        cimg_foroff(*this,off) *(buffer++) = (dtype)(*(ptrs++)); \
40042        buffer-=size(); \
40043        cimg::fwrite(buffer,size(),nfile); \
40044        delete[] buffer
40045 
40046 #define _cimg_save_pandore_case(sy,sz,sv,stype,id) \
40047       if (!saved && (sy?(sy==_height):true) && (sz?(sz==_depth):true) && (sv?(sv==_spectrum):true) && !std::strcmp(stype,pixel_type())) { \
40048         unsigned int *iheader = (unsigned int*)(header+12); \
40049         nbdims = _save_pandore_header_length((*iheader=id),dims,colorspace); \
40050         cimg::fwrite(header,36,nfile); \
40051         if (sizeof(unsigned long)==4) { unsigned long ndims[5] = { 0 }; for (int d = 0; d<5; ++d) ndims[d] = (unsigned long)dims[d]; cimg::fwrite(ndims,nbdims,nfile); } \
40052         else if (sizeof(unsigned int)==4) { unsigned int ndims[5] = { 0 }; for (int d = 0; d<5; ++d) ndims[d] = (unsigned int)dims[d]; cimg::fwrite(ndims,nbdims,nfile); } \
40053         else if (sizeof(unsigned short)==4) { unsigned short ndims[5] = { 0 }; for (int d = 0; d<5; ++d) ndims[d] = (unsigned short)dims[d]; cimg::fwrite(ndims,nbdims,nfile); } \
40054         else throw CImgIOException(_cimg_instance \
40055                                    "save_pandore() : Unsupported datatype for file '%s'.",\
40056                                    cimg_instance, \
40057                                    filename?filename:"(FILE*)"); \
40058         if (id==2 || id==5 || id==8 || id==16 || id==19 || id==22 || id==26 || id==30) { \
40059           __cimg_save_pandore_case(unsigned char); \
40060         } else if (id==3 || id==6 || id==9 || id==17 || id==20 || id==23 || id==27 || id==31) { \
40061           if (sizeof(unsigned long)==4) { __cimg_save_pandore_case(unsigned long); } \
40062           else if (sizeof(unsigned int)==4) { __cimg_save_pandore_case(unsigned int); } \
40063           else if (sizeof(unsigned short)==4) { __cimg_save_pandore_case(unsigned short); } \
40064           else throw CImgIOException(_cimg_instance \
40065                                      "save_pandore() : Unsupported datatype for file '%s'.",\
40066                                      cimg_instance, \
40067                                      filename?filename:"(FILE*)"); \
40068         } else if (id==4 || id==7 || id==10 || id==18 || id==21 || id==25 || id==29 || id==33) { \
40069           if (sizeof(double)==4) { __cimg_save_pandore_case(double); } \
40070           else if (sizeof(float)==4) { __cimg_save_pandore_case(float); } \
40071           else throw CImgIOException(_cimg_instance \
40072                                      "save_pandore() : Unsupported datatype for file '%s'.",\
40073                                      cimg_instance, \
40074                                      filename?filename:"(FILE*)"); \
40075         } \
40076         saved = true; \
40077       }
40078 
40079       if (!file && !filename)
40080         throw CImgArgumentException(_cimg_instance
40081                                     "save_pandore() : Specified filename is (null).",
40082                                     cimg_instance);
40083       if (is_empty())
40084         throw CImgInstanceException(_cimg_instance
40085                                     "save_pandore() : Empty instance, for file '%s'.",
40086                                     cimg_instance,
40087                                     filename?filename:"(FILE*)");
40088 
40089       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
40090       unsigned char header[36] = { 'P','A','N','D','O','R','E','0','4',0,0,0,
40091                                    0,0,0,0,'C','I','m','g',0,0,0,0,0,'N','o',' ','d','a','t','e',0,0,0,0 };
40092       unsigned int nbdims, dims[5] = { 0 };
40093       bool saved = false;
40094       _cimg_save_pandore_case(1,1,1,"unsigned char",2);
40095       _cimg_save_pandore_case(1,1,1,"char",3);
40096       _cimg_save_pandore_case(1,1,1,"short",3);
40097       _cimg_save_pandore_case(1,1,1,"unsigned short",3);
40098       _cimg_save_pandore_case(1,1,1,"unsigned int",3);
40099       _cimg_save_pandore_case(1,1,1,"int",3);
40100       _cimg_save_pandore_case(1,1,1,"unsigned long",4);
40101       _cimg_save_pandore_case(1,1,1,"long",3);
40102       _cimg_save_pandore_case(1,1,1,"float",4);
40103       _cimg_save_pandore_case(1,1,1,"double",4);
40104 
40105       _cimg_save_pandore_case(0,1,1,"unsigned char",5);
40106       _cimg_save_pandore_case(0,1,1,"char",6);
40107       _cimg_save_pandore_case(0,1,1,"short",6);
40108       _cimg_save_pandore_case(0,1,1,"unsigned short",6);
40109       _cimg_save_pandore_case(0,1,1,"unsigned int",6);
40110       _cimg_save_pandore_case(0,1,1,"int",6);
40111       _cimg_save_pandore_case(0,1,1,"unsigned long",7);
40112       _cimg_save_pandore_case(0,1,1,"long",6);
40113       _cimg_save_pandore_case(0,1,1,"float",7);
40114       _cimg_save_pandore_case(0,1,1,"double",7);
40115 
40116       _cimg_save_pandore_case(0,0,1,"unsigned char",8);
40117       _cimg_save_pandore_case(0,0,1,"char",9);
40118       _cimg_save_pandore_case(0,0,1,"short",9);
40119       _cimg_save_pandore_case(0,0,1,"unsigned short",9);
40120       _cimg_save_pandore_case(0,0,1,"unsigned int",9);
40121       _cimg_save_pandore_case(0,0,1,"int",9);
40122       _cimg_save_pandore_case(0,0,1,"unsigned long",10);
40123       _cimg_save_pandore_case(0,0,1,"long",9);
40124       _cimg_save_pandore_case(0,0,1,"float",10);
40125       _cimg_save_pandore_case(0,0,1,"double",10);
40126 
40127       _cimg_save_pandore_case(0,1,3,"unsigned char",16);
40128       _cimg_save_pandore_case(0,1,3,"char",17);
40129       _cimg_save_pandore_case(0,1,3,"short",17);
40130       _cimg_save_pandore_case(0,1,3,"unsigned short",17);
40131       _cimg_save_pandore_case(0,1,3,"unsigned int",17);
40132       _cimg_save_pandore_case(0,1,3,"int",17);
40133       _cimg_save_pandore_case(0,1,3,"unsigned long",18);
40134       _cimg_save_pandore_case(0,1,3,"long",17);
40135       _cimg_save_pandore_case(0,1,3,"float",18);
40136       _cimg_save_pandore_case(0,1,3,"double",18);
40137 
40138       _cimg_save_pandore_case(0,0,3,"unsigned char",19);
40139       _cimg_save_pandore_case(0,0,3,"char",20);
40140       _cimg_save_pandore_case(0,0,3,"short",20);
40141       _cimg_save_pandore_case(0,0,3,"unsigned short",20);
40142       _cimg_save_pandore_case(0,0,3,"unsigned int",20);
40143       _cimg_save_pandore_case(0,0,3,"int",20);
40144       _cimg_save_pandore_case(0,0,3,"unsigned long",21);
40145       _cimg_save_pandore_case(0,0,3,"long",20);
40146       _cimg_save_pandore_case(0,0,3,"float",21);
40147       _cimg_save_pandore_case(0,0,3,"double",21);
40148 
40149       _cimg_save_pandore_case(1,1,0,"unsigned char",22);
40150       _cimg_save_pandore_case(1,1,0,"char",23);
40151       _cimg_save_pandore_case(1,1,0,"short",23);
40152       _cimg_save_pandore_case(1,1,0,"unsigned short",23);
40153       _cimg_save_pandore_case(1,1,0,"unsigned int",23);
40154       _cimg_save_pandore_case(1,1,0,"int",23);
40155       _cimg_save_pandore_case(1,1,0,"unsigned long",25);
40156       _cimg_save_pandore_case(1,1,0,"long",23);
40157       _cimg_save_pandore_case(1,1,0,"float",25);
40158       _cimg_save_pandore_case(1,1,0,"double",25);
40159 
40160       _cimg_save_pandore_case(0,1,0,"unsigned char",26);
40161       _cimg_save_pandore_case(0,1,0,"char",27);
40162       _cimg_save_pandore_case(0,1,0,"short",27);
40163       _cimg_save_pandore_case(0,1,0,"unsigned short",27);
40164       _cimg_save_pandore_case(0,1,0,"unsigned int",27);
40165       _cimg_save_pandore_case(0,1,0,"int",27);
40166       _cimg_save_pandore_case(0,1,0,"unsigned long",29);
40167       _cimg_save_pandore_case(0,1,0,"long",27);
40168       _cimg_save_pandore_case(0,1,0,"float",29);
40169       _cimg_save_pandore_case(0,1,0,"double",29);
40170 
40171       _cimg_save_pandore_case(0,0,0,"unsigned char",30);
40172       _cimg_save_pandore_case(0,0,0,"char",31);
40173       _cimg_save_pandore_case(0,0,0,"short",31);
40174       _cimg_save_pandore_case(0,0,0,"unsigned short",31);
40175       _cimg_save_pandore_case(0,0,0,"unsigned int",31);
40176       _cimg_save_pandore_case(0,0,0,"int",31);
40177       _cimg_save_pandore_case(0,0,0,"unsigned long",33);
40178       _cimg_save_pandore_case(0,0,0,"long",31);
40179       _cimg_save_pandore_case(0,0,0,"float",33);
40180       _cimg_save_pandore_case(0,0,0,"double",33);
40181 
40182       if (!file) cimg::fclose(nfile);
40183       return *this;
40184     }
40185 
40186     //! Save image as a raw data file.
40187     /**
40188        \param filename Filename, as a C-string.
40189        \param is_multiplexed Tells if the image channels are stored in a multiplexed way (\c true) or not (\c false).
40190        \note The .raw format does not store the image dimensions in the output file, so you have to keep track of them somewhere
40191        to be able to read the file correctly afterwards.
40192     **/
40193     const CImg<T>& save_raw(const char *const filename, const bool is_multiplexed=false) const {
40194       return _save_raw(0,filename,is_multiplexed);
40195     }
40196 
40197     //! Save image as a raw data file \overloading.
40198     /**
40199        Same as save_raw(const char *,bool) const
40200        with a file stream argument instead of a filename string.
40201     **/
40202     const CImg<T>& save_raw(std::FILE *const file, const bool is_multiplexed=false) const {
40203       return _save_raw(file,0,is_multiplexed);
40204     }
40205 
40206     const CImg<T>& _save_raw(std::FILE *const file, const char *const filename, const bool is_multiplexed) const {
40207       if (!file && !filename)
40208         throw CImgArgumentException(_cimg_instance
40209                                     "save_raw() : Specified filename is (null).",
40210                                     cimg_instance);
40211       if (is_empty())
40212         throw CImgInstanceException(_cimg_instance
40213                                     "save_raw() : empty instance, for file '%s'.",
40214                                     cimg_instance,
40215                                     filename?filename:"(FILE*)");
40216 
40217       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
40218       if (!is_multiplexed) cimg::fwrite(_data,size(),nfile);
40219       else {
40220         CImg<T> buf(_spectrum);
40221         cimg_forXYZ(*this,x,y,z) {
40222           cimg_forC(*this,c) buf[c] = (*this)(x,y,z,c);
40223           cimg::fwrite(buf._data,_spectrum,nfile);
40224         }
40225       }
40226       if (!file) cimg::fclose(nfile);
40227       return *this;
40228     }
40229 
40230     //! Save image as a video file, using the FFmpeg library.
40231     /**
40232       \param filename Filename, as a C-string.
40233       \param first_frame First frame of the video sequence.
40234       \param last_frame Last frame of the video sequence.
40235       \param fps Video framerate.
40236       \param bitrate Video bitrate.
40237       \note
40238       - Each slice of the instance image is considered to be a single frame of the output video file.
40239       - This method uses functions provided by the <a href="http://www.ffmpeg.org">FFmpeg</a> library.
40240       Configuration macro \c cimg_use_ffmpeg must be set for the method to succeed natively.
40241       Otherwise, the method calls save_ffmpeg_external(const char*,unsigned int,unsigned int,const char*,unsigned int,unsigned int) const.
40242     **/
40243     const CImg<T>& save_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
40244                                const unsigned int fps=25, const unsigned int bitrate=2048) const {
40245       if (!filename)
40246         throw CImgArgumentException(_cimg_instance
40247                                     "save_ffmpeg() : Specified filename is (null).",
40248                                     cimg_instance);
40249       if (is_empty())
40250         throw CImgInstanceException(_cimg_instance
40251                                     "save_ffmpeg() : Empty instance, for file '%s'.",
40252                                     cimg_instance,
40253                                     filename);
40254       if (!fps)
40255         throw CImgArgumentException(_cimg_instance
40256                                     "save_ffmpeg() : Invalid specified framerate 0, for file '%s'.",
40257                                     cimg_instance,
40258                                     filename);
40259 #ifndef cimg_use_ffmpeg
40260       return save_ffmpeg_external(filename,first_frame,last_frame,0,fps,bitrate);
40261 #else
40262       const unsigned int
40263         __first_frame = first_frame<last_frame?first_frame:last_frame,
40264         __last_frame = first_frame<last_frame?last_frame:first_frame,
40265         _first_frame = __first_frame>=_depth?~0U:__first_frame,
40266         _last_frame = __last_frame>=_depth?_depth-1:__last_frame;
40267       if (_first_frame==~0U) throw CImgArgumentException(_cimg_instance
40268                                                          "save_ffmpeg() : Invalid arguments first_frame=%u, last_frame=%u.",
40269                                                          cimg_instance,
40270                                                          first_frame,
40271                                                          last_frame);
40272       CImgList<T> list;
40273       if (_first_frame==0 && _last_frame==_depth-1) get_split('z').move_to(list);
40274       else (get_slices(_first_frame,_last_frame)<'z').move_to(list);
40275       list.save_ffmpeg(filename,0,~0U,fps,bitrate);
40276 #endif
40277       return *this;
40278     }
40279 
40280     //! Save image as a .yuv video file.
40281     /**
40282        \param filename Filename, as a C-string.
40283        \param is_rgb Tells if pixel values of the instance image are RGB-coded (\c true) or YUV-coded (\c false).
40284        \note Each slice of the instance image is considered to be a single frame of the output video file.
40285     **/
40286     const CImg<T>& save_yuv(const char *const filename, const bool is_rgb=true) const {
40287       get_split('z').save_yuv(filename,is_rgb);
40288       return *this;
40289     }
40290 
40291     //! Save image as a .yuv video file \overloading.
40292     /**
40293        Same as save_yuv(const char*,bool) const
40294        with a file stream argument instead of a filename string.
40295     **/
40296     const CImg<T>& save_yuv(std::FILE *const file, const bool is_rgb=true) const {
40297       get_split('z').save_yuv(file,is_rgb);
40298       return *this;
40299     }
40300 
40301     //! Save 3d object as an Object File Format (.off) file.
40302     /**
40303        \param filename Filename, as a C-string.
40304        \param primitives List of 3d object primitives.
40305        \param colors List of 3d object colors.
40306        \note
40307        - Instance image contains the vertices data of the 3d object.
40308        - Textured, transparent or sphere-shaped primitives cannot be managed by the .off file format.
40309        Such primitives will be lost or simplified during file saving.
40310        - The .off file format is <a href="http://people.sc.fsu.edu/~jburkardt/html/off_format.html">described here</a>.
40311     **/
40312     template<typename tf, typename tc>
40313     const CImg<T>& save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
40314                             const char *const filename) const {
40315       return _save_off(primitives,colors,0,filename);
40316     }
40317 
40318     //! Save 3d object as an Object File Format (.off) file \overloading.
40319     /**
40320        Same as save_off(const CImgList<tf>&,const CImgList<tc>&,const char*) const
40321        with a file stream argument instead of a filename string.
40322     **/
40323     template<typename tf, typename tc>
40324     const CImg<T>& save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
40325                             std::FILE *const file) const {
40326       return _save_off(primitives,colors,file,0);
40327     }
40328 
40329     template<typename tf, typename tc>
40330     const CImg<T>& _save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
40331                              std::FILE *const file, const char *const filename) const {
40332       if (!file && !filename)
40333         throw CImgArgumentException(_cimg_instance
40334                                     "save_off() : Specified filename is (null).",
40335                                     cimg_instance);
40336       if (is_empty())
40337         throw CImgInstanceException(_cimg_instance
40338                                     "save_off() : Empty instance, for file '%s'.",
40339                                     cimg_instance,
40340                                     filename?filename:"(FILE*)");
40341 
40342       CImgList<T> opacities;
40343       char error_message[1024] = { 0 };
40344       if (!is_object3d(primitives,colors,opacities,true,error_message))
40345         throw CImgInstanceException(_cimg_instance
40346                                     "save_off() : Invalid specified 3d object, for file '%s' (%s).",
40347                                     cimg_instance,
40348                                     filename?filename:"(FILE*)",error_message);
40349 
40350       const CImg<tc> default_color(1,3,1,1,200);
40351       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
40352       unsigned int supported_primitives = 0;
40353       cimglist_for(primitives,l) if (primitives[l].size()!=5) ++supported_primitives;
40354       std::fprintf(nfile,"OFF\n%u %u %u\n",_width,supported_primitives,3*primitives._width);
40355       cimg_forX(*this,i) std::fprintf(nfile,"%f %f %f\n",(float)((*this)(i,0)),(float)((*this)(i,1)),(float)((*this)(i,2)));
40356       cimglist_for(primitives,l) {
40357         const CImg<tc>& color = l<colors.width()?colors[l]:default_color;
40358         const unsigned int psiz = primitives[l].size(), csiz = color.size();
40359         const float r = color[0]/255.0f, g = (csiz>1?color[1]:r)/255.0f, b = (csiz>2?color[2]:g)/255.0f;
40360         switch (psiz) {
40361         case 1 : std::fprintf(nfile,"1 %u %f %f %f\n",(unsigned int)primitives(l,0),r,g,b); break;
40362         case 2 : std::fprintf(nfile,"2 %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break;
40363         case 3 : std::fprintf(nfile,"3 %u %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,2),
40364                               (unsigned int)primitives(l,1),r,g,b); break;
40365         case 4 : std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,3),
40366                               (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),r,g,b); break;
40367         case 5 : std::fprintf(nfile,"2 %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break;
40368         case 6 : {
40369           const unsigned int xt = (unsigned int)primitives(l,2), yt = (unsigned int)primitives(l,3);
40370           const float rt = color.atXY(xt,yt,0)/255.0f, gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f, bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f;
40371           std::fprintf(nfile,"2 %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,1),rt,gt,bt);
40372         } break;
40373         case 9 : {
40374           const unsigned int xt = (unsigned int)primitives(l,3), yt = (unsigned int)primitives(l,4);
40375           const float rt = color.atXY(xt,yt,0)/255.0f, gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f, bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f;
40376           std::fprintf(nfile,"3 %u %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,2),
40377                        (unsigned int)primitives(l,1),rt,gt,bt);
40378         } break;
40379         case 12 : {
40380           const unsigned int xt = (unsigned int)primitives(l,4), yt = (unsigned int)primitives(l,5);
40381           const float rt = color.atXY(xt,yt,0)/255.0f, gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f, bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f;
40382           std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,3),
40383                        (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),rt,gt,bt);
40384         } break;
40385         }
40386       }
40387       if (!file) cimg::fclose(nfile);
40388       return *this;
40389     }
40390 
40391     //! Save volumetric image as a video, using ffmpeg external binary.
40392     /**
40393        \param filename Filename, as a C-string.
40394        \param first_frame First frame of the video sequence.
40395        \param last_frame Last frame of the video sequence.
40396        \param codec Video codec, as a C-string.
40397        \param fps Video framerate.
40398        \param bitrate Video bitrate.
40399        \note
40400        - Each slice of the instance image is considered to be a single frame of the output video file.
40401        - This method uses \c ffmpeg, an external executable binary provided by <a href="http://www.ffmpeg.org">FFmpeg</a>.
40402        It must be installed for the method to succeed.
40403     **/
40404     const CImg<T>& save_ffmpeg_external(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
40405                                         const char *const codec=0, const unsigned int fps=25, const unsigned int bitrate=2048) const {
40406       if (!filename)
40407         throw CImgArgumentException(_cimg_instance
40408                                     "save_ffmpeg_external() : Specified filename is (null).",
40409                                     cimg_instance);
40410       if (is_empty())
40411         throw CImgInstanceException(_cimg_instance
40412                                     "save_ffmpeg_external() : Empty instance, for file '%s'.",
40413                                     cimg_instance,
40414                                     filename);
40415       const unsigned int
40416         __first_frame = first_frame<last_frame?first_frame:last_frame,
40417         __last_frame = first_frame<last_frame?last_frame:first_frame,
40418         _first_frame = __first_frame>=_depth?~0U:__first_frame,
40419         _last_frame = __last_frame>=_depth?_depth-1:__last_frame;
40420       if (_first_frame==~0U) throw CImgArgumentException(_cimg_instance
40421                                                          "save_ffmpeg_external() : Invalid arguments first_frame=%u, last_frame=%u.",
40422                                                          cimg_instance,
40423                                                          first_frame,
40424                                                          last_frame);
40425       CImgList<T> list;
40426       if (_first_frame==0 && _last_frame==_depth-1) get_split('z').move_to(list);
40427       else (get_slices(_first_frame,_last_frame)<'z').move_to(list);
40428       list.save_ffmpeg_external(filename,0,~0U,codec,fps,bitrate);
40429       return *this;
40430     }
40431 
40432     //! Save image using gzip external binary.
40433     /**
40434        \param filename Filename, as a C-string.
40435        \note This method uses \c gzip, an external executable binary provided by <a href="//http://www.gzip.org">gzip</a>.
40436        It must be installed for the method to succeed.
40437     **/
40438     const CImg<T>& save_gzip_external(const char *const filename) const {
40439       if (!filename)
40440         throw CImgArgumentException(_cimg_instance
40441                                     "save_gzip_external() : Specified filename is (null).",
40442                                     cimg_instance);
40443       if (is_empty())
40444         throw CImgInstanceException(_cimg_instance
40445                                     "save_gzip_external() : Empty instance, for file '%s'.",
40446                                     cimg_instance,
40447                                     filename);
40448 
40449       char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
40450       const char
40451         *ext = cimg::split_filename(filename,body),
40452         *ext2 = cimg::split_filename(body,0);
40453       std::FILE *file;
40454       do {
40455         if (!cimg::strcasecmp(ext,"gz")) {
40456           if (*ext2) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
40457           else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.cimg",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
40458         } else {
40459           if (*ext) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
40460           else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.cimg",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
40461         }
40462         if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
40463       } while (file);
40464       save(filetmp);
40465       cimg_snprintf(command,sizeof(command),"%s -c %s > \"%s\"",cimg::gzip_path(),filetmp,filename);
40466       cimg::system(command);
40467       file = std::fopen(filename,"rb");
40468       if (!file)
40469         throw CImgIOException(_cimg_instance
40470                               "save_gzip_external() : Failed to save file '%s' with external command 'gzip'.",
40471                               cimg_instance,
40472                               filename);
40473 
40474       else cimg::fclose(file);
40475       std::remove(filetmp);
40476       return *this;
40477     }
40478 
40479     //! Save image using GraphicsMagick's external binary.
40480     /**
40481        \param filename Filename, as a C-string.
40482        \param quality Image quality (expressed in percent), when the file format supports it.
40483        \note This method uses \c gm, an external executable binary provided by <a href="http://www.graphicsmagick.org">GraphicsMagick</a>.
40484        It must be installed for the method to succeed.
40485     **/
40486     const CImg<T>& save_graphicsmagick_external(const char *const filename, const unsigned int quality=100) const {
40487       if (!filename)
40488         throw CImgArgumentException(_cimg_instance
40489                                     "save_graphicsmagick_external() : Specified filename is (null).",
40490                                     cimg_instance);
40491       if (is_empty())
40492         throw CImgInstanceException(_cimg_instance
40493                                     "save_graphicsmagick_external() : Empty instance, for file '%s'.",
40494                                     cimg_instance,
40495                                     filename);
40496 
40497       char command[1024] = { 0 }, filetmp[512] = { 0 };
40498       std::FILE *file;
40499       do {
40500         cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),_spectrum==1?"pgm":"ppm");
40501         if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
40502       } while (file);
40503       save_pnm(filetmp);
40504       cimg_snprintf(command,sizeof(command),"%s convert -quality %u \"%s\" \"%s\"",cimg::graphicsmagick_path(),quality,filetmp,filename);
40505       cimg::system(command);
40506       file = std::fopen(filename,"rb");
40507       if (!file)
40508         throw CImgIOException(_cimg_instance
40509                               "save_graphicsmagick_external() : Failed to save file '%s' with external command 'gm'.",
40510                               cimg_instance,
40511                               filename);
40512 
40513       if (file) cimg::fclose(file);
40514       std::remove(filetmp);
40515       return *this;
40516     }
40517 
40518     //! Save image using ImageMagick's external binary.
40519     /**
40520        \param filename Filename, as a C-string.
40521        \param quality Image quality (expressed in percent), when the file format supports it.
40522        \note This method uses \c convert, an external executable binary provided by <a href="http://www.imagemagick.org">ImageMagick</a>.
40523        It must be installed for the method to succeed.
40524     **/
40525     const CImg<T>& save_imagemagick_external(const char *const filename, const unsigned int quality=100) const {
40526       if (!filename)
40527         throw CImgArgumentException(_cimg_instance
40528                                     "save_imagemagick_external() : Specified filename is (null).",
40529                                     cimg_instance);
40530       if (is_empty())
40531         throw CImgInstanceException(_cimg_instance
40532                                     "save_imagemagick_external() : Empty instance, for file '%s'.",
40533                                     cimg_instance,
40534                                     filename);
40535 
40536       char command[1024] = { 0 }, filetmp[512] = { 0 };
40537       std::FILE *file;
40538       do {
40539         cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),_spectrum==1?"pgm":"ppm");
40540         if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
40541       } while (file);
40542       save_pnm(filetmp);
40543       cimg_snprintf(command,sizeof(command),"%s -quality %u \"%s\" \"%s\"",cimg::imagemagick_path(),quality,filetmp,filename);
40544       cimg::system(command);
40545       file = std::fopen(filename,"rb");
40546       if (!file)
40547         throw CImgIOException(_cimg_instance
40548                               "save_imagemagick_external() : Failed to save file '%s' with external command 'convert'.",
40549                               cimg_instance,
40550                               filename);
40551 
40552       if (file) cimg::fclose(file);
40553       std::remove(filetmp);
40554       return *this;
40555     }
40556 
40557     //! Save image as a Dicom file.
40558     /**
40559        \param filename Filename, as a C-string.
40560        \note This method uses \c medcon, an external executable binary provided by <a href="http://xmedcon.sourceforge.net">(X)Medcon</a>.
40561        It must be installed for the method to succeed.
40562     **/
40563     const CImg<T>& save_medcon_external(const char *const filename) const {
40564       if (!filename)
40565         throw CImgArgumentException(_cimg_instance
40566                                     "save_medcon_external() : Specified filename is (null).",
40567                                     cimg_instance);
40568       if (is_empty())
40569         throw CImgInstanceException(_cimg_instance
40570                                     "save_medcon_external() : Empty instance, for file '%s'.",
40571                                     cimg_instance,
40572                                     filename);
40573 
40574       char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
40575       std::FILE *file;
40576       do {
40577         cimg_snprintf(filetmp,sizeof(filetmp),"%s.hdr",cimg::filenamerand());
40578         if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
40579       } while (file);
40580       save_analyze(filetmp);
40581       cimg_snprintf(command,sizeof(command),"%s -w -c dicom -o %s -f %s",cimg::medcon_path(),filename,filetmp);
40582       cimg::system(command);
40583       std::remove(filetmp);
40584       cimg::split_filename(filetmp,body);
40585       cimg_snprintf(filetmp,sizeof(filetmp),"%s.img",body);
40586       std::remove(filetmp);
40587 
40588       file = std::fopen(filename,"rb");
40589       if (!file) {
40590         cimg_snprintf(command,sizeof(command),"m000-%s",filename);
40591         file = std::fopen(command,"rb");
40592         if (!file) {
40593           cimg::fclose(cimg::fopen(filename,"r"));
40594           throw CImgIOException(_cimg_instance
40595                                 "save_medcon_external() : Failed to save file '%s' with external command 'medcon'.",
40596                                 cimg_instance,
40597                                 filename);
40598         }
40599       }
40600       cimg::fclose(file);
40601       std::rename(command,filename);
40602       return *this;
40603     }
40604 
40605     // Save image for non natively supported formats.
40606     /**
40607        \param filename Filename, as a C-string.
40608        \param quality Image quality (expressed in percent), when the file format supports it.
40609        \note
40610        - The filename extension tells about the desired file format.
40611        - This method tries to save the instance image as a file, using external tools from
40612        <a href="http://www.imagemagick.org">ImageMagick</a> or
40613        <a href="http://www.graphicsmagick.org">GraphicsMagick</a>. At least one of these tool must be installed for the method to succeed.
40614        - It is recommended to use the generic method save(const char*, int) const instead, as it can handle some file formats natively.
40615     **/
40616     const CImg<T>& save_other(const char *const filename, const unsigned int quality=100) const {
40617       if (!filename)
40618         throw CImgArgumentException(_cimg_instance
40619                                     "save_other() : Specified filename is (null).",
40620                                     cimg_instance);
40621       if (is_empty())
40622         throw CImgInstanceException(_cimg_instance
40623                                     "save_other() : Empty instance, for file '%s'.",
40624                                     cimg_instance,
40625                                     filename);
40626 
40627       const unsigned int omode = cimg::exception_mode();
40628       bool is_saved = true;
40629       cimg::exception_mode() = 0;
40630       try { save_magick(filename); }
40631       catch (CImgException&) {
40632         try { save_imagemagick_external(filename,quality); }
40633         catch (CImgException&) {
40634           try { save_graphicsmagick_external(filename,quality); }
40635           catch (CImgException&) {
40636             is_saved = false;
40637           }
40638         }
40639       }
40640       cimg::exception_mode() = omode;
40641       if (!is_saved)
40642         throw CImgIOException(_cimg_instance
40643                               "save_other() : Failed to save file '%s'. Format is not natively supported, and no external commands succeeded.",
40644                               cimg_instance,
40645                               filename);
40646       return *this;
40647     }
40648 
40649     // [internal] Return a 40x38 color logo of a 'danger' item.
40650     static CImg<T> _logo40x38() {
40651       static bool first_time = true;
40652       static CImg<T> res(40,38,1,3);
40653       if (first_time) {
40654         const unsigned char *ptrs = cimg::logo40x38;
40655         T *ptr1 = res.data(0,0,0,0), *ptr2 = res.data(0,0,0,1), *ptr3 = res.data(0,0,0,2);
40656         for (unsigned long off = 0; off<(unsigned long)res._width*res._height;) {
40657           const unsigned char n = *(ptrs++), r = *(ptrs++), g = *(ptrs++), b = *(ptrs++);
40658           for (unsigned int l = 0; l<n; ++off, ++l) { *(ptr1++) = (T)r; *(ptr2++) = (T)g; *(ptr3++) = (T)b; }
40659         }
40660         first_time = false;
40661       }
40662       return res;
40663     }
40664 
40665     //@}
40666   };
40667 
40668   /*
40669    #-----------------------------------------
40670    #
40671    #
40672    #
40673    # Definition of the CImgList<T> structure
40674    #
40675    #
40676    #
40677    #------------------------------------------
40678    */
40679   //! Represent a list of images CImg<T>.
40680   template<typename T>
40681   struct CImgList {
40682     unsigned int _width, _allocated_width;
40683     CImg<T> *_data;
40684 
40685     //! Simple iterator type, to loop through each image of a list.
40686     /**
40687        \note
40688        - The \c CImgList<T>::iterator type is defined as a <tt>CImg<T>*</tt>.
40689        - You may use it like this :
40690        \code
40691        CImgList<> list;   // Assuming this image list is not empty.
40692        for (CImgList<>::iterator it = list.begin(); it<list.end(); ++it) (*it).mirror('x');
40693        \endcode
40694        - Using the loop macro \c cimglist_for is another (more concise) alternative :
40695        \code
40696        cimglist_for(list,l) list[l].mirror('x');
40697        \endcode
40698     **/
40699     typedef CImg<T>* iterator;
40700 
40701     //! Simple const iterator type, to loop through each image of a \c const list instance.
40702     /**
40703        \note
40704        - The \c CImgList<T>::const_iterator type is defined to be a <tt>const CImg<T>*</tt>.
40705        - Similar to CImgList<T>::iterator, but for constant list instances.
40706     **/
40707     typedef const CImg<T>* const_iterator;
40708 
40709     //! Pixel value type.
40710     /**
40711        Refer to the pixels value type of the images in the list.
40712        \note
40713        - The \c CImgList<T>::value_type type of a \c CImgList<T> is defined to be a \c T. It is then similar to CImg<T>::value_type.
40714        - \c CImgList<T>::value_type is actually not used in %CImg methods. It has been mainly defined for
40715          compatibility with STL naming conventions.
40716     **/
40717     typedef T value_type;
40718 
40719     // Define common T-dependant types.
40720     typedef typename cimg::superset<T,bool>::type Tbool;
40721     typedef typename cimg::superset<T,unsigned char>::type Tuchar;
40722     typedef typename cimg::superset<T,char>::type Tchar;
40723     typedef typename cimg::superset<T,unsigned short>::type Tushort;
40724     typedef typename cimg::superset<T,short>::type Tshort;
40725     typedef typename cimg::superset<T,unsigned int>::type Tuint;
40726     typedef typename cimg::superset<T,int>::type Tint;
40727     typedef typename cimg::superset<T,unsigned long>::type Tulong;
40728     typedef typename cimg::superset<T,long>::type Tlong;
40729     typedef typename cimg::superset<T,float>::type Tfloat;
40730     typedef typename cimg::superset<T,double>::type Tdouble;
40731     typedef typename cimg::last<T,bool>::type boolT;
40732     typedef typename cimg::last<T,unsigned char>::type ucharT;
40733     typedef typename cimg::last<T,char>::type charT;
40734     typedef typename cimg::last<T,unsigned short>::type ushortT;
40735     typedef typename cimg::last<T,short>::type shortT;
40736     typedef typename cimg::last<T,unsigned int>::type uintT;
40737     typedef typename cimg::last<T,int>::type intT;
40738     typedef typename cimg::last<T,unsigned long>::type ulongT;
40739     typedef typename cimg::last<T,long>::type longT;
40740     typedef typename cimg::last<T,float>::type floatT;
40741     typedef typename cimg::last<T,double>::type doubleT;
40742 
40743     //@}
40744     //---------------------------
40745     //
40746     //! \name Plugins
40747     //@{
40748     //---------------------------
40749 #ifdef cimglist_plugin
40750 #include cimglist_plugin
40751 #endif
40752 #ifdef cimglist_plugin1
40753 #include cimglist_plugin1
40754 #endif
40755 #ifdef cimglist_plugin2
40756 #include cimglist_plugin2
40757 #endif
40758 #ifdef cimglist_plugin3
40759 #include cimglist_plugin3
40760 #endif
40761 #ifdef cimglist_plugin4
40762 #include cimglist_plugin4
40763 #endif
40764 #ifdef cimglist_plugin5
40765 #include cimglist_plugin5
40766 #endif
40767 #ifdef cimglist_plugin6
40768 #include cimglist_plugin6
40769 #endif
40770 #ifdef cimglist_plugin7
40771 #include cimglist_plugin7
40772 #endif
40773 #ifdef cimglist_plugin8
40774 #include cimglist_plugin8
40775 #endif
40776 
40777     //@}
40778     //--------------------------------------------------------
40779     //
40780     //! \name Constructors / Destructor / Instance Management
40781     //@{
40782     //--------------------------------------------------------
40783 
40784     //! Destructor.
40785     /**
40786        Destroy current list instance.
40787        \note
40788        - Any allocated buffer is deallocated.
40789        - Destroying an empty list does nothing actually.
40790      **/
40791     ~CImgList() {
40792       delete[] _data;
40793     }
40794 
40795     //! Default constructor.
40796     /**
40797        Construct a new empty list instance.
40798        \note
40799        - An empty list has no pixel data and its dimension width() is set to \c 0, as well as its image buffer pointer data().
40800        - An empty list may be reassigned afterwards, with the family of the assign() methods. In all cases, the type of pixels stays \c T.
40801      **/
40802     CImgList():
40803       _width(0),_allocated_width(0),_data(0) {}
40804 
40805     //! Construct list containing empty images.
40806     /**
40807        \param n Number of empty images.
40808        \note Useful when you know by advance the number of images you want to manage, as
40809        it will allocate the right amount of memory for the list, without needs for reallocation
40810        (that may occur when starting from an empty list and inserting several images in it).
40811     **/
40812     explicit CImgList(const unsigned int n):_width(n) {
40813       if (n) _data = new CImg<T>[_allocated_width = cimg::max(16UL,cimg::nearest_pow2(n))];
40814       else { _allocated_width = 0; _data = 0; }
40815     }
40816 
40817     //! Construct list containing images of specified size.
40818     /**
40819        \param n Number of images.
40820        \param width Width of images.
40821        \param height Height of images.
40822        \param depth Depth of images.
40823        \param spectrum Number of channels of images.
40824        \note Pixel values are not initialized and may probably contain garbage.
40825     **/
40826     CImgList(const unsigned int n, const unsigned int width, const unsigned int height=1,
40827              const unsigned int depth=1, const unsigned int spectrum=1):
40828       _width(0),_allocated_width(0),_data(0) {
40829       assign(n);
40830       cimglist_apply(*this,assign)(width,height,depth,spectrum);
40831     }
40832 
40833     //! Construct list containing images of specified size, and initialize pixel values.
40834     /**
40835        \param n Number of images.
40836        \param width Width of images.
40837        \param height Height of images.
40838        \param depth Depth of images.
40839        \param spectrum Number of channels of images.
40840        \param val Initialization value for images pixels.
40841     **/
40842     CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
40843              const unsigned int depth, const unsigned int spectrum, const T val):
40844       _width(0),_allocated_width(0),_data(0) {
40845       assign(n);
40846       cimglist_apply(*this,assign)(width,height,depth,spectrum,val);
40847     }
40848 
40849     //! Construct list containing images of specified size, and initialize pixel values from a sequence of integers.
40850     /**
40851        \param n Number of images.
40852        \param width Width of images.
40853        \param height Height of images.
40854        \param depth Depth of images.
40855        \param spectrum Number of channels of images.
40856        \param val0 First value of the initializing integers sequence.
40857        \param val1 Second value of the initializing integers sequence.
40858        \warning You must specify at least <tt>width*height*depth*spectrum</tt> values in your argument list, or you will probably segfault.
40859     **/
40860     CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
40861              const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...):
40862       _width(0),_allocated_width(0),_data(0) {
40863 #define _CImgList_stdarg(t) { \
40864         assign(n,width,height,depth,spectrum); \
40865         const unsigned long siz = (unsigned long)width*height*depth*spectrum, nsiz = siz*n; \
40866         T *ptrd = _data->_data; \
40867         va_list ap; \
40868         va_start(ap,val1); \
40869         for (unsigned long l = 0, s = 0, i = 0; i<nsiz; ++i) { \
40870           *(ptrd++) = (T)(i==0?val0:(i==1?val1:va_arg(ap,t))); \
40871           if ((++s)==siz) { ptrd = _data[++l]._data; s = 0; } \
40872         } \
40873         va_end(ap); \
40874       }
40875       _CImgList_stdarg(int);
40876     }
40877 
40878     //! Construct list containing images of specified size, and initialize pixel values from a sequence of doubles.
40879     /**
40880        \param n Number of images.
40881        \param width Width of images.
40882        \param height Height of images.
40883        \param depth Depth of images.
40884        \param spectrum Number of channels of images.
40885        \param val0 First value of the initializing doubles sequence.
40886        \param val1 Second value of the initializing doubles sequence.
40887        \warning You must specify at least <tt>width*height*depth*spectrum</tt> values in your argument list, or you will probably segfault.
40888     **/
40889     CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
40890              const unsigned int depth, const unsigned int spectrum, const double val0, const double val1, ...):
40891       _width(0),_allocated_width(0),_data(0) {
40892       _CImgList_stdarg(double);
40893     }
40894 
40895     //! Construct list containing copies of an input image.
40896     /**
40897        \param n Number of images.
40898        \param img Input image to copy in the constructed list.
40899        \param is_shared Tells if the elements of the list are shared or non-shared copies of \c img.
40900     **/
40901     template<typename t>
40902     CImgList(const unsigned int n, const CImg<t>& img, const bool is_shared=false):
40903       _width(0),_allocated_width(0),_data(0) {
40904       assign(n);
40905       cimglist_apply(*this,assign)(img,is_shared);
40906     }
40907 
40908     //! Construct list from one image.
40909     /**
40910        \param img Input image to copy in the constructed list.
40911        \param is_shared Tells if the element of the list is a shared or non-shared copy of \c img.
40912      **/
40913     template<typename t>
40914     explicit CImgList(const CImg<t>& img, const bool is_shared=false):
40915       _width(0),_allocated_width(0),_data(0) {
40916       assign(1);
40917       _data[0].assign(img,is_shared);
40918     }
40919 
40920     //! Construct list from two images.
40921     /**
40922        \param img1 First input image to copy in the constructed list.
40923        \param img2 Second input image to copy in the constructed list.
40924        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
40925      **/
40926     template<typename t1, typename t2>
40927     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const bool is_shared=false):
40928       _width(0),_allocated_width(0),_data(0) {
40929       assign(2);
40930       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared);
40931     }
40932 
40933     //! Construct list from three images.
40934     /**
40935        \param img1 First input image to copy in the constructed list.
40936        \param img2 Second input image to copy in the constructed list.
40937        \param img3 Third input image to copy in the constructed list.
40938        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
40939     **/
40940     template<typename t1, typename t2, typename t3>
40941     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool is_shared=false):
40942       _width(0),_allocated_width(0),_data(0) {
40943       assign(3);
40944       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
40945     }
40946 
40947     //! Construct list from four images.
40948     /**
40949        \param img1 First input image to copy in the constructed list.
40950        \param img2 Second input image to copy in the constructed list.
40951        \param img3 Third input image to copy in the constructed list.
40952        \param img4 Fourth input image to copy in the constructed list.
40953        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
40954     **/
40955     template<typename t1, typename t2, typename t3, typename t4>
40956     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4, const bool is_shared=false):
40957       _width(0),_allocated_width(0),_data(0) {
40958       assign(4);
40959       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared);
40960     }
40961 
40962     //! Construct list from five images.
40963     /**
40964        \param img1 First input image to copy in the constructed list.
40965        \param img2 Second input image to copy in the constructed list.
40966        \param img3 Third input image to copy in the constructed list.
40967        \param img4 Fourth input image to copy in the constructed list.
40968        \param img5 Fifth input image to copy in the constructed list.
40969        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
40970     **/
40971     template<typename t1, typename t2, typename t3, typename t4, typename t5>
40972     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
40973              const CImg<t5>& img5, const bool is_shared=false):
40974       _width(0),_allocated_width(0),_data(0) {
40975       assign(5);
40976       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared);
40977       _data[4].assign(img5,is_shared);
40978     }
40979 
40980     //! Construct list from six images.
40981     /**
40982        \param img1 First input image to copy in the constructed list.
40983        \param img2 Second input image to copy in the constructed list.
40984        \param img3 Third input image to copy in the constructed list.
40985        \param img4 Fourth input image to copy in the constructed list.
40986        \param img5 Fifth input image to copy in the constructed list.
40987        \param img6 Sixth input image to copy in the constructed list.
40988        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
40989     **/
40990     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
40991     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
40992              const CImg<t5>& img5, const CImg<t6>& img6, const bool is_shared=false):
40993       _width(0),_allocated_width(0),_data(0) {
40994       assign(6);
40995       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared);
40996       _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
40997     }
40998 
40999     //! Construct list from seven images.
41000     /**
41001        \param img1 First input image to copy in the constructed list.
41002        \param img2 Second input image to copy in the constructed list.
41003        \param img3 Third input image to copy in the constructed list.
41004        \param img4 Fourth input image to copy in the constructed list.
41005        \param img5 Fifth input image to copy in the constructed list.
41006        \param img6 Sixth input image to copy in the constructed list.
41007        \param img7 Seventh input image to copy in the constructed list.
41008        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
41009     **/
41010     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
41011     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
41012              const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool is_shared=false):
41013       _width(0),_allocated_width(0),_data(0) {
41014       assign(7);
41015       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared);
41016       _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); _data[6].assign(img7,is_shared);
41017     }
41018 
41019     //! Construct list from eight images.
41020     /**
41021        \param img1 First input image to copy in the constructed list.
41022        \param img2 Second input image to copy in the constructed list.
41023        \param img3 Third input image to copy in the constructed list.
41024        \param img4 Fourth input image to copy in the constructed list.
41025        \param img5 Fifth input image to copy in the constructed list.
41026        \param img6 Sixth input image to copy in the constructed list.
41027        \param img7 Seventh input image to copy in the constructed list.
41028        \param img8 Eighth input image to copy in the constructed list.
41029        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
41030     **/
41031     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
41032     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
41033              const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8, const bool is_shared=false):
41034       _width(0),_allocated_width(0),_data(0) {
41035       assign(8);
41036       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared);
41037       _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared);
41038     }
41039 
41040     //! Construct list copy.
41041     /**
41042        \param list Input list to copy.
41043        \note The shared state of each element of the constructed list is kept the same as in \c list.
41044     **/
41045     template<typename t>
41046     CImgList(const CImgList<t>& list):_width(0),_allocated_width(0),_data(0) {
41047       assign(list._width);
41048       cimglist_for(*this,l) _data[l].assign(list[l],false);
41049     }
41050 
41051     //! Construct list copy \specialization.
41052     CImgList(const CImgList<T>& list):_width(0),_allocated_width(0),_data(0) {
41053       assign(list._width);
41054       cimglist_for(*this,l) _data[l].assign(list[l],list[l]._is_shared);
41055     }
41056 
41057     //! Construct list copy, and force the shared state of the list elements.
41058     /**
41059        \param list Input list to copy.
41060        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
41061     **/
41062     template<typename t>
41063     CImgList(const CImgList<t>& list, const bool is_shared):_width(0),_allocated_width(0),_data(0) {
41064       assign(list._width);
41065       cimglist_for(*this,l) _data[l].assign(list[l],is_shared);
41066     }
41067 
41068     //! Construct list by reading the content of a file.
41069     /**
41070        \param filename Filename, as a C-string.
41071     **/
41072     explicit CImgList(const char *const filename):_width(0),_allocated_width(0),_data(0) {
41073       assign(filename);
41074     }
41075 
41076     //! Construct list from the content of a display window.
41077     /**
41078        \param disp Display window to get content from.
41079        \note Constructed list contains a single image only.
41080     **/
41081     explicit CImgList(const CImgDisplay& disp):_width(0),_allocated_width(0),_data(0) {
41082       assign(disp);
41083     }
41084 
41085     //! Return a list with elements being shared copies of images in the list instance.
41086     /**
41087       \note <tt>list2 = list1.get_shared()</tt> is equivalent to <tt>list2.assign(list1,true)</tt>.
41088     **/
41089     CImgList<T> get_shared() {
41090       CImgList<T> res(_width);
41091       cimglist_for(*this,l) res[l].assign(_data[l],true);
41092       return res;
41093     }
41094 
41095     //! Return a list with elements being shared copies of images in the list instance \const.
41096     const CImgList<T> get_shared() const {
41097       CImgList<T> res(_width);
41098       cimglist_for(*this,l) res[l].assign(_data[l],true);
41099       return res;
41100     }
41101 
41102     //! Destructor \inplace.
41103     /**
41104        \see CImgList().
41105     **/
41106     CImgList<T>& assign() {
41107       delete[] _data;
41108       _width = _allocated_width = 0;
41109       _data = 0;
41110       return *this;
41111     }
41112 
41113     //! Destructor \inplace.
41114     /**
41115        Equivalent to assign().
41116        \note Only here for compatibility with STL naming conventions.
41117     **/
41118     CImgList<T>& clear() {
41119       return assign();
41120     }
41121 
41122     //! Construct list containing empty images \inplace.
41123     /**
41124        \see CImgList(unsigned int).
41125     **/
41126     CImgList<T>& assign(const unsigned int n) {
41127       if (!n) return assign();
41128       if (_allocated_width<n || _allocated_width>(n<<2)) {
41129         delete[] _data;
41130         _data = new CImg<T>[_allocated_width=cimg::max(16UL,cimg::nearest_pow2(n))];
41131       }
41132       _width = n;
41133       return *this;
41134     }
41135 
41136     //! Construct list containing images of specified size \inplace.
41137     /**
41138        \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int).
41139     **/
41140     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height=1,
41141                         const unsigned int depth=1, const unsigned int spectrum=1) {
41142       assign(n);
41143       cimglist_apply(*this,assign)(width,height,depth,spectrum);
41144       return *this;
41145     }
41146 
41147     //! Construct list containing images of specified size, and initialize pixel values \inplace.
41148     /**
41149        \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const T).
41150     **/
41151     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
41152                         const unsigned int depth, const unsigned int spectrum, const T val) {
41153       assign(n);
41154       cimglist_apply(*this,assign)(width,height,depth,spectrum,val);
41155       return *this;
41156     }
41157 
41158     //! Construct list containing images of specified size, and initialize pixel values from a sequence of integers \inplace.
41159     /**
41160        \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const int, const int, ...).
41161     **/
41162     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
41163                         const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...) {
41164       _CImgList_stdarg(int);
41165       return *this;
41166     }
41167 
41168     //! Construct list containing images of specified size, and initialize pixel values from a sequence of doubles \inplace.
41169     /**
41170        \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const double, const double, ...).
41171     **/
41172     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
41173                         const unsigned int depth, const unsigned int spectrum, const double val0, const double val1, ...) {
41174       _CImgList_stdarg(double);
41175       return *this;
41176     }
41177 
41178     //! Construct list containing copies of an input image \inplace.
41179     /**
41180        \see CImgList(unsigned int, const CImg<t>&, bool).
41181     **/
41182     template<typename t>
41183     CImgList<T>& assign(const unsigned int n, const CImg<t>& img, const bool is_shared=false) {
41184       assign(n);
41185       cimglist_apply(*this,assign)(img,is_shared);
41186       return *this;
41187     }
41188 
41189     //! Construct list from one image \inplace.
41190     /**
41191        \see CImgList(const CImg<t>&, bool).
41192     **/
41193     template<typename t>
41194     CImgList<T>& assign(const CImg<t>& img, const bool is_shared=false) {
41195       assign(1);
41196       _data[0].assign(img,is_shared);
41197       return *this;
41198     }
41199 
41200     //! Construct list from two images \inplace.
41201     /**
41202        \see CImgList(const CImg<t>&, const CImg<t>&, bool).
41203     **/
41204     template<typename t1, typename t2>
41205     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const bool is_shared=false) {
41206       assign(2);
41207       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared);
41208       return *this;
41209     }
41210 
41211     //! Construct list from three images \inplace.
41212     /**
41213        \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
41214     **/
41215     template<typename t1, typename t2, typename t3>
41216     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool is_shared=false) {
41217       assign(3);
41218       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
41219       return *this;
41220     }
41221 
41222     //! Construct list from four images \inplace.
41223     /**
41224        \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
41225     **/
41226     template<typename t1, typename t2, typename t3, typename t4>
41227     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
41228                         const bool is_shared=false) {
41229       assign(4);
41230       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared);
41231       return *this;
41232     }
41233 
41234     //! Construct list from five images \inplace.
41235     /**
41236        \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
41237     **/
41238     template<typename t1, typename t2, typename t3, typename t4, typename t5>
41239     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
41240                         const CImg<t5>& img5, const bool is_shared=false) {
41241       assign(5);
41242       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared);
41243       _data[4].assign(img5,is_shared);
41244       return *this;
41245     }
41246 
41247     //! Construct list from six images \inplace.
41248     /**
41249        \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
41250     **/
41251     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
41252     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
41253                         const CImg<t5>& img5, const CImg<t6>& img6, const bool is_shared=false) {
41254       assign(6);
41255       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared);
41256       _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
41257       return *this;
41258     }
41259 
41260     //! Construct list from seven images \inplace.
41261     /**
41262        \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
41263     **/
41264     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
41265     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
41266                         const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool is_shared=false) {
41267       assign(7);
41268       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared);
41269       _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); _data[6].assign(img7,is_shared);
41270       return *this;
41271     }
41272 
41273     //! Construct list from eight images \inplace.
41274     /**
41275        \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
41276     **/
41277     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
41278     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
41279                         const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8,
41280                         const bool is_shared=false) {
41281       assign(8);
41282       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared);
41283       _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared);
41284       return *this;
41285     }
41286 
41287     //! Construct list as a copy of an existing list and force the shared state of the list elements \inplace.
41288     /**
41289       \see CImgList(const CImgList<t>&, bool is_shared).
41290     **/
41291     template<typename t>
41292     CImgList<T>& assign(const CImgList<t>& list, const bool is_shared=false) {
41293       cimg::unused(is_shared);
41294       assign(list._width);
41295       cimglist_for(*this,l) _data[l].assign(list[l],false);
41296       return *this;
41297     }
41298 
41299     //! Construct list as a copy of an existing list and force the shared state of the list elements \inplace \specialization.
41300     CImgList<T>& assign(const CImgList<T>& list, const bool is_shared=false) {
41301       if (this==&list) return *this;
41302       CImgList<T> res(list._width);
41303       cimglist_for(res,l) res[l].assign(list[l],is_shared);
41304       return res.move_to(*this);
41305     }
41306 
41307     //! Construct list by reading the content of a file \inplace.
41308     /**
41309       \see CImgList(const char *const).
41310     **/
41311     CImgList<T>& assign(const char *const filename) {
41312       return load(filename);
41313     }
41314 
41315     //! Construct list from the content of a display window \inplace.
41316     /**
41317       \see CImgList(const CImgDisplay&).
41318     **/
41319     CImgList<T>& assign(const CImgDisplay &disp) {
41320       return assign(CImg<T>(disp));
41321     }
41322 
41323     //! Transfer the content of the list instance to another list.
41324     /**
41325        \param list Destination list.
41326        \note When returning, the current list instance is empty and the initial content of \c list is destroyed.
41327     **/
41328     template<typename t>
41329     CImgList<t>& move_to(CImgList<t>& list) {
41330       list.assign(_width);
41331       bool is_one_shared_element = false;
41332       cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared;
41333       if (is_one_shared_element) cimglist_for(*this,l) list[l].assign(_data[l]);
41334       else cimglist_for(*this,l) _data[l].move_to(list[l]);
41335       assign();
41336       return list;
41337     }
41338 
41339     //! Transfer the content of the list instance at a specified position in another list.
41340     /**
41341        \param list Destination list.
41342        \param pos Index of the insertion in the list.
41343        \note When returning, the list instance is empty and the initial content of \c list is preserved
41344        (only images indexes may be modified).
41345      **/
41346     template<typename t>
41347     CImgList<t>& move_to(CImgList<t>& list, const unsigned int pos) {
41348       if (is_empty()) return list;
41349       const unsigned int npos = pos>list._width?list._width:pos;
41350       list.insert(_width,npos);
41351       bool is_one_shared_element = false;
41352       cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared;
41353       if (is_one_shared_element) cimglist_for(*this,l) list[npos+l].assign(_data[l]);
41354       else cimglist_for(*this,l) _data[l].move_to(list[npos+l]);
41355       assign();
41356       return list;
41357     }
41358 
41359     //! Swap all fields between two list instances.
41360     /**
41361        \param list List to swap fields with.
41362        \note Can be used to exchange the content of two lists in a fast way.
41363     **/
41364     CImgList<T>& swap(CImgList<T>& list) {
41365       cimg::swap(_width,list._width);
41366       cimg::swap(_allocated_width,list._allocated_width);
41367       cimg::swap(_data,list._data);
41368       return list;
41369     }
41370 
41371     //! Return a reference to an empty list.
41372     /**
41373       \note Can be used to define default values in a function taking a CImgList<T> as an argument.
41374       \code
41375       void f(const CImgList<char>& list=CImgList<char>::empty());
41376       \endcode
41377     **/
41378     static CImgList<T>& empty() {
41379       static CImgList<T> _empty;
41380       return _empty.assign();
41381     }
41382 
41383     //@}
41384     //------------------------------------------
41385     //
41386     //! \name Overloaded Operators
41387     //@{
41388     //------------------------------------------
41389 
41390     //! Return a reference to one image element of the list.
41391     /**
41392        \param pos Indice of the image element.
41393     **/
41394     CImg<T>& operator()(const unsigned int pos) {
41395 #if cimg_verbosity>=3
41396       if (pos>=_width) {
41397         cimg::warn(_cimglist_instance
41398                    "operator() : Invalid image request, at position [%u].",
41399                    cimglist_instance,
41400                    pos);
41401         return *_data;
41402       }
41403 #endif
41404       return _data[pos];
41405     }
41406 
41407     //! Return a reference to one image of the list.
41408     /**
41409        \param pos Indice of the image element.
41410     **/
41411     const CImg<T>& operator()(const unsigned int pos) const {
41412       return const_cast<CImgList<T>*>(this)->operator()(pos);
41413     }
41414 
41415     //! Return a reference to one pixel value of one image of the list.
41416     /**
41417        \param pos Indice of the image element.
41418        \param x X-coordinate of the pixel value.
41419        \param y Y-coordinate of the pixel value.
41420        \param z Z-coordinate of the pixel value.
41421        \param c C-coordinate of the pixel value.
41422        \note <tt>list(n,x,y,z,c)</tt> is equivalent to <tt>list[n](x,y,z,c)</tt>.
41423     **/
41424     T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0,
41425                   const unsigned int z=0, const unsigned int c=0) {
41426       return (*this)[pos](x,y,z,c);
41427     }
41428 
41429     //! Return a reference to one pixel value of one image of the list \const.
41430     const T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0,
41431                         const unsigned int z=0, const unsigned int c=0) const {
41432       return (*this)[pos](x,y,z,c);
41433     }
41434 
41435     //! Return pointer to the first image of the list.
41436     /**
41437        \note Images in a list are stored as a buffer of \c CImg<T>.
41438     **/
41439     operator CImg<T>*() {
41440       return _data;
41441     }
41442 
41443     //! Return pointer to the first image of the list \const.
41444     operator const CImg<T>*() const {
41445       return _data;
41446     }
41447 
41448     //! Construct list from one image \inplace.
41449     /**
41450         \param img Input image to copy in the constructed list.
41451         \note <tt>list = img;</tt> is equivalent to <tt>list.assign(img);</tt>.
41452     **/
41453     template<typename t>
41454     CImgList<T>& operator=(const CImg<t>& img) {
41455       return assign(img);
41456     }
41457 
41458     //! Construct list from another list.
41459     /**
41460        \param list Input list to copy.
41461        \note <tt>list1 = list2</tt> is equivalent to <tt>list1.assign(list2);</tt>.
41462     **/
41463     template<typename t>
41464     CImgList<T>& operator=(const CImgList<t>& list) {
41465       return assign(list);
41466     }
41467 
41468     //! Construct list from another list \specialization.
41469     CImgList<T>& operator=(const CImgList<T>& list) {
41470       return assign(list);
41471     }
41472 
41473     //! Construct list by reading the content of a file \inplace.
41474     /**
41475        \see CImgList(const char *const).
41476     **/
41477     CImgList<T>& operator=(const char *const filename) {
41478       return assign(filename);
41479     }
41480 
41481     //! Construct list from the content of a display window \inplace.
41482     /**
41483         \see CImgList(const CImgDisplay&).
41484     **/
41485     CImgList<T>& operator=(const CImgDisplay& disp) {
41486       return assign(disp);
41487     }
41488 
41489     //! Return a non-shared copy of a list.
41490     /**
41491         \note <tt>+list</tt> is equivalent to <tt>CImgList<T>(list,false)</tt>. It forces the copy to have non-shared elements.
41492     **/
41493     CImgList<T> operator+() const {
41494       return CImgList<T>(*this,false);
41495     }
41496 
41497     //! Return a copy of the list instance, where image \c img has been inserted at the end.
41498     /**
41499        \param img Image inserted at the end of the instance copy.
41500        \note Define a convenient way to create temporary lists of images, as in the following code :
41501        \code
41502        (img1,img2,img3,img4).display("My four images");
41503        \endcode
41504     **/
41505     template<typename t>
41506     CImgList<T>& operator,(const CImg<t>& img) {
41507       return insert(img);
41508     }
41509 
41510     //! Return a copy of the list instance, where all elements of input list \c list have been inserted at the end.
41511     /**
41512        \param list List inserted at the end of the instance copy.
41513     **/
41514     template<typename t>
41515     CImgList<T>& operator,(const CImgList<t>& list) {
41516       return insert(list);
41517     }
41518 
41519     //! Return image corresponding to the appending of all images of the instance list along specified axis.
41520     /**
41521       \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
41522       \note <tt>list>'x'</tt> is equivalent to <tt>list.get_append('x')</tt>.
41523     **/
41524     CImg<T> operator>(const char axis) const {
41525       return get_append(axis,0);
41526     }
41527 
41528     //! Return list corresponding to the splitting of all images of the instance list along specified axis.
41529     /**
41530       \param axis Axis used for image splitting.
41531       \note <tt>list<'x'</tt> is equivalent to <tt>list.get_split('x')</tt>.
41532     **/
41533     CImgList<T> operator<(const char axis) const {
41534       return get_split(axis);
41535     }
41536 
41537     //@}
41538     //-------------------------------------
41539     //
41540     //! \name Instance Characteristics
41541     //@{
41542     //-------------------------------------
41543 
41544     //! Return the type of image pixel values as a C string.
41545     /**
41546        Return a \c char* string containing the usual type name of the image pixel values
41547        (i.e. a stringified version of the template parameter \c T).
41548        \note
41549        - The returned string may contain spaces (as in \c "unsigned char").
41550        - If the pixel type \c T does not correspond to a registered type, the string <tt>"unknown"</tt> is returned.
41551     **/
41552     static const char* pixel_type() {
41553       return cimg::type<T>::string();
41554     }
41555 
41556     //! Return the size of the list, i.e. the number of images contained in it.
41557     /**
41558       \note Similar to size() but returns result as a (signed) integer.
41559     **/
41560     int width() const {
41561       return (int)_width;
41562     }
41563 
41564     //! Return the size of the list, i.e. the number of images contained in it.
41565     /**
41566       \note Similar to width() but returns result as an unsigned integer.
41567     **/
41568     unsigned int size() const {
41569       return _width;
41570     }
41571 
41572     //! Return pointer to the first image of the list.
41573     /**
41574        \note Images in a list are stored as a buffer of \c CImg<T>.
41575     **/
41576     CImg<T> *data() {
41577       return _data;
41578     }
41579 
41580     //! Return pointer to the first image of the list \const.
41581     const CImg<T> *data() const {
41582       return _data;
41583     }
41584 
41585     //! Return pointer to the pos-th image of the list.
41586     /**
41587        \param pos Indice of the image element to access.
41588        \note <tt>list.data(n);</tt> is equivalent to <tt>list.data + n;</tt>.
41589     **/
41590 #if cimg_verbosity>=3
41591     CImg<T> *data(const unsigned int pos) {
41592       if (pos>=size()) {
41593         cimg::warn(_cimglist_instance
41594                    "data() : Invalid pointer request, at position [%u].",
41595                    cimglist_instance,
41596                    pos);
41597         return _data;
41598       }
41599       return _data + pos;
41600     }
41601 
41602     const CImg<T> *data(const unsigned int l) const {
41603       return const_cast<CImgList<T>*>(this)->data(l);
41604     }
41605 #else
41606     CImg<T> *data(const unsigned int l) {
41607       return _data + l;
41608     }
41609 
41610     //! Return pointer to the pos-th image of the list \const.
41611     const CImg<T> *data(const unsigned int l) const {
41612       return _data + l;
41613     }
41614 #endif
41615 
41616     //! Return iterator to the first image of the list.
41617     /**
41618     **/
41619     iterator begin() {
41620       return _data;
41621     }
41622 
41623     //! Return iterator to the first image of the list \const.
41624     const_iterator begin() const {
41625       return _data;
41626     }
41627 
41628     //! Return iterator to one position after the last image of the list.
41629     /**
41630     **/
41631     iterator end() {
41632       return _data + _width;
41633     }
41634 
41635     //! Return iterator to one position after the last image of the list \const.
41636     const_iterator end() const {
41637       return _data + _width;
41638     }
41639 
41640     //! Return reference to the first image of the list.
41641     /**
41642     **/
41643     CImg<T>& front() {
41644       return *_data;
41645     }
41646 
41647     //! Return reference to the first image of the list \const.
41648     const CImg<T>& front() const {
41649       return *_data;
41650     }
41651 
41652     //! Return a reference to the last image of the list.
41653     /**
41654     **/
41655     const CImg<T>& back() const {
41656       return *(_data + _width - 1);
41657     }
41658 
41659     //! Return a reference to the last image of the list \const.
41660     CImg<T>& back() {
41661       return *(_data + _width - 1);
41662     }
41663 
41664     //! Return pos-th image of the list.
41665     /**
41666        \param pos Indice of the image element to access.
41667     **/
41668     CImg<T>& at(const int pos) {
41669       if (is_empty())
41670         throw CImgInstanceException(_cimglist_instance
41671                                     "at() : Empty instance.",
41672                                     cimglist_instance);
41673 
41674       return _data[pos<0?0:pos>=(int)_width?(int)_width-1:pos];
41675     }
41676 
41677     //! Access to pixel value with Dirichlet boundary conditions.
41678     /**
41679        \param pos Indice of the image element to access.
41680        \param x X-coordinate of the pixel value.
41681        \param y Y-coordinate of the pixel value.
41682        \param z Z-coordinate of the pixel value.
41683        \param c C-coordinate of the pixel value.
41684        \param out_value Default value returned if \c offset is outside image bounds.
41685        \note <tt>list.atNXYZC(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZC(x,y,z,c);</tt>.
41686     **/
41687     T& atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T out_value) {
41688       return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atXYZC(x,y,z,c,out_value);
41689     }
41690 
41691     //! Access to pixel value with Dirichlet boundary conditions \const.
41692     T atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T out_value) const {
41693       return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atXYZC(x,y,z,c,out_value);
41694     }
41695 
41696     //! Access to pixel value with Neumann boundary conditions.
41697     /**
41698        \param pos Indice of the image element to access.
41699        \param x X-coordinate of the pixel value.
41700        \param y Y-coordinate of the pixel value.
41701        \param z Z-coordinate of the pixel value.
41702        \param c C-coordinate of the pixel value.
41703        \note <tt>list.atNXYZC(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZC(x,y,z,c);</tt>.
41704     **/
41705     T& atNXYZC(const int pos, const int x, const int y, const int z, const int c) {
41706       if (is_empty())
41707         throw CImgInstanceException(_cimglist_instance
41708                                     "atNXYZC() : Empty instance.",
41709                                     cimglist_instance);
41710 
41711       return _atNXYZC(pos,x,y,z,c);
41712     }
41713 
41714     //! Access to pixel value with Neumann boundary conditions \const.
41715     T atNXYZC(const int pos, const int x, const int y, const int z, const int c) const {
41716       if (is_empty())
41717         throw CImgInstanceException(_cimglist_instance
41718                                     "atNXYZC() : Empty instance.",
41719                                     cimglist_instance);
41720 
41721       return _atNXYZC(pos,x,y,z,c);
41722     }
41723 
41724     T& _atNXYZC(const int pos, const int x, const int y, const int z, const int c) {
41725       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZC(x,y,z,c);
41726     }
41727 
41728     T _atNXYZC(const int pos, const int x, const int y, const int z, const int c) const {
41729       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZC(x,y,z,c);
41730     }
41731 
41732     //! Access to pixel value with Dirichlet boundary conditions for the three first coordinates (\c pos, \c x,\c y,\c z).
41733     /**
41734        \param pos Indice of the image element to access.
41735        \param x X-coordinate of the pixel value.
41736        \param y Y-coordinate of the pixel value.
41737        \param z Z-coordinate of the pixel value.
41738        \param c C-coordinate of the pixel value.
41739        \param out_value Default value returned if \c offset is outside image bounds.
41740        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
41741     **/
41742     T& atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T out_value) {
41743       return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atXYZ(x,y,z,c,out_value);
41744     }
41745 
41746     //! Access to pixel value with Dirichlet boundary conditions for the three first coordinates (\c pos, \c x,\c y,\c z) \const.
41747     T atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T out_value) const {
41748       return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atXYZ(x,y,z,c,out_value);
41749     }
41750 
41751     //! Access to pixel value with Neumann boundary conditions for the four first coordinates (\c pos, \c x,\c y,\c z).
41752     /**
41753        \param pos Indice of the image element to access.
41754        \param x X-coordinate of the pixel value.
41755        \param y Y-coordinate of the pixel value.
41756        \param z Z-coordinate of the pixel value.
41757        \param c C-coordinate of the pixel value.
41758        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
41759     **/
41760    T& atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) {
41761       if (is_empty())
41762         throw CImgInstanceException(_cimglist_instance
41763                                     "atNXYZ() : Empty instance.",
41764                                     cimglist_instance);
41765 
41766       return _atNXYZ(pos,x,y,z,c);
41767     }
41768 
41769     //! Access to pixel value with Neumann boundary conditions for the four first coordinates (\c pos, \c x,\c y,\c z) \const.
41770     T atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const {
41771       if (is_empty())
41772         throw CImgInstanceException(_cimglist_instance
41773                                     "atNXYZ() : Empty instance.",
41774                                     cimglist_instance);
41775 
41776       return _atNXYZ(pos,x,y,z,c);
41777     }
41778 
41779     T& _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) {
41780       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZ(x,y,z,c);
41781     }
41782 
41783     T _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const {
41784       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZ(x,y,z,c);
41785     }
41786 
41787     //! Access to pixel value with Dirichlet boundary conditions for the three first coordinates (\c pos, \c x,\c y).
41788     /**
41789        \param pos Indice of the image element to access.
41790        \param x X-coordinate of the pixel value.
41791        \param y Y-coordinate of the pixel value.
41792        \param z Z-coordinate of the pixel value.
41793        \param c C-coordinate of the pixel value.
41794        \param out_value Default value returned if \c offset is outside image bounds.
41795        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
41796     **/
41797     T& atNXY(const int pos, const int x, const int y, const int z, const int c, const T out_value) {
41798       return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atXY(x,y,z,c,out_value);
41799     }
41800 
41801     //! Access to pixel value with Dirichlet boundary conditions for the three first coordinates (\c pos, \c x,\c y) \const.
41802     T atNXY(const int pos, const int x, const int y, const int z, const int c, const T out_value) const {
41803       return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atXY(x,y,z,c,out_value);
41804     }
41805 
41806     //! Access to pixel value with Neumann boundary conditions for the three first coordinates (\c pos, \c x,\c y).
41807     /**
41808        \param pos Indice of the image element to access.
41809        \param x X-coordinate of the pixel value.
41810        \param y Y-coordinate of the pixel value.
41811        \param z Z-coordinate of the pixel value.
41812        \param c C-coordinate of the pixel value.
41813        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
41814     **/
41815     T& atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) {
41816       if (is_empty())
41817         throw CImgInstanceException(_cimglist_instance
41818                                     "atNXY() : Empty instance.",
41819                                     cimglist_instance);
41820 
41821       return _atNXY(pos,x,y,z,c);
41822     }
41823 
41824     //! Access to pixel value with Neumann boundary conditions for the three first coordinates (\c pos, \c x,\c y) \const.
41825     T atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const {
41826       if (is_empty())
41827         throw CImgInstanceException(_cimglist_instance
41828                                     "atNXY() : Empty instance.",
41829                                     cimglist_instance);
41830 
41831       return _atNXY(pos,x,y,z,c);
41832     }
41833 
41834     T& _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) {
41835       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXY(x,y,z,c);
41836     }
41837 
41838     T _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const {
41839       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXY(x,y,z,c);
41840     }
41841 
41842     //! Access to pixel value with Dirichlet boundary conditions for the two first coordinates (\c pos,\c x).
41843     /**
41844        \param pos Indice of the image element to access.
41845        \param x X-coordinate of the pixel value.
41846        \param y Y-coordinate of the pixel value.
41847        \param z Z-coordinate of the pixel value.
41848        \param c C-coordinate of the pixel value.
41849        \param out_value Default value returned if \c offset is outside image bounds.
41850        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
41851     **/
41852     T& atNX(const int pos, const int x, const int y, const int z, const int c, const T out_value) {
41853       return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atX(x,y,z,c,out_value);
41854     }
41855 
41856     //! Access to pixel value with Dirichlet boundary conditions for the two first coordinates (\c pos,\c x) \const.
41857     T atNX(const int pos, const int x, const int y, const int z, const int c, const T out_value) const {
41858       return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atX(x,y,z,c,out_value);
41859     }
41860 
41861     //! Access to pixel value with Neumann boundary conditions for the two first coordinates (\c pos, \c x).
41862     /**
41863        \param pos Indice of the image element to access.
41864        \param x X-coordinate of the pixel value.
41865        \param y Y-coordinate of the pixel value.
41866        \param z Z-coordinate of the pixel value.
41867        \param c C-coordinate of the pixel value.
41868        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
41869     **/
41870     T& atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) {
41871       if (is_empty())
41872         throw CImgInstanceException(_cimglist_instance
41873                                     "atNX() : Empty instance.",
41874                                     cimglist_instance);
41875 
41876       return _atNX(pos,x,y,z,c);
41877     }
41878 
41879     //! Access to pixel value with Neumann boundary conditions for the two first coordinates (\c pos, \c x) \const.
41880     T atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const {
41881       if (is_empty())
41882         throw CImgInstanceException(_cimglist_instance
41883                                     "atNX() : Empty instance.",
41884                                     cimglist_instance);
41885 
41886       return _atNX(pos,x,y,z,c);
41887     }
41888 
41889     T& _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) {
41890       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atX(x,y,z,c);
41891     }
41892 
41893     T _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const {
41894       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atX(x,y,z,c);
41895     }
41896 
41897     //! Access to pixel value with Dirichlet boundary conditions for the first coordinates (\c pos).
41898     /**
41899        \param pos Indice of the image element to access.
41900        \param x X-coordinate of the pixel value.
41901        \param y Y-coordinate of the pixel value.
41902        \param z Z-coordinate of the pixel value.
41903        \param c C-coordinate of the pixel value.
41904        \param out_value Default value returned if \c offset is outside image bounds.
41905        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
41906     **/
41907     T& atN(const int pos, const int x, const int y, const int z, const int c, const T out_value) {
41908       return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):(*this)(pos,x,y,z,c);
41909     }
41910 
41911     //! Access to pixel value with Dirichlet boundary conditions for the first coordinates (\c pos) \const.
41912     T atN(const int pos, const int x, const int y, const int z, const int c, const T out_value) const {
41913       return (pos<0 || pos>=(int)_width)?out_value:(*this)(pos,x,y,z,c);
41914     }
41915 
41916     //! Return pixel value with Neumann boundary conditions for the first coordinates (\c pos).
41917     /**
41918        \param pos Indice of the image element to access.
41919        \param x X-coordinate of the pixel value.
41920        \param y Y-coordinate of the pixel value.
41921        \param z Z-coordinate of the pixel value.
41922        \param c C-coordinate of the pixel value.
41923        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
41924     **/
41925     T& atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) {
41926       if (is_empty())
41927         throw CImgInstanceException(_cimglist_instance
41928                                     "atN() : Empty instance.",
41929                                     cimglist_instance);
41930       return _atN(pos,x,y,z,c);
41931     }
41932 
41933     //! Return pixel value with Neumann boundary conditions for the first coordinates (\c pos) \const.
41934     T atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const {
41935       if (is_empty())
41936         throw CImgInstanceException(_cimglist_instance
41937                                     "atN() : Empty instance.",
41938                                     cimglist_instance);
41939       return _atN(pos,x,y,z,c);
41940     }
41941 
41942     T& _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) {
41943       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)](x,y,z,c);
41944     }
41945 
41946     T _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const {
41947       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)](x,y,z,c);
41948     }
41949 
41950     //! Return a C-string containing the values of all images in the instance list.
41951     /**
41952        \param separator Character separator set between consecutive pixel values.
41953        \param max_size Maximum size of the returned string.
41954        \note The result is returne as a <tt>CImg<char></tt> image whose pixel buffer contains the desired C-string.
41955     **/
41956     CImg<charT> value_string(const char separator=',', const unsigned int max_size=0) const {
41957       if (is_empty()) return CImg<ucharT>(1,1,1,1,0);
41958       CImgList<charT> items;
41959       for (unsigned int l = 0; l<_width-1; ++l) {
41960         CImg<charT> item = _data[l].value_string(separator,0);
41961         item.back() = separator;
41962         item.move_to(items);
41963       }
41964       _data[_width-1].value_string(separator,0).move_to(items);
41965       CImg<charT> res; (items>'x').move_to(res);
41966       if (max_size) { res.crop(0,max_size); res(max_size) = 0; }
41967       return res;
41968     }
41969 
41970     //@}
41971     //-------------------------------------
41972     //
41973     //! \name Instance Checking
41974     //@{
41975     //-------------------------------------
41976 
41977     //! Return \c true if list is empty.
41978     /**
41979     **/
41980     bool is_empty() const {
41981       return (!_data || !_width);
41982     }
41983 
41984     //! Test if number of image elements is equal to specified value.
41985     /**
41986         \param size_n Number of image elements to test.
41987     **/
41988     bool is_sameN(const unsigned int size_n) const {
41989       return _width==size_n;
41990     }
41991 
41992     //! Test if number of image elements is equal between two images lists.
41993     /**
41994         \param list Input list to compare with.
41995     **/
41996     template<typename t>
41997     bool is_sameN(const CImgList<t>& list) const {
41998       return is_sameN(list._width);
41999     }
42000 
42001     // Define useful functions to check list dimensions.
42002     // (cannot be documented because macro-generated).
42003 #define _cimglist_def_is_same1(axis) \
42004     bool is_same##axis(const unsigned int val) const { \
42005       bool res = true; for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(val); return res; \
42006     } \
42007     bool is_sameN##axis(const unsigned int n, const unsigned int val) const { \
42008       return is_sameN(n) && is_same##axis(val); \
42009     } \
42010 
42011 #define _cimglist_def_is_same2(axis1,axis2) \
42012     bool is_same##axis1##axis2(const unsigned int val1, const unsigned int val2) const { \
42013       bool res = true; for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2(val1,val2); return res; \
42014     } \
42015     bool is_sameN##axis1##axis2(const unsigned int n, const unsigned int val1, const unsigned int val2) const { \
42016       return is_sameN(n) && is_same##axis1##axis2(val1,val2); \
42017     } \
42018 
42019 #define _cimglist_def_is_same3(axis1,axis2,axis3) \
42020     bool is_same##axis1##axis2##axis3(const unsigned int val1, const unsigned int val2, const unsigned int val3) const { \
42021       bool res = true; for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2##axis3(val1,val2,val3); return res; \
42022     } \
42023     bool is_sameN##axis1##axis2##axis3(const unsigned int n, const unsigned int val1, const unsigned int val2, const unsigned int val3) const { \
42024       return is_sameN(n) && is_same##axis1##axis2##axis3(val1,val2,val3); \
42025     } \
42026 
42027 #define _cimglist_def_is_same(axis) \
42028     template<typename t> bool is_same##axis(const CImg<t>& img) const { \
42029       bool res = true; for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(img); return res; \
42030     } \
42031     template<typename t> bool is_same##axis(const CImgList<t>& list) const { \
42032       const unsigned int lmin = cimg::min(_width,list._width); \
42033       bool res = true; for (unsigned int l = 0; l<lmin && res; ++l) res = _data[l].is_same##axis(list[l]); return res; \
42034     } \
42035     template<typename t> bool is_sameN##axis(const unsigned int n, const CImg<t>& img) const { \
42036       return (is_sameN(n) && is_same##axis(img)); \
42037     } \
42038     template<typename t> bool is_sameN##axis(const CImgList<t>& list) const { \
42039       return (is_sameN(list) && is_same##axis(list)); \
42040     }
42041 
42042     _cimglist_def_is_same(XY)
42043     _cimglist_def_is_same(XZ)
42044     _cimglist_def_is_same(XC)
42045     _cimglist_def_is_same(YZ)
42046     _cimglist_def_is_same(YC)
42047     _cimglist_def_is_same(XYZ)
42048     _cimglist_def_is_same(XYC)
42049     _cimglist_def_is_same(YZC)
42050     _cimglist_def_is_same(XYZC)
42051     _cimglist_def_is_same1(X)
42052     _cimglist_def_is_same1(Y)
42053     _cimglist_def_is_same1(Z)
42054     _cimglist_def_is_same1(C)
42055     _cimglist_def_is_same2(X,Y)
42056     _cimglist_def_is_same2(X,Z)
42057     _cimglist_def_is_same2(X,C)
42058     _cimglist_def_is_same2(Y,Z)
42059     _cimglist_def_is_same2(Y,C)
42060     _cimglist_def_is_same2(Z,C)
42061     _cimglist_def_is_same3(X,Y,Z)
42062     _cimglist_def_is_same3(X,Y,C)
42063     _cimglist_def_is_same3(X,Z,C)
42064     _cimglist_def_is_same3(Y,Z,C)
42065 
42066     //! Test if dimensions of each image of the list match specified arguments.
42067     /**
42068       \param dx Checked image width.
42069       \param dy Checked image height.
42070       \param dz Checked image depth.
42071       \param dc Checked image spectrum.
42072     **/
42073     bool is_sameXYZC(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dc) const {
42074       bool res = true;
42075       for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_sameXYZC(dx,dy,dz,dc);
42076       return res;
42077     }
42078 
42079     //! Test if list dimensions match specified arguments.
42080     /**
42081        \param n Number of images in the list.
42082        \param dx Checked image width.
42083        \param dy Checked image height.
42084        \param dz Checked image depth.
42085        \param dc Checked image spectrum.
42086     **/
42087     bool is_sameNXYZC(const unsigned int n, const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dc) const {
42088       return is_sameN(n) && is_sameXYZC(dx,dy,dz,dc);
42089     }
42090 
42091     //! Test if list contains one particular pixel location.
42092     /**
42093        \param n Index of the image whom checked pixel value belong to.
42094        \param x X-coordinate of the checked pixel value.
42095        \param y Y-coordinate of the checked pixel value.
42096        \param z Z-coordinate of the checked pixel value.
42097        \param c C-coordinate of the checked pixel value.
42098     **/
42099     bool containsNXYZC(const int n, const int x=0, const int y=0, const int z=0, const int c=0) const {
42100       if (is_empty()) return false;
42101       return n>=0 && n<(int)_width && x>=0 && x<_data[n].width() && y>=0 && y<_data[n].height() &&
42102         z>=0 && z<_data[n].depth() && c>=0 && c<_data[n].spectrum();
42103     }
42104 
42105     //! Test if list contains image with specified indice.
42106     /**
42107        \param n Index of the checked image.
42108     **/
42109     bool containsN(const int n) const {
42110       if (is_empty()) return false;
42111       return n>=0 && n<(int)_width;
42112     }
42113 
42114     //! Test if one image of the list contains the specified referenced value.
42115     /**
42116        \param pixel Reference to pixel value to test.
42117        \param[out] n Index of image containing the pixel value, if test succeeds.
42118        \param[out] x X-coordinate of the pixel value, if test succeeds.
42119        \param[out] y Y-coordinate of the pixel value, if test succeeds.
42120        \param[out] z Z-coordinate of the pixel value, if test succeeds.
42121        \param[out] c C-coordinate of the pixel value, if test succeeds.
42122        \note If true, set coordinates (n,x,y,z,c).
42123     **/
42124     template<typename t>
42125     bool contains(const T& pixel, t& n, t& x, t&y, t& z, t& c) const {
42126       if (is_empty()) return false;
42127       cimglist_for(*this,l) if (_data[l].contains(pixel,x,y,z,c)) { n = (t)l; return true; }
42128       return false;
42129     }
42130 
42131     //! Test if one of the image list contains the specified referenced value.
42132     /**
42133        \param pixel Reference to pixel value to test.
42134        \param[out] n Index of image containing the pixel value, if test succeeds.
42135        \param[out] x X-coordinate of the pixel value, if test succeeds.
42136        \param[out] y Y-coordinate of the pixel value, if test succeeds.
42137        \param[out] z Z-coordinate of the pixel value, if test succeeds.
42138        \note If true, set coordinates (n,x,y,z).
42139     **/
42140     template<typename t>
42141     bool contains(const T& pixel, t& n, t& x, t&y, t& z) const {
42142       t c;
42143       return contains(pixel,n,x,y,z,c);
42144     }
42145 
42146     //! Test if one of the image list contains the specified referenced value.
42147     /**
42148        \param pixel Reference to pixel value to test.
42149        \param[out] n Index of image containing the pixel value, if test succeeds.
42150        \param[out] x X-coordinate of the pixel value, if test succeeds.
42151        \param[out] y Y-coordinate of the pixel value, if test succeeds.
42152        \note If true, set coordinates (n,x,y).
42153     **/
42154     template<typename t>
42155     bool contains(const T& pixel, t& n, t& x, t&y) const {
42156       t z, c;
42157       return contains(pixel,n,x,y,z,c);
42158     }
42159 
42160     //! Test if one of the image list contains the specified referenced value.
42161     /**
42162        \param pixel Reference to pixel value to test.
42163        \param[out] n Index of image containing the pixel value, if test succeeds.
42164        \param[out] x X-coordinate of the pixel value, if test succeeds.
42165        \note If true, set coordinates (n,x).
42166     **/
42167     template<typename t>
42168     bool contains(const T& pixel, t& n, t& x) const {
42169       t y, z, c;
42170       return contains(pixel,n,x,y,z,c);
42171     }
42172 
42173     //! Test if one of the image list contains the specified referenced value.
42174     /**
42175        \param pixel Reference to pixel value to test.
42176        \param[out] n Index of image containing the pixel value, if test succeeds.
42177        \note If true, set coordinates (n).
42178     **/
42179     template<typename t>
42180     bool contains(const T& pixel, t& n) const {
42181       t x, y, z, c;
42182       return contains(pixel,n,x,y,z,c);
42183     }
42184 
42185     //! Test if one of the image list contains the specified referenced value.
42186     /**
42187        \param pixel Reference to pixel value to test.
42188     **/
42189     bool contains(const T& pixel) const {
42190       unsigned int n, x, y, z, c;
42191       return contains(pixel,n,x,y,z,c);
42192     }
42193 
42194     //! Test if the list contains the image 'img'.
42195     /**
42196        \param img Reference to image to test.
42197        \param[out] n Index of image in the list, if test succeeds.
42198        \note If true, returns the position (n) of the image in the list.
42199     **/
42200     template<typename t>
42201     bool contains(const CImg<T>& img, t& n) const {
42202       if (is_empty()) return false;
42203       const CImg<T> *const ptr = &img;
42204       cimglist_for(*this,i) if (_data+i==ptr) { n = (t)i; return true; }
42205       return false;
42206     }
42207 
42208     //! Test if the list contains the image img.
42209     /**
42210        \param img Reference to image to test.
42211     **/
42212     bool contains(const CImg<T>& img) const {
42213       unsigned int n;
42214       return contains(img,n);
42215     }
42216 
42217     //@}
42218     //-------------------------------------
42219     //
42220     //! \name Mathematical Functions
42221     //@{
42222     //-------------------------------------
42223 
42224     //! Return a reference to the minimum pixel value of the instance list.
42225     /**
42226     **/
42227     T& min() {
42228       if (is_empty())
42229         throw CImgInstanceException(_cimglist_instance
42230                                     "min() : Empty instance.",
42231                                     cimglist_instance);
42232       T *ptr_min = _data->_data;
42233       T min_value = *ptr_min;
42234       cimglist_for(*this,l) {
42235         const CImg<T>& img = _data[l];
42236         cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
42237       }
42238       return *ptr_min;
42239     }
42240 
42241     //! Return a reference to the minimum pixel value of the instance list \const.
42242     const T& min() const {
42243       if (is_empty())
42244         throw CImgInstanceException(_cimglist_instance
42245                                     "min() : Empty instance.",
42246                                     cimglist_instance);
42247       const T *ptr_min = _data->_data;
42248       T min_value = *ptr_min;
42249       cimglist_for(*this,l) {
42250         const CImg<T>& img = _data[l];
42251         cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
42252       }
42253       return *ptr_min;
42254     }
42255 
42256     //! Return a reference to the maximum pixel value of the instance list.
42257     /**
42258     **/
42259     T& max() {
42260       if (is_empty())
42261         throw CImgInstanceException(_cimglist_instance
42262                                     "max() : Empty instance.",
42263                                     cimglist_instance);
42264       T *ptr_max = _data->_data;
42265       T max_value = *ptr_max;
42266       cimglist_for(*this,l) {
42267         const CImg<T>& img = _data[l];
42268         cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
42269       }
42270       return *ptr_max;
42271     }
42272 
42273     //! Return a reference to the maximum pixel value of the instance list \const.
42274     const T& max() const {
42275       if (is_empty())
42276         throw CImgInstanceException(_cimglist_instance
42277                                     "max() : Empty instance.",
42278                                     cimglist_instance);
42279       const T *ptr_max = _data->_data;
42280       T max_value = *ptr_max;
42281       cimglist_for(*this,l) {
42282         const CImg<T>& img = _data[l];
42283         cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
42284       }
42285       return *ptr_max;
42286     }
42287 
42288     //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well.
42289     /**
42290        \param[out] max_val Value of the maximum value found.
42291     **/
42292     template<typename t>
42293     T& min_max(t& max_val) {
42294       if (is_empty())
42295         throw CImgInstanceException(_cimglist_instance
42296                                     "min_max() : Empty instance.",
42297                                     cimglist_instance);
42298       T *ptr_min = _data->_data;
42299       T min_value = *ptr_min, max_value = min_value;
42300       cimglist_for(*this,l) {
42301         const CImg<T>& img = _data[l];
42302         cimg_for(img,ptrs,T) {
42303           const T val = *ptrs;
42304           if (val<min_value) { min_value = val; ptr_min = ptrs; }
42305           if (val>max_value) max_value = val;
42306         }
42307       }
42308       max_val = (t)max_value;
42309       return *ptr_min;
42310     }
42311 
42312     //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well \const.
42313     /**
42314        \param[out] max_val Value of the maximum value found.
42315     **/
42316     template<typename t>
42317     const T& min_max(t& max_val) const {
42318       if (is_empty())
42319         throw CImgInstanceException(_cimglist_instance
42320                                     "min_max() : Empty instance.",
42321                                     cimglist_instance);
42322       const T *ptr_min = _data->_data;
42323       T min_value = *ptr_min, max_value = min_value;
42324       cimglist_for(*this,l) {
42325         const CImg<T>& img = _data[l];
42326         cimg_for(img,ptrs,T) {
42327           const T val = *ptrs;
42328           if (val<min_value) { min_value = val; ptr_min = ptrs; }
42329           if (val>max_value) max_value = val;
42330         }
42331       }
42332       max_val = (t)max_value;
42333       return *ptr_min;
42334     }
42335 
42336     //! Return a reference to the minimum pixel value of the instance list and return the minimum value as well.
42337     /**
42338        \param[out] min_val Value of the minimum value found.
42339     **/
42340     template<typename t>
42341     T& max_min(t& min_val) {
42342       if (is_empty())
42343         throw CImgInstanceException(_cimglist_instance
42344                                     "max_min() : Empty instance.",
42345                                     cimglist_instance);
42346       T *ptr_max = _data->_data;
42347       T min_value = *ptr_max, max_value = min_value;
42348       cimglist_for(*this,l) {
42349         const CImg<T>& img = _data[l];
42350         cimg_for(img,ptrs,T) {
42351           const T val = *ptrs;
42352           if (val>max_value) { max_value = val; ptr_max = ptrs; }
42353           if (val<min_value) min_value = val;
42354         }
42355       }
42356       min_val = (t)min_value;
42357       return *ptr_max;
42358     }
42359 
42360     //! Return a reference to the minimum pixel value of the instance list and return the minimum value as well \const.
42361     template<typename t>
42362     const T& max_min(t& min_val) const {
42363       if (is_empty())
42364         throw CImgInstanceException(_cimglist_instance
42365                                     "max_min() : Empty instance.",
42366                                     cimglist_instance);
42367       const T *ptr_max = _data->_data;
42368       T min_value = *ptr_max, max_value = min_value;
42369       cimglist_for(*this,l) {
42370         const CImg<T>& img = _data[l];
42371         cimg_for(img,ptrs,T) {
42372           const T val = *ptrs;
42373           if (val>max_value) { max_value = val; ptr_max = ptrs; }
42374           if (val<min_value) min_value = val;
42375         }
42376       }
42377       min_val = (t)min_value;
42378       return *ptr_max;
42379     }
42380 
42381     //@}
42382     //---------------------------
42383     //
42384     //! \name List Manipulation
42385     //@{
42386     //---------------------------
42387 
42388     //! Insert a copy of the image \c img into the current image list, at position \c pos.
42389     /**
42390         \param img Image to insert a copy to the list.
42391         \param pos Index of the insertion.
42392         \param is_shared Tells if the inserted image is a shared copy of \c img or not.
42393     **/
42394     template<typename t>
42395     CImgList<T>& insert(const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) {
42396       const unsigned int npos = pos==~0U?_width:pos;
42397       if (npos>_width)
42398         throw CImgArgumentException(_cimglist_instance
42399                                     "insert() : Invalid insertion request of specified image (%u,%u,%u,%u,%p) at position %u.",
42400                                     cimglist_instance,
42401                                     img._width,img._height,img._depth,img._spectrum,img._data,npos);
42402       if (is_shared)
42403         throw CImgArgumentException(_cimglist_instance
42404                                     "insert() : Invalid insertion request of specified shared image CImg<%s>(%u,%u,%u,%u,%p) at position %u "
42405                                     "(pixel types are different).",
42406                                     cimglist_instance,
42407                                     img.pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data,npos);
42408 
42409       CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1):(_allocated_width=16)]:0;
42410       if (!_data) { // Insert new element into empty list.
42411         _data = new_data;
42412         *_data = img;
42413       } else {
42414         if (new_data) { // Insert with re-allocation.
42415           if (npos) std::memcpy(new_data,_data,sizeof(CImg<T>)*npos);
42416           if (npos!=_width-1) std::memcpy(new_data+npos+1,_data+npos,sizeof(CImg<T>)*(_width-1-npos));
42417           std::memset(_data,0,sizeof(CImg<T>)*(_width-1));
42418           delete[] _data;
42419           _data = new_data;
42420         } else if (npos!=_width-1) std::memmove(_data+npos+1,_data+npos,sizeof(CImg<T>)*(_width-1-npos)); // Insert without re-allocation.
42421         _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0; _data[npos]._data = 0;
42422         _data[npos] = img;
42423       }
42424       return *this;
42425     }
42426 
42427     //! Insert a copy of the image \c img into the current image list, at position \c pos \specialization.
42428     CImgList<T>& insert(const CImg<T>& img, const unsigned int pos=~0U, const bool is_shared=false) {
42429       const unsigned int npos = pos==~0U?_width:pos;
42430       if (npos>_width)
42431         throw CImgArgumentException(_cimglist_instance
42432                                     "insert() : Invalid insertion request of specified image (%u,%u,%u,%u,%p) at position %u.",
42433                                     cimglist_instance,
42434                                     img._width,img._height,img._depth,img._spectrum,img._data,npos);
42435       CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1):(_allocated_width=16)]:0;
42436       if (!_data) { // Insert new element into empty list.
42437         _data = new_data;
42438         if (is_shared && img) {
42439           _data->_width = img._width; _data->_height = img._height; _data->_depth = img._depth; _data->_spectrum = img._spectrum;
42440           _data->_is_shared = true; _data->_data = img._data;
42441         } else *_data = img;
42442       }
42443       else {
42444         if (new_data) { // Insert with re-allocation.
42445           if (npos) std::memcpy(new_data,_data,sizeof(CImg<T>)*npos);
42446           if (npos!=_width-1) std::memcpy(new_data+npos+1,_data+npos,sizeof(CImg<T>)*(_width-1-npos));
42447           if (is_shared && img) {
42448             new_data[npos]._width = img._width; new_data[npos]._height = img._height; new_data[npos]._depth = img._depth;
42449             new_data[npos]._spectrum = img._spectrum; new_data[npos]._is_shared = true; new_data[npos]._data = img._data;
42450           } else {
42451             new_data[npos]._width = new_data[npos]._height = new_data[npos]._depth = new_data[npos]._spectrum = 0; new_data[npos]._data = 0;
42452             new_data[npos] = img;
42453           }
42454           std::memset(_data,0,sizeof(CImg<T>)*(_width-1));
42455           delete[] _data;
42456           _data = new_data;
42457         } else { // Insert without re-allocation.
42458           if (npos!=_width-1) std::memmove(_data+npos+1,_data+npos,sizeof(CImg<T>)*(_width-1-npos));
42459           if (is_shared && img) {
42460             _data[npos]._width = img._width; _data[npos]._height = img._height; _data[npos]._depth = img._depth; _data[npos]._spectrum = img._spectrum;
42461             _data[npos]._is_shared = true; _data[npos]._data = img._data;
42462           } else {
42463             _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0; _data[npos]._data = 0;
42464             _data[npos] = img;
42465           }
42466         }
42467       }
42468       return *this;
42469     }
42470 
42471     //! Insert a copy of the image \c img into the current image list, at position \c pos \newinstance.
42472     template<typename t>
42473     CImgList<T> get_insert(const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) const {
42474       return (+*this).insert(img,pos,is_shared);
42475     }
42476 
42477     //! Insert n empty images img into the current image list, at position \p pos.
42478     /**
42479        \param n Number of empty images to insert.
42480        \param pos Index of the insertion.
42481     **/
42482     CImgList<T>& insert(const unsigned int n, const unsigned int pos=~0U) {
42483       CImg<T> empty;
42484       if (!n) return *this;
42485       const unsigned int npos = pos==~0U?_width:pos;
42486       for (unsigned int i = 0; i<n; ++i) insert(empty,npos+i);
42487       return *this;
42488     }
42489 
42490     //! Insert n empty images img into the current image list, at position \p pos \newinstance.
42491     CImgList<T> get_insert(const unsigned int n, const unsigned int pos=~0U) const {
42492       return (+*this).insert(n,pos);
42493     }
42494 
42495     //! Insert \c n copies of the image \c img into the current image list, at position \c pos.
42496     /**
42497        \param n Number of image copies to insert.
42498        \param img Image to insert by copy.
42499        \param pos Index of the insertion.
42500        \param is_shared Tells if inserted images are shared copies of \c img or not.
42501     **/
42502     template<typename t>
42503     CImgList<T>& insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) {
42504       if (!n) return *this;
42505       const unsigned int npos = pos==~0U?_width:pos;
42506       insert(img,npos,is_shared);
42507       for (unsigned int i = 1; i<n; ++i) insert(_data[npos],npos+i,is_shared);
42508       return *this;
42509     }
42510 
42511     //! Insert \c n copies of the image \c img into the current image list, at position \c pos \newinstance.
42512     template<typename t>
42513     CImgList<T> get_insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) const {
42514       return (+*this).insert(n,img,pos,is_shared);
42515     }
42516 
42517     //! Insert a copy of the image list \c list into the current image list, starting from position \c pos.
42518     /**
42519       \param list Image list to insert.
42520       \param pos Index of the insertion.
42521       \param is_shared Tells if inserted images are shared copies of images of \c list or not.
42522     **/
42523     template<typename t>
42524     CImgList<T>& insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) {
42525       const unsigned int npos = pos==~0U?_width:pos;
42526       if ((void*)this!=(void*)&list) cimglist_for(list,l) insert(list[l],npos+l,is_shared);
42527       else insert(CImgList<T>(list),npos,is_shared);
42528       return *this;
42529     }
42530 
42531     //! Insert a copy of the image list \c list into the current image list, starting from position \c pos \newinstance.
42532     template<typename t>
42533     CImgList<T> get_insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) const {
42534       return (+*this).insert(list,pos,is_shared);
42535     }
42536 
42537     //! Insert n copies of the list \c list at position \c pos of the current list.
42538     /**
42539       \param n Number of list copies to insert.
42540       \param list Image list to insert.
42541       \param pos Index of the insertion.
42542       \param is_shared Tells if inserted images are shared copies of images of \c list or not.
42543     **/
42544     template<typename t>
42545     CImgList<T>& insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) {
42546       if (!n) return *this;
42547       const unsigned int npos = pos==~0U?_width:pos;
42548       for (unsigned int i = 0; i<n; ++i) insert(list,npos,is_shared);
42549       return *this;
42550     }
42551 
42552     //! Insert n copies of the list \c list at position \c pos of the current list \newinstance.
42553     template<typename t>
42554     CImgList<T> get_insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) const {
42555       return (+*this).insert(n,list,pos,is_shared);
42556     }
42557 
42558     //! Remove all images between from indexes.
42559     /**
42560       \param pos1 Starting index of the removal.
42561       \param pos2 Ending index of the removal.
42562     **/
42563     CImgList<T>& remove(const unsigned int pos1, const unsigned int pos2) {
42564       const unsigned int
42565         npos1 = pos1<pos2?pos1:pos2,
42566         tpos2 = pos1<pos2?pos2:pos1,
42567         npos2 = tpos2<_width?tpos2:_width-1;
42568       if (npos1>=_width)
42569         throw CImgArgumentException(_cimglist_instance
42570                                     "remove() : Invalid remove request at positions %u->%u.",
42571                                     cimglist_instance,
42572                                     npos1,tpos2);
42573       else {
42574         if (tpos2>=_width)
42575           throw CImgArgumentException(_cimglist_instance
42576                                       "remove() : Invalid remove request at positions %u->%u.",
42577                                       cimglist_instance,
42578                                       npos1,tpos2);
42579 
42580         for (unsigned int k = npos1; k<=npos2; ++k) _data[k].assign();
42581         const unsigned int nb = 1 + npos2 - npos1;
42582         if (!(_width-=nb)) return assign();
42583         if (_width>(_allocated_width>>2) || _allocated_width<=16) { // Removing items without reallocation.
42584           if (npos1!=_width) std::memmove(_data+npos1,_data+npos2+1,sizeof(CImg<T>)*(_width - npos1));
42585           std::memset(_data + _width,0,sizeof(CImg<T>)*nb);
42586         } else { // Removing items with reallocation.
42587           _allocated_width>>=2;
42588           while (_allocated_width>16 && _width<(_allocated_width>>1)) _allocated_width>>=1;
42589           CImg<T> *const new_data = new CImg<T>[_allocated_width];
42590           if (npos1) std::memcpy(new_data,_data,sizeof(CImg<T>)*npos1);
42591           if (npos1!=_width) std::memcpy(new_data+npos1,_data+npos2+1,sizeof(CImg<T>)*(_width-npos1));
42592           if (_width!=_allocated_width) std::memset(new_data+_width,0,sizeof(_allocated_width - _width));
42593           std::memset(_data,0,sizeof(CImg<T>)*(_width+nb));
42594           delete[] _data;
42595           _data = new_data;
42596         }
42597       }
42598       return *this;
42599     }
42600 
42601     //! Remove all images between from indexes \newinstance.
42602     CImgList<T> get_remove(const unsigned int pos1, const unsigned int pos2) const {
42603       return (+*this).remove(pos1,pos2);
42604     }
42605 
42606     //! Remove image at index \c pos from the image list.
42607     /**
42608       \param pos Index of the image to remove.
42609     **/
42610     CImgList<T>& remove(const unsigned int pos) {
42611       return remove(pos,pos);
42612     }
42613 
42614     //! Remove image at index \c pos from the image list \newinstance.
42615     CImgList<T> get_remove(const unsigned int pos) const {
42616       return (+*this).remove(pos);
42617     }
42618 
42619     //! Remove last image.
42620     /**
42621     **/
42622     CImgList<T>& remove() {
42623       return remove(_width-1);
42624     }
42625 
42626     //! Remove last image \newinstance.
42627     CImgList<T> get_remove() const {
42628       return (+*this).remove();
42629     }
42630 
42631     //! Reverse list order.
42632     CImgList<T>& reverse() {
42633       for (unsigned int l = 0; l<_width/2; ++l) (*this)[l].swap((*this)[_width-1-l]);
42634       return *this;
42635     }
42636 
42637     //! Reverse list order \newinstance.
42638     CImgList<T> get_reverse() const {
42639       return (+*this).reverse();
42640     }
42641 
42642     //! Return a sublist.
42643     /**
42644       \param pos0 Starting index of the sublist.
42645       \param pos1 Ending index of the sublist.
42646     **/
42647     CImgList<T>& images(const unsigned int pos0, const unsigned int pos1) {
42648       return get_images(pos0,pos1).move_to(*this);
42649     }
42650 
42651     //! Return a sublist \newinstance.
42652     CImgList<T> get_images(const unsigned int pos0, const unsigned int pos1) const {
42653       if (pos0>pos1 || pos1>=_width)
42654         throw CImgArgumentException(_cimglist_instance
42655                                     "images() : Specified sub-list indices (%u->%u) are out of bounds.",
42656                                     cimglist_instance,
42657                                     pos0,pos1);
42658       CImgList<T> res(pos1-pos0+1);
42659       cimglist_for(res,l) res[l].assign(_data[pos0+l]);
42660       return res;
42661     }
42662 
42663     //! Return a shared sublist.
42664     /**
42665       \param pos0 Starting index of the sublist.
42666       \param pos1 Ending index of the sublist.
42667     **/
42668     CImgList<T> get_shared_images(const unsigned int pos0, const unsigned int pos1) {
42669       if (pos0>pos1 || pos1>=_width)
42670         throw CImgArgumentException(_cimglist_instance
42671                                     "get_shared_images() : Specified sub-list indices (%u->%u) are out of bounds.",
42672                                     cimglist_instance,
42673                                     pos0,pos1);
42674       CImgList<T> res(pos1-pos0+1);
42675       cimglist_for(res,l) res[l].assign(_data[pos0+l],true);
42676       return res;
42677     }
42678 
42679     //! Return a shared sublist \newinstance.
42680     const CImgList<T> get_shared_images(const unsigned int pos0, const unsigned int pos1) const {
42681       if (pos0>pos1 || pos1>=_width)
42682         throw CImgArgumentException(_cimglist_instance
42683                                     "get_shared_images() : Specified sub-list indices (%u->%u) are out of bounds.",
42684                                     cimglist_instance,
42685                                     pos0,pos1);
42686       CImgList<T> res(pos1-pos0+1);
42687       cimglist_for(res,l) res[l].assign(_data[pos0+l],true);
42688       return res;
42689     }
42690 
42691     //! Return a single image which is the appending of all images of the current CImgList instance.
42692     /**
42693        \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
42694        \param align Appending alignment.
42695     **/
42696     CImg<T> get_append(const char axis, const float align=0) const {
42697       if (is_empty()) return CImg<T>();
42698       if (_width==1) return +((*this)[0]);
42699       unsigned int dx = 0, dy = 0, dz = 0, dc = 0, pos = 0;
42700       CImg<T> res;
42701       switch (cimg::uncase(axis)) {
42702       case 'x' : { // Along the X-axis.
42703         cimglist_for(*this,l) {
42704           const CImg<T>& img = (*this)[l];
42705           if (img) { dx+=img._width; dy = cimg::max(dy,img._height); dz = cimg::max(dz,img._depth); dc = cimg::max(dc,img._spectrum); }
42706         }
42707         res.assign(dx,dy,dz,dc,0);
42708         if (res) cimglist_for(*this,l) {
42709             const CImg<T>& img = (*this)[l];
42710             if (img) res.draw_image(pos,
42711                                     (int)(align*(dy-img._height)),
42712                                     (int)(align*(dz-img._depth)),
42713                                     (int)(align*(dc-img._spectrum)),
42714                                     img);
42715             pos+=img._width;
42716           }
42717       } break;
42718       case 'y' : { // Along the Y-axis.
42719         cimglist_for(*this,l) {
42720           const CImg<T>& img = (*this)[l];
42721           if (img) { dx = cimg::max(dx,img._width); dy+=img._height; dz = cimg::max(dz,img._depth); dc = cimg::max(dc,img._spectrum); }
42722         }
42723         res.assign(dx,dy,dz,dc,0);
42724         if (res) cimglist_for(*this,l) {
42725             const CImg<T>& img = (*this)[l];
42726             if (img) res.draw_image((int)(align*(dx-img._width)),
42727                                     pos,
42728                                     (int)(align*(dz-img._depth)),
42729                                     (int)(align*(dc-img._spectrum)),
42730                                     img);
42731             pos+=img._height;
42732           }
42733       } break;
42734       case 'z' : { // Along the Z-axis.
42735         cimglist_for(*this,l) {
42736           const CImg<T>& img = (*this)[l];
42737           if (img) { dx = cimg::max(dx,img._width); dy = cimg::max(dy,img._height); dz+=img._depth; dc = cimg::max(dc,img._spectrum); }
42738         }
42739         res.assign(dx,dy,dz,dc,0);
42740         if (res) cimglist_for(*this,l) {
42741             const CImg<T>& img = (*this)[l];
42742             if (img) res.draw_image((int)(align*(dx-img._width)),
42743                                     (int)(align*(dy-img._height)),
42744                                     pos,
42745                                     (int)(align*(dc-img._spectrum)),
42746                                     img);
42747             pos+=img._depth;
42748           }
42749       } break;
42750       default : { // Along the C-axis.
42751         cimglist_for(*this,l) {
42752           const CImg<T>& img = (*this)[l];
42753           if (img) { dx = cimg::max(dx,img._width); dy = cimg::max(dy,img._height); dz = cimg::max(dz,img._depth); dc+=img._spectrum; }
42754         }
42755         res.assign(dx,dy,dz,dc,0);
42756         if (res) cimglist_for(*this,l) {
42757             const CImg<T>& img = (*this)[l];
42758             if (img) res.draw_image((int)(align*(dx-img._width)),
42759                                     (int)(align*(dy-img._height)),
42760                                     (int)(align*(dz-img._depth)),
42761                                     pos,
42762                                     img);
42763             pos+=img._spectrum;
42764           }
42765       }
42766       }
42767       return res;
42768     }
42769 
42770     //! Return a list where each image has been split along the specified axis.
42771     /**
42772         \param axis Axis to split images along.
42773         \param nb Number of spliting parts for each image.
42774     **/
42775     CImgList<T>& split(const char axis, const int nb=0) {
42776       return get_split(axis,nb).move_to(*this);
42777     }
42778 
42779     //! Return a list where each image has been split along the specified axis \newinstance.
42780     CImgList<T> get_split(const char axis, const int nb=0) const {
42781       CImgList<T> res;
42782       cimglist_for(*this,l) _data[l].get_split(axis,nb).move_to(res,~0U);
42783       return res;
42784     }
42785 
42786     //! Insert image at the end of the list.
42787     /**
42788       \param img Image to insert.
42789     **/
42790     template<typename t>
42791     CImgList<T>& push_back(const CImg<t>& img) {
42792       return insert(img);
42793     }
42794 
42795     //! Insert image at the front of the list.
42796     /**
42797       \param img Image to insert.
42798     **/
42799     template<typename t>
42800     CImgList<T>& push_front(const CImg<t>& img) {
42801       return insert(img,0);
42802     }
42803 
42804     //! Insert list at the end of the current list.
42805     /**
42806       \param list List to insert.
42807     **/
42808     template<typename t>
42809     CImgList<T>& push_back(const CImgList<t>& list) {
42810       return insert(list);
42811     }
42812 
42813     //! Insert list at the front of the current list.
42814     /**
42815       \param list List to insert.
42816     **/
42817     template<typename t>
42818     CImgList<T>& push_front(const CImgList<t>& list) {
42819       return insert(list,0);
42820     }
42821 
42822     //! Remove last image.
42823     /**
42824     **/
42825     CImgList<T>& pop_back() {
42826       return remove(_width-1);
42827     }
42828 
42829     //! Remove first image.
42830     /**
42831     **/
42832     CImgList<T>& pop_front() {
42833       return remove(0);
42834     }
42835 
42836     //! Remove image pointed by iterator.
42837     /**
42838       \param iter Iterator pointing to the image to remove.
42839     **/
42840     CImgList<T>& erase(const iterator iter) {
42841       return remove(iter-_data);
42842     }
42843 
42844     //@}
42845     //----------------------------------
42846     //
42847     //! \name Data Input
42848     //@{
42849     //----------------------------------
42850 
42851     //! Display a simple interactive interface to select images or sublists.
42852     /**
42853        \param disp Window instance to display selection and user interface.
42854        \param feature_type Can be \c false to select a single image, or \c true to select a sublist.
42855        \param axis Axis along whom images are appended for visualization.
42856        \param align Alignment setting when images have not all the same size.
42857        \return A one-column vector containing the selected image indexes.
42858     **/
42859     CImg<intT> get_select(CImgDisplay &disp, const bool feature_type=true,
42860                           const char axis='x', const float align=0) const {
42861       return _get_select(disp,0,feature_type,axis,align,0,false,false,false);
42862     }
42863 
42864     //! Display a simple interactive interface to select images or sublists.
42865     /**
42866        \param title Title of a new window used to display selection and user interface.
42867        \param feature_type Can be \c false to select a single image, or \c true to select a sublist.
42868        \param axis Axis along whom images are appended for visualization.
42869        \param align Alignment setting when images have not all the same size.
42870        \return A one-column vector containing the selected image indexes.
42871     **/
42872     CImg<intT> get_select(const char *const title, const bool feature_type=true,
42873                           const char axis='x', const float align=0) const {
42874       CImgDisplay disp;
42875       return _get_select(disp,title,feature_type,axis,align,0,false,false,false);
42876     }
42877 
42878     CImg<intT> _get_select(CImgDisplay &disp, const char *const title, const bool feature_type,
42879                            const char axis, const float align,
42880                            const unsigned int orig, const bool resize_disp,
42881                            const bool exit_on_rightbutton, const bool exit_on_wheel) const {
42882       if (is_empty())
42883         throw CImgInstanceException(_cimglist_instance
42884                                     "select() : Empty instance.",
42885                                     cimglist_instance);
42886 
42887       // Create image correspondence table and get list dimensions for visualization.
42888       CImgList<uintT> _indices;
42889       unsigned int max_width = 0, max_height = 0, sum_width = 0, sum_height = 0;
42890       cimglist_for(*this,l) if (_data[l]) {
42891         const CImg<T>& img = _data[l];
42892         const unsigned int
42893           w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
42894           h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
42895         if (w>max_width) max_width = w;
42896         if (h>max_height) max_height = h;
42897         sum_width+=w; sum_height+=h;
42898         if (axis=='x') CImg<uintT>(w,1,1,1,(unsigned int)l).move_to(_indices);
42899         else CImg<uintT>(h,1,1,1,(unsigned int)l).move_to(_indices);
42900       }
42901       const CImg<uintT> indices0 = _indices>'x';
42902 
42903       // Create display window.
42904       if (!disp) {
42905         if (axis=='x') disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:0,1);
42906         else disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:0,1);
42907         if (!title) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width);
42908       } else if (title) disp.set_title("%s",title);
42909       if (resize_disp) {
42910         if (axis=='x') disp.resize(cimg_fitscreen(sum_width,max_height,1),false);
42911         else disp.resize(cimg_fitscreen(max_width,sum_height,1),false);
42912       }
42913 
42914       const unsigned int old_normalization = disp.normalization();
42915       bool old_is_resized = disp.is_resized();
42916       disp._normalization = 0;
42917       disp.show().set_key(0);
42918       const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 };
42919 
42920       // Enter event loop.
42921       CImg<ucharT> visu0, visu;
42922       CImg<uintT> indices;
42923       CImg<intT> positions(_width,4,1,1,-1);
42924       int oindice0 = -1, oindice1 = -1, indice0 = -1, indice1 = -1;
42925       bool is_clicked = false, is_selected = false, text_down = false, update_display = true;
42926       unsigned int key = 0;
42927       while (!is_selected && !disp.is_closed() && !key) {
42928 
42929         // Create background image.
42930         if (!visu0) {
42931           visu0.assign(disp._width,disp._height,1,3,0); visu.assign();
42932           (indices0.get_resize(axis=='x'?visu0._width:visu0._height,1)).move_to(indices);
42933           unsigned int ind = 0;
42934           if (axis=='x') for (unsigned int x = 0; x<visu0._width; ) {
42935               const unsigned int x0 = x;
42936               ind = indices[x];
42937               while (x<indices._width && indices[++x]==ind) {}
42938               const CImg<T>
42939                 &src = _data[ind],
42940                 _img2d = src._depth>1?src.get_projections2d(src._width/2,src._height/2,src._depth/2):CImg<T>(),
42941                 &img2d = _img2d?_img2d:src;
42942               CImg<ucharT> res = old_normalization==1?
42943                 CImg<ucharT>(img2d.get_channels(0,cimg::min(2,img2d.spectrum()-1)).normalize(0,255)):
42944                 CImg<ucharT>(img2d.get_channels(0,cimg::min(2,img2d.spectrum()-1)));
42945               const unsigned int h = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,true);
42946               res.resize(x - x0,cimg::max(32U,h*disp._height/max_height),1,res._spectrum==1?3:-100);
42947               positions(ind,0) = positions(ind,2) = (int)x0;
42948               positions(ind,1) = positions(ind,3) = (int)(align*(visu0.height()-res.height()));
42949               positions(ind,2)+=res._width;
42950               positions(ind,3)+=res._height - 1;
42951               visu0.draw_image(positions(ind,0),positions(ind,1),res);
42952             } else for (unsigned int y = 0; y<visu0._height; ) {
42953               const unsigned int y0 = y;
42954               ind = indices[y];
42955               while (y<visu0._height && indices[++y]==ind) {}
42956               const CImg<T>
42957                 &src = _data[ind],
42958                 _img2d = src._depth>1?src.get_projections2d(src._width/2,src._height/2,src._depth/2):CImg<T>(),
42959                 &img2d = _img2d?_img2d:src;
42960               CImg<ucharT> res = old_normalization==1?CImg<ucharT>(img2d.get_normalize(0,255)):CImg<ucharT>(img2d);
42961               if (res._spectrum>3) res.channels(0,2);
42962               const unsigned int w = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,false);
42963               res.resize(cimg::max(32U,w*disp._width/max_width),y - y0,1,res._spectrum==1?3:-100);
42964               positions(ind,0) = positions(ind,2) = (int)(align*(visu0.width()-res.width()));
42965               positions(ind,1) = positions(ind,3) = (int)y0;
42966               positions(ind,2)+=res._width - 1;
42967               positions(ind,3)+=res._height;
42968               visu0.draw_image(positions(ind,0),positions(ind,1),res);
42969             }
42970           if (axis=='x') --positions(ind,2); else --positions(ind,3);
42971           update_display = true;
42972         }
42973 
42974         if (!visu || oindice0!=indice0 || oindice1!=indice1) {
42975           if (indice0>=0 && indice1>=0) {
42976             visu.assign(visu0,false);
42977             const int indm = cimg::min(indice0,indice1), indM = cimg::max(indice0,indice1);
42978             for (int ind = indm; ind<=indM; ++ind) if (positions(ind,0)>=0) {
42979                 visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),background_color,0.2f);
42980                 if ((axis=='x' && positions(ind,2) - positions(ind,0)>=8) ||
42981                     (axis!='x' && positions(ind,3) - positions(ind,1)>=8))
42982                   visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),foreground_color,0.9f,0x55555555);
42983               }
42984             const int yt = (int)text_down?visu.height()-13:0;
42985             if (is_clicked) visu.draw_text(0,yt," Images %u - %u, Size = %u",foreground_color,background_color,0.7f,13,
42986                                            orig + indm,orig + indM,indM - indm + 1);
42987             else visu.draw_text(0,yt," Image %u",foreground_color,background_color,0.7f,13,
42988                                 orig + indice0);
42989             update_display = true;
42990           } else visu.assign();
42991         }
42992         if (!visu) { visu.assign(visu0,true); update_display = true; }
42993         if (update_display) { visu.display(disp); update_display = false; }
42994         disp.wait();
42995 
42996         // Manage user events.
42997         const int xm = disp.mouse_x(), ym = disp.mouse_y();
42998         int indice = -1;
42999 
43000         if (xm>=0) {
43001           indice = (int)indices(axis=='x'?xm:ym);
43002           if (disp.button()&1) {
43003             if (!is_clicked) { is_clicked = true; oindice0 = indice0; indice0 = indice; }
43004             oindice1 = indice1; indice1 = indice;
43005             if (!feature_type) is_selected = true;
43006           } else {
43007             if (!is_clicked) { oindice0 = oindice1 = indice0; indice0 = indice1 = indice; }
43008             else is_selected = true;
43009           }
43010         } else {
43011           if (is_clicked) {
43012             if (!(disp.button()&1)) { is_clicked = is_selected = false; indice0 = indice1 = -1; }
43013             else indice1 = -1;
43014           } else indice0 = indice1 = -1;
43015         }
43016 
43017         if (disp.button()&4) { is_clicked = is_selected = false; indice0 = indice1 = -1; }
43018         if (disp.button()&2 && exit_on_rightbutton) { is_selected = true; indice1 = indice0 = -1; }
43019         if (disp.wheel() && exit_on_wheel) is_selected = true;
43020 
43021         switch (key = disp.key()) {
43022 #if cimg_OS!=2
43023         case cimg::keyCTRLRIGHT :
43024 #endif
43025         case 0 : case cimg::keyCTRLLEFT : key = 0; break;
43026         case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
43027             disp.set_fullscreen(false).resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
43028                                               CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
43029               _is_resized = true;
43030             disp.set_key(key,false); key = 0; visu0.assign();
43031           } break;
43032         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
43033             disp.set_fullscreen(false).resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
43034             disp.set_key(key,false); key = 0; visu0.assign();
43035           } break;
43036         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
43037             disp.set_fullscreen(false).resize(cimg_fitscreen(axis=='x'?sum_width:max_width,axis=='x'?max_height:sum_height,1),false)._is_resized = true;
43038             disp.set_key(key,false); key = 0; visu0.assign();
43039           } break;
43040         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
43041             disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
43042             disp.set_key(key,false); key = 0; visu0.assign();
43043           } break;
43044         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
43045             static unsigned int snap_number = 0;
43046             char filename[32] = { 0 };
43047             std::FILE *file;
43048             do {
43049               cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++);
43050               if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
43051             } while (file);
43052             if (visu0) {
43053               visu.draw_text(0,0," Saving snapshot... ",foreground_color,background_color,1,13).display(disp);
43054               visu0.save(filename);
43055               visu.draw_text(0,0," Snapshot '%s' saved. ",foreground_color,background_color,1,13,filename).display(disp);
43056             }
43057             disp.set_key(key,false).wait(); key = 0;
43058           } break;
43059         case cimg::keyO :
43060           if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
43061             static unsigned int snap_number = 0;
43062             char filename[32] = { 0 };
43063             std::FILE *file;
43064             do {
43065 #ifdef cimg_use_zlib
43066               cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++);
43067 #else
43068               cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++);
43069 #endif
43070               if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
43071             } while (file);
43072             visu.draw_text(0,0," Saving instance... ",foreground_color,background_color,1,13).display(disp);
43073             save(filename);
43074             visu.draw_text(0,0," Instance '%s' saved. ",foreground_color,background_color,1,13,filename).display(disp);
43075             disp.set_key(key,false).wait(); key = 0;
43076           } break;
43077         }
43078         if (disp.is_resized()) { disp.resize(false); visu0.assign(); }
43079         if (ym>=0 && ym<13) { if (!text_down) { visu.assign(); text_down = true; }}
43080         else if (ym>=visu.height()-13) { if(text_down) { visu.assign(); text_down = false; }}
43081       }
43082       CImg<intT> res(1,2,1,1,-1);
43083       if (is_selected) { if (feature_type) res.fill(cimg::min(indice0,indice1),cimg::max(indice0,indice1)); else res.fill(indice0); }
43084       if (!(disp.button()&2)) disp.set_button();
43085       disp._normalization = old_normalization;
43086       disp._is_resized = old_is_resized;
43087       disp.set_key(key);
43088       return res;
43089     }
43090 
43091     //! Load a list from a file.
43092     /**
43093      \param filename Filename to read data from.
43094     **/
43095     CImgList<T>& load(const char *const filename) {
43096       if (!filename)
43097         throw CImgArgumentException(_cimglist_instance
43098                                     "load() : Specified filename is (null).",
43099                                     cimglist_instance);
43100 
43101       if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) {
43102         char filename_local[1024] = { 0 };
43103         load(cimg::load_network_external(filename,filename_local));
43104         std::remove(filename_local);
43105         return *this;
43106       }
43107 
43108       const char *const ext = cimg::split_filename(filename);
43109       const unsigned int omode = cimg::exception_mode();
43110       cimg::exception_mode() = 0;
43111       try {
43112 #ifdef cimglist_load_plugin
43113         cimglist_load_plugin(filename);
43114 #endif
43115 #ifdef cimglist_load_plugin1
43116         cimglist_load_plugin1(filename);
43117 #endif
43118 #ifdef cimglist_load_plugin2
43119         cimglist_load_plugin2(filename);
43120 #endif
43121 #ifdef cimglist_load_plugin3
43122         cimglist_load_plugin3(filename);
43123 #endif
43124 #ifdef cimglist_load_plugin4
43125         cimglist_load_plugin4(filename);
43126 #endif
43127 #ifdef cimglist_load_plugin5
43128         cimglist_load_plugin5(filename);
43129 #endif
43130 #ifdef cimglist_load_plugin6
43131         cimglist_load_plugin6(filename);
43132 #endif
43133 #ifdef cimglist_load_plugin7
43134         cimglist_load_plugin7(filename);
43135 #endif
43136 #ifdef cimglist_load_plugin8
43137         cimglist_load_plugin8(filename);
43138 #endif
43139         if (!cimg::strcasecmp(ext,"tif") ||
43140             !cimg::strcasecmp(ext,"tiff")) load_tiff(filename);
43141         else if (!cimg::strcasecmp(ext,"cimg") ||
43142                  !cimg::strcasecmp(ext,"cimgz") ||
43143                  !*ext) load_cimg(filename);
43144         else if (!cimg::strcasecmp(ext,"rec") ||
43145                  !cimg::strcasecmp(ext,"par")) load_parrec(filename);
43146         else if (!cimg::strcasecmp(ext,"avi") ||
43147                  !cimg::strcasecmp(ext,"mov") ||
43148                  !cimg::strcasecmp(ext,"asf") ||
43149                  !cimg::strcasecmp(ext,"divx") ||
43150                  !cimg::strcasecmp(ext,"flv") ||
43151                  !cimg::strcasecmp(ext,"mpg") ||
43152                  !cimg::strcasecmp(ext,"m1v") ||
43153                  !cimg::strcasecmp(ext,"m2v") ||
43154                  !cimg::strcasecmp(ext,"m4v") ||
43155                  !cimg::strcasecmp(ext,"mjp") ||
43156                  !cimg::strcasecmp(ext,"mkv") ||
43157                  !cimg::strcasecmp(ext,"mpe") ||
43158                  !cimg::strcasecmp(ext,"movie") ||
43159                  !cimg::strcasecmp(ext,"ogm") ||
43160                  !cimg::strcasecmp(ext,"ogg") ||
43161                  !cimg::strcasecmp(ext,"qt") ||
43162                  !cimg::strcasecmp(ext,"rm") ||
43163                  !cimg::strcasecmp(ext,"vob") ||
43164                  !cimg::strcasecmp(ext,"wmv") ||
43165                  !cimg::strcasecmp(ext,"xvid") ||
43166                  !cimg::strcasecmp(ext,"mpeg")) load_ffmpeg(filename);
43167         else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename);
43168         else throw CImgIOException("CImgList<%s>::load()",
43169                                    pixel_type());
43170       } catch (CImgIOException&) {
43171         try {
43172           cimg::fclose(cimg::fopen(filename,"rb"));
43173         } catch (CImgIOException&) {
43174           cimg::exception_mode() = omode;
43175           throw CImgIOException(_cimglist_instance
43176                                 "load() : Failed to open file '%s'.",
43177                                 cimglist_instance,
43178                                 filename);
43179         }
43180         assign(1);
43181         try {
43182           _data->load(filename);
43183         } catch (CImgIOException&) {
43184           cimg::exception_mode() = omode;
43185           throw CImgIOException(_cimglist_instance
43186                                 "load() : Failed to recognize format of file '%s'.",
43187                                 cimglist_instance,
43188                                 filename);
43189         }
43190       }
43191       cimg::exception_mode() = omode;
43192       return *this;
43193     }
43194 
43195     //! Load a list from a file \newinstance.
43196     static CImgList<T> get_load(const char *const filename) {
43197       return CImgList<T>().load(filename);
43198     }
43199 
43200     //! Load a list from a .cimg file.
43201     /**
43202       \param filename Filename to read data from.
43203     **/
43204     CImgList<T>& load_cimg(const char *const filename) {
43205       return _load_cimg(0,filename);
43206     }
43207 
43208     //! Load a list from a .cimg file \newinstance.
43209     static CImgList<T> get_load_cimg(const char *const filename) {
43210       return CImgList<T>().load_cimg(filename);
43211     }
43212 
43213     //! Load a list from a .cimg file.
43214     /**
43215       \param file File to read data from.
43216     **/
43217     CImgList<T>& load_cimg(std::FILE *const file) {
43218       return _load_cimg(file,0);
43219     }
43220 
43221     //! Load a list from a .cimg file \newinstance.
43222     static CImgList<T> get_load_cimg(std::FILE *const file) {
43223       return CImgList<T>().load_cimg(file);
43224     }
43225 
43226     CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename) {
43227 #ifdef cimg_use_zlib
43228 #define _cimgz_load_cimg_case(Tss) { \
43229    Bytef *const cbuf = new Bytef[csiz]; \
43230    cimg::fread(cbuf,csiz,nfile); \
43231    raw.assign(W,H,D,C); \
43232    unsigned long destlen = (unsigned long)raw.size()*sizeof(Tss); \
43233    uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \
43234    delete[] cbuf; \
43235    const Tss *ptrs = raw._data; \
43236    for (unsigned long off = raw.size(); off; --off) *(ptrd++) = (T)*(ptrs++); \
43237 }
43238 #else
43239 #define _cimgz_load_cimg_case(Tss) \
43240    throw CImgIOException(_cimglist_instance \
43241                          "load_cimg() : Unable to load compressed data from file '%s' unless zlib is enabled.", \
43242                          cimglist_instance, \
43243                          filename?filename:"(FILE*)");
43244 #endif
43245 
43246 #define _cimg_load_cimg_case(Ts,Tss) \
43247       if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
43248         for (unsigned int l = 0; l<N; ++l) { \
43249           j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0) tmp[j++] = (char)i; tmp[j] = 0; \
43250           W = H = D = C = 0; csiz = 0; \
43251           if ((err = std::sscanf(tmp,"%u %u %u %u #%u",&W,&H,&D,&C,&csiz))<4) \
43252             throw CImgIOException(_cimglist_instance \
43253                                   "load_cimg() : Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'", \
43254                                   cimglist_instance, \
43255                                   W,H,D,C,l,filename?filename:("(FILE*)")); \
43256           if (W*H*D*C>0) { \
43257             CImg<Tss> raw; \
43258             CImg<T> &img = _data[l]; \
43259             img.assign(W,H,D,C); \
43260             T *ptrd = img._data; \
43261             if (err==5) _cimgz_load_cimg_case(Tss) \
43262             else for (long to_read = (long)img.size(); to_read>0; ) { \
43263               raw.assign(cimg::min(to_read,cimg_iobuffer)); \
43264               cimg::fread(raw._data,raw._width,nfile); \
43265               if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); \
43266               to_read-=raw._width; \
43267               const Tss *ptrs = raw._data; \
43268               for (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
43269             } \
43270           } \
43271         } \
43272         loaded = true; \
43273       }
43274 
43275       if (!filename && !file)
43276         throw CImgArgumentException(_cimglist_instance
43277                                     "load_cimg() : Specified filename is (null).",
43278                                     cimglist_instance);
43279 
43280       const int cimg_iobuffer = 12*1024*1024;
43281       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
43282       bool loaded = false, endian = cimg::endianness();
43283       char tmp[256] = { 0 }, str_pixeltype[256] = { 0 }, str_endian[256] = { 0 };
43284       unsigned int j, err, N = 0, W, H, D, C, csiz;
43285       int i;
43286       do {
43287         j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
43288       } while (*tmp=='#' && i!=EOF);
43289       err = std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype,str_endian);
43290       if (err<2) {
43291         if (!file) cimg::fclose(nfile);
43292         throw CImgIOException(_cimglist_instance
43293                               "load_cimg() : CImg header not found in file '%s'.",
43294                               cimglist_instance,
43295                               filename?filename:"(FILE*)");
43296       }
43297       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
43298       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
43299       assign(N);
43300       _cimg_load_cimg_case("bool",bool);
43301       _cimg_load_cimg_case("unsigned_char",unsigned char);
43302       _cimg_load_cimg_case("uchar",unsigned char);
43303       _cimg_load_cimg_case("char",char);
43304       _cimg_load_cimg_case("unsigned_short",unsigned short);
43305       _cimg_load_cimg_case("ushort",unsigned short);
43306       _cimg_load_cimg_case("short",short);
43307       _cimg_load_cimg_case("unsigned_int",unsigned int);
43308       _cimg_load_cimg_case("uint",unsigned int);
43309       _cimg_load_cimg_case("int",int);
43310       _cimg_load_cimg_case("unsigned_long",unsigned long);
43311       _cimg_load_cimg_case("ulong",unsigned long);
43312       _cimg_load_cimg_case("long",long);
43313       _cimg_load_cimg_case("float",float);
43314       _cimg_load_cimg_case("double",double);
43315       if (!loaded) {
43316         if (!file) cimg::fclose(nfile);
43317         throw CImgIOException(_cimglist_instance
43318                               "load_cimg() : Unsupported pixel type '%s' for file '%s'.",
43319                               cimglist_instance,
43320                               str_pixeltype,filename?filename:"(FILE*)");
43321       }
43322       if (!file) cimg::fclose(nfile);
43323       return *this;
43324     }
43325 
43326     //! Load a sublist list from a (non compressed) .cimg file.
43327     /**
43328       \param filename Filename to read data from.
43329       \param n0 Starting index of images to read.
43330       \param n1 Ending index of images to read.
43331       \param x0 Starting X-coordinates of image regions to read.
43332       \param y0 Starting Y-coordinates of image regions to read.
43333       \param z0 Starting Z-coordinates of image regions to read.
43334       \param c0 Starting C-coordinates of image regions to read.
43335       \param x1 Ending X-coordinates of image regions to read.
43336       \param y1 Ending Y-coordinates of image regions to read.
43337       \param z1 Ending Z-coordinates of image regions to read.
43338       \param c1 Ending C-coordinates of image regions to read.
43339     **/
43340     CImgList<T>& load_cimg(const char *const filename,
43341                            const unsigned int n0, const unsigned int n1,
43342                            const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
43343                            const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1) {
43344       return _load_cimg(0,filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
43345     }
43346 
43347     //! Load a sublist list from a (non compressed) .cimg file \newinstance.
43348     static CImgList<T> get_load_cimg(const char *const filename,
43349                                      const unsigned int n0, const unsigned int n1,
43350                                      const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
43351                                      const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1) {
43352       return CImgList<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
43353     }
43354 
43355     //! Load a sub-image list from a (non compressed) .cimg file.
43356     /**
43357       \param file File to read data from.
43358       \param n0 Starting index of images to read.
43359       \param n1 Ending index of images to read.
43360       \param x0 Starting X-coordinates of image regions to read.
43361       \param y0 Starting Y-coordinates of image regions to read.
43362       \param z0 Starting Z-coordinates of image regions to read.
43363       \param c0 Starting C-coordinates of image regions to read.
43364       \param x1 Ending X-coordinates of image regions to read.
43365       \param y1 Ending Y-coordinates of image regions to read.
43366       \param z1 Ending Z-coordinates of image regions to read.
43367       \param c1 Ending C-coordinates of image regions to read.
43368     **/
43369     CImgList<T>& load_cimg(std::FILE *const file,
43370                            const unsigned int n0, const unsigned int n1,
43371                            const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
43372                            const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1) {
43373       return _load_cimg(file,0,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
43374     }
43375 
43376     //! Load a sub-image list from a (non compressed) .cimg file \newinstance.
43377     static CImgList<T> get_load_cimg(std::FILE *const file,
43378                                      const unsigned int n0, const unsigned int n1,
43379                                      const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
43380                                      const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1) {
43381       return CImgList<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
43382     }
43383 
43384     CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename,
43385                             const unsigned int n0, const unsigned int n1,
43386                             const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
43387                             const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1) {
43388 #define _cimg_load_cimg_case2(Ts,Tss) \
43389       if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
43390         for (unsigned int l = 0; l<=nn1; ++l) { \
43391           j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0) tmp[j++] = (char)i; tmp[j] = 0; \
43392           W = H = D = C = 0; \
43393           if (std::sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
43394             throw CImgIOException(_cimglist_instance \
43395                                   "load_cimg() : Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'", \
43396                                   cimglist_instance, \
43397                                   W,H,D,C,l,filename?filename:"(FILE*)"); \
43398           if (W*H*D*C>0) { \
43399             if (l<n0 || x0>=W || y0>=H || z0>=D || c0>=D) std::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
43400             else { \
43401               const unsigned int \
43402                 nx1 = x1>=W?W-1:x1, \
43403                 ny1 = y1>=H?H-1:y1, \
43404                 nz1 = z1>=D?D-1:z1, \
43405                 nc1 = c1>=C?C-1:c1; \
43406               CImg<Tss> raw(1 + nx1 - x0); \
43407               CImg<T> &img = _data[l - n0]; \
43408               img.assign(1 + nx1 - x0,1 + ny1 - y0,1 + nz1 - z0,1 + nc1 - c0); \
43409               T *ptrd = img._data; \
43410               const unsigned int skipvb = c0*W*H*D*sizeof(Tss); \
43411               if (skipvb) std::fseek(nfile,skipvb,SEEK_CUR); \
43412               for (unsigned int v = 1 + nc1 - c0; v; --v) { \
43413                 const unsigned int skipzb = z0*W*H*sizeof(Tss); \
43414                 if (skipzb) std::fseek(nfile,skipzb,SEEK_CUR); \
43415                 for (unsigned int z = 1 + nz1 - z0; z; --z) { \
43416                   const unsigned int skipyb = y0*W*sizeof(Tss); \
43417                   if (skipyb) std::fseek(nfile,skipyb,SEEK_CUR); \
43418                   for (unsigned int y = 1 + ny1 - y0; y; --y) { \
43419                     const unsigned int skipxb = x0*sizeof(Tss); \
43420                     if (skipxb) std::fseek(nfile,skipxb,SEEK_CUR); \
43421                     cimg::fread(raw._data,raw._width,nfile); \
43422                     if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); \
43423                     const Tss *ptrs = raw._data; \
43424                     for (unsigned int off = raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
43425                     const unsigned int skipxe = (W-1-nx1)*sizeof(Tss); \
43426                     if (skipxe) std::fseek(nfile,skipxe,SEEK_CUR); \
43427                   } \
43428                   const unsigned int skipye = (H-1-ny1)*W*sizeof(Tss); \
43429                   if (skipye) std::fseek(nfile,skipye,SEEK_CUR); \
43430                 } \
43431                 const unsigned int skipze = (D-1-nz1)*W*H*sizeof(Tss); \
43432                 if (skipze) std::fseek(nfile,skipze,SEEK_CUR); \
43433               } \
43434               const unsigned int skipve = (C-1-nc1)*W*H*D*sizeof(Tss); \
43435               if (skipve) std::fseek(nfile,skipve,SEEK_CUR); \
43436             } \
43437           } \
43438         } \
43439         loaded = true; \
43440       }
43441 
43442       if (!filename && !file)
43443         throw CImgArgumentException(_cimglist_instance
43444                                     "load_cimg() : Specified filename is (null).",
43445                                     cimglist_instance);
43446 
43447       if (n1<n0 || x1<x0 || y1<y0 || z1<z0 || c1<c0)
43448         throw CImgArgumentException(_cimglist_instance
43449                                     "load_cimg() : Invalid specified sub-region coordinates [%u->%u] (%u,%u,%u,%u)->(%u,%u,%u,%u) for file '%s'.",
43450                                     cimglist_instance,
43451                                     n0,n1,x0,y0,z0,c0,x1,y1,z1,filename?filename:"(FILE*)");
43452 
43453       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
43454       bool loaded = false, endian = cimg::endianness();
43455       char tmp[256] = { 0 }, str_pixeltype[256] = { 0 }, str_endian[256] = { 0 };
43456       unsigned int j, err, N, W, H, D, C;
43457       int i;
43458       j = 0; while((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
43459       err = std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype,str_endian);
43460       if (err<2) {
43461         if (!file) cimg::fclose(nfile);
43462         throw CImgIOException(_cimglist_instance
43463                               "load_cimg() : CImg header not found in file '%s'.",
43464                               cimglist_instance,
43465                               filename?filename:"(FILE*)");
43466       }
43467       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
43468       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
43469       const unsigned int nn1 = n1>=N?N-1:n1;
43470       assign(1+nn1-n0);
43471       _cimg_load_cimg_case2("bool",bool);
43472       _cimg_load_cimg_case2("unsigned_char",unsigned char);
43473       _cimg_load_cimg_case2("uchar",unsigned char);
43474       _cimg_load_cimg_case2("char",char);
43475       _cimg_load_cimg_case2("unsigned_short",unsigned short);
43476       _cimg_load_cimg_case2("ushort",unsigned short);
43477       _cimg_load_cimg_case2("short",short);
43478       _cimg_load_cimg_case2("unsigned_int",unsigned int);
43479       _cimg_load_cimg_case2("uint",unsigned int);
43480       _cimg_load_cimg_case2("int",int);
43481       _cimg_load_cimg_case2("unsigned_long",unsigned long);
43482       _cimg_load_cimg_case2("ulong",unsigned long);
43483       _cimg_load_cimg_case2("long",long);
43484       _cimg_load_cimg_case2("float",float);
43485       _cimg_load_cimg_case2("double",double);
43486       if (!loaded) {
43487         if (!file) cimg::fclose(nfile);
43488         throw CImgIOException(_cimglist_instance
43489                               "load_cimg() : Unsupported pixel type '%s' for file '%s'.",
43490                               cimglist_instance,
43491                               str_pixeltype,filename?filename:"(FILE*)");
43492       }
43493       if (!file) cimg::fclose(nfile);
43494       return *this;
43495     }
43496 
43497     //! Load a list from a PAR/REC (Philips) file.
43498     /**
43499       \param filename Filename to read data from.
43500     **/
43501     CImgList<T>& load_parrec(const char *const filename) {
43502       if (!filename)
43503         throw CImgArgumentException(_cimglist_instance
43504                                     "load_parrec() : Specified filename is (null).",
43505                                     cimglist_instance);
43506 
43507       char body[1024] = { 0 }, filenamepar[1024] = { 0 }, filenamerec[1024] = { 0 };
43508       const char *const ext = cimg::split_filename(filename,body);
43509       if (!std::strcmp(ext,"par")) { std::strncpy(filenamepar,filename,sizeof(filenamepar)-1); cimg_snprintf(filenamerec,sizeof(filenamerec),"%s.rec",body); }
43510       if (!std::strcmp(ext,"PAR")) { std::strncpy(filenamepar,filename,sizeof(filenamepar)-1); cimg_snprintf(filenamerec,sizeof(filenamerec),"%s.REC",body); }
43511       if (!std::strcmp(ext,"rec")) { std::strncpy(filenamerec,filename,sizeof(filenamerec)-1); cimg_snprintf(filenamepar,sizeof(filenamepar),"%s.par",body); }
43512       if (!std::strcmp(ext,"REC")) { std::strncpy(filenamerec,filename,sizeof(filenamerec)-1); cimg_snprintf(filenamepar,sizeof(filenamepar),"%s.PAR",body); }
43513       std::FILE *file = cimg::fopen(filenamepar,"r");
43514 
43515       // Parse header file
43516       CImgList<floatT> st_slices;
43517       CImgList<uintT> st_global;
43518       int err;
43519       char line[256] = { 0 };
43520       do { err=std::fscanf(file,"%255[^\n]%*c",line); } while (err!=EOF && (*line=='#' || *line=='.'));
43521       do {
43522         unsigned int sn,size_x,size_y,pixsize;
43523         float rs,ri,ss;
43524         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);
43525         if (err==7) {
43526           CImg<floatT>::vector((float)sn,(float)pixsize,(float)size_x,(float)size_y,ri,rs,ss,0).move_to(st_slices);
43527           unsigned int i; for (i = 0; i<st_global._width && sn<=st_global[i][2]; ++i) {}
43528           if (i==st_global._width) CImg<uintT>::vector(size_x,size_y,sn).move_to(st_global);
43529           else {
43530             CImg<uintT> &vec = st_global[i];
43531             if (size_x>vec[0]) vec[0] = size_x;
43532             if (size_y>vec[1]) vec[1] = size_y;
43533             vec[2] = sn;
43534           }
43535           st_slices[st_slices._width-1][7] = (float)i;
43536         }
43537       } while (err==7);
43538 
43539       // Read data
43540       std::FILE *file2 = cimg::fopen(filenamerec,"rb");
43541       cimglist_for(st_global,l) {
43542         const CImg<uintT>& vec = st_global[l];
43543         CImg<T>(vec[0],vec[1],vec[2]).move_to(*this);
43544       }
43545 
43546       cimglist_for(st_slices,l) {
43547         const CImg<floatT>& vec = st_slices[l];
43548         const unsigned int
43549           sn = (unsigned int)vec[0] - 1,
43550           pixsize = (unsigned int)vec[1],
43551           size_x = (unsigned int)vec[2],
43552           size_y = (unsigned int)vec[3],
43553           imn = (unsigned int)vec[7];
43554         const float ri = vec[4], rs = vec[5], ss = vec[6];
43555         switch (pixsize) {
43556         case 8 : {
43557           CImg<ucharT> buf(size_x,size_y);
43558           cimg::fread(buf._data,size_x*size_y,file2);
43559           if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
43560           CImg<T>& img = (*this)[imn];
43561           cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
43562         } break;
43563         case 16 : {
43564           CImg<ushortT> buf(size_x,size_y);
43565           cimg::fread(buf._data,size_x*size_y,file2);
43566           if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
43567           CImg<T>& img = (*this)[imn];
43568           cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
43569         } break;
43570         case 32 : {
43571           CImg<uintT> buf(size_x,size_y);
43572           cimg::fread(buf._data,size_x*size_y,file2);
43573           if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
43574           CImg<T>& img = (*this)[imn];
43575           cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
43576         } break;
43577         default :
43578           cimg::fclose(file);
43579           cimg::fclose(file2);
43580           throw CImgIOException(_cimglist_instance
43581                                 "load_parrec() : Unsupported %d-bits pixel type for file '%s'.",
43582                                 cimglist_instance,
43583                                 pixsize,filename);
43584         }
43585       }
43586       cimg::fclose(file);
43587       cimg::fclose(file2);
43588       if (!_width)
43589         throw CImgIOException(_cimglist_instance
43590                               "load_parrec() : Failed to recognize valid PAR-REC data in file '%s'.",
43591                               cimglist_instance,
43592                               filename);
43593       return *this;
43594     }
43595 
43596     //! Load a list from a PAR/REC (Philips) file \newinstance.
43597     static CImgList<T> get_load_parrec(const char *const filename) {
43598       return CImgList<T>().load_parrec(filename);
43599     }
43600 
43601     //! Load a list from a YUV image sequence file.
43602     /**
43603         \param filename Filename to read data from.
43604         \param size_x Width of the images.
43605         \param size_y Height of the images.
43606         \param first_frame Index of first image frame to read.
43607         \param last_frame Index of last image frame to read.
43608         \param step_frame Step applied between each frame.
43609         \param yuv2rgb Apply YUV to RGB transformation during reading.
43610     **/
43611     CImgList<T>& load_yuv(const char *const filename,
43612                           const unsigned int size_x, const unsigned int size_y,
43613                           const unsigned int first_frame=0, const unsigned int last_frame=~0U,
43614                           const unsigned int step_frame=1, const bool yuv2rgb=true) {
43615       return _load_yuv(0,filename,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb);
43616     }
43617 
43618     //! Load a list from a YUV image sequence file \newinstance.
43619     static CImgList<T> get_load_yuv(const char *const filename,
43620                                     const unsigned int size_x, const unsigned int size_y=1,
43621                                     const unsigned int first_frame=0, const unsigned int last_frame=~0U,
43622                                     const unsigned int step_frame=1, const bool yuv2rgb=true) {
43623       return CImgList<T>().load_yuv(filename,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb);
43624     }
43625 
43626     //! Load a list from an image sequence YUV file \overloading.
43627     CImgList<T>& load_yuv(std::FILE *const file,
43628                           const unsigned int size_x, const unsigned int size_y,
43629                           const unsigned int first_frame=0, const unsigned int last_frame=~0U,
43630                           const unsigned int step_frame=1, const bool yuv2rgb=true) {
43631       return _load_yuv(file,0,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb);
43632     }
43633 
43634     //! Load a list from an image sequence YUV file \newinstance.
43635     static CImgList<T> get_load_yuv(std::FILE *const file,
43636                                     const unsigned int size_x, const unsigned int size_y=1,
43637                                     const unsigned int first_frame=0, const unsigned int last_frame=~0U,
43638                                     const unsigned int step_frame=1, const bool yuv2rgb=true) {
43639       return CImgList<T>().load_yuv(file,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb);
43640     }
43641 
43642     CImgList<T>& _load_yuv(std::FILE *const file, const char *const filename,
43643                            const unsigned int size_x, const unsigned int size_y,
43644                            const unsigned int first_frame, const unsigned int last_frame,
43645                            const unsigned int step_frame, const bool yuv2rgb) {
43646       if (!filename && !file)
43647         throw CImgArgumentException(_cimglist_instance
43648                                     "load_yuv() : Specified filename is (null).",
43649                                     cimglist_instance);
43650       if (size_x%2 || size_y%2)
43651         throw CImgArgumentException(_cimglist_instance
43652                                     "load_yuv() : Invalid odd XY dimensions %ux%u in file '%s'.",
43653                                     cimglist_instance,
43654                                     size_x,size_y,filename?filename:"(FILE*)");
43655       if (!size_x || !size_y)
43656         throw CImgArgumentException(_cimglist_instance
43657                                     "load_yuv() : Invalid sequence size (%u,%u) in file '%s'.",
43658                                     cimglist_instance,
43659                                     size_x,size_y,filename?filename:"(FILE*)");
43660 
43661       const unsigned int
43662         nfirst_frame = first_frame<last_frame?first_frame:last_frame,
43663         nlast_frame = first_frame<last_frame?last_frame:first_frame,
43664         nstep_frame = step_frame?step_frame:1;
43665 
43666       CImg<ucharT> tmp(size_x,size_y,1,3), UV(size_x/2,size_y/2,1,2);
43667       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
43668       bool stopflag = false;
43669       int err;
43670       if (nfirst_frame) {
43671         err = std::fseek(nfile,nfirst_frame*(size_x*size_y + size_x*size_y/2),SEEK_CUR);
43672         if (err) {
43673           if (!file) cimg::fclose(nfile);
43674           throw CImgIOException(_cimglist_instance
43675                                 "load_yuv() : File '%s' doesn't contain frame number %u.",
43676                                 cimglist_instance,
43677                                 filename?filename:"(FILE*)",nfirst_frame);
43678         }
43679       }
43680       unsigned int frame;
43681       for (frame = nfirst_frame; !stopflag && frame<=nlast_frame; frame+=nstep_frame) {
43682         tmp.fill(0);
43683         // *TRY* to read the luminance part, do not replace by cimg::fread !
43684         err = (int)std::fread((void*)(tmp._data),1,(unsigned long)tmp._width*tmp._height,nfile);
43685         if (err!=(int)(tmp._width*tmp._height)) {
43686           stopflag = true;
43687           if (err>0)
43688             cimg::warn(_cimglist_instance
43689                        "load_yuv() : File '%s' contains incomplete data or given image dimensions (%u,%u) are incorrect.",
43690                        cimglist_instance,
43691                        filename?filename:"(FILE*)",size_x,size_y);
43692         } else {
43693           UV.fill(0);
43694           // *TRY* to read the luminance part, do not replace by cimg::fread !
43695           err = (int)std::fread((void*)(UV._data),1,(size_t)(UV.size()),nfile);
43696           if (err!=(int)(UV.size())) {
43697             stopflag = true;
43698             if (err>0)
43699               cimg::warn(_cimglist_instance
43700                          "load_yuv() : File '%s' contains incomplete data or given image dimensions (%u,%u) are incorrect.",
43701                          cimglist_instance,
43702                          filename?filename:"(FILE*)",size_x,size_y);
43703           } else {
43704             cimg_forXY(UV,x,y) {
43705               const int x2 = x*2, y2 = y*2;
43706               tmp(x2,y2,1) = tmp(x2+1,y2,1) = tmp(x2,y2+1,1) = tmp(x2+1,y2+1,1) = UV(x,y,0);
43707               tmp(x2,y2,2) = tmp(x2+1,y2,2) = tmp(x2,y2+1,2) = tmp(x2+1,y2+1,2) = UV(x,y,1);
43708             }
43709             if (yuv2rgb) tmp.YCbCrtoRGB();
43710             insert(tmp);
43711             if (nstep_frame>1) std::fseek(nfile,(nstep_frame-1)*(size_x*size_y + size_x*size_y/2),SEEK_CUR);
43712           }
43713         }
43714       }
43715       if (stopflag && nlast_frame!=~0U && frame!=nlast_frame)
43716         cimg::warn(_cimglist_instance
43717                    "load_yuv() : Frame %d not reached since only %u frames were found in file '%s'.",
43718                    cimglist_instance,
43719                    nlast_frame,frame-1,filename?filename:"(FILE*)");
43720 
43721       if (!file) cimg::fclose(nfile);
43722       return *this;
43723     }
43724 
43725     //! Load an image from a video file, using ffmpeg libraries.
43726     /**
43727       \param filename Filename, as a C-string.
43728       \param first_frame Index of the first frame to read.
43729       \param last_frame Index of the last frame to read.
43730       \param step_frame Step value for frame reading.
43731       \param pixel_format To be documented.
43732       \param resume To be documented.
43733     **/
43734     // This piece of code has been firstly created by David Starweather (starkdg(at)users(dot)sourceforge(dot)net)
43735     // I modified it afterwards for direct inclusion in the library core.
43736     CImgList<T>& load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
43737                              const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false) {
43738       if (!filename)
43739         throw CImgArgumentException(_cimglist_instance
43740                                     "load_ffmpeg() : Specified filename is (null).",
43741                                     cimglist_instance);
43742 
43743       const unsigned int
43744         nfirst_frame = first_frame<last_frame?first_frame:last_frame,
43745         nlast_frame = first_frame<last_frame?last_frame:first_frame,
43746         nstep_frame = step_frame?step_frame:1;
43747       assign();
43748 
43749 #ifndef cimg_use_ffmpeg
43750       if ((nfirst_frame || nlast_frame!=~0U || nstep_frame>1) || (resume && (pixel_format || !pixel_format)))
43751         throw CImgArgumentException(_cimglist_instance
43752                                     "load_ffmpeg() : Unable to load sub-frames from file '%s' unless libffmpeg is enabled.",
43753                                     cimglist_instance,
43754                                     filename);
43755 
43756       return load_ffmpeg_external(filename);
43757 #else
43758       const PixelFormat ffmpeg_pixfmt = pixel_format?PIX_FMT_RGB24:PIX_FMT_GRAY8;
43759       avcodec_register_all();
43760       av_register_all();
43761       static AVFormatContext *format_ctx = 0;
43762       static AVCodecContext *codec_ctx = 0;
43763       static AVCodec *codec = 0;
43764       static AVFrame *avframe = avcodec_alloc_frame(), *converted_frame = avcodec_alloc_frame();
43765       static int vstream = 0;
43766 
43767       if (resume) {
43768         if (!format_ctx || !codec_ctx || !codec || !avframe || !converted_frame)
43769           throw CImgArgumentException(_cimglist_instance
43770                                       "load_ffmpeg() : Failed to resume loading of file '%s', due to unallocated FFMPEG structures.",
43771                                       cimglist_instance,
43772                                       filename);
43773       } else {
43774         // Open video file, find main video stream and codec.
43775         if (format_ctx) av_close_input_file(format_ctx);
43776         if (av_open_input_file(&format_ctx,filename,0,0,0)!=0)
43777           throw CImgIOException(_cimglist_instance
43778                                 "load_ffmpeg() : Failed to open file '%s'.",
43779                                 cimglist_instance,
43780                                 filename);
43781 
43782         if (!avframe || !converted_frame || av_find_stream_info(format_ctx)<0) {
43783           av_close_input_file(format_ctx); format_ctx = 0;
43784           return load_ffmpeg_external(filename);
43785         }
43786 #if cimg_verbosity>=3
43787         dump_format(format_ctx,0,0,0);
43788 #endif
43789 
43790         // Special command : Return informations on main video stream.
43791         // as a vector 1x4 containing : (nb_frames,width,height,fps).
43792         if (!first_frame && !last_frame && !step_frame) {
43793           for (vstream = 0; vstream<(int)(format_ctx->nb_streams); ++vstream)
43794             if (format_ctx->streams[vstream]->codec->codec_type==CODEC_TYPE_VIDEO) break;
43795           if (vstream==(int)format_ctx->nb_streams) assign();
43796           else {
43797             CImgList<doubleT> timestamps;
43798             int nb_frames;
43799             AVPacket packet;
43800             // Count frames and store timestamps.
43801             for (nb_frames = 0; av_read_frame(format_ctx,&packet)>=0; av_free_packet(&packet))
43802               if (packet.stream_index==vstream) {
43803                 CImg<doubleT>::vector((double)packet.pts).move_to(timestamps);
43804                 ++nb_frames;
43805               }
43806             // Get frame with, height and fps.
43807             const int
43808               framew = format_ctx->streams[vstream]->codec->width,
43809               frameh = format_ctx->streams[vstream]->codec->height;
43810             const float
43811               num = (float)(format_ctx->streams[vstream]->r_frame_rate).num,
43812               den = (float)(format_ctx->streams[vstream]->r_frame_rate).den,
43813               fps = num/den;
43814             // Return infos as a list.
43815             assign(2);
43816             (*this)[0].assign(1,4).fill((T)nb_frames,(T)framew,(T)frameh,(T)fps);
43817             (*this)[1] = (timestamps>'y');
43818           }
43819           av_close_input_file(format_ctx); format_ctx = 0;
43820           return *this;
43821         }
43822 
43823         for (vstream = 0; vstream<(int)(format_ctx->nb_streams) &&
43824                format_ctx->streams[vstream]->codec->codec_type!=CODEC_TYPE_VIDEO; ) ++vstream;
43825         if (vstream==(int)format_ctx->nb_streams) {
43826           av_close_input_file(format_ctx); format_ctx = 0;
43827           return load_ffmpeg_external(filename);
43828         }
43829         codec_ctx = format_ctx->streams[vstream]->codec;
43830         codec = avcodec_find_decoder(codec_ctx->codec_id);
43831         if (!codec) {
43832           return load_ffmpeg_external(filename);
43833         }
43834         if (avcodec_open(codec_ctx,codec)<0) { // Open codec
43835           return load_ffmpeg_external(filename);
43836         }
43837       }
43838 
43839       // Read video frames
43840       const unsigned int numBytes = avpicture_get_size(ffmpeg_pixfmt,codec_ctx->width,codec_ctx->height);
43841       uint8_t *const buffer = new uint8_t[numBytes];
43842       avpicture_fill((AVPicture *)converted_frame,buffer,ffmpeg_pixfmt,codec_ctx->width,codec_ctx->height);
43843       const T foo = (T)0;
43844       AVPacket packet;
43845       for (unsigned int frame = 0, next_frame = nfirst_frame; frame<=nlast_frame && av_read_frame(format_ctx,&packet)>=0; ) {
43846         if (packet.stream_index==(int)vstream) {
43847           int decoded = 0;
43848 #if defined(AV_VERSION_INT)
43849 #if LIBAVCODEC_VERSION_INT<AV_VERSION_INT(52,26,0)
43850           avcodec_decode_video(codec_ctx,avframe,&decoded,packet.data, packet.size);
43851 #else
43852           avcodec_decode_video2(codec_ctx,avframe,&decoded,&packet);
43853 #endif
43854 #else
43855           avcodec_decode_video(codec_ctx,avframe,&decoded,packet.data, packet.size);
43856 #endif
43857           if (decoded) {
43858             if (frame==next_frame) {
43859               SwsContext *c = sws_getContext(codec_ctx->width,codec_ctx->height,codec_ctx->pix_fmt,codec_ctx->width,
43860                                              codec_ctx->height,ffmpeg_pixfmt,1,0,0,0);
43861               sws_scale(c,avframe->data,avframe->linesize,0,codec_ctx->height,converted_frame->data,converted_frame->linesize);
43862               if (ffmpeg_pixfmt==PIX_FMT_RGB24) {
43863                 CImg<ucharT> next_image(*converted_frame->data,3,codec_ctx->width,codec_ctx->height,1,true);
43864                 next_image._get_permute_axes("yzcx",foo).move_to(*this);
43865               } else {
43866                 CImg<ucharT> next_image(*converted_frame->data,1,codec_ctx->width,codec_ctx->height,1,true);
43867                 next_image._get_permute_axes("yzcx",foo).move_to(*this);
43868               }
43869               next_frame+=nstep_frame;
43870             }
43871             ++frame;
43872           }
43873           av_free_packet(&packet);
43874           if (next_frame>nlast_frame) break;
43875         }
43876       }
43877       delete[] buffer;
43878 #endif
43879       return *this;
43880     }
43881 
43882     //! Load an image from a video file, using ffmpeg libraries \newinstance.
43883     static CImgList<T> get_load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
43884                                        const unsigned int step_frame=1, const bool pixel_format=true) {
43885       return CImgList<T>().load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format);
43886     }
43887 
43888     //! Load an image from a video file using the external tool 'ffmpeg'.
43889     /**
43890       \param filename Filename to read data from.
43891     **/
43892     CImgList<T>& load_ffmpeg_external(const char *const filename) {
43893       if (!filename)
43894         throw CImgArgumentException(_cimglist_instance
43895                                     "load_ffmpeg_external() : Specified filename is (null).",
43896                                     cimglist_instance);
43897       std::fclose(cimg::fopen(filename,"rb"));            // Check if file exists.
43898       char command[1024] = { 0 }, filetmp[512] = { 0 }, filetmp2[512] = { 0 };
43899       std::FILE *file = 0;
43900       do {
43901         cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
43902         cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_000001.ppm",filetmp);
43903         if ((file=std::fopen(filetmp2,"rb"))!=0) cimg::fclose(file);
43904       } while (file);
43905       cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_%%6d.ppm",filetmp);
43906 #if cimg_OS!=2
43907       cimg_snprintf(command,sizeof(command),"%s -i \"%s\" %s >/dev/null 2>&1",cimg::ffmpeg_path(),filename,filetmp2);
43908 #else
43909       cimg_snprintf(command,sizeof(command),"\"%s -i \"%s\" %s\" >NUL 2>&1",cimg::ffmpeg_path(),filename,filetmp2);
43910 #endif
43911       cimg::system(command,0);
43912       const unsigned int omode = cimg::exception_mode();
43913       cimg::exception_mode() = 0;
43914       assign();
43915       unsigned int i = 1;
43916       for (bool stopflag = false; !stopflag; ++i) {
43917         cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_%.6u.ppm",filetmp,i);
43918         CImg<T> img;
43919         try { img.load_pnm(filetmp2); }
43920         catch (CImgException&) { stopflag = true; }
43921         if (img) { img.move_to(*this); std::remove(filetmp2); }
43922       }
43923       cimg::exception_mode() = omode;
43924       if (is_empty())
43925         throw CImgIOException(_cimglist_instance
43926                               "load_ffmpeg_external() : Failed to open file '%s' with external command 'ffmpeg'.",
43927                               cimglist_instance,
43928                               filename);
43929       return *this;
43930     }
43931 
43932     //! Load an image from a video file using the external tool 'ffmpeg' \newinstance.
43933     static CImgList<T> get_load_ffmpeg_external(const char *const filename) {
43934       return CImgList<T>().load_ffmpeg_external(filename);
43935     }
43936 
43937     //! Load a gzipped list, using external tool 'gunzip'.
43938     /**
43939       \param filename Filename to read data from.
43940     **/
43941     CImgList<T>& load_gzip_external(const char *const filename) {
43942       if (!filename)
43943         throw CImgIOException(_cimglist_instance
43944                               "load_gzip_external() : Specified filename is (null).",
43945                               cimglist_instance);
43946       std::fclose(cimg::fopen(filename,"rb"));            // Check if file exists.
43947       char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
43948       const char
43949         *ext = cimg::split_filename(filename,body),
43950         *ext2 = cimg::split_filename(body,0);
43951       std::FILE *file = 0;
43952       do {
43953         if (!cimg::strcasecmp(ext,"gz")) {
43954           if (*ext2) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
43955           else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
43956         } else {
43957           if (*ext) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
43958           else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
43959         }
43960         if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
43961       } while (file);
43962       cimg_snprintf(command,sizeof(command),"%s -c \"%s\" > %s",cimg::gunzip_path(),filename,filetmp);
43963       cimg::system(command);
43964       if (!(file = std::fopen(filetmp,"rb"))) {
43965         cimg::fclose(cimg::fopen(filename,"r"));
43966         throw CImgIOException(_cimglist_instance
43967                               "load_gzip_external() : Failed to open file '%s'.",
43968                               cimglist_instance,
43969                               filename);
43970 
43971       } else cimg::fclose(file);
43972       load(filetmp);
43973       std::remove(filetmp);
43974       return *this;
43975     }
43976 
43977     //! Load a gzipped list, using external tool 'gunzip' \newinstance.
43978     static CImgList<T> get_load_gzip_external(const char *const filename) {
43979       return CImgList<T>().load_gzip_external(filename);
43980     }
43981 
43982     //! Load a 3d object from a .OFF file.
43983     /**
43984       \param filename Filename to read data from.
43985       \param[out] primitives At return, contains the list of 3d object primitives.
43986       \param[out] colors At return, contains the list of 3d object colors.
43987       \return List of 3d object vertices.
43988     **/
43989     template<typename tf, typename tc>
43990     CImgList<T>& load_off(const char *const filename,
43991                           CImgList<tf>& primitives, CImgList<tc>& colors) {
43992       return get_load_off(filename,primitives,colors).move_to(*this);
43993     }
43994 
43995     //! Load a 3d object from a .OFF file \newinstance.
43996     template<typename tf, typename tc>
43997       static CImgList<T> get_load_off(const char *const filename,
43998                                       CImgList<tf>& primitives, CImgList<tc>& colors) {
43999       return CImg<T>().load_off(filename,primitives,colors)<'x';
44000     }
44001 
44002     //! Load images from a TIFF file.
44003     /**
44004         \param filename Filename to read data from.
44005         \param first_frame Index of first image frame to read.
44006         \param last_frame Index of last image frame to read.
44007         \param step_frame Step applied between each frame.
44008     **/
44009     CImgList<T>& load_tiff(const char *const filename,
44010                            const unsigned int first_frame=0, const unsigned int last_frame=~0U,
44011                            const unsigned int step_frame=1) {
44012       const unsigned int
44013         nfirst_frame = first_frame<last_frame?first_frame:last_frame,
44014         nstep_frame = step_frame?step_frame:1;
44015       unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
44016 #ifndef cimg_use_tiff
44017       if (nfirst_frame || nlast_frame!=~0U || nstep_frame!=1)
44018         throw CImgArgumentException(_cimglist_instance
44019                                     "load_tiff() : Unable to load sub-images from file '%s' unless libtiff is enabled.",
44020                                     cimglist_instance,
44021                                     filename);
44022 
44023       return assign(CImg<T>::get_load_tiff(filename));
44024 #else
44025       TIFF *tif = TIFFOpen(filename,"r");
44026       if (tif) {
44027         unsigned int nb_images = 0;
44028         do ++nb_images; while (TIFFReadDirectory(tif));
44029         if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images))
44030           cimg::warn(_cimglist_instance
44031                      "load_tiff() : Invalid specified frame range is [%u,%u] (step %u) since file '%s' contains %u image(s).",
44032                      cimglist_instance,
44033                      nfirst_frame,nlast_frame,nstep_frame,filename,nb_images);
44034 
44035         if (nfirst_frame>=nb_images) return assign();
44036         if (nlast_frame>=nb_images) nlast_frame = nb_images-1;
44037         assign(1+(nlast_frame-nfirst_frame)/nstep_frame);
44038         TIFFSetDirectory(tif,0);
44039 #if cimg_verbosity>=3
44040         TIFFSetWarningHandler(0);
44041         TIFFSetErrorHandler(0);
44042 #endif
44043         cimglist_for(*this,l) _data[l]._load_tiff(tif,nfirst_frame + l*nstep_frame);
44044         TIFFClose(tif);
44045       } else throw CImgIOException(_cimglist_instance
44046                                    "load_tiff() : Failed to open file '%s'.",
44047                                    cimglist_instance,
44048                                    filename);
44049       return *this;
44050 #endif
44051     }
44052 
44053     //! Load a multi-page TIFF file \newinstance.
44054     static CImgList<T> get_load_tiff(const char *const filename,
44055                                      const unsigned int first_frame=0, const unsigned int last_frame=~0U,
44056                                      const unsigned int step_frame=1) {
44057       return CImgList<T>().load_tiff(filename,first_frame,last_frame,step_frame);
44058     }
44059 
44060     //@}
44061     //----------------------------------
44062     //
44063     //! \name Data Output
44064     //@{
44065     //----------------------------------
44066 
44067     //! Print informations about the list on the standard output.
44068     /**
44069       \param title Label set to the informations displayed.
44070       \param display_stats Tells if image statistics must be computed and displayed.
44071     **/
44072     const CImgList<T>& print(const char *const title=0, const bool display_stats=true) const {
44073       unsigned int msiz = 0;
44074       cimglist_for(*this,l) msiz+=_data[l].size();
44075       msiz*=sizeof(T);
44076       const unsigned int mdisp = msiz<8*1024?0:(msiz<8*1024*1024?1:2);
44077       char _title[64] = { 0 };
44078       if (!title) cimg_snprintf(_title,sizeof(_title),"CImgList<%s>",pixel_type());
44079       std::fprintf(cimg::output(),"%s%s%s%s : %sthis%s = %p, %ssize%s = %u/%u [%u %s], %sdata%s = (CImg<%s>*)%p",
44080                    cimg::t_magenta,cimg::t_bold,title?title:_title,cimg::t_normal,
44081                    cimg::t_bold,cimg::t_normal,(void*)this,
44082                    cimg::t_bold,cimg::t_normal,_width,_allocated_width,
44083                    mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)),
44084                    mdisp==0?"b":(mdisp==1?"Kio":"Mio"),
44085                    cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin());
44086       if (_data) std::fprintf(cimg::output(),"..%p.\n",(void*)((char*)end()-1));
44087       else std::fprintf(cimg::output(),".\n");
44088 
44089       char tmp[16] = { 0 };
44090       cimglist_for(*this,ll) {
44091         cimg_snprintf(tmp,sizeof(tmp),"[%d]",ll);
44092         std::fprintf(cimg::output(),"  ");
44093         _data[ll].print(tmp,display_stats);
44094         if (ll==3 && _width>8) { ll = _width-5; std::fprintf(cimg::output(),"  ...\n"); }
44095       }
44096       std::fflush(cimg::output());
44097       return *this;
44098     }
44099 
44100     //! Display the current CImgList instance in an existing CImgDisplay window (by reference).
44101     /**
44102        \param disp Reference to an existing CImgDisplay instance, where the current image list will be displayed.
44103        \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
44104        \param align Appending alignmenet.
44105        \note This function displays the list images of the current CImgList instance into an existing CImgDisplay window.
44106        Images of the list are appended in a single temporarly image for visualization purposes.
44107        The function returns immediately.
44108     **/
44109     const CImgList<T>& display(CImgDisplay &disp, const char axis='x', const float align=0) const {
44110       get_append(axis,align).display(disp);
44111       return *this;
44112     }
44113 
44114     //! Display the current CImgList instance in a new display window.
44115     /**
44116         \param disp Display window.
44117         \param display_info Tells if image informations are displayed on the standard output.
44118        \param axis Alignment axis for images viewing.
44119        \param align Apending alignment.
44120        \note This function opens a new window with a specific title and displays the list images of the current CImgList instance into it.
44121        Images of the list are appended in a single temporarly image for visualization purposes.
44122        The function returns when a key is pressed or the display window is closed by the user.
44123     **/
44124     const CImgList<T>& display(CImgDisplay &disp, const bool display_info,
44125                                const char axis='x', const float align=0) const {
44126       bool is_exit = false;
44127       return _display(disp,0,display_info,axis,align,0,true,is_exit);
44128     }
44129 
44130     //! Display the current CImgList instance in a new display window.
44131     /**
44132       \param title Title of the opening display window.
44133       \param display_info Tells if list informations must be written on standard output.
44134       \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
44135       \param align Appending alignment.
44136     **/
44137     const CImgList<T>& display(const char *const title=0, const bool display_info=true,
44138                                const char axis='x', const float align=0) const {
44139       CImgDisplay disp;
44140       bool is_exit = false;
44141       return _display(disp,title,display_info,axis,align,0,true,is_exit);
44142     }
44143 
44144     const CImgList<T>& _display(CImgDisplay &disp, const char *const title, const bool display_info,
44145                                 const char axis, const float align,
44146                                 const unsigned int orig, const bool is_first_call, bool &is_exit) const {
44147       if (is_empty())
44148         throw CImgInstanceException(_cimglist_instance
44149                                     "display() : Empty instance.",
44150                                     cimglist_instance);
44151       if (!disp) {
44152         if (axis=='x') {
44153           unsigned int sum_width = 0, max_height = 0;
44154           cimglist_for(*this,l) {
44155             const CImg<T> &img = _data[l];
44156             const unsigned int
44157               w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
44158               h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
44159             sum_width+=w;
44160             if (h>max_height) max_height = h;
44161           }
44162           disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:0,1);
44163         } else {
44164           unsigned int max_width = 0, sum_height = 0;
44165           cimglist_for(*this,l) {
44166             const CImg<T> &img = _data[l];
44167             const unsigned int
44168               w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
44169               h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
44170             if (w>max_width) max_width = w;
44171             sum_height+=h;
44172           }
44173           disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:0,1);
44174         }
44175         if (!title) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width);
44176       } else if (title) disp.set_title("%s",title);
44177       const CImg<char> dtitle = CImg<char>::string(disp.title());
44178       if (display_info) print(disp.title());
44179       disp.show().flush();
44180 
44181       if (_width==1) {
44182         const unsigned int dw = disp._width, dh = disp._height;
44183         if (!is_first_call)
44184           disp.resize(cimg_fitscreen(_data[0]._width,_data[0]._height,_data[0]._depth),false).
44185             set_title("%s (%ux%ux%ux%u)",dtitle.data(),_data[0]._width,_data[0]._height,_data[0]._depth,_data[0]._spectrum);
44186         _data[0]._display(disp,0,false,!is_first_call);
44187         if (disp.key()) is_exit = true;
44188         disp.resize(cimg_fitscreen(dw,dh,1),false).set_title("%s",dtitle.data());
44189       } else {
44190         bool disp_resize = !is_first_call;
44191         while (!disp.is_closed() && !is_exit) {
44192           const CImg<intT> s = _get_select(disp,0,true,axis,align,orig,disp_resize,!is_first_call,true);
44193           disp_resize = true;
44194           if (s[0]<0) { // No selections done.
44195             if (disp.button()&2) { disp.flush(); break; }
44196             is_exit = true;
44197           } else if (disp.wheel()) { // Zoom in/out.
44198             const int wheel = disp.wheel();
44199             disp.set_wheel();
44200             if (!is_first_call && wheel<0) break;
44201             if (wheel>0 && _width>=4) {
44202               const unsigned int
44203                 delta = cimg::max(1U,(unsigned int)cimg::round(0.3*_width)),
44204                 ind0 = (unsigned int)cimg::max(0,s[0] - (int)delta),
44205                 ind1 = (unsigned int)cimg::min(width() - 1,s[0] + (int)delta);
44206               if ((ind0!=0 || ind1!=_width-1) && ind1 - ind0>=3) get_shared_images(ind0,ind1)._display(disp,0,false,axis,align,orig + ind0,false,is_exit);
44207             }
44208           } else if (s[0]!=0 || s[1]!=width()-1) get_shared_images(s[0],s[1])._display(disp,0,false,axis,align,orig+s[0],false,is_exit);
44209         }
44210       }
44211       return *this;
44212     }
44213 
44214     //! Save list into a file.
44215     /**
44216       \param filename Filename to write data to.
44217       \param number Number of digits used when chosen format requires the saving of multiple files.
44218     **/
44219     const CImgList<T>& save(const char *const filename, const int number=-1) const {
44220       if (!filename)
44221         throw CImgArgumentException(_cimglist_instance
44222                                     "save() : Specified filename is (null).",
44223                                     cimglist_instance);
44224       // Do not test for empty instances, since .cimg format is able to manage empty instances.
44225       const char *const ext = cimg::split_filename(filename);
44226       char nfilename[1024] = { 0 };
44227       const char *const fn = (number>=0)?cimg::number_filename(filename,number,6,nfilename):filename;
44228 #ifdef cimglist_save_plugin
44229       cimglist_save_plugin(fn);
44230 #endif
44231 #ifdef cimglist_save_plugin1
44232       cimglist_save_plugin1(fn);
44233 #endif
44234 #ifdef cimglist_save_plugin2
44235       cimglist_save_plugin2(fn);
44236 #endif
44237 #ifdef cimglist_save_plugin3
44238       cimglist_save_plugin3(fn);
44239 #endif
44240 #ifdef cimglist_save_plugin4
44241       cimglist_save_plugin4(fn);
44242 #endif
44243 #ifdef cimglist_save_plugin5
44244       cimglist_save_plugin5(fn);
44245 #endif
44246 #ifdef cimglist_save_plugin6
44247       cimglist_save_plugin6(fn);
44248 #endif
44249 #ifdef cimglist_save_plugin7
44250       cimglist_save_plugin7(fn);
44251 #endif
44252 #ifdef cimglist_save_plugin8
44253       cimglist_save_plugin8(fn);
44254 #endif
44255       if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true);
44256       else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false);
44257       else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,true);
44258       else if (!cimg::strcasecmp(ext,"avi") ||
44259                !cimg::strcasecmp(ext,"mov") ||
44260                !cimg::strcasecmp(ext,"asf") ||
44261                !cimg::strcasecmp(ext,"divx") ||
44262                !cimg::strcasecmp(ext,"flv") ||
44263                !cimg::strcasecmp(ext,"mpg") ||
44264                !cimg::strcasecmp(ext,"m1v") ||
44265                !cimg::strcasecmp(ext,"m2v") ||
44266                !cimg::strcasecmp(ext,"m4v") ||
44267                !cimg::strcasecmp(ext,"mjp") ||
44268                !cimg::strcasecmp(ext,"mkv") ||
44269                !cimg::strcasecmp(ext,"mpe") ||
44270                !cimg::strcasecmp(ext,"movie") ||
44271                !cimg::strcasecmp(ext,"ogm") ||
44272                !cimg::strcasecmp(ext,"ogg") ||
44273                !cimg::strcasecmp(ext,"qt") ||
44274                !cimg::strcasecmp(ext,"rm") ||
44275                !cimg::strcasecmp(ext,"vob") ||
44276                !cimg::strcasecmp(ext,"wmv") ||
44277                !cimg::strcasecmp(ext,"xvid") ||
44278                !cimg::strcasecmp(ext,"mpeg")) return save_ffmpeg(fn);
44279 #ifdef cimg_use_tiff
44280       else if (!cimg::strcasecmp(ext,"tif") ||
44281           !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn);
44282 #endif
44283       else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn);
44284       else { if (_width==1) _data[0].save(fn,-1); else cimglist_for(*this,l) _data[l].save(fn,l); }
44285       return *this;
44286     }
44287 
44288     //! Tell if an image list can be saved as one single file.
44289     /**
44290        \param filename Filename, as a C-string.
44291        \return \c true if the file format supports multiple images, \c false otherwise.
44292     **/
44293     static bool is_saveable(const char *const filename) {
44294       const char *const ext = cimg::split_filename(filename);
44295       if (!cimg::strcasecmp(ext,"cimgz") ||
44296 #ifdef cimg_use_tiff
44297           !cimg::strcasecmp(ext,"tif") ||
44298           !cimg::strcasecmp(ext,"tiff") ||
44299 #endif
44300           !cimg::strcasecmp(ext,"yuv") ||
44301           !cimg::strcasecmp(ext,"avi") ||
44302           !cimg::strcasecmp(ext,"mov") ||
44303           !cimg::strcasecmp(ext,"asf") ||
44304           !cimg::strcasecmp(ext,"divx") ||
44305           !cimg::strcasecmp(ext,"flv") ||
44306           !cimg::strcasecmp(ext,"mpg") ||
44307           !cimg::strcasecmp(ext,"m1v") ||
44308           !cimg::strcasecmp(ext,"m2v") ||
44309           !cimg::strcasecmp(ext,"m4v") ||
44310           !cimg::strcasecmp(ext,"mjp") ||
44311           !cimg::strcasecmp(ext,"mkv") ||
44312           !cimg::strcasecmp(ext,"mpe") ||
44313           !cimg::strcasecmp(ext,"movie") ||
44314           !cimg::strcasecmp(ext,"ogm") ||
44315           !cimg::strcasecmp(ext,"ogg") ||
44316           !cimg::strcasecmp(ext,"qt") ||
44317           !cimg::strcasecmp(ext,"rm") ||
44318           !cimg::strcasecmp(ext,"vob") ||
44319           !cimg::strcasecmp(ext,"wmv") ||
44320           !cimg::strcasecmp(ext,"xvid") ||
44321           !cimg::strcasecmp(ext,"mpeg")) return true;
44322       return false;
44323     }
44324 
44325     //! Save image sequence, using FFMPEG library.
44326     /**
44327       \param filename Filename to write data to.
44328       \param first_frame Index of first image frame to write.
44329       \param last_frame Index of last image frame to write.
44330       \param fps Desired framerate (in frames per seconds) if chosen format supports it.
44331       \param bitrate Desired bitrate (in bits per seconds) if chosen format supports it.
44332     **/
44333     // This piece of code has been originally written by David. G. Starkweather.
44334     const CImgList<T>& save_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
44335                                    const unsigned int fps=25, const unsigned int bitrate=2048) const {
44336       if (!filename)
44337         throw CImgArgumentException(_cimglist_instance
44338                                     "save_ffmpeg() : Specified filename is (null).",
44339                                     cimglist_instance);
44340       if (is_empty())
44341         throw CImgInstanceException(_cimglist_instance
44342                                     "save_ffmpeg() : Empty instance, for file '%s'.",
44343                                     cimglist_instance,
44344                                     filename);
44345       if (!fps)
44346         throw CImgArgumentException(_cimglist_instance
44347                                     "save_ffmpeg() : Invalid specified framerate 0, for file '%s'.",
44348                                     cimglist_instance,
44349                                     filename);
44350 
44351       const unsigned int nlast_frame = last_frame==~0U?_width-1:last_frame;
44352       if (first_frame>=_width || nlast_frame>=_width)
44353         throw CImgArgumentException(_cimglist_instance
44354                                     "save_ffmpeg() : Out of range specified frames [%u,%u], for file '%s'.",
44355                                     cimglist_instance,
44356                                     first_frame,last_frame,filename);
44357 
44358       for (unsigned int ll = first_frame; ll<=nlast_frame; ++ll) if (!_data[ll].is_sameXYZ(_data[0]))
44359         throw CImgInstanceException(_cimglist_instance
44360                                     "save_ffmpeg() : Invalid instance dimensions, for file '%s'.",
44361                                     cimglist_instance,
44362                                     filename);
44363 
44364 #ifndef cimg_use_ffmpeg
44365       return save_ffmpeg_external(filename,first_frame,last_frame,0,fps,bitrate);
44366 #else
44367       avcodec_register_all();
44368       av_register_all();
44369       const int
44370         frame_dimx = _data[first_frame].width(),
44371         frame_dimy = _data[first_frame].height(),
44372         frame_dimv = _data[first_frame].spectrum();
44373       if (frame_dimv!=1 && frame_dimv!=3)
44374         throw CImgInstanceException(_cimglist_instance
44375                                     "save_ffmpeg() : Image[0] (%u,%u,%u,%u,%p) has not 1 or 3 channels, for file '%s'.",
44376                                     cimglist_instance,
44377                                     _data[0]._width,_data[0]._height,_data[0]._depth,_data[0]._spectrum,_data,filename);
44378 
44379       PixelFormat dest_pxl_fmt = PIX_FMT_YUV420P;
44380       PixelFormat src_pxl_fmt  = (frame_dimv==3)?PIX_FMT_RGB24:PIX_FMT_GRAY8;
44381 
44382       int sws_flags = SWS_FAST_BILINEAR; // Interpolation method (keeping same size images for now).
44383       AVOutputFormat *fmt = 0;
44384 #if defined(AV_VERSION_INT)
44385 #if LIBAVFORMAT_VERSION_INT<AV_VERSION_INT(52,45,0)
44386       fmt = guess_format(0,filename,0);
44387       if (!fmt) fmt = guess_format("mpeg",0,0); // Default format "mpeg".
44388 #else
44389       fmt = av_guess_format(0,filename,0);
44390       if (!fmt) fmt = av_guess_format("mpeg",0,0); // Default format "mpeg".
44391 #endif
44392 #else
44393       fmt = guess_format(0,filename,0);
44394       if (!fmt) fmt = guess_format("mpeg",0,0); // Default format "mpeg".
44395 #endif
44396 
44397       if (!fmt)
44398         throw CImgArgumentException(_cimglist_instance
44399                                     "save_ffmpeg() : Unable to determine codec for file '%s'.",
44400                                     cimglist_instance,
44401                                     filename);
44402 
44403       AVFormatContext *oc = 0;
44404 #if defined(AV_VERSION_INT)
44405 #if LIBAVFORMAT_VERSION_INT<AV_VERSION_INT(52,36,0)
44406       oc = av_alloc_format_context();
44407 #else
44408       oc = avformat_alloc_context();
44409 #endif
44410 #else
44411       oc = av_alloc_format_context();
44412 #endif
44413       if (!oc) // Failed to allocate format context.
44414         throw CImgIOException(_cimglist_instance
44415                               "save_ffmpeg() : Failed to allocate FFMPEG structure for format context, for file '%s'.",
44416                               cimglist_instance,
44417                               filename);
44418 
44419       AVCodec *codec = 0;
44420       AVFrame *picture = 0;
44421       AVFrame *tmp_pict = 0;
44422       oc->oformat = fmt;
44423       std::sprintf(oc->filename,"%s",filename);
44424 
44425       // Add video stream.
44426       int stream_index = 0;
44427       AVStream *video_str = 0;
44428       if (fmt->video_codec!=CODEC_ID_NONE) {
44429         video_str = av_new_stream(oc,stream_index);
44430         if (!video_str) { // Failed to allocate stream.
44431           av_free(oc);
44432           throw CImgIOException(_cimglist_instance
44433                                 "save_ffmpeg() : Failed to allocate FFMPEG structure for video stream, for file '%s'.",
44434                                 cimglist_instance,
44435                                 filename);
44436         }
44437       } else { // No codec identified.
44438         av_free(oc);
44439         throw CImgIOException(_cimglist_instance
44440                               "save_ffmpeg() : Failed to identify proper codec, for file '%s'.",
44441                               cimglist_instance,
44442                               filename);
44443       }
44444 
44445       AVCodecContext *c = video_str->codec;
44446       c->codec_id = fmt->video_codec;
44447       c->codec_type = CODEC_TYPE_VIDEO;
44448       c->bit_rate = 1024*bitrate;
44449       c->width = frame_dimx;
44450       c->height = frame_dimy;
44451       c->time_base.num = 1;
44452       c->time_base.den = fps;
44453       c->gop_size = 12;
44454       c->pix_fmt = dest_pxl_fmt;
44455       if (c->codec_id==CODEC_ID_MPEG2VIDEO) c->max_b_frames = 2;
44456       if (c->codec_id==CODEC_ID_MPEG1VIDEO) c->mb_decision = 2;
44457 
44458       if (av_set_parameters(oc,0)<0) { // Parameters not properly set.
44459         av_free(oc);
44460         throw CImgIOException(_cimglist_instance
44461                               "save_ffmpeg() : Invalid parameters set for avcodec, for file '%s'.",
44462                               cimglist_instance,
44463                               filename);
44464       }
44465 
44466       // Open codecs and alloc buffers.
44467       codec = avcodec_find_encoder(c->codec_id);
44468       if (!codec) { // Failed to find codec.
44469         av_free(oc);
44470         throw CImgIOException(_cimglist_instance
44471                               "save_ffmpeg() : No valid codec found for file '%s'.",
44472                               cimglist_instance,
44473                               filename);
44474       }
44475       if (avcodec_open(c,codec)<0) // Failed to open codec.
44476         throw CImgIOException(_cimglist_instance
44477                               "save_ffmpeg() : Failed to open codec for file '%s'.",
44478                               cimglist_instance,
44479                               filename);
44480 
44481       tmp_pict = avcodec_alloc_frame();
44482       if (!tmp_pict) { // Failed to allocate memory for tmp_pict frame.
44483         avcodec_close(video_str->codec);
44484         av_free(oc);
44485         throw CImgIOException(_cimglist_instance
44486                               "save_ffmpeg() : Failed to allocate memory for file '%s'.",
44487                               cimglist_instance,
44488                               filename);
44489       }
44490       tmp_pict->linesize[0] = (src_pxl_fmt==PIX_FMT_RGB24)?3*frame_dimx:frame_dimx;
44491       tmp_pict->type = FF_BUFFER_TYPE_USER;
44492       int tmp_size = avpicture_get_size(src_pxl_fmt,frame_dimx,frame_dimy);
44493       uint8_t *tmp_buffer = (uint8_t*)av_malloc(tmp_size);
44494       if (!tmp_buffer) { // Failed to allocate memory for tmp buffer.
44495         av_free(tmp_pict);
44496         avcodec_close(video_str->codec);
44497         av_free(oc);
44498         throw CImgIOException(_cimglist_instance
44499                               "save_ffmpeg() : Failed to allocate memory for file '%s'.",
44500                               cimglist_instance,
44501                               filename);
44502       }
44503 
44504       // Associate buffer with tmp_pict.
44505       avpicture_fill((AVPicture*)tmp_pict,tmp_buffer,src_pxl_fmt,frame_dimx,frame_dimy);
44506       picture = avcodec_alloc_frame();
44507       if (!picture) { // Failed to allocate picture frame.
44508         av_free(tmp_pict->data[0]);
44509         av_free(tmp_pict);
44510         avcodec_close(video_str->codec);
44511         av_free(oc);
44512         throw CImgIOException(_cimglist_instance
44513                               "save_ffmpeg() : Failed to allocate memory for file '%s'.",
44514                               cimglist_instance,
44515                               filename);
44516       }
44517 
44518       int size = avpicture_get_size(c->pix_fmt,frame_dimx,frame_dimy);
44519       uint8_t *buffer = (uint8_t*)av_malloc(size);
44520       if (!buffer) { // Failed to allocate picture frame buffer.
44521         av_free(picture);
44522         av_free(tmp_pict->data[0]);
44523         av_free(tmp_pict);
44524         avcodec_close(video_str->codec);
44525         av_free(oc);
44526         throw CImgIOException(_cimglist_instance
44527                               "save_ffmpeg() : Failed to allocate memory for file '%s'.",
44528                               cimglist_instance,
44529                               filename);
44530       }
44531 
44532       // Associate the buffer with picture.
44533       avpicture_fill((AVPicture*)picture,buffer,c->pix_fmt,frame_dimx,frame_dimy);
44534 
44535       // Open file.
44536       if (!(fmt->flags&AVFMT_NOFILE)) {
44537         if (url_fopen(&oc->pb,filename,URL_WRONLY)<0)
44538           throw CImgIOException(_cimglist_instance
44539                                 "save_ffmpeg() : Failed to open file '%s'.",
44540                                 cimglist_instance,
44541                                 filename);
44542       }
44543 
44544       if (av_write_header(oc)<0)
44545         throw CImgIOException(_cimglist_instance
44546                               "save_ffmpeg() : Failed to write header in file '%s'.",
44547                               cimglist_instance,
44548                               filename);
44549 
44550       SwsContext *img_convert_context = 0;
44551       img_convert_context = sws_getContext(frame_dimx,frame_dimy,src_pxl_fmt,
44552                                            c->width,c->height,c->pix_fmt,sws_flags,0,0,0);
44553       if (!img_convert_context) { // Failed to get swscale context.
44554         // if (!(fmt->flags & AVFMT_NOFILE)) url_fclose(&oc->pb);
44555         av_free(picture->data);
44556         av_free(picture);
44557         av_free(tmp_pict->data[0]);
44558         av_free(tmp_pict);
44559         avcodec_close(video_str->codec);
44560         av_free(oc);
44561         throw CImgIOException(_cimglist_instance
44562                               "save_ffmpeg() : Failed to get conversion context for file '%s'.",
44563                               cimglist_instance,
44564                               filename);
44565       }
44566       int ret = 0, out_size;
44567       uint8_t *video_outbuf = 0;
44568       int video_outbuf_size = 1000000;
44569       video_outbuf = (uint8_t*)av_malloc(video_outbuf_size);
44570       if (!video_outbuf) {
44571         // if (!(fmt->flags & AVFMT_NOFILE)) url_fclose(&oc->pb);
44572         av_free(picture->data);
44573         av_free(picture);
44574         av_free(tmp_pict->data[0]);
44575         av_free(tmp_pict);
44576         avcodec_close(video_str->codec);
44577         av_free(oc);
44578         throw CImgIOException(_cimglist_instance
44579                               "save_ffmpeg() : Failed to allocate memory, for file '%s'.",
44580                               cimglist_instance,
44581                               filename);
44582       }
44583 
44584       // Loop through each desired image in list.
44585       for (unsigned int i = first_frame; i<=nlast_frame; ++i) {
44586         CImg<uint8_t> currentIm = _data[i], red, green, blue, gray;
44587         if (src_pxl_fmt==PIX_FMT_RGB24) {
44588           red = currentIm.get_shared_channel(0);
44589           green = currentIm.get_shared_channel(1);
44590           blue = currentIm.get_shared_channel(2);
44591           cimg_forXY(currentIm,X,Y) { // Assign pizel values to data buffer in interlaced RGBRGB ... format.
44592             tmp_pict->data[0][Y*tmp_pict->linesize[0] + 3*X] = red(X,Y);
44593             tmp_pict->data[0][Y*tmp_pict->linesize[0] + 3*X + 1] = green(X,Y);
44594             tmp_pict->data[0][Y*tmp_pict->linesize[0] + 3*X + 2] = blue(X,Y);
44595           }
44596         } else {
44597           gray = currentIm.get_shared_channel(0);
44598           cimg_forXY(currentIm,X,Y) tmp_pict->data[0][Y*tmp_pict->linesize[0] + X] = gray(X,Y);
44599         }
44600         if (!video_str) break;
44601         if (sws_scale(img_convert_context,tmp_pict->data,tmp_pict->linesize,0,c->height,picture->data,picture->linesize)<0) break;
44602         out_size = avcodec_encode_video(c,video_outbuf,video_outbuf_size,picture);
44603         if (out_size>0) {
44604           AVPacket pkt;
44605           av_init_packet(&pkt);
44606           pkt.pts = av_rescale_q(c->coded_frame->pts,c->time_base,video_str->time_base);
44607           if (c->coded_frame->key_frame) pkt.flags|=PKT_FLAG_KEY;
44608           pkt.stream_index = video_str->index;
44609           pkt.data = video_outbuf;
44610           pkt.size = out_size;
44611           ret = av_write_frame(oc,&pkt);
44612         } else if (out_size<0) break;
44613         if (ret) break; // Error occured in writing frame.
44614       }
44615 
44616       // Close codec.
44617       if (video_str) {
44618         avcodec_close(video_str->codec);
44619         av_free(picture->data[0]);
44620         av_free(picture);
44621         av_free(tmp_pict->data[0]);
44622         av_free(tmp_pict);
44623       }
44624       if (av_write_trailer(oc)<0)
44625         throw CImgIOException(_cimglist_instance
44626                               "save_ffmpeg() : Failed to write trailer for file '%s'.",
44627                               cimglist_instance,
44628                               filename);
44629 
44630       av_freep(&oc->streams[stream_index]->codec);
44631       av_freep(&oc->streams[stream_index]);
44632       if (!(fmt->flags&AVFMT_NOFILE)) {
44633         /*if (url_fclose(oc->pb)<0)
44634           throw CImgIOException(_cimglist_instance
44635                                 "save_ffmpeg() : File '%s', failed to close file.",
44636                                 cimglist_instance,
44637                                 filename);
44638         */
44639       }
44640       av_free(oc);
44641       av_free(video_outbuf);
44642 #endif
44643       return *this;
44644     }
44645 
44646     const CImgList<T>& _save_yuv(std::FILE *const file, const char *const filename, const bool is_rgb) const {
44647       if (!file && !filename)
44648         throw CImgArgumentException(_cimglist_instance
44649                                     "save_yuv() : Specified filename is (null).",
44650                                     cimglist_instance);
44651       if (is_empty())
44652         throw CImgInstanceException(_cimglist_instance
44653                                     "save_yuv() : Empty instance, for file '%s'.",
44654                                     cimglist_instance,
44655                                     filename?filename:"(FILE*)");
44656 
44657       if ((*this)[0].width()%2 || (*this)[0].height()%2)
44658         throw CImgInstanceException(_cimglist_instance
44659                                     "save_yuv() : Invalid odd instance dimensions (%u,%u) for file '%s'.",
44660                                     cimglist_instance,
44661                                     (*this)[0].width(),(*this)[0].height(),
44662                                     filename?filename:"(FILE*)");
44663 
44664       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
44665       cimglist_for(*this,l) {
44666         CImg<ucharT> YCbCr((*this)[l]);
44667         if (is_rgb) YCbCr.RGBtoYCbCr();
44668         cimg::fwrite(YCbCr._data,(unsigned long)YCbCr._width*YCbCr._height,nfile);
44669         cimg::fwrite(YCbCr.get_resize(YCbCr._width/2, YCbCr._height/2,1,3,3).data(0,0,0,1),
44670                      (unsigned long)YCbCr._width*YCbCr._height/2,nfile);
44671       }
44672       if (!file) cimg::fclose(nfile);
44673       return *this;
44674     }
44675 
44676     //! Save list as a YUV image sequence file.
44677     /**
44678       \param filename Filename to write data to.
44679       \param is_rgb Tells if the RGB to YUV conversion must be done for saving.
44680     **/
44681     const CImgList<T>& save_yuv(const char *const filename=0, const bool is_rgb=true) const {
44682       return _save_yuv(0,filename,is_rgb);
44683     }
44684 
44685     //! Save image sequence into a YUV file.
44686     /**
44687       \param file File to write data to.
44688       \param is_rgb Tells if the RGB to YUV conversion must be done for saving.
44689     **/
44690     const CImgList<T>& save_yuv(std::FILE *const file, const bool is_rgb=true) const {
44691       return _save_yuv(file,0,is_rgb);
44692     }
44693 
44694     const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename, const bool is_compressed) const {
44695       if (!file && !filename)
44696         throw CImgArgumentException(_cimglist_instance
44697                                     "save_cimg() : Specified filename is (null).",
44698                                     cimglist_instance);
44699 #ifndef cimg_use_zlib
44700       if (is_compressed)
44701         cimg::warn(_cimglist_instance
44702                    "save_cimg() : Unable to save compressed data in file '%s' unless zlib is enabled, saving them uncompressed.",
44703                    cimglist_instance,
44704                    filename?filename:"(FILE*)");
44705 #endif
44706       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
44707       const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little";
44708       if (std::strstr(ptype,"unsigned")==ptype) std::fprintf(nfile,"%u unsigned_%s %s_endian\n",_width,ptype+9,etype);
44709       else std::fprintf(nfile,"%u %s %s_endian\n",_width,ptype,etype);
44710       cimglist_for(*this,l) {
44711         const CImg<T>& img = _data[l];
44712         std::fprintf(nfile,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum);
44713         if (img._data) {
44714           CImg<T> tmp;
44715           if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); }
44716           const CImg<T>& ref = cimg::endianness()?tmp:img;
44717           bool failed_to_compress = true;
44718           if (is_compressed) {
44719 #ifdef cimg_use_zlib
44720             const unsigned long siz = sizeof(T)*ref.size();
44721             unsigned long csiz = siz + siz/100 + 16;
44722             Bytef *const cbuf = new Bytef[csiz];
44723             if (compress(cbuf,&csiz,(Bytef*)ref._data,siz))
44724               cimg::warn(_cimglist_instance
44725                          "save_cimg() : Failed to save compressed data for file '%s', saving them uncompressed.",
44726                          cimglist_instance,
44727                          filename?filename:"(FILE*)");
44728             else {
44729               std::fprintf(nfile," #%lu\n",csiz);
44730               cimg::fwrite(cbuf,csiz,nfile);
44731               delete[] cbuf;
44732               failed_to_compress = false;
44733             }
44734 #endif
44735           }
44736           if (failed_to_compress) { // Write in a non-compressed way.
44737             std::fputc('\n',nfile);
44738             cimg::fwrite(ref._data,ref.size(),nfile);
44739           }
44740         } else std::fputc('\n',nfile);
44741       }
44742       if (!file) cimg::fclose(nfile);
44743       return *this;
44744     }
44745 
44746     //! Save list into a .cimg file.
44747     /**
44748        \param filename Filename to write data to.
44749        \param is_compressed Tells if data compression must be enabled.
44750     **/
44751     const CImgList<T>& save_cimg(const char *const filename, const bool is_compressed=false) const {
44752       return _save_cimg(0,filename,is_compressed);
44753     }
44754 
44755     //! Save list into a .cimg file.
44756     /**
44757        \param file File to write data to.
44758        \param is_compressed Tells if data compression must be enabled.
44759     **/
44760     const CImgList<T>& save_cimg(std::FILE *file, const bool is_compressed=false) const {
44761       return _save_cimg(file,0,is_compressed);
44762     }
44763 
44764     const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename,
44765                                  const unsigned int n0,
44766                                  const unsigned int x0, const unsigned int y0,
44767                                  const unsigned int z0, const unsigned int c0) const {
44768 #define _cimg_save_cimg_case(Ts,Tss) \
44769       if (!saved && !cimg::strcasecmp(Ts,str_pixeltype)) { \
44770         for (unsigned int l = 0; l<lmax; ++l) { \
44771           j = 0; while((i=std::fgetc(nfile))!='\n') tmp[j++]=(char)i; tmp[j] = 0; \
44772           W = H = D = C = 0; \
44773           if (std::sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
44774             throw CImgIOException(_cimglist_instance \
44775                                   "save_cimg() : Invalid size (%u,%u,%u,%u) of image[%u], for file '%s'.", \
44776                                   cimglist_instance, \
44777                                   W,H,D,C,l,filename?filename:"(FILE*)"); \
44778           if (W*H*D*C>0) { \
44779             if (l<n0 || x0>=W || y0>=H || z0>=D || c0>=D) std::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
44780             else { \
44781               const CImg<T>& img = (*this)[l - n0]; \
44782               const T *ptrs = img._data; \
44783               const unsigned int \
44784                 x1 = x0 + img._width - 1, \
44785                 y1 = y0 + img._height - 1, \
44786                 z1 = z0 + img._depth - 1, \
44787                 c1 = c0 + img._spectrum - 1, \
44788                 nx1 = x1>=W?W-1:x1, \
44789                 ny1 = y1>=H?H-1:y1, \
44790                 nz1 = z1>=D?D-1:z1, \
44791                 nc1 = c1>=C?C-1:c1; \
44792               CImg<Tss> raw(1+nx1-x0); \
44793               const unsigned int skipvb = c0*W*H*D*sizeof(Tss); \
44794               if (skipvb) std::fseek(nfile,skipvb,SEEK_CUR); \
44795               for (unsigned int v = 1 + nc1 - c0; v; --v) { \
44796                 const unsigned int skipzb = z0*W*H*sizeof(Tss); \
44797                 if (skipzb) std::fseek(nfile,skipzb,SEEK_CUR); \
44798                 for (unsigned int z = 1 + nz1 - z0; z; --z) { \
44799                   const unsigned int skipyb = y0*W*sizeof(Tss); \
44800                   if (skipyb) std::fseek(nfile,skipyb,SEEK_CUR); \
44801                   for (unsigned int y = 1 + ny1 - y0; y; --y) { \
44802                     const unsigned int skipxb = x0*sizeof(Tss); \
44803                     if (skipxb) std::fseek(nfile,skipxb,SEEK_CUR); \
44804                     raw.assign(ptrs, raw._width); \
44805                     ptrs+=img._width; \
44806                     if (endian) cimg::invert_endianness(raw._data,raw._width); \
44807                     cimg::fwrite(raw._data,raw._width,nfile); \
44808                     const unsigned int skipxe = (W - 1 - nx1)*sizeof(Tss); \
44809                     if (skipxe) std::fseek(nfile,skipxe,SEEK_CUR); \
44810                   } \
44811                   const unsigned int skipye = (H - 1 - ny1)*W*sizeof(Tss); \
44812                   if (skipye) std::fseek(nfile,skipye,SEEK_CUR); \
44813                 } \
44814                 const unsigned int skipze = (D - 1 - nz1)*W*H*sizeof(Tss); \
44815                 if (skipze) std::fseek(nfile,skipze,SEEK_CUR); \
44816               } \
44817               const unsigned int skipve = (C - 1 - nc1)*W*H*D*sizeof(Tss); \
44818               if (skipve) std::fseek(nfile,skipve,SEEK_CUR); \
44819             } \
44820           } \
44821         } \
44822         saved = true; \
44823       }
44824 
44825       if (!file && !filename)
44826         throw CImgArgumentException(_cimglist_instance
44827                                     "save_cimg() : Specified filename is (null).",
44828                                     cimglist_instance);
44829       if (is_empty())
44830         throw CImgInstanceException(_cimglist_instance
44831                                     "save_cimg() : Empty instance, for file '%s'.",
44832                                     cimglist_instance,
44833                                     filename?filename:"(FILE*)");
44834 
44835       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb+");
44836       bool saved = false, endian = cimg::endianness();
44837       char tmp[256] = { 0 }, str_pixeltype[256] = { 0 }, str_endian[256] = { 0 };
44838       unsigned int j, err, N, W, H, D, C;
44839       int i;
44840       j = 0; while((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
44841       err = std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype,str_endian);
44842       if (err<2) {
44843         if (!file) cimg::fclose(nfile);
44844         throw CImgIOException(_cimglist_instance
44845                               "save_cimg() : CImg header not found in file '%s'.",
44846                               cimglist_instance,
44847                               filename?filename:"(FILE*)");
44848       }
44849       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
44850       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
44851       const unsigned int lmax = cimg::min(N,n0+_width);
44852       _cimg_save_cimg_case("bool",bool);
44853       _cimg_save_cimg_case("unsigned_char",unsigned char);
44854       _cimg_save_cimg_case("uchar",unsigned char);
44855       _cimg_save_cimg_case("char",char);
44856       _cimg_save_cimg_case("unsigned_short",unsigned short);
44857       _cimg_save_cimg_case("ushort",unsigned short);
44858       _cimg_save_cimg_case("short",short);
44859       _cimg_save_cimg_case("unsigned_int",unsigned int);
44860       _cimg_save_cimg_case("uint",unsigned int);
44861       _cimg_save_cimg_case("int",int);
44862       _cimg_save_cimg_case("unsigned_long",unsigned long);
44863       _cimg_save_cimg_case("ulong",unsigned long);
44864       _cimg_save_cimg_case("long",long);
44865       _cimg_save_cimg_case("float",float);
44866       _cimg_save_cimg_case("double",double);
44867       if (!saved) {
44868         if (!file) cimg::fclose(nfile);
44869         throw CImgIOException(_cimglist_instance
44870                               "save_cimg() : Unsupported data type '%s' for file '%s'.",
44871                               cimglist_instance,
44872                               filename?filename:"(FILE*)",str_pixeltype);
44873       }
44874       if (!file) cimg::fclose(nfile);
44875       return *this;
44876     }
44877 
44878     //! Insert the image instance into into an existing .cimg file, at specified coordinates.
44879     /**
44880       \param filename Filename to write data to.
44881       \param n0 Starting index of images to write.
44882       \param x0 Starting X-coordinates of image regions to write.
44883       \param y0 Starting Y-coordinates of image regions to write.
44884       \param z0 Starting Z-coordinates of image regions to write.
44885       \param c0 Starting C-coordinates of image regions to write.
44886     **/
44887     const CImgList<T>& save_cimg(const char *const filename,
44888                                  const unsigned int n0,
44889                                  const unsigned int x0, const unsigned int y0,
44890                                  const unsigned int z0, const unsigned int c0) const {
44891       return _save_cimg(0,filename,n0,x0,y0,z0,c0);
44892     }
44893 
44894     //! Insert the image instance into into an existing .cimg file, at specified coordinates.
44895     /**
44896       \param file File to write data to.
44897       \param n0 Starting index of images to write.
44898       \param x0 Starting X-coordinates of image regions to write.
44899       \param y0 Starting Y-coordinates of image regions to write.
44900       \param z0 Starting Z-coordinates of image regions to write.
44901       \param c0 Starting C-coordinates of image regions to write.
44902     **/
44903     const CImgList<T>& save_cimg(std::FILE *const file,
44904                                  const unsigned int n0,
44905                                  const unsigned int x0, const unsigned int y0,
44906                                  const unsigned int z0, const unsigned int c0) const {
44907       return _save_cimg(file,0,n0,x0,y0,z0,c0);
44908     }
44909 
44910     static void _save_empty_cimg(std::FILE *const file, const char *const filename,
44911                                 const unsigned int nb,
44912                                 const unsigned int dx, const unsigned int dy,
44913                                 const unsigned int dz, const unsigned int dc) {
44914       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
44915       const unsigned long siz = (unsigned long)dx*dy*dz*dc*sizeof(T);
44916       std::fprintf(nfile,"%u %s\n",nb,pixel_type());
44917       for (unsigned int i=nb; i; --i) {
44918         std::fprintf(nfile,"%u %u %u %u\n",dx,dy,dz,dc);
44919         for (unsigned long off=siz; off; --off) std::fputc(0,nfile);
44920       }
44921       if (!file) cimg::fclose(nfile);
44922     }
44923 
44924     //! Save empty (non-compressed) .cimg file with specified dimensions.
44925     /**
44926         \param filename Filename to write data to.
44927         \param nb Number of images to write.
44928         \param dx Width of images in the written file.
44929         \param dy Height of images in the written file.
44930         \param dz Depth of images in the written file.
44931         \param dc Spectrum of images in the written file.
44932     **/
44933     static void save_empty_cimg(const char *const filename,
44934                                 const unsigned int nb,
44935                                 const unsigned int dx, const unsigned int dy=1,
44936                                 const unsigned int dz=1, const unsigned int dc=1) {
44937       return _save_empty_cimg(0,filename,nb,dx,dy,dz,dc);
44938     }
44939 
44940     //! Save empty .cimg file with specified dimensions.
44941     /**
44942         \param file File to write data to.
44943         \param nb Number of images to write.
44944         \param dx Width of images in the written file.
44945         \param dy Height of images in the written file.
44946         \param dz Depth of images in the written file.
44947         \param dc Spectrum of images in the written file.
44948     **/
44949     static void save_empty_cimg(std::FILE *const file,
44950                                 const unsigned int nb,
44951                                 const unsigned int dx, const unsigned int dy=1,
44952                                 const unsigned int dz=1, const unsigned int dc=1) {
44953       return _save_empty_cimg(file,0,nb,dx,dy,dz,dc);
44954     }
44955 
44956     //! Save list as a TIFF file.
44957     /**
44958       \param filename Filename to write data to.
44959       \param compression_type Compression mode used to write data.
44960     **/
44961     const CImgList<T>& save_tiff(const char *const filename, const unsigned int compression_type=0) const {
44962       if (!filename)
44963         throw CImgArgumentException(_cimglist_instance
44964                                     "save_tiff() : Specified filename is (null).",
44965                                     cimglist_instance);
44966       if (is_empty())
44967         throw CImgInstanceException(_cimglist_instance
44968                                     "save_tiff() : Empty instance, for file '%s'.",
44969                                     cimglist_instance,
44970                                     filename);
44971 #ifndef cimg_use_tiff
44972       if (_width==1) _data[0].save_tiff(filename,compression_type);
44973       else cimglist_for(*this,l) {
44974           char nfilename[1024] = { 0 };
44975           cimg::number_filename(filename,l,6,nfilename);
44976           _data[l].save_tiff(nfilename,compression_type);
44977         }
44978 #else
44979       TIFF *tif = TIFFOpen(filename,"w");
44980       if (tif) {
44981         for (unsigned int dir = 0, l = 0; l<_width; ++l) {
44982           const CImg<T>& img = (*this)[l];
44983           if (img) {
44984             if (img._depth==1) img._save_tiff(tif,dir++,compression_type);
44985             else cimg_forZ(img,z) img.get_slice(z)._save_tiff(tif,dir++,compression_type);
44986           }
44987         }
44988         TIFFClose(tif);
44989       } else
44990         throw CImgIOException(_cimglist_instance
44991                               "save_tiff() : Failed to open stream for file '%s'.",
44992                               cimglist_instance,
44993                               filename);
44994 #endif
44995       return *this;
44996     }
44997 
44998 
44999     //! Save list as a gzipped file, using external tool 'gzip'.
45000     /**
45001       \param filename Filename to write data to.
45002     **/
45003     const CImgList<T>& save_gzip_external(const char *const filename) const {
45004       if (!filename)
45005         throw CImgIOException(_cimglist_instance
45006                               "save_gzip_external() : Specified filename is (null).",
45007                               cimglist_instance);
45008 
45009       char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
45010       const char
45011         *ext = cimg::split_filename(filename,body),
45012         *ext2 = cimg::split_filename(body,0);
45013       std::FILE *file;
45014       do {
45015         if (!cimg::strcasecmp(ext,"gz")) {
45016           if (*ext2) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
45017           else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.cimg",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
45018         } else {
45019           if (*ext) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
45020           else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.cimg",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
45021         }
45022         if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
45023       } while (file);
45024 
45025       if (is_saveable(body)) {
45026         save(filetmp);
45027         cimg_snprintf(command,sizeof(command),"%s -c %s > \"%s\"",cimg::gzip_path(),filetmp,filename);
45028         cimg::system(command);
45029         file = std::fopen(filename,"rb");
45030         if (!file)
45031           throw CImgIOException(_cimglist_instance
45032                                 "save_gzip_external() : Failed to save file '%s' with external command 'gzip'.",
45033                                 cimglist_instance,
45034                                 filename);
45035         else cimg::fclose(file);
45036         std::remove(filetmp);
45037       } else {
45038         char nfilename[1024] = { 0 };
45039         cimglist_for(*this,l) {
45040           cimg::number_filename(body,l,6,nfilename);
45041           if (*ext) std::sprintf(nfilename + std::strlen(nfilename),".%s",ext);
45042           _data[l].save_gzip_external(nfilename);
45043         }
45044       }
45045       return *this;
45046     }
45047 
45048     //! Save image sequence, using the external tool 'ffmpeg'.
45049     /**
45050       \param filename Filename to write data to.
45051       \param first_frame Index of first image frame to write.
45052       \param last_frame Index of last image frame to write.
45053       \param codec Type of compression.
45054       \param fps Number of frames per second.
45055       \param bitrate Output bitrate
45056     **/
45057     const CImgList<T>& save_ffmpeg_external(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
45058                                             const char *const codec=0, const unsigned int fps=25, const unsigned int bitrate=2048) const {
45059       if (!filename)
45060         throw CImgArgumentException(_cimglist_instance
45061                                     "save_ffmpeg_external() : Specified filename is (null).",
45062                                     cimglist_instance);
45063       if (is_empty())
45064         throw CImgInstanceException(_cimglist_instance
45065                                     "save_ffmpeg_external() : Empty instance, for file '%s'.",
45066                                     cimglist_instance,
45067                                     filename);
45068       const char
45069         *const ext = cimg::split_filename(filename),
45070         *const _codec = codec?codec:!cimg::strcasecmp(ext,"flv")?"flv":"mpeg2video";
45071 
45072       char command[1024] = { 0 }, filetmp[512] = { 0 }, filetmp2[512] = { 0 };
45073       std::FILE *file = 0;
45074       const unsigned int nlast_frame = last_frame==~0U?_width-1:last_frame;
45075       if (first_frame>=_width || nlast_frame>=_width)
45076         throw CImgArgumentException(_cimglist_instance
45077                                     "save_ffmpeg_external() : Out of range specified frames [%u,%u] for file '%s'.",
45078                                     cimglist_instance,
45079                                     filename,first_frame,last_frame);
45080 
45081       for (unsigned int ll = first_frame; ll<=nlast_frame; ++ll) if (!_data[ll].is_sameXYZ(_data[0]))
45082         throw CImgInstanceException(_cimglist_instance
45083                                     "save_ffmpeg_external() : Invalid instance dimensions for file '%s'.",
45084                                     cimglist_instance,
45085                                     filename);
45086       do {
45087         cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
45088         cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_000001.ppm",filetmp);
45089         if ((file=std::fopen(filetmp2,"rb"))!=0) cimg::fclose(file);
45090       } while (file);
45091       for (unsigned int l = first_frame; l<=nlast_frame; ++l) {
45092         cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_%.6u.ppm",filetmp,l+1);
45093         if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save_pnm(filetmp2);
45094         else _data[l].save_pnm(filetmp2);
45095       }
45096 #if cimg_OS!=2
45097       cimg_snprintf(command,sizeof(command),"ffmpeg -i %s_%%6d.ppm -vcodec %s -b %uk -r %u -y \"%s\" >/dev/null 2>&1",filetmp,_codec,bitrate,fps,filename);
45098 #else
45099       cimg_snprintf(command,sizeof(command),"\"ffmpeg -i %s_%%6d.ppm -vcodec %s -b %uk -r %u -y \"%s\"\" >NUL 2>&1",filetmp,_codec,bitrate,fps,filename);
45100 #endif
45101       cimg::system(command);
45102       file = std::fopen(filename,"rb");
45103       if (!file)
45104         throw CImgIOException(_cimglist_instance
45105                               "save_ffmpeg_external() : Failed to save file '%s' with external command 'ffmpeg'.",
45106                               cimglist_instance,
45107                               filename);
45108       else cimg::fclose(file);
45109       cimglist_for(*this,lll) { cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_%.6u.ppm",filetmp,lll+1); std::remove(filetmp2); }
45110       return *this;
45111     }
45112 
45113     //@}
45114     //----------------------------------
45115     //
45116     //! \name Others
45117     //@{
45118     //----------------------------------
45119 
45120     //! Crop font along the X-axis.
45121     /**
45122     **/
45123     CImgList<T>& crop_font() {
45124       return get_crop_font().move_to(*this);
45125     }
45126 
45127     //! Crop font along the X-axis \newinstance.
45128     /**
45129     **/
45130     CImgList<T> get_crop_font() const {
45131       CImgList<T> res;
45132       cimglist_for(*this,l) {
45133         const CImg<T>& letter = (*this)[l];
45134         int xmin = letter._width, xmax = 0;
45135         cimg_forXY(letter,x,y) if (letter(x,y)) { if (x<xmin) xmin = x; if (x>xmax) xmax = x; }
45136         if (xmin>xmax) CImg<T>(letter._width,letter._height,1,letter._spectrum,0).move_to(res);
45137         else letter.get_crop(xmin,0,xmax,letter._height-1).move_to(res);
45138       }
45139       res[' '].resize(res['f']._width,-100,-100,-100,0);
45140       if (' '+256<res.size()) res[' '+256].resize(res['f']._width,-100,-100,-100,0);
45141       return res;
45142     }
45143 
45144 
45145     //! Return a CImg pre-defined font with desired size.
45146     /**
45147        \param font_height Height of the desired font (exact match for 11,13,17,19,24,32,38,57)
45148        \param is_variable_width Decide if the font has a variable (\c true) or fixed (\c false) width.
45149     **/
45150     static const CImgList<T>& font(const unsigned int font_height, const bool is_variable_width=true) {
45151 
45152 #define _cimg_font(sx,sy) \
45153       if (!is_variable_width && (!font || font[0]._height!=sy)) font = _font(cimg::font##sx##x##sy,sx,sy,false); \
45154       if (is_variable_width  && (!vfont || vfont[0]._height!=sy)) vfont = _font(cimg::font##sx##x##sy,sx,sy,true); \
45155       if (font_height==sy) return is_variable_width?vfont:font; \
45156       if (is_variable_width) { \
45157         if (cvfont && font_height==cvfont[0]._height) return cvfont; \
45158         cvfont = vfont; \
45159         cimglist_for(cvfont,l) \
45160           cvfont[l].resize(cimg::max(1U,cvfont[l]._width*font_height/cvfont[l]._height),font_height,-100,-100, \
45161                            cvfont[0]._height>font_height?2:5); \
45162         return cvfont; \
45163       } else { \
45164         if (cfont && font_height==cfont[0]._height) return cfont; \
45165         cfont = font; \
45166         cimglist_for(cfont,l) \
45167           cfont[l].resize(cimg::max(1U,cfont[l]._width*font_height/cfont[l]._height),font_height,-100,-100, \
45168                           cfont[0]._height>font_height?2:5); \
45169         return cfont; \
45170       } \
45171 
45172       static CImgList<T> font, vfont, cfont, cvfont;
45173       if (!font_height) return CImgList<T>::empty();
45174       if (font_height<=13) { _cimg_font(10,13); } // [1,13] -> ref 13
45175       if (font_height<=28) { _cimg_font(12,24); } // [14,28] -> ref 24
45176       if (font_height<=32) { _cimg_font(16,32); } // [29,32] -> ref 32
45177       _cimg_font(29,57);                          // [33,+inf] -> ref 57
45178     }
45179 
45180     static CImgList<T> _font(const unsigned int *const font, const unsigned int w, const unsigned int h, const bool is_variable_width) {
45181       CImgList<T> res(256,w,h,1,1);
45182       const unsigned int *ptr = font;
45183       unsigned int m = 0, val = 0;
45184       for (unsigned int y = 0; y<h; ++y)
45185         for (unsigned int x = 0; x<256*w; ++x) {
45186           m>>=1; if (!m) { m = 0x80000000; val = *(ptr++); }
45187           CImg<T>& img = res[x/w];
45188           unsigned int xm = x%w;
45189           img(xm,y) = (T)((val&m)?1:0);
45190         }
45191       if (is_variable_width) res.crop_font();
45192       return  res.insert(res);
45193     }
45194 
45195     //! Compute a 1d Fast Fourier Transform, along specified axis.
45196     /**
45197        \param axis Axis along which the Fourier transform is computed.
45198        \param invert Tells if the direct (\c false) or inverse transform (\c true) is computed.
45199     **/
45200     CImgList<T>& FFT(const char axis, const bool invert=false) {
45201       if (is_empty()) return *this;
45202       if (_width==1) insert(1);
45203       if (_width>2)
45204         cimg::warn(_cimglist_instance
45205                    "FFT() : Instance has more than 2 images",
45206                    cimglist_instance);
45207 
45208       CImg<T>::FFT(_data[0],_data[1],axis,invert);
45209       return *this;
45210     }
45211 
45212     //! Compute a 1-D Fast Fourier Transform, along specified axis \newinstance.
45213     CImgList<Tfloat> get_FFT(const char axis, const bool invert=false) const {
45214       return CImgList<Tfloat>(*this,false).FFT(axis,invert);
45215     }
45216 
45217     //! Compute a n-d Fast Fourier Transform.
45218     /**
45219       \param invert Tells if the direct (\c false) or inverse transform (\c true) is computed.
45220     **/
45221     CImgList<T>& FFT(const bool invert=false) {
45222       if (is_empty()) return *this;
45223       if (_width==1) insert(1);
45224       if (_width>2)
45225         cimg::warn(_cimglist_instance
45226                    "FFT() : Instance has more than 2 images",
45227                    cimglist_instance);
45228 
45229       CImg<T>::FFT(_data[0],_data[1],invert);
45230       return *this;
45231     }
45232 
45233     //! Compute a n-d Fast Fourier Transform \newinstance.
45234     CImgList<Tfloat> get_FFT(const bool invert=false) const {
45235       return CImgList<Tfloat>(*this,false).FFT(invert);
45236     }
45237 
45238     //! Reverse primitives orientations of a 3d object.
45239     /**
45240     **/
45241     CImgList<T>& reverse_object3d() {
45242       cimglist_for(*this,l) {
45243         CImg<T>& p = _data[l];
45244         switch (p.size()) {
45245         case 2: case 3: cimg::swap(p[0],p[1]); break;
45246         case 6: cimg::swap(p[0],p[1],p[2],p[4],p[3],p[5]); break;
45247         case 9: cimg::swap(p[0],p[1],p[3],p[5],p[4],p[6]); break;
45248         case 4: cimg::swap(p[0],p[1],p[2],p[3]); break;
45249         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;
45250         }
45251       }
45252       return *this;
45253     }
45254 
45255     //! Reverse primitives orientations of a 3d object \newinstance.
45256     CImgList<T> get_reverse_object3d() const {
45257       return (+*this).reverse_object3d();
45258     }
45259 
45260     //@}
45261   }; // struct CImgList<T> { ...
45262 
45263   /*
45264   #---------------------------------------------
45265   #
45266    # Completion of previously declared functions
45267    #
45268    #----------------------------------------------
45269   */
45270 
45271 namespace cimg {
45272 
45273   //! Display a simple dialog box, and wait for the user's response.
45274   /**
45275      \param title Title of the dialog window.
45276      \param msg Main message displayed inside the dialog window.
45277      \param button1_label Label of the 1st button.
45278      \param button2_label Label of the 2nd button (\c 0 to hide button).
45279      \param button3_label Label of the 3rd button (\c 0 to hide button).
45280      \param button4_label Label of the 4th button (\c 0 to hide button).
45281      \param button5_label Label of the 5th button (\c 0 to hide button).
45282      \param button6_label Label of the 6th button (\c 0 to hide button).
45283      \param logo Image logo displayed at the left of the main message.
45284      \param is_centered Tells if the dialog window must be centered on the screen.
45285      \return Indice of clicked button (from \c 0 to \c 5), or \c -1 if the dialog window has been closed by the user.
45286      \note
45287      - Up to 6 buttons can be defined in the dialog window.
45288      - The function returns when a user clicked one of the button or closed the dialog window.
45289      - If a button text is set to 0, the corresponding button (and the followings) will not appear in the dialog box. At least one button must be specified.
45290   **/
45291   template<typename t>
45292   inline int dialog(const char *const title, const char *const msg,
45293                     const char *const button1_label, const char *const button2_label,
45294                     const char *const button3_label, const char *const button4_label,
45295                     const char *const button5_label, const char *const button6_label,
45296                     const CImg<t>& logo, const bool is_centered = false) {
45297 #if cimg_display==0
45298     cimg::unused(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label,logo._data,is_centered);
45299     throw CImgIOException("cimg::dialog() : No display available.");
45300 #else
45301     const unsigned char
45302       black[] = { 0,0,0 }, white[] = { 255,255,255 }, gray[] = { 200,200,200 }, gray2[] = { 150,150,150 };
45303 
45304     // Create buttons and canvas graphics
45305     CImgList<unsigned char> buttons, cbuttons, sbuttons;
45306     if (button1_label) { CImg<unsigned char>().draw_text(0,0,button1_label,black,gray,1,13).move_to(buttons);
45307       if (button2_label) { CImg<unsigned char>().draw_text(0,0,button2_label,black,gray,1,13).move_to(buttons);
45308         if (button3_label) { CImg<unsigned char>().draw_text(0,0,button3_label,black,gray,1,13).move_to(buttons);
45309           if (button4_label) { CImg<unsigned char>().draw_text(0,0,button4_label,black,gray,1,13).move_to(buttons);
45310             if (button5_label) { CImg<unsigned char>().draw_text(0,0,button5_label,black,gray,1,13).move_to(buttons);
45311               if (button6_label) { CImg<unsigned char>().draw_text(0,0,button6_label,black,gray,1,13).move_to(buttons);
45312               }}}}}}
45313     if (!buttons._width)
45314       throw CImgArgumentException("cimg::dialog() : No buttons have been defined.");
45315     cimglist_for(buttons,l) buttons[l].resize(-100,-100,1,3);
45316 
45317     unsigned int bw = 0, bh = 0;
45318     cimglist_for(buttons,l) { bw = cimg::max(bw,buttons[l]._width); bh = cimg::max(bh,buttons[l]._height); }
45319     bw+=8; bh+=8;
45320     if (bw<64) bw = 64;
45321     if (bw>128) bw = 128;
45322     if (bh<24) bh = 24;
45323     if (bh>48) bh = 48;
45324 
45325     CImg<unsigned char> button(bw,bh,1,3);
45326     button.draw_rectangle(0,0,bw-1,bh-1,gray);
45327     button.draw_line(0,0,bw-1,0,white).draw_line(0,bh-1,0,0,white);
45328     button.draw_line(bw-1,0,bw-1,bh-1,black).draw_line(bw-1,bh-1,0,bh-1,black);
45329     button.draw_line(1,bh-2,bw-2,bh-2,gray2).draw_line(bw-2,bh-2,bw-2,1,gray2);
45330     CImg<unsigned char> sbutton(bw,bh,1,3);
45331     sbutton.draw_rectangle(0,0,bw-1,bh-1,gray);
45332     sbutton.draw_line(0,0,bw-1,0,black).draw_line(bw-1,0,bw-1,bh-1,black);
45333     sbutton.draw_line(bw-1,bh-1,0,bh-1,black).draw_line(0,bh-1,0,0,black);
45334     sbutton.draw_line(1,1,bw-2,1,white).draw_line(1,bh-2,1,1,white);
45335     sbutton.draw_line(bw-2,1,bw-2,bh-2,black).draw_line(bw-2,bh-2,1,bh-2,black);
45336     sbutton.draw_line(2,bh-3,bw-3,bh-3,gray2).draw_line(bw-3,bh-3,bw-3,2,gray2);
45337     sbutton.draw_line(4,4,bw-5,4,black,1,0xAAAAAAAA,true).draw_line(bw-5,4,bw-5,bh-5,black,1,0xAAAAAAAA,false);
45338     sbutton.draw_line(bw-5,bh-5,4,bh-5,black,1,0xAAAAAAAA,false).draw_line(4,bh-5,4,4,black,1,0xAAAAAAAA,false);
45339     CImg<unsigned char> cbutton(bw,bh,1,3);
45340     cbutton.draw_rectangle(0,0,bw-1,bh-1,black).draw_rectangle(1,1,bw-2,bh-2,gray2).draw_rectangle(2,2,bw-3,bh-3,gray);
45341     cbutton.draw_line(4,4,bw-5,4,black,1,0xAAAAAAAA,true).draw_line(bw-5,4,bw-5,bh-5,black,1,0xAAAAAAAA,false);
45342     cbutton.draw_line(bw-5,bh-5,4,bh-5,black,1,0xAAAAAAAA,false).draw_line(4,bh-5,4,4,black,1,0xAAAAAAAA,false);
45343 
45344     cimglist_for(buttons,ll) {
45345       CImg<unsigned char>(cbutton).draw_image(1+(bw-buttons[ll].width())/2,1+(bh-buttons[ll].height())/2,buttons[ll]).
45346         move_to(cbuttons);
45347       CImg<unsigned char>(sbutton).draw_image((bw-buttons[ll].width())/2,(bh-buttons[ll].height())/2,buttons[ll]).
45348         move_to(sbuttons);
45349       CImg<unsigned char>(button).draw_image((bw-buttons[ll].width())/2,(bh-buttons[ll].height())/2,buttons[ll]).
45350         move_to(buttons[ll]);
45351     }
45352 
45353     CImg<unsigned char> canvas;
45354     if (msg) CImg<unsigned char>().draw_text(0,0,"%s",black,gray,1,13,msg).resize(-100,-100,1,3).move_to(canvas);
45355     const unsigned int
45356       bwall = (buttons._width-1)*(12+bw) + bw,
45357       w = cimg::max(196U,36+logo._width+canvas._width,24+bwall),
45358       h = cimg::max(96U,36+canvas._height+bh,36+logo._height+bh),
45359       lx = 12 + (canvas._data?0:((w-24-logo._width)/2)),
45360       ly = (h-12-bh-logo._height)/2,
45361       tx = lx+logo._width+12,
45362       ty = (h-12-bh-canvas._height)/2,
45363       bx = (w-bwall)/2,
45364       by = h-12-bh;
45365 
45366     if (canvas._data)
45367       canvas = CImg<unsigned char>(w,h,1,3).
45368         draw_rectangle(0,0,w-1,h-1,gray).
45369         draw_line(0,0,w-1,0,white).draw_line(0,h-1,0,0,white).
45370         draw_line(w-1,0,w-1,h-1,black).draw_line(w-1,h-1,0,h-1,black).
45371         draw_image(tx,ty,canvas);
45372     else
45373       canvas = CImg<unsigned char>(w,h,1,3).
45374         draw_rectangle(0,0,w-1,h-1,gray).
45375         draw_line(0,0,w-1,0,white).draw_line(0,h-1,0,0,white).
45376         draw_line(w-1,0,w-1,h-1,black).draw_line(w-1,h-1,0,h-1,black);
45377     if (logo._data) canvas.draw_image(lx,ly,logo);
45378 
45379     unsigned int xbuttons[6] = { 0 };
45380     cimglist_for(buttons,lll) { xbuttons[lll] = bx+(bw+12)*lll; canvas.draw_image(xbuttons[lll],by,buttons[lll]); }
45381 
45382     // Open window and enter events loop
45383     CImgDisplay disp(canvas,title?title:" ",0,false,is_centered?true:false);
45384     if (is_centered) disp.move((CImgDisplay::screen_width() - disp.width())/2,
45385                              (CImgDisplay::screen_height() - disp.height())/2);
45386     bool stopflag = false, refresh = false;
45387     int oselected = -1, oclicked = -1, selected = -1, clicked = -1;
45388     while (!disp.is_closed() && !stopflag) {
45389       if (refresh) {
45390         if (clicked>=0) CImg<unsigned char>(canvas).draw_image(xbuttons[clicked],by,cbuttons[clicked]).display(disp);
45391         else {
45392           if (selected>=0) CImg<unsigned char>(canvas).draw_image(xbuttons[selected],by,sbuttons[selected]).display(disp);
45393           else canvas.display(disp);
45394         }
45395         refresh = false;
45396       }
45397       disp.wait(15);
45398       if (disp.is_resized()) disp.resize(disp,false);
45399 
45400       if (disp.button()&1)  {
45401         oclicked = clicked;
45402         clicked = -1;
45403         cimglist_for(buttons,l)
45404           if (disp.mouse_y()>=(int)by && disp.mouse_y()<(int)(by+bh) &&
45405               disp.mouse_x()>=(int)xbuttons[l] && disp.mouse_x()<(int)(xbuttons[l]+bw)) {
45406             clicked = selected = l;
45407             refresh = true;
45408           }
45409         if (clicked!=oclicked) refresh = true;
45410       } else if (clicked>=0) stopflag = true;
45411 
45412       if (disp.key()) {
45413         oselected = selected;
45414         switch (disp.key()) {
45415         case cimg::keyESC : selected=-1; stopflag=true; break;
45416         case cimg::keyENTER : if (selected<0) selected = 0; stopflag = true; break;
45417         case cimg::keyTAB :
45418         case cimg::keyARROWRIGHT :
45419         case cimg::keyARROWDOWN : selected = (selected+1)%buttons._width; break;
45420         case cimg::keyARROWLEFT :
45421         case cimg::keyARROWUP : selected = (selected+buttons._width-1)%buttons._width; break;
45422         }
45423         disp.set_key();
45424         if (selected!=oselected) refresh = true;
45425       }
45426     }
45427     if (!disp) selected = -1;
45428     return selected;
45429 #endif
45430   }
45431 
45432   //! Display a simple dialog box, and wait for the user's response \specialization.
45433   inline int dialog(const char *const title, const char *const msg,
45434                     const char *const button1_label, const char *const button2_label, const char *const button3_label,
45435                     const char *const button4_label, const char *const button5_label, const char *const button6_label,
45436                     const bool is_centered) {
45437     return dialog(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label,
45438                   CImg<unsigned char>::_logo40x38(),is_centered);
45439   }
45440 
45441   //! Evaluate math expression.
45442   /**
45443      \param expression C-string describing the formula to evaluate.
45444      \param x Value of the pre-defined variable \c x.
45445      \param y Value of the pre-defined variable \c y.
45446      \param z Value of the pre-defined variable \c z.
45447      \param c Value of the pre-defined variable \c c.
45448      \return Result of the formula evaluation.
45449      \note Set \c expression to \c 0 to keep evaluating the last specified \c expression.
45450      \par Example
45451      \code
45452      const double
45453        res1 = cimg::eval("cos(x)^2+sin(y)^2",2,2),  // will return '1'.
45454        res2 = cimg::eval(0,1,1);                    // will return '1' too.
45455      \endcode
45456   **/
45457   inline double eval(const char *const expression, const double x, const double y, const double z, const double c) {
45458     static const CImg<float> empty;
45459     return empty.eval(expression,x,y,z,c);
45460   }
45461 
45462   // End of cimg:: namespace
45463 }
45464 
45465   // End of cimg_library:: namespace
45466 }
45467 
45468 //! Short alias name.
45469 namespace cil = cimg_library_suffixed;
45470 
45471 #ifdef _cimg_redefine_False
45472 #define False 0
45473 #endif
45474 #ifdef _cimg_redefine_True
45475 #define True 1
45476 #endif
45477 #ifdef _cimg_redefine_None
45478 #define None 0
45479 #endif
45480 #ifdef _cimg_redefine_min
45481 #define min(a,b) (((a)<(b))?(a):(b))
45482 #endif
45483 #ifdef _cimg_redefine_max
45484 #define max(a,b) (((a)>(b))?(a):(b))
45485 #endif
45486 #ifdef _cimg_redefine_PI
45487 #define PI 3.141592653589793238462643383
45488 #endif
45489 
45490 #endif
45491 // Local Variables:
45492 // mode: c++
45493 // End:
45494