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://www.greyc.ensicaen.fr/~dtschump/ )
12  #
13  #                    The 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 // Define version number of the library file.
56 #ifndef cimg_version
57 #define cimg_version 149
58 
59 /*-----------------------------------------------------------
60  #
61  # Test and auto-set CImg configuration variables
62  # and include required headers.
63  #
64  # If you find that default configuration variables are
65  # not adapted to your case, you can override their values
66  # before including the header file "CImg.h"
67  # (use the #define directive).
68  #
69  ------------------------------------------------------------*/
70 
71 // Include required standard C++ headers.
72 #include <cstdio>
73 #include <cstdlib>
74 #include <cstdarg>
75 #include <cstring>
76 #include <cmath>
77 #include <ctime>
78 #include <exception>
79 
80 // Operating system configuration.
81 //
82 // Define 'cimg_OS' to : '0' for an unknown OS (will try to minize library dependancies).
83 //                       '1' for a Unix-like OS (Linux, Solaris, BSD, MacOSX, Irix, ...).
84 //                       '2' for Microsoft Windows.
85 //                       (autodetection is done by default).
86 #ifndef cimg_OS
87 #if defined(unix)        || defined(__unix)      || defined(__unix__) \
88  || defined(linux)       || defined(__linux)     || defined(__linux__) \
89  || defined(sun)         || defined(__sun) \
90  || defined(BSD)         || defined(__OpenBSD__) || defined(__NetBSD__) \
91  || defined(__FreeBSD__) || defined __DragonFly__ \
92  || defined(sgi)         || defined(__sgi) \
93  || defined(__MACOSX__)  || defined(__APPLE__) \
94  || defined(__CYGWIN__)
95 #define cimg_OS 1
96 #elif defined(_MSC_VER) || defined(WIN32)  || defined(_WIN32) || defined(__WIN32__) \
97    || defined(WIN64)    || defined(_WIN64) || defined(__WIN64__)
98 #define cimg_OS 2
99 #else
100 #define cimg_OS 0
101 #endif
102 #elif !(cimg_OS==0 || cimg_OS==1 || cimg_OS==2)
103 #error CImg Library : Configuration variable 'cimg_OS' is badly defined.
104 #error (valid values are '0 = unknown OS', '1 = Unix-like OS', '2 = Microsoft Windows').
105 #endif
106 
107 // Disable silly warnings on Microsoft VC++ compilers.
108 #ifdef _MSC_VER
109 #pragma warning(push)
110 #pragma warning(disable:4311)
111 #pragma warning(disable:4312)
112 #pragma warning(disable:4800)
113 #pragma warning(disable:4804)
114 #pragma warning(disable:4996)
115 #define _CRT_SECURE_NO_DEPRECATE 1
116 #define _CRT_NONSTDC_NO_DEPRECATE 1
117 #endif
118 
119 // Include OS-specific headers for system management.
120 #if cimg_OS==1
121 #include <sys/types.h>
122 #include <sys/time.h>
123 #include <unistd.h>
124 #elif cimg_OS==2
125 #ifndef NOMINMAX
126 #define NOMINMAX
127 #endif
128 #include <windows.h>
129 #ifndef _WIN32_IE
130 #define _WIN32_IE 0x0400
131 #endif
132 #include <shlobj.h>
133 #include <io.h>
134 #define cimg_snprintf _snprintf
135 #define cimg_vsnprintf _vsnprintf
136 #endif
137 #ifndef cimg_snprintf
138 #include <stdio.h>
139 #define cimg_snprintf snprintf
140 #define cimg_vsnprintf vsnprintf
141 #endif
142 
143 // Filename separator configuration.
144 //
145 // Default separator is '/' for Unix-based OS, and '\' or Windows.
146 #ifndef cimg_file_separator
147 #if cimg_OS==2
148 #define cimg_file_separator '\\'
149 #else
150 #define cimg_file_separator '/'
151 #endif
152 #endif
153 
154 // Output messages verbosity configuration.
155 //
156 // Define 'cimg_verbosity' to : '0' to hide library messages (quiet mode).
157 //                              '1' to print library messages on the console.
158 //                              '2' to display library messages on a dialog window (default behavior).
159 //                              '3' to do as '1' + add extra warnings (may slow down the code !).
160 //                              '4' to do as '2' + add extra warnings (may slow down the code !).
161 //
162 // Define 'cimg_strict_warnings' to replace warning messages by exception throwns.
163 //
164 // Define 'cimg_use_vt100' to allow output of color messages (require VT100-compatible terminal).
165 #ifndef cimg_verbosity
166 #define cimg_verbosity 2
167 #elif !(cimg_verbosity==0 || cimg_verbosity==1 || cimg_verbosity==2 || cimg_verbosity==3 || cimg_verbosity==4)
168 #error CImg Library : Configuration variable 'cimg_verbosity' is badly defined.
169 #error (should be { 0=quiet | 1=console | 2=dialog | 3=console+warnings | 4=dialog+warnings }).
170 #endif
171 
172 // Display framework configuration.
173 //
174 // Define 'cimg_display' to : '0' to disable display capabilities.
175 //                            '1' to use X-Window framework (X11).
176 //                            '2' to use Microsoft GDI32 framework.
177 #ifndef cimg_display
178 #if cimg_OS==0
179 #define cimg_display 0
180 #elif cimg_OS==1
181 #if defined(__MACOSX__) || defined(__APPLE__)
182 #define cimg_display 1
183 #else
184 #define cimg_display 1
185 #endif
186 #elif cimg_OS==2
187 #define cimg_display 2
188 #endif
189 #elif !(cimg_display==0 || cimg_display==1 || cimg_display==2)
190 #error CImg Library : Configuration variable 'cimg_display' is badly defined.
191 #error (should be { 0=none | 1=X-Window (X11) | 2=Microsoft GDI32 }).
192 #endif
193 
194 // Include display-specific headers.
195 #if cimg_display==1
196 #include <X11/Xlib.h>
197 #include <X11/Xutil.h>
198 #include <X11/keysym.h>
199 #include <pthread.h>
200 #ifdef cimg_use_xshm
201 #include <sys/ipc.h>
202 #include <sys/shm.h>
203 #include <X11/extensions/XShm.h>
204 #endif
205 #ifdef cimg_use_xrandr
206 #include <X11/extensions/Xrandr.h>
207 #endif
208 #endif
209 #ifndef cimg_appname
210 #define cimg_appname "CImg"
211 #endif
212 
213 // OpenMP configuration.
214 // (http://www.openmp.org)
215 //
216 // Define 'cimg_use_openmp' to enable OpenMP support.
217 //
218 // OpenMP directives can be used in few CImg functions to get
219 // advantages of multi-core CPUs. Using OpenMP is not mandatory.
220 #ifdef cimg_use_openmp
221 #include "omp.h"
222 #define _cimg_static
223 #else
224 #define _cimg_static static
225 #endif
226 
227 // OpenCV configuration
228 // (http://opencv.willowgarage.com/wiki/)
229 //
230 // Define 'cimg_use_opencv' to enable OpenCV support.
231 //
232 // OpenCV can be used to retrieve images from cameras
233 // (with function 'CImg<T>::load_camera()'.
234 // Using OpenCV is not mandatory.
235 #ifdef cimg_use_opencv
236 #include <cstddef>
237 #include "cv.h"
238 #include "highgui.h"
239 #endif
240 
241 // LibPNG configuration.
242 // (http://www.libpng.org)
243 //
244 // Define 'cimg_use_png' to enable LibPNG support.
245 //
246 // LibPNG can be used in functions 'CImg<T>::{load,save}_png()'
247 // to get a builtin support of PNG files. Using LibPNG is not mandatory.
248 #ifdef cimg_use_png
249 extern "C" {
250 #include "png.h"
251 }
252 #endif
253 
254 // LibJPEG configuration.
255 // (http://en.wikipedia.org/wiki/Libjpeg)
256 //
257 // Define 'cimg_use_jpeg' to enable LibJPEG support.
258 //
259 // LibJPEG can be used in functions 'CImg<T>::{load,save}_jpeg()'
260 // to get a builtin support of JPEG files. Using LibJPEG is not mandatory.
261 #ifdef cimg_use_jpeg
262 extern "C" {
263 #include "jpeglib.h"
264 #include "setjmp.h"
265 }
266 #endif
267 
268 // LibTIFF configuration.
269 // (http://www.libtiff.org)
270 //
271 // Define 'cimg_use_tiff' to enable LibTIFF support.
272 //
273 // LibTIFF can be used in functions 'CImg[List]<T>::{load,save}_tiff()'
274 // to get a builtin support of TIFF files. Using LibTIFF is not mandatory.
275 #ifdef cimg_use_tiff
276 extern "C" {
277 #include "tiffio.h"
278 }
279 #endif
280 
281 // LibMINC2 configuration.
282 // (http://en.wikibooks.org/wiki/MINC/Reference/MINC2.0_File_Format_Reference)
283 //
284 // Define 'cimg_use_minc2' to enable LibMINC2 support.
285 //
286 // LibMINC2 can be used in functions 'CImg<T>::{load,save}_minc2()'
287 // to get a builtin support of MINC2 files. Using LibMINC2 is not mandatory.
288 #ifdef cimg_use_minc2
289 extern "C" {
290 #include "minc2.h"
291 }
292 #endif
293 
294 // FFMPEG Avcodec and Avformat libraries configuration.
295 // (http://www.ffmpeg.org)
296 //
297 // Define 'cimg_use_ffmpeg' to enable FFMPEG lib support.
298 //
299 // Avcodec and Avformat libraries can be used in functions
300 // 'CImg[List]<T>::load_ffmpeg()' to get a builtin
301 // support of various image sequences files.
302 // Using FFMPEG libraries is not mandatory.
303 #ifdef cimg_use_ffmpeg
304 #if (defined(_STDINT_H) || defined(_STDINT_H_)) && !defined(UINT64_C)
305 #warning "__STDC_CONSTANT_MACROS has to be defined before including <stdint.h>, this file will probably not compile."
306 #endif
307 #ifndef __STDC_CONSTANT_MACROS
308 #define __STDC_CONSTANT_MACROS // ...or stdint.h doesn't define UINT64_C, needed for libavutil
309 #endif
310 extern "C" {
311 #include "avformat.h"
312 #include "avcodec.h"
313 #include "swscale.h"
314 }
315 #endif
316 
317 // Zlib configuration
318 // (http://www.zlib.net)
319 //
320 // Define 'cimg_use_zlib' to enable Zlib support.
321 //
322 // Zlib can be used in functions 'CImg[List]<T>::{load,save}_cimg()'
323 // to allow compressed data in '.cimg' files. Using Zlib is not mandatory.
324 #ifdef cimg_use_zlib
325 extern "C" {
326 #include "zlib.h"
327 }
328 #endif
329 
330 // Magick++ configuration.
331 // (http://www.imagemagick.org/Magick++)
332 //
333 // Define 'cimg_use_magick' to enable Magick++ support.
334 //
335 // Magick++ library can be used in functions 'CImg<T>::{load,save}()'
336 // to get a builtin support of various image formats (PNG,JPEG,TIFF,...).
337 // Using Magick++ is not mandatory.
338 #ifdef cimg_use_magick
339 #include "Magick++.h"
340 #endif
341 
342 // FFTW3 configuration.
343 // (http://www.fftw.org)
344 //
345 // Define 'cimg_use_fftw3' to enable libFFTW3 support.
346 //
347 // FFTW3 library can be used in functions 'CImg[List]<T>::FFT()' to
348 // efficiently compute the Fast Fourier Transform of image data.
349 #ifdef cimg_use_fftw3
350 extern "C" {
351 #include "fftw3.h"
352 }
353 #endif
354 
355 // Board configuration
356 // (http://libboard.sourceforge.net/)
357 //
358 // Define 'cimg_use_board' to enable Board support.
359 //
360 // Board library can be used in functions 'CImg<T>::draw_object3d()'
361 // to draw objects 3d in vector-graphics canvas that can be saved
362 // as .PS or .SVG files afterwards.
363 #ifdef cimg_use_board
364 #ifdef None
365 #undef None
366 #define _cimg_redefine_None
367 #endif
368 #include "Board.h"
369 #endif
370 
371 // OpenEXR configuration
372 // (http://www.openexr.com/)
373 //
374 // Define 'cimg_use_openexr' to enable OpenEXR support.
375 //
376 // OpenEXR can be used to read/write .exr file formats.
377 #ifdef cimg_use_openexr
378 #include "ImfRgbaFile.h"
379 #include "ImfInputFile.h"
380 #include "ImfChannelList.h"
381 #include "ImfMatrixAttribute.h"
382 #include "ImfArray.h"
383 #endif
384 
385 // Lapack configuration.
386 // (http://www.netlib.org/lapack)
387 //
388 // Define 'cimg_use_lapack' to enable LAPACK support.
389 //
390 // Lapack can be used in various CImg functions dealing with
391 // matrix computation and algorithms (eigenvalues, inverse, ...).
392 // Using Lapack is not mandatory.
393 #ifdef cimg_use_lapack
394 extern "C" {
395   extern void sgetrf_(int*, int*, float*, int*, int*, int*);
396   extern void sgetri_(int*, float*, int*, int*, float*, int*, int*);
397   extern void sgetrs_(char*, int*, int*, float*, int*, int*, float*, int*, int*);
398   extern void sgesvd_(char*, char*, int*, int*, float*, int*, float*, float*, int*, float*, int*, float*, int*, int*);
399   extern void ssyev_(char*, char*, int*, float*, int*, float*, float*, int*, int*);
400   extern void dgetrf_(int*, int*, double*, int*, int*, int*);
401   extern void dgetri_(int*, double*, int*, int*, double*, int*, int*);
402   extern void dgetrs_(char*, int*, int*, double*, int*, int*, double*, int*, int*);
403   extern void dgesvd_(char*, char*, int*, int*, double*, int*, double*, double*, int*, double*, int*, double*, int*, int*);
404   extern void dsyev_(char*, char*, int*, double*, int*, double*, double*, int*, int*);
405 }
406 #endif
407 
408 // Check if min/max/PI macros are defined.
409 //
410 // CImg does not compile if macros 'min', 'max' or 'PI' are defined,
411 // because min(), max() and PI labels are defined and used in the cimg:: namespace.
412 // so it '#undef' these macros if necessary, and restore them to reasonable
413 // values at the end of the file.
414 #ifdef min
415 #undef min
416 #define _cimg_redefine_min
417 #endif
418 #ifdef max
419 #undef max
420 #define _cimg_redefine_max
421 #endif
422 #ifdef PI
423 #undef PI
424 #define _cimg_redefine_PI
425 #endif
426 
427 /*------------------------------------------------------------------------------
428   #
429   # Define user-friendly macros.
430   #
431   # User macros are prefixed by 'cimg_' and can be used in your own code.
432   # They are particularly useful for option parsing, and image loops creation.
433   #
434   ------------------------------------------------------------------------------*/
435 
436 // Define the program usage, and retrieve command line arguments.
437 #define cimg_usage(usage) cimg_library::cimg::option((char*)0,argc,argv,(char*)0,usage,false)
438 #define cimg_help(str) cimg_library::cimg::option((char*)0,argc,argv,str,(char*)0)
439 #define cimg_option(name,defaut,usage) cimg_library::cimg::option(name,argc,argv,defaut,usage)
440 #define cimg_argument(pos) cimg_library::cimg::argument(pos,argc,argv)
441 #define cimg_argument1(pos,s0) cimg_library::cimg::argument(pos,argc,argv,1,s0)
442 #define cimg_argument2(pos,s0,s1) cimg_library::cimg::argument(pos,argc,argv,2,s0,s1)
443 #define cimg_argument3(pos,s0,s1,s2) cimg_library::cimg::argument(pos,argc,argv,3,s0,s1,s2)
444 #define cimg_argument4(pos,s0,s1,s2,s3) cimg_library::cimg::argument(pos,argc,argv,4,s0,s1,s2,s3)
445 #define cimg_argument5(pos,s0,s1,s2,s3,s4) cimg_library::cimg::argument(pos,argc,argv,5,s0,s1,s2,s3,s4)
446 #define cimg_argument6(pos,s0,s1,s2,s3,s4,s5) cimg_library::cimg::argument(pos,argc,argv,6,s0,s1,s2,s3,s4,s5)
447 #define cimg_argument7(pos,s0,s1,s2,s3,s4,s5,s6) cimg_library::cimg::argument(pos,argc,argv,7,s0,s1,s2,s3,s4,s5,s6)
448 #define cimg_argument8(pos,s0,s1,s2,s3,s4,s5,s6,s7) cimg_library::cimg::argument(pos,argc,argv,8,s0,s1,s2,s3,s4,s5,s6,s7)
449 #define cimg_argument9(pos,s0,s1,s2,s3,s4,s5,s6,s7,s8) cimg_library::cimg::argument(pos,argc,argv,9,s0,s1,s2,s3,s4,s5,s6,s7,s8)
450 
451 // Define and manipulate local neighborhoods.
452 #define CImg_2x2(I,T) T I[4]; \
453                       T& I##cc = I[0]; T& I##nc = I[1]; \
454                       T& I##cn = I[2]; T& I##nn = I[3]; \
455                       I##cc = I##nc = \
456                       I##cn = I##nn = 0
457 
458 #define CImg_3x3(I,T) T I[9]; \
459                       T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; \
460                       T& I##pc = I[3]; T& I##cc = I[4]; T& I##nc = I[5]; \
461                       T& I##pn = I[6]; T& I##cn = I[7]; T& I##nn = I[8]; \
462                       I##pp = I##cp = I##np = \
463                       I##pc = I##cc = I##nc = \
464                       I##pn = I##cn = I##nn = 0
465 
466 #define CImg_4x4(I,T) T I[16]; \
467                       T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; T& I##ap = I[3]; \
468                       T& I##pc = I[4]; T& I##cc = I[5]; T& I##nc = I[6]; T& I##ac = I[7]; \
469                       T& I##pn = I[8]; T& I##cn = I[9]; T& I##nn = I[10]; T& I##an = I[11]; \
470                       T& I##pa = I[12]; T& I##ca = I[13]; T& I##na = I[14]; T& I##aa = I[15]; \
471                       I##pp = I##cp = I##np = I##ap = \
472                       I##pc = I##cc = I##nc = I##ac = \
473                       I##pn = I##cn = I##nn = I##an = \
474                       I##pa = I##ca = I##na = I##aa = 0
475 
476 #define CImg_5x5(I,T) T I[25]; \
477                       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]; \
478                       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]; \
479                       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]; \
480                       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]; \
481                       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]; \
482                       I##bb = I##pb = I##cb = I##nb = I##ab = \
483                       I##bp = I##pp = I##cp = I##np = I##ap = \
484                       I##bc = I##pc = I##cc = I##nc = I##ac = \
485                       I##bn = I##pn = I##cn = I##nn = I##an = \
486                       I##ba = I##pa = I##ca = I##na = I##aa = 0
487 
488 #define CImg_2x2x2(I,T) T I[8]; \
489                       T& I##ccc = I[0]; T& I##ncc = I[1]; \
490                       T& I##cnc = I[2]; T& I##nnc = I[3]; \
491                       T& I##ccn = I[4]; T& I##ncn = I[5]; \
492                       T& I##cnn = I[6]; T& I##nnn = I[7]; \
493                       I##ccc = I##ncc = \
494                       I##cnc = I##nnc = \
495                       I##ccn = I##ncn = \
496                       I##cnn = I##nnn = 0
497 
498 #define CImg_3x3x3(I,T) T I[27]; \
499                       T& I##ppp = I[0]; T& I##cpp = I[1]; T& I##npp = I[2]; \
500                       T& I##pcp = I[3]; T& I##ccp = I[4]; T& I##ncp = I[5]; \
501                       T& I##pnp = I[6]; T& I##cnp = I[7]; T& I##nnp = I[8]; \
502                       T& I##ppc = I[9]; T& I##cpc = I[10]; T& I##npc = I[11]; \
503                       T& I##pcc = I[12]; T& I##ccc = I[13]; T& I##ncc = I[14]; \
504                       T& I##pnc = I[15]; T& I##cnc = I[16]; T& I##nnc = I[17]; \
505                       T& I##ppn = I[18]; T& I##cpn = I[19]; T& I##npn = I[20]; \
506                       T& I##pcn = I[21]; T& I##ccn = I[22]; T& I##ncn = I[23]; \
507                       T& I##pnn = I[24]; T& I##cnn = I[25]; T& I##nnn = I[26]; \
508                       I##ppp = I##cpp = I##npp = \
509                       I##pcp = I##ccp = I##ncp = \
510                       I##pnp = I##cnp = I##nnp = \
511                       I##ppc = I##cpc = I##npc = \
512                       I##pcc = I##ccc = I##ncc = \
513                       I##pnc = I##cnc = I##nnc = \
514                       I##ppn = I##cpn = I##npn = \
515                       I##pcn = I##ccn = I##ncn = \
516                       I##pnn = I##cnn = I##nnn = 0
517 
518 #define cimg_get2x2(img,x,y,z,c,I,T) \
519   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)
520 
521 #define cimg_get3x3(img,x,y,z,c,I,T) \
522   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), \
523   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), \
524   I[8] = (T)(img)(_n1##x,_n1##y,z,c)
525 
526 #define cimg_get4x4(img,x,y,z,c,I,T) \
527   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), \
528   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), \
529   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), \
530   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)
531 
532 #define cimg_get5x5(img,x,y,z,c,I,T) \
533   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), \
534   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), \
535   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), \
536   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), \
537   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), \
538   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), \
539   I[24] = (T)(img)(_n2##x,_n2##y,z,c)
540 
541 #define cimg_get6x6(img,x,y,z,c,I,T) \
542  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), \
543  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), \
544  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), \
545  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), \
546  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), \
547  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), \
548  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), \
549  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), \
550  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)
551 
552 #define cimg_get7x7(img,x,y,z,c,I,T) \
553  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), \
554  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), \
555  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), \
556  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), \
557  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), \
558  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), \
559  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), \
560  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), \
561  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), \
562  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), \
563  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), \
564  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), \
565  I[48] = (T)(img)(_n3##x,_n3##y,z,c)
566 
567 #define cimg_get8x8(img,x,y,z,c,I,T) \
568  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), \
569  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), \
570  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), \
571  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), \
572  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), \
573  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), \
574  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), \
575  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), \
576  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), \
577  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), \
578  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), \
579  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), \
580  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), \
581  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), \
582  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), \
583  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);
584 
585 #define cimg_get9x9(img,x,y,z,c,I,T) \
586  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), \
587  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), \
588  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), \
589  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), \
590  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), \
591  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), \
592  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), \
593  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), \
594  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), \
595  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), \
596  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), \
597  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), \
598  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), \
599  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), \
600  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), \
601  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), \
602  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), \
603  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), \
604  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), \
605  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), \
606  I[80] = (T)(img)(_n4##x,_n4##y,z,c)
607 
608 #define cimg_get2x2x2(img,x,y,z,c,I,T) \
609   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), \
610   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)
611 
612 #define cimg_get3x3x3(img,x,y,z,c,I,T) \
613   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), \
614   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), \
615   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), \
616   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), \
617   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), \
618   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), \
619   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), \
620   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), \
621   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)
622 
623 // Define various image loops.
624 //
625 // These macros generally avoid the use of iterators, but you are not forced to used them !
626 #define cimg_for(img,ptrs,T_ptrs) for (T_ptrs *ptrs = (img)._data + (img).size(); (ptrs--)>(img)._data; )
627 #define cimg_foroff(img,off) for (unsigned int off = 0, _max##off = (unsigned int)(img).size(); off<_max##off; ++off)
628 
629 #define cimg_for1(bound,i) for (int i = 0; i<(int)(bound); ++i)
630 #define cimg_forX(img,x) cimg_for1((img)._width,x)
631 #define cimg_forY(img,y) cimg_for1((img)._height,y)
632 #define cimg_forZ(img,z) cimg_for1((img)._depth,z)
633 #define cimg_forC(img,c) cimg_for1((img)._spectrum,c)
634 #define cimg_forXY(img,x,y) cimg_forY(img,y) cimg_forX(img,x)
635 #define cimg_forXZ(img,x,z) cimg_forZ(img,z) cimg_forX(img,x)
636 #define cimg_forYZ(img,y,z) cimg_forZ(img,z) cimg_forY(img,y)
637 #define cimg_forXC(img,x,c) cimg_forC(img,c) cimg_forX(img,x)
638 #define cimg_forYC(img,y,c) cimg_forC(img,c) cimg_forY(img,y)
639 #define cimg_forZC(img,z,c) cimg_forC(img,c) cimg_forZ(img,z)
640 #define cimg_forXYZ(img,x,y,z) cimg_forZ(img,z) cimg_forXY(img,x,y)
641 #define cimg_forXYC(img,x,y,c) cimg_forC(img,c) cimg_forXY(img,x,y)
642 #define cimg_forXZC(img,x,z,c) cimg_forC(img,c) cimg_forXZ(img,x,z)
643 #define cimg_forYZC(img,y,z,c) cimg_forC(img,c) cimg_forYZ(img,y,z)
644 #define cimg_forXYZC(img,x,y,z,c) cimg_forC(img,c) cimg_forXYZ(img,x,y,z)
645 
646 #define cimg_for_in1(bound,i0,i1,i) \
647  for (int i = (int)(i0)<0?0:(int)(i0), _max##i = (int)(i1)<(int)(bound)?(int)(i1):(int)(bound)-1; i<=_max##i; ++i)
648 #define cimg_for_inX(img,x0,x1,x) cimg_for_in1((img)._width,x0,x1,x)
649 #define cimg_for_inY(img,y0,y1,y) cimg_for_in1((img)._height,y0,y1,y)
650 #define cimg_for_inZ(img,z0,z1,z) cimg_for_in1((img)._depth,z0,z1,z)
651 #define cimg_for_inC(img,c0,c1,c) cimg_for_in1((img)._spectrum,c0,c1,c)
652 #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)
653 #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)
654 #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)
655 #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)
656 #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)
657 #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)
658 #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)
659 #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)
660 #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)
661 #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)
662 #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)
663 #define cimg_for_insideX(img,x,n) cimg_for_inX(img,n,(img)._width-1-(n),x)
664 #define cimg_for_insideY(img,y,n) cimg_for_inY(img,n,(img)._height-1-(n),y)
665 #define cimg_for_insideZ(img,z,n) cimg_for_inZ(img,n,(img)._depth-1-(n),z)
666 #define cimg_for_insideC(img,c,n) cimg_for_inC(img,n,(img)._spectrum-1-(n),c)
667 #define cimg_for_insideXY(img,x,y,n) cimg_for_inXY(img,n,n,(img)._width-1-(n),(img)._height-1-(n),x,y)
668 #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)
669 #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)
670 
671 #define cimg_for_out1(boundi,i0,i1,i) \
672  for (int i = (int)(i0)>0?0:(int)(i1)+1; i<(int)(boundi); ++i, i = i==(int)(i0)?(int)(i1)+1:i)
673 #define cimg_for_out2(boundi,boundj,i0,j0,i1,j1,i,j) \
674  for (int j = 0; j<(int)(boundj); ++j) \
675  for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j?0:(int)(i0)>0?0:(int)(i1)+1; i<(int)(boundi); \
676   ++i, i = _n1j?i:(i==(int)(i0)?(int)(i1)+1:i))
677 #define cimg_for_out3(boundi,boundj,boundk,i0,j0,k0,i1,j1,k1,i,j,k) \
678  for (int k = 0; k<(int)(boundk); ++k) \
679  for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \
680  for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k?0:(int)(i0)>0?0:(int)(i1)+1; i<(int)(boundi); \
681   ++i, i = _n1j || _n1k?i:(i==(int)(i0)?(int)(i1)+1:i))
682 #define cimg_for_out4(boundi,boundj,boundk,boundl,i0,j0,k0,l0,i1,j1,k1,l1,i,j,k,l) \
683  for (int l = 0; l<(int)(boundl); ++l) \
684  for (int _n1l = (int)(l<(int)(l0) || l>(int)(l1)), k = 0; k<(int)(boundk); ++k) \
685  for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \
686  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); \
687   ++i, i = _n1j || _n1k || _n1l?i:(i==(int)(i0)?(int)(i1)+1:i))
688 #define cimg_for_outX(img,x0,x1,x) cimg_for_out1((img)._width,x0,x1,x)
689 #define cimg_for_outY(img,y0,y1,y) cimg_for_out1((img)._height,y0,y1,y)
690 #define cimg_for_outZ(img,z0,z1,z) cimg_for_out1((img)._depth,z0,z1,z)
691 #define cimg_for_outC(img,c0,c1,c) cimg_for_out1((img)._spectrum,c0,c1,c)
692 #define cimg_for_outXY(img,x0,y0,x1,y1,x,y) cimg_for_out2((img)._width,(img)._height,x0,y0,x1,y1,x,y)
693 #define cimg_for_outXZ(img,x0,z0,x1,z1,x,z) cimg_for_out2((img)._width,(img)._depth,x0,z0,x1,z1,x,z)
694 #define cimg_for_outXC(img,x0,c0,x1,c1,x,c) cimg_for_out2((img)._width,(img)._spectrum,x0,c0,x1,c1,x,c)
695 #define cimg_for_outYZ(img,y0,z0,y1,z1,y,z) cimg_for_out2((img)._height,(img)._depth,y0,z0,y1,z1,y,z)
696 #define cimg_for_outYC(img,y0,c0,y1,c1,y,c) cimg_for_out2((img)._height,(img)._spectrum,y0,c0,y1,c1,y,c)
697 #define cimg_for_outZC(img,z0,c0,z1,c1,z,c) cimg_for_out2((img)._depth,(img)._spectrum,z0,c0,z1,c1,z,c)
698 #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)
699 #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)
700 #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)
701 #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)
702 #define cimg_for_outXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
703  cimg_for_out4((img)._width,(img)._height,(img)._depth,(img)._spectrum,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c)
704 #define cimg_for_borderX(img,x,n) cimg_for_outX(img,n,(img)._width-1-(n),x)
705 #define cimg_for_borderY(img,y,n) cimg_for_outY(img,n,(img)._height-1-(n),y)
706 #define cimg_for_borderZ(img,z,n) cimg_for_outZ(img,n,(img)._depth-1-(n),z)
707 #define cimg_for_borderC(img,c,n) cimg_for_outC(img,n,(img)._spectrum-1-(n),c)
708 #define cimg_for_borderXY(img,x,y,n) cimg_for_outXY(img,n,n,(img)._width-1-(n),(img)._height-1-(n),x,y)
709 #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)
710 #define cimg_for_borderXYZC(img,x,y,z,c,n) \
711  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)
712 
713 #define cimg_for_spiralXY(img,x,y) \
714  for (int x = 0, y = 0, _n1##x = 1, _n1##y = (img).width()*(img).height(); _n1##y; \
715       --_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)
716 
717 #define cimg_for_lineXY(x,y,x0,y0,x1,y1) \
718  for (int x = (int)(x0), y = (int)(y0), _sx = 1, _sy = 1, _steep = 0, \
719       _dx=(x1)>(x0)?(int)(x1)-(int)(x0):(_sx=-1,(int)(x0)-(int)(x1)), \
720       _dy=(y1)>(y0)?(int)(y1)-(int)(y0):(_sy=-1,(int)(y0)-(int)(y1)), \
721       _counter = _dx, \
722       _err = _dx>_dy?(_dy>>1):((_steep=1),(_counter=_dy),(_dx>>1)); \
723       _counter>=0; \
724       --_counter, x+=_steep? \
725       (y+=_sy,(_err-=_dx)<0?_err+=_dy,_sx:0): \
726       (y+=(_err-=_dy)<0?_err+=_dx,_sy:0,_sx))
727 
728 #define cimg_for2(bound,i) \
729  for (int i = 0, _n1##i = 1>=(bound)?(int)(bound)-1:1; \
730       _n1##i<(int)(bound) || i==--_n1##i; \
731       ++i, ++_n1##i)
732 #define cimg_for2X(img,x) cimg_for2((img)._width,x)
733 #define cimg_for2Y(img,y) cimg_for2((img)._height,y)
734 #define cimg_for2Z(img,z) cimg_for2((img)._depth,z)
735 #define cimg_for2C(img,c) cimg_for2((img)._spectrum,c)
736 #define cimg_for2XY(img,x,y) cimg_for2Y(img,y) cimg_for2X(img,x)
737 #define cimg_for2XZ(img,x,z) cimg_for2Z(img,z) cimg_for2X(img,x)
738 #define cimg_for2XC(img,x,c) cimg_for2C(img,c) cimg_for2X(img,x)
739 #define cimg_for2YZ(img,y,z) cimg_for2Z(img,z) cimg_for2Y(img,y)
740 #define cimg_for2YC(img,y,c) cimg_for2C(img,c) cimg_for2Y(img,y)
741 #define cimg_for2ZC(img,z,c) cimg_for2C(img,c) cimg_for2Z(img,z)
742 #define cimg_for2XYZ(img,x,y,z) cimg_for2Z(img,z) cimg_for2XY(img,x,y)
743 #define cimg_for2XZC(img,x,z,c) cimg_for2C(img,c) cimg_for2XZ(img,x,z)
744 #define cimg_for2YZC(img,y,z,c) cimg_for2C(img,c) cimg_for2YZ(img,y,z)
745 #define cimg_for2XYZC(img,x,y,z,c) cimg_for2C(img,c) cimg_for2XYZ(img,x,y,z)
746 
747 #define cimg_for_in2(bound,i0,i1,i) \
748  for (int i = (int)(i0)<0?0:(int)(i0), \
749       _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1; \
750       i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \
751       ++i, ++_n1##i)
752 #define cimg_for_in2X(img,x0,x1,x) cimg_for_in2((img)._width,x0,x1,x)
753 #define cimg_for_in2Y(img,y0,y1,y) cimg_for_in2((img)._height,y0,y1,y)
754 #define cimg_for_in2Z(img,z0,z1,z) cimg_for_in2((img)._depth,z0,z1,z)
755 #define cimg_for_in2C(img,c0,c1,c) cimg_for_in2((img)._spectrum,c0,c1,c)
756 #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)
757 #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)
758 #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)
759 #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)
760 #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)
761 #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)
762 #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)
763 #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)
764 #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)
765 #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)
766 
767 #define cimg_for3(bound,i) \
768  for (int i = 0, _p1##i = 0, \
769       _n1##i = 1>=(bound)?(int)(bound)-1:1; \
770       _n1##i<(int)(bound) || i==--_n1##i; \
771       _p1##i = i++, ++_n1##i)
772 #define cimg_for3X(img,x) cimg_for3((img)._width,x)
773 #define cimg_for3Y(img,y) cimg_for3((img)._height,y)
774 #define cimg_for3Z(img,z) cimg_for3((img)._depth,z)
775 #define cimg_for3C(img,c) cimg_for3((img)._spectrum,c)
776 #define cimg_for3XY(img,x,y) cimg_for3Y(img,y) cimg_for3X(img,x)
777 #define cimg_for3XZ(img,x,z) cimg_for3Z(img,z) cimg_for3X(img,x)
778 #define cimg_for3XC(img,x,c) cimg_for3C(img,c) cimg_for3X(img,x)
779 #define cimg_for3YZ(img,y,z) cimg_for3Z(img,z) cimg_for3Y(img,y)
780 #define cimg_for3YC(img,y,c) cimg_for3C(img,c) cimg_for3Y(img,y)
781 #define cimg_for3ZC(img,z,c) cimg_for3C(img,c) cimg_for3Z(img,z)
782 #define cimg_for3XYZ(img,x,y,z) cimg_for3Z(img,z) cimg_for3XY(img,x,y)
783 #define cimg_for3XZC(img,x,z,c) cimg_for3C(img,c) cimg_for3XZ(img,x,z)
784 #define cimg_for3YZC(img,y,z,c) cimg_for3C(img,c) cimg_for3YZ(img,y,z)
785 #define cimg_for3XYZC(img,x,y,z,c) cimg_for3C(img,c) cimg_for3XYZ(img,x,y,z)
786 
787 #define cimg_for_in3(bound,i0,i1,i) \
788  for (int i = (int)(i0)<0?0:(int)(i0), \
789       _p1##i = i-1<0?0:i-1, \
790       _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1; \
791       i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \
792       _p1##i = i++, ++_n1##i)
793 #define cimg_for_in3X(img,x0,x1,x) cimg_for_in3((img)._width,x0,x1,x)
794 #define cimg_for_in3Y(img,y0,y1,y) cimg_for_in3((img)._height,y0,y1,y)
795 #define cimg_for_in3Z(img,z0,z1,z) cimg_for_in3((img)._depth,z0,z1,z)
796 #define cimg_for_in3C(img,c0,c1,c) cimg_for_in3((img)._spectrum,c0,c1,c)
797 #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)
798 #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)
799 #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)
800 #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)
801 #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)
802 #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)
803 #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)
804 #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)
805 #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)
806 #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)
807 
808 #define cimg_for4(bound,i) \
809  for (int i = 0, _p1##i = 0, _n1##i = 1>=(bound)?(int)(bound)-1:1, \
810       _n2##i = 2>=(bound)?(int)(bound)-1:2; \
811       _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \
812       _p1##i = i++, ++_n1##i, ++_n2##i)
813 #define cimg_for4X(img,x) cimg_for4((img)._width,x)
814 #define cimg_for4Y(img,y) cimg_for4((img)._height,y)
815 #define cimg_for4Z(img,z) cimg_for4((img)._depth,z)
816 #define cimg_for4C(img,c) cimg_for4((img)._spectrum,c)
817 #define cimg_for4XY(img,x,y) cimg_for4Y(img,y) cimg_for4X(img,x)
818 #define cimg_for4XZ(img,x,z) cimg_for4Z(img,z) cimg_for4X(img,x)
819 #define cimg_for4XC(img,x,c) cimg_for4C(img,c) cimg_for4X(img,x)
820 #define cimg_for4YZ(img,y,z) cimg_for4Z(img,z) cimg_for4Y(img,y)
821 #define cimg_for4YC(img,y,c) cimg_for4C(img,c) cimg_for4Y(img,y)
822 #define cimg_for4ZC(img,z,c) cimg_for4C(img,c) cimg_for4Z(img,z)
823 #define cimg_for4XYZ(img,x,y,z) cimg_for4Z(img,z) cimg_for4XY(img,x,y)
824 #define cimg_for4XZC(img,x,z,c) cimg_for4C(img,c) cimg_for4XZ(img,x,z)
825 #define cimg_for4YZC(img,y,z,c) cimg_for4C(img,c) cimg_for4YZ(img,y,z)
826 #define cimg_for4XYZC(img,x,y,z,c) cimg_for4C(img,c) cimg_for4XYZ(img,x,y,z)
827 
828 #define cimg_for_in4(bound,i0,i1,i) \
829  for (int i = (int)(i0)<0?0:(int)(i0), \
830       _p1##i = i-1<0?0:i-1, \
831       _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
832       _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2; \
833       i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \
834       _p1##i = i++, ++_n1##i, ++_n2##i)
835 #define cimg_for_in4X(img,x0,x1,x) cimg_for_in4((img)._width,x0,x1,x)
836 #define cimg_for_in4Y(img,y0,y1,y) cimg_for_in4((img)._height,y0,y1,y)
837 #define cimg_for_in4Z(img,z0,z1,z) cimg_for_in4((img)._depth,z0,z1,z)
838 #define cimg_for_in4C(img,c0,c1,c) cimg_for_in4((img)._spectrum,c0,c1,c)
839 #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)
840 #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)
841 #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)
842 #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)
843 #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)
844 #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)
845 #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)
846 #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)
847 #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)
848 #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)
849 
850 #define cimg_for5(bound,i) \
851  for (int i = 0, _p2##i = 0, _p1##i = 0, \
852       _n1##i = 1>=(bound)?(int)(bound)-1:1, \
853       _n2##i = 2>=(bound)?(int)(bound)-1:2; \
854       _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \
855       _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i)
856 #define cimg_for5X(img,x) cimg_for5((img)._width,x)
857 #define cimg_for5Y(img,y) cimg_for5((img)._height,y)
858 #define cimg_for5Z(img,z) cimg_for5((img)._depth,z)
859 #define cimg_for5C(img,c) cimg_for5((img)._spectrum,c)
860 #define cimg_for5XY(img,x,y) cimg_for5Y(img,y) cimg_for5X(img,x)
861 #define cimg_for5XZ(img,x,z) cimg_for5Z(img,z) cimg_for5X(img,x)
862 #define cimg_for5XC(img,x,c) cimg_for5C(img,c) cimg_for5X(img,x)
863 #define cimg_for5YZ(img,y,z) cimg_for5Z(img,z) cimg_for5Y(img,y)
864 #define cimg_for5YC(img,y,c) cimg_for5C(img,c) cimg_for5Y(img,y)
865 #define cimg_for5ZC(img,z,c) cimg_for5C(img,c) cimg_for5Z(img,z)
866 #define cimg_for5XYZ(img,x,y,z) cimg_for5Z(img,z) cimg_for5XY(img,x,y)
867 #define cimg_for5XZC(img,x,z,c) cimg_for5C(img,c) cimg_for5XZ(img,x,z)
868 #define cimg_for5YZC(img,y,z,c) cimg_for5C(img,c) cimg_for5YZ(img,y,z)
869 #define cimg_for5XYZC(img,x,y,z,c) cimg_for5C(img,c) cimg_for5XYZ(img,x,y,z)
870 
871 #define cimg_for_in5(bound,i0,i1,i) \
872  for (int i = (int)(i0)<0?0:(int)(i0), \
873       _p2##i = i-2<0?0:i-2, \
874       _p1##i = i-1<0?0:i-1, \
875       _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
876       _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2; \
877       i<=(int)(i1) && (_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_for_in5X(img,x0,x1,x) cimg_for_in5((img)._width,x0,x1,x)
880 #define cimg_for_in5Y(img,y0,y1,y) cimg_for_in5((img)._height,y0,y1,y)
881 #define cimg_for_in5Z(img,z0,z1,z) cimg_for_in5((img)._depth,z0,z1,z)
882 #define cimg_for_in5C(img,c0,c1,c) cimg_for_in5((img)._spectrum,c0,c1,c)
883 #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)
884 #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)
885 #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)
886 #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)
887 #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)
888 #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)
889 #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)
890 #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)
891 #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)
892 #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)
893 
894 #define cimg_for6(bound,i) \
895  for (int i = 0, _p2##i = 0, _p1##i = 0, \
896       _n1##i = 1>=(bound)?(int)(bound)-1:1, \
897       _n2##i = 2>=(bound)?(int)(bound)-1:2, \
898       _n3##i = 3>=(bound)?(int)(bound)-1:3; \
899       _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \
900       _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
901 #define cimg_for6X(img,x) cimg_for6((img)._width,x)
902 #define cimg_for6Y(img,y) cimg_for6((img)._height,y)
903 #define cimg_for6Z(img,z) cimg_for6((img)._depth,z)
904 #define cimg_for6C(img,c) cimg_for6((img)._spectrum,c)
905 #define cimg_for6XY(img,x,y) cimg_for6Y(img,y) cimg_for6X(img,x)
906 #define cimg_for6XZ(img,x,z) cimg_for6Z(img,z) cimg_for6X(img,x)
907 #define cimg_for6XC(img,x,c) cimg_for6C(img,c) cimg_for6X(img,x)
908 #define cimg_for6YZ(img,y,z) cimg_for6Z(img,z) cimg_for6Y(img,y)
909 #define cimg_for6YC(img,y,c) cimg_for6C(img,c) cimg_for6Y(img,y)
910 #define cimg_for6ZC(img,z,c) cimg_for6C(img,c) cimg_for6Z(img,z)
911 #define cimg_for6XYZ(img,x,y,z) cimg_for6Z(img,z) cimg_for6XY(img,x,y)
912 #define cimg_for6XZC(img,x,z,c) cimg_for6C(img,c) cimg_for6XZ(img,x,z)
913 #define cimg_for6YZC(img,y,z,c) cimg_for6C(img,c) cimg_for6YZ(img,y,z)
914 #define cimg_for6XYZC(img,x,y,z,c) cimg_for6C(img,c) cimg_for6XYZ(img,x,y,z)
915 
916 #define cimg_for_in6(bound,i0,i1,i) \
917  for (int i = (int)(i0)<0?0:(int)(i0), \
918       _p2##i = i-2<0?0:i-2, \
919       _p1##i = i-1<0?0:i-1, \
920       _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
921       _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \
922       _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3; \
923       i<=(int)(i1) && (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \
924       _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
925 #define cimg_for_in6X(img,x0,x1,x) cimg_for_in6((img)._width,x0,x1,x)
926 #define cimg_for_in6Y(img,y0,y1,y) cimg_for_in6((img)._height,y0,y1,y)
927 #define cimg_for_in6Z(img,z0,z1,z) cimg_for_in6((img)._depth,z0,z1,z)
928 #define cimg_for_in6C(img,c0,c1,c) cimg_for_in6((img)._spectrum,c0,c1,c)
929 #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)
930 #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)
931 #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)
932 #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)
933 #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)
934 #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)
935 #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)
936 #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)
937 #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)
938 #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)
939 
940 #define cimg_for7(bound,i) \
941  for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
942       _n1##i = 1>=(bound)?(int)(bound)-1:1, \
943       _n2##i = 2>=(bound)?(int)(bound)-1:2, \
944       _n3##i = 3>=(bound)?(int)(bound)-1:3; \
945       _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \
946       _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
947 #define cimg_for7X(img,x) cimg_for7((img)._width,x)
948 #define cimg_for7Y(img,y) cimg_for7((img)._height,y)
949 #define cimg_for7Z(img,z) cimg_for7((img)._depth,z)
950 #define cimg_for7C(img,c) cimg_for7((img)._spectrum,c)
951 #define cimg_for7XY(img,x,y) cimg_for7Y(img,y) cimg_for7X(img,x)
952 #define cimg_for7XZ(img,x,z) cimg_for7Z(img,z) cimg_for7X(img,x)
953 #define cimg_for7XC(img,x,c) cimg_for7C(img,c) cimg_for7X(img,x)
954 #define cimg_for7YZ(img,y,z) cimg_for7Z(img,z) cimg_for7Y(img,y)
955 #define cimg_for7YC(img,y,c) cimg_for7C(img,c) cimg_for7Y(img,y)
956 #define cimg_for7ZC(img,z,c) cimg_for7C(img,c) cimg_for7Z(img,z)
957 #define cimg_for7XYZ(img,x,y,z) cimg_for7Z(img,z) cimg_for7XY(img,x,y)
958 #define cimg_for7XZC(img,x,z,c) cimg_for7C(img,c) cimg_for7XZ(img,x,z)
959 #define cimg_for7YZC(img,y,z,c) cimg_for7C(img,c) cimg_for7YZ(img,y,z)
960 #define cimg_for7XYZC(img,x,y,z,c) cimg_for7C(img,c) cimg_for7XYZ(img,x,y,z)
961 
962 #define cimg_for_in7(bound,i0,i1,i) \
963  for (int i = (int)(i0)<0?0:(int)(i0), \
964       _p3##i = i-3<0?0:i-3, \
965       _p2##i = i-2<0?0:i-2, \
966       _p1##i = i-1<0?0:i-1, \
967       _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
968       _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \
969       _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3; \
970       i<=(int)(i1) && (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \
971       _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
972 #define cimg_for_in7X(img,x0,x1,x) cimg_for_in7((img)._width,x0,x1,x)
973 #define cimg_for_in7Y(img,y0,y1,y) cimg_for_in7((img)._height,y0,y1,y)
974 #define cimg_for_in7Z(img,z0,z1,z) cimg_for_in7((img)._depth,z0,z1,z)
975 #define cimg_for_in7C(img,c0,c1,c) cimg_for_in7((img)._spectrum,c0,c1,c)
976 #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)
977 #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)
978 #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)
979 #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)
980 #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)
981 #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)
982 #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)
983 #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)
984 #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)
985 #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)
986 
987 #define cimg_for8(bound,i) \
988  for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
989       _n1##i = 1>=(bound)?(int)(bound)-1:1, \
990       _n2##i = 2>=(bound)?(int)(bound)-1:2, \
991       _n3##i = 3>=(bound)?(int)(bound)-1:3, \
992       _n4##i = 4>=(bound)?(int)(bound)-1:4; \
993       _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
994       i==(_n4##i = _n3##i = _n2##i = --_n1##i); \
995       _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
996 #define cimg_for8X(img,x) cimg_for8((img)._width,x)
997 #define cimg_for8Y(img,y) cimg_for8((img)._height,y)
998 #define cimg_for8Z(img,z) cimg_for8((img)._depth,z)
999 #define cimg_for8C(img,c) cimg_for8((img)._spectrum,c)
1000 #define cimg_for8XY(img,x,y) cimg_for8Y(img,y) cimg_for8X(img,x)
1001 #define cimg_for8XZ(img,x,z) cimg_for8Z(img,z) cimg_for8X(img,x)
1002 #define cimg_for8XC(img,x,c) cimg_for8C(img,c) cimg_for8X(img,x)
1003 #define cimg_for8YZ(img,y,z) cimg_for8Z(img,z) cimg_for8Y(img,y)
1004 #define cimg_for8YC(img,y,c) cimg_for8C(img,c) cimg_for8Y(img,y)
1005 #define cimg_for8ZC(img,z,c) cimg_for8C(img,c) cimg_for8Z(img,z)
1006 #define cimg_for8XYZ(img,x,y,z) cimg_for8Z(img,z) cimg_for8XY(img,x,y)
1007 #define cimg_for8XZC(img,x,z,c) cimg_for8C(img,c) cimg_for8XZ(img,x,z)
1008 #define cimg_for8YZC(img,y,z,c) cimg_for8C(img,c) cimg_for8YZ(img,y,z)
1009 #define cimg_for8XYZC(img,x,y,z,c) cimg_for8C(img,c) cimg_for8XYZ(img,x,y,z)
1010 
1011 #define cimg_for_in8(bound,i0,i1,i) \
1012  for (int i = (int)(i0)<0?0:(int)(i0), \
1013       _p3##i = i-3<0?0:i-3, \
1014       _p2##i = i-2<0?0:i-2, \
1015       _p1##i = i-1<0?0:i-1, \
1016       _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
1017       _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \
1018       _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3, \
1019       _n4##i = i+4>=(int)(bound)?(int)(bound)-1:i+4; \
1020       i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
1021       i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \
1022       _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
1023 #define cimg_for_in8X(img,x0,x1,x) cimg_for_in8((img)._width,x0,x1,x)
1024 #define cimg_for_in8Y(img,y0,y1,y) cimg_for_in8((img)._height,y0,y1,y)
1025 #define cimg_for_in8Z(img,z0,z1,z) cimg_for_in8((img)._depth,z0,z1,z)
1026 #define cimg_for_in8C(img,c0,c1,c) cimg_for_in8((img)._spectrum,c0,c1,c)
1027 #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)
1028 #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)
1029 #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)
1030 #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)
1031 #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)
1032 #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)
1033 #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)
1034 #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)
1035 #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)
1036 #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)
1037 
1038 #define cimg_for9(bound,i) \
1039   for (int i = 0, _p4##i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
1040        _n1##i = 1>=(int)(bound)?(int)(bound)-1:1, \
1041        _n2##i = 2>=(int)(bound)?(int)(bound)-1:2, \
1042        _n3##i = 3>=(int)(bound)?(int)(bound)-1:3, \
1043        _n4##i = 4>=(int)(bound)?(int)(bound)-1:4; \
1044        _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
1045        i==(_n4##i = _n3##i = _n2##i = --_n1##i); \
1046        _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
1047 #define cimg_for9X(img,x) cimg_for9((img)._width,x)
1048 #define cimg_for9Y(img,y) cimg_for9((img)._height,y)
1049 #define cimg_for9Z(img,z) cimg_for9((img)._depth,z)
1050 #define cimg_for9C(img,c) cimg_for9((img)._spectrum,c)
1051 #define cimg_for9XY(img,x,y) cimg_for9Y(img,y) cimg_for9X(img,x)
1052 #define cimg_for9XZ(img,x,z) cimg_for9Z(img,z) cimg_for9X(img,x)
1053 #define cimg_for9XC(img,x,c) cimg_for9C(img,c) cimg_for9X(img,x)
1054 #define cimg_for9YZ(img,y,z) cimg_for9Z(img,z) cimg_for9Y(img,y)
1055 #define cimg_for9YC(img,y,c) cimg_for9C(img,c) cimg_for9Y(img,y)
1056 #define cimg_for9ZC(img,z,c) cimg_for9C(img,c) cimg_for9Z(img,z)
1057 #define cimg_for9XYZ(img,x,y,z) cimg_for9Z(img,z) cimg_for9XY(img,x,y)
1058 #define cimg_for9XZC(img,x,z,c) cimg_for9C(img,c) cimg_for9XZ(img,x,z)
1059 #define cimg_for9YZC(img,y,z,c) cimg_for9C(img,c) cimg_for9YZ(img,y,z)
1060 #define cimg_for9XYZC(img,x,y,z,c) cimg_for9C(img,c) cimg_for9XYZ(img,x,y,z)
1061 
1062 #define cimg_for_in9(bound,i0,i1,i) \
1063   for (int i = (int)(i0)<0?0:(int)(i0), \
1064        _p4##i = i-4<0?0:i-4, \
1065        _p3##i = i-3<0?0:i-3, \
1066        _p2##i = i-2<0?0:i-2, \
1067        _p1##i = i-1<0?0:i-1, \
1068        _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
1069        _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \
1070        _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3, \
1071        _n4##i = i+4>=(int)(bound)?(int)(bound)-1:i+4; \
1072        i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
1073        i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \
1074        _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
1075 #define cimg_for_in9X(img,x0,x1,x) cimg_for_in9((img)._width,x0,x1,x)
1076 #define cimg_for_in9Y(img,y0,y1,y) cimg_for_in9((img)._height,y0,y1,y)
1077 #define cimg_for_in9Z(img,z0,z1,z) cimg_for_in9((img)._depth,z0,z1,z)
1078 #define cimg_for_in9C(img,c0,c1,c) cimg_for_in9((img)._spectrum,c0,c1,c)
1079 #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)
1080 #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)
1081 #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)
1082 #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)
1083 #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)
1084 #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)
1085 #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)
1086 #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)
1087 #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)
1088 #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)
1089 
1090 #define cimg_for2x2(img,x,y,z,c,I,T) \
1091   cimg_for2((img)._height,y) for (int x = 0, \
1092    _n1##x = (int)( \
1093    (I[0] = (T)(img)(0,y,z,c)), \
1094    (I[2] = (T)(img)(0,_n1##y,z,c)), \
1095    1>=(img)._width?(img).width()-1:1);  \
1096    (_n1##x<(img).width() && ( \
1097    (I[1] = (T)(img)(_n1##x,y,z,c)), \
1098    (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
1099    x==--_n1##x; \
1100    I[0] = I[1], \
1101    I[2] = I[3], \
1102    ++x, ++_n1##x)
1103 
1104 #define cimg_for_in2x2(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1105   cimg_for_in2((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1106    _n1##x = (int)( \
1107    (I[0] = (T)(img)(x,y,z,c)), \
1108    (I[2] = (T)(img)(x,_n1##y,z,c)), \
1109    x+1>=(int)(img)._width?(img).width()-1:x+1); \
1110    x<=(int)(x1) && ((_n1##x<(img).width() && (  \
1111    (I[1] = (T)(img)(_n1##x,y,z,c)), \
1112    (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
1113    x==--_n1##x); \
1114    I[0] = I[1], \
1115    I[2] = I[3], \
1116    ++x, ++_n1##x)
1117 
1118 #define cimg_for3x3(img,x,y,z,c,I,T) \
1119   cimg_for3((img)._height,y) for (int x = 0, \
1120    _p1##x = 0, \
1121    _n1##x = (int)( \
1122    (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
1123    (I[3] = I[4] = (T)(img)(0,y,z,c)), \
1124    (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)),      \
1125    1>=(img)._width?(img).width()-1:1); \
1126    (_n1##x<(img).width() && ( \
1127    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
1128    (I[5] = (T)(img)(_n1##x,y,z,c)), \
1129    (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
1130    x==--_n1##x; \
1131    I[0] = I[1], I[1] = I[2], \
1132    I[3] = I[4], I[4] = I[5], \
1133    I[6] = I[7], I[7] = I[8], \
1134    _p1##x = x++, ++_n1##x)
1135 
1136 #define cimg_for_in3x3(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1137   cimg_for_in3((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1138    _p1##x = x-1<0?0:x-1, \
1139    _n1##x = (int)( \
1140    (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \
1141    (I[3] = (T)(img)(_p1##x,y,z,c)), \
1142    (I[6] = (T)(img)(_p1##x,_n1##y,z,c)), \
1143    (I[1] = (T)(img)(x,_p1##y,z,c)), \
1144    (I[4] = (T)(img)(x,y,z,c)), \
1145    (I[7] = (T)(img)(x,_n1##y,z,c)), \
1146    x+1>=(int)(img)._width?(img).width()-1:x+1); \
1147    x<=(int)(x1) && ((_n1##x<(img).width() && ( \
1148    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
1149    (I[5] = (T)(img)(_n1##x,y,z,c)), \
1150    (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
1151    x==--_n1##x);            \
1152    I[0] = I[1], I[1] = I[2], \
1153    I[3] = I[4], I[4] = I[5], \
1154    I[6] = I[7], I[7] = I[8], \
1155    _p1##x = x++, ++_n1##x)
1156 
1157 #define cimg_for4x4(img,x,y,z,c,I,T) \
1158   cimg_for4((img)._height,y) for (int x = 0, \
1159    _p1##x = 0, \
1160    _n1##x = 1>=(img)._width?(img).width()-1:1, \
1161    _n2##x = (int)( \
1162    (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
1163    (I[4] = I[5] = (T)(img)(0,y,z,c)), \
1164    (I[8] = I[9] = (T)(img)(0,_n1##y,z,c)), \
1165    (I[12] = I[13] = (T)(img)(0,_n2##y,z,c)), \
1166    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
1167    (I[6] = (T)(img)(_n1##x,y,z,c)), \
1168    (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \
1169    (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \
1170    2>=(img)._width?(img).width()-1:2); \
1171    (_n2##x<(img).width() && ( \
1172    (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \
1173    (I[7] = (T)(img)(_n2##x,y,z,c)), \
1174    (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \
1175    (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
1176    _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \
1177    I[0] = I[1], I[1] = I[2], I[2] = I[3], \
1178    I[4] = I[5], I[5] = I[6], I[6] = I[7], \
1179    I[8] = I[9], I[9] = I[10], I[10] = I[11], \
1180    I[12] = I[13], I[13] = I[14], I[14] = I[15], \
1181    _p1##x = x++, ++_n1##x, ++_n2##x)
1182 
1183 #define cimg_for_in4x4(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1184   cimg_for_in4((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1185    _p1##x = x-1<0?0:x-1, \
1186    _n1##x = x+1>=(int)(img)._width?(img).width()-1:x+1, \
1187    _n2##x = (int)( \
1188    (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \
1189    (I[4] = (T)(img)(_p1##x,y,z,c)), \
1190    (I[8] = (T)(img)(_p1##x,_n1##y,z,c)), \
1191    (I[12] = (T)(img)(_p1##x,_n2##y,z,c)), \
1192    (I[1] = (T)(img)(x,_p1##y,z,c)), \
1193    (I[5] = (T)(img)(x,y,z,c)), \
1194    (I[9] = (T)(img)(x,_n1##y,z,c)), \
1195    (I[13] = (T)(img)(x,_n2##y,z,c)), \
1196    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
1197    (I[6] = (T)(img)(_n1##x,y,z,c)), \
1198    (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \
1199    (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \
1200    x+2>=(int)(img)._width?(img).width()-1:x+2); \
1201    x<=(int)(x1) && ((_n2##x<(img).width() && ( \
1202    (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \
1203    (I[7] = (T)(img)(_n2##x,y,z,c)), \
1204    (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \
1205    (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
1206    _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \
1207    I[0] = I[1], I[1] = I[2], I[2] = I[3], \
1208    I[4] = I[5], I[5] = I[6], I[6] = I[7], \
1209    I[8] = I[9], I[9] = I[10], I[10] = I[11], \
1210    I[12] = I[13], I[13] = I[14], I[14] = I[15], \
1211    _p1##x = x++, ++_n1##x, ++_n2##x)
1212 
1213 #define cimg_for5x5(img,x,y,z,c,I,T) \
1214  cimg_for5((img)._height,y) for (int x = 0, \
1215    _p2##x = 0, _p1##x = 0, \
1216    _n1##x = 1>=(img)._width?(img).width()-1:1, \
1217    _n2##x = (int)( \
1218    (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \
1219    (I[5] = I[6] = I[7] = (T)(img)(0,_p1##y,z,c)), \
1220    (I[10] = I[11] = I[12] = (T)(img)(0,y,z,c)), \
1221    (I[15] = I[16] = I[17] = (T)(img)(0,_n1##y,z,c)), \
1222    (I[20] = I[21] = I[22] = (T)(img)(0,_n2##y,z,c)), \
1223    (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
1224    (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \
1225    (I[13] = (T)(img)(_n1##x,y,z,c)), \
1226    (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \
1227    (I[23] = (T)(img)(_n1##x,_n2##y,z,c)),  \
1228    2>=(img)._width?(img).width()-1:2); \
1229    (_n2##x<(img).width() && ( \
1230    (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
1231    (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \
1232    (I[14] = (T)(img)(_n2##x,y,z,c)), \
1233    (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \
1234    (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
1235    _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \
1236    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \
1237    I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \
1238    I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \
1239    I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \
1240    I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \
1241    _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x)
1242 
1243 #define cimg_for_in5x5(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1244  cimg_for_in5((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1245    _p2##x = x-2<0?0:x-2, \
1246    _p1##x = x-1<0?0:x-1, \
1247    _n1##x = x+1>=(int)(img)._width?(img).width()-1:x+1, \
1248    _n2##x = (int)( \
1249    (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \
1250    (I[5] = (T)(img)(_p2##x,_p1##y,z,c)), \
1251    (I[10] = (T)(img)(_p2##x,y,z,c)), \
1252    (I[15] = (T)(img)(_p2##x,_n1##y,z,c)), \
1253    (I[20] = (T)(img)(_p2##x,_n2##y,z,c)), \
1254    (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \
1255    (I[6] = (T)(img)(_p1##x,_p1##y,z,c)), \
1256    (I[11] = (T)(img)(_p1##x,y,z,c)), \
1257    (I[16] = (T)(img)(_p1##x,_n1##y,z,c)), \
1258    (I[21] = (T)(img)(_p1##x,_n2##y,z,c)), \
1259    (I[2] = (T)(img)(x,_p2##y,z,c)), \
1260    (I[7] = (T)(img)(x,_p1##y,z,c)), \
1261    (I[12] = (T)(img)(x,y,z,c)), \
1262    (I[17] = (T)(img)(x,_n1##y,z,c)), \
1263    (I[22] = (T)(img)(x,_n2##y,z,c)), \
1264    (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
1265    (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \
1266    (I[13] = (T)(img)(_n1##x,y,z,c)), \
1267    (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \
1268    (I[23] = (T)(img)(_n1##x,_n2##y,z,c)), \
1269    x+2>=(int)(img)._width?(img).width()-1:x+2); \
1270    x<=(int)(x1) && ((_n2##x<(img).width() && ( \
1271    (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
1272    (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \
1273    (I[14] = (T)(img)(_n2##x,y,z,c)), \
1274    (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \
1275    (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
1276    _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \
1277    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \
1278    I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \
1279    I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \
1280    I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \
1281    I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \
1282    _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x)
1283 
1284 #define cimg_for6x6(img,x,y,z,c,I,T) \
1285  cimg_for6((img)._height,y) for (int x = 0, \
1286    _p2##x = 0, _p1##x = 0, \
1287    _n1##x = 1>=(img)._width?(img).width()-1:1, \
1288    _n2##x = 2>=(img)._width?(img).width()-1:2, \
1289    _n3##x = (int)( \
1290    (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \
1291    (I[6] = I[7] = I[8] = (T)(img)(0,_p1##y,z,c)), \
1292    (I[12] = I[13] = I[14] = (T)(img)(0,y,z,c)), \
1293    (I[18] = I[19] = I[20] = (T)(img)(0,_n1##y,z,c)), \
1294    (I[24] = I[25] = I[26] = (T)(img)(0,_n2##y,z,c)), \
1295    (I[30] = I[31] = I[32] = (T)(img)(0,_n3##y,z,c)), \
1296    (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
1297    (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \
1298    (I[15] = (T)(img)(_n1##x,y,z,c)), \
1299    (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \
1300    (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \
1301    (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \
1302    (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
1303    (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \
1304    (I[16] = (T)(img)(_n2##x,y,z,c)), \
1305    (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \
1306    (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \
1307    (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \
1308    3>=(img)._width?(img).width()-1:3); \
1309    (_n3##x<(img).width() && ( \
1310    (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \
1311    (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \
1312    (I[17] = (T)(img)(_n3##x,y,z,c)), \
1313    (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \
1314    (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \
1315    (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
1316    _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x); \
1317    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \
1318    I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \
1319    I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \
1320    I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \
1321    I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \
1322    I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \
1323    _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
1324 
1325 #define cimg_for_in6x6(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1326   cimg_for_in6((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)x0, \
1327    _p2##x = x-2<0?0:x-2, \
1328    _p1##x = x-1<0?0:x-1, \
1329    _n1##x = x+1>=(int)(img)._width?(img).width()-1:x+1, \
1330    _n2##x = x+2>=(int)(img)._width?(img).width()-1:x+2, \
1331    _n3##x = (int)( \
1332    (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \
1333    (I[6] = (T)(img)(_p2##x,_p1##y,z,c)), \
1334    (I[12] = (T)(img)(_p2##x,y,z,c)), \
1335    (I[18] = (T)(img)(_p2##x,_n1##y,z,c)), \
1336    (I[24] = (T)(img)(_p2##x,_n2##y,z,c)), \
1337    (I[30] = (T)(img)(_p2##x,_n3##y,z,c)), \
1338    (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \
1339    (I[7] = (T)(img)(_p1##x,_p1##y,z,c)), \
1340    (I[13] = (T)(img)(_p1##x,y,z,c)), \
1341    (I[19] = (T)(img)(_p1##x,_n1##y,z,c)), \
1342    (I[25] = (T)(img)(_p1##x,_n2##y,z,c)), \
1343    (I[31] = (T)(img)(_p1##x,_n3##y,z,c)), \
1344    (I[2] = (T)(img)(x,_p2##y,z,c)), \
1345    (I[8] = (T)(img)(x,_p1##y,z,c)), \
1346    (I[14] = (T)(img)(x,y,z,c)), \
1347    (I[20] = (T)(img)(x,_n1##y,z,c)), \
1348    (I[26] = (T)(img)(x,_n2##y,z,c)), \
1349    (I[32] = (T)(img)(x,_n3##y,z,c)), \
1350    (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
1351    (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \
1352    (I[15] = (T)(img)(_n1##x,y,z,c)), \
1353    (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \
1354    (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \
1355    (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \
1356    (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
1357    (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \
1358    (I[16] = (T)(img)(_n2##x,y,z,c)), \
1359    (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \
1360    (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \
1361    (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \
1362    x+3>=(int)(img)._width?(img).width()-1:x+3); \
1363    x<=(int)(x1) && ((_n3##x<(img).width() && ( \
1364    (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \
1365    (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \
1366    (I[17] = (T)(img)(_n3##x,y,z,c)), \
1367    (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \
1368    (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \
1369    (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
1370    _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x)); \
1371    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \
1372    I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \
1373    I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \
1374    I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \
1375    I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \
1376    I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \
1377    _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
1378 
1379 #define cimg_for7x7(img,x,y,z,c,I,T) \
1380   cimg_for7((img)._height,y) for (int x = 0, \
1381    _p3##x = 0, _p2##x = 0, _p1##x = 0, \
1382    _n1##x = 1>=(img)._width?(img).width()-1:1, \
1383    _n2##x = 2>=(img)._width?(img).width()-1:2, \
1384    _n3##x = (int)( \
1385    (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \
1386    (I[7] = I[8] = I[9] = I[10] = (T)(img)(0,_p2##y,z,c)), \
1387    (I[14] = I[15] = I[16] = I[17] = (T)(img)(0,_p1##y,z,c)), \
1388    (I[21] = I[22] = I[23] = I[24] = (T)(img)(0,y,z,c)), \
1389    (I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_n1##y,z,c)), \
1390    (I[35] = I[36] = I[37] = I[38] = (T)(img)(0,_n2##y,z,c)), \
1391    (I[42] = I[43] = I[44] = I[45] = (T)(img)(0,_n3##y,z,c)), \
1392    (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
1393    (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \
1394    (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \
1395    (I[25] = (T)(img)(_n1##x,y,z,c)), \
1396    (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \
1397    (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \
1398    (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \
1399    (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
1400    (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \
1401    (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \
1402    (I[26] = (T)(img)(_n2##x,y,z,c)), \
1403    (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \
1404    (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \
1405    (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \
1406    3>=(img)._width?(img).width()-1:3); \
1407    (_n3##x<(img).width() && ( \
1408    (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
1409    (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \
1410    (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \
1411    (I[27] = (T)(img)(_n3##x,y,z,c)), \
1412    (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \
1413    (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \
1414    (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
1415    _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x); \
1416    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \
1417    I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \
1418    I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \
1419    I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \
1420    I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \
1421    I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \
1422    I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \
1423    _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
1424 
1425 #define cimg_for_in7x7(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1426   cimg_for_in7((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1427    _p3##x = x-3<0?0:x-3, \
1428    _p2##x = x-2<0?0:x-2, \
1429    _p1##x = x-1<0?0:x-1, \
1430    _n1##x = x+1>=(int)(img)._width?(img).width()-1:x+1, \
1431    _n2##x = x+2>=(int)(img)._width?(img).width()-1:x+2, \
1432    _n3##x = (int)( \
1433    (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \
1434    (I[7] = (T)(img)(_p3##x,_p2##y,z,c)), \
1435    (I[14] = (T)(img)(_p3##x,_p1##y,z,c)), \
1436    (I[21] = (T)(img)(_p3##x,y,z,c)), \
1437    (I[28] = (T)(img)(_p3##x,_n1##y,z,c)), \
1438    (I[35] = (T)(img)(_p3##x,_n2##y,z,c)), \
1439    (I[42] = (T)(img)(_p3##x,_n3##y,z,c)), \
1440    (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \
1441    (I[8] = (T)(img)(_p2##x,_p2##y,z,c)), \
1442    (I[15] = (T)(img)(_p2##x,_p1##y,z,c)), \
1443    (I[22] = (T)(img)(_p2##x,y,z,c)), \
1444    (I[29] = (T)(img)(_p2##x,_n1##y,z,c)), \
1445    (I[36] = (T)(img)(_p2##x,_n2##y,z,c)), \
1446    (I[43] = (T)(img)(_p2##x,_n3##y,z,c)), \
1447    (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \
1448    (I[9] = (T)(img)(_p1##x,_p2##y,z,c)), \
1449    (I[16] = (T)(img)(_p1##x,_p1##y,z,c)), \
1450    (I[23] = (T)(img)(_p1##x,y,z,c)), \
1451    (I[30] = (T)(img)(_p1##x,_n1##y,z,c)), \
1452    (I[37] = (T)(img)(_p1##x,_n2##y,z,c)), \
1453    (I[44] = (T)(img)(_p1##x,_n3##y,z,c)), \
1454    (I[3] = (T)(img)(x,_p3##y,z,c)), \
1455    (I[10] = (T)(img)(x,_p2##y,z,c)), \
1456    (I[17] = (T)(img)(x,_p1##y,z,c)), \
1457    (I[24] = (T)(img)(x,y,z,c)), \
1458    (I[31] = (T)(img)(x,_n1##y,z,c)), \
1459    (I[38] = (T)(img)(x,_n2##y,z,c)), \
1460    (I[45] = (T)(img)(x,_n3##y,z,c)), \
1461    (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
1462    (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \
1463    (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \
1464    (I[25] = (T)(img)(_n1##x,y,z,c)), \
1465    (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \
1466    (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \
1467    (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \
1468    (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
1469    (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \
1470    (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \
1471    (I[26] = (T)(img)(_n2##x,y,z,c)), \
1472    (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \
1473    (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \
1474    (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \
1475    x+3>=(int)(img)._width?(img).width()-1:x+3); \
1476    x<=(int)(x1) && ((_n3##x<(img).width() && ( \
1477    (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
1478    (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \
1479    (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \
1480    (I[27] = (T)(img)(_n3##x,y,z,c)), \
1481    (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \
1482    (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \
1483    (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
1484    _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x)); \
1485    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \
1486    I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \
1487    I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \
1488    I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \
1489    I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \
1490    I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \
1491    I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \
1492    _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
1493 
1494 #define cimg_for8x8(img,x,y,z,c,I,T) \
1495   cimg_for8((img)._height,y) for (int x = 0, \
1496    _p3##x = 0, _p2##x = 0, _p1##x = 0, \
1497    _n1##x = 1>=((img)._width)?(img).width()-1:1, \
1498    _n2##x = 2>=((img)._width)?(img).width()-1:2, \
1499    _n3##x = 3>=((img)._width)?(img).width()-1:3, \
1500    _n4##x = (int)( \
1501    (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \
1502    (I[8] = I[9] = I[10] = I[11] = (T)(img)(0,_p2##y,z,c)), \
1503    (I[16] = I[17] = I[18] = I[19] = (T)(img)(0,_p1##y,z,c)), \
1504    (I[24] = I[25] = I[26] = I[27] = (T)(img)(0,y,z,c)), \
1505    (I[32] = I[33] = I[34] = I[35] = (T)(img)(0,_n1##y,z,c)), \
1506    (I[40] = I[41] = I[42] = I[43] = (T)(img)(0,_n2##y,z,c)), \
1507    (I[48] = I[49] = I[50] = I[51] = (T)(img)(0,_n3##y,z,c)), \
1508    (I[56] = I[57] = I[58] = I[59] = (T)(img)(0,_n4##y,z,c)), \
1509    (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
1510    (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \
1511    (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \
1512    (I[28] = (T)(img)(_n1##x,y,z,c)), \
1513    (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \
1514    (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \
1515    (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \
1516    (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \
1517    (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
1518    (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \
1519    (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \
1520    (I[29] = (T)(img)(_n2##x,y,z,c)), \
1521    (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \
1522    (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \
1523    (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \
1524    (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \
1525    (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
1526    (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \
1527    (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \
1528    (I[30] = (T)(img)(_n3##x,y,z,c)), \
1529    (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \
1530    (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \
1531    (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \
1532    (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \
1533    4>=((img)._width)?(img).width()-1:4); \
1534    (_n4##x<(img).width() && ( \
1535    (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \
1536    (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \
1537    (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \
1538    (I[31] = (T)(img)(_n4##x,y,z,c)), \
1539    (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \
1540    (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \
1541    (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \
1542    (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
1543    _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \
1544    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], \
1545    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], \
1546    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], \
1547    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], \
1548    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], \
1549    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], \
1550    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], \
1551    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], \
1552    _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
1553 
1554 #define cimg_for_in8x8(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1555   cimg_for_in8((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1556    _p3##x = x-3<0?0:x-3, \
1557    _p2##x = x-2<0?0:x-2, \
1558    _p1##x = x-1<0?0:x-1, \
1559    _n1##x = x+1>=(img).width()?(img).width()-1:x+1, \
1560    _n2##x = x+2>=(img).width()?(img).width()-1:x+2, \
1561    _n3##x = x+3>=(img).width()?(img).width()-1:x+3, \
1562    _n4##x = (int)( \
1563    (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \
1564    (I[8] = (T)(img)(_p3##x,_p2##y,z,c)), \
1565    (I[16] = (T)(img)(_p3##x,_p1##y,z,c)), \
1566    (I[24] = (T)(img)(_p3##x,y,z,c)), \
1567    (I[32] = (T)(img)(_p3##x,_n1##y,z,c)), \
1568    (I[40] = (T)(img)(_p3##x,_n2##y,z,c)), \
1569    (I[48] = (T)(img)(_p3##x,_n3##y,z,c)), \
1570    (I[56] = (T)(img)(_p3##x,_n4##y,z,c)), \
1571    (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \
1572    (I[9] = (T)(img)(_p2##x,_p2##y,z,c)), \
1573    (I[17] = (T)(img)(_p2##x,_p1##y,z,c)), \
1574    (I[25] = (T)(img)(_p2##x,y,z,c)), \
1575    (I[33] = (T)(img)(_p2##x,_n1##y,z,c)), \
1576    (I[41] = (T)(img)(_p2##x,_n2##y,z,c)), \
1577    (I[49] = (T)(img)(_p2##x,_n3##y,z,c)), \
1578    (I[57] = (T)(img)(_p2##x,_n4##y,z,c)), \
1579    (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \
1580    (I[10] = (T)(img)(_p1##x,_p2##y,z,c)), \
1581    (I[18] = (T)(img)(_p1##x,_p1##y,z,c)), \
1582    (I[26] = (T)(img)(_p1##x,y,z,c)), \
1583    (I[34] = (T)(img)(_p1##x,_n1##y,z,c)), \
1584    (I[42] = (T)(img)(_p1##x,_n2##y,z,c)), \
1585    (I[50] = (T)(img)(_p1##x,_n3##y,z,c)), \
1586    (I[58] = (T)(img)(_p1##x,_n4##y,z,c)), \
1587    (I[3] = (T)(img)(x,_p3##y,z,c)), \
1588    (I[11] = (T)(img)(x,_p2##y,z,c)), \
1589    (I[19] = (T)(img)(x,_p1##y,z,c)), \
1590    (I[27] = (T)(img)(x,y,z,c)), \
1591    (I[35] = (T)(img)(x,_n1##y,z,c)), \
1592    (I[43] = (T)(img)(x,_n2##y,z,c)), \
1593    (I[51] = (T)(img)(x,_n3##y,z,c)), \
1594    (I[59] = (T)(img)(x,_n4##y,z,c)), \
1595    (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
1596    (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \
1597    (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \
1598    (I[28] = (T)(img)(_n1##x,y,z,c)), \
1599    (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \
1600    (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \
1601    (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \
1602    (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \
1603    (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
1604    (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \
1605    (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \
1606    (I[29] = (T)(img)(_n2##x,y,z,c)), \
1607    (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \
1608    (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \
1609    (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \
1610    (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \
1611    (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
1612    (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \
1613    (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \
1614    (I[30] = (T)(img)(_n3##x,y,z,c)), \
1615    (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \
1616    (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \
1617    (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \
1618    (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \
1619    x+4>=(img).width()?(img).width()-1:x+4); \
1620    x<=(int)(x1) && ((_n4##x<(img).width() && ( \
1621    (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \
1622    (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \
1623    (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \
1624    (I[31] = (T)(img)(_n4##x,y,z,c)), \
1625    (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \
1626    (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \
1627    (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \
1628    (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
1629    _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \
1630    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], \
1631    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], \
1632    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], \
1633    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], \
1634    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], \
1635    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], \
1636    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], \
1637    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], \
1638    _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
1639 
1640 #define cimg_for9x9(img,x,y,z,c,I,T) \
1641   cimg_for9((img)._height,y) for (int x = 0, \
1642    _p4##x = 0, _p3##x = 0, _p2##x = 0, _p1##x = 0, \
1643    _n1##x = 1>=((img)._width)?(img).width()-1:1, \
1644    _n2##x = 2>=((img)._width)?(img).width()-1:2, \
1645    _n3##x = 3>=((img)._width)?(img).width()-1:3, \
1646    _n4##x = (int)( \
1647    (I[0] = I[1] = I[2] = I[3] = I[4] = (T)(img)(_p4##x,_p4##y,z,c)), \
1648    (I[9] = I[10] = I[11] = I[12] = I[13] = (T)(img)(0,_p3##y,z,c)), \
1649    (I[18] = I[19] = I[20] = I[21] = I[22] = (T)(img)(0,_p2##y,z,c)), \
1650    (I[27] = I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_p1##y,z,c)), \
1651    (I[36] = I[37] = I[38] = I[39] = I[40] = (T)(img)(0,y,z,c)), \
1652    (I[45] = I[46] = I[47] = I[48] = I[49] = (T)(img)(0,_n1##y,z,c)), \
1653    (I[54] = I[55] = I[56] = I[57] = I[58] = (T)(img)(0,_n2##y,z,c)), \
1654    (I[63] = I[64] = I[65] = I[66] = I[67] = (T)(img)(0,_n3##y,z,c)), \
1655    (I[72] = I[73] = I[74] = I[75] = I[76] = (T)(img)(0,_n4##y,z,c)), \
1656    (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \
1657    (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \
1658    (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \
1659    (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \
1660    (I[41] = (T)(img)(_n1##x,y,z,c)), \
1661    (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \
1662    (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \
1663    (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \
1664    (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \
1665    (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \
1666    (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \
1667    (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \
1668    (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \
1669    (I[42] = (T)(img)(_n2##x,y,z,c)), \
1670    (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \
1671    (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \
1672    (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \
1673    (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \
1674    (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \
1675    (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \
1676    (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \
1677    (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \
1678    (I[43] = (T)(img)(_n3##x,y,z,c)), \
1679    (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \
1680    (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \
1681    (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \
1682    (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \
1683    4>=((img)._width)?(img).width()-1:4); \
1684    (_n4##x<(img).width() && ( \
1685    (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \
1686    (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \
1687    (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \
1688    (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \
1689    (I[44] = (T)(img)(_n4##x,y,z,c)), \
1690    (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \
1691    (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \
1692    (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \
1693    (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
1694    _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \
1695    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], \
1696    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], \
1697    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], \
1698    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], \
1699    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], \
1700    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], \
1701    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], \
1702    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], \
1703    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], \
1704    _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
1705 
1706 #define cimg_for_in9x9(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1707   cimg_for_in9((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1708    _p4##x = x-4<0?0:x-4, \
1709    _p3##x = x-3<0?0:x-3, \
1710    _p2##x = x-2<0?0:x-2, \
1711    _p1##x = x-1<0?0:x-1, \
1712    _n1##x = x+1>=(img).width()?(img).width()-1:x+1, \
1713    _n2##x = x+2>=(img).width()?(img).width()-1:x+2, \
1714    _n3##x = x+3>=(img).width()?(img).width()-1:x+3, \
1715    _n4##x = (int)( \
1716    (I[0] = (T)(img)(_p4##x,_p4##y,z,c)), \
1717    (I[9] = (T)(img)(_p4##x,_p3##y,z,c)), \
1718    (I[18] = (T)(img)(_p4##x,_p2##y,z,c)), \
1719    (I[27] = (T)(img)(_p4##x,_p1##y,z,c)), \
1720    (I[36] = (T)(img)(_p4##x,y,z,c)), \
1721    (I[45] = (T)(img)(_p4##x,_n1##y,z,c)), \
1722    (I[54] = (T)(img)(_p4##x,_n2##y,z,c)), \
1723    (I[63] = (T)(img)(_p4##x,_n3##y,z,c)), \
1724    (I[72] = (T)(img)(_p4##x,_n4##y,z,c)), \
1725    (I[1] = (T)(img)(_p3##x,_p4##y,z,c)), \
1726    (I[10] = (T)(img)(_p3##x,_p3##y,z,c)), \
1727    (I[19] = (T)(img)(_p3##x,_p2##y,z,c)), \
1728    (I[28] = (T)(img)(_p3##x,_p1##y,z,c)), \
1729    (I[37] = (T)(img)(_p3##x,y,z,c)), \
1730    (I[46] = (T)(img)(_p3##x,_n1##y,z,c)), \
1731    (I[55] = (T)(img)(_p3##x,_n2##y,z,c)), \
1732    (I[64] = (T)(img)(_p3##x,_n3##y,z,c)), \
1733    (I[73] = (T)(img)(_p3##x,_n4##y,z,c)), \
1734    (I[2] = (T)(img)(_p2##x,_p4##y,z,c)), \
1735    (I[11] = (T)(img)(_p2##x,_p3##y,z,c)), \
1736    (I[20] = (T)(img)(_p2##x,_p2##y,z,c)), \
1737    (I[29] = (T)(img)(_p2##x,_p1##y,z,c)), \
1738    (I[38] = (T)(img)(_p2##x,y,z,c)), \
1739    (I[47] = (T)(img)(_p2##x,_n1##y,z,c)), \
1740    (I[56] = (T)(img)(_p2##x,_n2##y,z,c)), \
1741    (I[65] = (T)(img)(_p2##x,_n3##y,z,c)), \
1742    (I[74] = (T)(img)(_p2##x,_n4##y,z,c)), \
1743    (I[3] = (T)(img)(_p1##x,_p4##y,z,c)), \
1744    (I[12] = (T)(img)(_p1##x,_p3##y,z,c)), \
1745    (I[21] = (T)(img)(_p1##x,_p2##y,z,c)), \
1746    (I[30] = (T)(img)(_p1##x,_p1##y,z,c)), \
1747    (I[39] = (T)(img)(_p1##x,y,z,c)), \
1748    (I[48] = (T)(img)(_p1##x,_n1##y,z,c)), \
1749    (I[57] = (T)(img)(_p1##x,_n2##y,z,c)), \
1750    (I[66] = (T)(img)(_p1##x,_n3##y,z,c)), \
1751    (I[75] = (T)(img)(_p1##x,_n4##y,z,c)), \
1752    (I[4] = (T)(img)(x,_p4##y,z,c)), \
1753    (I[13] = (T)(img)(x,_p3##y,z,c)), \
1754    (I[22] = (T)(img)(x,_p2##y,z,c)), \
1755    (I[31] = (T)(img)(x,_p1##y,z,c)), \
1756    (I[40] = (T)(img)(x,y,z,c)), \
1757    (I[49] = (T)(img)(x,_n1##y,z,c)), \
1758    (I[58] = (T)(img)(x,_n2##y,z,c)), \
1759    (I[67] = (T)(img)(x,_n3##y,z,c)), \
1760    (I[76] = (T)(img)(x,_n4##y,z,c)), \
1761    (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \
1762    (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \
1763    (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \
1764    (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \
1765    (I[41] = (T)(img)(_n1##x,y,z,c)), \
1766    (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \
1767    (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \
1768    (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \
1769    (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \
1770    (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \
1771    (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \
1772    (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \
1773    (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \
1774    (I[42] = (T)(img)(_n2##x,y,z,c)), \
1775    (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \
1776    (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \
1777    (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \
1778    (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \
1779    (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \
1780    (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \
1781    (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \
1782    (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \
1783    (I[43] = (T)(img)(_n3##x,y,z,c)), \
1784    (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \
1785    (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \
1786    (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \
1787    (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \
1788    x+4>=(img).width()?(img).width()-1:x+4); \
1789    x<=(int)(x1) && ((_n4##x<(img).width() && ( \
1790    (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \
1791    (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \
1792    (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \
1793    (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \
1794    (I[44] = (T)(img)(_n4##x,y,z,c)), \
1795    (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \
1796    (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \
1797    (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \
1798    (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
1799    _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \
1800    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], \
1801    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], \
1802    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], \
1803    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], \
1804    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], \
1805    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], \
1806    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], \
1807    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], \
1808    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], \
1809    _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
1810 
1811 #define cimg_for2x2x2(img,x,y,z,c,I,T) \
1812  cimg_for2((img)._depth,z) cimg_for2((img)._height,y) for (int x = 0, \
1813    _n1##x = (int)( \
1814    (I[0] = (T)(img)(0,y,z,c)), \
1815    (I[2] = (T)(img)(0,_n1##y,z,c)), \
1816    (I[4] = (T)(img)(0,y,_n1##z,c)), \
1817    (I[6] = (T)(img)(0,_n1##y,_n1##z,c)), \
1818    1>=(img)._width?(img).width()-1:1); \
1819    (_n1##x<(img).width() && ( \
1820    (I[1] = (T)(img)(_n1##x,y,z,c)), \
1821    (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \
1822    (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \
1823    (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
1824    x==--_n1##x; \
1825    I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \
1826    ++x, ++_n1##x)
1827 
1828 #define cimg_for_in2x2x2(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \
1829  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), \
1830    _n1##x = (int)( \
1831    (I[0] = (T)(img)(x,y,z,c)), \
1832    (I[2] = (T)(img)(x,_n1##y,z,c)), \
1833    (I[4] = (T)(img)(x,y,_n1##z,c)), \
1834    (I[6] = (T)(img)(x,_n1##y,_n1##z,c)), \
1835    x+1>=(int)(img)._width?(img).width()-1:x+1); \
1836    x<=(int)(x1) && ((_n1##x<(img).width() && ( \
1837    (I[1] = (T)(img)(_n1##x,y,z,c)), \
1838    (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \
1839    (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \
1840    (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
1841    x==--_n1##x); \
1842    I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \
1843    ++x, ++_n1##x)
1844 
1845 #define cimg_for3x3x3(img,x,y,z,c,I,T) \
1846  cimg_for3((img)._depth,z) cimg_for3((img)._height,y) for (int x = 0, \
1847    _p1##x = 0, \
1848    _n1##x = (int)( \
1849    (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \
1850    (I[3] = I[4] = (T)(img)(0,y,_p1##z,c)),  \
1851    (I[6] = I[7] = (T)(img)(0,_n1##y,_p1##z,c)), \
1852    (I[9] = I[10] = (T)(img)(0,_p1##y,z,c)), \
1853    (I[12] = I[13] = (T)(img)(0,y,z,c)), \
1854    (I[15] = I[16] = (T)(img)(0,_n1##y,z,c)), \
1855    (I[18] = I[19] = (T)(img)(0,_p1##y,_n1##z,c)), \
1856    (I[21] = I[22] = (T)(img)(0,y,_n1##z,c)), \
1857    (I[24] = I[25] = (T)(img)(0,_n1##y,_n1##z,c)), \
1858    1>=(img)._width?(img).width()-1:1); \
1859    (_n1##x<(img).width() && ( \
1860    (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \
1861    (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \
1862    (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \
1863    (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \
1864    (I[14] = (T)(img)(_n1##x,y,z,c)), \
1865    (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \
1866    (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \
1867    (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \
1868    (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
1869    x==--_n1##x; \
1870    I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \
1871    I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \
1872    I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \
1873    _p1##x = x++, ++_n1##x)
1874 
1875 #define cimg_for_in3x3x3(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \
1876  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), \
1877    _p1##x = x-1<0?0:x-1, \
1878    _n1##x = (int)( \
1879    (I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \
1880    (I[3] = (T)(img)(_p1##x,y,_p1##z,c)),  \
1881    (I[6] = (T)(img)(_p1##x,_n1##y,_p1##z,c)), \
1882    (I[9] = (T)(img)(_p1##x,_p1##y,z,c)), \
1883    (I[12] = (T)(img)(_p1##x,y,z,c)), \
1884    (I[15] = (T)(img)(_p1##x,_n1##y,z,c)), \
1885    (I[18] = (T)(img)(_p1##x,_p1##y,_n1##z,c)), \
1886    (I[21] = (T)(img)(_p1##x,y,_n1##z,c)), \
1887    (I[24] = (T)(img)(_p1##x,_n1##y,_n1##z,c)), \
1888    (I[1] = (T)(img)(x,_p1##y,_p1##z,c)), \
1889    (I[4] = (T)(img)(x,y,_p1##z,c)),  \
1890    (I[7] = (T)(img)(x,_n1##y,_p1##z,c)), \
1891    (I[10] = (T)(img)(x,_p1##y,z,c)), \
1892    (I[13] = (T)(img)(x,y,z,c)), \
1893    (I[16] = (T)(img)(x,_n1##y,z,c)), \
1894    (I[19] = (T)(img)(x,_p1##y,_n1##z,c)), \
1895    (I[22] = (T)(img)(x,y,_n1##z,c)), \
1896    (I[25] = (T)(img)(x,_n1##y,_n1##z,c)), \
1897    x+1>=(int)(img)._width?(img).width()-1:x+1); \
1898    x<=(int)(x1) && ((_n1##x<(img).width() && ( \
1899    (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \
1900    (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \
1901    (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \
1902    (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \
1903    (I[14] = (T)(img)(_n1##x,y,z,c)), \
1904    (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \
1905    (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \
1906    (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \
1907    (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
1908    x==--_n1##x); \
1909    I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \
1910    I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \
1911    I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \
1912    _p1##x = x++, ++_n1##x)
1913 
1914 #define cimglist_for(list,l) for (int l = 0; l<(int)(list)._width; ++l)
1915 #define cimglist_for_in(list,l0,l1,l) \
1916   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)
1917 
1918 #define cimglist_apply(list,fn) cimglist_for(list,__##fn) (list)[__##fn].fn
1919 
1920 // Define macros used when exceptions are thrown.
1921 #define _cimgdisplay_instance "[instance(%u,%u,%u,%c%s%c)] CImgDisplay::"
1922 #define cimgdisplay_instance _width,_height,_normalization,_title?'\"':'[',_title?_title:"untitled",_title?'\"':']'
1923 #define _cimg_instance "[instance(%u,%u,%u,%u,%p,%sshared)] CImg<%s>::"
1924 #define cimg_instance _width,_height,_depth,_spectrum,_data,_is_shared?"":"non-",pixel_type()
1925 #define _cimglist_instance "[instance(%u,%u,%p)] CImgList<%s>::"
1926 #define cimglist_instance _width,_allocated_width,_data,pixel_type()
1927 
1928 /*------------------------------------------------
1929  #
1930  #
1931  #  Definition of the cimg_library:: namespace
1932  #
1933  #
1934  -------------------------------------------------*/
1935 //! This namespace encompasses all classes and functions of the %CImg library.
1936 /**
1937    This namespace is defined to avoid functions and class names collisions
1938    that could happen with the include of other C++ header files.
1939    Anyway, it should not happen often and you should reasonnably start most of your
1940    %CImg-based programs with
1941    \code
1942    #include "CImg.h"
1943    using namespace cimg_library;
1944    \endcode
1945    to simplify the declaration of %CImg Library variables afterwards.
1946 **/
1947 namespace cimg_library {
1948 
1949   // Declare the only four classes of the CImg Library.
1950   template<typename T=float> struct CImg;
1951   template<typename T=float> struct CImgList;
1952   struct CImgDisplay;
1953   struct CImgException;
1954 
1955   // (Pre)declare the cimg namespace.
1956   // This is not the complete namespace declaration. It only contains some
1957   // necessary stuffs to ensure a correct declaration order of classes and functions
1958   // defined afterwards.
1959   namespace cimg {
1960 
1961 #ifdef cimg_use_vt100
1962     const char t_normal[] = { 0x1b, '[', '0', ';', '0', ';', '0', 'm', 0 };
1963     const char t_red[] = { 0x1b, '[', '4', ';', '3', '1', ';', '5', '9', 'm', 0 };
1964     const char t_bold[] = { 0x1b, '[', '1', 'm', 0 };
1965     const char t_purple[] = { 0x1b, '[', '0', ';', '3', '5', ';', '5', '9', 'm', 0 };
1966     const char t_green[] = { 0x1b, '[', '0', ';', '3', '2', ';', '5', '9', 'm', 0 };
1967 #else
1968     const char t_normal[] = { 0 };
1969     const char *const t_red = cimg::t_normal, *const t_bold = cimg::t_normal,
1970       *const t_purple = cimg::t_normal, *const t_green = cimg::t_normal;
1971 #endif
1972 
1973     inline std::FILE* output(std::FILE *file=0);
1974     inline void info();
1975 
1976     // Function used to avoid warning messages due to unused parameters.
1977     template<typename T>
unused(const T &,...)1978     inline void unused(const T&, ...) {}
1979 
1980     //! Get/set the current CImg exception mode.
1981     /**
1982        The way error messages are handled by CImg can be changed dynamically, using this function.
1983        Possible values are :
1984        - '0' to hide library messages (quiet mode).
1985        - '1' to print library messages on the console.
1986        - '2' to display library messages on a dialog window (default behavior).
1987        - '3' to do as '1' + add extra warnings (may slow down the code !).
1988        - '4' to do as '2' + add extra warnings (may slow down the code !).
1989      **/
_exception_mode(const unsigned int value,const bool is_set)1990     inline unsigned int& _exception_mode(const unsigned int value, const bool is_set) {
1991       static unsigned int mode = cimg_verbosity;
1992       if (is_set) mode = value;
1993       return mode;
1994     }
exception_mode()1995     inline unsigned int& exception_mode() {
1996       return _exception_mode(0,false);
1997     }
exception_mode(const unsigned int mode)1998     inline unsigned int& exception_mode(const unsigned int mode) {
1999       return _exception_mode(mode,true);
2000     }
2001 
2002     inline int dialog(const char *const title, const char *const msg, const char *const button1_label="OK",
2003                       const char *const button2_label=0, const char *const button3_label=0,
2004                       const char *const button4_label=0, const char *const button5_label=0,
2005                       const char *const button6_label=0, const bool centering=false);
2006 
2007     //! Evaluate math expression.
2008     inline double eval(const char *const expression, const double x=0, const double y=0, const double z=0, const double v=0);
2009   }
2010 
2011   /*----------------------------------------------
2012    #
2013    # Definition of the CImgException structures
2014    #
2015    ----------------------------------------------*/
2016   //! Instances of this class are thrown when errors occur during a %CImg library function call.
2017   /**
2018      \section ex1 Overview
2019 
2020       CImgException is the base class of %CImg exceptions.
2021       Exceptions are thrown by the %CImg Library when an error occured in a %CImg library function call.
2022       CImgException is seldom thrown itself. Children classes that specify the kind of error encountered
2023       are generally used instead. These sub-classes are :
2024 
2025       - \b CImgInstanceException : Thrown when the instance associated to the called %CImg function is not
2026       correctly defined. Generally, this exception is thrown when one tries to process \a empty images. The example
2027       below will throw a \a CImgInstanceException.
2028       \code
2029       CImg<float> img;        // Construct an empty image.
2030       img.blur(10);           // Try to blur the image.
2031       \endcode
2032 
2033       - \b CImgArgumentException : Thrown when one of the arguments given to the called %CImg function is not correct.
2034       Generally, this exception is thrown when arguments passed to the function are outside an admissible range of values.
2035       The example below will throw a \a CImgArgumentException.
2036       \code
2037       CImg<float> img(100,100,1,3);   // Define a 100x100 color image with float pixels.
2038       img = 0;                     // Try to fill pixels from the 0 pointer (invalid argument to operator=() ).
2039       \endcode
2040 
2041       - \b CImgIOException : Thrown when an error occured when trying to load or save image files.
2042       The example below will throw a \a CImgIOException.
2043       \code
2044       CImg<float> img("file_doesnt_exist.jpg");    // Try to load a file that doesn't exist.
2045       \endcode
2046 
2047       The parent class CImgException may be thrown itself when errors that cannot be classified in one of
2048       the above type occur. It is recommended not to throw CImgExceptions yourself, since there are normally
2049       reserved to %CImg Library functions.
2050       \b CImgInstanceException, \b CImgArgumentException and \b CImgIOException are simple
2051       subclasses of CImgException and are thus not detailled more in this reference documentation.
2052 
2053       \section ex2 Exception handling
2054 
2055       When an error occurs, the %CImg Library first displays the error in a modal window.
2056       Then, it throws an instance of the corresponding exception class, generally leading the program to stop
2057       (this is the default behavior).
2058       You can bypass this default behavior by handling the exceptions yourself,
2059       using a code block <tt>try { ... } catch () { ... }</tt>.
2060       In this case, you can avoid the apparition of the modal window, by
2061       defining the environment variable <tt>cimg_verbosity</tt> to 0 before including the %CImg header file.
2062       The example below shows how to cleanly handle %CImg Library exceptions :
2063       \code
2064       #define cimg_verbosity 0     // Disable modal window in CImg exceptions.
2065       #define "CImg.h"
2066       int main() {
2067         try {
2068           ...; // Here, do what you want.
2069         }
2070         catch (CImgInstanceException &e) {
2071           std::fprintf(stderr,"CImg Library Error : %s",e.what());  // Display your own error message
2072           ...                                                       // Do what you want now.
2073         }
2074       }
2075       \endcode
2076   **/
2077   struct CImgException : public std::exception {
2078 #define _cimg_exception_err(etype,disp_flag) \
2079   std::va_list ap; va_start(ap,format); cimg_vsnprintf(_message,sizeof(_message),format,ap); va_end(ap); \
2080   if (cimg::exception_mode()) { \
2081     std::fprintf(cimg::output(),"\n%s[CImg] *** %s ***%s %s\n",cimg::t_red,etype,cimg::t_normal,_message); \
2082     if (cimg_display && disp_flag && !(cimg::exception_mode()%2)) try { cimg::dialog(etype,_message,"Abort"); } catch (CImgException&) {} \
2083     if (cimg::exception_mode()>=3) cimg_library::cimg::info(); \
2084   }
2085 
2086     char _message[16384];
CImgExceptionCImgException2087     CImgException() { *_message = 0; }
CImgExceptionCImgException2088     CImgException(const char *const format, ...) { _cimg_exception_err("CImgException",true); }
whatCImgException2089     const char *what() const throw() { return _message; }
2090   };
2091 
2092   // The CImgInstanceException class is used to throw an exception related
2093   // to a non suitable instance encountered in a library function call.
2094   struct CImgInstanceException : public CImgException {
CImgInstanceExceptionCImgInstanceException2095     CImgInstanceException(const char *const format, ...) { _cimg_exception_err("CImgInstanceException",true); }
2096   };
2097 
2098   // The CImgArgumentException class is used to throw an exception related
2099   // to invalid arguments encountered in a library function call.
2100   struct CImgArgumentException : public CImgException {
CImgArgumentExceptionCImgArgumentException2101     CImgArgumentException(const char *const format, ...) { _cimg_exception_err("CImgArgumentException",true); }
2102   };
2103 
2104   // The CImgIOException class is used to throw an exception related
2105   // to Input/Output file problems encountered in a library function call.
2106   struct CImgIOException : public CImgException {
CImgIOExceptionCImgIOException2107     CImgIOException(const char *const format, ...) { _cimg_exception_err("CImgIOException",true); }
2108   };
2109 
2110   // The CImgDisplayException class is used to throw an exception related
2111   // to display problems encountered in a library function call.
2112   struct CImgDisplayException : public CImgException {
CImgDisplayExceptionCImgDisplayException2113     CImgDisplayException(const char *const format, ...) { _cimg_exception_err("CImgDisplayException",false); }
2114   };
2115 
2116   // The CImgWarningException class is used to throw an exception for warnings
2117   // encountered in a library function call.
2118   struct CImgWarningException : public CImgException {
CImgWarningExceptionCImgWarningException2119     CImgWarningException(const char *const format, ...) { _cimg_exception_err("CImgWarningException",false); }
2120   };
2121 
2122   /*-------------------------------------
2123    #
2124    # Definition of the namespace 'cimg'
2125    #
2126    --------------------------------------*/
2127   //! Namespace that encompasses \a low-level functions and variables of the %CImg Library.
2128   /**
2129      Most of the functions and variables within this namespace are used by the library for low-level processing.
2130      Nevertheless, documented variables and functions of this namespace may be used safely in your own source code.
2131 
2132      \warning Never write <tt>using namespace cimg_library::cimg;</tt> in your source code, since a lot of functions of the
2133      <tt>cimg::</tt> namespace have prototypes similar to standard C functions that could defined in the global namespace <tt>::</tt>.
2134   **/
2135   namespace cimg {
2136 
2137     // Define the traits that will be used to determine the best data type to work with.
2138     //
2139     template<typename T> struct type {
stringtype2140       static const char* string() {
2141         static const char* s[] = { "unknown",   "unknown8",   "unknown16",  "unknown24",
2142                                    "unknown32", "unknown40",  "unknown48",  "unknown56",
2143                                    "unknown64", "unknown72",  "unknown80",  "unknown88",
2144                                    "unknown96", "unknown104", "unknown112", "unknown120",
2145                                    "unknown128" };
2146         return s[(sizeof(T)<17)?sizeof(T):0];
2147       }
idtype2148       static unsigned int id() { return 0U; }
is_floattype2149       static bool is_float() { return false; }
mintype2150       static T min() { return (T)-1>0?(T)0:(T)-1<<(8*sizeof(T)-1); }
maxtype2151       static T max() { return (T)-1>0?(T)-1:~((T)-1<<(8*sizeof(T)-1)); }
cuttype2152       static T cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(T)val; }
formattype2153       static const char* format() { return "%s"; }
formattype2154       static const char* format(const T /*val*/) { static const char *const s = "unknown"; return s; }
2155     };
2156 
2157     template<> struct type<bool> {
2158       static const char* string() { static const char *const s = "bool"; return s; }
2159       static unsigned int id() { return 1U; }
2160       static bool is_float() { return false; }
2161       static bool min() { return false; }
2162       static bool max() { return true; }
2163       static bool cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(bool)val; }
2164       static const char* format() { return "%s"; }
2165       static const char* format(const bool val) { static const char* s[] = { "false", "true" }; return s[val?1:0]; }
2166     };
2167 
2168     template<> struct type<unsigned char> {
2169       static const char* string() { static const char *const s = "unsigned char"; return s; }
2170       static unsigned int id() { return 2U; }
2171       static bool is_float() { return false; }
2172       static unsigned char min() { return 0; }
2173       static unsigned char max() { return (unsigned char)~0U; }
2174       static unsigned char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; }
2175       static const char* format() { return "%u"; }
2176       static unsigned int format(const unsigned char val) { return (unsigned int)val; }
2177     };
2178 
2179     template<> struct type<char> {
2180       static const char* string() { static const char *const s = "char"; return s; }
2181       static unsigned int id() { return 3U; }
2182       static bool is_float() { return false; }
2183       static char min() { return (char)(-1L<<(8*sizeof(char)-1)); }
2184       static char max() { return ~((char)(-1L<<(8*sizeof(char)-1))); }
2185       static char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(char)val; }
2186       static const char* format() { return "%d"; }
2187       static int format(const char val) { return (int)val; }
2188     };
2189 
2190     template<> struct type<signed char> {
2191       static const char* string() { static const char *const s = "signed char"; return s; }
2192       static unsigned int id() { return 4U; }
2193       static bool is_float() { return false; }
2194       static signed char min() { return (signed char)(-1L<<(8*sizeof(signed char)-1)); }
2195       static signed char max() { return ~((signed char)(-1L<<(8*sizeof(signed char)-1))); }
2196       static signed char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(signed char)val; }
2197       static const char* format() { return "%d"; }
2198       static unsigned int format(const signed char val) { return (int)val; }
2199     };
2200 
2201     template<> struct type<unsigned short> {
2202       static const char* string() { static const char *const s = "unsigned short"; return s; }
2203       static unsigned int id() { return 5U; }
2204       static bool is_float() { return false; }
2205       static unsigned short min() { return 0; }
2206       static unsigned short max() { return (unsigned short)~0U; }
2207       static unsigned short cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned short)val; }
2208       static const char* format() { return "%u"; }
2209       static unsigned int format(const unsigned short val) { return (unsigned int)val; }
2210     };
2211 
2212     template<> struct type<short> {
2213       static const char* string() { static const char *const s = "short"; return s; }
2214       static unsigned int id() { return 6U; }
2215       static bool is_float() { return false; }
2216       static short min() { return (short)(-1L<<(8*sizeof(short)-1)); }
2217       static short max() { return ~((short)(-1L<<(8*sizeof(short)-1))); }
2218       static short cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(short)val; }
2219       static const char* format() { return "%d"; }
2220       static int format(const short val) { return (int)val; }
2221     };
2222 
2223     template<> struct type<unsigned int> {
2224       static const char* string() { static const char *const s = "unsigned int"; return s; }
2225       static unsigned int id() { return 7U; }
2226       static bool is_float() { return false; }
2227       static unsigned int min() { return 0; }
2228       static unsigned int max() { return (unsigned int)~0U; }
2229       static unsigned int cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned int)val; }
2230       static const char* format() { return "%u"; }
2231       static unsigned int format(const unsigned int val) { return val; }
2232     };
2233 
2234     template<> struct type<int> {
2235       static const char* string() { static const char *const s = "int"; return s; }
2236       static unsigned int id() { return 8U; }
2237       static bool is_float() { return false; }
2238       static int min() { return (int)(-1L<<(8*sizeof(int)-1)); }
2239       static int max() { return ~((int)(-1L<<(8*sizeof(int)-1))); }
2240       static int cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(int)val; }
2241       static const char* format() { return "%d"; }
2242       static int format(const int val) { return val; }
2243     };
2244 
2245     template<> struct type<unsigned long> {
2246       static const char* string() { static const char *const s = "unsigned long"; return s; }
2247       static unsigned int id() { return 9U; }
2248       static bool is_float() { return false; }
2249       static unsigned long min() { return 0; }
2250       static unsigned long max() { return (unsigned long)~0UL; }
2251       static unsigned long cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned long)val; }
2252       static const char* format() { return "%lu"; }
2253       static unsigned long format(const unsigned long val) { return val; }
2254     };
2255 
2256     template<> struct type<long> {
2257       static const char* string() { static const char *const s = "long"; return s; }
2258       static unsigned int id() { return 10U; }
2259       static bool is_float() { return false; }
2260       static long min() { return (long)(-1L<<(8*sizeof(long)-1)); }
2261       static long max() { return ~((long)(-1L<<(8*sizeof(long)-1))); }
2262       static long cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(long)val; }
2263       static const char* format() { return "%ld"; }
2264       static long format(const long val) { return val; }
2265     };
2266 
2267     template<> struct type<double> {
2268       static const char* string() { static const char *const s = "double"; return s; }
2269       static unsigned int id() { return 11U; }
2270       static bool is_float() { return true; }
2271       static double min() { return -1.7E308; }
2272       static double max() { return  1.7E308; }
2273       static double cut(const double val) { return val<min()?min():val>max()?max():val; }
2274       static double inf() { return max()*max(); }
2275       static double nan() { static const double v_nan = std::sqrt(-1.0); return v_nan; }
2276       static const char* format() { return "%g"; }
2277       static double format(const double val) { return val; }
2278     };
2279 
2280     template<> struct type<float> {
2281       static const char* string() { static const char *const s = "float"; return s; }
2282       static unsigned int id() { return 12U; }
2283       static bool is_float() { return true; }
2284       static float min() { return -3.4E38f; }
2285       static float max() { return  3.4E38f; }
2286       static float cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(float)val; }
2287       static float inf() { return (float)cimg::type<double>::inf(); }
2288       static float nan() { return (float)cimg::type<double>::nan(); }
2289       static const char* format() { return "%g"; }
2290       static double format(const float val) { return (double)val; }
2291     };
2292 
2293     template<typename T, typename t> struct superset { typedef T type; };
2294     template<> struct superset<bool,unsigned char> { typedef unsigned char type; };
2295     template<> struct superset<bool,char> { typedef char type; };
2296     template<> struct superset<bool,signed char> { typedef signed char type; };
2297     template<> struct superset<bool,unsigned short> { typedef unsigned short type; };
2298     template<> struct superset<bool,short> { typedef short type; };
2299     template<> struct superset<bool,unsigned int> { typedef unsigned int type; };
2300     template<> struct superset<bool,int> { typedef int type; };
2301     template<> struct superset<bool,unsigned long> { typedef unsigned long type; };
2302     template<> struct superset<bool,long> { typedef long type; };
2303     template<> struct superset<bool,float> { typedef float type; };
2304     template<> struct superset<bool,double> { typedef double type; };
2305     template<> struct superset<unsigned char,char> { typedef short type; };
2306     template<> struct superset<unsigned char,signed char> { typedef short type; };
2307     template<> struct superset<unsigned char,unsigned short> { typedef unsigned short type; };
2308     template<> struct superset<unsigned char,short> { typedef short type; };
2309     template<> struct superset<unsigned char,unsigned int> { typedef unsigned int type; };
2310     template<> struct superset<unsigned char,int> { typedef int type; };
2311     template<> struct superset<unsigned char,unsigned long> { typedef unsigned long type; };
2312     template<> struct superset<unsigned char,long> { typedef long type; };
2313     template<> struct superset<unsigned char,float> { typedef float type; };
2314     template<> struct superset<unsigned char,double> { typedef double type; };
2315     template<> struct superset<signed char,unsigned char> { typedef short type; };
2316     template<> struct superset<signed char,char> { typedef short type; };
2317     template<> struct superset<signed char,unsigned short> { typedef int type; };
2318     template<> struct superset<signed char,short> { typedef short type; };
2319     template<> struct superset<signed char,unsigned int> { typedef long type; };
2320     template<> struct superset<signed char,int> { typedef int type; };
2321     template<> struct superset<signed char,unsigned long> { typedef long type; };
2322     template<> struct superset<signed char,long> { typedef long type; };
2323     template<> struct superset<signed char,float> { typedef float type; };
2324     template<> struct superset<signed char,double> { typedef double type; };
2325     template<> struct superset<char,unsigned char> { typedef short type; };
2326     template<> struct superset<char,signed char> { typedef short type; };
2327     template<> struct superset<char,unsigned short> { typedef int type; };
2328     template<> struct superset<char,short> { typedef short type; };
2329     template<> struct superset<char,unsigned int> { typedef long type; };
2330     template<> struct superset<char,int> { typedef int type; };
2331     template<> struct superset<char,unsigned long> { typedef long type; };
2332     template<> struct superset<char,long> { typedef long type; };
2333     template<> struct superset<char,float> { typedef float type; };
2334     template<> struct superset<char,double> { typedef double type; };
2335     template<> struct superset<unsigned short,char> { typedef int type; };
2336     template<> struct superset<unsigned short,signed char> { typedef int type; };
2337     template<> struct superset<unsigned short,short> { typedef int type; };
2338     template<> struct superset<unsigned short,unsigned int> { typedef unsigned int type; };
2339     template<> struct superset<unsigned short,int> { typedef int type; };
2340     template<> struct superset<unsigned short,unsigned long> { typedef unsigned long type; };
2341     template<> struct superset<unsigned short,long> { typedef long type; };
2342     template<> struct superset<unsigned short,float> { typedef float type; };
2343     template<> struct superset<unsigned short,double> { typedef double type; };
2344     template<> struct superset<short,unsigned short> { typedef int type; };
2345     template<> struct superset<short,unsigned int> { typedef long type; };
2346     template<> struct superset<short,int> { typedef int type; };
2347     template<> struct superset<short,unsigned long> { typedef long type; };
2348     template<> struct superset<short,long> { typedef long type; };
2349     template<> struct superset<short,float> { typedef float type; };
2350     template<> struct superset<short,double> { typedef double type; };
2351     template<> struct superset<unsigned int,char> { typedef long type; };
2352     template<> struct superset<unsigned int,signed char> { typedef long type; };
2353     template<> struct superset<unsigned int,short> { typedef long type; };
2354     template<> struct superset<unsigned int,int> { typedef long type; };
2355     template<> struct superset<unsigned int,unsigned long> { typedef unsigned long type; };
2356     template<> struct superset<unsigned int,long> { typedef long type; };
2357     template<> struct superset<unsigned int,float> { typedef float type; };
2358     template<> struct superset<unsigned int,double> { typedef double type; };
2359     template<> struct superset<int,unsigned int> { typedef long type; };
2360     template<> struct superset<int,unsigned long> { typedef long type; };
2361     template<> struct superset<int,long> { typedef long type; };
2362     template<> struct superset<int,float> { typedef float type; };
2363     template<> struct superset<int,double> { typedef double type; };
2364     template<> struct superset<unsigned long,char> { typedef long type; };
2365     template<> struct superset<unsigned long,signed char> { typedef long type; };
2366     template<> struct superset<unsigned long,short> { typedef long type; };
2367     template<> struct superset<unsigned long,int> { typedef long type; };
2368     template<> struct superset<unsigned long,long> { typedef long type; };
2369     template<> struct superset<unsigned long,float> { typedef float type; };
2370     template<> struct superset<unsigned long,double> { typedef double type; };
2371     template<> struct superset<long,float> { typedef float type; };
2372     template<> struct superset<long,double> { typedef double type; };
2373     template<> struct superset<float,double> { typedef double type; };
2374 
2375     template<typename t1, typename t2, typename t3> struct superset2 {
2376       typedef typename superset<t1, typename superset<t2,t3>::type>::type type;
2377     };
2378 
2379     template<typename t1, typename t2, typename t3, typename t4> struct superset3 {
2380       typedef typename superset<t1, typename superset2<t2,t3,t4>::type>::type type;
2381     };
2382 
2383     template<typename t1, typename t2> struct last { typedef t2 type; };
2384 
2385 #define _cimg_Tt      typename cimg::superset<T,t>::type
2386 #define _cimg_Tfloat  typename cimg::superset<T,float>::type
2387 #define _cimg_Ttfloat typename cimg::superset2<T,t,float>::type
2388 
2389     // Define internal library variables.
2390 #if cimg_display==1
2391     struct X11_info {
2392       volatile unsigned int nb_wins;
2393       pthread_t*       event_thread;
2394       CImgDisplay*     wins[1024];
2395       Display*         display;
2396       unsigned int     nb_bits;
2397       bool             is_blue_first;
2398       bool             is_shm_enabled;
2399       bool             byte_order;
2400 #ifdef cimg_use_xrandr
2401       XRRScreenSize *resolutions;
2402       Rotation curr_rotation;
2403       unsigned int curr_resolution;
2404       unsigned int nb_resolutions;
2405 #endif
2406       X11_info():nb_wins(0),event_thread(0),display(0),
2407                  nb_bits(0),is_blue_first(false),is_shm_enabled(false),byte_order(false) {
2408 #ifdef cimg_use_xrandr
2409         resolutions = 0;
2410         curr_rotation = 0;
2411         curr_resolution = nb_resolutions = 0;
2412 #endif
2413       }
2414     };
2415 #if defined(cimg_module)
2416     X11_info& X11_attr();
2417 #elif defined(cimg_main)
2418     X11_info& X11_attr() { static X11_info val; return val; }
2419 #else
2420     inline X11_info& X11_attr() { static X11_info val; return val; }
2421 #endif
2422 
2423 #elif cimg_display==2
2424     struct Win32_info {
2425       HANDLE wait_event;
2426       Win32_info() { wait_event = CreateEvent(0,FALSE,FALSE,0); }
2427     };
2428 #if defined(cimg_module)
2429     Win32_info& Win32_attr();
2430 #elif defined(cimg_main)
2431     Win32_info& Win32_attr() { static Win32_info val; return val; }
2432 #else
2433     inline Win32_info& Win32_attr() { static Win32_info val; return val; }
2434 #endif
2435 #endif
2436 
2437 #if defined(cimg_use_magick)
2438     static struct Magick_info {
2439       Magick_info() {
2440         Magick::InitializeMagick("");
2441       }
2442     } _Magick_info;
2443 #endif
2444 
2445 #if cimg_display==1
2446     // Keycodes for X11-based graphical systems.
2447     const unsigned int keyESC        = XK_Escape;
2448     const unsigned int keyF1         = XK_F1;
2449     const unsigned int keyF2         = XK_F2;
2450     const unsigned int keyF3         = XK_F3;
2451     const unsigned int keyF4         = XK_F4;
2452     const unsigned int keyF5         = XK_F5;
2453     const unsigned int keyF6         = XK_F6;
2454     const unsigned int keyF7         = XK_F7;
2455     const unsigned int keyF8         = XK_F8;
2456     const unsigned int keyF9         = XK_F9;
2457     const unsigned int keyF10        = XK_F10;
2458     const unsigned int keyF11        = XK_F11;
2459     const unsigned int keyF12        = XK_F12;
2460     const unsigned int keyPAUSE      = XK_Pause;
2461     const unsigned int key1          = XK_1;
2462     const unsigned int key2          = XK_2;
2463     const unsigned int key3          = XK_3;
2464     const unsigned int key4          = XK_4;
2465     const unsigned int key5          = XK_5;
2466     const unsigned int key6          = XK_6;
2467     const unsigned int key7          = XK_7;
2468     const unsigned int key8          = XK_8;
2469     const unsigned int key9          = XK_9;
2470     const unsigned int key0          = XK_0;
2471     const unsigned int keyBACKSPACE  = XK_BackSpace;
2472     const unsigned int keyINSERT     = XK_Insert;
2473     const unsigned int keyHOME       = XK_Home;
2474     const unsigned int keyPAGEUP     = XK_Page_Up;
2475     const unsigned int keyTAB        = XK_Tab;
2476     const unsigned int keyQ          = XK_q;
2477     const unsigned int keyW          = XK_w;
2478     const unsigned int keyE          = XK_e;
2479     const unsigned int keyR          = XK_r;
2480     const unsigned int keyT          = XK_t;
2481     const unsigned int keyY          = XK_y;
2482     const unsigned int keyU          = XK_u;
2483     const unsigned int keyI          = XK_i;
2484     const unsigned int keyO          = XK_o;
2485     const unsigned int keyP          = XK_p;
2486     const unsigned int keyDELETE     = XK_Delete;
2487     const unsigned int keyEND        = XK_End;
2488     const unsigned int keyPAGEDOWN   = XK_Page_Down;
2489     const unsigned int keyCAPSLOCK   = XK_Caps_Lock;
2490     const unsigned int keyA          = XK_a;
2491     const unsigned int keyS          = XK_s;
2492     const unsigned int keyD          = XK_d;
2493     const unsigned int keyF          = XK_f;
2494     const unsigned int keyG          = XK_g;
2495     const unsigned int keyH          = XK_h;
2496     const unsigned int keyJ          = XK_j;
2497     const unsigned int keyK          = XK_k;
2498     const unsigned int keyL          = XK_l;
2499     const unsigned int keyENTER      = XK_Return;
2500     const unsigned int keySHIFTLEFT  = XK_Shift_L;
2501     const unsigned int keyZ          = XK_z;
2502     const unsigned int keyX          = XK_x;
2503     const unsigned int keyC          = XK_c;
2504     const unsigned int keyV          = XK_v;
2505     const unsigned int keyB          = XK_b;
2506     const unsigned int keyN          = XK_n;
2507     const unsigned int keyM          = XK_m;
2508     const unsigned int keySHIFTRIGHT = XK_Shift_R;
2509     const unsigned int keyARROWUP    = XK_Up;
2510     const unsigned int keyCTRLLEFT   = XK_Control_L;
2511     const unsigned int keyAPPLEFT    = XK_Super_L;
2512     const unsigned int keyALT        = XK_Alt_L;
2513     const unsigned int keySPACE      = XK_space;
2514     const unsigned int keyALTGR      = XK_Alt_R;
2515     const unsigned int keyAPPRIGHT   = XK_Super_R;
2516     const unsigned int keyMENU       = XK_Menu;
2517     const unsigned int keyCTRLRIGHT  = XK_Control_R;
2518     const unsigned int keyARROWLEFT  = XK_Left;
2519     const unsigned int keyARROWDOWN  = XK_Down;
2520     const unsigned int keyARROWRIGHT = XK_Right;
2521     const unsigned int keyPAD0       = XK_KP_0;
2522     const unsigned int keyPAD1       = XK_KP_1;
2523     const unsigned int keyPAD2       = XK_KP_2;
2524     const unsigned int keyPAD3       = XK_KP_3;
2525     const unsigned int keyPAD4       = XK_KP_4;
2526     const unsigned int keyPAD5       = XK_KP_5;
2527     const unsigned int keyPAD6       = XK_KP_6;
2528     const unsigned int keyPAD7       = XK_KP_7;
2529     const unsigned int keyPAD8       = XK_KP_8;
2530     const unsigned int keyPAD9       = XK_KP_9;
2531     const unsigned int keyPADADD     = XK_KP_Add;
2532     const unsigned int keyPADSUB     = XK_KP_Subtract;
2533     const unsigned int keyPADMUL     = XK_KP_Multiply;
2534     const unsigned int keyPADDIV     = XK_KP_Divide;
2535 
2536 #elif cimg_display==2
2537     // Keycodes for Windows.
2538     const unsigned int keyESC        = VK_ESCAPE;
2539     const unsigned int keyF1         = VK_F1;
2540     const unsigned int keyF2         = VK_F2;
2541     const unsigned int keyF3         = VK_F3;
2542     const unsigned int keyF4         = VK_F4;
2543     const unsigned int keyF5         = VK_F5;
2544     const unsigned int keyF6         = VK_F6;
2545     const unsigned int keyF7         = VK_F7;
2546     const unsigned int keyF8         = VK_F8;
2547     const unsigned int keyF9         = VK_F9;
2548     const unsigned int keyF10        = VK_F10;
2549     const unsigned int keyF11        = VK_F11;
2550     const unsigned int keyF12        = VK_F12;
2551     const unsigned int keyPAUSE      = VK_PAUSE;
2552     const unsigned int key1          = '1';
2553     const unsigned int key2          = '2';
2554     const unsigned int key3          = '3';
2555     const unsigned int key4          = '4';
2556     const unsigned int key5          = '5';
2557     const unsigned int key6          = '6';
2558     const unsigned int key7          = '7';
2559     const unsigned int key8          = '8';
2560     const unsigned int key9          = '9';
2561     const unsigned int key0          = '0';
2562     const unsigned int keyBACKSPACE  = VK_BACK;
2563     const unsigned int keyINSERT     = VK_INSERT;
2564     const unsigned int keyHOME       = VK_HOME;
2565     const unsigned int keyPAGEUP     = VK_PRIOR;
2566     const unsigned int keyTAB        = VK_TAB;
2567     const unsigned int keyQ          = 'Q';
2568     const unsigned int keyW          = 'W';
2569     const unsigned int keyE          = 'E';
2570     const unsigned int keyR          = 'R';
2571     const unsigned int keyT          = 'T';
2572     const unsigned int keyY          = 'Y';
2573     const unsigned int keyU          = 'U';
2574     const unsigned int keyI          = 'I';
2575     const unsigned int keyO          = 'O';
2576     const unsigned int keyP          = 'P';
2577     const unsigned int keyDELETE     = VK_DELETE;
2578     const unsigned int keyEND        = VK_END;
2579     const unsigned int keyPAGEDOWN   = VK_NEXT;
2580     const unsigned int keyCAPSLOCK   = VK_CAPITAL;
2581     const unsigned int keyA          = 'A';
2582     const unsigned int keyS          = 'S';
2583     const unsigned int keyD          = 'D';
2584     const unsigned int keyF          = 'F';
2585     const unsigned int keyG          = 'G';
2586     const unsigned int keyH          = 'H';
2587     const unsigned int keyJ          = 'J';
2588     const unsigned int keyK          = 'K';
2589     const unsigned int keyL          = 'L';
2590     const unsigned int keyENTER      = VK_RETURN;
2591     const unsigned int keySHIFTLEFT  = VK_SHIFT;
2592     const unsigned int keyZ          = 'Z';
2593     const unsigned int keyX          = 'X';
2594     const unsigned int keyC          = 'C';
2595     const unsigned int keyV          = 'V';
2596     const unsigned int keyB          = 'B';
2597     const unsigned int keyN          = 'N';
2598     const unsigned int keyM          = 'M';
2599     const unsigned int keySHIFTRIGHT = VK_SHIFT;
2600     const unsigned int keyARROWUP    = VK_UP;
2601     const unsigned int keyCTRLLEFT   = VK_CONTROL;
2602     const unsigned int keyAPPLEFT    = VK_LWIN;
2603     const unsigned int keyALT        = VK_LMENU;
2604     const unsigned int keySPACE      = VK_SPACE;
2605     const unsigned int keyALTGR      = VK_CONTROL;
2606     const unsigned int keyAPPRIGHT   = VK_RWIN;
2607     const unsigned int keyMENU       = VK_APPS;
2608     const unsigned int keyCTRLRIGHT  = VK_CONTROL;
2609     const unsigned int keyARROWLEFT  = VK_LEFT;
2610     const unsigned int keyARROWDOWN  = VK_DOWN;
2611     const unsigned int keyARROWRIGHT = VK_RIGHT;
2612     const unsigned int keyPAD0       = 0x60;
2613     const unsigned int keyPAD1       = 0x61;
2614     const unsigned int keyPAD2       = 0x62;
2615     const unsigned int keyPAD3       = 0x63;
2616     const unsigned int keyPAD4       = 0x64;
2617     const unsigned int keyPAD5       = 0x65;
2618     const unsigned int keyPAD6       = 0x66;
2619     const unsigned int keyPAD7       = 0x67;
2620     const unsigned int keyPAD8       = 0x68;
2621     const unsigned int keyPAD9       = 0x69;
2622     const unsigned int keyPADADD     = VK_ADD;
2623     const unsigned int keyPADSUB     = VK_SUBTRACT;
2624     const unsigned int keyPADMUL     = VK_MULTIPLY;
2625     const unsigned int keyPADDIV     = VK_DIVIDE;
2626 
2627 #else
2628     // Define unknow keycodes when no display are available.
2629     // (should rarely be used then !).
2630     const unsigned int keyESC        = 1U;
2631     const unsigned int keyF1         = 2U;
2632     const unsigned int keyF2         = 3U;
2633     const unsigned int keyF3         = 4U;
2634     const unsigned int keyF4         = 5U;
2635     const unsigned int keyF5         = 6U;
2636     const unsigned int keyF6         = 7U;
2637     const unsigned int keyF7         = 8U;
2638     const unsigned int keyF8         = 9U;
2639     const unsigned int keyF9         = 10U;
2640     const unsigned int keyF10        = 11U;
2641     const unsigned int keyF11        = 12U;
2642     const unsigned int keyF12        = 13U;
2643     const unsigned int keyPAUSE      = 14U;
2644     const unsigned int key1          = 15U;
2645     const unsigned int key2          = 16U;
2646     const unsigned int key3          = 17U;
2647     const unsigned int key4          = 18U;
2648     const unsigned int key5          = 19U;
2649     const unsigned int key6          = 20U;
2650     const unsigned int key7          = 21U;
2651     const unsigned int key8          = 22U;
2652     const unsigned int key9          = 23U;
2653     const unsigned int key0          = 24U;
2654     const unsigned int keyBACKSPACE  = 25U;
2655     const unsigned int keyINSERT     = 26U;
2656     const unsigned int keyHOME       = 27U;
2657     const unsigned int keyPAGEUP     = 28U;
2658     const unsigned int keyTAB        = 29U;
2659     const unsigned int keyQ          = 30U;
2660     const unsigned int keyW          = 31U;
2661     const unsigned int keyE          = 32U;
2662     const unsigned int keyR          = 33U;
2663     const unsigned int keyT          = 34U;
2664     const unsigned int keyY          = 35U;
2665     const unsigned int keyU          = 36U;
2666     const unsigned int keyI          = 37U;
2667     const unsigned int keyO          = 38U;
2668     const unsigned int keyP          = 39U;
2669     const unsigned int keyDELETE     = 40U;
2670     const unsigned int keyEND        = 41U;
2671     const unsigned int keyPAGEDOWN   = 42U;
2672     const unsigned int keyCAPSLOCK   = 43U;
2673     const unsigned int keyA          = 44U;
2674     const unsigned int keyS          = 45U;
2675     const unsigned int keyD          = 46U;
2676     const unsigned int keyF          = 47U;
2677     const unsigned int keyG          = 48U;
2678     const unsigned int keyH          = 49U;
2679     const unsigned int keyJ          = 50U;
2680     const unsigned int keyK          = 51U;
2681     const unsigned int keyL          = 52U;
2682     const unsigned int keyENTER      = 53U;
2683     const unsigned int keySHIFTLEFT  = 54U;
2684     const unsigned int keyZ          = 55U;
2685     const unsigned int keyX          = 56U;
2686     const unsigned int keyC          = 57U;
2687     const unsigned int keyV          = 58U;
2688     const unsigned int keyB          = 59U;
2689     const unsigned int keyN          = 60U;
2690     const unsigned int keyM          = 61U;
2691     const unsigned int keySHIFTRIGHT = 62U;
2692     const unsigned int keyARROWUP    = 63U;
2693     const unsigned int keyCTRLLEFT   = 64U;
2694     const unsigned int keyAPPLEFT    = 65U;
2695     const unsigned int keyALT        = 66U;
2696     const unsigned int keySPACE      = 67U;
2697     const unsigned int keyALTGR      = 68U;
2698     const unsigned int keyAPPRIGHT   = 69U;
2699     const unsigned int keyMENU       = 70U;
2700     const unsigned int keyCTRLRIGHT  = 71U;
2701     const unsigned int keyARROWLEFT  = 72U;
2702     const unsigned int keyARROWDOWN  = 73U;
2703     const unsigned int keyARROWRIGHT = 74U;
2704     const unsigned int keyPAD0       = 75U;
2705     const unsigned int keyPAD1       = 76U;
2706     const unsigned int keyPAD2       = 77U;
2707     const unsigned int keyPAD3       = 78U;
2708     const unsigned int keyPAD4       = 79U;
2709     const unsigned int keyPAD5       = 80U;
2710     const unsigned int keyPAD6       = 81U;
2711     const unsigned int keyPAD7       = 82U;
2712     const unsigned int keyPAD8       = 83U;
2713     const unsigned int keyPAD9       = 84U;
2714     const unsigned int keyPADADD     = 85U;
2715     const unsigned int keyPADSUB     = 86U;
2716     const unsigned int keyPADMUL     = 87U;
2717     const unsigned int keyPADDIV     = 88U;
2718 #endif
2719 
2720     const double PI = 3.14159265358979323846;   //!< Definition of the mathematical constant PI
2721 
2722     // Definition of a 10x13 font (small size).
2723     const unsigned int font10x13[256*10*13/32] = {
2724       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2725       0x0,0x0,0x0,0x0,0x0,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,
2726       0x68000300,0x801,0xc00010,0x100c000,0x68100,0x100c0680,0x2,0x403000,0x1000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2727       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2728       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,
2729       0x58120480,0x402,0x1205008,0x2012050,0x58080,0x20120581,0x40000001,0x804812,0x2000000,0x0,0x300,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2730       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,
2731       0x0,0x7010,0x7000000,0x8000200,0x20000,0xc0002000,0x8008,0x0,0x0,0x0,0x0,0x808,0x4000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2732       0x0,0x0,0x80000000,0x0,0x0,0x0,0x40000,0x0,0x0,0x0,0x0,0x480,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x80100c0,0x68000480,0x1001,
2733       0xc00010,0x1018000,0x68100,0x100c0680,0x4,0x403000,0x1020000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20140,0x28081883,0x200801,
2734       0x2a00000,0x10,0x1c0201c0,0x70040f80,0xc0f81c07,0x0,0x70,0x3e0303c0,0x3c3c0f83,0xe03c2107,0xe08810,0x18c31070,0x3c0703c0,
2735       0x783e0842,0x22222208,0x83e04010,0x1008000,0x4000200,0x20001,0x2002,0x408008,0x0,0x0,0x100000,0x0,0x1008,0x2000000,0x0,0x0,0x0,
2736       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20080,0x38000880,0x8078140f,0x81c00000,0x3e000,0xc020180,0x60080001,0xe0000002,0xc00042,0x108e2010,
2737       0xc0300c0,0x300c0303,0xf83c3e0f,0x83e0f81c,0x701c070,0x3c0c41c0,0x701c0701,0xc0001d08,0x42108421,0x8820088,0x4020120,0x58140480,
2738       0x802,0x1205008,0x3014050,0xc058080,0x20120581,0x40000002,0x804814,0x2020050,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20140,
2739       0x281e2484,0x80200801,0x1c02000,0x10,0x22060220,0x880c0801,0x82208,0x80000001,0x20008,0x41030220,0x40220802,0x402102,0x209010,
2740       0x18c31088,0x22088220,0x80080842,0x22222208,0x80204010,0x1014000,0x200,0x20001,0x2000,0x8008,0x0,0x0,0x100000,0x0,0x1008,
2741       0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x40000500,0x80800010,0x40200000,0x41000,0x12020040,0x10000003,0xa0000006,
2742       0x12000c4,0x31014000,0xc0300c0,0x300c0302,0x80402008,0x2008008,0x2008020,0x220c4220,0x88220882,0x20002208,0x42108421,0x8820088,
2743       0x0,0x300,0x0,0x0,0x0,0x14000000,0x0,0x200200,0x0,0x20000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0xfc282504,0x80001000,
2744       0x82a02000,0x20,0x22020020,0x8140802,0x102208,0x80801006,0x18008,0x9c848220,0x80210802,0x802102,0x20a010,0x15429104,0x22104220,
2745       0x80080842,0x22221405,0x404008,0x1022000,0x703c0,0x381e0701,0xc0783c02,0xc09008,0x1d83c070,0x3c078140,0x381c0882,0x21242208,
2746       0x81e01008,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x201e0,0x40220500,0x80800027,0x20e02800,0x9c800,0x12020040,
2747       0x20000883,0xa0200002,0x120a044,0x11064010,0x12048120,0x48120484,0x80802008,0x2008008,0x2008020,0x210a4411,0x4411044,0x10884508,
2748       0x42108421,0x503c0b0,0x1c0701c0,0x701c0707,0x70381c07,0x1c07008,0x2008020,0x20f01c0,0x701c0701,0xc0201c08,0x82208822,0x883c088,
2749       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0x50281903,0x20001000,0x80802000,0x20,0x22020040,0x30240f03,0xc0101c08,0x80801018,
2750       0x1fc06010,0xa48483c0,0x80210f03,0xe0803f02,0x20c010,0x15429104,0x22104220,0x70080841,0x41540805,0x804008,0x1041000,0x8220,
2751       0x40220881,0x882202,0x40a008,0x12422088,0x22088180,0x40100882,0x21241408,0x80201008,0x2031000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2752       0x0,0x20280,0x401c0200,0x700028,0x21205000,0x92800,0xc1fc080,0x10000883,0xa0200002,0x1205049,0x12c19010,0x12048120,0x48120484,
2753       0xf0803c0f,0x3c0f008,0x2008020,0x790a4411,0x4411044,0x10504908,0x42108421,0x5022088,0x2008020,0x8020080,0x88402208,0x82208808,
2754       0x2008020,0x1e088220,0x88220882,0x20002608,0x82208822,0x8822088,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0x501c0264,
2755       0xa0001000,0x8001fc00,0x7000020,0x22020080,0x83e0082,0x20202207,0x80000020,0x1020,0xa4848220,0x80210802,0x9c2102,0x20c010,
2756       0x12425104,0x3c1043c0,0x8080841,0x41540802,0x804008,0x1000000,0x78220,0x40220f81,0x882202,0x40c008,0x12422088,0x22088100,
2757       0x60100881,0x41540805,0x406008,0x1849000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20280,0xf0140200,0x880028,0x20e0a03f,0x709c800,
2758       0x201c0,0x60000881,0xa0000007,0xc0284b,0x122eb020,0x12048120,0x48120487,0x80802008,0x2008008,0x2008020,0x21094411,0x4411044,
2759       0x10204908,0x42108421,0x2022088,0x1e0781e0,0x781e0787,0xf8403e0f,0x83e0f808,0x2008020,0x22088220,0x88220882,0x21fc2a08,0x82208822,
2760       0x5022050,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20001,0xf80a0294,0x40001000,0x80002000,0x20,0x22020100,0x8040082,0x20202200,
2761       0x80000018,0x1fc06020,0xa48fc220,0x80210802,0x842102,0x20a010,0x12425104,0x20104240,0x8080841,0x41541402,0x1004008,0x1000000,
2762       0x88220,0x40220801,0x882202,0x40a008,0x12422088,0x22088100,0x18100881,0x41540805,0x801008,0x2046000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2763       0x0,0x0,0x0,0x20280,0x401c0f80,0x80880028,0x20005001,0x94800,0x20000,0x880,0xa0000000,0x5015,0x4215040,0x3f0fc3f0,0xfc3f0fc8,
2764       0x80802008,0x2008008,0x2008020,0x21094411,0x4411044,0x10505108,0x42108421,0x203c088,0x22088220,0x88220888,0x80402008,0x2008008,
2765       0x2008020,0x22088220,0x88220882,0x20002a08,0x82208822,0x5022050,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa00a0494,0x60001000,
2766       0x80002004,0x8020,0x22020200,0x88040882,0x20402201,0x801006,0x18000,0x9f084220,0x40220802,0x442102,0x209010,0x10423088,0x20088220,
2767       0x8080840,0x80882202,0x2004008,0x1000000,0x88220,0x40220881,0x882202,0x409008,0x12422088,0x22088100,0x8100880,0x80881402,
2768       0x1001008,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20280,0x40220200,0x80700027,0x20002801,0x92800,0x1fc000,0x980,
2769       0xa0000000,0xa017,0x84417840,0x21084210,0x84210848,0x80402008,0x2008008,0x2008020,0x2208c220,0x88220882,0x20882208,0x42108421,
2770       0x2020088,0x22088220,0x88220888,0xc8402208,0x82208808,0x2008020,0x22088220,0x88220882,0x20203208,0x82208822,0x2022020,0x0,0x0,0x0,
2771       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0xa03c0463,0x90000801,0x2004,0x8040,0x1c0703e0,0x70040701,0xc0401c06,0x801001,0x20020,
2772       0x400843c0,0x3c3c0f82,0x3c2107,0x1c0881e,0x10423070,0x20070210,0xf0080780,0x80882202,0x3e04004,0x1000000,0x783c0,0x381e0701,
2773       0x782202,0x408808,0x12422070,0x3c078100,0x700c0780,0x80882202,0x1e01008,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x201e0,
2774       0xf8000200,0x80080010,0x40000001,0x41000,0x0,0xe80,0xa0000000,0x21,0x8e21038,0x21084210,0x84210848,0xf83c3e0f,0x83e0f81c,
2775       0x701c070,0x3c08c1c0,0x701c0701,0xc0005c07,0x81e0781e,0x20200b0,0x1e0781e0,0x781e0787,0x30381c07,0x1c07008,0x2008020,0x1c0881c0,
2776       0x701c0701,0xc0201c07,0x81e0781e,0x203c020,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000,0x801,0x4,0x40,0x0,0x0,0x0,0x1000,
2777       0x0,0x3c000000,0x0,0x0,0x0,0x0,0x10000,0x0,0x0,0x4004,0x1000000,0x0,0x0,0x80000,0x400000,0x0,0x20008000,0x0,0x4,0x1008,0x2000000,
2778       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x0,0x8008000f,0x80000000,0x3e000,0x0,0x800,0xa0000400,0x0,0x0,0x0,0x0,0x80000,0x0,
2779       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,
2780       0x402,0x8,0x40,0x0,0x0,0x0,0x2000,0x0,0x0,0x0,0x0,0x0,0x0,0xc000,0x0,0x0,0x7004,0x70000fc,0x0,0x0,0x700000,0x800000,0x0,0x20008000,
2781       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,
2782       0x300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600000,0x0,0x0,0x0,0x0,0x0,0x0,0x4020040
2783     };
2784 
2785     // Definition of a 12x24 font (normal size).
2786     const unsigned int font12x24[12*24*256/32] = {
2787       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2788       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,
2789       0x0,0x198,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc001806,0xc81980,0x60000000,0xc001806,0x1980c00,0x18060198,0xc80c,
2790       0x180600,0xc8198000,0xc001,0x80601980,0x18000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2791       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2792       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,
2793       0x0,0x0,0x0,0x0,0x0,0x0,0x600300f,0x1301980,0x90000000,0x600300f,0x1980600,0x300f0198,0x13006,0x300f01,0x30198000,0x6003,
2794       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,
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,0x6,0x0,0x60000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7007,0x3c0000,0x3006019,
2797       0x80000000,0x90000000,0x3006019,0x80000300,0x60198000,0x3,0x601980,0x0,0x3006,0x1980000,0x60000000,0x0,0x0,0xe0000000,0x0,
2798       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2799       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,
2800       0x0,0x0,0x0,0x0,0x0,0xc800019,0x80000000,0x198000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x0,0x0,0x1001,0x420000,0x0,0x0,0x90000000,
2801       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18000c06,0xc80001,0x10000000,0x18000c06,0x1800,0xc060000,0xc818,0xc0600,0xc8000000,
2802       0x18000,0xc0600000,0xc000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x80660207,0x800f8060,0x300c004,0x0,0x6,
2803       0xe00703f,0x3f00383,0xf80f07fc,0x1f01f000,0x0,0xf8,0x607f,0x7c7e07,0xfe7fe0f8,0x6063fc1f,0x86066007,0xe7060f0,0x7f80f07f,
2804       0x81f8fff6,0x6606c03,0x70ee077f,0xe0786000,0xf0070000,0xc000060,0xc0,0x3e000,0x60006003,0x600fc00,0x0,0x0,0x0,0x0,0x0,0x3c0603,
2805       0xc0000000,0x7800000,0xf0000,0x0,0xf00001f,0x80001fe0,0x7fe000,0x0,0x0,0x0,0x168fe609,0x0,0x90e07,0x6000,0x3c000e,0x70000f8,
2806       0x1980001f,0x0,0x1f8,0xf00000f,0xf00180,0xfe000,0xe00e,0x1001,0x20060,0x6006006,0x600600,0x600fe07c,0x7fe7fe7f,0xe7fe3fc3,
2807       0xfc3fc3fc,0x7e07060f,0xf00f00,0xf00f0000,0xf360660,0x6606606e,0x76001e0,0xc00180f,0x1681981,0x10000000,0xc00180f,0x1980c00,
2808       0x180f0198,0x3801680c,0x180f01,0x68198000,0xc001,0x80f01980,0x18600198,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,
2809       0x8044020c,0xc01f8060,0x2004004,0x0,0xc,0x3f81f07f,0x87f80383,0xf81f87fc,0x3f83f800,0x0,0x1fc,0x780607f,0x81fe7f87,0xfe7fe1fc,
2810       0x6063fc1f,0x860c6007,0xe7061f8,0x7fc1f87f,0xc3fcfff6,0x6606c03,0x30c6067f,0xe0783000,0xf00d8000,0x6000060,0xc0,0x7e000,0x60006003,
2811       0x600fc00,0x0,0x0,0xc00,0x0,0x0,0x7c0603,0xe0000000,0xfc00000,0x1f0000,0x0,0x900003f,0xc0003fe0,0x7fe000,0x0,0x0,0x0,0x1302660f,
2812       0x0,0xf0606,0x6004,0x7e0006,0x60601f8,0x19800001,0x80000000,0x1f8,0x19800010,0x81080300,0x3f2000,0x2011,0x1001,0x1c0060,0x6006006,
2813       0x600600,0x601fe1fe,0x7fe7fe7f,0xe7fe3fc3,0xfc3fc3fc,0x7f87061f,0x81f81f81,0xf81f8000,0x3fa60660,0x66066066,0x66003f0,0x6003009,
2814       0x1301981,0x10000000,0x6003009,0x1980600,0x30090198,0x1f013006,0x300901,0x30198000,0x6003,0x901980,0x30600198,0x0,0x0,0x0,
2815       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x80cc0f8c,0xc0180060,0x6006044,0x40000000,0xc,0x3181b041,0xc41c0783,0x388018,
2816       0x71c71800,0x0,0x106,0x18c0f061,0xc38261c6,0x600384,0x60606001,0x86186007,0xe78630c,0x60e30c60,0xe7040606,0x630cc03,0x39c30c00,
2817       0xc0603000,0x3018c000,0x3000060,0xc0,0x60000,0x60000000,0x6000c00,0x0,0x0,0xc00,0x0,0x0,0x600600,0x60000000,0x18400000,0x180000,
2818       0x0,0x19800070,0x40003600,0xc000,0x0,0x0,0x0,0x25a06,0x0,0x6030c,0x4,0xe20007,0xe060180,0xf000,0x80000000,0xf0000,0x10800000,
2819       0x80080600,0x7f2000,0x2020,0x80001001,0x20000,0xf00f00f,0xf00f00,0x601b0382,0x60060060,0x6000600,0x60060060,0x61c78630,0xc30c30c3,
2820       0xc30c000,0x30e60660,0x66066063,0xc600738,0x3006019,0x80000000,0xe0000000,0x3006019,0x80000300,0x60198000,0x3e000003,0x601980,
2821       0x0,0x3006,0x1980000,0x60600000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x80cc1fcc,0xc0180060,0x6006035,0x80000000,
2822       0x18,0x71c03000,0xc00c0583,0x300018,0x60c60c00,0x0,0x6,0x3060f060,0xc30060c6,0x600300,0x60606001,0x86306007,0x9e78670e,0x60670e60,
2823       0x66000606,0x630c606,0x19830c01,0xc0601800,0x30306000,0x60,0xc0,0x60000,0x60000000,0x6000c00,0x0,0x0,0xc00,0x0,0x0,0x600600,
2824       0x60000000,0x18000000,0x300000,0x0,0x78060,0x6600,0x1c000,0x300c,0x39819c0,0x0,0x25a00,0x0,0x30c,0x4,0xc00003,0xc060180,0x30c1f,
2825       0x80000000,0x30c000,0x10800001,0x80700000,0x7f2000,0x2020,0x80001001,0x20060,0xf00f00f,0xf00f00,0xf01b0300,0x60060060,0x6000600,
2826       0x60060060,0x60c78670,0xe70e70e7,0xe70e000,0x70c60660,0x66066063,0xc7f8618,0x0,0x0,0x0,0x0,0x0,0x0,0x7000000,0x0,0x0,0x0,
2827       0x0,0x600000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x87ff3a4c,0xc0180060,0x400600e,0x600000,0x18,0x60c03000,
2828       0xc00c0d83,0x700018,0x60c60c00,0x20,0x400006,0x3060f060,0xc6006066,0x600600,0x60606001,0x86606006,0x966c6606,0x60660660,0x66000606,
2829       0x630c666,0xf019801,0x80601800,0x30603000,0x1f06f,0xf01ec0,0xf03fe1ec,0x6703e01f,0x61c0c06,0xdc6701f0,0x6f01ec0c,0xe1f87fc6,
2830       0xc60cc03,0x71c60c7f,0xc0600600,0x60000000,0x30000000,0x300000,0x40040,0x88060,0x6600,0x18000,0x300c,0x1981980,0x0,0x2421f,
2831       0x80003ce0,0x7fc198,0x601f,0xc02021,0x980600c0,0x40230,0x80000000,0x402000,0x19806003,0x80006,0xc7f2000,0x2020,0x80001001,
2832       0x420060,0xf00f00f,0xf00f00,0xf01b0600,0x60060060,0x6000600,0x60060060,0x6066c660,0x66066066,0x6606208,0x60e60660,0x66066061,
2833       0x987fc670,0x1f01f01f,0x1f01f01,0xf039c0f0,0xf00f00f,0xf03e03,0xe03e03e0,0x1f06701f,0x1f01f01,0xf01f0060,0x1e660c60,0xc60c60c6,
2834       0xc6f060c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x7ff3207,0x8c0c0000,0xc00300e,0x600000,0x30,0x60c03000,
2835       0xc01c0983,0xf0600030,0x31860c06,0x6001e0,0x78000e,0x23e1f861,0xc6006066,0x600600,0x60606001,0x86c06006,0x966c6606,0x60660660,
2836       0xe7000606,0x630c666,0xf01f803,0x600c00,0x30000000,0x3f87f,0x83f83fc3,0xf83fe3fc,0x7f83e01f,0x6380c07,0xfe7f83f8,0x7f83fc0d,
2837       0xf3fc7fc6,0xc71cc03,0x3183187f,0xc0600600,0x60000000,0xff806000,0x300000,0x40040,0x88070,0x6600,0x60030060,0x6001818,0x1883180,
2838       0x0,0x2423f,0xc0007ff0,0x607fc1f8,0x603f,0x80c01fc1,0xf80601e0,0x5f220,0x80420000,0x5f2000,0xf006006,0x80006,0xc7f2000,0x2020,
2839       0x82107c07,0xc03c0060,0x1f81f81f,0x81f81f80,0xf03b0600,0x60060060,0x6000600,0x60060060,0x6066c660,0x66066066,0x660671c,0x61660660,
2840       0x66066061,0xf860e6c0,0x3f83f83f,0x83f83f83,0xf87fe3f8,0x3f83f83f,0x83f83e03,0xe03e03e0,0x3f87f83f,0x83f83f83,0xf83f8060,
2841       0x3fc60c60,0xc60c60c3,0x187f8318,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x883200,0x300c0000,0xc003035,0x80600000,
2842       0x30,0x66c03001,0xc0f81983,0xf86f0030,0x1f071c06,0x600787,0xfe1e001c,0x6261987f,0x86006067,0xfe7fc600,0x7fe06001,0x87c06006,
2843       0xf6646606,0x60e6067f,0xc3e00606,0x61986f6,0x600f007,0x600c00,0x30000000,0x21c71,0x830831c3,0x1c06031c,0x71c06003,0x6700c06,
2844       0x6671c318,0x71831c0f,0x16040c06,0xc318606,0x1b031803,0x80600600,0x60000000,0x30009000,0x300000,0x40040,0x7003e,0x67e0,0x90070090,
2845       0x9001818,0x8c3100,0x0,0x60,0x4000e730,0x900380f0,0x6034,0x80c018c7,0xfe060338,0xb0121,0x80c60000,0x909000,0x6008,0x1080006,
2846       0xc3f2000,0x2011,0x3180060,0x60060e0,0x19819819,0x81981981,0x9833c600,0x7fe7fe7f,0xe7fe0600,0x60060060,0x60664660,0x66066066,
2847       0x66063b8,0x62660660,0x66066060,0xf06066c0,0x21c21c21,0xc21c21c2,0x1c466308,0x31c31c31,0xc31c0600,0x60060060,0x31871c31,0x83183183,
2848       0x18318000,0x71860c60,0xc60c60c3,0x18718318,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x1981a00,0xe03e0000,0xc003044,
2849       0x40600000,0x60,0x66c03001,0x80f03182,0x1c7f8030,0x3f83fc06,0x601e07,0xfe078038,0x6661987f,0x86006067,0xfe7fc61e,0x7fe06001,
2850       0x87e06006,0x66666606,0x7fc6067f,0x81f80606,0x61986f6,0x6006006,0x600600,0x30000000,0xc60,0xc60060c6,0xc06060c,0x60c06003,
2851       0x6e00c06,0x6660c60c,0x60c60c0e,0x6000c06,0xc318666,0x1f031803,0x600600,0x603c2000,0x30016800,0x1fe0000,0x1f81f8,0x1c1f,0x804067e1,
2852       0x68060168,0x16800810,0xc42300,0x0,0x60,0x20c331,0x68030060,0x6064,0x3fc1040,0xf006031c,0xa011e,0x818c7fe0,0x909000,0x7fe1f,
2853       0x80f00006,0xc0f2060,0xf80e,0x18c0780,0x780781c0,0x19819819,0x81981981,0x9833c600,0x7fe7fe7f,0xe7fe0600,0x60060060,0xfc666660,
2854       0x66066066,0x66061f0,0x66660660,0x66066060,0x606066e0,0xc00c00,0xc00c00c0,0xc066600,0x60c60c60,0xc60c0600,0x60060060,0x60c60c60,
2855       0xc60c60c6,0xc60c000,0x61c60c60,0xc60c60c3,0x1860c318,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x1980f81,0x80373000,
2856       0xc003004,0x7fe0001,0xf0000060,0x60c03003,0x183180,0xc71c060,0x3181ec00,0x7000,0xe070,0x66619860,0xc6006066,0x60061e,0x60606001,
2857       0x87606006,0x66626606,0x7f860661,0xc01c0606,0x6198696,0xf00600e,0x600600,0x30000000,0x1fc60,0xc60060c7,0xfc06060c,0x60c06003,
2858       0x7c00c06,0x6660c60c,0x60c60c0c,0x7f00c06,0xc3b8666,0xe01b007,0x3c00600,0x3c7fe000,0xff03ec00,0x1fe0000,0x40040,0xe001,0xc0806603,
2859       0xec0e03ec,0x3ec00010,0x0,0x60000000,0x7f,0x10c3f3,0xec070060,0x6064,0x3fc1040,0x6000030c,0xa0100,0x3187fe1,0xf09f1000,0x7fe00,
2860       0x6,0xc012060,0x0,0xc63c03,0xc03c0380,0x19819819,0x81981981,0x98330600,0x60060060,0x6000600,0x60060060,0xfc662660,0x66066066,
2861       0x66060e0,0x6c660660,0x66066060,0x6060e630,0x1fc1fc1f,0xc1fc1fc1,0xfc3fe600,0x7fc7fc7f,0xc7fc0600,0x60060060,0x60c60c60,0xc60c60c6,
2862       0xc60c7fe,0x62c60c60,0xc60c60c1,0xb060c1b0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0xffe02c6,0x3c633000,0xc003004,
2863       0x7fe0001,0xf00000c0,0x60c03006,0xc6180,0xc60c060,0x60c00c00,0x7000,0xe060,0x66639c60,0x66006066,0x600606,0x60606001,0x86306006,
2864       0x66636606,0x60060660,0xc0060606,0x61f8696,0xf00600c,0x600300,0x30000000,0x3fc60,0xc60060c7,0xfc06060c,0x60c06003,0x7c00c06,
2865       0x6660c60c,0x60c60c0c,0x1f80c06,0xc1b0666,0xe01b00e,0x3c00600,0x3c43c000,0x3007de00,0x600000,0x40040,0x30000,0x61006607,0xde0c07de,
2866       0x7de00000,0x0,0xf07fefff,0x1f,0x8008c3f7,0xde0e0060,0x6064,0xc01047,0xfe00018c,0xb013f,0x86300061,0xf0911000,0x6000,0x6,
2867       0xc012060,0x3f,0x8063c0cc,0x3cc0c700,0x39c39c39,0xc39c39c1,0x98630600,0x60060060,0x6000600,0x60060060,0x60663660,0x66066066,
2868       0x66061f0,0x78660660,0x66066060,0x607fc618,0x3fc3fc3f,0xc3fc3fc3,0xfc7fe600,0x7fc7fc7f,0xc7fc0600,0x60060060,0x60c60c60,0xc60c60c6,
2869       0xc60c7fe,0x64c60c60,0xc60c60c1,0xb060c1b0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0xffe0260,0x6661b000,0xc003000,
2870       0x600000,0xc0,0x60c0300c,0xc7fe0,0xc60c060,0x60c01c00,0x1e07,0xfe078060,0x6663fc60,0x66006066,0x600606,0x60606001,0x86386006,
2871       0x6636606,0x60060660,0xe0060606,0x60f039c,0x1b806018,0x600300,0x30000000,0x70c60,0xc60060c6,0x6060c,0x60c06003,0x7600c06,
2872       0x6660c60c,0x60c60c0c,0x1c0c06,0xc1b03fc,0xe01f01c,0xe00600,0x70000000,0x3007fc00,0x600000,0x40040,0x0,0x62006607,0xfc1807fc,
2873       0x7fc00000,0x0,0xf0000000,0x1,0xc004c307,0xfc1c0060,0x6064,0xc018c0,0x600000d8,0x5f200,0x3180060,0x50a000,0x6000,0x6,0xc012000,
2874       0x0,0xc601c0,0x4201c600,0x3fc3fc3f,0xc3fc3fc3,0xfc7f0600,0x60060060,0x6000600,0x60060060,0x60663660,0x66066066,0x66063b8,
2875       0x70660660,0x66066060,0x607f860c,0x70c70c70,0xc70c70c7,0xcc60600,0x60060060,0x6000600,0x60060060,0x60c60c60,0xc60c60c6,0xc60c000,
2876       0x68c60c60,0xc60c60c1,0xf060c1f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3300260,0x6661e000,0xc003000,0x600000,
2877       0x180,0x71c03018,0xc7fe0,0xc60c0c0,0x60c01800,0x787,0xfe1e0060,0x6663fc60,0x630060c6,0x600306,0x60606001,0x86186006,0x661e70e,
2878       0x60070c60,0x60060606,0x60f039c,0x19806038,0x600180,0x30000000,0x60c60,0xc60060c6,0x6060c,0x60c06003,0x6700c06,0x6660c60c,
2879       0x60c60c0c,0xc0c06,0xc1b039c,0x1f00e018,0x600600,0x60000000,0x1803f800,0x600000,0x40040,0x39e00,0x63006603,0xf83803f8,0x3f800000,
2880       0x0,0x60000000,0x0,0xc00cc303,0xf8180060,0x6064,0xc01fc0,0x60060070,0x40200,0x18c0060,0x402000,0x6000,0x6,0xc012000,0x0,0x18c0140,
2881       0x2014600,0x3fc3fc3f,0xc3fc3fc3,0xfc7f0300,0x60060060,0x6000600,0x60060060,0x60c61e70,0xe70e70e7,0xe70e71c,0x60e60660,0x66066060,
2882       0x6060060c,0x60c60c60,0xc60c60c6,0xcc60600,0x60060060,0x6000600,0x60060060,0x60c60c60,0xc60c60c6,0xc60c000,0x70c60c60,0xc60c60c0,
2883       0xe060c0e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33022e0,0x6670c000,0xc003000,0x600600,0x60180,0x31803030,
2884       0x41c0184,0x1831c0c0,0x71c23806,0x6001e0,0x780000,0x62630c60,0xe38261c6,0x600386,0x60606043,0x860c6006,0x661e30c,0x60030c60,
2885       0x740e0607,0xe0f039c,0x31c06030,0x600180,0x30000000,0x61c71,0x830831c3,0x406031c,0x60c06003,0x6300c06,0x6660c318,0x71831c0c,
2886       0x41c0c07,0x1c0e039c,0x1b00e030,0x600600,0x60000000,0x1c41b00e,0x601cc0,0x401f8,0x45240,0xe1803601,0xb03001b0,0x1b000000,
2887       0x0,0x0,0x41,0xc008e711,0xb0300060,0x6034,0x80c02020,0x60060030,0x30c00,0xc60000,0x30c000,0x0,0x7,0x1c012000,0x0,0x3180240,
2888       0x6024608,0x30c30c30,0xc30c30c3,0xc630382,0x60060060,0x6000600,0x60060060,0x61c61e30,0xc30c30c3,0xc30c208,0x70c70e70,0xe70e70e0,
2889       0x6060068c,0x61c61c61,0xc61c61c6,0x1cc62308,0x30430430,0x43040600,0x60060060,0x31860c31,0x83183183,0x18318060,0x31c71c71,
2890       0xc71c71c0,0xe07180e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x2203fc0,0x663f6000,0x6006000,0x600600,0x60300,
2891       0x3f81fe7f,0xc7f80187,0xf83f80c0,0x3f83f006,0x600020,0x400060,0x33e6067f,0xc1fe7f87,0xfe6001fe,0x6063fc7f,0x60e7fe6,0x660e3f8,
2892       0x6001f860,0x37fc0603,0xfc06030c,0x30c0607f,0xe06000c0,0x30000000,0x7fc7f,0x83f83fc3,0xfc0603fc,0x60c7fe03,0x61807c6,0x6660c3f8,
2893       0x7f83fc0c,0x7f80fc3,0xfc0e039c,0x3180607f,0xc0600600,0x60000000,0xfc0e00c,0x601986,0x66040040,0x4527f,0xc0803fe0,0xe07fe0e0,
2894       0xe000000,0x0,0x0,0x7f,0x80107ff0,0xe07fc060,0x603f,0x83fe0000,0x60060018,0xf000,0x420000,0xf0000,0x7fe00,0x7,0xfe012000,
2895       0x0,0x2100640,0xc0643f8,0x60660660,0x66066067,0xec3e1fe,0x7fe7fe7f,0xe7fe3fc3,0xfc3fc3fc,0x7f860e3f,0x83f83f83,0xf83f8000,
2896       0x5fc3fc3f,0xc3fc3fc0,0x606006fc,0x7fc7fc7f,0xc7fc7fc7,0xfcffe3f8,0x3fc3fc3f,0xc3fc7fe7,0xfe7fe7fe,0x3f860c3f,0x83f83f83,
2897       0xf83f8060,0x7f83fc3f,0xc3fc3fc0,0x607f8060,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x2201f80,0x3c1e7000,0x6006000,
2898       0x600,0x60300,0xe01fe7f,0xc3f00183,0xe01f0180,0x1f01e006,0x600000,0x60,0x3006067f,0x807c7e07,0xfe6000f8,0x6063fc3e,0x6067fe6,
2899       0x660e0f0,0x6000f060,0x3bf80601,0xf806030c,0x60e0607f,0xe06000c0,0x30000000,0x1ec6f,0xf01ec0,0xf80601ec,0x60c7fe03,0x61c03c6,
2900       0x6660c1f0,0x6f01ec0c,0x3f007c1,0xcc0e030c,0x71c0c07f,0xc0600600,0x60000000,0x7804018,0xe01186,0x66040040,0x39e3f,0x80401fe0,
2901       0x407fe040,0x4000000,0x0,0x0,0x3f,0x203ce0,0x407fc060,0x601f,0x3fe0000,0x60060018,0x0,0x0,0x0,0x7fe00,0x6,0xe6012000,0x0,
2902       0x7e0,0x1807e1f0,0x60660660,0x66066066,0x6c3e07c,0x7fe7fe7f,0xe7fe3fc3,0xfc3fc3fc,0x7e060e0f,0xf00f00,0xf00f0000,0x8f01f81f,
2903       0x81f81f80,0x60600670,0x1ec1ec1e,0xc1ec1ec1,0xec79c0f0,0xf80f80f,0x80f87fe7,0xfe7fe7fe,0x1f060c1f,0x1f01f01,0xf01f0000,0x4f01cc1c,
2904       0xc1cc1cc0,0xc06f00c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200,0x0,0x6006000,0x600,0x600,0x0,0x0,0x0,0x0,
2905       0x600000,0x0,0x18000000,0x0,0x0,0x0,0x0,0x0,0x1800,0x0,0x0,0x0,0x600060,0x30000000,0x0,0x0,0xc,0x3,0x0,0x0,0x60000c00,0x0,
2906       0x0,0xc000,0x600600,0x60000000,0x18,0xc03100,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x601f8,0x0,0x0,0x0,0x0,0x6,
2907       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,
2908       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,
2909       0x0,0x1c000000,0x0,0x0,0x0,0x0,0x0,0xc00,0x0,0x0,0x0,0x780000,0xf0000000,0x0,0x0,0x21c,0x3,0x0,0x0,0x60000c00,0x0,0x0,0xc000,
2910       0x7c0603,0xe0000000,0x10,0xc02300,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x601f0,0x0,0x0,0x0,0x0,0x6,0x12000,0x1000000,
2911       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,
2912       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,
2913       0x0,0x0,0x0,0x0,0x800,0x0,0x0,0x0,0x780000,0xf0000000,0x0,0x0,0x3f8,0x3e,0x0,0x0,0x60000c00,0x0,0x0,0x38000,0x3c0603,0xc0000000,
2914       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,
2915       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,
2916       0x0,0x0,0x0,0x0,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,
2917       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,
2918       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,
2919       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,
2920       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,
2921       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2922       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 };
2923 
2924     // Definition of a 16x32 font (large size).
2925     const unsigned int font16x32[16*32*256/32] = {
2926       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2927       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2928       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,
2929       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70000e0,0x3c00730,0xe7001c0,0x0,0x70000e0,0x3c00e70,0x70000e0,0x3c00e70,0x730,0x70000e0,0x3c00730,
2930       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,
2931       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2932       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2933       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,
2934       0x0,0x0,0x18001c0,0x6600ff0,0xe7003e0,0x0,0x18001c0,0x6600e70,0x18001c0,0x6600e70,0xff0,0x18001c0,0x6600ff0,0xe700000,0x180,
2935       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,
2936       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2937       0x0,0x0,0x0,0x0,0x0,0x0,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,
2938       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,
2939       0xc300ce0,0xe700630,0x0,0x1c00380,0xc300e70,0x1c00380,0xc300e70,0xce0,0x1c00380,0xc300ce0,0xe700000,0x1c0,0x3800c30,0xe700380,
2940       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,
2941       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2942       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,
2943       0x0,0x0,0x0,0x0,0x0,0xc300000,0x0,0xc300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x700000,0x0,0x0,0x0,0x7c007c00,0x3e000000,
2944       0x0,0x0,0x630,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe000070,0x1800000,0xc60,0x0,0xe000070,0x1800000,0xe000070,
2945       0x1800000,0x0,0xe000070,0x1800000,0x0,0xe00,0x700180,0x70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2946       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,
2947       0x0,0x0,0x3f0,0xfc0,0x0,0x7000000,0x38000000,0x1c0000,0xfc0000,0x380001c0,0xe01c00,0x7f800000,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,
2948       0x1801f00,0x0,0x0,0x1c,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7300000,0x6600000,0x0,0x6600000,0x0,0x0,0x0,0x0,0xe700000,
2949       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,
2950       0xf80,0x70000e0,0x3c00730,0xe700c60,0x0,0x70000e0,0x3c00e70,0x70000e0,0x3c00e70,0xe000730,0x70000e0,0x3c00730,0xe700000,0x700,
2951       0xe003c0,0xe7000e0,0x38000e70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6300000,0x803c00,0x7c00180,
2952       0xc00300,0x1000000,0x0,0x1c,0x3c007c0,0xfc007e0,0xe01ff8,0x3f03ffc,0x7e007c0,0x0,0x0,0x7c0,0x1c0,0x7f8003f0,0x7f007ff8,0x7ff803f0,
2953       0x70381ffc,0xff0700e,0x7000783c,0x783807c0,0x7fc007c0,0x7fc00fc0,0x7fff7038,0x700ee007,0x780f780f,0x7ffc03f0,0x70000fc0,0x3c00000,
2954       0x3000000,0x38000000,0x1c0000,0x1fc0000,0x380001c0,0xe01c00,0x7f800000,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x1801f80,0x0,0x1f80000,
2955       0x7e,0x0,0x0,0x2400000,0xfc00000,0x7ff0000,0x7ffc0000,0x0,0x0,0x0,0x0,0xf30fb0c,0x2400000,0x0,0x240780f,0x1c0,0xfc,0x780f,
2956       0x18003f0,0xe700000,0x7c00000,0x0,0xff0,0x3c00000,0x78007c0,0xc00000,0xff80000,0xf80,0x7c00000,0xc000c00,0x18001c0,0x1c001c0,
2957       0x1c001c0,0x1c003e0,0x7fe03f0,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,0x1ffc1ffc,0x7f007838,0x7c007c0,0x7c007c0,0x7c00000,0x7c67038,
2958       0x70387038,0x7038780f,0x70001fe0,0x30000c0,0x2400f30,0xe700c60,0x0,0x30000c0,0x2400e70,0x30000c0,0x2400e70,0xf700f30,0x30000c0,
2959       0x2400f30,0xe700000,0x300,0xc00240,0xe7000c0,0x38000e70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,
2960       0x630018c,0x807e00,0xfe00180,0xc00300,0x1000000,0x0,0x38,0xff01fc0,0x3ff01ff0,0x1e01ff8,0x7f83ffc,0x1ff80ff0,0x0,0x0,0xff0,
2961       0x1f003e0,0x7fe00ff8,0x7fc07ff8,0x7ff80ff8,0x70381ffc,0xff0701c,0x7000783c,0x78381ff0,0x7fe01ff0,0x7fe01ff0,0x7fff7038,0x781ee007,
2962       0x3c1e380e,0x7ffc0380,0x380001c0,0x3c00000,0x1800000,0x38000000,0x1c0000,0x3c00000,0x380001c0,0xe01c00,0x3800000,0x0,0x0,
2963       0x0,0x7000000,0x0,0x0,0x1e0,0x18003c0,0x0,0x3fc0000,0x70,0x0,0x0,0x6600000,0x1ff00000,0x1fff0000,0x7ffc0000,0x0,0x0,0x0,0x0,
2964       0xcf0239c,0x3c00000,0x0,0x3c0380e,0x1c0,0x2001fe,0x380e,0x18007f8,0xe700000,0x8600000,0x0,0xff0,0x7e00000,0x8c00870,0x1800000,
2965       0x1ff80000,0x180,0xc600000,0xc000c00,0x38001c0,0x3e003e0,0x3e003e0,0x3e001c0,0x7fe0ff8,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,0x1ffc1ffc,
2966       0x7fc07838,0x1ff01ff0,0x1ff01ff0,0x1ff00000,0x1fec7038,0x70387038,0x7038380e,0x70003ce0,0x1800180,0x6600cf0,0xe7007c0,0x0,
2967       0x1800180,0x6600e70,0x1800180,0x6600e70,0x7c00cf0,0x1800180,0x6600cf0,0xe700000,0x180,0x1800660,0xe700180,0x38000e70,0x0,
2968       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x630030c,0x3f0e700,0x1e200180,0x1800180,0x21100000,0x0,
2969       0x38,0x1e7819c0,0x38781038,0x1e01c00,0xf080038,0x1c381c38,0x0,0x0,0x1878,0x7fc03e0,0x70e01e18,0x70e07000,0x70001e18,0x703801c0,
2970       0x707038,0x70007c7c,0x7c381c70,0x70701c70,0x70703830,0x1c07038,0x381ce007,0x1c1c3c1e,0x3c0380,0x380001c0,0x7e00000,0xc00000,
2971       0x38000000,0x1c0000,0x3800000,0x38000000,0x1c00,0x3800000,0x0,0x0,0x0,0x7000000,0x0,0x0,0x1c0,0x18001c0,0x0,0x70c0000,0xe0,
2972       0x0,0x0,0xc300000,0x38300000,0x3c700000,0x3c0000,0x0,0x0,0x0,0x0,0xce022f4,0x1800000,0x0,0x1803c1e,0x1c0,0x2003c2,0x3c1e,
2973       0x1800e08,0x7e0,0x300000,0x0,0x7e00000,0xe700000,0x600030,0x3000000,0x3f980000,0x180,0x18200000,0xc000c00,0x1e0001c0,0x3e003e0,
2974       0x3e003e0,0x3e003e0,0xfe01e18,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70e07c38,0x1c701c70,0x1c701c70,0x1c700000,0x3c787038,
2975       0x70387038,0x70383c1e,0x70003870,0xc00300,0xc300ce0,0x380,0x0,0xc00300,0xc300000,0xc00300,0xc300000,0xfc00ce0,0xc00300,0xc300ce0,
2976       0x0,0xc0,0x3000c30,0x300,0x38000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x630031c,0xff8c300,
2977       0x1c000180,0x1800180,0x39380000,0x0,0x70,0x1c3801c0,0x203c001c,0x3e01c00,0x1c000038,0x381c3838,0x0,0x0,0x1038,0xe0e03e0,0x70703c08,
2978       0x70707000,0x70003808,0x703801c0,0x707070,0x70007c7c,0x7c383838,0x70383838,0x70387010,0x1c07038,0x381c700e,0x1e3c1c1c,0x780380,
2979       0x1c0001c0,0xe700000,0x0,0x38000000,0x1c0000,0x3800000,0x38000000,0x1c00,0x3800000,0x0,0x0,0x0,0x7000000,0x0,0x0,0x1c0,0x18001c0,
2980       0x0,0xe000000,0xe0,0x0,0x1000100,0x3800,0x70100000,0x38700000,0x780000,0x1c0,0x7801ce0,0xe380000,0x0,0x2264,0x0,0x0,0x1c1c,
2981       0x0,0x200780,0x1c1c,0x1800c00,0x1818,0x7f00000,0x0,0x18180000,0xc300000,0x600070,0x0,0x7f980000,0x180,0x18300000,0xc000c00,
2982       0x3000000,0x3e003e0,0x3e003e0,0x3e003e0,0xee03c08,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70707c38,0x38383838,0x38383838,
2983       0x38380000,0x38387038,0x70387038,0x70381c1c,0x7fc03870,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc00000,0x0,0x0,0x0,0x0,0x0,0x0,
2984       0x38000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6300318,0xe88c300,0x1c000180,0x38001c0,
2985       0xfe00180,0x0,0x70,0x1c3801c0,0x1c001c,0x6e01c00,0x1c000078,0x381c3818,0x0,0x40000,0x40000038,0x1c0607e0,0x70703800,0x70707000,
2986       0x70003800,0x703801c0,0x7070e0,0x70007c7c,0x7c383838,0x70383838,0x70387000,0x1c07038,0x381c700e,0xf780e38,0x700380,0x1c0001c0,
2987       0x1c380000,0x0,0x38000000,0x1c0000,0x3800000,0x38000000,0x1c00,0x3800000,0x0,0x0,0x0,0x7000000,0x0,0x0,0x1c0,0x18001c0,0x0,
2988       0xe000000,0xe0,0x0,0x1000100,0x4400,0x70000000,0x38700000,0x700000,0xe0,0x7001c70,0xe380000,0x0,0x2264,0x0,0x0,0xe38,0x0,
2989       0x200700,0xe38,0x1800c00,0x300c,0xc300000,0x0,0x300c0000,0xc300180,0x6003c0,0x0,0x7f980000,0x180,0x18300000,0xc000c00,0x1800000,
2990       0x7e007e0,0x7e007e0,0x7e003e0,0xee03800,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70707c38,0x38383838,0x38383838,0x38380000,
2991       0x38387038,0x70387038,0x70380e38,0x7ff039f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e00000,0x0,0x0,0x0,0x40000,0x0,0x0,0x38000000,
2992       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6300318,0x1c80e700,0x1c000180,0x38001c0,0x3800180,
2993       0x0,0xe0,0x381c01c0,0x1c001c,0x6e01c00,0x38000070,0x381c381c,0x0,0x3c0000,0x78000078,0x38030770,0x70707800,0x70387000,0x70007000,
2994       0x703801c0,0x7071c0,0x7000745c,0x7638701c,0x7038701c,0x70387000,0x1c07038,0x1c38718e,0x7700f78,0xf00380,0xe0001c0,0x381c0000,
2995       0x7e0,0x39e003e0,0x79c03f0,0x3ffc079c,0x39e01fc0,0xfe01c1e,0x3807778,0x39e007e0,0x39e0079c,0x73c07e0,0x7ff83838,0x701ce007,
2996       0x783c701c,0x1ffc01c0,0x18001c0,0x0,0x1c000100,0xe0,0x0,0x1000100,0x4200,0x70000000,0x70700100,0xf00100,0x10000e0,0x7000c70,
2997       0xc700000,0x0,0x2204,0x7e00000,0x1e380100,0x1ffc0f78,0x0,0xf80700,0xf78,0x1800e00,0x63e6,0x18300000,0x0,0x6fe60000,0xe700180,
2998       0xc00060,0x3838,0x7f980000,0x180,0x18300000,0xc000c00,0x18001c0,0x7700770,0x7700770,0x77007f0,0xee07800,0x70007000,0x70007000,
2999       0x1c001c0,0x1c001c0,0x70387638,0x701c701c,0x701c701c,0x701c1008,0x707c7038,0x70387038,0x70380f78,0x707039c0,0x7e007e0,0x7e007e0,
3000       0x7e007e0,0x1f3c03e0,0x3f003f0,0x3f003f0,0x1fc01fc0,0x1fc01fc0,0x7f039e0,0x7e007e0,0x7e007e0,0x7e00380,0x7ce3838,0x38383838,
3001       0x3838701c,0x39e0701c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6307fff,0x1c807e0c,0xe000180,
3002       0x30000c0,0x3800180,0x0,0xe0,0x381c01c0,0x1c001c,0xce01fe0,0x38000070,0x381c381c,0x3800380,0xfc0000,0x7e0000f0,0x30030770,
3003       0x70707000,0x70387000,0x70007000,0x703801c0,0x707380,0x700076dc,0x7638701c,0x7038701c,0x70387800,0x1c07038,0x1c3873ce,0x7f00770,
3004       0xe00380,0xe0001c0,0x700e0000,0x1ff8,0x3ff00ff0,0xffc0ff8,0x3ffc0ffc,0x3bf01fc0,0xfe01c3c,0x3807f78,0x3bf00ff0,0x3ff00ffc,
3005       0x77e0ff0,0x7ff83838,0x3838e007,0x3c783838,0x1ffc01c0,0x18001c0,0x0,0x7ff00380,0x1e0,0x0,0x1000100,0x4200,0x78000000,0x70700380,
3006       0xe00380,0x3800060,0xe000e30,0x1c600000,0x0,0x2204,0xff00000,0x7f7c0380,0x1ffc0770,0x1c0,0x3fc0700,0x18040770,0x1800780,0x4e12,
3007       0x18300104,0x0,0x4c320000,0x7e00180,0x1c00030,0x3838,0x7f980000,0x180,0x18302080,0xc000c00,0x18001c0,0x7700770,0x7700770,
3008       0x7700770,0x1ee07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70387638,0x701c701c,0x701c701c,0x701c381c,0x705c7038,0x70387038,
3009       0x70380770,0x70383b80,0x1ff81ff8,0x1ff81ff8,0x1ff81ff8,0x3fbe0ff0,0xff80ff8,0xff80ff8,0x1fc01fc0,0x1fc01fc0,0xff83bf0,0xff00ff0,
3010       0xff00ff0,0xff00380,0xffc3838,0x38383838,0x38383838,0x3ff03838,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3011       0x0,0x1c0,0x7fff,0x1c803c38,0xf000000,0x70000e0,0xfe00180,0x0,0x1c0,0x381c01c0,0x3c0078,0xce01ff0,0x39e000f0,0x1c38381c,0x3800380,
3012       0x3e07ffc,0xf8001f0,0x307b0770,0x70e07000,0x70387000,0x70007000,0x703801c0,0x707700,0x700076dc,0x7638701c,0x7038701c,0x70387e00,
3013       0x1c07038,0x1c3873ce,0x3e007f0,0x1e00380,0x70001c0,0x0,0x1038,0x3c381e18,0x1c7c1e3c,0x3801e3c,0x3c7801c0,0xe01c78,0x380739c,
3014       0x3c781c38,0x3c381c3c,0x7c21e10,0x7003838,0x3838700e,0x1ef03838,0x3c01c0,0x18001c0,0x0,0x7fe007c0,0x1c0,0x0,0x1000100,0x6400,
3015       0x7e000000,0x707007c0,0x1e007c0,0x7c00070,0xe000638,0x18600000,0x0,0x0,0x1e100000,0x73ce07c0,0x3c07f0,0x1c0,0x7240700,0x1ddc3ffe,
3016       0x1800de0,0x8c01,0x1870030c,0x0,0x8c310000,0x3c00180,0x3800030,0x3838,0x7f980000,0x180,0x183030c0,0xc000c00,0x430001c0,0x7700770,
3017       0x7700770,0x7700770,0x1ce07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70387638,0x701c701c,0x701c701c,0x701c1c38,0x70dc7038,
3018       0x70387038,0x703807f0,0x70383b80,0x10381038,0x10381038,0x10381038,0x21e71e18,0x1e3c1e3c,0x1e3c1e3c,0x1c001c0,0x1c001c0,0x1e383c78,
3019       0x1c381c38,0x1c381c38,0x1c380380,0x1c383838,0x38383838,0x38383838,0x3c383838,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3020       0x0,0x0,0x0,0x0,0x0,0x1c0,0x630,0x1e8000e0,0x1f000000,0x70000e0,0x39380180,0x0,0x1c0,0x3b9c01c0,0x3c07f0,0x18e01078,0x3bf800e0,
3021       0x7e0383c,0x3800380,0x1f807ffc,0x3f001c0,0x61ff0e38,0x7fc07000,0x70387ff0,0x7ff07000,0x7ff801c0,0x707f00,0x7000729c,0x7338701c,
3022       0x7070701c,0x70703fc0,0x1c07038,0x1e7873ce,0x1c003e0,0x3c00380,0x70001c0,0x0,0x1c,0x3c381c00,0x1c3c1c1c,0x3801c3c,0x383801c0,
3023       0xe01cf0,0x380739c,0x38381c38,0x3c381c3c,0x7801c00,0x7003838,0x3838700e,0xfe03c78,0x7801c0,0x18001c0,0x0,0x1c000c20,0xff8,
3024       0x0,0x1ff01ff0,0x3818,0x3fc00100,0x707e0c20,0x3c00c20,0xc200030,0xc000618,0x18c00000,0x0,0x0,0x1c000080,0xe1ce0c20,0x7803e0,
3025       0x1c0,0xe200700,0xff83ffe,0x1801878,0x9801,0x1cf0071c,0x7ffc0000,0x8c310000,0x7ffe,0x7000030,0x3838,0x3f980380,0x180,0xc6038e0,
3026       0x7f9c7f9c,0x3e1c01c0,0xe380e38,0xe380e38,0xe380f78,0x1cfc7000,0x7ff07ff0,0x7ff07ff0,0x1c001c0,0x1c001c0,0xfe387338,0x701c701c,
3027       0x701c701c,0x701c0e70,0x719c7038,0x70387038,0x703803e0,0x70383b80,0x1c001c,0x1c001c,0x1c001c,0xe71c00,0x1c1c1c1c,0x1c1c1c1c,
3028       0x1c001c0,0x1c001c0,0x1c383838,0x1c381c38,0x1c381c38,0x1c380000,0x3c383838,0x38383838,0x38383c78,0x3c383c78,0x0,0x0,0x0,0x0,
3029       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x630,0xf800380,0x3f830000,0x70000e0,0x31080180,0x0,0x380,0x3b9c01c0,
3030       0x7807e0,0x38e00038,0x3c3800e0,0xff01c3c,0x3800380,0x7c000000,0x7c03c0,0x61870e38,0x7fc07000,0x70387ff0,0x7ff070fc,0x7ff801c0,
3031       0x707f80,0x7000739c,0x7338701c,0x7ff0701c,0x7fe00ff0,0x1c07038,0xe7073ce,0x1c003e0,0x3800380,0x38001c0,0x0,0x1c,0x381c3800,
3032       0x381c380e,0x380381c,0x383801c0,0xe01de0,0x380739c,0x3838381c,0x381c381c,0x7001e00,0x7003838,0x1c70718e,0x7e01c70,0xf00380,
3033       0x18001e0,0x1e000000,0x1c001bb0,0xff8,0x0,0x1000100,0xe0,0xff00300,0x707e1bb0,0x3801bb0,0x1bb00010,0x8000308,0x30c00000,0x0,
3034       0x0,0x1e0000c0,0xe1ce1bb0,0xf003e0,0x1c0,0x1c203ff8,0x63003e0,0x180181c,0x9801,0xfb00e38,0x7ffc0000,0x8fc10000,0x7ffe,0xe000860,
3035       0x3838,0x1f980380,0x180,0x7c01c70,0x1f001f0,0x1f003c0,0xe380e38,0xe380e38,0xe380e38,0x1cfc7000,0x7ff07ff0,0x7ff07ff0,0x1c001c0,
3036       0x1c001c0,0xfe387338,0x701c701c,0x701c701c,0x701c07e0,0x731c7038,0x70387038,0x703803e0,0x70383980,0x1c001c,0x1c001c,0x1c001c,
3037       0xe73800,0x380e380e,0x380e380e,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0000,0x387c3838,0x38383838,0x38381c70,
3038       0x381c1c70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0xc30,0x7f00e00,0x33c30000,0x70000e0,0x1007ffe,
3039       0x0,0x380,0x3b9c01c0,0xf00078,0x30e0001c,0x3c1c01c0,0x1c381fdc,0x0,0x70000000,0x1c0380,0x63030e38,0x70707000,0x70387000,0x700070fc,
3040       0x703801c0,0x707b80,0x7000739c,0x7338701c,0x7fc0701c,0x7fc001f0,0x1c07038,0xe703e5c,0x3e001c0,0x7800380,0x38001c0,0x0,0x7fc,
3041       0x381c3800,0x381c380e,0x380381c,0x383801c0,0xe01fe0,0x380739c,0x3838381c,0x381c381c,0x7001fc0,0x7003838,0x1c70718e,0x7c01c70,
3042       0xe01f00,0x180007c,0x7f8c0000,0x7fc03fb8,0x1c0,0x0,0x1000100,0x700,0x1f00600,0x70703fb8,0x7803fb8,0x3fb80000,0x8000000,0x180,
3043       0x0,0x0,0x1fc00060,0xe1ce3fb8,0xe001c0,0x1c0,0x1c203ff8,0xc1801c0,0x180c,0x9801,0x1c70,0xc0000,0x8cc10000,0x180,0xfe007c0,
3044       0x3838,0x7980380,0xff0,0xe38,0x3e003e00,0x3e000380,0xe380e38,0xe380e38,0xe380e38,0x38e07000,0x70007000,0x70007000,0x1c001c0,
3045       0x1c001c0,0x70387338,0x701c701c,0x701c701c,0x701c03c0,0x731c7038,0x70387038,0x703801c0,0x703838e0,0x7fc07fc,0x7fc07fc,0x7fc07fc,
3046       0xe73800,0x380e380e,0x380e380e,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c7ffc,0x38dc3838,0x38383838,0x38381c70,
3047       0x381c1c70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0xc60,0xf83878,0x71e30000,0x70000e0,0x1007ffe,
3048       0x7f0,0x380,0x381c01c0,0x1e0003c,0x60e0001c,0x381c01c0,0x381c079c,0x0,0x7c000000,0x7c0380,0x63031c1c,0x70307000,0x70387000,
3049       0x7000701c,0x703801c0,0x7071c0,0x7000739c,0x71b8701c,0x7000701c,0x71e00078,0x1c07038,0xe703e7c,0x7e001c0,0xf000380,0x38001c0,
3050       0x0,0x1ffc,0x381c3800,0x381c3ffe,0x380381c,0x383801c0,0xe01fc0,0x380739c,0x3838381c,0x381c381c,0x7000ff0,0x7003838,0x1ef03bdc,
3051       0x3800ee0,0x1e01f00,0x180007c,0x61fc0000,0x7fc07f3c,0x1c0,0x0,0x1000100,0x1800,0x780c00,0x70707f3c,0xf007f3c,0x7f3c0000,0x0,
3052       0x3c0,0x3ffcffff,0x0,0xff00030,0xe1fe7f3c,0x1e001c0,0x1c0,0x1c200700,0xc183ffe,0xe0c,0x9801,0x1ff038e0,0xc07f0,0x8c610000,
3053       0x180,0x0,0x3838,0x1980380,0x0,0x1ff0071c,0xe000e000,0xe0000f80,0x1c1c1c1c,0x1c1c1c1c,0x1c1c1e38,0x38e07000,0x70007000,0x70007000,
3054       0x1c001c0,0x1c001c0,0x703871b8,0x701c701c,0x701c701c,0x701c03c0,0x761c7038,0x70387038,0x703801c0,0x70703870,0x1ffc1ffc,0x1ffc1ffc,
3055       0x1ffc1ffc,0xfff3800,0x3ffe3ffe,0x3ffe3ffe,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c7ffc,0x389c3838,0x38383838,
3056       0x38380ee0,0x381c0ee0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0xfffc,0xbc60fc,0x70e30000,0x70000e0,
3057       0x180,0x7f0,0x700,0x381c01c0,0x3e0001c,0x7ffc001c,0x381c03c0,0x381c001c,0x0,0x1f807ffc,0x3f00380,0x63031ffc,0x70387000,0x70387000,
3058       0x7000701c,0x703801c0,0x7071e0,0x7000701c,0x71b8701c,0x7000701c,0x70f00038,0x1c07038,0x7e03e7c,0x77001c0,0xe000380,0x1c001c0,
3059       0x0,0x3c1c,0x381c3800,0x381c3ffe,0x380381c,0x383801c0,0xe01fe0,0x380739c,0x3838381c,0x381c381c,0x70003f8,0x7003838,0xee03bdc,
3060       0x3c00ee0,0x3c00380,0x18000e0,0xf00000,0x1c007e7c,0x3c0,0x0,0x1000100,0x0,0x381800,0x70707e7c,0xe007e7c,0x7e7c0000,0x0,0x7c0,
3061       0x0,0x0,0x3f80018,0xe1fe7e7c,0x3c001c0,0x1c0,0x1c200700,0xc183ffe,0xf0c,0x8c01,0x38e0,0xc07f0,0x8c710000,0x180,0x0,0x3838,
3062       0x1980000,0x0,0x71c,0x7000f0,0x700f00,0x1ffc1ffc,0x1ffc1ffc,0x1ffc1ffc,0x3fe07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0,
3063       0x703871b8,0x701c701c,0x701c701c,0x701c07e0,0x7c1c7038,0x70387038,0x703801c0,0x7ff03838,0x3c1c3c1c,0x3c1c3c1c,0x3c1c3c1c,
3064       0x3fff3800,0x3ffe3ffe,0x3ffe3ffe,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0000,0x391c3838,0x38383838,0x38380ee0,
3065       0x381c0ee0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfffc,0x9c01ce,0x70f60000,0x70000e0,0x180,
3066       0x0,0x700,0x381c01c0,0x780001c,0x7ffc001c,0x381c0380,0x381c003c,0x0,0x3e07ffc,0xf800380,0x63031ffc,0x70387000,0x70387000,
3067       0x7000701c,0x703801c0,0x7070f0,0x7000701c,0x71b8701c,0x7000701c,0x70700038,0x1c07038,0x7e03e7c,0xf7801c0,0x1e000380,0x1c001c0,
3068       0x0,0x381c,0x381c3800,0x381c3800,0x380381c,0x383801c0,0xe01fe0,0x380739c,0x3838381c,0x381c381c,0x7000078,0x7003838,0xee03a5c,
3069       0x7c00fe0,0x78001c0,0x18001c0,0x0,0x1c003ef8,0x380,0x0,0x1000100,0x810,0x383000,0x70703ef8,0x1e003ef8,0x3ef80000,0x0,0x7c0,
3070       0x0,0x0,0x78000c,0xe1c03ef8,0x78001c0,0x1c0,0x1c200700,0x63001c0,0x18003f8,0x4e12,0x1c70,0xc0000,0x4c320000,0x180,0x0,0x3838,
3071       0x1980000,0x0,0xe38,0x700118,0x701e00,0x1ffc1ffc,0x1ffc1ffc,0x1ffc1ffc,0x7fe07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0,
3072       0x703871b8,0x701c701c,0x701c701c,0x701c0e70,0x7c1c7038,0x70387038,0x703801c0,0x7fc0381c,0x381c381c,0x381c381c,0x381c381c,
3073       0x78e03800,0x38003800,0x38003800,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0000,0x3b1c3838,0x38383838,0x38380fe0,
3074       0x381c0fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1860,0x9c0186,0x707e0000,0x30000c0,0x180,
3075       0x0,0xe00,0x183801c0,0xf00001c,0xe0001c,0x181c0380,0x381c0038,0x0,0xfc0000,0x7e000000,0x61873c1e,0x70383800,0x70707000,0x7000381c,
3076       0x703801c0,0x707070,0x7000701c,0x70f83838,0x70003838,0x70780038,0x1c07038,0x7e03c3c,0xe3801c0,0x1c000380,0xe001c0,0x0,0x381c,
3077       0x381c3800,0x381c3800,0x380381c,0x383801c0,0xe01ef0,0x380739c,0x3838381c,0x381c381c,0x7000038,0x7003838,0xfe03e7c,0xfe007c0,
3078       0x70001c0,0x18001c0,0x0,0xe001ff0,0x380,0x0,0x1000100,0x162c,0x381800,0x30701ff0,0x1c001ff0,0x1ff00000,0x0,0x3c0,0x0,0x0,
3079       0x380018,0xe1c01ff0,0x70001c0,0x1c0,0x1c200700,0xff801c0,0x18000f0,0x63e6,0xe38,0x0,0x6c3e0000,0x0,0x0,0x3838,0x1980000,0x0,
3080       0x1c70,0xf0000c,0xf01c00,0x3c1e3c1e,0x3c1e3c1e,0x3c1e3c1c,0x70e03800,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x707070f8,
3081       0x38383838,0x38383838,0x38381c38,0x38387038,0x70387038,0x703801c0,0x7000381c,0x381c381c,0x381c381c,0x381c381c,0x70e03800,
3082       0x38003800,0x38003800,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0380,0x3e1c3838,0x38383838,0x383807c0,0x381c07c0,
3083       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18c0,0x9c0186,0x783c0000,0x38001c0,0x180,0x3800000,
3084       0x3800e00,0x1c3801c0,0x1e00003c,0xe00038,0x1c1c0780,0x381c0038,0x3800380,0x3c0000,0x78000000,0x61ff380e,0x70383808,0x70707000,
3085       0x7000381c,0x703801c0,0x40707078,0x7000701c,0x70f83838,0x70003838,0x70384038,0x1c07038,0x7e03c3c,0x1e3c01c0,0x3c000380,0xe001c0,
3086       0x0,0x383c,0x3c381c00,0x1c3c1c00,0x3801c3c,0x383801c0,0xe01c78,0x380739c,0x38381c38,0x3c381c3c,0x7000038,0x7003878,0x7c01e78,
3087       0x1ef007c0,0xf0001c0,0x18001c0,0x0,0xe000ee0,0x7800380,0xe380000,0x1001ff0,0x2242,0x40380c00,0x38700ee0,0x3c000ee0,0xee00000,
3088       0x0,0x0,0x0,0x0,0x380030,0xe1c00ee0,0xf0001c0,0x1c0,0xe200700,0xdd801c0,0x1800038,0x300c,0x71c,0x0,0x300c0000,0x0,0x0,0x3838,
3089       0x1980000,0x0,0x38e0,0xb0000c,0xb01c08,0x380e380e,0x380e380e,0x380e380e,0x70e03808,0x70007000,0x70007000,0x1c001c0,0x1c001c0,
3090       0x707070f8,0x38383838,0x38383838,0x3838381c,0x38387038,0x70387038,0x703801c0,0x7000381c,0x383c383c,0x383c383c,0x383c383c,
3091       0x70e01c00,0x1c001c00,0x1c001c00,0x1c001c0,0x1c001c0,0x1c383838,0x1c381c38,0x1c381c38,0x1c380380,0x1c383878,0x38783878,0x387807c0,
3092       0x3c3807c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x18c0,0x10b801ce,0x3c3e0000,0x38001c0,0x180,
3093       0x3800000,0x3801c00,0x1e7801c0,0x3c002078,0xe02078,0x1c380700,0x1c3810f0,0x3800380,0x40000,0x40000380,0x307b380e,0x70701e18,
3094       0x70e07000,0x70001c1c,0x703801c0,0x60e0703c,0x7000701c,0x70f83c78,0x70003c70,0x703c70f0,0x1c03870,0x3c01c3c,0x3c1c01c0,0x78000380,
3095       0x7001c0,0x0,0x3c7c,0x3c381e18,0x1c7c1e0c,0x3801c3c,0x383801c0,0xe01c38,0x3c0739c,0x38381c38,0x3c381c3c,0x7001078,0x7803c78,
3096       0x7c01c38,0x1c780380,0x1e0001c0,0x18001c0,0x0,0x70c06c0,0x7000380,0xe300000,0x1000100,0x2142,0x70f00600,0x3c7006c0,0x780006c0,
3097       0x6c00000,0x0,0x0,0x0,0x0,0x10780060,0x73e206c0,0x1e0001c0,0x1c0,0x7240700,0x180c01c0,0x1800018,0x1818,0x30c,0x0,0x18180000,
3098       0x0,0x0,0x3c78,0x1980000,0x0,0x30c0,0x130000c,0x1301c18,0x380e380e,0x380e380e,0x380e380e,0x70e01e18,0x70007000,0x70007000,
3099       0x1c001c0,0x1c001c0,0x70e070f8,0x3c783c78,0x3c783c78,0x3c781008,0x7c783870,0x38703870,0x387001c0,0x70003a3c,0x3c7c3c7c,0x3c7c3c7c,
3100       0x3c7c3c7c,0x79f11e18,0x1e0c1e0c,0x1e0c1e0c,0x1c001c0,0x1c001c0,0x1c783838,0x1c381c38,0x1c381c38,0x1c380380,0x1c383c78,0x3c783c78,
3101       0x3c780380,0x3c380380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x38c0,0x1ff800fc,0x1fee0000,
3102       0x1800180,0x180,0x3800000,0x3801c00,0xff01ffc,0x3ffc3ff0,0xe03ff0,0xff00700,0x1ff81fe0,0x3800380,0x0,0x380,0x3000780f,0x7ff00ff8,
3103       0x7fc07ff8,0x70000ffc,0x70381ffc,0x7fe0701c,0x7ff8701c,0x70781ff0,0x70001ff0,0x701c7ff0,0x1c01fe0,0x3c01c38,0x380e01c0,0x7ffc0380,
3104       0x7001c0,0x0,0x1fdc,0x3ff00ff0,0xffc0ffc,0x3800fdc,0x38383ffe,0xe01c3c,0x1fc739c,0x38380ff0,0x3ff00ffc,0x7001ff0,0x3f81fb8,
3105       0x7c01c38,0x3c3c0380,0x1ffc01c0,0x18001c0,0x0,0x3fc0380,0x7000380,0xc70718c,0x1000100,0x2244,0x7ff00200,0x1fff0380,0x7ffc0380,
3106       0x3800000,0x0,0x0,0x0,0x0,0x1ff000c0,0x7f7e0380,0x1ffc01c0,0x1c0,0x3fc3ffe,0x1c0,0x1800018,0x7e0,0x104,0x0,0x7e00000,0x7ffe,
3107       0x0,0x3fde,0x1980000,0x0,0x2080,0x3300018,0x3300ff0,0x780f780f,0x780f780f,0x780f780e,0xf0fe0ff8,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,
3108       0x1ffc1ffc,0x7fc07078,0x1ff01ff0,0x1ff01ff0,0x1ff00000,0x7ff01fe0,0x1fe01fe0,0x1fe001c0,0x70003bf8,0x1fdc1fdc,0x1fdc1fdc,
3109       0x1fdc1fdc,0x3fbf0ff0,0xffc0ffc,0xffc0ffc,0x3ffe3ffe,0x3ffe3ffe,0xff03838,0xff00ff0,0xff00ff0,0xff00000,0x3ff01fb8,0x1fb81fb8,
3110       0x1fb80380,0x3ff00380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x31c0,0x7e00078,0x7cf0000,0x1800180,
3111       0x0,0x3800000,0x3803800,0x3c01ffc,0x3ffc0fe0,0xe01fc0,0x3e00e00,0x7e00f80,0x3800380,0x0,0x380,0x18007007,0x7fc003f0,0x7f007ff8,
3112       0x700003f0,0x70381ffc,0x3f80701e,0x7ff8701c,0x707807c0,0x700007c0,0x701e1fc0,0x1c00fc0,0x3c01818,0x780f01c0,0x7ffc0380,0x3801c0,
3113       0x0,0xf9c,0x39e003e0,0x79c03f0,0x380079c,0x38383ffe,0xe01c1e,0x7c739c,0x383807e0,0x39e0079c,0x7000fc0,0x1f80f38,0x3801c38,
3114       0x781e0380,0x1ffc01c0,0x18001c0,0x0,0x1f80100,0xe000700,0x1c60718c,0x1000100,0x1e3c,0x1fc00100,0x7ff0100,0x7ffc0100,0x1000000,
3115       0x0,0x0,0x0,0x0,0xfc00080,0x3e3c0100,0x1ffc01c0,0x1c0,0xf83ffe,0x1c0,0x1800838,0x0,0x0,0x0,0x0,0x7ffe,0x0,0x3b9e,0x1980000,
3116       0x0,0x0,0x2300038,0x23003e0,0x70077007,0x70077007,0x70077007,0xe0fe03f0,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,0x1ffc1ffc,0x7f007078,
3117       0x7c007c0,0x7c007c0,0x7c00000,0xc7c00fc0,0xfc00fc0,0xfc001c0,0x700039f0,0xf9c0f9c,0xf9c0f9c,0xf9c0f9c,0x1f1e03e0,0x3f003f0,
3118       0x3f003f0,0x3ffe3ffe,0x3ffe3ffe,0x7e03838,0x7e007e0,0x7e007e0,0x7e00000,0x63e00f38,0xf380f38,0xf380380,0x39e00380,0x0,0x0,
3119       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,
3120       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,
3121       0x0,0x0,0x3800001c,0x0,0x0,0x0,0x700,0x1c0,0x18001c0,0x0,0x0,0xe000700,0x18600000,0x1000100,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3122       0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x1800ff0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x1980000,0x1800000,0x0,0x6300070,0x6300000,0x0,
3123       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,
3124       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,
3125       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,
3126       0x0,0x0,0x1c,0x0,0xe00000,0x0,0x0,0x3800001c,0x0,0x0,0x0,0x700,0x1e0,0x18003c0,0x0,0x0,0xc000700,0x18c00000,0x1000000,0x0,
3127       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x18007e0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x1980000,0xc00000,
3128       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,
3129       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,
3130       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,
3131       0x3f0,0xfc0,0x0,0x0,0x0,0x0,0x838,0x0,0x1e00000,0x0,0x0,0x3800001c,0x0,0x0,0x0,0xf00,0xfc,0x1801f80,0x0,0x0,0x8008e00,0x30c00000,
3132       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,
3133       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,
3134       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,
3135       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,
3136       0x0,0x0,0xff0,0x0,0x1fc00000,0x0,0x0,0x3800001c,0x0,0x0,0x0,0x3e00,0x7c,0x1801f00,0x0,0x0,0x800fe00,0x0,0x0,0x0,0x0,0x0,0x0,
3137       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x1800000,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x0,0x7c00000,0x0,0x3001fc,0x300000,
3138       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,
3139       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,
3140       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,
3141       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,
3142       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,
3143       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,
3144       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3145       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,
3146       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3147       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3148       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3149       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3150       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3151       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3152       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3153       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3154       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3155       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3156       0x0,0x0,0x0,0x0,0x0,0x0,0x0 };
3157 
3158     // Definition of a 29x57 font (extra large size).
3159     const unsigned int font29x57[29*57*256/32] = {
3160       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3161       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3162       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3163       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3164       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,
3165       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,
3166       0xc0000000,0x0,0x7c00,0xf80,0x7e000,0x0,0x7c00000,0xf80000,0x7e000000,0x0,0x0,0x1f00,0x3e0,0x1f800,0x0,0x0,0x0,0x3,0xe0000000,
3167       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,
3168       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3169       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3170       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3171       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3172       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,
3173       0x0,0x0,0x0,0x0,0x0,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,
3174       0x1f0000,0x7e00000,0xf838001f,0xf80001f,0xf0000000,0x0,0x3e00,0x1f00,0x7e000,0x3e1f000,0x3e00000,0x1f00000,0x7e00003e,0x1f000000,
3175       0x3e0,0xe0000f80,0x7c0,0x1f800,0x3e0e00,0x7c3e000,0x0,0x1,0xf0000000,0xf800003f,0x1f0f,0x800001f0,0x0,0x0,0x0,0x0,0x0,0x0,
3176       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3177       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3178       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3179       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3180       0x0,0x0,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,
3181       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,
3182       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e0000,0x1e0000,0xff00001,0xfe38001f,0xf80003f,
3183       0xf8000000,0x0,0x1e00,0x1e00,0xff000,0x3e1f000,0x1e00000,0x1e00000,0xff00003e,0x1f000000,0x7f8,0xe0000780,0x780,0x3fc00,0x7f8e00,
3184       0x7c3e000,0x0,0x0,0xf0000000,0xf000007f,0x80001f0f,0x800001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3185       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3186       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3187       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3188       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3189       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,
3190       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,
3191       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0000,0x3c0000,0x1e780003,0xfff8001f,0xf80003c,0x78000000,0x0,0xf00,0x3c00,0x1e7800,
3192       0x3e1f000,0xf00000,0x3c00001,0xe780003e,0x1f000000,0xfff,0xe00003c0,0xf00,0x79e00,0xfffe00,0x7c3e000,0x0,0x0,0x78000001,0xe00000f3,
3193       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,
3194       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3195       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3196       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3197       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3198       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,
3199       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,
3200       0x0,0x78000,0x780000,0x3c3c0003,0x8ff0001f,0xf800078,0x3c000000,0x0,0x780,0x7800,0x3c3c00,0x3e1f000,0x780000,0x7800003,0xc3c0003e,
3201       0x1f000000,0xe3f,0xc00001e0,0x1e00,0xf0f00,0xe3fc00,0x7c3e000,0x0,0x0,0x3c000003,0xc00001e1,0xe0001f0f,0x80000780,0x0,0x0,
3202       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,
3203       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3204       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3205       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3206       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3207       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,
3208       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,
3209       0x83e0001f,0xf800070,0x1c000000,0x0,0x3c0,0xf000,0x781e00,0x3e1f000,0x3c0000,0xf000007,0x81e0003e,0x1f000000,0xe0f,0x800000f0,
3210       0x3c00,0x1e0780,0xe0f800,0x7c3e000,0x0,0x0,0x1e000007,0x800003c0,0xf0001f0f,0x80000f00,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf8000000,
3211       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,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,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,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,0x3fc00,0x1fe000,0x3ff800,0x0,0x0,0x0,0x0,0x0,0x70,0x1c000000,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,0x3c,0x78000000,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,0x1f00000,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,0xf80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3224       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,
3225       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,
3226       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f00000,0x3e000,0x3e00000,0x0,0x78,0x3c000000,0x0,0x1f000,0x3e0,
3227       0x3e000,0x0,0x1f000000,0x3e0000,0x3e000000,0x0,0x0,0x7c00,0xf8,0xf800,0x0,0x0,0x0,0xf,0x80000000,0x1f00001f,0x0,0x3e,0x0,
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,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,
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,0xf80000,
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,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,
3233       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,
3234       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,
3235       0x0,0x0,0xf80000,0x7c000,0x3e00000,0xf0380000,0x70,0x1c000000,0x0,0xf800,0x7c0,0x3e000,0x0,0xf800000,0x7c0000,0x3e000000,
3236       0x0,0x3c0,0xe0003e00,0x1f0,0xf800,0x3c0e00,0x0,0x0,0x7,0xc0000000,0x3e00001f,0x0,0x7c,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,0x30000000,0xff,0x0,
3238       0xf8,0xf8000,0x1c000,0x0,0x0,0x0,0x0,0x1f,0xc0000000,0x1ff8,0xff00,0x0,0x0,0x3fe000,0x0,0x1fc00001,0xfe000000,0x0,0x0,0x0,
3239       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,
3240       0x0,0x3f,0xf0000000,0x7fe0,0x0,0x0,0x780000,0x1,0xe0000000,0x0,0x780000,0x3,0xfe000000,0x78000,0x3c00,0xf000,0x7800003,0xffe00000,
3241       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,
3242       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,
3243       0x0,0x0,0x1ff,0x1f0f8,0x0,0xff000,0x0,0x0,0x0,0x3f,0xff00000f,0x80000000,0xfe0,0x3f80,0xf00,0x0,0x0,0x0,0x1,0xf8000003,0xe0000000,
3244       0x1c00,0xe000,0xe00,0x0,0x0,0x0,0x0,0x0,0x3c,0x78000000,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f0,0x3f80,0x1fc00,0xfe000,
3245       0x7f0000,0x0,0x1fc07000,0x0,0x0,0x0,0x0,0x0,0x3f800,0x780000,0x78000,0x7f00001,0xfc38001f,0xf800070,0x1c000000,0x0,0x7800,
3246       0x780,0x7f000,0x3e1f000,0x7800000,0x780000,0x7f00003e,0x1f0003f0,0x7f0,0xe0001e00,0x1e0,0x1fc00,0x7f0e00,0x7c3e000,0x0,0x3,
3247       0xc0000000,0x3c00003f,0x80001f0f,0x80000078,0x1e0000,0x3e1f00,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,0x780000,0x3c1e0000,0x1e078000,0x30000000,0x3ff,0xc00001e0,0xf0,
3249       0x78000,0x1c000,0x0,0x0,0x0,0x0,0x1e0007f,0xf000007e,0x1ffff,0x7ffe0,0x1f80,0x3ffff80,0xfff803,0xfffff800,0xfff80007,0xff800000,
3250       0x0,0x0,0x0,0x0,0x1ffe00,0x0,0xfe0003,0xfff80000,0x3ffe01ff,0xe00003ff,0xffe01fff,0xff0003ff,0xe01e0007,0x803ffff0,0xfff80,
3251       0x3c000fc0,0x7800001f,0x8003f07e,0x1e000f,0xfe0007ff,0xf00003ff,0x8007ffe0,0x1fff8,0x7fffffe,0xf0003c1,0xe000079e,0xf1f,0x1f3e0,
3252       0x1f01ff,0xfff8003f,0xf003c000,0x7fe0,0x3f00,0x0,0x3c0000,0x1,0xe0000000,0x0,0x780000,0xf,0xfe000000,0x78000,0x3c00,0xf000,
3253       0x7800003,0xffe00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xfc0000f0,0x3fe00,0x0,0x0,0xfff00,0x0,0x0,0x3fe000,
3254       0x0,0x0,0x0,0x1dc0,0x0,0x3fff00,0x0,0x3ffff80,0x1f,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff1c07ff,0x3c0f001e,0x3c000000,
3255       0x0,0x0,0x1e3c0,0xf80007c,0x0,0x780000,0x0,0xfff8000,0x3e00,0x1f00000,0x7ff,0xc001f0f8,0x0,0x3ffc00,0x0,0x0,0x0,0x3f,0xff00003f,
3256       0xe0000000,0x3ff8,0xffe0,0x1e00,0x0,0xfffc00,0x0,0x7,0xf800000f,0xf8000000,0x1c00,0xe000,0xe00,0xf000,0x1fc000,0xfe0000,0x7f00000,
3257       0x3f800001,0xfc00003f,0xf80000ff,0xffc003ff,0xe007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01ffc,
3258       0xfc00,0x3c001ffc,0xffe0,0x7ff00,0x3ff800,0x1ffc000,0x0,0x7ff8f0f0,0x3c0780,0x1e03c00,0xf01e000,0x783e0001,0xf01e0000,0xffe00,
3259       0x3c0000,0xf0000,0x7700001,0xfe38001f,0xf800070,0x1c000000,0x0,0x3c00,0xf00,0x77000,0x3e1f000,0x3c00000,0xf00000,0x7700003e,
3260       0x1f0000f8,0xc0007f8,0xe0000f00,0x3c0,0x1dc00,0x7f8e00,0x7c3e000,0x0,0x1,0xe0000000,0x7800003b,0x80001f0f,0x800000f0,0x1e0000,
3261       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,
3262       0x0,0x0,0x780000,0x3c1e0000,0x1e070000,0x300001f0,0x7ff,0xc00001e0,0x1e0,0x7c000,0x1c000,0x0,0x0,0x0,0x0,0x3c000ff,0xf80007fe,
3263       0x3ffff,0x801ffff8,0x1f80,0x3ffff80,0x3fff803,0xfffff801,0xfffc000f,0xffc00000,0x0,0x0,0x0,0x0,0x7fff80,0x0,0xfe0003,0xffff0000,
3264       0xffff01ff,0xfc0003ff,0xffe01fff,0xff000fff,0xf01e0007,0x803ffff0,0xfff80,0x3c001f80,0x7800001f,0xc007f07e,0x1e001f,0xff0007ff,
3265       0xfc0007ff,0xc007fffc,0x3fffc,0x7fffffe,0xf0003c1,0xf0000f9e,0xf0f,0x8003e1e0,0x1e01ff,0xfff8003f,0xf001e000,0x7fe0,0x3f00,
3266       0x0,0x1e0000,0x1,0xe0000000,0x0,0x780000,0x1f,0xfe000000,0x78000,0x3c00,0xf000,0x7800003,0xffe00000,0x0,0x0,0x0,0x0,0x0,0x0,
3267       0x0,0x0,0x0,0x0,0x0,0x0,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x1fff80,0x0,0x0,0xffe000,0x0,0x0,0x0,0x3de0,0x0,0x7fff80,0x0,0xfffff80,
3268       0x1f,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xe7bc07ff,0x3e1f000f,0x78000000,0x0,0x0,0xf780,0x7800078,0x0,0x780000,0x180000,
3269       0x1fff8000,0x1e00,0x1e0003c,0xfff,0xc001f0f8,0x0,0x7ffe00,0x0,0x0,0x0,0x3f,0xff00007f,0xf0000000,0x3ffc,0xfff0,0x3c00,0x0,
3270       0x7fffc00,0x0,0x7,0xf800003f,0xfe000000,0x1c00,0xe000,0xe00,0xf000,0x1fc000,0xfe0000,0x7f00000,0x3f800001,0xfc00001f,0xe00001ff,
3271       0xffc00fff,0xf007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xc000fc00,0x3c003ffe,0x1fff0,
3272       0xfff80,0x7ffc00,0x3ffe000,0x0,0xfffce0f0,0x3c0780,0x1e03c00,0xf01e000,0x781e0001,0xe01e0000,0x3fff00,0x1e0000,0x1e0000,0xf780003,
3273       0xcf78001f,0xf800078,0x3c000000,0x0,0x1e00,0x1e00,0xf7800,0x3e1f000,0x1e00000,0x1e00000,0xf780003e,0x1f0000fc,0x7c000f3d,
3274       0xe0000780,0x780,0x3de00,0xf3de00,0x7c3e000,0x0,0x0,0xf0000000,0xf000007b,0xc0001f0f,0x800001e0,0x1e0000,0x3e1f00,0x0,0x0,
3275       0x0,0x0,0x0,0x0,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,
3276       0x3c1e0000,0x1e0f0000,0x300007fc,0xfff,0xc00001e0,0x1e0,0x3c000,0x1c000,0x0,0x0,0x0,0x0,0x3c001ff,0xfc001ffe,0x3ffff,0xc01ffffc,
3277       0x3f80,0x3ffff80,0x7fff803,0xfffff803,0xfffe001f,0xffe00000,0x0,0x0,0x0,0x0,0xffff80,0x7f800,0xfe0003,0xffff8001,0xffff01ff,
3278       0xff0003ff,0xffe01fff,0xff001fff,0xf01e0007,0x803ffff0,0xfff80,0x3c003f00,0x7800001f,0xc007f07f,0x1e003f,0xff8007ff,0xff000fff,
3279       0xe007ffff,0x7fffc,0x7fffffe,0xf0003c0,0xf0000f1e,0xf07,0x8003c1f0,0x3e01ff,0xfff8003f,0xf001e000,0x7fe0,0x7f80,0x0,0xe0000,
3280       0x1,0xe0000000,0x0,0x780000,0x1f,0xfe000000,0x78000,0x3c00,0xf000,0x7800003,0xffe00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,
3281       0x0,0x0,0x0,0x0,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x3fff80,0x0,0x0,0xffe000,0x0,0x0,0x0,0x78f0,0x0,0xffff80,0x0,0x3fffff80,0x1f,
3282       0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xc7f80070,0x3e1f0007,0x70000000,0x0,0x0,0x7700,0x7c000f8,0x0,0x780000,0x180000,
3283       0x3fff8000,0x1f00,0x3e0003c,0x1f03,0xc001f0f8,0x0,0x703f00,0x0,0x0,0x0,0x3f,0xff0000f0,0xf8000000,0x303e,0xc0f8,0x7800,0x0,
3284       0xffffc00,0x0,0x7,0x3800003e,0x3e000000,0x1c00,0xe000,0x3c00,0xf000,0x1fc000,0xfe0000,0x7f00000,0x3f800001,0xfc00000f,0xe00001ff,
3285       0xffc01fff,0xf007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xf000fe00,0x3c007fff,0x3fff8,
3286       0x1fffc0,0xfffe00,0x7fff000,0x1,0xffffc0f0,0x3c0780,0x1e03c00,0xf01e000,0x781f0003,0xe01e0000,0x3fff80,0xe0000,0x3c0000,0x1e3c0003,
3287       0x8ff0001f,0xf80003c,0x78000000,0x0,0xe00,0x3c00,0x1e3c00,0x3e1f000,0xe00000,0x3c00001,0xe3c0003e,0x1f00007f,0xf8000e3f,0xc0000380,
3288       0xf00,0x78f00,0xe3fc00,0x7c3e000,0x0,0x0,0x70000001,0xe00000f1,0xe0001f0f,0x800003c0,0x1e0000,0x3e1f00,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,0x780000,0x3c1e0000,0x3c0f0000,
3290       0x30000ffe,0xf80,0xc00001e0,0x3c0,0x1e000,0x101c040,0x0,0x0,0x0,0x0,0x78003f0,0x7e001ffe,0x3f807,0xe01f00fe,0x3f80,0x3ffff80,
3291       0x7e01803,0xfffff007,0xe03f003f,0x3f00000,0x0,0x0,0x0,0x0,0xfc0fc0,0x3ffe00,0xfe0003,0xffffc003,0xf81f01ff,0xff8003ff,0xffe01fff,
3292       0xff003f01,0xf01e0007,0x803ffff0,0xfff80,0x3c007e00,0x7800001f,0xc007f07f,0x1e007e,0xfc007ff,0xff801f83,0xf007ffff,0x800fc07c,
3293       0x7fffffe,0xf0003c0,0xf0000f0f,0x1e07,0xc007c0f8,0x7c01ff,0xfff8003c,0xf000,0x1e0,0xffc0,0x0,0xf0000,0x1,0xe0000000,0x0,0x780000,
3294       0x3e,0x0,0x78000,0x3c00,0xf000,0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1f,0x800000f0,0x1f80,
3295       0x0,0x0,0x7e0780,0x0,0x0,0x1f82000,0x0,0x0,0x0,0x7070,0x0,0x1f80f80,0x0,0x7fffff80,0x1f,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,
3296       0x0,0x1,0xc3f80070,0x3f3f0007,0xf0000000,0x0,0x0,0x7f00,0x3e001f0,0x0,0x780000,0x180000,0x7f018000,0xf80,0x7c0003c,0x3e00,
3297       0x4001f0f8,0xfe00,0x400f00,0x0,0x0,0x0,0x7f000000,0xe0,0x38000000,0x1e,0x38,0x7800,0x0,0x1ffe1c00,0x0,0x0,0x38000078,0xf000000,
3298       0x1c00,0xe000,0x7f800,0xf000,0x1fc000,0xfe0000,0x7f00000,0x3f800001,0xfc00001f,0xf00001ff,0xffc03f81,0xf007ffff,0xc03ffffe,
3299       0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xf800fe00,0x3c00fc1f,0x8007e0fc,0x3f07e0,0x1f83f00,0xfc1f800,
3300       0x3,0xf07fc0f0,0x3c0780,0x1e03c00,0xf01e000,0x780f8007,0xc01e0000,0x7e0fc0,0xf0000,0x3c0000,0x1c1c0003,0x87f0001f,0xf80003f,
3301       0xf8000000,0x0,0xf00,0x3c00,0x1c1c00,0x3e1f000,0xf00000,0x3c00001,0xc1c0003e,0x1f00003f,0xc0000e1f,0xc00003c0,0xf00,0x70700,
3302       0xe1fc00,0x7c3e000,0x0,0x0,0x78000001,0xe00000e0,0xe0001f0f,0x800003c0,0x1e0000,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3303       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,
3304       0x1f00,0x1e0,0x3c0,0x1e000,0x3c1c1e0,0x0,0x0,0x0,0x0,0x78007c0,0x1f001f9e,0x3c001,0xf010003e,0x7780,0x3c00000,0xf800000,0xf007,
3305       0xc01f007c,0x1f80000,0x0,0x0,0x0,0x0,0xe003e0,0x7fff00,0x1ef0003,0xc007e007,0xc00301e0,0x1fc003c0,0x1e00,0x7c00,0x301e0007,
3306       0x80007800,0x780,0x3c00fc00,0x7800001f,0xe00ff07f,0x1e00f8,0x3e00780,0x1fc03e00,0xf807801f,0xc01f001c,0xf000,0xf0003c0,0xf0000f0f,
3307       0x1e03,0xc00f8078,0x780000,0xf0003c,0xf000,0x1e0,0x1f3e0,0x0,0x78000,0x1,0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,
3308       0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1f,0xf0,0xf80,0x0,0x0,0xf80180,0x0,0x0,0x1e00000,
3309       0x0,0x0,0x0,0xe038,0x0,0x3e00380,0x0,0xfe0f0000,0x0,0xf0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xc0f00070,0x3b370003,0xe0000000,
3310       0x0,0x0,0x3e00,0x1e001e0,0x0,0x780000,0x180000,0x7c000000,0x780,0x780003c,0x3c00,0x0,0x7ffc0,0x780,0x0,0x0,0x3,0xffe00000,
3311       0x1c0,0x3c000000,0xe,0x38,0xf000,0x0,0x3ffe1c00,0x0,0x0,0x38000078,0xf000000,0x1c00,0xe000,0x7f000,0xf000,0x3de000,0x1ef0000,
3312       0xf780000,0x7bc00003,0xde00001e,0xf00003e7,0x80007c00,0x30078000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,
3313       0xe0001e03,0xfc00fe00,0x3c01f007,0xc00f803e,0x7c01f0,0x3e00f80,0x1f007c00,0x7,0xc01f80f0,0x3c0780,0x1e03c00,0xf01e000,0x78078007,
3314       0x801e0000,0x7803c0,0x78000,0x780000,0x380e0003,0x81e00000,0x1f,0xf0000000,0x0,0x780,0x7800,0x380e00,0x0,0x780000,0x7800003,
3315       0x80e00000,0x1ff,0x80000e07,0x800001e0,0x1e00,0xe0380,0xe07800,0x0,0x0,0x0,0x3c000003,0xc00001c0,0x70000000,0x780,0x1e0000,
3316       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3317       0x780000,0x3c1e0000,0x3c0e0007,0xfff01c07,0x1e00,0x1e0,0x780,0xf000,0x3e1c3e0,0x0,0x0,0x0,0x0,0xf0007c0,0x1f00181e,0x20000,
3318       0xf000001f,0xf780,0x3c00000,0x1f000000,0x1f00f,0x800f8078,0xf80000,0x0,0x0,0x0,0x0,0x8003e0,0x1fc0f80,0x1ef0003,0xc001e007,
3319       0x800101e0,0x7e003c0,0x1e00,0x7800,0x101e0007,0x80007800,0x780,0x3c00f800,0x7800001e,0xe00ef07f,0x801e00f0,0x1e00780,0x7c03c00,
3320       0x78078007,0xc01e0004,0xf000,0xf0003c0,0x78001e0f,0x1e03,0xe00f807c,0xf80000,0x1f0003c,0x7800,0x1e0,0x3e1f0,0x0,0x3c000,0x1,
3321       0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0,
3322       0x1e,0xf0,0x780,0x0,0x0,0x1f00080,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x1e03c,0x0,0x3c00080,0x0,0xf80f0000,0x0,0x1f0000,0x0,0x0,
3323       0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x3bf70003,0xe0000000,0x0,0x0,0x3e00,0x1f003e0,0x0,0x780000,0x180000,0x78000000,0x7c0,0xf80003c,
3324       0x3c00,0x0,0x1f01f0,0x780,0x0,0x0,0xf,0x80f80000,0x1c0,0x1c000000,0xe,0x38,0x1e000,0x0,0x7ffe1c00,0x0,0x0,0x380000f0,0x7800000,
3325       0x1c00,0xe000,0x7fc00,0xf000,0x3de000,0x1ef0000,0xf780000,0x7bc00003,0xde00001e,0xf00003c7,0x80007800,0x10078000,0x3c0000,
3326       0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x7e00ff00,0x3c01e003,0xc00f001e,0x7800f0,0x3c00780,0x1e003c00,
3327       0x7,0x800f00f0,0x3c0780,0x1e03c00,0xf01e000,0x7807c00f,0x801e0000,0xf803c0,0x3c000,0xf00000,0x780f0000,0x0,0x7,0xc0000000,
3328       0x0,0x3c0,0xf000,0x780f00,0x0,0x3c0000,0xf000007,0x80f00000,0x7ff,0xc0000000,0xf0,0x3c00,0x1e03c0,0x0,0x0,0x0,0x0,0x1e000007,
3329       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,
3330       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x3c1e001f,0xfff03803,0x80001e00,0x1e0,0x780,0xf000,0xf9cf80,
3331       0x0,0x0,0x0,0x0,0xf000780,0xf00001e,0x0,0xf800000f,0xe780,0x3c00000,0x1e000000,0x1e00f,0x78078,0x7c0000,0x0,0x0,0x0,0x0,0x1e0,
3332       0x3f003c0,0x1ef0003,0xc000f00f,0x800001e0,0x1f003c0,0x1e00,0xf000,0x1e0007,0x80007800,0x780,0x3c01f000,0x7800001e,0xe00ef07f,
3333       0x801e01f0,0x1e00780,0x3c07c00,0x78078003,0xc03e0000,0xf000,0xf0003c0,0x78001e0f,0x1e01,0xf01f003c,0xf00000,0x3e0003c,0x7800,
3334       0x1e0,0x7c0f8,0x0,0x0,0x1,0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,
3335       0x0,0x0,0x0,0x0,0x0,0x1e,0xf0,0x780,0x0,0x0,0x1e00000,0x0,0x0,0x3c00000,0x0,0x8,0x40,0x0,0x7e0000,0x7c00000,0x1,0xf00f0000,
3336       0x0,0x3e0000,0x0,0x3f,0xfc0,0xfc3f0,0xfc3f0,0x0,0x0,0x0,0x70,0x39e70000,0x0,0x0,0x0,0x0,0xf003c0,0x0,0x0,0x180000,0xf8000000,
3337       0x3c0,0xf00003c,0x3c00,0x0,0x3c0078,0x7ff80,0x0,0x0,0x1e,0x3c0000,0x1c0,0x1c000000,0xe,0xf0,0x0,0x0,0x7ffe1c00,0x0,0x0,0x380000f0,
3338       0x7800000,0x1c00,0xe000,0x3c00,0x0,0x3de000,0x1ef0000,0xf780000,0x7bc00003,0xde00001e,0xf00003c7,0x8000f800,0x78000,0x3c0000,
3339       0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x1f00ff00,0x3c03e003,0xc01f001e,0xf800f0,0x7c00780,0x3e003c00,
3340       0xf,0x800f80f0,0x3c0780,0x1e03c00,0xf01e000,0x7803c00f,0x1fffc0,0xf001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3341       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,
3342       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x781e003f,0xfff03803,
3343       0x80001e00,0x1e0,0xf80,0xf000,0x3dde00,0x0,0x0,0x0,0x0,0xf000f00,0x780001e,0x0,0x7800000f,0x1e780,0x3c00000,0x3e000000,0x3e00f,
3344       0x780f0,0x7c0000,0x0,0x0,0x0,0x0,0x1e0,0x7c001e0,0x3ef8003,0xc000f00f,0x1e0,0xf003c0,0x1e00,0xf000,0x1e0007,0x80007800,0x780,
3345       0x3c03e000,0x7800001e,0xf01ef07b,0xc01e01e0,0xf00780,0x3e07800,0x3c078003,0xe03c0000,0xf000,0xf0003c0,0x78001e0f,0x1e00,0xf01e003e,
3346       0x1f00000,0x3c0003c,0x7800,0x1e0,0x78078,0x0,0x0,0x1,0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,0x7800000,0x1e00000,
3347       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,
3348       0xe70000,0x7800000,0x1,0xe00f0000,0x0,0x3c0000,0x0,0x3f,0xfc0,0xfc1f0,0x1f83f0,0x0,0x0,0x0,0x70,0x39e70000,0x0,0x0,0x0,0x0,
3349       0xf807c0,0x0,0x0,0x180000,0xf0000000,0x3e0,0x1f00003c,0x3e00,0x0,0x70001c,0x3fff80,0x0,0x0,0x38,0xe0000,0x1c0,0x1c000078,
3350       0x1c,0x1fe0,0x0,0x0,0xfffe1c00,0x0,0x0,0x380000f0,0x7800000,0x1c00,0xe000,0xe00,0x0,0x7df000,0x3ef8000,0x1f7c0000,0xfbe00007,
3351       0xdf00003c,0x780003c7,0x8000f000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0xf00f780,
3352       0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,0xf,0xf80f0,0x3c0780,0x1e03c00,0xf01e000,0x7803e01f,0x1ffff8,0xf001e0,
3353       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,
3354       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3355       0x780000,0x3c1e0000,0x781e003e,0x30703803,0x80001e00,0x1e0,0xf00,0x7800,0xff800,0x1e0000,0x0,0x0,0x0,0x1e000f00,0x780001e,
3356       0x0,0x7800000f,0x3c780,0x3c00000,0x3c000000,0x3c00f,0x780f0,0x3c0000,0x0,0x0,0x2000000,0x800000,0x1e0,0x78000e0,0x3c78003,
3357       0xc000f01e,0x1e0,0xf803c0,0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c07c000,0x7800001e,0x701cf07b,0xc01e01e0,0xf00780,0x1e07800,
3358       0x3c078001,0xe03c0000,0xf000,0xf0003c0,0x7c003e0f,0x1e00,0xf83e001e,0x1e00000,0x7c0003c,0x3c00,0x1e0,0xf807c,0x0,0x0,0x1fe0001,
3359       0xe1fc0000,0x7f00003,0xf8780007,0xf000003c,0x7f0,0x783f0,0x0,0x0,0x7800000,0x1e00000,0x3e0f8000,0xfc00007,0xf8000007,0xf00001fc,
3360       0xf,0xc0003fc0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1e,0xf0,0x780,0x0,0x0,0x3c00000,0x0,0x0,0x3c00000,0x0,0x18,0xc0,0x0,0x1818000,
3361       0x7800000,0x1,0xe00f0000,0x0,0x7c0000,0x0,0x1f,0x80001f80,0x7c1f8,0x1f83e0,0x0,0x0,0x0,0x70,0x38c70007,0xf8000000,0x7f03,
3362       0xf0000000,0x0,0x780780,0x0,0x0,0xfe0000,0xf0000000,0x1e0,0x1e00003c,0x3f00,0x0,0xe07f0e,0x7fff80,0x0,0x0,0x70,0x70000,0x1c0,
3363       0x1c000078,0x3c,0x1fc0,0x0,0x0,0xfffe1c00,0x0,0x0,0x380000f0,0x7800000,0x1c00,0xe000,0xe00,0x0,0x78f000,0x3c78000,0x1e3c0000,
3364       0xf1e00007,0x8f00003c,0x78000787,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,
3365       0xf80f780,0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,0xf,0x1f80f0,0x3c0780,0x1e03c00,0xf01e000,0x7801e01e,0x1ffffc,
3366       0xf007e0,0x3fc000,0x1fe0000,0xff00000,0x7f800003,0xfc00001f,0xe0000fc0,0xfc00007f,0xfe0,0x7f00,0x3f800,0x1fc000,0x0,0x0,0x0,
3367       0x1,0xf000001f,0x80000ff0,0x7f80,0x3fc00,0x1fe000,0xff0000,0x1f80000,0x1fc1e000,0x0,0x0,0x0,0x0,0x1e1fc0,0x0,0x0,0x0,0x0,
3368       0x0,0x0,0x0,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,
3369       0x781c007c,0x30003803,0x80001f00,0x1e0,0xf00,0x7800,0x7f000,0x1e0000,0x0,0x0,0x0,0x1e000f00,0x780001e,0x0,0x7800000f,0x3c780,
3370       0x3c00000,0x3c000000,0x3c00f,0x780f0,0x3c0000,0x0,0x0,0x1e000000,0xf00000,0x3e0,0xf0000e0,0x3c78003,0xc000f01e,0x1e0,0x7803c0,
3371       0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c0f8000,0x7800001e,0x701cf079,0xe01e01e0,0xf00780,0x1e07800,0x3c078001,0xe03c0000,
3372       0xf000,0xf0003c0,0x3c003c0f,0x3e00,0x787c001f,0x3e00000,0xf80003c,0x3c00,0x1e0,0x1f003e,0x0,0x0,0x1fffc001,0xe7ff0000,0x3ffe000f,
3373       0xfe78003f,0xfc001fff,0xfe001ffc,0xf0078ffc,0x1ffc00,0x7ff000,0x7800f80,0x1e0000f,0x7f1fc01e,0x3ff0001f,0xfe00079f,0xfc0007ff,
3374       0x3c003c7f,0xf001fff8,0x1fffff0,0x3c003c0,0xf0000f1e,0xf1f,0x7c1f0,0x1f00ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x3c00000,0x100000,
3375       0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1818000,0x7800000,0x1,0xe00f0000,0x1000000,0xf80000,0x40000002,0xf,0x80001f00,0x7e0f8,0x1f07c0,
3376       0x0,0x0,0x0,0x70,0x38c7003f,0xff000000,0xff8f,0xf8000100,0xffffe,0x7c0f80,0x0,0x0,0x3ffc000,0xf0000020,0x1001f0,0x3c00003c,
3377       0x1f80,0x0,0x1c3ffc7,0x7c0780,0x0,0x0,0xe3,0xff038000,0xe0,0x38000078,0x78,0x1ff0,0x0,0x3c003c0,0xfffe1c00,0x0,0x0,0x380000f0,
3378       0x7800000,0x1c00,0xe000,0xe00,0xf000,0x78f000,0x3c78000,0x1e3c0000,0xf1e00007,0x8f00003c,0x78000787,0x8001e000,0x78000,0x3c0000,
3379       0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f3c0,0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,
3380       0x4000200f,0x3f80f0,0x3c0780,0x1e03c00,0xf01e000,0x7801f03e,0x1ffffe,0xf01fe0,0x3fff800,0x1fffc000,0xfffe0007,0xfff0003f,
3381       0xff8001ff,0xfc003ff3,0xfe0003ff,0xe0007ff8,0x3ffc0,0x1ffe00,0xfff000,0x3ff80001,0xffc0000f,0xfe00007f,0xf000003f,0xf8003c7f,
3382       0xe0003ffc,0x1ffe0,0xfff00,0x7ff800,0x3ffc000,0x1f80000,0xfff1c03c,0x3c01e0,0x1e00f00,0xf007800,0x781f0001,0xf01e7ff0,0x7c0007c,
3383       0x0,0x0,0x0,0x0,0x0,0x0,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,
3384       0x3c1e003f,0xfffff078,0x30003803,0x80000f00,0x1e0,0x1f00,0x7800,0x7f000,0x1e0000,0x0,0x0,0x0,0x3c000f00,0x780001e,0x0,0x7800000f,
3385       0x78780,0x3c00000,0x3c000000,0x7c00f,0x780f0,0x3c0007,0xe000003f,0x0,0xfe000000,0xfe0000,0x3c0,0x1f000070,0x7c7c003,0xc000f01e,
3386       0x1e0,0x7803c0,0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c1f0000,0x7800001e,0x783cf079,0xe01e03c0,0xf00780,0x1e0f000,0x3c078001,
3387       0xe03c0000,0xf000,0xf0003c0,0x3c003c07,0x81f03c00,0x7c7c000f,0x87c00000,0xf00003c,0x1e00,0x1e0,0x3e001f,0x0,0x0,0x3fffe001,
3388       0xefff8000,0x7fff001f,0xff78007f,0xfe001fff,0xfe003ffe,0xf0079ffe,0x1ffc00,0x7ff000,0x7801f00,0x1e0000f,0xffbfe01e,0x7ff8003f,
3389       0xff0007bf,0xfe000fff,0xbc003cff,0xf803fffc,0x1fffff0,0x3c003c0,0x78001e1e,0xf0f,0x800f80f0,0x1e00ff,0xffe0001e,0xf0,0x780,
3390       0x0,0x0,0x3c00000,0x380000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1008000,0x7800000,0x3,0xe00f0000,0x3800000,0xf00000,0xe0000007,
3391       0xf,0x80001f00,0x3e0f8,0x1e07c0,0x0,0x0,0x0,0x70,0x3807007f,0xff800000,0x1ffdf,0xfc000380,0xffffe,0x3e1f00,0x0,0x0,0xfffe000,
3392       0xf0000030,0x3800f8,0x7c00003c,0xfc0,0x0,0x18780c3,0xf00780,0x80100,0x0,0xc3,0xffc18000,0xf0,0x78000078,0xf0,0xf0,0x0,0x3c003c0,
3393       0xfffe1c00,0x0,0x0,0x380000f0,0x7800801,0x1c00,0xe000,0x1e00,0xf000,0xf8f800,0x7c7c000,0x3e3e0001,0xf1f0000f,0x8f80007c,0x7c000787,
3394       0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f3c0,0x3c078001,0xe03c000f,
3395       0x1e00078,0xf0003c0,0x78001e00,0xe000701f,0x3fc0f0,0x3c0780,0x1e03c00,0xf01e000,0x7800f87c,0x1e007f,0xf07e00,0x7fffc00,0x3fffe001,
3396       0xffff000f,0xfff8007f,0xffc003ff,0xfe007ff7,0xff0007ff,0xf000fffc,0x7ffe0,0x3fff00,0x1fff800,0x3ff80001,0xffc0000f,0xfe00007f,
3397       0xf00000ff,0xf8003cff,0xf0007ffe,0x3fff0,0x1fff80,0xfffc00,0x7ffe000,0x1f80001,0xfffb803c,0x3c01e0,0x1e00f00,0xf007800,0x780f0001,
3398       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,
3399       0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e003f,0xfffff078,0x30001c07,0xf80,0x1e0,0x1e00,0x3c00,0xff800,0x1e0000,0x0,0x0,0x0,0x3c001e00,
3400       0x3c0001e,0x0,0x7800001e,0x70780,0x3c00000,0x78000000,0x78007,0x800f00f0,0x3e0007,0xe000003f,0x3,0xfe000000,0xff8000,0x7c0,
3401       0x1e000070,0x783c003,0xc001f01e,0x1e0,0x7803c0,0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c3e0000,0x7800001e,0x3838f079,
3402       0xe01e03c0,0x780780,0x1e0f000,0x1e078001,0xe03c0000,0xf000,0xf0003c0,0x3c007c07,0x81f03c00,0x3ef80007,0x87800000,0x1f00003c,
3403       0x1e00,0x1e0,0x7c000f,0x80000000,0x0,0x3ffff001,0xffffc000,0xffff003f,0xff7800ff,0xff001fff,0xfe007ffe,0xf007bffe,0x1ffc00,
3404       0x7ff000,0x7803e00,0x1e0000f,0xffffe01e,0xfff8007f,0xff8007ff,0xff001fff,0xbc003dff,0xf807fffc,0x1fffff0,0x3c003c0,0x78001e0f,
3405       0x1e07,0xc01f00f0,0x1e00ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x7c00000,0x7c0000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1018000,0x7800000,
3406       0x3,0xc00f0000,0x7c00000,0x1f00001,0xf000000f,0x80000007,0xc0003e00,0x1e07c,0x3e0780,0x0,0x0,0x0,0x70,0x380700ff,0xff800000,
3407       0x3ffff,0xfe0007c0,0xffffe,0x1e1e00,0x0,0x780000,0x1fffe000,0xf0000078,0x7c0078,0x7800003c,0xff0,0x0,0x38e0003,0x80f00780,
3408       0x180300,0x0,0x1c3,0x81e1c000,0x7f,0xf0000078,0x1e0,0x38,0x0,0x3c003c0,0xfffe1c00,0x0,0x0,0x380000f0,0x7800c01,0x80001c00,
3409       0xe000,0x603e00,0xf000,0xf07800,0x783c000,0x3c1e0001,0xe0f0000f,0x7800078,0x3c000f87,0x8001e000,0x78000,0x3c0000,0x1e00000,
3410       0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f3c0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f01,0xf000f81e,
3411       0x7bc0f0,0x3c0780,0x1e03c00,0xf01e000,0x78007878,0x1e001f,0xf0f800,0x7fffe00,0x3ffff001,0xffff800f,0xfffc007f,0xffe003ff,
3412       0xff007fff,0xff800fff,0xf001fffe,0xffff0,0x7fff80,0x3fffc00,0x3ff80001,0xffc0000f,0xfe00007f,0xf00001ff,0xfc003dff,0xf000ffff,
3413       0x7fff8,0x3fffc0,0x1fffe00,0xffff000,0x1f80003,0xffff803c,0x3c01e0,0x1e00f00,0xf007800,0x780f0001,0xe01ffffc,0x3c00078,0x0,
3414       0x0,0x0,0x0,0x0,0x0,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,
3415       0x3c1e003f,0xfffff078,0x30001e0f,0x300780,0x1e0,0x1e00,0x3c00,0x3dde00,0x1e0000,0x0,0x0,0x0,0x78001e00,0x3c0001e,0x0,0xf800003e,
3416       0xf0780,0x3dfc000,0x783f8000,0xf8007,0xc01f00f0,0x3e0007,0xe000003f,0x1f,0xfc000000,0x7ff000,0xf80,0x3e007c70,0x783c003,0xc001e03c,
3417       0x1e0,0x3c03c0,0x1e00,0x3c000,0x1e0007,0x80007800,0x780,0x3c7c0000,0x7800001e,0x3878f078,0xf01e03c0,0x780780,0x1e0f000,0x1e078001,
3418       0xe03e0000,0xf000,0xf0003c0,0x1e007807,0x83f03c00,0x3ef00007,0xcf800000,0x3e00003c,0xf00,0x1e0,0xf80007,0xc0000000,0x0,0x3e01f801,
3419       0xfe07e001,0xf80f007e,0x7f801f8,0x1f801fff,0xfe00fc0f,0xf007f83f,0x1ffc00,0x7ff000,0x7807c00,0x1e0000f,0x87e1e01f,0xe0fc00fc,
3420       0xfc007f8,0x1f803f03,0xfc003df0,0x3807e03c,0x1fffff0,0x3c003c0,0x78003e0f,0x1e03,0xe03e00f8,0x3e00ff,0xffe0001e,0xf0,0x780,
3421       0x0,0x0,0x7800000,0xfe0000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1818000,0x7c00000,0x3,0xc00f0000,0xfe00000,0x3e00003,0xf800001f,
3422       0xc0000007,0xc0003e00,0x1e03c,0x3c0f80,0x0,0x0,0x0,0x70,0x380700fc,0x7800000,0x7c1fe,0x3e000fe0,0xffffe,0x1f3e00,0x0,0x780000,
3423       0x3f98e000,0xf000003c,0xfcf8007c,0xf800003c,0x3ffc,0x0,0x31c0001,0x80f00f80,0x380700,0x0,0x183,0x80e0c000,0x3f,0xe0000078,
3424       0x3c0,0x38,0x0,0x3c003c0,0xfffe1c00,0x0,0x0,0x38000078,0xf000e01,0xc003ffe0,0x1fff00,0x7ffc00,0xf000,0xf07800,0x783c000,0x3c1e0001,
3425       0xe0f0000f,0x7800078,0x3c000f07,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,
3426       0x3c0f1e0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0xf801f01e,0xf3c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78007cf8,
3427       0x1e000f,0x80f0f000,0x7c03f00,0x3e01f801,0xf00fc00f,0x807e007c,0x3f003e0,0x1f80707f,0x8f801f80,0xf003f03f,0x1f81f8,0xfc0fc0,
3428       0x7e07e00,0x3ff80001,0xffc0000f,0xfe00007f,0xf00003ff,0xfc003fc1,0xf801f81f,0x800fc0fc,0x7e07e0,0x3f03f00,0x1f81f800,0x1f80007,
3429       0xe07f003c,0x3c01e0,0x1e00f00,0xf007800,0x780f8003,0xe01fe07e,0x3e000f8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3430       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3f,0xfffff078,0x30000ffe,0x1f007c0,0x0,0x1e00,
3431       0x3c00,0xf9cf80,0x1e0000,0x0,0x0,0x0,0x78001e00,0x3c0001e,0x0,0xf00000fc,0x1e0780,0x3fff800,0x78ffe000,0xf0003,0xe03e00f0,
3432       0x3e0007,0xe000003f,0x7f,0xe01fffff,0xf00ffc00,0x1f80,0x3c01ff70,0x783c003,0xc007e03c,0x1e0,0x3c03c0,0x1e00,0x3c000,0x1e0007,
3433       0x80007800,0x780,0x3cfc0000,0x7800001e,0x3c78f078,0xf01e03c0,0x780780,0x3e0f000,0x1e078003,0xc01f0000,0xf000,0xf0003c0,0x1e007807,
3434       0x83f83c00,0x1ff00003,0xcf000000,0x3e00003c,0xf00,0x1e0,0x0,0x0,0x0,0x20007801,0xfc03e003,0xe003007c,0x3f803e0,0x7c0003c,
3435       0xf807,0xf007e00f,0x3c00,0xf000,0x780f800,0x1e0000f,0x87e1f01f,0x803c00f8,0x7c007f0,0xf803e01,0xfc003f80,0x80f8004,0x3c000,
3436       0x3c003c0,0x3c003c0f,0x1e03,0xe03e0078,0x3c0000,0x7c0001e,0xf0,0x780,0x0,0x0,0x3ffff800,0x1ff0000,0x0,0x7800000,0x0,0x18,
3437       0xc0,0x0,0x1818000,0x3e00000,0x3,0xc00f0000,0x1ff00000,0x3e00007,0xfc00003f,0xe0000003,0xc0003c00,0xf03c,0x3c0f00,0x0,0x0,
3438       0x0,0x70,0x380701f0,0x800000,0x780fc,0x1e001ff0,0x7c,0xf3c00,0x0,0x780000,0x7e182000,0xf000001f,0xfff00ffc,0xffc0003c,0x3cfe,
3439       0x0,0x31c0001,0x80f01f80,0x780f00,0x0,0x183,0x80e0c000,0xf,0x80000078,0x780,0x38,0x0,0x3c003c0,0x7ffe1c00,0x0,0x0,0x38000078,
3440       0xf000f01,0xe003ffe0,0x1fff00,0x7ff800,0xf000,0xf07800,0x783c000,0x3c1e0001,0xe0f0000f,0x78000f8,0x3e000f07,0x8003c000,0x78000,
3441       0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x3c0f1e0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,
3442       0x78000f00,0x7c03e01e,0x1e3c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78003cf0,0x1e0007,0x80f1e000,0x4000f00,0x20007801,0x3c008,
3443       0x1e0040,0xf00200,0x780403f,0x7803e00,0x3007c00f,0x803e007c,0x1f003e0,0xf801f00,0x780000,0x3c00000,0x1e000000,0xf00007f0,
3444       0x3e003f00,0x7801f00f,0x800f807c,0x7c03e0,0x3e01f00,0x1f00f800,0x1f80007,0xc03e003c,0x3c01e0,0x1e00f00,0xf007800,0x78078003,
3445       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,
3446       0x0,0x0,0x0,0x0,0x0,0x780000,0x0,0xf078007c,0x300007fc,0x7e00fe0,0x0,0x1e00,0x3c00,0x3e1c3e0,0x1e0000,0x0,0x0,0x0,0xf0001e00,
3447       0x3c0001e,0x1,0xf000fff8,0x1e0780,0x3fffe00,0x79fff000,0x1f0001,0xfffc00f0,0x7e0007,0xe000003f,0x3ff,0x801fffff,0xf003ff80,
3448       0x3f00,0x3c03fff0,0xf01e003,0xffffc03c,0x1e0,0x3c03ff,0xffc01fff,0xfe03c000,0x1fffff,0x80007800,0x780,0x3df80000,0x7800001e,
3449       0x1c70f078,0x781e03c0,0x780780,0x3c0f000,0x1e078007,0xc01f8000,0xf000,0xf0003c0,0x1e007807,0x83f83c00,0xfe00003,0xff000000,
3450       0x7c00003c,0x780,0x1e0,0x0,0x0,0x0,0x7c01,0xf801f007,0xc00100f8,0x1f803c0,0x3c0003c,0x1f003,0xf007c00f,0x80003c00,0xf000,
3451       0x783f000,0x1e0000f,0x3c0f01f,0x3e01f0,0x3e007e0,0x7c07c00,0xfc003f00,0xf0000,0x3c000,0x3c003c0,0x3c003c0f,0x1e01,0xf07c007c,
3452       0x7c0000,0xfc0001e,0xf0,0x780,0x0,0x0,0x3ffff000,0x3838000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0xff0000,0x3f00000,0x3,0xc00fff00,
3453       0x38380000,0x7c0000e,0xe000070,0x70000001,0xe0003c00,0xf01e,0x780e00,0x0,0x0,0x0,0x0,0x1e0,0x0,0x780f8,0xf003838,0xfc,0xffc00,
3454       0x0,0x780000,0x7c180000,0xf000000f,0xffe00fff,0xffc0003c,0x783f,0x80000000,0x6380000,0xc0f83f80,0xf81f00,0x0,0x303,0x80e06000,
3455       0x0,0x78,0xf00,0x78,0x0,0x3c003c0,0x7ffe1c00,0x0,0x0,0x3800003c,0x3e000f81,0xf003ffe0,0x1fff00,0x1fc000,0xf000,0x1e03c00,
3456       0xf01e000,0x780f0003,0xc078001e,0x3c000f0,0x1e000f07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,0xfffff00,0xf00000,0x7800000,
3457       0x3c000001,0xe0001e00,0x3c0f0f0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x3e07c01e,0x1e3c0f0,0x3c0780,0x1e03c00,
3458       0xf01e000,0x78003ff0,0x1e0007,0x80f1e000,0xf80,0x7c00,0x3e000,0x1f0000,0xf80000,0x7c0001e,0x3c07c00,0x10078007,0x803c003c,
3459       0x1e001e0,0xf000f00,0x780000,0x3c00000,0x1e000000,0xf00007c0,0x1e003e00,0x7c03e007,0xc01f003e,0xf801f0,0x7c00f80,0x3e007c00,
3460       0xf,0x801f003c,0x3c01e0,0x1e00f00,0xf007800,0x7807c007,0xc01f801f,0x1f001f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3461       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x0,0xe078003c,0x300001f0,0x3f801ff0,0x0,
3462       0x3c00,0x1e00,0x3c1c1e0,0x1e0000,0x0,0x0,0x0,0xf0001e0f,0x3c0001e,0x3,0xe000fff0,0x3c0780,0x3ffff00,0x7bfff800,0x1e0000,0x7ff00078,
3463       0x7e0007,0xe000003f,0x1ffc,0x1fffff,0xf0007ff0,0x7e00,0x3c07c3f0,0xf01e003,0xffff003c,0x1e0,0x3c03ff,0xffc01fff,0xfe03c000,
3464       0x1fffff,0x80007800,0x780,0x3ffc0000,0x7800001e,0x1ef0f078,0x781e03c0,0x780780,0x7c0f000,0x1e07801f,0x800ff000,0xf000,0xf0003c0,
3465       0xf00f807,0x83b83c00,0xfc00001,0xfe000000,0xf800003c,0x780,0x1e0,0x0,0x0,0x0,0x3c01,0xf000f007,0xc00000f0,0xf80780,0x3c0003c,
3466       0x1e001,0xf007c007,0x80003c00,0xf000,0x787e000,0x1e0000f,0x3c0f01f,0x1e01e0,0x1e007c0,0x3c07800,0x7c003f00,0xf0000,0x3c000,
3467       0x3c003c0,0x3e007c07,0x80003c00,0xf8f8003c,0x780000,0xf80001e,0xf0,0x780,0x0,0x0,0x7ffff000,0x601c000,0x3,0xffff0000,0x0,
3468       0xfff,0xf8007fff,0xc0000000,0x7e003c,0x1fe0000,0xc0003,0xc00fff00,0x601c0000,0xf800018,0x70000c0,0x38000001,0xe0007800,0x701e,
3469       0x701e00,0x0,0x0,0x0,0x0,0x1e0,0x6,0x700f8,0xf00601c,0xf8,0x7f800,0x0,0x780000,0xf8180000,0xf000000f,0x87c00fff,0xffc0003c,
3470       0xf01f,0xc0000000,0x6380000,0xc07ff780,0x1f03e03,0xfffffe00,0x303,0x81c06000,0x0,0x1ffff,0xfe001e00,0x180f8,0x0,0x3c003c0,
3471       0x3ffe1c00,0x3f00000,0x0,0x3800003f,0xfe0007c0,0xf8000000,0x18000000,0xc0000006,0x1f000,0x1e03c00,0xf01e000,0x780f0003,0xc078001e,
3472       0x3c000f0,0x1e001f07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,0xfffff00,0xf00000,0x7800000,0x3c000001,0xe000fff8,0x3c0f0f0,
3473       0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x1f0f801e,0x3c3c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78001fe0,0x1e0007,
3474       0x80f1e000,0x780,0x3c00,0x1e000,0xf0000,0x780000,0x3c0001e,0x3c07c00,0xf0007,0x8078003c,0x3c001e0,0x1e000f00,0x780000,0x3c00000,
3475       0x1e000000,0xf0000f80,0x1f003e00,0x3c03c003,0xc01e001e,0xf000f0,0x7800780,0x3c003c00,0xf,0x3f003c,0x3c01e0,0x1e00f00,0xf007800,
3476       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,
3477       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1,0xe078003f,0xb0000000,0xfc003cf0,0x0,0x3c00,0x1e00,0x101c040,0x1e0000,0x0,0x0,0x1,
3478       0xe0001e1f,0x83c0001e,0x7,0xe000fff0,0x3c0780,0x3c03f80,0x7fc0fc00,0x1e0000,0xfff80078,0xfe0007,0xe000003f,0x7fe0,0x1fffff,
3479       0xf0000ffc,0xfc00,0x780f81f0,0xf01e003,0xffff003c,0x1e0,0x3c03ff,0xffc01fff,0xfe03c000,0x1fffff,0x80007800,0x780,0x3ffc0000,
3480       0x7800001e,0x1ef0f078,0x3c1e03c0,0x780780,0x1fc0f000,0x1e07ffff,0x7ff00,0xf000,0xf0003c0,0xf00f007,0xc3b87c00,0x7c00001,0xfe000000,
3481       0xf800003c,0x3c0,0x1e0,0x0,0x0,0x0,0x3c01,0xf000f007,0x800000f0,0xf80780,0x1e0003c,0x1e001,0xf0078007,0x80003c00,0xf000,0x78fc000,
3482       0x1e0000f,0x3c0f01e,0x1e01e0,0x1e007c0,0x3c07800,0x7c003e00,0xf0000,0x3c000,0x3c003c0,0x1e007807,0x80003c00,0x7df0003c,0x780000,
3483       0x1f00001e,0xf0,0x780,0x0,0x0,0x7800000,0xe7ce000,0x3,0xffff0000,0x0,0xfff,0xf8007fff,0xc0000000,0x1f0,0xffe000,0x1c0003,
3484       0xc00fff00,0xe7ce0000,0xf800039,0xf38001cf,0x9c000000,0xe0007800,0x780e,0x701c00,0x0,0x0,0x0,0x0,0x1e0,0x7,0xf0078,0xf00e7ce,
3485       0x1f0,0x7f800,0x0,0x780000,0xf0180000,0xf000000e,0x1c0001f,0xe000003c,0xf007,0xe0000000,0x6380000,0xc03fe780,0x3e07c03,0xfffffe00,
3486       0x303,0xffc06000,0x0,0x1ffff,0xfe003ffe,0x1fff0,0x0,0x3c003c0,0x1ffe1c00,0x3f00000,0x7,0xffc0001f,0xfc0003e0,0x7c000001,0xfc00000f,
3487       0xe000007f,0x1e000,0x1e03c00,0xf01e000,0x780f0003,0xc078001e,0x3c000f0,0x1e001e07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,
3488       0xfffff00,0xf00000,0x7800000,0x3c000001,0xe000fff8,0x3c0f078,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0xf9f001e,
3489       0x783c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78001fe0,0x1e0007,0x80f1e000,0x780,0x3c00,0x1e000,0xf0000,0x780000,0x3c0001e,0x3c07800,
3490       0xf0003,0xc078001e,0x3c000f0,0x1e000780,0x780000,0x3c00000,0x1e000000,0xf0000f00,0xf003c00,0x3c03c003,0xc01e001e,0xf000f0,
3491       0x7800780,0x3c003c00,0xf,0x7f003c,0x3c01e0,0x1e00f00,0xf007800,0x7803c007,0x801f000f,0xf001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3492       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,
3493       0xf0007cf8,0x7800000,0x3c00,0x1e00,0x1c000,0x1e0000,0x0,0x0,0x1,0xe0001e1f,0x83c0001e,0xf,0xc000fff8,0x780780,0x2000f80,0x7f803e00,
3494       0x3e0003,0xfffe007c,0x1fe0000,0x0,0x3ff00,0x0,0x1ff,0x8001f000,0x780f00f0,0x1f00f003,0xffffc03c,0x1e0,0x3c03ff,0xffc01fff,
3495       0xfe03c00f,0xf81fffff,0x80007800,0x780,0x3ffe0000,0x7800001e,0xee0f078,0x3c1e03c0,0x7807ff,0xff80f000,0x1e07fffe,0x3ffe0,
3496       0xf000,0xf0003c0,0xf00f003,0xc7bc7800,0xfc00000,0xfc000001,0xf000003c,0x3c0,0x1e0,0x0,0x0,0x0,0x3c01,0xe000f80f,0x800001e0,
3497       0xf80f00,0x1e0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x79f8000,0x1e0000f,0x3c0f01e,0x1e03c0,0x1f00780,0x3e0f000,0x7c003e00,
3498       0xf0000,0x3c000,0x3c003c0,0x1e007807,0x81e03c00,0x7df0003e,0xf80000,0x3e00003e,0xf0,0x7c0,0xfc000,0x80000000,0x7800000,0x1e7cf000,
3499       0x3,0xffff0000,0x0,0x18,0xc0,0x0,0xf80,0x7ffc00,0x380003,0xc00fff01,0xe7cf0000,0x1f000079,0xf3c003cf,0x9e000000,0xe0007000,
3500       0x380e,0xe01c00,0x0,0x0,0x0,0x0,0x1e0,0x3,0x800f0078,0xf01e7cf,0x3e0,0x3f000,0x0,0x780000,0xf018001f,0xfff8001e,0x1e0000f,
3501       0xc000003c,0xf003,0xe0000000,0x6380000,0xc00fc780,0x7c0f803,0xfffffe00,0x303,0xfe006000,0x0,0x1ffff,0xfe003ffe,0x1ffe0,0x0,
3502       0x3c003c0,0xffe1c00,0x3f00000,0x7,0xffc00007,0xf00001f0,0x3e00001f,0xfc0000ff,0xe00007ff,0x3e000,0x3e01e00,0x1f00f000,0xf8078007,
3503       0xc03c003e,0x1e001e0,0xf001e07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,0xfffff00,0xf00000,0x7800000,0x3c000001,0xe000fff8,
3504       0x3c0f078,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x7fe001e,0xf03c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78000fc0,
3505       0x1e0007,0x80f1f000,0x780,0x3c00,0x1e000,0xf0000,0x780000,0x3c0001e,0x3c0f800,0x1e0003,0xc0f0001e,0x78000f0,0x3c000780,0x780000,
3506       0x3c00000,0x1e000000,0xf0000f00,0xf003c00,0x3c078003,0xe03c001f,0x1e000f8,0xf0007c0,0x78003e00,0x1e,0xf7803c,0x3c01e0,0x1e00f00,
3507       0xf007800,0x7803e00f,0x801e000f,0x80f803e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3508       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1,0xe0f0000f,0xff00001f,0x8000f87c,0x7800000,0x3c00,0x1e00,0x1c000,0x7fffff80,
3509       0x0,0x0,0x3,0xc0001e1f,0x83c0001e,0x1f,0x800000fe,0xf00780,0x7c0,0x7f001e00,0x3c0007,0xe03f003f,0x3fe0000,0x0,0x3fc00,0x0,
3510       0x7f,0x8001e000,0x781f00f0,0x1e00f003,0xc007e03c,0x1e0,0x3c03c0,0x1e00,0x3c00f,0xf81e0007,0x80007800,0x780,0x3f9f0000,0x7800001e,
3511       0xfe0f078,0x3c1e03c0,0x7807ff,0xff00f000,0x1e07fff8,0xfff8,0xf000,0xf0003c0,0xf81f003,0xc7bc7800,0xfe00000,0x78000003,0xe000003c,
3512       0x1e0,0x1e0,0x0,0x0,0x0,0x1fffc01,0xe000780f,0x1e0,0x780f00,0x1e0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x7bf0000,0x1e0000f,
3513       0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0xf8000,0x3c000,0x3c003c0,0x1f00f807,0x81f03c00,0x3fe0001e,0xf00000,0x7c00007c,
3514       0xf0,0x3e0,0x3ff801,0x80000000,0x7800000,0x3cfcf800,0x3,0xffff0000,0x0,0x18,0xc0,0x0,0x7c00,0x1fff00,0x700003,0xc00f0003,
3515       0xcfcf8000,0x3e0000f3,0xf3e0079f,0x9f000000,0xf000,0x1000,0x0,0x0,0x0,0x0,0x0,0x1f0,0x1,0xc00f0078,0xf03cfcf,0x800007c0,0x1e000,
3516       0x0,0x780001,0xe018001f,0xfff8001c,0xe00007,0x8000003c,0xf001,0xf0000000,0x6380000,0xc0000000,0xf81f003,0xfffffe00,0x303,
3517       0x87006000,0x0,0x1ffff,0xfe003ffe,0x7f00,0x0,0x3c003c0,0x3fe1c00,0x3f00000,0x7,0xffc00000,0xf8,0x1f0001ff,0xf0000fff,0x80007ffc,
3518       0xfc000,0x3c01e00,0x1e00f000,0xf0078007,0x803c003c,0x1e001e0,0xf001e07,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,
3519       0x7800000,0x3c000001,0xe000fff8,0x3c0f078,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x3fc001e,0x1e03c0f0,0x3c0780,
3520       0x1e03c00,0xf01e000,0x78000780,0x1e0007,0x80f0fc00,0x3fff80,0x1fffc00,0xfffe000,0x7fff0003,0xfff8001f,0xffc0001e,0x3c0f000,
3521       0x1e0003,0xc0f0001e,0x78000f0,0x3c000780,0x780000,0x3c00000,0x1e000000,0xf0001e00,0xf803c00,0x3c078001,0xe03c000f,0x1e00078,
3522       0xf0003c0,0x78001e07,0xfffffe1e,0x1e7803c,0x3c01e0,0x1e00f00,0xf007800,0x7801e00f,0x1e0007,0x807803c0,0x0,0x0,0x0,0x0,0x0,
3523       0x0,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,
3524       0xffc0007e,0xf03e,0x7800000,0x3c00,0x1e00,0x1c000,0x7fffff80,0x0,0x0,0x3,0xc0001e1f,0x83c0001e,0x3f,0x3e,0xf00780,0x3c0,0x7e001e00,
3525       0x7c000f,0x800f001f,0xffde0000,0x0,0x3e000,0x0,0xf,0x8003e000,0x781e0070,0x1e00f003,0xc001f03c,0x1e0,0x3c03c0,0x1e00,0x3c00f,
3526       0xf81e0007,0x80007800,0x780,0x3f1f0000,0x7800001e,0x7c0f078,0x1e1e03c0,0x7807ff,0xfc00f000,0x1e07fffe,0xffc,0xf000,0xf0003c0,
3527       0x781e003,0xc71c7800,0x1ff00000,0x78000003,0xe000003c,0x1e0,0x1e0,0x0,0x0,0x0,0xffffc01,0xe000780f,0x1e0,0x780fff,0xffe0003c,
3528       0x3c000,0xf0078007,0x80003c00,0xf000,0x7ff0000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x7f000,0x3c000,
3529       0x3c003c0,0xf00f007,0xc1f07c00,0x1fc0001f,0x1f00000,0xfc000ff8,0xf0,0x1ff,0xfffe07,0x80000000,0x7800000,0x7ffcfc00,0x0,0xf000000,
3530       0x0,0x18,0xc0,0x0,0x3e000,0x1ff80,0xe00003,0xc00f0007,0xffcfc000,0x3e0001ff,0xf3f00fff,0x9f800000,0x6000,0x0,0x0,0x7c000,
3531       0x0,0x0,0x0,0xfe,0x0,0xe00f007f,0xff07ffcf,0xc0000fc0,0x1e000,0x0,0x780001,0xe018001f,0xfff8001c,0xe00007,0x80000000,0xf800,
3532       0xf0000000,0x6380000,0xc0000000,0x1f03c000,0x1e00,0x303,0x83806000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xfe1c00,0x3f00000,0x0,
3533       0x0,0x3c,0xf801fff,0xfff8,0x7ffc0,0x1f8000,0x3c01e00,0x1e00f000,0xf0078007,0x803c003c,0x1e001e0,0xf003c07,0x8003c000,0x78000,
3534       0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x3c0f03c,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,
3535       0x78000f00,0x1f8001e,0x1e03c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e000f,0x80f0ff00,0x1ffff80,0xffffc00,0x7fffe003,
3536       0xffff001f,0xfff800ff,0xffc007ff,0xffc0f000,0x1fffff,0xc0fffffe,0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,0xf0001e00,
3537       0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e07,0xfffffe1e,0x3c7803c,0x3c01e0,0x1e00f00,0xf007800,0x7801f01f,
3538       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,
3539       0x0,0x0,0x0,0x0,0x780000,0x3,0xc0f00000,0xfff003f0,0x1f00f03e,0x7800000,0x3c00,0x1e00,0x1c000,0x7fffff80,0x0,0x7ff80000,0x3,
3540       0xc0001e0f,0x3c0001e,0x7e,0x1f,0x1e00780,0x3e0,0x7e000f00,0x78000f,0x7800f,0xff9e0000,0x0,0x3fc00,0x0,0x7f,0x8003c000,0x781e0070,
3541       0x3e00f803,0xc000f03c,0x1e0,0x3c03c0,0x1e00,0x3c00f,0xf81e0007,0x80007800,0x780,0x3e0f8000,0x7800001e,0x7c0f078,0x1e1e03c0,
3542       0x7807ff,0xf000f000,0x1e07807f,0xfe,0xf000,0xf0003c0,0x781e003,0xc71c7800,0x3ef00000,0x78000007,0xc000003c,0x1e0,0x1e0,0x0,
3543       0x0,0x0,0x1ffffc01,0xe000780f,0x1e0,0x780fff,0xffe0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x7ff0000,0x1e0000f,0x3c0f01e,
3544       0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x7ff80,0x3c000,0x3c003c0,0xf00f003,0xc1f07800,0x1fc0000f,0x1e00000,0xf8000ff0,0xf0,
3545       0xff,0xffffff,0x80000000,0x3fffc000,0xfff9fe00,0x0,0xf000000,0x0,0x18,0xc0,0x0,0x1f0000,0x1fc0,0x1c00003,0xc00f000f,0xff9fe000,
3546       0x7c0003ff,0xe7f81fff,0x3fc00000,0x0,0x0,0x0,0xfe000,0x1ffffc0f,0xfffffc00,0x0,0xff,0xf0000000,0x700f007f,0xff0fff9f,0xe0000f80,
3547       0x1e000,0x0,0x780001,0xe018001f,0xfff8001c,0xe00fff,0xffc00000,0xf800,0xf0000000,0x6380000,0xc0ffff80,0x3e078000,0x1e00,0x7ff80303,
3548       0x83c06000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x3f00000,0x0,0x7f,0xff00001e,0x7c1fff0,0xfff80,0x7ffc00,0x3f0000,0x7c01f00,
3549       0x3e00f801,0xf007c00f,0x803e007c,0x1f003e0,0xf803c07,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,
3550       0xe0001e00,0x3c0f03c,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x1f8001e,0x3c03c0f0,0x3c0780,0x1e03c00,0xf01e000,
3551       0x78000780,0x1e001f,0xf07f80,0x3ffff80,0x1ffffc00,0xffffe007,0xffff003f,0xfff801ff,0xffc03fff,0xffc0f000,0x1fffff,0xc0fffffe,
3552       0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e07,
3553       0xfffffe1e,0x787803c,0x3c01e0,0x1e00f00,0xf007800,0x7800f01e,0x1e0007,0x803c0780,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3554       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,
3555       0x7800000,0x3c00,0x1e00,0x0,0x7fffff80,0x0,0x7ff80000,0x7,0x80001e00,0x3c0001e,0xfc,0xf,0x1e00780,0x1e0,0x7c000f00,0x78000f,
3556       0x78007,0xff1e0000,0x0,0x3ff00,0x0,0x1ff,0x8003c000,0x781e0070,0x3c007803,0xc000f03c,0x1e0,0x3c03c0,0x1e00,0x3c000,0x781e0007,
3557       0x80007800,0x780,0x3c07c000,0x7800001e,0x7c0f078,0xf1e03c0,0x780780,0xf000,0x1e07801f,0x3e,0xf000,0xf0003c0,0x781e003,0xcf1c7800,
3558       0x3cf80000,0x7800000f,0x8000003c,0xf0,0x1e0,0x0,0x0,0x0,0x3ffffc01,0xe000780f,0x1e0,0x780fff,0xffe0003c,0x3c000,0xf0078007,
3559       0x80003c00,0xf000,0x7ff8000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x3fff0,0x3c000,0x3c003c0,0xf81f003,
3560       0xc3b87800,0xf80000f,0x1e00001,0xf0000ff0,0xf0,0xff,0xf03fff,0x80000000,0x3fff8001,0xfff1ff00,0x0,0xf000000,0x0,0x18,0xc0,
3561       0x0,0x380000,0x7c0,0x3c00003,0xc00f001f,0xff1ff000,0xf80007ff,0xc7fc3ffe,0x3fe00000,0x0,0x0,0x0,0x1ff000,0x7ffffe1f,0xffffff00,
3562       0x0,0x7f,0xfe000000,0x780f007f,0xff1fff1f,0xf0001f00,0x1e000,0x0,0x780001,0xe0180000,0xf000001c,0xe00fff,0xffc00000,0x7c00,
3563       0xf0000000,0x31c0001,0x80ffff80,0x3e078000,0x1e00,0x7ff80183,0x81c0c000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x3f00000,
3564       0x0,0x7f,0xff00001e,0x7c7ff03,0xc03ff8fe,0x1ffc0f0,0x7e0000,0x7800f00,0x3c007801,0xe003c00f,0x1e0078,0xf003c0,0x7803c07,0x8003c000,
3565       0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x3c0f01e,0x3c078000,0xf03c0007,0x81e0003c,
3566       0xf0001e0,0x78000f00,0x3fc001e,0x7803c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e007f,0xf03fe0,0x7ffff80,0x3ffffc01,
3567       0xffffe00f,0xffff007f,0xfff803ff,0xffc07fff,0xffc0f000,0x1fffff,0xc0fffffe,0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,
3568       0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e07,0xfffffe1e,0x707803c,0x3c01e0,0x1e00f00,0xf007800,
3569       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,
3570       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1ff,0xffff8000,0x30f81f00,0xffe1e00f,0x87800000,0x3c00,0x1e00,0x0,0x1e0000,0x0,0x7ff80000,
3571       0x7,0x80001e00,0x3c0001e,0x1f8,0x7,0x83c00780,0x1e0,0x7c000f00,0xf8001e,0x3c001,0xfc1e0000,0x0,0x7fe0,0x0,0xffc,0x3c000,0x781e0070,
3572       0x3ffff803,0xc000783c,0x1e0,0x3c03c0,0x1e00,0x3c000,0x781e0007,0x80007800,0x780,0x3c07c000,0x7800001e,0x380f078,0xf1e03c0,
3573       0x780780,0xf000,0x1e07800f,0x8000001e,0xf000,0xf0003c0,0x3c3c003,0xcf1e7800,0x7c780000,0x7800000f,0x8000003c,0xf0,0x1e0,0x0,
3574       0x0,0x0,0x7f003c01,0xe000780f,0x1e0,0x780fff,0xffe0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x7f7c000,0x1e0000f,0x3c0f01e,
3575       0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0xfff8,0x3c000,0x3c003c0,0x781e003,0xc3b87800,0x1fc00007,0x83e00003,0xe0000ff8,0xf0,
3576       0x1ff,0xc007fe,0x0,0x7fff8001,0xffe3ff00,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x0,0x3c0,0x7800003,0xc00f001f,0xfe3ff000,0xf80007ff,
3577       0x8ffc3ffc,0x7fe00000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x1f,0xff000000,0x3c0f007f,0xff1ffe3f,0xf0003e00,0x1e000,0x0,0x780001,
3578       0xe0180000,0xf000001e,0x1e00fff,0xffc00000,0x3f00,0xf0000000,0x31c0001,0x80ffff80,0x1f03c000,0x1e00,0x7ff80183,0x81c0c000,
3579       0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x7f,0xff00003c,0xf87f007,0xc03f83ff,0x81fc01f0,0x7c0000,0x7ffff00,0x3ffff801,
3580       0xffffc00f,0xfffe007f,0xfff003ff,0xff807fff,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,
3581       0xe0001e00,0x3c0f01e,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x7fe001e,0xf003c0f0,0x3c0780,0x1e03c00,0xf01e000,
3582       0x78000780,0x1ffffe,0xf00ff0,0xfe00780,0x7f003c03,0xf801e01f,0xc00f00fe,0x7807f0,0x3c0ffff,0xffc0f000,0x1fffff,0xc0fffffe,
3583       0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00,
3584       0x1e,0xf07803c,0x3c01e0,0x1e00f00,0xf007800,0x7800783e,0x1e0007,0x801e0f80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3585       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1ff,0xffff8000,0x307c0801,0xe1f1e00f,0x87000000,
3586       0x3c00,0x1e00,0x0,0x1e0000,0x0,0x7ff80000,0xf,0x1e00,0x3c0001e,0x3f0,0x7,0x83fffffc,0x1e0,0x7c000f00,0xf0001e,0x3c000,0x3e0000,
3587       0x0,0x1ffc,0x1fffff,0xf0007ff0,0x3c000,0x781e0070,0x7ffffc03,0xc000781e,0x1e0,0x7803c0,0x1e00,0x3c000,0x781e0007,0x80007800,
3588       0x780,0x3c03e000,0x7800001e,0xf078,0x79e03c0,0x780780,0xf000,0x1e078007,0x8000000f,0xf000,0xf0003c0,0x3c3c001,0xee0ef000,
3589       0xf87c0000,0x7800001f,0x3c,0x78,0x1e0,0x0,0x0,0x0,0x7c003c01,0xe000780f,0x1e0,0x780f00,0x3c,0x3c000,0xf0078007,0x80003c00,
3590       0xf000,0x7e3e000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x1ffc,0x3c000,0x3c003c0,0x781e003,0xe3b8f800,
3591       0x1fc00007,0x83c00007,0xc00000fc,0xf0,0x3e0,0x8001f8,0x0,0x7800000,0xffc7fe00,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x0,0x1e0,
3592       0xf000003,0xc00f000f,0xfc7fe001,0xf00003ff,0x1ff81ff8,0xffc00000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x3,0xff800000,0x1e0f0078,
3593       0xffc7f,0xe0007c00,0x1e000,0x0,0x780001,0xe0180000,0xf000000e,0x1c00007,0x80000000,0x1f81,0xe0000000,0x38e0003,0x80000000,
3594       0xf81f000,0x1e00,0x7ff801c3,0x80e1c000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0xf8,0x1f070007,0xc03803ff,0xc1c001f0,
3595       0xf80000,0xfffff00,0x7ffff803,0xffffc01f,0xfffe00ff,0xfff007ff,0xffc07fff,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,
3596       0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f00f,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0xf9f001e,0xf003c0f0,
3597       0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1ffffc,0xf003f8,0xf800780,0x7c003c03,0xe001e01f,0xf00f8,0x7807c0,0x3c0fc1e,0xf000,
3598       0x1e0000,0xf00000,0x7800000,0x3c000000,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,
3599       0xf0003c0,0x78001e00,0x1e,0x1e07803c,0x3c01e0,0x1e00f00,0xf007800,0x7800783c,0x1e0007,0x801e0f00,0x0,0x0,0x0,0x0,0x0,0x0,
3600       0x0,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,
3601       0xc071e007,0xcf000000,0x3c00,0x1e00,0x0,0x1e0000,0x0,0x0,0xf,0xf00,0x780001e,0x7e0,0x7,0x83fffffc,0x1e0,0x7c000f00,0x1f0001e,
3602       0x3c000,0x3c0000,0x0,0x3ff,0x801fffff,0xf003ff80,0x3c000,0x781e0070,0x7ffffc03,0xc000781e,0x1e0,0x7803c0,0x1e00,0x1e000,0x781e0007,
3603       0x80007800,0x780,0x3c01f000,0x7800001e,0xf078,0x79e03c0,0xf00780,0xf000,0x3e078007,0xc000000f,0xf000,0xf0003c0,0x3c3c001,
3604       0xee0ef000,0xf03e0000,0x7800003e,0x3c,0x78,0x1e0,0x0,0x0,0x0,0xf8003c01,0xe000780f,0x1e0,0x780f00,0x3c,0x3c000,0xf0078007,
3605       0x80003c00,0xf000,0x7c3e000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0xfc,0x3c000,0x3c003c0,0x3c3e001,0xe7b8f000,
3606       0x3fe00007,0xc7c0000f,0xc000003e,0xf0,0x7c0,0x0,0x0,0x7c00000,0x7fcffc00,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x0,0x1e0,0x1e000003,
3607       0xc00f0007,0xfcffc003,0xe00001ff,0x3ff00ff9,0xff800000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x0,0x1f800000,0xf0f0078,0x7fcff,
3608       0xc000fc00,0x1e000,0x0,0x780001,0xe0180000,0xf000000f,0x87c00007,0x80000000,0xfe3,0xe0000000,0x18780c3,0x0,0x7c0f800,0x1e00,
3609       0xc3,0x80e18000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0x1f0,0x3e00000f,0xc0000303,0xe00003f0,0xf00000,0xfffff80,
3610       0x7ffffc03,0xffffe01f,0xffff00ff,0xfff807ff,0xffc07fff,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,
3611       0x3c000001,0xe0001e00,0x780f00f,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00,0x1f0f801f,0xe00780f0,0x3c0780,0x1e03c00,
3612       0xf01e000,0x78000780,0x1ffff8,0xf000f8,0x1f000780,0xf8003c07,0xc001e03e,0xf01f0,0x780f80,0x3c1f01e,0xf000,0x1e0000,0xf00000,
3613       0x7800000,0x3c000000,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00,
3614       0x1e,0x3c07803c,0x3c01e0,0x1e00f00,0xf007800,0x78007c7c,0x1e0007,0x801f1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3615       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x81c00000,0x303c0003,0x8039e003,0xef000000,
3616       0x3c00,0x1e00,0x0,0x1e0000,0x0,0x0,0x1e,0xf00,0x780001e,0xfc0,0x7,0x83fffffc,0x1e0,0x3c000f00,0x1e0001e,0x3c000,0x3c0000,
3617       0x0,0x7f,0xe01fffff,0xf00ffc00,0x3c000,0x781f00f0,0x7ffffc03,0xc000781e,0x1e0,0x7803c0,0x1e00,0x1e000,0x781e0007,0x80007800,
3618       0x780,0x3c01f000,0x7800001e,0xf078,0x7de01e0,0xf00780,0x7800,0x3c078003,0xc000000f,0xf000,0xf0003c0,0x3e7c001,0xee0ef001,
3619       0xf01e0000,0x7800003e,0x3c,0x3c,0x1e0,0x0,0x0,0x0,0xf0003c01,0xe000780f,0x1e0,0x780f00,0x3c,0x3c000,0xf0078007,0x80003c00,
3620       0xf000,0x781f000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x3e,0x3c000,0x3c003c0,0x3c3c001,0xe71cf000,0x7df00003,
3621       0xc780000f,0x8000003e,0xf0,0x780,0x0,0x0,0x3c00000,0x3fcff800,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x1f00fc,0x1e0,0x1e000001,
3622       0xe00f0003,0xfcff8003,0xe00000ff,0x3fe007f9,0xff000000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x0,0x7c00000,0xf0f0078,0x3fcff,0x8000f800,
3623       0x1e000,0x0,0x780001,0xe0180000,0xf000001f,0xffe00007,0x8000003c,0x7ff,0xc0000000,0x1c3ffc7,0x0,0x3e07c00,0x1e00,0xe3,0x80738000,
3624       0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0x3e0,0x7c00001d,0xc0000001,0xe0000770,0x1f00000,0xfffff80,0x7ffffc03,
3625       0xffffe01f,0xffff00ff,0xfff807ff,0xffc07fff,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,
3626       0xe0001e00,0x780f00f,0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,0x3e07c01f,0xc00780f0,0x3c0780,0x1e03c00,0xf01e000,
3627       0x78000780,0x1fffc0,0xf0007c,0x1e000780,0xf0003c07,0x8001e03c,0xf01e0,0x780f00,0x3c1e01e,0xf000,0x1e0000,0xf00000,0x7800000,
3628       0x3c000000,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00,0x1e,0x7807803c,
3629       0x3c01e0,0x1e00f00,0xf007800,0x78003c78,0x1e0007,0x800f1e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3630       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x83c00000,0x303c0003,0x8039e001,0xee000000,0x1e00,0x3c00,
3631       0x0,0x1e0000,0x0,0x0,0x1e,0xf00,0x780001e,0x1f80,0x7,0x83fffffc,0x1e0,0x3c000f00,0x1e0001e,0x3c000,0x3c0000,0x0,0x1f,0xfc1fffff,
3632       0xf07ff000,0x0,0x780f00f0,0x78003c03,0xc000781e,0x1e0,0xf803c0,0x1e00,0x1e000,0x781e0007,0x80007800,0x780,0x3c00f800,0x7800001e,
3633       0xf078,0x3de01e0,0xf00780,0x7800,0x3c078003,0xe000000f,0xf000,0xf0003c0,0x1e78001,0xfe0ff003,0xe01f0000,0x7800007c,0x3c,0x3c,
3634       0x1e0,0x0,0x0,0x0,0xf0007c01,0xe000f80f,0x800001e0,0xf80f00,0x3c,0x1e001,0xf0078007,0x80003c00,0xf000,0x780f800,0x1e0000f,
3635       0x3c0f01e,0x1e03c0,0x1f00780,0x3e0f000,0x7c003c00,0x1e,0x3c000,0x3c003c0,0x3c3c001,0xe71cf000,0xf8f80003,0xe780001f,0x1e,
3636       0xf0,0x780,0x0,0x0,0x3c00000,0x1ffff000,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x3bc1de,0x1e0,0xf000001,0xe00f0001,0xffff0007,0xc000007f,
3637       0xffc003ff,0xfe000000,0x0,0x0,0x0,0xfe000,0x0,0x0,0x0,0x0,0x3c00000,0x1e0f0078,0x1ffff,0x1f000,0x1e000,0x0,0x780000,0xf0180000,
3638       0xf000001f,0xfff00007,0x8000003c,0x1ff,0x80000000,0xe0ff0e,0x0,0x1f03e00,0x1e00,0x70,0x70000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,
3639       0xe1c00,0x0,0x0,0x0,0x7c0,0xf8000019,0xc0000000,0xe0000670,0x1e00000,0xf000780,0x78003c03,0xc001e01e,0xf00f0,0x780780,0x3c0f807,
3640       0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0xf80f007,0xbc03c001,0xe01e000f,
3641       0xf00078,0x78003c0,0x3c001e00,0x7c03e00f,0x800780f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e0000,0xf0003c,0x1e000f80,
3642       0xf0007c07,0x8003e03c,0x1f01e0,0xf80f00,0x7c1e01e,0xf800,0x1e0000,0xf00000,0x7800000,0x3c000000,0x780000,0x3c00000,0x1e000000,
3643       0xf0001e00,0x7803c00,0x3c078003,0xe03c001f,0x1e000f8,0xf0007c0,0x78003e00,0x1f8001f,0xf00f803c,0x3c01e0,0x1e00f00,0xf007800,
3644       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,
3645       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x3c00000,0x303c0003,0x8039f001,0xfe000000,0x1e00,0x3c00,0x0,0x1e0000,0x0,0x0,0x3c,0xf00,
3646       0x780001e,0x3f00,0x7,0x80000780,0x3e0,0x3e000f00,0x3c0001e,0x3c000,0x7c0000,0x0,0x3,0xfe000000,0xff8000,0x0,0x3c0f81f0,0xf0001e03,
3647       0xc000780f,0x1e0,0xf003c0,0x1e00,0xf000,0x781e0007,0x80007800,0x780,0x3c007c00,0x7800001e,0xf078,0x3de01e0,0xf00780,0x7800,
3648       0x3c078001,0xe000000f,0xf000,0xf0003c0,0x1e78001,0xfc07f003,0xe00f0000,0x78000078,0x3c,0x1e,0x1e0,0x0,0x0,0x0,0xf0007c01,
3649       0xf000f007,0x800000f0,0xf80780,0x3c,0x1e001,0xf0078007,0x80003c00,0xf000,0x7807c00,0x1e0000f,0x3c0f01e,0x1e01e0,0x1e007c0,
3650       0x3c07800,0x7c003c00,0x1e,0x3c000,0x3c007c0,0x1e78001,0xe71df000,0xf8f80001,0xef80003e,0x1e,0xf0,0x780,0x0,0x0,0x3c00000,
3651       0xfffe000,0x0,0x3e000000,0x0,0x18,0x7fff,0xc0000000,0x60c306,0x1e0,0x7800001,0xe00f0000,0xfffe0007,0x8000003f,0xff8001ff,
3652       0xfc000000,0x0,0x0,0x0,0x7c000,0x0,0x0,0x0,0x0,0x3c00000,0x3c0f0078,0xfffe,0x3e000,0x1e000,0x0,0x780000,0xf0180000,0xf000003c,
3653       0xfcf80007,0x8000003c,0x7f,0x0,0x70001c,0x0,0xf81f00,0x0,0x38,0xe0000,0x0,0x0,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0xf81,
3654       0xf0000039,0xc0000000,0xe0000e70,0x1e00000,0x1e0003c0,0xf0001e07,0x8000f03c,0x781e0,0x3c0f00,0x1e0f007,0x8000f000,0x78000,
3655       0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0xf00f007,0xbc03c001,0xe01e000f,0xf00078,0x78003c0,
3656       0x3c001e00,0xf801f00f,0x800780f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e0000,0xf0003c,0x1e000f80,0xf0007c07,0x8003e03c,
3657       0x1f01e0,0xf80f00,0x7c1e01e,0x7800,0xf0000,0x780000,0x3c00000,0x1e000000,0x780000,0x3c00000,0x1e000000,0xf0000f00,0xf003c00,
3658       0x3c03c003,0xc01e001e,0xf000f0,0x7800780,0x3c003c00,0x1f8000f,0xe00f003c,0x7c01e0,0x3e00f00,0x1f007800,0xf8001ef8,0x1f000f,
3659       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,
3660       0x0,0x0,0xf,0x3c00000,0x307c0003,0x8038f000,0xfc000000,0x1e00,0x3c00,0x0,0x1e0000,0xfc0000,0x0,0x7e00003c,0x780,0xf00001e,
3661       0x7e00,0xf,0x80000780,0x3c0,0x3e001e00,0x3c0001f,0x7c000,0x780007,0xe000003f,0x0,0xfe000000,0xfe0000,0x0,0x3c07c3f0,0xf0001e03,
3662       0xc000f80f,0x800001e0,0x1f003c0,0x1e00,0xf000,0x781e0007,0x80007800,0x4000f80,0x3c003c00,0x7800001e,0xf078,0x1fe01f0,0x1f00780,
3663       0x7c00,0x7c078001,0xf000001f,0xf000,0xf0003c0,0x1e78001,0xfc07f007,0xc00f8000,0x780000f8,0x3c,0x1e,0x1e0,0x0,0x0,0x0,0xf0007c01,
3664       0xf000f007,0xc00000f0,0xf80780,0x3c,0x1f003,0xf0078007,0x80003c00,0xf000,0x7807c00,0x1e0000f,0x3c0f01e,0x1e01e0,0x1e007c0,
3665       0x3c07800,0x7c003c00,0x1e,0x3c000,0x3c007c0,0x1e78000,0xfe0fe001,0xf07c0001,0xef00007c,0x1e,0xf0,0x780,0x0,0x0,0x1e00000,
3666       0x7cfc000,0xfc00000,0x3c00000f,0xc3f00000,0x18,0x7fff,0xc0000000,0x406303,0x3e0,0x3c00001,0xf00f0000,0x7cfc000f,0x8000001f,
3667       0x3f0000f9,0xf8000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x780700f8,0x7cfc,0x7c000,0x1e000,0x0,0x780000,0xf8180000,
3668       0xf0000070,0x3c0007,0x8000003c,0x3f,0x80000000,0x3c0078,0x0,0x780f00,0x0,0x1e,0x3c0000,0x0,0x0,0x0,0x0,0x0,0x3e007c0,0xe1c00,
3669       0x0,0x0,0x0,0xf01,0xe0000071,0xc0000000,0xe0001c70,0x1e00000,0x1e0003c0,0xf0001e07,0x8000f03c,0x781e0,0x3c0f00,0x1e0f007,
3670       0x8000f800,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x1f00f003,0xfc03e003,0xe01f001f,
3671       0xf800f8,0x7c007c0,0x3e003e01,0xf000f80f,0xf00f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e0000,0xf0003c,0x1e000f80,0xf0007c07,
3672       0x8003e03c,0x1f01e0,0xf80f00,0x7c1e01e,0x7c00,0xf0000,0x780000,0x3c00000,0x1e000000,0x780000,0x3c00000,0x1e000000,0xf0000f00,
3673       0xf003c00,0x3c03c003,0xc01e001e,0xf000f0,0x7800780,0x3c003c00,0x1f8000f,0xc00f003c,0x7c01e0,0x3e00f00,0x1f007800,0xf8001ef0,
3674       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,
3675       0x0,0x0,0x0,0x0,0x780000,0xf,0x3800040,0x30780003,0x8038f800,0x78000000,0x1e00,0x3c00,0x0,0x1e0000,0xfc0000,0x0,0x7e000078,
3676       0x780,0x1f00001e,0xfc00,0x20001f,0x780,0x80007c0,0x1f001e00,0x7c0000f,0x78000,0xf80007,0xe000003f,0x0,0x1e000000,0xf00000,
3677       0x3c000,0x3c03fff0,0xf0001e03,0xc001f007,0x800101e0,0x7e003c0,0x1e00,0x7800,0x781e0007,0x80007800,0x6000f00,0x3c003e00,0x7800001e,
3678       0xf078,0x1fe00f0,0x1e00780,0x3c00,0x78078000,0xf020001e,0xf000,0x7800780,0xff0001,0xfc07f00f,0x8007c000,0x780001f0,0x3c,0xf,
3679       0x1e0,0x0,0x0,0x0,0xf800fc01,0xf801f007,0xc00100f8,0x1f807c0,0x40003c,0xf807,0xf0078007,0x80003c00,0xf000,0x7803e00,0x1f0000f,
3680       0x3c0f01e,0x1e01f0,0x3e007e0,0x7c07c00,0xfc003c00,0x1e,0x3e000,0x3e007c0,0x1ff8000,0xfe0fe003,0xe03e0001,0xff0000fc,0x1e,
3681       0xf0,0x780,0x0,0x0,0x1f00080,0x3cf8000,0xfc00000,0x3c00001f,0x83f00000,0x18,0xc0,0x0,0xc06203,0x40003c0,0x1c00000,0xf80f0000,
3682       0x3cf8001f,0xf,0x3e000079,0xf0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x700780fc,0x3cf8,0xfc000,0x1e000,0x0,0x780000,
3683       0x7c180000,0xf0000020,0x100007,0x8000003c,0xf,0x80000000,0x1f01f0,0x0,0x380700,0x0,0xf,0x80f80000,0x0,0x0,0x0,0x0,0x0,0x3e007c0,
3684       0xe1c00,0x0,0x0,0x0,0xe01,0xc0000071,0xc0000001,0xc0001c70,0x1e00040,0x1e0003c0,0xf0001e07,0x8000f03c,0x781e0,0x3c0f00,0x1e0f007,
3685       0x80007800,0x10078000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x7e00f003,0xfc01e003,0xc00f001e,
3686       0x7800f0,0x3c00780,0x1e003c00,0xe000700f,0x800f0078,0x7803c0,0x3c01e00,0x1e00f000,0xf0000780,0x1e0000,0xf0003c,0x1f001f80,
3687       0xf800fc07,0xc007e03e,0x3f01f0,0x1f80f80,0xfc1e01f,0x7c00,0x100f8000,0x807c0004,0x3e00020,0x1f000100,0x780000,0x3c00000,0x1e000000,
3688       0xf0000f80,0x1f003c00,0x3c03e007,0xc01f003e,0xf801f0,0x7c00f80,0x3e007c00,0x1f8000f,0x801f003e,0x7c01f0,0x3e00f80,0x1f007c00,
3689       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,
3690       0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0xf,0x7800078,0x31f80001,0xc070fc00,0xfc000000,0x1e00,0x7c00,0x0,0x1e0000,0xfc0000,0x0,0x7e000078,
3691       0x7c0,0x1f00001e,0x1f000,0x38003f,0x780,0xe000f80,0x1f803e00,0x780000f,0x800f8000,0x1f00007,0xe000003f,0x0,0x2000000,0x800000,
3692       0x3c000,0x3e01ff71,0xf0001f03,0xc007f007,0xc00301e0,0x1fc003c0,0x1e00,0x7c00,0x781e0007,0x80007800,0x7801f00,0x3c001f00,0x7800001e,
3693       0xf078,0xfe00f8,0x3e00780,0x3e00,0xf8078000,0xf838003e,0xf000,0x7c00f80,0xff0000,0xfc07e00f,0x8003c000,0x780001e0,0x3c,0xf,
3694       0x1e0,0x0,0x0,0x0,0xf801fc01,0xfc03e003,0xe003007c,0x3f803e0,0x1c0003c,0xfc0f,0xf0078007,0x80003c00,0xf000,0x7801f00,0xf8000f,
3695       0x3c0f01e,0x1e00f8,0x7c007f0,0xf803e01,0xfc003c00,0x8003e,0x1f000,0x1e00fc0,0xff0000,0xfe0fe007,0xc01f0000,0xfe0000f8,0x1e,
3696       0xf0,0x780,0x0,0x0,0xf80180,0x1cf0000,0x1f800000,0x3c00001f,0x83e00000,0x18,0xc0,0x0,0xc06203,0x70007c0,0xe00000,0x7e0f0000,
3697       0x1cf0001e,0x7,0x3c000039,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100,0x7c00000,0xe00780fc,0x2001cf0,0xf8000,0x1e000,0x0,
3698       0x780000,0x7e182000,0xf0000000,0x7,0x8000003c,0x7,0xc0000000,0x7ffc0,0x0,0x180300,0x0,0x3,0xffe00000,0x0,0x0,0x0,0x0,0x0,
3699       0x3f00fc0,0xe1c00,0x0,0x0,0x0,0xc01,0x800000e1,0xc0000003,0xc0003870,0x1f001c0,0x3e0003e1,0xf0001f0f,0x8000f87c,0x7c3e0,0x3e1f00,
3700       0x1f1e007,0x80007c00,0x30078000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e03,0xfc00f001,0xfc01f007,
3701       0xc00f803e,0x7c01f0,0x3e00f80,0x1f007c00,0x4000201f,0xc01f007c,0xf803e0,0x7c01f00,0x3e00f801,0xf0000780,0x1e0000,0xf0007c,
3702       0x1f003f80,0xf801fc07,0xc00fe03e,0x7f01f0,0x3f80f80,0x1fc1f03f,0x803e00,0x3007c003,0x803e001c,0x1f000e0,0xf800700,0x780000,
3703       0x3c00000,0x1e000000,0xf00007c0,0x3e003c00,0x3c01f00f,0x800f807c,0x7c03e0,0x3e01f00,0x1f00f800,0x1f80007,0xc03e001e,0xfc00f0,
3704       0x7e00780,0x3f003c01,0xf8000fe0,0x1fc03e,0x3f800,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3705       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0x780007f,0xfff00001,0xe0f07f03,0xfe000000,0xf00,0x7800,0x0,
3706       0x1e0000,0xfc0000,0x0,0x7e0000f0,0x3f0,0x7e000fff,0xfc03ffff,0xf83f00fe,0x780,0xfc03f80,0xfc0fc00,0xf800007,0xe03f0018,0x7e00007,
3707       0xe000003f,0x0,0x0,0x0,0x3c000,0x1e007c71,0xe0000f03,0xffffe003,0xf01f01ff,0xff8003ff,0xffe01e00,0x3f01,0xf81e0007,0x803ffff0,
3708       0x7e03f00,0x3c000f00,0x7ffffe1e,0xf078,0xfe007e,0xfc00780,0x1f83,0xf0078000,0x783f00fe,0xf000,0x3f03f00,0xff0000,0xfc07e01f,
3709       0x3e000,0x780003ff,0xfffc003c,0x7,0x800001e0,0x0,0x0,0x0,0x7e07fc01,0xfe07e001,0xf80f007e,0x7f801f8,0xfc0003c,0x7ffe,0xf0078007,
3710       0x807ffffe,0xf000,0x7801f00,0xfff00f,0x3c0f01e,0x1e00fc,0xfc007f8,0x1f803f03,0xfc003c00,0xf80fc,0x1fff0,0x1f83fc0,0xff0000,
3711       0xfc07e007,0xc01f0000,0xfe0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0xfe0780,0xfe0000,0x1f000000,0x3c00001f,0x7c00e03,0x81c00018,
3712       0xc0,0x0,0x406203,0x7e01fc0,0x700000,0x7fffff80,0xfe0003f,0xffffc003,0xf800001f,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f0,
3713       0x1f800001,0xc007c1fe,0x6000fe0,0x1ffffe,0x1e000,0x0,0x780000,0x3f98e03f,0xffff8000,0x7,0x8000003c,0x7,0xc0000000,0xfe00,
3714       0x0,0x80100,0x0,0x0,0x7f000000,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3f83fe8,0xe1c00,0x0,0x0,0x0,0x801,0xc1,0xc0000007,0x80003070,
3715       0xfc0fc0,0x3c0001e1,0xe0000f0f,0x7878,0x3c3c0,0x1e1e00,0xf1e007,0xffc03f01,0xf007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,
3716       0xffff001f,0xfff800ff,0xffc01fff,0xf800f001,0xfc00fc1f,0x8007e0fc,0x3f07e0,0x1f83f00,0xfc1f800,0x1f,0xf07e003f,0x3f001f8,
3717       0x1f800fc0,0xfc007e07,0xe0000780,0x1e0000,0xf301f8,0xfc0ff80,0x7e07fc03,0xf03fe01f,0x81ff00fc,0xff807e0,0x7fc0f87f,0x81801f80,
3718       0xf003f01f,0x801f80fc,0xfc07e0,0x7e03f00,0xfffffc07,0xffffe03f,0xffff01ff,0xfff807e0,0x7e003c00,0x3c01f81f,0x800fc0fc,0x7e07e0,
3719       0x3f03f00,0x1f81f800,0x1f8000f,0xe07e001f,0x83fc00fc,0x1fe007e0,0xff003f07,0xf8000fe0,0x1fe07e,0x3f800,0x0,0x0,0x0,0x0,0x0,
3720       0x0,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,
3721       0xffe00000,0xffe03fff,0xdf000000,0xf00,0x7800,0x0,0x0,0xfc0000,0x0,0x7e0000f0,0x1ff,0xfc000fff,0xfc03ffff,0xf83ffffc,0x780,
3722       0xfffff00,0x7fff800,0xf000007,0xffff001f,0xffe00007,0xe000003f,0x0,0x0,0x0,0x3c000,0x1e000001,0xe0000f03,0xffffc001,0xffff01ff,
3723       0xff0003ff,0xffe01e00,0x1fff,0xf81e0007,0x803ffff0,0x7fffe00,0x3c000f80,0x7ffffe1e,0xf078,0xfe003f,0xff800780,0xfff,0xf0078000,
3724       0x7c3ffffc,0xf000,0x3ffff00,0xff0000,0xf803e01e,0x1e000,0x780003ff,0xfffc003c,0x7,0x800001e0,0x0,0x0,0x0,0x7fffbc01,0xffffc000,
3725       0xffff003f,0xfff800ff,0xffc0003c,0x3ffe,0xf0078007,0x807ffffe,0xf000,0x7800f80,0x7ff00f,0x3c0f01e,0x1e007f,0xff8007ff,0xff001fff,
3726       0xbc003c00,0xffffc,0x1fff0,0x1fffbc0,0xff0000,0x7c07c00f,0x800f8000,0x7e0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x7fff80,0x7c0000,
3727       0x1f000000,0x3c00001e,0x7c00f07,0xc1e00018,0xc0,0x0,0x60e303,0x7ffff80,0x380000,0x3fffff80,0x7c0003f,0xffffc001,0xf000000f,
3728       0x80000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0xff800003,0x8003ffff,0xfe0007c0,0x1ffffe,0x1e000,0x0,0x780000,0x1fffe03f,0xffff8000,
3729       0x7,0x8000003c,0x3,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3fffdf8,0xe1c00,0x0,0x0,0x0,0x0,0x1c1,
3730       0xc000000f,0x7070,0x7fffc0,0x3c0001e1,0xe0000f0f,0x7878,0x3c3c0,0x1e1e00,0xf1e007,0xffc01fff,0xf007ffff,0xc03ffffe,0x1fffff0,
3731       0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xf000f001,0xfc007fff,0x3fff8,0x1fffc0,0xfffe00,0x7fff000,0x3b,0xfffc003f,
3732       0xfff001ff,0xff800fff,0xfc007fff,0xe0000780,0x1e0000,0xf3fff8,0xffff780,0x7fffbc03,0xfffde01f,0xffef00ff,0xff7807ff,0xfbc0ffff,
3733       0xff800fff,0xf001ffff,0x800ffffc,0x7fffe0,0x3ffff00,0xfffffc07,0xffffe03f,0xffff01ff,0xfff803ff,0xfc003c00,0x3c00ffff,0x7fff8,
3734       0x3fffc0,0x1fffe00,0xffff000,0x1f,0xfffc001f,0xffbc00ff,0xfde007ff,0xef003fff,0x780007e0,0x1ffffc,0x1f800,0x0,0x0,0x0,0x0,
3735       0x0,0x0,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,
3736       0xffc00000,0x7fc01fff,0x9f800000,0xf80,0xf800,0x0,0x0,0xfc0000,0x0,0x7e0000f0,0xff,0xf8000fff,0xfc03ffff,0xf83ffff8,0x780,
3737       0xffffe00,0x7fff000,0xf000003,0xfffe001f,0xffc00007,0xe000003f,0x0,0x0,0x0,0x3c000,0xf000003,0xe0000f83,0xffff0000,0xffff01ff,
3738       0xfc0003ff,0xffe01e00,0xfff,0xf01e0007,0x803ffff0,0x7fffc00,0x3c0007c0,0x7ffffe1e,0xf078,0x7e003f,0xff000780,0x7ff,0xe0078000,
3739       0x3c3ffff8,0xf000,0x1fffe00,0x7e0000,0xf803e03e,0x1f000,0x780003ff,0xfffc003c,0x7,0x800001e0,0x0,0x0,0x0,0x3fff3c01,0xefff8000,
3740       0x7ffe001f,0xff78007f,0xff80003c,0x1ffc,0xf0078007,0x807ffffe,0xf000,0x78007c0,0x3ff00f,0x3c0f01e,0x1e003f,0xff0007bf,0xfe000fff,
3741       0xbc003c00,0xffff8,0xfff0,0xfff3c0,0x7e0000,0x7c07c01f,0x7c000,0x7c0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x3fff80,0x380000,
3742       0x3e000000,0x7c00003e,0x7801f07,0xc1e00018,0xc0,0x0,0x39c1ce,0x7ffff00,0x1c0000,0xfffff80,0x380003f,0xffffc000,0xe0000007,
3743       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0xff000007,0x1ffcf,0xfe000380,0x1ffffe,0x1e000,0x0,0x780000,0xfffe03f,0xffff8000,0x7,
3744       0x8000003c,0x3,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3dffdf8,0xe1c00,0x0,0x0,0x0,0x0,0x381,
3745       0xc000001e,0xe070,0x7fff80,0x7c0001f3,0xe0000f9f,0x7cf8,0x3e7c0,0x1f3e00,0xfbe007,0xffc00fff,0xf007ffff,0xc03ffffe,0x1fffff0,
3746       0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xc000f000,0xfc007ffe,0x3fff0,0x1fff80,0xfffc00,0x7ffe000,0x79,0xfff8001f,
3747       0xffe000ff,0xff0007ff,0xf8003fff,0xc0000780,0x1e0000,0xf3fff0,0x7ffe780,0x3fff3c01,0xfff9e00f,0xffcf007f,0xfe7803ff,0xf3c07ff3,
3748       0xff8007ff,0xe000ffff,0x7fff8,0x3fffc0,0x1fffe00,0xfffffc07,0xffffe03f,0xffff01ff,0xfff801ff,0xf8003c00,0x3c007ffe,0x3fff0,
3749       0x1fff80,0xfffc00,0x7ffe000,0x1d,0xfff8000f,0xff3c007f,0xf9e003ff,0xcf001ffe,0x780007c0,0x1efff8,0x1f000,0x0,0x0,0x0,0x0,
3750       0x0,0x0,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,
3751       0xfe000000,0x1f000fff,0xfc00000,0x780,0xf000,0x0,0x0,0xf80000,0x0,0x7e0001e0,0x7f,0xf0000fff,0xfc03ffff,0xf81ffff0,0x780,
3752       0x7fff800,0x1ffe000,0x1f000000,0xfff8001f,0xff000007,0xe000003e,0x0,0x0,0x0,0x3c000,0xf800003,0xc0000783,0xfff80000,0x3ffe01ff,
3753       0xe00003ff,0xffe01e00,0x7ff,0xc01e0007,0x803ffff0,0x3fff800,0x3c0003c0,0x7ffffe1e,0xf078,0x7e000f,0xfe000780,0x3ff,0xc0078000,
3754       0x3e1fffe0,0xf000,0x7ff800,0x7e0000,0xf803e07c,0xf800,0x780003ff,0xfffc003c,0x3,0xc00001e0,0x0,0x0,0x0,0xffe3c01,0xe7ff0000,
3755       0x3ffc000f,0xfe78003f,0xfe00003c,0x7f0,0xf0078007,0x807ffffe,0xf000,0x78003e0,0xff00f,0x3c0f01e,0x1e001f,0xfe00079f,0xfc0007ff,
3756       0x3c003c00,0x7ffe0,0x1ff0,0x7fe3c0,0x7e0000,0x7c07c03e,0x3e000,0x7c0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0xfff00,0x100000,
3757       0x3e000000,0x7800003c,0xf800f07,0xc1e00018,0xc0,0x0,0x1f80fc,0x3fffc00,0xc0000,0x3ffff80,0x100003f,0xffffc000,0x40000002,
3758       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xfc000006,0xff87,0xfc000100,0x1ffffe,0x1e000,0x0,0x780000,0x3ffc03f,0xffff8000,0x7,
3759       0x8000003c,0x3,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3dff9f8,0xe1c00,0x0,0x0,0x0,0x0,0x3ff,
3760       0xf800003c,0xfffe,0x1ffe00,0x780000f3,0xc000079e,0x3cf0,0x1e780,0xf3c00,0x7bc007,0xffc003ff,0xe007ffff,0xc03ffffe,0x1fffff0,
3761       0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01ffc,0xf000,0xfc001ffc,0xffe0,0x7ff00,0x3ff800,0x1ffc000,0x70,0xfff00007,
3762       0xff80003f,0xfc0001ff,0xe0000fff,0x780,0x1e0000,0xf3ffe0,0x1ffc780,0xffe3c00,0x7ff1e003,0xff8f001f,0xfc7800ff,0xe3c03fe1,
3763       0xff0003ff,0xc0007ffc,0x3ffe0,0x1fff00,0xfff800,0xfffffc07,0xffffe03f,0xffff01ff,0xfff800ff,0xf0003c00,0x3c003ffc,0x1ffe0,
3764       0xfff00,0x7ff800,0x3ffc000,0x38,0xfff00007,0xfe3c003f,0xf1e001ff,0x8f000ffc,0x780007c0,0x1e7ff0,0x1f000,0x0,0x0,0x0,0x0,0x0,
3765       0x0,0x0,0x0,0x0,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,
3766       0x1fc,0x0,0x780,0xf000,0x0,0x0,0x1f80000,0x0,0x1e0,0x1f,0xc0000000,0x0,0x1ff80,0x0,0xffc000,0x7f8000,0x0,0x3fe00007,0xfc000000,
3767       0x7e,0x0,0x0,0x0,0x0,0x7c00000,0x0,0x0,0xff00000,0x0,0x0,0xfe,0x0,0x0,0x3fc000,0x0,0x0,0x0,0x3,0xf8000000,0xff,0xc0000000,
3768       0x1ff00,0x0,0x1fe000,0x0,0x0,0x0,0x0,0x3c,0x3,0xc00001e0,0x0,0x0,0x0,0x3f80000,0x1fc0000,0x7f00003,0xf8000007,0xf0000000,
3769       0x0,0xf0000000,0x0,0xf000,0x0,0x0,0x0,0x7,0xf8000787,0xf00001fc,0x3c000000,0x7f80,0x0,0x1f8000,0x0,0x0,0x0,0x7c000000,0x1e,
3770       0xf0,0x780,0x0,0x0,0x3fc00,0x0,0x3c000000,0x7800003c,0xf000601,0xc00018,0xc0,0x0,0x0,0x3fe000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3771       0x0,0x0,0x0,0x0,0x0,0x0,0xf,0xf0000000,0x7e03,0xf0000000,0x0,0x0,0x0,0x0,0xfe0000,0x0,0x0,0x3c,0x2007,0x80000000,0x0,0x0,
3772       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c7e0f0,0xe1c00,0x0,0x3800000,0x0,0x0,0x3ff,0xf8000078,0xfffe,0x7f800,0x0,0x0,0x0,0x0,
3773       0x0,0x0,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f0,0x3f80,0x1fc00,0xfe000,0x7f0000,0x70,0x3fc00001,0xfe00000f,0xf000007f,
3774       0x800003fc,0x0,0x0,0xff00,0x7f0000,0x3f80000,0x1fc00000,0xfe000007,0xf000003f,0x80001f80,0xfc00007f,0xfe0,0x7f00,0x3f800,
3775       0x1fc000,0x0,0x0,0x0,0x3f,0xc0000000,0xff0,0x7f80,0x3fc00,0x1fe000,0xff0000,0x78,0x3fc00001,0xf800000f,0xc000007e,0x3f0,0x7c0,
3776       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,
3777       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,
3778       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,
3779       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,
3780       0x0,0x0,0x0,0x0,0x0,0x0,0x78000000,0x1e,0xf0,0x780,0x0,0x0,0x0,0x0,0x3c000000,0x78000078,0xf000000,0x18,0xc0,0x0,0x0,0x0,
3781       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,
3782       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0xe1c00,0x0,0x1800000,0x0,0x0,0x3ff,0xf80000f0,0xfffe,0x0,0x0,0x0,0x0,
3783       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,
3784       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,
3785       0x0,0x0,0x0,0x0,0x0,0x0,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,
3786       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,
3787       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,
3788       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,
3789       0x1f,0xf0,0xf80,0x0,0x0,0x0,0x0,0x78000000,0xf8000078,0x1e000000,0x8,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3790       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,
3791       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,
3792       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,
3793       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,
3794       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,
3795       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,
3796       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,
3797       0x0,0xf000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x0,0xf0000000,0x1f,0x800000f0,0x1f80,0x0,0x0,0x0,0x0,
3798       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,
3799       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,
3800       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,
3801       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,
3802       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,
3803       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,
3804       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,
3805       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf0000000,0x7fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4003,0xe0000000,0x0,0x1f000,0x0,0x0,
3806       0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x1,0xf0000000,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x0,0x0,0x70000001,0xf00000e0,
3807       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,
3808       0x0,0x0,0x3c,0xff8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0xe1c00,0x0,0xe00000,0x0,0x0,0x1,0xc00003ff,
3809       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,
3810       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,
3811       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,
3812       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,
3813       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,
3814       0xf0000000,0x7fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780f,0xc0000000,0x0,0x3e000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,
3815       0x0,0x0,0x0,0x0,0x3,0xe0000000,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x0,0x0,0xf0000103,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3816       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,
3817       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x21e00000,0x0,0x0,0x1,0xc00003ff,0xe0000070,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10f,
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,0x0,0x0,0x10f,0x0,
3819       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,
3820       0x0,0x0,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,
3821       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,
3822       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,
3823       0x0,0x0,0x7fff,0xc0000000,0x0,0x3ffe000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x7f,0xe0000000,0x7,0xfc0000f0,
3824       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,
3825       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,
3826       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,
3827       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,
3828       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,
3829       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3830       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3831       0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x7fff,0x80000000,0x0,0x3ffc000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,
3832       0x0,0x0,0x0,0x0,0x7f,0xc0000000,0x0,0xfc0000f0,0x3f000,0x0,0x0,0x0,0x0,0x1ff,0xc0000000,0x0,0x0,0x0,0x0,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,0x3c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3834       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,
3835       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,
3836       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,
3837       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3838       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3839       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,
3840       0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x7f,0x80000000,0x0,0xf0,0x0,0x0,0x0,0x0,0x0,0x1ff,0x80000000,0x0,0x0,0x0,0x0,
3841       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3842       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,
3843       0x0,0x0,0x0,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,
3844       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,
3845       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3846       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3847       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,
3848       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,
3849       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3850       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,
3851       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3852       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,
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,0x0,
3854       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3855       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,
3856       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,
3857       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,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,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3859       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,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,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3861       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,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,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3863       0x0,0x0,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,
3864       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3865       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3866       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,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,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 };
3868 
3869     // Definition of a 40x38 'danger' color logo.
3870     const unsigned char logo40x38[4576] = {
3871       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,
3872       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,
3873       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,
3874       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,
3875       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,
3876       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,
3877       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,
3878       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,
3879       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,
3880       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,
3881       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,
3882       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,
3883       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,
3884       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,
3885       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,
3886       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,
3887       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,
3888       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,
3889       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,
3890       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,
3891       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,
3892       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,
3893       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,
3894       200,200,200,91,49,124,118,124,71,32,124,95,49,56,114,52,82,121,0};
3895 
3896     //! Set/get output stream for CImg library messages.
3897     inline std::FILE* output(std::FILE *file) {
3898       static std::FILE *res = stderr;
3899       if (file) res = file;
3900       return res;
3901     }
3902 
3903     //! Display a warning message.
3904     /**
3905        \param format : C-string describing the format of the message, as in <tt>std::printf()</tt>.
3906     **/
3907     inline void warn(const char *const format, ...) {
3908       if (cimg::exception_mode()>=1) {
3909         char message[16384] = { 0 };
3910         std::va_list ap;
3911         va_start(ap,format);
3912         cimg_vsnprintf(message,sizeof(message),format,ap);
3913         va_end(ap);
3914 #ifdef cimg_strict_warnings
3915         throw CImgWarningException(message);
3916 #else
3917         std::fprintf(cimg::output(),"\n%s[CImg] *** Warning ***%s%s",cimg::t_red,cimg::t_normal,message);
3918 #endif
3919       }
3920     }
3921 
3922     // Execute an external system command.
3923     /**
3924        \note This function is similar to <tt>std::system()</tt>
3925        and is here because using the <tt>std::</tt> version on
3926        Windows may open undesired consoles.
3927     **/
3928     inline int system(const char *const command, const char *const module_name=0) {
3929 #if cimg_OS==2
3930       PROCESS_INFORMATION pi;
3931       STARTUPINFO si;
3932       std::memset(&pi,0,sizeof(PROCESS_INFORMATION));
3933       std::memset(&si,0,sizeof(STARTUPINFO));
3934       GetStartupInfo(&si);
3935       si.cb = sizeof(si);
3936       si.wShowWindow = SW_HIDE;
3937       si.dwFlags |= SW_HIDE;
3938       const BOOL res = CreateProcess((LPCTSTR)module_name,(LPTSTR)command,0,0,FALSE,0,0,0,&si,&pi);
3939       if (res) {
3940         WaitForSingleObject(pi.hProcess, INFINITE);
3941         CloseHandle(pi.hThread);
3942         CloseHandle(pi.hProcess);
3943         return 0;
3944       } else
3945 #endif
3946         return std::system(command);
3947       return module_name?0:1;
3948     }
3949 
3950     //! Get a reference to a temporary variable of type T.
3951     template<typename T>
3952     inline T& temporary(const T&) {
3953       static T temp;
3954       return temp;
3955     }
3956 
3957     //! Exchange values of variables \p a and \p b.
3958     template<typename T>
3959     inline void swap(T& a, T& b) { T t = a; a = b; b = t; }
3960 
3961     //! Exchange values of variables (\p a1,\p a2) and (\p b1,\p b2).
3962     template<typename T1, typename T2>
3963     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2) {
3964       cimg::swap(a1,b1); cimg::swap(a2,b2);
3965     }
3966 
3967     //! Exchange values of variables (\p a1,\p a2,\p a3) and (\p b1,\p b2,\p b3).
3968     template<typename T1, typename T2, typename T3>
3969     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3) {
3970       cimg::swap(a1,b1,a2,b2); cimg::swap(a3,b3);
3971     }
3972 
3973     //! Exchange values of variables (\p a1,\p a2,...,\p a4) and (\p b1,\p b2,...,\p b4).
3974     template<typename T1, typename T2, typename T3, typename T4>
3975     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4) {
3976       cimg::swap(a1,b1,a2,b2,a3,b3); cimg::swap(a4,b4);
3977     }
3978 
3979     //! Exchange values of variables (\p a1,\p a2,...,\p a5) and (\p b1,\p b2,...,\p b5).
3980     template<typename T1, typename T2, typename T3, typename T4, typename T5>
3981     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5) {
3982       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4); cimg::swap(a5,b5);
3983     }
3984 
3985     //! Exchange values of variables (\p a1,\p a2,...,\p a6) and (\p b1,\p b2,...,\p b6).
3986     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
3987     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) {
3988       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5); cimg::swap(a6,b6);
3989     }
3990 
3991     //! Exchange values of variables (\p a1,\p a2,...,\p a7) and (\p b1,\p b2,...,\p b7).
3992     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
3993     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,
3994                      T7& a7, T7& b7) {
3995       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6); cimg::swap(a7,b7);
3996     }
3997 
3998     //! Exchange values of variables (\p a1,\p a2,...,\p a8) and (\p b1,\p b2,...,\p b8).
3999     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
4000     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,
4001                      T7& a7, T7& b7, T8& a8, T8& b8) {
4002       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6,a7,b7); cimg::swap(a8,b8);
4003     }
4004 
4005     //! Get the current endianness of the CPU.
4006     /**
4007        \return \c false for "Little Endian", \c true for "Big Endian".
4008     **/
4009     inline bool endianness() {
4010       const int x = 1;
4011       return ((unsigned char*)&x)[0]?false:true;
4012     }
4013 
4014     //! Invert endianness of a memory buffer.
4015     template<typename T>
4016     inline void invert_endianness(T* const buffer, const unsigned int size) {
4017       if (size) switch (sizeof(T)) {
4018       case 1 : break;
4019       case 2 : { for (unsigned short *ptr = (unsigned short*)buffer+size; ptr>(unsigned short*)buffer; ) {
4020         const unsigned short val = *(--ptr);
4021         *ptr = (unsigned short)((val>>8)|((val<<8)));
4022       }
4023       } break;
4024       case 4 : { for (unsigned int *ptr = (unsigned int*)buffer+size; ptr>(unsigned int*)buffer; ) {
4025         const unsigned int val = *(--ptr);
4026         *ptr = (val>>24)|((val>>8)&0xff00)|((val<<8)&0xff0000)|(val<<24);
4027       }
4028       } break;
4029       default : { for (T* ptr = buffer+size; ptr>buffer; ) {
4030         unsigned char *pb = (unsigned char*)(--ptr), *pe = pb + sizeof(T);
4031         for (int i = 0; i<(int)sizeof(T)/2; ++i) swap(*(pb++),*(--pe));
4032       }
4033       }
4034       }
4035     }
4036 
4037     //! Invert endianness of a single variable.
4038     template<typename T>
4039     inline T& invert_endianness(T& a) {
4040       invert_endianness(&a,1);
4041       return a;
4042     }
4043 
4044     // Conversion function to gain more precision in storage of unsigned ints as floats.
4045     inline unsigned int float2uint(const float f) {
4046       int tmp = 0;
4047       std::memcpy(&tmp,&f,sizeof(float));
4048       if (tmp>=0) return (unsigned int)f;
4049       unsigned int u;
4050       std::memcpy(&u,&f,sizeof(float));  // use memcpy instead of assignment to avoid wrong optimizations with g++.
4051       return ((u)<<1)>>1; // set sign bit to 0.
4052     }
4053 
4054     inline float uint2float(const unsigned int u) {
4055       if (u<(1U<<19)) return (float)u;  // Consider safe storage until 19bits (i.e 524287).
4056       float f;
4057       const unsigned int v = u|(1U<<(8*sizeof(unsigned int)-1)); // set sign bit to 1.
4058       std::memcpy(&f,&v,sizeof(float)); // use memcpy instead of simple assignment to avoir wrong optimizations with g++.
4059       return f;
4060     }
4061 
4062     //! Get the value of a system timer with a millisecond precision.
4063     inline unsigned long time() {
4064 #if cimg_OS==1
4065       struct timeval st_time;
4066       gettimeofday(&st_time,0);
4067       return (unsigned long)(st_time.tv_usec/1000 + st_time.tv_sec*1000);
4068 #elif cimg_OS==2
4069       static SYSTEMTIME st_time;
4070       GetSystemTime(&st_time);
4071       return (unsigned long)(st_time.wMilliseconds + 1000*(st_time.wSecond + 60*(st_time.wMinute + 60*st_time.wHour)));
4072 #else
4073       return 0;
4074 #endif
4075     }
4076 
4077     // Implement a tic/toc mechanism to display elapsed time of algorithms.
4078     inline unsigned long tictoc(const bool is_tic) {
4079       static unsigned long t0 = 0;
4080       const unsigned long t = cimg::time();
4081       if (is_tic) return (t0 = t);
4082       const unsigned long dt = t>=t0?(t - t0):cimg::type<unsigned long>::max();
4083       const unsigned int
4084         edays = (unsigned int)(dt/86400000.0),
4085         ehours = (unsigned int)((dt - edays*86400000.0)/3600000.0),
4086         emin = (unsigned int)((dt - edays*86400000.0 - ehours*3600000.0)/60000.0),
4087         esec = (unsigned int)((dt - edays*86400000.0 - ehours*3600000.0 - emin*60000.0)/1000.0),
4088         ems = (unsigned int)(dt - edays*86400000.0 - ehours*3600000.0 - emin*60000.0 - esec*1000.0);
4089       if (!edays && !ehours && !emin && !esec)
4090         std::fprintf(cimg::output(),"%s[CImg] Elapsed time : %u ms%s\n",cimg::t_red,ems,cimg::t_normal);
4091       else {
4092         if (!edays && !ehours && !emin)
4093           std::fprintf(cimg::output(),"%s[CImg] Elapsed time : %u sec %u ms%s\n",cimg::t_red,esec,ems,cimg::t_normal);
4094         else {
4095           if (!edays && !ehours)
4096             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);
4097           else{
4098             if (!edays)
4099               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);
4100             else{
4101               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);
4102             }
4103           }
4104         }
4105       }
4106       return t;
4107     }
4108 
4109     inline unsigned long tic() {
4110       return cimg::tictoc(true);
4111     }
4112 
4113     inline unsigned long toc() {
4114       return cimg::tictoc(false);
4115     }
4116 
4117     //! Sleep for a certain numbers of milliseconds.
4118     /**
4119        This function frees the CPU ressources during the sleeping time.
4120        It may be used to temporize your program properly, without wasting CPU time.
4121     **/
4122     inline void sleep(const unsigned int milliseconds) {
4123 #if cimg_OS==1
4124       struct timespec tv;
4125       tv.tv_sec = milliseconds/1000;
4126       tv.tv_nsec = (milliseconds%1000)*1000000;
4127       nanosleep(&tv,0);
4128 #elif cimg_OS==2
4129       Sleep(milliseconds);
4130 #endif
4131     }
4132 
4133     inline unsigned int _sleep(const unsigned int milliseconds, unsigned long& timer) {
4134       if (!timer) timer = cimg::time();
4135       const unsigned long current_time = cimg::time();
4136       if (current_time>=timer+milliseconds) { timer = current_time; return 0; }
4137       const unsigned long time_diff = timer + milliseconds - current_time;
4138       timer = current_time + time_diff;
4139       cimg::sleep(time_diff);
4140       return (unsigned int)time_diff;
4141     }
4142 
4143     //! Wait for a certain number of milliseconds since the last call.
4144     /**
4145        This function is equivalent to sleep() but the waiting time is computed with regard to the last call
4146        of wait(). It may be used to temporize your program properly.
4147     **/
4148     inline unsigned int wait(const unsigned int milliseconds) {
4149       static unsigned long timer = 0;
4150       if (!timer) timer = cimg::time();
4151       return _sleep(milliseconds,timer);
4152     }
4153 
4154     // Use a specific srand initialization to avoid multi-threads to have to the
4155     // same series of random numbers (executed only once for a single program).
4156     inline void srand() {
4157       static bool first_time = true;
4158       if (first_time) {
4159         std::srand(cimg::time());
4160         unsigned char *const rand_ptr = new unsigned char[sizeof(unsigned int)+std::rand()%2048];
4161         std::srand((unsigned int)std::rand() + *(unsigned int*)(void*)rand_ptr);
4162         delete[] rand_ptr;
4163         first_time = false;
4164       }
4165     }
4166 
4167     //! Get a left bitwise-rotated number.
4168     template<typename T>
4169     inline T rol(const T a, const unsigned int n=1) {
4170       return n?(T)((a<<n)|(a>>((sizeof(T)<<3)-n))):a;
4171     }
4172 
4173     inline float rol(const float a, const unsigned int n=1) {
4174       return (float)rol((int)a,n);
4175     }
4176 
4177     inline double rol(const double a, const unsigned int n=1) {
4178       return (double)rol((long)a,n);
4179     }
4180 
4181     //! Get a right bitwise-rotated number.
4182     template<typename T>
4183     inline T ror(const T a, const unsigned int n=1) {
4184       return n?(T)((a>>n)|(a<<((sizeof(T)<<3)-n))):a;
4185     }
4186 
4187     inline float ror(const float a, const unsigned int n=1) {
4188       return (float)ror((int)a,n);
4189     }
4190 
4191     inline double ror(const double a, const unsigned int n=1) {
4192       return (double)ror((long)a,n);
4193     }
4194 
4195     //! Get the absolute value of a number.
4196     /**
4197        \note This function is different from <tt>std::abs()</tt> or <tt>std::fabs()</tt>
4198        because it is able to consider a variable of any type, without cast needed.
4199     **/
4200     template<typename T>
4201     inline T abs(const T a) {
4202       return a>=0?a:-a;
4203     }
4204     inline bool abs(const bool a) {
4205       return a;
4206     }
4207     inline unsigned char abs(const unsigned char a) {
4208       return a;
4209     }
4210     inline unsigned short abs(const unsigned short a) {
4211       return a;
4212     }
4213     inline unsigned int abs(const unsigned int a) {
4214       return a;
4215     }
4216     inline unsigned long abs(const unsigned long a) {
4217       return a;
4218     }
4219     inline double abs(const double a) {
4220       return std::fabs(a);
4221     }
4222     inline float abs(const float a) {
4223       return (float)std::fabs((double)a);
4224     }
4225     inline int abs(const int a) {
4226       return std::abs(a);
4227     }
4228 
4229     //! Get the square of a number.
4230     template<typename T>
4231     inline T sqr(const T val) {
4232       return val*val;
4233     }
4234 
4235     //! Get <tt>1 + log_10(x)</tt>.
4236     inline int xln(const int x) {
4237       return x>0?(int)(1+std::log10((double)x)):1;
4238     }
4239 
4240     //! Get the minimum value between two numbers.
4241     template<typename t1, typename t2>
4242     inline typename cimg::superset<t1,t2>::type min(const t1& a, const t2& b) {
4243       typedef typename cimg::superset<t1,t2>::type t1t2;
4244       return (t1t2)(a<=b?a:b);
4245     }
4246 
4247     //! Get the minimum value between three numbers.
4248     template<typename t1, typename t2, typename t3>
4249     inline typename cimg::superset2<t1,t2,t3>::type min(const t1& a, const t2& b, const t3& c) {
4250       typedef typename cimg::superset2<t1,t2,t3>::type t1t2t3;
4251       return (t1t2t3)cimg::min(cimg::min(a,b),c);
4252     }
4253 
4254     //! Get the minimum value between four numbers.
4255     template<typename t1, typename t2, typename t3, typename t4>
4256     inline typename cimg::superset3<t1,t2,t3,t4>::type min(const t1& a, const t2& b, const t3& c, const t4& d) {
4257       typedef typename cimg::superset3<t1,t2,t3,t4>::type t1t2t3t4;
4258       return (t1t2t3t4)cimg::min(cimg::min(a,b,c),d);
4259     }
4260 
4261     //! Get the maximum value between two numbers.
4262     template<typename t1, typename t2>
4263     inline typename cimg::superset<t1,t2>::type max(const t1& a, const t2& b) {
4264       typedef typename cimg::superset<t1,t2>::type t1t2;
4265       return (t1t2)(a>=b?a:b);
4266     }
4267 
4268     //! Get the maximum value between three numbers.
4269     template<typename t1, typename t2, typename t3>
4270     inline typename cimg::superset2<t1,t2,t3>::type max(const t1& a, const t2& b, const t3& c) {
4271       typedef typename cimg::superset2<t1,t2,t3>::type t1t2t3;
4272       return (t1t2t3)cimg::max(cimg::max(a,b),c);
4273     }
4274 
4275     //! Get the maximum value between four numbers.
4276     template<typename t1, typename t2, typename t3, typename t4>
4277     inline typename cimg::superset3<t1,t2,t3,t4>::type max(const t1& a, const t2& b, const t3& c, const t4& d) {
4278       typedef typename cimg::superset3<t1,t2,t3,t4>::type t1t2t3t4;
4279       return (t1t2t3t4)cimg::max(cimg::max(a,b,c),d);
4280     }
4281 
4282     //! Get the sign of a number.
4283     template<typename T>
4284     inline T sign(const T x) {
4285       return (x<0)?(T)(-1):(x==0?(T)0:(T)1);
4286     }
4287 
4288     //! Get the nearest power of 2 higher than a given number.
4289     template<typename T>
4290     inline unsigned int nearest_pow2(const T x) {
4291       unsigned int i = 1;
4292       while (x>i) i<<=1;
4293       return i;
4294     }
4295 
4296     //! Get the sinc() of a given number.
4297     inline double sinc(const double x) {
4298       return x?std::sin(x)/x:1;
4299     }
4300 
4301     //! Get the modulo of a number.
4302     /**
4303        \note This modulo function accepts negative and floating-points modulo numbers, as well as
4304        variable of any type.
4305     **/
4306     template<typename T>
4307     inline T mod(const T& x, const T& m) {
4308       const double dx = (double)x, dm = (double)m;
4309       if (x<0) { return (T)(dm+dx+dm*std::floor(-dx/dm)); }
4310       return (T)(dx-dm*std::floor(dx/dm));
4311     }
4312     inline int mod(const bool x, const bool m) {
4313       return m?(x?1:0):0;
4314     }
4315     inline int mod(const char x, const char m) {
4316       return x>=0?x%m:(x%m?m+x%m:0);
4317     }
4318     inline int mod(const short x, const short m) {
4319       return x>=0?x%m:(x%m?m+x%m:0);
4320     }
4321     inline int mod(const int x, const int m) {
4322       return x>=0?x%m:(x%m?m+x%m:0);
4323     }
4324     inline int mod(const long x, const long m) {
4325       return x>=0?x%m:(x%m?m+x%m:0);
4326     }
4327     inline int mod(const unsigned char x, const unsigned char m) {
4328       return x%m;
4329     }
4330     inline int mod(const unsigned short x, const unsigned short m) {
4331       return x%m;
4332     }
4333     inline int mod(const unsigned int x, const unsigned int m) {
4334       return x%m;
4335     }
4336     inline int mod(const unsigned long x, const unsigned long m) {
4337       return x%m;
4338     }
4339 
4340     //! Get the minmod of two numbers.
4341     /**
4342        <i>minmod(\p a,\p b)</i> is defined to be :
4343        - <i>minmod(\p a,\p b) = min(\p a,\p b)</i>, if \p a and \p b have the same sign.
4344        - <i>minmod(\p a,\p b) = 0</i>, if \p a and \p b have different signs.
4345     **/
4346     template<typename T>
4347     inline T minmod(const T a, const T b) {
4348       return a*b<=0?0:(a>0?(a<b?a:b):(a<b?b:a));
4349     }
4350 
4351     //! Get a random variable between [0,1] with respect to an uniform distribution.
4352     inline double rand() {
4353       cimg::srand();
4354       return (double)std::rand()/RAND_MAX;
4355     }
4356 
4357     //! Get a random variable between [-1,1] with respect to an uniform distribution.
4358     inline double crand() {
4359       return 1-2*cimg::rand();
4360     }
4361 
4362     //! Get a random variable following a gaussian distribution and a standard deviation of 1.
4363     inline double grand() {
4364       double x1, w;
4365       do {
4366         const double x2 = 2*cimg::rand() - 1.0;
4367         x1 = 2*cimg::rand()-1.0;
4368         w = x1*x1 + x2*x2;
4369       } while (w<=0 || w>=1.0);
4370       return x1*std::sqrt((-2*std::log(w))/w);
4371     }
4372 
4373     //! Get a random variable following a Poisson distribution of parameter z.
4374     inline unsigned int prand(const double z) {
4375       if (z<=1.0e-10) return 0;
4376       if (z>100) return (unsigned int)((std::sqrt(z) * cimg::grand()) + z);
4377       unsigned int k = 0;
4378       const double y = std::exp(-z);
4379       for (double s = 1.0; s>=y; ++k) s*=cimg::rand();
4380       return k-1;
4381     }
4382 
4383     //! Get a rounded number.
4384     /**
4385        \param x : the number to be rounded.
4386        \param y : the rounding precision.
4387        \param rounding_type : the type of rounding (0=nearest, -1=backward, 1=forward).
4388        \return the rounded value, with the same type as parameter x.
4389     **/
4390     template<typename T>
4391     inline T round(const T x, const double y=1, const int rounding_type=0) {
4392       if (y<=0) return x;
4393       const double delta = cimg::mod((double)x,y);
4394       if (delta==0.0) return x;
4395       const double backward = x - delta, forward = backward + y;
4396       return (T)(rounding_type<0?backward:rounding_type>0?forward:2*delta<y?backward:2*delta>y?forward:x<0?backward:forward);
4397     }
4398 
4399     inline double _pythagore(double a, double b) {
4400       const double absa = cimg::abs(a), absb = cimg::abs(b);
4401       if (absa>absb) { const double tmp = absb/absa; return absa*std::sqrt(1.0+tmp*tmp); }
4402       else { const double tmp = absa/absb; return (absb==0?0:absb*std::sqrt(1.0+tmp*tmp)); }
4403     }
4404 
4405     //! Remove the 'case' of an ASCII character.
4406     inline char uncase(const char x) {
4407       return (char)((x<'A'||x>'Z')?x:x-'A'+'a');
4408     }
4409 
4410     //! Remove the 'case' of a C string.
4411     /**
4412        Acts in-place.
4413     **/
4414     inline void uncase(char *const string) {
4415       if (string) for (char *ptr = string; *ptr; ++ptr) *ptr = uncase(*ptr);
4416     }
4417 
4418     //! Read a double number from a C-string.
4419     /**
4420        \note This function is quite similar to <tt>std::atof()</tt>,
4421        but that it allows the retrieval of fractions as in "1/2".
4422     **/
4423     inline double atof(const char *const str) {
4424       double x = 0, y = 1;
4425       if (!str) return 0; else { std::sscanf(str,"%lf/%lf",&x,&y); return x/y; }
4426     }
4427 
4428     //! Compare the first \p n characters of two C-strings, ignoring the case.
4429     /**
4430        \note This function is defined since it is not provided by all compilers
4431        (not an ANSI function).
4432     **/
4433     inline int strncasecmp(const char *const s1, const char *const s2, const int l) {
4434       if (!l) return 0;
4435       if (!s1) return s2?-1:0;
4436       const char *ns1 = s1, *ns2 = s2;
4437       int k, diff = 0; for (k = 0; k<l && !(diff = uncase(*ns1)-uncase(*ns2)); ++k) { ++ns1; ++ns2; }
4438       return k!=l?diff:0;
4439     }
4440 
4441     //! Compare two C-strings, ignoring the case.
4442     /**
4443        \note This function is defined since it is not provided by all compilers
4444        (not an ANSI function).
4445     **/
4446     inline int strcasecmp(const char *const s1, const char *const s2) {
4447       if (!s1) return s2?-1:0;
4448       const unsigned int l1 = std::strlen(s1), l2 = std::strlen(s2);
4449       return cimg::strncasecmp(s1,s2,1+(l1<l2?l1:l2));
4450     }
4451 
4452     //! Remove useless delimiters on the borders of a C-string
4453     inline bool strpare(char *const s, const char delimiter=' ', const bool symmetric=false, const bool is_iterative=false) {
4454       if (!s) return false;
4455       const int l = (int)std::strlen(s);
4456       int p, q;
4457       if (symmetric) for (p = 0, q = l-1; p<q && s[p]==delimiter && s[q]==delimiter; ) { --q; ++p; if (!is_iterative) break; }
4458       else {
4459         for (p = 0; p<l && s[p]==delimiter; ) { ++p; if (!is_iterative) break; }
4460         for (q = l-1; q>p && s[q]==delimiter; ) { --q; if (!is_iterative) break; }
4461       }
4462       const int n = q - p + 1;
4463       if (n!=l) { std::memmove(s,s+p,n); s[n] = 0; return true; }
4464       return false;
4465     }
4466 
4467     //! Replace explicit escape sequences '\x' in C-strings.
4468     inline void strescape(char *const s) {
4469 #define cimg_strescape(ci,co) case ci: *nd = co; ++ns; break;
4470       static unsigned int val = 0;
4471       for (char *ns = s, *nd = s; *ns || (bool)(*nd=0); ++nd) if (*ns=='\\') switch (*(++ns)) {
4472             cimg_strescape('n','\n');
4473             cimg_strescape('t','\t');
4474             cimg_strescape('v','\v');
4475             cimg_strescape('b','\b');
4476             cimg_strescape('r','\r');
4477             cimg_strescape('f','\f');
4478             cimg_strescape('a','\a');
4479             cimg_strescape('\\','\\');
4480             cimg_strescape('\?','\?');
4481             cimg_strescape('\'','\'');
4482             cimg_strescape('\"','\"');
4483           case 0 : *nd = 0; break;
4484           case '0' : case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' :
4485             std::sscanf(ns,"%o",&val); while (*ns>='0' && *ns<='7') ++ns;
4486             *nd = val; break;
4487           case 'x':
4488             std::sscanf(++ns,"%x",&val); while ((*ns>='0' && *ns<='7') || (*ns>='a' && *ns<='f') || (*ns>='A' && *ns<='F')) ++ns;
4489             *nd = val; break;
4490           default : *nd = *(ns++);
4491           } else *nd = *(ns++);
4492     }
4493 
4494     // Get a temporary string describing the size of a buffer.
4495     inline const char *strbuffersize(const unsigned long size) {
4496       static char res[256] = { 0 };
4497       if (size<1024LU) cimg_snprintf(res,sizeof(res),"%lu byte%s",size,size>1?"s":"");
4498       else if (size<1024*1024LU) { const float nsize = size/1024.0f; cimg_snprintf(res,sizeof(res),"%.1f Kb",nsize); }
4499       else if (size<1024*1024*1024LU) { const float nsize = size/(1024*1024.0f); cimg_snprintf(res,sizeof(res),"%.1f Mb",nsize); }
4500       else { const float nsize = size/(1024*1024*1024.0f); cimg_snprintf(res,sizeof(res),"%.1f Gb",nsize); }
4501       return res;
4502     }
4503 
4504     //! Compute the basename of a filename.
4505     inline const char* basename(const char *const s)  {
4506       const char *p = 0;
4507       for (const char *np = s; np>=s && (p=np); np = std::strchr(np,cimg_file_separator)+1) {}
4508       return p;
4509     }
4510 
4511     // Generate a random filename.
4512     inline const char* filenamerand() {
4513       static char randomid[9] = { 0,0,0,0,0,0,0,0,0 };
4514       cimg::srand();
4515       for (unsigned int k = 0; k<8; ++k) {
4516         const int v = (int)std::rand()%3;
4517         randomid[k] = (char)(v==0?('0'+(std::rand()%10)):(v==1?('a'+(std::rand()%26)):('A'+(std::rand()%26))));
4518       }
4519       return randomid;
4520     }
4521 
4522     // Convert filename into a Windows-style filename.
4523     inline void winformat_string(char *const s) {
4524       if (s && *s) {
4525 #if cimg_OS==2
4526         char *const ns = new char[MAX_PATH];
4527         if (GetShortPathNameA(s,ns,MAX_PATH)) std::strcpy(s,ns);
4528 #endif
4529       }
4530     }
4531 
4532     //! Open a file, and check for possible errors.
4533     inline std::FILE *fopen(const char *const path, const char *const mode) {
4534       if (!path)
4535         throw CImgArgumentException("cimg::fopen() : Specified file path is (null).");
4536       if (!mode)
4537         throw CImgArgumentException("cimg::fopen() : File '%s', specified mode is (null).",
4538                                     path);
4539       std::FILE *res = 0;
4540       if (*path=='-' && path[1]=='.') {
4541         res = (*mode=='r')?stdin:stdout;
4542 #if cimg_OS==2
4543         if (*mode && mode[1]=='b') { // Force stdin/stdout to be in binary mode.
4544           if (_setmode(_fileno(res),0x8000)==-1) res = 0;
4545         }
4546 #endif
4547       } else res = std::fopen(path,mode);
4548       if (!res) throw CImgIOException("cimg::fopen() : Failed to open file '%s' with mode '%s'.",
4549                                       path,mode);
4550       return res;
4551     }
4552 
4553     //! Close a file, and check for possible errors.
4554     inline int fclose(std::FILE *file) {
4555       if (!file) warn("cimg::fclose() : Specified file is (null).");
4556       if (!file || file==stdin || file==stdout) return 0;
4557       const int errn = std::fclose(file);
4558       if (errn!=0) warn("cimg::fclose() : Error code %d returned during file closing.",
4559                         errn);
4560       return errn;
4561     }
4562 
4563     //! Get or set path to store temporary files.
4564     inline const char* temporary_path(const char *const user_path=0, const bool reinit_path=false) {
4565 #define _cimg_test_temporary_path(p) \
4566       if (!path_found) { \
4567         cimg_snprintf(st_path,1024,"%s",p); \
4568         cimg_snprintf(tmp,sizeof(tmp),"%s%c%s",st_path,cimg_file_separator,filetmp); \
4569         if ((file=std::fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } \
4570       }
4571       static char *st_path = 0;
4572       if (reinit_path) { delete[] st_path; st_path = 0; }
4573       if (user_path) {
4574         if (!st_path) st_path = new char[1024];
4575         std::memset(st_path,0,1024);
4576         std::strncpy(st_path,user_path,1023);
4577       } else if (!st_path) {
4578         st_path = new char[1024];
4579         std::memset(st_path,0,1024);
4580         bool path_found = false;
4581         char tmp[1024] = { 0 }, filetmp[512] = { 0 };
4582         std::FILE *file = 0;
4583         cimg_snprintf(filetmp,sizeof(filetmp),"%s.tmp",cimg::filenamerand());
4584         char *tmpPath = std::getenv("TMP");
4585         if (!tmpPath) { tmpPath = std::getenv("TEMP"); winformat_string(tmpPath); }
4586         if (tmpPath) _cimg_test_temporary_path(tmpPath);
4587 #if cimg_OS==2
4588         _cimg_test_temporary_path("C:\\WINNT\\Temp");
4589         _cimg_test_temporary_path("C:\\WINDOWS\\Temp");
4590         _cimg_test_temporary_path("C:\\Temp");
4591         _cimg_test_temporary_path("C:");
4592         _cimg_test_temporary_path("D:\\WINNT\\Temp");
4593         _cimg_test_temporary_path("D:\\WINDOWS\\Temp");
4594         _cimg_test_temporary_path("D:\\Temp");
4595         _cimg_test_temporary_path("D:");
4596 #else
4597         _cimg_test_temporary_path("/tmp");
4598         _cimg_test_temporary_path("/var/tmp");
4599 #endif
4600         if (!path_found) {
4601           *st_path = 0;
4602           std::strncpy(tmp,filetmp,sizeof(tmp)-1);
4603           if ((file=std::fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; }
4604         }
4605         if (!path_found)
4606           throw CImgIOException("cimg::temporary_path() : Failed to locate path for writing temporary files.\n");
4607       }
4608       return st_path;
4609     }
4610 
4611     // Get or set path to the "Program files/" directory (windows only).
4612 #if cimg_OS==2
4613     inline const char* programfiles_path(const char *const user_path=0, const bool reinit_path=false) {
4614       static char *st_path = 0;
4615       if (reinit_path) { delete[] st_path; st_path = 0; }
4616       if (user_path) {
4617         if (!st_path) st_path = new char[1024];
4618         std::memset(st_path,0,1024);
4619         std::strncpy(st_path,user_path,1023);
4620       } else if (!st_path) {
4621         st_path = new char[MAX_PATH];
4622         std::memset(st_path,0,MAX_PATH);
4623         // Note : in the following line, 0x26 = CSIDL_PROGRAM_FILES (not defined on every compiler).
4624 #if !defined(__INTEL_COMPILER)
4625         if (!SHGetSpecialFolderPathA(0,st_path,0x0026,false)) {
4626           const char *const pfPath = std::getenv("PROGRAMFILES");
4627           if (pfPath) std::strncpy(st_path,pfPath,MAX_PATH-1);
4628           else std::strcpy(st_path,"C:\\PROGRA~1");
4629         }
4630 #else
4631         std::strcpy(st_path,"C:\\PROGRA~1");
4632 #endif
4633       }
4634       return st_path;
4635     }
4636 #endif
4637 
4638     //! Get or set path to the ImageMagick's \c convert tool.
4639     inline const char* imagemagick_path(const char *const user_path=0, const bool reinit_path=false) {
4640       static char *st_path = 0;
4641       if (reinit_path) { delete[] st_path; st_path = 0; }
4642       if (user_path) {
4643         if (!st_path) st_path = new char[1024];
4644         std::memset(st_path,0,1024);
4645         std::strncpy(st_path,user_path,1023);
4646       } else if (!st_path) {
4647         st_path = new char[1024];
4648         std::memset(st_path,0,1024);
4649         bool path_found = false;
4650         std::FILE *file = 0;
4651 #if cimg_OS==2
4652         const char *const pf_path = programfiles_path();
4653         if (!path_found) {
4654           std::strcpy(st_path,".\\convert.exe");
4655           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4656         }
4657         for (int k = 32; k>=10 && !path_found; --k) {
4658           cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%.2d-\\convert.exe",pf_path,k);
4659           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4660         }
4661         for (int k = 9; k>=0 && !path_found; --k) {
4662           cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%d-Q\\convert.exe",pf_path,k);
4663           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4664         }
4665         for (int k = 32; k>=0 && !path_found; --k) {
4666           cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%d\\convert.exe",pf_path,k);
4667           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4668         }
4669         for (int k = 32; k>=10 && !path_found; --k) {
4670           cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",pf_path,k);
4671           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4672         }
4673         for (int k = 9; k>=0 && !path_found; --k) {
4674           cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",pf_path,k);
4675           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4676         }
4677         for (int k = 32; k>=0 && !path_found; --k) {
4678           cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",pf_path,k);
4679           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4680         }
4681         for (int k = 32; k>=10 && !path_found; --k) {
4682           cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%.2d-\\convert.exe",k);
4683           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4684         }
4685         for (int k = 9; k>=0 && !path_found; --k) {
4686           cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%d-Q\\convert.exe",k);
4687           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4688         }
4689         for (int k = 32; k>=0 && !path_found; --k) {
4690           cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%d\\convert.exe",k);
4691           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4692         }
4693         for (int k = 32; k>=10 && !path_found; --k) {
4694           cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",k);
4695           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4696         }
4697         for (int k = 9; k>=0 && !path_found; --k) {
4698           cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",k);
4699           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4700         }
4701         for (int k = 32; k>=0 && !path_found; --k) {
4702           cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",k);
4703           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4704         }
4705         for (int k = 32; k>=10 && !path_found; --k) {
4706           cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%.2d-\\convert.exe",k);
4707           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4708         }
4709         for (int k = 9; k>=0 && !path_found; --k) {
4710           cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%d-Q\\convert.exe",k);
4711           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4712         }
4713         for (int k = 32; k>=0 && !path_found; --k) {
4714           cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%d\\convert.exe",k);
4715           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4716         }
4717         for (int k = 32; k>=10 && !path_found; --k) {
4718           cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",k);
4719           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4720         }
4721         for (int k = 9; k>=0 && !path_found; --k) {
4722           cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",k);
4723           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4724         }
4725         for (int k = 32; k>=0 && !path_found; --k) {
4726           cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",k);
4727           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4728         }
4729         if (!path_found) std::strcpy(st_path,"convert.exe");
4730 #else
4731         if (!path_found) {
4732           std::strcpy(st_path,"./convert");
4733           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4734         }
4735         if (!path_found) std::strcpy(st_path,"convert");
4736 #endif
4737         winformat_string(st_path);
4738       }
4739       return st_path;
4740     }
4741 
4742     //! Get path of the GraphicsMagick's \c gm tool.
4743     inline const char* graphicsmagick_path(const char *const user_path=0, const bool reinit_path=false) {
4744       static char *st_path = 0;
4745       if (reinit_path) { delete[] st_path; st_path = 0; }
4746       if (user_path) {
4747         if (!st_path) st_path = new char[1024];
4748         std::memset(st_path,0,1024);
4749         std::strncpy(st_path,user_path,1023);
4750       } else if (!st_path) {
4751         st_path = new char[1024];
4752         std::memset(st_path,0,1024);
4753         bool path_found = false;
4754         std::FILE *file = 0;
4755 #if cimg_OS==2
4756         const char *const pf_path = programfiles_path();
4757         if (!path_found) {
4758           std::strcpy(st_path,".\\gm.exe");
4759           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4760         }
4761         for (int k = 32; k>=10 && !path_found; --k) {
4762           cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%.2d-\\gm.exe",pf_path,k);
4763           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4764         }
4765         for (int k = 9; k>=0 && !path_found; --k) {
4766           cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%d-Q\\gm.exe",pf_path,k);
4767           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4768         }
4769         for (int k = 32; k>=0 && !path_found; --k) {
4770           cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%d\\gm.exe",pf_path,k);
4771           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4772         }
4773         for (int k = 32; k>=10 && !path_found; --k) {
4774           cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",pf_path,k);
4775           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4776         }
4777         for (int k = 9; k>=0 && !path_found; --k) {
4778           cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",pf_path,k);
4779           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4780         }
4781         for (int k = 32; k>=0 && !path_found; --k) {
4782           cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",pf_path,k);
4783           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4784         }
4785         for (int k = 32; k>=10 && !path_found; --k) {
4786           cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%.2d-\\gm.exe",k);
4787           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4788         }
4789         for (int k = 9; k>=0 && !path_found; --k) {
4790           cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%d-Q\\gm.exe",k);
4791           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4792         }
4793         for (int k = 32; k>=0 && !path_found; --k) {
4794           cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%d\\gm.exe",k);
4795           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4796         }
4797         for (int k = 32; k>=10 && !path_found; --k) {
4798           cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k);
4799           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4800         }
4801         for (int k = 9; k>=0 && !path_found; --k) {
4802           cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k);
4803           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4804         }
4805         for (int k = 32; k>=0 && !path_found; --k) {
4806           cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k);
4807           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4808         }
4809         for (int k = 32; k>=10 && !path_found; --k) {
4810           cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%.2d-\\gm.exe",k);
4811           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4812         }
4813         for (int k = 9; k>=0 && !path_found; --k) {
4814           cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%d-Q\\gm.exe",k);
4815           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4816         }
4817         for (int k = 32; k>=0 && !path_found; --k) {
4818           cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%d\\gm.exe",k);
4819           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4820         }
4821         for (int k = 32; k>=10 && !path_found; --k) {
4822           cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k);
4823           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4824         }
4825         for (int k = 9; k>=0 && !path_found; --k) {
4826           cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k);
4827           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4828         }
4829         for (int k = 32; k>=0 && !path_found; --k) {
4830           cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k);
4831           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4832         }
4833         if (!path_found) std::strcpy(st_path,"gm.exe");
4834 #else
4835         if (!path_found) {
4836           std::strcpy(st_path,"./gm");
4837           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4838         }
4839         if (!path_found) std::strcpy(st_path,"gm");
4840 #endif
4841         winformat_string(st_path);
4842       }
4843       return st_path;
4844     }
4845 
4846     //! Get or set path of the \c XMedcon tool.
4847     inline const char* medcon_path(const char *const user_path=0, const bool reinit_path=false) {
4848       static char *st_path = 0;
4849       if (reinit_path) { delete[] st_path; st_path = 0; }
4850       if (user_path) {
4851         if (!st_path) st_path = new char[1024];
4852         std::memset(st_path,0,1024);
4853         std::strncpy(st_path,user_path,1023);
4854       } else if (!st_path) {
4855         st_path = new char[1024];
4856         std::memset(st_path,0,1024);
4857         bool path_found = false;
4858         std::FILE *file = 0;
4859 #if cimg_OS==2
4860         const char *const pf_path = programfiles_path();
4861         if (!path_found) {
4862           std::strcpy(st_path,".\\medcon.exe");
4863           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4864         }
4865         if (!path_found) {
4866           cimg_snprintf(st_path,sizeof(st_path),"%s\\XMedCon\\bin\\medcon.bat",pf_path);
4867           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4868         }
4869         if (!path_found) {
4870           cimg_snprintf(st_path,sizeof(st_path),"%s\\XMedCon\\bin\\medcon.exe",pf_path);
4871           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4872         }
4873         if (!path_found) std::strcpy(st_path,"medcon.exe");
4874 #else
4875         if (!path_found) {
4876           std::strcpy(st_path,"./medcon");
4877           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4878         }
4879         if (!path_found) std::strcpy(st_path,"medcon");
4880 #endif
4881         winformat_string(st_path);
4882       }
4883       return st_path;
4884     }
4885 
4886     //! Get or set path to the 'ffmpeg' command.
4887     inline const char *ffmpeg_path(const char *const user_path=0, const bool reinit_path=false) {
4888       static char *st_path = 0;
4889       if (reinit_path) { delete[] st_path; st_path = 0; }
4890       if (user_path) {
4891         if (!st_path) st_path = new char[1024];
4892         std::memset(st_path,0,1024);
4893         std::strncpy(st_path,user_path,1023);
4894       } else if (!st_path) {
4895         st_path = new char[1024];
4896         std::memset(st_path,0,1024);
4897         bool path_found = false;
4898         std::FILE *file = 0;
4899 #if cimg_OS==2
4900         if (!path_found) {
4901           std::strcpy(st_path,".\\ffmpeg.exe");
4902           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4903         }
4904         if (!path_found) std::strcpy(st_path,"ffmpeg.exe");
4905 #else
4906         if (!path_found) {
4907           std::strcpy(st_path,"./ffmpeg");
4908           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4909         }
4910         if (!path_found) std::strcpy(st_path,"ffmpeg");
4911 #endif
4912         winformat_string(st_path);
4913       }
4914       return st_path;
4915     }
4916 
4917     //! Get or set path to the 'gzip' command.
4918     inline const char *gzip_path(const char *const user_path=0, const bool reinit_path=false) {
4919       static char *st_path = 0;
4920       if (reinit_path) { delete[] st_path; st_path = 0; }
4921       if (user_path) {
4922         if (!st_path) st_path = new char[1024];
4923         std::memset(st_path,0,1024);
4924         std::strncpy(st_path,user_path,1023);
4925       } else if (!st_path) {
4926         st_path = new char[1024];
4927         std::memset(st_path,0,1024);
4928         bool path_found = false;
4929         std::FILE *file = 0;
4930 #if cimg_OS==2
4931         if (!path_found) {
4932           std::strcpy(st_path,".\\gzip.exe");
4933           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4934         }
4935         if (!path_found) std::strcpy(st_path,"gzip.exe");
4936 #else
4937         if (!path_found) {
4938           std::strcpy(st_path,"./gzip");
4939           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4940         }
4941         if (!path_found) std::strcpy(st_path,"gzip");
4942 #endif
4943         winformat_string(st_path);
4944       }
4945       return st_path;
4946     }
4947 
4948     //! Get or set path to the 'gunzip' command.
4949     inline const char *gunzip_path(const char *const user_path=0, const bool reinit_path=false) {
4950       static char *st_path = 0;
4951       if (reinit_path) { delete[] st_path; st_path = 0; }
4952       if (user_path) {
4953         if (!st_path) st_path = new char[1024];
4954         std::memset(st_path,0,1024);
4955         std::strncpy(st_path,user_path,1023);
4956       } else if (!st_path) {
4957         st_path = new char[1024];
4958         std::memset(st_path,0,1024);
4959         bool path_found = false;
4960         std::FILE *file = 0;
4961 #if cimg_OS==2
4962         if (!path_found) {
4963           std::strcpy(st_path,".\\gunzip.exe");
4964           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4965         }
4966         if (!path_found) std::strcpy(st_path,"gunzip.exe");
4967 #else
4968         if (!path_found) {
4969           std::strcpy(st_path,"./gunzip");
4970           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4971         }
4972         if (!path_found) std::strcpy(st_path,"gunzip");
4973 #endif
4974         winformat_string(st_path);
4975       }
4976       return st_path;
4977     }
4978 
4979     //! Get or set path to the 'dcraw' command.
4980     inline const char *dcraw_path(const char *const user_path=0, const bool reinit_path=false) {
4981       static char *st_path = 0;
4982       if (reinit_path) { delete[] st_path; st_path = 0; }
4983       if (user_path) {
4984         if (!st_path) st_path = new char[1024];
4985         std::memset(st_path,0,1024);
4986         std::strncpy(st_path,user_path,1023);
4987       } else if (!st_path) {
4988         st_path = new char[1024];
4989         std::memset(st_path,0,1024);
4990         bool path_found = false;
4991         std::FILE *file = 0;
4992 #if cimg_OS==2
4993         if (!path_found) {
4994           std::strcpy(st_path,".\\dcraw.exe");
4995           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4996         }
4997         if (!path_found) std::strcpy(st_path,"dcraw.exe");
4998 #else
4999         if (!path_found) {
5000           std::strcpy(st_path,"./dcraw");
5001           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5002         }
5003         if (!path_found) std::strcpy(st_path,"dcraw");
5004 #endif
5005         winformat_string(st_path);
5006       }
5007       return st_path;
5008     }
5009 
5010     //! Get or set path to the 'wget' command.
5011     inline const char *wget_path(const char *const user_path=0, const bool reinit_path=false) {
5012       static char *st_path = 0;
5013       if (reinit_path) { delete[] st_path; st_path = 0; }
5014       if (user_path) {
5015         if (!st_path) st_path = new char[1024];
5016         std::memset(st_path,0,1024);
5017         std::strncpy(st_path,user_path,1023);
5018       } else if (!st_path) {
5019         st_path = new char[1024];
5020         std::memset(st_path,0,1024);
5021         bool path_found = false;
5022         std::FILE *file = 0;
5023 #if cimg_OS==2
5024         if (!path_found) {
5025           std::strcpy(st_path,".\\wget.exe");
5026           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5027         }
5028         if (!path_found) std::strcpy(st_path,"wget.exe");
5029 #else
5030         if (!path_found) {
5031           std::strcpy(st_path,"./wget");
5032           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5033         }
5034         if (!path_found) std::strcpy(st_path,"wget");
5035 #endif
5036         winformat_string(st_path);
5037       }
5038       return st_path;
5039     }
5040 
5041     //! Get or set path to the 'curl' command.
5042     inline const char *curl_path(const char *const user_path=0, const bool reinit_path=false) {
5043       static char *st_path = 0;
5044       if (reinit_path) { delete[] st_path; st_path = 0; }
5045       if (user_path) {
5046         if (!st_path) st_path = new char[1024];
5047         std::memset(st_path,0,1024);
5048         std::strncpy(st_path,user_path,1023);
5049       } else if (!st_path) {
5050         st_path = new char[1024];
5051         std::memset(st_path,0,1024);
5052         bool path_found = false;
5053         std::FILE *file = 0;
5054 #if cimg_OS==2
5055         if (!path_found) {
5056           std::strcpy(st_path,".\\curl.exe");
5057           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5058         }
5059         if (!path_found) std::strcpy(st_path,"curl.exe");
5060 #else
5061         if (!path_found) {
5062           std::strcpy(st_path,"./curl");
5063           if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5064         }
5065         if (!path_found) std::strcpy(st_path,"curl");
5066 #endif
5067         winformat_string(st_path);
5068       }
5069       return st_path;
5070     }
5071 
5072     //! Split a filename into two strings 'body' and 'extension'.
5073     inline const char *split_filename(const char *const filename, char *const body=0) {
5074       if (!filename) { if (body) *body = 0; return 0; }
5075       const char *p = 0; for (const char *np = filename; np>=filename && (p=np); np = std::strchr(np,'.')+1) {}
5076       if (p==filename) {
5077         if (body) std::strcpy(body,filename);
5078         return filename + std::strlen(filename);
5079       }
5080       const unsigned int l = p - filename - 1;
5081       if (body) { std::memcpy(body,filename,l); body[l] = 0; }
5082       return p;
5083     }
5084 
5085     //! Create a numbered version of a filename.
5086     inline char* number_filename(const char *const filename, const int number, const unsigned int n, char *const string) {
5087       if (!filename) { if (string) *string = 0; return 0; }
5088       char format[1024] = { 0 }, body[1024] = { 0 };
5089       const char *const ext = cimg::split_filename(filename,body);
5090       if (n>0) cimg_snprintf(format,sizeof(format),"%s_%%.%ud.%s",body,n,ext);
5091       else cimg_snprintf(format,sizeof(format),"%s_%%d.%s",body,ext);
5092       std::sprintf(string,format,number);
5093       return string;
5094     }
5095 
5096     //! Try to guess the image format of a filename, using the magic numbers in its header.
5097     inline const char *file_type(std::FILE *const file, const char *const filename) {
5098       if (!file && !filename)
5099         throw CImgArgumentException("cimg::file_type() : Specified filename is (null).");
5100       static const char
5101         *const _pnm = "pnm",
5102         *const _pfm = "pfm",
5103         *const _bmp = "bmp",
5104         *const _gif = "gif",
5105         *const _jpg = "jpg",
5106         *const _off = "off",
5107         *const _pan = "pan",
5108         *const _png = "png",
5109         *const _tif = "tif",
5110         *const _inr = "inr",
5111         *const _dcm = "dcm";
5112       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
5113       const char *f_type = 0, *head;
5114       char header[2048] = { 0 }, item[1024] = { 0 };
5115       const unsigned char *const uheader = (unsigned char*)header;
5116       int err; char cerr;
5117       const unsigned int siz = (unsigned int)std::fread(header,2048,1,nfile);   // Read first 2048 bytes.
5118       if (!file) cimg::fclose(nfile);
5119 
5120       if (!std::strncmp(header,"OFF\n",4)) f_type = _off; // Check for OFF format.
5121       else if (!std::strncmp(header,"#INRIMAGE",9)) f_type = _inr; // Check for INRIMAGE format.
5122       else if (!std::strncmp(header,"PANDORE",7)) f_type = _pan; // Check for PANDORE format.
5123       else if (!std::strncmp(header+128,"DICM",4)) f_type = _dcm; // Check for DICOM format.
5124       else if (uheader[0]==0xFF && uheader[1]==0xD8 && uheader[2]==0xFF) f_type = _jpg;  // Check for JPEG format.
5125       else if (header[0]=='B' && header[1]=='M') f_type = _bmp;  // Check for BMP format.
5126       else if (header[0]=='G' && header[1]=='I' && header[2]=='F' && header[3]=='8' && header[5]=='a' && // Check for GIF format.
5127                (header[4]=='7' || header[4]=='9')) f_type = _gif;
5128       else if (uheader[0]==0x89 && uheader[1]==0x50 && uheader[2]==0x4E && uheader[3]==0x47 &&  // Check for PNG format.
5129                uheader[4]==0x0D && uheader[5]==0x0A && uheader[6]==0x1A && uheader[7]==0x0A) f_type = _png;
5130       else if ((uheader[0]==0x49 && uheader[1]==0x49) || (uheader[0]==0x4D && uheader[1]==0x4D)) f_type = _tif; // Check for TIFF format.
5131       else { // Check for PNM or PFM format.
5132         head = header;
5133         while (head<header+siz && (err=std::sscanf(head,"%1023[^\n]",item))!=EOF && (*item=='#' || !err))
5134           head+=1+(err?std::strlen(item):0);
5135         if (std::sscanf(item," P%d",&err)==1) f_type = _pnm;
5136         else if (std::sscanf(item," P%c",&cerr)==1 && (cerr=='f' || cerr=='F')) f_type = _pfm;
5137       }
5138       return f_type;
5139     }
5140 
5141     //! Read file data, and check for possible errors.
5142     template<typename T>
5143     inline int fread(T *const ptr, const unsigned int nmemb, std::FILE *stream) {
5144       if (!ptr || nmemb<=0 || !stream)
5145         throw CImgArgumentException("cimg::fread() : Invalid reading request of %u %s%s from file %p to buffer %p.",
5146                                     nmemb,cimg::type<T>::string(),nmemb>1?"s":"",stream,ptr);
5147 
5148       const unsigned long wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T);
5149       unsigned int to_read = nmemb, al_read = 0, l_to_read = 0, l_al_read = 0;
5150       do {
5151         l_to_read = (to_read*sizeof(T))<wlimitT?to_read:wlimit;
5152         l_al_read = (unsigned int)std::fread((void*)(ptr+al_read),sizeof(T),l_to_read,stream);
5153         al_read+=l_al_read;
5154         to_read-=l_al_read;
5155       } while (l_to_read==l_al_read && to_read>0);
5156       if (to_read>0)
5157         warn("cimg::fread() : Only %u/%u elements could be read from file.",
5158              al_read,nmemb);
5159       return al_read;
5160     }
5161 
5162     //! Write data to a file, and check for possible errors.
5163     template<typename T>
5164     inline int fwrite(const T *ptr, const unsigned int nmemb, std::FILE *stream) {
5165       if (!ptr || !stream)
5166         throw CImgArgumentException("cimg::fwrite() : Invalid writing request of %u %s%s from buffer %p to file %p.",
5167                                     nmemb,cimg::type<T>::string(),nmemb>1?"s":"",ptr,stream);
5168       if (nmemb<=0) return 0;
5169       const unsigned long wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T);
5170       unsigned int to_write = nmemb, al_write = 0, l_to_write = 0, l_al_write = 0;
5171       do {
5172         l_to_write = (to_write*sizeof(T))<wlimitT?to_write:wlimit;
5173         l_al_write = (unsigned int)std::fwrite((void*)(ptr+al_write),sizeof(T),l_to_write,stream);
5174         al_write+=l_al_write;
5175         to_write-=l_al_write;
5176       } while (l_to_write==l_al_write && to_write>0);
5177       if (to_write>0)
5178         warn("cimg::fwrite() : Only %u/%u elements could be written in file.",
5179              al_write,nmemb);
5180       return al_write;
5181     }
5182 
5183     //! Load file from network as a local temporary file, using 'wget' or 'curl' if found.
5184     inline char *load_network_external(const char *const filename, char *const filename_local) {
5185       if (!filename)
5186         throw CImgArgumentException("cimg::load_network_external() : Specified filename is (null).");
5187       if (!filename_local)
5188         throw CImgArgumentException("cimg::load_network_external() : Specified destination string is (null).");
5189       const char *const _ext = cimg::split_filename(filename), *const ext = (*_ext && _ext>filename)?_ext-1:_ext;
5190       char command[1024] = { 0 };
5191       std::FILE *file = 0;
5192       *filename_local = 0;
5193       do {
5194         cimg_snprintf(filename_local,512,"%s%c%s%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
5195         if ((file=std::fopen(filename_local,"rb"))!=0) cimg::fclose(file);
5196       } while (file);
5197 
5198       // Try with 'curl' first.
5199       cimg_snprintf(command,sizeof(command),"%s -f --silent --compressed -o \"%s\" \"%s\"",cimg::curl_path(),filename_local,filename);
5200       cimg::system(command);
5201       if (!(file = std::fopen(filename_local,"rb"))) {
5202 
5203         // Try with 'wget' else.
5204         cimg_snprintf(command,sizeof(command),"%s -q -r -l 0 --no-cache -O \"%s\" \"%s\"",cimg::wget_path(),filename_local,filename);
5205         cimg::system(command);
5206         if (!(file = std::fopen(filename_local,"rb")))
5207           throw CImgIOException("cimg::load_network_external() : Failed to load file '%s' with external tools 'wget' or 'curl'.",filename);
5208         cimg::fclose(file);
5209 
5210         // Try gunzip it.
5211         cimg_snprintf(command,sizeof(command),"%s.gz",filename_local);
5212         std::rename(filename_local,command);
5213         cimg_snprintf(command,sizeof(command),"%s --quiet %s.gz",gunzip_path(),filename_local);
5214         cimg::system(command);
5215         file = std::fopen(filename_local,"rb");
5216         if (!file) {
5217           cimg_snprintf(command,sizeof(command),"%s.gz",filename_local);
5218           std::rename(command,filename_local);
5219           file = std::fopen(filename_local,"rb");
5220         }
5221       }
5222       std::fseek(file,0,SEEK_END); // Check if file size is 0.
5223       if (!(unsigned int)std::ftell(file))
5224         throw CImgIOException("cimg::load_network_external() : Failed to load file '%s' with external commands 'wget' or 'curl'.",filename);
5225       cimg::fclose(file);
5226       return filename_local;
5227     }
5228 
5229     inline const char* option(const char *const name, const int argc, const char *const *const argv,
5230                               const char *const defaut, const char *const usage, const bool reset_static) {
5231       static bool first = true, visu = false;
5232       if (reset_static) { first = true; return 0; }
5233       const char *res = 0;
5234       if (first) {
5235         first = false;
5236         visu = cimg::option("-h",argc,argv,(char*)0,(char*)0,false)!=0;
5237         visu |= cimg::option("-help",argc,argv,(char*)0,(char*)0,false)!=0;
5238         visu |= cimg::option("--help",argc,argv,(char*)0,(char*)0,false)!=0;
5239       }
5240       if (!name && visu) {
5241         if (usage) {
5242           std::fprintf(cimg::output(),"\n %s%s%s",cimg::t_red,cimg::basename(argv[0]),cimg::t_normal);
5243           std::fprintf(cimg::output()," : %s",usage);
5244           std::fprintf(cimg::output()," (%s, %s)\n\n",__DATE__,__TIME__);
5245         }
5246         if (defaut) std::fprintf(cimg::output(),"%s\n",defaut);
5247       }
5248       if (name) {
5249         if (argc>0) {
5250           int k = 0;
5251           while (k<argc && std::strcmp(argv[k],name)) ++k;
5252           res = (k++==argc?defaut:(k==argc?argv[--k]:argv[k]));
5253         } else res = defaut;
5254         if (visu && usage) std::fprintf(cimg::output(),"    %s%-16s%s %-24s %s%s%s\n",
5255                                         cimg::t_bold,name,cimg::t_normal,res?res:"0",cimg::t_green,usage,cimg::t_normal);
5256       }
5257       return res;
5258     }
5259 
5260     inline const char* option(const char *const name, const int argc, const char *const *const argv,
5261                               const char *const defaut, const char *const usage=0) {
5262       return option(name,argc,argv,defaut,usage,false);
5263     }
5264 
5265     inline bool option(const char *const name, const int argc, const char *const *const argv,
5266                        const bool defaut, const char *const usage=0) {
5267       const char *const s = cimg::option(name,argc,argv,(char*)0);
5268       const bool res = s?(cimg::strcasecmp(s,"false") && cimg::strcasecmp(s,"off") && cimg::strcasecmp(s,"0")):defaut;
5269       cimg::option(name,0,0,res?"true":"false",usage);
5270       return res;
5271     }
5272 
5273     inline int option(const char *const name, const int argc, const char *const *const argv,
5274                       const int defaut, const char *const usage=0) {
5275       const char *const s = cimg::option(name,argc,argv,(char*)0);
5276       const int res = s?std::atoi(s):defaut;
5277       char tmp[256] = { 0 };
5278       cimg_snprintf(tmp,sizeof(tmp),"%d",res);
5279       cimg::option(name,0,0,tmp,usage);
5280       return res;
5281     }
5282 
5283     inline char option(const char *const name, const int argc, const char *const *const argv,
5284                        const char defaut, const char *const usage=0) {
5285       const char *const s = cimg::option(name,argc,argv,(char*)0);
5286       const char res = s?*s:defaut;
5287       char tmp[8] = { 0 };
5288       *tmp = res;
5289       cimg::option(name,0,0,tmp,usage);
5290       return res;
5291     }
5292 
5293     inline float option(const char *const name, const int argc, const char *const *const argv,
5294                         const float defaut, const char *const usage=0) {
5295       const char *const s = cimg::option(name,argc,argv,(char*)0);
5296       const float res = s?(float)cimg::atof(s):defaut;
5297       char tmp[256] = { 0 };
5298       cimg_snprintf(tmp,sizeof(tmp),"%g",res);
5299       cimg::option(name,0,0,tmp,usage);
5300       return res;
5301     }
5302 
5303     inline double option(const char *const name, const int argc, const char *const *const argv,
5304                          const double defaut, const char *const usage=0) {
5305       const char *const s = cimg::option(name,argc,argv,(char*)0);
5306       const double res = s?cimg::atof(s):defaut;
5307       char tmp[256] = { 0 };
5308       cimg_snprintf(tmp,sizeof(tmp),"%g",res);
5309       cimg::option(name,0,0,tmp,usage);
5310       return res;
5311     }
5312 
5313     inline const char* argument(const unsigned int nb, const int argc, const char *const *const argv, const unsigned int nb_singles=0, ...) {
5314       for (int k = 1, pos = 0; k<argc;) {
5315         const char *const item = argv[k];
5316         bool option = (*item=='-'), single_option = false;
5317         if (option) {
5318           va_list ap;
5319           va_start(ap,nb_singles);
5320           for (unsigned int i = 0; i<nb_singles; ++i) if (!cimg::strcasecmp(item,va_arg(ap,char*))) { single_option = true; break; }
5321           va_end(ap);
5322         }
5323         if (option) { ++k; if (!single_option) ++k; }
5324         else { if (pos++==(int)nb) return item; else ++k; }
5325       }
5326       return 0;
5327     }
5328 
5329     //! Print informations about %CImg environement variables.
5330     /**
5331        Printing is done on the standard error output.
5332     **/
5333     inline void info() {
5334       char tmp[1024] = { 0 };
5335       std::fprintf(cimg::output(),"\n %sCImg Library %u.%u.%u%s, compiled %s ( %s ) with the following flags :\n\n",
5336                    cimg::t_red,cimg_version/100,(cimg_version/10)%10,cimg_version%10,
5337                    cimg::t_normal,__DATE__,__TIME__);
5338 
5339       std::fprintf(cimg::output(),"  > Operating System :       %s%-13s%s %s('cimg_OS'=%d)%s\n",
5340                    cimg::t_bold,
5341                    cimg_OS==1?"Unix":(cimg_OS==2?"Windows":"Unknow"),
5342                    cimg::t_normal,cimg::t_green,
5343                    cimg_OS,
5344                    cimg::t_normal);
5345 
5346       std::fprintf(cimg::output(),"  > CPU endianness :         %s%s Endian%s\n",
5347                    cimg::t_bold,
5348                    cimg::endianness()?"Big":"Little",
5349                    cimg::t_normal);
5350 
5351       std::fprintf(cimg::output(),"  > Verbosity mode :         %s%-13s%s %s('cimg_verbosity'=%d)%s\n",
5352                    cimg::t_bold,
5353                    cimg_verbosity==0?"Quiet":(cimg_verbosity==1?"Console":(cimg_verbosity==2?"Dialog":(cimg_verbosity==3?"Console+Warnings":"Dialog+Warnings"))),
5354                    cimg::t_normal,cimg::t_green,
5355                    cimg_verbosity,
5356                    cimg::t_normal);
5357 
5358       std::fprintf(cimg::output(),"  > Stricts warnings :       %s%-13s%s %s('cimg_strict_warnings' %s)%s\n",
5359                    cimg::t_bold,
5360 #ifdef cimg_strict_warnings
5361                    "Yes",cimg::t_normal,cimg::t_green,"defined",
5362 #else
5363                    "No",cimg::t_normal,cimg::t_green,"undefined",
5364 #endif
5365                    cimg::t_normal);
5366 
5367       std::fprintf(cimg::output(),"  > Using VT100 messages :   %s%-13s%s %s('cimg_use_vt100' %s)%s\n",
5368                    cimg::t_bold,
5369 #ifdef cimg_use_vt100
5370                    "Yes",cimg::t_normal,cimg::t_green,"defined",
5371 #else
5372                    "No",cimg::t_normal,cimg::t_green,"undefined",
5373 #endif
5374                    cimg::t_normal);
5375 
5376       std::fprintf(cimg::output(),"  > Display type :           %s%-13s%s %s('cimg_display'=%d)%s\n",
5377                    cimg::t_bold,
5378                    cimg_display==0?"No display":cimg_display==1?"X11":cimg_display==2?"Windows GDI":"Unknown",
5379                    cimg::t_normal,cimg::t_green,
5380                    cimg_display,
5381                    cimg::t_normal);
5382 
5383 #if cimg_display==1
5384       std::fprintf(cimg::output(),"  > Using XShm for X11 :     %s%-13s%s %s('cimg_use_xshm' %s)%s\n",
5385                    cimg::t_bold,
5386 #ifdef cimg_use_xshm
5387                    "Yes",cimg::t_normal,cimg::t_green,"defined",
5388 #else
5389                    "No",cimg::t_normal,cimg::t_green,"undefined",
5390 #endif
5391                    cimg::t_normal);
5392 
5393       std::fprintf(cimg::output(),"  > Using XRand for X11 :    %s%-13s%s %s('cimg_use_xrandr' %s)%s\n",
5394                    cimg::t_bold,
5395 #ifdef cimg_use_xrandr
5396                    "Yes",cimg::t_normal,cimg::t_green,"defined",
5397 #else
5398                    "No",cimg::t_normal,cimg::t_green,"undefined",
5399 #endif
5400                    cimg::t_normal);
5401 #endif
5402       std::fprintf(cimg::output(),"  > Using OpenMP :           %s%-13s%s %s('cimg_use_openmp' %s)%s\n",
5403                    cimg::t_bold,
5404 #ifdef cimg_use_openmp
5405                    "Yes",cimg::t_normal,cimg::t_green,"defined",
5406 #else
5407                    "No",cimg::t_normal,cimg::t_green,"undefined",
5408 #endif
5409                    cimg::t_normal);
5410       std::fprintf(cimg::output(),"  > Using PNG library :      %s%-13s%s %s('cimg_use_png' %s)%s\n",
5411                    cimg::t_bold,
5412 #ifdef cimg_use_png
5413                    "Yes",cimg::t_normal,cimg::t_green,"defined",
5414 #else
5415                    "No",cimg::t_normal,cimg::t_green,"undefined",
5416 #endif
5417                    cimg::t_normal);
5418       std::fprintf(cimg::output(),"  > Using JPEG library :     %s%-13s%s %s('cimg_use_jpeg' %s)%s\n",
5419                    cimg::t_bold,
5420 #ifdef cimg_use_jpeg
5421                    "Yes",cimg::t_normal,cimg::t_green,"defined",
5422 #else
5423                    "No",cimg::t_normal,cimg::t_green,"undefined",
5424 #endif
5425                    cimg::t_normal);
5426 
5427       std::fprintf(cimg::output(),"  > Using TIFF library :     %s%-13s%s %s('cimg_use_tiff' %s)%s\n",
5428                    cimg::t_bold,
5429 #ifdef cimg_use_tiff
5430                    "Yes",cimg::t_normal,cimg::t_green,"defined",
5431 #else
5432                    "No",cimg::t_normal,cimg::t_green,"undefined",
5433 #endif
5434                    cimg::t_normal);
5435 
5436       std::fprintf(cimg::output(),"  > Using Magick++ library : %s%-13s%s %s('cimg_use_magick' %s)%s\n",
5437                    cimg::t_bold,
5438 #ifdef cimg_use_magick
5439                    "Yes",cimg::t_normal,cimg::t_green,"defined",
5440 #else
5441                    "No",cimg::t_normal,cimg::t_green,"undefined",
5442 #endif
5443                    cimg::t_normal);
5444 
5445       std::fprintf(cimg::output(),"  > Using FFTW3 library :    %s%-13s%s %s('cimg_use_fftw3' %s)%s\n",
5446                    cimg::t_bold,
5447 #ifdef cimg_use_fftw3
5448                    "Yes",cimg::t_normal,cimg::t_green,"defined",
5449 #else
5450                    "No",cimg::t_normal,cimg::t_green,"undefined",
5451 #endif
5452                    cimg::t_normal);
5453 
5454       std::fprintf(cimg::output(),"  > Using LAPACK library :   %s%-13s%s %s('cimg_use_lapack' %s)%s\n",
5455                    cimg::t_bold,
5456 #ifdef cimg_use_lapack
5457                    "Yes",cimg::t_normal,cimg::t_green,"defined",
5458 #else
5459                    "No",cimg::t_normal,cimg::t_green,"undefined",
5460 #endif
5461                    cimg::t_normal);
5462 
5463       cimg_snprintf(tmp,sizeof(tmp),"\"%.1020s\"",cimg::imagemagick_path());
5464       std::fprintf(cimg::output(),"  > Path of ImageMagick :    %s%-13s%s\n",
5465                    cimg::t_bold,
5466                    tmp,
5467                    cimg::t_normal);
5468 
5469       cimg_snprintf(tmp,sizeof(tmp),"\"%.1020s\"",cimg::graphicsmagick_path());
5470       std::fprintf(cimg::output(),"  > Path of GraphicsMagick : %s%-13s%s\n",
5471                    cimg::t_bold,
5472                    tmp,
5473                    cimg::t_normal);
5474 
5475       cimg_snprintf(tmp,sizeof(tmp),"\"%.1020s\"",cimg::medcon_path());
5476       std::fprintf(cimg::output(),"  > Path of 'medcon' :       %s%-13s%s\n",
5477                    cimg::t_bold,
5478                    tmp,
5479                    cimg::t_normal);
5480 
5481       cimg_snprintf(tmp,sizeof(tmp),"\"%.1020s\"",cimg::temporary_path());
5482       std::fprintf(cimg::output(),"  > Temporary path :         %s%-13s%s\n",
5483                    cimg::t_bold,
5484                    tmp,
5485                    cimg::t_normal);
5486 
5487       std::fprintf(cimg::output(),"\n");
5488     }
5489 
5490     // Declare LAPACK function signatures if necessary.
5491 #ifdef cimg_use_lapack
5492     template<typename T>
5493     inline void getrf(int &N, T *lapA, int *IPIV, int &INFO) {
5494       dgetrf_(&N,&N,lapA,&N,IPIV,&INFO);
5495     }
5496 
5497     inline void getrf(int &N, float *lapA, int *IPIV, int &INFO) {
5498       sgetrf_(&N,&N,lapA,&N,IPIV,&INFO);
5499     }
5500 
5501     template<typename T>
5502     inline void getri(int &N, T *lapA, int *IPIV, T* WORK, int &LWORK, int &INFO) {
5503       dgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO);
5504     }
5505 
5506     inline void getri(int &N, float *lapA, int *IPIV, float* WORK, int &LWORK, int &INFO) {
5507       sgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO);
5508     }
5509 
5510     template<typename T>
5511     inline void gesvd(char &JOB, int &M, int &N, T *lapA, int &MN,
5512                       T *lapS, T *lapU, T *lapV, T *WORK, int &LWORK, int &INFO) {
5513       dgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO);
5514     }
5515 
5516     inline void gesvd(char &JOB, int &M, int &N, float *lapA, int &MN,
5517                       float *lapS, float *lapU, float *lapV, float *WORK, int &LWORK, int &INFO) {
5518       sgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO);
5519     }
5520 
5521     template<typename T>
5522     inline void getrs(char &TRANS, int &N, T *lapA, int *IPIV, T *lapB, int &INFO) {
5523       int one = 1;
5524       dgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO);
5525     }
5526 
5527     inline void getrs(char &TRANS, int &N, float *lapA, int *IPIV, float *lapB, int &INFO) {
5528       int one = 1;
5529       sgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO);
5530     }
5531 
5532     template<typename T>
5533     inline void syev(char &JOB, char &UPLO, int &N, T *lapA, T *lapW, T *WORK, int &LWORK, int &INFO) {
5534       dsyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO);
5535     }
5536 
5537     inline void syev(char &JOB, char &UPLO, int &N, float *lapA, float *lapW, float *WORK, int &LWORK, int &INFO) {
5538       ssyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO);
5539     }
5540 #endif
5541 
5542     // End of the 'cimg' namespace
5543   }
5544 
5545   /*------------------------------------------------
5546    #
5547    #
5548    #   Definition of mathematical operators and
5549    #   external functions.
5550    #
5551    #
5552    -------------------------------------------------*/
5553 
5554 #define _cimg_create_ext_operators(typ) \
5555   template<typename T> \
5556   inline CImg<typename cimg::superset<T,typ>::type> operator+(const typ val, const CImg<T>& img) { \
5557     return img + val; \
5558   } \
5559   template<typename T> \
5560   inline CImg<typename cimg::superset<T,typ>::type> operator-(const typ val, const CImg<T>& img) { \
5561     typedef typename cimg::superset<T,typ>::type Tt; \
5562     return CImg<Tt>(img._width,img._height,img._depth,img._spectrum,val)-=img; \
5563   } \
5564   template<typename T> \
5565   inline CImg<typename cimg::superset<T,typ>::type> operator*(const typ val, const CImg<T>& img) { \
5566     return img*val; \
5567   } \
5568   template<typename T> \
5569   inline CImg<typename cimg::superset<T,typ>::type> operator/(const typ val, const CImg<T>& img) { \
5570     return val*img.get_invert(); \
5571   } \
5572   template<typename T> \
5573   inline CImg<typename cimg::superset<T,typ>::type> operator&(const typ val, const CImg<T>& img) { \
5574     return img & val; \
5575   } \
5576   template<typename T> \
5577   inline CImg<typename cimg::superset<T,typ>::type> operator|(const typ val, const CImg<T>& img) { \
5578     return img | val; \
5579   } \
5580   template<typename T> \
5581   inline CImg<typename cimg::superset<T,typ>::type> operator^(const typ val, const CImg<T>& img) { \
5582     return img ^ val; \
5583   } \
5584 
5585   _cimg_create_ext_operators(bool)
5586   _cimg_create_ext_operators(unsigned char)
5587   _cimg_create_ext_operators(char)
5588   _cimg_create_ext_operators(signed char)
5589   _cimg_create_ext_operators(unsigned short)
5590   _cimg_create_ext_operators(short)
5591   _cimg_create_ext_operators(unsigned int)
5592   _cimg_create_ext_operators(int)
5593   _cimg_create_ext_operators(unsigned long)
5594   _cimg_create_ext_operators(long)
5595   _cimg_create_ext_operators(float)
5596   _cimg_create_ext_operators(double)
5597 
5598   template<typename T>
5599   inline CImg<_cimg_Tfloat> operator+(const char *const expression, const CImg<T>& img) {
5600     return img + expression;
5601   }
5602 
5603   template<typename T>
5604   inline CImg<_cimg_Tfloat> operator-(const char *const expression, const CImg<T>& img) {
5605     return (CImg<_cimg_Tfloat>(img._width,img._height,img._depth,img._spectrum)=expression)-=img;
5606   }
5607 
5608   template<typename T>
5609   inline CImg<_cimg_Tfloat> operator*(const char *const expression, const CImg<T>& img) {
5610     return img*expression;
5611   }
5612 
5613   template<typename T>
5614   inline CImg<_cimg_Tfloat> operator/(const char *const expression, const CImg<T>& img) {
5615     return expression*img.get_invert();
5616   }
5617 
5618   template<typename T>
5619   inline CImg<T> operator&(const char *const expression, const CImg<T>& img) {
5620     return img & expression;
5621   }
5622 
5623   template<typename T>
5624   inline CImg<T> operator|(const char *const expression, const CImg<T>& img) {
5625     return img | expression;
5626   }
5627 
5628   template<typename T>
5629   inline CImg<T> operator^(const char *const expression, const CImg<T>& img) {
5630     return img ^ expression;
5631   }
5632 
5633   template<typename T>
5634   inline CImg<_cimg_Tfloat> sqr(const CImg<T>& instance) {
5635     return instance.get_sqr();
5636   }
5637 
5638   template<typename T>
5639   inline CImg<_cimg_Tfloat> sqrt(const CImg<T>& instance) {
5640     return instance.get_sqrt();
5641   }
5642 
5643   template<typename T>
5644   inline CImg<_cimg_Tfloat> exp(const CImg<T>& instance) {
5645     return instance.get_exp();
5646   }
5647 
5648   template<typename T>
5649   inline CImg<_cimg_Tfloat> log(const CImg<T>& instance) {
5650     return instance.get_log();
5651   }
5652 
5653   template<typename T>
5654   inline CImg<_cimg_Tfloat> log10(const CImg<T>& instance) {
5655     return instance.get_log10();
5656   }
5657 
5658   template<typename T>
5659   inline CImg<_cimg_Tfloat> abs(const CImg<T>& instance) {
5660     return instance.get_abs();
5661   }
5662 
5663   template<typename T>
5664   inline CImg<_cimg_Tfloat> sign(const CImg<T>& instance) {
5665     return instance.get_sign();
5666   }
5667 
5668   template<typename T>
5669   inline CImg<_cimg_Tfloat> cos(const CImg<T>& instance) {
5670     return instance.get_cos();
5671   }
5672 
5673   template<typename T>
5674   inline CImg<_cimg_Tfloat> sin(const CImg<T>& instance) {
5675     return instance.get_sin();
5676   }
5677 
5678   template<typename T>
5679   inline CImg<_cimg_Tfloat> sinc(const CImg<T>& instance) {
5680     return instance.get_sinc();
5681   }
5682 
5683   template<typename T>
5684   inline CImg<_cimg_Tfloat> tan(const CImg<T>& instance) {
5685     return instance.get_tan();
5686   }
5687 
5688   template<typename T>
5689   inline CImg<_cimg_Tfloat> acos(const CImg<T>& instance) {
5690     return instance.get_acos();
5691   }
5692 
5693   template<typename T>
5694   inline CImg<_cimg_Tfloat> asin(const CImg<T>& instance) {
5695     return instance.get_asin();
5696   }
5697 
5698   template<typename T>
5699   inline CImg<_cimg_Tfloat> atan(const CImg<T>& instance) {
5700     return instance.get_atan();
5701   }
5702 
5703   template<typename T>
5704   inline CImg<_cimg_Tfloat> cosh(const CImg<T>& instance) {
5705     return instance.get_cosh();
5706   }
5707 
5708   template<typename T>
5709   inline CImg<_cimg_Tfloat> sinh(const CImg<T>& instance) {
5710     return instance.get_sinh();
5711   }
5712 
5713   template<typename T>
5714   inline CImg<_cimg_Tfloat> tanh(const CImg<T>& instance) {
5715     return instance.get_tanh();
5716   }
5717 
5718   template<typename T>
5719   inline CImg<T> transpose(const CImg<T>& instance) {
5720     return instance.get_transpose();
5721   }
5722 
5723   template<typename T>
5724   inline CImg<_cimg_Tfloat> invert(const CImg<T>& instance) {
5725     return instance.get_invert();
5726   }
5727 
5728   template<typename T>
5729   inline CImg<_cimg_Tfloat> pseudoinvert(const CImg<T>& instance) {
5730     return instance.get_pseudoinvert();
5731   }
5732 
5733   /*-------------------------------------------
5734    #
5735    #
5736    #
5737    # Definition of the CImgDisplay structure
5738    #
5739    #
5740    #
5741    --------------------------------------------*/
5742 
5743   //! This class represents a window which can display \c CImg<T> images and handles mouse and keyboard events.
5744   /**
5745      Creating a \c CImgDisplay instance opens a window that can be used to display a \c CImg<T> image
5746      of a \c CImgList<T> image list inside. When a display is created, associated window events
5747      (such as mouse motion, keyboard and window size changes) are handled and can be easily
5748      detected by testing specific \c CImgDisplay data fields.
5749      See \ref cimg_displays for a complete tutorial on using the \c CImgDisplay class.
5750   **/
5751 
5752   struct CImgDisplay {
5753 
5754     //! Width of the display.
5755     unsigned int _width;
5756 
5757     //! Height of the display.
5758     unsigned int _height;
5759 
5760     //! Width of the underlying window.
5761     volatile unsigned int _window_width;
5762 
5763     //! Height of the underlying window.
5764     volatile unsigned int _window_height;
5765 
5766     //! X-pos of the display on the screen.
5767     volatile int _window_x;
5768 
5769     //! Y-pos of the display on the screen.
5770     volatile int _window_y;
5771 
5772     //! X-coordinate of the mouse pointer on the display.
5773     volatile int _mouse_x;
5774 
5775     //! Y-coordinate of the mouse pointer on the display.
5776     volatile int _mouse_y;
5777 
5778     //! Normalization type used for the display.
5779     unsigned int _normalization;
5780 
5781     //! Display title.
5782     char *_title;
5783 
5784     //! Button state of the mouse.
5785     volatile unsigned int _button;
5786 
5787     //! Wheel state of the mouse.
5788     volatile int _wheel;
5789 
5790     //! Key value if pressed.
5791     volatile unsigned int _keys[128];
5792     volatile unsigned int _released_keys[128];
5793 
5794     //! Closed state of the window.
5795     volatile bool _is_closed;
5796 
5797     //! Resized state of the window.
5798     volatile bool _is_resized;
5799 
5800     //! Moved state of the window.
5801     volatile bool _is_moved;
5802 
5803     //! Event state of the window.
5804     volatile bool _is_event;
5805 
5806     //! Current state of the corresponding key (exists for all referenced keys).
5807     volatile bool _is_keyESC;
5808     volatile bool _is_keyF1;
5809     volatile bool _is_keyF2;
5810     volatile bool _is_keyF3;
5811     volatile bool _is_keyF4;
5812     volatile bool _is_keyF5;
5813     volatile bool _is_keyF6;
5814     volatile bool _is_keyF7;
5815     volatile bool _is_keyF8;
5816     volatile bool _is_keyF9;
5817     volatile bool _is_keyF10;
5818     volatile bool _is_keyF11;
5819     volatile bool _is_keyF12;
5820     volatile bool _is_keyPAUSE;
5821     volatile bool _is_key1;
5822     volatile bool _is_key2;
5823     volatile bool _is_key3;
5824     volatile bool _is_key4;
5825     volatile bool _is_key5;
5826     volatile bool _is_key6;
5827     volatile bool _is_key7;
5828     volatile bool _is_key8;
5829     volatile bool _is_key9;
5830     volatile bool _is_key0;
5831     volatile bool _is_keyBACKSPACE;
5832     volatile bool _is_keyINSERT;
5833     volatile bool _is_keyHOME;
5834     volatile bool _is_keyPAGEUP;
5835     volatile bool _is_keyTAB;
5836     volatile bool _is_keyQ;
5837     volatile bool _is_keyW;
5838     volatile bool _is_keyE;
5839     volatile bool _is_keyR;
5840     volatile bool _is_keyT;
5841     volatile bool _is_keyY;
5842     volatile bool _is_keyU;
5843     volatile bool _is_keyI;
5844     volatile bool _is_keyO;
5845     volatile bool _is_keyP;
5846     volatile bool _is_keyDELETE;
5847     volatile bool _is_keyEND;
5848     volatile bool _is_keyPAGEDOWN;
5849     volatile bool _is_keyCAPSLOCK;
5850     volatile bool _is_keyA;
5851     volatile bool _is_keyS;
5852     volatile bool _is_keyD;
5853     volatile bool _is_keyF;
5854     volatile bool _is_keyG;
5855     volatile bool _is_keyH;
5856     volatile bool _is_keyJ;
5857     volatile bool _is_keyK;
5858     volatile bool _is_keyL;
5859     volatile bool _is_keyENTER;
5860     volatile bool _is_keySHIFTLEFT;
5861     volatile bool _is_keyZ;
5862     volatile bool _is_keyX;
5863     volatile bool _is_keyC;
5864     volatile bool _is_keyV;
5865     volatile bool _is_keyB;
5866     volatile bool _is_keyN;
5867     volatile bool _is_keyM;
5868     volatile bool _is_keySHIFTRIGHT;
5869     volatile bool _is_keyARROWUP;
5870     volatile bool _is_keyCTRLLEFT;
5871     volatile bool _is_keyAPPLEFT;
5872     volatile bool _is_keyALT;
5873     volatile bool _is_keySPACE;
5874     volatile bool _is_keyALTGR;
5875     volatile bool _is_keyAPPRIGHT;
5876     volatile bool _is_keyMENU;
5877     volatile bool _is_keyCTRLRIGHT;
5878     volatile bool _is_keyARROWLEFT;
5879     volatile bool _is_keyARROWDOWN;
5880     volatile bool _is_keyARROWRIGHT;
5881     volatile bool _is_keyPAD0;
5882     volatile bool _is_keyPAD1;
5883     volatile bool _is_keyPAD2;
5884     volatile bool _is_keyPAD3;
5885     volatile bool _is_keyPAD4;
5886     volatile bool _is_keyPAD5;
5887     volatile bool _is_keyPAD6;
5888     volatile bool _is_keyPAD7;
5889     volatile bool _is_keyPAD8;
5890     volatile bool _is_keyPAD9;
5891     volatile bool _is_keyPADADD;
5892     volatile bool _is_keyPADSUB;
5893     volatile bool _is_keyPADMUL;
5894     volatile bool _is_keyPADDIV;
5895 
5896     //! Fullscreen state of the display.
5897     bool _is_fullscreen;
5898 
5899     // Internal variables.
5900     float _fps_fps, _min, _max;
5901     unsigned long _timer, _fps_frames, _fps_timer;
5902 
5903     //@}
5904     //---------------------------
5905     //
5906     //! \name Plugins
5907     //@{
5908     //---------------------------
5909 
5910 #ifdef cimgdisplay_plugin
5911 #include cimgdisplay_plugin
5912 #endif
5913 #ifdef cimgdisplay_plugin1
5914 #include cimgdisplay_plugin1
5915 #endif
5916 #ifdef cimgdisplay_plugin2
5917 #include cimgdisplay_plugin2
5918 #endif
5919 #ifdef cimgdisplay_plugin3
5920 #include cimgdisplay_plugin3
5921 #endif
5922 #ifdef cimgdisplay_plugin4
5923 #include cimgdisplay_plugin4
5924 #endif
5925 #ifdef cimgdisplay_plugin5
5926 #include cimgdisplay_plugin5
5927 #endif
5928 #ifdef cimgdisplay_plugin6
5929 #include cimgdisplay_plugin6
5930 #endif
5931 #ifdef cimgdisplay_plugin7
5932 #include cimgdisplay_plugin7
5933 #endif
5934 #ifdef cimgdisplay_plugin8
5935 #include cimgdisplay_plugin8
5936 #endif
5937 
5938     //@}
5939     //--------------------------------------------------------
5940     //
5941     //! \name Constructors / Destructor / Instance Management
5942     //@{
5943     //--------------------------------------------------------
5944 
5945     //! Destructor.
5946     ~CImgDisplay() {
5947       assign();
5948     }
5949 
5950     //! Create an empty display window.
5951     CImgDisplay():
5952       _width(0),_height(0),_window_width(0),_window_height(0),_window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),
5953       _normalization(0),_title(0),_button(0),_wheel(0),_is_closed(true),_is_resized(false),_is_moved(false),_is_event(false),
5954       _is_fullscreen(false),_min(0),_max(0) {
5955       assign();
5956     }
5957 
5958     //! Create a display window with a specified size \p pwidth x \p height.
5959     /** \param width : Width of the display window.
5960         \param height : Height of the display window.
5961         \param title : Title of the display window.
5962         \param normalization : Normalization type of the display window (0=none, 1=always, 2=once).
5963         \param is_fullscreen : Fullscreen mode.
5964         \param is_closed : Initially visible mode.
5965         A black image will be initially displayed in the display window.
5966     **/
5967     CImgDisplay(const unsigned int width, const unsigned int height,
5968                 const char *const title=0, const unsigned int normalization=3,
5969                 const bool is_fullscreen=false, const bool is_closed=false):
5970       _width(0),_height(0),_window_width(0),_window_height(0),_window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),
5971       _normalization(0),_title(0),_button(0),_wheel(0),_is_closed(true),_is_resized(false),_is_moved(false),_is_event(false),
5972       _is_fullscreen(false),_min(0),_max(0) {
5973       assign(width,height,title,normalization,is_fullscreen,is_closed);
5974     }
5975 
5976     //! Create a display window from an image.
5977     /** \param img : Image that will be used to create the display window.
5978         \param title : Title of the display window
5979         \param normalization : Normalization type of the display window.
5980         \param is_fullscreen : Fullscreen mode.
5981         \param is_closed : Initially visible mode.
5982     **/
5983     template<typename T>
5984     explicit CImgDisplay(const CImg<T>& img,
5985                          const char *const title=0, const unsigned int normalization=3,
5986                          const bool is_fullscreen=false, const bool is_closed=false):
5987       _width(0),_height(0),_window_width(0),_window_height(0),_window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),
5988       _normalization(0),_title(0),_button(0),_wheel(0),_is_closed(true),_is_resized(false),_is_moved(false),_is_event(false),
5989       _is_fullscreen(false),_min(0),_max(0) {
5990       assign(img,title,normalization,is_fullscreen,is_closed);
5991     }
5992 
5993     //! Create a display window from an image list.
5994     /** \param list : The list of images to display.
5995         \param title : Title of the display window
5996         \param normalization : Normalization type of the display window.
5997         \param is_fullscreen : Fullscreen mode.
5998         \param is_closed : Initially visible mode.
5999     **/
6000     template<typename T>
6001     explicit CImgDisplay(const CImgList<T>& list,
6002                          const char *const title=0, const unsigned int normalization=3,
6003                          const bool is_fullscreen=false, const bool is_closed=false):
6004       _width(0),_height(0),_window_width(0),_window_height(0),_window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),
6005       _normalization(0),_title(0),_button(0),_wheel(0),_is_closed(true),_is_resized(false),_is_moved(false),_is_event(false),
6006       _is_fullscreen(false),_min(0),_max(0) {
6007       assign(list,title,normalization,is_fullscreen,is_closed);
6008     }
6009 
6010     //! Create a display window by copying another one.
6011     /**
6012         \param disp  : Display window to copy.
6013     **/
6014     CImgDisplay(const CImgDisplay& disp):
6015       _width(0),_height(0),_window_width(0),_window_height(0),_window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),
6016       _normalization(0),_title(0),_button(0),_wheel(0),_is_closed(true),_is_resized(false),_is_moved(false),_is_event(false),
6017       _is_fullscreen(false),_min(0),_max(0) {
6018       assign(disp);
6019     }
6020 
6021 #if cimg_display==0
6022 
6023     static void _no_display_exception() {
6024       throw CImgDisplayException("CImgDisplay() : No display available.");
6025     }
6026 
6027     //! In-place version of the destructor.
6028     CImgDisplay& assign() {
6029       _no_display_exception();
6030       return flush();
6031     }
6032 
6033     //! In-place version of the constructor.
6034     CImgDisplay& assign(const unsigned int width, const unsigned int height,
6035                         const char *const title=0, const unsigned int normalization=3,
6036                         const bool is_fullscreen=false, const bool is_closed=false) {
6037       cimg::unused(width,height,title,normalization,is_fullscreen,is_closed);
6038       return assign();
6039     }
6040 
6041     //! In-place version of the constructor.
6042     template<typename T>
6043     CImgDisplay& assign(const CImg<T>& img,
6044                         const char *const title=0, const unsigned int normalization=3,
6045                         const bool is_fullscreen=false, const bool is_closed=false) {
6046       return assign(img._width,img._height,title,normalization,is_fullscreen,is_closed);
6047     }
6048 
6049     //! In-place version of the constructor.
6050     template<typename T>
6051     CImgDisplay& assign(const CImgList<T>& list,
6052                         const char *const title=0, const unsigned int normalization=3,
6053                         const bool is_fullscreen=false, const bool is_closed=false) {
6054       return assign(list._width,list._width,title,normalization,is_fullscreen,is_closed);
6055     }
6056 
6057     //! In-place version of the constructor.
6058     CImgDisplay& assign(const CImgDisplay &disp) {
6059       return assign(disp._width,disp._height);
6060     }
6061 
6062 #endif
6063 
6064     //! Get a reference to an empty display.
6065     static CImgDisplay& empty() {
6066       static CImgDisplay _empty;
6067       return _empty.assign();
6068     }
6069 
6070 #define cimg_fitscreen(dx,dy,dz) CImgDisplay::_fitscreen(dx,dy,dz,128,-85,false),CImgDisplay::_fitscreen(dx,dy,dz,128,-85,true)
6071     static unsigned int _fitscreen(const unsigned int dx, const unsigned int dy, const unsigned int dz,
6072                                    const int dmin, const int dmax,const bool return_y) {
6073       unsigned int nw = dx + (dz>1?dz:0), nh = dy + (dz>1?dz:0);
6074       const unsigned int
6075         sw = CImgDisplay::screen_width(), sh = CImgDisplay::screen_height(),
6076         mw = dmin<0?(unsigned int)(sw*-dmin/100):(unsigned int)dmin,
6077         mh = dmin<0?(unsigned int)(sh*-dmin/100):(unsigned int)dmin,
6078         Mw = dmax<0?(unsigned int)(sw*-dmax/100):(unsigned int)dmax,
6079         Mh = dmax<0?(unsigned int)(sh*-dmax/100):(unsigned int)dmax;
6080       if (nw<mw) { nh = nh*mw/nw; nh+=(nh==0?1:0); nw = mw; }
6081       if (nh<mh) { nw = nw*mh/nh; nw+=(nw==0?1:0); nh = mh; }
6082       if (nw>Mw) { nh = nh*Mw/nw; nh+=(nh==0?1:0); nw = Mw; }
6083       if (nh>Mh) { nw = nw*Mh/nh; nw+=(nw==0?1:0); nh = Mh; }
6084       if (nw<mw) nw = mw;
6085       if (nh<mh) nh = mh;
6086       return return_y?nh:nw;
6087     }
6088 
6089     //@}
6090     //------------------------------------------
6091     //
6092     //! \name Overloaded Operators
6093     //@{
6094     //------------------------------------------
6095 
6096     // Operator=().
6097     template<typename t>
6098     CImgDisplay& operator=(const CImg<t>& img) {
6099       return display(img);
6100     }
6101 
6102     // Operator=().
6103     template<typename t>
6104     CImgDisplay& operator=(const CImgList<t>& list) {
6105       return display(list);
6106     }
6107 
6108     //! Operator=().
6109     CImgDisplay& operator=(const CImgDisplay& disp) {
6110       return assign(disp);
6111     }
6112 
6113     //! Return true if display is not empty.
6114     operator bool() const {
6115       return !is_empty();
6116     }
6117 
6118     //@}
6119     //------------------------------------------
6120     //
6121     //! \name Instance Checking
6122     //@{
6123     //------------------------------------------
6124 
6125     //! Return true is display is empty.
6126     bool is_empty() const {
6127       return !(_width && _height);
6128     }
6129 
6130     bool is_closed() const {
6131       return _is_closed;
6132     }
6133 
6134     bool is_resized() const {
6135       return _is_resized;
6136     }
6137 
6138     bool is_moved() const {
6139       return _is_moved;
6140     }
6141 
6142     bool is_event() const {
6143       return _is_event;
6144     }
6145 
6146     bool is_fullscreen() const {
6147       return _is_fullscreen;
6148     }
6149 
6150     bool is_key() const {
6151       return _is_keyESC || _is_keyF1 || _is_keyF2 || _is_keyF3 ||
6152         _is_keyF4 || _is_keyF5 || _is_keyF6 || _is_keyF7 ||
6153         _is_keyF8 || _is_keyF9 || _is_keyF10 || _is_keyF11 ||
6154         _is_keyF12 || _is_keyPAUSE || _is_key1 || _is_key2 ||
6155         _is_key3 || _is_key4 || _is_key5 || _is_key6 ||
6156         _is_key7 || _is_key8 || _is_key9 || _is_key0 ||
6157         _is_keyBACKSPACE || _is_keyINSERT || _is_keyHOME ||
6158         _is_keyPAGEUP || _is_keyTAB || _is_keyQ || _is_keyW ||
6159         _is_keyE || _is_keyR || _is_keyT || _is_keyY ||
6160         _is_keyU || _is_keyI || _is_keyO || _is_keyP ||
6161         _is_keyDELETE || _is_keyEND || _is_keyPAGEDOWN ||
6162         _is_keyCAPSLOCK || _is_keyA || _is_keyS || _is_keyD ||
6163         _is_keyF || _is_keyG || _is_keyH || _is_keyJ ||
6164         _is_keyK || _is_keyL || _is_keyENTER ||
6165         _is_keySHIFTLEFT || _is_keyZ || _is_keyX || _is_keyC ||
6166         _is_keyV || _is_keyB || _is_keyN || _is_keyM ||
6167         _is_keySHIFTRIGHT || _is_keyARROWUP || _is_keyCTRLLEFT ||
6168         _is_keyAPPLEFT || _is_keyALT || _is_keySPACE || _is_keyALTGR ||
6169         _is_keyAPPRIGHT || _is_keyMENU || _is_keyCTRLRIGHT ||
6170         _is_keyARROWLEFT || _is_keyARROWDOWN || _is_keyARROWRIGHT ||
6171         _is_keyPAD0 || _is_keyPAD1 || _is_keyPAD2 ||
6172         _is_keyPAD3 || _is_keyPAD4 || _is_keyPAD5 ||
6173         _is_keyPAD6 || _is_keyPAD7 || _is_keyPAD8 ||
6174         _is_keyPAD9 || _is_keyPADADD || _is_keyPADSUB ||
6175         _is_keyPADMUL || _is_keyPADDIV;
6176     }
6177 
6178 #define _cimg_iskey_def(k) \
6179     bool is_key##k() const { \
6180       return _is_key##k; \
6181     }
6182     _cimg_iskey_def(ESC); _cimg_iskey_def(F1); _cimg_iskey_def(F2); _cimg_iskey_def(F3);
6183     _cimg_iskey_def(F4); _cimg_iskey_def(F5); _cimg_iskey_def(F6); _cimg_iskey_def(F7);
6184     _cimg_iskey_def(F8); _cimg_iskey_def(F9); _cimg_iskey_def(F10); _cimg_iskey_def(F11);
6185     _cimg_iskey_def(F12); _cimg_iskey_def(PAUSE); _cimg_iskey_def(1); _cimg_iskey_def(2);
6186     _cimg_iskey_def(3); _cimg_iskey_def(4); _cimg_iskey_def(5); _cimg_iskey_def(6);
6187     _cimg_iskey_def(7); _cimg_iskey_def(8); _cimg_iskey_def(9); _cimg_iskey_def(0);
6188     _cimg_iskey_def(BACKSPACE); _cimg_iskey_def(INSERT); _cimg_iskey_def(HOME);
6189     _cimg_iskey_def(PAGEUP); _cimg_iskey_def(TAB); _cimg_iskey_def(Q); _cimg_iskey_def(W);
6190     _cimg_iskey_def(E); _cimg_iskey_def(R); _cimg_iskey_def(T); _cimg_iskey_def(Y);
6191     _cimg_iskey_def(U); _cimg_iskey_def(I); _cimg_iskey_def(O); _cimg_iskey_def(P);
6192     _cimg_iskey_def(DELETE); _cimg_iskey_def(END); _cimg_iskey_def(PAGEDOWN);
6193     _cimg_iskey_def(CAPSLOCK); _cimg_iskey_def(A); _cimg_iskey_def(S); _cimg_iskey_def(D);
6194     _cimg_iskey_def(F); _cimg_iskey_def(G); _cimg_iskey_def(H); _cimg_iskey_def(J);
6195     _cimg_iskey_def(K); _cimg_iskey_def(L); _cimg_iskey_def(ENTER);
6196     _cimg_iskey_def(SHIFTLEFT); _cimg_iskey_def(Z); _cimg_iskey_def(X); _cimg_iskey_def(C);
6197     _cimg_iskey_def(V); _cimg_iskey_def(B); _cimg_iskey_def(N); _cimg_iskey_def(M);
6198     _cimg_iskey_def(SHIFTRIGHT); _cimg_iskey_def(ARROWUP); _cimg_iskey_def(CTRLLEFT);
6199     _cimg_iskey_def(APPLEFT); _cimg_iskey_def(ALT); _cimg_iskey_def(SPACE); _cimg_iskey_def(ALTGR);
6200     _cimg_iskey_def(APPRIGHT); _cimg_iskey_def(MENU); _cimg_iskey_def(CTRLRIGHT);
6201     _cimg_iskey_def(ARROWLEFT); _cimg_iskey_def(ARROWDOWN); _cimg_iskey_def(ARROWRIGHT);
6202     _cimg_iskey_def(PAD0); _cimg_iskey_def(PAD1); _cimg_iskey_def(PAD2);
6203     _cimg_iskey_def(PAD3); _cimg_iskey_def(PAD4); _cimg_iskey_def(PAD5);
6204     _cimg_iskey_def(PAD6); _cimg_iskey_def(PAD7); _cimg_iskey_def(PAD8);
6205     _cimg_iskey_def(PAD9); _cimg_iskey_def(PADADD); _cimg_iskey_def(PADSUB);
6206     _cimg_iskey_def(PADMUL); _cimg_iskey_def(PADDIV);
6207 
6208     bool is_key(const unsigned int key) const {
6209 #define _cimg_iskey_test(k) if (key==cimg::key##k) return _is_key##k;
6210       _cimg_iskey_test(ESC); _cimg_iskey_test(F1); _cimg_iskey_test(F2); _cimg_iskey_test(F3);
6211       _cimg_iskey_test(F4); _cimg_iskey_test(F5); _cimg_iskey_test(F6); _cimg_iskey_test(F7);
6212       _cimg_iskey_test(F8); _cimg_iskey_test(F9); _cimg_iskey_test(F10); _cimg_iskey_test(F11);
6213       _cimg_iskey_test(F12); _cimg_iskey_test(PAUSE); _cimg_iskey_test(1); _cimg_iskey_test(2);
6214       _cimg_iskey_test(3); _cimg_iskey_test(4); _cimg_iskey_test(5); _cimg_iskey_test(6);
6215       _cimg_iskey_test(7); _cimg_iskey_test(8); _cimg_iskey_test(9); _cimg_iskey_test(0);
6216       _cimg_iskey_test(BACKSPACE); _cimg_iskey_test(INSERT); _cimg_iskey_test(HOME);
6217       _cimg_iskey_test(PAGEUP); _cimg_iskey_test(TAB); _cimg_iskey_test(Q); _cimg_iskey_test(W);
6218       _cimg_iskey_test(E); _cimg_iskey_test(R); _cimg_iskey_test(T); _cimg_iskey_test(Y);
6219       _cimg_iskey_test(U); _cimg_iskey_test(I); _cimg_iskey_test(O); _cimg_iskey_test(P);
6220       _cimg_iskey_test(DELETE); _cimg_iskey_test(END); _cimg_iskey_test(PAGEDOWN);
6221       _cimg_iskey_test(CAPSLOCK); _cimg_iskey_test(A); _cimg_iskey_test(S); _cimg_iskey_test(D);
6222       _cimg_iskey_test(F); _cimg_iskey_test(G); _cimg_iskey_test(H); _cimg_iskey_test(J);
6223       _cimg_iskey_test(K); _cimg_iskey_test(L); _cimg_iskey_test(ENTER);
6224       _cimg_iskey_test(SHIFTLEFT); _cimg_iskey_test(Z); _cimg_iskey_test(X); _cimg_iskey_test(C);
6225       _cimg_iskey_test(V); _cimg_iskey_test(B); _cimg_iskey_test(N); _cimg_iskey_test(M);
6226       _cimg_iskey_test(SHIFTRIGHT); _cimg_iskey_test(ARROWUP); _cimg_iskey_test(CTRLLEFT);
6227       _cimg_iskey_test(APPLEFT); _cimg_iskey_test(ALT); _cimg_iskey_test(SPACE); _cimg_iskey_test(ALTGR);
6228       _cimg_iskey_test(APPRIGHT); _cimg_iskey_test(MENU); _cimg_iskey_test(CTRLRIGHT);
6229       _cimg_iskey_test(ARROWLEFT); _cimg_iskey_test(ARROWDOWN); _cimg_iskey_test(ARROWRIGHT);
6230       _cimg_iskey_test(PAD0); _cimg_iskey_test(PAD1); _cimg_iskey_test(PAD2);
6231       _cimg_iskey_test(PAD3); _cimg_iskey_test(PAD4); _cimg_iskey_test(PAD5);
6232       _cimg_iskey_test(PAD6); _cimg_iskey_test(PAD7); _cimg_iskey_test(PAD8);
6233       _cimg_iskey_test(PAD9); _cimg_iskey_test(PADADD); _cimg_iskey_test(PADSUB);
6234       _cimg_iskey_test(PADMUL); _cimg_iskey_test(PADDIV);
6235       return false;
6236     }
6237 
6238     //! Get keycode corresponding to given input string.
6239     bool is_key(const char *const textcode) const {
6240 #define _cimg_iskey_test2(k) if (!cimg::strcasecmp(textcode,#k)) return _is_key##k;
6241       _cimg_iskey_test2(ESC); _cimg_iskey_test2(F1); _cimg_iskey_test2(F2); _cimg_iskey_test2(F3);
6242       _cimg_iskey_test2(F4); _cimg_iskey_test2(F5); _cimg_iskey_test2(F6); _cimg_iskey_test2(F7);
6243       _cimg_iskey_test2(F8); _cimg_iskey_test2(F9); _cimg_iskey_test2(F10); _cimg_iskey_test2(F11);
6244       _cimg_iskey_test2(F12); _cimg_iskey_test2(PAUSE); _cimg_iskey_test2(1); _cimg_iskey_test2(2);
6245       _cimg_iskey_test2(3); _cimg_iskey_test2(4); _cimg_iskey_test2(5); _cimg_iskey_test2(6);
6246       _cimg_iskey_test2(7); _cimg_iskey_test2(8); _cimg_iskey_test2(9); _cimg_iskey_test2(0);
6247       _cimg_iskey_test2(BACKSPACE); _cimg_iskey_test2(INSERT); _cimg_iskey_test2(HOME);
6248       _cimg_iskey_test2(PAGEUP); _cimg_iskey_test2(TAB); _cimg_iskey_test2(Q); _cimg_iskey_test2(W);
6249       _cimg_iskey_test2(E); _cimg_iskey_test2(R); _cimg_iskey_test2(T); _cimg_iskey_test2(Y);
6250       _cimg_iskey_test2(U); _cimg_iskey_test2(I); _cimg_iskey_test2(O); _cimg_iskey_test2(P);
6251       _cimg_iskey_test2(DELETE); _cimg_iskey_test2(END); _cimg_iskey_test2(PAGEDOWN);
6252       _cimg_iskey_test2(CAPSLOCK); _cimg_iskey_test2(A); _cimg_iskey_test2(S); _cimg_iskey_test2(D);
6253       _cimg_iskey_test2(F); _cimg_iskey_test2(G); _cimg_iskey_test2(H); _cimg_iskey_test2(J);
6254       _cimg_iskey_test2(K); _cimg_iskey_test2(L); _cimg_iskey_test2(ENTER);
6255       _cimg_iskey_test2(SHIFTLEFT); _cimg_iskey_test2(Z); _cimg_iskey_test2(X); _cimg_iskey_test2(C);
6256       _cimg_iskey_test2(V); _cimg_iskey_test2(B); _cimg_iskey_test2(N); _cimg_iskey_test2(M);
6257       _cimg_iskey_test2(SHIFTRIGHT); _cimg_iskey_test2(ARROWUP); _cimg_iskey_test2(CTRLLEFT);
6258       _cimg_iskey_test2(APPLEFT); _cimg_iskey_test2(ALT); _cimg_iskey_test2(SPACE); _cimg_iskey_test2(ALTGR);
6259       _cimg_iskey_test2(APPRIGHT); _cimg_iskey_test2(MENU); _cimg_iskey_test2(CTRLRIGHT);
6260       _cimg_iskey_test2(ARROWLEFT); _cimg_iskey_test2(ARROWDOWN); _cimg_iskey_test2(ARROWRIGHT);
6261       _cimg_iskey_test2(PAD0); _cimg_iskey_test2(PAD1); _cimg_iskey_test2(PAD2);
6262       _cimg_iskey_test2(PAD3); _cimg_iskey_test2(PAD4); _cimg_iskey_test2(PAD5);
6263       _cimg_iskey_test2(PAD6); _cimg_iskey_test2(PAD7); _cimg_iskey_test2(PAD8);
6264       _cimg_iskey_test2(PAD9); _cimg_iskey_test2(PADADD); _cimg_iskey_test2(PADSUB);
6265       _cimg_iskey_test2(PADMUL); _cimg_iskey_test2(PADDIV);
6266       return false;
6267     }
6268 
6269     //! Test if a key sequence has been typed.
6270     bool is_key_sequence(const unsigned int *const key_sequence, const unsigned int length, const bool remove_sequence=false) {
6271       if (key_sequence && length) {
6272         const unsigned int
6273           *const ps_end = key_sequence + length - 1,
6274           *const pk_end = (unsigned int*)_keys + 1 + sizeof(_keys)/sizeof(unsigned int) - length,
6275           k = *ps_end;
6276         for (unsigned int *pk = (unsigned int*)_keys; pk<pk_end; ) {
6277           if (*(pk++)==k) {
6278             bool res = true;
6279             const unsigned int *ps = ps_end, *pk2 = pk;
6280             for (unsigned int i = 1; i<length; ++i) res = (*(--ps)==*(pk2++));
6281             if (res) {
6282               if (remove_sequence) std::memset((void*)(pk-1),0,sizeof(unsigned int)*length);
6283               return true;
6284             }
6285           }
6286         }
6287       }
6288       return false;
6289     }
6290 
6291     //@}
6292     //------------------------------------------
6293     //
6294     //! \name Instance Characteristics
6295     //@{
6296     //------------------------------------------
6297 
6298     //! Get display width.
6299     int width() const {
6300       return (int)_width;
6301     }
6302 
6303     //! Get display height.
6304     int height() const {
6305       return (int)_height;
6306     }
6307 
6308     //! Get X-coordinate of the mouse pointer.
6309     int mouse_x() const {
6310       return _mouse_x;
6311     }
6312 
6313     //! Get Y-coordinate of the mouse pointer.
6314     int mouse_y() const {
6315       return _mouse_y;
6316     }
6317 
6318     //! Get current or previous state of the mouse buttons.
6319     unsigned int button() const {
6320       return _button;
6321     }
6322 
6323     //! Get current state of the mouse wheel.
6324     int wheel() const {
6325       return _wheel;
6326     }
6327 
6328     //! Get current or previous state of the keyboard.
6329     unsigned int key(const unsigned int pos=0) const {
6330       return pos<(sizeof(_keys)/sizeof(unsigned int))?_keys[pos]:0;
6331     }
6332 
6333     unsigned int released_key(const unsigned int pos=0) const {
6334       return pos<(sizeof(_released_keys)/sizeof(unsigned int))?_released_keys[pos]:0;
6335     }
6336 
6337     //! Get keycode corresponding to given input string.
6338     static unsigned int keycode(const char *const textcode) {
6339 #define _cimg_keycode(k) if (!cimg::strcasecmp(textcode,#k)) return cimg::key##k;
6340       _cimg_keycode(ESC); _cimg_keycode(F1); _cimg_keycode(F2); _cimg_keycode(F3);
6341       _cimg_keycode(F4); _cimg_keycode(F5); _cimg_keycode(F6); _cimg_keycode(F7);
6342       _cimg_keycode(F8); _cimg_keycode(F9); _cimg_keycode(F10); _cimg_keycode(F11);
6343       _cimg_keycode(F12); _cimg_keycode(PAUSE); _cimg_keycode(1); _cimg_keycode(2);
6344       _cimg_keycode(3); _cimg_keycode(4); _cimg_keycode(5); _cimg_keycode(6);
6345       _cimg_keycode(7); _cimg_keycode(8); _cimg_keycode(9); _cimg_keycode(0);
6346       _cimg_keycode(BACKSPACE); _cimg_keycode(INSERT); _cimg_keycode(HOME);
6347       _cimg_keycode(PAGEUP); _cimg_keycode(TAB); _cimg_keycode(Q); _cimg_keycode(W);
6348       _cimg_keycode(E); _cimg_keycode(R); _cimg_keycode(T); _cimg_keycode(Y);
6349       _cimg_keycode(U); _cimg_keycode(I); _cimg_keycode(O); _cimg_keycode(P);
6350       _cimg_keycode(DELETE); _cimg_keycode(END); _cimg_keycode(PAGEDOWN);
6351       _cimg_keycode(CAPSLOCK); _cimg_keycode(A); _cimg_keycode(S); _cimg_keycode(D);
6352       _cimg_keycode(F); _cimg_keycode(G); _cimg_keycode(H); _cimg_keycode(J);
6353       _cimg_keycode(K); _cimg_keycode(L); _cimg_keycode(ENTER);
6354       _cimg_keycode(SHIFTLEFT); _cimg_keycode(Z); _cimg_keycode(X); _cimg_keycode(C);
6355       _cimg_keycode(V); _cimg_keycode(B); _cimg_keycode(N); _cimg_keycode(M);
6356       _cimg_keycode(SHIFTRIGHT); _cimg_keycode(ARROWUP); _cimg_keycode(CTRLLEFT);
6357       _cimg_keycode(APPLEFT); _cimg_keycode(ALT); _cimg_keycode(SPACE); _cimg_keycode(ALTGR);
6358       _cimg_keycode(APPRIGHT); _cimg_keycode(MENU); _cimg_keycode(CTRLRIGHT);
6359       _cimg_keycode(ARROWLEFT); _cimg_keycode(ARROWDOWN); _cimg_keycode(ARROWRIGHT);
6360       _cimg_keycode(PAD0); _cimg_keycode(PAD1); _cimg_keycode(PAD2);
6361       _cimg_keycode(PAD3); _cimg_keycode(PAD4); _cimg_keycode(PAD5);
6362       _cimg_keycode(PAD6); _cimg_keycode(PAD7); _cimg_keycode(PAD8);
6363       _cimg_keycode(PAD9); _cimg_keycode(PADADD); _cimg_keycode(PADSUB);
6364       _cimg_keycode(PADMUL); _cimg_keycode(PADDIV);
6365       return 0;
6366     }
6367 
6368     //! Get normalization type of the display.
6369     unsigned int normalization() const {
6370       return _normalization;
6371     }
6372 
6373     //! Get title of the display.
6374     const char *title() const {
6375       return _title;
6376     }
6377 
6378     //! Get display window width.
6379     int window_width() const {
6380       return (int)_window_width;
6381     }
6382 
6383     //! Get display window height.
6384     int window_height() const {
6385       return (int)_window_height;
6386     }
6387 
6388     //! Get X-coordinate of the window.
6389     int window_x() const {
6390       return _window_x;
6391     }
6392 
6393     //! Get Y-coordinate of the window.
6394     int window_y() const {
6395       return _window_y;
6396     }
6397 
6398 #if cimg_display==0
6399 
6400     //! Get the width of the screen resolution.
6401     static int screen_width() {
6402       _no_display_exception();
6403       return 0;
6404     }
6405 
6406     //! Get the height of the screen resolution.
6407     static int screen_height() {
6408       _no_display_exception();
6409       return 0;
6410     }
6411 
6412 #endif
6413 
6414     //! Get the frame per second rate.
6415     float frames_per_second() {
6416       if (!_fps_timer) _fps_timer = cimg::time();
6417       const float delta = (cimg::time()-_fps_timer)/1000.0f;
6418       ++_fps_frames;
6419       if (delta>=1) {
6420         _fps_fps = _fps_frames/delta;
6421         _fps_frames = 0;
6422         _fps_timer = cimg::time();
6423       }
6424       return _fps_fps;
6425     }
6426 
6427     //@}
6428     //------------------------------------------
6429     //
6430     //! \name Display Manipulation
6431     //@{
6432     //------------------------------------------
6433 
6434 #if cimg_display==0
6435 
6436     //! Display an image in a window.
6437     template<typename T>
6438     CImgDisplay& display(const CImg<T>& img) {
6439       return assign(img);
6440     }
6441 
6442 #endif
6443 
6444     //! Display an image list CImgList<T> into a display window.
6445     /** First, all images of the list are appended into a single image used for visualization,
6446         then this image is displayed in the current display window.
6447         \param list : The list of images to display.
6448         \param axis : The axis used to append the image for visualization. Can be 'x' (default),'y','z' or 'c'.
6449         \param align : Defines the relative alignment of images when displaying images of different sizes.
6450         Can be '\p c' (centered, which is the default), '\p p' (top alignment) and '\p n' (bottom aligment).
6451     **/
6452     template<typename T>
6453     CImgDisplay& display(const CImgList<T>& list, const char axis='x', const float align=0) {
6454       return display(list.get_append(axis,align));
6455     }
6456 
6457     //! Resize a display window in its current size.
6458     CImgDisplay& resize(const bool force_redraw=true) {
6459       resize(_window_width,_window_height,force_redraw);
6460       return *this;
6461     }
6462 
6463     //! Resize a display window with the size of an image.
6464     /** \param img    : Input image. \p image.width and \p image.height give the new dimensions of the display window.
6465         \param force_redraw : If \p true (default), the current displayed image in the display window will
6466         be bloc-interpolated to fit the new dimensions. If \p false, a black image will be drawn in the resized window.
6467     **/
6468     template<typename T>
6469     CImgDisplay& resize(const CImg<T>& img, const bool force_redraw=true) {
6470       return resize(img._width,img._height,force_redraw);
6471     }
6472 
6473     //! Resize a display window using the size of the given display \p disp.
6474     CImgDisplay& resize(const CImgDisplay& disp, const bool force_redraw=true) {
6475       return resize(disp._width,disp._height,force_redraw);
6476     }
6477 
6478 #if cimg_display==0
6479 
6480     //! Resize window.
6481     CImgDisplay& resize(const int width, const int height, const bool force_redraw=true) {
6482       return assign(width,height,0,3,force_redraw);
6483     }
6484 
6485 #endif
6486 
6487     // Render pixel buffer with size (wd,hd) from source buffer of size (ws,hs).
6488     template<typename t, typename T>
6489     static void _render_resize(const T *ptrs, const unsigned int ws, const unsigned int hs,
6490                                t *ptrd, const unsigned int wd, const unsigned int hd) {
6491       unsigned int *const offx = new unsigned int[wd], *const offy = new unsigned int[hd+1], *poffx, *poffy;
6492       float s, curr, old;
6493       s = (float)ws/wd;
6494       poffx = offx; curr = 0; for (unsigned int x = 0; x<wd; ++x) { old = curr; curr+=s; *(poffx++) = (unsigned int)curr - (unsigned int)old; }
6495       s = (float)hs/hd;
6496       poffy = offy; curr = 0; for (unsigned int y = 0; y<hd; ++y) { old = curr; curr+=s; *(poffy++) = ws*((unsigned int)curr - (unsigned int)old); }
6497       *poffy = 0;
6498       poffy = offy;
6499       for (unsigned int y = 0; y<hd; ) {
6500         const T *ptr = ptrs;
6501         poffx = offx;
6502         for (unsigned int x = 0; x<wd; ++x) { *(ptrd++) = *ptr; ptr+=*(poffx++); }
6503         ++y;
6504         unsigned int dy = *(poffy++);
6505         for ( ; !dy && y<hd; std::memcpy(ptrd,ptrd - wd,sizeof(t)*wd), ++y, ptrd+=wd, dy = *(poffy++)) {}
6506         ptrs+=dy;
6507       }
6508       delete[] offx; delete[] offy;
6509     }
6510 
6511     //! Set fullscreen mode.
6512     CImgDisplay& set_fullscreen(const bool is_fullscreen, const bool force_redraw=true) {
6513       if (is_empty() || _is_fullscreen==is_fullscreen) return *this;
6514       return toggle_fullscreen(force_redraw);
6515     }
6516 
6517 #if cimg_display==0
6518 
6519     //! Toggle fullscreen mode.
6520     CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
6521       return assign(_width,_height,0,3,force_redraw);
6522     }
6523 
6524     //! Show a closed display.
6525     CImgDisplay& show() {
6526       return assign();
6527     }
6528 
6529     //! Close a visible display.
6530     CImgDisplay& close() {
6531       return assign();
6532     }
6533 
6534     //! Move window.
6535     CImgDisplay& move(const int pos_x, const int pos_y) {
6536       return assign(pos_x,pos_y);
6537     }
6538 
6539     //! Show mouse pointer.
6540     CImgDisplay& show_mouse() {
6541       return assign();
6542     }
6543 
6544     //! Hide mouse pointer.
6545     CImgDisplay& hide_mouse() {
6546       return assign();
6547     }
6548 
6549     //! Move mouse pointer to a specific location.
6550     CImgDisplay& set_mouse(const int pos_x, const int pos_y) {
6551       return assign(pos_x,pos_y);
6552     }
6553 
6554     CImgDisplay& set_title(const char *const format, ...) {
6555       return assign(0,0,format);
6556     }
6557 
6558     //! Render image buffer into GDI native image format.
6559     template<typename T>
6560     CImgDisplay& render(const CImg<T>& img) {
6561       return assign(img);
6562     }
6563 
6564     //! Re-paint image content in window.
6565     CImgDisplay& paint() {
6566       return assign();
6567     }
6568 
6569     //! Take a snapshot of the display in the specified image.
6570     template<typename T>
6571     const CImgDisplay& snapshot(CImg<T>& img) const {
6572       _no_display_exception();
6573       return *this;
6574     }
6575 #endif
6576 
6577     //! Simulate a mouse button event.
6578     CImgDisplay& set_button() {
6579       _button = 0;
6580       _is_event = true;
6581       return *this;
6582     }
6583 
6584     CImgDisplay& set_button(const unsigned int button, const bool is_pressed=true) {
6585       const unsigned int buttoncode = button==1?1:button==2?2:button==3?4:0;
6586       if (is_pressed) _button |= buttoncode; else _button &= ~buttoncode;
6587       _is_event = buttoncode?true:false;
6588       return *this;
6589     }
6590 
6591     //! Simulate a mouse wheel event, or flush wheel events.
6592     CImgDisplay& set_wheel() {
6593       _wheel = 0;
6594       _is_event = true;
6595       return *this;
6596     }
6597 
6598     CImgDisplay& set_wheel(const int amplitude) {
6599       _wheel+=amplitude;
6600       _is_event = amplitude?true:false;
6601       return *this;
6602     }
6603 
6604     //! Simulate a keyboard press/release event, or flush all key events.
6605     CImgDisplay& set_key() {
6606       std::memset((void*)_keys,0,sizeof(_keys));
6607       std::memset((void*)_released_keys,0,sizeof(_released_keys));
6608       _is_keyESC = _is_keyF1 = _is_keyF2 = _is_keyF3 = _is_keyF4 = _is_keyF5 = _is_keyF6 = _is_keyF7 = _is_keyF8 = _is_keyF9 =
6609         _is_keyF10 = _is_keyF11 = _is_keyF12 = _is_keyPAUSE = _is_key1 = _is_key2 = _is_key3 = _is_key4 = _is_key5 = _is_key6 =
6610         _is_key7 = _is_key8 = _is_key9 = _is_key0 = _is_keyBACKSPACE = _is_keyINSERT = _is_keyHOME = _is_keyPAGEUP = _is_keyTAB =
6611         _is_keyQ = _is_keyW = _is_keyE = _is_keyR = _is_keyT = _is_keyY = _is_keyU = _is_keyI = _is_keyO = _is_keyP = _is_keyDELETE =
6612         _is_keyEND = _is_keyPAGEDOWN = _is_keyCAPSLOCK = _is_keyA = _is_keyS = _is_keyD = _is_keyF = _is_keyG = _is_keyH = _is_keyJ =
6613         _is_keyK = _is_keyL = _is_keyENTER = _is_keySHIFTLEFT = _is_keyZ = _is_keyX = _is_keyC = _is_keyV = _is_keyB = _is_keyN =
6614         _is_keyM = _is_keySHIFTRIGHT = _is_keyARROWUP = _is_keyCTRLLEFT = _is_keyAPPLEFT = _is_keyALT = _is_keySPACE = _is_keyALTGR = _is_keyAPPRIGHT =
6615         _is_keyMENU = _is_keyCTRLRIGHT = _is_keyARROWLEFT = _is_keyARROWDOWN = _is_keyARROWRIGHT = _is_keyPAD0 = _is_keyPAD1 = _is_keyPAD2 =
6616         _is_keyPAD3 = _is_keyPAD4 = _is_keyPAD5 = _is_keyPAD6 = _is_keyPAD7 = _is_keyPAD8 = _is_keyPAD9 = _is_keyPADADD = _is_keyPADSUB =
6617         _is_keyPADMUL = _is_keyPADDIV = false;
6618       _is_event = true;
6619       return *this;
6620     }
6621 
6622     CImgDisplay& set_key(const unsigned int keycode, const bool pressed=true) {
6623 #define _cimg_set_key(k) if (keycode==cimg::key##k) _is_key##k = pressed;
6624       _cimg_set_key(ESC); _cimg_set_key(F1); _cimg_set_key(F2); _cimg_set_key(F3);
6625       _cimg_set_key(F4); _cimg_set_key(F5); _cimg_set_key(F6); _cimg_set_key(F7);
6626       _cimg_set_key(F8); _cimg_set_key(F9); _cimg_set_key(F10); _cimg_set_key(F11);
6627       _cimg_set_key(F12); _cimg_set_key(PAUSE); _cimg_set_key(1); _cimg_set_key(2);
6628       _cimg_set_key(3); _cimg_set_key(4); _cimg_set_key(5); _cimg_set_key(6);
6629       _cimg_set_key(7); _cimg_set_key(8); _cimg_set_key(9); _cimg_set_key(0);
6630       _cimg_set_key(BACKSPACE); _cimg_set_key(INSERT); _cimg_set_key(HOME);
6631       _cimg_set_key(PAGEUP); _cimg_set_key(TAB); _cimg_set_key(Q); _cimg_set_key(W);
6632       _cimg_set_key(E); _cimg_set_key(R); _cimg_set_key(T); _cimg_set_key(Y);
6633       _cimg_set_key(U); _cimg_set_key(I); _cimg_set_key(O); _cimg_set_key(P);
6634       _cimg_set_key(DELETE); _cimg_set_key(END); _cimg_set_key(PAGEDOWN);
6635       _cimg_set_key(CAPSLOCK); _cimg_set_key(A); _cimg_set_key(S); _cimg_set_key(D);
6636       _cimg_set_key(F); _cimg_set_key(G); _cimg_set_key(H); _cimg_set_key(J);
6637       _cimg_set_key(K); _cimg_set_key(L); _cimg_set_key(ENTER);
6638       _cimg_set_key(SHIFTLEFT); _cimg_set_key(Z); _cimg_set_key(X); _cimg_set_key(C);
6639       _cimg_set_key(V); _cimg_set_key(B); _cimg_set_key(N); _cimg_set_key(M);
6640       _cimg_set_key(SHIFTRIGHT); _cimg_set_key(ARROWUP); _cimg_set_key(CTRLLEFT);
6641       _cimg_set_key(APPLEFT); _cimg_set_key(ALT); _cimg_set_key(SPACE); _cimg_set_key(ALTGR);
6642       _cimg_set_key(APPRIGHT); _cimg_set_key(MENU); _cimg_set_key(CTRLRIGHT);
6643       _cimg_set_key(ARROWLEFT); _cimg_set_key(ARROWDOWN); _cimg_set_key(ARROWRIGHT);
6644       _cimg_set_key(PAD0); _cimg_set_key(PAD1); _cimg_set_key(PAD2);
6645       _cimg_set_key(PAD3); _cimg_set_key(PAD4); _cimg_set_key(PAD5);
6646       _cimg_set_key(PAD6); _cimg_set_key(PAD7); _cimg_set_key(PAD8);
6647       _cimg_set_key(PAD9); _cimg_set_key(PADADD); _cimg_set_key(PADSUB);
6648       _cimg_set_key(PADMUL); _cimg_set_key(PADDIV);
6649       if (pressed) {
6650         if (*_keys)
6651           std::memmove((void*)(_keys+1),(void*)_keys,sizeof(_keys) - sizeof(unsigned int));
6652         *_keys = keycode;
6653         if (*_released_keys) {
6654           std::memmove((void*)(_released_keys+1),(void*)_released_keys,sizeof(_released_keys) - sizeof(unsigned int));
6655           *_released_keys = 0;
6656         }
6657       } else {
6658         if (*_keys) {
6659           std::memmove((void*)(_keys+1),(void*)_keys,sizeof(_keys) - sizeof(unsigned int));
6660           *_keys = 0;
6661         }
6662         if (*_released_keys)
6663           std::memmove((void*)(_released_keys+1),(void*)_released_keys,sizeof(_released_keys) - sizeof(unsigned int));
6664         *_released_keys = keycode;
6665       }
6666       _is_event = keycode?true:false;
6667       return *this;
6668     }
6669 
6670     //! Flush all display events.
6671     CImgDisplay& flush() {
6672       set_key().set_button().set_wheel();
6673       _is_resized = _is_moved = _is_event = false;
6674       _fps_timer = _fps_frames = _timer = 0;
6675       _fps_fps = 0;
6676       return *this;
6677     }
6678 
6679     //! Synchronized waiting function. Same as cimg::wait().
6680     CImgDisplay& wait(const unsigned int milliseconds) {
6681       cimg::_sleep(milliseconds,_timer);
6682       return *this;
6683     }
6684 
6685     //! Wait for an event occuring on the current display.
6686     CImgDisplay& wait() {
6687       wait(*this);
6688       return *this;
6689     }
6690 
6691     //! Wait for any event occuring on the display \c disp1.
6692     static void wait(CImgDisplay& disp1) {
6693       disp1._is_event = 0;
6694       while (!disp1._is_closed && !disp1._is_event) wait_all();
6695     }
6696 
6697     //! Wait for any event occuring either on the display \c disp1 or \c disp2.
6698     static void wait(CImgDisplay& disp1, CImgDisplay& disp2) {
6699       disp1._is_event = disp2._is_event = 0;
6700       while ((!disp1._is_closed || !disp2._is_closed) &&
6701              !disp1._is_event && !disp2._is_event) wait_all();
6702     }
6703 
6704     //! Wait for any event occuring either on the display \c disp1, \c disp2 or \c disp3.
6705     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3) {
6706       disp1._is_event = disp2._is_event = disp3._is_event = 0;
6707       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed) &&
6708              !disp1._is_event && !disp2._is_event && !disp3._is_event) wait_all();
6709     }
6710 
6711     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3 or \c disp4.
6712     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4) {
6713       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = 0;
6714       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed) &&
6715              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event) wait_all();
6716     }
6717 
6718     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4 or \c disp5.
6719     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5) {
6720       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = 0;
6721       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed) &&
6722              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event) wait_all();
6723     }
6724 
6725     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp6.
6726     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
6727                      CImgDisplay& disp6) {
6728       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
6729         disp6._is_event = 0;
6730       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
6731               !disp6._is_closed) &&
6732              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
6733              !disp6._is_event) wait_all();
6734     }
6735 
6736     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp7.
6737     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
6738                      CImgDisplay& disp6, CImgDisplay& disp7) {
6739       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
6740         disp6._is_event = disp7._is_event = 0;
6741       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
6742               !disp6._is_closed || !disp7._is_closed) &&
6743              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
6744              !disp6._is_event && !disp7._is_event) wait_all();
6745     }
6746 
6747     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp8.
6748     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
6749                      CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8) {
6750       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
6751         disp6._is_event = disp7._is_event = disp8._is_event = 0;
6752       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
6753               !disp6._is_closed || !disp7._is_closed || !disp8._is_closed) &&
6754              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
6755              !disp6._is_event && !disp7._is_event && !disp8._is_event) wait_all();
6756     }
6757 
6758     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp9.
6759     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
6760                      CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9) {
6761       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
6762         disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = 0;
6763       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
6764               !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed) &&
6765              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
6766              !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event) wait_all();
6767     }
6768 
6769     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp10.
6770     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
6771                      CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9, CImgDisplay& disp10) {
6772       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
6773         disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = disp10._is_event = 0;
6774       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
6775               !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed || !disp10._is_closed) &&
6776              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
6777              !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event && !disp10._is_event) wait_all();
6778     }
6779 
6780 #if cimg_display==0
6781 
6782     //! Wait for a window event in any CImg window.
6783     static void wait_all() {
6784       return _no_display_exception();
6785     }
6786 
6787 #endif
6788 
6789     // X11-based implementation
6790     //--------------------------
6791 #if cimg_display==1
6792 
6793     Atom _wm_window_atom, _wm_protocol_atom;
6794     Window _window, _background_window;
6795     Colormap _colormap;
6796     XImage *_image;
6797     void *_data;
6798 #ifdef cimg_use_xshm
6799     XShmSegmentInfo *_shminfo;
6800 #endif
6801 
6802     static int screen_width() {
6803       Display *const dpy = cimg::X11_attr().display;
6804       int res = 0;
6805       if (!dpy) {
6806         Display *const _dpy = XOpenDisplay(0);
6807         if (!_dpy)
6808           throw CImgDisplayException("CImgDisplay::screen_width() : Failed to open X11 display.");
6809         res = DisplayWidth(_dpy,DefaultScreen(_dpy));
6810         XCloseDisplay(_dpy);
6811       } else {
6812 #ifdef cimg_use_xrandr
6813         if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution)
6814           res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width;
6815         else res = DisplayWidth(dpy,DefaultScreen(dpy));
6816 #else
6817         res = DisplayWidth(dpy,DefaultScreen(dpy));
6818 #endif
6819       }
6820       return res;
6821     }
6822 
6823     static int screen_height() {
6824       Display *const dpy = cimg::X11_attr().display;
6825       int res = 0;
6826       if (!dpy) {
6827         Display *const _dpy = XOpenDisplay(0);
6828         if (!_dpy)
6829           throw CImgDisplayException("CImgDisplay::screen_height() : Failed to open X11 display.");
6830         res = DisplayHeight(_dpy,DefaultScreen(_dpy));
6831         XCloseDisplay(_dpy);
6832       } else {
6833 #ifdef cimg_use_xrandr
6834         if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution)
6835           res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height;
6836         else res = DisplayHeight(dpy,DefaultScreen(dpy));
6837 #else
6838         res = DisplayHeight(dpy,DefaultScreen(dpy));
6839 #endif
6840       }
6841       return res;
6842     }
6843 
6844     static void wait_all() {
6845       Display *const dpy = cimg::X11_attr().display;
6846       if (!dpy) return;
6847       XLockDisplay(dpy);
6848       bool flag = true;
6849       XEvent event;
6850       while (flag) {
6851         XNextEvent(dpy,&event);
6852         for (unsigned int i = 0; i<cimg::X11_attr().nb_wins; ++i)
6853           if (!cimg::X11_attr().wins[i]->_is_closed && event.xany.window==cimg::X11_attr().wins[i]->_window) {
6854             cimg::X11_attr().wins[i]->_handle_events(&event);
6855             if (cimg::X11_attr().wins[i]->_is_event) flag = false;
6856           }
6857       }
6858       XUnlockDisplay(dpy);
6859     }
6860 
6861     void _handle_events(const XEvent *const pevent) {
6862       Display *const dpy = cimg::X11_attr().display;
6863       XEvent event = *pevent;
6864       switch (event.type) {
6865       case ClientMessage : {
6866         if ((int)event.xclient.message_type==(int)_wm_protocol_atom &&
6867             (int)event.xclient.data.l[0]==(int)_wm_window_atom) {
6868           XUnmapWindow(cimg::X11_attr().display,_window);
6869           _is_closed = _is_event = true;
6870         }
6871       } break;
6872       case ConfigureNotify : {
6873         while (XCheckWindowEvent(dpy,_window,StructureNotifyMask,&event)) {}
6874         const unsigned int nw = event.xconfigure.width, nh = event.xconfigure.height;
6875         const int nx = event.xconfigure.x, ny = event.xconfigure.y;
6876         if (nw && nh && (nw!=_window_width || nh!=_window_height)) {
6877           _window_width = nw; _window_height = nh; _mouse_x = _mouse_y = -1;
6878           XResizeWindow(dpy,_window,_window_width,_window_height);
6879           _is_resized = _is_event = true;
6880         }
6881         if (nx!=_window_x || ny!=_window_y) { _window_x = nx; _window_y = ny; _is_moved = _is_event = true; }
6882       } break;
6883       case Expose : {
6884         while (XCheckWindowEvent(dpy,_window,ExposureMask,&event)) {}
6885         _paint(false);
6886         if (_is_fullscreen) {
6887           XWindowAttributes attr;
6888           XGetWindowAttributes(dpy,_window,&attr);
6889           while (attr.map_state!=IsViewable) XSync(dpy,False);
6890           XSetInputFocus(dpy,_window,RevertToParent,CurrentTime);
6891         }
6892       } break;
6893       case ButtonPress : {
6894         do {
6895           _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y;
6896           if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
6897           switch (event.xbutton.button) {
6898           case 1 : set_button(1); break;
6899           case 3 : set_button(2); break;
6900           case 2 : set_button(3); break;
6901           }
6902         } while (XCheckWindowEvent(dpy,_window,ButtonPressMask,&event));
6903       } break;
6904       case ButtonRelease : {
6905         do {
6906           _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y;
6907           if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
6908           switch (event.xbutton.button) {
6909           case 1 : set_button(1,false); break;
6910           case 3 : set_button(2,false); break;
6911           case 2 : set_button(3,false); break;
6912           case 4 : set_wheel(1); break;
6913           case 5 : set_wheel(-1); break;
6914           }
6915         } while (XCheckWindowEvent(dpy,_window,ButtonReleaseMask,&event));
6916       } break;
6917       case KeyPress : {
6918         char tmp = 0; KeySym ksym;
6919         XLookupString(&event.xkey,&tmp,1,&ksym,0);
6920         set_key((unsigned int)ksym,true);
6921       } break;
6922       case KeyRelease : {
6923         char tmp = 0; KeySym ksym;
6924         XLookupString(&event.xkey,&tmp,1,&ksym,0);
6925         set_key((unsigned int)ksym,false);
6926       } break;
6927       case EnterNotify: {
6928         while (XCheckWindowEvent(dpy,_window,EnterWindowMask,&event)) {}
6929         _mouse_x = event.xmotion.x;
6930         _mouse_y = event.xmotion.y;
6931         if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
6932       } break;
6933       case LeaveNotify : {
6934         while (XCheckWindowEvent(dpy,_window,LeaveWindowMask,&event)) {}
6935         _mouse_x = _mouse_y =-1; _is_event = true;
6936       } break;
6937       case MotionNotify : {
6938         while (XCheckWindowEvent(dpy,_window,PointerMotionMask,&event)) {}
6939         _mouse_x = event.xmotion.x;
6940         _mouse_y = event.xmotion.y;
6941         if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
6942         _is_event = true;
6943       } break;
6944       }
6945     }
6946 
6947     static void* _events_thread(void *) { // Only one thread to handle events for all opened display windows.
6948       Display *const dpy = cimg::X11_attr().display;
6949       XEvent event;
6950       pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0);
6951       pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0);
6952       for (;;) {
6953         XLockDisplay(dpy);
6954         bool event_flag = XCheckTypedEvent(dpy,ClientMessage,&event);
6955         if (!event_flag) event_flag = XCheckMaskEvent(dpy,
6956                                                       ExposureMask | StructureNotifyMask | ButtonPressMask|
6957                                                       KeyPressMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask|
6958                                                       ButtonReleaseMask | KeyReleaseMask,&event);
6959         if (event_flag)
6960           for (unsigned int i = 0; i<cimg::X11_attr().nb_wins; ++i)
6961             if (!cimg::X11_attr().wins[i]->_is_closed && event.xany.window==cimg::X11_attr().wins[i]->_window)
6962               cimg::X11_attr().wins[i]->_handle_events(&event);
6963         XUnlockDisplay(dpy);
6964         pthread_testcancel();
6965         cimg::sleep(8);
6966       }
6967       return 0;
6968     }
6969 
6970     void _set_colormap(Colormap& colormap, const unsigned int dim) {
6971       XColor palette[256];
6972       switch (dim) {
6973       case 1 : { // palette for greyscale images
6974         for (unsigned int index = 0; index<256; ++index) {
6975           palette[index].pixel = index;
6976           palette[index].red = palette[index].green = palette[index].blue = (unsigned short)(index<<8);
6977           palette[index].flags = DoRed | DoGreen | DoBlue;
6978         }
6979       } break;
6980       case 2 : { // palette for RG images
6981         for (unsigned int index = 0, r = 8; r<256; r+=16)
6982           for (unsigned int g = 8; g<256; g+=16) {
6983             palette[index].pixel = index;
6984             palette[index].red = palette[index].blue = (unsigned short)(r<<8);
6985             palette[index].green = (unsigned short)(g<<8);
6986             palette[index++].flags = DoRed | DoGreen | DoBlue;
6987           }
6988       } break;
6989       default : { // palette for RGB images
6990         for (unsigned int index = 0, r = 16; r<256; r+=32)
6991           for (unsigned int g = 16; g<256; g+=32)
6992             for (unsigned int b = 32; b<256; b+=64) {
6993               palette[index].pixel = index;
6994               palette[index].red = (unsigned short)(r<<8);
6995               palette[index].green = (unsigned short)(g<<8);
6996               palette[index].blue = (unsigned short)(b<<8);
6997               palette[index++].flags = DoRed | DoGreen | DoBlue;
6998             }
6999       }
7000       }
7001       XStoreColors(cimg::X11_attr().display,colormap,palette,256);
7002     }
7003 
7004     void _map_window() {
7005       Display *const dpy = cimg::X11_attr().display;
7006       bool is_exposed = false, is_mapped = false;
7007       XWindowAttributes attr;
7008       XEvent event;
7009       XMapRaised(dpy,_window);
7010       do { // Wait for the window to be mapped.
7011         XWindowEvent(dpy,_window,StructureNotifyMask | ExposureMask,&event);
7012         switch (event.type) {
7013         case MapNotify : is_mapped = true; break;
7014         case Expose : is_exposed = true; break;
7015         }
7016       } while (!is_exposed || !is_mapped);
7017       do { // Wait for the window to be visible.
7018         XGetWindowAttributes(dpy,_window,&attr);
7019         if (attr.map_state!=IsViewable) { XSync(dpy,False); cimg::sleep(10); }
7020       } while (attr.map_state!=IsViewable);
7021       _window_x = attr.x;
7022       _window_y = attr.y;
7023     }
7024 
7025     void _paint(const bool wait_expose=true) {
7026       if (_is_closed || !_image) return;
7027       Display *const dpy = cimg::X11_attr().display;
7028       if (wait_expose) { // Send an expose event sticked to display window to force repaint.
7029         static XEvent event;
7030         event.xexpose.type = Expose;
7031         event.xexpose.serial = 0;
7032         event.xexpose.send_event = True;
7033         event.xexpose.display = dpy;
7034         event.xexpose.window = _window;
7035         event.xexpose.x = 0;
7036         event.xexpose.y = 0;
7037         event.xexpose.width = width();
7038         event.xexpose.height = height();
7039         event.xexpose.count = 0;
7040         XSendEvent(dpy,_window,False,0,&event);
7041       } else { // Repaint directly (may be called from the expose event).
7042         GC gc = DefaultGC(dpy,DefaultScreen(dpy));
7043 #ifdef cimg_use_xshm
7044         if (_shminfo) {
7045           const int completion_type = XShmGetEventBase(dpy) + ShmCompletion;
7046           XEvent event;
7047           XShmPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height,True);
7048           do { XNextEvent(dpy,&event); } while (event.type!=completion_type);  // Wait for the image drawing to be completed.
7049         } else XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height);
7050 #else
7051         XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height);
7052 #endif
7053       }
7054     }
7055 
7056     template<typename T>
7057     void _resize(T pixel_type, const unsigned int ndimx, const unsigned int ndimy, const bool force_redraw) {
7058       Display *const dpy = cimg::X11_attr().display;
7059       cimg::unused(pixel_type);
7060 
7061 #ifdef cimg_use_xshm
7062       if (_shminfo) {
7063         XShmSegmentInfo *const nshminfo = new XShmSegmentInfo;
7064         XImage *const nimage = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),
7065                                                cimg::X11_attr().nb_bits,ZPixmap,0,nshminfo,ndimx,ndimy);
7066         if (!nimage) { delete nshminfo; return; }
7067         else {
7068           nshminfo->shmid = shmget(IPC_PRIVATE,ndimx*ndimy*sizeof(T),IPC_CREAT | 0777);
7069           if (nshminfo->shmid==-1) { XDestroyImage(nimage); delete nshminfo; return; }
7070           else {
7071             nshminfo->shmaddr = nimage->data = (char*)shmat(nshminfo->shmid,0,0);
7072             if (nshminfo->shmaddr==(char*)-1) { shmctl(nshminfo->shmid,IPC_RMID,0); XDestroyImage(nimage); delete nshminfo; return; }
7073             else {
7074               nshminfo->readOnly = False;
7075               cimg::X11_attr().is_shm_enabled = true;
7076               XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm);
7077               XShmAttach(dpy,nshminfo);
7078               XFlush(dpy);
7079               XSetErrorHandler(oldXErrorHandler);
7080               if (!cimg::X11_attr().is_shm_enabled) {
7081                 shmdt(nshminfo->shmaddr);
7082                 shmctl(nshminfo->shmid,IPC_RMID,0);
7083                 XDestroyImage(nimage);
7084                 delete nshminfo;
7085                 return;
7086               } else {
7087                 T *const ndata = (T*)nimage->data;
7088                 if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy);
7089                 else std::memset(ndata,0,sizeof(T)*ndimx*ndimy);
7090                 XShmDetach(dpy,_shminfo);
7091                 XDestroyImage(_image);
7092                 shmdt(_shminfo->shmaddr);
7093                 shmctl(_shminfo->shmid,IPC_RMID,0);
7094                 delete _shminfo;
7095                 _shminfo = nshminfo;
7096                 _image = nimage;
7097                 _data = (void*)ndata;
7098               }
7099             }
7100           }
7101         }
7102       } else
7103 #endif
7104         {
7105           T *ndata = (T*)std::malloc(ndimx*ndimy*sizeof(T));
7106           if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy);
7107           else std::memset(ndata,0,sizeof(T)*ndimx*ndimy);
7108           _data = (void*)ndata;
7109           XDestroyImage(_image);
7110           _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),
7111                                 cimg::X11_attr().nb_bits,ZPixmap,0,(char*)_data,ndimx,ndimy,8,0);
7112         }
7113     }
7114 
7115     void _init_fullscreen() {
7116       if (!_is_fullscreen || _is_closed) return;
7117       Display *const dpy = cimg::X11_attr().display;
7118       _background_window = 0;
7119 
7120 #ifdef cimg_use_xrandr
7121       int foo;
7122       if (XRRQueryExtension(dpy,&foo,&foo)) {
7123         XRRRotations(dpy,DefaultScreen(dpy),&cimg::X11_attr().curr_rotation);
7124         if (!cimg::X11_attr().resolutions) {
7125           cimg::X11_attr().resolutions = XRRSizes(dpy,DefaultScreen(dpy),&foo);
7126           cimg::X11_attr().nb_resolutions = (unsigned int)foo;
7127         }
7128         if (cimg::X11_attr().resolutions) {
7129           cimg::X11_attr().curr_resolution = 0;
7130           for (unsigned int i = 0; i<cimg::X11_attr().nb_resolutions; ++i) {
7131             const unsigned int
7132               nw = (unsigned int)(cimg::X11_attr().resolutions[i].width),
7133               nh = (unsigned int)(cimg::X11_attr().resolutions[i].height);
7134             if (nw>=_width && nh>=_height &&
7135                 nw<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width) &&
7136                 nh<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height))
7137               cimg::X11_attr().curr_resolution = i;
7138           }
7139           if (cimg::X11_attr().curr_resolution>0) {
7140             XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy));
7141             XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),
7142                                cimg::X11_attr().curr_resolution,cimg::X11_attr().curr_rotation,CurrentTime);
7143             XRRFreeScreenConfigInfo(config);
7144             XSync(dpy,False);
7145           }
7146         }
7147       }
7148       if (!cimg::X11_attr().resolutions)
7149         cimg::warn(_cimgdisplay_instance
7150                    "init_fullscreen() : Xrandr extension not supported by the X server.",
7151                    cimgdisplay_instance);
7152 #endif
7153 
7154       const unsigned int sx = screen_width(), sy = screen_height();
7155       if (sx==_width && sy==_height) return;
7156       XSetWindowAttributes winattr;
7157       winattr.override_redirect = True;
7158       _background_window = XCreateWindow(dpy,DefaultRootWindow(dpy),0,0,sx,sy,0,0,
7159                                          InputOutput,CopyFromParent,CWOverrideRedirect,&winattr);
7160       const unsigned int buf_size = sx*sy*(cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4));
7161       void *background_data = std::malloc(buf_size);
7162       std::memset(background_data,0,buf_size);
7163       XImage *background_image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,
7164                                               ZPixmap,0,(char*)background_data,sx,sy,8,0);
7165       XEvent event;
7166       XSelectInput(dpy,_background_window,StructureNotifyMask);
7167       XMapRaised(dpy,_background_window);
7168       do XWindowEvent(dpy,_background_window,StructureNotifyMask,&event);
7169       while (event.type!=MapNotify);
7170       GC gc = DefaultGC(dpy,DefaultScreen(dpy));
7171 #ifdef cimg_use_xshm
7172       if (_shminfo) XShmPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy,False);
7173       else XPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy);
7174 #else
7175       XPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy);
7176 #endif
7177       XWindowAttributes attr;
7178       XGetWindowAttributes(dpy,_background_window,&attr);
7179       while (attr.map_state!=IsViewable) XSync(dpy,False);
7180       XDestroyImage(background_image);
7181     }
7182 
7183     void _desinit_fullscreen() {
7184       if (!_is_fullscreen) return;
7185       Display *const dpy = cimg::X11_attr().display;
7186       XUngrabKeyboard(dpy,CurrentTime);
7187 #ifdef cimg_use_xrandr
7188       if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) {
7189         XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy));
7190         XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),0,cimg::X11_attr().curr_rotation,CurrentTime);
7191         XRRFreeScreenConfigInfo(config);
7192         XSync(dpy,False);
7193         cimg::X11_attr().curr_resolution = 0;
7194       }
7195 #endif
7196       if (_background_window) XDestroyWindow(dpy,_background_window);
7197       _background_window = 0;
7198       _is_fullscreen = false;
7199     }
7200 
7201     static int _assign_xshm(Display *dpy, XErrorEvent *error) {
7202       cimg::unused(dpy,error);
7203       cimg::X11_attr().is_shm_enabled = false;
7204       return 0;
7205     }
7206 
7207     void _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0,
7208                  const unsigned int normalization_type=3,
7209                  const bool fullscreen_flag=false, const bool closed_flag=false) {
7210 
7211       // Allocate space for window title
7212       const char *const nptitle = ptitle?ptitle:"";
7213       const unsigned int s = std::strlen(nptitle) + 1;
7214       char *const tmp_title = s?new char[s]:0;
7215       if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char));
7216 
7217       // Destroy previous display window if existing
7218       if (!is_empty()) assign();
7219 
7220       // Open X11 display and retrieve graphical properties.
7221       Display* &dpy = cimg::X11_attr().display;
7222       if (!dpy) {
7223         static const int xinit_status = XInitThreads();
7224         cimg::unused(xinit_status);
7225         dpy = XOpenDisplay(0);
7226         if (!dpy)
7227           throw CImgDisplayException(_cimgdisplay_instance
7228                                      "assign() : Failed to open X11 display.",
7229                                      cimgdisplay_instance);
7230 
7231         cimg::X11_attr().nb_bits = DefaultDepth(dpy,DefaultScreen(dpy));
7232         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)
7233           throw CImgDisplayException(_cimgdisplay_instance
7234                                      "assign() : Invalid %u bits screen mode detected "
7235                                      "(only 8, 16, 24 and 32 bits modes are managed).",
7236                                      cimgdisplay_instance,
7237                                      cimg::X11_attr().nb_bits);
7238         XVisualInfo vtemplate;
7239         vtemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,DefaultScreen(dpy)));
7240         int nb_visuals;
7241         XVisualInfo *vinfo = XGetVisualInfo(dpy,VisualIDMask,&vtemplate,&nb_visuals);
7242         if (vinfo && vinfo->red_mask<vinfo->blue_mask) cimg::X11_attr().is_blue_first = true;
7243         cimg::X11_attr().byte_order = ImageByteOrder(dpy);
7244         XFree(vinfo);
7245 
7246         XLockDisplay(dpy);
7247         cimg::X11_attr().event_thread = new pthread_t;
7248         pthread_create(cimg::X11_attr().event_thread,0,_events_thread,0);
7249       } else XLockDisplay(dpy);
7250 
7251       // Set display variables.
7252       _width = cimg::min(dimw,(unsigned int)screen_width());
7253       _height = cimg::min(dimh,(unsigned int)screen_height());
7254       _normalization = normalization_type<4?normalization_type:3;
7255       _is_fullscreen = fullscreen_flag;
7256       _window_x = _window_y = 0;
7257       _is_closed = closed_flag;
7258       _title = tmp_title;
7259       flush();
7260 
7261       // Create X11 window (and LUT, if 8bits display)
7262       if (_is_fullscreen) {
7263         if (!_is_closed) _init_fullscreen();
7264         const unsigned int sx = screen_width(), sy = screen_height();
7265         XSetWindowAttributes winattr;
7266         winattr.override_redirect = True;
7267         _window = XCreateWindow(dpy,DefaultRootWindow(dpy),(sx-_width)/2,(sy-_height)/2,_width,_height,0,0,
7268                                 InputOutput,CopyFromParent,CWOverrideRedirect,&winattr);
7269       } else
7270         _window = XCreateSimpleWindow(dpy,DefaultRootWindow(dpy),0,0,_width,_height,0,0L,0L);
7271 
7272       XSelectInput(dpy,_window,
7273                    ExposureMask | StructureNotifyMask | ButtonPressMask | KeyPressMask | PointerMotionMask |
7274                    EnterWindowMask | LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask);
7275 
7276       XStoreName(dpy,_window,_title?_title:" ");
7277       if (cimg::X11_attr().nb_bits==8) {
7278         _colormap = XCreateColormap(dpy,_window,DefaultVisual(dpy,DefaultScreen(dpy)),AllocAll);
7279         _set_colormap(_colormap,3);
7280         XSetWindowColormap(dpy,_window,_colormap);
7281       }
7282 
7283       static const char *const _window_class = cimg_appname;
7284       XClassHint *const window_class = XAllocClassHint();
7285       window_class->res_name = (char*)_window_class;
7286       window_class->res_class = (char*)_window_class;
7287       XSetClassHint(dpy,_window,window_class);
7288       XFree(window_class);
7289 
7290       _window_width = _width;
7291       _window_height = _height;
7292 
7293       // Create XImage
7294 #ifdef cimg_use_xshm
7295       _shminfo = 0;
7296       if (XShmQueryExtension(dpy)) {
7297         _shminfo = new XShmSegmentInfo;
7298         _image = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,ZPixmap,0,_shminfo,_width,_height);
7299         if (!_image) { delete _shminfo; _shminfo = 0; }
7300         else {
7301           _shminfo->shmid = shmget(IPC_PRIVATE,_image->bytes_per_line*_image->height,IPC_CREAT|0777);
7302           if (_shminfo->shmid==-1) { XDestroyImage(_image); delete _shminfo; _shminfo = 0; }
7303           else {
7304             _shminfo->shmaddr = _image->data = (char*)(_data = shmat(_shminfo->shmid,0,0));
7305             if (_shminfo->shmaddr==(char*)-1) { shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image); delete _shminfo; _shminfo = 0; }
7306             else {
7307               _shminfo->readOnly = False;
7308               cimg::X11_attr().is_shm_enabled = true;
7309               XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm);
7310               XShmAttach(dpy,_shminfo);
7311               XSync(dpy,False);
7312               XSetErrorHandler(oldXErrorHandler);
7313               if (!cimg::X11_attr().is_shm_enabled) {
7314                 shmdt(_shminfo->shmaddr); shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image); delete _shminfo; _shminfo = 0;
7315               }
7316             }
7317           }
7318         }
7319       }
7320       if (!_shminfo)
7321 #endif
7322         {
7323           const unsigned int buf_size = _width*_height*(cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4));
7324           _data = std::malloc(buf_size);
7325           _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,ZPixmap,0,(char*)_data,_width,_height,8,0);
7326         }
7327 
7328       _wm_window_atom = XInternAtom(dpy,"WM_DELETE_WINDOW",False);
7329       _wm_protocol_atom = XInternAtom(dpy,"WM_PROTOCOLS",False);
7330       XSetWMProtocols(dpy,_window,&_wm_window_atom,1);
7331 
7332       if (_is_fullscreen) XGrabKeyboard(dpy,_window,True,GrabModeAsync,GrabModeAsync,CurrentTime);
7333       cimg::X11_attr().wins[cimg::X11_attr().nb_wins++]=this;
7334       if (!_is_closed) _map_window(); else { _window_x = _window_y = cimg::type<int>::min(); }
7335       XUnlockDisplay(dpy);
7336     }
7337 
7338     CImgDisplay& assign() {
7339       if (is_empty()) return flush();
7340       Display *const dpy = cimg::X11_attr().display;
7341       XLockDisplay(dpy);
7342 
7343       // Remove display window from event thread list.
7344       unsigned int i;
7345       for (i = 0; i<cimg::X11_attr().nb_wins && cimg::X11_attr().wins[i]!=this; ++i) {}
7346       for (; i<cimg::X11_attr().nb_wins-1; ++i) cimg::X11_attr().wins[i] = cimg::X11_attr().wins[i+1];
7347       --cimg::X11_attr().nb_wins;
7348 
7349       // Destroy window, image, colormap and title.
7350       if (_is_fullscreen && !_is_closed) _desinit_fullscreen();
7351       XDestroyWindow(dpy,_window);
7352       _window = 0;
7353 #ifdef cimg_use_xshm
7354       if (_shminfo) {
7355         XShmDetach(dpy,_shminfo);
7356         XDestroyImage(_image);
7357         shmdt(_shminfo->shmaddr);
7358         shmctl(_shminfo->shmid,IPC_RMID,0);
7359         delete _shminfo;
7360         _shminfo = 0;
7361       } else
7362 #endif
7363         XDestroyImage(_image);
7364       _data = 0; _image = 0;
7365       if (cimg::X11_attr().nb_bits==8) XFreeColormap(dpy,_colormap);
7366       _colormap = 0;
7367       XSync(dpy,False);
7368 
7369       // Reset display variables
7370       delete[] _title;
7371       _width = _height = _normalization = _window_width = _window_height = 0;
7372       _window_x = _window_y = 0;
7373       _is_fullscreen = false;
7374       _is_closed = true;
7375       _min = _max = 0;
7376       _title = 0;
7377       flush();
7378 
7379       // End event thread and close display if necessary
7380       XUnlockDisplay(dpy);
7381       if (!cimg::X11_attr().nb_wins) {
7382         // Kill event thread
7383         //pthread_cancel(*cimg::X11_attr().event_thread);
7384         //XUnlockDisplay(cimg::X11_attr().display);
7385         //pthread_join(*cimg::X11_attr().event_thread,0);
7386         //delete cimg::X11_attr().event_thread;
7387         //cimg::X11_attr().event_thread = 0;
7388         // XUnlockDisplay(cimg::X11_attr().display); // <- This call make the library hang sometimes (fix required).
7389         // XCloseDisplay(cimg::X11_attr().display); // <- This call make the library hang sometimes (fix required).
7390         //cimg::X11_attr().display = 0;
7391       }
7392       return *this;
7393     }
7394 
7395     CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0,
7396                         const unsigned int normalization_type=3,
7397                         const bool fullscreen_flag=false, const bool closed_flag=false) {
7398       if (!dimw || !dimh) return assign();
7399       _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag);
7400       _min = _max = 0;
7401       std::memset(_data,0,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char):
7402                           (cimg::X11_attr().nb_bits==16?sizeof(unsigned short):sizeof(unsigned int)))*_width*_height);
7403       return paint();
7404     }
7405 
7406     template<typename T>
7407     CImgDisplay& assign(const CImg<T>& img, const char *const title=0,
7408                         const unsigned int normalization_type=3,
7409                         const bool fullscreen_flag=false, const bool closed_flag=false) {
7410       if (!img) return assign();
7411       CImg<T> tmp;
7412       const CImg<T>& nimg = (img._depth==1)?img:(tmp=img.get_projections2d(img._width/2,img._height/2,img._depth/2));
7413       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
7414       if (_normalization==2) _min = (float)nimg.min_max(_max);
7415       return render(nimg).paint();
7416     }
7417 
7418     template<typename T>
7419     CImgDisplay& assign(const CImgList<T>& list, const char *const title=0,
7420                         const unsigned int normalization_type=3,
7421                         const bool fullscreen_flag=false, const bool closed_flag=false) {
7422       if (!list) return assign();
7423       CImg<T> tmp;
7424       const CImg<T> img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d(img._width/2,img._height/2,img._depth/2));
7425       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
7426       if (_normalization==2) _min = (float)nimg.min_max(_max);
7427       return render(nimg).paint();
7428     }
7429 
7430     CImgDisplay& assign(const CImgDisplay& disp) {
7431       if (!disp) return assign();
7432       _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed);
7433       std::memcpy(_data,disp._data,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char):
7434                                     cimg::X11_attr().nb_bits==16?sizeof(unsigned short):
7435                                     sizeof(unsigned int))*_width*_height);
7436       return paint();
7437     }
7438 
7439     CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) {
7440       if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign();
7441       if (is_empty()) return assign(nwidth,nheight);
7442       Display *const dpy = cimg::X11_attr().display;
7443       const unsigned int
7444         tmpdimx = (nwidth>0)?nwidth:(-nwidth*_width/100),
7445         tmpdimy = (nheight>0)?nheight:(-nheight*_height/100),
7446         dimx = tmpdimx?tmpdimx:1,
7447         dimy = tmpdimy?tmpdimy:1;
7448       XLockDisplay(dpy);
7449       if (_window_width!=dimx || _window_height!=dimy) XResizeWindow(dpy,_window,dimx,dimy);
7450       if (_width!=dimx || _height!=dimy) switch (cimg::X11_attr().nb_bits) {
7451         case 8 :  { unsigned char pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break;
7452         case 16 : { unsigned short pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break;
7453         default : { unsigned int pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); }
7454         }
7455       _window_width = _width = dimx; _window_height = _height = dimy;
7456       _is_resized = false;
7457       XUnlockDisplay(dpy);
7458       if (_is_fullscreen) move((screen_width()-_width)/2,(screen_height()-_height)/2);
7459       if (force_redraw) return paint();
7460       return *this;
7461     }
7462 
7463     CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
7464       if (is_empty()) return *this;
7465       if (force_redraw) {
7466         const unsigned int buf_size = _width*_height*(cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4));
7467         void *image_data = std::malloc(buf_size);
7468         std::memcpy(image_data,_data,buf_size);
7469         assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
7470         std::memcpy(_data,image_data,buf_size);
7471         std::free(image_data);
7472         return paint();
7473       }
7474       return assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
7475     }
7476 
7477     CImgDisplay& show() {
7478       if (is_empty() || !_is_closed) return *this;
7479       Display *const dpy = cimg::X11_attr().display;
7480       XLockDisplay(dpy);
7481       if (_is_fullscreen) _init_fullscreen();
7482       _map_window();
7483       _is_closed = false;
7484       XUnlockDisplay(dpy);
7485       return paint();
7486     }
7487 
7488     CImgDisplay& close() {
7489       if (is_empty() || _is_closed) return *this;
7490       Display *const dpy = cimg::X11_attr().display;
7491       XLockDisplay(dpy);
7492       if (_is_fullscreen) _desinit_fullscreen();
7493       XUnmapWindow(dpy,_window);
7494       _window_x = _window_y = -1;
7495       _is_closed = true;
7496       XUnlockDisplay(dpy);
7497       return *this;
7498     }
7499 
7500     CImgDisplay& move(const int posx, const int posy) {
7501       if (is_empty()) return *this;
7502       Display *const dpy = cimg::X11_attr().display;
7503       show();
7504       XLockDisplay(dpy);
7505       XMoveWindow(dpy,_window,posx,posy);
7506       _window_x = posx; _window_y = posy;
7507       _is_moved = false;
7508       XUnlockDisplay(dpy);
7509       return paint();
7510     }
7511 
7512     CImgDisplay& show_mouse() {
7513       if (is_empty()) return *this;
7514       Display *const dpy = cimg::X11_attr().display;
7515       XLockDisplay(dpy);
7516       XUndefineCursor(dpy,_window);
7517       XUnlockDisplay(dpy);
7518       return *this;
7519     }
7520 
7521     CImgDisplay& hide_mouse() {
7522       if (is_empty()) return *this;
7523       Display *const dpy = cimg::X11_attr().display;
7524       XLockDisplay(dpy);
7525       const char pix_data[8] = { 0 };
7526       XColor col;
7527       col.red = col.green = col.blue = 0;
7528       Pixmap pix = XCreateBitmapFromData(dpy,_window,pix_data,8,8);
7529       Cursor cur = XCreatePixmapCursor(dpy,pix,pix,&col,&col,0,0);
7530       XFreePixmap(dpy,pix);
7531       XDefineCursor(dpy,_window,cur);
7532       XUnlockDisplay(dpy);
7533       return *this;
7534     }
7535 
7536     CImgDisplay& set_mouse(const int posx, const int posy) {
7537       if (is_empty() || _is_closed) return *this;
7538       Display *const dpy = cimg::X11_attr().display;
7539       XLockDisplay(dpy);
7540       XWarpPointer(dpy,0L,_window,0,0,0,0,posx,posy);
7541       _mouse_x = posx; _mouse_y = posy;
7542       _is_moved = false;
7543       XSync(dpy,False);
7544       XUnlockDisplay(dpy);
7545       return *this;
7546     }
7547 
7548     CImgDisplay& set_title(const char *const format, ...) {
7549       if (is_empty()) return *this;
7550       char tmp[1024] = { 0 };
7551       va_list ap;
7552       va_start(ap, format);
7553       cimg_vsnprintf(tmp,sizeof(tmp),format,ap);
7554       va_end(ap);
7555       if (!std::strcmp(_title,tmp)) return *this;
7556       delete[] _title;
7557       const unsigned int s = std::strlen(tmp) + 1;
7558       _title = new char[s];
7559       std::memcpy(_title,tmp,s*sizeof(char));
7560       Display *const dpy = cimg::X11_attr().display;
7561       XLockDisplay(dpy);
7562       XStoreName(dpy,_window,tmp);
7563       XUnlockDisplay(dpy);
7564       return *this;
7565     }
7566 
7567     template<typename T>
7568     CImgDisplay& display(const CImg<T>& img) {
7569       if (!img)
7570         throw CImgArgumentException(_cimgdisplay_instance
7571                                     "display() : Empty specified image.",
7572                                     cimgdisplay_instance);
7573       if (is_empty()) return assign(img);
7574       return render(img).paint(false);
7575     }
7576 
7577     CImgDisplay& paint(const bool wait_expose=true) {
7578       if (is_empty()) return *this;
7579       Display *const dpy = cimg::X11_attr().display;
7580       XLockDisplay(dpy);
7581       _paint(wait_expose);
7582       XUnlockDisplay(dpy);
7583       return *this;
7584     }
7585 
7586     template<typename T>
7587     CImgDisplay& render(const CImg<T>& img, const bool flag8=false) {
7588       if (!img)
7589         throw CImgArgumentException(_cimgdisplay_instance
7590                                     "render() : Empty specified image.",
7591                                     cimgdisplay_instance);
7592       if (is_empty()) return *this;
7593       if (img._depth!=1) return render(img.get_projections2d(img._width/2,img._height/2,img._depth/2));
7594       if (cimg::X11_attr().nb_bits==8 && (img._width!=_width || img._height!=_height)) return render(img.get_resize(_width,_height,1,-100,1));
7595       if (cimg::X11_attr().nb_bits==8 && !flag8 && img._spectrum==3) {
7596         static const CImg<typename CImg<T>::ucharT> default_palette = CImg<typename CImg<T>::ucharT>::default_LUT256();
7597         return render(img.get_index(default_palette,true,false));
7598       }
7599 
7600       Display *const dpy = cimg::X11_attr().display;
7601       const T
7602         *data1 = img._data,
7603         *data2 = (img._spectrum>1)?img.data(0,0,0,1):data1,
7604         *data3 = (img._spectrum>2)?img.data(0,0,0,2):data1;
7605 
7606       if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3);
7607       XLockDisplay(dpy);
7608 
7609       if (!_normalization || (_normalization==3 && cimg::type<T>::string()==cimg::type<unsigned char>::string())) {
7610         _min = _max = 0;
7611         switch (cimg::X11_attr().nb_bits) {
7612         case 8 : { // 256 color palette, no normalization
7613           _set_colormap(_colormap,img._spectrum);
7614           unsigned char *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data:new unsigned char[img._width*img._height];
7615           unsigned char *ptrd = (unsigned char*)ndata;
7616           switch (img._spectrum) {
7617           case 1 : for (unsigned int xy = img._width*img._height; xy>0; --xy) (*ptrd++) = (unsigned char)*(data1++);
7618             break;
7619           case 2 : for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7620               const unsigned char R = (unsigned char)*(data1++), G = (unsigned char)*(data2++);
7621               (*ptrd++) = (R&0xf0) | (G>>4);
7622             } break;
7623           default : for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7624               const unsigned char R = (unsigned char)*(data1++), G = (unsigned char)*(data2++), B = (unsigned char)*(data3++);
7625               (*ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6);
7626             }
7627           }
7628           if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height); delete[] ndata; }
7629         } break;
7630         case 16 : { // 16 bits colors, no normalization
7631           unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data:new unsigned short[img._width*img._height];
7632           unsigned char *ptrd = (unsigned char*)ndata;
7633           const unsigned int M = 248;
7634           switch (img._spectrum) {
7635           case 1 :
7636             if (cimg::X11_attr().byte_order) for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7637               const unsigned char val = (unsigned char)*(data1++), G = val>>2;
7638               *(ptrd++) = (val&M) | (G>>3);
7639               *(ptrd++) = (G<<5) | (G>>1);
7640             } else for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7641               const unsigned char val = (unsigned char)*(data1++), G = val>>2;
7642               *(ptrd++) = (G<<5) | (G>>1);
7643               *(ptrd++) = (val&M) | (G>>3);
7644             }
7645             break;
7646           case 2 :
7647             if (cimg::X11_attr().byte_order) for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7648               const unsigned char G = (unsigned char)*(data2++)>>2;
7649               *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3);
7650               *(ptrd++) = (G<<5);
7651             } else for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7652               const unsigned char G = (unsigned char)*(data2++)>>2;
7653               *(ptrd++) = (G<<5);
7654               *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3);
7655             }
7656             break;
7657           default :
7658             if (cimg::X11_attr().byte_order) for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7659               const unsigned char G = (unsigned char)*(data2++)>>2;
7660               *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3);
7661               *(ptrd++) = (G<<5) | ((unsigned char)*(data3++)>>3);
7662             } else for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7663               const unsigned char G = (unsigned char)*(data2++)>>2;
7664               *(ptrd++) = (G<<5) | ((unsigned char)*(data3++)>>3);
7665               *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3);
7666             }
7667           }
7668           if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height); delete[] ndata; }
7669         } break;
7670         default : { // 24 bits colors, no normalization
7671           unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data:new unsigned int[img._width*img._height];
7672           if (sizeof(int)==4) { // 32 bits int uses optimized version
7673             unsigned int *ptrd = ndata;
7674             switch (img._spectrum) {
7675             case 1 :
7676               if (cimg::X11_attr().byte_order==cimg::endianness())
7677                 for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7678                   const unsigned char val = (unsigned char)*(data1++);
7679                   *(ptrd++) = (val<<16) | (val<<8) | val;
7680                 }
7681               else
7682                for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7683                  const unsigned char val = (unsigned char)*(data1++);
7684                   *(ptrd++) = (val<<16) | (val<<8) | val;
7685                 }
7686               break;
7687             case 2 :
7688               if (cimg::X11_attr().byte_order==cimg::endianness())
7689                for (unsigned int xy = img._width*img._height; xy>0; --xy)
7690                   *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8);
7691               else
7692                for (unsigned int xy = img._width*img._height; xy>0; --xy)
7693                   *(ptrd++) = ((unsigned char)*(data2++)<<16) | ((unsigned char)*(data1++)<<8);
7694               break;
7695             default :
7696               if (cimg::X11_attr().byte_order==cimg::endianness())
7697                for (unsigned int xy = img._width*img._height; xy>0; --xy)
7698                   *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) | (unsigned char)*(data3++);
7699               else
7700                for (unsigned int xy = img._width*img._height; xy>0; --xy)
7701                   *(ptrd++) = ((unsigned char)*(data3++)<<24) | ((unsigned char)*(data2++)<<16) | ((unsigned char)*(data1++)<<8);
7702             }
7703           } else {
7704             unsigned char *ptrd = (unsigned char*)ndata;
7705             switch (img._spectrum) {
7706             case 1 :
7707               if (cimg::X11_attr().byte_order) for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7708                 *(ptrd++) = 0;
7709                 *(ptrd++) = (unsigned char)*(data1++);
7710                 *(ptrd++) = 0;
7711                 *(ptrd++) = 0;
7712               } else for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7713                 *(ptrd++) = 0;
7714                 *(ptrd++) = 0;
7715                 *(ptrd++) = (unsigned char)*(data1++);
7716                 *(ptrd++) = 0;
7717               }
7718               break;
7719             case 2 :
7720               if (cimg::X11_attr().byte_order) cimg::swap(data1,data2);
7721               for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7722                 *(ptrd++) = 0;
7723                 *(ptrd++) = (unsigned char)*(data2++);
7724                 *(ptrd++) = (unsigned char)*(data1++);
7725                 *(ptrd++) = 0;
7726               }
7727               break;
7728             default :
7729               if (cimg::X11_attr().byte_order) for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7730                 *(ptrd++) = 0;
7731                 *(ptrd++) = (unsigned char)*(data1++);
7732                 *(ptrd++) = (unsigned char)*(data2++);
7733                 *(ptrd++) = (unsigned char)*(data3++);
7734               } else for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7735                 *(ptrd++) = (unsigned char)*(data3++);
7736                 *(ptrd++) = (unsigned char)*(data2++);
7737                 *(ptrd++) = (unsigned char)*(data1++);
7738                 *(ptrd++) = 0;
7739               }
7740             }
7741           }
7742           if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height); delete[] ndata; }
7743         }
7744         }
7745       } else {
7746         if (_normalization==3) {
7747           if (cimg::type<T>::is_float()) _min = (float)img.min_max(_max);
7748           else { _min = (float)cimg::type<T>::min(); _max = (float)cimg::type<T>::max(); }
7749         } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max);
7750         const float delta = _max - _min, mm = 255/(delta?delta:1.0f);
7751         switch (cimg::X11_attr().nb_bits) {
7752         case 8 : { // 256 color palette, with normalization
7753           _set_colormap(_colormap,img._spectrum);
7754           unsigned char *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data:new unsigned char[img._width*img._height];
7755           unsigned char *ptrd = (unsigned char*)ndata;
7756           switch (img._spectrum) {
7757           case 1 : for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7758               const unsigned char R = (unsigned char)((*(data1++)-_min)*mm);
7759               *(ptrd++) = R;
7760             } break;
7761           case 2 : for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7762               const unsigned char
7763                 R = (unsigned char)((*(data1++)-_min)*mm),
7764                 G = (unsigned char)((*(data2++)-_min)*mm);
7765             (*ptrd++) = (R&0xf0) | (G>>4);
7766           } break;
7767           default :
7768             for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7769               const unsigned char
7770                 R = (unsigned char)((*(data1++)-_min)*mm),
7771                 G = (unsigned char)((*(data2++)-_min)*mm),
7772                 B = (unsigned char)((*(data3++)-_min)*mm);
7773               *(ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6);
7774             }
7775           }
7776           if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height); delete[] ndata; }
7777         } break;
7778         case 16 : { // 16 bits colors, with normalization
7779           unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data:new unsigned short[img._width*img._height];
7780           unsigned char *ptrd = (unsigned char*)ndata;
7781           const unsigned int M = 248;
7782           switch (img._spectrum) {
7783           case 1 :
7784             if (cimg::X11_attr().byte_order) for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7785               const unsigned char val = (unsigned char)((*(data1++)-_min)*mm), G = val>>2;
7786               *(ptrd++) = (val&M) | (G>>3);
7787               *(ptrd++) = (G<<5) | (val>>3);
7788             } else for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7789               const unsigned char val = (unsigned char)((*(data1++)-_min)*mm), G = val>>2;
7790               *(ptrd++) = (G<<5) | (val>>3);
7791               *(ptrd++) = (val&M) | (G>>3);
7792             }
7793             break;
7794           case 2 :
7795             if (cimg::X11_attr().byte_order) for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7796               const unsigned char G = (unsigned char)((*(data2++)-_min)*mm)>>2;
7797               *(ptrd++) = ((unsigned char)((*(data1++)-_min)*mm)&M) | (G>>3);
7798               *(ptrd++) = (G<<5);
7799             } else for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7800               const unsigned char G = (unsigned char)((*(data2++)-_min)*mm)>>2;
7801               *(ptrd++) = (G<<5);
7802               *(ptrd++) = ((unsigned char)((*(data1++)-_min)*mm)&M) | (G>>3);
7803             }
7804             break;
7805           default :
7806             if (cimg::X11_attr().byte_order) for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7807               const unsigned char G = (unsigned char)((*(data2++)-_min)*mm)>>2;
7808               *(ptrd++) = ((unsigned char)((*(data1++)-_min)*mm)&M) | (G>>3);
7809               *(ptrd++) = (G<<5) | ((unsigned char)((*(data3++)-_min)*mm)>>3);
7810             } else for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7811               const unsigned char G = (unsigned char)((*(data2++)-_min)*mm)>>2;
7812               *(ptrd++) = (G<<5) | ((unsigned char)((*(data3++)-_min)*mm)>>3);
7813               *(ptrd++) = ((unsigned char)((*(data1++)-_min)*mm)&M) | (G>>3);
7814             }
7815           }
7816           if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height); delete[] ndata; }
7817         } break;
7818         default : { // 24 bits colors, with normalization
7819           unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data:new unsigned int[img._width*img._height];
7820           if (sizeof(int)==4) { // 32 bits int uses optimized version
7821             unsigned int *ptrd = ndata;
7822             switch (img._spectrum) {
7823             case 1 :
7824               if (cimg::X11_attr().byte_order==cimg::endianness())
7825                 for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7826                   const unsigned char val = (unsigned char)((*(data1++)-_min)*mm);
7827                   *(ptrd++) = (val<<16) | (val<<8) | val;
7828                 }
7829               else
7830                 for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7831                   const unsigned char val = (unsigned char)((*(data1++)-_min)*mm);
7832                   *(ptrd++) = (val<<24) | (val<<16) | (val<<8);
7833                 }
7834               break;
7835             case 2 :
7836               if (cimg::X11_attr().byte_order==cimg::endianness())
7837                 for (unsigned int xy = img._width*img._height; xy>0; --xy)
7838                   *(ptrd++) =
7839                     ((unsigned char)((*(data1++)-_min)*mm)<<16) |
7840                     ((unsigned char)((*(data2++)-_min)*mm)<<8);
7841               else
7842                 for (unsigned int xy = img._width*img._height; xy>0; --xy)
7843                   *(ptrd++) =
7844                     ((unsigned char)((*(data2++)-_min)*mm)<<16) |
7845                     ((unsigned char)((*(data1++)-_min)*mm)<<8);
7846               break;
7847             default :
7848               if (cimg::X11_attr().byte_order==cimg::endianness())
7849                 for (unsigned int xy = img._width*img._height; xy>0; --xy)
7850                   *(ptrd++) =
7851                     ((unsigned char)((*(data1++)-_min)*mm)<<16) |
7852                     ((unsigned char)((*(data2++)-_min)*mm)<<8) |
7853                     (unsigned char)((*(data3++)-_min)*mm);
7854               else
7855                 for (unsigned int xy = img._width*img._height; xy>0; --xy)
7856                   *(ptrd++) =
7857                     ((unsigned char)((*(data3++)-_min)*mm)<<24) |
7858                     ((unsigned char)((*(data2++)-_min)*mm)<<16) |
7859                     ((unsigned char)((*(data1++)-_min)*mm)<<8);
7860             }
7861           } else {
7862             unsigned char *ptrd = (unsigned char*)ndata;
7863             switch (img._spectrum) {
7864             case 1 :
7865               if (cimg::X11_attr().byte_order) for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7866                 const unsigned char val = (unsigned char)((*(data1++)-_min)*mm);
7867                 (*ptrd++) = 0;
7868                 (*ptrd++) = val;
7869                 (*ptrd++) = val;
7870                 (*ptrd++) = val;
7871               } else for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7872                 const unsigned char val = (unsigned char)((*(data1++)-_min)*mm);
7873                 (*ptrd++) = val;
7874                 (*ptrd++) = val;
7875                 (*ptrd++) = val;
7876                 (*ptrd++) = 0;
7877               }
7878               break;
7879             case 2 :
7880               if (cimg::X11_attr().byte_order) cimg::swap(data1,data2);
7881               for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7882                 (*ptrd++) = 0;
7883                 (*ptrd++) = (unsigned char)((*(data2++)-_min)*mm);
7884                 (*ptrd++) = (unsigned char)((*(data1++)-_min)*mm);
7885                 (*ptrd++) = 0;
7886               }
7887               break;
7888             default :
7889               if (cimg::X11_attr().byte_order) for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7890                 (*ptrd++) = 0;
7891                 (*ptrd++) = (unsigned char)((*(data1++)-_min)*mm);
7892                 (*ptrd++) = (unsigned char)((*(data2++)-_min)*mm);
7893                 (*ptrd++) = (unsigned char)((*(data3++)-_min)*mm);
7894               } else for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7895                 (*ptrd++) = (unsigned char)((*(data3++)-_min)*mm);
7896                 (*ptrd++) = (unsigned char)((*(data2++)-_min)*mm);
7897                 (*ptrd++) = (unsigned char)((*(data1++)-_min)*mm);
7898                 (*ptrd++) = 0;
7899               }
7900             }
7901           }
7902           if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height); delete[] ndata; }
7903         }
7904         }
7905       }
7906       XUnlockDisplay(dpy);
7907       return *this;
7908     }
7909 
7910     template<typename T>
7911     const CImgDisplay& snapshot(CImg<T>& img) const {
7912       if (is_empty()) { img.assign(); return *this; }
7913       const unsigned char *ptrs = (unsigned char*)_data;
7914       img.assign(_width,_height,1,3);
7915       T
7916         *data1 = img.data(0,0,0,0),
7917         *data2 = img.data(0,0,0,1),
7918         *data3 = img.data(0,0,0,2);
7919       if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3);
7920       switch (cimg::X11_attr().nb_bits) {
7921       case 8 : {
7922         for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7923           const unsigned char val = *(ptrs++);
7924           *(data1++) = val&0xe0;
7925           *(data2++) = (val&0x1c)<<3;
7926           *(data3++) = val<<6;
7927         }
7928       } break;
7929       case 16 : {
7930         if (cimg::X11_attr().byte_order) for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7931           const unsigned char val0 = *(ptrs++), val1 = *(ptrs++);
7932           *(data1++) = val0&0xf8;
7933           *(data2++) = (val0<<5) | ((val1&0xe0)>>5);
7934           *(data3++) = val1<<3;
7935         } else for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7936           const unsigned short val0 = *(ptrs++), val1 = *(ptrs++);
7937           *(data1++) = val1&0xf8;
7938           *(data2++) = (val1<<5) | ((val0&0xe0)>>5);
7939           *(data3++) = val0<<3;
7940         }
7941       } break;
7942       default : {
7943         if (cimg::X11_attr().byte_order) for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7944           ++ptrs;
7945           *(data1++) = *(ptrs++);
7946           *(data2++) = *(ptrs++);
7947           *(data3++) = *(ptrs++);
7948         } else for (unsigned int xy = img._width*img._height; xy>0; --xy) {
7949           *(data3++) = *(ptrs++);
7950           *(data2++) = *(ptrs++);
7951           *(data1++) = *(ptrs++);
7952           ++ptrs;
7953         }
7954       }
7955       }
7956       return *this;
7957     }
7958 
7959     // Windows-based implementation.
7960     //-------------------------------
7961 #elif cimg_display==2
7962 
7963     CLIENTCREATESTRUCT _ccs;
7964     BITMAPINFO _bmi;
7965     unsigned int *_data;
7966     DEVMODE _curr_mode;
7967     HWND _window;
7968     HWND _background_window;
7969     HDC _hdc;
7970     HANDLE _thread;
7971     HANDLE _is_created;
7972     HANDLE _mutex;
7973     bool _is_mouse_tracked;
7974     bool _is_cursor_visible;
7975 
7976     static int screen_width() {
7977       DEVMODE mode;
7978       mode.dmSize = sizeof(DEVMODE);
7979       mode.dmDriverExtra = 0;
7980       EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode);
7981       return mode.dmPelsWidth;
7982     }
7983 
7984     static int screen_height() {
7985       DEVMODE mode;
7986       mode.dmSize = sizeof(DEVMODE);
7987       mode.dmDriverExtra = 0;
7988       EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode);
7989       return mode.dmPelsHeight;
7990     }
7991 
7992     static void wait_all() {
7993       WaitForSingleObject(cimg::Win32_attr().wait_event,INFINITE);
7994     }
7995 
7996     static LRESULT APIENTRY _handle_events(HWND window,UINT msg,WPARAM wParam,LPARAM lParam) {
7997 #ifdef _WIN64
7998       CImgDisplay *const disp = (CImgDisplay*)GetWindowLongPtr(window,GWLP_USERDATA);
7999 #else
8000       CImgDisplay *const disp = (CImgDisplay*)GetWindowLong(window,GWL_USERDATA);
8001 #endif
8002       MSG st_msg;
8003       switch (msg) {
8004       case WM_CLOSE :
8005         disp->_mouse_x = disp->_mouse_y = -1;
8006         disp->_window_x = disp->_window_y = 0;
8007         disp->set_button().set_key(0).set_key(0,false)._is_closed = true;
8008         ReleaseMutex(disp->_mutex);
8009         ShowWindow(disp->_window,SW_HIDE);
8010         disp->_is_event = true;
8011         SetEvent(cimg::Win32_attr().wait_event);
8012         return 0;
8013       case WM_SIZE : {
8014         while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {}
8015         WaitForSingleObject(disp->_mutex,INFINITE);
8016         const unsigned int nw = LOWORD(lParam),nh = HIWORD(lParam);
8017         if (nw && nh && (nw!=disp->_width || nh!=disp->_height)) {
8018           disp->_window_width = nw;
8019           disp->_window_height = nh;
8020           disp->_mouse_x = disp->_mouse_y = -1;
8021           disp->_is_resized = disp->_is_event = true;
8022           SetEvent(cimg::Win32_attr().wait_event);
8023         }
8024         ReleaseMutex(disp->_mutex);
8025       } break;
8026       case WM_MOVE : {
8027         while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {}
8028         WaitForSingleObject(disp->_mutex,INFINITE);
8029         const int nx = (int)(short)(LOWORD(lParam)), ny = (int)(short)(HIWORD(lParam));
8030         if (nx!=disp->_window_x || ny!=disp->_window_y) {
8031           disp->_window_x = nx;
8032           disp->_window_y = ny;
8033           disp->_is_moved = disp->_is_event = true;
8034           SetEvent(cimg::Win32_attr().wait_event);
8035         }
8036         ReleaseMutex(disp->_mutex);
8037       } break;
8038       case WM_PAINT :
8039         disp->paint();
8040         break;
8041       case WM_KEYDOWN :
8042         disp->set_key((unsigned int)wParam);
8043         SetEvent(cimg::Win32_attr().wait_event);
8044         break;
8045       case WM_KEYUP :
8046         disp->set_key((unsigned int)wParam,false);
8047         SetEvent(cimg::Win32_attr().wait_event);
8048         break;
8049       case WM_MOUSEMOVE : {
8050         while (PeekMessage(&st_msg,window,WM_MOUSEMOVE,WM_MOUSEMOVE,PM_REMOVE)) {}
8051         disp->_mouse_x = LOWORD(lParam);
8052         disp->_mouse_y = HIWORD(lParam);
8053 #if (_WIN32_WINNT>=0x0400) && !defined(NOTRACKMOUSEEVENT)
8054         if (!disp->_is_mouse_tracked) {
8055           TRACKMOUSEEVENT tme;
8056           tme.cbSize = sizeof(TRACKMOUSEEVENT);
8057           tme.dwFlags = TME_LEAVE;
8058           tme.hwndTrack = disp->_window;
8059           if (TrackMouseEvent(&tme)) disp->_is_mouse_tracked = true;
8060         }
8061 #endif
8062         if (disp->_mouse_x<0 || disp->_mouse_y<0 || disp->_mouse_x>=disp->width() || disp->_mouse_y>=disp->height())
8063           disp->_mouse_x = disp->_mouse_y = -1;
8064         disp->_is_event = true;
8065         SetEvent(cimg::Win32_attr().wait_event);
8066       } break;
8067       case WM_MOUSELEAVE : {
8068         disp->_mouse_x = disp->_mouse_y = -1;
8069         disp->_is_mouse_tracked = false;
8070       } break;
8071       case WM_LBUTTONDOWN :
8072         disp->set_button(1);
8073         SetEvent(cimg::Win32_attr().wait_event);
8074         break;
8075       case WM_RBUTTONDOWN :
8076         disp->set_button(2);
8077         SetEvent(cimg::Win32_attr().wait_event);
8078         break;
8079       case WM_MBUTTONDOWN :
8080         disp->set_button(3);
8081         SetEvent(cimg::Win32_attr().wait_event);
8082         break;
8083       case WM_LBUTTONUP :
8084         disp->set_button(1,false);
8085         SetEvent(cimg::Win32_attr().wait_event);
8086         break;
8087       case WM_RBUTTONUP :
8088         disp->set_button(2,false);
8089         SetEvent(cimg::Win32_attr().wait_event);
8090         break;
8091       case WM_MBUTTONUP :
8092         disp->set_button(3,false);
8093         SetEvent(cimg::Win32_attr().wait_event);
8094         break;
8095       case 0x020A : // WM_MOUSEWHEEL:
8096         disp->set_wheel((int)((short)HIWORD(wParam))/120);
8097         SetEvent(cimg::Win32_attr().wait_event);
8098       case WM_SETCURSOR :
8099         if (disp->_is_cursor_visible) ShowCursor(TRUE);
8100         else ShowCursor(FALSE);
8101         break;
8102       }
8103       return DefWindowProc(window,msg,wParam,lParam);
8104     }
8105 
8106     static DWORD WINAPI _events_thread(void* arg) {
8107       CImgDisplay *const disp = (CImgDisplay*)(((void**)arg)[0]);
8108       const char *const title = (const char*)(((void**)arg)[1]);
8109       MSG msg;
8110       delete[] (void**)arg;
8111       disp->_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
8112       disp->_bmi.bmiHeader.biWidth = disp->width();
8113       disp->_bmi.bmiHeader.biHeight = -disp->height();
8114       disp->_bmi.bmiHeader.biPlanes = 1;
8115       disp->_bmi.bmiHeader.biBitCount = 32;
8116       disp->_bmi.bmiHeader.biCompression = BI_RGB;
8117       disp->_bmi.bmiHeader.biSizeImage = 0;
8118       disp->_bmi.bmiHeader.biXPelsPerMeter = 1;
8119       disp->_bmi.bmiHeader.biYPelsPerMeter = 1;
8120       disp->_bmi.bmiHeader.biClrUsed = 0;
8121       disp->_bmi.bmiHeader.biClrImportant = 0;
8122       disp->_data = new unsigned int[disp->_width*disp->_height];
8123       if (!disp->_is_fullscreen) { // Normal window
8124         RECT rect;
8125         rect.left = rect.top = 0; rect.right = disp->_width-1; rect.bottom = disp->_height-1;
8126         AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
8127         const int
8128           border1 = (rect.right - rect.left + 1 - disp->_width)/2,
8129           border2 = rect.bottom - rect.top + 1 - disp->_height - border1;
8130         disp->_window = CreateWindowA("MDICLIENT",title?title:" ",
8131                                      WS_OVERLAPPEDWINDOW | (disp->_is_closed?0:WS_VISIBLE), CW_USEDEFAULT,CW_USEDEFAULT,
8132                                      disp->_width + 2*border1, disp->_height + border1 + border2,
8133                                      0,0,0,&(disp->_ccs));
8134         if (!disp->_is_closed) {
8135           GetWindowRect(disp->_window,&rect);
8136           disp->_window_x = rect.left + border1;
8137           disp->_window_y = rect.top + border2;
8138         } else disp->_window_x = disp->_window_y = 0;
8139       } else { // Fullscreen window
8140         const unsigned int sx = screen_width(), sy = screen_height();
8141         disp->_window = CreateWindowA("MDICLIENT",title?title:" ",
8142                                      WS_POPUP | (disp->_is_closed?0:WS_VISIBLE), (sx-disp->_width)/2, (sy-disp->_height)/2,
8143                                      disp->_width,disp->_height,0,0,0,&(disp->_ccs));
8144         disp->_window_x = disp->_window_y = 0;
8145       }
8146       SetForegroundWindow(disp->_window);
8147       disp->_hdc = GetDC(disp->_window);
8148       disp->_window_width = disp->_width;
8149       disp->_window_height = disp->_height;
8150       disp->flush();
8151 #ifdef _WIN64
8152       SetWindowLongPtr(disp->_window,GWLP_USERDATA,(LONG_PTR)disp);
8153       SetWindowLongPtr(disp->_window,GWLP_WNDPROC,(LONG_PTR)_handle_events);
8154 #else
8155       SetWindowLong(disp->_window,GWL_USERDATA,(LONG)disp);
8156       SetWindowLong(disp->_window,GWL_WNDPROC,(LONG)_handle_events);
8157 #endif
8158       SetEvent(disp->_is_created);
8159       while (GetMessage(&msg,0,0,0)) DispatchMessage(&msg);
8160       return 0;
8161     }
8162 
8163     CImgDisplay& _update_window_pos() {
8164       if (_is_closed) _window_x = _window_y = -1;
8165       else {
8166         RECT rect;
8167         rect.left = rect.top = 0; rect.right = _width-1; rect.bottom = _height-1;
8168         AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
8169         const int
8170           border1 = (rect.right - rect.left + 1 - _width)/2,
8171           border2 = rect.bottom - rect.top + 1 - _height - border1;
8172         GetWindowRect(_window,&rect);
8173         _window_x = rect.left + border1;
8174         _window_y = rect.top + border2;
8175       }
8176       return *this;
8177     }
8178 
8179     void _init_fullscreen() {
8180       _background_window = 0;
8181       if (!_is_fullscreen || _is_closed) _curr_mode.dmSize = 0;
8182       else {
8183         DEVMODE mode;
8184         unsigned int imode = 0, ibest = 0, bestbpp = 0, bw = ~0U, bh = ~0U;
8185         for (mode.dmSize = sizeof(DEVMODE), mode.dmDriverExtra = 0; EnumDisplaySettings(0,imode,&mode); ++imode) {
8186           const unsigned int nw = mode.dmPelsWidth, nh = mode.dmPelsHeight;
8187           if (nw>=_width && nh>=_height && mode.dmBitsPerPel>=bestbpp && nw<=bw && nh<=bh) {
8188             bestbpp = mode.dmBitsPerPel;
8189             ibest = imode;
8190             bw = nw; bh = nh;
8191           }
8192         }
8193         if (bestbpp) {
8194           _curr_mode.dmSize = sizeof(DEVMODE); _curr_mode.dmDriverExtra = 0;
8195           EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&_curr_mode);
8196           EnumDisplaySettings(0,ibest,&mode);
8197           ChangeDisplaySettings(&mode,0);
8198         } else _curr_mode.dmSize = 0;
8199 
8200         const unsigned int sx = screen_width(), sy = screen_height();
8201         if (sx!=_width || sy!=_height) {
8202           CLIENTCREATESTRUCT background_ccs;
8203           _background_window = CreateWindowA("MDICLIENT","",WS_POPUP | WS_VISIBLE, 0,0,sx,sy,0,0,0,&background_ccs);
8204           SetForegroundWindow(_background_window);
8205         }
8206       }
8207     }
8208 
8209     void _desinit_fullscreen() {
8210       if (!_is_fullscreen) return;
8211       if (_background_window) DestroyWindow(_background_window);
8212       _background_window = 0;
8213       if (_curr_mode.dmSize) ChangeDisplaySettings(&_curr_mode,0);
8214       _is_fullscreen = false;
8215     }
8216 
8217     CImgDisplay& _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0,
8218                          const unsigned int normalization_type=3,
8219                          const bool fullscreen_flag=false, const bool closed_flag=false) {
8220 
8221       // Allocate space for window title
8222       const char *const nptitle = ptitle?ptitle:"";
8223       const unsigned int s = std::strlen(nptitle) + 1;
8224       char *const tmp_title = s?new char[s]:0;
8225       if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char));
8226 
8227       // Destroy previous window if existing
8228       if (!is_empty()) assign();
8229 
8230       // Set display variables
8231       _width = cimg::min(dimw,(unsigned int)screen_width());
8232       _height = cimg::min(dimh,(unsigned int)screen_height());
8233       _normalization = normalization_type<4?normalization_type:3;
8234       _is_fullscreen = fullscreen_flag;
8235       _window_x = _window_y = 0;
8236       _is_closed = closed_flag;
8237       _is_cursor_visible = true;
8238       _is_mouse_tracked = false;
8239       _title = tmp_title;
8240       flush();
8241       if (_is_fullscreen) _init_fullscreen();
8242 
8243       // Create event thread
8244       void *const arg = (void*)(new void*[2]);
8245       ((void**)arg)[0] = (void*)this;
8246       ((void**)arg)[1] = (void*)_title;
8247       unsigned long ThreadID = 0;
8248       _mutex = CreateMutex(0,FALSE,0);
8249       _is_created = CreateEvent(0,FALSE,FALSE,0);
8250       _thread = CreateThread(0,0,_events_thread,arg,0,&ThreadID);
8251       WaitForSingleObject(_is_created,INFINITE);
8252       return *this;
8253     }
8254 
8255     CImgDisplay& assign() {
8256       if (is_empty()) return flush();
8257       DestroyWindow(_window);
8258       TerminateThread(_thread,0);
8259       delete[] _data;
8260       delete[] _title;
8261       _data = 0;
8262       _title = 0;
8263       if (_is_fullscreen) _desinit_fullscreen();
8264       _width = _height = _normalization = _window_width = _window_height = 0;
8265       _window_x = _window_y = 0;
8266       _is_fullscreen = false;
8267       _is_closed = true;
8268       _min = _max = 0;
8269       _title = 0;
8270       flush();
8271       return *this;
8272     }
8273 
8274     CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0,
8275                         const unsigned int normalization_type=3,
8276                         const bool fullscreen_flag=false, const bool closed_flag=false) {
8277       if (!dimw || !dimh) return assign();
8278       _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag);
8279       _min = _max = 0;
8280       std::memset(_data,0,sizeof(unsigned int)*_width*_height);
8281       return paint();
8282     }
8283 
8284     template<typename T>
8285     CImgDisplay& assign(const CImg<T>& img, const char *const title=0,
8286                         const unsigned int normalization_type=3,
8287                         const bool fullscreen_flag=false, const bool closed_flag=false) {
8288       if (!img) return assign();
8289       CImg<T> tmp;
8290       const CImg<T>& nimg = (img._depth==1)?img:(tmp=img.get_projections2d(img._width/2,img._height/2,img._depth/2));
8291       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
8292       if (_normalization==2) _min = (float)nimg.min_max(_max);
8293       return display(nimg);
8294     }
8295 
8296     template<typename T>
8297     CImgDisplay& assign(const CImgList<T>& list, const char *const title=0,
8298                         const unsigned int normalization_type=3,
8299                         const bool fullscreen_flag=false, const bool closed_flag=false) {
8300       if (!list) return assign();
8301       CImg<T> tmp;
8302       const CImg<T> img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d(img._width/2,img._height/2,img._depth/2));
8303       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
8304       if (_normalization==2) _min = (float)nimg.min_max(_max);
8305       return display(nimg);
8306     }
8307 
8308     CImgDisplay& assign(const CImgDisplay& disp) {
8309       if (!disp) return assign();
8310       _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed);
8311       std::memcpy(_data,disp._data,sizeof(unsigned int)*_width*_height);
8312       return paint();
8313     }
8314 
8315     CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) {
8316       if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign();
8317       if (is_empty()) return assign(nwidth,nheight);
8318       const unsigned int
8319         tmpdimx = (nwidth>0)?nwidth:(-nwidth*_width/100),
8320         tmpdimy = (nheight>0)?nheight:(-nheight*_height/100),
8321         dimx = tmpdimx?tmpdimx:1,
8322         dimy = tmpdimy?tmpdimy:1;
8323       if (_window_width!=dimx || _window_height!=dimy) {
8324         RECT rect; rect.left = rect.top = 0; rect.right = dimx - 1; rect.bottom = dimy - 1;
8325         AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
8326         const int cwidth = rect.right - rect.left + 1, cheight = rect.bottom - rect.top + 1;
8327         SetWindowPos(_window,0,0,0,cwidth,cheight,SWP_NOMOVE | SWP_NOZORDER | SWP_NOCOPYBITS);
8328       }
8329       if (_width!=dimx || _height!=dimy) {
8330         unsigned int *const ndata = new unsigned int[dimx*dimy];
8331         if (force_redraw) _render_resize(_data,_width,_height,ndata,dimx,dimy);
8332         else std::memset(ndata,0x80,sizeof(unsigned int)*dimx*dimy);
8333         delete[] _data;
8334         _data = ndata;
8335         _bmi.bmiHeader.biWidth = dimx;
8336         _bmi.bmiHeader.biHeight = -(int)dimy;
8337         _width = dimx;
8338         _height = dimy;
8339       }
8340       _window_width = dimx; _window_height = dimy;
8341       _is_resized = false;
8342       if (_is_fullscreen) move((screen_width()-_width)/2,(screen_height()-_height)/2);
8343       if (force_redraw) return paint();
8344       return *this;
8345     }
8346 
8347     CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
8348       if (is_empty()) return *this;
8349       if (force_redraw) {
8350         const unsigned int buf_size = _width*_height*4;
8351         void *odata = std::malloc(buf_size);
8352         std::memcpy(odata,_data,buf_size);
8353         assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
8354         std::memcpy(_data,odata,buf_size);
8355         std::free(odata);
8356         return paint();
8357       }
8358       return assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
8359     }
8360 
8361     CImgDisplay& show() {
8362       if (is_empty() || !_is_closed) return *this;
8363       _is_closed = false;
8364       if (_is_fullscreen) _init_fullscreen();
8365       ShowWindow(_window,SW_SHOW);
8366       _update_window_pos();
8367       return paint();
8368     }
8369 
8370     CImgDisplay& close() {
8371       if (is_empty() || _is_closed) return *this;
8372       _is_closed = true;
8373       if (_is_fullscreen) _desinit_fullscreen();
8374       ShowWindow(_window,SW_HIDE);
8375       _window_x = _window_y = 0;
8376       return *this;
8377     }
8378 
8379     CImgDisplay& move(const int posx, const int posy) {
8380       if (is_empty()) return *this;
8381       if (!_is_fullscreen) {
8382         RECT rect; rect.left = rect.top = 0; rect.right = _window_width-1; rect.bottom = _window_height-1;
8383         AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
8384         const int border1 = (rect.right-rect.left+1-_width)/2, border2 = rect.bottom-rect.top+1-_height-border1;
8385         SetWindowPos(_window,0,posx-border1,posy-border2,0,0,SWP_NOSIZE | SWP_NOZORDER);
8386       } else SetWindowPos(_window,0,posx,posy,0,0,SWP_NOSIZE | SWP_NOZORDER);
8387       _window_x = posx;
8388       _window_y = posy;
8389       _is_moved = false;
8390       return show();
8391     }
8392 
8393     CImgDisplay& show_mouse() {
8394       if (is_empty()) return *this;
8395       _is_cursor_visible = true;
8396       ShowCursor(TRUE);
8397       SendMessage(_window,WM_SETCURSOR,0,0);
8398       return *this;
8399     }
8400 
8401     CImgDisplay& hide_mouse() {
8402       if (is_empty()) return *this;
8403       _is_cursor_visible = false;
8404       ShowCursor(FALSE);
8405       SendMessage(_window,WM_SETCURSOR,0,0);
8406       return *this;
8407     }
8408 
8409     CImgDisplay& set_mouse(const int posx, const int posy) {
8410       if (_is_closed || posx<0 || posy<0) return *this;
8411       _update_window_pos();
8412       const int res = (int)SetCursorPos(_window_x + posx,_window_y + posy);
8413       if (res) { _mouse_x = posx; _mouse_y = posy; }
8414       return *this;
8415     }
8416 
8417     CImgDisplay& set_title(const char *const format, ...) {
8418       if (is_empty()) return *this;
8419       char tmp[1024] = { 0 };
8420       va_list ap;
8421       va_start(ap, format);
8422       cimg_vsnprintf(tmp,sizeof(tmp),format,ap);
8423       va_end(ap);
8424       if (!std::strcmp(_title,tmp)) return *this;
8425       delete[] _title;
8426       const unsigned int s = std::strlen(tmp) + 1;
8427       _title = new char[s];
8428       std::memcpy(_title,tmp,s*sizeof(char));
8429       SetWindowTextA(_window, tmp);
8430       return *this;
8431     }
8432 
8433     template<typename T>
8434     CImgDisplay& display(const CImg<T>& img) {
8435       if (!img)
8436         throw CImgArgumentException(_cimgdisplay_instance
8437                                     "display() : Empty specified image.",
8438                                     cimgdisplay_instance);
8439       if (is_empty()) return assign(img);
8440       return render(img).paint();
8441     }
8442 
8443     CImgDisplay& paint() {
8444       if (_is_closed) return *this;
8445       WaitForSingleObject(_mutex,INFINITE);
8446       SetDIBitsToDevice(_hdc,0,0,_width,_height,0,0,0,_height,_data,&_bmi,DIB_RGB_COLORS);
8447       ReleaseMutex(_mutex);
8448       return *this;
8449     }
8450 
8451     template<typename T>
8452     CImgDisplay& render(const CImg<T>& img) {
8453       if (!img)
8454         throw CImgArgumentException(_cimgdisplay_instance
8455                                     "render() : Empty specified image.",
8456                                     cimgdisplay_instance);
8457 
8458       if (is_empty()) return *this;
8459       if (img._depth!=1) return render(img.get_projections2d(img._width/2,img._height/2,img._depth/2));
8460 
8461       const T
8462         *data1 = img._data,
8463         *data2 = (img._spectrum>=2)?img.data(0,0,0,1):data1,
8464         *data3 = (img._spectrum>=3)?img.data(0,0,0,2):data1;
8465 
8466       WaitForSingleObject(_mutex,INFINITE);
8467       unsigned int
8468         *const ndata = (img._width==_width && img._height==_height)?_data:new unsigned int[img._width*img._height],
8469         *ptrd = ndata;
8470 
8471       if (!_normalization || (_normalization==3 && cimg::type<T>::string()==cimg::type<unsigned char>::string())) {
8472         _min = _max = 0;
8473         switch (img._spectrum) {
8474         case 1 : {
8475           for (unsigned int xy = img._width*img._height; xy>0; --xy) {
8476             const unsigned char val = (unsigned char)*(data1++);
8477             *(ptrd++) = (val<<16) | (val<<8) | val;
8478           }
8479         } break;
8480         case 2 : {
8481           for (unsigned int xy = img._width*img._height; xy>0; --xy)
8482             *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8);
8483         } break;
8484         default : {
8485           for (unsigned int xy = img._width*img._height; xy>0; --xy)
8486             *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) | (unsigned char)*(data3++);
8487         }
8488         }
8489       } else {
8490         if (_normalization==3) {
8491           if (cimg::type<T>::is_float()) _min = (float)img.min_max(_max);
8492           else { _min = (float)cimg::type<T>::min(); _max = (float)cimg::type<T>::max(); }
8493         } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max);
8494         const float delta = _max - _min, mm = 255/(delta?delta:1.0f);
8495         switch (img._spectrum) {
8496         case 1 : {
8497           for (unsigned int xy = img._width*img._height; xy>0; --xy) {
8498             const unsigned char val = (unsigned char)((*(data1++)-_min)*mm);
8499             *(ptrd++) = (val<<16) | (val<<8) | val;
8500           }
8501         } break;
8502         case 2 : {
8503           for (unsigned int xy = img._width*img._height; xy>0; --xy) {
8504             const unsigned char
8505               R = (unsigned char)((*(data1++)-_min)*mm),
8506               G = (unsigned char)((*(data2++)-_min)*mm);
8507             *(ptrd++) = (R<<16) | (G<<8);
8508           }
8509         } break;
8510         default : {
8511           for (unsigned int xy = img._width*img._height; xy>0; --xy) {
8512             const unsigned char
8513               R = (unsigned char)((*(data1++)-_min)*mm),
8514               G = (unsigned char)((*(data2++)-_min)*mm),
8515               B = (unsigned char)((*(data3++)-_min)*mm);
8516             *(ptrd++) = (R<<16) | (G<<8) | B;
8517           }
8518         }
8519         }
8520       }
8521       if (ndata!=_data) { _render_resize(ndata,img._width,img._height,_data,_width,_height); delete[] ndata; }
8522       ReleaseMutex(_mutex);
8523       return *this;
8524     }
8525 
8526     template<typename T>
8527     const CImgDisplay& snapshot(CImg<T>& img) const {
8528       if (is_empty()) { img.assign(); return *this; }
8529       const unsigned int *ptrs = _data;
8530       img.assign(_width,_height,1,3);
8531       T
8532         *data1 = img.data(0,0,0,0),
8533         *data2 = img.data(0,0,0,1),
8534         *data3 = img.data(0,0,0,2);
8535       for (unsigned int xy = img._width*img._height; xy>0; --xy) {
8536         const unsigned int val = *(ptrs++);
8537         *(data1++) = (unsigned char)(val>>16);
8538         *(data2++) = (unsigned char)((val>>8)&0xFF);
8539         *(data3++) = (unsigned char)(val&0xFF);
8540       }
8541       return *this;
8542     }
8543 #endif
8544 
8545     //@}
8546   };
8547 
8548   /*
8549    #--------------------------------------
8550    #
8551    #
8552    #
8553    # Definition of the CImg<T> structure
8554    #
8555    #
8556    #
8557    #--------------------------------------
8558    */
8559 
8560   //! Class representing an image (up to 4 dimensions wide), each pixel being of type \c T.
8561   /**
8562      This is the main class of the %CImg Library. It declares and constructs
8563      an image, allows access to its pixel values, and is able to perform various image operations.
8564 
8565      \par Image representation
8566 
8567      A %CImg image is defined as an instance of the container \c CImg<T>, which contains a regular grid of pixels,
8568      each pixel value being of type \c T. The image grid can have up to 4 dimensions : width, height, depth
8569      and number of channels.
8570      Usually, the three first dimensions are used to describe spatial coordinates <tt>(x,y,z)</tt>, while the number of channels
8571      is rather used as a vector-valued dimension (it may describe the R,G,B color channels for instance).
8572      If you need a fifth dimension, you can use image lists \c CImgList<T> rather than simple images \c CImg<T>.
8573 
8574      Thus, the \c CImg<T> class is able to represent volumetric images of vector-valued pixels,
8575      as well as images with less dimensions (1d scalar signal, 2d color images, ...).
8576      Most member functions of the class CImg<\c T> are designed to handle this maximum case of (3+1) dimensions.
8577 
8578      Concerning the pixel value type \c T :
8579      fully supported template types are the basic C++ types : <tt>unsigned char, char, short, unsigned int, int,
8580      unsigned long, long, float, double, ... </tt>.
8581      Typically, fast image display can be done using <tt>CImg<unsigned char></tt> images,
8582      while complex image processing algorithms may be rather coded using <tt>CImg<float></tt> or <tt>CImg<double></tt>
8583      images that have floating-point pixel values. The default value for the template T is \c float.
8584      Using your own template types may be possible. However, you will certainly have to define the complete set
8585      of arithmetic and logical operators for your class.
8586 
8587      \par Image structure
8588 
8589      The \c CImg<T> structure contains \e six fields :
8590      - \c _width defines the number of \a columns of the image (size along the X-axis).
8591      - \c _height defines the number of \a rows of the image (size along the Y-axis).
8592      - \c _depth defines the number of \a slices of the image (size along the Z-axis).
8593      - \c _spectrum defines the number of \a channels of the image (size along the C-axis).
8594      - \c _data defines a \a pointer to the \a pixel \a data (of type \c T).
8595      - \c _is_shared is a boolean that tells if the memory buffer \c data is shared with
8596        another image.
8597 
8598      You can access these fields publicly although it is recommended to use the dedicated functions
8599      width(), height(), depth(), spectrum() and ptr() to do so.
8600      Image dimensions are not limited to a specific range (as long as you got enough available memory).
8601      A value of \e 1 usually means that the corresponding dimension is \a flat.
8602      If one of the dimensions is \e 0, or if the data pointer is null, the image is considered as \e empty.
8603      Empty images should not contain any pixel data and thus, will not be processed by CImg member functions
8604      (a CImgInstanceException will be thrown instead).
8605      Pixel data are stored in memory, in a non interlaced mode (See \ref cimg_storage).
8606 
8607      \par Image declaration and construction
8608 
8609      Declaring an image can be done by using one of the several available constructors.
8610      Here is a list of the most used :
8611 
8612      - Construct images from arbitrary dimensions :
8613          - <tt>CImg<char> img;</tt> declares an empty image.
8614          - <tt>CImg<unsigned char> img(128,128);</tt> declares a 128x128 greyscale image with
8615          \c unsigned \c char pixel values.
8616          - <tt>CImg<double> img(3,3);</tt> declares a 3x3 matrix with \c double coefficients.
8617          - <tt>CImg<unsigned char> img(256,256,1,3);</tt> declares a 256x256x1x3 (color) image
8618          (colors are stored as an image with three channels).
8619          - <tt>CImg<double> img(128,128,128);</tt> declares a 128x128x128 volumetric and greyscale image
8620          (with \c double pixel values).
8621          - <tt>CImg<> img(128,128,128,3);</tt> declares a 128x128x128 volumetric color image
8622          (with \c float pixels, which is the default value of the template parameter \c T).
8623          - \b Note : images pixels are <b>not automatically initialized to 0</b>. You may use the function \c fill() to
8624          do it, or use the specific constructor taking 5 parameters like this :
8625          <tt>CImg<> img(128,128,128,3,0);</tt> declares a 128x128x128 volumetric color image with all pixel values to 0.
8626 
8627      - Construct images from filenames :
8628          - <tt>CImg<unsigned char> img("image.jpg");</tt> reads a JPEG color image from the file "image.jpg".
8629          - <tt>CImg<float> img("analyze.hdr");</tt> reads a volumetric image (ANALYZE7.5 format) from the file "analyze.hdr".
8630          - \b Note : You need to install <a href="http://www.imagemagick.org">ImageMagick</a>
8631          to be able to read common compressed image formats (JPG,PNG, ...) (See \ref cimg_files_io).
8632 
8633      - Construct images from C-style arrays :
8634          - <tt>CImg<int> img(data_buffer,256,256);</tt> constructs a 256x256 greyscale image from a \c int* buffer
8635          \c data_buffer (of size 256x256=65536).
8636          - <tt>CImg<unsigned char> img(data_buffer,256,256,1,3,false);</tt> constructs a 256x256 color image
8637          from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels follow each others).
8638          - <tt>CImg<unsigned char> img(data_buffer,256,256,1,3,true);</tt> constructs a 256x256 color image
8639          from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels are multiplexed).
8640 
8641          The complete list of constructors can be found <a href="#constructors">here</a>.
8642 
8643      \par Most useful functions
8644 
8645      The \c CImg<T> class contains a lot of functions that operates on images.
8646      Some of the most useful are :
8647 
8648      - operator()() : allows to access or write pixel values.
8649      - display() : displays the image in a new window.
8650   **/
8651   template<typename T>
8652   struct CImg {
8653 
8654     unsigned int _width, _height, _depth, _spectrum;
8655     bool _is_shared;
8656     T *_data;
8657 
8658     //! Simple iterator type, to loop through each pixel value of an image instance.
8659     /**
8660        \note
8661        - The \c CImg<T>::iterator type is defined to be a <tt>T*</tt>.
8662        - You will seldom have to use iterators in %CImg, most classical operations
8663          being achieved (often in a faster way) using methods of \c CImg<T>.
8664        \par Sample code :
8665        \code
8666        CImg<float> img("reference.jpg");                                         // Load image from file.
8667        for (CImg<float>::iterator it = img.begin(), it<img.end(); ++it) *it = 0; // Set all pixels to '0', through a CImg iterator.
8668        img.fill(0);                                                              // Do the same with a built-in method.
8669        \endcode
8670        \sa const_iterator.
8671    **/
8672     typedef T* iterator;
8673 
8674     //! Simple const iterator type, to loop through each pixel value of a \c const image instance.
8675     /**
8676        \note
8677        - The \c CImg<T>::const_iterator type is defined to be a \c const \c T*.
8678        - You will seldom have to use iterators in %CImg, most classical operations
8679          being achieved (often in a faster way) using methods of \c CImg<T>.
8680        \par Sample code :
8681        \code
8682        const CImg<float> img("reference.jpg");                                    // Load image from file.
8683        float sum = 0;
8684        for (CImg<float>::iterator it = img.begin(), it<img.end(); ++it) sum+=*it; // Compute sum of all pixel values, through a CImg iterator.
8685        const float sum2 = img.sum();                                              // Do the same with a built-in method.
8686        \endcode
8687        \sa iterator.
8688     **/
8689     typedef const T* const_iterator;
8690 
8691     //! Pixel value type.
8692     /**
8693        Refer to the type of the pixel values of an image instance.
8694        \note
8695        - The \c CImg<T>::value_type type of a \c CImg<T> is defined to be a \c T.
8696        - \c CImg<T>::value_type is actually not used in %CImg methods. It has been mainly defined for
8697          compatibility with STL naming conventions.
8698     **/
8699     typedef T value_type;
8700 
8701     // Define common types related to template type T.
8702     typedef typename cimg::superset<T,bool>::type Tbool;
8703     typedef typename cimg::superset<T,unsigned char>::type Tuchar;
8704     typedef typename cimg::superset<T,char>::type Tchar;
8705     typedef typename cimg::superset<T,unsigned short>::type Tushort;
8706     typedef typename cimg::superset<T,short>::type Tshort;
8707     typedef typename cimg::superset<T,unsigned int>::type Tuint;
8708     typedef typename cimg::superset<T,int>::type Tint;
8709     typedef typename cimg::superset<T,unsigned long>::type Tulong;
8710     typedef typename cimg::superset<T,long>::type Tlong;
8711     typedef typename cimg::superset<T,float>::type Tfloat;
8712     typedef typename cimg::superset<T,double>::type Tdouble;
8713     typedef typename cimg::last<T,bool>::type boolT;
8714     typedef typename cimg::last<T,unsigned char>::type ucharT;
8715     typedef typename cimg::last<T,char>::type charT;
8716     typedef typename cimg::last<T,unsigned short>::type ushortT;
8717     typedef typename cimg::last<T,short>::type shortT;
8718     typedef typename cimg::last<T,unsigned int>::type uintT;
8719     typedef typename cimg::last<T,int>::type intT;
8720     typedef typename cimg::last<T,unsigned long>::type ulongT;
8721     typedef typename cimg::last<T,long>::type longT;
8722     typedef typename cimg::last<T,float>::type floatT;
8723     typedef typename cimg::last<T,double>::type doubleT;
8724 
8725     //@}
8726     //---------------------------
8727     //
8728     //! \name Plugins
8729     //@{
8730     //---------------------------
8731 #ifdef cimg_plugin
8732 #include cimg_plugin
8733 #endif
8734 #ifdef cimg_plugin1
8735 #include cimg_plugin1
8736 #endif
8737 #ifdef cimg_plugin2
8738 #include cimg_plugin2
8739 #endif
8740 #ifdef cimg_plugin3
8741 #include cimg_plugin3
8742 #endif
8743 #ifdef cimg_plugin4
8744 #include cimg_plugin4
8745 #endif
8746 #ifdef cimg_plugin5
8747 #include cimg_plugin5
8748 #endif
8749 #ifdef cimg_plugin6
8750 #include cimg_plugin6
8751 #endif
8752 #ifdef cimg_plugin7
8753 #include cimg_plugin7
8754 #endif
8755 #ifdef cimg_plugin8
8756 #include cimg_plugin8
8757 #endif
8758 
8759     //@}
8760     //---------------------------------------------------------
8761     //
8762     //! \name Constructors / Destructor / Instance Management
8763     //@{
8764     //---------------------------------------------------------
8765 
8766     //! Destructor.
8767     /**
8768        Destroy current image instance.
8769        \note
8770        - The pixel buffer data() is deallocated if necessary, e.g. for non-empty and non-shared image instances.
8771        - Destroying an empty or shared image does nothing actually.
8772        \warning
8773        - When destroying a non-shared image, make sure that you will \e not operate on a remaining shared image
8774          that shares its buffer with the destroyed instance, in order to avoid further invalid memory access (to a deallocated buffer).
8775        \sa CImg(),
8776            assign().
8777     **/
8778     ~CImg() {
8779       if (!_is_shared) delete[] _data;
8780     }
8781 
8782     //! Default constructor.
8783     /**
8784        Construct a new empty image instance.
8785        \note
8786        - An empty image has no pixel data and all of its dimensions width(), height(), depth(), spectrum()
8787          are set to \c 0, as well as its pixel buffer pointer data().
8788        - An empty image may be re-assigned afterwards, e.g. with the family of assign(unsigned int,unsigned int,unsigned int,unsigned int) methods,
8789          or by operator=(const CImg<t>&). In all cases, the type of pixels stays \c T.
8790        - An empty image is never shared.
8791        \par Sample code :
8792        \code
8793        CImg<float> img1, img2;      // Construct two empty images.
8794        img1.assign(256,256,1,3);    // Re-assign 'img1' to be a 256x256x1x3 (color) image.
8795        img2 = img1.get_rand(0,255); // Re-assign 'img2' to be a random-valued version of 'img1'.
8796        img2.assign();               // Re-assign 'img2' to be an empty image again.
8797        \endcode
8798        \sa ~CImg(),
8799            assign(),
8800            is_empty().
8801     **/
8802     CImg():_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {}
8803 
8804     //! Construct image with specified size.
8805     /**
8806        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.
8807        \param size_x : Desired image width().
8808        \param size_y : Desired image height().
8809        \param size_z : Desired image depth().
8810        \param size_c : Desired image spectrum().
8811        \note
8812        - It is able to create only \e non-shared images, and allocates thus a pixel buffer data() for each constructed image instance.
8813        - 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.
8814        - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated (e.g. when requested size is too big for available memory).
8815        \warning
8816        - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values.
8817          In order to initialize pixel values during construction (e.g. with \c 0), use constructor
8818          CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) instead.
8819        \par Sample code :
8820        \code
8821        CImg<float> img1(256,256,1,3);   // Construct a 256x256x1x3 (color) image, filled with garbage values.
8822        CImg<float> img2(256,256,1,3,0); // Construct a 256x256x1x3 (color) image, filled with value '0'.
8823        \endcode
8824        \sa CImg(unsigned int,unsigned int,unsigned int,unsigned int,T),
8825            assign(unsigned int,unsigned int,unsigned int,unsigned int).
8826     **/
8827     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):
8828       _is_shared(false) {
8829       const unsigned int siz = size_x*size_y*size_z*size_c;
8830       if (siz) {
8831         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
8832         try { _data = new T[siz]; } catch (...) {
8833           _width = _height = _depth = _spectrum = 0; _data = 0;
8834           throw CImgInstanceException(_cimg_instance
8835                                       "CImg() : Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
8836                                       cimg_instance,
8837                                       cimg::strbuffersize(size_x*size_y*size_z*size_c*sizeof(T)),size_x,size_y,size_z,size_c);
8838         }
8839       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
8840     }
8841 
8842     //! Construct image with specified size and initialize pixel values.
8843     /**
8844        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 set all pixel
8845        values to specified \c value.
8846        \param size_x : Desired image width().
8847        \param size_y : Desired image height().
8848        \param size_z : Desired image depth().
8849        \param size_c : Desired image spectrum().
8850        \param value : Value used for initialization.
8851        \note
8852        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int),
8853          but it also fills the pixel buffer with the specified \c value.
8854        \warning
8855        - 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).
8856          For this task, you may use fillC() after construction.
8857        \sa CImg(unsigned int,unsigned int,unsigned int,unsigned int),
8858            assign(unsigned int,unsigned int,unsigned int,unsigned int,T).
8859     **/
8860     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, const T value):
8861       _is_shared(false) {
8862       const unsigned int siz = size_x*size_y*size_z*size_c;
8863       if (siz) {
8864         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
8865         try { _data = new T[siz]; } catch (...) {
8866           _width = _height = _depth = _spectrum = 0; _data = 0;
8867           throw CImgInstanceException(_cimg_instance
8868                                       "CImg() : Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
8869                                       cimg_instance,
8870                                       cimg::strbuffersize(size_x*size_y*size_z*size_c*sizeof(T)),size_x,size_y,size_z,size_c);
8871         }
8872         fill(value);
8873       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
8874     }
8875 
8876     //! Construct image with specified size and initialize pixel values from a sequence of integers.
8877     /**
8878        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
8879        values from the specified sequence of integers \c value0,\c value1,\c ...
8880        \param size_x : Desired image width().
8881        \param size_y : Desired image height().
8882        \param size_z : Desired image depth().
8883        \param size_c : Desired image spectrum().
8884        \param value0 : First value of the initialization sequence (must be an \e integer).
8885        \param value1 : Second value of the initialization sequence (must be an \e integer).
8886        \param ...
8887        \note
8888        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills
8889          the pixel buffer with a sequence of specified integer values.
8890        \warning
8891        - You must specify \e exactly \c size_x*\c size_y*\c size_z*\c size_c integers in the initialization sequence.
8892          Otherwise, the constructor may crash or fill your image pixels with garbage.
8893        \par Sample code :
8894        \code
8895        const CImg<float> img(2,2,1,3,      // Construct a 2x2 color (RGB) image.
8896                              0,255,0,255,  // Set the 4 values for the red component.
8897                              0,0,255,255,  // Set the 4 values for the green component.
8898                              64,64,64,64); // Set the 4 values for the blue component.
8899        img.resize(150,150).display();
8900        \endcode
8901        \image html ref_constructor1.jpg
8902        \sa CImg(unsigned int,unsigned int,unsigned int,unsigned int),
8903            CImg(unsigned int,unsigned int,unsigned int,unsigned int,double,double,...),
8904            assign(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...).
8905      **/
8906     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
8907          const int value0, const int value1, ...):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
8908 #define _CImg_stdarg(img,a0,a1,N,t) { \
8909         unsigned int _siz = (unsigned int)N; \
8910         if (_siz--) { \
8911           va_list ap; \
8912           va_start(ap,a1); \
8913           T *ptrd = (img)._data; \
8914           *(ptrd++) = (T)a0; \
8915           if (_siz--) { \
8916             *(ptrd++) = (T)a1; \
8917             for (; _siz; --_siz) *(ptrd++) = (T)va_arg(ap,t); \
8918           } \
8919           va_end(ap); \
8920         } \
8921       }
8922       assign(size_x,size_y,size_z,size_c);
8923       _CImg_stdarg(*this,value0,value1,size_x*size_y*size_z*size_c,int);
8924     }
8925 
8926     //! Construct image with specified size and initialize pixel values from a sequence of doubles.
8927     /**
8928        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
8929        values from the specified sequence of doubles \c value0,\c value1,\c ...
8930        \param size_x : Desired image width().
8931        \param size_y : Desired image height().
8932        \param size_z : Desired image depth().
8933        \param size_c : Desired image spectrum().
8934        \param value0 : First value of the initialization sequence (must be a \e double).
8935        \param value1 : Second value of the initialization sequence (must be a \e double).
8936        \param ...
8937        \note
8938        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...), but
8939          takes a sequence of double values instead of integers.
8940        \warning
8941        - You must specify \e exactly \c dx*\c dy*\c dz*\c dc doubles in the initialization sequence.
8942          Otherwise, the constructor may crash or fill your image with garbage.
8943          For instance, the code below will probably crash on most platforms :
8944          \code
8945          const CImg<float> img(2,2,1,1, 0.5,0.5,255,255); // FAIL : The two last arguments are 'int', not 'double' !
8946          \endcode
8947        \sa CImg(unsigned int,unsigned int,unsigned int,unsigned int),
8948            CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...),
8949            assign(unsigned int,unsigned int,unsigned int,unsigned int,double,double,...).
8950      **/
8951     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
8952          const double value0, const double value1, ...):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
8953       assign(size_x,size_y,size_z,size_c);
8954       _CImg_stdarg(*this,value0,value1,size_x*size_y*size_z*size_c,double);
8955     }
8956 
8957     //! Construct image with specified size and initialize pixel values from a value string.
8958     /**
8959        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
8960        values from the specified string \c values.
8961        \param size_x : Desired image width().
8962        \param size_y : Desired image height().
8963        \param size_z : Desired image depth().
8964        \param size_c : Desired image spectrum().
8965        \param values : Value string describing the way pixel values are set.
8966        \param repeat_values : Flag telling if the value filling process is periodic.
8967        \note
8968        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills
8969          the pixel buffer with values described in the value string \c values.
8970        - Value string \c values may describe two different filling processes :
8971          - Either \c values is a sequences of values assigned to the image pixels, as in <tt>"1,2,3,7,8,2"</tt>.
8972            In this case, set \c repeat_values to \c true to periodically fill the image with the value sequence.
8973          - 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.
8974        - For both cases, specifying \c repeat_values is mandatory. It disambiguates the possible overloading of constructor
8975          CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) with \c T being a <tt>const char*</tt>.
8976        - A \c CImgArgumentException is thrown when an invalid value string \c values is specified.
8977        \par Sample code :
8978        \code
8979        const CImg<float> img1(129,129,1,3,"0,64,128,192,255",true),                   // Construct image filled from a value sequence.
8980                          img2(129,129,1,3,"if(c==0,255*abs(cos(x/10)),1.8*y)",false); // Construct image filled from a formula.
8981        (img1,img2).display();
8982        \endcode
8983        \image html ref_constructor2.jpg
8984        \sa CImg(unsigned int,unsigned int,unsigned int,unsigned int),
8985            assign(unsigned int,unsigned int,unsigned int,unsigned int,const char*,bool).
8986      **/
8987     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
8988          const char *const values, const bool repeat_values):_is_shared(false) {
8989       const unsigned int siz = size_x*size_y*size_z*size_c;
8990       if (siz) {
8991         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
8992         try { _data = new T[siz]; } catch (...) {
8993           _width = _height = _depth = _spectrum = 0; _data = 0;
8994           throw CImgInstanceException(_cimg_instance
8995                                       "CImg() : Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
8996                                       cimg_instance,
8997                                       cimg::strbuffersize(size_x*size_y*size_z*size_c*sizeof(T)),size_x,size_y,size_z,size_c);
8998         }
8999         fill(values,repeat_values);
9000       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
9001     }
9002 
9003     //! Construct image with specified size and initialize pixel values from a memory buffer.
9004     /**
9005        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
9006        values from the specified \c t* memory buffer.
9007        \param values : Pointer to the input memory buffer.
9008        \param size_x : Desired image width().
9009        \param size_y : Desired image height().
9010        \param size_z : Desired image depth().
9011        \param size_c : Desired image spectrum().
9012        \param is_shared : Flag telling if input memory buffer must be shared by the current instance.
9013        \note
9014        - If \c is_shared is \c false, the image instance allocates its own pixel buffer, and values from the specified input buffer
9015          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.
9016        - Otherwise, the image instance does \e not allocate a new buffer, and uses the input memory buffer as its own pixel buffer. This case
9017          requires that types \c T and \c t are the same. Later, destroying such a shared image will not deallocate the pixel buffer,
9018          this task being obviously charged to the initial buffer allocator.
9019        - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated (e.g. when requested size is too big for available memory).
9020        \warning
9021        - You must take care when operating on a shared image, since it may have an invalid pixel buffer pointer data() (e.g. already deallocated).
9022        \par Sample code :
9023        \code
9024        unsigned char tab[256*256] = { 0 };
9025        CImg<unsigned char> img1(tab,256,256,1,1,false), // Construct new non-shared image from buffer 'tab'.
9026                            img2(tab,256,256,1,1,true);  // Construct new shared-image from buffer 'tab'.
9027        tab[1024] = 255;                                 // Here, 'img2' is indirectly modified, but not 'img1'.
9028        \endcode
9029        \sa CImg(unsigned int,unsigned int,unsigned int,unsigned int),
9030            assign(const t*,unsigned int,unsigned int,unsigned int,unsigned int,bool),
9031            is_shared().
9032     **/
9033     template<typename t>
9034     CImg(const t *const values, const unsigned int size_x, const unsigned int size_y=1,
9035          const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false):_is_shared(false) {
9036       if (is_shared) {
9037         _width = _height = _depth = _spectrum = 0; _data = 0;
9038         throw CImgArgumentException(_cimg_instance
9039                                     "CImg() : Invalid construction request of a (%u,%u,%u,%u) shared instance from a (%s*) buffer "
9040                                     "(pixel types are different).",
9041                                     cimg_instance,
9042                                     size_x,size_y,size_z,size_c,CImg<t>::pixel_type());
9043       }
9044       const unsigned int siz = size_x*size_y*size_z*size_c;
9045       if (values && siz) {
9046         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
9047         try { _data = new T[siz]; } catch (...) {
9048           _width = _height = _depth = _spectrum = 0; _data = 0;
9049           throw CImgInstanceException(_cimg_instance
9050                                       "CImg() : Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9051                                       cimg_instance,
9052                                       cimg::strbuffersize(size_x*size_y*size_z*size_c*sizeof(T)),size_x,size_y,size_z,size_c);
9053 
9054         }
9055         const t *ptrs = values + siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs);
9056       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
9057     }
9058 
9059     //! Construct image with specified size and initialize pixel values from a memory buffer \specialization.
9060     CImg(const T *const values, const unsigned int size_x, const unsigned int size_y=1,
9061          const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false) {
9062       const unsigned int siz = size_x*size_y*size_z*size_c;
9063       if (values && siz) {
9064         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = is_shared;
9065         if (_is_shared) _data = const_cast<T*>(values);
9066         else {
9067           try { _data = new T[siz]; } catch (...) {
9068             _width = _height = _depth = _spectrum = 0; _data = 0;
9069             throw CImgInstanceException(_cimg_instance
9070                                         "CImg() : Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9071                                         cimg_instance,
9072                                         cimg::strbuffersize(size_x*size_y*size_z*size_c*sizeof(T)),size_x,size_y,size_z,size_c);
9073           }
9074           std::memcpy(_data,values,siz*sizeof(T)); }
9075       } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
9076     }
9077 
9078     //! Construct image from an image file.
9079     /**
9080        Construct a new image instance with pixels of type \c T, and initialize pixel values with the data read from an image file.
9081        \param filename : Input image filename.
9082        \note
9083        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it reads the image
9084          dimensions and pixel values from the specified image file.
9085        - The recognition of the image file format by %CImg higly depends on the tools installed on your system
9086          and on the external libraries you used to link your code against.
9087        - Considered pixel type \c T should better fit the file format specification, or data loss may occur during file load
9088          (e.g. constructing a \c CImg<unsigned char> from a float-valued image file).
9089        - A \c CImgIOException is thrown when the specified \c filename cannot be read, or if the file format is not recognized.
9090        \par Sample code :
9091        \code
9092        const CImg<float> img("reference.jpg");
9093        img.display();
9094        \endcode
9095        \image html ref_image.jpg
9096        \sa CImg(unsigned int,unsigned int,unsigned int,unsigned int),
9097            assign(const char*).
9098     **/
9099     explicit CImg(const char *const filename):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
9100       assign(filename);
9101     }
9102 
9103     //! Copy constructor.
9104     /**
9105        Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg<t> instance.
9106        \param img : Input image to copy.
9107        \note
9108        - Constructed copy has the same size width() x height() x depth() x spectrum() and pixel values as the input image \c img.
9109        - 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,
9110          and shares its pixel buffer with \c img.
9111          Modifying a pixel value in the constructed copy will thus also modifies it in the input image \c img.
9112          This behavior is needful to allow functions to return shared images.
9113        - Otherwise, the constructed copy allocates its own pixel buffer, and copies pixel values from the input image \c img
9114          into its buffer. The copied pixel values may be eventually statically casted if types \c T and \c t are different.
9115        - 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.
9116        - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated (e.g. not enough available memory).
9117        \sa CImg(const CImg<t>&,bool),
9118            assign(const CImg<t>&),
9119     **/
9120     template<typename t>
9121     CImg(const CImg<t>& img):_is_shared(false) {
9122       const unsigned int siz = img.size();
9123       if (img._data && siz) {
9124         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
9125         try { _data = new T[siz]; } catch (...) {
9126           _width = _height = _depth = _spectrum = 0; _data = 0;
9127           throw CImgInstanceException(_cimg_instance
9128                                       "CImg() : Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9129                                       cimg_instance,
9130                                       cimg::strbuffersize(img._width*img._height*img._depth*img._spectrum*sizeof(T)),
9131                                       img._width,img._height,img._depth,img._spectrum);
9132         }
9133         const t *ptrs = img._data + siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs);
9134       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
9135     }
9136 
9137     //! Copy constructor \specialization.
9138     CImg(const CImg<T>& img) {
9139       const unsigned int siz = img.size();
9140       if (img._data && siz) {
9141         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; _is_shared = img._is_shared;
9142         if (_is_shared) _data = const_cast<T*>(img._data);
9143         else {
9144           try { _data = new T[siz]; } catch (...) {
9145             _width = _height = _depth = _spectrum = 0; _data = 0;
9146             throw CImgInstanceException(_cimg_instance
9147                                         "CImg() : Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9148                                         cimg_instance,
9149                                         cimg::strbuffersize(img._width*img._height*img._depth*img._spectrum*sizeof(T)),
9150                                         img._width,img._height,img._depth,img._spectrum);
9151 
9152           }
9153           std::memcpy(_data,img._data,siz*sizeof(T));
9154         }
9155       } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
9156     }
9157 
9158     //! Advanced copy constructor.
9159     /**
9160        Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg<t> instance,
9161        while forcing the shared state of the constructed copy.
9162        \param img : Input image to copy.
9163        \param is_shared : Desired shared state of the constructed copy.
9164        \note
9165        - Similar to CImg(const CImg<t>&), except that it allows to decide the shared state of
9166          the constructed image, which does not depend anymore on the shared state of the input image \c img :
9167          - If \c is_shared is \c true, the constructed copy will share its pixel buffer with the input image \c img.
9168            For that case, the pixel types \c T and \c t \e must be the same.
9169          - If \c is_shared is \c false, the constructed copy will allocate its own pixel buffer, whether the input image \c img is
9170            shared or not.
9171        - A \c CImgArgumentException is thrown when a shared copy is requested with different pixel types \c T and \c t.
9172        \sa CImg(const CImg<t>&),
9173            assign(const CImg<t>&,bool).
9174     **/
9175     template<typename t>
9176     CImg(const CImg<t>& img, const bool is_shared):_is_shared(false) {
9177       if (is_shared) {
9178         _width = _height = _depth = _spectrum = 0; _data = 0;
9179         throw CImgArgumentException(_cimg_instance
9180                                     "CImg() : Invalid construction request of a shared instance from a "
9181                                     "CImg<%s> image (%u,%u,%u,%u,%p) (pixel types are different).",
9182                                     cimg_instance,
9183                                     CImg<t>::pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data);
9184       }
9185       const unsigned int siz = img.size();
9186       if (img._data && siz) {
9187         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
9188         try { _data = new T[siz]; } catch (...) {
9189           _width = _height = _depth = _spectrum = 0; _data = 0;
9190           throw CImgInstanceException(_cimg_instance
9191                                       "CImg() : Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9192                                       cimg_instance,
9193                                       cimg::strbuffersize(img._width*img._height*img._depth*img._spectrum*sizeof(T)),
9194                                       img._width,img._height,img._depth,img._spectrum);
9195         }
9196         const t *ptrs = img._data + siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs);
9197       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
9198     }
9199 
9200     //! Advanced copy constructor \specialization.
9201     CImg(const CImg<T>& img, const bool is_shared) {
9202       const unsigned int siz = img.size();
9203       if (img._data && siz) {
9204         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; _is_shared = is_shared;
9205         if (_is_shared) _data = const_cast<T*>(img._data);
9206         else {
9207           try { _data = new T[siz]; } catch (...) {
9208             _width = _height = _depth = _spectrum = 0; _data = 0;
9209             throw CImgInstanceException(_cimg_instance
9210                                         "CImg() : Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9211                                         cimg_instance,
9212                                         cimg::strbuffersize(img._width*img._height*img._depth*img._spectrum*sizeof(T)),
9213                                         img._width,img._height,img._depth,img._spectrum);
9214           }
9215           std::memcpy(_data,img._data,siz*sizeof(T));
9216         }
9217       } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
9218     }
9219 
9220     //! Construct image with dimensions borrowed from another image.
9221     /**
9222        Construct a new image instance with pixels of type \c T, and size get from some dimensions of an existing \c CImg<t> instance.
9223        \param img : Input image from which dimensions are borrowed.
9224        \param dimensions : String describing the image size along the X,Y,Z and C-dimensions.
9225        \note
9226        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it takes the image dimensions
9227          (\e not its pixel values) from an existing \c CImg<t> instance.
9228        - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values.
9229          In order to initialize pixel values (e.g. with \c 0), use constructor CImg(const CImg<t>&,const char*,T) instead.
9230        \par Sample code :
9231        \code
9232        const CImg<float> img1(256,128,1,3),      // 'img1' is a 256x128x1x3 image.
9233                          img2(img1,"xyzc"),      // 'img2' is a 256x128x1x3 image.
9234                          img3(img1,"y,x,z,c"),   // 'img3' is a 128x256x1x3 image.
9235                          img4(img1,"c,x,y,3",0), // 'img4' is a 3x128x256x3 image (with pixels initialized to '0').
9236        \endcode
9237        \sa CImg(unsigned int,unsigned int,unsigned int,unsigned int),
9238            CImg(const CImg<t>&,const char*,T),
9239            assign(const CImg<t>&,const char*).
9240      **/
9241     template<typename t>
9242     CImg(const CImg<t>& img, const char *const dimensions):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
9243       assign(img,dimensions);
9244     }
9245 
9246     //! Construct image with dimensions borrowed from another image and initialize pixel values.
9247     /**
9248        Construct a new image instance with pixels of type \c T, and size get from the dimensions of an existing \c CImg<t> instance,
9249        and set all pixel values to specified \c value.
9250        \param img : Input image from which dimensions are borrowed.
9251        \param dimensions : String describing the image size along the X,Y,Z and V-dimensions.
9252        \param value : Value used for initialization.
9253        \note
9254        - Similar to CImg(const CImg<t>&,const char*), but it also fills the pixel buffer with the specified \c value.
9255        \sa CImg(unsigned int,unsigned int,unsigned int,unsigned int,T),
9256            CImg(const CImg<t>&,const char*),
9257            assign(const CImg<t>&,const char*,T).
9258      **/
9259     template<typename t>
9260     CImg(const CImg<t>& img, const char *const dimensions, const T value):
9261       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
9262       assign(img,dimensions).fill(value);
9263     }
9264 
9265     //! Construct image from a display window.
9266     /**
9267        Construct a new image instance with pixels of type \c T, as a snapshot of an existing \c CImgDisplay instance.
9268        \param disp : Input display window.
9269        \note
9270        - The width() and height() of the constructed image instance are the same as the specified \c CImgDisplay.
9271        - The depth() and spectrum() of the constructed image instance are respectively set to \c 1 and \c 3 (i.e. a 2d color image).
9272        - The image pixels are read as 8-bits RGB values.
9273        \sa CImgDisplay,
9274            assign(const CImgDisplay&).
9275      **/
9276     explicit CImg(const CImgDisplay &disp):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
9277       disp.snapshot(*this);
9278     }
9279 
9280     //! In-place version of the default constructor/destructor.
9281     /**
9282        In-place version of the default constructor CImg(). It simply resets the instance to an empty image.
9283        \note
9284        - It reinitializes the current image instance to a new constructed image instance.
9285        - Memory used by the previous pixel buffer of the image instance is deallocated if necessary (i.e. if instance was not empty nor shared).
9286        - If the image instance was shared, it is replaced by a (non-shared) empty image without a deallocation process.
9287        - It can be useful to force memory deallocation of a pixel buffer used by an image instance, before its formal destruction.
9288        \sa CImg(),
9289            ~CImg().
9290     **/
9291     CImg<T>& assign() {
9292       if (!_is_shared) delete[] _data;
9293       _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0;
9294       return *this;
9295     }
9296 
9297     //! In-place version of a constructor.
9298     /**
9299        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int).
9300        \param size_x : Desired image width().
9301        \param size_y : Desired image height().
9302        \param size_z : Desired image depth().
9303        \param size_c : Desired image spectrum().
9304        \note
9305        - It reinitializes the current image instance to a new constructed image instance.
9306        \sa assign(unsigned int,unsigned int,unsigned int,unsigned int,T).
9307            CImg(unsigned int,unsigned int,unsigned int,unsigned int).
9308     **/
9309     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) {
9310       const unsigned int siz = size_x*size_y*size_z*size_c;
9311       if (!siz) return assign();
9312       const unsigned int curr_siz = size();
9313       if (siz!=curr_siz) {
9314         if (_is_shared)
9315           throw CImgArgumentException(_cimg_instance
9316                                       "assign() : Invalid assignement request of shared instance from specified image (%u,%u,%u,%u).",
9317                                       cimg_instance,
9318                                       size_x,size_y,size_z,size_c);
9319         else {
9320           delete[] _data;
9321           try { _data = new T[siz]; } catch (...) {
9322             _width = _height = _depth = _spectrum = 0; _data = 0;
9323             throw CImgInstanceException(_cimg_instance
9324                                         "assign() : Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9325                                         cimg_instance,
9326                                         cimg::strbuffersize(size_x*size_y*size_z*size_c*sizeof(T)),size_x,size_y,size_z,size_c);
9327           }
9328         }
9329       }
9330       _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
9331       return *this;
9332     }
9333 
9334     //! In-place version of a constructor.
9335     /**
9336        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,T).
9337        \param size_x : Desired image width().
9338        \param size_y : Desired image height().
9339        \param size_z : Desired image depth().
9340        \param size_c : Desired image spectrum().
9341        \param value : Value for initialization.
9342        \note
9343        - It reinitializes the current image instance to a new constructed image instance.
9344        \sa assign(unsigned int,unsigned int,unsigned int,unsigned int),
9345            CImg(unsigned int,unsigned int,unsigned int,unsigned int,T).
9346     **/
9347     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) {
9348       return assign(size_x,size_y,size_z,size_c).fill(value);
9349     }
9350 
9351     //! In-place version of a constructor.
9352     /**
9353        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...).
9354        \param size_x : Desired image width().
9355        \param size_y : Desired image height().
9356        \param size_z : Desired image depth().
9357        \param size_c : Desired image spectrum().
9358        \param value0 : First value of the initialization sequence (must be an integer).
9359        \param value1 : Second value of the initialization sequence (must be an integer).
9360        \param ...
9361        \note
9362        - It reinitializes the current image instance to a new constructed image instance.
9363        \sa assign(unsigned int,unsigned int,unsigned int,unsigned int,double,double,...),
9364            CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...).
9365     **/
9366     CImg<T>& assign(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
9367                     const int value0, const int value1, ...) {
9368       assign(size_x,size_y,size_z,size_c);
9369       _CImg_stdarg(*this,value0,value1,size_x*size_y*size_z*size_c,int);
9370       return *this;
9371     }
9372 
9373     //! In-place version of a constructor.
9374     /**
9375        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,double,double,...).
9376        \param size_x : Desired image width().
9377        \param size_y : Desired image height().
9378        \param size_z : Desired image depth().
9379        \param size_c : Desired image spectrum().
9380        \param value0 : First value of the initialization sequence (must be a double).
9381        \param value1 : Second value of the initialization sequence (must be a double).
9382        \param ...
9383        \note
9384        - It reinitializes the current image instance to a new constructed image instance.
9385        \sa assign(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...),
9386            CImg(unsigned int,unsigned int,unsigned int,unsigned int,double,double,...).
9387     **/
9388     CImg<T>& assign(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
9389                     const double value0, const double value1, ...) {
9390       assign(size_x,size_y,size_z,size_c);
9391       _CImg_stdarg(*this,value0,value1,size_x*size_y*size_z*size_c,double);
9392       return *this;
9393     }
9394 
9395     //! In-place version of a constructor.
9396     /**
9397        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,const char*,bool).
9398        \param size_x : Desired image width().
9399        \param size_y : Desired image height().
9400        \param size_z : Desired image depth().
9401        \param size_c : Desired image spectrum().
9402        \param values : Value string describing the way pixel values are set.
9403        \param repeat_values : Flag telling if filling process is periodic.
9404        \note
9405        - It reinitializes the current image instance to a new constructed image instance.
9406        \sa CImg(unsigned int,unsigned int,unsigned int,unsigned int,const char*,bool).
9407     **/
9408     CImg<T>& assign(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
9409                     const char *const values, const bool repeat_values) {
9410       return assign(size_x,size_y,size_z,size_c).fill(values,repeat_values);
9411     }
9412 
9413     //! In-place version of a constructor.
9414     /**
9415        In-place version of the constructor CImg(const t*,unsigned int,unsigned int,unsigned int,unsigned int,bool).
9416        \param values : Pointer to the input memory buffer.
9417        \param size_x : Desired image width().
9418        \param size_y : Desired image height().
9419        \param size_z : Desired image depth().
9420        \param size_c : Desired image spectrum().
9421        \note
9422        - It reinitializes the current image instance to a new constructed image instance.
9423        \sa assign(const t*,unsigned int,unsigned int,unsigned int,unsigned int,bool).
9424            CImg(const t*,unsigned int,unsigned int,unsigned int,unsigned int,bool).
9425     **/
9426     template<typename t>
9427     CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y=1,
9428                     const unsigned int size_z=1, const unsigned int size_c=1) {
9429       const unsigned int siz = size_x*size_y*size_z*size_c;
9430       if (!values || !siz) return assign();
9431       assign(size_x,size_y,size_z,size_c);
9432       const t *ptrs = values + siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs);
9433       return *this;
9434     }
9435 
9436     //! In-place version of a constructor \specialization.
9437     CImg<T>& assign(const T *const values, const unsigned int size_x, const unsigned int size_y=1,
9438                     const unsigned int size_z=1, const unsigned int size_c=1) {
9439       const unsigned int siz = size_x*size_y*size_z*size_c;
9440       if (!values || !siz) return assign();
9441       const unsigned int curr_siz = size();
9442       if (values==_data && siz==curr_siz) return assign(size_x,size_y,size_z,size_c);
9443       if (_is_shared || values+siz<_data || values>=_data+size()) {
9444         assign(size_x,size_y,size_z,size_c);
9445         if (_is_shared) std::memmove(_data,values,siz*sizeof(T));
9446         else std::memcpy(_data,values,siz*sizeof(T));
9447       } else {
9448         T *new_data = 0;
9449         try { new_data = new T[siz]; } catch (...) {
9450           _width = _height = _depth = _spectrum = 0; _data = 0;
9451           throw CImgInstanceException(_cimg_instance
9452                                       "assign() : Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9453                                       cimg_instance,
9454                                       cimg::strbuffersize(size_x*size_y*size_z*size_c*sizeof(T)),size_x,size_y,size_z,size_c);
9455         }
9456         std::memcpy(new_data,values,siz*sizeof(T));
9457         delete[] _data; _data = new_data; _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
9458       }
9459       return *this;
9460     }
9461 
9462     //! In-place version of a constructor.
9463     /**
9464        In-place version of the constructor CImg(const t*,unsigned int,unsigned int,unsigned int,unsigned int,bool).
9465        \param values : Pointer to the input memory buffer.
9466        \param size_x : Desired image width().
9467        \param size_y : Desired image height().
9468        \param size_z : Desired image depth().
9469        \param size_c : Desired image spectrum().
9470        \param is_shared : Flag telling if input memory buffer must be shared by the current instance.
9471        \note
9472        - It reinitializes the current image instance to a new constructed image instance.
9473        \sa assign(const t*,unsigned int,unsigned int,unsigned int,unsigned int).
9474            CImg(const t*,unsigned int,unsigned int,unsigned int,unsigned int,bool).
9475     **/
9476     template<typename t>
9477     CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y,
9478                     const unsigned int size_z, const unsigned int size_c, const bool is_shared) {
9479       if (is_shared)
9480         throw CImgArgumentException(_cimg_instance
9481                                     "assign() : Invalid assignment request of shared instance from (%s*) buffer"
9482                                     "(pixel types are different).",
9483                                     cimg_instance,
9484                                     CImg<t>::pixel_type());
9485       return assign(values,size_x,size_y,size_z,size_c);
9486     }
9487 
9488     //! In-place version of a constructor \specialization.
9489     CImg<T>& assign(const T *const values, const unsigned int size_x, const unsigned int size_y,
9490                     const unsigned int size_z, const unsigned int size_c, const bool is_shared) {
9491       const unsigned int siz = size_x*size_y*size_z*size_c;
9492       if (!values || !siz) {
9493         if (is_shared)
9494           throw CImgArgumentException(_cimg_instance
9495                                       "assign() : Invalid assignment request of shared instance from (null) or empty buffer.",
9496                                       cimg_instance);
9497         else return assign();
9498       }
9499       if (!is_shared) { if (_is_shared) assign(); assign(values,size_x,size_y,size_z,size_c); }
9500       else {
9501         if (!_is_shared) {
9502           if (values+siz<_data || values>=_data+size()) assign();
9503           else cimg::warn(_cimg_instance
9504                           "assign() : Shared image instance has overlapping memory.",
9505                           cimg_instance);
9506         }
9507         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = true;
9508         _data = const_cast<T*>(values);
9509       }
9510       return *this;
9511     }
9512 
9513     //! In-place version of a constructor.
9514     /**
9515        In-place version of the constructor CImg(const char*).
9516        \param filename : Input image filename.
9517        \note
9518        - It reinitializes the current image instance to a new constructed image instance.
9519        - Equivalent to load(const char*).
9520        \sa CImg(const char*),
9521            load(const char*).
9522     **/
9523     CImg<T>& assign(const char *const filename) {
9524       return load(filename);
9525     }
9526 
9527     //! In-place version of the default copy constructor.
9528     /**
9529        In-place version of the constructor CImg(const CImg<t>&).
9530        \param img : Input image to copy.
9531        \note
9532        - It reinitializes the current image instance to a new constructed image instance.
9533        \sa assign(const CImg<t>&,bool),
9534            CImg(const CImg<t>&).
9535     **/
9536     template<typename t>
9537     CImg<T>& assign(const CImg<t>& img) {
9538       return assign(img._data,img._width,img._height,img._depth,img._spectrum);
9539     }
9540 
9541     //! In-place version of the advanced copy constructor.
9542     /**
9543        In-place version of the constructor CImg(const CImg<t>&,bool).
9544        \param img : Input image to copy.
9545        \param is_shared : Desired shared state of the constructed copy.
9546        \sa assign(const CImg<t>&),
9547            CImg(const CImg<t>&,bool).
9548      **/
9549     template<typename t>
9550     CImg<T>& assign(const CImg<t>& img, const bool is_shared) {
9551       return assign(img._data,img._width,img._height,img._depth,img._spectrum,is_shared);
9552     }
9553 
9554     //! In-place version of a constructor.
9555     /**
9556        In-place version of the constructor CImg(const CImg<t>&,const char*).
9557        \param img : Input image from which dimensions are borrowed.
9558        \param dimensions : String describing the image size along the X,Y,Z and V-dimensions.
9559        \note
9560        - It reinitializes the current image instance to a new constructed image instance.
9561        \sa assign(const CImg<t>&,const char*,T),
9562            CImg(const CImg<t>&,const char*).
9563      **/
9564     template<typename t>
9565     CImg<T>& assign(const CImg<t>& img, const char *const dimensions) {
9566       if (!dimensions || !*dimensions) return assign(img._width,img._height,img._depth,img._spectrum);
9567       unsigned int siz[4] = { 0,1,1,1 }, k = 0;
9568       for (const char *s = dimensions; *s && k<4; ++k) {
9569         char item[256] = { 0 };
9570         if (std::sscanf(s,"%255[^0-9%xyzvwhdcXYZVWHDC]",item)>0) s+=std::strlen(item);
9571         if (*s) {
9572           unsigned int val = 0; char sep = 0;
9573           if (std::sscanf(s,"%u%c",&val,&sep)>0) {
9574             if (sep=='%') siz[k] = val*(k==0?_width:k==1?_height:k==2?_depth:_spectrum)/100;
9575             else siz[k] = val;
9576             while (*s>='0' && *s<='9') ++s; if (sep=='%') ++s;
9577           } else switch (cimg::uncase(*s)) {
9578           case 'x' : case 'w' : siz[k] = img._width; ++s; break;
9579           case 'y' : case 'h' : siz[k] = img._height; ++s; break;
9580           case 'z' : case 'd' : siz[k] = img._depth; ++s; break;
9581           case 'c' : case 's' : siz[k] = img._spectrum; ++s; break;
9582           default :
9583             throw CImgArgumentException(_cimg_instance
9584                                         "assign() : Invalid character '%c' detected in specified dimension string '%s'.",
9585                                         cimg_instance,
9586                                         *s,dimensions);
9587           }
9588         }
9589       }
9590       return assign(siz[0],siz[1],siz[2],siz[3]);
9591     }
9592 
9593     //! In-place version of a constructor.
9594     /**
9595        In-place version of the constructor CImg(const CImg<t>&,const char*,T).
9596        \param img : Input image from which dimensions are borrowed.
9597        \param dimensions : String describing the image size along the X,Y,Z and V-dimensions.
9598        \param value : Value for initialization.
9599        \note
9600        - It reinitializes the current image instance to a new constructed image instance.
9601        \sa assign(const CImg<t>&,const char*),
9602            CImg(const CImg<t>&,const char*,T).
9603      **/
9604     template<typename t>
9605     CImg<T>& assign(const CImg<t>& img, const char *const dimensions, const T value) {
9606       return assign(img,dimensions).fill(value);
9607     }
9608 
9609     //! In-place version of a constructor.
9610     /**
9611        In-place version of the constructor CImg(const CImgDisplay&).
9612        \param disp : Input \c CImgDisplay.
9613        \note
9614        - It reinitializes the current image instance to a new constructed image instance.
9615        \sa CImg(const CImgDisplay&).
9616     **/
9617     CImg<T>& assign(const CImgDisplay &disp) {
9618       disp.snapshot(*this);
9619       return *this;
9620     }
9621 
9622     //! In-place version of the default constructor.
9623     /**
9624        Equivalent to assign().
9625        \note
9626        - It has been defined for compatibility with STL naming conventions.
9627        \sa assign().
9628     **/
9629     CImg<T>& clear() {
9630       return assign();
9631     }
9632 
9633     //! Transfer content of an image instance into another one.
9634     /**
9635        Transfer the dimensions and the pixel buffer content of an image instance into another one,
9636        and replace instance by an empty image. It avoids the copy of the pixel buffer
9637        when possible.
9638        \param img : Destination image.
9639        \note
9640        - Pixel types \c T and \c t of source and destination images can be different, though the process is designed to be
9641          instantaneous when \c T and \c t are the same.
9642        \par Sample code :
9643        \code
9644        CImg<float> src(256,256,1,3,0), // Construct a 256x256x1x3 (color) image filled with value '0'.
9645                    dest(16,16);        // Construct a 16x16x1x1 (scalar) image.
9646        src.move_to(dest);              // Now, 'src' is empty and 'dest' is the 256x256x1x3 image.
9647        \endcode
9648        \sa move_to(CImgList<t>&,unsigned int),
9649            swap(CImg<T>&).
9650     **/
9651     template<typename t>
9652     CImg<t>& move_to(CImg<t>& img) {
9653       img.assign(*this);
9654       assign();
9655       return img;
9656     }
9657 
9658     //! Transfer content of an image instance into another one \specialization.
9659     CImg<T>& move_to(CImg<T>& img) {
9660       if (_is_shared || img._is_shared) img.assign(*this);
9661       else swap(img);
9662       assign();
9663       return img;
9664     }
9665 
9666     //! Transfer content of an image instance into a new image in an image list.
9667     /**
9668        Transfer the dimensions and the pixel buffer content of an image instance
9669        into a newly inserted image at position \c pos in specified \c CImgList<t> instance.
9670        \param list : Destination list.
9671        \param pos : Position of the newly inserted image in the list.
9672        \note
9673        - When optionnal parameter \c pos is ommited, the image instance is transfered as a new
9674          image at the end of the specified \c list.
9675        - It is convenient to sequentially insert new images into image lists, with no
9676          additional copies of memory buffer.
9677        \par Sample code :
9678        \code
9679        CImgList<float> list;             // Construct an empty image list.
9680        CImg<float> img("reference.jpg"); // Read image from filename.
9681        img.move_to(list);                // Transfer image content as a new item in the list (no buffer copy).
9682        \endcode
9683        \sa move_to(CImg<t>&),
9684            swap(CImg<T>&).
9685     **/
9686     template<typename t>
9687     CImgList<t>& move_to(CImgList<t>& list, const unsigned int pos=~0U) {
9688       const unsigned int npos = pos>list._width?list._width:pos;
9689       move_to(list.insert(1,npos)[npos]);
9690       return list;
9691     }
9692 
9693     //! Swap fields of two image instances.
9694     /**
9695       \param img : Image to swap fields with.
9696       \note
9697       - It can be used to interchange the content of two images in a very fast way. Can be convenient when dealing
9698         with algorithms requiring two swapping buffers.
9699       \par Sample code :
9700       \code
9701       CImg<float> img1("lena.jpg"),
9702                   img2("milla.jpg");
9703       img1.swap(img2);               // Now, 'img1' is 'milla' and 'img2' is 'lena'.
9704       \endcode
9705     **/
9706     CImg<T>& swap(CImg<T>& img) {
9707       cimg::swap(_width,img._width);
9708       cimg::swap(_height,img._height);
9709       cimg::swap(_depth,img._depth);
9710       cimg::swap(_spectrum,img._spectrum);
9711       cimg::swap(_data,img._data);
9712       cimg::swap(_is_shared,img._is_shared);
9713       return img;
9714     }
9715 
9716     //! Get a reference to an empty image.
9717     /**
9718        \note
9719        This function is useful mainly to declare optional parameters having type \c CImg<T> in functions prototypes, e.g.
9720        \code
9721        void f(const int x=0, const int y=0, const CImg<float>& img=CImg<float>::empty());
9722        \endcode
9723      **/
9724     static CImg<T>& empty() {
9725       static CImg<T> _empty;
9726       return _empty.assign();
9727     }
9728 
9729     //@}
9730     //------------------------------------------
9731     //
9732     //! \name Overloaded Operators
9733     //@{
9734     //------------------------------------------
9735 
9736     //! Access to a pixel value.
9737     /**
9738        Return a reference to a located pixel value of the image instance,
9739        being possibly \e const, whether the image instance is \e const or not.
9740        This is the standard method to get/set pixel values in \c CImg<T> images.
9741        \param x : X-coordinate of the pixel value.
9742        \param y : Y-coordinate of the pixel value.
9743        \param z : Z-coordinate of the pixel value.
9744        \param c : C-coordinate of the pixel value.
9745        \note
9746        - Range of pixel coordinates start from <tt>(0,0,0,0)</tt> to <tt>(width()-1,height()-1,depth()-1,spectrum()-1)</tt>.
9747        - Due to the particular arrangement of the pixel buffers defined in %CImg, you can omit one coordinate if the corresponding dimension
9748          is equal to \c 1.
9749          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>.
9750        \warning
9751        - There is \e no boundary checking done in this operator, to make it as fast as possible.
9752          You \e must take care of out-of-bounds access by yourself, if necessary.
9753          For debuging purposes, you may want to define macro \c 'cimg_verbosity'>=3 to enable additional boundary checking operations
9754          in this operator. In that case, warning messages will be printed on the error output when accessing out-of-bounds pixels.
9755        \par Sample code :
9756        \code
9757        CImg<float> img(100,100,1,3,0);                   // Construct a 100x100x1x3 (color) image with pixels set to '0'.
9758        const float
9759           valR = img(10,10,0,0),                         // Read red value at coordinates (10,10).
9760           valG = img(10,10,0,1),                         // Read green value at coordinates (10,10)
9761           valB = img(10,10,2),                           // Read blue value at coordinates (10,10) (Z-coordinate can be omitted).
9762           avg = (valR + valG + valB)/3;                  // Compute average pixel value.
9763        img(10,10,0) = img(10,10,1) = img(10,10,2) = avg; // Replace the color pixel (10,10) by the average grey value.
9764        \endcode
9765        \sa at(),
9766            atX(),
9767            atXY(),
9768            atXYZ(),
9769            atXYZC().
9770     **/
9771 #if cimg_verbosity>=3
9772     T& operator()(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
9773       const unsigned int off = (unsigned int)offset(x,y,z,c);
9774       if (!_data || off>=size()) {
9775         cimg::warn(_cimg_instance
9776                    "operator() : Invalid pixel request, at coordinates (%u,%u,%u,%u) [offset=%u].",
9777                    cimg_instance,
9778                    x,y,z,c,off);
9779         return *_data;
9780       }
9781       else return _data[off];
9782     }
9783 
9784     //! Access to a pixel value \const.
9785     const T& operator()(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
9786       return const_cast<CImg<T>*>(this)->operator()(x,y,z,c);
9787     }
9788 
9789     //! Access to a pixel value.
9790     /**
9791        \param x : X-coordinate of the pixel value.
9792        \param y : Y-coordinate of the pixel value.
9793        \param z : Z-coordinate of the pixel value.
9794        \param c : C-coordinate of the pixel value.
9795        \param wh : Precomputed offset, must be equal to <tt>width()*\ref height()</tt>.
9796        \param whd : Precomputed offset, must be equal to <tt>width()*\ref height()*\ref depth()</tt>.
9797        \note
9798        - Similar to (but faster than) operator()().
9799          It uses precomputed offsets to optimize memory access. You may use it to optimize
9800          the reading/writing of several pixel values in the same image (e.g. in a loop).
9801        \sa operator()().
9802      **/
9803     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
9804                   const unsigned long wh, const unsigned long whd=0) {
9805       cimg::unused(wh,whd);
9806       return (*this)(x,y,z,c);
9807     }
9808 
9809     //! Access to a pixel value \const.
9810     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
9811                         const unsigned long wh, const unsigned long whd=0) const {
9812       cimg::unused(wh,whd);
9813       return (*this)(x,y,z,c);
9814     }
9815 #else
9816     T& operator()(const unsigned int x) {
9817       return _data[x];
9818     }
9819 
9820     const T& operator()(const unsigned int x) const {
9821       return _data[x];
9822     }
9823 
9824     T& operator()(const unsigned int x, const unsigned int y) {
9825       return _data[x + y*_width];
9826     }
9827 
9828     const T& operator()(const unsigned int x, const unsigned int y) const {
9829       return _data[x + y*_width];
9830     }
9831 
9832     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) {
9833       return _data[x + y*_width + z*_width*_height];
9834    }
9835 
9836     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) const {
9837       return _data[x + y*_width + z*_width*_height];
9838     }
9839 
9840     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) {
9841       return _data[x + y*_width + z*_width*_height + c*_width*_height*_depth];
9842     }
9843 
9844     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) const {
9845       return _data[x + y*_width + z*_width*_height + c*_width*_height*_depth];
9846     }
9847 
9848     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int,
9849                   const unsigned long wh) {
9850       return _data[x + y*_width + z*wh];
9851     }
9852 
9853     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int,
9854                         const unsigned long wh) const {
9855       return _data[x + y*_width + z*wh];
9856     }
9857 
9858     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
9859                   const unsigned long wh, const unsigned long whd) {
9860       return _data[x + y*_width + z*wh + c*whd];
9861     }
9862 
9863     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
9864                         const unsigned long wh, const unsigned long whd) const {
9865       return _data[x + y*_width + z*wh + c*whd];
9866     }
9867 #endif
9868 
9869     //! Implicitely cast an image into a \c T*.
9870     /**
9871        Implicitely cast a \c CImg<T> instance into a \c T* or \c const \c T* pointer, whether the image instance
9872        is \e const or not. The returned pointer points on the first value of the image pixel buffer.
9873        \note
9874        - It simply returns the pointer data() to the pixel buffer.
9875        - This implicit conversion is convenient to test the empty state of images (data() being \c 0 in this case), e.g.
9876        \code
9877        CImg<float> img1(100,100), img2; // 'img1' is a 100x100 image, 'img2' is an empty image.
9878        if (img1) {                      // Test succeeds, 'img1' is not an empty image.
9879          if (!img2) {                   // Test succeeds, 'img2' is an empty image.
9880            std::printf("'img1' is not empty, 'img2' is empty.");
9881          }
9882        }
9883        \endcode
9884        - It also allows to use brackets to access pixel values, without need for a \c CImg<T>::operator[](), e.g.
9885        \code
9886        CImg<float> img(100,100);
9887        const float value = img[99]; // Access to value of the last pixel on the first line.
9888        img[510] = 255;              // Set pixel value at (10,5).
9889        \endcode
9890        \sa operator()().
9891     **/
9892     operator T*() {
9893       return _data;
9894     }
9895 
9896     //! Implicitely cast an image into a \c T* \const.
9897     operator const T*() const {
9898       return _data;
9899     }
9900 
9901     //! Assign a value to all image pixels.
9902     /**
9903        Assign specified \c value to each pixel value of the image instance.
9904        \param value : Value that will be assigned to image pixels.
9905        \note
9906        - The image size is never modified.
9907        - The \c value may be casted to pixel type \c T if necessary.
9908        \par Sample code
9909        \code
9910        CImg<char> img(100,100); // Declare image (with garbage values).
9911        img = 0;                 // Set all pixel values to '0'.
9912        img = 1.2;               // Set all pixel values to '1' (cast of '1.2' as a 'char').
9913        \endcode
9914        \sa fill(const T).
9915     **/
9916     CImg<T>& operator=(const T value) {
9917       return fill(value);
9918     }
9919 
9920     //! Assign pixels values from a specified expression.
9921     /**
9922        Initialize all pixel values from the specified string \c expression.
9923        \param expression : Value string describing the way pixel values are set.
9924        \note
9925        - String parameter \c expression may describe different things :
9926          - 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"),
9927            the pixel values are set from specified \c expression and the image size is not modified.
9928          - If \c expression is a filename (as in \c "reference.jpg"), the corresponding image file is loaded and replace the image instance.
9929            The image size is modified if necessary.
9930        \par Sample code :
9931        \code
9932        CImg<float> img1(100,100), img2(img1), img3(img1); // Declare three 100x100 scalar images with unitialized pixel values.
9933        img1 = "0,50,100,150,200,250,200,150,100,50";      // Set pixel values of 'img1' from a value sequence.
9934        img2 = "10*((x*y)%25)";                            // Set pixel values of 'img2' from a formula.
9935        img3 = "reference.jpg";                            // Set pixel values of 'img3' from a file (image size is modified).
9936        (img1,img2,img3).display();
9937        \endcode
9938        \image html ref_operator_eq.jpg
9939        \sa fill(const char*, bool),
9940            load(const char*).
9941     **/
9942     CImg<T>& operator=(const char *const expression) {
9943       const unsigned int omode = cimg::exception_mode();
9944       cimg::exception_mode() = 0;
9945       try {
9946         fill(expression,true);
9947       } catch (CImgException&) {
9948         cimg::exception_mode() = omode;
9949         load(expression);
9950       }
9951       cimg::exception_mode() = omode;
9952       return *this;
9953     }
9954 
9955     //! Copy an image into the current image instance.
9956     /**
9957        Similar to the in-place copy constructor assign(const CImg<t>&).
9958     **/
9959     template<typename t>
9960     CImg<T>& operator=(const CImg<t>& img) {
9961       return assign(img);
9962     }
9963 
9964     //! Copy an image into the current image instance \specialization.
9965     CImg<T>& operator=(const CImg<T>& img) {
9966       return assign(img);
9967     }
9968 
9969     //! Copy the content of a display window to the current image instance.
9970     /**
9971        Similar to assign(const CImgDisplay&).
9972     **/
9973     CImg<T>& operator=(const CImgDisplay& disp) {
9974       disp.snapshot(*this);
9975       return *this;
9976     }
9977 
9978     //! In-place addition operator.
9979     /**
9980        Add specified \c value to all pixels of an image instance.
9981        \param value : Value to add.
9982        \note
9983        - 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.
9984        - Overflow values are treated as with standard C++ numeric types. For instance,
9985        \code
9986        CImg<unsigned char> img(100,100,1,1,255); // Construct a 100x100 image with pixel values '255'.
9987        img+=1;                                   // Add '1' to each pixels -> Overflow.
9988        // here all pixels of image 'img' are equal to '0'.
9989        \endcode
9990        - To prevent value overflow, you may want to consider pixel type \c T as \c float or \c double, and use cut() after addition.
9991        \par Sample code :
9992        \code
9993        CImg<unsigned char> img1("reference.jpg");          // Load a 8-bits RGB image (values in [0,255]).
9994        CImg<float> img2(img1);                             // Construct a float-valued copy of 'img1'.
9995        img2+=100;                                          // Add '100' to pixel values -> goes out of [0,255] but no problems with floats.
9996        img2.cut(0,255);                                    // Cut values in [0,255] to fit the 'unsigned char' constraint.
9997        img1 = img2;                                        // Rewrite safe result in 'unsigned char' version 'img1'.
9998        const CImg<unsigned char> img3 = (img1 + 100).cut(0,255); // Do the same in a more simple and elegant way.
9999        (img1,img2,img3).display();
10000        \endcode
10001        \image html ref_operator_plus.jpg
10002        \sa operator+(const t) const,
10003            operator-=(const t),
10004            operator*=(const t),
10005            operator/=(const t),
10006            operator%=(const t),
10007            operator&=(const t),
10008            operator|=(const t),
10009            operator^=(const t),
10010            operator<<=(const t),
10011            operator>>=(const t).
10012      **/
10013     template<typename t>
10014     CImg<T>& operator+=(const t value) {
10015       cimg_for(*this,ptrd,T) *ptrd = (T)(*ptrd + value);
10016       return *this;
10017     }
10018 
10019     //! In-place addition operator.
10020     /**
10021        Add values to image pixels, according to the specified string \c expression.
10022        \param expression : Value string describing the way pixel values are added.
10023        \note
10024        - Similar to operator=(const char*), except that it adds values to the pixels of the current image instance,
10025          instead of assigning them.
10026        \sa operator+=(const t),
10027            operator=(const char*),
10028            operator+(const char*) const,
10029            operator-=(const char*),
10030            operator*=(const char*),
10031            operator/=(const char*),
10032            operator%=(const char*),
10033            operator&=(const char*),
10034            operator|=(const char*),
10035            operator^=(const char*),
10036            operator<<=(const char*),
10037            operator>>=(const char*).
10038     **/
10039     CImg<T>& operator+=(const char *const expression) {
10040       const unsigned int omode = cimg::exception_mode();
10041       cimg::exception_mode() = 0;
10042       try {
10043         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
10044         _cimg_math_parser mp(base,expression,"operator+=");
10045         T *ptrd = _data;
10046         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd + mp.eval(x,y,z,c)); ++ptrd; }
10047       } catch (CImgException&) {
10048         cimg::exception_mode() = omode;
10049         *this+=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
10050       }
10051       cimg::exception_mode() = omode;
10052       return *this;
10053     }
10054 
10055     //! In-place addition operator.
10056     /**
10057        Add values to image pixels, according to the values of the input image \c img.
10058        \param img : Input image to add.
10059        \note
10060        - The size of the image instance is never modified.
10061        - It is not mandatory that input image \c img has the same size as the image instance. If less values are available
10062          in \c img, then the values are added cyclically. For instance, adding one WxH scalar image (spectrum() equal to \c 1) to
10063          one WxH color image (spectrum() equal to \c 3) means each color channel will be incremented with the same values at the same
10064          locations.
10065        \par Sample code :
10066        \code
10067        CImg<float> img1("reference.jpg");                                   // Load a RGB color image (img1.spectrum()==3)
10068        const CImg<float> img2(img1.width(),img.height(),1,1,"255*(x/w)^2"); // Construct a scalar shading (img2.spectrum()==1).
10069        img1+=img2;                                                          // Add shading to each channel of 'img1'.
10070        img1.cut(0,255);                                                     // Prevent [0,255] overflow.
10071        (img2,img1).display();
10072        \endcode
10073        \image html ref_operator_plus1.jpg
10074        \sa operator+(const CImg<t>&) const,
10075            operator=(const CImg<t>&),
10076            operator-=(const CImg<t>&),
10077            operator*=(const CImg<t>&),
10078            operator/=(const CImg<t>&),
10079            operator%=(const CImg<t>&),
10080            operator&=(const CImg<t>&),
10081            operator|=(const CImg<t>&),
10082            operator^=(const CImg<t>&),
10083            operator<<=(const CImg<t>&),
10084            operator>>=(const CImg<t>&).
10085     **/
10086     template<typename t>
10087     CImg<T>& operator+=(const CImg<t>& img) {
10088       const unsigned int siz = size(), isiz = img.size();
10089       if (siz && isiz) {
10090         if (is_overlapped(img)) return *this+=+img;
10091         T *ptrd = _data, *const ptre = _data + siz;
10092         if (siz>isiz) for (unsigned int n = siz/isiz; n; --n)
10093           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)(*ptrd + *(ptrs++));
10094         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd + *(ptrs++));
10095       }
10096       return *this;
10097     }
10098 
10099     //! In-place increment operator (prefix).
10100     /**
10101        Add \c 1 to all image pixels, and return a reference to the current incremented image instance.
10102        \note
10103        - Writing \c ++img is equivalent to \c img+=1.
10104        \sa operator++(int),
10105            operator--().
10106      **/
10107     CImg<T>& operator++() {
10108       cimg_for(*this,ptrd,T) ++*ptrd;
10109       return *this;
10110     }
10111 
10112     //! In-place increment operator (postfix).
10113     /**
10114        Add \c 1 to all image pixels, and return a new copy of the initial (pre-incremented) image instance.
10115        \note
10116        - Use the prefixed version operator++() if you don't need a copy of the initial (pre-incremented) image instance, since
10117          a useless image copy may be expensive in terms of memory usage.
10118        \sa operator++(),
10119            operator--(int).
10120      **/
10121     CImg<T> operator++(int) {
10122       const CImg<T> copy(*this,false);
10123       ++*this;
10124       return copy;
10125     }
10126 
10127     //! Get a non-shared copy of the image instance.
10128     /**
10129        \note
10130        - Use this operator to ensure you get a non-shared copy of an image instance with same pixel type \c T.
10131          Indeed, the usual copy constructor CImg<T>(const CImg<T>&) returns a shared copy of a shared input image, and it may be
10132          not desirable to work on a regular copy (e.g. for a resize operation) if you have no informations about the shared state
10133          of the input image.
10134        - Writing \c (+img) is equivalent to \c CImg<T>(img,false).
10135        \sa CImg(const CImg<T>&),
10136            CImg(const CImg<T>&,bool),
10137            operator-() const,
10138            operator~() const.
10139     **/
10140     CImg<T> operator+() const {
10141       return CImg<T>(*this,false);
10142     }
10143 
10144     //! Addition operator.
10145     /**
10146        Similar to operator+=(const t), except that it returns a new image instance instead of operating in-place.
10147        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10148      **/
10149     template<typename t>
10150     CImg<_cimg_Tt> operator+(const t value) const {
10151       return CImg<_cimg_Tt>(*this,false)+=value;
10152     }
10153 
10154     //! Addition operator.
10155     /**
10156        Similar to operator+=(const char*), except that it returns a new image instance instead of operating in-place.
10157        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10158      **/
10159     CImg<Tfloat> operator+(const char *const expression) const {
10160       return CImg<Tfloat>(*this,false)+=expression;
10161     }
10162 
10163     //! Addition operator.
10164     /**
10165        Similar to operator+=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
10166        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10167      **/
10168     template<typename t>
10169     CImg<_cimg_Tt> operator+(const CImg<t>& img) const {
10170       return CImg<_cimg_Tt>(*this,false)+=img;
10171     }
10172 
10173     //! In-place substraction operator.
10174     /**
10175        Similar to operator+=(const t), except that it performs a substraction instead of an addition.
10176      **/
10177     template<typename t>
10178     CImg<T>& operator-=(const t value) {
10179       cimg_for(*this,ptrd,T) *ptrd = (T)(*ptrd - value);
10180       return *this;
10181     }
10182 
10183     //! In-place substraction operator.
10184     /**
10185        Similar to operator+=(const char*), except that it performs a substraction instead of an addition.
10186      **/
10187     CImg<T>& operator-=(const char *const expression) {
10188       const unsigned int omode = cimg::exception_mode();
10189       cimg::exception_mode() = 0;
10190       try {
10191         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
10192         _cimg_math_parser mp(base,expression,"operator-=");
10193         T *ptrd = _data;
10194         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd - mp.eval(x,y,z,c)); ++ptrd; }
10195       } catch (CImgException&) {
10196         cimg::exception_mode() = omode;
10197         *this-=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
10198       }
10199       cimg::exception_mode() = omode;
10200       return *this;
10201     }
10202 
10203     //! In-place substraction operator.
10204     /**
10205        Similar to operator+=(const CImg<t>&), except that it performs a substraction instead of an addition.
10206      **/
10207     template<typename t>
10208     CImg<T>& operator-=(const CImg<t>& img) {
10209       const unsigned int siz = size(), isiz = img.size();
10210       if (siz && isiz) {
10211         if (is_overlapped(img)) return *this-=+img;
10212         T *ptrd = _data, *const ptre = _data + siz;
10213         if (siz>isiz) for (unsigned int n = siz/isiz; n; --n)
10214           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)(*ptrd - *(ptrs++));
10215         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd - *(ptrs++));
10216       }
10217       return *this;
10218     }
10219 
10220     //! In-place decrement operator (prefix).
10221     /**
10222        Similar to operator++(), except that it performs a decrement instead of an increment.
10223     **/
10224     CImg<T>& operator--() {
10225       cimg_for(*this,ptrd,T) *ptrd = *ptrd-(T)1;
10226       return *this;
10227     }
10228 
10229     //! In-place decrement operator (postfix).
10230     /**
10231        Similar to operator++(int), except that it performs a decrement instead of an increment.
10232     **/
10233     CImg<T> operator--(int) {
10234       const CImg<T> copy(*this,false);
10235       --*this;
10236       return copy;
10237     }
10238 
10239     //! Replace each pixel by its opposite value.
10240     /**
10241        \note
10242        - If the computed opposite values are out-of-range, they are treated as with standard C++ numeric types. For instance,
10243          the \c unsigned \c char opposite of \c 1 is \c 255.
10244        \par Sample code :
10245        \code
10246        const CImg<unsigned char>
10247          img1("reference.jpg"),   // Load a RGB color image.
10248          img2 = -img1;            // Compute its opposite (in 'unsigned char').
10249        (img1,img2).display();
10250        \endcode
10251        \image html ref_operator_minus.jpg
10252        \sa operator+(),
10253            operator~().
10254      **/
10255     CImg<T> operator-() const {
10256       return CImg<T>(_width,_height,_depth,_spectrum,(T)0)-=*this;
10257     }
10258 
10259     //! Substraction operator.
10260     /**
10261        Similar to operator-=(const t), except that it returns a new image instance instead of operating in-place.
10262        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10263     **/
10264     template<typename t>
10265     CImg<_cimg_Tt> operator-(const t value) const {
10266       return CImg<_cimg_Tt>(*this,false)-=value;
10267     }
10268 
10269     //! Substraction operator.
10270     /**
10271        Similar to operator-=(const char*), except that it returns a new image instance instead of operating in-place.
10272        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10273     **/
10274     CImg<Tfloat> operator-(const char *const expression) const {
10275       return CImg<Tfloat>(*this,false)-=expression;
10276     }
10277 
10278     //! Substraction operator.
10279     /**
10280        Similar to operator-=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
10281        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10282     **/
10283     template<typename t>
10284     CImg<_cimg_Tt> operator-(const CImg<t>& img) const {
10285       return CImg<_cimg_Tt>(*this,false)-=img;
10286     }
10287 
10288     //! In-place multiplication operator.
10289     /**
10290        Similar to operator+=(const t), except that it performs a multiplication instead of an addition.
10291      **/
10292     template<typename t>
10293     CImg<T>& operator*=(const t value) {
10294       cimg_for(*this,ptrd,T) *ptrd = (T)(*ptrd * value);
10295       return *this;
10296     }
10297 
10298     //! In-place multiplication operator.
10299     /**
10300        Similar to operator+=(const char*), except that it performs a multiplication instead of an addition.
10301      **/
10302     CImg<T>& operator*=(const char *const expression) {
10303       const unsigned int omode = cimg::exception_mode();
10304       cimg::exception_mode() = 0;
10305       try {
10306         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
10307         _cimg_math_parser mp(base,expression,"operator*=");
10308         T *ptrd = _data;
10309         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd * mp.eval(x,y,z,c)); ++ptrd; }
10310       } catch (CImgException&) {
10311         cimg::exception_mode() = omode;
10312         mul(CImg<T>(_width,_height,_depth,_spectrum,expression,true));
10313       }
10314       cimg::exception_mode() = omode;
10315       return *this;
10316     }
10317 
10318     //! In-place multiplication operator.
10319     /**
10320        Replace the image instance by the matrix multiplication between the image instance and the specified matrix \c img.
10321        \param img : Second operand of the matrix multiplication.
10322        \note
10323        - It does \e not compute a pointwise multiplication between two images. For this purpose, use mul(const CImg<t>&) instead.
10324        - The size of the image instance can be modified by this operator.
10325        \par Sample code :
10326        \code
10327        CImg<float> A(2,2,1,1, 1,2,3,4);   // Construct 2x2 matrix A = [1,2;3,4].
10328        const CImg<float> X(1,2,1,1, 1,2); // Construct 1x2 vector X = [1;2].
10329        A*=X;                              // Assign matrix multiplication A*X to 'A'.
10330        // 'A' is now a 1x2 vector whose values are [5;11].
10331        \endcode
10332        \sa operator*(const CImg<t>&) const,
10333            mul().
10334     **/
10335     template<typename t>
10336     CImg<T>& operator*=(const CImg<t>& img) {
10337       return ((*this)*img).move_to(*this);
10338     }
10339 
10340     //! Multiplication operator.
10341     /**
10342        Similar to operator*=(const t), except that it returns a new image instance instead of operating in-place.
10343        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10344     **/
10345     template<typename t>
10346     CImg<_cimg_Tt> operator*(const t value) const {
10347       return CImg<_cimg_Tt>(*this,false)*=value;
10348     }
10349 
10350     //! Multiplication operator.
10351     /**
10352        Similar to operator*=(const char*), except that it returns a new image instance instead of operating in-place.
10353        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10354     **/
10355     CImg<Tfloat> operator*(const char *const expression) const {
10356       return CImg<Tfloat>(*this,false)*=expression;
10357     }
10358 
10359     //! Multiplication operator.
10360     /**
10361        Similar to operator*=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
10362        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10363     **/
10364     template<typename t>
10365     CImg<_cimg_Tt> operator*(const CImg<t>& img) const {
10366       if (_width!=img._height || _depth!=1 || _spectrum!=1)
10367         throw CImgArgumentException(_cimg_instance
10368                                     "operator*() : Invalid multiplication of instance by specified matrix (%u,%u,%u,%u,%p)",
10369                                     cimg_instance,
10370                                     img._width,img._height,img._depth,img._spectrum,img._data);
10371 
10372       CImg<_cimg_Tt> res(img._width,_height);
10373       _cimg_Tt value, *ptrd = res._data;
10374 #ifdef cimg_use_openmp
10375 #pragma omp parallel for if (size()>=1000 && img.size()>=1000) private(value)
10376 #endif
10377       cimg_forXY(res,i,j) { value = 0; cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k); *(ptrd++) = value; }
10378       return res;
10379     }
10380 
10381     //! In-place division operator.
10382     /**
10383        Similar to operator+=(const t), except that it performs a division instead of an addition.
10384      **/
10385     template<typename t>
10386     CImg<T>& operator/=(const t value) {
10387       cimg_for(*this,ptrd,T) *ptrd = (T)(*ptrd / value);
10388       return *this;
10389     }
10390 
10391     //! In-place division operator.
10392     /**
10393        Similar to operator+=(const char*), except that it performs a division instead of an addition.
10394      **/
10395     CImg<T>& operator/=(const char *const expression) {
10396       const unsigned int omode = cimg::exception_mode();
10397       cimg::exception_mode() = 0;
10398       try {
10399         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
10400         _cimg_math_parser mp(base,expression,"operator/=");
10401         T *ptrd = _data;
10402         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd / mp.eval(x,y,z,c)); ++ptrd; }
10403       } catch (CImgException&) {
10404         cimg::exception_mode() = omode;
10405         div(CImg<T>(_width,_height,_depth,_spectrum,expression,true));
10406       }
10407       cimg::exception_mode() = omode;
10408       return *this;
10409     }
10410 
10411     //! In-place division operator.
10412     /**
10413        Replace the image instance by the (right) matrix division between the image instance and the specified matrix \c img.
10414        \param img : Second operand of the matrix division.
10415        \note
10416        - It does \e not compute a pointwise division between two images. For this purpose, use div(const CImg<t>&) instead.
10417        - It returns the matrix operation \c A*inverse(img).
10418        - The size of the image instance can be modified by this operator.
10419        \sa operator/(const CImg<t>&) const,
10420            operator*(const CImg<t>&) const,
10421            div().
10422      **/
10423     template<typename t>
10424     CImg<T>& operator/=(const CImg<t>& img) {
10425       return (*this*img.get_invert()).move_to(*this);
10426     }
10427 
10428     //! Division operator.
10429     /**
10430        Similar to operator/=(const t), except that it returns a new image instance instead of operating in-place.
10431        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10432     **/
10433     template<typename t>
10434     CImg<_cimg_Tt> operator/(const t value) const {
10435       return CImg<_cimg_Tt>(*this,false)/=value;
10436     }
10437 
10438     //! Division operator.
10439     /**
10440        Similar to operator/=(const char*), except that it returns a new image instance instead of operating in-place.
10441        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10442     **/
10443     CImg<Tfloat> operator/(const char *const expression) const {
10444       return CImg<Tfloat>(*this,false)/=expression;
10445     }
10446 
10447     //! Division operator.
10448     /**
10449        Similar to operator/=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
10450        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10451     **/
10452     template<typename t>
10453     CImg<_cimg_Tt> operator/(const CImg<t>& img) const {
10454       return (*this)*img.get_invert();
10455     }
10456 
10457     //! In-place modulo operator.
10458     /**
10459        Similar to operator+=(const t), except that it performs a modulo operation instead of an addition.
10460     **/
10461     template<typename t>
10462     CImg<T>& operator%=(const t value) {
10463       cimg_for(*this,ptrd,T) *ptrd = (T)cimg::mod(*ptrd,(T)value);
10464       return *this;
10465     }
10466 
10467     //! In-place modulo operator.
10468     /**
10469        Similar to operator+=(const char*), except that it performs a modulo operation instead of an addition.
10470     **/
10471     CImg<T>& operator%=(const char *const expression) {
10472       const unsigned int omode = cimg::exception_mode();
10473       cimg::exception_mode() = 0;
10474       try {
10475         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
10476         _cimg_math_parser mp(base,expression,"operator%=");
10477         T *ptrd = _data;
10478         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::mod(*ptrd,(T)mp.eval(x,y,z,c)); ++ptrd; }
10479       } catch (CImgException&) {
10480         cimg::exception_mode() = omode;
10481         *this%=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
10482       }
10483       cimg::exception_mode() = omode;
10484       return *this;
10485     }
10486 
10487     //! In-place modulo operator.
10488     /**
10489        Similar to operator+=(const CImg<t>&), except that it performs a modulo operation instead of an addition.
10490     **/
10491     template<typename t>
10492     CImg<T>& operator%=(const CImg<t>& img) {
10493       const unsigned int 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 int n = siz/isiz; n; --n)
10498           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = cimg::mod(*ptrd,(T)*(ptrs++));
10499         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::mod(*ptrd,(T)*(ptrs++));
10500       }
10501       return *this;
10502     }
10503 
10504     //! Modulo operator.
10505     /**
10506        Similar to operator%=(const t), except that it returns a new image instance instead of operating in-place.
10507        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10508     **/
10509     template<typename t>
10510     CImg<_cimg_Tt> operator%(const t value) const {
10511       return CImg<_cimg_Tt>(*this,false)%=value;
10512     }
10513 
10514     //! Modulo operator.
10515     /**
10516        Similar to operator%=(const char*), except that it returns a new image instance instead of operating in-place.
10517        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10518     **/
10519     CImg<Tfloat> operator%(const char *const expression) const {
10520       return CImg<Tfloat>(*this,false)%=expression;
10521     }
10522 
10523     //! Modulo operator.
10524     /**
10525        Similar to operator%=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
10526        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
10527     **/
10528     template<typename t>
10529     CImg<_cimg_Tt> operator%(const CImg<t>& img) const {
10530       return CImg<_cimg_Tt>(*this,false)%=img;
10531     }
10532 
10533     //! In-place bitwise AND operator.
10534     /**
10535        Similar to operator+=(const t), except that it performs a bitwise AND operation instead of an addition.
10536     **/
10537     template<typename t>
10538     CImg<T>& operator&=(const t value) {
10539       cimg_for(*this,ptrd,T) *ptrd = (T)((unsigned long)*ptrd & (unsigned long)value);
10540       return *this;
10541     }
10542 
10543     //! In-place bitwise AND operator.
10544     /**
10545        Similar to operator+=(const char*), except that it performs a bitwise AND operation instead of an addition.
10546     **/
10547     CImg<T>& operator&=(const char *const expression) {
10548       const unsigned int omode = cimg::exception_mode();
10549       cimg::exception_mode() = 0;
10550       try {
10551         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
10552         _cimg_math_parser mp(base,expression,"operator&=");
10553         T *ptrd = _data;
10554         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd & (unsigned long)mp.eval(x,y,z,c)); ++ptrd; }
10555       } catch (CImgException&) {
10556         cimg::exception_mode() = omode;
10557         *this&=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
10558       }
10559       cimg::exception_mode() = omode;
10560       return *this;
10561     }
10562 
10563     //! In-place bitwise AND operator.
10564     /**
10565        Similar to operator+=(const CImg<t>&), except that it performs a bitwise AND operation instead of an addition.
10566     **/
10567     template<typename t>
10568     CImg<T>& operator&=(const CImg<t>& img) {
10569       const unsigned int siz = size(), isiz = img.size();
10570       if (siz && isiz) {
10571         if (is_overlapped(img)) return *this&=+img;
10572         T *ptrd = _data, *const ptre = _data + siz;
10573         if (siz>isiz) for (unsigned int n = siz/isiz; n; --n)
10574           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)((unsigned long)*ptrd & (unsigned long)*(ptrs++));
10575         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((unsigned long)*ptrd & (unsigned long)*(ptrs++));
10576       }
10577       return *this;
10578     }
10579 
10580     //! Bitwise AND operator.
10581     /**
10582        Similar to operator&=(const t), except that it returns a new image instance instead of operating in-place.
10583        The pixel type of the returned image is \c T.
10584     **/
10585     template<typename t>
10586     CImg<T> operator&(const t value) const {
10587       return (+*this)&=value;
10588     }
10589 
10590     //! Bitwise AND operator.
10591     /**
10592        Similar to operator&=(const char*), except that it returns a new image instance instead of operating in-place.
10593        The pixel type of the returned image is \c T.
10594     **/
10595     CImg<T> operator&(const char *const expression) const {
10596       return (+*this)&=expression;
10597     }
10598 
10599     //! Bitwise AND operator.
10600     /**
10601        Similar to operator&=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
10602        The pixel type of the returned image is \c T.
10603     **/
10604     template<typename t>
10605     CImg<T> operator&(const CImg<t>& img) const {
10606       return (+*this)&=img;
10607     }
10608 
10609     //! In-place bitwise OR operator.
10610     /**
10611        Similar to operator+=(const t), except that it performs a bitwise OR operation instead of an addition.
10612     **/
10613     template<typename t>
10614     CImg<T>& operator|=(const t value) {
10615       cimg_for(*this,ptrd,T) *ptrd = (T)((unsigned long)*ptrd | (unsigned long)value);
10616       return *this;
10617     }
10618 
10619     //! In-place bitwise OR operator.
10620     /**
10621        Similar to operator+=(const char*), except that it performs a bitwise OR operation instead of an addition.
10622     **/
10623     CImg<T>& operator|=(const char *const expression) {
10624       const unsigned int omode = cimg::exception_mode();
10625       cimg::exception_mode() = 0;
10626       try {
10627         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
10628         _cimg_math_parser mp(base,expression,"operator|=");
10629         T *ptrd = _data;
10630         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd | (unsigned long)mp.eval(x,y,z,c)); ++ptrd; }
10631       } catch (CImgException&) {
10632         cimg::exception_mode() = omode;
10633         *this|=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
10634       }
10635       cimg::exception_mode() = omode;
10636       return *this;
10637     }
10638 
10639     //! In-place bitwise OR operator.
10640     /**
10641        Similar to operator+=(const CImg<t>&), except that it performs a bitwise OR operation instead of an addition.
10642     **/
10643     template<typename t>
10644     CImg<T>& operator|=(const CImg<t>& img) {
10645       const unsigned int siz = size(), isiz = img.size();
10646       if (siz && isiz) {
10647         if (is_overlapped(img)) return *this|=+img;
10648         T *ptrd = _data, *const ptre = _data + siz;
10649         if (siz>isiz) for (unsigned int n = siz/isiz; n; --n)
10650           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)((unsigned long)*ptrd | (unsigned long)*(ptrs++));
10651         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((unsigned long)*ptrd | (unsigned long)*(ptrs++));
10652       }
10653       return *this;
10654     }
10655 
10656     //! Bitwise OR operator.
10657     /**
10658        Similar to operator|=(const t), except that it returns a new image instance instead of operating in-place.
10659        The pixel type of the returned image is \c T.
10660     **/
10661     template<typename t>
10662     CImg<T> operator|(const t value) const {
10663       return (+*this)|=value;
10664     }
10665 
10666     //! Bitwise OR operator.
10667     /**
10668        Similar to operator|=(const char*), except that it returns a new image instance instead of operating in-place.
10669        The pixel type of the returned image is \c T.
10670     **/
10671     CImg<T> operator|(const char *const expression) const {
10672       return (+*this)|=expression;
10673     }
10674 
10675     //! Bitwise OR operator.
10676     /**
10677        Similar to operator|=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
10678        The pixel type of the returned image is \c T.
10679     **/
10680     template<typename t>
10681     CImg<T> operator|(const CImg<t>& img) const {
10682       return (+*this)|=img;
10683     }
10684 
10685     //! In-place bitwise XOR operator.
10686     /**
10687        Similar to operator+=(const t), except that it performs a bitwise XOR operation instead of an addition.
10688        \warning
10689        - It does \e not compute the \e power of pixel values. For this purpose, use pow(const t) instead.
10690     **/
10691     template<typename t>
10692     CImg<T>& operator^=(const t value) {
10693       cimg_for(*this,ptrd,T) *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)value);
10694       return *this;
10695     }
10696 
10697     //! In-place bitwise XOR operator.
10698     /**
10699        Similar to operator+=(const char*), except that it performs a bitwise XOR operation instead of an addition.
10700        \warning
10701        - It does \e not compute the \e power of pixel values. For this purpose, use pow(const char*) instead.
10702     **/
10703     CImg<T>& operator^=(const char *const expression) {
10704       const unsigned int omode = cimg::exception_mode();
10705       cimg::exception_mode() = 0;
10706       try {
10707         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
10708         _cimg_math_parser mp(base,expression,"operator^=");
10709         T *ptrd = _data;
10710         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)mp.eval(x,y,z,c)); ++ptrd; }
10711       } catch (CImgException&) {
10712         cimg::exception_mode() = omode;
10713         *this^=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
10714       }
10715       cimg::exception_mode() = omode;
10716       return *this;
10717     }
10718 
10719     //! In-place bitwise XOR operator.
10720     /**
10721        Similar to operator+=(const CImg<t>&), except that it performs a bitwise XOR operation instead of an addition.
10722        \warning
10723        - It does \e not compute the \e power of pixel values. For this purpose, use pow(const CImg<t>&) instead.
10724     **/
10725     template<typename t>
10726     CImg<T>& operator^=(const CImg<t>& img) {
10727       const unsigned int siz = size(), isiz = img.size();
10728       if (siz && isiz) {
10729         if (is_overlapped(img)) return *this^=+img;
10730         T *ptrd = _data, *const ptre = _data + siz;
10731         if (siz>isiz) for (unsigned int n = siz/isiz; n; --n)
10732           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)*(ptrs++));
10733         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)*(ptrs++));
10734       }
10735       return *this;
10736     }
10737 
10738     //! Bitwise XOR operator.
10739     /**
10740        Similar to operator^=(const t), except that it returns a new image instance instead of operating in-place.
10741        The pixel type of the returned image is \c T.
10742     **/
10743     template<typename t>
10744     CImg<T> operator^(const t value) const {
10745       return (+*this)^=value;
10746     }
10747 
10748     //! Bitwise XOR operator.
10749     /**
10750        Similar to operator^=(const char*), except that it returns a new image instance instead of operating in-place.
10751        The pixel type of the returned image is \c T.
10752     **/
10753     CImg<T> operator^(const char *const expression) const {
10754       return (+*this)^=expression;
10755     }
10756 
10757     //! Bitwise XOR operator.
10758     /**
10759        Similar to operator^=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
10760        The pixel type of the returned image is \c T.
10761     **/
10762     template<typename t>
10763     CImg<T> operator^(const CImg<t>& img) const {
10764       return (+*this)^=img;
10765     }
10766 
10767     //! In-place bitwise left shift operator.
10768     /**
10769        Similar to operator+=(const t), except that it performs a bitwise left shift instead of an addition.
10770     **/
10771     template<typename t>
10772     CImg<T>& operator<<=(const t value) {
10773       cimg_for(*this,ptrd,T) *ptrd = (T)(((long)*ptrd) << (int)value);
10774       return *this;
10775     }
10776 
10777     //! In-place bitwise left shift operator.
10778     /**
10779        Similar to operator+=(const char*), except that it performs a bitwise left shift instead of an addition.
10780     **/
10781     CImg<T>& operator<<=(const char *const expression) {
10782       const unsigned int omode = cimg::exception_mode();
10783       cimg::exception_mode() = 0;
10784       try {
10785         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
10786         _cimg_math_parser mp(base,expression,"operator<<=");
10787         T *ptrd = _data;
10788         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd << (int)mp.eval(x,y,z,c)); ++ptrd; }
10789       } catch (CImgException&) {
10790         cimg::exception_mode() = omode;
10791         *this<<=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
10792       }
10793       cimg::exception_mode() = omode;
10794       return *this;
10795     }
10796 
10797     //! In-place bitwise left shift operator.
10798     /**
10799        Similar to operator+=(const CImg<t>&), except that it performs a bitwise left shift instead of an addition.
10800     **/
10801     template<typename t>
10802     CImg<T>& operator<<=(const CImg<t>& img) {
10803       const unsigned int siz = size(), isiz = img.size();
10804       if (siz && isiz) {
10805         if (is_overlapped(img)) return *this^=+img;
10806         T *ptrd = _data, *const ptre = _data + siz;
10807         if (siz>isiz) for (unsigned int n = siz/isiz; n; --n)
10808           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)((long)*ptrd << (int)*(ptrs++));
10809         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((long)*ptrd << (int)*(ptrs++));
10810       }
10811       return *this;
10812     }
10813 
10814     //! Bitwise left shift operator.
10815     /**
10816        Similar to operator<<=(const t), except that it returns a new image instance instead of operating in-place.
10817        The pixel type of the returned image is \c T.
10818     **/
10819     template<typename t>
10820     CImg<T> operator<<(const t value) const {
10821       return (+*this)<<=value;
10822     }
10823 
10824     //! Bitwise left shift operator.
10825     /**
10826        Similar to operator<<=(const char*), except that it returns a new image instance instead of operating in-place.
10827        The pixel type of the returned image is \c T.
10828     **/
10829     CImg<T> operator<<(const char *const expression) const {
10830       return (+*this)<<=expression;
10831     }
10832 
10833     //! Bitwise left shift operator.
10834     /**
10835        Similar to operator<<=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
10836        The pixel type of the returned image is \c T.
10837     **/
10838     template<typename t>
10839     CImg<T> operator<<(const CImg<t>& img) const {
10840       return (+*this)<<=img;
10841     }
10842 
10843     //! In-place bitwise right shift operator.
10844     /**
10845        Similar to operator+=(const t), except that it performs a bitwise right shift instead of an addition.
10846     **/
10847     template<typename t>
10848     CImg<T>& operator>>=(const t value) {
10849       cimg_for(*this,ptrd,T) *ptrd = (T)(((long)*ptrd) >> (int)value);
10850       return *this;
10851     }
10852 
10853     //! In-place bitwise right shift operator.
10854     /**
10855        Similar to operator+=(const char*), except that it performs a bitwise right shift instead of an addition.
10856     **/
10857     CImg<T>& operator>>=(const char *const expression) {
10858       const unsigned int omode = cimg::exception_mode();
10859       cimg::exception_mode() = 0;
10860       try {
10861         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
10862         _cimg_math_parser mp(base,expression,"operator<<=");
10863         T *ptrd = _data;
10864         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd >> (int)mp.eval(x,y,z,c)); ++ptrd; }
10865       } catch (CImgException&) {
10866         cimg::exception_mode() = omode;
10867         *this>>=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
10868       }
10869       cimg::exception_mode() = omode;
10870       return *this;
10871     }
10872 
10873     //! In-place bitwise right shift operator.
10874     /**
10875        Similar to operator+=(const CImg<t>&), except that it performs a bitwise right shift instead of an addition.
10876     **/
10877     template<typename t>
10878     CImg<T>& operator>>=(const CImg<t>& img) {
10879       const unsigned int siz = size(), isiz = img.size();
10880       if (siz && isiz) {
10881         if (is_overlapped(img)) return *this^=+img;
10882         T *ptrd = _data, *const ptre = _data + siz;
10883         if (siz>isiz) for (unsigned int n = siz/isiz; n; --n)
10884           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)((long)*ptrd >> (int)*(ptrs++));
10885         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((long)*ptrd >> (int)*(ptrs++));
10886       }
10887       return *this;
10888     }
10889 
10890     //! Bitwise right shift operator.
10891     /**
10892        Similar to operator>>=(const t), except that it returns a new image instance instead of operating in-place.
10893        The pixel type of the returned image is \c T.
10894     **/
10895     template<typename t>
10896     CImg<T> operator>>(const t value) const {
10897       return (+*this)>>=value;
10898     }
10899 
10900     //! Bitwise right shift operator.
10901     /**
10902        Similar to operator>>=(const char*), except that it returns a new image instance instead of operating in-place.
10903        The pixel type of the returned image is \c T.
10904     **/
10905     CImg<T> operator>>(const char *const expression) const {
10906       return (+*this)>>=expression;
10907     }
10908 
10909     //! Bitwise right shift operator.
10910     /**
10911        Similar to operator>>=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
10912        The pixel type of the returned image is \c T.
10913     **/
10914     template<typename t>
10915     CImg<T> operator>>(const CImg<t>& img) const {
10916       return (+*this)>>=img;
10917     }
10918 
10919     //! Bitwise inversion operator.
10920     /**
10921        Similar to operator-(), except that it compute the bitwise inverse instead of the opposite value.
10922     **/
10923     CImg<T> operator~() const {
10924       CImg<T> res(_width,_height,_depth,_spectrum);
10925       const T *ptrs = end();
10926       cimg_for(res,ptrd,T) { const unsigned long value = (unsigned long)*(--ptrs); *ptrd = (T)~value; }
10927       return res;
10928     }
10929 
10930     //! Test if two images have the same size and values.
10931     /**
10932        Return \c true if the image instance and the input image \c img have the same dimensions and pixel values, and \c false otherwise.
10933        \param img : input image to compare with.
10934        \note
10935        - The pixel buffer pointers data() of the two compared images do not have to be the same for operator==() to return \c true.
10936          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.
10937        \par Sample code :
10938        \code
10939        const CImg<float> img1(1,3,1,1, 0,1,2); // Construct a 1x3 vector [0;1;2] (with 'float' pixel values).
10940        const CImg<char> img2(1,3,1,1, 0,1,2);  // Construct a 1x3 vector [0;1;2] (with 'char' pixel values).
10941        if (img1==img2) {                       // Test succeeds, image dimensions and values are the same.
10942          std::printf("'img1' and 'img2' have same dimensions and values.");
10943        }
10944        \endcode
10945        \sa operator!=().
10946     **/
10947     template<typename t>
10948     bool operator==(const CImg<t>& img) const {
10949       const unsigned int siz = size();
10950       bool vequal = true;
10951       if (siz!=img.size()) return false;
10952       t *ptrs = img._data + siz;
10953       for (T *ptrd = _data + siz; vequal && ptrd>_data; vequal = vequal && ((*(--ptrd))==(*(--ptrs)))) {}
10954       return vequal;
10955     }
10956 
10957     //! Test if two images have different sizes or values.
10958     /**
10959        Return \c true if the image instance and the input image \c img have different dimensions or pixel values, and \c false otherwise.
10960        \param img : input image to compare with.
10961        \note
10962        - Writing \c img1!=img2 is equivalent to \c !(img1==img2).
10963        \sa operator==().
10964     **/
10965     template<typename t>
10966     bool operator!=(const CImg<t>& img) const {
10967       return !((*this)==img);
10968     }
10969 
10970     //! Construct an image list from two images.
10971     /**
10972        Return a new list of image (\c CImgList instance) containing exactly two elements :
10973          - A copy of the image instance, at position [\c 0].
10974          - A copy of the specified image \c img, at position [\c 1].
10975 
10976        \param img : Input image that will be the second image of the resulting list.
10977        \note
10978        - 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).
10979        - Constructed lists contain no shared images. If image instance or input image \c img are shared, they are
10980          inserted as new non-shared copies in the resulting list.
10981        - The pixel type of the returned list may be a superset of the initial pixel type \c T, if necessary.
10982        \warning
10983        - Pipelining operator,() \c N times will perform \c N copies of the entire content of a (growing) image list.
10984          This may become very expensive in terms of speed and used memory. You should avoid using this technique to
10985          build a new CImgList instance from several images, if you are seeking for performance.
10986          Fast insertions of images in an image list are possible with CImgList<T>::insert(const CImg<t>&,unsigned int,bool) or
10987          move_to(CImgList<t>&,unsigned int).
10988        \par Sample code :
10989        \code
10990        const CImg<float>
10991           img1("reference.jpg"),
10992           img2 = img1.get_mirror('x'),
10993           img3 = img2.get_blur(5);
10994        const CImgList<float> list = (img1,img2); // Create list of two elements from 'img1' and 'img2'.
10995        (list,img3).display();                    // Display image list containing copies of 'img1','img2' and 'img3'.
10996        \endcode
10997        \image html ref_operator_comma.jpg
10998        \sa operator,(const CImgList<t>&) const,
10999            move_to(CImgList<t>&,unsigned int).
11000            CImgList<T>::insert(const CImg<t>&,unsigned int,bool).
11001     **/
11002     template<typename t>
11003     CImgList<_cimg_Tt> operator,(const CImg<t>& img) const {
11004       return CImgList<_cimg_Tt>(*this,img);
11005     }
11006 
11007     //! Construct an image list from image instance and an input image list.
11008     /**
11009        Return a new list of images (\c CImgList instance) containing exactly \c list.size() \c + \c 1 elements :
11010          - A copy of the image instance, at position [\c 0].
11011          - A copy of the specified image list \c list, from positions [\c 1] to [\c list.size()].
11012 
11013        \param list : Input image list that will be appended to the image instance.
11014        \note
11015        - Similar to operator,(const CImg<t>&) const, except that it takes an image list as an argument.
11016        \sa operator,(const CImg<t>&) const,
11017            CImgList<T>::insert(const CImgList<t>&,unsigned int,bool).
11018     **/
11019     template<typename t>
11020     CImgList<_cimg_Tt> operator,(const CImgList<t>& list) const {
11021       return CImgList<_cimg_Tt>(list,false).insert(*this,0);
11022     }
11023 
11024     //! Split image along specified axis.
11025     /**
11026        Return a new list of images (\c CImgList instance) containing the splitted components
11027        of the instance image along the specified axis.
11028        \param axis : Splitting axis (can be '\c x','\c y','\c z' or '\c c')
11029        \note
11030        - Similar to get_split(char,int) const, with default second argument.
11031        \par Sample code :
11032        \code
11033        const CImg<unsigned char> img("reference.jpg"); // Load a RGB color image.
11034        const CImgList<unsigned char> list = (img<'c'); // Get a list of its three R,G,B channels.
11035        (img,list).display();
11036        \endcode
11037        \image html ref_operator_less.jpg
11038        \sa get_split(char,int) const.
11039     **/
11040     CImgList<T> operator<(const char axis) const {
11041       return get_split(axis);
11042     }
11043 
11044     //@}
11045     //-------------------------------------
11046     //
11047     //! \name Instance Characteristics
11048     //@{
11049     //-------------------------------------
11050 
11051     //! Get the type of image pixel values as a C string.
11052     /**
11053        Return a \c char* string containing the usual type name of the image pixel values
11054        (i.e. a stringified version of the template parameter \c T).
11055        \note
11056        - The returned string may contain spaces (as in \c "unsigned char").
11057        - If the pixel type \c T does not correspond to a registered type, the string <tt>"unknown"</tt> is returned.
11058        \sa value_type.
11059     **/
11060     static const char* pixel_type() {
11061       return cimg::type<T>::string();
11062     }
11063 
11064     //! Get the number of image columns.
11065     /**
11066        Return the image width, i.e. the image dimension along the X-axis.
11067        \note
11068        - The width() of an empty image is equal to \c 0.
11069        - width() is typically equal to \c 1 when considering images as \e vectors for matrix calculations.
11070        - width() returns an \c int, although the image width is internally stored as an \c unsigned \c int.
11071          Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
11072          \c unsigned \c int variables.
11073          Access to the initial \c unsigned \c int variable is possible (though not recommended) by <tt>(*this)._width</tt>.
11074        \sa height(),
11075            depth(),
11076            spectrum(),
11077            size().
11078     **/
11079     int width() const {
11080       return (int)_width;
11081     }
11082 
11083     //! Get the number of image rows.
11084     /**
11085        Return the image height, i.e. the image dimension along the Y-axis.
11086        \note
11087        - The height() of an empty image is equal to \c 0.
11088        - height() returns an \c int, although the image height is internally stored as an \c unsigned \c int.
11089          Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
11090          \c unsigned \c int variables.
11091          Access to the initial \c unsigned \c int variable is possible (though not recommended) by <tt>(*this)._height</tt>.
11092        \sa width(),
11093            depth(),
11094            spectrum(),
11095            size().
11096     **/
11097     int height() const {
11098       return (int)_height;
11099     }
11100 
11101     //! Get the number of image slices.
11102     /**
11103        Return the image depth, i.e. the image dimension along the Z-axis.
11104        \note
11105        - The depth() of an empty image is equal to \c 0.
11106        - depth() is typically equal to \c 1 when considering usual 2d images. When depth()\c > \c 1, the image
11107          is said to be \e volumetric.
11108        - depth() returns an \c int, although the image depth is internally stored as an \c unsigned \c int.
11109          Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
11110          \c unsigned \c int variables.
11111          Access to the initial \c unsigned \c int variable is possible (though not recommended) by <tt>(*this)._depth</tt>.
11112        \sa width(),
11113            height(),
11114            spectrum(),
11115            size().
11116     **/
11117     int depth() const {
11118       return (int)_depth;
11119     }
11120 
11121     //! Get the number of image channels.
11122     /**
11123        Return the number of image channels, i.e. the image dimension along the C-axis.
11124        \note
11125        - The spectrum() of an empty image is equal to \c 0.
11126        - spectrum() is typically equal to \c 1 when considering scalar-valued images, to \c 3 for RGB-coded color images, and to
11127          \c 4 for RGBA-coded color images (with alpha-channel). The number of channels of an image instance
11128          is not limited. The meaning of the pixel values is not linked up to the number of channels
11129          (e.g. a 4-channel image may indifferently stands for a RGBA or CMYK color image).
11130        - spectrum() returns an \c int, although the image spectrum is internally stored as an \c unsigned \c int.
11131          Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
11132          \c unsigned \c int variables.
11133          Access to the initial \c unsigned \c int variable is possible (though not recommended) by <tt>(*this)._spectrum</tt>.
11134        \sa width(),
11135            height(),
11136            depth(),
11137            size().
11138     **/
11139     int spectrum() const {
11140       return (int)_spectrum;
11141     }
11142 
11143     //! Get the total number of pixel values.
11144     /**
11145        Return <tt>width()*\ref height()*\ref depth()*\ref spectrum()</tt>,
11146        i.e. the total number of values of type \c T in the pixel buffer of the image instance.
11147        \note
11148        - The size() of an empty image is equal to \c 0.
11149        - The allocated memory size for a pixel buffer of a non-shared \c CImg<T> instance is equal to <tt>size()*sizeof(T)</tt>.
11150        \par Sample code :
11151        \code
11152        const CImg<float> img(100,100,1,3);               // Construct new 100x100 color image.
11153        if (img.size()==30000)                            // Test succeeds.
11154          std::printf("Pixel buffer uses %lu bytes",
11155                      img.size()*sizeof(float));
11156        \endcode
11157        \sa width(),
11158            height(),
11159            depth(),
11160            spectrum().
11161     **/
11162     unsigned int size() const {
11163       return _width*_height*_depth*_spectrum;
11164     }
11165 
11166     //! Get a pointer to the first pixel value.
11167     /**
11168        Return a \c T*, or a \c const \c T* pointer to the first value in the pixel buffer of the image instance,
11169        whether the instance is \c const or not.
11170        \note
11171        - The data() of an empty image is equal to \c 0 (null pointer).
11172        - The allocated pixel buffer for the image instance starts from \c data()
11173          and goes to <tt>data()+\ref size()-1</tt> (included).
11174        - To get the pointer to one particular location of the pixel buffer, use data(unsigned int,unsigned int,unsigned int,unsigned int) instead.
11175        \sa operator T*() const,
11176            data(unsigned int,unsigned int,unsigned int,unsigned int).
11177     **/
11178     T* data() {
11179       return _data;
11180     }
11181 
11182     //! Get a pointer to the first pixel value \const.
11183     const T* data() const {
11184       return _data;
11185     }
11186 
11187     //! Get a pointer to a located pixel value.
11188     /**
11189        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,
11190        whether the instance is \c const or not.
11191        \param x : X-coordinate of the pixel value.
11192        \param y : Y-coordinate of the pixel value.
11193        \param z : Z-coordinate of the pixel value.
11194        \param c : C-coordinate of the pixel value.
11195        \note
11196        - 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
11197          operator()(unsigned int,unsigned int,unsigned int,unsigned int).
11198        \sa operator()(unsigned int,unsigned int,unsigned int,unsigned int),
11199            data().
11200      **/
11201 #if cimg_verbosity>=3
11202     T *data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
11203       const unsigned int off = (unsigned int)offset(x,y,z,c);
11204       if (off>=size()) {
11205         cimg::warn(_cimg_instance
11206                    "data() : Invalid pointer request, at coordinates (%u,%u,%u,%u) [offset=%u].",
11207                    cimg_instance,
11208                    x,y,z,c,off);
11209         return _data;
11210       }
11211       return _data + off;
11212     }
11213 
11214     //! Get a pointer to a located pixel value \const.
11215     const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
11216       return const_cast<CImg<T>*>(this)->data(x,y,z,c);
11217     }
11218 #else
11219     T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
11220       return _data + x + y*_width + z*_width*_height + c*_width*_height*_depth;
11221     }
11222 
11223     const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
11224       return _data + x + y*_width + z*_width*_height + c*_width*_height*_depth;
11225     }
11226 #endif
11227 
11228     //! Get the offset to a located pixel value, with respect to the beginning of the pixel buffer.
11229     /**
11230        \param x : X-coordinate of the pixel value.
11231        \param y : Y-coordinate of the pixel value.
11232        \param z : Z-coordinate of the pixel value.
11233        \param c : C-coordinate of the pixel value.
11234        \note
11235        - Writing \c img.data(x,y,z,c) is equivalent to <tt>&(img(x,y,z,c)) - img.data()</tt>.
11236          Thus, this method has the same properties as operator()(unsigned int,unsigned int,unsigned int,unsigned int).
11237        \par Sample code :
11238        \code
11239        const CImg<float> img(100,100,1,3);      // Define a 100x100 RGB-color image.
11240        const long off = img.offset(10,10,0,2);  // Get the offset of the blue value of the pixel located at (10,10).
11241        const float val = img[off];              // Get the blue value of this pixel.
11242        \endcode
11243        \sa operator()(unsigned int,unsigned int,unsigned int,unsigned int),
11244            data(unsigned int,unsigned int,unsigned int,unsigned int).
11245     **/
11246     int offset(const int x, const int y=0, const int z=0, const int c=0) const {
11247       return x + y*_width + z*_width*_height + c*_width*_height*_depth;
11248     }
11249 
11250     //! Get a CImg<T>::iterator pointing to the first pixel value.
11251     /**
11252        \note
11253        - Equivalent to data().
11254        - It has been mainly defined for compatibility with STL naming conventions.
11255        \sa data().
11256      **/
11257     iterator begin() {
11258       return _data;
11259     }
11260 
11261     //! Get a CImg<T>::iterator pointing to the first value of the pixel buffer \const.
11262     const_iterator begin() const {
11263       return _data;
11264     }
11265 
11266     //! Get a CImg<T>::iterator pointing next to the last pixel value.
11267     /**
11268        \note
11269        - Writing \c img.end() is equivalent to <tt>img.data() + img.size()</tt>.
11270        - It has been mainly defined for compatibility with STL naming conventions.
11271        \warning
11272        - The returned iterator actually points to a value located \e outside the acceptable bounds of the pixel buffer. Trying
11273          to read or write the content of the returned iterator will probably result in a crash. Use it mainly as an
11274          strict upper bound for a CImg<T>::iterator.
11275        \par Sample code :
11276        \code
11277        CImg<float> img(100,100,1,3);                                     // Define a 100x100 RGB color image.
11278        for (CImg<float>::iterator it = img.begin(); it<img.end(); ++it)  // 'img.end()' used here as an upper bound for the iterator.
11279          *it = 0;
11280        \endcode
11281        \sa data().
11282     **/
11283     iterator end() {
11284       return _data + size();
11285     }
11286 
11287     //! Get a CImg<T>::iterator pointing next to the last pixel value \const.
11288     const_iterator end() const {
11289       return _data + size();
11290     }
11291 
11292     //! Get a reference to the first pixel value.
11293     /**
11294        \note
11295        - Writing \c img.front() is equivalent to <tt>img[0]</tt>, or <tt>img(0,0,0,0)</tt>.
11296        - It has been mainly defined for compatibility with STL naming conventions.
11297        \sa data(),
11298            offset(),
11299            begin().
11300     **/
11301     T& front() {
11302       return *_data;
11303     }
11304 
11305     //! Get a reference to the first pixel value \const.
11306     const T& front() const {
11307       return *_data;
11308     }
11309 
11310     //! Get a reference to the last pixel value.
11311     /**
11312        \note
11313        - Writing \c img.end() is equivalent to <tt>img[img.size()-1]</tt>, or
11314          <tt>img(img.width()-1,img.height()-1,img.depth()-1,img.spectrum()-1)</tt>.
11315        - It has been mainly defined for compatibility with STL naming conventions.
11316        \sa data(),
11317            offset(),
11318            end().
11319     **/
11320     T& back() {
11321       return *(_data + size() - 1);
11322     }
11323 
11324     //! Get a reference to the last pixel value \const.
11325     const T& back() const {
11326       return *(_data + size() - 1);
11327     }
11328 
11329     //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions.
11330     /**
11331        Return a reference to the pixel value of the image instance located at a specified \c offset,
11332        or to a specified default value in case of out-of-bounds access.
11333        \param offset : Offset to the desired pixel value.
11334        \param out_value : Default value returned if \c offset is outside image bounds.
11335        \note
11336        - Writing \c img.at(offset,out_value) is similar to <tt>img[offset]</tt>, except that if \c offset
11337          is outside bounds (e.g. \c offset<0 or \c offset>=img.size()), a reference to a value \c out_value
11338          is safely returned instead.
11339        - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
11340          you are \e not sure about the validity of the specified pixel offset.
11341        \sa operator()(),
11342            offset(),
11343            at(int).
11344     **/
11345     T& at(const int offset, const T out_value) {
11346       return (offset<0 || offset>=(int)size())?(cimg::temporary(out_value)=out_value):(*this)[offset];
11347     }
11348 
11349     //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions \const.
11350     T at(const int offset, const T out_value) const {
11351       return (offset<0 || offset>=(int)size())?out_value:(*this)[offset];
11352     }
11353 
11354     //! Access to a pixel value at a specified offset, using Neumann boundary conditions.
11355     /**
11356        Return a reference to the pixel value of the image instance located at a specified \c offset,
11357        or to the nearest pixel location in the image instance in case of out-of-bounds access.
11358        \param offset : Offset to the desired pixel value.
11359        \note
11360        - Similar to at(int,const T), except that an out-of-bounds access returns the value of the
11361          nearest pixel in the image instance, regarding the specified offset, i.e.
11362          - If \c offset<0, then \c img[0] is returned.
11363          - If \c offset>=img.size(), then \c img[img.size()-1] is returned.
11364        - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
11365          you are \e not sure about the validity of the specified pixel offset.
11366        - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _at(int).
11367        \sa operator()(),
11368            offset(),
11369            at(int,const T).
11370      **/
11371     T& at(const int offset) {
11372       if (is_empty())
11373         throw CImgInstanceException(_cimg_instance
11374                                     "at() : Empty instance.",
11375                                     cimg_instance);
11376       return _at(offset);
11377     }
11378 
11379     T& _at(const int offset) {
11380       const unsigned int siz = (unsigned int)size();
11381       return (*this)[offset<0?0:(unsigned int)offset>=siz?siz-1:offset];
11382     }
11383 
11384     //! Access to a pixel value at a specified offset, using Neumann boundary conditions \const.
11385     T at(const int offset) const {
11386       if (is_empty())
11387         throw CImgInstanceException(_cimg_instance
11388                                     "at() : Empty instance.",
11389                                     cimg_instance);
11390       return _at(offset);
11391     }
11392 
11393     T _at(const int offset) const {
11394       const unsigned int siz = (unsigned int)size();
11395       return (*this)[offset<0?0:(unsigned int)offset>=siz?siz-1:offset];
11396     }
11397 
11398     //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate.
11399     /**
11400        Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c),
11401        or to a specified default value in case of out-of-bounds access along the X-axis.
11402        \param x : X-coordinate of the pixel value.
11403        \param y : Y-coordinate of the pixel value.
11404        \param z : Z-coordinate of the pixel value.
11405        \param c : C-coordinate of the pixel value.
11406        \param out_value : Default value returned if \c (\c x,\c y,\c z,\c c) is outside image bounds.
11407        \note
11408        - Similar to operator()(), except that an out-of-bounds access along the X-axis returns the specified value \c out_value.
11409        - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
11410          you are \e not sure about the validity of the specified pixel coordinates.
11411        \warning
11412        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
11413        \sa operator()(),
11414            at(int,const T).
11415            atX(int,int,int,int),
11416            atXY(int,int,int,int,const T),
11417            atXYZ(int,int,int,int,const T),
11418            atXYZC(int,int,int,int,const T).
11419     **/
11420     T& atX(const int x, const int y, const int z, const int c, const T out_value) {
11421       return (x<0 || x>=width())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
11422     }
11423 
11424     //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate \const.
11425     T atX(const int x, const int y, const int z, const int c, const T out_value) const {
11426       return (x<0 || x>=width())?out_value:(*this)(x,y,z,c);
11427     }
11428 
11429     //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate.
11430     /**
11431        Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c),
11432        or to the nearest pixel location in the image instance in case of out-of-bounds access along the X-axis.
11433        \param x : X-coordinate of the pixel value.
11434        \param y : Y-coordinate of the pixel value.
11435        \param z : Z-coordinate of the pixel value.
11436        \param c : C-coordinate of the pixel value.
11437        \note
11438        - Similar to at(int,int,int,int,const T), except that an out-of-bounds access returns the value of the
11439          nearest pixel in the image instance, regarding the specified X-coordinate.
11440        - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
11441          you are \e not sure about the validity of the specified pixel coordinates.
11442        - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _at(int,int,int,int).
11443        \warning
11444        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
11445        \sa operator()(),
11446            at(int),
11447            atX(int,int,int,int,const T),
11448            atXY(int,int,int,int),
11449            atXYZ(int,int,int,int),
11450            atXYZC(int,int,int,int).
11451      **/
11452     T& atX(const int x, const int y=0, const int z=0, const int c=0) {
11453       if (is_empty())
11454         throw CImgInstanceException(_cimg_instance
11455                                     "atX() : Empty instance.",
11456                                     cimg_instance);
11457       return _atX(x,y,z,c);
11458     }
11459 
11460     T& _atX(const int x, const int y=0, const int z=0, const int c=0) {
11461       return (*this)(x<0?0:(x>=width()?width()-1:x),y,z,c);
11462     }
11463 
11464     //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate \const.
11465     T atX(const int x, const int y=0, const int z=0, const int c=0) const {
11466       if (is_empty())
11467         throw CImgInstanceException(_cimg_instance
11468                                     "atX() : Empty instance.",
11469                                     cimg_instance);
11470       return _atX(x,y,z,c);
11471     }
11472 
11473     T _atX(const int x, const int y=0, const int z=0, const int c=0) const {
11474       return (*this)(x<0?0:(x>=width()?width()-1:x),y,z,c);
11475     }
11476 
11477     //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y-coordinates.
11478     /**
11479        Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on X and Y-coordinates.
11480     **/
11481     T& atXY(const int x, const int y, const int z, const int c, const T out_value) {
11482       return (x<0 || y<0 || x>=width() || y>=height())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
11483     }
11484 
11485     //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y coordinates \const.
11486     T atXY(const int x, const int y, const int z, const int c, const T out_value) const {
11487       return (x<0 || y<0 || x>=width() || y>=height())?out_value:(*this)(x,y,z,c);
11488     }
11489 
11490     //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates.
11491     /**
11492        Similar to atX(int,int,int,int), except that boundary checking is performed both on X and Y-coordinates.
11493        \note
11494        - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _atXY(int,int,int,int).
11495      **/
11496     T& atXY(const int x, const int y, const int z=0, const int c=0) {
11497       if (is_empty())
11498         throw CImgInstanceException(_cimg_instance
11499                                     "atXY() : Empty instance.",
11500                                     cimg_instance);
11501       return _atXY(x,y,z,c);
11502     }
11503 
11504     T& _atXY(const int x, const int y, const int z=0, const int c=0) {
11505       return (*this)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1:y),z,c);
11506     }
11507 
11508     //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates \const.
11509     T atXY(const int x, const int y, const int z=0, const int c=0) const {
11510       if (is_empty())
11511         throw CImgInstanceException(_cimg_instance
11512                                     "atXY() : Empty instance.",
11513                                     cimg_instance);
11514       return _atXY(x,y,z,c);
11515     }
11516 
11517     T _atXY(const int x, const int y, const int z=0, const int c=0) const {
11518       return (*this)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1:y),z,c);
11519     }
11520 
11521     //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates.
11522     /**
11523        Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on X,Y and Z-coordinates.
11524     **/
11525     T& atXYZ(const int x, const int y, const int z, const int c, const T out_value) {
11526       return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?
11527         (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
11528     }
11529 
11530     //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates \const.
11531     T atXYZ(const int x, const int y, const int z, const int c, const T out_value) const {
11532       return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?out_value:(*this)(x,y,z,c);
11533     }
11534 
11535     //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates.
11536     /**
11537        Similar to atX(int,int,int,int), except that boundary checking is performed both on X,Y and Z-coordinates.
11538        \note
11539        - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _atXYZ(int,int,int,int).
11540     **/
11541     T& atXYZ(const int x, const int y, const int z, const int c=0) {
11542       if (is_empty())
11543         throw CImgInstanceException(_cimg_instance
11544                                     "atXYZ() : Empty instance.",
11545                                     cimg_instance);
11546       return _atXYZ(x,y,z,c);
11547     }
11548 
11549     T& _atXYZ(const int x, const int y, const int z, const int c=0) {
11550       return (*this)(x<0?0:(x>=width()?width()-1:x),y<0?0:(y>=height()?height()-1:y),
11551                      z<0?0:(z>=depth()?depth()-1:z),c);
11552     }
11553 
11554     //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates \const.
11555     T atXYZ(const int x, const int y, const int z, const int c=0) const {
11556       if (is_empty())
11557         throw CImgInstanceException(_cimg_instance
11558                                     "atXYZ() : Empty instance.",
11559                                     cimg_instance);
11560       return _atXYZ(x,y,z,c);
11561     }
11562 
11563     T _atXYZ(const int x, const int y, const int z, const int c=0) const {
11564       return (*this)(x<0?0:(x>=width()?width()-1:x),y<0?0:(y>=height()?height()-1:y),
11565                      z<0?0:(z>=depth()?depth()-1:z),c);
11566     }
11567 
11568     //! Access to a pixel value, using Dirichlet boundary conditions.
11569     /**
11570        Similar to atX(int,int,int,int,const T), except that boundary checking is performed on all X,Y,Z and C-coordinates.
11571     **/
11572     T& atXYZC(const int x, const int y, const int z, const int c, const T out_value) {
11573       return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?
11574         (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
11575     }
11576 
11577     //! Access to a pixel value, using Dirichlet boundary conditions \const.
11578     T atXYZC(const int x, const int y, const int z, const int c, const T out_value) const {
11579       return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?out_value:(*this)(x,y,z,c);
11580     }
11581 
11582     //! Access to a pixel value, using Neumann boundary conditions.
11583     /**
11584        Similar to atX(int,int,int,int), except that boundary checking is performed on all X,Y,Z and C-coordinates.
11585        \note
11586        - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _atXYZC(int,int,int,int).
11587     **/
11588     T& atXYZC(const int x, const int y, const int z, const int c) {
11589       if (is_empty())
11590         throw CImgInstanceException(_cimg_instance
11591                                     "atXYZC() : Empty instance.",
11592                                     cimg_instance);
11593       return _atXYZC(x,y,z,c);
11594     }
11595 
11596     T& _atXYZC(const int x, const int y, const int z, const int c) {
11597       return (*this)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1:y),
11598                      z<0?0:(z>=depth()?depth()-1:z), c<0?0:(c>=spectrum()?spectrum()-1:c));
11599     }
11600 
11601     //! Access to a pixel value, using Neumann boundary conditions \const.
11602     T atXYZC(const int x, const int y, const int z, const int c) const {
11603       if (is_empty())
11604         throw CImgInstanceException(_cimg_instance
11605                                     "atXYZC() : Empty instance.",
11606                                     cimg_instance);
11607       return _atXYZC(x,y,z,c);
11608     }
11609 
11610     T _atXYZC(const int x, const int y, const int z, const int c) const {
11611       return (*this)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1:y),
11612                      z<0?0:(z>=depth()?depth()-1:z), c<0?0:(c>=spectrum()?spectrum()-1:c));
11613     }
11614 
11615     //! Get pixel value, using linear interpolation and Dirichlet boundary conditions for the X-coordinate.
11616     /**
11617        Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
11618        or a specified default value in case of out-of-bounds access along the X-axis.
11619        \param fx : X-coordinate of the pixel value (float-valued).
11620        \param y : Y-coordinate of the pixel value.
11621        \param z : Z-coordinate of the pixel value.
11622        \param c : C-coordinate of the pixel value.
11623        \param out_value : Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds.
11624        \note
11625        - 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,
11626          if corresponding coordinates are not integers.
11627        - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued.
11628        \warning
11629        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
11630        \sa operator()(),
11631            atX(int,int,int,int,const T),
11632            linear_atX(float,int,int,int) const,
11633            linear_atXY(float,float,int,int,const T) const,
11634            linear_atXYZ(float,float,float,int,const T) const,
11635            linear_atXYZC(float,float,float,float,const T) const.
11636     **/
11637     Tfloat linear_atX(const float fx, const int y, const int z, const int c, const T out_value) const {
11638       const int
11639         x = (int)fx - (fx>=0?0:1), nx = x + 1;
11640       const float
11641         dx = fx - x;
11642       const Tfloat
11643         Ic = (Tfloat)atX(x,y,z,c,out_value), In = (Tfloat)atXY(nx,y,z,c,out_value);
11644       return Ic + dx*(In-Ic);
11645     }
11646 
11647     //! Get pixel value, using linear interpolation and Neumann boundary conditions for the X-coordinate.
11648     /**
11649        Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
11650        or the value of the nearest pixel location in the image instance in case of out-of-bounds access along the X-axis.
11651        \param fx : X-coordinate of the pixel value (float-valued).
11652        \param y : Y-coordinate of the pixel value.
11653        \param z : Z-coordinate of the pixel value.
11654        \param c : C-coordinate of the pixel value.
11655        \note
11656        - Similar to linear_atX(float,int,int,int,const T) const, except that an out-of-bounds access returns the value of the
11657          nearest pixel in the image instance, regarding the specified X-coordinate.
11658        - 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).
11659        \warning
11660        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
11661        \sa operator()(),
11662            atX(int,int,int,int),
11663            linear_atX(float,int,int,int,const T) const,
11664            linear_atXY(float,float,int,int) const,
11665            linear_atXYZ(float,float,float,int) const,
11666            linear_atXYZC(float,float,float,float) const.
11667     **/
11668     Tfloat linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
11669       if (is_empty())
11670         throw CImgInstanceException(_cimg_instance
11671                                     "linear_atX() : Empty instance.",
11672                                     cimg_instance);
11673 
11674       return _linear_atX(fx,y,z,c);
11675     }
11676 
11677     Tfloat _linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
11678       const float
11679         nfx = fx<0?0:(fx>_width-1?_width-1:fx);
11680       const unsigned int
11681         x = (unsigned int)nfx;
11682       const float
11683         dx = nfx - x;
11684       const unsigned int
11685         nx = dx>0?x+1:x;
11686       const Tfloat
11687         Ic = (Tfloat)(*this)(x,y,z,c), In = (Tfloat)(*this)(nx,y,z,c);
11688       return Ic + dx*(In-Ic);
11689     }
11690 
11691     //! Get pixel value, using linear interpolation and Dirichlet boundary conditions for the X and Y-coordinates.
11692     /**
11693        Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the boundary checking
11694        are achieved both for X and Y-coordinates.
11695     **/
11696     Tfloat linear_atXY(const float fx, const float fy, const int z, const int c, const T out_value) const {
11697       const int
11698         x = (int)fx - (fx>=0?0:1), nx = x + 1,
11699         y = (int)fy - (fy>=0?0:1), ny = y + 1;
11700       const float
11701         dx = fx - x,
11702         dy = fy - y;
11703       const Tfloat
11704         Icc = (Tfloat)atXY(x,y,z,c,out_value),  Inc = (Tfloat)atXY(nx,y,z,c,out_value),
11705         Icn = (Tfloat)atXY(x,ny,z,c,out_value), Inn = (Tfloat)atXY(nx,ny,z,c,out_value);
11706       return Icc + dx*(Inc-Icc + dy*(Icc+Inn-Icn-Inc)) + dy*(Icn-Icc);
11707     }
11708 
11709     //! Get pixel value, using linear interpolation and Neumann boundary conditions for the X and Y-coordinates.
11710     /**
11711        Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking
11712        are achieved both for X and Y-coordinates.
11713        \note
11714        - 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).
11715     **/
11716     Tfloat linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
11717       if (is_empty())
11718         throw CImgInstanceException(_cimg_instance
11719                                     "linear_atXY() : Empty instance.",
11720                                     cimg_instance);
11721 
11722       return _linear_atXY(fx,fy,z,c);
11723     }
11724 
11725     Tfloat _linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
11726       const float
11727         nfx = fx<0?0:(fx>_width-1?_width-1:fx),
11728         nfy = fy<0?0:(fy>_height-1?_height-1:fy);
11729       const unsigned int
11730         x = (unsigned int)nfx,
11731         y = (unsigned int)nfy;
11732       const float
11733         dx = nfx - x,
11734         dy = nfy - y;
11735       const unsigned int
11736         nx = dx>0?x+1:x,
11737         ny = dy>0?y+1:y;
11738       const Tfloat
11739         Icc = (Tfloat)(*this)(x,y,z,c),  Inc = (Tfloat)(*this)(nx,y,z,c),
11740         Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c);
11741       return Icc + dx*(Inc-Icc + dy*(Icc+Inn-Icn-Inc)) + dy*(Icn-Icc);
11742     }
11743 
11744     //! Get pixel value, using linear interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates.
11745     /**
11746        Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the boundary checking
11747        are achieved both for X,Y and Z-coordinates.
11748     **/
11749     Tfloat linear_atXYZ(const float fx, const float fy, const float fz, const int c, const T out_value) const {
11750       const int
11751         x = (int)fx - (fx>=0?0:1), nx = x + 1,
11752         y = (int)fy - (fy>=0?0:1), ny = y + 1,
11753         z = (int)fz - (fz>=0?0:1), nz = z + 1;
11754       const float
11755         dx = fx - x,
11756         dy = fy - y,
11757         dz = fz - z;
11758       const Tfloat
11759         Iccc = (Tfloat)atXYZ(x,y,z,c,out_value), Incc = (Tfloat)atXYZ(nx,y,z,c,out_value),
11760         Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value), Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value),
11761         Iccn = (Tfloat)atXYZ(x,y,nz,c,out_value), Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value),
11762         Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value), Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value);
11763       return Iccc +
11764         dx*(Incc-Iccc +
11765             dy*(Iccc+Innc-Icnc-Incc +
11766                 dz*(Iccn+Innn+Icnc+Incc-Icnn-Incn-Iccc-Innc)) +
11767             dz*(Iccc+Incn-Iccn-Incc)) +
11768         dy*(Icnc-Iccc +
11769             dz*(Iccc+Icnn-Iccn-Icnc)) +
11770         dz*(Iccn-Iccc);
11771     }
11772 
11773     //! Get pixel value, using linear interpolation and Neumann boundary conditions for the X,Y and Z-coordinates.
11774     /**
11775        Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking
11776        are achieved both for X,Y and Z-coordinates.
11777        \note
11778        - 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).
11779     **/
11780     Tfloat linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const {
11781       if (is_empty())
11782         throw CImgInstanceException(_cimg_instance
11783                                     "linear_atXYZ() : Empty instance.",
11784                                     cimg_instance);
11785 
11786       return _linear_atXYZ(fx,fy,fz,c);
11787     }
11788 
11789     Tfloat _linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const {
11790       const float
11791         nfx = fx<0?0:(fx>_width-1?_width-1:fx),
11792         nfy = fy<0?0:(fy>_height-1?_height-1:fy),
11793         nfz = fz<0?0:(fz>_depth-1?_depth-1:fz);
11794       const unsigned int
11795         x = (unsigned int)nfx,
11796         y = (unsigned int)nfy,
11797         z = (unsigned int)nfz;
11798       const float
11799         dx = nfx - x,
11800         dy = nfy - y,
11801         dz = nfz - z;
11802       const unsigned int
11803         nx = dx>0?x+1:x,
11804         ny = dy>0?y+1:y,
11805         nz = dz>0?z+1:z;
11806       const Tfloat
11807         Iccc = (Tfloat)(*this)(x,y,z,c), Incc = (Tfloat)(*this)(nx,y,z,c),
11808         Icnc = (Tfloat)(*this)(x,ny,z,c), Innc = (Tfloat)(*this)(nx,ny,z,c),
11809         Iccn = (Tfloat)(*this)(x,y,nz,c), Incn = (Tfloat)(*this)(nx,y,nz,c),
11810         Icnn = (Tfloat)(*this)(x,ny,nz,c), Innn = (Tfloat)(*this)(nx,ny,nz,c);
11811       return Iccc +
11812         dx*(Incc-Iccc +
11813             dy*(Iccc+Innc-Icnc-Incc +
11814                 dz*(Iccn+Innn+Icnc+Incc-Icnn-Incn-Iccc-Innc)) +
11815             dz*(Iccc+Incn-Iccn-Incc)) +
11816         dy*(Icnc-Iccc +
11817             dz*(Iccc+Icnn-Iccn-Icnc)) +
11818         dz*(Iccn-Iccc);
11819     }
11820 
11821     //! Get pixel value, using linear interpolation and Dirichlet boundary conditions for all X,Y,Z and C-coordinates.
11822     /**
11823        Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the boundary checking
11824        are achieved for all X,Y,Z and C-coordinates.
11825     **/
11826     Tfloat linear_atXYZC(const float fx, const float fy, const float fz, const float fc, const T out_value) const {
11827       const int
11828         x = (int)fx - (fx>=0?0:1), nx = x + 1,
11829         y = (int)fy - (fy>=0?0:1), ny = y + 1,
11830         z = (int)fz - (fz>=0?0:1), nz = z + 1,
11831         c = (int)fc - (fc>=0?0:1), nc = c + 1;
11832       const float
11833         dx = fx - x,
11834         dy = fy - y,
11835         dz = fz - z,
11836         dc = fc - c;
11837       const Tfloat
11838         Icccc = (Tfloat)atXYZC(x,y,z,c,out_value), Inccc = (Tfloat)atXYZC(nx,y,z,c,out_value),
11839         Icncc = (Tfloat)atXYZC(x,ny,z,c,out_value), Inncc = (Tfloat)atXYZC(nx,ny,z,c,out_value),
11840         Iccnc = (Tfloat)atXYZC(x,y,nz,c,out_value), Incnc = (Tfloat)atXYZC(nx,y,nz,c,out_value),
11841         Icnnc = (Tfloat)atXYZC(x,ny,nz,c,out_value), Innnc = (Tfloat)atXYZC(nx,ny,nz,c,out_value),
11842         Icccn = (Tfloat)atXYZC(x,y,z,nc,out_value), Inccn = (Tfloat)atXYZC(nx,y,z,nc,out_value),
11843         Icncn = (Tfloat)atXYZC(x,ny,z,nc,out_value), Inncn = (Tfloat)atXYZC(nx,ny,z,nc,out_value),
11844         Iccnn = (Tfloat)atXYZC(x,y,nz,nc,out_value), Incnn = (Tfloat)atXYZC(nx,y,nz,nc,out_value),
11845         Icnnn = (Tfloat)atXYZC(x,ny,nz,nc,out_value), Innnn = (Tfloat)atXYZC(nx,ny,nz,nc,out_value);
11846       return Icccc +
11847         dx*(Inccc-Icccc +
11848             dy*(Icccc+Inncc-Icncc-Inccc +
11849                 dz*(Iccnc+Innnc+Icncc+Inccc-Icnnc-Incnc-Icccc-Inncc +
11850                     dc*(Iccnn+Innnn+Icncn+Inccn+Icnnc+Incnc+Icccc+Inncc-Icnnn-Incnn-Icccn-Inncn-Iccnc-Innnc-Icncc-Inccc)) +
11851                 dc*(Icccn+Inncn+Icncc+Inccc-Icncn-Inccn-Icccc-Inncc)) +
11852             dz*(Icccc+Incnc-Iccnc-Inccc +
11853                 dc*(Icccn+Incnn+Iccnc+Inccc-Iccnn-Inccn-Icccc-Incnc)) +
11854             dc*(Icccc+Inccn-Inccc-Icccn)) +
11855         dy*(Icncc-Icccc +
11856             dz*(Icccc+Icnnc-Iccnc-Icncc +
11857                 dc*(Icccn+Icnnn+Iccnc+Icncc-Iccnn-Icncn-Icccc-Icnnc)) +
11858             dc*(Icccc+Icncn-Icncc-Icccn)) +
11859         dz*(Iccnc-Icccc +
11860             dc*(Icccc+Iccnn-Iccnc-Icccn)) +
11861         dc*(Icccn-Icccc);
11862     }
11863 
11864     //! Get pixel value, using linear interpolation and Neumann boundary conditions for all X,Y,Z and C-coordinates.
11865     /**
11866        Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking
11867        are achieved for all X,Y,Z and C-coordinates.
11868        \note
11869        - 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).
11870     **/
11871     Tfloat linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
11872       if (is_empty())
11873         throw CImgInstanceException(_cimg_instance
11874                                     "linear_atXYZC() : Empty instance.",
11875                                     cimg_instance);
11876 
11877       return _linear_atXYZC(fx,fy,fz,fc);
11878     }
11879 
11880     Tfloat _linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
11881       const float
11882         nfx = fx<0?0:(fx>_width-1?_width-1:fx),
11883         nfy = fy<0?0:(fy>_height-1?_height-1:fy),
11884         nfz = fz<0?0:(fz>_depth-1?_depth-1:fz),
11885         nfc = fc<0?0:(fc>_spectrum-1?_spectrum-1:fc);
11886       const unsigned int
11887         x = (unsigned int)nfx,
11888         y = (unsigned int)nfy,
11889         z = (unsigned int)nfz,
11890         c = (unsigned int)nfc;
11891       const float
11892         dx = nfx - x,
11893         dy = nfy - y,
11894         dz = nfz - z,
11895         dc = nfc - c;
11896       const unsigned int
11897         nx = dx>0?x+1:x,
11898         ny = dy>0?y+1:y,
11899         nz = dz>0?z+1:z,
11900         nc = dc>0?c+1:c;
11901       const Tfloat
11902         Icccc = (Tfloat)(*this)(x,y,z,c), Inccc = (Tfloat)(*this)(nx,y,z,c),
11903         Icncc = (Tfloat)(*this)(x,ny,z,c), Inncc = (Tfloat)(*this)(nx,ny,z,c),
11904         Iccnc = (Tfloat)(*this)(x,y,nz,c), Incnc = (Tfloat)(*this)(nx,y,nz,c),
11905         Icnnc = (Tfloat)(*this)(x,ny,nz,c), Innnc = (Tfloat)(*this)(nx,ny,nz,c),
11906         Icccn = (Tfloat)(*this)(x,y,z,nc), Inccn = (Tfloat)(*this)(nx,y,z,nc),
11907         Icncn = (Tfloat)(*this)(x,ny,z,nc), Inncn = (Tfloat)(*this)(nx,ny,z,nc),
11908         Iccnn = (Tfloat)(*this)(x,y,nz,nc), Incnn = (Tfloat)(*this)(nx,y,nz,nc),
11909         Icnnn = (Tfloat)(*this)(x,ny,nz,nc), Innnn = (Tfloat)(*this)(nx,ny,nz,nc);
11910       return Icccc +
11911         dx*(Inccc-Icccc +
11912             dy*(Icccc+Inncc-Icncc-Inccc +
11913                 dz*(Iccnc+Innnc+Icncc+Inccc-Icnnc-Incnc-Icccc-Inncc +
11914                     dc*(Iccnn+Innnn+Icncn+Inccn+Icnnc+Incnc+Icccc+Inncc-Icnnn-Incnn-Icccn-Inncn-Iccnc-Innnc-Icncc-Inccc)) +
11915                 dc*(Icccn+Inncn+Icncc+Inccc-Icncn-Inccn-Icccc-Inncc)) +
11916             dz*(Icccc+Incnc-Iccnc-Inccc +
11917                 dc*(Icccn+Incnn+Iccnc+Inccc-Iccnn-Inccn-Icccc-Incnc)) +
11918             dc*(Icccc+Inccn-Inccc-Icccn)) +
11919         dy*(Icncc-Icccc +
11920             dz*(Icccc+Icnnc-Iccnc-Icncc +
11921                 dc*(Icccn+Icnnn+Iccnc+Icncc-Iccnn-Icncn-Icccc-Icnnc)) +
11922             dc*(Icccc+Icncn-Icncc-Icccn)) +
11923         dz*(Iccnc-Icccc +
11924             dc*(Icccc+Iccnn-Iccnc-Icccn)) +
11925         dc*(Icccn-Icccc);
11926     }
11927 
11928     //! Get pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate.
11929     /**
11930        Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
11931        or a specified default value in case of out-of-bounds access along the X-axis.
11932        \param fx : X-coordinate of the pixel value (float-valued).
11933        \param y : Y-coordinate of the pixel value.
11934        \param z : Z-coordinate of the pixel value.
11935        \param c : C-coordinate of the pixel value.
11936        \param out_value : Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds.
11937        \note
11938        - Similar to linear_atX(float,int,int,int,const T) const, except that the returned pixel value is approximated by a
11939          \e cubic interpolation along the X-axis.
11940        - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued.
11941        \warning
11942        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
11943        \sa operator()(),
11944            atX(int,int,int,int,const T),
11945            linear_atX(float,int,int,int,const T) const,
11946            cubic_atX(float,int,int,int) const,
11947            cubic_atXY(float,float,int,int,const T) const,
11948            cubic_atXYZ(float,float,float,int,const T) const.
11949     **/
11950     Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T out_value) const {
11951       const int
11952         x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2;
11953       const float
11954         dx = fx - x;
11955       const Tfloat
11956         Ip = (Tfloat)atX(px,y,z,c,out_value), Ic = (Tfloat)atX(x,y,z,c,out_value),
11957         In = (Tfloat)atX(nx,y,z,c,out_value), Ia = (Tfloat)atX(ax,y,z,c,out_value);
11958       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));
11959     }
11960 
11961     //! Get damped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate.
11962     /**
11963        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.
11964     **/
11965     Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T out_value,
11966                      const Tfloat min_value, const Tfloat max_value) const {
11967       const Tfloat val = cubic_atX(fx,y,z,c,out_value);
11968       return val<min_value?min_value:val>max_value?max_value:val;
11969     }
11970 
11971     //! Get pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate.
11972     /**
11973        Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
11974        or the value of the nearest pixel location in the image instance in case of out-of-bounds access along the X-axis.
11975        \param fx : X-coordinate of the pixel value (float-valued).
11976        \param y : Y-coordinate of the pixel value.
11977        \param z : Z-coordinate of the pixel value.
11978        \param c : C-coordinate of the pixel value.
11979        \note
11980        - 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.
11981        - 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).
11982        \warning
11983        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
11984        \sa operator()(),
11985            atX(int,int,int,int),
11986            linear_atX(float,int,int,int) const,
11987            cubic_atX(float,int,int,int,const T) const,
11988            cubic_atXY(float,float,int,int) const,
11989            cubic_atXYZ(float,float,float,int) const.
11990     **/
11991     Tfloat cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
11992       if (is_empty())
11993         throw CImgInstanceException(_cimg_instance
11994                                     "cubic_atX() : Empty instance.",
11995                                     cimg_instance);
11996       return _cubic_atX(fx,y,z,c);
11997     }
11998 
11999     Tfloat _cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
12000       const float
12001         nfx = fx<0?0:(fx>_width-1?_width-1:fx);
12002       const int
12003         x = (int)nfx;
12004       const float
12005         dx = nfx - x;
12006       const int
12007         px = x-1<0?0:x-1, nx = dx>0?x+1:x, ax = x+2>=width()?width()-1:x+2;
12008       const Tfloat
12009         Ip = (Tfloat)(*this)(px,y,z,c), Ic = (Tfloat)(*this)(x,y,z,c),
12010         In = (Tfloat)(*this)(nx,y,z,c), Ia = (Tfloat)(*this)(ax,y,z,c);
12011       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));
12012     }
12013 
12014     //! Get damped pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate.
12015     /**
12016        Similar to cubic_atX(float,int,int,int) const, except that you can specify the authorized minimum and maximum of the returned value.
12017     **/
12018     Tfloat cubic_atX(const float fx, const int y, const int z, const int c,
12019                      const Tfloat min_value, const Tfloat max_value) const {
12020       const Tfloat val = cubic_atX(fx,y,z,c);
12021       return val<min_value?min_value:val>max_value?max_value:val;
12022     }
12023 
12024     Tfloat _cubic_atX(const float fx, const int y, const int z, const int c,
12025                       const Tfloat min_value, const Tfloat max_value) const {
12026       const Tfloat val = _cubic_atX(fx,y,z,c);
12027       return val<min_value?min_value:val>max_value?max_value:val;
12028     }
12029 
12030     //! Get pixel value, using cubic interpolation and Dirichlet boundary conditions for the X and Y-coordinates.
12031     /**
12032        Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking
12033        are achieved both for X and Y-coordinates.
12034     **/
12035     Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T out_value) const {
12036       const int
12037         x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2,
12038         y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2;
12039       const float dx = fx - x, dy = fy - y;
12040       const Tfloat
12041         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),
12042         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)),
12043         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),
12044         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)),
12045         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),
12046         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)),
12047         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),
12048         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));
12049       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));
12050     }
12051 
12052     //! Get damped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X and Y-coordinates.
12053     /**
12054        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.
12055     **/
12056     Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T out_value,
12057                       const Tfloat min_value, const Tfloat max_value) const {
12058       const Tfloat val = cubic_atXY(fx,fy,z,c,out_value);
12059       return val<min_value?min_value:val>max_value?max_value:val;
12060     }
12061 
12062     //! Get pixel value, using cubic interpolation and Neumann boundary conditions for the X and Y-coordinates.
12063     /**
12064        Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking
12065        are achieved for both X and Y-coordinates.
12066        \note
12067        - 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).
12068     **/
12069     Tfloat cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
12070       if (is_empty())
12071         throw CImgInstanceException(_cimg_instance
12072                                     "cubic_atXY() : Empty instance.",
12073                                     cimg_instance);
12074       return _cubic_atXY(fx,fy,z,c);
12075     }
12076 
12077     Tfloat _cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
12078       const float
12079         nfx = fx<0?0:(fx>_width-1?_width-1:fx),
12080         nfy = fy<0?0:(fy>_height-1?_height-1:fy);
12081       const int x = (int)nfx, y = (int)nfy;
12082       const float dx = nfx - x, dy = nfy - y;
12083       const int
12084         px = x-1<0?0:x-1, nx = dx>0?x+1:x, ax = x+2>=width()?width()-1:x+2,
12085         py = y-1<0?0:y-1, ny = dy>0?y+1:y, ay = y+2>=height()?height()-1:y+2;
12086       const Tfloat
12087         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),
12088         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)),
12089         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),
12090         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)),
12091         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),
12092         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)),
12093         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),
12094         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));
12095       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));
12096     }
12097 
12098     //! Get damped pixel value, using cubic interpolation and Neumann boundary conditions for the X and Y-coordinates.
12099     /**
12100        Similar to cubic_atXY(float,float,int,int) const, except that you can specify the authorized minimum and maximum of the returned value.
12101     **/
12102     Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c,
12103                       const Tfloat min_value, const Tfloat max_value) const {
12104       const Tfloat val = cubic_atXY(fx,fy,z,c);
12105       return val<min_value?min_value:val>max_value?max_value:val;
12106     }
12107 
12108     Tfloat _cubic_atXY(const float fx, const float fy, const int z, const int c,
12109                        const Tfloat min_value, const Tfloat max_value) const {
12110       const Tfloat val = _cubic_atXY(fx,fy,z,c);
12111       return val<min_value?min_value:val>max_value?max_value:val;
12112     }
12113 
12114     //! Get pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates.
12115     /**
12116        Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking
12117        are achieved both for X,Y and Z-coordinates.
12118     **/
12119     Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T out_value) const {
12120       const int
12121         x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2,
12122         y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2,
12123         z = (int)fz - (fz>=0?0:1), pz = z - 1, nz = z + 1, az = z + 2;
12124       const float dx = fx - x, dy = fy - y, dz = fz - z;
12125       const Tfloat
12126         Ippp = (Tfloat)atXYZ(px,py,pz,c,out_value), Icpp = (Tfloat)atXYZ(x,py,pz,c,out_value),
12127         Inpp = (Tfloat)atXYZ(nx,py,pz,c,out_value), Iapp = (Tfloat)atXYZ(ax,py,pz,c,out_value),
12128         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)),
12129         Ipcp = (Tfloat)atXYZ(px,y,pz,c,out_value),  Iccp = (Tfloat)atXYZ(x, y,pz,c,out_value),
12130         Incp = (Tfloat)atXYZ(nx,y,pz,c,out_value),  Iacp = (Tfloat)atXYZ(ax,y,pz,c,out_value),
12131         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)),
12132         Ipnp = (Tfloat)atXYZ(px,ny,pz,c,out_value), Icnp = (Tfloat)atXYZ(x,ny,pz,c,out_value),
12133         Innp = (Tfloat)atXYZ(nx,ny,pz,c,out_value), Ianp = (Tfloat)atXYZ(ax,ny,pz,c,out_value),
12134         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)),
12135         Ipap = (Tfloat)atXYZ(px,ay,pz,c,out_value), Icap = (Tfloat)atXYZ(x,ay,pz,c,out_value),
12136         Inap = (Tfloat)atXYZ(nx,ay,pz,c,out_value), Iaap = (Tfloat)atXYZ(ax,ay,pz,c,out_value),
12137         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)),
12138         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)),
12139         Ippc = (Tfloat)atXYZ(px,py,z,c,out_value), Icpc = (Tfloat)atXYZ(x,py,z,c,out_value),
12140         Inpc = (Tfloat)atXYZ(nx,py,z,c,out_value), Iapc = (Tfloat)atXYZ(ax,py,z,c,out_value),
12141         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)),
12142         Ipcc = (Tfloat)atXYZ(px,y,z,c,out_value),  Iccc = (Tfloat)atXYZ(x, y,z,c,out_value),
12143         Incc = (Tfloat)atXYZ(nx,y,z,c,out_value),  Iacc = (Tfloat)atXYZ(ax,y,z,c,out_value),
12144         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)),
12145         Ipnc = (Tfloat)atXYZ(px,ny,z,c,out_value), Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value),
12146         Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value), Ianc = (Tfloat)atXYZ(ax,ny,z,c,out_value),
12147         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)),
12148         Ipac = (Tfloat)atXYZ(px,ay,z,c,out_value), Icac = (Tfloat)atXYZ(x,ay,z,c,out_value),
12149         Inac = (Tfloat)atXYZ(nx,ay,z,c,out_value), Iaac = (Tfloat)atXYZ(ax,ay,z,c,out_value),
12150         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)),
12151         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)),
12152         Ippn = (Tfloat)atXYZ(px,py,nz,c,out_value), Icpn = (Tfloat)atXYZ(x,py,nz,c,out_value),
12153         Inpn = (Tfloat)atXYZ(nx,py,nz,c,out_value), Iapn = (Tfloat)atXYZ(ax,py,nz,c,out_value),
12154         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)),
12155         Ipcn = (Tfloat)atXYZ(px,y,nz,c,out_value),  Iccn = (Tfloat)atXYZ(x, y,nz,c,out_value),
12156         Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value),  Iacn = (Tfloat)atXYZ(ax,y,nz,c,out_value),
12157         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)),
12158         Ipnn = (Tfloat)atXYZ(px,ny,nz,c,out_value), Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value),
12159         Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value), Iann = (Tfloat)atXYZ(ax,ny,nz,c,out_value),
12160         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)),
12161         Ipan = (Tfloat)atXYZ(px,ay,nz,c,out_value), Ican = (Tfloat)atXYZ(x,ay,nz,c,out_value),
12162         Inan = (Tfloat)atXYZ(nx,ay,nz,c,out_value), Iaan = (Tfloat)atXYZ(ax,ay,nz,c,out_value),
12163         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)),
12164         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)),
12165         Ippa = (Tfloat)atXYZ(px,py,az,c,out_value), Icpa = (Tfloat)atXYZ(x,py,az,c,out_value),
12166         Inpa = (Tfloat)atXYZ(nx,py,az,c,out_value), Iapa = (Tfloat)atXYZ(ax,py,az,c,out_value),
12167         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)),
12168         Ipca = (Tfloat)atXYZ(px,y,az,c,out_value),  Icca = (Tfloat)atXYZ(x, y,az,c,out_value),
12169         Inca = (Tfloat)atXYZ(nx,y,az,c,out_value),  Iaca = (Tfloat)atXYZ(ax,y,az,c,out_value),
12170         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)),
12171         Ipna = (Tfloat)atXYZ(px,ny,az,c,out_value), Icna = (Tfloat)atXYZ(x,ny,az,c,out_value),
12172         Inna = (Tfloat)atXYZ(nx,ny,az,c,out_value), Iana = (Tfloat)atXYZ(ax,ny,az,c,out_value),
12173         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)),
12174         Ipaa = (Tfloat)atXYZ(px,ay,az,c,out_value), Icaa = (Tfloat)atXYZ(x,ay,az,c,out_value),
12175         Inaa = (Tfloat)atXYZ(nx,ay,az,c,out_value), Iaaa = (Tfloat)atXYZ(ax,ay,az,c,out_value),
12176         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)),
12177         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));
12178       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));
12179     }
12180 
12181     //! Get damped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates.
12182     /**
12183        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.
12184     **/
12185     Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T out_value,
12186                        const Tfloat min_value, const Tfloat max_value) const {
12187       const Tfloat val = cubic_atXYZ(fx,fy,fz,c,out_value);
12188       return val<min_value?min_value:val>max_value?max_value:val;
12189     }
12190 
12191     //! Get pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y and Z-coordinates.
12192     /**
12193        Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking
12194        are achieved both for X,Y and Z-coordinates.
12195        \note
12196        - 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).
12197     **/
12198     Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const {
12199       if (is_empty())
12200         throw CImgInstanceException(_cimg_instance
12201                                     "cubic_atXYZ() : Empty instance.",
12202                                     cimg_instance);
12203       return _cubic_atXYZ(fx,fy,fz,c);
12204     }
12205 
12206     Tfloat _cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const {
12207       const float
12208         nfx = fx<0?0:(fx>_width-1?_width-1:fx),
12209         nfy = fy<0?0:(fy>_height-1?_height-1:fy),
12210         nfz = fz<0?0:(fz>_depth-1?_depth-1:fz);
12211       const int x = (int)nfx, y = (int)nfy, z = (int)nfz;
12212       const float dx = nfx - x, dy = nfy - y, dz = nfz - z;
12213       const int
12214         px = x-1<0?0:x-1, nx = dx>0?x+1:x, ax = x+2>=width()?width()-1:x+2,
12215         py = y-1<0?0:y-1, ny = dy>0?y+1:y, ay = y+2>=height()?height()-1:y+2,
12216         pz = z-1<0?0:z-1, nz = dz>0?z+1:z, az = z+2>=depth()?depth()-1:z+2;
12217       const Tfloat
12218         Ippp = (Tfloat)(*this)(px,py,pz,c), Icpp = (Tfloat)(*this)(x,py,pz,c),
12219         Inpp = (Tfloat)(*this)(nx,py,pz,c), Iapp = (Tfloat)(*this)(ax,py,pz,c),
12220         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)),
12221         Ipcp = (Tfloat)(*this)(px,y,pz,c),  Iccp = (Tfloat)(*this)(x, y,pz,c),
12222         Incp = (Tfloat)(*this)(nx,y,pz,c),  Iacp = (Tfloat)(*this)(ax,y,pz,c),
12223         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)),
12224         Ipnp = (Tfloat)(*this)(px,ny,pz,c), Icnp = (Tfloat)(*this)(x,ny,pz,c),
12225         Innp = (Tfloat)(*this)(nx,ny,pz,c), Ianp = (Tfloat)(*this)(ax,ny,pz,c),
12226         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)),
12227         Ipap = (Tfloat)(*this)(px,ay,pz,c), Icap = (Tfloat)(*this)(x,ay,pz,c),
12228         Inap = (Tfloat)(*this)(nx,ay,pz,c), Iaap = (Tfloat)(*this)(ax,ay,pz,c),
12229         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)),
12230         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)),
12231         Ippc = (Tfloat)(*this)(px,py,z,c), Icpc = (Tfloat)(*this)(x,py,z,c),
12232         Inpc = (Tfloat)(*this)(nx,py,z,c), Iapc = (Tfloat)(*this)(ax,py,z,c),
12233         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)),
12234         Ipcc = (Tfloat)(*this)(px,y,z,c),  Iccc = (Tfloat)(*this)(x, y,z,c),
12235         Incc = (Tfloat)(*this)(nx,y,z,c),  Iacc = (Tfloat)(*this)(ax,y,z,c),
12236         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)),
12237         Ipnc = (Tfloat)(*this)(px,ny,z,c), Icnc = (Tfloat)(*this)(x,ny,z,c),
12238         Innc = (Tfloat)(*this)(nx,ny,z,c), Ianc = (Tfloat)(*this)(ax,ny,z,c),
12239         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)),
12240         Ipac = (Tfloat)(*this)(px,ay,z,c), Icac = (Tfloat)(*this)(x,ay,z,c),
12241         Inac = (Tfloat)(*this)(nx,ay,z,c), Iaac = (Tfloat)(*this)(ax,ay,z,c),
12242         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)),
12243         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)),
12244         Ippn = (Tfloat)(*this)(px,py,nz,c), Icpn = (Tfloat)(*this)(x,py,nz,c),
12245         Inpn = (Tfloat)(*this)(nx,py,nz,c), Iapn = (Tfloat)(*this)(ax,py,nz,c),
12246         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)),
12247         Ipcn = (Tfloat)(*this)(px,y,nz,c),  Iccn = (Tfloat)(*this)(x, y,nz,c),
12248         Incn = (Tfloat)(*this)(nx,y,nz,c),  Iacn = (Tfloat)(*this)(ax,y,nz,c),
12249         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)),
12250         Ipnn = (Tfloat)(*this)(px,ny,nz,c), Icnn = (Tfloat)(*this)(x,ny,nz,c),
12251         Innn = (Tfloat)(*this)(nx,ny,nz,c), Iann = (Tfloat)(*this)(ax,ny,nz,c),
12252         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)),
12253         Ipan = (Tfloat)(*this)(px,ay,nz,c), Ican = (Tfloat)(*this)(x,ay,nz,c),
12254         Inan = (Tfloat)(*this)(nx,ay,nz,c), Iaan = (Tfloat)(*this)(ax,ay,nz,c),
12255         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)),
12256         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)),
12257         Ippa = (Tfloat)(*this)(px,py,az,c), Icpa = (Tfloat)(*this)(x,py,az,c),
12258         Inpa = (Tfloat)(*this)(nx,py,az,c), Iapa = (Tfloat)(*this)(ax,py,az,c),
12259         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)),
12260         Ipca = (Tfloat)(*this)(px,y,az,c),  Icca = (Tfloat)(*this)(x, y,az,c),
12261         Inca = (Tfloat)(*this)(nx,y,az,c),  Iaca = (Tfloat)(*this)(ax,y,az,c),
12262         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)),
12263         Ipna = (Tfloat)(*this)(px,ny,az,c), Icna = (Tfloat)(*this)(x,ny,az,c),
12264         Inna = (Tfloat)(*this)(nx,ny,az,c), Iana = (Tfloat)(*this)(ax,ny,az,c),
12265         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)),
12266         Ipaa = (Tfloat)(*this)(px,ay,az,c), Icaa = (Tfloat)(*this)(x,ay,az,c),
12267         Inaa = (Tfloat)(*this)(nx,ay,az,c), Iaaa = (Tfloat)(*this)(ax,ay,az,c),
12268         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)),
12269         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));
12270       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));
12271     }
12272 
12273     //! Get damped pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y and Z-coordinates.
12274     /**
12275        Similar to cubic_atXYZ(float,float,float,int) const, except that you can specify the authorized minimum and maximum of the returned value.
12276     **/
12277     Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c,
12278                        const Tfloat min_value, const Tfloat max_value) const {
12279       const Tfloat val = cubic_atXYZ(fx,fy,fz,c);
12280       return val<min_value?min_value:val>max_value?max_value:val;
12281     }
12282 
12283     Tfloat _cubic_atXYZ(const float fx, const float fy, const float fz, const int c,
12284                         const Tfloat min_value, const Tfloat max_value) const {
12285       const Tfloat val = _cubic_atXYZ(fx,fy,fz,c);
12286       return val<min_value?min_value:val>max_value?max_value:val;
12287     }
12288 
12289     //! Set pixel value, using linear interpolation for the X and Y-coordinates.
12290     /**
12291        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
12292        amongst several neighbors if the pixel coordinates are indeed float-valued.
12293        \param value : Pixel value to set.
12294        \param fx : X-coordinate of the pixel value (float-valued).
12295        \param fy : Y-coordinate of the pixel value (float-valued).
12296        \param z : Z-coordinate of the pixel value.
12297        \param c : C-coordinate of the pixel value.
12298        \param is_added : Boolean telling if the pixel value is added to (\c true), or simply replace (\c false) the current image pixel(s).
12299        \return A reference to the current image instance.
12300        \note
12301        - If specified coordinates are outside image bounds, no operations are performed.
12302        \sa linear_atXY(),
12303            set_linear_atXYZ().
12304     **/
12305     CImg<T>& set_linear_atXY(const T& value, const float fx, const float fy=0, const int z=0, const int c=0,
12306                              const bool is_added=false) {
12307       const int
12308         x = (int)fx - (fx>=0?0:1), nx = x + 1,
12309         y = (int)fy - (fy>=0?0:1), ny = y + 1;
12310       const float
12311         dx = fx - x,
12312         dy = fy - y;
12313       if (z>=0 && z<depth() && c>=0 && c<spectrum()) {
12314         if (y>=0 && y<height()) {
12315           if (x>=0 && x<width()) {
12316             const float w1 = (1-dx)*(1-dy), w2 = is_added?1:(1-w1);
12317             (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
12318           }
12319           if (nx>=0 && nx<width()) {
12320             const float w1 = dx*(1-dy), w2 = is_added?1:(1-w1);
12321             (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
12322           }
12323         }
12324         if (ny>=0 && ny<height()) {
12325           if (x>=0 && x<width()) {
12326             const float w1 = (1-dx)*dy, w2 = is_added?1:(1-w1);
12327             (*this)(x,ny,z,c) = (T)(w1*value + w2*(*this)(x,ny,z,c));
12328           }
12329           if (nx>=0 && nx<width()) {
12330             const float w1 = dx*dy, w2 = is_added?1:(1-w1);
12331             (*this)(nx,ny,z,c) = (T)(w1*value + w2*(*this)(nx,ny,z,c));
12332           }
12333         }
12334       }
12335       return *this;
12336     }
12337 
12338     //! Set pixel value, using linear interpolation for the X,Y and Z-coordinates.
12339     /**
12340        Similar to set_linear_atXY(const T&,float,float,int,int,bool), except that the linear interpolation
12341        is achieved both for X,Y and Z-coordinates.
12342     **/
12343     CImg<T>& set_linear_atXYZ(const T& value, const float fx, const float fy=0, const float fz=0, const int c=0,
12344                               const bool is_added=false) {
12345       const int
12346         x = (int)fx - (fx>=0?0:1), nx = x + 1,
12347         y = (int)fy - (fy>=0?0:1), ny = y + 1,
12348         z = (int)fz - (fz>=0?0:1), nz = z + 1;
12349       const float
12350         dx = fx - x,
12351         dy = fy - y,
12352         dz = fz - z;
12353       if (c>=0 && c<spectrum()) {
12354         if (z>=0 && z<depth()) {
12355           if (y>=0 && y<height()) {
12356             if (x>=0 && x<width()) {
12357               const float w1 = (1-dx)*(1-dy)*(1-dz), w2 = is_added?1:(1-w1);
12358               (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
12359             }
12360             if (nx>=0 && nx<width()) {
12361               const float w1 = dx*(1-dy)*(1-dz), w2 = is_added?1:(1-w1);
12362               (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
12363             }
12364           }
12365           if (ny>=0 && ny<height()) {
12366             if (x>=0 && x<width()) {
12367               const float w1 = (1-dx)*dy*(1-dz), w2 = is_added?1:(1-w1);
12368               (*this)(x,ny,z,c) = (T)(w1*value + w2*(*this)(x,ny,z,c));
12369             }
12370             if (nx>=0 && nx<width()) {
12371               const float w1 = dx*dy*(1-dz), w2 = is_added?1:(1-w1);
12372               (*this)(nx,ny,z,c) = (T)(w1*value + w2*(*this)(nx,ny,z,c));
12373             }
12374           }
12375         }
12376         if (nz>=0 && nz<depth()) {
12377           if (y>=0 && y<height()) {
12378             if (x>=0 && x<width()) {
12379               const float w1 = (1-dx)*(1-dy), w2 = is_added?1:(1-w1);
12380               (*this)(x,y,nz,c) = (T)(w1*value + w2*(*this)(x,y,nz,c));
12381             }
12382             if (nx>=0 && nx<width()) {
12383               const float w1 = dx*(1-dy), w2 = is_added?1:(1-w1);
12384               (*this)(nx,y,nz,c) = (T)(w1*value + w2*(*this)(nx,y,nz,c));
12385             }
12386           }
12387           if (ny>=0 && ny<height()) {
12388             if (x>=0 && x<width()) {
12389               const float w1 = (1-dx)*dy, w2 = is_added?1:(1-w1);
12390               (*this)(x,ny,nz,c) = (T)(w1*value + w2*(*this)(x,ny,nz,c));
12391             }
12392             if (nx>=0 && nx<width()) {
12393               const float w1 = dx*dy, w2 = is_added?1:(1-w1);
12394               (*this)(nx,ny,nz,c) = (T)(w1*value + w2*(*this)(nx,ny,nz,c));
12395             }
12396           }
12397         }
12398       }
12399       return *this;
12400     }
12401 
12402     //! Get a C-string containing a list of all values of the image instance.
12403     /**
12404        Return a new \c CImg<char> image whose buffer data() is a \c char* string describing the list of all pixel values
12405        of the image instance (written in base 10), separated by specified \c separator character.
12406        \param separator : a \c char character which specifies the separator between values in the returned C-string.
12407        \param max_size : Maximum size of the returned image.
12408        \note
12409        - The returned image is never empty.
12410        - For an empty image instance, the returned string is <tt>""</tt>.
12411        - If \c max_size is equal to \c 0, there are no limits on the size of the returned string.
12412        - Otherwise, if the maximum number of string characters is exceeded, the value string is cut off
12413          and terminated by character \c '\0'. In that case, the returned image size is <tt>max_size + 1</tt>.
12414        \sa pixel_type().
12415     **/
12416     CImg<charT> value_string(const char separator=',', const unsigned int max_size=0) const {
12417       if (is_empty()) return CImg<charT>(1,1,1,1,0);
12418       CImgList<charT> items;
12419       char s_item[256] = { 0 };
12420       const T *ptrs = _data;
12421       unsigned int string_size = 0;
12422       for (unsigned int off = 0, siz = (unsigned int)size(); off<siz && string_size<=max_size; ++off) {
12423         const unsigned int printed_size = 1U + cimg_snprintf(s_item,sizeof(s_item),cimg::type<T>::format(),cimg::type<T>::format(*(ptrs++)));
12424         CImg<charT> item(s_item,printed_size);
12425         item[printed_size-1] = separator;
12426         item.move_to(items);
12427         if (max_size) string_size+=printed_size;
12428       }
12429       CImg<charT> res;
12430       (items>'x').move_to(res);
12431       if (max_size && res._width>max_size) res.crop(0,max_size);
12432       res.back() = 0;
12433       return res;
12434     }
12435 
12436     //@}
12437     //-------------------------------------
12438     //
12439     //! \name Instance Checking
12440     //@{
12441     //-------------------------------------
12442 
12443     //! Test shared state of the pixel buffer.
12444     /**
12445        Return \c true if image instance has a shared memory buffer, and \c false otherwise.
12446        \note
12447        - A shared image do not own his pixel buffer data() and will not deallocate it on destruction.
12448        - Most of the time, a \c CImg<T> image instance will \e not be shared.
12449        - A shared image can only be obtained by a limited set of constructors and methods (see list below).
12450        \sa CImg(const t *const,unsigned int,unsigned int,unsigned int,unsigned int,bool),
12451            CImg(const CImg<t>&),
12452            CImg(const CImg<t>&,bool),
12453            assign(const t *const,unsigned int,unsigned int,unsigned int,unsigned int,bool),
12454            get_shared_points(),
12455            get_shared_line(),
12456            get_shared_lines(),
12457            get_shared_plane(),
12458            get_shared_planes(),
12459            get_shared_channel(),
12460            get_shared_channels(),
12461            get_shared().
12462     **/
12463     bool is_shared() const {
12464       return _is_shared;
12465     }
12466 
12467     //! Test if image instance is empty.
12468     /**
12469        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
12470        and a pixel buffer pointer set to \c 0 (null pointer), and \c false otherwise.
12471        \sa CImg(),
12472            assign().
12473     **/
12474     bool is_empty() const {
12475       return !(_data && _width && _height && _depth && _spectrum);
12476     }
12477 
12478     //! Test if image width is equal to a specified value.
12479     /**
12480        Return \c true if image instance has \c size_x columns, and \c false otherwise.
12481        \param size_x : Desired width to test with.
12482        \note
12483        - Return the boolean <tt>width()==size_x</tt>.
12484        \sa is_sameX(const CImg<t>&) const,
12485            is_sameX(const CImgDisplay&) const,
12486            is_sameY(unsigned int) const,
12487            is_sameZ(unsigned int) const,
12488            is_sameC(unsigned int) const,
12489            is_sameXY(unsigned int,unsigned int) const,
12490            is_sameXYZ(unsigned int,unsigned int,unsigned int) const,
12491            is_sameXYZC(unsigned int,unsigned int,unsigned int,unsigned int) const.
12492     **/
12493     bool is_sameX(const unsigned int size_x) const {
12494       return (_width==size_x);
12495     }
12496 
12497     //! Test if image width is the same as that of another image.
12498     /**
12499        Return \c true if image instance has \c img.width() columns, and \c false otherwise.
12500        \param img : Input image to test width() with.
12501        \note
12502        - Return the boolean <tt>width()==img.width()</tt>.
12503        \sa is_sameX(unsigned int) const,
12504            is_sameX(const CImgDisplay&) const,
12505            is_sameY(const CImg<t>&) const,
12506            is_sameZ(const CImg<t>&) const,
12507            is_sameC(const CImg<t>&) const,
12508            is_sameXY(const CImg<t>&) const,
12509            is_sameXYZ(const CImg<t>&) const,
12510            is_sameXYZC(const CImg<t>&) const.
12511     **/
12512     template<typename t>
12513     bool is_sameX(const CImg<t>& img) const {
12514       return is_sameX(img._width);
12515     }
12516 
12517     //! Test if image width is the same as that of an existing display window.
12518     /**
12519        Return \c true if image instance has \c disp.width() columns, and \c false otherwise.
12520        \param disp : Input display window to test width() with.
12521        \note
12522        - Return the boolean <tt>width()==disp.width()</tt>.
12523        \sa is_sameX(unsigned int) const,
12524            is_sameX(const CImg<t>&) const,
12525            is_sameY(const CImgDisplay&) const,
12526            is_sameXY(const CImgDisplay&) const,
12527     **/
12528     bool is_sameX(const CImgDisplay& disp) const {
12529       return is_sameX((unsigned int)disp.width());
12530     }
12531 
12532     //! Test if image height is equal to a specified value.
12533     /**
12534        Similar to is_sameX(unsigned int) const, except that the test focuses on the image height().
12535     **/
12536     bool is_sameY(const unsigned int size_y) const {
12537       return (_height==size_y);
12538     }
12539 
12540     //! Test if image height is the same as that of another image.
12541     /**
12542        Similar to is_sameX(const CImg<t>&) const, except that the test focuses on the image height().
12543     **/
12544     template<typename t>
12545     bool is_sameY(const CImg<t>& img) const {
12546       return is_sameY(img._height);
12547     }
12548 
12549     //! Test if image height is the same as that of an existing display window.
12550     /**
12551        Similar to is_sameX(const CImgDisplay&) const, except that the test focuses on the image height().
12552     **/
12553     bool is_sameY(const CImgDisplay& disp) const {
12554       return is_sameY((unsigned int)disp.height());
12555     }
12556 
12557     //! Test if image depth is equal to a specified value.
12558     /**
12559        Similar to is_sameX(unsigned int) const, except that the test focuses on the image depth().
12560     **/
12561     bool is_sameZ(const unsigned int size_z) const {
12562       return (_depth==size_z);
12563     }
12564 
12565     //! Test if image depth is the same as that of another image.
12566     /**
12567        Similar to is_sameX(const CImg<t>&) const, except that the test focuses on the image depth().
12568     **/
12569     template<typename t>
12570     bool is_sameZ(const CImg<t>& img) const {
12571       return is_sameZ(img._depth);
12572     }
12573 
12574     //! Test if image spectrum is equal to a specified value.
12575     /**
12576        Similar to is_sameX(unsigned int) const, except that the test focuses on the image spectrum().
12577     **/
12578     bool is_sameC(const unsigned int size_c) const {
12579       return (_spectrum==size_c);
12580     }
12581 
12582     //! Test if image spectrum is the same as that of another image.
12583     /**
12584        Similar to is_sameX(const CImg<t>&) const, except that the test focuses on the image spectrum().
12585     **/
12586     template<typename t>
12587     bool is_sameC(const CImg<t>& img) const {
12588       return is_sameC(img._spectrum);
12589     }
12590 
12591     //! Test if image width and height are equal to specified values.
12592     /**
12593        Test if is_sameX(unsigned int) const and is_sameY(unsigned int) const are both verified.
12594     **/
12595     bool is_sameXY(const unsigned int size_x, const unsigned int size_y) const {
12596       return (is_sameX(size_x) && is_sameY(size_y));
12597     }
12598 
12599     //! Test if image width and height are the same as that of another image.
12600     /**
12601        Test if is_sameX(const CImg<t>&) const and is_sameY(const CImg<t>&) const are both verified.
12602     **/
12603     template<typename t>
12604     bool is_sameXY(const CImg<t>& img) const {
12605       return (is_sameX(img) && is_sameY(img));
12606     }
12607 
12608     //! Test if image width and height are the same as that of an existing display window.
12609     /**
12610        Test if is_sameX(const CImgDisplay&) const and is_sameY(const CImgDisplay&) const are both verified.
12611     **/
12612     bool is_sameXY(const CImgDisplay& disp) const {
12613       return (is_sameX(disp) && is_sameY(disp));
12614     }
12615 
12616     //! Test if image width and depth are equal to specified values.
12617     /**
12618        Test if is_sameX(unsigned int) const and is_sameZ(unsigned int) const are both verified.
12619     **/
12620     bool is_sameXZ(const unsigned int size_x, const unsigned int size_z) const {
12621       return (is_sameX(size_x) && is_sameZ(size_z));
12622     }
12623 
12624     //! Test if image width and depth are the same as that of another image.
12625     /**
12626        Test if is_sameX(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified.
12627     **/
12628     template<typename t>
12629     bool is_sameXZ(const CImg<t>& img) const {
12630       return (is_sameX(img) && is_sameZ(img));
12631     }
12632 
12633     //! Test if image width and spectrum are equal to specified values.
12634     /**
12635        Test if is_sameX(unsigned int) const and is_sameC(unsigned int) const are both verified.
12636     **/
12637     bool is_sameXC(const unsigned int size_x, const unsigned int size_c) const {
12638       return (is_sameX(size_x) && is_sameC(size_c));
12639     }
12640 
12641     //! Test if image width and spectrum are the same as that of another image.
12642     /**
12643        Test if is_sameX(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
12644     **/
12645     template<typename t>
12646     bool is_sameXC(const CImg<t>& img) const {
12647       return (is_sameX(img) && is_sameC(img));
12648     }
12649 
12650     //! Test if image height and depth are equal to specified values.
12651     /**
12652        Test if is_sameY(unsigned int) const and is_sameZ(unsigned int) const are both verified.
12653     **/
12654     bool is_sameYZ(const unsigned int size_y, const unsigned int size_z) const {
12655       return (is_sameY(size_y) && is_sameZ(size_z));
12656     }
12657 
12658     //! Test if image height and depth are the same as that of another image.
12659     /**
12660        Test if is_sameY(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified.
12661     **/
12662     template<typename t>
12663     bool is_sameYZ(const CImg<t>& img) const {
12664       return (is_sameY(img) && is_sameZ(img));
12665     }
12666 
12667     //! Test if image height and spectrum are equal to specified values.
12668     /**
12669        Test if is_sameY(unsigned int) const and is_sameC(unsigned int) const are both verified.
12670     **/
12671     bool is_sameYC(const unsigned int size_y, const unsigned int size_c) const {
12672       return (is_sameY(size_y) && is_sameC(size_c));
12673     }
12674 
12675     //! Test if image height and spectrum are the same as that of another image.
12676     /**
12677        Test if is_sameY(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
12678     **/
12679     template<typename t>
12680     bool is_sameYC(const CImg<t>& img) const {
12681       return (is_sameY(img) && is_sameC(img));
12682     }
12683 
12684     //! Test if image depth and spectrum are equal to specified values.
12685     /**
12686        Test if is_sameZ(unsigned int) const and is_sameC(unsigned int) const are both verified.
12687     **/
12688     bool is_sameZC(const unsigned int size_z, const unsigned int size_c) const {
12689       return (is_sameZ(size_z) && is_sameC(size_c));
12690     }
12691 
12692     //! Test if image depth and spectrum are the same as that of another image.
12693     /**
12694        Test if is_sameZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
12695     **/
12696     template<typename t>
12697     bool is_sameZC(const CImg<t>& img) const {
12698       return (is_sameZ(img) && is_sameC(img));
12699     }
12700 
12701     //! Test if image width, height and depth are equal to specified values.
12702     /**
12703        Test if is_sameXY(unsigned int,unsigned int) const and is_sameZ(unsigned int) const are both verified.
12704     **/
12705     bool is_sameXYZ(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z) const {
12706       return (is_sameXY(size_x,size_y) && is_sameZ(size_z));
12707     }
12708 
12709     //! Test if image width, height and depth are the same as that of another image.
12710     /**
12711        Test if is_sameXY(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified.
12712     **/
12713     template<typename t>
12714     bool is_sameXYZ(const CImg<t>& img) const {
12715       return (is_sameXY(img) && is_sameZ(img));
12716     }
12717 
12718     //! Test if image width, height and spectrum are equal to specified values.
12719     /**
12720        Test if is_sameXY(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
12721     **/
12722     bool is_sameXYC(const unsigned int size_x, const unsigned int size_y, const unsigned int size_c) const {
12723       return (is_sameXY(size_x,size_y) && is_sameC(size_c));
12724     }
12725 
12726     //! Test if image width, height and spectrum are the same as that of another image.
12727     /**
12728        Test if is_sameXY(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
12729     **/
12730     template<typename t>
12731     bool is_sameXYC(const CImg<t>& img) const {
12732       return (is_sameXY(img) && is_sameC(img));
12733     }
12734 
12735     //! Test if image width, depth and spectrum are equal to specified values.
12736     /**
12737        Test if is_sameXZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
12738     **/
12739     bool is_sameXZC(const unsigned int size_x, const unsigned int size_z, const unsigned int size_c) const {
12740       return (is_sameXZ(size_x,size_z) && is_sameC(size_c));
12741     }
12742 
12743     //! Test if image width, depth and spectrum are the same as that of another image.
12744     /**
12745        Test if is_sameXZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
12746     **/
12747     template<typename t>
12748     bool is_sameXZC(const CImg<t>& img) const {
12749       return (is_sameXZ(img) && is_sameC(img));
12750     }
12751 
12752     //! Test if image height, depth and spectrum are equal to specified values.
12753     /**
12754        Test if is_sameYZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
12755     **/
12756     bool is_sameYZC(const unsigned int size_y, const unsigned int size_z, const unsigned int size_c) const {
12757       return (is_sameYZ(size_y,size_z) && is_sameC(size_c));
12758     }
12759 
12760     //! Test if image height, depth and spectrum are the same as that of another image.
12761     /**
12762        Test if is_sameYZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
12763     **/
12764     template<typename t>
12765     bool is_sameYZC(const CImg<t>& img) const {
12766       return (is_sameYZ(img) && is_sameC(img));
12767     }
12768 
12769     //! Test if image width, height, depth and spectrum are equal to specified values.
12770     /**
12771        Test if is_sameXYZ(unsigned int,unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
12772     **/
12773     bool is_sameXYZC(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c) const {
12774       return (is_sameXYZ(size_x,size_y,size_z) && is_sameC(size_c));
12775     }
12776 
12777     //! Test if image width, height, depth and spectrum are the same as that of another image.
12778     /**
12779        Test if is_sameXYZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
12780     **/
12781     template<typename t>
12782     bool is_sameXYZC(const CImg<t>& img) const {
12783       return (is_sameXYZ(img) && is_sameC(img));
12784     }
12785 
12786     //! Test if specified coordinates are inside image bounds.
12787     /**
12788        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.
12789        \param x : X-coordinate of the pixel value.
12790        \param y : Y-coordinate of the pixel value.
12791        \param z : Z-coordinate of the pixel value.
12792        \param c : C-coordinate of the pixel value.
12793        \note
12794        - Return \c true only if all these conditions are verified :
12795          - The image instance is \e not empty.
12796          - <tt>0<=x<=\ref width()-1</tt>.
12797          - <tt>0<=y<=\ref height()-1</tt>.
12798          - <tt>0<=z<=\ref depth()-1</tt>.
12799          - <tt>0<=c<=\ref spectrum()-1</tt>.
12800        \sa contains(const T&,t&,t&,t&,t&) const,
12801            contains(const T&,t&,t&,t&) const,
12802            contains(const T&,t&,t&) const,
12803            contains(const T&,t&) const,
12804            contains(const T&) const.
12805     **/
12806     bool containsXYZC(const int x, const int y=0, const int z=0, const int c=0) const {
12807       return !is_empty() && x>=0 && x<width() && y>=0 && y<height() && z>=0 && z<depth() && c>=0 && c<spectrum();
12808     }
12809 
12810     //! Test if pixel value is inside image bounds and get its X,Y,Z and C-coordinates.
12811     /**
12812        Return \c true, if specified reference refers to a pixel value inside bounds of the image instance, and \c false otherwise.
12813        \param pixel : Reference to pixel value to test.
12814        \param[out] x : X-coordinate of the pixel value, if test succeeds.
12815        \param[out] y : Y-coordinate of the pixel value, if test succeeds.
12816        \param[out] z : Z-coordinate of the pixel value, if test succeeds.
12817        \param[out] c : C-coordinate of the pixel value, if test succeeds.
12818        \note
12819        - Useful to convert an offset to a  buffer value into pixel value coordinates :
12820        \code
12821        const CImg<float> img(100,100,1,3);      // Construct a 100x100 RGB color image.
12822        const unsigned int offset = 1249;        // Offset to the pixel (49,12,0,0).
12823        unsigned int x,y,z,c;
12824        if (img.contains(img[offset],x,y,z,c)) { // Convert offset to (x,y,z,c) coordinates.
12825          std::printf("Offset %u refers to pixel located at (%u,%u,%u,%u).\n",
12826                      offset,x,y,z,c);
12827        }
12828        \endcode
12829        \sa containsXYZC(int,int,int,int) const,
12830            contains(const T&,t&,t&,t&) const,
12831            contains(const T&,t&,t&) const,
12832            contains(const T&,t&) const,
12833            contains(const T&) const.
12834     **/
12835     template<typename t>
12836     bool contains(const T& pixel, t& x, t& y, t& z, t& c) const {
12837       const unsigned int wh = _width*_height, whd = wh*_depth, siz = whd*_spectrum;
12838       const T *const ppixel = &pixel;
12839       if (is_empty() || ppixel<_data || ppixel>=_data+siz) return false;
12840       unsigned int off = (unsigned int)(ppixel - _data);
12841       const unsigned int nc = off/whd;
12842       off%=whd;
12843       const unsigned int nz = off/wh;
12844       off%=wh;
12845       const unsigned int ny = off/_width, nx = off%_width;
12846       x = (t)nx; y = (t)ny; z = (t)nz; c = (t)nc;
12847       return true;
12848     }
12849 
12850     //! Test if pixel value is inside image bounds and get its X,Y and Z-coordinates.
12851     /**
12852        Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X,Y and Z-coordinates are set.
12853     **/
12854     template<typename t>
12855     bool contains(const T& pixel, t& x, t& y, t& z) const {
12856       const unsigned int wh = _width*_height, whd = wh*_depth, siz = whd*_spectrum;
12857       const T *const ppixel = &pixel;
12858       if (is_empty() || ppixel<_data || ppixel>=_data+siz) return false;
12859       unsigned int off = ((unsigned int)(ppixel - _data))%whd;
12860       const unsigned int nz = off/wh;
12861       off%=wh;
12862       const unsigned int ny = off/_width, nx = off%_width;
12863       x = (t)nx; y = (t)ny; z = (t)nz;
12864       return true;
12865     }
12866 
12867     //! Test if pixel value is inside image bounds and get its X and Y-coordinates.
12868     /**
12869        Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X and Y-coordinates are set.
12870     **/
12871     template<typename t>
12872     bool contains(const T& pixel, t& x, t& y) const {
12873       const unsigned int wh = _width*_height, siz = wh*_depth*_spectrum;
12874       const T *const ppixel = &pixel;
12875       if (is_empty() || ppixel<_data || ppixel>=_data+siz) return false;
12876       unsigned int off = ((unsigned int)(ppixel - _data))%wh;
12877       const unsigned int ny = off/_width, nx = off%_width;
12878       x = (t)nx; y = (t)ny;
12879       return true;
12880     }
12881 
12882     //! Test if pixel value is inside image bounds and get its X-coordinate.
12883     /**
12884        Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X-coordinate is set.
12885     **/
12886     template<typename t>
12887     bool contains(const T& pixel, t& x) const {
12888       const T *const ppixel = &pixel;
12889       if (is_empty() || ppixel<_data || ppixel>=_data+size()) return false;
12890       x = (t)(((unsigned int)(ppixel - _data))%_width);
12891       return true;
12892     }
12893 
12894     //! Test if pixel value is inside image bounds.
12895     /**
12896        Similar to contains(const T&,t&,t&,t&,t&) const, except that no pixel coordinates are set.
12897     **/
12898     bool contains(const T& pixel) const {
12899       const T *const ppixel = &pixel;
12900       return !is_empty() && ppixel>=_data && ppixel<_data + size();
12901     }
12902 
12903     //! Test if pixel buffers of instance and input images overlap.
12904     /**
12905        Return \c true, if pixel buffers attached to image instance and input image \c img overlap, and \c false otherwise.
12906        \param img : Input image to compare with.
12907        \note
12908        - Buffer overlapping may happen when manipulating \e shared images.
12909        - If two image buffers overlap, operating on one of the image will probably modify the other one.
12910        - Most of the time, \c CImg<T> instances are \e non-shared and do not overlap between each others.
12911        \par Sample code :
12912        \code
12913        const CImg<float>
12914          img1("reference.jpg"),             // Load RGB-color image.
12915          img2 = img1.get_shared_channel(1); // Get shared version of the green channel.
12916        if (img1.is_overlapped(img2)) {      // Test succeeds, 'img1' and 'img2' overlaps.
12917          std::printf("Buffers overlap !\n");
12918        }
12919        \endcode
12920        \sa is_shared().
12921     **/
12922     template<typename t>
12923     bool is_overlapped(const CImg<t>& img) const {
12924       const unsigned int csiz = size(), isiz = img.size();
12925       return !((void*)(_data + csiz)<=(void*)img._data || (void*)_data>=(void*)(img._data + isiz));
12926     }
12927 
12928     //! Test if the set {\c *this,\c primitives,\c colors,\c opacities} defines a valid 3d object.
12929     /**
12930        Return \c true is the 3d object represented by the set {\c *this,\c primitives,\c colors,\c opacities} defines a
12931        valid 3d object, and \c false otherwise. The vertex coordinates are defined by the instance image.
12932        \param primitives : List of primitives of the 3d object.
12933        \param colors : List of colors of the 3d object.
12934        \param opacities : List (or image) of opacities of the 3d object.
12935        \param is_full_check : Boolean telling if full checking of the 3d object must be performed.
12936        \param[out] error_message : C-string to contain the error message, if the test does not succeed.
12937        \note
12938        - Set \c is_full_checking to \c false to speed-up the 3d object checking. In this case, only the size of
12939          each 3d object component is checked.
12940        - Size of the string \c error_message should be at least 128-bytes long, to be able to contain the error message.
12941        \sa is_CImg3d(),
12942            draw_object3d(),
12943            display_object3d().
12944     **/
12945     template<typename tp, typename tc, typename to>
12946     bool is_object3d(const CImgList<tp>& primitives,
12947                      const CImgList<tc>& colors,
12948                      const to& opacities,
12949                      const bool is_full_check=true,
12950                      char *const error_message=0) const {
12951       if (error_message) *error_message = 0;
12952 
12953       // Check consistency for the particular case of an empty 3d object.
12954       if (is_empty()) {
12955         if (primitives || colors || opacities) {
12956           if (error_message) std::sprintf(error_message,
12957                                           "3d object has no vertices but %u primitives, %u colors and %u opacities",
12958                                           primitives._width,colors._width,opacities.size());
12959           return false;
12960         }
12961         return true;
12962       }
12963 
12964       // Check consistency of vertices.
12965       if (_height!=3 || _depth>1 || _spectrum>1) { // Check vertices dimensions.
12966         if (error_message) std::sprintf(error_message,
12967                                         "3d object (%u,%u) has invalid vertices dimensions (%u,%u,%u,%u)",
12968                                         _width,primitives._width,_width,_height,_depth,_spectrum);
12969         return false;
12970       }
12971       if (colors._width>primitives._width+1) {
12972         if (error_message) std::sprintf(error_message,
12973                                         "3d object (%u,%u) defines %u colors",
12974                                         _width,primitives._width,colors._width);
12975         return false;
12976       }
12977       if (opacities.size()>primitives._width) {
12978         if (error_message) std::sprintf(error_message,
12979                                         "3d object (%u,%u) defines %u opacities",
12980                                         _width,primitives._width,opacities.size());
12981         return false;
12982       }
12983       if (!is_full_check) return true;
12984 
12985       // Check consistency of primitives.
12986       cimglist_for(primitives,l) {
12987         const CImg<tp>& primitive = primitives[l];
12988         const unsigned int psiz = primitive.size();
12989         switch (psiz) {
12990         case 1 : { // Point.
12991           const unsigned int i0 = (unsigned int)primitive(0);
12992           if (i0>=_width) {
12993             if (error_message) std::sprintf(error_message,
12994                                             "3d object (%u,%u) refers to invalid vertex indice %u in point primitive %u",
12995                                             _width,primitives._width,i0,l);
12996             return false;
12997           }
12998         } break;
12999         case 5 : { // Sphere.
13000           const unsigned int
13001             i0 = (unsigned int)primitive(0),
13002             i1 = (unsigned int)primitive(1);
13003           if (i0>=_width || i1>=_width) {
13004             if (error_message) std::sprintf(error_message,
13005                                             "3d object (%u,%u) refers to invalid vertex indices (%u,%u) in sphere primitive %u",
13006                                             _width,primitives._width,i0,i1,l);
13007             return false;
13008           }
13009         } break;
13010         case 2 : // Segment.
13011         case 6 : {
13012           const unsigned int
13013             i0 = (unsigned int)primitive(0),
13014             i1 = (unsigned int)primitive(1);
13015           if (i0>=_width || i1>=_width) {
13016             if (error_message) std::sprintf(error_message,
13017                                             "3d object (%u,%u) refers to invalid vertex indices (%u,%u) in segment primitive %u",
13018                                             _width,primitives._width,i0,i1,l);
13019             return false;
13020           }
13021         } break;
13022         case 3 : // Triangle.
13023         case 9 : {
13024           const unsigned int
13025             i0 = (unsigned int)primitive(0),
13026             i1 = (unsigned int)primitive(1),
13027             i2 = (unsigned int)primitive(2);
13028           if (i0>=_width || i1>=_width || i2>=_width) {
13029             if (error_message) std::sprintf(error_message,
13030                                             "3d object (%u,%u) refers to invalid vertex indices (%u,%u,%u) in triangle primitive %u",
13031                                             _width,primitives._width,i0,i1,i2,l);
13032             return false;
13033           }
13034         } break;
13035         case 4 : // Quadrangle.
13036         case 12 : {
13037           const unsigned int
13038             i0 = (unsigned int)primitive(0),
13039             i1 = (unsigned int)primitive(1),
13040             i2 = (unsigned int)primitive(2),
13041             i3 = (unsigned int)primitive(3);
13042           if (i0>=_width || i1>=_width || i2>=_width || i3>=_width) {
13043             if (error_message) std::sprintf(error_message,
13044                                             "3d object (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in quadrangle primitive %u",
13045                                             _width,primitives._width,i0,i1,i2,i3,l);
13046             return false;
13047           }
13048         } break;
13049         default :
13050           if (error_message) std::sprintf(error_message,
13051                                           "3d object has invalid primitive %u of size %u",
13052                                           l,psiz);
13053           return false;
13054         }
13055       }
13056 
13057       // Check consistency of colors.
13058       cimglist_for(colors,c) {
13059         const CImg<tc>& color = colors[c];
13060         if (!color) {
13061           if (error_message) std::sprintf(error_message,
13062                                           "3d object has empty color for primitive %u",
13063                                           c);
13064           return false;
13065         }
13066       }
13067 
13068       // Check consistency of light texture.
13069       if (colors._width>primitives._width) {
13070         const CImg<tc> &light = colors.back();
13071         if (!light || light._depth>1) {
13072           if (error_message) std::sprintf(error_message,
13073                                           "3d object has invalid light texture (%u,%u,%u,%u)",
13074                                           light._width,light._height,light._depth,light._spectrum);
13075           return false;
13076         }
13077       }
13078 
13079       return true;
13080     }
13081 
13082     //! Test if image instance represents a valid serialization of a 3d object.
13083     /**
13084        Return \c true if the image instance represents a valid serialization of a 3d object, and \c false otherwise.
13085        \param is_full_check : Boolean telling if full checking of the instance must be performed.
13086        \param[out] error_message : C-string to contain the error message, if the test does not succeed.
13087        \note
13088        - Set \c is_full_checking to \c false to speed-up the 3d object checking. In this case, only the size of
13089          each 3d object component is checked.
13090        - Size of the string \c error_message should be at least 128-bytes long, to be able to contain the error message.
13091        \sa is_object3d(),
13092            object3dtoCImg3d(const CImgList<tp>&,const CImgList<tc>&,const to&),
13093            draw_object3d(),
13094            display_object3d().
13095     **/
13096     bool is_CImg3d(const bool is_full_check=true, char *const error_message=0) const {
13097       if (error_message) *error_message = 0;
13098 
13099       // Check instance dimension and header.
13100       if (_width!=1 || _height<8 || _depth!=1 || _spectrum!=1) {
13101         if (error_message) std::sprintf(error_message,
13102                                         "CImg3d has invalid dimensions (%u,%u,%u,%u)",
13103                                         _width,_height,_depth,_spectrum);
13104         return false;
13105       }
13106       const T *ptrs = _data, *const ptre = end();
13107       if (!_is_CImg3d(*(ptrs++),'C') || !_is_CImg3d(*(ptrs++),'I') || !_is_CImg3d(*(ptrs++),'m') ||
13108           !_is_CImg3d(*(ptrs++),'g') || !_is_CImg3d(*(ptrs++),'3') || !_is_CImg3d(*(ptrs++),'d')) {
13109         if (error_message) std::sprintf(error_message,
13110                                         "CImg3d header not found");
13111         return false;
13112       }
13113       const unsigned int
13114         nb_points = cimg::float2uint((float)*(ptrs++)),
13115         nb_primitives = cimg::float2uint((float)*(ptrs++));
13116 
13117       // Check consistency of vertex data.
13118       if (!nb_points) {
13119         if (nb_primitives) {
13120           if (error_message) std::sprintf(error_message,
13121                                           "CImg3d has no vertices but %u primitives",
13122                                           nb_primitives);
13123           return false;
13124         }
13125         if (ptrs!=ptre) {
13126           if (error_message) std::sprintf(error_message,
13127                                           "CImg3d (%u,%u) is empty but contains %u byte%s more than expected",
13128                                           nb_points,nb_primitives,(unsigned int)(ptre-ptrs),(ptre-ptrs)>1?"s":"");
13129           return false;
13130         }
13131         return true;
13132       }
13133       ptrs+=3*nb_points;
13134       if (ptrs>ptre) {
13135         if (error_message) std::sprintf(error_message,
13136                                         "CImg3d (%u,%u) has incomplete vertex data",
13137                                         nb_points,nb_primitives);
13138         return false;
13139       }
13140       if (!is_full_check) return true;
13141 
13142       // Check consistency of primitive data.
13143       if (ptrs==ptre) {
13144         if (error_message) std::sprintf(error_message,
13145                                         "CImg3d (%u,%u) has no primitive data",
13146                                         nb_points,nb_primitives);
13147         return false;
13148       }
13149       for (unsigned int p = 0; p<nb_primitives; ++p) {
13150         const unsigned int nb_inds = (unsigned int)*(ptrs++);
13151         switch (nb_inds) {
13152         case 1 : { // Point.
13153           const unsigned int i0 = cimg::float2uint((float)*(ptrs++));
13154           if (i0>=nb_points) {
13155             if (error_message) std::sprintf(error_message,
13156                                             "CImg3d (%u,%u) refers to invalid vertex indice %u in point primitive %u",
13157                                             nb_points,nb_primitives,i0,p);
13158             return false;
13159           }
13160         } break;
13161         case 5 : { // Sphere.
13162           const unsigned int
13163             i0 = cimg::float2uint((float)*(ptrs++)),
13164             i1 = cimg::float2uint((float)*(ptrs++));
13165           ptrs+=3;
13166           if (i0>=nb_points || i1>=nb_points) {
13167             if (error_message) std::sprintf(error_message,
13168                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in sphere primitive %u",
13169                                             nb_points,nb_primitives,i0,i1,p);
13170             return false;
13171           }
13172         } break;
13173         case 2 : case 6 : { // Segment.
13174           const unsigned int
13175             i0 = cimg::float2uint((float)*(ptrs++)),
13176             i1 = cimg::float2uint((float)*(ptrs++));
13177           if (nb_inds==6) ptrs+=4;
13178           if (i0>=nb_points || i1>=nb_points) {
13179             if (error_message) std::sprintf(error_message,
13180                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in segment primitive %u",
13181                                             nb_points,nb_primitives,i0,i1,p);
13182             return false;
13183           }
13184         } break;
13185         case 3 : case 9 : { // Triangle.
13186           const unsigned int
13187             i0 = cimg::float2uint((float)*(ptrs++)),
13188             i1 = cimg::float2uint((float)*(ptrs++)),
13189             i2 = cimg::float2uint((float)*(ptrs++));
13190           if (nb_inds==9) ptrs+=6;
13191           if (i0>=nb_points || i1>=nb_points || i2>=nb_points) {
13192             if (error_message) std::sprintf(error_message,
13193                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u) in triangle primitive %u",
13194                                             nb_points,nb_primitives,i0,i1,i2,p);
13195             return false;
13196           }
13197         } break;
13198         case 4 : case 12 : { // Quadrangle.
13199           const unsigned int
13200             i0 = cimg::float2uint((float)*(ptrs++)),
13201             i1 = cimg::float2uint((float)*(ptrs++)),
13202             i2 = cimg::float2uint((float)*(ptrs++)),
13203             i3 = cimg::float2uint((float)*(ptrs++));
13204           if (nb_inds==12) ptrs+=8;
13205           if (i0>=nb_points || i1>=nb_points || i2>=nb_points || i3>=nb_points) {
13206             if (error_message) std::sprintf(error_message,
13207                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in quadrangle primitive %u",
13208                                             nb_points,nb_primitives,i0,i1,i2,i3,p);
13209             return false;
13210           }
13211         } break;
13212         default :
13213           if (error_message) std::sprintf(error_message,
13214                                           "CImg3d (%u,%u) has invalid primitive %u of size %u",
13215                                           nb_points,nb_primitives,p,nb_inds);
13216           return false;
13217         }
13218         if (ptrs>ptre) {
13219           if (error_message) std::sprintf(error_message,
13220                                           "CImg3d (%u,%u) has incomplete primitive data for primitive %u",
13221                                           nb_points,nb_primitives,p);
13222           return false;
13223         }
13224       }
13225 
13226       // Check consistency of color data.
13227       if (ptrs==ptre) {
13228         if (error_message) std::sprintf(error_message,
13229                                         "CImg3d (%u,%u) has no color/texture data",
13230                                         nb_points,nb_primitives);
13231         return false;
13232       }
13233       for (unsigned int c = 0; c<nb_primitives; ++c) {
13234         if ((int)*(ptrs++)!=-128) ptrs+=2;
13235         else if ((ptrs+=3)<ptre) {
13236           const unsigned int w = (unsigned int)*(ptrs-3), h = (unsigned int)*(ptrs-2), s = (unsigned int)*(ptrs-1);
13237           if (!h && !s) {
13238             if (w>=c) {
13239               if (error_message) std::sprintf(error_message,
13240                                               "CImg3d (%u,%u) refers to invalid shared sprite/texture indice %u for primitive %u",
13241                                               nb_points,nb_primitives,w,c);
13242               return false;
13243             }
13244           } else ptrs+=w*h*s;
13245         }
13246         if (ptrs>ptre) {
13247           if (error_message) std::sprintf(error_message,
13248                                           "CImg3d (%u,%u) has incomplete color/texture data for primitive %u",
13249                                           nb_points,nb_primitives,c);
13250           return false;
13251         }
13252       }
13253 
13254       // Check consistency of opacity data.
13255       if (ptrs==ptre) {
13256         if (error_message) std::sprintf(error_message,
13257                                         "CImg3d (%u,%u) has no opacity data",
13258                                         nb_points,nb_primitives);
13259         return false;
13260       }
13261       for (unsigned int o = 0; o<nb_primitives; ++o) {
13262         if ((int)*(ptrs++)==-128 && (ptrs+=3)<ptre) {
13263           const unsigned int w = (unsigned int)*(ptrs-3), h = (unsigned int)*(ptrs-2), s = (unsigned int)*(ptrs-1);
13264           if (!h && !s) {
13265             if (w>=o) {
13266               if (error_message) std::sprintf(error_message,
13267                                               "CImg3d (%u,%u) refers to invalid shared opacity indice %u for primitive %u",
13268                                               nb_points,nb_primitives,w,o);
13269               return false;
13270             }
13271           } else ptrs+=w*h*s;
13272         }
13273         if (ptrs>ptre) {
13274           if (error_message) std::sprintf(error_message,
13275                                           "CImg3d (%u,%u) has incomplete opacity data for primitive %u",
13276                                           nb_points,nb_primitives,o);
13277           return false;
13278         }
13279       }
13280 
13281       // Check end of data.
13282       if (ptrs<ptre) {
13283         if (error_message) std::sprintf(error_message,
13284                                         "CImg3d (%u,%u) contains %u byte%s more than expected",
13285                                         nb_points,nb_primitives,(unsigned int)(ptre-ptrs),(ptre-ptrs)>1?"s":"");
13286         return false;
13287       }
13288       return true;
13289     }
13290 
13291     static bool _is_CImg3d(const T val, const char c) {
13292       return val>=(T)c && val<(T)(c+1);
13293     }
13294 
13295     //@}
13296     //-------------------------------------
13297     //
13298     //! \name Mathematical Functions
13299     //@{
13300     //-------------------------------------
13301 
13302     // Define the math formula parser/compiler and evaluator.
13303     struct _cimg_math_parser {
13304       CImgList<charT> label;
13305       CImgList<uintT> code;
13306       CImg<uintT> level, opcode;
13307       CImg<doubleT> mem;
13308       CImg<charT> expr;
13309       const CImg<T>& reference;
13310       CImg<Tdouble> reference_stats;
13311       unsigned int mempos, result;
13312       const char *const calling_function;
13313 #define _cimg_mp_return(x) { *se = saved_char; return x; }
13314 #define _cimg_mp_opcode0(op) _cimg_mp_return(opcode0(op));
13315 #define _cimg_mp_opcode1(op,i1) _cimg_mp_return(opcode1(op,i1));
13316 #define _cimg_mp_opcode2(op,i1,i2) { const unsigned int _i1 = i1, _i2 = i2; _cimg_mp_return(opcode2(op,_i1,_i2)); }
13317 #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)); }
13318 #define _cimg_mp_opcode5(op,i1,i2,i3,i4,i5) { const unsigned int _i1 = i1, _i2 = i2, _i3 = i3, _i4 = i4, _i5 = i5; \
13319           _cimg_mp_return(opcode5(op,_i1,_i2,_i3,_i4,_i5)); }
13320 
13321       // Constructor - Destructor.
13322       _cimg_math_parser(const CImg<T>& img, const char *const expression, const char *const funcname=0):
13323         reference(img),calling_function(funcname?funcname:"cimg_math_parser") {
13324         unsigned int l = 0;
13325         if (expression) {
13326           l = std::strlen(expression);
13327           expr.assign(expression,l+1);
13328           if (*expr._data) {
13329             char *d = expr._data;
13330             for (const char *s = expr._data; *s || (bool)(*d=0); ++s) if (*s!=' ') *(d++) = *s;
13331             l = d - expr._data;
13332           }
13333         }
13334         if (!l) throw CImgArgumentException("[_cimg_math_parser] "
13335                                             "CImg<%s>::%s() : Empty specified expression.",
13336                                             pixel_type(),calling_function);
13337 
13338         int lv = 0; // Count parenthesis level of expression.
13339         level.assign(l);
13340         unsigned int *pd = level._data;
13341         for (const char *ps = expr._data; *ps && lv>=0; ++ps) *(pd++) = (unsigned int)(*ps=='('?lv++:*ps==')'?--lv:lv);
13342         if (lv!=0) {
13343           throw CImgArgumentException("[_cimg_math_parser] "
13344                                       "CImg<%s>::%s() : Unbalanced parentheses in specified expression '%s'.",
13345                                       pixel_type(),calling_function,
13346                                       expr._data);
13347         }
13348         // Init constant values.
13349         mem.assign(512);
13350         label.assign(512);
13351         mem[0] = 0;
13352         mem[1] = 1;
13353         mem[2] = (double)reference._width;
13354         mem[3] = (double)reference._height;
13355         mem[4] = (double)reference._depth;
13356         mem[5] = (double)reference._spectrum;
13357         mem[6] = cimg::PI;
13358         mem[7] = std::exp(1.0); // Then [8] = x, [9] = y, [10] = z, [11] = c
13359         mempos = 12;
13360         result = compile(expr._data,expr._data+l); // Compile formula into a serie of opcodes.
13361       }
13362 
13363       // Insert code instructions.
13364       unsigned int opcode0(const char op) {
13365         if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
13366         const unsigned int pos = mempos++;
13367         CImg<uintT>::vector(op,pos).move_to(code);
13368         return pos;
13369       }
13370 
13371       unsigned int opcode1(const char op, const unsigned int arg1) {
13372         if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
13373         const unsigned int pos = mempos++;
13374         CImg<uintT>::vector(op,pos,arg1).move_to(code);
13375         return pos;
13376       }
13377 
13378       unsigned int opcode2(const char op, const unsigned int arg1, const unsigned int arg2) {
13379         if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
13380         const unsigned int pos = mempos++;
13381         CImg<uintT>::vector(op,pos,arg1,arg2).move_to(code);
13382         return pos;
13383       }
13384 
13385       unsigned int opcode3(const char op, const unsigned int arg1, const unsigned int arg2, const unsigned int arg3) {
13386         if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
13387         const unsigned int pos = mempos++;
13388         CImg<uintT>::vector(op,pos,arg1,arg2,arg3).move_to(code);
13389         return pos;
13390       }
13391 
13392       unsigned int opcode5(const char op, const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
13393                            const unsigned int arg4, const unsigned int arg5) {
13394         if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
13395         const unsigned int pos = mempos++;
13396         CImg<uintT>::vector(op,pos,arg1,arg2,arg3,arg4,arg5).move_to(code);
13397         return pos;
13398       }
13399 
13400       // Compilation procedure.
13401       unsigned int compile(char *const ss, char *const se) {
13402         if (!ss || se<=ss || !*ss) {
13403           throw CImgArgumentException("[_cimg_math_parser] "
13404                                       "CImg<%s>::%s() : Missing item in specified expression '%s'.",
13405                                       pixel_type(),calling_function,
13406                                       expr._data);
13407         }
13408         char
13409           *const se1 = se-1, *const se2 = se-2, *const se3 = se-3, *const se4 = se-4,
13410           *const ss1 = ss+1, *const ss2 = ss+2, *const ss3 = ss+3, *const ss4 = ss+4,
13411           *const ss5 = ss+5, *const ss6 = ss+6, *const ss7 = ss+7;
13412         const char saved_char = *se; *se = 0;
13413         const unsigned int clevel = level[ss-expr._data], clevel1 = clevel+1;
13414         if (*se1==';') return compile(ss,se1);
13415 
13416         // Look for a single value, variable or variable assignment.
13417         char end = 0, sep = 0; double val = 0;
13418         const int nb = std::sscanf(ss,"%lf%c%c",&val,&sep,&end);
13419         if (nb==1) {
13420           if (val==0) _cimg_mp_return(0);
13421           if (val==1) _cimg_mp_return(1);
13422           if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
13423           const unsigned int pos = mempos++;
13424           mem[pos] = val;
13425           _cimg_mp_return(pos);
13426         }
13427         if (nb==2 && sep=='%') {
13428           if (val==0) _cimg_mp_return(0);
13429           if (val==100) _cimg_mp_return(1);
13430           if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
13431           const unsigned int pos = mempos++;
13432           mem[pos] = val/100;
13433           _cimg_mp_return(pos);
13434         }
13435         if (ss1==se) switch (*ss) {
13436           case 'w' : _cimg_mp_return(2); case 'h' : _cimg_mp_return(3); case 'd' : _cimg_mp_return(4); case 's' : _cimg_mp_return(5);
13437           case 'x' : _cimg_mp_return(8); case 'y' : _cimg_mp_return(9); case 'z' : _cimg_mp_return(10); case 'c' : _cimg_mp_return(11);
13438           case 'e' : _cimg_mp_return(7);
13439           case 'u' : case '?' : _cimg_mp_opcode2(0,0,1);
13440           case 'g' : _cimg_mp_opcode0(1);
13441           case 'i' : _cimg_mp_opcode0(2);
13442           }
13443         if (ss1==se1) {
13444           if (*ss=='p' && *ss1=='i') _cimg_mp_return(6); // pi
13445           if (*ss=='i') {
13446             if (*ss1=='m') _cimg_mp_opcode0(57); // im
13447             if (*ss1=='M') _cimg_mp_opcode0(58); // iM
13448             if (*ss1=='a') _cimg_mp_opcode0(59); // ia
13449             if (*ss1=='v') _cimg_mp_opcode0(60); // iv
13450           }
13451           if (*ss1=='m') {
13452             if (*ss=='x') _cimg_mp_opcode0(61); // xm
13453             if (*ss=='y') _cimg_mp_opcode0(62); // ym
13454             if (*ss=='z') _cimg_mp_opcode0(63); // zm
13455             if (*ss=='c') _cimg_mp_opcode0(64); // cm
13456           }
13457           if (*ss1=='M') {
13458             if (*ss=='x') _cimg_mp_opcode0(65); // xM
13459             if (*ss=='y') _cimg_mp_opcode0(66); // yM
13460             if (*ss=='z') _cimg_mp_opcode0(67); // zM
13461             if (*ss=='c') _cimg_mp_opcode0(68); // cM
13462           }
13463         }
13464         if (ss3==se) {
13465           if (*ss=='x' && *ss1=='/' && *ss2=='w') _cimg_mp_opcode0(3);
13466           if (*ss=='y' && *ss1=='/' && *ss2=='h') _cimg_mp_opcode0(4);
13467           if (*ss=='z' && *ss1=='/' && *ss2=='d') _cimg_mp_opcode0(5);
13468           if (*ss=='c' && *ss1=='/' && *ss2=='s') _cimg_mp_opcode0(6);
13469         }
13470 
13471         // Look for variable declarations.
13472         for (char *s = se2; s>ss; --s) if (*s==';' && level[s-expr._data]==clevel) { compile(ss,s); _cimg_mp_return(compile(s+1,se)); }
13473         for (char *s = ss1, *ps = ss, *ns = ss2; s<se1; ++s, ++ps, ++ns)
13474           if (*s=='=' && *ns!='=' && *ps!='=' && *ps!='>' && *ps!='<' && *ps!='!' && level[s-expr._data]==clevel) {
13475              CImg<charT> variable_name(ss,s-ss+1); variable_name.back() = 0;
13476              bool is_valid_name = true;
13477              if ((*ss>='0' && *ss<='9') ||
13478                  (s==ss+1 && (*ss=='x' || *ss=='y' || *ss=='z' || *ss=='c' ||
13479                               *ss=='w' || *ss=='h' || *ss=='d' || *ss=='s' ||
13480                               *ss=='e' || *ss=='u' || *ss=='g' || *ss=='i')) ||
13481                  (s==ss+2 && ((*ss=='p' && *(ss+1)=='i') ||
13482                               (*ss=='i' && (*(ss+1)=='m' || *(ss+1)=='M' || *(ss+1)=='a' || *(ss+1)=='v'))))) is_valid_name = false;
13483              for (const char *ns = ss; ns<s; ++ns)
13484                if ((*ns<'a' || *ns>'z') && (*ns<'A' || *ns>'Z') && (*ns<'0' || *ns>'9') && *ns!='_') {
13485                  is_valid_name = false; break;
13486                }
13487              if (!is_valid_name) {
13488                *se = saved_char;
13489                if (!std::strcmp(variable_name,"x") || !std::strcmp(variable_name,"y") || !std::strcmp(variable_name,"z") ||
13490                    !std::strcmp(variable_name,"c") || !std::strcmp(variable_name,"w") || !std::strcmp(variable_name,"h") ||
13491                    !std::strcmp(variable_name,"d") || !std::strcmp(variable_name,"s") || !std::strcmp(variable_name,"e") ||
13492                    !std::strcmp(variable_name,"u") || !std::strcmp(variable_name,"g") || !std::strcmp(variable_name,"i") ||
13493                    !std::strcmp(variable_name,"pi") || !std::strcmp(variable_name,"im") || !std::strcmp(variable_name,"iM") ||
13494                    !std::strcmp(variable_name,"ia") || !std::strcmp(variable_name,"iv"))
13495                   throw CImgArgumentException("[_cimg_math_parser] "
13496                                              "CImg<%s>::%s() : Invalid assignment of reserved variable name '%s' in specified expression '%s'.",
13497                                              pixel_type(),calling_function,
13498                                              variable_name._data,expr._data);
13499                else
13500                  throw CImgArgumentException("[_cimg_math_parser] "
13501                                              "CImg<%s>::%s() : Invalid variable name '%s' in specified expression '%s'.",
13502                                              pixel_type(),calling_function,
13503                                              variable_name._data,expr._data);
13504              }
13505              for (unsigned int i = 0; i<mempos; ++i) // Check for existing variable with same name.
13506                if (label[i]._data && !std::strcmp(variable_name,label[i])) {
13507                  *se = saved_char;
13508                  throw CImgArgumentException("[_cimg_math_parser] "
13509                                              "CImg<%s>::%s() : Invalid multiple assignments of variable '%s' in specified expression '%s'.",
13510                                              pixel_type(),calling_function,
13511                                              variable_name._data,expr._data);
13512                }
13513              const unsigned int src_pos = compile(s+1,se);
13514              if (mempos>=mem.size()) mem.resize(-200,1,1,1,0);
13515              const unsigned int dest_pos = mempos++;
13516              variable_name.move_to(label[dest_pos]);
13517              CImg<uintT>::vector(7,dest_pos,src_pos).move_to(code);
13518              _cimg_mp_return(dest_pos);
13519            }
13520 
13521         // Look for unary/binary operators. The operator precedences is defined as in C++.
13522         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));
13523         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));
13524         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));
13525         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));
13526         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));
13527         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));
13528         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));
13529         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));
13530         for (char *s = se2, *ns = se1, *ps = se3; s>ss; --s, --ns, --ps)
13531           if (*s=='<' && *ns!='<' && *ps!='<' && level[s-expr._data]==clevel) _cimg_mp_opcode2(16,compile(ss,s),compile(s+1,se));
13532         for (char *s = se2, *ns = se1, *ps = se3; s>ss; --s, --ns, --ps)
13533           if (*s=='>' && *ns!='>' && *ps!='>' && level[s-expr._data]==clevel) _cimg_mp_opcode2(17,compile(ss,s),compile(s+1,se));
13534         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));
13535         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));
13536         for (char *s = se2, *ps = se3; s>ss; --s, --ps)
13537           if (*s=='+' && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' && *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' &&
13538               (*ps!='e' || !(ps>ss && (*(ps-1)=='.' || (*(ps-1)>='0' && *(ps-1)<='9')))) && level[s-expr._data]==clevel)
13539             _cimg_mp_opcode2(21,compile(ss,s),compile(s+1,se));
13540         for (char *s = se2, *ps = se3; s>ss; --s, --ps)
13541           if (*s=='-' && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' && *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' &&
13542               (*ps!='e' || !(ps>ss && (*(ps-1)=='.' || (*(ps-1)>='0' && *(ps-1)<='9')))) && level[s-expr._data]==clevel)
13543             _cimg_mp_opcode2(20,compile(ss,s),compile(s+1,se));
13544         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));
13545         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));
13546         for (char *s = se2, *ns = se1; s>ss; --s, --ns)
13547           if (*s=='%' && *ns!='^' && level[s-expr._data]==clevel)
13548             _cimg_mp_opcode2(24,compile(ss,s),compile(s+1,se));
13549         if (ss<se1) {
13550           if (*ss=='+') _cimg_mp_return(compile(ss1,se));
13551           if (*ss=='-') _cimg_mp_opcode1(26,compile(ss1,se));
13552           if (*ss=='!') _cimg_mp_opcode1(27,compile(ss1,se));
13553           if (*ss=='~') _cimg_mp_opcode1(28,compile(ss1,se));
13554         }
13555         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));
13556 
13557         // Look for a function call or a parenthesis.
13558         if (*se1==')') {
13559           if (*ss=='(') _cimg_mp_return(compile(ss1,se1));
13560           if (!std::strncmp(ss,"sin(",4)) _cimg_mp_opcode1(29,compile(ss4,se1));
13561           if (!std::strncmp(ss,"cos(",4)) _cimg_mp_opcode1(30,compile(ss4,se1));
13562           if (!std::strncmp(ss,"tan(",4)) _cimg_mp_opcode1(31,compile(ss4,se1));
13563           if (!std::strncmp(ss,"asin(",5)) _cimg_mp_opcode1(32,compile(ss5,se1));
13564           if (!std::strncmp(ss,"acos(",5)) _cimg_mp_opcode1(33,compile(ss5,se1));
13565           if (!std::strncmp(ss,"atan(",5)) _cimg_mp_opcode1(34,compile(ss5,se1));
13566           if (!std::strncmp(ss,"sinh(",5)) _cimg_mp_opcode1(35,compile(ss5,se1));
13567           if (!std::strncmp(ss,"cosh(",5)) _cimg_mp_opcode1(36,compile(ss5,se1));
13568           if (!std::strncmp(ss,"tanh(",5)) _cimg_mp_opcode1(37,compile(ss5,se1));
13569           if (!std::strncmp(ss,"log10(",6)) _cimg_mp_opcode1(38,compile(ss6,se1));
13570           if (!std::strncmp(ss,"log(",4)) _cimg_mp_opcode1(39,compile(ss4,se1));
13571           if (!std::strncmp(ss,"exp(",4)) _cimg_mp_opcode1(40,compile(ss4,se1));
13572           if (!std::strncmp(ss,"sqrt(",5)) _cimg_mp_opcode1(41,compile(ss5,se1));
13573           if (!std::strncmp(ss,"sign(",5)) _cimg_mp_opcode1(42,compile(ss5,se1));
13574           if (!std::strncmp(ss,"abs(",4)) _cimg_mp_opcode1(43,compile(ss4,se1));
13575           if (!std::strncmp(ss,"atan2(",6)) {
13576             char *s1 = ss6; while (s1<se2 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
13577             _cimg_mp_opcode2(44,compile(ss6,s1),compile(s1+1,se1));
13578           }
13579           if (!std::strncmp(ss,"if(",3)) {
13580             char *s1 = ss3; while (s1<se4 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
13581             char *s2 = s1+1; while (s2<se2 && (*s2!=',' || level[s2-expr._data]!=clevel1)) ++s2;
13582             _cimg_mp_opcode3(45,compile(ss3,s1),compile(s1+1,s2),compile(s2+1,se1));
13583           }
13584           if (!std::strncmp(ss,"round(",6)) {
13585             unsigned int value = 0, round = 1, direction = 0;
13586             char *s1 = ss6; while (s1<se2 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
13587             value = compile(ss6,s1==se2?++s1:s1);
13588             if (s1<se1) {
13589               char *s2 = s1+1; while (s2<se2 && (*s2!=',' || level[s2-expr._data]!=clevel1)) ++s2;
13590               round = compile(s1+1,s2==se2?++s2:s2);
13591               if (s2<se1) direction = compile(s2+1,se1);
13592             }
13593             _cimg_mp_opcode3(46,value,round,direction);
13594           }
13595           if (!std::strncmp(ss,"?(",2) || !std::strncmp(ss,"u(",2)) {
13596             if (*ss2==')') _cimg_mp_opcode2(0,0,1);
13597             char *s1 = ss2; while (s1<se1 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
13598             if (s1<se1) _cimg_mp_opcode2(0,compile(ss2,s1),compile(s1+1,se1));
13599             _cimg_mp_opcode2(0,0,compile(ss2,s1));
13600           }
13601           if (!std::strncmp(ss,"i(",2)) {
13602             if (*ss2==')') _cimg_mp_return(0);
13603             unsigned int indx = 8, indy = 9, indz = 10, indc = 11, borders = 0;
13604             if (ss2!=se1) {
13605               char *s1 = ss2; while (s1<se2 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
13606               indx = compile(ss2,s1==se2?++s1:s1);
13607               if (s1<se1) {
13608                 char *s2 = s1+1; while (s2<se2 && (*s2!=',' || level[s2-expr._data]!=clevel1)) ++s2;
13609                 indy = compile(s1+1,s2==se2?++s2:s2);
13610                 if (s2<se1) {
13611                   char *s3 = s2+1; while (s3<se2 && (*s3!=',' || level[s3-expr._data]!=clevel1)) ++s3;
13612                   indz = compile(s2+1,s3==se2?++s3:s3);
13613                   if (s3<se1) {
13614                     char *s4 = s3+1; while (s4<se2 && (*s4!=',' || level[s4-expr._data]!=clevel1)) ++s4;
13615                     indc = compile(s3+1,s4==se2?++s4:s4);
13616                     if (s4<se1) borders = compile(s4+1,se1);
13617                   }
13618                 }
13619               }
13620             }
13621             _cimg_mp_opcode5(47,indx,indy,indz,indc,borders);
13622           }
13623           if (!std::strncmp(ss,"min(",4) || !std::strncmp(ss,"max(",4)) {
13624             CImgList<uintT> opcode;
13625             if (mempos>=mem.size()) mem.resize(-200,1,1,1,0);
13626             const unsigned int pos = mempos++;
13627             CImg<uintT>::vector(ss[1]=='i'?48:49,pos).move_to(opcode);
13628             for (char *s = ss4; s<se; ++s) {
13629               char *ns = s; while (ns<se && (*ns!=',' || level[ns-expr._data]!=clevel1) && (*ns!=')' || level[ns-expr._data]!=clevel)) ++ns;
13630               CImg<uintT>::vector(compile(s,ns)).move_to(opcode);
13631               s = ns;
13632             }
13633             (opcode>'y').move_to(code);
13634             _cimg_mp_return(pos);
13635           }
13636           if (!std::strncmp(ss,"arg(",4)) {
13637             CImgList<uintT> opcode;
13638             if (mempos>=mem.size()) mem.resize(-200,1,1,1,0);
13639             const unsigned int pos = mempos++;
13640             CImg<uintT>::vector(69,pos).move_to(opcode);
13641             for (char *s = ss4; s<se; ++s) {
13642               char *ns = s; while (ns<se && (*ns!=',' || level[ns-expr._data]!=clevel1) && (*ns!=')' || level[ns-expr._data]!=clevel)) ++ns;
13643               CImg<uintT>::vector(compile(s,ns)).move_to(opcode);
13644               s = ns;
13645             }
13646             (opcode>'y').move_to(code);
13647             _cimg_mp_return(pos);
13648           }
13649           if (!std::strncmp(ss,"narg(",5)) {
13650             if (*ss5==')') _cimg_mp_return(0);
13651             unsigned int nb_args = 0;
13652             for (char *s = ss5; s<se; ++s) {
13653               char *ns = s; while (ns<se && (*ns!=',' || level[ns-expr._data]!=clevel1) && (*ns!=')' || level[ns-expr._data]!=clevel)) ++ns;
13654               ++nb_args; s = ns;
13655             }
13656             if (nb_args==0 || nb_args==1) _cimg_mp_return(nb_args);
13657             if (mempos>=mem.size()) mem.resize(-200,1,1,1,0);
13658             const unsigned int pos = mempos++;
13659             mem[pos] = nb_args;
13660             _cimg_mp_return(pos);
13661           }
13662           if (!std::strncmp(ss,"isval(",6)) {
13663             char sep = 0, end = 0; double val = 0;
13664             if (std::sscanf(ss6,"%lf%c%c",&val,&sep,&end)==2 && sep==')') _cimg_mp_return(1);
13665             _cimg_mp_return(0);
13666           }
13667           if (!std::strncmp(ss,"isnan(",6)) _cimg_mp_opcode1(50,compile(ss6,se1));
13668           if (!std::strncmp(ss,"isinf(",6)) _cimg_mp_opcode1(51,compile(ss6,se1));
13669           if (!std::strncmp(ss,"isint(",6)) _cimg_mp_opcode1(52,compile(ss6,se1));
13670           if (!std::strncmp(ss,"isbool(",7)) _cimg_mp_opcode1(53,compile(ss7,se1));
13671           if (!std::strncmp(ss,"rol(",4) || !std::strncmp(ss,"ror(",4)) {
13672             unsigned int value = 0, nb = 1;
13673             char *s1 = ss4; while (s1<se2 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
13674             value = compile(ss4,s1==se2?++s1:s1);
13675             if (s1<se1) {
13676               char *s2 = s1+1; while (s2<se2 && (*s2!=',' || level[s2-expr._data]!=clevel1)) ++s2;
13677               nb = compile(s1+1,se1);
13678             }
13679             _cimg_mp_opcode2(*ss2=='l'?54:55,value,nb);
13680           }
13681 
13682           if (!std::strncmp(ss,"sinc(",5)) _cimg_mp_opcode1(56,compile(ss5,se1));
13683           if (!std::strncmp(ss,"int(",4)) _cimg_mp_opcode1(70,compile(ss4,se1));
13684         }
13685 
13686         // No known item found, assuming this is an already initialized variable.
13687         CImg<charT> variable_name(ss,se-ss+1); variable_name.back() = 0;
13688         for (unsigned int i = 0; i<mempos; ++i) if (label[i]._data && !std::strcmp(variable_name,label[i])) _cimg_mp_return(i);
13689         *se = saved_char;
13690         throw CImgArgumentException("[_cimg_math_parser] "
13691                                     "CImg<%s>::%s() : Invalid item '%s' in specified expression '%s'.\n",
13692                                     pixel_type(),calling_function,
13693                                     variable_name._data,expr._data);
13694         return 0;
13695       }
13696 
13697       // Evaluation functions, known by the parser.
13698       double mp_u() {
13699         return mem[opcode(2)] + cimg::rand()*(mem[opcode(3)]-mem[opcode(2)]);
13700       }
13701       double mp_g() {
13702         return cimg::grand();
13703       }
13704       double mp_i() {
13705         return (double)reference.atXYZC((int)mem[8],(int)mem[9],(int)mem[10],(int)mem[11],0);
13706       }
13707       double mp_xw() {
13708         return mem[8]/reference.width();
13709       }
13710       double mp_yh() {
13711         return mem[9]/reference.height();
13712       }
13713       double mp_zd() {
13714         return mem[10]/reference.depth();
13715       }
13716       double mp_cs() {
13717         return mem[11]/reference.spectrum();
13718       }
13719       double mp_equal() {
13720         return mem[opcode[2]];
13721       }
13722       double mp_logical_and() {
13723         return (double)((bool)mem[opcode(2)] && (bool)mem[opcode(3)]);
13724       }
13725       double mp_logical_or() {
13726         return (double)((bool)mem[opcode(2)] || (bool)mem[opcode(3)]);
13727       }
13728       double mp_infeq() {
13729         return (double)(mem[opcode(2)]<=mem[opcode(3)]);
13730       }
13731       double mp_supeq() {
13732         return (double)(mem[opcode(2)]>=mem[opcode(3)]);
13733       }
13734       double mp_noteq() {
13735         return (double)(mem[opcode(2)]!=mem[opcode(3)]);
13736       }
13737       double mp_eqeq() {
13738         return (double)(mem[opcode(2)]==mem[opcode(3)]);
13739       }
13740       double mp_inf() {
13741         return (double)(mem[opcode(2)]<mem[opcode(3)]);
13742       }
13743       double mp_sup() {
13744         return (double)(mem[opcode(2)]>mem[opcode(3)]);
13745       }
13746       double mp_add() {
13747         return mem[opcode(2)] + mem[opcode(3)];
13748       }
13749       double mp_sub() {
13750         return mem[opcode(2)] - mem[opcode(3)];
13751       }
13752       double mp_mul() {
13753         return mem[opcode(2)] * mem[opcode(3)];
13754       }
13755       double mp_div() {
13756         return mem[opcode(2)] / mem[opcode(3)];
13757       }
13758       double mp_minus() {
13759         return -mem[opcode(2)];
13760       }
13761       double mp_not() {
13762         return !mem[opcode(2)];
13763       }
13764       double mp_logical_not() {
13765         return !mem[opcode(2)];
13766       }
13767       double mp_bitwise_not() {
13768         return ~(unsigned long)mem[opcode(2)];
13769       }
13770       double mp_modulo() {
13771         return cimg::mod(mem[opcode(2)],mem[opcode(3)]);
13772       }
13773       double mp_bitwise_and() {
13774         return ((unsigned long)mem[opcode(2)] & (unsigned long)mem[opcode(3)]);
13775       }
13776       double mp_bitwise_or() {
13777         return ((unsigned long)mem[opcode(2)] | (unsigned long)mem[opcode(3)]);
13778       }
13779       double mp_pow() {
13780         return std::pow(mem[opcode(2)],mem[opcode(3)]);
13781       }
13782       double mp_sin() {
13783         return std::sin(mem[opcode(2)]);
13784       }
13785       double mp_cos() {
13786         return std::cos(mem[opcode(2)]);
13787       }
13788       double mp_tan() {
13789         return std::tan(mem[opcode(2)]);
13790       }
13791       double mp_asin() {
13792         return std::asin(mem[opcode(2)]);
13793       }
13794       double mp_acos() {
13795         return std::acos(mem[opcode(2)]);
13796       }
13797       double mp_atan() {
13798         return std::atan(mem[opcode(2)]);
13799       }
13800       double mp_sinh() {
13801         return std::sinh(mem[opcode(2)]);
13802       }
13803       double mp_cosh() {
13804         return std::cosh(mem[opcode(2)]);
13805       }
13806       double mp_tanh() {
13807         return std::tanh(mem[opcode(2)]);
13808       }
13809       double mp_log10() {
13810         return std::log10(mem[opcode(2)]);
13811       }
13812       double mp_log() {
13813         return std::log(mem[opcode(2)]);
13814       }
13815       double mp_exp() {
13816         return std::exp(mem[opcode(2)]);
13817       }
13818       double mp_sqrt() {
13819         return std::sqrt(mem[opcode(2)]);
13820       }
13821       double mp_sign() {
13822         return cimg::sign(mem[opcode(2)]);
13823       }
13824       double mp_abs() {
13825         return cimg::abs(mem[opcode(2)]);
13826       }
13827       double mp_atan2() {
13828         return std::atan2(mem[opcode(2)],mem[opcode(3)]);
13829       }
13830       double mp_if() {
13831         return mem[opcode(2)]?mem[opcode(3)]:mem[opcode(4)];
13832       }
13833       double mp_round() {
13834         return cimg::round(mem[opcode(2)],mem[opcode(3)],(int)mem[opcode(4)]);
13835       }
13836       double mp_ixyzc() {
13837         const int b = (int)mem[opcode(6)];
13838         if (b==2) return (double)reference.atXYZC(cimg::mod((int)mem[opcode(2)],reference.width()),
13839                                                   cimg::mod((int)mem[opcode(3)],reference.height()),
13840                                                   cimg::mod((int)mem[opcode(4)],reference.depth()),
13841                                                   cimg::mod((int)mem[opcode(5)],reference.spectrum()));
13842         if (b==1) return (double)reference.atXYZC((int)mem[opcode(2)],
13843                                                   (int)mem[opcode(3)],
13844                                                   (int)mem[opcode(4)],
13845                                                   (int)mem[opcode(5)]);
13846         return (double)reference.atXYZC((int)mem[opcode(2)],
13847                                         (int)mem[opcode(3)],
13848                                         (int)mem[opcode(4)],
13849                                         (int)mem[opcode(5)],0);
13850       }
13851       double mp_min() {
13852         double val = mem[opcode(2)];
13853         for (unsigned int i = 3; i<opcode._height; ++i) val = cimg::min(val,mem[opcode(i)]);
13854         return val;
13855       }
13856       double mp_max() {
13857         double val = mem[opcode(2)];
13858         for (unsigned int i = 3; i<opcode._height; ++i) val = cimg::max(val,mem[opcode(i)]);
13859         return val;
13860       }
13861       double mp_isnan() {
13862         const double val = mem[opcode(2)];
13863         return !(val==val);
13864       }
13865       double mp_isinf() {
13866         const double val = mem[opcode(2)];
13867         return val==(val+1);
13868       }
13869       double mp_isint() {
13870         const double val = mem[opcode(2)];
13871         return (double)(int)val==val;
13872       }
13873       double mp_isbool() {
13874         const double val = mem[opcode(2)];
13875         return (val==0.0 || val==1.0);
13876       }
13877       double mp_rol() {
13878         return cimg::rol(mem[opcode(2)],(unsigned int)mem[opcode(3)]);
13879       }
13880       double mp_ror() {
13881         return cimg::ror(mem[opcode(2)],(unsigned int)mem[opcode(3)]);
13882       }
13883       double mp_lsl() {
13884         return (long)mem[opcode(2)]<<(unsigned int)mem[opcode(3)];
13885       }
13886       double mp_lsr() {
13887         return (long)mem[opcode(2)]>>(unsigned int)mem[opcode(3)];
13888       }
13889       double mp_sinc() {
13890         return cimg::sinc(mem[opcode(2)]);
13891       }
13892       double mp_im() {
13893         if (!reference_stats) reference.get_stats().move_to(reference_stats);
13894         return reference_stats?reference_stats[0]:0;
13895       }
13896       double mp_iM() {
13897         if (!reference_stats) reference.get_stats().move_to(reference_stats);
13898         return reference_stats?reference_stats[1]:0;
13899       }
13900       double mp_ia() {
13901         if (!reference_stats) reference.get_stats().move_to(reference_stats);
13902         return reference_stats?reference_stats[2]:0;
13903       }
13904       double mp_iv() {
13905         if (!reference_stats) reference.get_stats().move_to(reference_stats);
13906         return reference_stats?reference_stats[3]:0;
13907       }
13908       double mp_xm() {
13909         if (!reference_stats) reference.get_stats().move_to(reference_stats);
13910         return reference_stats?reference_stats[4]:0;
13911       }
13912       double mp_ym() {
13913         if (!reference_stats) reference.get_stats().move_to(reference_stats);
13914         return reference_stats?reference_stats[5]:0;
13915       }
13916       double mp_zm() {
13917         if (!reference_stats) reference.get_stats().move_to(reference_stats);
13918         return reference_stats?reference_stats[6]:0;
13919       }
13920       double mp_cm() {
13921         if (!reference_stats) reference.get_stats().move_to(reference_stats);
13922         return reference_stats?reference_stats[7]:0;
13923       }
13924       double mp_xM() {
13925         if (!reference_stats) reference.get_stats().move_to(reference_stats);
13926         return reference_stats?reference_stats[8]:0;
13927       }
13928       double mp_yM() {
13929         if (!reference_stats) reference.get_stats().move_to(reference_stats);
13930         return reference_stats?reference_stats[9]:0;
13931       }
13932       double mp_zM() {
13933         if (!reference_stats) reference.get_stats().move_to(reference_stats);
13934         return reference_stats?reference_stats[10]:0;
13935       }
13936       double mp_cM() {
13937         if (!reference_stats) reference.get_stats().move_to(reference_stats);
13938         return reference_stats?reference_stats[11]:0;
13939       }
13940       double mp_arg() {
13941         const int _ind = (int)mem[opcode(2)];
13942         const unsigned int nb_args = opcode._height-2, ind = _ind<0?_ind+nb_args:(unsigned int)_ind;
13943         if (ind>=nb_args) return 0;
13944         return mem[opcode(ind+2)];
13945       }
13946       double mp_int() {
13947         return (double)(long)mem[opcode(2)];
13948       }
13949 
13950       // Evaluation procedure, with image data.
13951       double eval(const double x, const double y, const double z, const double c) {
13952         typedef double (_cimg_math_parser::*mp_func)();
13953         const mp_func mp_funcs[] = {
13954           &_cimg_math_parser::mp_u,            // 0
13955           &_cimg_math_parser::mp_g,            // 1
13956           &_cimg_math_parser::mp_i,            // 2
13957           &_cimg_math_parser::mp_xw,           // 3
13958           &_cimg_math_parser::mp_yh,           // 4
13959           &_cimg_math_parser::mp_zd,           // 5
13960           &_cimg_math_parser::mp_cs,           // 6
13961           &_cimg_math_parser::mp_equal,        // 7
13962           &_cimg_math_parser::mp_logical_or,   // 8
13963           &_cimg_math_parser::mp_logical_and,  // 9
13964           &_cimg_math_parser::mp_bitwise_or,   // 10
13965           &_cimg_math_parser::mp_bitwise_and,  // 11
13966           &_cimg_math_parser::mp_noteq,        // 12
13967           &_cimg_math_parser::mp_eqeq,         // 13
13968           &_cimg_math_parser::mp_infeq,        // 14
13969           &_cimg_math_parser::mp_supeq,        // 15
13970           &_cimg_math_parser::mp_inf,          // 16
13971           &_cimg_math_parser::mp_sup,          // 17
13972           &_cimg_math_parser::mp_lsl,          // 18
13973           &_cimg_math_parser::mp_lsr,          // 19
13974           &_cimg_math_parser::mp_sub,          // 20
13975           &_cimg_math_parser::mp_add,          // 21
13976           &_cimg_math_parser::mp_mul,          // 22
13977           &_cimg_math_parser::mp_div,          // 23
13978           &_cimg_math_parser::mp_modulo,       // 24
13979           &_cimg_math_parser::mp_pow,          // 25
13980           &_cimg_math_parser::mp_minus,        // 26
13981           &_cimg_math_parser::mp_logical_not,  // 27
13982           &_cimg_math_parser::mp_bitwise_not,  // 28
13983           &_cimg_math_parser::mp_sin,          // 29
13984           &_cimg_math_parser::mp_cos,          // 30
13985           &_cimg_math_parser::mp_tan,          // 31
13986           &_cimg_math_parser::mp_asin,         // 32
13987           &_cimg_math_parser::mp_acos,         // 33
13988           &_cimg_math_parser::mp_atan,         // 34
13989           &_cimg_math_parser::mp_sinh,         // 35
13990           &_cimg_math_parser::mp_cosh,         // 36
13991           &_cimg_math_parser::mp_tanh,         // 37
13992           &_cimg_math_parser::mp_log10,        // 38
13993           &_cimg_math_parser::mp_log,          // 39
13994           &_cimg_math_parser::mp_exp,          // 40
13995           &_cimg_math_parser::mp_sqrt,         // 41
13996           &_cimg_math_parser::mp_sign,         // 42
13997           &_cimg_math_parser::mp_abs,          // 43
13998           &_cimg_math_parser::mp_atan2,        // 44
13999           &_cimg_math_parser::mp_if,           // 45
14000           &_cimg_math_parser::mp_round,        // 46
14001           &_cimg_math_parser::mp_ixyzc,        // 47
14002           &_cimg_math_parser::mp_min,          // 48
14003           &_cimg_math_parser::mp_max,          // 49
14004           &_cimg_math_parser::mp_isnan,        // 50
14005           &_cimg_math_parser::mp_isinf,        // 51
14006           &_cimg_math_parser::mp_isint,        // 52
14007           &_cimg_math_parser::mp_isbool,       // 53
14008           &_cimg_math_parser::mp_rol,          // 54
14009           &_cimg_math_parser::mp_ror,          // 55
14010           &_cimg_math_parser::mp_sinc,         // 56
14011           &_cimg_math_parser::mp_im,           // 57
14012           &_cimg_math_parser::mp_iM,           // 58
14013           &_cimg_math_parser::mp_ia,           // 59
14014           &_cimg_math_parser::mp_iv,           // 60
14015           &_cimg_math_parser::mp_xm,           // 61
14016           &_cimg_math_parser::mp_ym,           // 62
14017           &_cimg_math_parser::mp_zm,           // 63
14018           &_cimg_math_parser::mp_cm,           // 64
14019           &_cimg_math_parser::mp_xM,           // 65
14020           &_cimg_math_parser::mp_yM,           // 66
14021           &_cimg_math_parser::mp_zM,           // 67
14022           &_cimg_math_parser::mp_cM,           // 68
14023           &_cimg_math_parser::mp_arg,          // 69
14024           &_cimg_math_parser::mp_int           // 70
14025         };
14026 
14027         if (!mem) return 0;
14028         mem[8] = x; mem[9] = y; mem[10] = z; mem[11] = c;
14029         opcode._is_shared = true; opcode._width = opcode._depth = opcode._spectrum = 1;
14030         cimglist_for(code,l) {
14031           const CImg<uintT> &op = code[l];
14032           opcode._data = op._data; opcode._height = op._height;  // Allows to avoid parameter passing to evaluation functions.
14033           mem[opcode(1)] = (this->*mp_funcs[opcode[0]])();
14034         }
14035         return mem[result];
14036       }
14037     };
14038 
14039     //! Compute the square value of each pixel value.
14040     /**
14041        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$.
14042        \note
14043        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14044        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14045        \par Sample code :
14046        \code
14047        const CImg<float> img("reference.jpg");
14048        (img,img.get_sqr().normalize(0,255)).display();
14049        \endcode
14050        \image html ref_sqr.jpg
14051        \sa get_sqr() const,
14052            sqrt(),
14053            pow(),
14054            exp(),
14055            log(),
14056            log10().
14057     **/
14058     CImg<T>& sqr() {
14059       cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(val*val); };
14060       return *this;
14061     }
14062 
14063     //! Compute the square value of each pixel value \newinstance.
14064     CImg<Tfloat> get_sqr() const {
14065       return CImg<Tfloat>(*this,false).sqr();
14066     }
14067 
14068     //! Compute the square root of each pixel value.
14069     /**
14070        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$.
14071        \note
14072        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14073        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14074        \par Sample code :
14075        \code
14076        const CImg<float> img("reference.jpg");
14077        (img,img.get_sqrt().normalize(0,255)).display();
14078        \endcode
14079        \image html ref_sqrt.jpg
14080        \sa get_sqrt() const,,
14081            sqr(),
14082            pow(),
14083            exp(),
14084            log(),
14085            log10().
14086     **/
14087     CImg<T>& sqrt() {
14088       cimg_for(*this,ptrd,T) *ptrd = (T)std::sqrt((double)*ptrd);
14089       return *this;
14090     }
14091 
14092     //! Compute the square root of each pixel value \newinstance.
14093     CImg<Tfloat> get_sqrt() const {
14094       return CImg<Tfloat>(*this,false).sqrt();
14095     }
14096 
14097     //! Compute the exponential of each pixel value.
14098     /**
14099        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$.
14100        \note
14101        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14102        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14103        \sa get_exp() const,
14104            sqr(),
14105            sqrt(),
14106            pow(),
14107            log(),
14108            log10().
14109     **/
14110     CImg<T>& exp() {
14111       cimg_for(*this,ptrd,T) *ptrd = (T)std::exp((double)*ptrd);
14112       return *this;
14113     }
14114 
14115     //! Compute the exponential of each pixel value \newinstance.
14116     CImg<Tfloat> get_exp() const {
14117       return CImg<Tfloat>(*this,false).exp();
14118     }
14119 
14120     //! Compute the logarithm of each pixel value.
14121     /**
14122        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its logarithm \f$log_e(I_{(x,y,z,c)})\f$.
14123        \note
14124        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14125        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14126        \sa get_log() const,
14127            sqr(),
14128            sqrt(),
14129            pow(),
14130            exp(),
14131            log10().
14132     **/
14133     CImg<T>& log() {
14134       cimg_for(*this,ptrd,T) *ptrd = (T)std::log((double)*ptrd);
14135       return *this;
14136     }
14137 
14138     //! Compute the logarithm of each pixel value \newinstance.
14139     CImg<Tfloat> get_log() const {
14140       return CImg<Tfloat>(*this,false).log();
14141     }
14142 
14143     //! Compute the base-10 logarithm of each pixel value.
14144     /**
14145        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-10 logarithm \f$log_{10}(I_{(x,y,z,c)})\f$.
14146        \note
14147        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14148        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14149        \sa get_log10(),
14150            sqr(),
14151            sqrt(),
14152            pow(),
14153            exp(),
14154            log().
14155     **/
14156     CImg<T>& log10() {
14157       cimg_for(*this,ptrd,T) *ptrd = (T)std::log10((double)*ptrd);
14158       return *this;
14159     }
14160 
14161     //! Compute the base-10 logarithm of each pixel value \newinstance.
14162     CImg<Tfloat> get_log10() const {
14163       return CImg<Tfloat>(*this,false).log10();
14164     }
14165 
14166     //! Compute the absolute value of each pixel value.
14167     /**
14168        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$.
14169        \note
14170        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14171        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14172        \sa get_abs(),
14173            sign().
14174     **/
14175     CImg<T>& abs() {
14176       cimg_for(*this,ptrd,T) *ptrd = cimg::abs(*ptrd);
14177       return *this;
14178     }
14179 
14180     //! Compute the absolute value of each pixel value \newinstance.
14181     CImg<Tfloat> get_abs() const {
14182       return CImg<Tfloat>(*this,false).abs();
14183     }
14184 
14185     //! Compute the sign of each pixel value.
14186     /**
14187        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sign \f$sign(I_{(x,y,z,c)})\f$.
14188        \note
14189        - The sign is set to :
14190          - \c 1 if pixel value is strictly positive.
14191          - \c -1 if pixel value is strictly negative.
14192          - \c 0 if pixel value is equal to \c 0.
14193        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14194        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14195        \sa get_sign(),
14196            abs().
14197     **/
14198     CImg<T>& sign() {
14199       cimg_for(*this,ptrd,T) *ptrd = cimg::sign(*ptrd);
14200       return *this;
14201     }
14202 
14203     //! Compute the sign of each pixel value \newinstance.
14204     CImg<Tfloat> get_sign() const {
14205       return CImg<Tfloat>(*this,false).sign();
14206     }
14207 
14208     //! Compute the cosine of each pixel value.
14209     /**
14210        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$.
14211        \note
14212        - Pixel values are regarded as being in \e radian.
14213        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14214        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14215        \sa get_cos(),
14216            sin(),
14217            sinc(),
14218            tan().
14219     **/
14220     CImg<T>& cos() {
14221       cimg_for(*this,ptrd,T) *ptrd = (T)std::cos((double)*ptrd);
14222       return *this;
14223     }
14224 
14225     //! Compute the cosine of each pixel value \newinstance.
14226     CImg<Tfloat> get_cos() const {
14227       return CImg<Tfloat>(*this,false).cos();
14228     }
14229 
14230     //! Compute the sine of each pixel value.
14231     /**
14232        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$.
14233        \note
14234        - Pixel values are regarded as being in \e radian.
14235        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14236        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14237        \sa get_sin(),
14238            cos(),
14239            sinc(),
14240            tan().
14241     **/
14242     CImg<T>& sin() {
14243       cimg_for(*this,ptrd,T) *ptrd = (T)std::sin((double)*ptrd);
14244       return *this;
14245     }
14246 
14247     //! Compute the sine of each pixel value \newinstance.
14248     CImg<Tfloat> get_sin() const {
14249       return CImg<Tfloat>(*this,false).sin();
14250     }
14251 
14252     //! Compute the sinc of each pixel value.
14253     /**
14254        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sinc \f$sinc(I_{(x,y,z,c)})\f$.
14255        \note
14256        - Pixel values are regarded as being exin \e radian.
14257        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14258        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14259        \sa get_sinc(),
14260            cos(),
14261            sin(),
14262            tan().
14263     **/
14264     CImg<T>& sinc() {
14265       cimg_for(*this,ptrd,T) *ptrd = (T)cimg::sinc((double)*ptrd);
14266       return *this;
14267     }
14268 
14269     //! Compute the sinc of each pixel value \newinstance.
14270     CImg<Tfloat> get_sinc() const {
14271       return CImg<Tfloat>(*this,false).sinc();
14272     }
14273 
14274     //! Compute the tangent of each pixel value.
14275     /**
14276        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$.
14277        \note
14278        - Pixel values are regarded as being exin \e radian.
14279        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14280        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14281        \sa get_tan(),
14282            cos(),
14283            sin(),
14284            sinc().
14285     **/
14286     CImg<T>& tan() {
14287       cimg_for(*this,ptrd,T) *ptrd = (T)std::tan((double)*ptrd);
14288       return *this;
14289     }
14290 
14291     //! Compute the tangent of each pixel value \newinstance.
14292     CImg<Tfloat> get_tan() const {
14293       return CImg<Tfloat>(*this,false).tan();
14294     }
14295 
14296     //! Compute the hyperbolic cosine of each pixel value.
14297     /**
14298        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic cosine \f$cosh(I_{(x,y,z,c)})\f$.
14299        \note
14300        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14301        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14302        \sa get_cosh(),
14303            sinh(),
14304            tanh().
14305     **/
14306     CImg<T>& cosh() {
14307       cimg_for(*this,ptrd,T) *ptrd = (T)std::cosh((double)*ptrd);
14308       return *this;
14309     }
14310 
14311     //! Compute the hyperbolic cosine of each pixel value \newinstance.
14312     CImg<Tfloat> get_cosh() const {
14313       return CImg<Tfloat>(*this,false).cosh();
14314     }
14315 
14316     //! Compute the hyperbolic sine of each pixel value.
14317     /**
14318        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic sine \f$sinh(I_{(x,y,z,c)})\f$.
14319        \note
14320        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14321        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14322        \sa get_sinh(),
14323            cosh(),
14324            tanh().
14325     **/
14326     CImg<T>& sinh() {
14327       cimg_for(*this,ptrd,T) *ptrd = (T)std::sinh((double)*ptrd);
14328       return *this;
14329     }
14330 
14331     //! Compute the hyperbolic sine of each pixel value \newinstance.
14332     CImg<Tfloat> get_sinh() const {
14333       return CImg<Tfloat>(*this,false).sinh();
14334     }
14335 
14336     //! Compute the hyperbolic tangent of each pixel value.
14337     /**
14338        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic tangent \f$tanh(I_{(x,y,z,c)})\f$.
14339        \note
14340        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14341        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14342        \sa get_tanh(),
14343            cosh(),
14344            sinh().
14345     **/
14346     CImg<T>& tanh() {
14347       cimg_for(*this,ptrd,T) *ptrd = (T)std::tanh((double)*ptrd);
14348       return *this;
14349     }
14350 
14351     //! Compute the hyperbolic tangent of each pixel value \newinstance.
14352     CImg<Tfloat> get_tanh() const {
14353       return CImg<Tfloat>(*this,false).tanh();
14354     }
14355 
14356     //! Compute the arccosine of each pixel value.
14357     /**
14358        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arccosine \f$acos(I_{(x,y,z,c)})\f$.
14359        \note
14360        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14361        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14362        \sa get_acos(),
14363            asin(),
14364            atan().
14365     **/
14366     CImg<T>& acos() {
14367       cimg_for(*this,ptrd,T) *ptrd = (T)std::acos((double)*ptrd);
14368       return *this;
14369     }
14370 
14371     //! Compute the arccosine of each pixel value \newinstance.
14372     CImg<Tfloat> get_acos() const {
14373       return CImg<Tfloat>(*this,false).acos();
14374     }
14375 
14376     //! Compute the arcsine of each pixel value.
14377     /**
14378        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arcsine \f$asin(I_{(x,y,z,c)})\f$.
14379        \note
14380        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14381        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14382        \sa get_asin(),
14383            acos(),
14384            atan().
14385     **/
14386     CImg<T>& asin() {
14387       cimg_for(*this,ptrd,T) *ptrd = (T)std::asin((double)*ptrd);
14388       return *this;
14389     }
14390 
14391     //! Compute the arcsine of each pixel value \newinstance.
14392     CImg<Tfloat> get_asin() const {
14393       return CImg<Tfloat>(*this,false).asin();
14394     }
14395 
14396     //! Compute the arctangent of each pixel value.
14397     /**
14398        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent \f$atan(I_{(x,y,z,c)})\f$.
14399        \note
14400        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14401        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14402        \sa get_atan(),
14403            acos(),
14404            asin().
14405     **/
14406     CImg<T>& atan() {
14407       cimg_for(*this,ptrd,T) *ptrd = (T)std::atan((double)*ptrd);
14408       return *this;
14409     }
14410 
14411     //! Compute the arctangent of each pixel value \newinstance.
14412     CImg<Tfloat> get_atan() const {
14413       return CImg<Tfloat>(*this,false).atan();
14414     }
14415 
14416     //! Compute the arctangent2 of each pixel value.
14417     /**
14418        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent2 \f$atan2(I_{(x,y,z,c)})\f$.
14419        \param img : The image whose pixel values specify the second argument of the \c atan2() function.
14420        \note
14421        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14422        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14423        \par Sample code :
14424        \code
14425        const CImg<float>
14426           img_x(100,100,1,1,"x-w/2",false),   // Define an horizontal centered gradient, from '-width/2' to 'width/2'.
14427           img_y(100,100,1,1,"y-h/2",false),   // Define a vertical centered gradient, from '-height/2' to 'height/2'.
14428           img_atan2 = img_y.get_atan2(img_x); // Compute atan2(y,x) for each pixel value.
14429        (img_x,img_y,img_atan2).display();
14430        \endcode
14431        \image html ref_atan2.jpg
14432        \sa get_atan2(),
14433            atan().
14434     **/
14435     template<typename t>
14436     CImg<T>& atan2(const CImg<t>& img) {
14437       const unsigned int siz = size(), isiz = img.size();
14438       if (siz && isiz) {
14439         if (is_overlapped(img)) return atan2(+img);
14440         T *ptrd = _data, *const ptre = _data + siz;
14441         if (siz>isiz) for (unsigned int n = siz/isiz; n; --n)
14442           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)std::atan2((double)*ptrd,(double)*(ptrs++));
14443         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)std::atan2((double)*ptrd,(double)*(ptrs++));
14444       }
14445       return *this;
14446     }
14447 
14448     //! Compute the arctangent2 of each pixel value \newinstance.
14449     template<typename t>
14450     CImg<Tfloat> get_atan2(const CImg<t>& img) const {
14451       return CImg<Tfloat>(*this,false).atan2(img);
14452     }
14453 
14454     //! In-place pointwise multiplication.
14455     /**
14456        Compute the pointwise multiplication between the image instance and the specified input image \c img.
14457        \param img : Input image, as the second operand of the multiplication.
14458        \note
14459        - Similar to operator+=(const CImg<t>&), except that it performs a pointwise multiplication instead of an addition.
14460        - It does \e not perform a \e matrix multiplication. For this purpose, use operator*=(const CImg<t>&) instead.
14461        \par Sample code :
14462        \code
14463        CImg<float>
14464          img("reference.jpg"),
14465          shade(img.width,img.height(),1,1,"-(x-w/2)^2-(y-h/2)^2",false);
14466        shade.normalize(0,1);
14467        (img,shade,img.get_mul(shade)).display();
14468        \endcode
14469        \image html ref_mul.jpg
14470        \sa get_mul(),
14471            div(),
14472            operator*=(const CImg<t>&).
14473     **/
14474     template<typename t>
14475     CImg<T>& mul(const CImg<t>& img) {
14476       const unsigned int siz = size(), isiz = img.size();
14477       if (siz && isiz) {
14478         if (is_overlapped(img)) return mul(+img);
14479         T *ptrd = _data, *const ptre = _data + siz;
14480         if (siz>isiz) for (unsigned int n = siz/isiz; n; --n)
14481           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)(*ptrd * *(ptrs++));
14482         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd * *(ptrs++));
14483       }
14484       return *this;
14485     }
14486 
14487     //! In-place pointwise multiplication \newinstance.
14488     template<typename t>
14489     CImg<_cimg_Tt> get_mul(const CImg<t>& img) const {
14490       return CImg<_cimg_Tt>(*this,false).mul(img);
14491     }
14492 
14493     //! In-place pointwise division.
14494     /**
14495        Similar to mul(const CImg<t>&), except that it performs a pointwise division instead of a multiplication.
14496        \sa get_div(const CImg<t>&) const,
14497            operator/=(const CImg<t>&)
14498     **/
14499     template<typename t>
14500     CImg<T>& div(const CImg<t>& img) {
14501       const unsigned int siz = size(), isiz = img.size();
14502       if (siz && isiz) {
14503         if (is_overlapped(img)) return div(+img);
14504         T *ptrd = _data, *const ptre = _data + siz;
14505         if (siz>isiz) for (unsigned int n = siz/isiz; n; --n)
14506           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)(*ptrd / *(ptrs++));
14507         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd / *(ptrs++));
14508       }
14509       return *this;
14510     }
14511 
14512     //! In-place pointwise division \newinstance.
14513     template<typename t>
14514     CImg<_cimg_Tt> get_div(const CImg<t>& img) const {
14515       return CImg<_cimg_Tt>(*this,false).div(img);
14516     }
14517 
14518     //! Raise each pixel value to a specified power.
14519     /**
14520        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$.
14521        \param p : Used exponent.
14522        \note
14523        - The \inplace of this method statically casts the computed values to the pixel type \c T.
14524        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
14525        \par Sample code :
14526        \code
14527        const CImg<float>
14528          img0("reference.jpg"),           // Load reference color image.
14529          img1 = (img0/255).pow(1.8)*=255, // Compute gamma correction, with gamma = 1.8.
14530          img2 = (img0/255).pow(0.5)*=255; // Compute gamma correction, with gamma = 0.5.
14531        (img0,img1,img2).display();
14532        \endcode
14533        \image html ref_pow.jpg
14534        \sa get_pow(double) const,
14535            pow(const char*),
14536            pow(const CImg<t>&),
14537            sqr(),
14538            sqrt(),
14539            exp(),
14540            log(),
14541            log10().
14542     **/
14543     CImg<T>& pow(const double p) {
14544       if (p==0) return fill(1);
14545       if (p==0.5) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)std::sqrt((double)val); } return *this; }
14546       if (p==1) return *this;
14547       if (p==2) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val; } return *this; }
14548       if (p==3) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val; } return *this; }
14549       if (p==4) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val*val; } return *this; }
14550       cimg_for(*this,ptrd,T) *ptrd = (T)std::pow((double)*ptrd,p);
14551       return *this;
14552     }
14553 
14554     //! Raise each pixel value to a specified power \newinstance.
14555     CImg<Tfloat> get_pow(const double p) const {
14556       return CImg<Tfloat>(*this,false).pow(p);
14557     }
14558 
14559     //! Raise each pixel value to a power, specified from an expression.
14560     /**
14561        Similar to operator+=(const char*), except it performs a pointwise exponentiation instead of an addition.
14562        \sa get_pow(const char*) const,
14563            pow(double),
14564            pow(CImg<t>&).
14565     **/
14566     CImg<T>& pow(const char *const expression) {
14567       const unsigned int omode = cimg::exception_mode();
14568       cimg::exception_mode() = 0;
14569       try {
14570         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
14571         _cimg_math_parser mp(base,expression,"pow");
14572         T *ptrd = _data;
14573         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)std::pow((double)*ptrd,mp.eval(x,y,z,c)); ++ptrd; }
14574       } catch (CImgException&) {
14575         CImg<Tfloat> values(_width,_height,_depth,_spectrum);
14576         try {
14577           values.fill(expression,true);
14578         } catch (CImgException&) {
14579           cimg::exception_mode() = omode;
14580           values.load(expression);
14581         }
14582         pow(values);
14583       }
14584       cimg::exception_mode() = omode;
14585       return *this;
14586     }
14587 
14588     //! Raise each pixel value to a power, specified from an expression \newinstance.
14589     CImg<Tfloat> get_pow(const char *const expression) const {
14590       return CImg<Tfloat>(*this,false).pow(expression);
14591     }
14592 
14593     //! Raise each pixel value to a power, pointwisely specified from another image.
14594     /**
14595        Similar to operator+=(const CImg<t>& img), except that it performs an exponentiation instead of an addition.
14596        \sa get_pow(const CImg<t>&) const,
14597            pow(double),
14598            pow(const char*).
14599     **/
14600     template<typename t>
14601     CImg<T>& pow(const CImg<t>& img) {
14602       const unsigned int siz = size(), isiz = img.size();
14603       if (siz && isiz) {
14604         if (is_overlapped(img)) return pow(+img);
14605         T *ptrd = _data, *const ptre = _data + siz;
14606         if (siz>isiz) for (unsigned int n = siz/isiz; n; --n)
14607           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)std::pow((double)*ptrd,(double)(*(ptrs++)));
14608         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)std::pow((double)*ptrd,(double)(*(ptrs++)));
14609       }
14610       return *this;
14611     }
14612 
14613     //! Raise each pixel value to a power, pointwisely specified from another image \newinstance.
14614     template<typename t>
14615     CImg<Tfloat> get_pow(const CImg<t>& img) const {
14616       return CImg<Tfloat>(*this,false).pow(img);
14617     }
14618 
14619     //! Compute the bitwise left rotation of each pixel value.
14620     /**
14621        Similar to operator<<=(unsigned int), except that it performs a left rotation instead of a left shift.
14622        \sa get_rol(unsigned int) const,
14623            rol(const char*),
14624            rol(const CImg<t>&),
14625            operator<<=(unsigned int),
14626            operator>>=(unsigned int).
14627     **/
14628     CImg<T>& rol(const unsigned int n=1) {
14629       cimg_for(*this,ptrd,T) *ptrd = (T)cimg::rol(*ptrd,n);
14630       return *this;
14631     }
14632 
14633     //! Compute the bitwise left rotation of each pixel value \newinstance.
14634     CImg<T> get_rol(const unsigned int n=1) const {
14635       return (+*this).rol(n);
14636     }
14637 
14638     //! Compute the bitwise left rotation of each pixel value.
14639     /**
14640        Similar to operator<<=(const char*), except that it performs a left rotation instead of a left shift.
14641        \sa get_rol(const char*) const,
14642            rol(unsigned int),
14643            rol(const CImg<t>&),
14644            operator<<=(const char*),
14645            operator>>=(const char*).
14646     **/
14647     CImg<T>& rol(const char *const expression) {
14648       const unsigned int omode = cimg::exception_mode();
14649       cimg::exception_mode() = 0;
14650       try {
14651         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
14652         _cimg_math_parser mp(base,expression,"rol");
14653         T *ptrd = _data;
14654         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::rol(*ptrd,(unsigned int)mp.eval(x,y,z,c)); ++ptrd; }
14655       } catch (CImgException&) {
14656         CImg<Tfloat> values(_width,_height,_depth,_spectrum);
14657         try {
14658           values.fill(expression,true);
14659         } catch (CImgException&) {
14660           cimg::exception_mode() = omode;
14661           values.load(expression);
14662         }
14663         rol(values);
14664       }
14665       cimg::exception_mode() = omode;
14666       return *this;
14667     }
14668 
14669     //! Compute the bitwise left rotation of each pixel value \newinstance.
14670     CImg<T> get_rol(const char *const expression) const {
14671       return (+*this).rol(expression);
14672     }
14673 
14674     //! Compute the bitwise left rotation of each pixel value.
14675     /**
14676        Similar to operator<<=(const CImg<t>&), except that it performs a left rotation instead of a left shift.
14677        \sa get_rol(const CImg<t>&) const,
14678            rol(unsigned int),
14679            rol(const char*),
14680            operator<<=(const CImg<t>&),
14681            operator>>=(const CImg<t>&).
14682     **/
14683     template<typename t>
14684     CImg<T>& rol(const CImg<t>& img) {
14685       const unsigned int siz = size(), isiz = img.size();
14686       if (siz && isiz) {
14687         if (is_overlapped(img)) return rol(+img);
14688         T *ptrd = _data, *const ptre = _data + siz;
14689         if (siz>isiz) for (unsigned int n = siz/isiz; n; --n)
14690           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)cimg::rol(*ptrd,(unsigned int)(*(ptrs++)));
14691         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)cimg::rol(*ptrd,(unsigned int)(*(ptrs++)));
14692       }
14693       return *this;
14694     }
14695 
14696     //! Compute the bitwise left rotation of each pixel value \newinstance.
14697     template<typename t>
14698     CImg<T> get_rol(const CImg<t>& img) const {
14699       return (+*this).rol(img);
14700     }
14701 
14702     //! Compute the bitwise right rotation of each pixel value.
14703     /**
14704        Similar to operator>>=(unsigned int), except that it performs a right rotation instead of a right shift.
14705        \sa get_ror(unsigned int) const,
14706            ror(const char*),
14707            ror(const CImg<t>&),
14708            operator<<=(unsigned int),
14709            operator>>=(unsigned int).
14710     **/
14711     CImg<T>& ror(const unsigned int n=1) {
14712       cimg_for(*this,ptrd,T) *ptrd = (T)cimg::ror(*ptrd,n);
14713       return *this;
14714     }
14715 
14716     //! Compute the bitwise right rotation of each pixel value \newinstance.
14717     CImg<T> get_ror(const unsigned int n=1) const {
14718       return (+*this).ror(n);
14719     }
14720 
14721     //! Compute the bitwise right rotation of each pixel value.
14722     /**
14723        Similar to operator>>=(const char*), except that it performs a right rotation instead of a right shift.
14724        \sa get_ror(const char*) const,
14725            ror(unsigned int),
14726            ror(const CImg<t>&),
14727            operator<<=(const char*),
14728            operator>>=(const char*).
14729     **/
14730     CImg<T>& ror(const char *const expression) {
14731       const unsigned int omode = cimg::exception_mode();
14732       cimg::exception_mode() = 0;
14733       try {
14734         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
14735         _cimg_math_parser mp(base,expression,"ror");
14736         T *ptrd = _data;
14737         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::ror(*ptrd,(unsigned int)mp.eval(x,y,z,c)); ++ptrd; }
14738       } catch (CImgException&) {
14739         CImg<Tfloat> values(_width,_height,_depth,_spectrum);
14740         try {
14741           values.fill(expression,true);
14742         } catch (CImgException&) {
14743           cimg::exception_mode() = omode;
14744           values.load(expression);
14745         }
14746         ror(values);
14747       }
14748       cimg::exception_mode() = omode;
14749       return *this;
14750     }
14751 
14752     //! Compute the bitwise right rotation of each pixel value \newinstance.
14753     CImg<T> get_ror(const char *const expression) const {
14754       return (+*this).ror(expression);
14755     }
14756 
14757     //! Compute the bitwise right rotation of each pixel value.
14758     /**
14759        Similar to operator>>=(const CImg<t>&), except that it performs a right rotation instead of a right shift.
14760        \sa get_ror(const CImg<t>&),
14761            ror(unsigned int),
14762            ror(const char *),
14763            operator<<=(const CImg<t>&),
14764            operator>>=(const CImg<t>&).
14765     **/
14766     template<typename t>
14767     CImg<T>& ror(const CImg<t>& img) {
14768       const unsigned int siz = size(), isiz = img.size();
14769       if (siz && isiz) {
14770         if (is_overlapped(img)) return ror(+img);
14771         T *ptrd = _data, *const ptre = _data + siz;
14772         if (siz>isiz) for (unsigned int n = siz/isiz; n; --n)
14773           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)cimg::ror(*ptrd,(unsigned int)(*(ptrs++)));
14774         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)cimg::ror(*ptrd,(unsigned int)(*(ptrs++)));
14775       }
14776       return *this;
14777     }
14778 
14779     //! Compute the bitwise right rotation of each pixel value \newinstance.
14780     template<typename t>
14781     CImg<T> get_ror(const CImg<t>& img) const {
14782       return (+*this).ror(img);
14783     }
14784 
14785     //! Pointwise min operator between an image and a value.
14786     CImg<T>& min(const T val) {
14787       cimg_for(*this,ptrd,T) *ptrd = cimg::min(*ptrd,val);
14788       return *this;
14789     }
14790 
14791     CImg<T> get_min(const T val) const {
14792       return (+*this).min(val);
14793     }
14794 
14795     //! Pointwise min operator between two images.
14796     template<typename t>
14797     CImg<T>& min(const CImg<t>& img) {
14798       const unsigned int siz = size(), isiz = img.size();
14799       if (siz && isiz) {
14800         if (is_overlapped(img)) return min(+img);
14801         T *ptrd = _data, *const ptre = _data + siz;
14802         if (siz>isiz) for (unsigned int n = siz/isiz; n; --n)
14803           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = cimg::min((T)*(ptrs++),*ptrd);
14804         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::min((T)*(ptrs++),*ptrd);
14805       }
14806       return *this;
14807     }
14808 
14809     template<typename t>
14810     CImg<_cimg_Tt> get_min(const CImg<t>& img) const {
14811       return CImg<_cimg_Tt>(*this,false).min(img);
14812     }
14813 
14814     //! Pointwise min operator between an image and a string.
14815     CImg<T>& min(const char *const expression) {
14816       const unsigned int omode = cimg::exception_mode();
14817       cimg::exception_mode() = 0;
14818       try {
14819         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
14820         _cimg_math_parser mp(base,expression,"min");
14821         T *ptrd = _data;
14822         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::min(*ptrd,(T)mp.eval(x,y,z,c)); ++ptrd; }
14823       } catch (CImgException&) {
14824         CImg<T> values(_width,_height,_depth,_spectrum);
14825         try {
14826           values.fill(expression,true);
14827         } catch (CImgException&) {
14828           cimg::exception_mode() = omode;
14829           values.load(expression);
14830         }
14831         min(values);
14832       }
14833       cimg::exception_mode() = omode;
14834       return *this;
14835     }
14836 
14837     CImg<Tfloat> get_min(const char *const expression) const {
14838       return CImg<Tfloat>(*this,false).min(expression);
14839     }
14840 
14841     //! Pointwise max operator between an image and a value.
14842     CImg<T>& max(const T val) {
14843       cimg_for(*this,ptrd,T) *ptrd = cimg::max(*ptrd,val);
14844       return *this;
14845     }
14846 
14847     CImg<T> get_max(const T val) const {
14848       return (+*this).max(val);
14849     }
14850 
14851     //! Pointwise max operator between two images.
14852     template<typename t>
14853     CImg<T>& max(const CImg<t>& img) {
14854       const unsigned int siz = size(), isiz = img.size();
14855       if (siz && isiz) {
14856         if (is_overlapped(img)) return max(+img);
14857         T *ptrd = _data, *const ptre = _data + siz;
14858         if (siz>isiz) for (unsigned int n = siz/isiz; n; --n)
14859           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = cimg::max((T)*(ptrs++),*ptrd);
14860         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::max((T)*(ptrs++),*ptrd);
14861       }
14862       return *this;
14863     }
14864 
14865     template<typename t>
14866     CImg<_cimg_Tt> get_max(const CImg<t>& img) const {
14867       return CImg<_cimg_Tt>(*this,false).max(img);
14868     }
14869 
14870     //! Pointwise max operator between an image and a string.
14871     CImg<T>& max(const char *const expression) {
14872       const unsigned int omode = cimg::exception_mode();
14873       cimg::exception_mode() = 0;
14874       try {
14875         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
14876         _cimg_math_parser mp(base,expression,"max");
14877         T *ptrd = _data;
14878         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::max(*ptrd,(T)mp.eval(x,y,z,c)); ++ptrd; }
14879       } catch (CImgException&) {
14880         CImg<T> values(_width,_height,_depth,_spectrum);
14881         try {
14882           values.fill(expression,true);
14883         } catch (CImgException&) {
14884           cimg::exception_mode() = omode;
14885           values.load(expression);
14886         }
14887         max(values);
14888       }
14889       cimg::exception_mode() = omode;
14890       return *this;
14891     }
14892 
14893     CImg<Tfloat> get_max(const char *const expression) const {
14894       return CImg<Tfloat>(*this,false).max(expression);
14895     }
14896 
14897     //! Return a reference to the minimum pixel value of the image instance
14898     T& min() {
14899       if (is_empty())
14900         throw CImgInstanceException(_cimg_instance
14901                                     "min() : Empty instance.",
14902                                     cimg_instance);
14903       T *ptr_min = _data;
14904       T min_value = *ptr_min;
14905       cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
14906       return *ptr_min;
14907     }
14908 
14909     const T& min() const {
14910       if (is_empty())
14911         throw CImgInstanceException(_cimg_instance
14912                                     "min() : Empty instance.",
14913                                     cimg_instance);
14914       const T *ptr_min = _data;
14915       T min_value = *ptr_min;
14916       cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
14917       return *ptr_min;
14918     }
14919 
14920     //! Return a reference to the maximum pixel value of the image instance
14921     T& max() {
14922       if (is_empty())
14923         throw CImgInstanceException(_cimg_instance
14924                                     "max() : Empty instance.",
14925                                     cimg_instance);
14926       T *ptr_max = _data;
14927       T max_value = *ptr_max;
14928       cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
14929       return *ptr_max;
14930     }
14931 
14932     const T& max() const {
14933       if (is_empty())
14934         throw CImgInstanceException(_cimg_instance
14935                                     "max() : Empty instance.",
14936                                     cimg_instance);
14937       const T *ptr_max = _data;
14938       T max_value = *ptr_max;
14939       cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
14940       return *ptr_max;
14941     }
14942 
14943     //! Return a reference to the minimum pixel value and return also the maximum pixel value.
14944     template<typename t>
14945     T& min_max(t& max_val) {
14946       if (is_empty())
14947         throw CImgInstanceException(_cimg_instance
14948                                     "min_max() : Empty instance.",
14949                                     cimg_instance);
14950       T *ptr_min = _data;
14951       T min_value = *ptr_min, max_value = min_value;
14952       cimg_for(*this,ptrs,T) {
14953         const T val = *ptrs;
14954         if (val<min_value) { min_value = val; ptr_min = ptrs; }
14955         if (val>max_value) max_value = val;
14956       }
14957       max_val = (t)max_value;
14958       return *ptr_min;
14959     }
14960 
14961     template<typename t>
14962     const T& min_max(t& max_val) const {
14963       if (is_empty())
14964         throw CImgInstanceException(_cimg_instance
14965                                     "min_max() : Empty instance.",
14966                                     cimg_instance);
14967       const T *ptr_min = _data;
14968       T min_value = *ptr_min, max_value = min_value;
14969       cimg_for(*this,ptrs,T) {
14970         const T val = *ptrs;
14971         if (val<min_value) { min_value = val; ptr_min = ptrs; }
14972         if (val>max_value) max_value = val;
14973       }
14974       max_val = (t)max_value;
14975       return *ptr_min;
14976     }
14977 
14978     //! Return a reference to the maximum pixel value and return also the minimum pixel value.
14979     template<typename t>
14980     T& max_min(t& min_val) {
14981       if (is_empty())
14982         throw CImgInstanceException(_cimg_instance
14983                                     "max_min() : Empty instance.",
14984                                     cimg_instance);
14985       T *ptr_max = _data;
14986       T max_value = *ptr_max, min_value = max_value;
14987       cimg_for(*this,ptrs,T) {
14988         const T val = *ptrs;
14989         if (val>max_value) { max_value = val; ptr_max = ptrs; }
14990         if (val<min_value) min_value = val;
14991       }
14992       min_val = (t)min_value;
14993       return *ptr_max;
14994     }
14995 
14996     template<typename t>
14997     const T& max_min(t& min_val) const {
14998       if (is_empty())
14999         throw CImgInstanceException(_cimg_instance
15000                                     "max_min() : Empty instance.",
15001                                     cimg_instance);
15002       const T *ptr_max = _data;
15003       T max_value = *ptr_max, min_value = max_value;
15004       cimg_for(*this,ptrs,T) {
15005         const T val = *ptrs;
15006         if (val>max_value) { max_value = val; ptr_max = ptrs; }
15007         if (val<min_value) min_value = val;
15008       }
15009       min_val = (t)min_value;
15010       return *ptr_max;
15011     }
15012 
15013     //! Return the kth smallest element of the image.
15014     T kth_smallest(const unsigned int k) const {
15015       if (is_empty())
15016         throw CImgInstanceException(_cimg_instance
15017                                     "kth_smallest() : Empty instance.",
15018                                     cimg_instance);
15019       CImg<T> arr(*this);
15020       unsigned int l = 0, ir = size() - 1;
15021       for (;;) {
15022         if (ir<=l+1) {
15023           if (ir==l+1 && arr[ir]<arr[l]) cimg::swap(arr[l],arr[ir]);
15024           return arr[k];
15025         } else {
15026           const unsigned int mid = (l + ir)>>1;
15027           cimg::swap(arr[mid],arr[l+1]);
15028           if (arr[l]>arr[ir]) cimg::swap(arr[l],arr[ir]);
15029           if (arr[l+1]>arr[ir]) cimg::swap(arr[l+1],arr[ir]);
15030           if (arr[l]>arr[l+1]) cimg::swap(arr[l],arr[l+1]);
15031           unsigned int i = l + 1, j = ir;
15032           const T pivot = arr[l+1];
15033           for (;;) {
15034             do ++i; while (arr[i]<pivot);
15035             do --j; while (arr[j]>pivot);
15036             if (j<i) break;
15037             cimg::swap(arr[i],arr[j]);
15038           }
15039           arr[l+1] = arr[j];
15040           arr[j] = pivot;
15041           if (j>=k) ir = j - 1;
15042           if (j<=k) l = i;
15043         }
15044       }
15045       return 0;
15046     }
15047 
15048     //! Return the median value of the image.
15049     T median() const {
15050       const unsigned int s = size();
15051       const T res = kth_smallest(s>>1);
15052       return (s%2)?res:((res+kth_smallest((s>>1)-1))/2);
15053     }
15054 
15055     //! Return the sum of all the pixel values in an image.
15056     Tdouble sum() const {
15057       if (is_empty())
15058         throw CImgInstanceException(_cimg_instance
15059                                     "sum() : Empty instance.",
15060                                     cimg_instance);
15061       Tdouble res = 0;
15062       cimg_for(*this,ptrs,T) res+=(Tdouble)*ptrs;
15063       return res;
15064     }
15065 
15066     //! Return the mean pixel value of the image instance.
15067     Tdouble mean() const {
15068       if (is_empty())
15069         throw CImgInstanceException(_cimg_instance
15070                                     "mean() : Empty instance.",
15071                                     cimg_instance);
15072       Tdouble res = 0;
15073       cimg_for(*this,ptrs,T) res+=(Tdouble)*ptrs;
15074       return res/size();
15075     }
15076 
15077     //! Return the variance of the image.
15078     /**
15079        @param variance_method Determines how to calculate the variance
15080        <table border="0">
15081        <tr><td>0</td>
15082        <td>Second moment:
15083        @f$ v = 1/N \sum\limits_{k=1}^{N} (x_k - \bar x)^2
15084        = 1/N \left( \sum\limits_{k=1}^N x_k^2 - \left( \sum\limits_{k=1}^N x_k \right)^2 / N \right) @f$
15085        with @f$ \bar x = 1/N \sum\limits_{k=1}^N x_k \f$</td></tr>
15086        <tr><td>1</td>
15087        <td>Best unbiased estimator: @f$ v = \frac{1}{N-1} \sum\limits_{k=1}^{N} (x_k - \bar x)^2 @f$</td></tr>
15088        <tr><td>2</td>
15089        <td>Least median of squares</td></tr>
15090        <tr><td>3</td>
15091        <td>Least trimmed of squares</td></tr>
15092        </table>
15093     */
15094     Tdouble variance(const unsigned int variance_method=1) const {
15095       Tdouble foo;
15096       return variance_mean(variance_method,foo);
15097     }
15098 
15099     //! Return the variance and the mean of the image.
15100     template<typename t>
15101     Tdouble variance_mean(const unsigned int variance_method, t& mean) const {
15102       if (is_empty())
15103         throw CImgInstanceException(_cimg_instance
15104                                     "variance() : Empty instance.",
15105                                     cimg_instance);
15106 
15107       Tdouble variance = 0, average = 0;
15108       const unsigned int siz = size();
15109       switch (variance_method) {
15110       case 0 :{ // Least mean square (standard definition)
15111         Tdouble S = 0, S2 = 0;
15112         cimg_for(*this,ptrs,T) { const Tdouble val = (Tdouble)*ptrs; S+=val; S2+=val*val; }
15113         variance = (S2 - S*S/siz)/siz;
15114         average = S;
15115       } break;
15116       case 1 : { // Least mean square (robust definition)
15117         Tdouble S = 0, S2 = 0;
15118         cimg_for(*this,ptrs,T) { const Tdouble val = (Tdouble)*ptrs; S+=val; S2+=val*val; }
15119         variance = siz>1?(S2 - S*S/siz)/(siz - 1):0;
15120         average = S;
15121       } break;
15122       case 2 : { // Least Median of Squares (MAD)
15123         CImg<Tfloat> buf(*this);
15124         buf.sort();
15125         const unsigned int siz2 = siz>>1;
15126         const Tdouble med_i = (double)buf[siz2];
15127         cimg_for(buf,ptrs,Tfloat) { const Tdouble val = (Tdouble)*ptrs; *ptrs = (Tfloat)cimg::abs(val - med_i); average+=val; }
15128         buf.sort();
15129         const Tdouble sig = (Tdouble)(1.4828*buf[siz2]);
15130         variance = sig*sig;
15131       } break;
15132       default : { // Least trimmed of Squares
15133         CImg<Tfloat> buf(*this);
15134         const unsigned int siz2 = siz>>1;
15135         cimg_for(buf,ptrs,Tfloat) { const Tdouble val = (Tdouble)*ptrs; (*ptrs)=(Tfloat)((*ptrs)*val); average+=val; }
15136         buf.sort();
15137         Tdouble a = 0;
15138         const Tfloat *ptrs = buf._data;
15139         for (unsigned int j = 0; j<siz2; ++j) a+=(Tdouble)*(ptrs++);
15140         const Tdouble sig = (Tdouble)(2.6477*std::sqrt(a/siz2));
15141         variance = sig*sig;
15142       }
15143       }
15144       mean = (t)(average/siz);
15145       return variance>0?variance:0;
15146     }
15147 
15148     //! Estimate noise variance of the image instance.
15149     /**
15150        \param variance_method : method to compute the variance
15151        \note Because of structures such as edges in images it is
15152        recommanded to use a robust variance estimation. The variance of the
15153        noise is estimated by computing the variance of the Laplacian \f$(\Delta
15154        I)^2 \f$ scaled by a factor \f$c\f$ insuring \f$ c E[(\Delta I)^2]=
15155        \sigma^2\f$ where \f$\sigma\f$ is the noise variance.
15156        \see variance()
15157     **/
15158     Tdouble variance_noise(const unsigned int variance_method=2) const {
15159       const unsigned int siz = size();
15160       if (!siz || !_data) return 0;
15161       if (variance_method>1) { // Compute a scaled version of the Laplacian.
15162         CImg<Tdouble> tmp(*this);
15163         if (_depth==1) {
15164           const Tdouble cste = 1.0/std::sqrt(20.0); // Depends on how the Laplacian is computed.
15165           CImg_3x3(I,T);
15166           cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) {
15167             tmp(x,y,c) = cste*((Tdouble)Inc + (Tdouble)Ipc + (Tdouble)Icn +
15168                                (Tdouble)Icp - 4*(Tdouble)Icc);
15169           }
15170         } else {
15171           const Tdouble cste = 1.0/std::sqrt(42.0); // Depends on how the Laplacian is computed.
15172           CImg_3x3x3(I,T);
15173           cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,T) {
15174             tmp(x,y,z,c) = cste*(
15175                                  (Tdouble)Incc + (Tdouble)Ipcc + (Tdouble)Icnc + (Tdouble)Icpc +
15176                                  (Tdouble)Iccn + (Tdouble)Iccp - 6*(Tdouble)Iccc);
15177           }
15178         }
15179         return tmp.variance(variance_method);
15180       }
15181 
15182       // Version that doesn't need intermediate images.
15183       Tdouble variance = 0, S = 0, S2 = 0;
15184       if (_depth==1) {
15185         const Tdouble cste = 1.0/std::sqrt(20.0);
15186         CImg_3x3(I,T);
15187         cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) {
15188           const Tdouble val = cste*((Tdouble)Inc + (Tdouble)Ipc +
15189                                     (Tdouble)Icn + (Tdouble)Icp - 4*(Tdouble)Icc);
15190           S+=val; S2+=val*val;
15191         }
15192       } else {
15193         const Tdouble cste = 1.0/std::sqrt(42.0);
15194         CImg_3x3x3(I,T);
15195         cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,T) {
15196           const Tdouble val = cste *
15197             ((Tdouble)Incc + (Tdouble)Ipcc + (Tdouble)Icnc +
15198              (Tdouble)Icpc +
15199              (Tdouble)Iccn + (Tdouble)Iccp - 6*(Tdouble)Iccc);
15200           S+=val; S2+=val*val;
15201         }
15202       }
15203       if (variance_method) variance = siz>1?(S2 - S*S/siz)/(siz - 1):0;
15204       else variance = (S2 - S*S/siz)/siz;
15205       return variance>0?variance:0;
15206     }
15207 
15208     //! Compute the MSE (Mean-Squared Error) between two images.
15209     template<typename t>
15210     Tdouble MSE(const CImg<t>& img) const {
15211       if (img.size()!=size())
15212         throw CImgArgumentException(_cimg_instance
15213                                     "MSE() : Instance and specified image (%u,%u,%u,%u,%p) have different dimensions.",
15214                                     cimg_instance,
15215                                     img._width,img._height,img._depth,img._spectrum,img._data);
15216       Tdouble vMSE = 0;
15217       const t* ptr2 = img.end();
15218       cimg_for(*this,ptr1,T) {
15219         const Tdouble diff = (Tdouble)*ptr1 - (Tdouble)*(--ptr2);
15220         vMSE+=diff*diff;
15221       }
15222       vMSE/=img.size();
15223       return vMSE;
15224     }
15225 
15226     //! Compute the PSNR between two images.
15227     template<typename t>
15228     Tdouble PSNR(const CImg<t>& img, const Tdouble valmax=255) const {
15229       const Tdouble vMSE = (Tdouble)std::sqrt(MSE(img));
15230       return (vMSE!=0)?(Tdouble)(20*std::log10(valmax/vMSE)):(Tdouble)(cimg::type<Tdouble>::max());
15231     }
15232 
15233     //! Evaluate math expression.
15234     /**
15235        If you make successive evaluations on the same image and with the same expression,
15236        you can set 'expr' to 0 after the first call, to skip the math parsing step.
15237     **/
15238     double eval(const char *const expression, const double x=0, const double y=0, const double z=0, const double c=0) const {
15239       static _cimg_math_parser *mp = 0;
15240       if (expression) { delete mp; mp = 0; mp = new _cimg_math_parser(*this,expression,"eval"); }
15241       if (!mp)
15242         throw CImgArgumentException(_cimg_instance
15243                                     "eval() : No expression has been previously specified.",
15244                                     cimg_instance);
15245       return mp->eval(x,y,z,c);
15246     }
15247 
15248     //! Compute a statistics vector (min,max,mean,variance,xmin,ymin,zmin,cmin,xmax,ymax,zmax,cmax).
15249     CImg<T>& stats(const unsigned int variance_method=1) {
15250       return get_stats(variance_method).move_to(*this);
15251     }
15252 
15253     CImg<Tdouble> get_stats(const unsigned int variance_method=1) const {
15254       if (is_empty()) return CImg<doubleT>();
15255       const unsigned int siz = size();
15256       const T *const odata = _data;
15257       const T *pm = odata, *pM = odata;
15258       Tdouble S = 0, S2 = 0;
15259       T m = *pm, M = m;
15260       cimg_for(*this,ptrs,T) {
15261         const T val = *ptrs;
15262         const Tdouble _val = (Tdouble)val;
15263         if (val<m) { m = val; pm = ptrs; }
15264         if (val>M) { M = val; pM = ptrs; }
15265         S+=_val;
15266         S2+=_val*_val;
15267       }
15268       const Tdouble
15269         mean_value = S/siz,
15270         _variance_value = variance_method==0?(S2 - S*S/siz)/siz:
15271         (variance_method==1?(siz>1?(S2 - S*S/siz)/(siz - 1):0):
15272          variance(variance_method)),
15273         variance_value = _variance_value>0?_variance_value:0;
15274       int
15275         xm = 0, ym = 0, zm = 0, cm = 0,
15276         xM = 0, yM = 0, zM = 0, cM = 0;
15277       contains(*pm,xm,ym,zm,cm);
15278       contains(*pM,xM,yM,zM,cM);
15279       return CImg<Tdouble>(1,12).fill((Tdouble)m,(Tdouble)M,mean_value,variance_value,
15280                                       (Tdouble)xm,(Tdouble)ym,(Tdouble)zm,(Tdouble)cm,
15281                                       (Tdouble)xM,(Tdouble)yM,(Tdouble)zM,(Tdouble)cM);
15282     }
15283 
15284     //@}
15285     //-------------------------------------
15286     //
15287     //! \name Vector / Matrix Operations
15288     //@{
15289     //-------------------------------------
15290 
15291     //! Return the norm of the current vector/matrix. \p ntype = norm type (0=L2, 1=L1, -1=Linf).
15292     Tdouble magnitude(const int magnitude_type=2) const {
15293       if (is_empty())
15294         throw CImgInstanceException(_cimg_instance
15295                                     "magnitude() : Empty instance.",
15296                                     cimg_instance);
15297       Tdouble res = 0;
15298       switch (magnitude_type) {
15299       case -1 : {
15300         cimg_for(*this,ptrs,T) { const Tdouble val = (Tdouble)cimg::abs(*ptrs); if (val>res) res = val; }
15301       } break;
15302       case 1 : {
15303         cimg_for(*this,ptrs,T) res+=(Tdouble)cimg::abs(*ptrs);
15304       } break;
15305       default : {
15306         cimg_for(*this,ptrs,T) res+=(Tdouble)cimg::sqr(*ptrs);
15307         res = (Tdouble)std::sqrt(res);
15308       }
15309       }
15310       return res;
15311     }
15312 
15313     //! Return the trace of the image, viewed as a matrix.
15314     Tdouble trace() const {
15315       if (is_empty())
15316         throw CImgInstanceException(_cimg_instance
15317                                     "trace() : Empty instance.",
15318                                     cimg_instance);
15319       Tdouble res = 0;
15320       cimg_forX(*this,k) res+=(Tdouble)(*this)(k,k);
15321       return res;
15322     }
15323 
15324     //! Return the determinant of the image, viewed as a matrix.
15325     Tdouble det() const {
15326       if (is_empty() || _width!=_height || _depth!=1 || _spectrum!=1)
15327         throw CImgInstanceException(_cimg_instance
15328                                     "det() : Instance is empty or not a square matrix.",
15329                                     cimg_instance);
15330 
15331       switch (_width) {
15332       case 1 : return (Tdouble)((*this)(0,0));
15333       case 2 : return (Tdouble)((*this)(0,0))*(Tdouble)((*this)(1,1)) - (Tdouble)((*this)(0,1))*(Tdouble)((*this)(1,0));
15334       case 3 : {
15335         const Tdouble
15336           a = (Tdouble)_data[0], d = (Tdouble)_data[1], g = (Tdouble)_data[2],
15337           b = (Tdouble)_data[3], e = (Tdouble)_data[4], h = (Tdouble)_data[5],
15338           c = (Tdouble)_data[6], f = (Tdouble)_data[7], i = (Tdouble)_data[8];
15339         return i*a*e - a*h*f - i*b*d + b*g*f + c*d*h - c*g*e;
15340       }
15341       default : {
15342         CImg<Tfloat> lu(*this);
15343         CImg<uintT> indx;
15344         bool d;
15345         lu._LU(indx,d);
15346         Tdouble res = d?(Tdouble)1:(Tdouble)-1;
15347         cimg_forX(lu,i) res*=lu(i,i);
15348         return res;
15349       }
15350       }
15351       return 0;
15352     }
15353 
15354     //! Return the dot product of the current vector/matrix with the vector/matrix \p img.
15355     template<typename t>
15356     Tdouble dot(const CImg<t>& img) const {
15357       if (is_empty())
15358         throw CImgInstanceException(_cimg_instance
15359                                     "dot() : Empty instance.",
15360                                     cimg_instance);
15361       if (!img)
15362         throw CImgArgumentException(_cimg_instance
15363                                     "dot() : Empty specified image.",
15364                                     cimg_instance);
15365 
15366       const unsigned int nb = cimg::min(size(),img.size());
15367       Tdouble res = 0;
15368       for (unsigned int off = 0; off<nb; ++off) res+=(Tdouble)_data[off]*(Tdouble)img[off];
15369       return res;
15370     }
15371 
15372     //! Return a new image corresponding to the vector located at (\p x,\p y,\p z) of the current vector-valued image.
15373     CImg<T> get_vector_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const {
15374       _cimg_static CImg<T> res;
15375       if (res._height!=_spectrum) res.assign(1,_spectrum);
15376       const unsigned int whd = _width*_height*_depth;
15377       const T *ptrs = data(x,y,z);
15378       T *ptrd = res._data;
15379       cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
15380       return res;
15381     }
15382 
15383     //! Return a new image corresponding to the \a square \a matrix located at (\p x,\p y,\p z) of the current vector-valued image.
15384     CImg<T> get_matrix_at(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const {
15385       const int n = (int)std::sqrt((double)_spectrum);
15386       const T *ptrs = data(x,y,z,0);
15387       const unsigned int whd = _width*_height*_depth;
15388       CImg<T> res(n,n);
15389       T *ptrd = res._data;
15390       cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
15391       return res;
15392     }
15393 
15394     //! Return a new image corresponding to the \a diffusion \a tensor located at (\p x,\p y,\p z) of the current vector-valued image.
15395     CImg<T> get_tensor_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const {
15396       const T *ptrs = data(x,y,z,0);
15397       const unsigned int whd = _width*_height*_depth;
15398       if (_spectrum==6) return tensor(*ptrs,*(ptrs+whd),*(ptrs+2*whd),*(ptrs+3*whd),*(ptrs+4*whd),*(ptrs+5*whd));
15399       if (_spectrum==3) return tensor(*ptrs,*(ptrs+whd),*(ptrs+2*whd));
15400       return tensor(*ptrs);
15401     }
15402 
15403     //! Set the image \p vec as the \a vector \a valued pixel located at (\p x,\p y,\p z) of the current vector-valued image.
15404     template<typename t>
15405     CImg<T>& set_vector_at(const CImg<t>& vec, const unsigned int x, const unsigned int y=0, const unsigned int z=0) {
15406       if (x<_width && y<_height && z<_depth) {
15407         const t *ptrs = vec._data;
15408         const unsigned int whd = _width*_height*_depth;
15409         T *ptrd = data(x,y,z);
15410         for (unsigned int k = cimg::min((unsigned int)vec.size(),_spectrum); k; --k) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
15411       }
15412       return *this;
15413     }
15414 
15415     //! Set the image \p vec as the \a square \a matrix-valued pixel located at (\p x,\p y,\p z) of the current vector-valued image.
15416     template<typename t>
15417     CImg<T>& set_matrix_at(const CImg<t>& mat, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
15418       return set_vector_at(mat,x,y,z);
15419     }
15420 
15421     //! Set the image \p vec as the \a tensor \a valued pixel located at (\p x,\p y,\p z) of the current vector-valued image.
15422     template<typename t>
15423     CImg<T>& set_tensor_at(const CImg<t>& ten, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
15424       T *ptrd = data(x,y,z,0);
15425       const unsigned int siz = _width*_height*_depth;
15426       if (ten._height==2) {
15427         *ptrd = (T)ten[0]; ptrd+=siz;
15428         *ptrd = (T)ten[1]; ptrd+=siz;
15429         *ptrd = (T)ten[3];
15430       }
15431       else {
15432         *ptrd = (T)ten[0]; ptrd+=siz;
15433         *ptrd = (T)ten[1]; ptrd+=siz;
15434         *ptrd = (T)ten[2]; ptrd+=siz;
15435         *ptrd = (T)ten[4]; ptrd+=siz;
15436         *ptrd = (T)ten[5]; ptrd+=siz;
15437         *ptrd = (T)ten[8];
15438       }
15439       return *this;
15440     }
15441 
15442     //! Unroll all images values into a one-column vector.
15443     CImg<T>& vector() {
15444       return unroll('y');
15445     }
15446 
15447     CImg<T> get_vector() const {
15448       return get_unroll('y');
15449     }
15450 
15451     //! Realign pixel values of the image instance as a square matrix
15452     CImg<T>& matrix() {
15453       const unsigned int siz = size();
15454       switch (siz) {
15455       case 1 : break;
15456       case 4 : _width = _height = 2; break;
15457       case 9 : _width = _height = 3; break;
15458       case 16 : _width = _height = 4; break;
15459       case 25 : _width = _height = 5; break;
15460       case 36 : _width = _height = 6; break;
15461       case 49 : _width = _height = 7; break;
15462       case 64 : _width = _height = 8; break;
15463       case 81 : _width = _height = 9; break;
15464       case 100 : _width = _height = 10; break;
15465       default : {
15466         unsigned int i = 11, i2 = i*i;
15467         while (i2<siz) { i2+=2*i + 1; ++i; }
15468         if (i2==siz) _width = _height = i;
15469         else throw CImgInstanceException(_cimg_instance
15470                                          "matrix() : Invalid instance size %u (should be a square integer).",
15471                                          cimg_instance,
15472                                          siz);
15473       }
15474       }
15475       return *this;
15476     }
15477 
15478     CImg<T> get_matrix() const {
15479       return (+*this).matrix();
15480     }
15481 
15482     //! Realign pixel values of the image instance as a symmetric tensor.
15483     CImg<T>& tensor() {
15484       return get_tensor().move_to(*this);
15485     }
15486 
15487     CImg<T> get_tensor() const {
15488       CImg<T> res;
15489       const unsigned int siz = size();
15490       switch (siz) {
15491       case 1 : break;
15492       case 3 :
15493         res.assign(2,2);
15494         res(0,0) = (*this)(0);
15495         res(1,0) = res(0,1) = (*this)(1);
15496         res(1,1) = (*this)(2);
15497         break;
15498       case 6 :
15499         res.assign(3,3);
15500         res(0,0) = (*this)(0);
15501         res(1,0) = res(0,1) = (*this)(1);
15502         res(2,0) = res(0,2) = (*this)(2);
15503         res(1,1) = (*this)(3);
15504         res(2,1) = res(1,2) = (*this)(4);
15505         res(2,2) = (*this)(5);
15506         break;
15507       default :
15508         throw CImgInstanceException(_cimg_instance
15509                                     "tensor() : Invalid instance size (does not define a 1x1, 2x2 or 3x3 tensor).",
15510                                     cimg_instance);
15511       }
15512       return res;
15513     }
15514 
15515     //! Get a diagonal matrix, whose diagonal coefficients are the coefficients of the input image.
15516     CImg<T>& diagonal() {
15517       return get_diagonal().move_to(*this);
15518     }
15519 
15520     CImg<T> get_diagonal() const {
15521       if (is_empty()) return *this;
15522       CImg<T> res(size(),size(),1,1,0);
15523       cimg_foroff(*this,off) res(off,off) = (*this)(off);
15524       return res;
15525     }
15526 
15527     //! Get an identity matrix having same dimension than image instance.
15528     CImg<T>& identity_matrix() {
15529       return identity_matrix(cimg::max(_width,_height)).move_to(*this);
15530     }
15531 
15532     CImg<T> get_identity_matrix() const {
15533       return identity_matrix(cimg::max(_width,_height));
15534     }
15535 
15536     //! Return a N-numbered sequence vector from \p a0 to \p a1.
15537     CImg<T>& sequence(const T a0, const T a1) {
15538       if (is_empty()) return *this;
15539       const unsigned int siz = size() - 1;
15540       T* ptr = _data;
15541       if (siz) {
15542         const Tdouble delta = (Tdouble)a1 - (Tdouble)a0;
15543         cimg_foroff(*this,l) *(ptr++) = (T)(a0 + delta*l/siz);
15544       } else *ptr = a0;
15545       return *this;
15546     }
15547 
15548     CImg<T> get_sequence(const T a0, const T a1) const {
15549       return (+*this).sequence(a0,a1);
15550     }
15551 
15552     //! Transpose the current matrix.
15553     CImg<T>& transpose() {
15554       if (_width==1) { _width = _height; _height = 1; return *this; }
15555       if (_height==1) { _height = _width; _width = 1; return *this; }
15556       if (_width==_height) {
15557         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));
15558         return *this;
15559       }
15560       return get_transpose().move_to(*this);
15561     }
15562 
15563     CImg<T> get_transpose() const {
15564       return get_permute_axes("yxzc");
15565     }
15566 
15567     //! Compute the cross product between two 3d vectors.
15568     template<typename t>
15569     CImg<T>& cross(const CImg<t>& img) {
15570       if (_width!=1 || _height<3 || img._width!=1 || img._height<3)
15571         throw CImgInstanceException(_cimg_instance
15572                                     "cross() : Instance and/or specified image (%u,%u,%u,%u,%p) are not 3d vectors.",
15573                                     cimg_instance,
15574                                     img._width,img._height,img._depth,img._spectrum,img._data);
15575 
15576       const T x = (*this)[0], y = (*this)[1], z = (*this)[2];
15577       (*this)[0] = (T)(y*img[2] - z*img[1]);
15578       (*this)[1] = (T)(z*img[0] - x*img[2]);
15579       (*this)[2] = (T)(x*img[1] - y*img[0]);
15580       return *this;
15581     }
15582 
15583     template<typename t>
15584     CImg<_cimg_Tt> get_cross(const CImg<t>& img) const {
15585       return CImg<_cimg_Tt>(*this).cross(img);
15586     }
15587 
15588     //! Invert the current matrix.
15589     CImg<T>& invert(const bool use_LU=true) {
15590       if (_width!=_height || _depth!=1 || _spectrum!=1)
15591         throw CImgInstanceException(_cimg_instance
15592                                     "invert() : Instance is not a square matrix.",
15593                                     cimg_instance);
15594 #ifdef cimg_use_lapack
15595       int INFO = (int)use_LU, N = _width, LWORK = 4*N, *const IPIV = new int[N];
15596       Tfloat
15597         *const lapA = new Tfloat[N*N],
15598         *const WORK = new Tfloat[LWORK];
15599       cimg_forXY(*this,k,l) lapA[k*N+l] = (Tfloat)((*this)(k,l));
15600       cimg::getrf(N,lapA,IPIV,INFO);
15601       if (INFO)
15602         cimg::warn(_cimg_instance
15603                    "invert() : LAPACK function dgetrf_() returned error code %d.",
15604                    cimg_instance,
15605                    INFO);
15606       else {
15607         cimg::getri(N,lapA,IPIV,WORK,LWORK,INFO);
15608         if (INFO)
15609           cimg::warn(_cimg_instance
15610                      "invert() : LAPACK function dgetri_() returned error code %d.",
15611                      cimg_instance,
15612                      INFO);
15613       }
15614       if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)(lapA[k*N+l]); else fill(0);
15615       delete[] IPIV; delete[] lapA; delete[] WORK;
15616 #else
15617       const double dete = _width>3?-1.0:det();
15618       if (dete!=0.0 && _width==2) {
15619         const double
15620           a = _data[0], c = _data[1],
15621           b = _data[2], d = _data[3];
15622         _data[0] = (T)(d/dete); _data[1] = (T)(-c/dete);
15623         _data[2] = (T)(-b/dete); _data[3] = (T)(a/dete);
15624       } else if (dete!=0.0 && _width==3) {
15625         const double
15626           a = _data[0], d = _data[1], g = _data[2],
15627           b = _data[3], e = _data[4], h = _data[5],
15628           c = _data[6], f = _data[7], i = _data[8];
15629         _data[0] = (T)((i*e-f*h)/dete), _data[1] = (T)((g*f-i*d)/dete), _data[2] = (T)((d*h-g*e)/dete);
15630         _data[3] = (T)((h*c-i*b)/dete), _data[4] = (T)((i*a-c*g)/dete), _data[5] = (T)((g*b-a*h)/dete);
15631         _data[6] = (T)((b*f-e*c)/dete), _data[7] = (T)((d*c-a*f)/dete), _data[8] = (T)((a*e-d*b)/dete);
15632       } else {
15633         if (use_LU) { // LU-based inverse computation
15634           CImg<Tfloat> A(*this), indx, col(1,_width);
15635           bool d;
15636           A._LU(indx,d);
15637           cimg_forX(*this,j) {
15638             col.fill(0);
15639             col(j) = 1;
15640             col._solve(A,indx);
15641             cimg_forX(*this,i) (*this)(j,i) = (T)col(i);
15642           }
15643         } else { // SVD-based inverse computation
15644           CImg<Tfloat> U(_width,_width), S(1,_width), V(_width,_width);
15645           SVD(U,S,V,false);
15646           U.transpose();
15647           cimg_forY(S,k) if (S[k]!=0) S[k]=1/S[k];
15648           S.diagonal();
15649           *this = V*S*U;
15650         }
15651       }
15652 #endif
15653       return *this;
15654     }
15655 
15656     CImg<Tfloat> get_invert(const bool use_LU=true) const {
15657       return CImg<Tfloat>(*this,false).invert(use_LU);
15658     }
15659 
15660     //! Compute the pseudo-inverse (Moore-Penrose) of the matrix.
15661     CImg<T>& pseudoinvert() {
15662       return get_pseudoinvert().move_to(*this);
15663     }
15664 
15665     CImg<Tfloat> get_pseudoinvert() const {
15666       CImg<Tfloat> U, S, V;
15667       SVD(U,S,V);
15668       cimg_forX(V,x) {
15669         const Tfloat s = S(x), invs = s!=0?1/s:(Tfloat)0;
15670         cimg_forY(V,y) V(x,y)*=invs;
15671       }
15672       return V*U.transpose();
15673     }
15674 
15675     //! Solve a linear system AX=B where B=*this.
15676     template<typename t>
15677     CImg<T>& solve(const CImg<t>& A) {
15678       if (_width!=1 || _depth!=1 || _spectrum!=1 || _height!=A._height || A._depth!=1 || A._spectrum!=1)
15679         throw CImgArgumentException(_cimg_instance
15680                                     "solve() : Instance and specified matrix (%u,%u,%u,%u,%p) have incompatible dimensions.",
15681                                     cimg_instance,
15682                                     A._width,A._height,A._depth,A._spectrum,A._data);
15683       typedef _cimg_Ttfloat Ttfloat;
15684       if (A._width==A._height) {
15685 #ifdef cimg_use_lapack
15686         char TRANS = 'N';
15687         int INFO, N = _height, LWORK = 4*N, one = 1, *const IPIV = new int[N];
15688         Ttfloat
15689           *const lapA = new Ttfloat[N*N],
15690           *const lapB = new Ttfloat[N],
15691           *const WORK = new Ttfloat[LWORK];
15692         cimg_forXY(A,k,l) lapA[k*N+l] = (Ttfloat)(A(k,l));
15693         cimg_forY(*this,i) lapB[i] = (Ttfloat)((*this)(i));
15694         cimg::getrf(N,lapA,IPIV,INFO);
15695         if (INFO)
15696           cimg::warn(_cimg_instance
15697                      "solve() : LAPACK library function dgetrf_() returned error code %d.",
15698                      cimg_instance,
15699                      INFO);
15700 
15701         if (!INFO) {
15702           cimg::getrs(TRANS,N,lapA,IPIV,lapB,INFO);
15703           if (INFO)
15704             cimg::warn(_cimg_instance
15705                        "solve() : LAPACK library function dgetrs_() returned error code %d.",
15706                        cimg_instance,
15707                        INFO);
15708         }
15709         if (!INFO) cimg_forY(*this,i) (*this)(i) = (T)(lapB[i]); else fill(0);
15710         delete[] IPIV; delete[] lapA; delete[] lapB; delete[] WORK;
15711 #else
15712         CImg<Ttfloat> lu(A,false);
15713         CImg<Ttfloat> indx;
15714         bool d;
15715         lu._LU(indx,d);
15716         _solve(lu,indx);
15717 #endif
15718       } else assign(A.get_pseudoinvert()*(*this));
15719       return *this;
15720     }
15721 
15722     template<typename t>
15723     CImg<_cimg_Ttfloat> get_solve(const CImg<t>& A) const {
15724       return CImg<_cimg_Ttfloat>(*this,false).solve(A);
15725     }
15726 
15727     template<typename t, typename ti>
15728     CImg<T>& _solve(const CImg<t>& A, const CImg<ti>& indx) {
15729       typedef _cimg_Ttfloat Ttfloat;
15730       const int N = size();
15731       int ii = -1;
15732       Ttfloat sum;
15733       for (int i = 0; i<N; ++i) {
15734         const int ip = (int)indx[i];
15735         Ttfloat sum = (*this)(ip);
15736         (*this)(ip) = (*this)(i);
15737         if (ii>=0) for (int j = ii; j<=i-1; ++j) sum-=A(j,i)*(*this)(j);
15738         else if (sum!=0) ii = i;
15739         (*this)(i) = (T)sum;
15740       }
15741       for (int i = N - 1; i>=0; --i) {
15742         sum = (*this)(i);
15743         for (int j = i + 1; j<N; ++j) sum-=A(j,i)*(*this)(j);
15744         (*this)(i) = (T)(sum/A(i,i));
15745       }
15746       return *this;
15747     }
15748 
15749     //! Solve a linear system AX=B where B=*this and A is a tridiagonal matrix A = [ b0,c0,0,...; a1,b1,c1,0,... ; ... ; ...,0,aN,bN ],
15750     // stored as a 3 columns matrix (resolution uses the Thomas Algorithm).
15751     template<typename t>
15752     CImg<T>& solve_tridiagonal(const CImg<t>& A) {
15753       const unsigned int siz = (int)size();
15754       if (A._width!=3 || A._height!=siz)
15755         throw CImgArgumentException(_cimg_instance
15756                                     "solve_tridiagonal() : Instance and tridiagonal matrix "
15757                                     "(%u,%u,%u,%u,%p) have incompatible dimensions.",
15758                                     cimg_instance,
15759                                     A._width,A._height,A._depth,A._spectrum,A._data);
15760       typedef _cimg_Ttfloat Ttfloat;
15761       const Ttfloat epsilon = 1e-4;
15762       CImg<Ttfloat> B = A.get_column(1), V(*this,false);
15763       for (int i = 1; i<(int)siz; ++i) {
15764         const Ttfloat m = A(0,i)/(B[i-1]?B[i-1]:epsilon);
15765         B[i] -= m*A(2,i-1);
15766         V[i] -= m*V[i-1];
15767       }
15768       (*this)[siz-1] = (T)(V[siz-1]/(B[siz-1]?B[siz-1]:epsilon));
15769       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));
15770       return *this;
15771     }
15772 
15773     template<typename t>
15774     CImg<_cimg_Ttfloat> get_solve_tridiagonal(const CImg<t>& A) const {
15775       return CImg<_cimg_Ttfloat>(*this,false).solve_tridiagonal(A);
15776     }
15777 
15778     //! Compute the eigenvalues and eigenvectors of a matrix.
15779     template<typename t>
15780     const CImg<T>& eigen(CImg<t>& val, CImg<t> &vec) const {
15781       if (is_empty()) { val.assign(); vec.assign(); }
15782       else {
15783         if (_width!=_height || _depth>1 || _spectrum>1)
15784           throw CImgInstanceException(_cimg_instance
15785                                       "eigen() : Instance is not a square matrix.",
15786                                       cimg_instance);
15787 
15788         if (val.size()<_width) val.assign(1,_width);
15789         if (vec.size()<_width*_width) vec.assign(_width,_width);
15790         switch (_width) {
15791         case 1 : { val[0] = (t)(*this)[0]; vec[0] = (t)1; } break;
15792         case 2 : {
15793           const double a = (*this)[0], b = (*this)[1], c = (*this)[2], d = (*this)[3], e = a + d;
15794           double f = e*e - 4*(a*d - b*c);
15795           if (f<0)
15796             cimg::warn(_cimg_instance
15797                        "CImg<%s>::eigen() : Complex eigenvalues found.",
15798                        cimg_instance);
15799 
15800           f = std::sqrt(f);
15801           const double l1 = 0.5*(e-f), l2 = 0.5*(e+f);
15802           const double theta1 = std::atan2(l2-a,b), theta2 = std::atan2(l1-a,b);
15803           val[0] = (t)l2;
15804           val[1] = (t)l1;
15805           vec(0,0) = (t)std::cos(theta1);
15806           vec(0,1) = (t)std::sin(theta1);
15807           vec(1,0) = (t)std::cos(theta2);
15808           vec(1,1) = (t)std::sin(theta2);
15809         } break;
15810         default :
15811           throw CImgInstanceException(_cimg_instance
15812                                       "eigen() : Eigenvalues computation of general matrices is limited to 2x2 matrices.",
15813                                       cimg_instance);
15814         }
15815       }
15816       return *this;
15817     }
15818 
15819     CImgList<Tfloat> get_eigen() const {
15820       CImgList<Tfloat> res(2);
15821       eigen(res[0],res[1]);
15822       return res;
15823     }
15824 
15825     //! Compute the eigenvalues and eigenvectors of a symmetric matrix.
15826     template<typename t>
15827     const CImg<T>& symmetric_eigen(CImg<t>& val, CImg<t>& vec) const {
15828       if (is_empty()) { val.assign(); vec.assign(); }
15829       else {
15830 #ifdef cimg_use_lapack
15831         char JOB = 'V', UPLO = 'U';
15832         int N = _width, LWORK = 4*N, INFO;
15833         Tfloat
15834           *const lapA = new Tfloat[N*N],
15835           *const lapW = new Tfloat[N],
15836           *const WORK = new Tfloat[LWORK];
15837         cimg_forXY(*this,k,l) lapA[k*N+l] = (Tfloat)((*this)(k,l));
15838         cimg::syev(JOB,UPLO,N,lapA,lapW,WORK,LWORK,INFO);
15839         if (INFO)
15840           cimg::warn(_cimg_instance
15841                      "symmetric_eigen() : LAPACK library function dsyev_() returned error code %d.",
15842                      cimg_instance,
15843                      INFO);
15844 
15845         val.assign(1,N);
15846         vec.assign(N,N);
15847         if (!INFO) {
15848           cimg_forY(val,i) val(i) = (T)lapW[N-1-i];
15849           cimg_forXY(vec,k,l) vec(k,l) = (T)(lapA[(N-1-k)*N+l]);
15850         } else { val.fill(0); vec.fill(0); }
15851         delete[] lapA; delete[] lapW; delete[] WORK;
15852 #else
15853         if (_width!=_height || _depth>1 || _spectrum>1)
15854           throw CImgInstanceException(_cimg_instance
15855                                       "eigen() : Instance is not a square matrix.",
15856                                       cimg_instance);
15857 
15858         val.assign(1,_width);
15859         if (vec._data) vec.assign(_width,_width);
15860         if (_width<3) {
15861           eigen(val,vec);
15862           if (_width==2) { vec[1] = -vec[2]; vec[3] = vec[0]; } // Force orthogonality for 2x2 matrices.
15863           return *this;
15864         }
15865         CImg<t> V(_width,_width);
15866         SVD(vec,val,V,false);
15867         bool is_ambiguous = false;
15868         float eig = 0;
15869         cimg_forY(val,p) {       // check for ambiguous cases.
15870           if (val[p]>eig) eig = (float)val[p];
15871           t scal = 0;
15872           cimg_forY(vec,y) scal+=vec(p,y)*V(p,y);
15873           if (cimg::abs(scal)<0.9f) is_ambiguous = true;
15874           if (scal<0) val[p] = -val[p];
15875         }
15876         if (is_ambiguous) {
15877           ++(eig*=2);
15878           SVD(vec,val,V,false,40,eig);
15879           val-=eig;
15880         }
15881         CImg<intT> permutations;  // sort eigenvalues in decreasing order
15882         CImg<t> tmp(_width);
15883         val.sort(permutations,false);
15884         cimg_forY(vec,k) {
15885           cimg_forY(permutations,y) tmp(y) = vec(permutations(y),k);
15886           std::memcpy(vec.data(0,k),tmp._data,sizeof(t)*_width);
15887         }
15888 #endif
15889       }
15890       return *this;
15891     }
15892 
15893     CImgList<Tfloat> get_symmetric_eigen() const {
15894       CImgList<Tfloat> res(2);
15895       symmetric_eigen(res[0],res[1]);
15896       return res;
15897     }
15898 
15899     //! Sort values of a vector and get corresponding permutations.
15900     template<typename t>
15901     CImg<T>& sort(CImg<t>& permutations, const bool increasing=true) {
15902       permutations.assign(_width,_height,_depth,_spectrum);
15903       if (is_empty()) return *this;
15904       cimg_foroff(permutations,off) permutations[off] = (t)off;
15905       return _quicksort(0,size()-1,permutations,increasing,true);
15906     }
15907 
15908     template<typename t>
15909     CImg<T> get_sort(CImg<t>& permutations, const bool increasing=true) const {
15910       return (+*this).sort(permutations,increasing);
15911     }
15912 
15913     //! Sort image values.
15914     CImg<T>& sort(const bool increasing=true, const char axis=0) {
15915       if (is_empty()) return *this;
15916       CImg<uintT> perm;
15917       switch (axis) {
15918       case 0 :
15919         _quicksort(0,size()-1,perm,increasing,false);
15920         break;
15921       case 'x' : {
15922         perm.assign(_width);
15923         get_crop(0,0,0,0,_width-1,0,0,0).sort(perm,increasing);
15924         CImg<T> img(*this,false);
15925         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(perm[x],y,z,c);
15926       } break;
15927       case 'y' : {
15928         perm.assign(_height);
15929         get_crop(0,0,0,0,0,_height-1,0,0).sort(perm,increasing);
15930         CImg<T> img(*this,false);
15931         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,perm[y],z,c);
15932       } break;
15933       case 'z' : {
15934         perm.assign(_depth);
15935         get_crop(0,0,0,0,0,0,_depth-1,0).sort(perm,increasing);
15936         CImg<T> img(*this,false);
15937         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,perm[z],c);
15938       } break;
15939       case 'c' : {
15940         perm.assign(_spectrum);
15941         get_crop(0,0,0,0,0,0,0,_spectrum-1).sort(perm,increasing);
15942         CImg<T> img(*this,false);
15943         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,z,perm[c]);
15944       } break;
15945       default :
15946         throw CImgArgumentException(_cimg_instance
15947                                     "sort() : Invalid specified axis '%c' "
15948                                     "(should be { x | y | z | c }).",
15949                                     cimg_instance,axis);
15950       }
15951       return *this;
15952     }
15953 
15954     CImg<T> get_sort(const bool increasing=true, const char axis=0) const {
15955       return (+*this).sort(increasing,axis);
15956     }
15957 
15958     template<typename t>
15959     CImg<T>& _quicksort(const int indm, const int indM, CImg<t>& permutations, const bool increasing, const bool is_permutations) {
15960       if (indm<indM) {
15961         const int mid = (indm + indM)/2;
15962         if (increasing) {
15963           if ((*this)[indm]>(*this)[mid]) {
15964             cimg::swap((*this)[indm],(*this)[mid]); if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
15965           }
15966           if ((*this)[mid]>(*this)[indM]) {
15967             cimg::swap((*this)[indM],(*this)[mid]); if (is_permutations) cimg::swap(permutations[indM],permutations[mid]);
15968           }
15969           if ((*this)[indm]>(*this)[mid]) {
15970             cimg::swap((*this)[indm],(*this)[mid]); if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
15971           }
15972         } else {
15973           if ((*this)[indm]<(*this)[mid]) {
15974             cimg::swap((*this)[indm],(*this)[mid]); if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
15975           }
15976           if ((*this)[mid]<(*this)[indM]) {
15977             cimg::swap((*this)[indM],(*this)[mid]); if (is_permutations) cimg::swap(permutations[indM],permutations[mid]);
15978           }
15979           if ((*this)[indm]<(*this)[mid]) {
15980             cimg::swap((*this)[indm],(*this)[mid]); if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
15981           }
15982         }
15983         if (indM - indm>=3) {
15984           const T pivot = (*this)[mid];
15985           int i = indm, j = indM;
15986           if (increasing) {
15987             do {
15988               while ((*this)[i]<pivot) ++i;
15989               while ((*this)[j]>pivot) --j;
15990               if (i<=j) {
15991                 if (is_permutations) cimg::swap(permutations[i],permutations[j]);
15992                 cimg::swap((*this)[i++],(*this)[j--]);
15993               }
15994             } while (i<=j);
15995           } else {
15996             do {
15997               while ((*this)[i]>pivot) ++i;
15998               while ((*this)[j]<pivot) --j;
15999               if (i<=j) {
16000                 if (is_permutations) cimg::swap(permutations[i],permutations[j]);
16001                 cimg::swap((*this)[i++],(*this)[j--]);
16002               }
16003             } while (i<=j);
16004           }
16005           if (indm<j) _quicksort(indm,j,permutations,increasing,is_permutations);
16006           if (i<indM) _quicksort(i,indM,permutations,increasing,is_permutations);
16007         }
16008       }
16009       return *this;
16010     }
16011 
16012     //! Compute the SVD of a general matrix.
16013     template<typename t>
16014     const CImg<T>& SVD(CImg<t>& U, CImg<t>& S, CImg<t>& V, const bool sorting=true,
16015                        const unsigned int max_iteration=40, const float lambda=0) const {
16016       if (is_empty()) { U.assign(); S.assign(); V.assign(); }
16017       else {
16018         U = *this;
16019         if (lambda!=0) {
16020           const unsigned int delta = cimg::min(U._width,U._height);
16021           for (unsigned int i = 0; i<delta; ++i) U(i,i) = (t)(U(i,i) + lambda);
16022         }
16023         if (S.size()<_width) S.assign(1,_width);
16024         if (V._width<_width || V._height<_height) V.assign(_width,_width);
16025         CImg<t> rv1(_width);
16026         t anorm = 0, c, f, g = 0, h, s, scale = 0;
16027         int l = 0, nm = 0;
16028 
16029         cimg_forX(U,i) {
16030           l = i+1; rv1[i] = scale*g; g = s = scale = 0;
16031           if (i<height()) {
16032             for (int k = i; k<height(); ++k) scale+= cimg::abs(U(i,k));
16033             if (scale) {
16034               for (int k = i; k<height(); ++k) { U(i,k)/=scale; s+= U(i,k)*U(i,k); }
16035               f = U(i,i); g = (t)((f>=0?-1:1)*std::sqrt(s)); h=f*g-s; U(i,i) = f-g;
16036               for (int j = l; j<width(); ++j) {
16037                 s = 0;
16038                 for (int k=i; k<height(); ++k) s+= U(i,k)*U(j,k);
16039                 f = s/h;
16040                 for (int k = i; k<height(); ++k) U(j,k)+= f*U(i,k);
16041               }
16042               for (int k = i; k<height(); ++k) U(i,k)*= scale;
16043             }
16044           }
16045           S[i]=scale*g;
16046 
16047           g = s = scale = 0;
16048           if (i<height() && i!=width()-1) {
16049             for (int k = l; k<width(); ++k) scale+=cimg::abs(U(k,i));
16050             if (scale) {
16051               for (int k = l; k<width(); ++k) { U(k,i)/= scale; s+= U(k,i)*U(k,i); }
16052               f = U(l,i); g = (t)((f>=0?-1:1)*std::sqrt(s)); h = f*g-s; U(l,i) = f-g;
16053               for (int k = l; k<width(); ++k) rv1[k]=U(k,i)/h;
16054               for (int j = l; j<height(); ++j) {
16055                 s = 0;
16056                 for (int k = l; k<width(); ++k) s+= U(k,j)*U(k,i);
16057                 for (int k = l; k<width(); ++k) U(k,j)+= s*rv1[k];
16058               }
16059               for (int k = l; k<width(); ++k) U(k,i)*= scale;
16060             }
16061           }
16062           anorm = (t)cimg::max((float)anorm,(float)(cimg::abs(S[i])+cimg::abs(rv1[i])));
16063         }
16064 
16065         for (int i = width()-1; i>=0; --i) {
16066           if (i<width()-1) {
16067             if (g) {
16068               for (int j = l; j<width(); ++j) V(i,j) =(U(j,i)/U(l,i))/g;
16069               for (int j = l; j<width(); ++j) {
16070                 s = 0;
16071                 for (int k = l; k<width(); ++k) s+= U(k,i)*V(j,k);
16072                 for (int k = l; k<width(); ++k) V(j,k)+= s*V(i,k);
16073               }
16074             }
16075             for (int j = l; j<width(); ++j) V(j,i) = V(i,j) = (t)0.0;
16076           }
16077           V(i,i) = (t)1.0; g = rv1[i]; l = i;
16078         }
16079 
16080         for (int i = cimg::min(width(),height())-1; i>=0; --i) {
16081           l = i+1; g = S[i];
16082           for (int j = l; j<width(); ++j) U(j,i) = 0;
16083           if (g) {
16084             g = 1/g;
16085             for (int j = l; j<width(); ++j) {
16086               s = 0; for (int k = l; k<height(); ++k) s+= U(i,k)*U(j,k);
16087               f = (s/U(i,i))*g;
16088               for (int k = i; k<height(); ++k) U(j,k)+= f*U(i,k);
16089             }
16090             for (int j = i; j<height(); ++j) U(i,j)*= g;
16091           } else for (int j = i; j<height(); ++j) U(i,j) = 0;
16092           ++U(i,i);
16093         }
16094 
16095         for (int k = width()-1; k>=0; --k) {
16096           for (unsigned int its = 0; its<max_iteration; ++its) {
16097             bool flag = true;
16098             for (l = k; l>=1; --l) {
16099               nm = l-1;
16100               if ((cimg::abs(rv1[l])+anorm)==anorm) { flag = false; break; }
16101               if ((cimg::abs(S[nm])+anorm)==anorm) break;
16102             }
16103             if (flag) {
16104               c = 0; s = 1;
16105               for (int i = l; i<=k; ++i) {
16106                 f = s*rv1[i]; rv1[i] = c*rv1[i];
16107                 if ((cimg::abs(f)+anorm)==anorm) break;
16108                 g = S[i]; h = (t)cimg::_pythagore(f,g); S[i] = h; h = 1/h; c = g*h; s = -f*h;
16109                 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; }
16110               }
16111             }
16112             const t z = S[k];
16113             if (l==k) { if (z<0) { S[k] = -z; cimg_forX(U,j) V(k,j) = -V(k,j); } break; }
16114             nm = k-1;
16115             t x = S[l], y = S[nm];
16116             g = rv1[nm]; h = rv1[k];
16117             f = ((y-z)*(y+z)+(g-h)*(g+h))/(2*h*y);
16118             g = (t)cimg::_pythagore(f,1.0);
16119             f = ((x-z)*(x+z)+h*((y/(f+ (f>=0?g:-g)))-h))/x;
16120             c = s = 1;
16121             for (int j = l; j<=nm; ++j) {
16122               const int i = j+1;
16123               g = rv1[i]; h = s*g; g = c*g;
16124               t y = S[i];
16125               t z = (t)cimg::_pythagore(f,h);
16126               rv1[j] = z; c = f/z; s = h/z;
16127               f = x*c+g*s; g = g*c-x*s; h = y*s; y*=c;
16128               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; }
16129               z = (t)cimg::_pythagore(f,h); S[j] = z;
16130               if (z) { z = 1/z; c = f*z; s = h*z; }
16131               f = c*g+s*y; x = c*y-s*g;
16132               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; }
16133             }
16134             rv1[l] = 0; rv1[k]=f; S[k]=x;
16135           }
16136         }
16137 
16138         if (sorting) {
16139           CImg<intT> permutations;
16140           CImg<t> tmp(_width);
16141           S.sort(permutations,false);
16142           cimg_forY(U,k) {
16143             cimg_forY(permutations,y) tmp(y) = U(permutations(y),k);
16144             std::memcpy(U.data(0,k),tmp._data,sizeof(t)*_width);
16145           }
16146           cimg_forY(V,k) {
16147             cimg_forY(permutations,y) tmp(y) = V(permutations(y),k);
16148             std::memcpy(V.data(0,k),tmp._data,sizeof(t)*_width);
16149           }
16150         }
16151       }
16152       return *this;
16153     }
16154 
16155     CImgList<Tfloat> get_SVD(const bool sorting=true,
16156                              const unsigned int max_iteration=40, const float lambda=0) const {
16157       CImgList<Tfloat> res(3);
16158       SVD(res[0],res[1],res[2],sorting,max_iteration,lambda);
16159       return res;
16160     }
16161 
16162     // INNER ROUTINE : Compute the LU decomposition of a permuted matrix (c.f. numerical recipies)
16163     template<typename t>
16164     CImg<T>& _LU(CImg<t>& indx, bool& d) {
16165       const int N = width();
16166       int imax = 0;
16167       CImg<Tfloat> vv(N);
16168       indx.assign(N);
16169       d = true;
16170       cimg_forX(*this,i) {
16171         Tfloat vmax = 0;
16172         cimg_forX(*this,j) {
16173           const Tfloat tmp = cimg::abs((*this)(j,i));
16174           if (tmp>vmax) vmax = tmp;
16175         }
16176         if (vmax==0) { indx.fill(0); return fill(0); }
16177         vv[i] = 1/vmax;
16178       }
16179       cimg_forX(*this,j) {
16180         for (int i = 0; i<j; ++i) {
16181           Tfloat sum=(*this)(j,i);
16182           for (int k = 0; k<i; ++k) sum-=(*this)(k,i)*(*this)(j,k);
16183           (*this)(j,i) = (T)sum;
16184         }
16185         Tfloat vmax = 0;
16186         for (int i = j; i<width(); ++i) {
16187           Tfloat sum=(*this)(j,i);
16188           for (int k = 0; k<j; ++k) sum-=(*this)(k,i)*(*this)(j,k);
16189           (*this)(j,i) = (T)sum;
16190           const Tfloat tmp = vv[i]*cimg::abs(sum);
16191           if (tmp>=vmax) { vmax=tmp; imax=i; }
16192         }
16193         if (j!=imax) {
16194           cimg_forX(*this,k) cimg::swap((*this)(k,imax),(*this)(k,j));
16195           d =!d;
16196           vv[imax] = vv[j];
16197         }
16198         indx[j] = (t)imax;
16199         if ((*this)(j,j)==0) (*this)(j,j) = (T)1e-20;
16200         if (j<N) {
16201           const Tfloat tmp = 1/(Tfloat)(*this)(j,j);
16202           for (int i=j+1; i<N; ++i) (*this)(j,i) = (T)((*this)(j,i)*tmp);
16203         }
16204       }
16205       return *this;
16206     }
16207 
16208     //! Compute minimal path in a graph, using the Dijkstra algorithm.
16209     /**
16210        \param distance An object having operator()(unsigned int i, unsigned int j) which returns distance between two nodes (i,j).
16211        \param nb_nodes Number of graph nodes.
16212        \param starting_node Indice of the starting node.
16213        \param ending_node Indice of the ending node (set to ~0U to ignore ending node).
16214        \param previous Array that gives the previous node indice in the path to the starting node (optional parameter).
16215        \return Array of distances of each node to the starting node.
16216     **/
16217     template<typename tf, typename t>
16218     static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
16219                             const unsigned int starting_node, const unsigned int ending_node,
16220                             CImg<t>& previous) {
16221       if (starting_node>=nb_nodes)
16222         throw CImgArgumentException("CImg<%s>::dijkstra() : Specified indice of starting node %u is higher than number of nodes %u.",
16223                                     pixel_type(),starting_node,nb_nodes);
16224       CImg<T> dist(1,nb_nodes,1,1,cimg::type<T>::max());
16225       dist(starting_node) = 0;
16226       previous.assign(1,nb_nodes,1,1,(t)-1);
16227       previous(starting_node) = (t)starting_node;
16228       CImg<uintT> Q(nb_nodes);
16229       cimg_forX(Q,u) Q(u) = u;
16230       cimg::swap(Q(starting_node),Q(0));
16231       unsigned int sizeQ = nb_nodes;
16232       while (sizeQ) {
16233         // Update neighbors from minimal vertex
16234         const unsigned int umin = Q(0);
16235         if (umin==ending_node) sizeQ = 0;
16236         else {
16237           const T dmin = dist(umin);
16238           const T infty = cimg::type<T>::max();
16239           for (unsigned int q = 1; q<sizeQ; ++q) {
16240             const unsigned int v = Q(q);
16241             const T d = (T)distance(v,umin);
16242             if (d<infty) {
16243               const T alt = dmin + d;
16244               if (alt<dist(v)) {
16245                 dist(v) = alt;
16246                 previous(v) = (t)umin;
16247                 const T distpos = dist(Q(q));
16248                 for (unsigned int pos = q, par = 0; pos && distpos<dist(Q(par=(pos+1)/2-1)); pos=par) cimg::swap(Q(pos),Q(par));
16249               }
16250             }
16251           }
16252           // Remove minimal vertex from queue
16253           Q(0) = Q(--sizeQ);
16254           const T distpos = dist(Q(0));
16255           for (unsigned int pos = 0, left = 0, right = 0;
16256                ((right=2*(pos+1),(left=right-1))<sizeQ && distpos>dist(Q(left))) || (right<sizeQ && distpos>dist(Q(right)));) {
16257             if (right<sizeQ) {
16258               if (dist(Q(left))<dist(Q(right))) { cimg::swap(Q(pos),Q(left)); pos = left; }
16259               else { cimg::swap(Q(pos),Q(right)); pos = right; }
16260             } else { cimg::swap(Q(pos),Q(left)); pos = left; }
16261           }
16262         }
16263       }
16264       return dist;
16265     }
16266 
16267     //! Return minimal path in a graph, using the Dijkstra algorithm.
16268     template<typename tf, typename t>
16269     static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
16270                             const unsigned int starting_node, const unsigned int ending_node=~0U) {
16271       CImg<uintT> foo;
16272       return dijkstra(distance,nb_nodes,starting_node,ending_node,foo);
16273     }
16274 
16275     //! Return minimal path in a graph, using the Dijkstra algorithm.
16276     /**
16277        image instance corresponds to the adjacency matrix of the graph.
16278        \param starting_node Indice of the starting node.
16279        \param previous Array that gives the previous node indice in the path to the starting node (optional parameter).
16280        \return Array of distances of each node to the starting node.
16281     **/
16282     template<typename t>
16283     CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node, CImg<t>& previous) {
16284       return get_dijkstra(starting_node,ending_node,previous).move_to(*this);
16285     }
16286 
16287     template<typename t>
16288     CImg<T> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node, CImg<t>& previous) const {
16289       if (_width!=_height || _depth!=1 || _spectrum!=1)
16290         throw CImgInstanceException(_cimg_instance
16291                                     "dijkstra() : Instance is not a graph adjacency matrix.",
16292                                     cimg_instance);
16293 
16294       return dijkstra(*this,_width,starting_node,ending_node,previous);
16295     }
16296 
16297     //! Return minimal path in a graph, using the Dijkstra algorithm.
16298     CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) {
16299       return get_dijkstra(starting_node,ending_node).move_to(*this);
16300     }
16301 
16302     CImg<Tfloat> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) const {
16303       CImg<uintT> foo;
16304       return get_dijkstra(starting_node,ending_node,foo);
16305     }
16306 
16307     //! Return stream line of a 2d or 3d vector field.
16308     CImg<floatT> get_streamline(const float x, const float y, const float z,
16309                                 const float L=256, const float dl=0.1f,
16310                                 const unsigned int interpolation_type=2, const bool is_backward_tracking=false,
16311                                 const bool is_oriented_only=false) const {
16312       if (_spectrum!=2 && _spectrum!=3)
16313         throw CImgInstanceException(_cimg_instance
16314                                     "streamline() : Instance is not a 2d or 3d vector field.",
16315                                     cimg_instance);
16316       if (_spectrum==2) {
16317         if (is_oriented_only) {
16318           typename CImg<T>::_functor4d_streamline2d_oriented func(*this);
16319           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);
16320         } else {
16321           typename CImg<T>::_functor4d_streamline2d_directed func(*this);
16322           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);
16323         }
16324       }
16325       if (is_oriented_only) {
16326         typename CImg<T>::_functor4d_streamline3d_oriented func(*this);
16327         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);
16328       }
16329       typename CImg<T>::_functor4d_streamline3d_directed func(*this);
16330       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);
16331     }
16332 
16333     //! Return stream line of a 3d vector field.
16334     /**
16335        \param interpolation_type Type of interpolation (can be 0=nearest int, 1=linear, 2=2nd-order RK, 3=4th-order RK.
16336 
16337      **/
16338     template<typename tfunc>
16339     static CImg<floatT> streamline(const tfunc& func,
16340                                    const float x, const float y, const float z,
16341                                    const float L=256, const float dl=0.1f,
16342                                    const unsigned int interpolation_type=2, const bool is_backward_tracking=false,
16343                                    const bool is_oriented_only=false,
16344                                    const float x0=0, const float y0=0, const float z0=0,
16345                                    const float x1=0, const float y1=0, const float z1=0) {
16346       if (dl<=0)
16347         throw CImgArgumentException("CImg<%s>::streamline() : Invalid specified integration length %g "
16348                                     "(should be >0).",
16349                                     pixel_type(),
16350                                     dl);
16351 
16352       const bool is_bounded = (x0!=x1 || y0!=y1 || z0!=z1);
16353       if (L<=0 || (is_bounded && (x<x0 || x>x1 || y<y0 || y>y1 || z<z0 || z>z1))) return CImg<floatT>();
16354       const unsigned int size_L = (unsigned int)cimg::round(L/dl+1);
16355       CImg<floatT> coordinates(size_L,3);
16356       const float dl2 = dl/2;
16357       float
16358         *ptr_x = coordinates.data(0,0),
16359         *ptr_y = coordinates.data(0,1),
16360         *ptr_z = coordinates.data(0,2),
16361         pu = (float)(dl*func(x,y,z,0)),
16362         pv = (float)(dl*func(x,y,z,1)),
16363         pw = (float)(dl*func(x,y,z,2)),
16364         X = x, Y = y, Z = z;
16365 
16366       switch (interpolation_type) {
16367       case 0 : { // Nearest integer interpolation.
16368         cimg_forX(coordinates,l) {
16369           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
16370           const int
16371             xi = (int)(X>0?X+0.5f:X-0.5f),
16372             yi = (int)(Y>0?Y+0.5f:Y-0.5f),
16373             zi = (int)(Z>0?Z+0.5f:Z-0.5f);
16374           float
16375             u = (float)(dl*func((float)xi,(float)yi,(float)zi,0)),
16376             v = (float)(dl*func((float)xi,(float)yi,(float)zi,1)),
16377             w = (float)(dl*func((float)xi,(float)yi,(float)zi,2));
16378           if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
16379           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
16380           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
16381         }
16382       } break;
16383       case 1 : { // First-order interpolation.
16384         cimg_forX(coordinates,l) {
16385           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
16386           float
16387             u = (float)(dl*func(X,Y,Z,0)),
16388             v = (float)(dl*func(X,Y,Z,1)),
16389             w = (float)(dl*func(X,Y,Z,2));
16390           if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
16391           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
16392           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
16393         }
16394       } break;
16395       case 2 : { // Second order interpolation.
16396         cimg_forX(coordinates,l) {
16397           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
16398           float
16399             u0 = (float)(dl2*func(X,Y,Z,0)),
16400             v0 = (float)(dl2*func(X,Y,Z,1)),
16401             w0 = (float)(dl2*func(X,Y,Z,2));
16402           if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
16403           float
16404             u = (float)(dl*func(X+u0,Y+v0,Z+w0,0)),
16405             v = (float)(dl*func(X+u0,Y+v0,Z+w0,1)),
16406             w = (float)(dl*func(X+u0,Y+v0,Z+w0,2));
16407           if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
16408           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
16409           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
16410         }
16411       } break;
16412       default : { // Fourth order interpolation.
16413         cimg_forX(coordinates,x) {
16414           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
16415           float
16416             u0 = (float)(dl2*func(X,Y,Z,0)),
16417             v0 = (float)(dl2*func(X,Y,Z,1)),
16418             w0 = (float)(dl2*func(X,Y,Z,2));
16419           if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
16420           float
16421             u1 = (float)(dl2*func(X+u0,Y+v0,Z+w0,0)),
16422             v1 = (float)(dl2*func(X+u0,Y+v0,Z+w0,1)),
16423             w1 = (float)(dl2*func(X+u0,Y+v0,Z+w0,2));
16424           if (is_oriented_only && u1*pu + v1*pv + w1*pw<0) { u1 = -u1; v1 = -v1; w1 = -w1; }
16425           float
16426             u2 = (float)(dl2*func(X+u1,Y+v1,Z+w1,0)),
16427             v2 = (float)(dl2*func(X+u1,Y+v1,Z+w1,1)),
16428             w2 = (float)(dl2*func(X+u1,Y+v1,Z+w1,2));
16429           if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u2 = -u2; v2 = -v2; w2 = -w2; }
16430           float
16431             u3 = (float)(dl2*func(X+u2,Y+v2,Z+w2,0)),
16432             v3 = (float)(dl2*func(X+u2,Y+v2,Z+w2,1)),
16433             w3 = (float)(dl2*func(X+u2,Y+v2,Z+w2,2));
16434           if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u3 = -u3; v3 = -v3; w3 = -w3; }
16435           const float
16436             u = (u0 + u3)/3 + (u1 + u2)/1.5f,
16437             v = (v0 + v3)/3 + (v1 + v2)/1.5f,
16438             w = (w0 + w3)/3 + (w1 + w2)/1.5f;
16439           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
16440           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
16441         }
16442       }
16443       }
16444       if (ptr_x!=coordinates.data(0,1)) coordinates.resize((int)(ptr_x-coordinates.data()),3,1,1,0);
16445       return coordinates;
16446     }
16447 
16448     //! Return stream line of a vector field.
16449     static CImg<floatT> streamline(const char *const expression,
16450                                    const float x, const float y, const float z,
16451                                    const float L=256, const float dl=0.1f,
16452                                    const unsigned int interpolation_type=2, const bool is_backward_tracking=true,
16453                                    const bool is_oriented_only=false,
16454                                    const float x0=0, const float y0=0, const float z0=0,
16455                                    const float x1=0, const float y1=0, const float z1=0) {
16456       _functor4d_streamline_expr func(expression);
16457       return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,is_oriented_only,x0,y0,z0,x1,y1,z1);
16458     }
16459 
16460     struct _functor4d_streamline2d_directed {
16461       const CImg<T>& ref;
16462       _functor4d_streamline2d_directed(const CImg<T>& pref):ref(pref) {}
16463       float operator()(const float x, const float y, const float z, const unsigned int c) const {
16464         return c<2?(float)ref._linear_atXY(x,y,(int)z,c):0;
16465       }
16466     };
16467 
16468     struct _functor4d_streamline3d_directed {
16469       const CImg<T>& ref;
16470       _functor4d_streamline3d_directed(const CImg<T>& pref):ref(pref) {}
16471       float operator()(const float x, const float y, const float z, const unsigned int c) const {
16472         return (float)ref._linear_atXYZ(x,y,z,c);
16473       }
16474     };
16475 
16476     struct _functor4d_streamline2d_oriented {
16477       const CImg<T>& ref;
16478       CImg<floatT> *pI;
16479       _functor4d_streamline2d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,1,2); }
16480       ~_functor4d_streamline2d_oriented() { delete pI; }
16481       float operator()(const float x, const float y, const float z, const unsigned int c) const {
16482 #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); }
16483         int
16484           xi = (int)x - (x>=0?0:1), nxi = xi + 1,
16485           yi = (int)y - (y>=0?0:1), nyi = yi + 1,
16486           zi = (int)z;
16487         const float
16488           dx = x - xi,
16489           dy = y - yi;
16490         if (c==0) {
16491           CImg<floatT>& I = *pI;
16492           if (xi<0) xi = 0; if (nxi<0) nxi = 0;
16493           if (xi>=ref.width()) xi = ref.width()-1; if (nxi>=ref.width()) nxi = ref.width()-1;
16494           if (yi<0) yi = 0; if (nyi<0) nyi = 0;
16495           if (yi>=ref.height()) yi = ref.height()-1; if (nyi>=ref.height()) nyi = ref.height()-1;
16496           I(0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,1) = (float)ref(xi,yi,zi,1);
16497           I(1,0,0) = (float)ref(nxi,yi,zi,0); I(1,0,1) = (float)ref(nxi,yi,zi,1);
16498           I(1,1,0) = (float)ref(nxi,nyi,zi,0); I(1,1,1) = (float)ref(nxi,nyi,zi,1);
16499           I(0,1,0) = (float)ref(xi,nyi,zi,0); I(0,1,1) = (float)ref(xi,nyi,zi,1);
16500           _cimg_vecalign2d(1,0); _cimg_vecalign2d(1,1); _cimg_vecalign2d(0,1);
16501         }
16502         return c<2?(float)pI->_linear_atXY(dx,dy,0,c):0;
16503       }
16504     };
16505 
16506     struct _functor4d_streamline3d_oriented {
16507       const CImg<T>& ref;
16508       CImg<floatT> *pI;
16509       _functor4d_streamline3d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,2,3); }
16510       ~_functor4d_streamline3d_oriented() { delete pI; }
16511       float operator()(const float x, const float y, const float z, const unsigned int c) const {
16512 #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) { \
16513   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); }
16514         int
16515           xi = (int)x - (x>=0?0:1), nxi = xi + 1,
16516           yi = (int)y - (y>=0?0:1), nyi = yi + 1,
16517           zi = (int)z - (z>=0?0:1), nzi = zi + 1;
16518         const float
16519           dx = x - xi,
16520           dy = y - yi,
16521           dz = z - zi;
16522         if (c==0) {
16523           CImg<floatT>& I = *pI;
16524           if (xi<0) xi = 0; if (nxi<0) nxi = 0;
16525           if (xi>=ref.width()) xi = ref.width()-1; if (nxi>=ref.width()) nxi = ref.width()-1;
16526           if (yi<0) yi = 0; if (nyi<0) nyi = 0;
16527           if (yi>=ref.height()) yi = ref.height()-1; if (nyi>=ref.height()) nyi = ref.height()-1;
16528           if (zi<0) zi = 0; if (nzi<0) nzi = 0;
16529           if (zi>=ref.depth()) zi = ref.depth()-1; if (nzi>=ref.depth()) nzi = ref.depth()-1;
16530           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);
16531           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);
16532           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);
16533           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);
16534           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);
16535           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);
16536           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);
16537           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);
16538           _cimg_vecalign3d(1,0,0); _cimg_vecalign3d(1,1,0); _cimg_vecalign3d(0,1,0);
16539           _cimg_vecalign3d(0,0,1); _cimg_vecalign3d(1,0,1); _cimg_vecalign3d(1,1,1); _cimg_vecalign3d(0,1,1);
16540         }
16541         return (float)pI->_linear_atXYZ(dx,dy,dz,c);
16542       }
16543     };
16544 
16545     struct _functor4d_streamline_expr {
16546       _cimg_math_parser *mp;
16547       ~_functor4d_streamline_expr() { delete mp; }
16548       _functor4d_streamline_expr(const char *const expr):mp(0) { mp = new _cimg_math_parser(CImg<T>::empty(),expr,"streamline"); }
16549       float operator()(const float x, const float y, const float z, const unsigned int c) const {
16550         return (float)mp->eval(x,y,z,c);
16551       }
16552     };
16553 
16554     //! Return an image containing the specified string.
16555     static CImg<T> string(const char *const str, const bool include_last_zero=true) {
16556       if (!str) return CImg<T>();
16557       return CImg<T>(str,std::strlen(str)+(include_last_zero?1:0));
16558     }
16559 
16560     //! Return a vector with specified coefficients.
16561     static CImg<T> vector(const T& a0) {
16562       _cimg_static CImg<T> r(1,1);
16563       r[0] = a0;
16564       return r;
16565     }
16566 
16567     //! Return a vector with specified coefficients.
16568     static CImg<T> vector(const T& a0, const T& a1) {
16569       _cimg_static CImg<T> r(1,2); T *ptr = r._data;
16570       *(ptr++) = a0; *(ptr++) = a1;
16571       return r;
16572     }
16573 
16574     //! Return a vector with specified coefficients.
16575     static CImg<T> vector(const T& a0, const T& a1, const T& a2) {
16576       _cimg_static CImg<T> r(1,3); T *ptr = r._data;
16577       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2;
16578       return r;
16579     }
16580 
16581     //! Return a vector with specified coefficients.
16582     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3) {
16583       _cimg_static CImg<T> r(1,4); T *ptr = r._data;
16584       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16585       return r;
16586     }
16587 
16588     //! Return a vector with specified coefficients.
16589     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
16590       _cimg_static CImg<T> r(1,5); T *ptr = r._data;
16591       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4;
16592       return r;
16593     }
16594 
16595     //! Return a vector with specified coefficients.
16596     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
16597       _cimg_static CImg<T> r(1,6); T *ptr = r._data;
16598       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5;
16599       return r;
16600     }
16601 
16602     //! Return a vector with specified coefficients.
16603     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16604                           const T& a4, const T& a5, const T& a6) {
16605       _cimg_static CImg<T> r(1,7); T *ptr = r._data;
16606       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16607       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6;
16608       return r;
16609     }
16610 
16611     //! Return a vector with specified coefficients.
16612     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16613                           const T& a4, const T& a5, const T& a6, const T& a7) {
16614       _cimg_static CImg<T> r(1,8); T *ptr = r._data;
16615       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16616       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16617       return r;
16618     }
16619 
16620     //! Return a vector with specified coefficients.
16621     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16622                           const T& a4, const T& a5, const T& a6, const T& a7,
16623                           const T& a8) {
16624       _cimg_static CImg<T> r(1,9); T *ptr = r._data;
16625       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16626       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16627       *(ptr++) = a8;
16628       return r;
16629     }
16630 
16631     //! Return a vector with specified coefficients.
16632     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16633                           const T& a4, const T& a5, const T& a6, const T& a7,
16634                           const T& a8, const T& a9) {
16635       _cimg_static CImg<T> r(1,10); T *ptr = r._data;
16636       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16637       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16638       *(ptr++) = a8; *(ptr++) = a9;
16639       return r;
16640     }
16641 
16642     //! Return a vector with specified coefficients.
16643     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16644                           const T& a4, const T& a5, const T& a6, const T& a7,
16645                           const T& a8, const T& a9, const T& a10) {
16646       _cimg_static CImg<T> r(1,11); T *ptr = r._data;
16647       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16648       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16649       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10;
16650       return r;
16651     }
16652 
16653     //! Return a vector with specified coefficients.
16654     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16655                           const T& a4, const T& a5, const T& a6, const T& a7,
16656                           const T& a8, const T& a9, const T& a10, const T& a11) {
16657       _cimg_static CImg<T> r(1,12); T *ptr = r._data;
16658       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16659       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16660       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
16661       return r;
16662     }
16663 
16664     //! Return a vector with specified coefficients.
16665     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16666                           const T& a4, const T& a5, const T& a6, const T& a7,
16667                           const T& a8, const T& a9, const T& a10, const T& a11,
16668                           const T& a12) {
16669       _cimg_static CImg<T> r(1,13); T *ptr = r._data;
16670       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16671       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16672       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
16673       *(ptr++) = a12;
16674       return r;
16675     }
16676 
16677     //! Return a vector with specified coefficients.
16678     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16679                           const T& a4, const T& a5, const T& a6, const T& a7,
16680                           const T& a8, const T& a9, const T& a10, const T& a11,
16681                           const T& a12, const T& a13) {
16682       _cimg_static CImg<T> r(1,14); T *ptr = r._data;
16683       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16684       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16685       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
16686       *(ptr++) = a12; *(ptr++) = a13;
16687       return r;
16688     }
16689 
16690     //! Return a vector with specified coefficients.
16691     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16692                           const T& a4, const T& a5, const T& a6, const T& a7,
16693                           const T& a8, const T& a9, const T& a10, const T& a11,
16694                           const T& a12, const T& a13, const T& a14) {
16695       _cimg_static CImg<T> r(1,15); T *ptr = r._data;
16696       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16697       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16698       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
16699       *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14;
16700       return r;
16701     }
16702 
16703     //! Return a vector with specified coefficients.
16704     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16705                           const T& a4, const T& a5, const T& a6, const T& a7,
16706                           const T& a8, const T& a9, const T& a10, const T& a11,
16707                           const T& a12, const T& a13, const T& a14, const T& a15) {
16708       _cimg_static CImg<T> r(1,16); T *ptr = r._data;
16709       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16710       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16711       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
16712       *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15;
16713       return r;
16714     }
16715 
16716     //! Return a 1x1 square matrix with specified coefficients.
16717     static CImg<T> matrix(const T& a0) {
16718       return vector(a0);
16719     }
16720 
16721     //! Return a 2x2 square matrix with specified coefficients.
16722     static CImg<T> matrix(const T& a0, const T& a1,
16723                           const T& a2, const T& a3) {
16724       _cimg_static CImg<T> r(2,2); T *ptr = r._data;
16725       *(ptr++) = a0; *(ptr++) = a1;
16726       *(ptr++) = a2; *(ptr++) = a3;
16727       return r;
16728     }
16729 
16730     //! Return a 3x3 square matrix with specified coefficients.
16731     static CImg<T> matrix(const T& a0, const T& a1, const T& a2,
16732                           const T& a3, const T& a4, const T& a5,
16733                           const T& a6, const T& a7, const T& a8) {
16734       _cimg_static CImg<T> r(3,3); T *ptr = r._data;
16735       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2;
16736       *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5;
16737       *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8;
16738       return r;
16739     }
16740 
16741     //! Return a 4x4 square matrix with specified coefficients.
16742     static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3,
16743                           const T& a4, const T& a5, const T& a6, const T& a7,
16744                           const T& a8, const T& a9, const T& a10, const T& a11,
16745                           const T& a12, const T& a13, const T& a14, const T& a15) {
16746       _cimg_static CImg<T> r(4,4); T *ptr = r._data;
16747       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16748       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16749       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
16750       *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15;
16751       return r;
16752     }
16753 
16754     //! Return a 5x5 square matrix with specified coefficients.
16755     static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4,
16756                           const T& a5, const T& a6, const T& a7, const T& a8, const T& a9,
16757                           const T& a10, const T& a11, const T& a12, const T& a13, const T& a14,
16758                           const T& a15, const T& a16, const T& a17, const T& a18, const T& a19,
16759                           const T& a20, const T& a21, const T& a22, const T& a23, const T& a24) {
16760       _cimg_static CImg<T> r(5,5); T *ptr = r._data;
16761       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4;
16762       *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9;
16763       *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14;
16764       *(ptr++) = a15; *(ptr++) = a16; *(ptr++) = a17; *(ptr++) = a18; *(ptr++) = a19;
16765       *(ptr++) = a20; *(ptr++) = a21; *(ptr++) = a22; *(ptr++) = a23; *(ptr++) = a24;
16766       return r;
16767     }
16768 
16769     //! Return a 1x1 symmetric matrix with specified coefficients.
16770     static CImg<T> tensor(const T& a1) {
16771       return matrix(a1);
16772     }
16773 
16774     //! Return a 2x2 symmetric matrix tensor with specified coefficients.
16775     static CImg<T> tensor(const T& a1, const T& a2, const T& a3) {
16776       return matrix(a1,a2,a2,a3);
16777     }
16778 
16779     //! Return a 3x3 symmetric matrix with specified coefficients.
16780     static CImg<T> tensor(const T& a1, const T& a2, const T& a3, const T& a4, const T& a5, const T& a6) {
16781       return matrix(a1,a2,a3,a2,a4,a5,a3,a5,a6);
16782     }
16783 
16784     //! Return a 1x1 diagonal matrix with specified coefficients.
16785     static CImg<T> diagonal(const T& a0) {
16786       return matrix(a0);
16787     }
16788 
16789     //! Return a 2x2 diagonal matrix with specified coefficients.
16790     static CImg<T> diagonal(const T& a0, const T& a1) {
16791       return matrix(a0,0,0,a1);
16792     }
16793 
16794     //! Return a 3x3 diagonal matrix with specified coefficients.
16795     static CImg<T> diagonal(const T& a0, const T& a1, const T& a2) {
16796       return matrix(a0,0,0,0,a1,0,0,0,a2);
16797     }
16798 
16799     //! Return a 4x4 diagonal matrix with specified coefficients.
16800     static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3) {
16801       return matrix(a0,0,0,0,0,a1,0,0,0,0,a2,0,0,0,0,a3);
16802     }
16803 
16804     //! Return a 5x5 diagonal matrix with specified coefficients.
16805     static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
16806       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);
16807     }
16808 
16809     //! Return a NxN identity matrix.
16810     static CImg<T> identity_matrix(const unsigned int N) {
16811       CImg<T> res(N,N,1,1,0);
16812       cimg_forX(res,x) res(x,x) = 1;
16813       return res;
16814     }
16815 
16816     //! Return a N-numbered sequence vector from \p a0 to \p a1.
16817     static CImg<T> sequence(const unsigned int N, const T a0, const T a1) {
16818       if (N) return CImg<T>(1,N).sequence(a0,a1);
16819       return CImg<T>();
16820     }
16821 
16822     //! Return a 3x3 rotation matrix along the (x,y,z)-axis with an angle w.
16823     static CImg<T> rotation_matrix(const float x, const float y, const float z, const float w, const bool quaternion_data=false) {
16824       float X,Y,Z,W;
16825       if (!quaternion_data) {
16826         const float norm = (float)std::sqrt(x*x + y*y + z*z),
16827           nx = norm>0?x/norm:0,
16828           ny = norm>0?y/norm:0,
16829           nz = norm>0?z/norm:1,
16830           nw = norm>0?w:0,
16831           sina = (float)std::sin(nw/2),
16832           cosa = (float)std::cos(nw/2);
16833         X = nx*sina;
16834         Y = ny*sina;
16835         Z = nz*sina;
16836         W = cosa;
16837       } else {
16838         const float norm = (float)std::sqrt(x*x + y*y + z*z + w*w);
16839         if (norm>0) { X = x/norm; Y = y/norm; Z = z/norm; W = w/norm; }
16840         else { X = Y = Z = 0; W = 1; }
16841       }
16842       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;
16843       return CImg<T>::matrix((T)(1-2*(yy+zz)), (T)(2*(xy+zw)),   (T)(2*(xz-yw)),
16844                              (T)(2*(xy-zw)),   (T)(1-2*(xx+zz)), (T)(2*(yz+xw)),
16845                              (T)(2*(xz+yw)),   (T)(2*(yz-xw)),   (T)(1-2*(xx+yy)));
16846     }
16847 
16848     //@}
16849     //-----------------------------------
16850     //
16851     //! \name Value Manipulation
16852     //@{
16853     //-----------------------------------
16854 
16855     //! Fill an image by a value \p val.
16856     /**
16857        \param val = fill value
16858        \note All pixel values of the image instance will be initialized by \p val.
16859     **/
16860     CImg<T>& fill(const T val) {
16861       if (is_empty()) return *this;
16862       if (val && sizeof(T)!=1) cimg_for(*this,ptrd,T) *ptrd = val;
16863       else std::memset(_data,(int)val,size()*sizeof(T));
16864       return *this;
16865     }
16866 
16867     CImg<T> get_fill(const T val) const {
16868       return CImg<T>(_width,_height,_depth,_spectrum).fill(val);
16869     }
16870 
16871     //! Fill sequentially all pixel values with values \a val0 and \a val1 respectively.
16872     CImg<T>& fill(const T val0, const T val1) {
16873       if (is_empty()) return *this;
16874       T *ptrd, *ptre = end()-1;
16875       for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; }
16876       if (ptrd!=ptre+1) *(ptrd++) = val0;
16877       return *this;
16878     }
16879 
16880     CImg<T> get_fill(const T val0, const T val1) const {
16881       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1);
16882     }
16883 
16884     //! Fill sequentially all pixel values with values \a val0 and \a val1 and \a val2.
16885     CImg<T>& fill(const T val0, const T val1, const T val2) {
16886       if (is_empty()) return *this;
16887       T *ptrd, *ptre = end()-2;
16888       for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; }
16889       ptre+=2;
16890       switch (ptre - ptrd) {
16891       case 2 : *(--ptre) = val1;
16892       case 1 : *(--ptre) = val0;
16893       }
16894       return *this;
16895     }
16896 
16897     CImg<T> get_fill(const T val0, const T val1, const T val2) const {
16898       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2);
16899     }
16900 
16901     //! Fill sequentially all pixel values with values \a val0 and \a val1 and \a val2 and \a val3.
16902     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3) {
16903       if (is_empty()) return *this;
16904       T *ptrd, *ptre = end()-3;
16905       for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; }
16906       ptre+=3;
16907       switch (ptre - ptrd) {
16908       case 3 : *(--ptre) = val2;
16909       case 2 : *(--ptre) = val1;
16910       case 1 : *(--ptre) = val0;
16911       }
16912       return *this;
16913     }
16914 
16915     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3) const {
16916       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3);
16917     }
16918 
16919     //! Fill sequentially all pixel values with values \a val0 and \a val1 and \a val2 and \a val3 and \a val4.
16920     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4) {
16921       if (is_empty()) return *this;
16922       T *ptrd, *ptre = end()-4;
16923       for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; }
16924       ptre+=4;
16925       switch (ptre - ptrd) {
16926       case 4 : *(--ptre) = val3;
16927       case 3 : *(--ptre) = val2;
16928       case 2 : *(--ptre) = val1;
16929       case 1 : *(--ptre) = val0;
16930       }
16931       return *this;
16932     }
16933 
16934     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4) const {
16935       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4);
16936     }
16937 
16938     //! Fill sequentially all pixel values with values \a val0 and \a val1 and \a val2 and \a val3 and \a val4 and \a val5.
16939     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5) {
16940       if (is_empty()) return *this;
16941       T *ptrd, *ptre = end()-5;
16942       for (ptrd = _data; ptrd<ptre; ) {
16943         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
16944       }
16945       ptre+=5;
16946       switch (ptre - ptrd) {
16947       case 5 : *(--ptre) = val4;
16948       case 4 : *(--ptre) = val3;
16949       case 3 : *(--ptre) = val2;
16950       case 2 : *(--ptre) = val1;
16951       case 1 : *(--ptre) = val0;
16952       }
16953       return *this;
16954     }
16955 
16956     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5) const {
16957       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5);
16958     }
16959 
16960     //! Fill sequentially pixel values.
16961     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6) {
16962       if (is_empty()) return *this;
16963       T *ptrd, *ptre = end()-6;
16964       for (ptrd = _data; ptrd<ptre; ) {
16965         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5; *(ptrd++) = val6;
16966       }
16967       ptre+=6;
16968       switch (ptre - ptrd) {
16969       case 6 : *(--ptre) = val5;
16970       case 5 : *(--ptre) = val4;
16971       case 4 : *(--ptre) = val3;
16972       case 3 : *(--ptre) = val2;
16973       case 2 : *(--ptre) = val1;
16974       case 1 : *(--ptre) = val0;
16975       }
16976       return *this;
16977     }
16978 
16979     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 {
16980       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6);
16981     }
16982 
16983     //! Fill sequentially pixel values.
16984     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
16985                   const T val7) {
16986       if (is_empty()) return *this;
16987       T *ptrd, *ptre = end()-7;
16988       for (ptrd = _data; ptrd<ptre; ) {
16989         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3;
16990         *(ptrd++) = val4; *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7;
16991       }
16992       ptre+=7;
16993       switch (ptre - ptrd) {
16994       case 7 : *(--ptre) = val6;
16995       case 6 : *(--ptre) = val5;
16996       case 5 : *(--ptre) = val4;
16997       case 4 : *(--ptre) = val3;
16998       case 3 : *(--ptre) = val2;
16999       case 2 : *(--ptre) = val1;
17000       case 1 : *(--ptre) = val0;
17001       }
17002       return *this;
17003     }
17004 
17005     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,
17006                      const T val7) const {
17007       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7);
17008     }
17009 
17010     //! Fill sequentially pixel values.
17011     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17012                   const T val7, const T val8) {
17013       if (is_empty()) return *this;
17014       T *ptrd, *ptre = end()-8;
17015       for (ptrd = _data; ptrd<ptre; ) {
17016         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2;
17017         *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
17018         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8;
17019       }
17020       ptre+=8;
17021       switch (ptre - ptrd) {
17022       case 8 : *(--ptre) = val7;
17023       case 7 : *(--ptre) = val6;
17024       case 6 : *(--ptre) = val5;
17025       case 5 : *(--ptre) = val4;
17026       case 4 : *(--ptre) = val3;
17027       case 3 : *(--ptre) = val2;
17028       case 2 : *(--ptre) = val1;
17029       case 1 : *(--ptre) = val0;
17030       }
17031       return *this;
17032     }
17033 
17034     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,
17035                      const T val7, const T val8) const {
17036       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8);
17037     }
17038 
17039     //! Fill sequentially pixel values.
17040     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17041                   const T val7, const T val8, const T val9) {
17042       if (is_empty()) return *this;
17043       T *ptrd, *ptre = end()-9;
17044       for (ptrd = _data; ptrd<ptre; ) {
17045         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
17046         *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9;
17047       }
17048       ptre+=9;
17049       switch (ptre - ptrd) {
17050       case 9 : *(--ptre) = val8;
17051       case 8 : *(--ptre) = val7;
17052       case 7 : *(--ptre) = val6;
17053       case 6 : *(--ptre) = val5;
17054       case 5 : *(--ptre) = val4;
17055       case 4 : *(--ptre) = val3;
17056       case 3 : *(--ptre) = val2;
17057       case 2 : *(--ptre) = val1;
17058       case 1 : *(--ptre) = val0;
17059       }
17060       return *this;
17061     }
17062 
17063     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,
17064                      const T val7, const T val8, const T val9) const {
17065       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9);
17066     }
17067 
17068     //! Fill sequentially pixel values.
17069     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17070                   const T val7, const T val8, const T val9, const T val10) {
17071       if (is_empty()) return *this;
17072       T *ptrd, *ptre = end()-10;
17073       for (ptrd = _data; ptrd<ptre; ) {
17074         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
17075         *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9;
17076         *(ptrd++) = val10;
17077       }
17078       ptre+=10;
17079       switch (ptre - ptrd) {
17080       case 10 : *(--ptre) = val9;
17081       case 9 : *(--ptre) = val8;
17082       case 8 : *(--ptre) = val7;
17083       case 7 : *(--ptre) = val6;
17084       case 6 : *(--ptre) = val5;
17085       case 5 : *(--ptre) = val4;
17086       case 4 : *(--ptre) = val3;
17087       case 3 : *(--ptre) = val2;
17088       case 2 : *(--ptre) = val1;
17089       case 1 : *(--ptre) = val0;
17090       }
17091       return *this;
17092     }
17093 
17094     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,
17095                      const T val7, const T val8, const T val9, const T val10) const {
17096       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10);
17097     }
17098 
17099     //! Fill sequentially pixel values.
17100     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17101                   const T val7, const T val8, const T val9, const T val10, const T val11) {
17102       if (is_empty()) return *this;
17103       T *ptrd, *ptre = end()-11;
17104       for (ptrd = _data; ptrd<ptre; ) {
17105         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
17106         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
17107       }
17108       ptre+=11;
17109       switch (ptre - ptrd) {
17110       case 11 : *(--ptre) = val10;
17111       case 10 : *(--ptre) = val9;
17112       case 9 : *(--ptre) = val8;
17113       case 8 : *(--ptre) = val7;
17114       case 7 : *(--ptre) = val6;
17115       case 6 : *(--ptre) = val5;
17116       case 5 : *(--ptre) = val4;
17117       case 4 : *(--ptre) = val3;
17118       case 3 : *(--ptre) = val2;
17119       case 2 : *(--ptre) = val1;
17120       case 1 : *(--ptre) = val0;
17121       }
17122       return *this;
17123     }
17124 
17125     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,
17126                      const T val7, const T val8, const T val9, const T val10, const T val11) const {
17127       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11);
17128     }
17129 
17130     //! Fill sequentially pixel values.
17131     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17132                   const T val7, const T val8, const T val9, const T val10, const T val11, const T val12) {
17133       if (is_empty()) return *this;
17134       T *ptrd, *ptre = end()-12;
17135       for (ptrd = _data; ptrd<ptre; ) {
17136         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
17137         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
17138         *(ptrd++) = val12;
17139       }
17140       ptre+=12;
17141       switch (ptre - ptrd) {
17142       case 12 : *(--ptre) = val11;
17143       case 11 : *(--ptre) = val10;
17144       case 10 : *(--ptre) = val9;
17145       case 9 : *(--ptre) = val8;
17146       case 8 : *(--ptre) = val7;
17147       case 7 : *(--ptre) = val6;
17148       case 6 : *(--ptre) = val5;
17149       case 5 : *(--ptre) = val4;
17150       case 4 : *(--ptre) = val3;
17151       case 3 : *(--ptre) = val2;
17152       case 2 : *(--ptre) = val1;
17153       case 1 : *(--ptre) = val0;
17154       }
17155       return *this;
17156     }
17157 
17158     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,
17159                      const T val7, const T val8, const T val9, const T val10, const T val11, const T val12) const {
17160       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12);
17161     }
17162 
17163     //! Fill sequentially pixel values.
17164     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17165                   const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
17166                   const T val13) {
17167       if (is_empty()) return *this;
17168       T *ptrd, *ptre = end()-13;
17169       for (ptrd = _data; ptrd<ptre; ) {
17170         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
17171         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
17172         *(ptrd++) = val12; *(ptrd++) = val13;
17173       }
17174       ptre+=13;
17175       switch (ptre - ptrd) {
17176       case 13 : *(--ptre) = val12;
17177       case 12 : *(--ptre) = val11;
17178       case 11 : *(--ptre) = val10;
17179       case 10 : *(--ptre) = val9;
17180       case 9 : *(--ptre) = val8;
17181       case 8 : *(--ptre) = val7;
17182       case 7 : *(--ptre) = val6;
17183       case 6 : *(--ptre) = val5;
17184       case 5 : *(--ptre) = val4;
17185       case 4 : *(--ptre) = val3;
17186       case 3 : *(--ptre) = val2;
17187       case 2 : *(--ptre) = val1;
17188       case 1 : *(--ptre) = val0;
17189       }
17190       return *this;
17191     }
17192 
17193     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,
17194                      const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
17195                      const T val13) const {
17196       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,
17197                                                   val13);
17198     }
17199 
17200     //! Fill sequentially pixel values.
17201     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17202                   const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
17203                   const T val13, const T val14) {
17204       if (is_empty()) return *this;
17205       T *ptrd, *ptre = end()-14;
17206       for (ptrd = _data; ptrd<ptre; ) {
17207         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
17208         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
17209         *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14;
17210       }
17211       ptre+=14;
17212       switch (ptre - ptrd) {
17213       case 14 : *(--ptre) = val13;
17214       case 13 : *(--ptre) = val12;
17215       case 12 : *(--ptre) = val11;
17216       case 11 : *(--ptre) = val10;
17217       case 10 : *(--ptre) = val9;
17218       case 9 : *(--ptre) = val8;
17219       case 8 : *(--ptre) = val7;
17220       case 7 : *(--ptre) = val6;
17221       case 6 : *(--ptre) = val5;
17222       case 5 : *(--ptre) = val4;
17223       case 4 : *(--ptre) = val3;
17224       case 3 : *(--ptre) = val2;
17225       case 2 : *(--ptre) = val1;
17226       case 1 : *(--ptre) = val0;
17227       }
17228       return *this;
17229     }
17230 
17231     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,
17232                      const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
17233                      const T val13, const T val14) const {
17234       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,
17235                                                   val13,val14);
17236     }
17237 
17238     //! Fill sequentially pixel values.
17239     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17240                   const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
17241                   const T val13, const T val14, const T val15) {
17242       if (is_empty()) return *this;
17243       T *ptrd, *ptre = end()-15;
17244       for (ptrd = _data; ptrd<ptre; ) {
17245         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
17246         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
17247         *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14; *(ptrd++) = val15;
17248       }
17249       ptre+=15;
17250       switch (ptre - ptrd) {
17251       case 15 : *(--ptre) = val14;
17252       case 14 : *(--ptre) = val13;
17253       case 13 : *(--ptre) = val12;
17254       case 12 : *(--ptre) = val11;
17255       case 11 : *(--ptre) = val10;
17256       case 10 : *(--ptre) = val9;
17257       case 9 : *(--ptre) = val8;
17258       case 8 : *(--ptre) = val7;
17259       case 7 : *(--ptre) = val6;
17260       case 6 : *(--ptre) = val5;
17261       case 5 : *(--ptre) = val4;
17262       case 4 : *(--ptre) = val3;
17263       case 3 : *(--ptre) = val2;
17264       case 2 : *(--ptre) = val1;
17265       case 1 : *(--ptre) = val0;
17266       }
17267       return *this;
17268     }
17269 
17270     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,
17271                      const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
17272                      const T val13, const T val14, const T val15) const {
17273       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,
17274                                                   val13,val14,val15);
17275     }
17276 
17277     //! Fill image values according to the given expression, which can be a formula or a list of values.
17278     CImg<T>& fill(const char *const expression, const bool repeat_flag) {
17279       if (is_empty() || !expression || !*expression) return *this;
17280       const unsigned int omode = cimg::exception_mode();
17281       cimg::exception_mode() = 0;
17282       try { // Try to fill values according to a formula.
17283         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
17284         _cimg_math_parser mp(base,expression,"fill");
17285         T *ptrd = _data;
17286         cimg_forXYZC(*this,x,y,z,c) *(ptrd++) = (T)mp.eval((double)x,(double)y,(double)z,(double)c);
17287       } catch (CImgException& e) { // If failed, try to recognize a list of values.
17288         char item[16384] = { 0 }, sep = 0;
17289         const char *nexpression = expression;
17290         unsigned int nb = 0; const unsigned int siz = size();
17291         T *ptrd = _data;
17292         for (double val = 0; *nexpression && nb<siz; ++nb) {
17293           sep = 0;
17294           const int err = std::sscanf(nexpression,"%4095[ \n\t0-9.e+-]%c",item,&sep);
17295           if (err>0 && std::sscanf(item,"%lf",&val)==1) {
17296             nexpression+=std::strlen(item) + (err>1?1:0);
17297             *(ptrd++) = (T)val;
17298           } else break;
17299         }
17300         cimg::exception_mode() = omode;
17301         if (nb<siz && (sep || *nexpression))
17302           throw CImgArgumentException(e.what(),pixel_type(),expression);
17303         if (repeat_flag && nb && nb<siz) for (T *ptrs = _data, *const ptre = _data + siz; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs;
17304       }
17305       cimg::exception_mode() = omode;
17306       return *this;
17307     }
17308 
17309     CImg<T> get_fill(const char *const values, const bool repeat_values) const {
17310       return (+*this).fill(values,repeat_values);
17311     }
17312 
17313     //! Fill image values according to the values found in the specified image.
17314     template<typename t>
17315     CImg<T>& fill(const CImg<t>& values, const bool repeat_values=true) {
17316       if (is_empty() || !values) return *this;
17317       T *ptrd = _data, *ptre = ptrd + size();
17318       for (t *ptrs = values._data, *ptrs_end = ptrs + values.size(); ptrs<ptrs_end && ptrd<ptre; ++ptrs) *(ptrd++) = (T)*ptrs;
17319       if (repeat_values && ptrd<ptre) for (T *ptrs = _data; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs;
17320       return *this;
17321     }
17322 
17323     template<typename t>
17324     CImg<T> get_fill(const CImg<t>& values, const bool repeat_values=true) const {
17325       return repeat_values?CImg<T>(_width,_height,_depth,_spectrum).fill(values,repeat_values):(+*this).fill(values,repeat_values);
17326     }
17327 
17328     //! Fill image values along the X-axis at the specified pixel position (y,z,c).
17329     CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const int a0, ...) {
17330 #define _cimg_fill1(x,y,z,c,off,siz,t) { \
17331     va_list ap; va_start(ap,a0); T *ptrd = data(x,y,z,c); *ptrd = (T)a0; \
17332     for (unsigned int k = 1; k<siz; ++k) { ptrd+=off; *ptrd = (T)va_arg(ap,t); } \
17333     va_end(ap); }
17334       if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,int);
17335       return *this;
17336     }
17337 
17338     CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const double a0, ...) {
17339       if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,double);
17340       return *this;
17341     }
17342 
17343     //! Fill image values along the Y-axis at the specified pixel position (x,z,c).
17344     CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const int a0, ...) {
17345       if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,int);
17346       return *this;
17347     }
17348 
17349     CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const double a0, ...) {
17350       if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,double);
17351       return *this;
17352     }
17353 
17354     //! Fill image values along the Z-axis at the specified pixel position (x,y,c).
17355     CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const int a0, ...) {
17356       const unsigned int wh = _width*_height;
17357       if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,int);
17358       return *this;
17359     }
17360 
17361     CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const double a0, ...) {
17362       const unsigned int wh = _width*_height;
17363       if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,double);
17364       return *this;
17365     }
17366 
17367     //! Fill image values along the C-axis at the specified pixel position (x,y,z).
17368     CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const int a0, ...) {
17369       const unsigned int whd = _width*_height*_depth;
17370       if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,int);
17371       return *this;
17372     }
17373 
17374     CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const double a0, ...) {
17375       const unsigned int whd = _width*_height*_depth;
17376       if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,double);
17377       return *this;
17378     }
17379 
17380     //! Remove specified value from the image buffer, and return resulting buffer as a one-column vector.
17381     CImg<T>& discard(const T value) {
17382       return get_discard(value).move_to(*this);
17383     }
17384 
17385     CImg<T> get_discard(const T value) const {
17386       CImg<T> res(1,size());
17387       T *pd = res._data;
17388       for (const T *ps = _data, *const pse = end(); ps<pse; ++ps)
17389         if (*ps!=value) *(pd++) = *ps;
17390       if (pd==res._data) return CImg<T>();
17391       return res.resize(1,pd-res._data,1,1,-1);
17392     }
17393 
17394     //! Remove specified values sequence from the image buffer, and return resulting buffer as a one-column vector.
17395     template<typename t>
17396     CImg<T>& discard(const CImg<t>& values) {
17397       return get_discard(values).move_to(*this);
17398     }
17399 
17400     template<typename t>
17401     CImg<T> get_discard(const CImg<t>& values) const {
17402       if (!values) return *this;
17403       if (values.size()==1) return get_discard(*values);
17404       CImg<T> res(1,size());
17405       T *pd = res._data;
17406       const t *const pve = values.end();
17407       for (const T *ps = _data, *const pse = end(); ps<pse; ) {
17408         const T *_ps = ps;
17409         const t *pv = values._data;
17410         while (_ps<pse && pv<pve) { if (*(_ps++)!=(T)*pv) break; ++pv; }
17411         if (pv!=pve) {
17412           const unsigned int l = _ps - ps;
17413           if (l==1) *(pd++) = *ps; else { std::memcpy(pd,ps,sizeof(T)*l); pd+=l; }
17414         }
17415         ps = _ps;
17416       }
17417       if (pd==res._data) return CImg<T>();
17418       return res.resize(1,pd-res._data,1,1,-1);
17419     }
17420 
17421     //! Invert endianness of the image buffer.
17422     CImg<T>& invert_endianness() {
17423       cimg::invert_endianness(_data,size());
17424       return *this;
17425     }
17426 
17427     CImg<T> get_invert_endianness() const {
17428       return (+*this).invert_endianness();
17429     }
17430 
17431     //! Fill the image instance with random values between specified range.
17432     CImg<T>& rand(const T val_min, const T val_max) {
17433       const float delta = (float)val_max - (float)val_min;
17434       cimg_for(*this,ptrd,T) *ptrd = (T)(val_min + cimg::rand()*delta);
17435       return *this;
17436     }
17437 
17438     CImg<T> get_rand(const T val_min, const T val_max) const {
17439       return (+*this).rand(val_min,val_max);
17440     }
17441 
17442     //! Compute image with rounded pixel values.
17443     /**
17444        \param y Rounding precision.
17445        \param rounding_type Roundin type, can be 0 (nearest), 1 (forward), -1(backward).
17446     **/
17447     CImg<T>& round(const double y=1, const int rounding_type=0) {
17448       if (y>0) cimg_for(*this,ptrd,T) *ptrd = cimg::round(*ptrd,y,rounding_type);
17449       return *this;
17450     }
17451 
17452     CImg<T> get_round(const double y=1, const unsigned int rounding_type=0) const {
17453       return (+*this).round(y,rounding_type);
17454     }
17455 
17456     //! Add random noise to the values of the image instance.
17457     /**
17458        \param sigma Amplitude of the random additive noise. If \p sigma<0, it stands for a percentage of the global value range.
17459        \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).
17460        \return A reference to the modified image instance.
17461        \note
17462        - For Poisson noise (\p noise_type=3), parameter \p sigma is ignored, as Poisson noise only depends on the image value itself.
17463        - Function \p CImg<T>::get_noise() is also defined. It returns a non-shared modified copy of the image instance.
17464        \par Sample code :
17465        \code
17466        const CImg<float> img("reference.jpg"), res = img.get_noise(40);
17467        (img,res.normalize(0,255)).display();
17468        \endcode
17469        \image html ref_noise.jpg
17470     **/
17471     CImg<T>& noise(const double sigma, const unsigned int noise_type=0) {
17472       if (!is_empty()) {
17473         const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
17474         Tfloat nsigma = (Tfloat)sigma, m = 0, M = 0;
17475         if (nsigma==0 && noise_type!=3) return *this;
17476         if (nsigma<0 || noise_type==2) m = (Tfloat)min_max(M);
17477         if (nsigma<0) nsigma = (Tfloat)(-nsigma*(M-m)/100.0);
17478         switch (noise_type) {
17479         case 0 : { // Gaussian noise
17480           cimg_for(*this,ptrd,T) {
17481             Tfloat val = (Tfloat)(*ptrd + nsigma*cimg::grand());
17482             if (val>vmax) val = vmax;
17483             if (val<vmin) val = vmin;
17484             *ptrd = (T)val;
17485           }
17486         } break;
17487         case 1 : { // Uniform noise
17488           cimg_for(*this,ptrd,T) {
17489             Tfloat val = (Tfloat)(*ptrd + nsigma*cimg::crand());
17490             if (val>vmax) val = vmax;
17491             if (val<vmin) val = vmin;
17492             *ptrd = (T)val;
17493           }
17494         } break;
17495         case 2 : { // Salt & Pepper noise
17496           if (nsigma<0) nsigma = -nsigma;
17497           if (M==m) { m = 0; M = (Tfloat)(cimg::type<T>::is_float()?1:cimg::type<T>::max()); }
17498           cimg_for(*this,ptrd,T) if (cimg::rand()*100<nsigma) *ptrd = (T)(cimg::rand()<0.5?M:m);
17499         } break;
17500 
17501         case 3 : { // Poisson Noise
17502           cimg_for(*this,ptrd,T) *ptrd = (T)cimg::prand(*ptrd);
17503         } break;
17504 
17505         case 4 : { // Rice noise
17506           const Tfloat sqrt2 = (Tfloat)std::sqrt(2.0);
17507           cimg_for(*this,ptrd,T) {
17508             const Tfloat
17509               val0 = (Tfloat)*ptrd/sqrt2,
17510               re = (Tfloat)(val0 + nsigma*cimg::grand()),
17511               im = (Tfloat)(val0 + nsigma*cimg::grand());
17512             Tfloat val = (Tfloat)std::sqrt(re*re + im*im);
17513             if (val>vmax) val = vmax;
17514             if (val<vmin) val = vmin;
17515             *ptrd = (T)val;
17516           }
17517         } break;
17518         default :
17519           throw CImgArgumentException(_cimg_instance
17520                                       "noise() : Invalid specified noise type %d "
17521                                       "(should be { 0=gaussian | 1=uniform | 2=salt&Pepper | 3=poisson }).",
17522                                       cimg_instance,
17523                                       noise_type);
17524         }
17525       }
17526       return *this;
17527     }
17528 
17529     CImg<T> get_noise(const double sigma, const unsigned int noise_type=0) const {
17530       return (+*this).noise(sigma,noise_type);
17531     }
17532 
17533     //! Linearly normalize values of the image instance between \p value_min and \p value_max.
17534     /**
17535        \param value_min Minimum desired value of the resulting image.
17536        \param value_max Maximum desired value of the resulting image.
17537        \return A reference to the modified image instance.
17538        \note
17539        - Function \p CImg<T>::get_normalize() is also defined. It returns a non-shared modified copy of the image instance.
17540        \par Sample code :
17541        \code
17542        const CImg<float> img("reference.jpg"), res = img.get_normalize(160,220);
17543        (img,res).display();
17544        \endcode
17545        \image html ref_normalize2.jpg
17546     **/
17547     CImg<T>& normalize(const T value_min, const T value_max) {
17548       if (is_empty()) return *this;
17549       const T a = value_min<value_max?value_min:value_max, b = value_min<value_max?value_max:value_min;
17550       T m, M = max_min(m);
17551       const Tfloat fm = (Tfloat)m, fM = (Tfloat)M;
17552       if (m==M) return fill(value_min);
17553       if (m!=a || M!=b) cimg_for(*this,ptrd,T) *ptrd = (T)((*ptrd-fm)/(fM-fm)*(b-a)+a);
17554       return *this;
17555     }
17556 
17557     CImg<Tfloat> get_normalize(const T value_min, const T value_max) const {
17558       return CImg<Tfloat>(*this,false).normalize((Tfloat)value_min,(Tfloat)value_max);
17559     }
17560 
17561     //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm.
17562     /**
17563        \return A reference to the modified image instance.
17564        \note
17565        - Function \p CImg<T>::get_normalize() is also defined. It returns a non-shared modified copy of the image instance.
17566        \par Sample code :
17567        \code
17568        const CImg<float> img("reference.jpg"), res = img.get_normalize();
17569        (img,res.normalize(0,255)).display();
17570        \endcode
17571        \image html ref_normalize.jpg
17572     **/
17573     CImg<T>& normalize() {
17574       T *ptrd = _data;
17575       const unsigned int whd = _width*_height*_depth;
17576       cimg_forXYZ(*this,x,y,z) {
17577         const T *ptrs = ptrd;
17578         float n = 0;
17579         cimg_forC(*this,c) { n+=cimg::sqr((float)*ptrs); ptrs+=whd; }
17580         n = (float)std::sqrt(n);
17581         T *_ptrd = ptrd++;
17582         if (n>0) cimg_forC(*this,c) { *_ptrd = (T)(*_ptrd/n); _ptrd+=whd; }
17583         else cimg_forC(*this,c) { *_ptrd = (T)0; _ptrd+=whd; }
17584       }
17585       return *this;
17586     }
17587 
17588     CImg<Tfloat> get_normalize() const {
17589       return CImg<Tfloat>(*this,false).normalize();
17590     }
17591 
17592     //! Compute L2-norm of each multi-valued pixel of the image instance.
17593     /**
17594        \param norm_type Type of computed vector norm (can be \p 0=Linf, \p 1=L1 or \p 2=L2).
17595        \return A reference to the modified image instance.
17596        \note
17597        - Function \p CImg<T>::get_norm() is also defined. It returns a non-shared modified copy of the image instance.
17598        \par Sample code :
17599        \code
17600        const CImg<float> img("reference.jpg"), res = img.get_norm();
17601        (img,res.normalize(0,255)).display();
17602        \endcode
17603        \image html ref_norm.jpg
17604     **/
17605     CImg<T>& norm(const int norm_type=2) {
17606       if (_spectrum==1) return abs();
17607       return get_norm(norm_type).move_to(*this);
17608     }
17609 
17610     CImg<Tfloat> get_norm(const int norm_type=2) const {
17611       if (is_empty()) return *this;
17612       if (_spectrum==1) return get_abs();
17613       const T *ptrs = _data;
17614       const unsigned int whd = _width*_height*_depth;
17615       CImg<Tfloat> res(_width,_height,_depth);
17616       Tfloat *ptrd = res._data;
17617       switch (norm_type) {
17618       case -1 : {             // Linf norm
17619         cimg_forXYZ(*this,x,y,z) {
17620           Tfloat n = 0;
17621           const T *_ptrs = ptrs++;
17622           cimg_forC(*this,c) { const Tfloat val = (Tfloat)cimg::abs(*_ptrs); if (val>n) n = val; _ptrs+=whd; }
17623           *(ptrd++) = n;
17624         }
17625       } break;
17626       case 1 : {              // L1 norm
17627         cimg_forXYZ(*this,x,y,z) {
17628           Tfloat n = 0;
17629           const T *_ptrs = ptrs++;
17630           cimg_forC(*this,c) { n+=cimg::abs(*_ptrs); _ptrs+=whd; }
17631           *(ptrd++) = n;
17632         }
17633       } break;
17634       default : {             // L2 norm
17635         cimg_forXYZ(*this,x,y,z) {
17636           Tfloat n = 0;
17637           const T *_ptrs = ptrs++;
17638           cimg_forC(*this,c) { n+=cimg::sqr((Tfloat)*_ptrs); _ptrs+=whd; }
17639           *(ptrd++) = (Tfloat)std::sqrt((Tfloat)n);
17640         }
17641       }
17642       }
17643       return res;
17644     }
17645 
17646     //! Cut values of the image instance between \p value_min and \p value_max.
17647     /**
17648        \param value_min Minimum desired value of the resulting image.
17649        \param value_max Maximum desired value of the resulting image.
17650        \return A reference to the modified image instance.
17651        \note
17652        - Function \p CImg<T>::get_cut() is also defined. It returns a non-shared modified copy of the image instance.
17653        \par Sample code :
17654        \code
17655        const CImg<float> img("reference.jpg"), res = img.get_cut(160,220);
17656        (img,res).display();
17657        \endcode
17658        \image html ref_cut.jpg
17659     **/
17660     CImg<T>& cut(const T value_min, const T value_max) {
17661       if (is_empty()) return *this;
17662       const T a = value_min<value_max?value_min:value_max, b = value_min<value_max?value_max:value_min;
17663       cimg_for(*this,ptrd,T) *ptrd = (*ptrd<a)?a:((*ptrd>b)?b:*ptrd);
17664       return *this;
17665     }
17666 
17667     CImg<T> get_cut(const T value_min, const T value_max) const {
17668       return (+*this).cut(value_min,value_max);
17669     }
17670 
17671     //! Uniformly quantize values of the image instance into \p nb_levels levels.
17672     /**
17673        \param nb_levels Number of quantization levels.
17674        \param keep_range Tells if resulting values keep the same range as the original ones.
17675        \return A reference to the modified image instance.
17676        \note
17677        - Function \p CImg<T>::get_quantize() is also defined. It returns a non-shared modified copy of the image instance.
17678        \par Sample code :
17679        \code
17680        const CImg<float> img("reference.jpg"), res = img.get_quantize(4);
17681        (img,res).display();
17682        \endcode
17683        \image html ref_quantize.jpg
17684     **/
17685     CImg<T>& quantize(const unsigned int nb_levels, const bool keep_range=true) {
17686       if (!nb_levels)
17687         throw CImgArgumentException(_cimg_instance
17688                                     "quantize() : Invalid quantization request with 0 values.",
17689                                     cimg_instance);
17690 
17691       if (is_empty()) return *this;
17692       Tfloat m, M = (Tfloat)max_min(m), range = M - m;
17693       if (range>0) {
17694         if (keep_range) cimg_for(*this,ptrd,T) {
17695           const unsigned int val = (unsigned int)((*ptrd-m)*nb_levels/range);
17696           *ptrd = (T)(m + cimg::min(val,nb_levels-1)*range/nb_levels);
17697         } else cimg_for(*this,ptrd,T) {
17698           const unsigned int val = (unsigned int)((*ptrd-m)*nb_levels/range);
17699           *ptrd = (T)cimg::min(val,nb_levels-1);
17700         }
17701       }
17702       return *this;
17703     }
17704 
17705     CImg<T> get_quantize(const unsigned int n, const bool keep_range=true) const {
17706       return (+*this).quantize(n,keep_range);
17707     }
17708 
17709     //! Threshold values of the image instance.
17710     /**
17711        \param value Threshold value
17712        \param soft_threshold Tells if soft thresholding must be applied (instead of hard one).
17713        \param strict_threshold Tells if threshold value is strict.
17714        \return A reference to the modified image instance. Resulting pixel values are either equal to 0 or 1.
17715        \note
17716        - Function \p CImg<T>::get_threshold() is also defined. It returns a non-shared modified copy of the image instance.
17717        \par Sample code :
17718        \code
17719        const CImg<float> img("reference.jpg"), res = img.get_threshold(128);
17720        (img,res.normalize(0,255)).display();
17721        \endcode
17722        \image html ref_threshold.jpg
17723     **/
17724     CImg<T>& threshold(const T value, const bool soft_threshold=false, const bool strict_threshold=false) {
17725       if (is_empty()) return *this;
17726       if (strict_threshold) {
17727         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; }
17728         else cimg_for(*this,ptrd,T) *ptrd = *ptrd>value?(T)1:(T)0;
17729       } else {
17730         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; }
17731         else cimg_for(*this,ptrd,T) *ptrd = *ptrd>=value?(T)1:(T)0;
17732       }
17733       return *this;
17734     }
17735 
17736     CImg<T> get_threshold(const T value, const bool soft_threshold=false, const bool strict_threshold=false) const {
17737       return (+*this).threshold(value,soft_threshold,strict_threshold);
17738     }
17739 
17740     //! Compute the histogram of the image instance.
17741     /**
17742        \param nb_levels Number of desired histogram levels.
17743        \param value_min Minimum pixel value considered for the histogram computation. All pixel values lower than \p value_min will not be counted.
17744        \param value_max Maximum pixel value considered for the histogram computation. All pixel values higher than \p value_max will not be counted.
17745        \return image instance is replaced by its histogram, defined as a \p CImg<T>(nb_levels) image.
17746        \note
17747        - 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.
17748        - If \p value_min==value_max==0 (default behavior), the function first estimates the whole range of pixel values
17749        then uses it to compute the histogram.
17750        - The resulting histogram is always defined in 1d. Histograms of multi-valued images are not multi-dimensional.
17751        - Function \p CImg<T>::get_histogram() is also defined. It returns a non-shared modified copy of the image instance.
17752        \par Sample code :
17753        \code
17754        const CImg<float> img = CImg<float>("reference.jpg").histogram(256);
17755        img.display_graph(0,3);
17756        \endcode
17757        \image html ref_histogram.jpg
17758     **/
17759     CImg<T>& histogram(const unsigned int nb_levels, const T value_min=(T)0, const T value_max=(T)0) {
17760       return get_histogram(nb_levels,value_min,value_max).move_to(*this);
17761     }
17762 
17763     CImg<floatT> get_histogram(const unsigned int nb_levels, const T value_min=(T)0, const T value_max=(T)0) const {
17764       if (!nb_levels)
17765         throw CImgArgumentException(_cimg_instance
17766                                     "histogram() : Invalid histogram request with 0 levels.",
17767                                     cimg_instance);
17768 
17769       if (is_empty()) return CImg<floatT>();
17770       T vmin = value_min, vmax = value_max;
17771       CImg<floatT> res(nb_levels,1,1,1,0);
17772       if (vmin>=vmax && vmin==0) vmin = min_max(vmax);
17773       if (vmin<vmax) cimg_for(*this,ptrs,T) {
17774         const T val = *ptrs;
17775         if (val>=vmin && val<=vmax) ++res[val==vmax?nb_levels-1:(int)((val-vmin)*nb_levels/(vmax-vmin))];
17776       } else res[0]+=size();
17777       return res;
17778     }
17779 
17780     //! Compute the histogram-equalized version of the image instance.
17781     /**
17782        \param nb_levels Number of histogram levels used for the equalization.
17783        \param value_min Minimum pixel value considered for the histogram computation. All pixel values lower than \p value_min will not be counted.
17784        \param value_max Maximum pixel value considered for the histogram computation. All pixel values higher than \p value_max will not be counted.
17785        \return A reference to the modified image instance.
17786        \note
17787        - If \p value_min==value_max==0 (default behavior), the function first estimates the whole range of pixel values
17788        then uses it to equalize the histogram.
17789        - Function \p CImg<T>::get_equalize() is also defined. It returns a non-shared modified copy of the image instance.
17790        \par Sample code :
17791        \code
17792        const CImg<float> img("reference.jpg"), res = img.get_equalize(256);
17793        (img,res).display();
17794        \endcode
17795        \image html ref_equalize.jpg
17796     **/
17797     CImg<T>& equalize(const unsigned int nb_levels, const T value_min=(T)0, const T value_max=(T)0) {
17798       if (is_empty()) return *this;
17799       T vmin = value_min, vmax = value_max;
17800       if (vmin==vmax && vmin==0) vmin = min_max(vmax);
17801       if (vmin<vmax) {
17802         CImg<floatT> hist = get_histogram(nb_levels,vmin,vmax);
17803         float cumul = 0;
17804         cimg_forX(hist,pos) { cumul+=hist[pos]; hist[pos] = cumul; }
17805         cimg_for(*this,ptrd,T) {
17806           const int pos = (unsigned int)((*ptrd-vmin)*(nb_levels-1)/(vmax-vmin));
17807           if (pos>=0 && pos<(int)nb_levels) *ptrd = (T)(vmin + (vmax-vmin)*hist[pos]/size());
17808         }
17809       }
17810       return *this;
17811     }
17812 
17813     CImg<T> get_equalize(const unsigned int nblevels, const T val_min=(T)0, const T val_max=(T)0) const {
17814       return (+*this).equalize(nblevels,val_min,val_max);
17815     }
17816 
17817     //! Index multi-valued pixels of the image instance, regarding to a predefined palette.
17818     /**
17819        \param palette Multi-valued palette used as the basis for multi-valued pixel indexing.
17820        \param dithering Tells if Floyd-Steinberg dithering is activated or not.
17821        \param map_indexes Tell if the values of the resulting image are the palette indices or the palette vectors.
17822        \return A reference to the modified image instance.
17823        \note
17824        - \p img.index(palette,dithering,1) is equivalent to <tt>img.index(palette,dithering,0).map(palette)</tt>.
17825        - Function \p CImg<T>::get_index() is also defined. It returns a non-shared modified copy of the image instance.
17826        \par Sample code :
17827        \code
17828        const CImg<float> img("reference.jpg"), palette(3,1,1,3, 0,128,255, 0,128,255, 0,128,255);
17829        const CImg<float> res = img.get_index(palette,true,true);
17830        (img,res).display();
17831        \endcode
17832        \image html ref_index.jpg
17833     **/
17834     template<typename t>
17835     CImg<T>& index(const CImg<t>& palette, const bool dithering=false, const bool map_indexes=false) {
17836       return get_index(palette,dithering,map_indexes).move_to(*this);
17837     }
17838 
17839     template<typename t>
17840     CImg<typename CImg<t>::Tuint>
17841     get_index(const CImg<t>& palette, const bool dithering=false, const bool map_indexes=true) const {
17842       if (palette._spectrum!=_spectrum)
17843         throw CImgArgumentException(_cimg_instance
17844                                     "index() : Instance and specified palette (%u,%u,%u,%u,%p) "
17845                                     "have incompatible dimensions.",
17846                                     cimg_instance,
17847                                     palette._width,palette._height,palette._depth,palette._spectrum,palette._data);
17848 
17849       typedef typename CImg<t>::Tuint tuint;
17850       if (is_empty()) return CImg<tuint>();
17851       const unsigned int whd = _width*_height*_depth, pwhd = palette._width*palette._height*palette._depth;
17852       CImg<tuint> res(_width,_height,_depth,map_indexes?_spectrum:1);
17853       tuint *ptrd = res._data;
17854       if (dithering) { // Dithered versions.
17855         Tfloat valm = 0, valM = (Tfloat)max_min(valm);
17856         if (valm==valM && valm>=0 && valM<=255) { valm = 0; valM = 255; }
17857         CImg<Tfloat> cache = get_crop(-1,0,0,0,_width,1,0,_spectrum-1);
17858         Tfloat *cache_current = cache.data(1,0,0,0), *cache_next = cache.data(1,1,0,0);
17859         const unsigned int cwhd = cache._width*cache._height*cache._depth;
17860         switch (_spectrum) {
17861         case 1 : { // Optimized for scalars.
17862           cimg_forYZ(*this,y,z) {
17863             if (y<height()-2) {
17864               Tfloat *ptrc0 = cache_next; const T *ptrs0 = data(0,y+1,z,0);
17865               cimg_forX(*this,x) *(ptrc0++) = (Tfloat)*(ptrs0++);
17866             }
17867             Tfloat *ptrs0 = cache_current, *ptrsn0 = cache_next;
17868             cimg_forX(*this,x) {
17869               const Tfloat _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0;
17870               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = palette._data;
17871               for (const t *ptrp0 = palette._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) {
17872                 const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0;
17873                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
17874               }
17875               const Tfloat err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)/16;
17876               *ptrs0+=7*err0; *(ptrsn0-1)+=3*err0; *(ptrsn0++)+=5*err0; *ptrsn0+=err0;
17877               if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - palette._data);
17878             }
17879             cimg::swap(cache_current,cache_next);
17880           }
17881         } break;
17882         case 2 : { // Optimized for 2d vectors.
17883           tuint *ptrd1 = ptrd + whd;
17884           cimg_forYZ(*this,y,z) {
17885             if (y<height()-2) {
17886               Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd;
17887               const T *ptrs0 = data(0,y+1,z,0), *ptrs1 = ptrs0 + whd;
17888               cimg_forX(*this,x) { *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); }
17889             }
17890             Tfloat
17891               *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd,
17892               *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd;
17893             cimg_forX(*this,x) {
17894               const Tfloat
17895                 _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0,
17896                 _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1;
17897               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = palette._data;
17898               for (const t *ptrp0 = palette._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
17899                 const Tfloat
17900                   pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1,
17901                   dist = pval0*pval0 + pval1*pval1;
17902                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
17903               }
17904               const t *const ptrmin1 = ptrmin0 + pwhd;
17905               const Tfloat
17906                 err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)/16,
17907                 err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)/16;
17908               *ptrs0+=7*err0; *ptrs1+=7*err1;
17909               *(ptrsn0-1)+=3*err0; *(ptrsn1-1)+=3*err1;
17910               *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1;
17911               *ptrsn0+=err0; *ptrsn1+=err1;
17912               if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; }
17913               else *(ptrd++) = (tuint)(ptrmin0 - palette._data);
17914             }
17915             cimg::swap(cache_current,cache_next);
17916           }
17917         } break;
17918         case 3 : { // Optimized for 3d vectors (colors).
17919           tuint *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd;
17920           cimg_forYZ(*this,y,z) {
17921             if (y<height()-2) {
17922               Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd, *ptrc2 = ptrc1 + cwhd;
17923               const T *ptrs0 = data(0,y+1,z,0), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd;
17924               cimg_forX(*this,x) { *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); *(ptrc2++) = (Tfloat)*(ptrs2++); }
17925             }
17926             Tfloat
17927               *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd, *ptrs2 = ptrs1 + cwhd,
17928               *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd, *ptrsn2 = ptrsn1 + cwhd;
17929             cimg_forX(*this,x) {
17930               const Tfloat
17931                 _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0,
17932                 _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1,
17933                 _val2 = (Tfloat)*ptrs2, val2 = _val2<valm?valm:_val2>valM?valM:_val2;
17934               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = palette._data;
17935               for (const t *ptrp0 = palette._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
17936                 const Tfloat
17937                   pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1, pval2 = (Tfloat)*(ptrp2++) - val2,
17938                   dist = pval0*pval0 + pval1*pval1 + pval2*pval2;
17939                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
17940               }
17941               const t *const ptrmin1 = ptrmin0 + pwhd, *const ptrmin2 = ptrmin1 + pwhd;
17942               const Tfloat
17943                 err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)/16,
17944                 err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)/16,
17945                 err2 = ((*(ptrs2++)=val2) - (Tfloat)*ptrmin2)/16;
17946               *ptrs0+=7*err0; *ptrs1+=7*err1; *ptrs2+=7*err2;
17947               *(ptrsn0-1)+=3*err0; *(ptrsn1-1)+=3*err1; *(ptrsn2-1)+=3*err2;
17948               *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1; *(ptrsn2++)+=5*err2;
17949               *ptrsn0+=err0; *ptrsn1+=err1; *ptrsn2+=err2;
17950               if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; *(ptrd2++) = (tuint)*ptrmin2; }
17951               else *(ptrd++) = (tuint)(ptrmin0 - palette._data);
17952             }
17953             cimg::swap(cache_current,cache_next);
17954           }
17955         } break;
17956         default : // Generic version
17957           cimg_forYZ(*this,y,z) {
17958             if (y<height()-2) {
17959               Tfloat *ptrc = cache_next;
17960               cimg_forC(*this,c) {
17961                 Tfloat *_ptrc = ptrc; const T *_ptrs = data(0,y+1,z,c);
17962                 cimg_forX(*this,x) *(_ptrc++) = (Tfloat)*(_ptrs++);
17963                 ptrc+=cwhd;
17964               }
17965             }
17966             Tfloat *ptrs = cache_current, *ptrsn = cache_next;
17967             cimg_forX(*this,x) {
17968               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = palette._data;
17969               for (const t *ptrp = palette._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) {
17970                 Tfloat dist = 0; Tfloat *_ptrs = ptrs; const t *_ptrp = ptrp;
17971                 cimg_forC(*this,c) {
17972                   const Tfloat _val = *_ptrs, val = _val<valm?valm:_val>valM?valM:_val;
17973                   dist+=cimg::sqr((*_ptrs=val) - (Tfloat)*_ptrp); _ptrs+=cwhd; _ptrp+=pwhd;
17974                 }
17975                 if (dist<distmin) { ptrmin = ptrp; distmin = dist; }
17976               }
17977               const t *_ptrmin = ptrmin; Tfloat *_ptrs = ptrs++, *_ptrsn = (ptrsn++)-1;
17978               cimg_forC(*this,c) {
17979                 const Tfloat err = (*(_ptrs++) - (Tfloat)*_ptrmin)/16;
17980                 *_ptrs+=7*err; *(_ptrsn++)+=3*err; *(_ptrsn++)+=5*err; *_ptrsn+=err;
17981                 _ptrmin+=pwhd; _ptrs+=cwhd-1; _ptrsn+=cwhd-2;
17982               }
17983               if (map_indexes) {
17984                 tuint *_ptrd = ptrd++;
17985                 cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; }
17986               }
17987               else *(ptrd++) = (tuint)(ptrmin - palette._data);
17988             }
17989             cimg::swap(cache_current,cache_next);
17990           }
17991         }
17992       } else { // Non-dithered versions
17993         switch (_spectrum) {
17994         case 1 : { // Optimized for scalars.
17995           for (const T *ptrs0 = _data, *ptrs_end = ptrs0 + whd; ptrs0<ptrs_end; ) {
17996             const Tfloat val0 = (Tfloat)*(ptrs0++);
17997             Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = palette._data;
17998             for (const t *ptrp0 = palette._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) {
17999               const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0;
18000               if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
18001             }
18002             if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - palette._data);
18003           }
18004         } break;
18005         case 2 : { // Optimized for 2d vectors.
18006           tuint *ptrd1 = ptrd + whd;
18007           for (const T *ptrs0 = _data, *ptrs1 = ptrs0 + whd, *ptrs_end = ptrs1; ptrs0<ptrs_end; ) {
18008             const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++);
18009             Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = palette._data;
18010             for (const t *ptrp0 = palette._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
18011               const Tfloat
18012                 pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1,
18013                 dist = pval0*pval0 + pval1*pval1;
18014               if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
18015             }
18016             if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*(ptrmin0 + pwhd); }
18017             else *(ptrd++) = (tuint)(ptrmin0 - palette._data);
18018           }
18019         } break;
18020         case 3 : { // Optimized for 3d vectors (colors).
18021           tuint *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd;
18022           for (const T *ptrs0 = _data, *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd, *ptrs_end = ptrs1; ptrs0<ptrs_end; ) {
18023             const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++), val2 = (Tfloat)*(ptrs2++);
18024             Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = palette._data;
18025             for (const t *ptrp0 = palette._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
18026               const Tfloat
18027                 pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1, pval2 = (Tfloat)*(ptrp2++) - val2,
18028                 dist = pval0*pval0 + pval1*pval1 + pval2*pval2;
18029               if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
18030             }
18031             if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*(ptrmin0 + pwhd); *(ptrd2++) = (tuint)*(ptrmin0 + 2*pwhd); }
18032             else *(ptrd++) = (tuint)(ptrmin0 - palette._data);
18033           }
18034         } break;
18035         default : // Generic version.
18036           for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ++ptrs) {
18037             Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = palette._data;
18038             for (const t *ptrp = palette._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) {
18039               Tfloat dist = 0; const T *_ptrs = ptrs; const t *_ptrp = ptrp;
18040               cimg_forC(*this,c) { dist+=cimg::sqr((Tfloat)*_ptrs - (Tfloat)*_ptrp); _ptrs+=whd; _ptrp+=pwhd; }
18041               if (dist<distmin) { ptrmin = ptrp; distmin = dist; }
18042             }
18043             if (map_indexes) {
18044               tuint *_ptrd = ptrd++;
18045               cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; }
18046             }
18047             else *(ptrd++) = (tuint)(ptrmin - palette._data);
18048           }
18049         }
18050       }
18051       return res;
18052     }
18053 
18054     //! Map predefined palette on the scalar (indexed) image instance.
18055     /**
18056        \param palette Multi-valued palette used for mapping the indexes.
18057        \return A reference to the modified image instance.
18058        \note
18059        - Function \p CImg<T>::get_map() is also defined. It returns a non-shared modified copy of the image instance.
18060        \par Sample code :
18061        \code
18062        const CImg<float> img("reference.jpg"),
18063                          palette1(3,1,1,3, 0,128,255, 0,128,255, 0,128,255),
18064                          palette2(3,1,1,3, 255,0,0, 0,255,0, 0,0,255),
18065                          res = img.get_index(palette1,false).map(palette2);
18066        (img,res).display();
18067        \endcode
18068        \image html ref_map.jpg
18069     **/
18070     template<typename t>
18071     CImg<T>& map(const CImg<t>& palette) {
18072       return get_map(palette).move_to(*this);
18073     }
18074 
18075     template<typename t>
18076     CImg<t> get_map(const CImg<t>& palette) const {
18077       if (_spectrum!=1 && palette._spectrum!=1)
18078         throw CImgArgumentException(_cimg_instance
18079                                     "map() : Instance and specified palette (%u,%u,%u,%u,%p) "
18080                                     "have incompatible dimensions.",
18081                                     cimg_instance,
18082                                     palette._width,palette._height,palette._depth,palette._spectrum,palette._data);
18083 
18084       const unsigned int whd = _width*_height*_depth, pwhd = palette._width*palette._height*palette._depth;
18085       CImg<t> res(_width,_height,_depth,palette._spectrum==1?_spectrum:palette._spectrum);
18086       switch (palette._spectrum) {
18087       case 1 : { // Optimized for scalars.
18088         const T *ptrs = _data + whd*_spectrum;
18089         cimg_for(res,ptrd,t) {
18090           const unsigned int _ind = (unsigned int)*(--ptrs), ind = _ind<pwhd?_ind:0;
18091           *ptrd = palette[ind];
18092         }
18093       } break;
18094       case 2 : { // Optimized for 2d vectors.
18095         const t *const ptrp0 = palette._data, *ptrp1 = ptrp0 + pwhd;
18096         t *ptrd0 = res._data, *ptrd1 = ptrd0 + whd;
18097         for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
18098           const unsigned int _ind = (unsigned int)*(ptrs++), ind = _ind<pwhd?_ind:0;
18099           *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind];
18100         }
18101       } break;
18102       case 3 : { // Optimized for 3d vectors (colors).
18103         const t *const ptrp0 = palette._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd;
18104         t *ptrd0 = res._data, *ptrd1 = ptrd0 + whd, *ptrd2 = ptrd1 + whd;
18105         for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
18106           const unsigned int _ind = (unsigned int)*(ptrs++), ind = _ind<pwhd?_ind:0;
18107           *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind]; *(ptrd2++) = ptrp2[ind];
18108         }
18109       } break;
18110       default : { // Generic version.
18111         t *ptrd = res._data;
18112         for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
18113           const unsigned int _ind = (unsigned int)*(ptrs++), ind = _ind<pwhd?_ind:0;
18114           const t *ptrp = palette._data + ind;
18115           t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = *ptrp; _ptrd+=whd; ptrp+=pwhd; }
18116         }
18117       }
18118       }
18119       return res;
18120     }
18121 
18122     //! Label connected components.
18123     /**
18124        \param is_high_connectivity Boolean that choose between 4(false)- or 8(true)-connectivity
18125                                    in 2d case, and between 6(false)- or 26(true)-connectivity in 3d case.
18126         \note The algorithm of connected components computation has been primarily done
18127               by A. Meijster, according to the publication :
18128              'W.H. Hesselink, A. Meijster, C. Bron, "Concurrent Determination of Connected Components.",
18129              In: Science of Computer Programming 41 (2001), pp. 173--194'.
18130              The submitted code has then been modified to fit CImg coding style and constraints.
18131     **/
18132     CImg<T>& label(const bool is_high_connectivity=false, const Tfloat tolerance=0) {
18133       return get_label(is_high_connectivity,tolerance).move_to(*this);
18134     }
18135 
18136     CImg<unsigned long> get_label(const bool is_high_connectivity=false, const Tfloat tolerance=0) const {
18137       if (is_empty()) return CImg<unsigned long>();
18138 
18139       // Create neighborhood tables.
18140       int dx[26], dy[26], dz[26], nb = 0;
18141       if (_depth>1) { // 3d version.
18142         for (unsigned int _dx = 0; _dx<=1; ++_dx)
18143           for (unsigned int _dy = 0; _dy<=1; ++_dy)
18144             for (unsigned int _dz = 0; _dz<=1; ++_dz)
18145               if (_dx+_dy+_dz && (is_high_connectivity || _dx+_dy+_dz==1)) { dx[nb] = (int)_dx; dy[nb] = (int)_dy; dz[nb] = (int)_dz; ++nb; }
18146       } else { // 2d version.
18147         for (unsigned int _dx = 0; _dx<=1; ++_dx)
18148           for (unsigned int _dy = 0; _dy<=1; ++_dy)
18149             if (_dx+_dy && (is_high_connectivity || _dx+_dy==1)) { dx[nb] = (int)_dx; dy[nb] = (int)_dy; dz[nb] = 0; ++nb; }
18150       }
18151       return _get_label(nb,dx,dy,dz,tolerance);
18152     }
18153 
18154     template<typename t>
18155     CImg<T>& label(const CImg<t>& connectivity_mask, const Tfloat tolerance=0) {
18156       return get_label(connectivity_mask,tolerance).move_to(*this);
18157     }
18158 
18159     template<typename t>
18160     CImg<unsigned long> get_label(const CImg<t>& connectivity_mask, const Tfloat tolerance=0) const {
18161       int nb = 0;
18162       cimg_for(connectivity_mask,ptr,t) if (*ptr) ++nb;
18163       CImg<intT> dx(nb,1,1,1,0), dy(nb,1,1,1,0), dz(nb,1,1,1,0);
18164       nb = 0;
18165       cimg_forXYZ(connectivity_mask,x,y,z) if ((x || y || z) && connectivity_mask(x,y,z)) {
18166         dx[nb] = x; dy[nb] = y; dz[nb++] = z;
18167       }
18168       return _get_label(nb,dx,dy,dz,tolerance);
18169     }
18170 
18171     // Generic version, allows any kind of neighbor connectivity. Use it at your own risk :)
18172     CImg<unsigned long> _get_label(const unsigned int nb, const int *const dx, const int *const dy, const int *const dz,
18173                                    const Tfloat tolerance) const {
18174       CImg<unsigned long> res(_width,_height,_depth,_spectrum);
18175       cimg_forC(*this,c) {
18176         CImg<unsigned long> _res = res.get_shared_channel(c);
18177 
18178         // Init label numbers.
18179         unsigned long *ptr = _res.data();
18180         cimg_foroff(_res,p) *(ptr++) = p;
18181 
18182         // For each neighbour-direction, label.
18183         for (unsigned int n = 0; n<nb; ++n) {
18184           const int _dx = dx[n], _dy = dy[n], _dz = dz[n];
18185           if (_dx || _dy || _dz) {
18186             const unsigned int
18187               x0 = _dx<0?-_dx:0,
18188               x1 = _dx<0?_width:_width - _dx,
18189               y0 = _dy<0?-_dy:0,
18190               y1 = _dy<0?_height:_height - _dy,
18191               z0 = _dz<0?-_dz:0,
18192               z1 = _dz<0?_depth:_depth - _dz,
18193               wh = _width*_height;
18194             const unsigned long offset = _dz*wh + _dy*_width + _dx;
18195             for (unsigned long z = z0, nz = z0 + _dz, pz = z0*wh; z<z1; ++z, ++nz, pz+=wh) {
18196               for (unsigned long y = y0, ny = y0 + _dy, py = y0*_width + pz; y<y1; ++y, ++ny, py+=_width) {
18197                 for (unsigned long x = x0, nx = x0 + _dx, p = x0 + py; x<x1; ++x, ++nx, ++p) {
18198                   if ((Tfloat)cimg::abs((*this)(x,y,z,0,wh)-(*this)(nx,ny,nz,0,wh))<=tolerance) {
18199                     const unsigned long q = p + offset;
18200                     unsigned long x, y;
18201                     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); }
18202                     if (x!=y) _res[x] = y;
18203                     for (unsigned long _p = p; _p!=y; ) { const unsigned long h = _res[_p]; _res[_p] = y; _p = h; }
18204                     for (unsigned long _q = q; _q!=y; ) { const unsigned long h = _res[_q]; _res[_q] = y; _q = h; }
18205                   }
18206                 }
18207               }
18208             }
18209           }
18210         }
18211 
18212         // Remove equivalences.
18213         unsigned long counter = 0;
18214         ptr = _res.data();
18215         cimg_foroff(_res,p) { *ptr = *ptr==p?counter++:_res[*ptr]; ++ptr; }
18216       }
18217       return res;
18218     }
18219 
18220     //@}
18221     //---------------------------------
18222     //
18223     //! \name Color Base Management
18224     //@{
18225     //---------------------------------
18226 
18227     //! Return a palette 'default' with 256 RGB entries.
18228     static const CImg<Tuchar>& default_LUT256() {
18229       static CImg<Tuchar> palette;
18230       if (!palette) {
18231         palette.assign(1,256,1,3);
18232         for (unsigned int index = 0, r = 16; r<256; r+=32)
18233           for (unsigned int g = 16; g<256; g+=32)
18234             for (unsigned int b = 32; b<256; b+=64) {
18235               palette(0,index,0) = (Tuchar)r;
18236               palette(0,index,1) = (Tuchar)g;
18237               palette(0,index++,2) = (Tuchar)b;
18238             }
18239       }
18240       return palette;
18241     }
18242 
18243     //! Return palette 'HSV' with 256 RGB entries.
18244     static const CImg<Tuchar>& HSV_LUT256() {
18245       static CImg<Tuchar> palette;
18246       if (!palette) {
18247         CImg<Tint> tmp(1,256,1,3,1);
18248         tmp.get_shared_channel(0).sequence(0,359);
18249         palette = tmp.HSVtoRGB();
18250       }
18251       return palette;
18252     }
18253 
18254     //! Return palette 'lines' with 256 RGB entries.
18255     static const CImg<Tuchar>& lines_LUT256() {
18256       static const unsigned char pal[] = {
18257         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,
18258         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,
18259         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,
18260         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,
18261         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,
18262         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,
18263         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,
18264         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,
18265         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,
18266         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,
18267         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,
18268         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,
18269         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,
18270         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,
18271         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,
18272         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,
18273         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,
18274         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,
18275         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,
18276         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,
18277         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,
18278         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,
18279         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,
18280         23,168,19,50,240,244,185,0,1,144,10,168,31,82,1,13 };
18281       static const CImg<Tuchar> palette(pal,1,256,1,3,false);
18282       return palette;
18283     }
18284 
18285     //! Return the palette 'hot' with 256 RGB entries.
18286     static const CImg<Tuchar>& hot_LUT256() {
18287       static CImg<Tuchar> palette;
18288       if (!palette) {
18289         palette.assign(1,4,1,3,0);
18290         palette[1] = palette[2] = palette[3] = palette[6] = palette[7] = palette[11] = 255;
18291         palette.resize(1,256,1,3,3);
18292       }
18293       return palette;
18294     }
18295 
18296     //! Return the palette 'cool' with 256 RGB entries.
18297     static const CImg<Tuchar>& cool_LUT256() {
18298       static CImg<Tuchar> palette;
18299       if (!palette) palette.assign(1,2,1,3).fill(0,255,255,0,255,255).resize(1,256,1,3,3);
18300       return palette;
18301     }
18302 
18303     //! Return palette 'jet' with 256 RGB entries.
18304     static const CImg<Tuchar>& jet_LUT256() {
18305       static CImg<Tuchar> palette;
18306       if (!palette) {
18307         palette.assign(1,4,1,3,0);
18308         palette[2] = palette[3] = palette[5] = palette[6] = palette[8] = palette[9] = 255;
18309         palette.resize(1,256,1,3,3);
18310       }
18311       return palette;
18312     }
18313 
18314     //! Return palette 'flag' with 256 RGB entries.
18315     static const CImg<Tuchar>& flag_LUT256() {
18316       static CImg<Tuchar> palette;
18317       if (!palette) {
18318         palette.assign(1,4,1,3,0);
18319         palette[0] = palette[1] = palette[5] = palette[9] = palette[10] = 255;
18320         palette.resize(1,256,1,3,0,2);
18321       }
18322       return palette;
18323     }
18324 
18325     //! Return palette 'cube' with 256 RGB entries.
18326     static const CImg<Tuchar>& cube_LUT256() {
18327       static CImg<Tuchar> palette;
18328       if (!palette) {
18329         palette.assign(1,8,1,3,0);
18330         palette[1] = palette[3] = palette[5] = palette[7] =
18331           palette[10] = palette[11] = palette[12] = palette[13] =
18332           palette[20] = palette[21] = palette[22] = palette[23] = 255;
18333         palette.resize(1,256,1,3,3);
18334       }
18335       return palette;
18336     }
18337 
18338     //! Convert color pixels from sRGB to RGB.
18339     CImg<T>& sRGBtoRGB() {
18340       cimg_for(*this,ptr,T) {
18341         const Tfloat
18342           sval = (Tfloat)*ptr,
18343           nsval = (sval<0?0:sval>255?255:sval)/255,
18344           val = (Tfloat)(nsval<=0.04045f?nsval/12.92f:std::pow((nsval+0.055f)/(1.055f),2.4f));
18345         *ptr = (T)(val*255);
18346       }
18347       return *this;
18348     }
18349 
18350     CImg<Tfloat> get_sRGBtoRGB() const {
18351       return CImg<Tfloat>(*this,false).sRGBtoRGB();
18352     }
18353 
18354     //! Convert color pixels from RGB to sRGB.
18355     CImg<T>& RGBtosRGB() {
18356       cimg_for(*this,ptr,T) {
18357         const Tfloat
18358           val = (Tfloat)*ptr,
18359           nval = (val<0?0:val>255?255:val)/255,
18360           sval = (Tfloat)(nval<=0.0031308f?nval*12.92f:1.055f*std::pow(nval,0.416667f)-0.055f);
18361         *ptr = (T)(sval*255);
18362       }
18363       return *this;
18364     }
18365 
18366     CImg<Tfloat> get_RGBtosRGB() const {
18367       return CImg<Tfloat>(*this,false).RGBtosRGB();
18368     }
18369 
18370     //! Convert color pixels from RGB to HSV.
18371     CImg<T>& RGBtoHSV() {
18372       if (_spectrum!=3)
18373         throw CImgInstanceException(_cimg_instance
18374                                     "RGBtoHSV() : Instance is not a RGB image.",
18375                                     cimg_instance);
18376 
18377       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18378       for (unsigned int N = _width*_height*_depth; N; --N) {
18379         const Tfloat
18380           R = (Tfloat)*p1,
18381           G = (Tfloat)*p2,
18382           B = (Tfloat)*p3,
18383           nR = (R<0?0:(R>255?255:R))/255,
18384           nG = (G<0?0:(G>255?255:G))/255,
18385           nB = (B<0?0:(B>255?255:B))/255,
18386           m = cimg::min(nR,nG,nB),
18387           M = cimg::max(nR,nG,nB);
18388         Tfloat H = 0, S = 0;
18389         if (M!=m) {
18390           const Tfloat
18391             f = (nR==m)?(nG-nB):((nG==m)?(nB-nR):(nR-nG)),
18392             i = (Tfloat)((nR==m)?3:((nG==m)?5:1));
18393           H = (i-f/(M-m));
18394           if (H>=6) H-=6;
18395           H*=60;
18396           S = (M-m)/M;
18397         }
18398         *(p1++) = (T)H;
18399         *(p2++) = (T)S;
18400         *(p3++) = (T)M;
18401       }
18402       return *this;
18403     }
18404 
18405     CImg<Tfloat> get_RGBtoHSV() const {
18406       return CImg<Tfloat>(*this,false).RGBtoHSV();
18407     }
18408 
18409     //! Convert color pixels from HSV to RGB.
18410     CImg<T>& HSVtoRGB() {
18411       if (_spectrum!=3)
18412         throw CImgInstanceException(_cimg_instance
18413                                     "HSVtoRGB() : Instance is not a HSV image.",
18414                                     cimg_instance);
18415 
18416       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18417       for (unsigned int N = _width*_height*_depth; N; --N) {
18418         Tfloat
18419           H = (Tfloat)*p1,
18420           S = (Tfloat)*p2,
18421           V = (Tfloat)*p3,
18422           R = 0, G = 0, B = 0;
18423         if (H==0 && S==0) R = G = B = V;
18424         else {
18425           H/=60;
18426           const int i = (int)std::floor(H);
18427           const Tfloat
18428             f = (i&1)?(H - i):(1 - H + i),
18429             m = V*(1 - S),
18430             n = V*(1 - S*f);
18431           switch (i) {
18432           case 6 :
18433           case 0 : R = V; G = n; B = m; break;
18434           case 1 : R = n; G = V; B = m; break;
18435           case 2 : R = m; G = V; B = n; break;
18436           case 3 : R = m; G = n; B = V; break;
18437           case 4 : R = n; G = m; B = V; break;
18438           case 5 : R = V; G = m; B = n; break;
18439           }
18440         }
18441         R*=255; G*=255; B*=255;
18442         *(p1++) = (T)(R<0?0:(R>255?255:R));
18443         *(p2++) = (T)(G<0?0:(G>255?255:G));
18444         *(p3++) = (T)(B<0?0:(B>255?255:B));
18445       }
18446       return *this;
18447     }
18448 
18449     CImg<Tuchar> get_HSVtoRGB() const {
18450       return CImg<Tuchar>(*this,false).HSVtoRGB();
18451     }
18452 
18453     //! Convert color pixels from RGB to HSL.
18454     CImg<T>& RGBtoHSL() {
18455       if (_spectrum!=3)
18456         throw CImgInstanceException(_cimg_instance
18457                                     "RGBtoHSL() : Instance is not a RGB image.",
18458                                     cimg_instance);
18459 
18460       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18461       for (unsigned int N = _width*_height*_depth; N; --N) {
18462         const Tfloat
18463           R = (Tfloat)*p1,
18464           G = (Tfloat)*p2,
18465           B = (Tfloat)*p3,
18466           nR = (R<0?0:(R>255?255:R))/255,
18467           nG = (G<0?0:(G>255?255:G))/255,
18468           nB = (B<0?0:(B>255?255:B))/255,
18469           m = cimg::min(nR,nG,nB),
18470           M = cimg::max(nR,nG,nB),
18471           L = (m + M)/2;
18472         Tfloat H = 0, S = 0;
18473         if (M==m) H = S = 0;
18474         else {
18475           const Tfloat
18476             f = (nR==m)?(nG-nB):((nG==m)?(nB-nR):(nR-nG)),
18477             i = (nR==m)?3.0f:((nG==m)?5.0f:1.0f);
18478           H = (i-f/(M-m));
18479           if (H>=6) H-=6;
18480           H*=60;
18481           S = (2*L<=1)?((M-m)/(M+m)):((M-m)/(2-M-m));
18482         }
18483         *(p1++) = (T)H;
18484         *(p2++) = (T)S;
18485         *(p3++) = (T)L;
18486       }
18487       return *this;
18488     }
18489 
18490     CImg<Tfloat> get_RGBtoHSL() const {
18491       return CImg< Tfloat>(*this,false).RGBtoHSL();
18492     }
18493 
18494     //! Convert color pixels from HSL to RGB.
18495     CImg<T>& HSLtoRGB() {
18496       if (_spectrum!=3)
18497         throw CImgInstanceException(_cimg_instance
18498                                     "HSLtoRGB() : Instance is not a HSL image.",
18499                                     cimg_instance);
18500 
18501       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18502       for (unsigned int N = _width*_height*_depth; N; --N) {
18503         const Tfloat
18504           H = (Tfloat)*p1,
18505           S = (Tfloat)*p2,
18506           L = (Tfloat)*p3,
18507           q = 2*L<1?L*(1+S):(L+S-L*S),
18508           p = 2*L-q,
18509           h = H/360,
18510           tr = h + 1.0f/3,
18511           tg = h,
18512           tb = h - 1.0f/3,
18513           ntr = tr<0?tr+1:(tr>1?tr-1:tr),
18514           ntg = tg<0?tg+1:(tg>1?tg-1:tg),
18515           ntb = tb<0?tb+1:(tb>1?tb-1:tb),
18516           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))),
18517           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))),
18518           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)));
18519         *(p1++) = (T)(R<0?0:(R>255?255:R));
18520         *(p2++) = (T)(G<0?0:(G>255?255:G));
18521         *(p3++) = (T)(B<0?0:(B>255?255:B));
18522       }
18523       return *this;
18524     }
18525 
18526     CImg<Tuchar> get_HSLtoRGB() const {
18527       return CImg<Tuchar>(*this,false).HSLtoRGB();
18528     }
18529 
18530     //! Convert color pixels from RGB to HSI.
18531     //! Reference: "Digital Image Processing, 2nd. edition", R. Gonzalez and R. Woods. Prentice Hall, 2002.
18532     CImg<T>& RGBtoHSI() {
18533       if (_spectrum!=3)
18534         throw CImgInstanceException(_cimg_instance
18535                                     "RGBtoHSI() : Instance is not a RGB image.",
18536                                     cimg_instance);
18537 
18538       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18539       for (unsigned int N = _width*_height*_depth; N; --N) {
18540         const Tfloat
18541           R = (Tfloat)*p1,
18542           G = (Tfloat)*p2,
18543           B = (Tfloat)*p3,
18544           nR = (R<0?0:(R>255?255:R))/255,
18545           nG = (G<0?0:(G>255?255:G))/255,
18546           nB = (B<0?0:(B>255?255:B))/255,
18547           m = cimg::min(nR,nG,nB),
18548           theta = (Tfloat)(std::acos(0.5f*((nR-nG)+(nR-nB))/std::sqrt(std::pow(nR-nG,2)+(nR-nB)*(nG-nB)))*180/cimg::PI),
18549           sum = nR + nG + nB;
18550         Tfloat H = 0, S = 0, I = 0;
18551         if (theta>0) H = (nB<=nG)?theta:360-theta;
18552         if (sum>0) S = 1 - 3/sum*m;
18553         I = sum/3;
18554         *(p1++) = (T)H;
18555         *(p2++) = (T)S;
18556         *(p3++) = (T)I;
18557       }
18558       return *this;
18559     }
18560 
18561     CImg<Tfloat> get_RGBtoHSI() const {
18562       return CImg<Tfloat>(*this,false).RGBtoHSI();
18563     }
18564 
18565     //! Convert color pixels from HSI to RGB.
18566     CImg<T>& HSItoRGB() {
18567       if (_spectrum!=3)
18568         throw CImgInstanceException(_cimg_instance
18569                                     "HSItoRGB() : Instance is not a HSI image.",
18570                                     cimg_instance);
18571 
18572       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18573       for (unsigned int N = _width*_height*_depth; N; --N) {
18574         Tfloat
18575           H = (Tfloat)*p1,
18576           S = (Tfloat)*p2,
18577           I = (Tfloat)*p3,
18578           a = I*(1-S),
18579           R = 0, G = 0, B = 0;
18580         if (H<120) {
18581           B = a;
18582           R = (Tfloat)(I*(1+S*std::cos(H*cimg::PI/180)/std::cos((60-H)*cimg::PI/180)));
18583           G = 3*I-(R+B);
18584         } else if (H<240) {
18585           H-=120;
18586           R = a;
18587           G = (Tfloat)(I*(1+S*std::cos(H*cimg::PI/180)/std::cos((60-H)*cimg::PI/180)));
18588           B = 3*I-(R+G);
18589         } else {
18590           H-=240;
18591           G = a;
18592           B = (Tfloat)(I*(1+S*std::cos(H*cimg::PI/180)/std::cos((60-H)*cimg::PI/180)));
18593           R = 3*I-(G+B);
18594         }
18595         R*=255; G*=255; B*=255;
18596         *(p1++) = (T)(R<0?0:(R>255?255:R));
18597         *(p2++) = (T)(G<0?0:(G>255?255:G));
18598         *(p3++) = (T)(B<0?0:(B>255?255:B));
18599       }
18600       return *this;
18601     }
18602 
18603     CImg<Tfloat> get_HSItoRGB() const {
18604       return CImg< Tuchar>(*this,false).HSItoRGB();
18605     }
18606 
18607     //! Convert color pixels from RGB to YCbCr.
18608     CImg<T>& RGBtoYCbCr() {
18609       if (_spectrum!=3)
18610         throw CImgInstanceException(_cimg_instance
18611                                     "RGBtoYCbCr() : Instance is not a RGB image.",
18612                                     cimg_instance);
18613 
18614       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18615       for (unsigned int N = _width*_height*_depth; N; --N) {
18616         const Tfloat
18617           R = (Tfloat)*p1,
18618           G = (Tfloat)*p2,
18619           B = (Tfloat)*p3,
18620           Y = (66*R + 129*G + 25*B + 128)/256 + 16,
18621           Cb = (-38*R - 74*G + 112*B + 128)/256 + 128,
18622           Cr = (112*R - 94*G - 18*B + 128)/256 + 128;
18623         *(p1++) = (T)(Y<0?0:(Y>255?255:Y));
18624         *(p2++) = (T)(Cb<0?0:(Cb>255?255:Cb));
18625         *(p3++) = (T)(Cr<0?0:(Cr>255?255:Cr));
18626       }
18627       return *this;
18628     }
18629 
18630     CImg<Tuchar> get_RGBtoYCbCr() const {
18631       return CImg<Tuchar>(*this,false).RGBtoYCbCr();
18632     }
18633 
18634     //! Convert color pixels from RGB to YCbCr.
18635     CImg<T>& YCbCrtoRGB() {
18636       if (_spectrum!=3)
18637         throw CImgInstanceException(_cimg_instance
18638                                     "YCbCrtoRGB() : Instance is not a YCbCr image.",
18639                                     cimg_instance);
18640 
18641       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18642       for (unsigned int N = _width*_height*_depth; N; --N) {
18643         const Tfloat
18644           Y = (Tfloat)*p1 - 16,
18645           Cb = (Tfloat)*p2 - 128,
18646           Cr = (Tfloat)*p3 - 128,
18647           R = (298*Y + 409*Cr + 128)/256,
18648           G = (298*Y - 100*Cb - 208*Cr + 128)/256,
18649           B = (298*Y + 516*Cb + 128)/256;
18650         *(p1++) = (T)(R<0?0:(R>255?255:R));
18651         *(p2++) = (T)(G<0?0:(G>255?255:G));
18652         *(p3++) = (T)(B<0?0:(B>255?255:B));
18653       }
18654       return *this;
18655     }
18656 
18657     CImg<Tuchar> get_YCbCrtoRGB() const {
18658       return CImg<Tuchar>(*this,false).YCbCrtoRGB();
18659     }
18660 
18661     //! Convert color pixels from RGB to YUV.
18662     CImg<T>& RGBtoYUV() {
18663       if (_spectrum!=3)
18664         throw CImgInstanceException(_cimg_instance
18665                                     "RGBtoYUV() : Instance is not a RGB image.",
18666                                     cimg_instance);
18667 
18668       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18669       for (unsigned int N = _width*_height*_depth; N; --N) {
18670         const Tfloat
18671           R = (Tfloat)*p1/255,
18672           G = (Tfloat)*p2/255,
18673           B = (Tfloat)*p3/255,
18674           Y = 0.299f*R + 0.587f*G + 0.114f*B;
18675         *(p1++) = (T)Y;
18676         *(p2++) = (T)(0.492f*(B-Y));
18677         *(p3++) = (T)(0.877*(R-Y));
18678       }
18679       return *this;
18680     }
18681 
18682     CImg<Tfloat> get_RGBtoYUV() const {
18683       return CImg<Tfloat>(*this,false).RGBtoYUV();
18684     }
18685 
18686     //! Convert color pixels from YUV to RGB.
18687     CImg<T>& YUVtoRGB() {
18688       if (_spectrum!=3)
18689         throw CImgInstanceException(_cimg_instance
18690                                     "YUVtoRGB() : Instance is not a YUV image.",
18691                                     cimg_instance);
18692 
18693       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18694       for (unsigned int N = _width*_height*_depth; N; --N) {
18695         const Tfloat
18696           Y = (Tfloat)*p1,
18697           U = (Tfloat)*p2,
18698           V = (Tfloat)*p3,
18699           R = (Y + 1.140f*V)*255,
18700           G = (Y - 0.395f*U - 0.581f*V)*255,
18701           B = (Y + 2.032f*U)*255;
18702         *(p1++) = (T)(R<0?0:(R>255?255:R));
18703         *(p2++) = (T)(G<0?0:(G>255?255:G));
18704         *(p3++) = (T)(B<0?0:(B>255?255:B));
18705       }
18706       return *this;
18707     }
18708 
18709     CImg<Tuchar> get_YUVtoRGB() const {
18710       return CImg< Tuchar>(*this,false).YUVtoRGB();
18711     }
18712 
18713     //! Convert color pixels from RGB to CMY.
18714     CImg<T>& RGBtoCMY() {
18715       if (_spectrum!=3)
18716         throw CImgInstanceException(_cimg_instance
18717                                     "RGBtoCMY() : Instance is not a RGB image.",
18718                                     cimg_instance);
18719 
18720       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18721       for (unsigned int N = _width*_height*_depth; N; --N) {
18722         const Tfloat
18723           R = (Tfloat)*p1,
18724           G = (Tfloat)*p2,
18725           B = (Tfloat)*p3,
18726           C = 255 - R,
18727           M = 255 - G,
18728           Y = 255 - B;
18729         *(p1++) = (T)(C<0?0:(C>255?255:C));
18730         *(p2++) = (T)(M<0?0:(M>255?255:M));
18731         *(p3++) = (T)(Y<0?0:(Y>255?255:Y));
18732       }
18733       return *this;
18734     }
18735 
18736     CImg<Tuchar> get_RGBtoCMY() const {
18737       return CImg<Tfloat>(*this,false).RGBtoCMY();
18738     }
18739 
18740     //! Convert CMY pixels of a color image into the RGB color space.
18741     CImg<T>& CMYtoRGB() {
18742       if (_spectrum!=3)
18743         throw CImgInstanceException(_cimg_instance
18744                                     "CMYtoRGB() : Instance is not a CMY image.",
18745                                     cimg_instance);
18746 
18747       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18748       for (unsigned int N = _width*_height*_depth; N; --N) {
18749         const Tfloat
18750           C = (Tfloat)*p1,
18751           M = (Tfloat)*p2,
18752           Y = (Tfloat)*p3,
18753           R = 255 - C,
18754           G = 255 - M,
18755           B = 255 - Y;
18756         *(p1++) = (T)(R<0?0:(R>255?255:R));
18757         *(p2++) = (T)(G<0?0:(G>255?255:G));
18758         *(p3++) = (T)(B<0?0:(B>255?255:B));
18759       }
18760       return *this;
18761     }
18762 
18763     CImg<Tuchar> get_CMYtoRGB() const {
18764       return CImg<Tuchar>(*this,false).CMYtoRGB();
18765     }
18766 
18767     //! Convert color pixels from CMY to CMYK.
18768     CImg<T>& CMYtoCMYK() {
18769       return get_CMYtoCMYK().move_to(*this);
18770     }
18771 
18772     CImg<Tuchar> get_CMYtoCMYK() const {
18773       if (_spectrum!=3)
18774         throw CImgInstanceException(_cimg_instance
18775                                     "CMYtoCMYK() : Instance is not a CMY image.",
18776                                     cimg_instance);
18777 
18778       CImg<Tfloat> res(_width,_height,_depth,4);
18779       const T *ps1 = data(0,0,0,0), *ps2 = data(0,0,0,1), *ps3 = data(0,0,0,2);
18780       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);
18781       for (unsigned int N = _width*_height*_depth; N; --N) {
18782         Tfloat
18783           C = (Tfloat)*(ps1++),
18784           M = (Tfloat)*(ps2++),
18785           Y = (Tfloat)*(ps3++),
18786           K = cimg::min(C,M,Y);
18787         if (K>=255) C = M = Y = 0;
18788         else { const Tfloat K1 = 255 - K; C = 255*(C - K)/K1; M = 255*(M - K)/K1; Y = 255*(Y - K)/K1; }
18789         *(pd1++) = (Tfloat)(C<0?0:(C>255?255:C));
18790         *(pd2++) = (Tfloat)(M<0?0:(M>255?255:M));
18791         *(pd3++) = (Tfloat)(Y<0?0:(Y>255?255:Y));
18792         *(pd4++) = (Tfloat)(K<0?0:(K>255?255:K));
18793       }
18794       return res;
18795     }
18796 
18797     //! Convert CMYK pixels of a color image into the CMY color space.
18798     CImg<T>& CMYKtoCMY() {
18799       return get_CMYKtoCMY().move_to(*this);
18800     }
18801 
18802     CImg<Tfloat> get_CMYKtoCMY() const {
18803       if (_spectrum!=4)
18804         throw CImgInstanceException(_cimg_instance
18805                                     "CMYKtoCMY() : Instance is not a CMYK image.",
18806                                     cimg_instance);
18807 
18808       CImg<Tfloat> res(_width,_height,_depth,3);
18809       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);
18810       Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2);
18811       for (unsigned int N = _width*_height*_depth; N; --N) {
18812         const Tfloat
18813           C = (Tfloat)*(ps1++),
18814           M = (Tfloat)*(ps2++),
18815           Y = (Tfloat)*(ps3++),
18816           K = (Tfloat)*(ps4++),
18817           K1 = 1 - K/255,
18818           nC = C*K1 + K,
18819           nM = M*K1 + K,
18820           nY = Y*K1 + K;
18821         *(pd1++) = (Tfloat)(nC<0?0:(nC>255?255:nC));
18822         *(pd2++) = (Tfloat)(nM<0?0:(nM>255?255:nM));
18823         *(pd3++) = (Tfloat)(nY<0?0:(nY>255?255:nY));
18824       }
18825       return res;
18826     }
18827 
18828     //! Convert color pixels from RGB to XYZ_709.
18829     CImg<T>& RGBtoXYZ() {
18830       if (_spectrum!=3)
18831         throw CImgInstanceException(_cimg_instance
18832                                     "RGBtoXYZ() : Instance is not a RGB image.",
18833                                     cimg_instance);
18834 
18835       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18836       for (unsigned int N = _width*_height*_depth; N; --N) {
18837         const Tfloat
18838           R = (Tfloat)*p1/255,
18839           G = (Tfloat)*p2/255,
18840           B = (Tfloat)*p3/255;
18841         *(p1++) = (T)(0.412453f*R + 0.357580f*G + 0.180423f*B);
18842         *(p2++) = (T)(0.212671f*R + 0.715160f*G + 0.072169f*B);
18843         *(p3++) = (T)(0.019334f*R + 0.119193f*G + 0.950227f*B);
18844       }
18845       return *this;
18846     }
18847 
18848     CImg<Tfloat> get_RGBtoXYZ() const {
18849       return CImg<Tfloat>(*this,false).RGBtoXYZ();
18850     }
18851 
18852     //! Convert XYZ_709 pixels of a color image into the RGB color space.
18853     CImg<T>& XYZtoRGB() {
18854       if (_spectrum!=3)
18855         throw CImgInstanceException(_cimg_instance
18856                                     "XYZtoRGB() : Instance is not a XYZ image.",
18857                                     cimg_instance);
18858 
18859       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18860       for (unsigned int N = _width*_height*_depth; N; --N) {
18861         const Tfloat
18862           X = (Tfloat)*p1*255,
18863           Y = (Tfloat)*p2*255,
18864           Z = (Tfloat)*p3*255,
18865           R = 3.240479f*X  - 1.537150f*Y - 0.498535f*Z,
18866           G = -0.969256f*X + 1.875992f*Y + 0.041556f*Z,
18867           B = 0.055648f*X  - 0.204043f*Y + 1.057311f*Z;
18868         *(p1++) = (T)(R<0?0:(R>255?255:R));
18869         *(p2++) = (T)(G<0?0:(G>255?255:G));
18870         *(p3++) = (T)(B<0?0:(B>255?255:B));
18871       }
18872       return *this;
18873     }
18874 
18875     CImg<Tuchar> get_XYZtoRGB() const {
18876       return CImg<Tuchar>(*this,false).XYZtoRGB();
18877     }
18878 
18879     //! Convert XYZ_709 pixels of a color image into the (L*,a*,b*) color space.
18880     CImg<T>& XYZtoLab() {
18881 #define _cimg_Labf(x) ((x)>=0.008856f?(std::pow(x,(Tfloat)1/3)):(7.787f*(x)+16.0f/116))
18882 
18883       if (_spectrum!=3)
18884         throw CImgInstanceException(_cimg_instance
18885                                     "XYZtoLab() : Instance is not a XYZ image.",
18886                                     cimg_instance);
18887 
18888       const Tfloat
18889         Xn = (Tfloat)(0.412453f + 0.357580f + 0.180423f),
18890         Yn = (Tfloat)(0.212671f + 0.715160f + 0.072169f),
18891         Zn = (Tfloat)(0.019334f + 0.119193f + 0.950227f);
18892       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18893       for (unsigned int N = _width*_height*_depth; N; --N) {
18894         const Tfloat
18895           X = (Tfloat)*p1,
18896           Y = (Tfloat)*p2,
18897           Z = (Tfloat)*p3,
18898           XXn = X/Xn, YYn = Y/Yn, ZZn = Z/Zn,
18899           fX = (Tfloat)_cimg_Labf(XXn),
18900           fY = (Tfloat)_cimg_Labf(YYn),
18901           fZ = (Tfloat)_cimg_Labf(ZZn);
18902         *(p1++) = (T)(116*fY - 16);
18903         *(p2++) = (T)(500*(fX - fY));
18904         *(p3++) = (T)(200*(fY - fZ));
18905       }
18906       return *this;
18907     }
18908 
18909     CImg<Tfloat> get_XYZtoLab() const {
18910       return CImg<Tfloat>(*this,false).XYZtoLab();
18911     }
18912 
18913     //! Convert Lab pixels of a color image into the XYZ color space.
18914     CImg<T>& LabtoXYZ() {
18915 #define _cimg_Labfi(x) ((x)>=0.206893f?((x)*(x)*(x)):(((x)-16.0f/116)/7.787f))
18916 
18917       if (_spectrum!=3)
18918         throw CImgInstanceException(_cimg_instance
18919                                     "LabtoXYZ() : Instance is not a Lab image.",
18920                                     cimg_instance);
18921 
18922       const Tfloat
18923         Xn = (Tfloat)(0.412453f + 0.357580f + 0.180423f),
18924         Yn = (Tfloat)(0.212671f + 0.715160f + 0.072169f),
18925         Zn = (Tfloat)(0.019334f + 0.119193f + 0.950227f);
18926       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18927       for (unsigned int N = _width*_height*_depth; N; --N) {
18928         const Tfloat
18929           L = (Tfloat)*p1,
18930           a = (Tfloat)*p2,
18931           b = (Tfloat)*p3,
18932           cY = (L + 16)/116,
18933           Y = (Tfloat)(Yn*_cimg_Labfi(cY)),
18934           pY = (Tfloat)std::pow(Y/Yn,(Tfloat)1/3),
18935           cX = a/500 + pY,
18936           X = Xn*cX*cX*cX,
18937           cZ = pY - b/200,
18938           Z = Zn*cZ*cZ*cZ;
18939         *(p1++) = (T)(X);
18940         *(p2++) = (T)(Y);
18941         *(p3++) = (T)(Z);
18942       }
18943       return *this;
18944     }
18945 
18946     CImg<Tfloat> get_LabtoXYZ() const {
18947       return CImg<Tfloat>(*this,false).LabtoXYZ();
18948     }
18949 
18950     //! Convert XYZ_709 pixels of a color image into the xyY color space.
18951     CImg<T>& XYZtoxyY() {
18952       if (_spectrum!=3)
18953         throw CImgInstanceException(_cimg_instance
18954                                     "XYZtoxyY() : Instance is not a XYZ image.",
18955                                     cimg_instance);
18956 
18957       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18958       for (unsigned int N = _width*_height*_depth; N; --N) {
18959         const Tfloat
18960           X = (Tfloat)*p1,
18961           Y = (Tfloat)*p2,
18962           Z = (Tfloat)*p3,
18963           sum = (X+Y+Z),
18964           nsum = sum>0?sum:1;
18965         *(p1++) = (T)(X/nsum);
18966         *(p2++) = (T)(Y/nsum);
18967         *(p3++) = (T)Y;
18968       }
18969       return *this;
18970     }
18971 
18972     CImg<Tfloat> get_XYZtoxyY() const {
18973       return CImg<Tfloat>(*this,false).XYZtoxyY();
18974     }
18975 
18976     //! Convert xyY pixels of a color image into the XYZ_709 color space.
18977     CImg<T>& xyYtoXYZ() {
18978       if (_spectrum!=3)
18979         throw CImgInstanceException(_cimg_instance
18980                                     "xyYtoXYZ() : Instance is not a xyY image.",
18981                                     cimg_instance);
18982 
18983       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18984       for (unsigned int N = _width*_height*_depth; N; --N) {
18985         const Tfloat
18986          px = (Tfloat)*p1,
18987          py = (Tfloat)*p2,
18988          Y = (Tfloat)*p3,
18989          ny = py>0?py:1;
18990         *(p1++) = (T)(px*Y/ny);
18991         *(p2++) = (T)Y;
18992         *(p3++) = (T)((1-px-py)*Y/ny);
18993       }
18994       return *this;
18995     }
18996 
18997     CImg<Tfloat> get_xyYtoXYZ() const {
18998       return CImg<Tfloat>(*this,false).xyYtoXYZ();
18999     }
19000 
19001     //! Convert a RGB image to a Lab one.
19002     CImg<T>& RGBtoLab() {
19003       return RGBtoXYZ().XYZtoLab();
19004     }
19005 
19006     CImg<Tfloat> get_RGBtoLab() const {
19007       return CImg<Tfloat>(*this,false).RGBtoLab();
19008     }
19009 
19010     //! Convert a Lab image to a RGB one.
19011     CImg<T>& LabtoRGB() {
19012       return LabtoXYZ().XYZtoRGB();
19013     }
19014 
19015     CImg<Tuchar> get_LabtoRGB() const {
19016       return CImg<Tuchar>(*this,false).LabtoRGB();
19017     }
19018 
19019     //! Convert a RGB image to a xyY one.
19020     CImg<T>& RGBtoxyY() {
19021       return RGBtoXYZ().XYZtoxyY();
19022     }
19023 
19024     CImg<Tfloat> get_RGBtoxyY() const {
19025       return CImg<Tfloat>(*this,false).RGBtoxyY();
19026     }
19027 
19028     //! Convert a xyY image to a RGB one.
19029     CImg<T>& xyYtoRGB() {
19030       return xyYtoXYZ().XYZtoRGB();
19031     }
19032 
19033     CImg<Tuchar> get_xyYtoRGB() const {
19034       return CImg<Tuchar>(*this,false).xyYtoRGB();
19035     }
19036 
19037     //! Convert a RGB image to a CMYK one.
19038     CImg<T>& RGBtoCMYK() {
19039       return RGBtoCMY().CMYtoCMYK();
19040     }
19041 
19042     CImg<Tfloat> get_RGBtoCMYK() const {
19043       return CImg<Tfloat>(*this,false).RGBtoCMYK();
19044     }
19045 
19046     //! Convert a CMYK image to a RGB one.
19047     CImg<T>& CMYKtoRGB() {
19048       return CMYKtoCMY().CMYtoRGB();
19049     }
19050 
19051     CImg<Tuchar> get_CMYKtoRGB() const {
19052       return CImg<Tuchar>(*this,false).CMYKtoRGB();
19053     }
19054 
19055     //! Convert a RGB image to a Bayer-coded representation.
19056     /**
19057        \note First (upper-left) pixel if the red component of the pixel color.
19058     **/
19059     CImg<T>& RGBtoBayer() {
19060       return get_RGBtoBayer().move_to(*this);
19061     }
19062 
19063     CImg<T> get_RGBtoBayer() const {
19064       if (_spectrum!=3)
19065         throw CImgInstanceException(_cimg_instance
19066                                     "RGBtoBayer() : Instance is not a RGB image.",
19067                                     cimg_instance);
19068 
19069       CImg<T> res(_width,_height,_depth,1);
19070       const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
19071       T *ptrd = res._data;
19072       cimg_forXYZ(*this,x,y,z) {
19073         if (y%2) {
19074           if (x%2) *(ptrd++) = *ptr_b;
19075           else *(ptrd++) = *ptr_g;
19076         } else {
19077           if (x%2) *(ptrd++) = *ptr_g;
19078           else *(ptrd++) = *ptr_r;
19079         }
19080         ++ptr_r; ++ptr_g; ++ptr_b;
19081       }
19082       return res;
19083     }
19084 
19085     //! Convert a Bayer-coded image to a RGB color image.
19086     CImg<T>& BayertoRGB(const unsigned int interpolation_type=3) {
19087       return get_BayertoRGB(interpolation_type).move_to(*this);
19088     }
19089 
19090     CImg<Tuchar> get_BayertoRGB(const unsigned int interpolation_type=3) const {
19091       if (_spectrum!=1)
19092         throw CImgInstanceException(_cimg_instance
19093                                     "BayertoRGB() : Instance is not a Bayer image.",
19094                                     cimg_instance);
19095 
19096       CImg<Tuchar> res(_width,_height,_depth,3);
19097       CImg_3x3(I,T);
19098       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);
19099       switch (interpolation_type) {
19100       case 3 : { // Edge-directed
19101         CImg_3x3(R,T);
19102         CImg_3x3(G,T);
19103         CImg_3x3(B,T);
19104         cimg_forXYZ(*this,x,y,z) {
19105           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;
19106           cimg_get3x3(*this,x,y,z,0,I,T);
19107           if (y%2) {
19108             if (x%2) {
19109               const Tfloat alpha = cimg::sqr((Tfloat)Inc - Ipc), beta = cimg::sqr((Tfloat)Icn - Icp), cx = 1/(1+alpha), cy = 1/(1+beta);
19110               *ptr_g = (Tuchar)((cx*(Inc+Ipc) + cy*(Icn+Icp))/(2*(cx+cy)));
19111             } else *ptr_g = (Tuchar)Icc;
19112           } else {
19113             if (x%2) *ptr_g = (Tuchar)Icc;
19114             else {
19115               const Tfloat alpha = cimg::sqr((Tfloat)Inc - Ipc), beta = cimg::sqr((Tfloat)Icn - Icp), cx = 1/(1+alpha), cy = 1/(1+beta);
19116               *ptr_g = (Tuchar)((cx*(Inc+Ipc) + cy*(Icn+Icp))/(2*(cx+cy)));
19117             }
19118           }
19119           ++ptr_g;
19120         }
19121         cimg_forXYZ(*this,x,y,z) {
19122           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;
19123           cimg_get3x3(*this,x,y,z,0,I,T);
19124           cimg_get3x3(res,x,y,z,1,G,T);
19125           if (y%2) {
19126             if (x%2) *ptr_b = (Tuchar)Icc;
19127             else { *ptr_r = (Tuchar)((Icn+Icp)/2); *ptr_b = (Tuchar)((Inc+Ipc)/2); }
19128           } else {
19129             if (x%2) { *ptr_r = (Tuchar)((Inc+Ipc)/2); *ptr_b = (Tuchar)((Icn+Icp)/2); }
19130             else *ptr_r = (Tuchar)Icc;
19131           }
19132           ++ptr_r; ++ptr_b;
19133         }
19134         ptr_r = res.data(0,0,0,0);
19135         ptr_g = res.data(0,0,0,1);
19136         ptr_b = res.data(0,0,0,2);
19137         cimg_forXYZ(*this,x,y,z) {
19138           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;
19139           cimg_get3x3(res,x,y,z,0,R,T);
19140           cimg_get3x3(res,x,y,z,1,G,T);
19141           cimg_get3x3(res,x,y,z,2,B,T);
19142           if (y%2) {
19143             if (x%2) {
19144               const float alpha = (float)cimg::sqr(Rnc-Rpc), beta = (float)cimg::sqr(Rcn-Rcp), cx = 1/(1+alpha), cy = 1/(1+beta);
19145               *ptr_r = (Tuchar)((cx*(Rnc+Rpc) + cy*(Rcn+Rcp))/(2*(cx+cy)));
19146             }
19147           } else {
19148             if (!(x%2)) {
19149               const float alpha = (float)cimg::sqr(Bnc-Bpc), beta = (float)cimg::sqr(Bcn-Bcp), cx = 1/(1+alpha), cy = 1/(1+beta);
19150               *ptr_b = (Tuchar)((cx*(Bnc+Bpc) + cy*(Bcn+Bcp))/(2*(cx+cy)));
19151             }
19152           }
19153           ++ptr_r; ++ptr_g; ++ptr_b;
19154         }
19155       } break;
19156       case 2 : { // Linear interpolation
19157         cimg_forXYZ(*this,x,y,z) {
19158           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;
19159           cimg_get3x3(*this,x,y,z,0,I,T);
19160           if (y%2) {
19161             if (x%2) { *ptr_r = (Tuchar)((Ipp+Inn+Ipn+Inp)/4); *ptr_g = (Tuchar)((Inc+Ipc+Icn+Icp)/4); *ptr_b = (Tuchar)Icc; }
19162             else { *ptr_r = (Tuchar)((Icp+Icn)/2); *ptr_g = (Tuchar)Icc; *ptr_b = (Tuchar)((Inc+Ipc)/2); }
19163           } else {
19164             if (x%2) { *ptr_r = (Tuchar)((Ipc+Inc)/2); *ptr_g = (Tuchar)Icc; *ptr_b = (Tuchar)((Icn+Icp)/2); }
19165             else { *ptr_r = (Tuchar)Icc; *ptr_g = (Tuchar)((Inc+Ipc+Icn+Icp)/4); *ptr_b = (Tuchar)((Ipp+Inn+Ipn+Inp)/4); }
19166           }
19167           ++ptr_r; ++ptr_g; ++ptr_b;
19168         }
19169       } break;
19170       case 1 : { // Nearest neighbor interpolation
19171         cimg_forXYZ(*this,x,y,z) {
19172           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;
19173           cimg_get3x3(*this,x,y,z,0,I,T);
19174           if (y%2) {
19175             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; }
19176             else { *ptr_r = (Tuchar)cimg::min(Icn,Icp); *ptr_g = (Tuchar)Icc; *ptr_b = (Tuchar)cimg::min(Inc,Ipc); }
19177           } else {
19178             if (x%2) { *ptr_r = (Tuchar)cimg::min(Inc,Ipc); *ptr_g = (Tuchar)Icc; *ptr_b = (Tuchar)cimg::min(Icn,Icp); }
19179             else { *ptr_r = (Tuchar)Icc; *ptr_g = (Tuchar)cimg::min(Inc,Ipc,Icn,Icp); *ptr_b = (Tuchar)cimg::min(Ipp,Inn,Ipn,Inp); }
19180           }
19181           ++ptr_r; ++ptr_g; ++ptr_b;
19182         }
19183       } break;
19184       default : { // 0-filling interpolation
19185         const T *ptrs = _data;
19186         res.fill(0);
19187         cimg_forXYZ(*this,x,y,z) {
19188           const T val = *(ptrs++);
19189           if (y%2) { if (x%2) *ptr_b = val; else *ptr_g = val; } else { if (x%2) *ptr_g = val; else *ptr_r = val; }
19190           ++ptr_r; ++ptr_g; ++ptr_b;
19191         }
19192       }
19193       }
19194       return res;
19195     }
19196 
19197     //@}
19198     //------------------------------------------
19199     //
19200     //! \name Geometric / Spatial Manipulation
19201     //@{
19202     //------------------------------------------
19203 
19204     static float _cimg_lanczos(const float x) {
19205       if (x<=-2 || x>=2) return 0;
19206       const float a = (float)cimg::PI*x, b = 0.5f*a;
19207       return (float)(x?std::sin(a)*std::sin(b)/(a*b):1);
19208     }
19209 
19210     //! Resize an image.
19211     /**
19212        \param size_x Number of columns (new size along the X-axis).
19213        \param size_y Number of rows (new size along the Y-axis).
19214        \param size_z Number of slices (new size along the Z-axis).
19215        \param size_c Number of vector-channels (new size along the C-axis).
19216        \param interpolation_type Method of interpolation :
19217        - -1 = no interpolation : raw memory resizing.
19218        - 0 = no interpolation : additional space is filled according to \p border_condition.
19219        - 1 = nearest-neighbor interpolation.
19220        - 2 = moving average interpolation.
19221        - 3 = linear interpolation.
19222        - 4 = grid interpolation.
19223        - 5 = bicubic interpolation.
19224        - 6 = lanczos interpolation.
19225        \param border_conditions Border condition type.
19226        \param centering_x Set centering type (only if \p interpolation_type=0).
19227        \param centering_y Set centering type (only if \p interpolation_type=0).
19228        \param centering_z Set centering type (only if \p interpolation_type=0).
19229        \param centering_c Set centering type (only if \p interpolation_type=0).
19230        \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100).
19231     **/
19232     CImg<T>& resize(const int size_x, const int size_y=-100,
19233                     const int size_z=-100, const int size_c=-100,
19234                     const int interpolation_type=1, const unsigned int border_conditions=0,
19235                     const float centering_x = 0, const float centering_y = 0,
19236                     const float centering_z = 0, const float centering_c = 0) {
19237       if (!size_x || !size_y || !size_z || !size_c) return assign();
19238       const unsigned int
19239         _sx = (unsigned int)(size_x<0?-size_x*_width/100:size_x),
19240         _sy = (unsigned int)(size_y<0?-size_y*_height/100:size_y),
19241         _sz = (unsigned int)(size_z<0?-size_z*_depth/100:size_z),
19242         _sc = (unsigned int)(size_c<0?-size_c*_spectrum/100:size_c),
19243         sx = _sx?_sx:1, sy = _sy?_sy:1, sz = _sz?_sz:1, sc = _sc?_sc:1;
19244       if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return *this;
19245       if (is_empty()) return assign(sx,sy,sz,sc,0);
19246       if (interpolation_type==-1 && sx*sy*sz*sc==size()) {
19247         _width = sx; _height = sy; _depth = sz; _spectrum = sc;
19248         return *this;
19249       }
19250       return get_resize(sx,sy,sz,sc,interpolation_type,border_conditions,centering_x,centering_y,centering_z,centering_c).move_to(*this);
19251     }
19252 
19253     CImg<T> get_resize(const int size_x, const int size_y = -100,
19254                        const int size_z = -100, const int size_c = -100,
19255                        const int interpolation_type=1, const unsigned int border_conditions=0,
19256                        const float centering_x = 0, const float centering_y = 0,
19257                        const float centering_z = 0, const float centering_c = 0) const {
19258       if (centering_x<0 || centering_x>1 || centering_y<0 || centering_y>1 ||
19259           centering_z<0 || centering_z>1 || centering_c<0 || centering_c>1)
19260         throw CImgArgumentException(_cimg_instance
19261                                     "resize() : Specified centering arguments (%g,%g,%g,%g) are outside range [0,1].",
19262                                     cimg_instance,
19263                                     centering_x,centering_y,centering_z,centering_c);
19264 
19265       if (!size_x || !size_y || !size_z || !size_c) return CImg<T>();
19266       const unsigned int
19267         _sx = (unsigned int)(size_x<0?-size_x*_width/100:size_x),
19268         _sy = (unsigned int)(size_y<0?-size_y*_height/100:size_y),
19269         _sz = (unsigned int)(size_z<0?-size_z*_depth/100:size_z),
19270         _sc = (unsigned int)(size_c<0?-size_c*_spectrum/100:size_c),
19271         sx = _sx?_sx:1, sy = _sy?_sy:1, sz = _sz?_sz:1, sc = _sc?_sc:1;
19272       if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return +*this;
19273       if (is_empty()) return CImg<T>(sx,sy,sz,sc,0);
19274 
19275       CImg<T> res;
19276       switch (interpolation_type) {
19277 
19278         // Raw resizing.
19279         //
19280       case -1 :
19281         std::memcpy(res.assign(sx,sy,sz,sc,0)._data,_data,sizeof(T)*cimg::min(size(),sx*sy*sz*sc));
19282         break;
19283 
19284         // No interpolation.
19285         //
19286       case 0 : {
19287         const int
19288           xc = (int)(centering_x*((int)sx - width())),
19289           yc = (int)(centering_y*((int)sy - height())),
19290           zc = (int)(centering_z*((int)sz - depth())),
19291           cc = (int)(centering_c*((int)sc - spectrum()));
19292 
19293         switch (border_conditions) {
19294         case 2 : { // Cyclic borders.
19295           res.assign(sx,sy,sz,sc);
19296           const int
19297             x0 = ((int)xc%width()) - width(),
19298             y0 = ((int)yc%height()) - height(),
19299             z0 = ((int)zc%depth()) - depth(),
19300             c0 = ((int)cc%spectrum()) - spectrum();
19301           for (int c = c0; c<(int)sc; c+=spectrum())
19302             for (int z = z0; z<(int)sz; z+=depth())
19303               for (int y = y0; y<(int)sy; y+=height())
19304                 for (int x = x0; x<(int)sx; x+=width())
19305                   res.draw_image(x,y,z,c,*this);
19306         } break;
19307         case 1 : { // Neumann borders.
19308           res.assign(sx,sy,sz,sc).draw_image(xc,yc,zc,cc,*this);
19309           CImg<T> sprite;
19310           if (xc>0) {  // X-backward
19311             res.get_crop(xc,yc,zc,cc,xc,yc+height()-1,zc+depth()-1,cc+spectrum()-1).move_to(sprite);
19312             for (int x = xc-1; x>=0; --x) res.draw_image(x,yc,zc,cc,sprite);
19313           }
19314           if (xc+width()<(int)sx) { // X-forward
19315             res.get_crop(xc+width()-1,yc,zc,cc,xc+width()-1,yc+height()-1,zc+depth()-1,cc+spectrum()-1).move_to(sprite);
19316             for (int x = xc+width(); x<(int)sx; ++x) res.draw_image(x,yc,zc,cc,sprite);
19317           }
19318           if (yc>0) {  // Y-backward
19319             res.get_crop(0,yc,zc,cc,sx-1,yc,zc+depth()-1,cc+spectrum()-1).move_to(sprite);
19320             for (int y = yc-1; y>=0; --y) res.draw_image(0,y,zc,cc,sprite);
19321           }
19322           if (yc+height()<(int)sy) { // Y-forward
19323             res.get_crop(0,yc+height()-1,zc,cc,sx-1,yc+height()-1,zc+depth()-1,cc+spectrum()-1).move_to(sprite);
19324             for (int y = yc+height(); y<(int)sy; ++y) res.draw_image(0,y,zc,cc,sprite);
19325           }
19326           if (zc>0) {  // Z-backward
19327             res.get_crop(0,0,zc,cc,sx-1,sy-1,zc,cc+spectrum()-1).move_to(sprite);
19328             for (int z = zc-1; z>=0; --z) res.draw_image(0,0,z,cc,sprite);
19329           }
19330           if (zc+depth()<(int)sz) { // Z-forward
19331             res.get_crop(0,0,zc+depth()-1,cc,sx-1,sy-1,zc+depth()-1,cc+spectrum()-1).move_to(sprite);
19332             for (int z = zc+depth(); z<(int)sz; ++z) res.draw_image(0,0,z,cc,sprite);
19333           }
19334           if (cc>0) {  // C-backward
19335             res.get_crop(0,0,0,cc,sx-1,sy-1,sz-1,cc).move_to(sprite);
19336             for (int c = cc-1; c>=0; --c) res.draw_image(0,0,0,c,sprite);
19337           }
19338           if (cc+spectrum()<(int)sc) { // C-forward
19339             res.get_crop(0,0,0,cc+spectrum()-1,sx-1,sy-1,sz-1,cc+spectrum()-1).move_to(sprite);
19340             for (int c = cc+spectrum(); c<(int)sc; ++c) res.draw_image(0,0,0,c,sprite);
19341           }
19342         } break;
19343         default : // Dirichlet borders.
19344           res.assign(sx,sy,sz,sc,0).draw_image(xc,yc,zc,cc,*this);
19345         }
19346         break;
19347       } break;
19348 
19349         // Nearest neighbor interpolation.
19350         //
19351       case 1 : {
19352         res.assign(sx,sy,sz,sc);
19353         CImg<ulongT> off_x(sx), off_y(sy+1), off_z(sz+1), off_c(sc+1);
19354         unsigned long *poff_x, *poff_y, *poff_z, *poff_c, curr, old;
19355         const unsigned long
19356           wh = (unsigned long)_width*_height,
19357           whd = (unsigned long)_width*_height*_depth,
19358           sxy = (unsigned long)sx*sy,
19359           sxyz = (unsigned long)sx*sy*sz;
19360         if (sx==_width) off_x.fill(1);
19361         else {
19362           poff_x = off_x._data; curr = 0;
19363           cimg_forX(res,x) { old = curr; curr = ((x+1LU)*_width/sx); *(poff_x++) = curr - old; }
19364         }
19365         if (sy==_height) off_y.fill(_width);
19366         else {
19367           poff_y = off_y._data; curr = 0;
19368           cimg_forY(res,y) { old = curr; curr = ((y+1LU)*_height/sy); *(poff_y++) = _width*(curr - old); } *poff_y = 0;
19369         }
19370         if (sz==_depth) off_z.fill(wh);
19371         else {
19372           poff_z = off_z._data; curr = 0;
19373           cimg_forZ(res,z) { old = curr; curr = ((z+1LU)*_depth/sz); *(poff_z++) = wh*(curr - old); } *poff_z = 0;
19374         }
19375         if (sc==_spectrum) off_c.fill(whd);
19376         else {
19377           poff_c = off_c._data; curr = 0;
19378           cimg_forC(res,c) { old = curr; curr = ((c+1LU)*_spectrum/sc); *(poff_c++) = whd*(curr - old); } *poff_c = 0;
19379         }
19380 
19381         T *ptrd = res._data;
19382         const T* ptrv = _data;
19383         poff_c = off_c._data;
19384         for (unsigned int c = 0; c<sc; ) {
19385           const T *ptrz = ptrv;
19386           poff_z = off_z._data;
19387           for (unsigned int z = 0; z<sz; ) {
19388             const T *ptry = ptrz;
19389             poff_y = off_y._data;
19390             for (unsigned int y = 0; y<sy; ) {
19391               const T *ptrx = ptry;
19392               poff_x = off_x._data;
19393               cimg_forX(res,x) { *(ptrd++) = *ptrx; ptrx+=*(poff_x++); }
19394               ++y;
19395               unsigned long dy = *(poff_y++);
19396               for (;!dy && y<dy; std::memcpy(ptrd,ptrd - sx,sizeof(T)*sx), ++y, ptrd+=sx, dy = *(poff_y++)) {}
19397               ptry+=dy;
19398             }
19399             ++z;
19400             unsigned long dz = *(poff_z++);
19401             for (;!dz && z<dz; std::memcpy(ptrd,ptrd-sxy,sizeof(T)*sxy), ++z, ptrd+=sxy, dz = *(poff_z++)) {}
19402             ptrz+=dz;
19403           }
19404           ++c;
19405           unsigned long dc = *(poff_c++);
19406           for (;!dc && c<dc; std::memcpy(ptrd,ptrd-sxyz,sizeof(T)*sxyz), ++c, ptrd+=sxyz, dc = *(poff_c++)) {}
19407           ptrv+=dc;
19408         }
19409       } break;
19410 
19411         // Moving average.
19412         //
19413       case 2 : {
19414         bool instance_first = true;
19415         if (sx!=_width) {
19416           CImg<Tfloat> tmp(sx,_height,_depth,_spectrum,0);
19417           for (unsigned int a = _width*sx, b = _width, c = sx, s = 0, t = 0; a; ) {
19418             const unsigned int d = cimg::min(b,c);
19419             a-=d; b-=d; c-=d;
19420             cimg_forYZC(tmp,y,z,v) tmp(t,y,z,v)+=(Tfloat)(*this)(s,y,z,v)*d;
19421             if (!b) { cimg_forYZC(tmp,y,z,v) tmp(t,y,z,v)/=_width; ++t; b = _width; }
19422             if (!c) { ++s; c = sx; }
19423           }
19424           tmp.move_to(res);
19425           instance_first = false;
19426         }
19427         if (sy!=_height) {
19428           CImg<Tfloat> tmp(sx,sy,_depth,_spectrum,0);
19429           for (unsigned int a = _height*sy, b = _height, c = sy, s = 0, t = 0; a; ) {
19430             const unsigned int d = cimg::min(b,c);
19431             a-=d; b-=d; c-=d;
19432             if (instance_first) cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)(*this)(x,s,z,v)*d;
19433             else cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)res(x,s,z,v)*d;
19434             if (!b) { cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)/=_height; ++t; b = _height; }
19435             if (!c) { ++s; c = sy; }
19436           }
19437           tmp.move_to(res);
19438           instance_first = false;
19439         }
19440         if (sz!=_depth) {
19441           CImg<Tfloat> tmp(sx,sy,sz,_spectrum,0);
19442           for (unsigned int a = _depth*sz, b = _depth, c = sz, s = 0, t = 0; a; ) {
19443             const unsigned int d = cimg::min(b,c);
19444             a-=d; b-=d; c-=d;
19445             if (instance_first) cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)(*this)(x,y,s,v)*d;
19446             else cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)res(x,y,s,v)*d;
19447             if (!b) { cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)/=_depth; ++t; b = _depth; }
19448             if (!c) { ++s; c = sz; }
19449           }
19450           tmp.move_to(res);
19451           instance_first = false;
19452         }
19453         if (sc!=_spectrum) {
19454           CImg<Tfloat> tmp(sx,sy,sz,sc,0);
19455           for (unsigned int a = _spectrum*sc, b = _spectrum, c = sc, s = 0, t = 0; a; ) {
19456             const unsigned int d = cimg::min(b,c);
19457             a-=d; b-=d; c-=d;
19458             if (instance_first) cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(Tfloat)(*this)(x,y,z,s)*d;
19459             else cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(Tfloat)res(x,y,z,s)*d;
19460             if (!b) { cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)/=_spectrum; ++t; b = _spectrum; }
19461             if (!c) { ++s; c = sc; }
19462           }
19463           tmp.move_to(res);
19464           instance_first = false;
19465         }
19466       } break;
19467 
19468         // Linear interpolation.
19469         //
19470       case 3 : {
19471         CImg<uintT> off(cimg::max(sx,sy,sz,sc));
19472         CImg<floatT> foff(off._width);
19473         unsigned int *poff;
19474         float *pfoff, old, curr;
19475         CImg<T> resx, resy, resz, resc;
19476         T *ptrd;
19477 
19478         if (sx!=_width) {
19479           if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
19480           else {
19481             const float fx = (!border_conditions && sx>_width)?(sx>1?(_width-1.0f)/(sx-1):0):(float)_width/sx;
19482             resx.assign(sx,_height,_depth,_spectrum);
19483             curr = old = 0; poff = off._data; pfoff = foff._data;
19484             cimg_forX(resx,x) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fx; *(poff++) = (unsigned int)curr - (unsigned int)old; }
19485             ptrd = resx._data;
19486             const T *ptrs0 = _data;
19487             cimg_forYZC(resx,y,z,c) {
19488               poff = off._data; pfoff = foff._data;
19489               const T *ptrs = ptrs0, *const ptrsmax = ptrs0 + (_width-1);
19490               cimg_forX(resx,x) {
19491                 const float alpha = *(pfoff++);
19492                 const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs+1):val1;
19493                 *(ptrd++) = (T)((1-alpha)*val1 + alpha*val2);
19494                 ptrs+=*(poff++);
19495               }
19496               ptrs0+=_width;
19497             }
19498           }
19499         } else resx.assign(*this,true);
19500 
19501         if (sy!=_height) {
19502           if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
19503           else {
19504             const float fy = (!border_conditions && sy>_height)?(sy>1?(_height-1.0f)/(sy-1):0):(float)_height/sy;
19505             resy.assign(sx,sy,_depth,_spectrum);
19506             curr = old = 0; poff = off._data; pfoff = foff._data;
19507             cimg_forY(resy,y) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fy; *(poff++) = sx*((unsigned int)curr-(unsigned int)old); }
19508             cimg_forXZC(resy,x,z,c) {
19509               ptrd = resy.data(x,0,z,c);
19510               const T *ptrs = resx.data(x,0,z,c), *const ptrsmax = ptrs + (_height-1)*sx;
19511               poff = off._data; pfoff = foff._data;
19512               cimg_forY(resy,y) {
19513                 const float alpha = *(pfoff++);
19514                 const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs+sx):val1;
19515                 *ptrd = (T)((1-alpha)*val1 + alpha*val2);
19516                 ptrd+=sx;
19517                 ptrs+=*(poff++);
19518               }
19519             }
19520           }
19521           resx.assign();
19522         } else resy.assign(resx,true);
19523 
19524         if (sz!=_depth) {
19525           if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
19526           else {
19527             const float fz = (!border_conditions && sz>_depth)?(sz>1?(_depth-1.0f)/(sz-1):0):(float)_depth/sz;
19528             const unsigned int sxy = sx*sy;
19529             resz.assign(sx,sy,sz,_spectrum);
19530             curr = old = 0; poff = off._data; pfoff = foff._data;
19531             cimg_forZ(resz,z) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fz; *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); }
19532             cimg_forXYC(resz,x,y,c) {
19533               ptrd = resz.data(x,y,0,c);
19534               const T *ptrs = resy.data(x,y,0,c), *const ptrsmax = ptrs + (_depth-1)*sxy;
19535               poff = off._data; pfoff = foff._data;
19536               cimg_forZ(resz,z) {
19537                 const float alpha = *(pfoff++);
19538                 const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs+sxy):val1;
19539                 *ptrd = (T)((1-alpha)*val1 + alpha*val2);
19540                 ptrd+=sxy;
19541                 ptrs+=*(poff++);
19542               }
19543             }
19544           }
19545           resy.assign();
19546         } else resz.assign(resy,true);
19547 
19548         if (sc!=_spectrum) {
19549           if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
19550           else {
19551             const float fc = (!border_conditions && sc>_spectrum)?(sc>1?(_spectrum-1.0f)/(sc-1):0):(float)_spectrum/sc;
19552             const unsigned int sxyz = sx*sy*sz;
19553             resc.assign(sx,sy,sz,sc);
19554             curr = old = 0; poff = off._data; pfoff = foff._data;
19555             cimg_forC(resc,c) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fc; *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); }
19556             cimg_forXYZ(resc,x,y,z) {
19557               ptrd = resc.data(x,y,z,0);
19558               const T *ptrs = resz.data(x,y,z,0), *const ptrsmax = ptrs + (_spectrum-1)*sxyz;
19559               poff = off._data; pfoff = foff._data;
19560               cimg_forC(resc,c) {
19561                 const float alpha = *(pfoff++);
19562                 const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs+sxyz):val1;
19563                 *ptrd = (T)((1-alpha)*val1 + alpha*val2);
19564                 ptrd+=sxyz;
19565                 ptrs+=*(poff++);
19566               }
19567             }
19568           }
19569           resz.assign();
19570         } else resc.assign(resz,true);
19571         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
19572       } break;
19573 
19574         // Grid interpolation.
19575         //
19576       case 4 : {
19577         CImg<T> resx, resy, resz, resc;
19578         const unsigned int sxy = sx*sy, sxyz = sx*sy*sz;
19579         if (sx!=_width) {
19580           if (sx<_width) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
19581           else {
19582             resx.assign(sx,_height,_depth,_spectrum,0);
19583             const T *ptrs = _data;
19584             T *ptrd = resx._data + (int)(centering_x*(sx-1)/_width);
19585             cimg_forYZC(*this,y,z,c) {
19586               cimg_forX(*this,x) ptrd[x*sx/_width] = *(ptrs++);
19587               ptrd+=sx;
19588             }
19589           }
19590         } else resx.assign(*this,true);
19591 
19592         if (sy!=_height) {
19593           if (sy<_height) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
19594           else {
19595             resy.assign(sx,sy,_depth,_spectrum,0);
19596             const T *ptrs = resx._data;
19597             T *ptrd = resy._data + (int)(centering_y*(sy-1)/_height)*sx;
19598             cimg_forZC(*this,z,c) {
19599               cimg_forY(*this,y) { std::memcpy(ptrd + (y*sy/_height)*sx,ptrs,sizeof(T)*sx); ptrs+=sx; }
19600               ptrd+=sxy;
19601             }
19602           }
19603           resx.assign();
19604         } else resy.assign(resx,true);
19605 
19606         if (sz!=_depth) {
19607           if (sz<_depth) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
19608           else {
19609             resz.assign(sx,sy,sz,_spectrum,0);
19610             const T *ptrs = resy._data;
19611             T *ptrd = resz._data + (int)(centering_z*(sz-1)/_depth)*sxy;
19612             cimg_forC(*this,c) {
19613               cimg_forZ(*this,z) { std::memcpy(ptrd + (z*sz/_depth)*sxy,ptrs,sizeof(T)*sxy); ptrs+=sxy; }
19614               ptrd+=sxyz;
19615             }
19616           }
19617           resy.assign();
19618         } else resz.assign(resy,true);
19619 
19620         if (sc!=_spectrum) {
19621           if (sc<_spectrum) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
19622           else {
19623             resc.assign(sx,sy,sz,sc,0);
19624             const T *ptrs = resz._data;
19625             T *ptrd = resc._data + (int)(centering_c*(sc-1)/_spectrum)*sxyz;
19626             cimg_forC(*this,c) { std::memcpy(ptrd + (c*sc/_spectrum)*sxyz,ptrs,sizeof(T)*sxyz); ptrs+=sxyz; }
19627           }
19628           resz.assign();
19629         } else resc.assign(resz,true);
19630 
19631         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
19632       } break;
19633 
19634         // Bicubic interpolation.
19635         //
19636       case 5 : {
19637         const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
19638         CImg<uintT> off(cimg::max(sx,sy,sz,sc));
19639         CImg<floatT> foff(off._width);
19640         unsigned int *poff;
19641         float *pfoff, old, curr;
19642         CImg<T> resx, resy, resz, resc;
19643         T *ptrd;
19644 
19645         if (sx!=_width) {
19646           if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
19647           else {
19648             const float fx = (!border_conditions && sx>_width)?(sx>1?(_width-1.0f)/(sx-1):0):(float)_width/sx;
19649             resx.assign(sx,_height,_depth,_spectrum);
19650             curr = old = 0; poff = off._data; pfoff = foff._data;
19651             cimg_forX(resx,x) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fx; *(poff++) = (unsigned int)curr - (unsigned int)old; }
19652             ptrd = resx._data;
19653             const T *ptrs0 = _data;
19654             cimg_forYZC(resx,y,z,c) {
19655               poff = off._data; pfoff = foff._data;
19656               const T *ptrs = ptrs0, *const ptrsmax = ptrs0 + (_width-2);
19657               cimg_forX(resx,x) {
19658                 const float t = *(pfoff++);
19659                 const Tfloat
19660                   val1 = (Tfloat)*ptrs,
19661                   val0 = ptrs>ptrs0?(Tfloat)*(ptrs-1):val1,
19662                   val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+1):val1,
19663                   val3 = ptrs<ptrsmax?(Tfloat)*(ptrs+2):val2,
19664                   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));
19665                 *(ptrd++) = (T)(val<vmin?vmin:val>vmax?vmax:val);
19666                 ptrs+=*(poff++);
19667               }
19668               ptrs0+=_width;
19669             }
19670           }
19671         } else resx.assign(*this,true);
19672 
19673         if (sy!=_height) {
19674           if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
19675           else {
19676             const float fy = (!border_conditions && sy>_height)?(sy>1?(_height-1.0f)/(sy-1):0):(float)_height/sy;
19677             resy.assign(sx,sy,_depth,_spectrum);
19678             curr = old = 0; poff = off._data; pfoff = foff._data;
19679             cimg_forY(resy,y) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fy; *(poff++) = sx*((unsigned int)curr-(unsigned int)old); }
19680             cimg_forXZC(resy,x,z,c) {
19681               ptrd = resy.data(x,0,z,c);
19682               const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs0 + (_height-2)*sx;
19683               poff = off._data; pfoff = foff._data;
19684               cimg_forY(resy,y) {
19685                 const float t = *(pfoff++);
19686                 const Tfloat
19687                   val1 = (Tfloat)*ptrs,
19688                   val0 = ptrs>ptrs0?(Tfloat)*(ptrs-sx):val1,
19689                   val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sx):val1,
19690                   val3 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sx):val2,
19691                   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));
19692                 *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
19693                 ptrd+=sx;
19694                 ptrs+=*(poff++);
19695               }
19696             }
19697           }
19698           resx.assign();
19699         } else resy.assign(resx,true);
19700 
19701         if (sz!=_depth) {
19702           if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
19703           else {
19704             const float fz = (!border_conditions && sz>_depth)?(sz>1?(_depth-1.0f)/(sz-1):0):(float)_depth/sz;
19705             const unsigned int sxy = sx*sy;
19706             resz.assign(sx,sy,sz,_spectrum);
19707             curr = old = 0; poff = off._data; pfoff = foff._data;
19708             cimg_forZ(resz,z) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fz; *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); }
19709             cimg_forXYC(resz,x,y,c) {
19710               ptrd = resz.data(x,y,0,c);
19711               const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmax = ptrs0 + (_depth-2)*sxy;
19712               poff = off._data; pfoff = foff._data;
19713               cimg_forZ(resz,z) {
19714                 const float t = *(pfoff++);
19715                 const Tfloat
19716                   val1 = (Tfloat)*ptrs,
19717                   val0 = ptrs>ptrs0?(Tfloat)*(ptrs-sxy):val1,
19718                   val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxy):val1,
19719                   val3 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sxy):val2,
19720                   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));
19721                 *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
19722                 ptrd+=sxy;
19723                 ptrs+=*(poff++);
19724               }
19725             }
19726           }
19727           resy.assign();
19728         } else resz.assign(resy,true);
19729 
19730         if (sc!=_spectrum) {
19731           if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
19732           else {
19733             const float fc = (!border_conditions && sc>_spectrum)?(sc>1?(_spectrum-1.0f)/(sc-1):0):(float)_spectrum/sc;
19734             const unsigned int sxyz = sx*sy*sz;
19735             resc.assign(sx,sy,sz,sc);
19736             curr = old = 0; poff = off._data; pfoff = foff._data;
19737             cimg_forC(resc,c) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fc; *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); }
19738             cimg_forXYZ(resc,x,y,z) {
19739               ptrd = resc.data(x,y,z,0);
19740               const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmax = ptrs + (_spectrum-2)*sxyz;
19741               poff = off._data; pfoff = foff._data;
19742               cimg_forC(resc,c) {
19743                 const float t = *(pfoff++);
19744                 const Tfloat
19745                   val1 = (Tfloat)*ptrs,
19746                   val0 = ptrs>ptrs0?(Tfloat)*(ptrs-sxyz):val1,
19747                   val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxyz):val1,
19748                   val3 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sxyz):val2,
19749                   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));
19750                 *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
19751                 ptrd+=sxyz;
19752                 ptrs+=*(poff++);
19753               }
19754             }
19755           }
19756           resz.assign();
19757         } else resc.assign(resz,true);
19758 
19759         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
19760       } break;
19761 
19762         // Lanczos interpolation.
19763         //
19764       case 6 : {
19765         const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
19766         CImg<uintT> off(cimg::max(sx,sy,sz,sc));
19767         CImg<floatT> foff(off._width);
19768         unsigned int *poff;
19769         float *pfoff, old, curr;
19770         CImg<T> resx, resy, resz, resc;
19771         T *ptrd;
19772 
19773         if (sx!=_width) {
19774           if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
19775           else {
19776             const float fx = (!border_conditions && sx>_width)?(sx>1?(_width-1.0f)/(sx-1):0):(float)_width/sx;
19777             resx.assign(sx,_height,_depth,_spectrum);
19778             curr = old = 0; poff = off._data; pfoff = foff._data;
19779             cimg_forX(resx,x) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fx; *(poff++) = (unsigned int)curr - (unsigned int)old; }
19780             ptrd = resx._data;
19781             const T *ptrs0 = _data;
19782             cimg_forYZC(resx,y,z,c) {
19783               poff = off._data; pfoff = foff._data;
19784               const T *ptrs = ptrs0, *const ptrsmin = ptrs0 + 1, *const ptrsmax = ptrs0 + (_width-2);
19785               cimg_forX(resx,x) {
19786                 const float
19787                   t = *(pfoff++),
19788                   w0 = _cimg_lanczos(t+2),
19789                   w1 = _cimg_lanczos(t+1),
19790                   w2 = _cimg_lanczos(t),
19791                   w3 = _cimg_lanczos(t-1),
19792                   w4 = _cimg_lanczos(t-2);
19793                 const Tfloat
19794                   val2 = (Tfloat)*ptrs,
19795                   val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-1):val2,
19796                   val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2):val1,
19797                   val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+1):val2,
19798                   val4 = ptrs<ptrsmax?(Tfloat)*(ptrs+2):val3,
19799                   val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
19800                 *(ptrd++) = (T)(val<vmin?vmin:val>vmax?vmax:val);
19801                 ptrs+=*(poff++);
19802               }
19803               ptrs0+=_width;
19804             }
19805           }
19806         } else resx.assign(*this,true);
19807 
19808         if (sy!=_height) {
19809           if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
19810           else {
19811             const float fy = (!border_conditions && sy>_height)?(sy>1?(_height-1.0f)/(sy-1):0):(float)_height/sy;
19812             resy.assign(sx,sy,_depth,_spectrum);
19813             curr = old = 0; poff = off._data; pfoff = foff._data;
19814             cimg_forY(resy,y) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fy; *(poff++) = sx*((unsigned int)curr-(unsigned int)old); }
19815             cimg_forXZC(resy,x,z,c) {
19816               ptrd = resy.data(x,0,z,c);
19817               const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sx, *const ptrsmax = ptrs0 + (_height-2)*sx;
19818               poff = off._data; pfoff = foff._data;
19819               cimg_forY(resy,y) {
19820                 const float
19821                   t = *(pfoff++),
19822                   w0 = _cimg_lanczos(t+2),
19823                   w1 = _cimg_lanczos(t+1),
19824                   w2 = _cimg_lanczos(t),
19825                   w3 = _cimg_lanczos(t-1),
19826                   w4 = _cimg_lanczos(t-2);
19827                 const Tfloat
19828                   val2 = (Tfloat)*ptrs,
19829                   val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-sx):val2,
19830                   val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2*sx):val1,
19831                   val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sx):val2,
19832                   val4 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sx):val3,
19833                   val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
19834                 *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
19835                 ptrd+=sx;
19836                 ptrs+=*(poff++);
19837               }
19838             }
19839           }
19840           resx.assign();
19841         } else resy.assign(resx,true);
19842 
19843         if (sz!=_depth) {
19844           if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
19845           else {
19846             const float fz = (!border_conditions && sz>_depth)?(sz>1?(_depth-1.0f)/(sz-1):0):(float)_depth/sz;
19847             const unsigned int sxy = sx*sy;
19848             resz.assign(sx,sy,sz,_spectrum);
19849             curr = old = 0; poff = off._data; pfoff = foff._data;
19850             cimg_forZ(resz,z) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fz; *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); }
19851             cimg_forXYC(resz,x,y,c) {
19852               ptrd = resz.data(x,y,0,c);
19853               const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxy, *const ptrsmax = ptrs0 + (_depth-2)*sxy;
19854               poff = off._data; pfoff = foff._data;
19855               cimg_forZ(resz,z) {
19856                 const float
19857                   t = *(pfoff++),
19858                   w0 = _cimg_lanczos(t+2),
19859                   w1 = _cimg_lanczos(t+1),
19860                   w2 = _cimg_lanczos(t),
19861                   w3 = _cimg_lanczos(t-1),
19862                   w4 = _cimg_lanczos(t-2);
19863                 const Tfloat
19864                   val2 = (Tfloat)*ptrs,
19865                   val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-sxy):val2,
19866                   val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2*sxy):val1,
19867                   val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxy):val2,
19868                   val4 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sxy):val3,
19869                   val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
19870                 *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
19871                 ptrd+=sxy;
19872                 ptrs+=*(poff++);
19873               }
19874             }
19875           }
19876           resy.assign();
19877         } else resz.assign(resy,true);
19878 
19879         if (sc!=_spectrum) {
19880           if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
19881           else {
19882             const float fc = (!border_conditions && sc>_spectrum)?(sc>1?(_spectrum-1.0f)/(sc-1):0):(float)_spectrum/sc;
19883             const unsigned int sxyz = sx*sy*sz;
19884             resc.assign(sx,sy,sz,sc);
19885             curr = old = 0; poff = off._data; pfoff = foff._data;
19886             cimg_forC(resc,c) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fc; *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); }
19887             cimg_forXYZ(resc,x,y,z) {
19888               ptrd = resc.data(x,y,z,0);
19889               const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxyz, *const ptrsmax = ptrs + (_spectrum-2)*sxyz;
19890               poff = off._data; pfoff = foff._data;
19891               cimg_forC(resc,c) {
19892                 const float
19893                   t = *(pfoff++),
19894                   w0 = _cimg_lanczos(t+2),
19895                   w1 = _cimg_lanczos(t+1),
19896                   w2 = _cimg_lanczos(t),
19897                   w3 = _cimg_lanczos(t-1),
19898                   w4 = _cimg_lanczos(t-2);
19899                 const Tfloat
19900                   val2 = (Tfloat)*ptrs,
19901                   val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-sxyz):val2,
19902                   val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2*sxyz):val1,
19903                   val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxyz):val2,
19904                   val4 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sxyz):val3,
19905                   val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
19906                 *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
19907                 ptrd+=sxyz;
19908                 ptrs+=*(poff++);
19909               }
19910             }
19911           }
19912           resz.assign();
19913         } else resc.assign(resz,true);
19914 
19915         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
19916       } break;
19917 
19918         // Unknow interpolation.
19919         //
19920       default :
19921         throw CImgArgumentException(_cimg_instance
19922                                     "resize() : Invalid specified interpolation %d "
19923                                     "(should be { -1=raw | 0=none | 1=nearest | 2=average | 3=linear | 4=grid | 5=bicubic | 6=lanczos }).",
19924                                     cimg_instance,
19925                                     interpolation_type);
19926       }
19927       return res;
19928     }
19929 
19930     //! Resize an image.
19931     template<typename t>
19932     CImg<T>& resize(const CImg<t>& src,
19933                     const int interpolation_type=1, const unsigned int border_conditions=0,
19934                     const float centering_x = 0, const float centering_y = 0,
19935                     const float centering_z = 0, const float centering_c = 0) {
19936       return resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,border_conditions,
19937                     centering_x,centering_y,centering_z,centering_c);
19938     }
19939 
19940     template<typename t>
19941     CImg<T> get_resize(const CImg<t>& src,
19942                        const int interpolation_type=1, const unsigned int border_conditions=0,
19943                        const float centering_x = 0, const float centering_y = 0,
19944                        const float centering_z = 0, const float centering_c = 0) const {
19945       return get_resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,border_conditions,
19946                         centering_x,centering_y,centering_z,centering_c);
19947     }
19948 
19949     //! Resize an image.
19950     CImg<T>& resize(const CImgDisplay& disp,
19951                     const int interpolation_type=1, const unsigned int border_conditions=0,
19952                     const float centering_x = 0, const float centering_y = 0,
19953                     const float centering_z = 0, const float centering_c = 0) {
19954       return resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,border_conditions,
19955                     centering_x,centering_y,centering_z,centering_c);
19956     }
19957 
19958     CImg<T> get_resize(const CImgDisplay& disp,
19959                        const int interpolation_type=1, const unsigned int border_conditions=0,
19960                        const float centering_x = 0, const float centering_y = 0,
19961                        const float centering_z = 0, const float centering_c = 0) const {
19962       return get_resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,border_conditions,
19963                         centering_x,centering_y,centering_z,centering_c);
19964     }
19965 
19966     //! Half-resize an image, using a special optimized filter.
19967     CImg<T>& resize_halfXY() {
19968       return get_resize_halfXY().move_to(*this);
19969     }
19970 
19971     CImg<T> get_resize_halfXY() const {
19972       if (is_empty()) return *this;
19973       const Tfloat mask[9] = { 0.07842776544f, 0.1231940459f, 0.07842776544f,
19974                               0.1231940459f,  0.1935127547f, 0.1231940459f,
19975                               0.07842776544f, 0.1231940459f, 0.07842776544f };
19976       T I[9] = { 0 };
19977       CImg<T> res(_width/2,_height/2,_depth,_spectrum);
19978       T *ptrd = res._data;
19979       cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,T)
19980         if (x%2 && y%2) *(ptrd++) = (T)
19981                           (I[0]*mask[0] + I[1]*mask[1] + I[2]*mask[2] +
19982                            I[3]*mask[3] + I[4]*mask[4] + I[5]*mask[5] +
19983                            I[6]*mask[6] + I[7]*mask[7] + I[8]*mask[8]);
19984       return res;
19985     }
19986 
19987     //! Upscale an image by a factor 2x.
19988     /**
19989        Use anisotropic upscaling algorithm described at
19990        http://scale2x.sourceforge.net/algorithm.html
19991     **/
19992     CImg<T>& resize_doubleXY() {
19993       return get_resize_doubleXY().move_to(*this);
19994     }
19995 
19996     CImg<T> get_resize_doubleXY() const {
19997 #define _cimg_gs2x_for3(bound,i) \
19998  for (int i = 0, _p1##i = 0, \
19999       _n1##i = 1>=(bound)?(int)(bound)-1:1; \
20000       _n1##i<(int)(bound) || i==--_n1##i; \
20001       _p1##i = i++, ++_n1##i, ptrd1+=(res)._width, ptrd2+=(res)._width)
20002 
20003 #define _cimg_gs2x_for3x3(img,x,y,z,c,I,T) \
20004   _cimg_gs2x_for3((img)._height,y) for (int x = 0, \
20005    _p1##x = 0, \
20006    _n1##x = (int)( \
20007    (I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
20008    (I[3] = I[4] = (T)(img)(0,y,z,c)), \
20009    (I[7] = (T)(img)(0,_n1##y,z,c)),     \
20010    1>=(img)._width?(img).width()-1:1); \
20011    (_n1##x<(img).width() && ( \
20012    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
20013    (I[5] = (T)(img)(_n1##x,y,z,c)), \
20014    (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
20015    x==--_n1##x; \
20016    I[1] = I[2], \
20017    I[3] = I[4], I[4] = I[5], \
20018    I[7] = I[8], \
20019    _p1##x = x++, ++_n1##x)
20020 
20021       if (is_empty()) return *this;
20022       CImg<T> res(_width<<1,_height<<1,_depth,_spectrum);
20023       CImg_3x3(I,T);
20024       cimg_forZC(*this,z,c) {
20025         T
20026           *ptrd1 = res.data(0,0,z,c),
20027           *ptrd2 = ptrd1 + res._width;
20028         _cimg_gs2x_for3x3(*this,x,y,z,c,I,T) {
20029           if (Icp!=Icn && Ipc!=Inc) {
20030             *(ptrd1++) = Ipc==Icp?Ipc:Icc;
20031             *(ptrd1++) = Icp==Inc?Inc:Icc;
20032             *(ptrd2++) = Ipc==Icn?Ipc:Icc;
20033             *(ptrd2++) = Icn==Inc?Inc:Icc;
20034           } else { *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc; }
20035         }
20036       }
20037       return res;
20038     }
20039 
20040     //! Upscale an image by a factor 3x.
20041     /**
20042        Use anisotropic upscaling algorithm described at
20043        http://scale2x.sourceforge.net/algorithm.html
20044     **/
20045     CImg<T>& resize_tripleXY() {
20046       return get_resize_tripleXY().move_to(*this);
20047     }
20048 
20049     CImg<T> get_resize_tripleXY() const {
20050 #define _cimg_gs3x_for3(bound,i) \
20051  for (int i = 0, _p1##i = 0, \
20052       _n1##i = 1>=(bound)?(int)(bound)-1:1; \
20053       _n1##i<(int)(bound) || i==--_n1##i; \
20054       _p1##i = i++, ++_n1##i, ptrd1+=2*(res)._width, ptrd2+=2*(res)._width, ptrd3+=2*(res)._width)
20055 
20056 #define _cimg_gs3x_for3x3(img,x,y,z,c,I,T) \
20057   _cimg_gs3x_for3((img)._height,y) for (int x = 0, \
20058    _p1##x = 0, \
20059    _n1##x = (int)( \
20060    (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
20061    (I[3] = I[4] = (T)(img)(0,y,z,c)), \
20062    (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)),      \
20063    1>=(img)._width?(img).width()-1:1); \
20064    (_n1##x<(img).width() && ( \
20065    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
20066    (I[5] = (T)(img)(_n1##x,y,z,c)), \
20067    (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
20068    x==--_n1##x; \
20069    I[0] = I[1], I[1] = I[2], \
20070    I[3] = I[4], I[4] = I[5], \
20071    I[6] = I[7], I[7] = I[8], \
20072    _p1##x = x++, ++_n1##x)
20073 
20074       if (is_empty()) return *this;
20075       CImg<T> res(3*_width,3*_height,_depth,_spectrum);
20076       CImg_3x3(I,T);
20077       cimg_forZC(*this,z,c) {
20078         T
20079           *ptrd1 = res.data(0,0,z,c),
20080           *ptrd2 = ptrd1 + res._width,
20081           *ptrd3 = ptrd2 + res._width;
20082         _cimg_gs3x_for3x3(*this,x,y,z,c,I,T) {
20083           if (Icp != Icn && Ipc != Inc) {
20084             *(ptrd1++) = Ipc==Icp?Ipc:Icc;
20085             *(ptrd1++) = (Ipc==Icp && Icc!=Inp) || (Icp==Inc && Icc!=Ipp)?Icp:Icc;
20086             *(ptrd1++) = Icp==Inc?Inc:Icc;
20087             *(ptrd2++) = (Ipc==Icp && Icc!=Ipn) || (Ipc==Icn && Icc!=Ipp)?Ipc:Icc;
20088             *(ptrd2++) = Icc;
20089             *(ptrd2++) = (Icp==Inc && Icc!=Inn) || (Icn==Inc && Icc!=Inp)?Inc:Icc;
20090             *(ptrd3++) = Ipc==Icn?Ipc:Icc;
20091             *(ptrd3++) = (Ipc==Icn && Icc!=Inn) || (Icn==Inc && Icc!=Ipn)?Icn:Icc;
20092             *(ptrd3++) = Icn==Inc?Inc:Icc;
20093           } else {
20094             *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd1++) = Icc;
20095             *(ptrd2++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc;
20096             *(ptrd3++) = Icc; *(ptrd3++) = Icc; *(ptrd3++) = Icc;
20097           }
20098         }
20099       }
20100       return res;
20101     }
20102 
20103     //! Mirror an image along the specified axis.
20104     CImg<T>& mirror(const char axis) {
20105       if (is_empty()) return *this;
20106       T *pf, *pb, *buf = 0;
20107       switch (cimg::uncase(axis)) {
20108       case 'x' : {
20109         pf = _data; pb = data(_width-1);
20110         const unsigned int width2 = _width/2;
20111         for (unsigned int yzv = 0; yzv<_height*_depth*_spectrum; ++yzv) {
20112           for (unsigned int x = 0; x<width2; ++x) { const T val = *pf; *(pf++) = *pb; *(pb--) = val; }
20113           pf+=_width - width2;
20114           pb+=_width + width2;
20115         }
20116       } break;
20117       case 'y' : {
20118         buf = new T[_width];
20119         pf = _data; pb = data(0,_height-1);
20120         const unsigned int height2 = _height/2;
20121         for (unsigned int zv = 0; zv<_depth*_spectrum; ++zv) {
20122           for (unsigned int y = 0; y<height2; ++y) {
20123             std::memcpy(buf,pf,_width*sizeof(T));
20124             std::memcpy(pf,pb,_width*sizeof(T));
20125             std::memcpy(pb,buf,_width*sizeof(T));
20126             pf+=_width;
20127             pb-=_width;
20128           }
20129           pf+=_width*(_height - height2);
20130           pb+=_width*(_height + height2);
20131         }
20132       } break;
20133       case 'z' : {
20134         buf = new T[_width*_height];
20135         pf = _data; pb = data(0,0,_depth-1);
20136         const unsigned int depth2 = _depth/2;
20137         cimg_forC(*this,c) {
20138           for (unsigned int z = 0; z<depth2; ++z) {
20139             std::memcpy(buf,pf,_width*_height*sizeof(T));
20140             std::memcpy(pf,pb,_width*_height*sizeof(T));
20141             std::memcpy(pb,buf,_width*_height*sizeof(T));
20142             pf+=_width*_height;
20143             pb-=_width*_height;
20144           }
20145           pf+=_width*_height*(_depth - depth2);
20146           pb+=_width*_height*(_depth + depth2);
20147         }
20148       } break;
20149       default : {
20150         buf = new T[_width*_height*_depth];
20151         pf = _data; pb = data(0,0,0,_spectrum-1);
20152         const unsigned int _spectrum2 = _spectrum/2;
20153         for (unsigned int v = 0; v<_spectrum2; ++v) {
20154           std::memcpy(buf,pf,_width*_height*_depth*sizeof(T));
20155           std::memcpy(pf,pb,_width*_height*_depth*sizeof(T));
20156           std::memcpy(pb,buf,_width*_height*_depth*sizeof(T));
20157           pf+=_width*_height*_depth;
20158           pb-=_width*_height*_depth;
20159         }
20160       }
20161       }
20162       delete[] buf;
20163       return *this;
20164     }
20165 
20166     CImg<T> get_mirror(const char axis) const {
20167       return (+*this).mirror(axis);
20168     }
20169 
20170     //! Shift the image.
20171     /**
20172        \param deltax Amount of displacement along the X-axis.
20173        \param deltay Amount of displacement along the Y-axis.
20174        \param deltaz Amount of displacement along the Z-axis.
20175        \param deltac Amount of displacement along the C-axis.
20176        \param border_condition Border condition.
20177 
20178        - \c border_condition can be :
20179           - 0 : Zero border condition (Dirichlet).
20180           - 1 : Nearest neighbors (Neumann).
20181           - 2 : Repeat Pattern (Fourier style).
20182     **/
20183     CImg<T>& shift(const int deltax, const int deltay=0, const int deltaz=0, const int deltac=0,
20184                    const int border_condition=0) {
20185       if (is_empty()) return *this;
20186       if (deltax) // Shift along X-axis
20187         switch (border_condition) {
20188         case 0 :
20189           if (cimg::abs(deltax)>=width()) return fill(0);
20190           if (deltax<0) cimg_forYZC(*this,y,z,c) {
20191             std::memmove(data(0,y,z,c),data(-deltax,y,z,c),(_width+deltax)*sizeof(T));
20192             std::memset(data(_width+deltax,y,z,c),0,-deltax*sizeof(T));
20193           } else cimg_forYZC(*this,y,z,c) {
20194             std::memmove(data(deltax,y,z,c),data(0,y,z,c),(_width-deltax)*sizeof(T));
20195             std::memset(data(0,y,z,c),0,deltax*sizeof(T));
20196           }
20197           break;
20198         case 1 :
20199           if (deltax<0) {
20200             const int ndeltax = (-deltax>=width())?_width-1:-deltax;
20201             if (!ndeltax) return *this;
20202             cimg_forYZC(*this,y,z,c) {
20203               std::memmove(data(0,y,z,c),data(ndeltax,y,z,c),(_width-ndeltax)*sizeof(T));
20204               T *ptrd = data(_width-1,y,z,c);
20205               const T val = *ptrd;
20206               for (int l = 0; l<ndeltax-1; ++l) *(--ptrd) = val;
20207             }
20208           } else {
20209             const int ndeltax = (deltax>=width())?_width-1:deltax;
20210             if (!ndeltax) return *this;
20211             cimg_forYZC(*this,y,z,c) {
20212               std::memmove(data(ndeltax,y,z,c),data(0,y,z,c),(_width-ndeltax)*sizeof(T));
20213               T *ptrd = data(0,y,z,c);
20214               const T val = *ptrd;
20215               for (int l = 0; l<ndeltax-1; ++l) *(++ptrd) = val;
20216             }
20217           }
20218           break;
20219         default : {
20220           const int ml = cimg::mod(-deltax,width()), ndeltax = (ml<=width()/2)?ml:(ml-width());
20221           if (!ndeltax) return *this;
20222           T *const buf = new T[cimg::abs(ndeltax)];
20223           if (ndeltax>0) cimg_forYZC(*this,y,z,c) {
20224             std::memcpy(buf,data(0,y,z,c),ndeltax*sizeof(T));
20225             std::memmove(data(0,y,z,c),data(ndeltax,y,z,c),(_width-ndeltax)*sizeof(T));
20226             std::memcpy(data(_width-ndeltax,y,z,c),buf,ndeltax*sizeof(T));
20227           } else cimg_forYZC(*this,y,z,c) {
20228             std::memcpy(buf,data(_width+ndeltax,y,z,c),-ndeltax*sizeof(T));
20229             std::memmove(data(-ndeltax,y,z,c),data(0,y,z,c),(_width+ndeltax)*sizeof(T));
20230             std::memcpy(data(0,y,z,c),buf,-ndeltax*sizeof(T));
20231           }
20232           delete[] buf;
20233         }
20234         }
20235 
20236       if (deltay) // Shift along Y-axis
20237         switch (border_condition) {
20238         case 0 :
20239           if (cimg::abs(deltay)>=height()) return fill(0);
20240           if (deltay<0) cimg_forZC(*this,z,c) {
20241             std::memmove(data(0,0,z,c),data(0,-deltay,z,c),_width*(_height+deltay)*sizeof(T));
20242             std::memset(data(0,_height+deltay,z,c),0,-deltay*_width*sizeof(T));
20243           } else cimg_forZC(*this,z,c) {
20244             std::memmove(data(0,deltay,z,c),data(0,0,z,c),_width*(_height-deltay)*sizeof(T));
20245             std::memset(data(0,0,z,c),0,deltay*_width*sizeof(T));
20246           }
20247           break;
20248         case 1 :
20249           if (deltay<0) {
20250             const int ndeltay = (-deltay>=height())?_height-1:-deltay;
20251             if (!ndeltay) return *this;
20252             cimg_forZC(*this,z,c) {
20253               std::memmove(data(0,0,z,c),data(0,ndeltay,z,c),_width*(_height-ndeltay)*sizeof(T));
20254               T *ptrd = data(0,_height-ndeltay,z,c), *ptrs = data(0,_height-1,z,c);
20255               for (int l = 0; l<ndeltay-1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; }
20256             }
20257           } else {
20258             const int ndeltay = (deltay>=height())?_height-1:deltay;
20259             if (!ndeltay) return *this;
20260             cimg_forZC(*this,z,c) {
20261               std::memmove(data(0,ndeltay,z,c),data(0,0,z,c),_width*(_height-ndeltay)*sizeof(T));
20262               T *ptrd = data(0,1,z,c), *ptrs = data(0,0,z,c);
20263               for (int l = 0; l<ndeltay-1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; }
20264             }
20265           }
20266           break;
20267         default : {
20268           const int ml = cimg::mod(-deltay,height()), ndeltay = (ml<=height()/2)?ml:(ml-height());
20269           if (!ndeltay) return *this;
20270           T *const buf = new T[_width*cimg::abs(ndeltay)];
20271           if (ndeltay>0) cimg_forZC(*this,z,c) {
20272             std::memcpy(buf,data(0,0,z,c),_width*ndeltay*sizeof(T));
20273             std::memmove(data(0,0,z,c),data(0,ndeltay,z,c),_width*(_height-ndeltay)*sizeof(T));
20274             std::memcpy(data(0,_height-ndeltay,z,c),buf,_width*ndeltay*sizeof(T));
20275           } else cimg_forZC(*this,z,c) {
20276             std::memcpy(buf,data(0,_height+ndeltay,z,c),-ndeltay*_width*sizeof(T));
20277             std::memmove(data(0,-ndeltay,z,c),data(0,0,z,c),_width*(_height+ndeltay)*sizeof(T));
20278             std::memcpy(data(0,0,z,c),buf,-ndeltay*_width*sizeof(T));
20279           }
20280           delete[] buf;
20281         }
20282         }
20283 
20284       if (deltaz) // Shift along Z-axis
20285         switch (border_condition) {
20286         case 0 :
20287           if (cimg::abs(deltaz)>=depth()) return fill(0);
20288           if (deltaz<0) cimg_forC(*this,c) {
20289             std::memmove(data(0,0,0,c),data(0,0,-deltaz,c),_width*_height*(_depth+deltaz)*sizeof(T));
20290             std::memset(data(0,0,_depth+deltaz,c),0,_width*_height*(-deltaz)*sizeof(T));
20291           } else cimg_forC(*this,c) {
20292             std::memmove(data(0,0,deltaz,c),data(0,0,0,c),_width*_height*(_depth-deltaz)*sizeof(T));
20293             std::memset(data(0,0,0,c),0,deltaz*_width*_height*sizeof(T));
20294           }
20295           break;
20296         case 1 :
20297           if (deltaz<0) {
20298             const int ndeltaz = (-deltaz>=depth())?_depth-1:-deltaz;
20299             if (!ndeltaz) return *this;
20300             cimg_forC(*this,c) {
20301               std::memmove(data(0,0,0,c),data(0,0,ndeltaz,c),_width*_height*(_depth-ndeltaz)*sizeof(T));
20302               T *ptrd = data(0,0,_depth-ndeltaz,c), *ptrs = data(0,0,_depth-1,c);
20303               for (int l = 0; l<ndeltaz-1; ++l) { std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=_width*_height; }
20304             }
20305           } else {
20306             const int ndeltaz = (deltaz>=depth())?_depth-1:deltaz;
20307             if (!ndeltaz) return *this;
20308             cimg_forC(*this,c) {
20309               std::memmove(data(0,0,ndeltaz,c),data(0,0,0,c),_width*_height*(_depth-ndeltaz)*sizeof(T));
20310               T *ptrd = data(0,0,1,c), *ptrs = data(0,0,0,c);
20311               for (int l = 0; l<ndeltaz-1; ++l) { std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=_width*_height; }
20312             }
20313           }
20314           break;
20315         default : {
20316           const int ml = cimg::mod(-deltaz,depth()), ndeltaz = (ml<=depth()/2)?ml:(ml-depth());
20317           if (!ndeltaz) return *this;
20318           T *const buf = new T[_width*_height*cimg::abs(ndeltaz)];
20319           if (ndeltaz>0) cimg_forC(*this,c) {
20320             std::memcpy(buf,data(0,0,0,c),_width*_height*ndeltaz*sizeof(T));
20321             std::memmove(data(0,0,0,c),data(0,0,ndeltaz,c),_width*_height*(_depth-ndeltaz)*sizeof(T));
20322             std::memcpy(data(0,0,_depth-ndeltaz,c),buf,_width*_height*ndeltaz*sizeof(T));
20323           } else cimg_forC(*this,c) {
20324             std::memcpy(buf,data(0,0,_depth+ndeltaz,c),-ndeltaz*_width*_height*sizeof(T));
20325             std::memmove(data(0,0,-ndeltaz,c),data(0,0,0,c),_width*_height*(_depth+ndeltaz)*sizeof(T));
20326             std::memcpy(data(0,0,0,c),buf,-ndeltaz*_width*_height*sizeof(T));
20327           }
20328           delete[] buf;
20329         }
20330         }
20331 
20332       if (deltac) // Shift along C-axis
20333         switch (border_condition) {
20334         case 0 :
20335           if (cimg::abs(deltac)>=spectrum()) return fill(0);
20336           if (deltac<0) {
20337             std::memmove(_data,data(0,0,0,-deltac),_width*_height*_depth*(_spectrum+deltac)*sizeof(T));
20338             std::memset(data(0,0,0,_spectrum+deltac),0,_width*_height*_depth*(-deltac)*sizeof(T));
20339           } else {
20340             std::memmove(data(0,0,0,deltac),_data,_width*_height*_depth*(_spectrum-deltac)*sizeof(T));
20341             std::memset(_data,0,deltac*_width*_height*_depth*sizeof(T));
20342           }
20343           break;
20344         case 1 :
20345           if (deltac<0) {
20346             const int ndeltac = (-deltac>=spectrum())?_spectrum-1:-deltac;
20347             if (!ndeltac) return *this;
20348             std::memmove(_data,data(0,0,0,ndeltac),_width*_height*_depth*(_spectrum-ndeltac)*sizeof(T));
20349             T *ptrd = data(0,0,0,_spectrum-ndeltac), *ptrs = data(0,0,0,_spectrum-1);
20350             for (int l = 0; l<ndeltac-1; ++l) { std::memcpy(ptrd,ptrs,_width*_height*_depth*sizeof(T)); ptrd+=_width*_height*_depth; }
20351           } else {
20352             const int ndeltac = (deltac>=spectrum())?_spectrum-1:deltac;
20353             if (!ndeltac) return *this;
20354             std::memmove(data(0,0,0,ndeltac),_data,_width*_height*_depth*(_spectrum-ndeltac)*sizeof(T));
20355             T *ptrd = data(0,0,0,1);
20356             for (int l = 0; l<ndeltac-1; ++l) { std::memcpy(ptrd,_data,_width*_height*_depth*sizeof(T)); ptrd+=_width*_height*_depth; }
20357           }
20358           break;
20359         default : {
20360           const int ml = cimg::mod(-deltac,spectrum()), ndeltac = (ml<=spectrum()/2)?ml:(ml-spectrum());
20361           if (!ndeltac) return *this;
20362           T *const buf = new T[_width*_height*_depth*cimg::abs(ndeltac)];
20363           if (ndeltac>0) {
20364             std::memcpy(buf,_data,_width*_height*_depth*ndeltac*sizeof(T));
20365             std::memmove(_data,data(0,0,0,ndeltac),_width*_height*_depth*(_spectrum-ndeltac)*sizeof(T));
20366             std::memcpy(data(0,0,0,_spectrum-ndeltac),buf,_width*_height*_depth*ndeltac*sizeof(T));
20367           } else {
20368             std::memcpy(buf,data(0,0,0,_spectrum+ndeltac),-ndeltac*_width*_height*_depth*sizeof(T));
20369             std::memmove(data(0,0,0,-ndeltac),_data,_width*_height*_depth*(_spectrum+ndeltac)*sizeof(T));
20370             std::memcpy(_data,buf,-ndeltac*_width*_height*_depth*sizeof(T));
20371           }
20372           delete[] buf;
20373         }
20374         }
20375       return *this;
20376     }
20377 
20378     CImg<T> get_shift(const int deltax, const int deltay=0, const int deltaz=0, const int deltac=0,
20379                           const int border_condition=0) const {
20380       return (+*this).shift(deltax,deltay,deltaz,deltac,border_condition);
20381     }
20382 
20383     // Permute axes order (internal).
20384     template<typename t>
20385     CImg<t> _get_permute_axes(const char *const permut, const t&) const {
20386       if (is_empty() || !permut) return CImg<t>(*this,false);
20387       CImg<t> res;
20388       const T* ptrs = _data;
20389       if (!cimg::strncasecmp(permut,"xyzc",4)) return (+*this);
20390       if (!cimg::strncasecmp(permut,"xycz",4)) {
20391         res.assign(_width,_height,_spectrum,_depth);
20392         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20393         cimg_forXYZC(*this,x,y,z,c) res(x,y,c,z,wh,whd) = (t)*(ptrs++);
20394       }
20395       if (!cimg::strncasecmp(permut,"xzyc",4)) {
20396         res.assign(_width,_depth,_height,_spectrum);
20397         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20398         cimg_forXYZC(*this,x,y,z,c) res(x,z,y,c,wh,whd) = (t)*(ptrs++);
20399       }
20400       if (!cimg::strncasecmp(permut,"xzcy",4)) {
20401         res.assign(_width,_depth,_spectrum,_height);
20402         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20403         cimg_forXYZC(*this,x,y,z,c) res(x,z,c,y,wh,whd) = (t)*(ptrs++);
20404       }
20405       if (!cimg::strncasecmp(permut,"xcyz",4)) {
20406         res.assign(_width,_spectrum,_height,_depth);
20407         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20408         cimg_forXYZC(*this,x,y,z,c) res(x,c,y,z,wh,whd) = (t)*(ptrs++);
20409       }
20410       if (!cimg::strncasecmp(permut,"xczy",4)) {
20411         res.assign(_width,_spectrum,_depth,_height);
20412         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20413         cimg_forXYZC(*this,x,y,z,c) res(x,c,z,y,wh,whd) = (t)*(ptrs++);
20414       }
20415       if (!cimg::strncasecmp(permut,"yxzc",4)) {
20416         res.assign(_height,_width,_depth,_spectrum);
20417         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20418         cimg_forXYZC(*this,x,y,z,c) res(y,x,z,c,wh,whd) = (t)*(ptrs++);
20419       }
20420       if (!cimg::strncasecmp(permut,"yxcz",4)) {
20421         res.assign(_height,_width,_spectrum,_depth);
20422         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20423         cimg_forXYZC(*this,x,y,z,c) res(y,x,c,z,wh,whd) = (t)*(ptrs++);
20424       }
20425       if (!cimg::strncasecmp(permut,"yzxc",4)) {
20426         res.assign(_height,_depth,_width,_spectrum);
20427         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20428         cimg_forXYZC(*this,x,y,z,c) res(y,z,x,c,wh,whd) = (t)*(ptrs++);
20429       }
20430       if (!cimg::strncasecmp(permut,"yzcx",4)) {
20431         res.assign(_height,_depth,_spectrum,_width);
20432         switch (_width) {
20433         case 1 : {
20434           t *ptr_r = res.data(0,0,0,0);
20435           for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
20436             *(ptr_r++) = (t)*(ptrs++);
20437           }
20438         } break;
20439         case 2 : {
20440           t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1);
20441           for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
20442             *(ptr_r++) = (t)*(ptrs++); *(ptr_g++) = (t)*(ptrs++);
20443           }
20444         } break;
20445         case 3 : { // Optimization for the classical conversion from interleaved RGB to planar RGB
20446           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);
20447           for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
20448             *(ptr_r++) = (t)*(ptrs++); *(ptr_g++) = (t)*(ptrs++); *(ptr_b++) = (t)*(ptrs++);
20449           }
20450         } break;
20451         case 4 : { // Optimization for the classical conversion from interleaved RGBA to planar RGBA
20452           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);
20453           for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
20454             *(ptr_r++) = (t)*(ptrs++); *(ptr_g++) = (t)*(ptrs++); *(ptr_b++) = (t)*(ptrs++); *(ptr_a++) = (t)*(ptrs++);
20455           }
20456         } break;
20457         default : {
20458           const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20459           cimg_forXYZC(*this,x,y,z,c) res(y,z,c,x,wh,whd) = *(ptrs++);
20460           return res;
20461         }
20462         }
20463       }
20464       if (!cimg::strncasecmp(permut,"ycxz",4)) {
20465         res.assign(_height,_spectrum,_width,_depth);
20466         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20467         cimg_forXYZC(*this,x,y,z,c) res(y,c,x,z,wh,whd) = (t)*(ptrs++);
20468       }
20469       if (!cimg::strncasecmp(permut,"yczx",4)) {
20470         res.assign(_height,_spectrum,_depth,_width);
20471         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20472         cimg_forXYZC(*this,x,y,z,c) res(y,c,z,x,wh,whd) = (t)*(ptrs++);
20473       }
20474       if (!cimg::strncasecmp(permut,"zxyc",4)) {
20475         res.assign(_depth,_width,_height,_spectrum);
20476         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20477         cimg_forXYZC(*this,x,y,z,c) res(z,x,y,c,wh,whd) = (t)*(ptrs++);
20478       }
20479       if (!cimg::strncasecmp(permut,"zxcy",4)) {
20480         res.assign(_depth,_width,_spectrum,_height);
20481         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20482         cimg_forXYZC(*this,x,y,z,c) res(z,x,c,y,wh,whd) = (t)*(ptrs++);
20483       }
20484       if (!cimg::strncasecmp(permut,"zyxc",4)) {
20485         res.assign(_depth,_height,_width,_spectrum);
20486         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20487         cimg_forXYZC(*this,x,y,z,c) res(z,y,x,c,wh,whd) = (t)*(ptrs++);
20488       }
20489       if (!cimg::strncasecmp(permut,"zycx",4)) {
20490         res.assign(_depth,_height,_spectrum,_width);
20491         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20492         cimg_forXYZC(*this,x,y,z,c) res(z,y,c,x,wh,whd) = (t)*(ptrs++);
20493       }
20494       if (!cimg::strncasecmp(permut,"zcxy",4)) {
20495         res.assign(_depth,_spectrum,_width,_height);
20496         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20497         cimg_forXYZC(*this,x,y,z,c) res(z,c,x,y,wh,whd) = (t)*(ptrs++);
20498       }
20499       if (!cimg::strncasecmp(permut,"zcyx",4)) {
20500         res.assign(_depth,_spectrum,_height,_width);
20501         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20502         cimg_forXYZC(*this,x,y,z,c) res(z,c,y,x,wh,whd) = (t)*(ptrs++);
20503       }
20504       if (!cimg::strncasecmp(permut,"cxyz",4)) {
20505         res.assign(_spectrum,_width,_height,_depth);
20506         switch (_spectrum) {
20507         case 1 : {
20508           const T *ptr_r = data(0,0,0,0);
20509           t *ptrd = res._data;
20510           for (unsigned int siz = _width*_height*_depth; siz; --siz) {
20511             *(ptrd++) = (t)*(ptr_r++);
20512           }
20513         } break;
20514         case 2 : {
20515           const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1);
20516           t *ptrd = res._data;
20517           for (unsigned int siz = _width*_height*_depth; siz; --siz) {
20518             *(ptrd++) = (t)*(ptr_r++); *(ptrd++) = (t)*(ptr_g++);
20519           }
20520         } break;
20521         case 3 : { // Optimization for the classical conversion from planar RGB to interleaved RGB
20522           const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
20523           t *ptrd = res._data;
20524           for (unsigned int siz = _width*_height*_depth; siz; --siz) {
20525             *(ptrd++) = (t)*(ptr_r++); *(ptrd++) = (t)*(ptr_g++); *(ptrd++) = (t)*(ptr_b++);
20526           }
20527         } break;
20528         case 4 : { // Optimization for the classical conversion from planar RGBA to interleaved RGBA
20529           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);
20530           t *ptrd = res._data;
20531           for (unsigned int siz = _width*_height*_depth; siz; --siz) {
20532             *(ptrd++) = (t)*(ptr_r++); *(ptrd++) = (t)*(ptr_g++); *(ptrd++) = (t)*(ptr_b++); *(ptrd++) = (t)*(ptr_a++);
20533           }
20534         } break;
20535         default : {
20536           const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20537           cimg_forXYZC(*this,x,y,z,c) res(c,x,y,z,wh,whd) = (t)*(ptrs++);
20538         }
20539         }
20540       }
20541       if (!cimg::strncasecmp(permut,"cxzy",4)) {
20542         res.assign(_spectrum,_width,_depth,_height);
20543         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20544         cimg_forXYZC(*this,x,y,z,c) res(c,x,z,y,wh,whd) = (t)*(ptrs++);
20545       }
20546       if (!cimg::strncasecmp(permut,"cyxz",4)) {
20547         res.assign(_spectrum,_height,_width,_depth);
20548         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20549         cimg_forXYZC(*this,x,y,z,c) res(c,y,x,z,wh,whd) = (t)*(ptrs++);
20550       }
20551       if (!cimg::strncasecmp(permut,"cyzx",4)) {
20552         res.assign(_spectrum,_height,_depth,_width);
20553         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20554         cimg_forXYZC(*this,x,y,z,c) res(c,y,z,x,wh,whd) = (t)*(ptrs++);
20555       }
20556       if (!cimg::strncasecmp(permut,"czxy",4)) {
20557         res.assign(_spectrum,_depth,_width,_height);
20558         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20559         cimg_forXYZC(*this,x,y,z,c) res(c,z,x,y,wh,whd) = (t)*(ptrs++);
20560       }
20561       if (!cimg::strncasecmp(permut,"czyx",4)) {
20562         res.assign(_spectrum,_depth,_height,_width);
20563         const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20564         cimg_forXYZC(*this,x,y,z,c) res(c,z,y,x,wh,whd) = (t)*(ptrs++);
20565       }
20566       if (!res)
20567         throw CImgArgumentException(_cimg_instance
20568                                     "permute_axes() : Invalid specified permutation '%s'.",
20569                                     cimg_instance,
20570                                     permut);
20571       return res;
20572     }
20573 
20574     //! Permute axes order.
20575     /**
20576        This function permutes image axes.
20577        \param permut = String describing the permutation (4 characters).
20578     **/
20579     CImg<T>& permute_axes(const char *const order) {
20580       return get_permute_axes(order).move_to(*this);
20581     }
20582 
20583     CImg<T> get_permute_axes(const char *const order) const {
20584       const T foo = (T)0;
20585       return _get_permute_axes(order,foo);
20586     }
20587 
20588     //! Unroll all images values into specified axis.
20589     CImg<T>& unroll(const char axis) {
20590       const unsigned int siz = size();
20591       if (siz) switch (axis) {
20592       case 'x' : _width = siz; _height = _depth = _spectrum = 1; break;
20593       case 'y' : _height = siz; _width = _depth = _spectrum = 1; break;
20594       case 'z' : _depth = siz; _width = _height = _spectrum = 1; break;
20595       default : _spectrum = siz; _width = _height = _depth = 1;
20596       }
20597       return *this;
20598     }
20599 
20600     CImg<T> get_unroll(const char axis) const {
20601       return (+*this).unroll(axis);
20602     }
20603 
20604     //! Rotate an image.
20605     /**
20606        \param angle = rotation angle (in degrees).
20607        \param cond = rotation type. can be :
20608        - 0 = zero-value at borders
20609        - 1 = nearest pixel.
20610        - 2 = cyclic.
20611        \note Returned image will probably have a different size than the image instance *this.
20612     **/
20613     CImg<T>& rotate(const float angle, const unsigned int border_conditions=0, const unsigned int interpolation=1) {
20614       return get_rotate(angle,border_conditions,interpolation).move_to(*this);
20615     }
20616 
20617     CImg<T> get_rotate(const float angle, const unsigned int border_conditions=0, const unsigned int interpolation=1) const {
20618       if (is_empty()) return *this;
20619       CImg<T> res;
20620       const float nangle = cimg::mod(angle,360.0f);
20621       if (border_conditions!=1 && cimg::mod(nangle,90.0f)==0) { // optimized version for orthogonal angles
20622         const int wm1 = width() - 1, hm1 = height() - 1;
20623         const int iangle = (int)nangle/90;
20624         switch (iangle) {
20625         case 1 : {
20626           res.assign(_height,_width,_depth,_spectrum);
20627           T *ptrd = res._data;
20628           cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(y,hm1-x,z,c);
20629         } break;
20630         case 2 : {
20631           res.assign(_width,_height,_depth,_spectrum);
20632           T *ptrd = res._data;
20633           cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1-x,hm1-y,z,c);
20634         } break;
20635         case 3 : {
20636           res.assign(_height,_width,_depth,_spectrum);
20637           T *ptrd = res._data;
20638           cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1-y,x,z,c);
20639         } break;
20640         default :
20641           return *this;
20642         }
20643       } else { // generic version
20644         const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
20645         const float
20646           rad = (float)(nangle*cimg::PI/180.0),
20647           ca = (float)std::cos(rad),
20648           sa = (float)std::sin(rad),
20649           ux = cimg::abs(_width*ca), uy = cimg::abs(_width*sa),
20650           vx = cimg::abs(_height*sa), vy = cimg::abs(_height*ca),
20651           w2 = 0.5f*_width, h2 = 0.5f*_height,
20652           dw2 = 0.5f*(ux+vx), dh2 = 0.5f*(uy+vy);
20653         res.assign((int)(ux+vx),(int)(uy+vy),_depth,_spectrum);
20654         switch (border_conditions) {
20655         case 0 : {
20656           switch (interpolation) {
20657           case 2 : {
20658             cimg_forXY(res,x,y) cimg_forZC(*this,z,c) {
20659               const Tfloat val = cubic_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,c,0);
20660               res(x,y,z,c) = (T)(val<vmin?vmin:val>vmax?vmax:val);
20661             }
20662           } break;
20663           case 1 : {
20664             cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
20665               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);
20666           } break;
20667           default : {
20668             cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
20669               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);
20670           }
20671           }
20672         } break;
20673         case 1 : {
20674           switch (interpolation) {
20675           case 2 : {
20676             cimg_forXY(res,x,y) cimg_forZC(*this,z,c) {
20677               const Tfloat val = _cubic_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,c);
20678               res(x,y,z,c) = (T)(val<vmin?vmin:val>vmax?vmax:val);
20679             }
20680           } break;
20681           case 1 : {
20682             cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
20683               res(x,y,z,c) = (T)_linear_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,c);
20684           } break;
20685           default : {
20686             cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
20687               res(x,y,z,c) = _atXY((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),(int)(h2 - (x-dw2)*sa + (y-dh2)*ca),z,c);
20688           }
20689           }
20690         } break;
20691         case 2 : {
20692           switch (interpolation) {
20693           case 2 : {
20694             cimg_forXY(res,x,y) cimg_forZC(*this,z,c) {
20695               const Tfloat val = _cubic_atXY(cimg::mod(w2 + (x-dw2)*ca + (y-dh2)*sa,(float)width()),
20696                                              cimg::mod(h2 - (x-dw2)*sa + (y-dh2)*ca,(float)height()),z,c);
20697               res(x,y,z,c) = (T)(val<vmin?vmin:val>vmax?vmax:val);
20698             }
20699           } break;
20700           case 1 : {
20701             cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
20702               res(x,y,z,c) = (T)_linear_atXY(cimg::mod(w2 + (x-dw2)*ca + (y-dh2)*sa,(float)width()),
20703                                              cimg::mod(h2 - (x-dw2)*sa + (y-dh2)*ca,(float)height()),z,c);
20704           } break;
20705           default : {
20706             cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
20707               res(x,y,z,c) = (*this)(cimg::mod((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),width()),
20708                                       cimg::mod((int)(h2 - (x-dw2)*sa + (y-dh2)*ca),height()),z,c);
20709           }
20710           }
20711         } break;
20712         default :
20713           throw CImgArgumentException(_cimg_instance
20714                                       "rotate() : Invalid specified border conditions %d "
20715                                       "(should be { 0=dirichlet | 1=neumann | 2=cyclic }).",
20716                                       cimg_instance,
20717                                       border_conditions);
20718         }
20719       }
20720       return res;
20721     }
20722 
20723     //! Rotate an image around a center point (\c cx,\c cy).
20724     /**
20725        \param angle = rotation angle (in degrees).
20726        \param cx = X-coordinate of the rotation center.
20727        \param cy = Y-coordinate of the rotation center.
20728        \param zoom = zoom.
20729        \param cond = rotation type. can be :
20730        - 0 = zero-value at borders
20731        - 1 = repeat image at borders
20732        - 2 = zero-value at borders and linear interpolation
20733     **/
20734     CImg<T>& rotate(const float angle, const float cx, const float cy, const float zoom,
20735                     const unsigned int border_conditions=3, const unsigned int interpolation=1) {
20736       return get_rotate(angle,cx,cy,zoom,border_conditions,interpolation).move_to(*this);
20737     }
20738 
20739     CImg<T> get_rotate(const float angle, const float cx, const float cy, const float zoom,
20740                        const unsigned int border_conditions=3, const unsigned int interpolation=1) const {
20741       if (interpolation>2)
20742         throw CImgArgumentException(_cimg_instance
20743                                     "rotate() : Invalid specified interpolation %d "
20744                                     "(should be { 0=none | 1=linear | 2=bicubic }).",
20745                                     cimg_instance,
20746                                     interpolation);
20747 
20748       if (is_empty()) return *this;
20749       CImg<T> res(_width,_height,_depth,_spectrum);
20750       const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
20751       const float
20752         rad = (float)((angle*cimg::PI)/180.0),
20753         ca = (float)std::cos(rad)/zoom,
20754         sa = (float)std::sin(rad)/zoom;
20755       switch (border_conditions) {
20756       case 0 : {
20757         switch (interpolation) {
20758         case 2 : {
20759           cimg_forXY(res,x,y) cimg_forZC(*this,z,c) {
20760             const Tfloat val = cubic_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,c,0);
20761             res(x,y,z,c) = (T)(val<vmin?vmin:val>vmax?vmax:val);
20762           }
20763         } break;
20764         case 1 : {
20765           cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
20766             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);
20767         } break;
20768         default : {
20769           cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
20770             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);
20771         }
20772         }
20773       } break;
20774       case 1 : {
20775         switch (interpolation) {
20776         case 2 : {
20777           cimg_forXY(res,x,y) cimg_forZC(*this,z,c) {
20778             const Tfloat val = _cubic_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,c);
20779             res(x,y,z,c) = (T)(val<vmin?vmin:val>vmax?vmax:val);
20780           }
20781         } break;
20782         case 1 : {
20783           cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
20784             res(x,y,z,c) = (T)_linear_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,c);
20785         } break;
20786         default : {
20787           cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
20788             res(x,y,z,c) = _atXY((int)(cx + (x-cx)*ca + (y-cy)*sa),(int)(cy - (x-cx)*sa + (y-cy)*ca),z,c);
20789         }
20790         }
20791       } break;
20792       case 2 : {
20793         switch (interpolation) {
20794         case 2 : {
20795           cimg_forXY(res,x,y) cimg_forZC(*this,z,c) {
20796             const Tfloat val = _cubic_atXY(cimg::mod(cx + (x-cx)*ca + (y-cy)*sa,(float)width()),
20797                                            cimg::mod(cy - (x-cx)*sa + (y-cy)*ca,(float)height()),z,c);
20798             res(x,y,z,c) = (T)(val<vmin?vmin:val>vmax?vmax:val);
20799           }
20800         } break;
20801         case 1 : {
20802           cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
20803             res(x,y,z,c) = (T)_linear_atXY(cimg::mod(cx + (x-cx)*ca + (y-cy)*sa,(float)width()),
20804                                            cimg::mod(cy - (x-cx)*sa + (y-cy)*ca,(float)height()),z,c);
20805         } break;
20806         default : {
20807           cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
20808             res(x,y,z,c) = (*this)(cimg::mod((int)(cx + (x-cx)*ca + (y-cy)*sa),width()),
20809                                     cimg::mod((int)(cy - (x-cx)*sa + (y-cy)*ca),height()),z,c);
20810         }
20811         }
20812       } break;
20813       default :
20814         throw CImgArgumentException(_cimg_instance
20815                                     "rotate() : Invalid specified border conditions %d "
20816                                     "(should be { 0=dirichlet | 1=neumann | 2=cyclic }).",
20817                                     cimg_instance,
20818                                     border_conditions);
20819       }
20820       return res;
20821     }
20822 
20823     //! Warp an image.
20824     template<typename t>
20825     CImg<T>& warp(const CImg<t>& warp, const bool is_relative=false,
20826                   const bool interpolation=true, const unsigned int border_conditions=0) {
20827       return get_warp(warp,is_relative,interpolation,border_conditions).move_to(*this);
20828     }
20829 
20830     template<typename t>
20831     CImg<T> get_warp(const CImg<t>& warp, const bool is_relative=false,
20832                      const bool interpolation=true, const unsigned int border_conditions=0) const {
20833       if (is_empty() || !warp) return *this;
20834       if (is_relative && !is_sameXYZ(warp))
20835         throw CImgArgumentException(_cimg_instance
20836                                     "warp() : Instance and specified relative warping field (%u,%u,%u,%u,%p) "
20837                                     "have different XYZ dimensions.",
20838                                     cimg_instance,
20839                                     warp._width,warp._height,warp._depth,warp._spectrum,warp._data);
20840 
20841       CImg<T> res(warp._width,warp._height,warp._depth,_spectrum);
20842       T *ptrd = res._data;
20843       switch (warp._spectrum) {
20844       case 1 : // 1d warping.
20845         if (is_relative) { // Relative warp coordinates
20846           if (interpolation) switch (border_conditions) {
20847             case 2 : {
20848               cimg_forC(res,c) {
20849                 const t *ptrs0 = warp._data;
20850                 cimg_forXYZ(res,x,y,z)
20851                   *(ptrd++) = (T)_linear_atX(cimg::mod(x - (float)*(ptrs0++),(float)_width),y,z,c);
20852               }
20853             } break;
20854             case 1 : {
20855               cimg_forC(res,c) {
20856                 const t *ptrs0 = warp._data;
20857                 cimg_forXYZ(res,x,y,z)
20858                   *(ptrd++) = (T)_linear_atX(x - (float)*(ptrs0++),y,z,c);
20859               }
20860             } break;
20861             default : {
20862               cimg_forC(res,c) {
20863                 const t *ptrs0 = warp._data;
20864                 cimg_forXYZ(res,x,y,z)
20865                   *(ptrd++) = (T)linear_atX(x - (float)*(ptrs0++),y,z,c,0);
20866               }
20867             }
20868             } else switch (border_conditions) {
20869             case 2 : {
20870               cimg_forC(res,c) {
20871                 const t *ptrs0 = warp._data;
20872                 cimg_forXYZ(res,x,y,z)
20873                   *(ptrd++) = (*this)(cimg::mod(x - (int)*(ptrs0++),(int)_width),y,z,c);
20874               }
20875             } break;
20876             case 1 : {
20877               cimg_forC(res,c) {
20878                 const t *ptrs0 = warp._data;
20879                 cimg_forXYZ(res,x,y,z)
20880                   *(ptrd++) = _atX(x - (int)*(ptrs0++),y,z,c);
20881               }
20882             } break;
20883             default : {
20884               cimg_forC(res,c) {
20885                 const t *ptrs0 = warp._data;
20886                 cimg_forXYZ(res,x,y,z)
20887                   *(ptrd++) = atX(x - (int)*(ptrs0++),y,z,c,0);
20888               }
20889             }
20890             }
20891         } else { // Absolute warp coordinates
20892           if (interpolation) switch (border_conditions) {
20893             case 2 : {
20894               cimg_forC(res,c) {
20895                 const t *ptrs0 = warp._data;
20896                 cimg_forXYZ(res,x,y,z)
20897                   *(ptrd++) = (T)_linear_atX(cimg::mod((float)*(ptrs0++),(float)_width),0,0,c);
20898               }
20899             } break;
20900             case 1 : {
20901               cimg_forC(res,c) {
20902                 const t *ptrs0 = warp._data;
20903                 cimg_forXYZ(res,x,y,z)
20904                   *(ptrd++) = (T)_linear_atX((float)*(ptrs0++),0,0,c);
20905               }
20906             } break;
20907             default : {
20908               cimg_forC(res,c) {
20909                 const t *ptrs0 = warp._data;
20910                 cimg_forXYZ(res,x,y,z)
20911                   *(ptrd++) = (T)linear_atX((float)*(ptrs0++),0,0,c,0);
20912               }
20913             }
20914             } else switch (border_conditions) {
20915             case 2 : {
20916               cimg_forC(res,c) {
20917                 const t *ptrs0 = warp._data;
20918                 cimg_forXYZ(res,x,y,z)
20919                   *(ptrd++) = (*this)(cimg::mod((int)*(ptrs0++),(int)_width),0,0,c);
20920               }
20921             } break;
20922             case 1 : {
20923               cimg_forC(res,c) {
20924                 const t *ptrs0 = warp._data;
20925                 cimg_forXYZ(res,x,y,z)
20926                   *(ptrd++) = _atX((int)*(ptrs0++),0,0,c);
20927               }
20928             } break;
20929             default : {
20930               cimg_forC(res,c) {
20931                 const t *ptrs0 = warp._data;
20932                 cimg_forXYZ(res,x,y,z)
20933                   *(ptrd++) = atX((int)*(ptrs0++),0,0,c,0);
20934               }
20935             }
20936             }
20937         }
20938         break;
20939 
20940       case 2 : // 2d warping
20941         if (is_relative) { // Relative warp coordinates
20942           if (interpolation) switch (border_conditions) {
20943             case 2 : {
20944               cimg_forC(res,c) {
20945                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
20946                 cimg_forXYZ(res,x,y,z)
20947                   *(ptrd++) = (T)_linear_atXY(cimg::mod(x - (float)*(ptrs0++),(float)_width),
20948                                               cimg::mod(y - (float)*(ptrs1++),(float)_height),z,c);
20949               }
20950             } break;
20951             case 1 : {
20952               cimg_forC(res,c) {
20953                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
20954                 cimg_forXYZ(res,x,y,z)
20955                   *(ptrd++) = (T)_linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
20956               }
20957             } break;
20958             default : {
20959               cimg_forC(res,c) {
20960                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
20961                 cimg_forXYZ(res,x,y,z)
20962                   *(ptrd++) = (T)linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,0);
20963               }
20964             }
20965             } else switch (border_conditions) {
20966             case 2 : {
20967               cimg_forC(res,c) {
20968                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
20969                 cimg_forXYZ(res,x,y,z)
20970                   *(ptrd++) = (*this)(cimg::mod(x - (int)*(ptrs0++),(int)_width),
20971                                       cimg::mod(y - (int)*(ptrs1++),(int)_height),z,c);
20972               }
20973             } break;
20974             case 1 : {
20975               cimg_forC(res,c) {
20976                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
20977                 cimg_forXYZ(res,x,y,z)
20978                   *(ptrd++) = _atXY(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z,c);
20979               }
20980             } break;
20981             default : {
20982               cimg_forC(res,c) {
20983                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
20984                 cimg_forXYZ(res,x,y,z)
20985                   *(ptrd++) = atXY(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z,c,0);
20986               }
20987             }
20988             }
20989         } else { // Absolute warp coordinates
20990           if (interpolation) switch (border_conditions) {
20991             case 2 : {
20992               cimg_forC(res,c) {
20993                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
20994                 cimg_forXYZ(res,x,y,z)
20995                   *(ptrd++) = (T)_linear_atXY(cimg::mod((float)*(ptrs0++),(float)_width),
20996                                               cimg::mod((float)*(ptrs1++),(float)_height),0,c);
20997               }
20998             } break;
20999             case 1 : {
21000               cimg_forC(res,c) {
21001                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21002                 cimg_forXYZ(res,x,y,z)
21003                   *(ptrd++) = (T)_linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c);
21004               }
21005             } break;
21006             default : {
21007               cimg_forC(res,c) {
21008                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21009                 cimg_forXYZ(res,x,y,z)
21010                   *(ptrd++) = (T)linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c,0);
21011               }
21012             }
21013             } else switch (border_conditions) {
21014             case 2 : {
21015               cimg_forC(res,c) {
21016                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21017                 cimg_forXYZ(res,x,y,z)
21018                   *(ptrd++) = (*this)(cimg::mod((int)*(ptrs0++),(int)_width),
21019                                       cimg::mod((int)*(ptrs1++),(int)_height),0,c);
21020               }
21021             } break;
21022             case 1 : {
21023               cimg_forC(res,c) {
21024                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21025                 cimg_forXYZ(res,x,y,z)
21026                   *(ptrd++) = _atXY((int)*(ptrs0++),(int)*(ptrs1++),0,c);
21027               }
21028             } break;
21029             default : {
21030               cimg_forC(res,c) {
21031                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21032                 cimg_forXYZ(res,x,y,z)
21033                   *(ptrd++) = atXY((int)*(ptrs0++),(int)*(ptrs1++),0,c,0);
21034               }
21035             }
21036             }
21037         }
21038         break;
21039 
21040       default : // 3d warping
21041         if (is_relative) { // Relative warp coordinates
21042           if (interpolation) switch (border_conditions) {
21043             case 2 : {
21044               cimg_forC(res,c) {
21045                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21046                 cimg_forXYZ(res,x,y,z)
21047                   *(ptrd++) = (T)_linear_atXYZ(cimg::mod(x - (float)*(ptrs0++),(float)_width),
21048                                                cimg::mod(y - (float)*(ptrs1++),(float)_height),
21049                                                cimg::mod(z - (float)*(ptrs2++),(float)_depth),c);
21050               }
21051             } break;
21052             case 1 : {
21053               cimg_forC(res,c) {
21054                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21055                 cimg_forXYZ(res,x,y,z)
21056                   *(ptrd++) = (T)_linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c);
21057               }
21058             } break;
21059             default : {
21060               cimg_forC(res,c) {
21061                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21062                 cimg_forXYZ(res,x,y,z)
21063                   *(ptrd++) = (T)linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c,0);
21064               }
21065             }
21066             } else switch (border_conditions) {
21067             case 2 : {
21068               cimg_forC(res,c) {
21069                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21070                 cimg_forXYZ(res,x,y,z)
21071                   *(ptrd++) = (*this)(cimg::mod(x - (int)*(ptrs0++),(int)_width),
21072                                       cimg::mod(y - (int)*(ptrs1++),(int)_height),
21073                                       cimg::mod(z - (int)*(ptrs2++),(int)_depth),c);
21074               }
21075             } break;
21076             case 1 : {
21077               cimg_forC(res,c) {
21078                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21079                 cimg_forXYZ(res,x,y,z)
21080                   *(ptrd++) = _atXYZ(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z - (int)*(ptrs2++),c);
21081               }
21082             } break;
21083             default : {
21084               cimg_forC(res,c) {
21085                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21086                 cimg_forXYZ(res,x,y,z)
21087                   *(ptrd++) = atXYZ(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z - (int)*(ptrs2++),c,0);
21088               }
21089             }
21090             }
21091         } else { // Absolute warp coordinates
21092           if (interpolation) switch (border_conditions) {
21093             case 2 : {
21094               cimg_forC(res,c) {
21095                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21096                 cimg_forXYZ(res,x,y,z)
21097                   *(ptrd++) = (T)_linear_atXYZ(cimg::mod((float)*(ptrs0++),(float)_width),
21098                                                cimg::mod((float)*(ptrs1++),(float)_height),
21099                                                cimg::mod((float)*(ptrs2++),(float)_depth),c);
21100               }
21101             } break;
21102             case 1 : {
21103               cimg_forC(res,c) {
21104                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21105                 cimg_forXYZ(res,x,y,z)
21106                   *(ptrd++) = (T)_linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
21107               }
21108             } break;
21109             default : {
21110               cimg_forC(res,c) {
21111                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21112                 cimg_forXYZ(res,x,y,z)
21113                   *(ptrd++) = (T)linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c,0);
21114               }
21115             }
21116             } else switch (border_conditions) {
21117             case 2 : {
21118               cimg_forC(res,c) {
21119                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21120                 cimg_forXYZ(res,x,y,z)
21121                   *(ptrd++) = (*this)(cimg::mod((int)*(ptrs0++),(int)_width),
21122                                       cimg::mod((int)*(ptrs1++),(int)_height),
21123                                       cimg::mod((int)*(ptrs2++),(int)_depth),c);
21124               }
21125             } break;
21126             case 1 : {
21127               cimg_forC(res,c) {
21128                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21129                 cimg_forXYZ(res,x,y,z)
21130                   *(ptrd++) = _atXYZ((int)*(ptrs0++),(int)*(ptrs1++),(int)*(ptrs2++),c);
21131               }
21132             } break;
21133             default : {
21134               cimg_forC(res,c) {
21135                 const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21136                 cimg_forXYZ(res,x,y,z)
21137                   *(ptrd++) = atXYZ((int)*(ptrs0++),(int)*(ptrs1++),(int)*(ptrs2++),c,0);
21138               }
21139             }
21140             }
21141         }
21142       }
21143       return res;
21144     }
21145 
21146     //! Return a 2d representation of a 3d image, with three slices.
21147     CImg<T>& projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) {
21148       if (_depth<2) return *this;
21149       return get_projections2d(x0,y0,z0).move_to(*this);
21150     }
21151 
21152     CImg<T> get_projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) const {
21153       if (is_empty() || _depth<2) return +*this;
21154       const unsigned int
21155         _x0 = (x0>=_width)?_width - 1:x0,
21156         _y0 = (y0>=_height)?_height - 1:y0,
21157         _z0 = (z0>=_depth)?_depth - 1:z0;
21158       const CImg<T>
21159         img_xy = get_crop(0,0,_z0,0,_width-1,_height-1,_z0,_spectrum-1),
21160         img_zy = get_crop(_x0,0,0,0,_x0,_height-1,_depth-1,_spectrum-1).permute_axes("xzyc").resize(_depth,_height,1,-100,-1),
21161         img_xz = get_crop(0,_y0,0,0,_width-1,_y0,_depth-1,_spectrum-1).resize(_width,_depth,1,-100,-1);
21162       return CImg<T>(_width + _depth,_height + _depth,1,_spectrum,cimg::min(img_xy.min(),img_zy.min(),img_xz.min())).
21163         draw_image(0,0,img_xy).draw_image(img_xy._width,0,img_zy).
21164         draw_image(0,img_xy._height,img_xz);
21165     }
21166 
21167     //! Get a square region of the image.
21168     /**
21169        \param x0 = X-coordinate of the upper-left crop rectangle corner.
21170        \param y0 = Y-coordinate of the upper-left crop rectangle corner.
21171        \param z0 = Z-coordinate of the upper-left crop rectangle corner.
21172        \param c0 = C-coordinate of the upper-left crop rectangle corner.
21173        \param x1 = X-coordinate of the lower-right crop rectangle corner.
21174        \param y1 = Y-coordinate of the lower-right crop rectangle corner.
21175        \param z1 = Z-coordinate of the lower-right crop rectangle corner.
21176        \param c1 = C-coordinate of the lower-right crop rectangle corner.
21177        \param border_condition = Dirichlet (false) or Neumann border conditions.
21178     **/
21179     CImg<T>& crop(const int x0, const int y0, const int z0, const int c0,
21180                   const int x1, const int y1, const int z1, const int c1,
21181                   const bool border_condition=false) {
21182       return get_crop(x0,y0,z0,c0,x1,y1,z1,c1,border_condition).move_to(*this);
21183     }
21184 
21185     CImg<T> get_crop(const int x0, const int y0, const int z0, const int c0,
21186                      const int x1, const int y1, const int z1, const int c1,
21187                      const bool border_condition=false) const {
21188       if (is_empty()) return *this;
21189       const int
21190         nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0,
21191         ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0,
21192         nz0 = z0<z1?z0:z1, nz1 = z0^z1^nz0,
21193         nc0 = c0<c1?c0:c1, nc1 = c0^c1^nc0;
21194       CImg<T> res(1U + nx1 - nx0,1U + ny1 - ny0,1U + nz1 - nz0,1U + nc1 - nc0);
21195       if (nx0<0 || nx1>=width() || ny0<0 || ny1>=height() || nz0<0 || nz1>=depth() || nc0<0 || nc1>=spectrum()) {
21196         if (border_condition) cimg_forXYZC(res,x,y,z,c) res(x,y,z,c) = _atXYZC(nx0+x,ny0+y,nz0+z,nc0+c);
21197         else res.fill(0).draw_image(-nx0,-ny0,-nz0,-nc0,*this);
21198       } else res.draw_image(-nx0,-ny0,-nz0,-nc0,*this);
21199       return res;
21200     }
21201 
21202     //! Get a rectangular part of the image instance.
21203     /**
21204        \param x0 = X-coordinate of the upper-left crop rectangle corner.
21205        \param y0 = Y-coordinate of the upper-left crop rectangle corner.
21206        \param z0 = Z-coordinate of the upper-left crop rectangle corner.
21207        \param x1 = X-coordinate of the lower-right crop rectangle corner.
21208        \param y1 = Y-coordinate of the lower-right crop rectangle corner.
21209        \param z1 = Z-coordinate of the lower-right crop rectangle corner.
21210        \param border_condition = determine the type of border condition if
21211        some of the desired region is outside the image.
21212     **/
21213     CImg<T>& crop(const int x0, const int y0, const int z0,
21214                   const int x1, const int y1, const int z1,
21215                   const bool border_condition=false) {
21216       return crop(x0,y0,z0,0,x1,y1,z1,_spectrum-1,border_condition);
21217     }
21218 
21219     CImg<T> get_crop(const int x0, const int y0, const int z0,
21220                      const int x1, const int y1, const int z1,
21221                      const bool border_condition=false) const {
21222       return get_crop(x0,y0,z0,0,x1,y1,z1,_spectrum-1,border_condition);
21223     }
21224 
21225     //! Get a rectangular part of the image instance.
21226     /**
21227        \param x0 = X-coordinate of the upper-left crop rectangle corner.
21228        \param y0 = Y-coordinate of the upper-left crop rectangle corner.
21229        \param x1 = X-coordinate of the lower-right crop rectangle corner.
21230        \param y1 = Y-coordinate of the lower-right crop rectangle corner.
21231        \param border_condition = determine the type of border condition if
21232        some of the desired region is outside the image.
21233     **/
21234     CImg<T>& crop(const int x0, const int y0,
21235                   const int x1, const int y1,
21236                   const bool border_condition=false) {
21237       return crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,border_condition);
21238     }
21239 
21240     CImg<T> get_crop(const int x0, const int y0,
21241                      const int x1, const int y1,
21242                      const bool border_condition=false) const {
21243       return get_crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,border_condition);
21244     }
21245 
21246     //! Get a rectangular part of the image instance.
21247     /**
21248        \param x0 = X-coordinate of the upper-left crop rectangle corner.
21249        \param x1 = X-coordinate of the lower-right crop rectangle corner.
21250        \param border_condition = determine the type of border condition if
21251        some of the desired region is outside the image.
21252     **/
21253     CImg<T>& crop(const int x0, const int x1, const bool border_condition=false) {
21254       return crop(x0,0,0,0,x1,_height-1,_depth-1,_spectrum-1,border_condition);
21255     }
21256 
21257     CImg<T> get_crop(const int x0, const int x1, const bool border_condition=false) const {
21258       return get_crop(x0,0,0,0,x1,_height-1,_depth-1,_spectrum-1,border_condition);
21259     }
21260 
21261     //! Autocrop an image, regarding of the specified backround value.
21262     CImg<T>& autocrop(const T value, const char *const axes="czyx") {
21263       if (is_empty()) return *this;
21264       for (const char *s = axes; *s; ++s) {
21265         const char axis = cimg::uncase(*s);
21266         const CImg<intT> coords = _autocrop(value,axis);
21267         switch (axis) {
21268         case 'x' : {
21269           const int x0 = coords[0], x1 = coords[1];
21270           if (x0>=0 && x1>=0) crop(x0,x1);
21271         } break;
21272         case 'y' : {
21273           const int y0 = coords[0], y1 = coords[1];
21274           if (y0>=0 && y1>=0) crop(0,y0,_width-1,y1);
21275         } break;
21276         case 'z' : {
21277           const int z0 = coords[0], z1 = coords[1];
21278           if (z0>=0 && z1>=0) crop(0,0,z0,_width-1,_height-1,z1);
21279         } break;
21280         default : {
21281           const int c0 = coords[0], c1 = coords[1];
21282           if (c0>=0 && c1>=0) crop(0,0,0,c0,_width-1,_height-1,_depth-1,c1);
21283         }
21284         }
21285       }
21286       return *this;
21287     }
21288 
21289     CImg<T> get_autocrop(const T value, const char *const axes="czyx") const {
21290       return (+*this).autocrop(value,axes);
21291     }
21292 
21293     //! Autocrop an image, regarding of the specified backround color.
21294     CImg<T>& autocrop(const T *const color, const char *const axes="zyx") {
21295       if (is_empty()) return *this;
21296       for (const char *s = axes; *s; ++s) {
21297         const char axis = cimg::uncase(*s);
21298         switch (axis) {
21299         case 'x' : {
21300           int x0 = _width, x1 = -1;
21301           cimg_forC(*this,c) {
21302             const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'x');
21303             const int nx0 = coords[0], nx1 = coords[1];
21304             if (nx0>=0 && nx1>=0) { x0 = cimg::min(x0,nx0); x1 = cimg::max(x1,nx1); }
21305           }
21306           if (x0<=x1) crop(x0,x1);
21307         } break;
21308         case 'y' : {
21309           int y0 = _height, y1 = -1;
21310           cimg_forC(*this,c) {
21311             const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'y');
21312             const int ny0 = coords[0], ny1 = coords[1];
21313             if (ny0>=0 && ny1>=0) { y0 = cimg::min(y0,ny0); y1 = cimg::max(y1,ny1); }
21314           }
21315           if (y0<=y1) crop(0,y0,_width-1,y1);
21316         } break;
21317         default : {
21318           int z0 = _depth, z1 = -1;
21319           cimg_forC(*this,c) {
21320             const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'z');
21321             const int nz0 = coords[0], nz1 = coords[1];
21322             if (nz0>=0 && nz1>=0) { z0 = cimg::min(z0,nz0); z1 = cimg::max(z1,nz1); }
21323           }
21324           if (z0<=z1) crop(0,0,z0,_width-1,_height-1,z1);
21325         }
21326         }
21327       }
21328       return *this;
21329     }
21330 
21331     CImg<T> get_autocrop(const T *const color, const char *const axes="zyx") const {
21332       return (+*this).autocrop(color,axes);
21333     }
21334 
21335     //! Autocrop an image, regarding of the specified backround color.
21336     template<typename t> CImg<T>& autocrop(const CImg<t>& color, const char *const axes="zyx") {
21337       return get_autocrop(color,axes).move_to(*this);
21338     }
21339 
21340     template<typename t> CImg<T> get_autocrop(const CImg<t>& color, const char *const axes="zyx") const {
21341       return get_autocrop(color._data,axes);
21342     }
21343 
21344     CImg<intT> _autocrop(const T value, const char axis) const {
21345       CImg<intT> res;
21346       int x0 = -1, y0 = -1, z0 = -1, c0 = -1, x1 = -1, y1 = -1, z1 = -1, c1 = -1;
21347       switch (cimg::uncase(axis)) {
21348       case 'x' : {
21349         cimg_forX(*this,x) cimg_forYZC(*this,y,z,c)
21350           if ((*this)(x,y,z,c)!=value) { x0 = x; x = width(); y = height(); z = depth(); c = spectrum(); }
21351         if (x0>=0) {
21352           for (int x = width()-1; x>=0; --x) cimg_forYZC(*this,y,z,c)
21353             if ((*this)(x,y,z,c)!=value) { x1 = x; x = 0; y = height(); z = depth(); c = spectrum(); }
21354         }
21355         res = CImg<intT>::vector(x0,x1);
21356       } break;
21357       case 'y' : {
21358         cimg_forY(*this,y) cimg_forXZC(*this,x,z,c)
21359           if ((*this)(x,y,z,c)!=value) { y0 = y; x = width(); y = height(); z = depth(); c = spectrum(); }
21360         if (y0>=0) {
21361           for (int y = height()-1; y>=0; --y) cimg_forXZC(*this,x,z,c)
21362             if ((*this)(x,y,z,c)!=value) { y1 = y; x = width(); y = 0; z = depth(); c = spectrum(); }
21363         }
21364         res = CImg<intT>::vector(y0,y1);
21365       } break;
21366       case 'z' : {
21367         cimg_forZ(*this,z) cimg_forXYC(*this,x,y,c)
21368           if ((*this)(x,y,z,c)!=value) { z0 = z; x = width(); y = height(); z = depth(); c = spectrum(); }
21369         if (z0>=0) {
21370           for (int z = depth()-1; z>=0; --z) cimg_forXYC(*this,x,y,c)
21371             if ((*this)(x,y,z,c)!=value) { z1 = z; x = width(); y = height(); z = 0; c = spectrum(); }
21372         }
21373         res = CImg<intT>::vector(z0,z1);
21374       } break;
21375       default : {
21376         cimg_forC(*this,c) cimg_forXYZ(*this,x,y,z)
21377           if ((*this)(x,y,z,c)!=value) { c0 = c; x = width(); y = height(); z = depth(); c = spectrum(); }
21378         if (c0>=0) {
21379           for (int c = spectrum()-1; c>=0; --c) cimg_forXYZ(*this,x,y,z)
21380             if ((*this)(x,y,z,c)!=value) { c1 = c; x = width(); y = height(); z = depth(); c = 0; }
21381         }
21382         res = CImg<intT>::vector(c0,c1);
21383       }
21384       }
21385       return res;
21386     }
21387 
21388     //! Get one column.
21389     CImg<T>& column(const unsigned int x0) {
21390       return columns(x0,x0);
21391     }
21392 
21393     CImg<T> get_column(const unsigned int x0) const {
21394       return get_columns(x0,x0);
21395     }
21396 
21397     //! Get a set of columns.
21398     CImg<T>& columns(const unsigned int x0, const unsigned int x1) {
21399       return get_columns(x0,x1).move_to(*this);
21400     }
21401 
21402     CImg<T> get_columns(const unsigned int x0, const unsigned int x1) const {
21403       return get_crop((int)x0,0,0,0,(int)x1,height()-1,depth()-1,spectrum()-1);
21404     }
21405 
21406     //! Get a line.
21407     CImg<T>& line(const unsigned int y0) {
21408       return lines(y0,y0);
21409     }
21410 
21411     CImg<T> get_line(const unsigned int y0) const {
21412       return get_lines(y0,y0);
21413     }
21414 
21415     //! Get a set of lines.
21416     CImg<T>& lines(const unsigned int y0, const unsigned int y1) {
21417       return get_lines(y0,y1).move_to(*this);
21418     }
21419 
21420     CImg<T> get_lines(const unsigned int y0, const unsigned int y1) const {
21421       return get_crop(0,(int)y0,0,0,width()-1,(int)y1,depth()-1,spectrum()-1);
21422     }
21423 
21424     //! Get a slice.
21425     CImg<T>& slice(const unsigned int z0) {
21426       return slices(z0,z0);
21427     }
21428 
21429     CImg<T> get_slice(const unsigned int z0) const {
21430       return get_slices(z0,z0);
21431     }
21432 
21433     //! Get a set of slices.
21434     CImg<T>& slices(const unsigned int z0, const unsigned int z1) {
21435       return get_slices(z0,z1).move_to(*this);
21436     }
21437 
21438     CImg<T> get_slices(const unsigned int z0, const unsigned int z1) const {
21439       return get_crop(0,0,(int)z0,0,width()-1,height()-1,(int)z1,spectrum()-1);
21440     }
21441 
21442     //! Get a channel.
21443     CImg<T>& channel(const unsigned int c0) {
21444       return channels(c0,c0);
21445     }
21446 
21447     CImg<T> get_channel(const unsigned int c0) const {
21448       return get_channels(c0,c0);
21449     }
21450 
21451     //! Get a set of channels.
21452     CImg<T>& channels(const unsigned int c0, const unsigned int c1) {
21453       return get_channels(c0,c1).move_to(*this);
21454     }
21455 
21456     CImg<T> get_channels(const unsigned int c0, const unsigned int c1) const {
21457       return get_crop(0,0,0,(int)c0,width()-1,height()-1,depth()-1,(int)c1);
21458     }
21459 
21460     //! Get a shared-memory image referencing a set of points of the image instance.
21461     CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
21462                               const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) {
21463       const unsigned int beg = (unsigned int)offset(x0,y0,z0,c0), end = offset(x1,y0,z0,c0);
21464       if (beg>end || beg>=size() || end>=size())
21465         throw CImgArgumentException(_cimg_instance
21466                                     "get_shared_points() : Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).",
21467                                     cimg_instance,
21468                                     x0,x1,y0,z0,c0);
21469 
21470       return CImg<T>(_data+beg,x1-x0+1,1,1,1,true);
21471     }
21472 
21473     const CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
21474                                     const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) const {
21475       const unsigned int beg = (unsigned int)offset(x0,y0,z0,c0), end = offset(x1,y0,z0,c0);
21476       if (beg>end || beg>=size() || end>=size())
21477         throw CImgArgumentException(_cimg_instance
21478                                     "get_shared_points() : Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).",
21479                                     cimg_instance,
21480                                     x0,x1,y0,z0,c0);
21481 
21482       return CImg<T>(_data+beg,x1-x0+1,1,1,1,true);
21483     }
21484 
21485     //! Return a shared-memory image referencing a set of lines of the image instance.
21486     CImg<T> get_shared_lines(const unsigned int y0, const unsigned int y1,
21487                              const unsigned int z0=0, const unsigned int c0=0) {
21488       const unsigned int beg = offset(0,y0,z0,c0), end = offset(0,y1,z0,c0);
21489       if (beg>end || beg>=size() || end>=size())
21490         throw CImgArgumentException(_cimg_instance
21491                                     "get_shared_lines() : Invalid request of a shared-memory subset (0->%u,%u->%u,%u,%u).",
21492                                     cimg_instance,
21493                                     _width-1,y0,y1,z0,c0);
21494 
21495       return CImg<T>(_data+beg,_width,y1-y0+1,1,1,true);
21496     }
21497 
21498     const CImg<T> get_shared_lines(const unsigned int y0, const unsigned int y1,
21499                                    const unsigned int z0=0, const unsigned int c0=0) const {
21500       const unsigned int beg = offset(0,y0,z0,c0), end = offset(0,y1,z0,c0);
21501       if (beg>end || beg>=size() || end>=size())
21502         throw CImgArgumentException(_cimg_instance
21503                                     "get_shared_lines() : Invalid request of a shared-memory subset (0->%u,%u->%u,%u,%u).",
21504                                     cimg_instance,
21505                                     _width-1,y0,y1,z0,c0);
21506 
21507       return CImg<T>(_data+beg,_width,y1-y0+1,1,1,true);
21508     }
21509 
21510     //! Return a shared-memory image referencing one particular line (y0,z0,c0) of the image instance.
21511     CImg<T> get_shared_line(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) {
21512       return get_shared_lines(y0,y0,z0,c0);
21513     }
21514 
21515     const CImg<T> get_shared_line(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) const {
21516       return get_shared_lines(y0,y0,z0,c0);
21517     }
21518 
21519     //! Return a shared memory image referencing a set of planes (z0->z1,c0) of the image instance.
21520     CImg<T> get_shared_planes(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) {
21521       const unsigned int beg = offset(0,0,z0,c0), end = offset(0,0,z1,c0);
21522       if (beg>end || beg>=size() || end>=size())
21523         throw CImgArgumentException(_cimg_instance
21524                                     "get_shared_planes() : Invalid request of a shared-memory subset (0->%u,0->%u,%u->%u,%u).",
21525                                     cimg_instance,
21526                                     _width-1,_height-1,z0,z1,c0);
21527 
21528       return CImg<T>(_data+beg,_width,_height,z1-z0+1,1,true);
21529     }
21530 
21531     const CImg<T> get_shared_planes(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) const {
21532       const unsigned int beg = offset(0,0,z0,c0), end = offset(0,0,z1,c0);
21533       if (beg>end || beg>=size() || end>=size())
21534         throw CImgArgumentException(_cimg_instance
21535                                     "get_shared_planes() : Invalid request of a shared-memory subset (0->%u,0->%u,%u->%u,%u).",
21536                                     cimg_instance,
21537                                     _width-1,_height-1,z0,z1,c0);
21538 
21539       return CImg<T>(_data+beg,_width,_height,z1-z0+1,1,true);
21540     }
21541 
21542     //! Return a shared-memory image referencing one plane (z0,c0) of the image instance.
21543     CImg<T> get_shared_plane(const unsigned int z0, const unsigned int c0=0) {
21544       return get_shared_planes(z0,z0,c0);
21545     }
21546 
21547     const CImg<T> get_shared_plane(const unsigned int z0, const unsigned int c0=0) const {
21548       return get_shared_planes(z0,z0,c0);
21549     }
21550 
21551     //! Return a shared-memory image referencing a set of channels (c0->c1) of the image instance.
21552     CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) {
21553       const unsigned int beg = offset(0,0,0,c0), end = offset(0,0,0,c1);
21554       if (beg>end || beg>=size() || end>=size())
21555         throw CImgArgumentException(_cimg_instance
21556                                     "get_shared_channels() : Invalid request of a shared-memory subset (0->%u,0->%u,0->%u,%u->%u).",
21557                                     cimg_instance,
21558                                     _width-1,_height-1,_depth-1,c0,c1);
21559 
21560       return CImg<T>(_data+beg,_width,_height,_depth,c1-c0+1,true);
21561     }
21562 
21563     const CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) const {
21564       const unsigned int beg = offset(0,0,0,c0), end = offset(0,0,0,c1);
21565       if (beg>end || beg>=size() || end>=size())
21566         throw CImgArgumentException(_cimg_instance
21567                                     "get_shared_channels() : Invalid request of a shared-memory subset (0->%u,0->%u,0->%u,%u->%u).",
21568                                     cimg_instance,
21569                                     _width-1,_height-1,_depth-1,c0,c1);
21570 
21571       return CImg<T>(_data+beg,_width,_height,_depth,c1-c0+1,true);
21572     }
21573 
21574     //! Return a shared-memory image referencing one channel c0 of the image instance.
21575     CImg<T> get_shared_channel(const unsigned int c0) {
21576       return get_shared_channels(c0,c0);
21577     }
21578 
21579     const CImg<T> get_shared_channel(const unsigned int c0) const {
21580       return get_shared_channels(c0,c0);
21581     }
21582 
21583     //! Return a shared version of the image instance.
21584     CImg<T> get_shared() {
21585       return CImg<T>(_data,_width,_height,_depth,_spectrum,true);
21586     }
21587 
21588     const CImg<T> get_shared() const {
21589       return CImg<T>(_data,_width,_height,_depth,_spectrum,true);
21590     }
21591 
21592     //! Split image into a list.
21593     CImgList<T> get_split(const char axis, const int nb=0) const {
21594       CImgList<T> res;
21595       const char naxis = cimg::uncase(axis);
21596       const unsigned int nnb = (unsigned int)(nb==0?1:(nb>0?nb:-nb));
21597       if (nb<=0) switch (naxis) { // Split by bloc size
21598       case 'x': {
21599         for (unsigned int p = 0; p<_width; p+=nnb) get_crop(p,0,0,0,cimg::min(p+nnb-1,_width-1),_height-1,_depth-1,_spectrum-1).move_to(res);
21600       } break;
21601       case 'y': {
21602         for (unsigned int p = 0; p<_height; p+=nnb) get_crop(0,p,0,0,_width-1,cimg::min(p+nnb-1,_height-1),_depth-1,_spectrum-1).move_to(res);
21603       } break;
21604       case 'z': {
21605         for (unsigned int p = 0; p<_depth; p+=nnb) get_crop(0,0,p,0,_width-1,_height-1,cimg::min(p+nnb-1,_depth-1),_spectrum-1).move_to(res);
21606       } break;
21607       default: {
21608         for (unsigned int p = 0; p<_spectrum; p+=nnb) get_crop(0,0,0,p,_width-1,_height-1,_depth-1,cimg::min(p+nnb-1,_spectrum-1)).move_to(res);
21609       }
21610       } else { // Split by number of blocs
21611         const unsigned int siz = naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:naxis=='c'?_spectrum:0;
21612         if (nnb>siz)
21613           throw CImgArgumentException(_cimg_instance
21614                                       "get_split() : Instance cannot be split along %c-axis into %u blocs.",
21615                                       cimg_instance,
21616                                       axis,nnb);
21617         res.assign(nnb);
21618         switch (naxis) {
21619         case 'x' : {
21620           cimglist_for(res,p) get_crop(p*siz/nnb,0,0,0,(p+1)*siz/nnb-1,_height-1,_depth-1,_spectrum-1).move_to(res[p]);
21621         } break;
21622         case 'y' : {
21623           cimglist_for(res,p) get_crop(0,p*siz/nnb,0,0,_width-1,(p+1)*siz/nnb-1,_depth-1,_spectrum-1).move_to(res[p]);
21624         } break;
21625         case 'z' : {
21626           cimglist_for(res,p) get_crop(0,0,p*siz/nnb,0,_width-1,_height-1,(p+1)*siz/nnb-1,_spectrum-1).move_to(res[p]);
21627         } break;
21628         default : {
21629           cimglist_for(res,p) get_crop(0,0,0,p*siz/nnb,_width-1,_height-1,_depth-1,(p+1)*siz/nnb-1).move_to(res[p]);
21630         }
21631         }
21632       }
21633       return res;
21634     }
21635 
21636     //! Split image into a list of one-column vectors, according to specified splitting value.
21637     CImgList<T> get_split(const T value, const bool keep_values, const bool is_shared) const {
21638       CImgList<T> res;
21639       for (const T *ps = _data, *_ps = ps, *const pe = end(); ps<pe; ) {
21640         while (_ps<pe && *_ps==value) ++_ps;
21641         unsigned int siz = _ps - ps;
21642         if (siz && keep_values) res.insert(CImg<T>(ps,1,siz,1,1,is_shared),~0U,is_shared);
21643         ps = _ps;
21644         while (_ps<pe && *_ps!=value) ++_ps;
21645         siz = _ps - ps;
21646         if (siz) res.insert(CImg<T>(ps,1,siz,1,1,is_shared),~0U,is_shared);
21647         ps = _ps;
21648       }
21649       return res;
21650     }
21651 
21652     //! Split image into a list of one-column vectors, according to specified sequence of splitting values.
21653     /**
21654        \param values The splitting pattern of values.
21655        \param keep_values Can be :
21656           - false : Discard splitting values in resulting list.
21657           - true : Keep splitting values as separate images in resulting list.
21658      **/
21659     template<typename t>
21660     CImgList<T> get_split(const CImg<t>& values, const bool keep_values, const bool is_shared) const {
21661       if (!values) return CImgList<T>(*this);
21662       if (values.size()==1) return get_split(*values,keep_values,is_shared);
21663       CImgList<T> res;
21664       const t *pve = values.end();
21665       for (const T *ps = _data, *_ps = ps, *const pe = end(); ps<pe; ) {
21666 
21667         // Try to find match from current position.
21668         const t *pv = 0;
21669         do {
21670           pv = values._data;
21671           const T *__ps = _ps;
21672           while (__ps<pe && pv<pve && *__ps==(T)*pv) { ++__ps; ++pv; }
21673           if (pv==pve) _ps = __ps;
21674         } while (pv==pve);
21675         unsigned int siz = _ps - ps;
21676         if (siz && keep_values) res.insert(CImg<T>(ps,1,siz,1,1,is_shared),~0U,is_shared); // If match found.
21677         ps = _ps;
21678 
21679         // Try to find non-match from current position.
21680         do {
21681           pv = values._data;
21682           while (_ps<pe && *_ps!=(T)*pv) ++_ps;
21683           if (_ps<pe) {
21684             const T *__ps = _ps + 1;
21685             ++pv;
21686             while (__ps<pe && pv<pve && *__ps==(T)*pv) { ++__ps; ++pv; }
21687             if (pv!=pve) _ps = __ps;
21688           }
21689         } while (_ps<pe && pv!=pve);
21690 
21691         // Here, EOF of match found.
21692         siz = _ps - ps;
21693         if (siz) res.insert(CImg<T>(ps,1,siz,1,1,is_shared),~0U,is_shared);
21694         ps = _ps;
21695       }
21696       return res;
21697     }
21698 
21699     //! Append an image.
21700     template<typename t>
21701     CImg<T>& append(const CImg<t>& img, const char axis='x', const float align=0) {
21702       if (is_empty()) return assign(img,false);
21703       if (!img) return *this;
21704       return CImgList<T>(*this,true).insert(img).get_append(axis,align).move_to(*this);
21705     }
21706 
21707     CImg<T>& append(const CImg<T>& img, const char axis='x', const float align=0) {
21708       if (is_empty()) return assign(img,false);
21709       if (!img) return *this;
21710       return CImgList<T>(*this,img,true).get_append(axis,align).move_to(*this);
21711     }
21712 
21713     template<typename t>
21714     CImg<_cimg_Tt> get_append(const CImg<T>& img, const char axis='x', const float align=0) const {
21715       if (is_empty()) return +img;
21716       if (!img) return +*this;
21717       return CImgList<_cimg_Tt>(*this,true).insert(img).get_append(axis,align);
21718     }
21719 
21720     CImg<T> get_append(const CImg<T>& img, const char axis='x', const float align=0) const {
21721       if (is_empty()) return +img;
21722       if (!img) return +*this;
21723       return CImgList<T>(*this,img,true).get_append(axis,align);
21724     }
21725 
21726     //@}
21727     //---------------------------------------
21728     //
21729     //! \name Filtering / Transforms
21730     //@{
21731     //---------------------------------------
21732 
21733     //! Compute the correlation of the image instance by a mask.
21734     /**
21735        The correlation of the image instance \p *this by the mask \p mask is defined to be :
21736 
21737        res(x,y,z) = sum_{i,j,k} (*this)(x+i,y+j,z+k)*mask(i,j,k)
21738 
21739        \param mask = the correlation kernel.
21740        \param border_conditions = the border condition type (0=zero, 1=dirichlet)
21741        \param is_normalized = enable local normalization.
21742     **/
21743     template<typename t>
21744     CImg<T>& correlate(const CImg<t>& mask, const unsigned int border_conditions=1, const bool is_normalized=false) {
21745       if (is_empty() || !mask) return *this;
21746       return get_correlate(mask,border_conditions,is_normalized).move_to(*this);
21747     }
21748 
21749     template<typename t>
21750     CImg<_cimg_Ttfloat> get_correlate(const CImg<t>& mask, const unsigned int border_conditions=1,
21751                                       const bool is_normalized=false) const {
21752       if (is_empty() || !mask) return *this;
21753       typedef _cimg_Ttfloat Ttfloat;
21754       CImg<Ttfloat> res(_width,_height,_depth,cimg::max(_spectrum,mask._spectrum));
21755       if (border_conditions && mask._width==mask._height && ((mask._depth==1 && mask._width<=5) || (mask._depth==mask._width && mask._width<=3))) {
21756         // A special optimization is done for 2x2, 3x3, 4x4, 5x5, 2x2x2 and 3x3x3 mask (with border_conditions=1)
21757         Ttfloat *ptrd = res._data;
21758         switch (mask._depth) {
21759         case 3 : {
21760           T I[27] = { 0 };
21761           cimg_forC(res,c) {
21762             const CImg<T> _img = get_shared_channel(c%_spectrum);
21763             const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
21764             if (is_normalized) {
21765               const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
21766               cimg_forZ(_img,z) cimg_for3x3x3(_img,x,y,z,0,I,T) {
21767                 const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] +
21768                                      I[ 3]*I[ 3] + I[ 4]*I[ 4] + I[ 5]*I[ 5] +
21769                                      I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] +
21770                                      I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] +
21771                                      I[12]*I[12] + I[13]*I[13] + I[14]*I[14] +
21772                                      I[15]*I[15] + I[16]*I[16] + I[17]*I[17] +
21773                                      I[18]*I[18] + I[19]*I[19] + I[20]*I[20] +
21774                                      I[21]*I[21] + I[22]*I[22] + I[23]*I[23] +
21775                                      I[24]*I[24] + I[25]*I[25] + I[26]*I[26]);
21776                 *(ptrd++) = (Ttfloat)(N?(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] +
21777                                          I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] +
21778                                          I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] +
21779                                          I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] +
21780                                          I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] +
21781                                          I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] +
21782                                          I[18]*_mask[18] + I[19]*_mask[19] + I[20]*_mask[20] +
21783                                          I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] +
21784                                          I[24]*_mask[24] + I[25]*_mask[25] + I[26]*_mask[26])/std::sqrt(N):0);
21785               }
21786             } else cimg_forZ(_img,z) cimg_for3x3x3(_img,x,y,z,0,I,T)
21787                      *(ptrd++) = (Ttfloat)(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] +
21788                                            I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] +
21789                                            I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] +
21790                                            I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] +
21791                                            I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] +
21792                                            I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] +
21793                                            I[18]*_mask[18] + I[19]*_mask[19] + I[20]*_mask[20] +
21794                                            I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] +
21795                                            I[24]*_mask[24] + I[25]*_mask[25] + I[26]*_mask[26]);
21796           }
21797         } break;
21798         case 2 : {
21799           T I[8] = { 0 };
21800           cimg_forC(res,c) {
21801             const CImg<T> _img = get_shared_channel(c%_spectrum);
21802             const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
21803             if (is_normalized) {
21804               const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
21805               cimg_forZ(_img,z) cimg_for2x2x2(_img,x,y,z,0,I,T) {
21806                 const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] +
21807                                      I[2]*I[2] + I[3]*I[3] +
21808                                      I[4]*I[4] + I[5]*I[5] +
21809                                      I[6]*I[6] + I[7]*I[7]);
21810                 *(ptrd++) = (Ttfloat)(N?(I[0]*_mask[0] + I[1]*_mask[1] +
21811                                          I[2]*_mask[2] + I[3]*_mask[3] +
21812                                          I[4]*_mask[4] + I[5]*_mask[5] +
21813                                          I[6]*_mask[6] + I[7]*_mask[7])/std::sqrt(N):0);
21814               }
21815             } else cimg_forZ(_img,z) cimg_for2x2x2(_img,x,y,z,0,I,T)
21816                      *(ptrd++) = (Ttfloat)(I[0]*_mask[0] + I[1]*_mask[1] +
21817                                            I[2]*_mask[2] + I[3]*_mask[3] +
21818                                            I[4]*_mask[4] + I[5]*_mask[5] +
21819                                            I[6]*_mask[6] + I[7]*_mask[7]);
21820           }
21821         } break;
21822         default :
21823         case 1 :
21824           switch (mask._width) {
21825           case 6 : {
21826             T I[36] = { 0 };
21827             cimg_forC(res,c) {
21828               const CImg<T> _img = get_shared_channel(c%_spectrum);
21829               const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
21830               if (is_normalized) {
21831                 const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
21832                 cimg_forZ(_img,z) cimg_for6x6(_img,x,y,z,0,I,T) {
21833                   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] +
21834                                        I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] +
21835                                        I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + I[15]*I[15] + I[16]*I[16] + I[17]*I[17] +
21836                                        I[18]*I[18] + I[19]*I[19] + I[20]*I[20] + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] +
21837                                        I[24]*I[24] + I[25]*I[25] + I[26]*I[26] + I[27]*I[27] + I[28]*I[28] + I[29]*I[29] +
21838                                        I[30]*I[30] + I[31]*I[31] + I[32]*I[32] + I[33]*I[33] + I[34]*I[34] + I[35]*I[35]);
21839                   *(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] +
21840                                            I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] +
21841                                            I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] + I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] +
21842                                            I[18]*_mask[18] + I[19]*_mask[19] + I[20]*_mask[20] + I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] +
21843                                            I[24]*_mask[24] + I[25]*_mask[25] + I[26]*_mask[26] + I[27]*_mask[27] + I[28]*_mask[28] + I[29]*_mask[29] +
21844                                            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);
21845                 }
21846               } else cimg_forZ(_img,z) cimg_for6x6(_img,x,y,z,0,I,T)
21847                        *(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] +
21848                                              I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] +
21849                                              I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] + I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] +
21850                                              I[18]*_mask[18] + I[19]*_mask[19] + I[20]*_mask[20] + I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] +
21851                                              I[24]*_mask[24] + I[25]*_mask[25] + I[26]*_mask[26] + I[27]*_mask[27] + I[28]*_mask[28] + I[29]*_mask[29] +
21852                                              I[30]*_mask[30] + I[31]*_mask[31] + I[32]*_mask[32] + I[33]*_mask[33] + I[34]*_mask[34] + I[35]*_mask[35]);
21853             }
21854           } break;
21855           case 5 : {
21856             T I[25] = { 0 };
21857             cimg_forC(res,c) {
21858               const CImg<T> _img = get_shared_channel(c%_spectrum);
21859               const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
21860               if (is_normalized) {
21861                 const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
21862                 cimg_forZ(_img,z) cimg_for5x5(_img,x,y,z,0,I,T) {
21863                   const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] +
21864                                        I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] +
21865                                        I[10]*I[10] + I[11]*I[11] + I[12]*I[12] + I[13]*I[13] + I[14]*I[14] +
21866                                        I[15]*I[15] + I[16]*I[16] + I[17]*I[17] + I[18]*I[18] + I[19]*I[19] +
21867                                        I[20]*I[20] + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] + I[24]*I[24]);
21868                   *(ptrd++) = (Ttfloat)(N?(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] +
21869                                            I[ 5]*_mask[ 5] + I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] +
21870                                            I[10]*_mask[10] + I[11]*_mask[11] + I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] +
21871                                            I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] + I[18]*_mask[18] + I[19]*_mask[19] +
21872                                            I[20]*_mask[20] + I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] + I[24]*_mask[24])/std::sqrt(N):0);
21873                 }
21874               } else cimg_forZ(_img,z) cimg_for5x5(_img,x,y,z,0,I,T)
21875                        *(ptrd++) = (Ttfloat)(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] +
21876                                              I[ 5]*_mask[ 5] + I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] +
21877                                              I[10]*_mask[10] + I[11]*_mask[11] + I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] +
21878                                              I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] + I[18]*_mask[18] + I[19]*_mask[19] +
21879                                              I[20]*_mask[20] + I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] + I[24]*_mask[24]);
21880             }
21881           } break;
21882           case 4 : {
21883             T I[16] = { 0 };
21884             cimg_forC(res,c) {
21885               const CImg<T> _img = get_shared_channel(c%_spectrum);
21886               const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
21887               if (is_normalized) {
21888                 const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
21889                 cimg_forZ(_img,z) cimg_for4x4(_img,x,y,z,0,I,T) {
21890                   const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] +
21891                                        I[ 4]*I[ 4] + I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] +
21892                                        I[ 8]*I[ 8] + I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] +
21893                                        I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + I[15]*I[15]);
21894                   *(ptrd++) = (Ttfloat)(N?(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] +
21895                                            I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] + I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] +
21896                                            I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] +
21897                                            I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] + I[15]*_mask[15])/std::sqrt(N):0);
21898                 }
21899               } else cimg_forZ(_img,z) cimg_for4x4(_img,x,y,z,0,I,T)
21900                        *(ptrd++) = (Ttfloat)(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] +
21901                                              I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] + I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] +
21902                                              I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] +
21903                                              I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] + I[15]*_mask[15]);
21904             }
21905           } break;
21906           case 3 : {
21907             T I[9] = { 0 };
21908             cimg_forC(res,c) {
21909               const CImg<T> _img = get_shared_channel(c%_spectrum);
21910               const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
21911               if (is_normalized) {
21912                 const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
21913                 cimg_forZ(_img,z) cimg_for3x3(_img,x,y,z,0,I,T) {
21914                   const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] + I[2]*I[2] +
21915                                        I[3]*I[3] + I[4]*I[4] + I[5]*I[5] +
21916                                        I[6]*I[6] + I[7]*I[7] + I[8]*I[8]);
21917                   *(ptrd++) = (Ttfloat)(N?(I[0]*_mask[0] + I[1]*_mask[1] + I[2]*_mask[2] +
21918                                            I[3]*_mask[3] + I[4]*_mask[4] + I[5]*_mask[5] +
21919                                            I[6]*_mask[6] + I[7]*_mask[7] + I[8]*_mask[8])/std::sqrt(N):0);
21920                 }
21921               } else cimg_forZ(_img,z) cimg_for3x3(_img,x,y,z,0,I,T)
21922                        *(ptrd++) = (Ttfloat)(I[0]*_mask[0] + I[1]*_mask[1] + I[2]*_mask[2] +
21923                                              I[3]*_mask[3] + I[4]*_mask[4] + I[5]*_mask[5] +
21924                                              I[6]*_mask[6] + I[7]*_mask[7] + I[8]*_mask[8]);
21925             }
21926           } break;
21927           case 2 : {
21928             T I[4] = { 0 };
21929             cimg_forC(res,c) {
21930               const CImg<T> _img = get_shared_channel(c%_spectrum);
21931               const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
21932               if (is_normalized) {
21933                 const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
21934                 cimg_forZ(_img,z) cimg_for2x2(_img,x,y,z,0,I,T) {
21935                   const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] +
21936                                        I[2]*I[2] + I[3]*I[3]);
21937                   *(ptrd++) = (Ttfloat)(N?(I[0]*_mask[0] + I[1]*_mask[1] +
21938                                            I[2]*_mask[2] + I[3]*_mask[3])/std::sqrt(N):0);
21939                 }
21940               } else cimg_forZ(_img,z) cimg_for2x2(_img,x,y,z,0,I,T)
21941                        *(ptrd++) = (Ttfloat)(I[0]*_mask[0] + I[1]*_mask[1] +
21942                                              I[2]*_mask[2] + I[3]*_mask[3]);
21943             }
21944           } break;
21945           case 1 :
21946             if (is_normalized) res.fill(1);
21947             else cimg_forC(res,c) {
21948                 const CImg<T> _img = get_shared_channel(c%_spectrum);
21949                 const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
21950                 res.get_shared_channel(c).assign(_img)*=_mask[0];
21951               }
21952             break;
21953           }
21954         }
21955       } else { // Generic version for other masks and borders conditions.
21956         const int
21957           mx2 = mask.width()/2, my2 = mask.height()/2, mz2 = mask.depth()/2,
21958           mx1 = mx2 - 1 + (mask.width()%2), my1 = my2 - 1 + (mask.height()%2), mz1 = mz2 - 1 + (mask.depth()%2),
21959           mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
21960         cimg_forC(res,c) {
21961           const CImg<T> _img = get_shared_channel(c%_spectrum);
21962           const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
21963           if (is_normalized) { // Normalized correlation.
21964             const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
21965             for (int z = mz1; z<mze; ++z)
21966               for (int y = my1; y<mye; ++y)
21967                 for (int x = mx1; x<mxe; ++x) {
21968                   Ttfloat val = 0, N = 0;
21969                   for (int zm = -mz1; zm<=mz2; ++zm)
21970                     for (int ym = -my1; ym<=my2; ++ym)
21971                       for (int xm = -mx1; xm<=mx2; ++xm) {
21972                         const Ttfloat _val = (Ttfloat)_img(x+xm,y+ym,z+zm);
21973                         val+=_val*_mask(mx1+xm,my1+ym,mz1+zm);
21974                         N+=_val*_val;
21975                       }
21976                   N*=M;
21977                   res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0);
21978                 }
21979             if (border_conditions)
21980               cimg_forYZ(res,y,z)
21981                 for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
21982                   Ttfloat val = 0, N = 0;
21983                   for (int zm = -mz1; zm<=mz2; ++zm)
21984                     for (int ym = -my1; ym<=my2; ++ym)
21985                       for (int xm = -mx1; xm<=mx2; ++xm) {
21986                         const Ttfloat _val = (Ttfloat)_img._atXYZ(x+xm,y+ym,z+zm);
21987                         val+=_val*_mask(mx1+xm,my1+ym,mz1+zm);
21988                         N+=_val*_val;
21989                       }
21990                   N*=M;
21991                   res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0);
21992                 }
21993             else
21994               cimg_forYZ(res,y,z)
21995                 for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
21996                   Ttfloat val = 0, N = 0;
21997                   for (int zm = -mz1; zm<=mz2; ++zm)
21998                     for (int ym = -my1; ym<=my2; ++ym)
21999                       for (int xm = -mx1; xm<=mx2; ++xm) {
22000                         const Ttfloat _val = (Ttfloat)_img.atXYZ(x+xm,y+ym,z+zm,0,0);
22001                         val+=_val*_mask(mx1+xm,my1+ym,mz1+zm);
22002                         N+=_val*_val;
22003                       }
22004                   N*=M;
22005                   res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0);
22006                 }
22007           } else { // Classical correlation.
22008             for (int z = mz1; z<mze; ++z)
22009               for (int y = my1; y<mye; ++y)
22010                 for (int x = mx1; x<mxe; ++x) {
22011                   Ttfloat val = 0;
22012                   for (int zm = -mz1; zm<=mz2; ++zm)
22013                     for (int ym = -my1; ym<=my2; ++ym)
22014                       for (int xm = -mx1; xm<=mx2; ++xm)
22015                         val+=_img(x+xm,y+ym,z+zm)*_mask(mx1+xm,my1+ym,mz1+zm);
22016                   res(x,y,z,c) = (Ttfloat)val;
22017                 }
22018             if (border_conditions)
22019               cimg_forYZ(res,y,z)
22020                 for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
22021                   Ttfloat val = 0;
22022                   for (int zm = -mz1; zm<=mz2; ++zm)
22023                     for (int ym = -my1; ym<=my2; ++ym)
22024                       for (int xm = -mx1; xm<=mx2; ++xm)
22025                         val+=_img._atXYZ(x+xm,y+ym,z+zm)*_mask(mx1+xm,my1+ym,mz1+zm);
22026                   res(x,y,z,c) = (Ttfloat)val;
22027                 }
22028             else
22029               cimg_forYZ(res,y,z)
22030                 for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
22031                   Ttfloat val = 0;
22032                   for (int zm = -mz1; zm<=mz2; ++zm)
22033                     for (int ym = -my1; ym<=my2; ++ym)
22034                       for (int xm = -mx1; xm<=mx2; ++xm)
22035                         val+=_img.atXYZ(x+xm,y+ym,z+zm,0,0)*_mask(mx1+xm,my1+ym,mz1+zm);
22036                   res(x,y,z,c) = (Ttfloat)val;
22037                 }
22038           }
22039         }
22040       }
22041       return res;
22042     }
22043 
22044     //! Compute the convolution of the image by a mask.
22045     /**
22046        The result \p res of the convolution of an image \p img by a mask \p mask is defined to be :
22047 
22048        res(x,y,z) = sum_{i,j,k} img(x-i,y-j,z-k)*mask(i,j,k)
22049 
22050        \param mask = the correlation kernel.
22051        \param border_conditions = the border condition type (0=zero, 1=dirichlet)
22052        \param is_normalized = enable local normalization.
22053     **/
22054     template<typename t>
22055     CImg<T>& convolve(const CImg<t>& mask, const unsigned int border_conditions=1, const bool is_normalized=false) {
22056       if (is_empty() || !mask) return *this;
22057       return get_convolve(mask,border_conditions,is_normalized).move_to(*this);
22058     }
22059 
22060     template<typename t>
22061     CImg<_cimg_Ttfloat> get_convolve(const CImg<t>& mask, const unsigned int border_conditions=1,
22062                                      const bool is_normalized=false) const {
22063       if (is_empty() || !mask) return *this;
22064       return get_correlate(CImg<t>(mask._data,mask.size(),1,1,1,true).get_mirror('x').resize(mask,-1),border_conditions,is_normalized);
22065     }
22066 
22067     //! Return the erosion of the image by a structuring element.
22068     template<typename t>
22069     CImg<T>& erode(const CImg<t>& mask, const unsigned int border_conditions=1, const bool is_normalized=false) {
22070       if (is_empty() || !mask) return *this;
22071       return get_erode(mask,border_conditions,is_normalized).move_to(*this);
22072     }
22073 
22074     template<typename t>
22075     CImg<_cimg_Tt> get_erode(const CImg<t>& mask, const unsigned int border_conditions=1,
22076                              const bool is_normalized=false) const {
22077       if (is_empty() || !mask) return *this;
22078       typedef _cimg_Tt Tt;
22079       CImg<Tt> res(_width,_height,_depth,cimg::max(_spectrum,mask._spectrum));
22080       const int
22081         mx2 = mask.width()/2, my2 = mask.height()/2, mz2 = mask.depth()/2,
22082         mx1 = mx2 - 1 + (mask.width()%2), my1 = my2 - 1 + (mask.height()%2), mz1 = mz2 - 1 + (mask.depth()%2),
22083         mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
22084       cimg_forC(*this,c) {
22085         const CImg<T> _img = get_shared_channel(c%_spectrum);
22086         const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
22087         if (is_normalized) { // Normalized erosion.
22088           for (int z = mz1; z<mze; ++z)
22089             for (int y = my1; y<mye; ++y)
22090               for (int x = mx1; x<mxe; ++x) {
22091                 Tt min_val = cimg::type<Tt>::max();
22092                 for (int zm = -mz1; zm<=mz2; ++zm)
22093                   for (int ym = -my1; ym<=my2; ++ym)
22094                     for (int xm = -mx1; xm<=mx2; ++xm) {
22095                       const t mval = _mask(mx1+xm,my1+ym,mz1+zm);
22096                       const Tt cval = (Tt)(_img(x+xm,y+ym,z+zm) + mval);
22097                       if (mval && cval<min_val) min_val = cval;
22098                     }
22099                 res(x,y,z,c) = min_val;
22100               }
22101           if (border_conditions)
22102             cimg_forYZ(res,y,z)
22103               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
22104                 Tt min_val = cimg::type<Tt>::max();
22105                 for (int zm = -mz1; zm<=mz2; ++zm)
22106                   for (int ym = -my1; ym<=my2; ++ym)
22107                     for (int xm = -mx1; xm<=mx2; ++xm) {
22108                       const t mval = _mask(mx1+xm,my1+ym,mz1+zm);
22109                       const Tt cval = (Tt)(_img._atXYZ(x+xm,y+ym,z+zm) + mval);
22110                       if (mval && cval<min_val) min_val = cval;
22111                     }
22112                 res(x,y,z,c) = min_val;
22113               }
22114           else
22115             cimg_forYZ(res,y,z)
22116               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
22117                 Tt min_val = cimg::type<Tt>::max();
22118                 for (int zm = -mz1; zm<=mz2; ++zm)
22119                   for (int ym = -my1; ym<=my2; ++ym)
22120                     for (int xm = -mx1; xm<=mx2; ++xm) {
22121                       const t mval = _mask(mx1+xm,my1+ym,mz1+zm);
22122                       const Tt cval = (Tt)(_img.atXYZ(x+xm,y+ym,z+zm,0,0) + mval);
22123                       if (mval && cval<min_val) min_val = cval;
22124                     }
22125                 res(x,y,z,c) = min_val;
22126               }
22127         } else { // Classical erosion.
22128           for (int z = mz1; z<mze; ++z)
22129             for (int y = my1; y<mye; ++y)
22130               for (int x = mx1; x<mxe; ++x) {
22131                 Tt min_val = cimg::type<Tt>::max();
22132                 for (int zm = -mz1; zm<=mz2; ++zm)
22133                   for (int ym = -my1; ym<=my2; ++ym)
22134                     for (int xm = -mx1; xm<=mx2; ++xm) {
22135                       const Tt cval = (Tt)_img(x+xm,y+ym,z+zm);
22136                       if (_mask(mx1+xm,my1+ym,mz1+zm) && cval<min_val) min_val = cval;
22137                     }
22138                 res(x,y,z,c) = min_val;
22139               }
22140           if (border_conditions)
22141             cimg_forYZ(res,y,z)
22142               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
22143                 Tt min_val = cimg::type<Tt>::max();
22144                 for (int zm = -mz1; zm<=mz2; ++zm)
22145                   for (int ym = -my1; ym<=my2; ++ym)
22146                     for (int xm = -mx1; xm<=mx2; ++xm) {
22147                       const T cval = (Tt)_img._atXYZ(x+xm,y+ym,z+zm);
22148                       if (_mask(mx1+xm,my1+ym,mz1+zm) && cval<min_val) min_val = cval;
22149                     }
22150                 res(x,y,z,c) = min_val;
22151               }
22152           else
22153             cimg_forYZ(res,y,z)
22154               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
22155                 Tt min_val = cimg::type<Tt>::max();
22156                 for (int zm = -mz1; zm<=mz2; ++zm)
22157                   for (int ym = -my1; ym<=my2; ++ym)
22158                     for (int xm = -mx1; xm<=mx2; ++xm) {
22159                       const T cval = (Tt)_img.atXYZ(x+xm,y+ym,z+zm,0,0);
22160                       if (_mask(mx1+xm,my1+ym,mz1+zm) && cval<min_val) min_val = cval;
22161                     }
22162                 res(x,y,z,c) = min_val;
22163               }
22164         }
22165       }
22166       return res;
22167     }
22168 
22169     //! Erode the image by a rectangular structuring element of size sx,sy,sz.
22170     CImg<T>& erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
22171       if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this;
22172       if (sx>1 && _width>1) { // Along X-axis.
22173         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;
22174         CImg<T> buf(L);
22175         T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
22176         cimg_forYZC(*this,y,z,c) {
22177           const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
22178           ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true;
22179           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;
22180           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; }
22181           for (int p = L - s - 1; p>0; --p) {
22182             const T val = *ptrs; ptrs+=off;
22183             if (is_first) {
22184               const T *nptrs = ptrs - off; cur = val;
22185               for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
22186               nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
22187             } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
22188             *(ptrd++) = cur;
22189           }
22190           ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
22191           for (int p = s1; p>0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val<cur) cur = val; } *(ptrd--) = cur;
22192           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; }
22193           T *pd = data(_width-1,y,z,c) + off; cimg_for(buf,ps,T) { pd-=off; *pd = *ps; }
22194         }
22195       }
22196 
22197       if (sy>1 && _height>1) { // Along Y-axis.
22198         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;
22199         CImg<T> buf(L);
22200         T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
22201         cimg_forXZC(*this,x,z,c) {
22202           const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
22203           ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true;
22204           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;
22205           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; }
22206           for (int p = L - s - 1; p>0; --p) {
22207             const T val = *ptrs; ptrs+=off;
22208             if (is_first) {
22209               const T *nptrs = ptrs - off; cur = val;
22210               for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
22211               nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
22212             } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
22213             *(ptrd++) = cur;
22214           }
22215           ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
22216           for (int p = s1; p>0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val<cur) cur = val; } *(ptrd--) = cur;
22217           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; }
22218           T *pd = data(x,_height-1,z,c) + off; cimg_for(buf,ps,T) { pd-=off; *pd = *ps; }
22219         }
22220       }
22221 
22222       if (sz>1 && _depth>1) { // Along Z-axis.
22223         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;
22224         CImg<T> buf(L);
22225         T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
22226         cimg_forXYC(*this,x,y,c) {
22227           const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
22228           ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true;
22229           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;
22230           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; }
22231           for (int p = L - s - 1; p>0; --p) {
22232             const T val = *ptrs; ptrs+=off;
22233             if (is_first) {
22234               const T *nptrs = ptrs - off; cur = val;
22235               for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
22236               nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
22237             } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
22238             *(ptrd++) = cur;
22239           }
22240           ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
22241           for (int p = s1; p>0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val<cur) cur = val; } *(ptrd--) = cur;
22242           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; }
22243           T *pd = data(x,y,_depth-1,c) + off; cimg_for(buf,ps,T) { pd-=off; *pd = *ps; }
22244         }
22245       }
22246       return *this;
22247     }
22248 
22249     CImg<T> get_erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
22250       return (+*this).erode(sx,sy,sz);
22251     }
22252 
22253     //! Erode the image by a square structuring element of size sx.
22254     CImg<T>& erode(const unsigned int s) {
22255       return erode(s,s,s);
22256     }
22257 
22258     CImg<T> get_erode(const unsigned int s) const {
22259       return (+*this).erode(s);
22260     }
22261 
22262     //! Dilate the image by a structuring element.
22263     template<typename t>
22264     CImg<T>& dilate(const CImg<t>& mask, const unsigned int border_conditions=1, const bool is_normalized=false) {
22265       if (is_empty() || !mask) return *this;
22266       return get_dilate(mask,border_conditions,is_normalized).move_to(*this);
22267     }
22268 
22269     template<typename t>
22270     CImg<_cimg_Tt> get_dilate(const CImg<t>& mask, const unsigned int border_conditions=1,
22271                               const bool is_normalized=false) const {
22272       if (is_empty() || !mask) return *this;
22273       typedef _cimg_Tt Tt;
22274       CImg<Tt> res(_width,_height,_depth,_spectrum);
22275       const int
22276         mx2 = mask.width()/2, my2 = mask.height()/2, mz2 = mask.depth()/2,
22277         mx1 = mx2 - 1 + (mask.width()%2), my1 = my2 - 1 + (mask.height()%2), mz1 = mz2 - 1 + (mask.depth()%2),
22278         mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
22279       cimg_forC(*this,c) {
22280         const CImg<T> _img = get_shared_channel(c%_spectrum);
22281         const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
22282         if (is_normalized) { // Normalized dilation.
22283           for (int z = mz1; z<mze; ++z)
22284             for (int y = my1; y<mye; ++y)
22285               for (int x = mx1; x<mxe; ++x) {
22286                 Tt max_val = cimg::type<Tt>::min();
22287                 for (int zm = -mz1; zm<=mz2; ++zm)
22288                   for (int ym = -my1; ym<=my2; ++ym)
22289                     for (int xm = -mx1; xm<=mx2; ++xm) {
22290                       const t mval = _mask(mx1+xm,my1+ym,mz1+zm);
22291                       const Tt cval = (Tt)(_img(x+xm,y+ym,z+zm) - mval);
22292                       if (mval && cval>max_val) max_val = cval;
22293                     }
22294                 res(x,y,z,c) = max_val;
22295               }
22296           if (border_conditions)
22297             cimg_forYZ(res,y,z)
22298               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
22299                 Tt max_val = cimg::type<Tt>::min();
22300                 for (int zm = -mz1; zm<=mz2; ++zm)
22301                   for (int ym = -my1; ym<=my2; ++ym)
22302                     for (int xm = -mx1; xm<=mx2; ++xm) {
22303                       const t mval = _mask(mx1+xm,my1+ym,mz1+zm);
22304                       const Tt cval = (Tt)(_img._atXYZ(x+xm,y+ym,z+zm) - mval);
22305                       if (mval && cval>max_val) max_val = cval;
22306                     }
22307                 res(x,y,z,c) = max_val;
22308               }
22309           else
22310             cimg_forYZ(*this,y,z)
22311               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
22312                 Tt max_val = cimg::type<Tt>::min();
22313                 for (int zm = -mz1; zm<=mz2; ++zm)
22314                   for (int ym = -my1; ym<=my2; ++ym)
22315                     for (int xm = -mx1; xm<=mx2; ++xm) {
22316                       const t mval = _mask(mx1+xm,my1+ym,mz1+zm);
22317                       const Tt cval = (Tt)(_img.atXYZ(x+xm,y+ym,z+zm,0,0) - mval);
22318                       if (mval && cval>max_val) max_val = cval;
22319                     }
22320                 res(x,y,z,c) = max_val;
22321               }
22322         } else { // Classical dilation.
22323           for (int z = mz1; z<mze; ++z)
22324             for (int y = my1; y<mye; ++y)
22325               for (int x = mx1; x<mxe; ++x) {
22326                 Tt max_val = cimg::type<Tt>::min();
22327                 for (int zm = -mz1; zm<=mz2; ++zm)
22328                   for (int ym = -my1; ym<=my2; ++ym)
22329                     for (int xm = -mx1; xm<=mx2; ++xm) {
22330                       const Tt cval = (Tt)_img(x+xm,y+ym,z+zm);
22331                       if (_mask(mx1+xm,my1+ym,mz1+zm) && cval>max_val) max_val = cval;
22332                     }
22333                 res(x,y,z,c) = max_val;
22334               }
22335           if (border_conditions)
22336             cimg_forYZ(res,y,z)
22337               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
22338                 Tt max_val = cimg::type<Tt>::min();
22339                 for (int zm = -mz1; zm<=mz2; ++zm)
22340                   for (int ym = -my1; ym<=my2; ++ym)
22341                     for (int xm = -mx1; xm<=mx2; ++xm) {
22342                       const T cval = (Tt)_img._atXYZ(x+xm,y+ym,z+zm);
22343                       if (_mask(mx1+xm,my1+ym,mz1+zm) && cval>max_val) max_val = cval;
22344                     }
22345                 res(x,y,z,c) = max_val;
22346               }
22347           else
22348             cimg_forYZ(res,y,z)
22349               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
22350                 Tt max_val = cimg::type<Tt>::min();
22351                 for (int zm = -mz1; zm<=mz2; ++zm)
22352                   for (int ym = -my1; ym<=my2; ++ym)
22353                     for (int xm = -mx1; xm<=mx2; ++xm) {
22354                       const T cval = (Tt)_img.atXYZ(x+xm,y+ym,z+zm,0,0);
22355                       if (_mask(mx1+xm,my1+ym,mz1+zm) && cval>max_val) max_val = cval;
22356                     }
22357                 res(x,y,z,c) = max_val;
22358               }
22359         }
22360       }
22361       return res;
22362     }
22363 
22364     //! Dilate the image by a rectangular structuring element of size sx,sy,sz.
22365     CImg<T>& dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
22366       if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this;
22367       if (sx>1 && _width>1) { // Along X-axis.
22368         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;
22369         CImg<T> buf(L);
22370         T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
22371         cimg_forYZC(*this,y,z,c) {
22372           const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
22373           ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true;
22374           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;
22375           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; }
22376           for (int p = L - s - 1; p>0; --p) {
22377             const T val = *ptrs; ptrs+=off;
22378             if (is_first) {
22379               const T *nptrs = ptrs - off; cur = val;
22380               for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
22381               nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
22382             } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
22383             *(ptrd++) = cur;
22384           }
22385           ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
22386           for (int p = s1; p>0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val>cur) cur = val; } *(ptrd--) = cur;
22387           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; }
22388           T *pd = data(_width-1,y,z,c) + off; cimg_for(buf,ps,T) { pd-=off; *pd = *ps; }
22389         }
22390       }
22391 
22392       if (sy>1 && _height>1) { // Along Y-axis.
22393         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;
22394         CImg<T> buf(L);
22395         T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
22396         cimg_forXZC(*this,x,z,c) {
22397           const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
22398           ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true;
22399           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;
22400           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; }
22401           for (int p = L - s - 1; p>0; --p) {
22402             const T val = *ptrs; ptrs+=off;
22403             if (is_first) {
22404               const T *nptrs = ptrs - off; cur = val;
22405               for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
22406               nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
22407             } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
22408             *(ptrd++) = cur;
22409           }
22410           ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
22411           for (int p = s1; p>0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val>cur) cur = val; } *(ptrd--) = cur;
22412           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; }
22413           T *pd = data(x,_height-1,z,c) + off; cimg_for(buf,ps,T) { pd-=off; *pd = *ps; }
22414         }
22415       }
22416 
22417       if (sz>1 && _depth>1) { // Along Z-axis.
22418         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;
22419         CImg<T> buf(L);
22420         T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
22421         cimg_forXYC(*this,x,y,c) {
22422           const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
22423           ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true;
22424           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;
22425           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; }
22426           for (int p = L - s - 1; p>0; --p) {
22427             const T val = *ptrs; ptrs+=off;
22428             if (is_first) {
22429               const T *nptrs = ptrs - off; cur = val;
22430               for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
22431               nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
22432             } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
22433             *(ptrd++) = cur;
22434           }
22435           ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
22436           for (int p = s1; p>0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val>cur) cur = val; } *(ptrd--) = cur;
22437           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; }
22438           T *pd = data(x,y,_depth-1,c) + off; cimg_for(buf,ps,T) { pd-=off; *pd = *ps; }
22439         }
22440       }
22441       return *this;
22442     }
22443 
22444     CImg<T> get_dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
22445       return (+*this).dilate(sx,sy,sz);
22446     }
22447 
22448     //! Erode the image by a square structuring element of size sx.
22449     CImg<T>& dilate(const unsigned int s) {
22450       return dilate(s,s,s);
22451     }
22452 
22453     CImg<T> get_dilate(const unsigned int s) const {
22454       return (+*this).dilate(s);
22455     }
22456 
22457     //! Compute the watershed transform, from an image instance of non-zero labels.
22458     template<typename t>
22459     CImg<T>& watershed(const CImg<t>& priority, const bool fill_lines=true) {
22460       if (is_empty()) return *this;
22461       if (!is_sameXYZ(priority))
22462         throw CImgArgumentException(_cimg_instance
22463                                     "watershed() : image instance and specified priority (%u,%u,%u,%u,%p) have different dimensions.",
22464                                     cimg_instance,priority._width,priority._height,priority._depth,priority._spectrum,priority._data);
22465       if (_spectrum!=1) { cimg_forC(*this,c) get_shared_channel(c).watershed(priority.get_shared_channel(c%priority._spectrum),fill_lines); return *this; }
22466 
22467       CImg<boolT> in_queue(_width,_height,_depth,1,0);
22468       CImg<typename cimg::superset2<T,t,int>::type> Q;
22469       unsigned int sizeQ = 0;
22470 
22471       // Find seed points and insert them in priority queue.
22472       const T *ptrs = _data;
22473       cimg_forXYZ(*this,x,y,z) if (*(ptrs++)) {
22474         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);
22475         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);
22476         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);
22477         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);
22478         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);
22479         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);
22480       }
22481 
22482       // Start watershed computation.
22483       while (sizeQ) {
22484 
22485         // Get and remove point with maximal priority from the queue.
22486         const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
22487         Q._priority_queue_remove(sizeQ);
22488 
22489         // Check labels of the neighbors.
22490         bool is_same_label = true;
22491         unsigned int label = 0;
22492         if (x-1>=0) {
22493           if ((*this)(x-1,y,z)) { if (!label) label = (*this)(x-1,y,z); else if (label!=(*this)(x-1,y,z)) is_same_label = false; }
22494           else Q._priority_queue_insert(in_queue,sizeQ,priority(x-1,y,z),x-1,y,z);
22495         }
22496         if (x+1<width()) {
22497           if ((*this)(x+1,y,z)) { if (!label) label = (*this)(x+1,y,z); else if (label!=(*this)(x+1,y,z)) is_same_label = false; }
22498           else Q._priority_queue_insert(in_queue,sizeQ,priority(x+1,y,z),x+1,y,z);
22499         }
22500         if (y-1>=0) {
22501           if ((*this)(x,y-1,z)) { if (!label) label = (*this)(x,y-1,z); else if (label!=(*this)(x,y-1,z)) is_same_label = false; }
22502           else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y-1,z),x,y-1,z);
22503         }
22504         if (y+1<height()) {
22505           if ((*this)(x,y+1,z)) { if (!label) label = (*this)(x,y+1,z); else if (label!=(*this)(x,y+1,z)) is_same_label = false; }
22506           else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y+1,z),x,y+1,z);
22507         }
22508         if (z-1>=0) {
22509           if ((*this)(x,y,z-1)) { if (!label) label = (*this)(x,y,z-1); else if (label!=(*this)(x,y,z-1)) is_same_label = false; }
22510           else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z-1),x,y,z-1);
22511         }
22512         if (z+1<depth()) {
22513           if ((*this)(x,y,z+1)) { if (!label) label = (*this)(x,y,z+1); else if (label!=(*this)(x,y,z+1)) is_same_label = false; }
22514           else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z+1),x,y,z+1);
22515         }
22516         if (is_same_label) (*this)(x,y,z) = label;
22517       }
22518 
22519       // Fill lines.
22520       if (fill_lines) {
22521 
22522         // Sort all non-labeled pixels with labeled neighbors.
22523         in_queue = false;
22524         const T *ptrs = _data;
22525         cimg_forXYZ(*this,x,y,z) if (!*(ptrs++) &&
22526                                      ((x-1>=0 && (*this)(x-1,y,z)) || (x+1<width() && (*this)(x+1,y,z)) ||
22527                                       (y-1>=0 && (*this)(x,y-1,z)) || (y+1<height() && (*this)(x,y+1,z)) ||
22528                                       (z-1>=0 && (*this)(x,y,z-1)) || (z+1>depth() && (*this)(x,y,z+1))))
22529           Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z),x,y,z);
22530 
22531         // Start line filling process.
22532         while (sizeQ) {
22533           const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
22534           Q._priority_queue_remove(sizeQ);
22535           t pmax = cimg::type<t>::min();
22536           int xmax = 0, ymax = 0, zmax = 0;
22537           if (x-1>=0) {
22538             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; }}
22539             else Q._priority_queue_insert(in_queue,sizeQ,priority(x-1,y,z),x-1,y,z);
22540           }
22541           if (x+1<width()) {
22542             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; }}
22543             else Q._priority_queue_insert(in_queue,sizeQ,priority(x+1,y,z),x+1,y,z);
22544           }
22545           if (y-1>=0) {
22546             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; }}
22547             else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y-1,z),x,y-1,z);
22548           }
22549           if (y+1<height()) {
22550             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; }}
22551             else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y+1,z),x,y+1,z);
22552           }
22553           if (z-1>=0) {
22554             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; }}
22555             else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z-1),x,y,z-1);
22556           }
22557           if (z+1<depth()) {
22558             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; }}
22559             else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z+1),x,y,z+1);
22560           }
22561           (*this)(x,y,z) = (*this)(xmax,ymax,zmax);
22562         }
22563       }
22564       return *this;
22565     }
22566 
22567     template<typename t>
22568     CImg<T> get_watershed(const CImg<t>& priority, const bool fill_lines=true) const {
22569       return (+*this).watershed(priority,fill_lines);
22570     }
22571 
22572     // Insert/Remove items in priority queue, for watershed/distance transforms.
22573     template<typename t>
22574     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) {
22575       if (in_queue(x,y,z)) return false;
22576       in_queue(x,y,z) = true;
22577       if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); }
22578       (*this)(siz-1,0) = (T)value; (*this)(siz-1,1) = (T)x; (*this)(siz-1,2) = (T)y; (*this)(siz-1,3) = (T)z;
22579       for (unsigned int pos = siz - 1, par = 0; pos && value>(*this)(par=(pos+1)/2-1,0); pos = par) {
22580         cimg::swap((*this)(pos,0),(*this)(par,0)); cimg::swap((*this)(pos,1),(*this)(par,1));
22581         cimg::swap((*this)(pos,2),(*this)(par,2)); cimg::swap((*this)(pos,3),(*this)(par,3));
22582       }
22583       return true;
22584     }
22585 
22586     CImg<T>& _priority_queue_remove(unsigned int& siz) {
22587       (*this)(0,0) = (*this)(--siz,0); (*this)(0,1) = (*this)(siz,1); (*this)(0,2) = (*this)(siz,2); (*this)(0,3) = (*this)(siz,3);
22588       const float value = (*this)(0,0);
22589       for (unsigned int pos = 0, left = 0, right = 0;
22590            ((right=2*(pos+1),(left=right-1))<siz && value<(*this)(left,0)) || (right<siz && value<(*this)(right,0));) {
22591         if (right<siz) {
22592           if ((*this)(left,0)>(*this)(right,0)) {
22593             cimg::swap((*this)(pos,0),(*this)(left,0)); cimg::swap((*this)(pos,1),(*this)(left,1));
22594             cimg::swap((*this)(pos,2),(*this)(left,2)); cimg::swap((*this)(pos,3),(*this)(left,3));
22595             pos = left;
22596           } else {
22597             cimg::swap((*this)(pos,0),(*this)(right,0)); cimg::swap((*this)(pos,1),(*this)(right,1));
22598             cimg::swap((*this)(pos,2),(*this)(right,2)); cimg::swap((*this)(pos,3),(*this)(right,3));
22599             pos = right;
22600           }
22601         } else {
22602           cimg::swap((*this)(pos,0),(*this)(left,0)); cimg::swap((*this)(pos,1),(*this)(left,1));
22603           cimg::swap((*this)(pos,2),(*this)(left,2)); cimg::swap((*this)(pos,3),(*this)(left,3));
22604           pos = left;
22605         }
22606       }
22607       return *this;
22608     }
22609 
22610     //! Compute the result of the Deriche filter.
22611     /**
22612        The Canny-Deriche filter is a recursive algorithm allowing to compute blurred derivatives of
22613        order 0,1 or 2 of an image.
22614     **/
22615     CImg<T>& deriche(const float sigma, const int order=0, const char axis='x', const bool cond=true) {
22616 #define _cimg_deriche2_apply \
22617   Tfloat *ptrY = Y._data, yb = 0, yp = 0; \
22618   T xp = (T)0; \
22619   if (cond) { xp = *ptrX; yb = yp = (Tfloat)(coefp*xp); } \
22620   for (int m = 0; m<N; ++m) { \
22621     const T xc = *ptrX; ptrX+=off; \
22622     const Tfloat yc = *(ptrY++) = (Tfloat)(a0*xc + a1*xp - b1*yp - b2*yb); \
22623     xp = xc; yb = yp; yp = yc; \
22624   } \
22625   T xn = (T)0, xa = (T)0; \
22626   Tfloat yn = 0, ya = 0; \
22627   if (cond) { xn = xa = *(ptrX-off); yn = ya = (Tfloat)coefn*xn; } \
22628   for (int n = N-1; n>=0; --n) { \
22629     const T xc = *(ptrX-=off); \
22630     const Tfloat yc = (Tfloat)(a2*xn + a3*xa - b1*yn - b2*ya); \
22631     xa = xn; xn = xc; ya = yn; yn = yc; \
22632     *ptrX = (T)(*(--ptrY)+yc); \
22633   }
22634       const char naxis = cimg::uncase(axis);
22635       const float nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100;
22636       if (is_empty() || (nsigma<0.1 && !order)) return *this;
22637       const float
22638         nnsigma = nsigma<0.1f?0.1f:nsigma,
22639         alpha = 1.695f/nnsigma,
22640         ema = (float)std::exp(-alpha),
22641         ema2 = (float)std::exp(-2*alpha),
22642         b1 = -2*ema,
22643         b2 = ema2;
22644       float a0 = 0, a1 = 0, a2 = 0, a3 = 0, coefp = 0, coefn = 0;
22645       switch (order) {
22646       case 0 : {
22647         const float k = (1-ema)*(1-ema)/(1+2*alpha*ema-ema2);
22648         a0 = k;
22649         a1 = k*(alpha-1)*ema;
22650         a2 = k*(alpha+1)*ema;
22651         a3 = -k*ema2;
22652       } break;
22653       case 1 : {
22654         const float k = (1-ema)*(1-ema)/ema;
22655         a0 = k*ema;
22656         a1 = a3 = 0;
22657         a2 = -a0;
22658       } break;
22659       case 2 : {
22660         const float
22661           ea = (float)std::exp(-alpha),
22662           k = -(ema2-1)/(2*alpha*ema),
22663           kn = (-2*(-1+3*ea-3*ea*ea+ea*ea*ea)/(3*ea+1+3*ea*ea+ea*ea*ea));
22664         a0 = kn;
22665         a1 = -kn*(1+k*alpha)*ema;
22666         a2 = kn*(1-k*alpha)*ema;
22667         a3 = -kn*ema2;
22668       } break;
22669       default :
22670         throw CImgArgumentException(_cimg_instance
22671                                     "deriche() : Invalid specified filter order %u "
22672                                     "(should be { 0=smoothing | 1=1st-derivative | 2=2nd-derivative }).",
22673                                     cimg_instance,
22674                                     order);
22675       }
22676       coefp = (a0+a1)/(1+b1+b2);
22677       coefn = (a2+a3)/(1+b1+b2);
22678       switch (naxis) {
22679       case 'x' : {
22680         const int N = _width, off = 1;
22681         CImg<Tfloat> Y(N);
22682         cimg_forYZC(*this,y,z,c) { T *ptrX = data(0,y,z,c); _cimg_deriche2_apply; }
22683       } break;
22684       case 'y' : {
22685         const int N = _height, off = _width;
22686         CImg<Tfloat> Y(N);
22687         cimg_forXZC(*this,x,z,c) { T *ptrX = data(x,0,z,c); _cimg_deriche2_apply; }
22688       } break;
22689       case 'z' : {
22690         const int N = _depth, off = _width*_height;
22691         CImg<Tfloat> Y(N);
22692         cimg_forXYC(*this,x,y,c) { T *ptrX = data(x,y,0,c); _cimg_deriche2_apply; }
22693       } break;
22694       default : {
22695         const int N = _spectrum, off = _width*_height*_depth;
22696         CImg<Tfloat> Y(N);
22697         cimg_forXYZ(*this,x,y,z) { T *ptrX = data(x,y,z,0); _cimg_deriche2_apply; }
22698       }
22699       }
22700       return *this;
22701     }
22702 
22703     CImg<Tfloat> get_deriche(const float sigma, const int order=0, const char axis='x', const bool cond=true) const {
22704       return CImg<Tfloat>(*this,false).deriche(sigma,order,axis,cond);
22705     }
22706 
22707     //! Return a blurred version of the image, using a Canny-Deriche filter.
22708     /**
22709        Blur the image with an anisotropic exponential filter (Deriche filter of order 0).
22710     **/
22711     CImg<T>& blur(const float sigmax, const float sigmay, const float sigmaz, const bool cond=true) {
22712       if (!is_empty()) {
22713         if (_width>1) deriche(sigmax,0,'x',cond);
22714         if (_height>1) deriche(sigmay,0,'y',cond);
22715         if (_depth>1) deriche(sigmaz,0,'z',cond);
22716       }
22717       return *this;
22718     }
22719 
22720     CImg<Tfloat> get_blur(const float sigmax, const float sigmay, const float sigmaz, const bool cond=true) const {
22721       return CImg<Tfloat>(*this,false).blur(sigmax,sigmay,sigmaz,cond);
22722     }
22723 
22724     //! Return a blurred version of the image, using a Canny-Deriche filter.
22725     CImg<T>& blur(const float sigma, const bool cond=true) {
22726       const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100;
22727       return blur(nsigma,nsigma,nsigma,cond);
22728     }
22729 
22730     CImg<Tfloat> get_blur(const float sigma, const bool cond=true) const {
22731       return CImg<Tfloat>(*this,false).blur(sigma,cond);
22732     }
22733 
22734     //! Blur the image anisotropically following a field of diffusion tensors.
22735     /**
22736        \param G = Field of square roots of diffusion tensors/vectors used to drive the smoothing.
22737        \param amplitude = amplitude of the smoothing.
22738        \param dl = spatial discretization.
22739        \param da = angular discretization.
22740        \param gauss_prec = precision of the gaussian function.
22741        \param interpolation Used interpolation scheme (0 = nearest-neighbor, 1 = linear, 2 = Runge-Kutta)
22742        \param fast_approx = Tell to use the fast approximation or not.
22743     **/
22744     template<typename t>
22745     CImg<T>& blur_anisotropic(const CImg<t>& G,
22746                               const float amplitude=60, const float dl=0.8f, const float da=30,
22747                               const float gauss_prec=2, const unsigned int interpolation_type=0,
22748                               const bool fast_approx=1) {
22749 
22750       // Check arguments and init variables
22751       if (!is_sameXYZ(G) || (G._spectrum!=3 && G._spectrum!=6))
22752         throw CImgArgumentException(_cimg_instance
22753                                     "blur_anisotropic() : Invalid specified diffusion tensor field (%u,%u,%u,%u,%p).",
22754                                     cimg_instance,
22755                                     G._width,G._height,G._depth,G._spectrum,G._data);
22756 
22757       if (is_empty() || amplitude<=0 || dl<0) return *this;
22758       const bool is_3d = (G._spectrum==6);
22759       T val_min, val_max = max_min(val_min);
22760 
22761       if (da<=0) {  // Iterated oriented Laplacians
22762         CImg<Tfloat> velocity(_width,_height,_depth,_spectrum);
22763         for (unsigned int iteration = 0; iteration<(unsigned int)amplitude; ++iteration) {
22764           Tfloat *ptrd = velocity._data, veloc_max = 0;
22765           if (is_3d) { // 3d version
22766             CImg_3x3x3(I,Tfloat);
22767             cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
22768               const Tfloat
22769                 ixx = Incc + Ipcc - 2*Iccc,
22770                 ixy = (Innc + Ippc - Inpc - Ipnc)/4,
22771                 ixz = (Incn + Ipcp - Incp - Ipcn)/4,
22772                 iyy = Icnc + Icpc - 2*Iccc,
22773                 iyz = (Icnn + Icpp - Icnp - Icpn)/4,
22774                 izz = Iccn + Iccp - 2*Iccc,
22775                 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);
22776               *(ptrd++) = veloc;
22777               if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
22778             }
22779           } else { // 2d version
22780             CImg_3x3(I,Tfloat);
22781             cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
22782               const Tfloat
22783                 ixx = Inc + Ipc - 2*Icc,
22784                 ixy = (Inn + Ipp - Inp - Ipn)/4,
22785                 iyy = Icn + Icp - 2*Icc,
22786                 veloc = (Tfloat)(G(x,y,0,0)*ixx + 2*G(x,y,0,1)*ixy + G(x,y,0,2)*iyy);
22787               *(ptrd++) = veloc;
22788               if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
22789             }
22790           }
22791           if (veloc_max>0) *this+=(velocity*=dl/veloc_max);
22792         }
22793       } else { // LIC-based smoothing.
22794         const unsigned int whd = _width*_height*_depth;
22795         const float sqrt2amplitude = (float)std::sqrt(2*amplitude);
22796         const int dx1 = width() - 1, dy1 = height() - 1, dz1 = depth() - 1;
22797         CImg<Tfloat> res(_width,_height,_depth,_spectrum,0), W(_width,_height,_depth,is_3d?4:3), val(_spectrum);
22798         int N = 0;
22799         if (is_3d) { // 3d version
22800           for (float phi = (180%(int)da)/2.0f; phi<=180; phi+=da) {
22801             const float phir = (float)(phi*cimg::PI/180), datmp = (float)(da/std::cos(phir)), da2 = datmp<1?360.0f:datmp;
22802             for (float theta = 0; theta<360; (theta+=da2),++N) {
22803               const float
22804                 thetar = (float)(theta*cimg::PI/180),
22805                 vx = (float)(std::cos(thetar)*std::cos(phir)),
22806                 vy = (float)(std::sin(thetar)*std::cos(phir)),
22807                 vz = (float)std::sin(phir);
22808               const t
22809                 *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2),
22810                 *pd = G.data(0,0,0,3), *pe = G.data(0,0,0,4), *pf = G.data(0,0,0,5);
22811               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);
22812               cimg_forXYZ(G,xg,yg,zg) {
22813                 const t a = *(pa++), b = *(pb++), c = *(pc++), d = *(pd++), e = *(pe++), f = *(pf++);
22814                 const float
22815                   u = (float)(a*vx + b*vy + c*vz),
22816                   v = (float)(b*vx + d*vy + e*vz),
22817                   w = (float)(c*vx + e*vy + f*vz),
22818                   n = (float)std::sqrt(1e-5+u*u+v*v+w*w),
22819                   dln = dl/n;
22820                 *(pd0++) = (Tfloat)(u*dln);
22821                 *(pd1++) = (Tfloat)(v*dln);
22822                 *(pd2++) = (Tfloat)(w*dln);
22823                 *(pd3++) = (Tfloat)n;
22824               }
22825 
22826               Tfloat *ptrd = res._data;
22827               cimg_forXYZ(*this,x,y,z) {
22828                 val.fill(0);
22829                 const float
22830                   n = (float)W(x,y,z,3),
22831                   fsigma = (float)(n*sqrt2amplitude),
22832                   fsigma2 = 2*fsigma*fsigma,
22833                   length = gauss_prec*fsigma;
22834                 float
22835                   S = 0,
22836                   X = (float)x,
22837                   Y = (float)y,
22838                   Z = (float)z;
22839                 switch (interpolation_type) {
22840                 case 0 : { // Nearest neighbor
22841                   for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
22842                     const int
22843                       cx = (int)(X+0.5f),
22844                       cy = (int)(Y+0.5f),
22845                       cz = (int)(Z+0.5f);
22846                     const float
22847                       u = (float)W(cx,cy,cz,0),
22848                       v = (float)W(cx,cy,cz,1),
22849                       w = (float)W(cx,cy,cz,2);
22850                     if (fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,cz,c); ++S; }
22851                     else {
22852                       const float coef = (float)std::exp(-l*l/fsigma2);
22853                       cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,cz,c));
22854                       S+=coef;
22855                     }
22856                     X+=u; Y+=v; Z+=w;
22857                   }
22858                 } break;
22859                 case 1 : { // Linear interpolation
22860                   for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
22861                     const float
22862                       u = (float)(W._linear_atXYZ(X,Y,Z,0)),
22863                       v = (float)(W._linear_atXYZ(X,Y,Z,1)),
22864                       w = (float)(W._linear_atXYZ(X,Y,Z,2));
22865                     if (fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
22866                     else {
22867                       const float coef = (float)std::exp(-l*l/fsigma2);
22868                       cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
22869                       S+=coef;
22870                     }
22871                     X+=u; Y+=v; Z+=w;
22872                   }
22873                 } break;
22874                 default : { // 2nd order Runge Kutta
22875                   for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
22876                     const float
22877                       u0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,0)),
22878                       v0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,1)),
22879                       w0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,2)),
22880                       u = (float)(W._linear_atXYZ(X+u0,Y+v0,Z+w0,0)),
22881                       v = (float)(W._linear_atXYZ(X+u0,Y+v0,Z+w0,1)),
22882                       w = (float)(W._linear_atXYZ(X+u0,Y+v0,Z+w0,2));
22883                     if (fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
22884                     else {
22885                       const float coef = (float)std::exp(-l*l/fsigma2);
22886                       cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
22887                       S+=coef;
22888                     }
22889                     X+=u; Y+=v; Z+=w;
22890                   }
22891                 } break;
22892                 }
22893                 Tfloat *_ptrd = ptrd++;
22894                 if (S>0) cimg_forC(res,c) { *_ptrd+=val[c]/S; _ptrd+=whd; }
22895                 else cimg_forC(res,c) { *_ptrd+=(Tfloat)((*this)(x,y,z,c)); _ptrd+=whd; }
22896               }
22897             }
22898           }
22899         } else { // 2d LIC algorithm
22900           for (float theta = (360%(int)da)/2.0f; theta<360; (theta+=da),++N) {
22901             const float thetar = (float)(theta*cimg::PI/180), vx = (float)(std::cos(thetar)), vy = (float)(std::sin(thetar));
22902             const t *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2);
22903             Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(0,0,0,2);
22904             cimg_forXY(G,xg,yg) {
22905               const t a = *(pa++), b = *(pb++), c = *(pc++);
22906               const float
22907                 u = (float)(a*vx + b*vy),
22908                 v = (float)(b*vx + c*vy),
22909                 n = (float)std::sqrt(1e-5+u*u+v*v),
22910                 dln = dl/n;
22911               *(pd0++) = (Tfloat)(u*dln);
22912               *(pd1++) = (Tfloat)(v*dln);
22913               *(pd2++) = (Tfloat)n;
22914             }
22915             Tfloat *ptrd = res._data;
22916             cimg_forXY(*this,x,y) {
22917               val.fill(0);
22918               const float
22919                 n = (float)W(x,y,0,2),
22920                 fsigma = (float)(n*sqrt2amplitude),
22921                 fsigma2 = 2*fsigma*fsigma,
22922                 length = gauss_prec*fsigma;
22923               float
22924                 S = 0,
22925                 X = (float)x,
22926                 Y = (float)y;
22927               switch (interpolation_type) {
22928               case 0 : { // Nearest-neighbor
22929                 for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
22930                   const int
22931                     cx = (int)(X+0.5f),
22932                     cy = (int)(Y+0.5f);
22933                   const float
22934                     u = (float)W(cx,cy,0,0),
22935                     v = (float)W(cx,cy,0,1);
22936                   if (fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,0,c); ++S; }
22937                   else {
22938                     const float coef = (float)std::exp(-l*l/fsigma2);
22939                     cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,0,c));
22940                     S+=coef;
22941                   }
22942                   X+=u; Y+=v;
22943                 }
22944               } break;
22945               case 1 : { // Linear interpolation
22946                 for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
22947                   const float
22948                     u = (float)(W._linear_atXY(X,Y,0,0)),
22949                     v = (float)(W._linear_atXY(X,Y,0,1));
22950                   if (fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
22951                   else {
22952                     const float coef = (float)std::exp(-l*l/fsigma2);
22953                     cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
22954                     S+=coef;
22955                   }
22956                   X+=u; Y+=v;
22957                 }
22958               } break;
22959               default : { // 2nd-order Runge-kutta interpolation
22960                 for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
22961                   const float
22962                     u0 = (float)(0.5f*W._linear_atXY(X,Y,0,0)),
22963                     v0 = (float)(0.5f*W._linear_atXY(X,Y,0,1)),
22964                     u = (float)(W._linear_atXY(X+u0,Y+v0,0,0)),
22965                     v = (float)(W._linear_atXY(X+u0,Y+v0,0,1));
22966                   if (fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
22967                   else {
22968                     const float coef = (float)std::exp(-l*l/fsigma2);
22969                     cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
22970                     S+=coef;
22971                   }
22972                   X+=u; Y+=v;
22973                 }
22974               }
22975               }
22976               Tfloat *_ptrd = ptrd++;
22977               if (S>0) cimg_forC(res,c) { *_ptrd+=val[c]/S; _ptrd+=whd; }
22978               else cimg_forC(res,c) { *_ptrd+=(Tfloat)((*this)(x,y,0,c)); _ptrd+=whd; }
22979             }
22980           }
22981         }
22982         const Tfloat *ptrs = res.end();
22983         cimg_for(*this,ptrd,T) { const Tfloat val = *(--ptrs)/N; *ptrd = val<val_min?val_min:(val>val_max?val_max:(T)val); }
22984       }
22985       return *this;
22986     }
22987 
22988     template<typename t>
22989     CImg<T> get_blur_anisotropic(const CImg<t>& G,
22990                                  const float amplitude=60, const float dl=0.8f, const float da=30,
22991                                  const float gauss_prec=2, const unsigned int interpolation_type=0,
22992                                  const bool fast_approx=true) const {
22993       return (+*this).blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation_type,fast_approx);
22994     }
22995 
22996     //! Blur an image following in an anisotropic way.
22997     CImg<T>& blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f,
22998                               const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30,
22999                               const float gauss_prec=2, const unsigned int interpolation_type=0,
23000                               const bool fast_approx=true) {
23001       return blur_anisotropic(get_edge_tensors(sharpness,anisotropy,alpha,sigma,interpolation_type!=3),
23002                               amplitude,dl,da,gauss_prec,interpolation_type,fast_approx);
23003     }
23004 
23005     CImg<T> get_blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f,
23006                                  const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f,
23007                                  const float da=30, const float gauss_prec=2, const unsigned int interpolation_type=0,
23008                                  const bool fast_approx=true) const {
23009       return (+*this).blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,interpolation_type,fast_approx);
23010     }
23011 
23012     //! Blur an image using the bilateral filter.
23013     /**
23014        \param sigma_x Amount of blur along the X-axis.
23015        \param sigma_y Amount of blur along the Y-axis.
23016        \param sigma_z Amount of blur along the Z-axis.
23017        \param sigma_r Amount of blur along the range axis.
23018        \param bgrid_x Size of the bilateral grid along the X-axis.
23019        \param bgrid_y Size of the bilateral grid along the Y-axis.
23020        \param bgrid_z Size of the bilateral grid along the Z-axis.
23021        \param bgrid_r Size of the bilateral grid along the range axis.
23022        \param interpolation_type Use interpolation for image slicing.
23023        \note This algorithm uses the optimisation technique proposed by S. Paris and F. Durand, in ECCV'2006
23024        (extended for 3d volumetric images).
23025     **/
23026     CImg<T>& blur_bilateral(const float sigma_x, const float sigma_y, const float sigma_z, const float sigma_r,
23027                             const int bgrid_x, const int bgrid_y, const int bgrid_z, const int bgrid_r,
23028                             const bool interpolation_type=true) {
23029       if (is_empty()) return *this;
23030       T m, M = max_min(m);
23031       const float range = (float)(1.0f + M - m);
23032       const unsigned int
23033         bx0 = bgrid_x>=0?bgrid_x:_width*(-bgrid_x)/100,
23034         by0 = bgrid_y>=0?bgrid_y:_height*(-bgrid_y)/100,
23035         bz0 = bgrid_z>=0?bgrid_z:_depth*(-bgrid_z)/100,
23036         br0 = bgrid_r>=0?bgrid_r:(int)(-range*bgrid_r/100),
23037         bx = bx0>0?bx0:1,
23038         by = by0>0?by0:1,
23039         bz = bz0>0?bz0:1,
23040         br = br0>0?br0:1;
23041       const float
23042         _sigma_x = sigma_x>=0?sigma_x:-sigma_x*_width/100,
23043         _sigma_y = sigma_y>=0?sigma_y:-sigma_y*_height/100,
23044         _sigma_z = sigma_z>=0?sigma_z:-sigma_z*_depth/100,
23045         nsigma_x = _sigma_x*bx/_width,
23046         nsigma_y = _sigma_y*by/_height,
23047         nsigma_z = _sigma_z*bz/_depth,
23048         nsigma_r = sigma_r*br/range;
23049       if (nsigma_x>0 || nsigma_y>0 || nsigma_z>0 || nsigma_r>0) {
23050         const bool is_3d = (_depth>1);
23051         if (is_3d) { // 3d version of the algorithm
23052           CImg<floatT> bgrid(bx,by,bz,br), bgridw(bx,by,bz,br);
23053           cimg_forC(*this,c) {
23054             bgrid.fill(0); bgridw.fill(0);
23055             cimg_forXYZ(*this,x,y,z) {
23056               const T val = (*this)(x,y,z,c);
23057               const int X = x*bx/_width, Y = y*by/_height, Z = z*bz/_depth, R = (int)((val-m)*br/range);
23058               bgrid(X,Y,Z,R) += (float)val;
23059               bgridw(X,Y,Z,R) += 1;
23060             }
23061             bgrid.blur(nsigma_x,nsigma_y,nsigma_z,true).deriche(nsigma_r,0,'c',false);
23062             bgridw.blur(nsigma_x,nsigma_y,nsigma_z,true).deriche(nsigma_r,0,'c',false);
23063             if (interpolation_type) cimg_forXYZ(*this,x,y,z) {
23064               const T val = (*this)(x,y,z,c);
23065               const float X = (float)x*bx/_width, Y = (float)y*by/_height, Z = (float)z*bz/_depth, R = (float)((val-m)*br/range),
23066                 bval0 = bgrid._linear_atXYZC(X,Y,Z,R), bval1 = bgridw._linear_atXYZC(X,Y,Z,R);
23067               (*this)(x,y,z,c) = (T)(bval0/bval1);
23068             } else cimg_forXYZ(*this,x,y,z) {
23069               const T val = (*this)(x,y,z,c);
23070               const int X = x*bx/_width, Y = y*by/_height, Z = z*bz/_depth, R = (int)((val-m)*br/range);
23071               const float bval0 = bgrid(X,Y,Z,R), bval1 = bgridw(X,Y,Z,R);
23072               (*this)(x,y,z,c) = (T)(bval0/bval1);
23073             }
23074           }
23075         } else { // 2d version of the algorithm
23076           CImg<floatT> bgrid(bx,by,br,2);
23077           cimg_forC(*this,c) {
23078             bgrid.fill(0);
23079             cimg_forXY(*this,x,y) {
23080               const T val = (*this)(x,y,c);
23081               const int X = x*bx/_width, Y = y*by/_height, R = (int)((val-m)*br/range);
23082               bgrid(X,Y,R,0) += (float)val;
23083               bgrid(X,Y,R,1) += 1;
23084             }
23085             bgrid.blur(nsigma_x,nsigma_y,0,true).blur(0,0,nsigma_r,false);
23086             if (interpolation_type) cimg_forXY(*this,x,y) {
23087               const T val = (*this)(x,y,c);
23088               const float X = (float)x*bx/_width, Y = (float)y*by/_height, R = (float)((val-m)*br/range),
23089                 bval0 = bgrid._linear_atXYZ(X,Y,R,0), bval1 = bgrid._linear_atXYZ(X,Y,R,1);
23090               (*this)(x,y,c) = (T)(bval0/bval1);
23091             } else cimg_forXY(*this,x,y) {
23092               const T val = (*this)(x,y,c);
23093               const int X = x*bx/_width, Y = y*by/_height, R = (int)((val-m)*br/range);
23094               const float bval0 = bgrid(X,Y,R,0), bval1 = bgrid(X,Y,R,1);
23095               (*this)(x,y,c) = (T)(bval0/bval1);
23096             }
23097           }
23098         }
23099       }
23100       return *this;
23101     }
23102 
23103     CImg<T> get_blur_bilateral(const float sigma_x, const float sigma_y, const float sigma_z, const float sigma_r,
23104                                const int bgrid_x, const int bgrid_y, const int bgrid_z, const int bgrid_r,
23105                                const bool interpolation_type=true) const {
23106       return (+*this).blur_bilateral(sigma_x,sigma_y,sigma_z,sigma_r,bgrid_x,bgrid_y,bgrid_z,bgrid_r,interpolation_type);
23107     }
23108 
23109     //! Blur an image using the bilateral filter.
23110     CImg<T>& blur_bilateral(const float sigma_s, const float sigma_r, const int bgrid_s=-33, const int bgrid_r=32,
23111                             const bool interpolation_type=true) {
23112       const float nsigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100;
23113       return blur_bilateral(nsigma_s,nsigma_s,nsigma_s,sigma_r,bgrid_s,bgrid_s,bgrid_s,bgrid_r,interpolation_type);
23114     }
23115 
23116     CImg<T> get_blur_bilateral(const float sigma_s, const float sigma_r, const int bgrid_s=-33, const int bgrid_r=32,
23117                                const bool interpolation_type=true) const {
23118       return (+*this).blur_bilateral(sigma_s,sigma_s,sigma_s,sigma_r,bgrid_s,bgrid_s,bgrid_s,bgrid_r,interpolation_type);
23119     }
23120 
23121     //! Blur an image in its patch-based space.
23122     CImg<T>& blur_patch(const float sigma_s, const float sigma_p, const unsigned int patch_size=3,
23123                         const unsigned int lookup_size=4, const float smoothness=0, const bool fast_approx=true) {
23124       if (is_empty() || !patch_size || !lookup_size) return *this;
23125       return get_blur_patch(sigma_s,sigma_p,patch_size,lookup_size,smoothness,fast_approx).move_to(*this);
23126     }
23127 
23128     CImg<T> get_blur_patch(const float sigma_s, const float sigma_p, const unsigned int patch_size=3,
23129                            const unsigned int lookup_size=4, const float smoothness=0, const bool fast_approx=true) const {
23130 
23131 #define _cimg_blur_patch3d_fast(N) \
23132       cimg_for##N##XYZ(res,x,y,z) { \
23133         T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,x,y,z,c,pP,T); pP+=N3; } \
23134         const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \
23135         float sum_weights = 0; \
23136         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) { \
23137           T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,p,q,r,c,pQ,T); pQ+=N3; } \
23138           float distance2 = 0; \
23139           pQ = Q.end(); cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(--pQ); distance2+=dI*dI; } \
23140           distance2/=Pnorm; \
23141           const float dx = (float)p - x, dy = (float)q - y, dz = (float)r - z, \
23142             alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = alldist>3?0.0f:1.0f; \
23143           sum_weights+=weight; \
23144           cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c); \
23145         } \
23146         if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights; \
23147         else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \
23148     }
23149 
23150 #define _cimg_blur_patch3d(N) \
23151       cimg_for##N##XYZ(res,x,y,z) { \
23152         T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,x,y,z,c,pP,T); pP+=N3; } \
23153         const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \
23154         float sum_weights = 0, weight_max = 0; \
23155         cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) { \
23156           T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,p,q,r,c,pQ,T); pQ+=N3; } \
23157           float distance2 = 0; \
23158           pQ = Q.end(); cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(--pQ); distance2+=dI*dI; } \
23159           distance2/=Pnorm; \
23160           const float dx = (float)p - x, dy = (float)q - y, dz = (float)r - z, \
23161             alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = (float)std::exp(-alldist); \
23162           if (weight>weight_max) weight_max = weight; \
23163           sum_weights+=weight; \
23164           cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c); \
23165         } \
23166         sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=weight_max*(*this)(x,y,z,c); \
23167         if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights; \
23168         else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \
23169       }
23170 
23171 #define _cimg_blur_patch2d_fast(N) \
23172         cimg_for##N##XY(res,x,y) { \
23173           T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N(img,x,y,0,c,pP,T); pP+=N2; } \
23174           const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \
23175           float sum_weights = 0; \
23176           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) { \
23177             T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N(img,p,q,0,c,pQ,T); pQ+=N2; } \
23178             float distance2 = 0; \
23179             pQ = Q.end(); cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(--pQ); distance2+=dI*dI; } \
23180             distance2/=Pnorm; \
23181             const float dx = (float)p - x, dy = (float)q - y, \
23182               alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = alldist>3?0.0f:1.0f; \
23183             sum_weights+=weight; \
23184             cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c); \
23185           } \
23186           if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights; \
23187           else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \
23188         }
23189 
23190 #define _cimg_blur_patch2d(N) \
23191         cimg_for##N##XY(res,x,y) { \
23192           T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N(img,x,y,0,c,pP,T); pP+=N2; } \
23193           const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \
23194           float sum_weights = 0, weight_max = 0; \
23195           cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) { \
23196             T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N(img,p,q,0,c,pQ,T); pQ+=N2; } \
23197             float distance2 = 0; \
23198             pQ = Q.end(); cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(--pQ); distance2+=dI*dI; } \
23199             distance2/=Pnorm; \
23200             const float dx = (float)p - x, dy = (float)q - y, \
23201               alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = (float)std::exp(-alldist); \
23202             if (weight>weight_max) weight_max = weight; \
23203             sum_weights+=weight; \
23204             cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c); \
23205           } \
23206           sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=weight_max*(*this)(x,y,c); \
23207           if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights; \
23208           else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \
23209     }
23210 
23211       if (is_empty() || !patch_size || !lookup_size) return (+*this);
23212       CImg<Tfloat> res(_width,_height,_depth,_spectrum,0);
23213       const CImg<T> _img = smoothness>0?get_blur(smoothness):CImg<Tfloat>(),&img = smoothness>0?_img:*this;
23214       CImg<T> P(patch_size*patch_size*_spectrum), Q(P);
23215       const float
23216         nsigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100,
23217         sigma_s2 = nsigma_s*nsigma_s, sigma_p2 = sigma_p*sigma_p, sigma_p3 = 3*sigma_p,
23218         Pnorm = P.size()*sigma_p2;
23219       const int rsize2 = (int)lookup_size/2, rsize1 = (int)lookup_size - rsize2 - 1;
23220       const unsigned int N2 = patch_size*patch_size, N3 = N2*patch_size;
23221       if (_depth>1) switch (patch_size) { // 3d
23222       case 2 : if (fast_approx) _cimg_blur_patch3d_fast(2) else _cimg_blur_patch3d(2) break;
23223       case 3 : if (fast_approx) _cimg_blur_patch3d_fast(3) else _cimg_blur_patch3d(3) break;
23224       default : {
23225         const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1;
23226         if (fast_approx) cimg_forXYZ(res,x,y,z) { // Fast
23227           P = img.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true);
23228           const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2;
23229           float sum_weights = 0;
23230           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) {
23231             (Q = img.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P;
23232             const float
23233               dx = (float)x - p, dy = (float)y - q, dz = (float)z - r,
23234               distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2),
23235               weight = distance2>3?0.0f:1.0f;
23236             sum_weights+=weight;
23237             cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c);
23238           }
23239           if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights;
23240           else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c));
23241         } else cimg_forXYZ(res,x,y,z) { // Exact
23242           P = img.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true);
23243           const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2;
23244           float sum_weights = 0, weight_max = 0;
23245           cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) {
23246             (Q = img.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P;
23247             const float
23248               dx = (float)x - p, dy = (float)y - q, dz = (float)z - r,
23249               distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2),
23250               weight = (float)std::exp(-distance2);
23251             if (weight>weight_max) weight_max = weight;
23252             sum_weights+=weight;
23253             cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c);
23254           }
23255           sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=weight_max*(*this)(x,y,z,c);
23256           if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights;
23257           else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c));
23258         }
23259       }
23260       } else switch (patch_size) { // 2d
23261       case 2 : if (fast_approx) _cimg_blur_patch2d_fast(2) else _cimg_blur_patch2d(2) break;
23262       case 3 : if (fast_approx) _cimg_blur_patch2d_fast(3) else _cimg_blur_patch2d(3) break;
23263       case 4 : if (fast_approx) _cimg_blur_patch2d_fast(4) else _cimg_blur_patch2d(4) break;
23264       case 5 : if (fast_approx) _cimg_blur_patch2d_fast(5) else _cimg_blur_patch2d(5) break;
23265       case 6 : if (fast_approx) _cimg_blur_patch2d_fast(6) else _cimg_blur_patch2d(6) break;
23266       case 7 : if (fast_approx) _cimg_blur_patch2d_fast(7) else _cimg_blur_patch2d(7) break;
23267       case 8 : if (fast_approx) _cimg_blur_patch2d_fast(8) else _cimg_blur_patch2d(8) break;
23268       case 9 : if (fast_approx) _cimg_blur_patch2d_fast(9) else _cimg_blur_patch2d(9) break;
23269       default : { // Fast
23270         const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1;
23271         if (fast_approx) cimg_forXY(res,x,y) { // 2d fast approximation.
23272           P = img.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true);
23273           const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2;
23274           float sum_weights = 0;
23275           cimg_for_inXY(res,x0,y0,x1,y1,p,q) if (cimg::abs(img(x,y,0)-img(p,q,0))<sigma_p3) {
23276             (Q = img.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P;
23277             const float
23278               dx = (float)x - p, dy = (float)y - q,
23279               distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2),
23280               weight = distance2>3?0.0f:1.0f;
23281             sum_weights+=weight;
23282             cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c);
23283           }
23284           if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights;
23285           else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c));
23286         } else cimg_forXY(res,x,y) { // 2d exact algorithm.
23287           P = img.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true);
23288           const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2;
23289           float sum_weights = 0, weight_max = 0;
23290           cimg_for_inXY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) {
23291             (Q = img.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P;
23292             const float
23293               dx = (float)x - p, dy = (float)y - q,
23294               distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2),
23295               weight = (float)std::exp(-distance2);
23296             if (weight>weight_max) weight_max = weight;
23297             sum_weights+=weight;
23298             cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c);
23299           }
23300           sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=weight_max*(*this)(x,y,c);
23301           if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights;
23302           else cimg_forC(res,c) res(x,y,0,c) = (Tfloat)((*this)(x,y,c));
23303         }
23304       }
23305       }
23306       return res;
23307     }
23308 
23309     //! Apply a median filter.
23310     CImg<T>& blur_median(const unsigned int n) {
23311       if (!n) return *this;
23312       return get_blur_median(n).move_to(*this);
23313     }
23314 
23315     CImg<T> get_blur_median(const unsigned int n) const {
23316       if (is_empty() || n<=1) return (+*this);
23317       CImg<T> res(_width,_height,_depth,_spectrum);
23318       T *ptrd = res._data;
23319       const int hl = n/2, hr = hl - 1 + n%2;
23320       if (res._depth!=1) cimg_forXYZC(*this,x,y,z,c) { // 3d
23321         const int
23322           x0 = x - hl, y0 = y - hl, z0 = z-hl, x1 = x + hr, y1 = y + hr, z1 = z+hr,
23323           nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0,
23324           nx1 = x1>=width()?width()-1:x1, ny1 = y1>=height()?height()-1:y1, nz1 = z1>=depth()?depth()-1:z1;
23325         *(ptrd++) = get_crop(nx0,ny0,nz0,c,nx1,ny1,nz1,c).median();
23326       } else {
23327 #define _cimg_median_sort(a,b) if ((a)>(b)) cimg::swap(a,b)
23328         if (res._height!=1) switch (n) { // 2d
23329         case 3 : {
23330           T I[9] = { 0 };
23331           CImg_3x3(J,T);
23332           cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) {
23333             std::memcpy(J,I,9*sizeof(T));
23334             _cimg_median_sort(Jcp, Jnp); _cimg_median_sort(Jcc, Jnc); _cimg_median_sort(Jcn, Jnn);
23335             _cimg_median_sort(Jpp, Jcp); _cimg_median_sort(Jpc, Jcc); _cimg_median_sort(Jpn, Jcn);
23336             _cimg_median_sort(Jcp, Jnp); _cimg_median_sort(Jcc, Jnc); _cimg_median_sort(Jcn, Jnn);
23337             _cimg_median_sort(Jpp, Jpc); _cimg_median_sort(Jnc, Jnn); _cimg_median_sort(Jcc, Jcn);
23338             _cimg_median_sort(Jpc, Jpn); _cimg_median_sort(Jcp, Jcc); _cimg_median_sort(Jnp, Jnc);
23339             _cimg_median_sort(Jcc, Jcn); _cimg_median_sort(Jcc, Jnp); _cimg_median_sort(Jpn, Jcc);
23340             _cimg_median_sort(Jcc, Jnp);
23341             *(ptrd++) = Jcc;
23342           }
23343         } break;
23344         case 5 : {
23345           T I[25] = { 0 };
23346           CImg_5x5(J,T);
23347           cimg_forC(*this,c) cimg_for5x5(*this,x,y,0,c,I,T) {
23348             std::memcpy(J,I,25*sizeof(T));
23349             _cimg_median_sort(Jbb, Jpb); _cimg_median_sort(Jnb, Jab); _cimg_median_sort(Jcb, Jab); _cimg_median_sort(Jcb, Jnb);
23350             _cimg_median_sort(Jpp, Jcp); _cimg_median_sort(Jbp, Jcp); _cimg_median_sort(Jbp, Jpp); _cimg_median_sort(Jap, Jbc);
23351             _cimg_median_sort(Jnp, Jbc); _cimg_median_sort(Jnp, Jap); _cimg_median_sort(Jcc, Jnc); _cimg_median_sort(Jpc, Jnc);
23352             _cimg_median_sort(Jpc, Jcc); _cimg_median_sort(Jbn, Jpn); _cimg_median_sort(Jac, Jpn); _cimg_median_sort(Jac, Jbn);
23353             _cimg_median_sort(Jnn, Jan); _cimg_median_sort(Jcn, Jan); _cimg_median_sort(Jcn, Jnn); _cimg_median_sort(Jpa, Jca);
23354             _cimg_median_sort(Jba, Jca); _cimg_median_sort(Jba, Jpa); _cimg_median_sort(Jna, Jaa); _cimg_median_sort(Jcb, Jbp);
23355             _cimg_median_sort(Jnb, Jpp); _cimg_median_sort(Jbb, Jpp); _cimg_median_sort(Jbb, Jnb); _cimg_median_sort(Jab, Jcp);
23356             _cimg_median_sort(Jpb, Jcp); _cimg_median_sort(Jpb, Jab); _cimg_median_sort(Jpc, Jac); _cimg_median_sort(Jnp, Jac);
23357             _cimg_median_sort(Jnp, Jpc); _cimg_median_sort(Jcc, Jbn); _cimg_median_sort(Jap, Jbn); _cimg_median_sort(Jap, Jcc);
23358             _cimg_median_sort(Jnc, Jpn); _cimg_median_sort(Jbc, Jpn); _cimg_median_sort(Jbc, Jnc); _cimg_median_sort(Jba, Jna);
23359             _cimg_median_sort(Jcn, Jna); _cimg_median_sort(Jcn, Jba); _cimg_median_sort(Jpa, Jaa); _cimg_median_sort(Jnn, Jaa);
23360             _cimg_median_sort(Jnn, Jpa); _cimg_median_sort(Jan, Jca); _cimg_median_sort(Jnp, Jcn); _cimg_median_sort(Jap, Jnn);
23361             _cimg_median_sort(Jbb, Jnn); _cimg_median_sort(Jbb, Jap); _cimg_median_sort(Jbc, Jan); _cimg_median_sort(Jpb, Jan);
23362             _cimg_median_sort(Jpb, Jbc); _cimg_median_sort(Jpc, Jba); _cimg_median_sort(Jcb, Jba); _cimg_median_sort(Jcb, Jpc);
23363             _cimg_median_sort(Jcc, Jpa); _cimg_median_sort(Jnb, Jpa); _cimg_median_sort(Jnb, Jcc); _cimg_median_sort(Jnc, Jca);
23364             _cimg_median_sort(Jab, Jca); _cimg_median_sort(Jab, Jnc); _cimg_median_sort(Jac, Jna); _cimg_median_sort(Jbp, Jna);
23365             _cimg_median_sort(Jbp, Jac); _cimg_median_sort(Jbn, Jaa); _cimg_median_sort(Jpp, Jaa); _cimg_median_sort(Jpp, Jbn);
23366             _cimg_median_sort(Jcp, Jpn); _cimg_median_sort(Jcp, Jan); _cimg_median_sort(Jnc, Jpa); _cimg_median_sort(Jbn, Jna);
23367             _cimg_median_sort(Jcp, Jnc); _cimg_median_sort(Jcp, Jbn); _cimg_median_sort(Jpb, Jap); _cimg_median_sort(Jnb, Jpc);
23368             _cimg_median_sort(Jbp, Jcn); _cimg_median_sort(Jpc, Jcn); _cimg_median_sort(Jap, Jcn); _cimg_median_sort(Jab, Jbc);
23369             _cimg_median_sort(Jpp, Jcc); _cimg_median_sort(Jcp, Jac); _cimg_median_sort(Jab, Jpp); _cimg_median_sort(Jab, Jcp);
23370             _cimg_median_sort(Jcc, Jac); _cimg_median_sort(Jbc, Jac); _cimg_median_sort(Jpp, Jcp); _cimg_median_sort(Jbc, Jcc);
23371             _cimg_median_sort(Jpp, Jbc); _cimg_median_sort(Jpp, Jcn); _cimg_median_sort(Jcc, Jcn); _cimg_median_sort(Jcp, Jcn);
23372             _cimg_median_sort(Jcp, Jbc); _cimg_median_sort(Jcc, Jnn); _cimg_median_sort(Jcp, Jcc); _cimg_median_sort(Jbc, Jnn);
23373             _cimg_median_sort(Jcc, Jba); _cimg_median_sort(Jbc, Jba); _cimg_median_sort(Jbc, Jcc);
23374             *(ptrd++) = Jcc;
23375           }
23376         } break;
23377         default : {
23378           cimg_forXYC(*this,x,y,c) {
23379             const int
23380               x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr,
23381               nx0 = x0<0?0:x0, ny0 = y0<0?0:y0,
23382               nx1 = x1>=width()?width()-1:x1, ny1 = y1>=height()?height()-1:y1;
23383             *(ptrd++) = get_crop(nx0,ny0,0,c,nx1,ny1,0,c).median();
23384           }
23385         }
23386         } else switch (n) { // 1d
23387         case 2 : {
23388           T I[4] = { 0 };
23389           cimg_forC(*this,c) cimg_for2x2(*this,x,y,0,c,I,T) *(ptrd++) = (T)(0.5f*(I[0]+I[1]));
23390         } break;
23391         case 3 : {
23392           T I[9] = { 0 };
23393           cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T)
23394             *(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]));
23395         } break;
23396         default : {
23397           cimg_forXC(*this,x,c) {
23398             const int
23399               x0 = x - hl, x1 = x + hr,
23400               nx0 = x0<0?0:x0, nx1 = x1>=width()?width()-1:x1;
23401             *(ptrd++) = get_crop(nx0,0,0,c,nx1,0,0,c).median();
23402           }
23403         }
23404         }
23405       }
23406       return res;
23407     }
23408 
23409     //! Sharpen image using anisotropic shock filters or inverse diffusion.
23410     CImg<T>& sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1, const float alpha=0, const float sigma=0) {
23411       if (is_empty()) return *this;
23412       T val_min, val_max = max_min(val_min);
23413       const float nedge = edge/2;
23414       CImg<Tfloat> val, vec, velocity(_width,_height,_depth,_spectrum);
23415       Tfloat *ptrd = velocity._data, veloc_max = 0;
23416 
23417       if (_depth>1) { // 3d
23418         CImg_3x3x3(I,Tfloat);
23419         if (sharpen_type) { // Shock filters.
23420           CImg<Tfloat> G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors());
23421           if (sigma>0) G.blur(sigma);
23422           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);
23423           cimg_forXYZ(G,x,y,z) {
23424             G.get_tensor_at(x,y,z).symmetric_eigen(val,vec);
23425             if (val[0]<0) val[0] = 0;
23426             if (val[1]<0) val[1] = 0;
23427             if (val[2]<0) val[2] = 0;
23428             *(ptrG0++) = vec(0,0);
23429             *(ptrG1++) = vec(0,1);
23430             *(ptrG2++) = vec(0,2);
23431             *(ptrG3++) = 1 - (Tfloat)std::pow(1+val[0]+val[1]+val[2],-(Tfloat)nedge);
23432           }
23433           cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
23434             const Tfloat
23435               u = G(x,y,z,0),
23436               v = G(x,y,z,1),
23437               w = G(x,y,z,2),
23438               amp = G(x,y,z,3),
23439               ixx = Incc + Ipcc - 2*Iccc,
23440               ixy = (Innc + Ippc - Inpc - Ipnc)/4,
23441               ixz = (Incn + Ipcp - Incp - Ipcn)/4,
23442               iyy = Icnc + Icpc - 2*Iccc,
23443               iyz = (Icnn + Icpp - Icnp - Icpn)/4,
23444               izz = Iccn + Iccp - 2*Iccc,
23445               ixf = Incc - Iccc,
23446               ixb = Iccc - Ipcc,
23447               iyf = Icnc - Iccc,
23448               iyb = Iccc - Icpc,
23449               izf = Iccn - Iccc,
23450               izb = Iccc - Iccp,
23451               itt = u*u*ixx + v*v*iyy + w*w*izz + 2*u*v*ixy + 2*u*w*ixz + 2*v*w*iyz,
23452               it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb) + w*cimg::minmod(izf,izb),
23453               veloc = -amp*cimg::sign(itt)*cimg::abs(it);
23454             *(ptrd++) = veloc;
23455             if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
23456           }
23457         } else cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { // Inverse diffusion.
23458           const Tfloat veloc = -Ipcc - Incc - Icpc - Icnc - Iccp - Iccn + 6*Iccc;
23459           *(ptrd++) = veloc;
23460           if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
23461         }
23462       } else {
23463         CImg_3x3(I,Tfloat);
23464         if (sharpen_type) { // Shock filters.
23465           CImg<Tfloat> G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors());
23466           if (sigma>0) G.blur(sigma);
23467           Tfloat *ptrG0 = G.data(0,0,0,0), *ptrG1 = G.data(0,0,0,1), *ptrG2 = G.data(0,0,0,2);
23468           cimg_forXY(G,x,y) {
23469             G.get_tensor_at(x,y).symmetric_eigen(val,vec);
23470             if (val[0]<0) val[0] = 0;
23471             if (val[1]<0) val[1] = 0;
23472             *(ptrG0++) = vec(0,0);
23473             *(ptrG1++) = vec(0,1);
23474             *(ptrG2++) = 1 - (Tfloat)std::pow(1 + val[0] + val[1],-(Tfloat)nedge);
23475           }
23476           cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
23477             const Tfloat
23478               u = G(x,y,0),
23479               v = G(x,y,1),
23480               amp = G(x,y,2),
23481               ixx = Inc + Ipc - 2*Icc,
23482               ixy = (Inn + Ipp - Inp - Ipn)/4,
23483               iyy = Icn + Icp - 2*Icc,
23484               ixf = Inc - Icc,
23485               ixb = Icc - Ipc,
23486               iyf = Icn - Icc,
23487               iyb = Icc - Icp,
23488               itt = u*u*ixx + v*v*iyy + 2*u*v*ixy,
23489               it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb),
23490               veloc = -amp*cimg::sign(itt)*cimg::abs(it);
23491             *(ptrd++) = veloc;
23492             if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
23493           }
23494         } else cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) { // Inverse diffusion.
23495           const Tfloat veloc = -Ipc - Inc - Icp - Icn + 4*Icc;
23496           *(ptrd++) = veloc;
23497           if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
23498         }
23499       }
23500       if (veloc_max<=0) return *this;
23501       return ((velocity*=amplitude/veloc_max)+=*this).cut(val_min,val_max).move_to(*this);
23502     }
23503 
23504     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 {
23505       return (+*this).sharpen(amplitude,sharpen_type,edge,alpha,sigma);
23506     }
23507 
23508     //! Compute the list of images, corresponding to the XY-gradients of an image.
23509     /**
23510        \param scheme = Numerical scheme used for the gradient computation :
23511        - -1 = Backward finite differences
23512        - 0 = Centered finite differences
23513        - 1 = Forward finite differences
23514        - 2 = Using Sobel masks
23515        - 3 = Using rotation invariant masks
23516        - 4 = Using Deriche recusrsive filter.
23517     **/
23518     CImgList<Tfloat> get_gradient(const char *const axes=0, const int scheme=3) const {
23519       CImgList<Tfloat> grad(2,_width,_height,_depth,_spectrum);
23520       Tfloat *ptrd0 = grad[0]._data, *ptrd1 = grad[1]._data;
23521       bool is_3d = false;
23522       if (axes) {
23523         for (unsigned int a = 0; axes[a]; ++a) {
23524           const char axis = cimg::uncase(axes[a]);
23525           switch (axis) {
23526           case 'x' : case 'y' : break;
23527           case 'z' : is_3d = true; break;
23528           default :
23529             throw CImgArgumentException(_cimg_instance
23530                                         "get_gradient() : Invalid specified axis '%c'.",
23531                                         cimg_instance,
23532                                         axis);
23533           }
23534         }
23535       } else is_3d = (_depth>1);
23536       if (is_3d) {
23537         CImg<Tfloat>(_width,_height,_depth,_spectrum).move_to(grad);
23538         Tfloat *ptrd2 = grad[2]._data;
23539         switch (scheme) { // 3d.
23540         case -1 : { // Backward finite differences.
23541           CImg_3x3x3(I,Tfloat);
23542           cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
23543             *(ptrd0++) = Iccc - Ipcc;
23544             *(ptrd1++) = Iccc - Icpc;
23545             *(ptrd2++) = Iccc - Iccp;
23546           }
23547         } break;
23548         case 1 : { // Forward finite differences.
23549           CImg_2x2x2(I,Tfloat);
23550           cimg_forC(*this,c) cimg_for2x2x2(*this,x,y,z,c,I,Tfloat) {
23551             *(ptrd0++) = Incc - Iccc;
23552             *(ptrd1++) = Icnc - Iccc;
23553             *(ptrd2++) = Iccn - Iccc;
23554           }
23555         } break;
23556         case 4 : { // Using Deriche filter with low standard variation.
23557           grad[0] = get_deriche(0,1,'x');
23558           grad[1] = get_deriche(0,1,'y');
23559           grad[2] = get_deriche(0,1,'z');
23560         } break;
23561         default : { // Central finite differences.
23562           CImg_3x3x3(I,Tfloat);
23563           cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
23564             *(ptrd0++) = (Incc - Ipcc)/2;
23565             *(ptrd1++) = (Icnc - Icpc)/2;
23566             *(ptrd2++) = (Iccn - Iccp)/2;
23567           }
23568         }
23569         }
23570       } else switch (scheme) { // 2d.
23571       case -1 : { // Backward finite differences.
23572         CImg_3x3(I,Tfloat);
23573         cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
23574           *(ptrd0++) = Icc - Ipc;
23575           *(ptrd1++) = Icc - Icp;
23576         }
23577       } break;
23578       case 1 : { // Forward finite differences.
23579         CImg_2x2(I,Tfloat);
23580         cimg_forZC(*this,z,c) cimg_for2x2(*this,x,y,z,c,I,Tfloat) {
23581           *(ptrd0++) = Inc - Icc;
23582           *(ptrd1++) = Icn - Icc;
23583         }
23584       } break;
23585       case 2 : { // Sobel scheme.
23586         CImg_3x3(I,Tfloat);
23587         const Tfloat a = 1, b = 2;
23588         cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
23589           *(ptrd0++) = -a*Ipp - b*Ipc - a*Ipn + a*Inp + b*Inc + a*Inn;
23590           *(ptrd1++) = -a*Ipp - b*Icp - a*Inp + a*Ipn + b*Icn + a*Inn;
23591         }
23592       } break;
23593       case 3 : { // Rotation invariant mask.
23594         CImg_3x3(I,Tfloat);
23595         const Tfloat a = (Tfloat)(0.25f*(2-std::sqrt(2.0f))), b = (Tfloat)(0.5f*(std::sqrt(2.0f)-1));
23596         cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
23597           *(ptrd0++) = -a*Ipp - b*Ipc - a*Ipn + a*Inp + b*Inc + a*Inn;
23598           *(ptrd1++) = -a*Ipp - b*Icp - a*Inp + a*Ipn + b*Icn + a*Inn;
23599         }
23600       } break;
23601       case 4 : { // using Deriche filter with low standard variation
23602         grad[0] = get_deriche(0,1,'x');
23603         grad[1] = get_deriche(0,1,'y');
23604       } break;
23605       default : { // central finite differences
23606         CImg_3x3(I,Tfloat);
23607         cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
23608           *(ptrd0++) = (Inc - Ipc)/2;
23609           *(ptrd1++) = (Icn - Icp)/2;
23610         }
23611       }
23612       }
23613       if (!axes) return grad;
23614       CImgList<Tfloat> res;
23615       for (unsigned int l = 0; axes[l]; ++l) {
23616         const char axis = cimg::uncase(axes[l]);
23617         switch (axis) {
23618         case 'x' : res.insert(grad[0]); break;
23619         case 'y' : res.insert(grad[1]); break;
23620         case 'z' : res.insert(grad[2]); break;
23621         }
23622       }
23623       grad.assign();
23624       return res;
23625     }
23626 
23627     //! Get components of the Hessian matrix of an image.
23628     CImgList<Tfloat> get_hessian(const char *const axes=0) const {
23629       CImgList<Tfloat> res;
23630       const char *naxes = axes, *const def_axes2d = "xxxyyy", *const def_axes3d = "xxxyxzyyyzzz";
23631       if (!axes) naxes = _depth>1?def_axes3d:def_axes2d;
23632       const unsigned int lmax = std::strlen(naxes);
23633       if (lmax%2)
23634         throw CImgArgumentException(_cimg_instance
23635                                     "get_hessian() : Invalid specified axes '%s'.",
23636                                     cimg_instance,
23637                                     naxes);
23638 
23639       res.assign(lmax/2,_width,_height,_depth,_spectrum);
23640       if (!cimg::strcasecmp(naxes,def_axes3d)) { // 3d
23641         Tfloat
23642           *ptrd0 = res[0]._data, *ptrd1 = res[1]._data, *ptrd2 = res[2]._data,
23643           *ptrd3 = res[3]._data, *ptrd4 = res[4]._data, *ptrd5 = res[5]._data;
23644         CImg_3x3x3(I,Tfloat);
23645         cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
23646           *(ptrd0++) = Ipcc + Incc - 2*Iccc;          // Ixx
23647           *(ptrd1++) = (Ippc + Innc - Ipnc - Inpc)/4; // Ixy
23648           *(ptrd2++) = (Ipcp + Incn - Ipcn - Incp)/4; // Ixz
23649           *(ptrd3++) = Icpc + Icnc - 2*Iccc;          // Iyy
23650           *(ptrd4++) = (Icpp + Icnn - Icpn - Icnp)/4; // Iyz
23651           *(ptrd5++) = Iccn + Iccp - 2*Iccc;          // Izz
23652         }
23653       } else if (!cimg::strcasecmp(naxes,def_axes2d)) { // 2d
23654         Tfloat *ptrd0 = res[0]._data, *ptrd1 = res[1]._data, *ptrd2 = res[2]._data;
23655         CImg_3x3(I,Tfloat);
23656         cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
23657           *(ptrd0++) = Ipc + Inc - 2*Icc;         // Ixx
23658           *(ptrd1++) = (Ipp + Inn - Ipn - Inp)/4; // Ixy
23659           *(ptrd2++) = Icp + Icn - 2*Icc;         // Iyy
23660         }
23661       } else for (unsigned int l = 0; l<lmax; ) { // Version with custom axes.
23662         const unsigned int l2 = l/2;
23663           char axis1 = naxes[l++], axis2 = naxes[l++];
23664           if (axis1>axis2) cimg::swap(axis1,axis2);
23665           bool valid_axis = false;
23666           Tfloat *ptrd = res[l2]._data;
23667           if (axis1=='x' && axis2=='x') { // Ixx
23668             valid_axis = true; CImg_3x3(I,Tfloat);
23669             cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Ipc + Inc - 2*Icc;
23670           }
23671           else if (axis1=='x' && axis2=='y') { // Ixy
23672             valid_axis = true; CImg_3x3(I,Tfloat);
23673             cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Ipp + Inn - Ipn - Inp)/4;
23674           }
23675           else if (axis1=='x' && axis2=='z') { // Ixz
23676             valid_axis = true; CImg_3x3x3(I,Tfloat);
23677             cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Ipcp + Incn - Ipcn - Incp)/4;
23678           }
23679           else if (axis1=='y' && axis2=='y') { // Iyy
23680             valid_axis = true; CImg_3x3(I,Tfloat);
23681             cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Icp + Icn - 2*Icc;
23682           }
23683           else if (axis1=='y' && axis2=='z') { // Iyz
23684             valid_axis = true; CImg_3x3x3(I,Tfloat);
23685             cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Icpp + Icnn - Icpn - Icnp)/4;
23686           }
23687           else if (axis1=='z' && axis2=='z') { // Izz
23688             valid_axis = true; CImg_3x3x3(I,Tfloat);
23689             cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Iccn + Iccp - 2*Iccc;
23690           }
23691           else if (!valid_axis)
23692             throw CImgArgumentException(_cimg_instance
23693                                         "get_hessian() : Invalid specified axes '%s'.",
23694                                         cimg_instance,
23695                                         naxes);
23696         }
23697       return res;
23698     }
23699 
23700     //! Compute the laplacian of the image instance.
23701     CImg<T>& laplacian() {
23702       return get_laplacian().move_to(*this);
23703     }
23704 
23705     CImg<Tfloat> get_laplacian() const {
23706       if (is_empty()) return CImg<Tfloat>();
23707       CImg<Tfloat> res(_width,_height,_depth,_spectrum);
23708       Tfloat *ptrd = res._data;
23709       if (_depth>1) { // 3d
23710         CImg_3x3x3(I,Tfloat);
23711         cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat)
23712           *(ptrd++) = Incc + Ipcc + Icnc + Icpc + Iccn + Iccp - 6*Iccc;
23713       } else if (_height>1) { // 2d
23714         CImg_3x3(I,Tfloat);
23715         cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat)
23716           *(ptrd++) = Inc + Ipc + Icn + Icp - 4*Icc;
23717       } else { // 1d
23718         CImg_3x3(I,Tfloat);
23719         cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat)
23720           *(ptrd++) = Inc + Ipc - 2*Icc;
23721       }
23722       return res;
23723     }
23724 
23725     //! Compute the structure tensor field of an image.
23726     CImg<T>& structure_tensors(const unsigned int scheme=2) {
23727       return get_structure_tensors(scheme).move_to(*this);
23728     }
23729 
23730     CImg<Tfloat> get_structure_tensors(const unsigned int scheme=2) const {
23731       if (is_empty()) return *this;
23732       CImg<Tfloat> res;
23733       if (_depth>1) { // 3d
23734         res.assign(_width,_height,_depth,6,0);
23735         CImg_3x3x3(I,Tfloat);
23736         switch (scheme) {
23737         case 0 : { // classical central finite differences
23738           cimg_forC(*this,c) {
23739             Tfloat
23740               *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
23741               *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
23742             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
23743               const Tfloat
23744                 ix = (Incc - Ipcc)/2,
23745                 iy = (Icnc - Icpc)/2,
23746                 iz = (Iccn - Iccp)/2;
23747               *(ptrd0++)+=ix*ix;
23748               *(ptrd1++)+=ix*iy;
23749               *(ptrd2++)+=ix*iz;
23750               *(ptrd3++)+=iy*iy;
23751               *(ptrd4++)+=iy*iz;
23752               *(ptrd5++)+=iz*iz;
23753             }
23754           }
23755         } break;
23756         case 1 : { // Forward/backward finite differences (version 1).
23757           cimg_forC(*this,c) {
23758             Tfloat
23759               *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
23760               *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
23761             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
23762               const Tfloat
23763                 ixf = Incc - Iccc, ixb = Iccc - Ipcc,
23764                 iyf = Icnc - Iccc, iyb = Iccc - Icpc,
23765                 izf = Iccn - Iccc, izb = Iccc - Iccp;
23766               *(ptrd0++)+=(ixf*ixf + ixf*ixb + ixb*ixf + ixb*ixb)/4;
23767               *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
23768               *(ptrd2++)+=(ixf*izf + ixf*izb + ixb*izf + ixb*izb)/4;
23769               *(ptrd3++)+=(iyf*iyf + iyf*iyb + iyb*iyf + iyb*iyb)/4;
23770               *(ptrd4++)+=(iyf*izf + iyf*izb + iyb*izf + iyb*izb)/4;
23771               *(ptrd5++)+=(izf*izf + izf*izb + izb*izf + izb*izb)/4;
23772             }
23773           }
23774         } break;
23775         default : { // Forward/backward finite differences (version 2).
23776           cimg_forC(*this,c) {
23777             Tfloat
23778               *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
23779               *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
23780             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
23781               const Tfloat
23782                 ixf = Incc - Iccc, ixb = Iccc - Ipcc,
23783                 iyf = Icnc - Iccc, iyb = Iccc - Icpc,
23784                 izf = Iccn - Iccc, izb = Iccc - Iccp;
23785               *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2;
23786               *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
23787               *(ptrd2++)+=(ixf*izf + ixf*izb + ixb*izf + ixb*izb)/4;
23788               *(ptrd3++)+=(iyf*iyf + iyb*iyb)/2;
23789               *(ptrd4++)+=(iyf*izf + iyf*izb + iyb*izf + iyb*izb)/4;
23790               *(ptrd5++)+=(izf*izf + izb*izb)/2;
23791             }
23792           }
23793         } break;
23794         }
23795       } else { // 2d
23796         res.assign(_width,_height,_depth,3,0);
23797         CImg_3x3(I,Tfloat);
23798         switch (scheme) {
23799         case 0 : { // classical central finite differences
23800           cimg_forC(*this,c) {
23801             Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
23802             cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
23803               const Tfloat
23804                 ix = (Inc - Ipc)/2,
23805                 iy = (Icn - Icp)/2;
23806               *(ptrd0++)+=ix*ix;
23807               *(ptrd1++)+=ix*iy;
23808               *(ptrd2++)+=iy*iy;
23809             }
23810           }
23811         } break;
23812         case 1 : { // Forward/backward finite differences (version 1).
23813           cimg_forC(*this,c) {
23814             Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
23815             cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
23816               const Tfloat
23817                 ixf = Inc - Icc, ixb = Icc - Ipc,
23818                 iyf = Icn - Icc, iyb = Icc - Icp;
23819               *(ptrd0++)+=(ixf*ixf + ixf*ixb + ixb*iyf + ixb*ixb)/4;
23820               *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
23821               *(ptrd2++)+=(iyf*iyf + iyf*iyb + iyb*iyf + iyb*iyb)/4;
23822             }
23823           }
23824         } break;
23825         default : { // Forward/backward finite differences (version 2).
23826           cimg_forC(*this,c) {
23827             Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
23828             cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
23829               const Tfloat
23830                 ixf = Inc - Icc, ixb = Icc - Ipc,
23831                 iyf = Icn - Icc, iyb = Icc - Icp;
23832               *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2;
23833               *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
23834               *(ptrd2++)+=(iyf*iyf + iyb*iyb)/2;
23835             }
23836           }
23837         } break;
23838         }
23839       }
23840       return res;
23841     }
23842 
23843     //! Get a diffusion tensor for edge-preserving anisotropic smoothing of an image.
23844     CImg<T>& edge_tensors(const float sharpness=0.7f, const float anisotropy=0.6f,
23845                           const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) {
23846       CImg<Tfloat> res;
23847       const float nsharpness = cimg::max(sharpness,1e-5f), power1 = (is_sqrt?0.5f:1)*nsharpness, power2 = power1/(1e-7f+1-anisotropy);
23848       blur(alpha).normalize(0,(T)255);
23849 
23850       if (_depth>1) { // 3d
23851         CImg<floatT> val(3), vec(3,3);
23852         get_structure_tensors().move_to(res).blur(sigma);
23853         Tfloat
23854           *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
23855           *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
23856         cimg_forXYZ(*this,x,y,z) {
23857           res.get_tensor_at(x,y,z).symmetric_eigen(val,vec);
23858           const float
23859             _l1 = val[2], _l2 = val[1], _l3 = val[0],
23860             l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0, l3 = _l3>0?_l3:0,
23861             ux = vec(0,0), uy = vec(0,1), uz = vec(0,2),
23862             vx = vec(1,0), vy = vec(1,1), vz = vec(1,2),
23863             wx = vec(2,0), wy = vec(2,1), wz = vec(2,2),
23864             n1 = (float)std::pow(1+l1+l2+l3,-power1),
23865             n2 = (float)std::pow(1+l1+l2+l3,-power2);
23866           *(ptrd0++) = n1*(ux*ux + vx*vx) + n2*wx*wx;
23867           *(ptrd1++) = n1*(ux*uy + vx*vy) + n2*wx*wy;
23868           *(ptrd2++) = n1*(ux*uz + vx*vz) + n2*wx*wz;
23869           *(ptrd3++) = n1*(uy*uy + vy*vy) + n2*wy*wy;
23870           *(ptrd4++) = n1*(uy*uz + vy*vz) + n2*wy*wz;
23871           *(ptrd5++) = n1*(uz*uz + vz*vz) + n2*wz*wz;
23872         }
23873       } else { // for 2d images
23874         CImg<floatT> val(2), vec(2,2);
23875         get_structure_tensors().move_to(res).blur(sigma);
23876         Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
23877         cimg_forXY(*this,x,y) {
23878           res.get_tensor_at(x,y).symmetric_eigen(val,vec);
23879           const float
23880             _l1 = val[1], _l2 = val[0],
23881             l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0,
23882             ux = vec(1,0), uy = vec(1,1),
23883             vx = vec(0,0), vy = vec(0,1),
23884             n1 = (float)std::pow(1+l1+l2,-power1),
23885             n2 = (float)std::pow(1+l1+l2,-power2);
23886           *(ptrd0++) = n1*ux*ux + n2*vx*vx;
23887           *(ptrd1++) = n1*ux*uy + n2*vx*vy;
23888           *(ptrd2++) = n1*uy*uy + n2*vy*vy;
23889         }
23890       }
23891       return res.move_to(*this);
23892     }
23893 
23894     CImg<Tfloat> get_edge_tensors(const float sharpness=0.7f, const float anisotropy=0.6f,
23895                                   const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) const {
23896       return CImg<Tfloat>(*this,false).edge_tensors(sharpness,anisotropy,alpha,sigma,is_sqrt);
23897     }
23898 
23899     //! Estimate a displacement field between specified source image and image instance.
23900     /**
23901        \param is_backward : if false, match I2(X+U(X)) = I1(X), else match I2(X) = I1(X-U(X)).
23902     **/
23903     CImg<T>& displacement(const CImg<T>& source, const float smoothness=0.1f, const float precision=5.0f,
23904                           const unsigned int nb_scales=0, const unsigned int iteration_max=10000,
23905                           const bool is_backward=false) {
23906       return get_displacement(source,smoothness,precision,nb_scales,iteration_max,is_backward).move_to(*this);
23907     }
23908 
23909     CImg<Tfloat> get_displacement(const CImg<T>& source,
23910                                   const float smoothness=0.1f, const float precision=5.0f,
23911                                   const unsigned int nb_scales=0, const unsigned int iteration_max=10000,
23912                                   const bool is_backward=false) const {
23913       if (is_empty() || !source) return (+*this);
23914       if (!is_sameXYZC(source))
23915         throw CImgArgumentException(_cimg_instance
23916                                     "displacement() : Instance and source image (%u,%u,%u,%u,%p) have different dimensions.",
23917                                     cimg_instance,
23918                                     source._width,source._height,source._depth,source._spectrum,source._data);
23919       if (precision<0)
23920         throw CImgArgumentException(_cimg_instance
23921                                     "displacement() : Invalid specified precision %g "
23922                                     "(should be >=0)",
23923                                     cimg_instance,
23924                                     precision);
23925       const unsigned int _nb_scales = nb_scales>0?nb_scales:(unsigned int)(2*std::log((double)(cimg::max(_width,_height))));
23926       const float _precision = (float)std::pow(10.0,-(double)precision);
23927       float sm, sM = source.max_min(sm), tm, tM = max_min(tm);
23928       const float sdelta = sm==sM?1:(sM - sm), tdelta = tm==tM?1:(tM - tm);
23929       const bool is_3d = source._depth>1;
23930       CImg<floatT> U;
23931 
23932       for (int scale = _nb_scales-1; scale>=0; --scale) {
23933         const float factor = (float)std::pow(1.5,(double)scale);
23934         const unsigned int
23935           _sw = (unsigned int)(_width/factor), sw = _sw?_sw:1,
23936           _sh = (unsigned int)(_height/factor), sh = _sh?_sh:1,
23937           _sd = (unsigned int)(_depth/factor), sd = _sd?_sd:1;
23938         const CImg<Tfloat>
23939           I1 = (source.get_resize(sw,sh,sd,-100,2)-=sm)/=sdelta,
23940           I2 = (get_resize(I1,2)-=tm)/=tdelta;
23941         if (U) (U*=1.5f).resize(I2._width,I2._height,I2._depth,-100,3);
23942         else U.assign(I2._width,I2._height,I2._depth,is_3d?3:2,0);
23943         float dt = 2, energy = cimg::type<float>::max();
23944         const CImgList<Tfloat> dI = is_backward?I1.get_gradient():I2.get_gradient();
23945 
23946         for (unsigned int iteration = 0; iteration<iteration_max; ++iteration) {
23947           float _energy = 0;
23948 
23949           if (is_3d) { // 3d version.
23950             if (smoothness>=0) cimg_for3XYZ(U,x,y,z) { // Isotropic regularization.
23951                 const float
23952                   X = is_backward?x - U(x,y,z,0):x + U(x,y,z,0),
23953                   Y = is_backward?y - U(x,y,z,1):y + U(x,y,z,1),
23954                   Z = is_backward?z - U(x,y,z,2):z + U(x,y,z,2);
23955                 float deltaI = 0, _energy_regul = 0;
23956                 if (is_backward) cimg_forC(I2,c) deltaI+=(float)(I1.linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c));
23957                 else cimg_forC(I2,c) deltaI+=(float)(I1(x,y,z,c) - I2.linear_atXYZ(X,Y,Z,c));
23958                 cimg_forC(U,c) {
23959                   const float
23960                     Ux = 0.5f*(U(_n1x,y,z,c) - U(_p1x,y,z,c)),
23961                     Uy = 0.5f*(U(x,_n1y,z,c) - U(x,_p1y,z,c)),
23962                     Uz = 0.5f*(U(x,y,_n1z,c) - U(x,y,_p1z,c)),
23963                     Uxx = U(_n1x,y,z,c) + U(_p1x,y,z,c),
23964                     Uyy = U(x,_n1y,z,c) + U(x,_p1y,z,c),
23965                     Uzz = U(x,y,_n1z,c) + U(x,y,_p1z,c);
23966                   U(x,y,z,c) = (float)(U(x,y,z,c) + dt*(deltaI*dI[c].linear_atXYZ(X,Y,Z) + smoothness* ( Uxx + Uyy + Uzz)))/(1+6*smoothness*dt);
23967                   _energy_regul+=Ux*Ux + Uy*Uy + Uz*Uz;
23968                 }
23969                 _energy+=deltaI*deltaI + smoothness*_energy_regul;
23970               } else {
23971               const float nsmoothness = -smoothness;
23972               cimg_for3XYZ(U,x,y,z) { // Anisotropic regularization.
23973                 const float
23974                   X = is_backward?x - U(x,y,z,0):x + U(x,y,z,0),
23975                   Y = is_backward?y - U(x,y,z,1):y + U(x,y,z,1),
23976                   Z = is_backward?z - U(x,y,z,2):z + U(x,y,z,2);
23977                 float deltaI = 0, _energy_regul = 0;
23978                 if (is_backward) cimg_forC(I2,c) deltaI+=(float)(I1.linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c));
23979                 else cimg_forC(I2,c) deltaI+=(float)(I1(x,y,z,c) - I2.linear_atXYZ(X,Y,Z,c));
23980                 cimg_forC(U,c) {
23981                   const float
23982                     Ux = 0.5f*(U(_n1x,y,z,c) - U(_p1x,y,z,c)),
23983                     Uy = 0.5f*(U(x,_n1y,z,c) - U(x,_p1y,z,c)),
23984                     Uz = 0.5f*(U(x,y,_n1z,c) - U(x,y,_p1z,c)),
23985                     N2 = Ux*Ux + Uy*Uy + Uz*Uz,
23986                     N = std::sqrt(N2),
23987                     N3 = 1e-5 + N2*N,
23988                     coef_a = (1 - Ux*Ux/N2)/N,
23989                     coef_b = -Ux*Uy/N3,
23990                     coef_c = -Ux*Uz/N3,
23991                     coef_d = (1 - Uy*Uy/N2)/N,
23992                     coef_e = -Uy*Uz/N3,
23993                     coef_f = (1 - Uz*Uz/N2)/N,
23994                     Uxx = U(_n1x,y,z,c) + U(_p1x,y,z,c),
23995                     Uyy = U(x,_n1y,z,c) + U(x,_p1y,z,c),
23996                     Uzz = U(x,y,_n1z,c) + U(x,y,_p1z,c),
23997                     Uxy = 0.25f*(U(_n1x,_n1y,z,c) + U(_p1x,_p1y,z,c) - U(_n1x,_p1y,z,c) - U(_n1x,_p1y,z,c)),
23998                     Uxz = 0.25f*(U(_n1x,y,_n1z,c) + U(_p1x,y,_p1z,c) - U(_n1x,y,_p1z,c) - U(_n1x,y,_p1z,c)),
23999                     Uyz = 0.25f*(U(x,_n1y,_n1z,c) + U(x,_p1y,_p1z,c) - U(x,_n1y,_p1z,c) - U(x,_n1y,_p1z,c));
24000                   U(x,y,z,c) = (float)(U(x,y,z,c) + dt*(deltaI*dI[c].linear_atXYZ(X,Y,Z) +
24001                                                         nsmoothness* ( coef_a*Uxx + 2*coef_b*Uxy + 2*coef_c*Uxz + coef_d*Uyy + 2*coef_e*Uyz + coef_f*Uzz ))
24002                                        )/(1+2*(coef_a+coef_d+coef_f)*nsmoothness*dt);
24003                   _energy_regul+=N;
24004                 }
24005                 _energy+=deltaI*deltaI + nsmoothness*_energy_regul;
24006               }
24007             }
24008           } else { // 2d version.
24009             if (smoothness>=0) cimg_for3XY(U,x,y) { // Isotropic regularization.
24010                 const float
24011                   X = is_backward?x - U(x,y,0):x + U(x,y,0),
24012                   Y = is_backward?y - U(x,y,1):y + U(x,y,1);
24013                 float deltaI = 0, _energy_regul = 0;
24014                 if (is_backward) cimg_forC(I2,c) deltaI+=(float)(I1.linear_atXY(X,Y,c) - I2(x,y,c));
24015                 else cimg_forC(I2,c) deltaI+=(float)(I1(x,y,c) - I2.linear_atXY(X,Y,c));
24016                 cimg_forC(U,c) {
24017                   const float
24018                     Ux = 0.5f*(U(_n1x,y,c) - U(_p1x,y,c)),
24019                     Uy = 0.5f*(U(x,_n1y,c) - U(x,_p1y,c)),
24020                     Uxx = U(_n1x,y,c) + U(_p1x,y,c),
24021                     Uyy = U(x,_n1y,c) + U(x,_p1y,c);
24022                   U(x,y,c) = (float)(U(x,y,c) + dt*(deltaI*dI[c].linear_atXY(X,Y) + smoothness*( Uxx + Uyy )))/(1+4*smoothness*dt);
24023                   _energy_regul+=Ux*Ux + Uy*Uy;
24024                 }
24025                 _energy+=deltaI*deltaI + smoothness*_energy_regul;
24026               } else {
24027               const float nsmoothness = -smoothness;
24028               cimg_for3XY(U,x,y) { // Anisotropic regularization.
24029                 const float
24030                   X = is_backward?x - U(x,y,0):x + U(x,y,0),
24031                   Y = is_backward?y - U(x,y,1):y + U(x,y,1);
24032                 float deltaI = 0, _energy_regul = 0;
24033                 if (is_backward) cimg_forC(I2,c) deltaI+=(float)(I1.linear_atXY(X,Y,c) - I2(x,y,c));
24034                 else cimg_forC(I2,c) deltaI+=(float)(I1(x,y,c) - I2.linear_atXY(X,Y,c));
24035                 cimg_forC(U,c) {
24036                   const float
24037                     Ux = 0.5f*(U(_n1x,y,c) - U(_p1x,y,c)),
24038                     Uy = 0.5f*(U(x,_n1y,c) - U(x,_p1y,c)),
24039                     N2 = Ux*Ux + Uy*Uy,
24040                     N = std::sqrt(N2),
24041                     N3 = 1e-5 + N2*N,
24042                     coef_a = Uy*Uy/N3,
24043                     coef_b = -Ux*Uy/N3,
24044                     coef_c = Ux*Ux/N3,
24045                     Uxx = U(_n1x,y,c) + U(_p1x,y,c),
24046                     Uyy = U(x,_n1y,c) + U(x,_p1y,c),
24047                     Uxy = 0.25f*(U(_n1x,_n1y,c) + U(_p1x,_p1y,c) - U(_n1x,_p1y,c) - U(_n1x,_p1y,c));
24048                   U(x,y,c) = (float)(U(x,y,c) + dt*(deltaI*dI[c].linear_atXY(X,Y) + nsmoothness*( coef_a*Uxx + 2*coef_b*Uxy + coef_c*Uyy )))/(1+2*(coef_a+coef_c)*nsmoothness*dt);
24049                   _energy_regul+=N;
24050                 }
24051                 _energy+=deltaI*deltaI + nsmoothness*_energy_regul;
24052               }
24053             }
24054           }
24055           const float d_energy = (_energy - energy)/(sw*sh*sd);
24056           if (d_energy<=0 && -d_energy<_precision) break;
24057           if (d_energy>0) dt*=0.5f;
24058           energy = _energy;
24059         }
24060       }
24061       return U;
24062     }
24063 
24064     //! Compute the distance transform according to a specified value.
24065     /** The distance transform implementation has been submitted by A. Meijster, and implements
24066         the article 'W.H. Hesselink, A. Meijster, J.B.T.M. Roerdink,
24067                      "A general algorithm for computing distance transforms in linear time.",
24068                      In: Mathematical Morphology and its Applications to Image and Signal Processing,
24069                      J. Goutsias, L. Vincent, and D.S. Bloomberg (eds.), Kluwer, 2000, pp. 331-340.'
24070          The submitted code has then been modified to fit CImg coding style and constraints.
24071     **/
24072     CImg<T>& distance(const T value, const unsigned int metric=2) {
24073       if (is_empty()) return *this;
24074       bool is_value = false;
24075       cimg_for(*this,ptr,T) *ptr = (T)(*ptr==value?is_value=true,0:999999999);
24076       if (!is_value) return fill(cimg::type<T>::max());
24077       switch (metric) {
24078       case 0 : return _distance_core(_distance_sep_cdt,_distance_dist_cdt);          // Chebyshev.
24079       case 1 : return _distance_core(_distance_sep_mdt,_distance_dist_mdt);          // Manhattan.
24080       case 3 : return _distance_core(_distance_sep_edt,_distance_dist_edt);          // Euclidean.
24081       default : return _distance_core(_distance_sep_edt,_distance_dist_edt).sqrt();  // Squared Euclidean.
24082       }
24083       return *this;
24084     }
24085 
24086     CImg<Tfloat> get_distance(const T value, const unsigned int metric=2) const {
24087       return CImg<Tfloat>(*this,false).distance((Tfloat)value,metric);
24088     }
24089 
24090     static long _distance_sep_edt(const long i, const long u, const long *const g) {
24091       return (u*u-i*i+g[u]-g[i])/(2*(u-i));
24092     }
24093 
24094     static long _distance_dist_edt(const long x, const long i, const long *const g) {
24095       return (x-i)*(x-i) + g[i];
24096     }
24097 
24098     static long _distance_sep_mdt(const long i, const long u, const long *const g) {
24099       return (u-i<=g[u]-g[i]?999999999:(g[u]-g[i]+u+i)/2);
24100     }
24101 
24102     static long _distance_dist_mdt(const long x, const long i, const long *const g) {
24103       return (x<i?i-x:x-i) + g[i];
24104     }
24105 
24106     static long _distance_sep_cdt(const long i, const long u, const long *const g) {
24107       const long h = (i+u)/2;
24108       if (g[i]<=g[u]) { return h<i+g[u]?i+g[u]:h; }
24109       return h<u-g[i]?h:u-g[i];
24110     }
24111 
24112     static long _distance_dist_cdt(const long x, const long i, const long *const g) {
24113       const long d = x<i?i-x:x-i;
24114       return d<g[i]?g[i]:d;
24115     }
24116 
24117     static void _distance_scan(const unsigned int len,
24118                                const long *const g,
24119                                long (*const sep)(const long, const long, const long *const),
24120                                long (*const f)(const long, const long, const long *const),
24121                                long *const s,
24122                                long *const t,
24123                                long *const dt) {
24124       long q = s[0] = t[0] = 0;
24125       for (int u = 1; u<(int)len; ++u) { // Forward scan.
24126         while ((q>=0) && f(t[q],s[q],g)>f(t[q],u,g)) { --q; }
24127         if (q<0) { q = 0; s[0] = u; }
24128         else { const long w = 1 + sep(s[q], u, g); if (w<(long)len) { ++q; s[q] = u; t[q] = w; }}
24129       }
24130       for (int u = (int)len-1; u>=0; --u) { dt[u] = f(u,s[q],g); if (u==t[q]) --q; } // Backward scan.
24131     }
24132 
24133     CImg<T>& _distance_core(long (*const sep)(const long, const long, const long *const),
24134                             long (*const f)(const long, const long, const long *const)) {
24135       const unsigned int wh = _width*_height;
24136       cimg_forC(*this,c) {
24137         CImg<longT> g(_width), dt(_width), s(_width), t(_width);
24138         CImg<T> img = get_shared_channel(c);
24139         cimg_forYZ(*this,y,z) { // Over X-direction.
24140           cimg_forX(*this,x) g[x] = (long)img(x,y,z,0,wh);
24141           _distance_scan(_width,g,sep,f,s,t,dt);
24142           cimg_forX(*this,x) img(x,y,z,0,wh) = (T)dt[x];
24143         }
24144         g.assign(_height); dt.assign(_height); s.assign(_height); t.assign(_height);
24145         cimg_forXZ(*this,x,z) { // Over Y-direction.
24146           cimg_forY(*this,y) g[y] = (long)img(x,y,z,0,wh);
24147           _distance_scan(_height,g,sep,f,s,t,dt);
24148           cimg_forY(*this,y) img(x,y,z,0,wh) = (T)dt[y];
24149         }
24150         if (_depth>1) {
24151           g.assign(_depth); dt.assign(_depth); s.assign(_depth); t.assign(_depth);
24152           cimg_forXY(*this,x,y) { // Over Z-direction.
24153             cimg_forZ(*this,z) g[z] = (long)img(x,y,z,0,wh);
24154             _distance_scan(_depth,g,sep,f,s,t,dt);
24155             cimg_forZ(*this,z) img(x,y,z,0,wh) = (T)dt[z];
24156           }
24157         }
24158       }
24159       return *this;
24160     }
24161 
24162     //! Compute the chamfer distance transform according to a specified value, with a custom metric.
24163     /**
24164        The algorithm code has been initially proposed by A. Meijster, and modified by D. Tschumperlé.
24165      **/
24166     template<typename t>
24167     CImg<T>& distance(const T value, const CImg<t>& metric_mask) {
24168       if (is_empty()) return *this;
24169       bool is_value = false;
24170       cimg_for(*this,ptr,T) *ptr = (T)(*ptr==value?is_value=true,0:999999999);
24171       if (!is_value) return fill(cimg::type<T>::max());
24172       const unsigned long wh = _width*_height;
24173       cimg_forC(*this,c) {
24174         CImg<T> img = get_shared_channel(c);
24175         cimg_forXYZ(metric_mask,dx,dy,dz) {
24176           const t weight = metric_mask(dx,dy,dz);
24177           if (weight) {
24178             for (int z = dz, nz = 0; z<depth(); ++z,++nz) { // Forward scan.
24179               for (int y = dy , ny = 0; y<height(); ++y,++ny) {
24180                 for (int x = dx, nx = 0; x<width(); ++x,++nx) {
24181                   const T dd = img(nx,ny,nz,0,wh) + weight;
24182                   if (dd<img(x,y,z,0,wh)) img(x,y,z,0,wh) = dd;
24183                 }
24184               }
24185             }
24186             for (int z = depth() - 1 - dz, nz = depth() - 1; z>=0; --z,--nz) { // Backward scan.
24187               for (int y = height() - 1 - dy, ny = height() - 1; y>=0; --y,--ny) {
24188                 for (int x = width() - 1 - dx, nx = width() - 1; x>=0; --x,--nx) {
24189                   const T dd = img(nx,ny,nz,0,wh) + weight;
24190                   if (dd<img(x,y,z,0,wh)) img(x,y,z,0,wh) = dd;
24191                 }
24192               }
24193             }
24194           }
24195         }
24196       }
24197       return *this;
24198     }
24199 
24200     template<typename t>
24201     CImg<Tfloat> get_distance(const T value, const CImg<t>& metric_mask) const {
24202       return CImg<Tfloat>(*this,false).distance(value,metric_mask);
24203     }
24204 
24205     //! Compute the distance map to one specified point.
24206     CImg<T>& distance_dijkstra(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
24207       return get_distance_dijkstra(x,y,z).move_to(*this);
24208     }
24209 
24210     //! Compute the distance map to one specified point \newinstance.
24211     CImg<Tfloat> get_distance_dijkstra(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const {
24212       if (is_empty()) return *this;
24213       if (!containsXYZC(x,y,z,0))
24214         throw CImgArgumentException(_cimg_instance
24215                                     "distance_dijkstra() : image instance does not contain specified starting point (%u,%u,%u).",
24216                                     cimg_instance,
24217                                     x,y,z);
24218       if (_spectrum!=1)
24219         throw CImgInstanceException(_cimg_instance
24220                                     "distance_dijkstra() : image instance is not a scalar image.",
24221                                     cimg_instance);
24222       CImg<Tfloat> res(_width,_height,_depth,2);
24223       CImg<boolT> in_queue(_width,_height,_depth,1,0);
24224       CImg<Tint> Q;
24225       unsigned int sizeQ = 0;
24226 
24227       // Put specified point in priority queue.
24228       Q._priority_queue_insert(in_queue,sizeQ,0,x,y,z);
24229       res(x,y,z) = 0; res(x,y,z,1) = 0;
24230 
24231       // Start distance propagation.
24232       while (sizeQ) {
24233 
24234         // Get and remove point with minimal potential from the queue.
24235         const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
24236         const Tfloat potential = (Tfloat)-Q(0,0);
24237         Q._priority_queue_remove(sizeQ);
24238 
24239         // Update neighbors.
24240         Tfloat npot = 0;
24241         if (x-1>=0 && Q._priority_queue_insert(in_queue,sizeQ,-(npot=(*this)(x-1,y,z)+potential),x-1,y,z)) {
24242           res(x-1,y,z) = npot; res(x-1,y,z,1) = 2;
24243         }
24244         if (x+1<width() && Q._priority_queue_insert(in_queue,sizeQ,-(npot=(*this)(x+1,y,z)+potential),x+1,y,z)) {
24245           res(x+1,y,z) = npot; res(x+1,y,z,1) = 1;
24246         }
24247         if (y-1>=0 && Q._priority_queue_insert(in_queue,sizeQ,-(npot=(*this)(x,y-1,z)+potential),x,y-1,z)) {
24248           res(x,y-1,z) = npot; res(x,y-1,z,1) = 4;
24249         }
24250         if (y+1<height() && Q._priority_queue_insert(in_queue,sizeQ,-(npot=(*this)(x,y+1,z)+potential),x,y+1,z)) {
24251           res(x,y+1,z) = npot; res(x,y+1,z,1) = 3;
24252         }
24253         if (z-1>=0 && Q._priority_queue_insert(in_queue,sizeQ,-(npot=(*this)(x,y,z-1)+potential),x,y,z-1)) {
24254           res(x,y,z-1) = npot; res(x,y,z+1,1) = 6;
24255         }
24256         if (z+1<depth() && Q._priority_queue_insert(in_queue,sizeQ,-(npot=(*this)(x,y,z+1)+potential),x,y,z+1)) {
24257           res(x,y,z+1) = npot; res(x,y,z+1,1) = 5;
24258         }
24259       }
24260       return res;
24261     }
24262 
24263     //! Compute distance function from 0-valued isophotes by the application of an Eikonal PDE.
24264     CImg<T>& distance_eikonal(const unsigned int nb_iterations, const float band_size=0, const float time_step=0.5f) {
24265       if (is_empty()) return *this;
24266       CImg<Tfloat> velocity(*this);
24267       for (unsigned int iteration = 0; iteration<nb_iterations; ++iteration) {
24268         Tfloat *ptrd = velocity._data, veloc_max = 0;
24269         if (_depth>1) { // 3d
24270           CImg_3x3x3(I,Tfloat);
24271           cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) if (band_size<=0 || cimg::abs(Iccc)<band_size) {
24272             const Tfloat
24273               gx = (Incc - Ipcc)/2,
24274               gy = (Icnc - Icpc)/2,
24275               gz = (Iccn - Iccp)/2,
24276               sgn = -cimg::sign(Iccc),
24277               ix = gx*sgn>0?(Incc - Iccc):(Iccc - Ipcc),
24278               iy = gy*sgn>0?(Icnc - Iccc):(Iccc - Icpc),
24279               iz = gz*sgn>0?(Iccn - Iccc):(Iccc - Iccp),
24280               ng = (Tfloat)(1e-5f + std::sqrt(gx*gx + gy*gy + gz*gz)),
24281               ngx = gx/ng,
24282               ngy = gy/ng,
24283               ngz = gz/ng,
24284               veloc = sgn*(ngx*ix + ngy*iy + ngz*iz - 1);
24285             *(ptrd++) = veloc;
24286             if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
24287           } else *(ptrd++) = 0;
24288         } else { // 2d version
24289           CImg_3x3(I,Tfloat);
24290           cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) if (band_size<=0 || cimg::abs(Icc)<band_size) {
24291             const Tfloat
24292               gx = (Inc - Ipc)/2,
24293               gy = (Icn - Icp)/2,
24294               sgn = -cimg::sign(Icc),
24295               ix = gx*sgn>0?(Inc - Icc):(Icc - Ipc),
24296               iy = gy*sgn>0?(Icn - Icc):(Icc - Icp),
24297               ng = (Tfloat)(1e-5f + std::sqrt(gx*gx + gy*gy)),
24298               ngx = gx/ng,
24299               ngy = gy/ng,
24300               veloc = sgn*(ngx*ix + ngy*iy - 1);
24301             *(ptrd++) = veloc;
24302             if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
24303           } else *(ptrd++) = 0;
24304         }
24305         if (veloc_max>0) *this+=(velocity*=time_step/veloc_max);
24306       }
24307       return *this;
24308     }
24309 
24310     CImg<Tfloat> get_distance_eikonal(const unsigned int nb_iterations, const float band_size=0, const float time_step=0.5f) const {
24311       return CImg<Tfloat>(*this,false).distance_eikonal(nb_iterations,band_size,time_step);
24312     }
24313 
24314     //! Compute the Haar multiscale wavelet transform (monodimensional version).
24315     /**
24316        \param axis Axis considered for the transform.
24317        \param invert Set inverse of direct transform.
24318        \param nb_scales Number of scales used for the transform.
24319     **/
24320     CImg<T>& haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) {
24321       return get_haar(axis,invert,nb_scales).move_to(*this);
24322     }
24323 
24324     CImg<Tfloat> get_haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) const {
24325       if (is_empty() || !nb_scales) return (+*this);
24326       CImg<Tfloat> res;
24327 
24328       if (nb_scales==1) {
24329         switch (cimg::uncase(axis)) { // Single scale transform
24330         case 'x' : {
24331           const unsigned int w = _width/2;
24332           if (w) {
24333             if (w%2)
24334               throw CImgInstanceException(_cimg_instance
24335                                           "haar() : Sub-image width %u is not even at scale %u.",
24336                                           cimg_instance,
24337                                           w);
24338 
24339             res.assign(_width,_height,_depth,_spectrum);
24340             if (invert) cimg_forYZC(*this,y,z,c) { // Inverse transform along X
24341               for (unsigned int x = 0, xw = w, x2 = 0; x<w; ++x, ++xw) {
24342                 const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(xw,y,z,c);
24343                 res(x2++,y,z,c) = val0 - val1;
24344                 res(x2++,y,z,c) = val0 + val1;
24345               }
24346             } else cimg_forYZC(*this,y,z,c) { // Direct transform along X
24347               for (unsigned int x = 0, xw = w, x2 = 0; x<w; ++x, ++xw) {
24348                 const Tfloat val0 = (Tfloat)(*this)(x2++,y,z,c), val1 = (Tfloat)(*this)(x2++,y,z,c);
24349                 res(x,y,z,c) = (val0 + val1)/2;
24350                 res(xw,y,z,c) = (val1 - val0)/2;
24351               }
24352             }
24353           } else return *this;
24354         } break;
24355         case 'y' : {
24356           const unsigned int h = _height/2;
24357           if (h) {
24358             if (h%2)
24359               throw CImgInstanceException(_cimg_instance
24360                                           "haar() : Sub-image height %u is not even at scale %u.",
24361                                           cimg_instance,
24362                                           h);
24363 
24364             res.assign(_width,_height,_depth,_spectrum);
24365             if (invert) cimg_forXZC(*this,x,z,c) { // Inverse transform along Y
24366               for (unsigned int y = 0, yh = h, y2 = 0; y<h; ++y, ++yh) {
24367                 const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(x,yh,z,c);
24368                 res(x,y2++,z,c) = val0 - val1;
24369                 res(x,y2++,z,c) = val0 + val1;
24370               }
24371             } else cimg_forXZC(*this,x,z,c) {
24372               for (unsigned int y = 0, yh = h, y2 = 0; y<h; ++y, ++yh) { // Direct transform along Y
24373                 const Tfloat val0 = (Tfloat)(*this)(x,y2++,z,c), val1 = (Tfloat)(*this)(x,y2++,z,c);
24374                 res(x,y,z,c) = (val0 + val1)/2;
24375                 res(x,yh,z,c) = (val1 - val0)/2;
24376               }
24377             }
24378           } else return *this;
24379         } break;
24380         case 'z' : {
24381           const unsigned int d = _depth/2;
24382           if (d) {
24383             if (d%2)
24384               throw CImgInstanceException(_cimg_instance
24385                                           "haar() : Sub-image depth %u is not even at scale %u.",
24386                                           cimg_instance,
24387                                           d);
24388 
24389             res.assign(_width,_height,_depth,_spectrum);
24390             if (invert) cimg_forXYC(*this,x,y,c) { // Inverse transform along Z
24391               for (unsigned int z = 0, zd = d, z2 = 0; z<d; ++z, ++zd) {
24392                 const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(x,y,zd,c);
24393                 res(x,y,z2++,c) = val0 - val1;
24394                 res(x,y,z2++,c) = val0 + val1;
24395               }
24396             } else cimg_forXYC(*this,x,y,c) {
24397               for (unsigned int z = 0, zd = d, z2 = 0; z<d; ++z, ++zd) { // Direct transform along Z
24398                 const Tfloat val0 = (Tfloat)(*this)(x,y,z2++,c), val1 = (Tfloat)(*this)(x,y,z2++,c);
24399                 res(x,y,z,c) = (val0 + val1)/2;
24400                 res(x,y,zd,c) = (val1 - val0)/2;
24401               }
24402             }
24403           } else return *this;
24404         } break;
24405         default :
24406           throw CImgArgumentException(_cimg_instance
24407                                       "haar() : Invalid specified axis '%c' "
24408                                       "(should be { x | y | z }).",
24409                                       cimg_instance,
24410                                       axis);
24411         }
24412       } else { // Multi-scale version
24413         if (invert) {
24414           res.assign(*this);
24415           switch (cimg::uncase(axis)) {
24416           case 'x' : {
24417             unsigned int w = _width;
24418             for (unsigned int s = 1; w && s<nb_scales; ++s) w/=2;
24419             for (w = w?w:1; w<=_width; w*=2) res.draw_image(res.get_crop(0,w-1).get_haar('x',true,1));
24420           } break;
24421           case 'y' : {
24422             unsigned int h = _width;
24423             for (unsigned int s = 1; h && s<nb_scales; ++s) h/=2;
24424             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));
24425           } break;
24426           case 'z' : {
24427             unsigned int d = _depth;
24428             for (unsigned int s = 1; d && s<nb_scales; ++s) d/=2;
24429             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));
24430           } break;
24431           default :
24432             throw CImgArgumentException(_cimg_instance
24433                                         "haar() : Invalid specified axis '%c' "
24434                                         "(should be { x | y | z }).",
24435                                         cimg_instance,
24436                                         axis);
24437           }
24438         } else { // Direct transform
24439           res = get_haar(axis,false,1);
24440           switch (cimg::uncase(axis)) {
24441           case 'x' : {
24442             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));
24443           } break;
24444           case 'y' : {
24445             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));
24446           } break;
24447           case 'z' : {
24448             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));
24449           } break;
24450           default :
24451             throw CImgArgumentException(_cimg_instance
24452                                         "haar() : Invalid specified axis '%c' "
24453                                         "(should be { x | y | z }).",
24454                                         cimg_instance,
24455                                         axis);
24456           }
24457         }
24458       }
24459       return res;
24460     }
24461 
24462     //! Compute the Haar multiscale wavelet transform.
24463     /**
24464        \param invert Set inverse of direct transform.
24465        \param nb_scales Number of scales used for the transform.
24466     **/
24467     CImg<T>& haar(const bool invert=false, const unsigned int nb_scales=1) {
24468       return get_haar(invert,nb_scales).move_to(*this);
24469     }
24470 
24471     CImg<Tfloat> get_haar(const bool invert=false, const unsigned int nb_scales=1) const {
24472       CImg<Tfloat> res;
24473 
24474       if (nb_scales==1) { // Single scale transform
24475         if (_width>1) get_haar('x',invert,1).move_to(res);
24476         if (_height>1) { if (res) res.haar('y',invert,1); else get_haar('y',invert,1).move_to(res); }
24477         if (_depth>1) { if (res) res.haar('z',invert,1); else get_haar('z',invert,1).move_to(res); }
24478         if (res) return res;
24479       } else { // Multi-scale transform
24480         if (invert) { // Inverse transform
24481           res.assign(*this);
24482           if (_width>1) {
24483             if (_height>1) {
24484               if (_depth>1) {
24485                 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; }
24486                 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)
24487                   res.draw_image(res.get_crop(0,0,0,w-1,h-1,d-1).get_haar(true,1));
24488               } else {
24489                 unsigned int w = _width, h = _height; for (unsigned int s = 1; w && h && s<nb_scales; ++s) { w/=2; h/=2; }
24490                 for (w = w?w:1, h = h?h:1; w<=_width && h<=_height; w*=2, h*=2)
24491                   res.draw_image(res.get_crop(0,0,0,w-1,h-1,0).get_haar(true,1));
24492               }
24493             } else {
24494               if (_depth>1) {
24495                 unsigned int w = _width, d = _depth; for (unsigned int s = 1; w && d && s<nb_scales; ++s) { w/=2; d/=2; }
24496                 for (w = w?w:1, d = d?d:1; w<=_width && d<=_depth; w*=2, d*=2)
24497                   res.draw_image(res.get_crop(0,0,0,w-1,0,d-1).get_haar(true,1));
24498               } else {
24499                 unsigned int w = _width; for (unsigned int s = 1; w && s<nb_scales; ++s) w/=2;
24500                 for (w = w?w:1; w<=_width; w*=2)
24501                   res.draw_image(res.get_crop(0,0,0,w-1,0,0).get_haar(true,1));
24502               }
24503             }
24504           } else {
24505             if (_height>1) {
24506               if (_depth>1) {
24507                 unsigned int h = _height, d = _depth; for (unsigned int s = 1; h && d && s<nb_scales; ++s) { h/=2; d/=2; }
24508                 for (h = h?h:1, d = d?d:1; h<=_height && d<=_depth; h*=2, d*=2)
24509                   res.draw_image(res.get_crop(0,0,0,0,h-1,d-1).get_haar(true,1));
24510               } else {
24511                 unsigned int h = _height; for (unsigned int s = 1; h && s<nb_scales; ++s) h/=2;
24512                 for (h = h?h:1; h<=_height; h*=2)
24513                   res.draw_image(res.get_crop(0,0,0,0,h-1,0).get_haar(true,1));
24514               }
24515             } else {
24516               if (_depth>1) {
24517                 unsigned int d = _depth; for (unsigned int s = 1; d && s<nb_scales; ++s) d/=2;
24518                 for (d = d?d:1; d<=_depth; d*=2)
24519                   res.draw_image(res.get_crop(0,0,0,0,0,d-1).get_haar(true,1));
24520               } else return *this;
24521             }
24522           }
24523         } else { // Direct transform
24524           res = get_haar(false,1);
24525           if (_width>1) {
24526             if (_height>1) {
24527               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)
24528                 res.draw_image(res.get_crop(0,0,0,w-1,h-1,d-1).haar(false,1));
24529               else for (unsigned int s = 1, w = _width/2, h = _height/2; w && h && s<nb_scales; ++s, w/=2, h/=2)
24530                 res.draw_image(res.get_crop(0,0,0,w-1,h-1,0).haar(false,1));
24531             } else {
24532               if (_depth>1) for (unsigned int s = 1, w = _width/2, d = _depth/2; w && d && s<nb_scales; ++s, w/=2, d/=2)
24533                 res.draw_image(res.get_crop(0,0,0,w-1,0,d-1).haar(false,1));
24534               else for (unsigned int s = 1, w = _width/2; w && s<nb_scales; ++s, w/=2)
24535                 res.draw_image(res.get_crop(0,0,0,w-1,0,0).haar(false,1));
24536             }
24537           } else {
24538             if (_height>1) {
24539               if (_depth>1) for (unsigned int s = 1, h = _height/2, d = _depth/2; h && d && s<nb_scales; ++s, h/=2, d/=2)
24540                 res.draw_image(res.get_crop(0,0,0,0,h-1,d-1).haar(false,1));
24541               else for (unsigned int s = 1, h = _height/2; h && s<nb_scales; ++s, h/=2)
24542                 res.draw_image(res.get_crop(0,0,0,0,h-1,0).haar(false,1));
24543             } else {
24544               if (_depth>1) for (unsigned int s = 1, d = _depth/2; d && s<nb_scales; ++s, d/=2)
24545                 res.draw_image(res.get_crop(0,0,0,0,0,d-1).haar(false,1));
24546               else return *this;
24547             }
24548           }
24549         }
24550         return res;
24551       }
24552       return *this;
24553     }
24554 
24555     //! Compute a 1d Fast Fourier Transform, along a specified axis.
24556     CImgList<Tfloat> get_FFT(const char axis, const bool invert=false) const {
24557       CImgList<Tfloat> res(*this,CImg<Tfloat>());
24558       CImg<Tfloat>::FFT(res[0],res[1],axis,invert);
24559       return res;
24560     }
24561 
24562     //! Compute a n-d Fast-Fourier Transform.
24563     CImgList<Tfloat> get_FFT(const bool invert=false) const {
24564       CImgList<Tfloat> res(*this,CImg<Tfloat>());
24565       CImg<Tfloat>::FFT(res[0],res[1],invert);
24566       return res;
24567     }
24568 
24569     //! Compute a 1d Fast Fourier Transform, along a specified axis.
24570     static void FFT(CImg<T>& real, CImg<T>& imag, const char axis, const bool invert=false) {
24571       if (!real)
24572         throw CImgInstanceException("CImg<%s>::FFT() : Specified real part is empty.",
24573                                     pixel_type());
24574 
24575       if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,0);
24576       if (!real.is_sameXYZC(imag))
24577         throw CImgInstanceException("CImg<%s>::FFT() : Specified real part (%u,%u,%u,%u,%p) and imaginary part (%u,%u,%u,%u,%p) have different dimensions.",
24578                                     pixel_type(),
24579                                     real._width,real._height,real._depth,real._spectrum,real._data,
24580                                     imag._width,imag._height,imag._depth,imag._spectrum,imag._data);
24581 #ifdef cimg_use_fftw3
24582       fftw_complex *data_in;
24583       fftw_plan data_plan;
24584 
24585       switch (cimg::uncase(axis)) {
24586       case 'x' : { // Fourier along X, using FFTW library.
24587         data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width);
24588         data_plan = fftw_plan_dft_1d(real._width,data_in,data_in,invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
24589         cimg_forYZC(real,y,z,c) {
24590           T *ptrr = real.data(0,y,z,c), *ptri = imag.data(0,y,z,c);
24591           double *ptrd = (double*)data_in;
24592           cimg_forX(real,x) { *(ptrd++) = (double)*(ptrr++); *(ptrd++) = (double)*(ptri++); }
24593           fftw_execute(data_plan);
24594           const unsigned int fact = real._width;
24595           if (invert) cimg_forX(real,x) { *(--ptri) = (T)(*(--ptrd)/fact); *(--ptrr) = (T)(*(--ptrd)/fact); }
24596           else cimg_forX(real,x) { *(--ptri) = (T)*(--ptrd); *(--ptrr) = (T)*(--ptrd); }
24597         }
24598       } break;
24599       case 'y' : { // Fourier along Y, using FFTW library.
24600         data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * real._height);
24601         data_plan = fftw_plan_dft_1d(real._height,data_in,data_in,invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
24602         const unsigned int off = real._width;
24603         cimg_forXZC(real,x,z,c) {
24604           T *ptrr = real.data(x,0,z,c), *ptri = imag.data(x,0,z,c);
24605           double *ptrd = (double*)data_in;
24606           cimg_forY(real,y) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; }
24607           fftw_execute(data_plan);
24608           const unsigned int fact = real._height;
24609           if (invert) cimg_forY(real,y) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); }
24610           else cimg_forY(real,y) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); }
24611         }
24612       } break;
24613       case 'z' : { // Fourier along Z, using FFTW library.
24614         data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * real._depth);
24615         data_plan = fftw_plan_dft_1d(real._depth,data_in,data_in,invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
24616         const unsigned int off = real._width*real._height;
24617         cimg_forXYC(real,x,y,c) {
24618           T *ptrr = real.data(x,y,0,c), *ptri = imag.data(x,y,0,c);
24619           double *ptrd = (double*)data_in;
24620           cimg_forZ(real,z) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; }
24621           fftw_execute(data_plan);
24622           const unsigned int fact = real._depth;
24623           if (invert) cimg_forZ(real,z) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); }
24624           else cimg_forZ(real,z) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); }
24625         }
24626       } break;
24627       default : { // Fourier along C, using FFTW library.
24628         data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * real._spectrum);
24629         data_plan = fftw_plan_dft_1d(real._spectrum,data_in,data_in,invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
24630         const unsigned int off = real._width*real._height*real._depth;
24631         cimg_forXYZ(real,x,y,z) {
24632           T *ptrr = real.data(x,y,z,0), *ptri = imag.data(x,y,z,0);
24633           double *ptrd = (double*)data_in;
24634           cimg_forC(real,c) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; }
24635           fftw_execute(data_plan);
24636           const unsigned int fact = real._spectrum;
24637           if (invert) cimg_forC(real,c) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); }
24638           else cimg_forC(real,c) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); }
24639         }
24640       }
24641       }
24642       fftw_destroy_plan(data_plan);
24643       fftw_free(data_in);
24644 #else
24645       switch (cimg::uncase(axis)) {
24646       case 'x' : { // Fourier along X, using built-in functions.
24647         const unsigned int N = real._width, N2 = (N>>1);
24648         if (((N-1)&N) && N!=1)
24649           throw CImgInstanceException("CImgList<%s>::FFT() : Specified real and imaginary parts (%u,%u,%u,%u) have non 2^N dimension along the X-axis.",
24650                                       pixel_type(),
24651                                       real._width,real._height,real._depth,real._spectrum);
24652 
24653         for (unsigned int i = 0, j = 0; i<N2; ++i) {
24654           if (j>i) cimg_forYZC(real,y,z,c) {
24655             cimg::swap(real(i,y,z,c),real(j,y,z,c)); cimg::swap(imag(i,y,z,c),imag(j,y,z,c));
24656             if (j<N2) {
24657               const unsigned int ri = N-1-i, rj = N-1-j;
24658               cimg::swap(real(ri,y,z,c),real(rj,y,z,c)); cimg::swap(imag(ri,y,z,c),imag(rj,y,z,c));
24659             }
24660           }
24661           for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
24662         }
24663         for (unsigned int delta = 2; delta<=N; delta<<=1) {
24664           const unsigned int delta2 = (delta>>1);
24665           for (unsigned int i = 0; i<N; i+=delta) {
24666             float wr = 1, wi = 0;
24667             const float angle = (float)((invert?+1:-1)*2*cimg::PI/delta),
24668                         ca = (float)std::cos(angle),
24669                         sa = (float)std::sin(angle);
24670             for (unsigned int k = 0; k<delta2; ++k) {
24671               const unsigned int j = i + k, nj = j + delta2;
24672               cimg_forYZC(real,y,z,c) {
24673                 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);
24674                 const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
24675                 nir = (T)(ir - tmpr);
24676                 nii = (T)(ii - tmpi);
24677                 ir+=(T)tmpr;
24678                 ii+=(T)tmpi;
24679               }
24680               const float nwr = wr*ca-wi*sa;
24681               wi = wi*ca + wr*sa;
24682               wr = nwr;
24683             }
24684           }
24685         }
24686         if (invert) { real/=N; imag/=N; }
24687       } break;
24688       case 'y' : { // Fourier along Y, using built-in functions.
24689         const unsigned int N = real._height, N2 = (N>>1);
24690         if (((N-1)&N) && N!=1)
24691           throw CImgInstanceException("CImgList<%s>::FFT() : Specified real and imaginary parts (%u,%u,%u,%u) have non 2^N dimension along the Y-axis.",
24692                                       pixel_type(),
24693                                       real._width,real._height,real._depth,real._spectrum);
24694 
24695         for (unsigned int i = 0, j = 0; i<N2; ++i) {
24696           if (j>i) cimg_forXZC(real,x,z,c) {
24697             cimg::swap(real(x,i,z,c),real(x,j,z,c)); cimg::swap(imag(x,i,z,c),imag(x,j,z,c));
24698             if (j<N2) {
24699               const unsigned int ri = N - 1 - i, rj = N - 1 - j;
24700               cimg::swap(real(x,ri,z,c),real(x,rj,z,c)); cimg::swap(imag(x,ri,z,c),imag(x,rj,z,c));
24701             }
24702           }
24703           for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
24704         }
24705         for (unsigned int delta = 2; delta<=N; delta<<=1) {
24706           const unsigned int delta2 = (delta>>1);
24707           for (unsigned int i = 0; i<N; i+=delta) {
24708             float wr = 1, wi = 0;
24709             const float angle = (float)((invert?+1:-1)*2*cimg::PI/delta),
24710                         ca = (float)std::cos(angle), sa = (float)std::sin(angle);
24711             for (unsigned int k = 0; k<delta2; ++k) {
24712               const unsigned int j = i + k, nj = j + delta2;
24713               cimg_forXZC(real,x,z,c) {
24714                 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);
24715                 const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
24716                 nir = (T)(ir - tmpr);
24717                 nii = (T)(ii - tmpi);
24718                 ir+=(T)tmpr;
24719                 ii+=(T)tmpi;
24720               }
24721               const float nwr = wr*ca-wi*sa;
24722               wi = wi*ca + wr*sa;
24723               wr = nwr;
24724             }
24725           }
24726         }
24727         if (invert) { real/=N; imag/=N; }
24728       } break;
24729       case 'z' : { // Fourier along Z, using built-in functions.
24730         const unsigned int N = real._depth, N2 = (N>>1);
24731         if (((N-1)&N) && N!=1)
24732           throw CImgInstanceException("CImgList<%s>::FFT() : Specified real and imaginary parts (%u,%u,%u,%u) have non 2^N dimension along the Z-axis.",
24733                                       pixel_type(),
24734                                       real._width,real._height,real._depth,real._spectrum);
24735 
24736         for (unsigned int i = 0, j = 0; i<N2; ++i) {
24737           if (j>i) cimg_forXYC(real,x,y,c) {
24738             cimg::swap(real(x,y,i,c),real(x,y,j,c)); cimg::swap(imag(x,y,i,c),imag(x,y,j,c));
24739             if (j<N2) {
24740               const unsigned int ri = N - 1 - i, rj = N - 1 - j;
24741               cimg::swap(real(x,y,ri,c),real(x,y,rj,c)); cimg::swap(imag(x,y,ri,c),imag(x,y,rj,c));
24742             }
24743           }
24744           for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
24745         }
24746         for (unsigned int delta = 2; delta<=N; delta<<=1) {
24747           const unsigned int delta2 = (delta>>1);
24748           for (unsigned int i = 0; i<N; i+=delta) {
24749             float wr = 1, wi = 0;
24750             const float angle = (float)((invert?+1:-1)*2*cimg::PI/delta),
24751                         ca = (float)std::cos(angle), sa = (float)std::sin(angle);
24752             for (unsigned int k = 0; k<delta2; ++k) {
24753               const unsigned int j = i + k, nj = j + delta2;
24754               cimg_forXYC(real,x,y,c) {
24755                 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);
24756                 const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
24757                 nir = (T)(ir - tmpr);
24758                 nii = (T)(ii - tmpi);
24759                 ir+=(T)tmpr;
24760                 ii+=(T)tmpi;
24761               }
24762               const float nwr = wr*ca-wi*sa;
24763               wi = wi*ca + wr*sa;
24764               wr = nwr;
24765             }
24766           }
24767         }
24768         if (invert) { real/=N; imag/=N; }
24769       } break;
24770       default :
24771         throw CImgArgumentException("CImgList<%s>::FFT() : Invalid specified axis '%c' for real and imaginary parts (%u,%u,%u,%u) "
24772                                     "(should be { x | y | z }).",
24773                                     pixel_type(),axis,
24774                                     real._width,real._height,real._depth,real._spectrum);
24775       }
24776 #endif
24777     }
24778 
24779     //! Compute a n-d Fast Fourier Transform.
24780     static void FFT(CImg<T>& real, CImg<T>& imag, const bool invert=false) {
24781       if (!real)
24782         throw CImgInstanceException("CImgList<%s>::FFT() : Empty specified real part.",
24783                                     pixel_type());
24784 
24785       if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,0);
24786       if (!real.is_sameXYZC(imag))
24787         throw CImgInstanceException("CImgList<%s>::FFT() : Specified real part (%u,%u,%u,%u,%p) and imaginary part (%u,%u,%u,%u,%p) have different dimensions.",
24788                                     pixel_type(),
24789                                     real._width,real._height,real._depth,real._spectrum,real._data,
24790                                     imag._width,imag._height,imag._depth,imag._spectrum,imag._data);
24791 
24792 #ifdef cimg_use_fftw3
24793       fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width*real._height*real._depth);
24794       fftw_plan data_plan;
24795       const unsigned int w = real._width, wh = w*real._height, whd = wh*real._depth;
24796       data_plan = fftw_plan_dft_3d(real._width,real._height,real._depth,data_in,data_in,invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
24797       cimg_forC(real,c) {
24798         T *ptrr = real.data(0,0,0,c), *ptri = imag.data(0,0,0,c);
24799         double *ptrd = (double*)data_in;
24800         for (unsigned int x = 0; x<real._width; ++x, ptrr-=wh-1, ptri-=wh-1)
24801           for (unsigned int y = 0; y<real._height; ++y, ptrr-=whd-w, ptri-=whd-w)
24802             for (unsigned int z = 0; z<real._depth; ++z, ptrr+=wh, ptri+=wh) {
24803               *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri;
24804             }
24805         fftw_execute(data_plan);
24806         ptrd = (double*)data_in;
24807         ptrr = real.data(0,0,0,c);
24808         ptri = imag.data(0,0,0,c);
24809         if (!invert) for (unsigned int x = 0; x<real._width; ++x, ptrr-=wh-1, ptri-=wh-1)
24810           for (unsigned int y = 0; y<real._height; ++y, ptrr-=whd-w, ptri-=whd-w)
24811             for (unsigned int z = 0; z<real._depth; ++z, ptrr+=wh, ptri+=wh) {
24812               *ptrr = (T)*(ptrd++); *ptri = (T)*(ptrd++);
24813             }
24814         else for (unsigned int x = 0; x<real._width; ++x, ptrr-=wh-1, ptri-=wh-1)
24815           for (unsigned int y = 0; y<real._height; ++y, ptrr-=whd-w, ptri-=whd-w)
24816             for (unsigned int z = 0; z<real._depth; ++z, ptrr+=wh, ptri+=wh) {
24817               *ptrr = (T)(*(ptrd++)/whd); *ptri = (T)(*(ptrd++)/whd);
24818             }
24819       }
24820       fftw_destroy_plan(data_plan);
24821       fftw_free(data_in);
24822 #else
24823       if (real._depth>1)  FFT(real,imag,'z',invert);
24824       if (real._height>1) FFT(real,imag,'y',invert);
24825       if (real._width>1)  FFT(real,imag,'x',invert);
24826 #endif
24827     }
24828 
24829     //@}
24830     //-------------------------------------
24831     //
24832     //! \name 3d Objects Management
24833     //@{
24834     //-------------------------------------
24835 
24836     //! Shift a 3d object.
24837     CImg<T>& shift_object3d(const float tx, const float ty=0, const float tz=0) {
24838       if (_height!=3 || _depth>1 || _spectrum>1)
24839         throw CImgInstanceException(_cimg_instance
24840                                     "shift_object3d() : Instance is not a set of 3d vertices.",
24841                                     cimg_instance);
24842 
24843       get_shared_line(0)+=tx; get_shared_line(1)+=ty; get_shared_line(2)+=tz;
24844       return *this;
24845     }
24846 
24847     CImg<Tfloat> get_shift_object3d(const float tx, const float ty=0, const float tz=0) const {
24848       return CImg<Tfloat>(*this,false).shift_object3d(tx,ty,tz);
24849     }
24850 
24851     //! Shift a 3d object so that it becomes centered.
24852     CImg<T>& shift_object3d() {
24853       if (_height!=3 || _depth>1 || _spectrum>1)
24854         throw CImgInstanceException(_cimg_instance
24855                                     "shift_object3d() : Instance is not a set of 3d vertices.",
24856                                     cimg_instance);
24857 
24858       CImg<T> xcoords = get_shared_line(0), ycoords = get_shared_line(1), zcoords = get_shared_line(2);
24859       float xm, xM = (float)xcoords.max_min(xm), ym, yM = (float)ycoords.max_min(ym), zm, zM = (float)zcoords.max_min(zm);
24860       xcoords-=(xm + xM)/2; ycoords-=(ym + yM)/2; zcoords-=(zm + zM)/2;
24861       return *this;
24862     }
24863 
24864     CImg<Tfloat> get_shift_object3d() const {
24865       return CImg<Tfloat>(*this,false).shift_object3d();
24866     }
24867 
24868     //! Resize a 3d object.
24869     CImg<T>& resize_object3d(const float sx, const float sy=-100, const float sz=-100) {
24870       if (_height!=3 || _depth>1 || _spectrum>1)
24871         throw CImgInstanceException(_cimg_instance
24872                                     "resize_object3d() : Instance is not a set of 3d vertices.",
24873                                     cimg_instance);
24874 
24875       CImg<T> xcoords = get_shared_line(0), ycoords = get_shared_line(1), zcoords = get_shared_line(2);
24876       float xm, xM = (float)xcoords.max_min(xm), ym, yM = (float)ycoords.max_min(ym), zm, zM = (float)zcoords.max_min(zm);
24877       if (xm<xM) { if (sx>0) xcoords*=sx/(xM-xm); else xcoords*=-sx/100; }
24878       if (ym<yM) { if (sy>0) ycoords*=sy/(yM-ym); else ycoords*=-sy/100; }
24879       if (zm<zM) { if (sz>0) zcoords*=sz/(zM-zm); else zcoords*=-sz/100; }
24880       return *this;
24881     }
24882 
24883     CImg<Tfloat> get_resize_object3d(const float sx, const float sy=-100, const float sz=-100) const {
24884       return CImg<Tfloat>(*this,false).resize_object3d(sx,sy,sz);
24885     }
24886 
24887     //! Resize a 3d object so that its max dimension if one.
24888     CImg<T> resize_object3d() {
24889       if (_height!=3 || _depth>1 || _spectrum>1)
24890         throw CImgInstanceException(_cimg_instance
24891                                     "resize_object3d() : Instance is not a set of 3d vertices.",
24892                                     cimg_instance);
24893 
24894       CImg<T> xcoords = get_shared_line(0), ycoords = get_shared_line(1), zcoords = get_shared_line(2);
24895       float xm, xM = (float)xcoords.max_min(xm), ym, yM = (float)ycoords.max_min(ym), zm, zM = (float)zcoords.max_min(zm);
24896       const float dx = xM - xm, dy = yM - ym, dz = zM - zm, dmax = cimg::max(dx,dy,dz);
24897       if (dmax>0) { xcoords/=dmax; ycoords/=dmax; zcoords/=dmax; }
24898       return *this;
24899     }
24900 
24901     CImg<Tfloat> get_resize_object3d() const {
24902       return CImg<Tfloat>(*this,false).resize_object3d();
24903     }
24904 
24905     //! Append a 3d object to another one.
24906     template<typename tf, typename tp, typename tff>
24907     CImg<T>& append_object3d(CImgList<tf>& primitives, const CImg<tp>& obj_vertices, const CImgList<tff>& obj_primitives) {
24908       if (!obj_vertices || !obj_primitives) return *this;
24909       if (obj_vertices._height!=3 || obj_vertices._depth>1 || obj_vertices._spectrum>1)
24910         throw CImgInstanceException(_cimg_instance
24911                                     "append_object3d() : Specified vertice image (%u,%u,%u,%u,%p) is not a set of 3d vertices.",
24912                                     cimg_instance,
24913                                     obj_vertices._width,obj_vertices._height,obj_vertices._depth,obj_vertices._spectrum,obj_vertices._data);
24914 
24915       if (is_empty()) { primitives.assign(obj_primitives); return assign(obj_vertices); }
24916       if (_height!=3 || _depth>1 || _spectrum>1)
24917         throw CImgInstanceException(_cimg_instance
24918                                     "append_object3d() : Instance is not a set of 3d vertices.",
24919                                     cimg_instance);
24920 
24921       const unsigned int P = _width;
24922       append(obj_vertices,'x');
24923       const unsigned int N = primitives._width;
24924       primitives.insert(obj_primitives);
24925       for (unsigned int i = N; i<primitives._width; ++i) {
24926         CImg<tf> &p = primitives[i];
24927         switch (p.size()) {
24928         case 1 : p[0]+=P; break; // Point.
24929         case 5 : p[0]+=P; p[1]+=P; break; // Sphere.
24930         case 2 : case 6 : p[0]+=P; p[1]+=P; break; // Segment.
24931         case 3 : case 9 : p[0]+=P; p[1]+=P; p[2]+=P; break; // Triangle.
24932         case 4 : case 12 : p[0]+=P; p[1]+=P; p[2]+=P; p[3]+=P; break; // Rectangle.
24933         }
24934       }
24935       return *this;
24936     }
24937 
24938     //! Texturize primitives of a 3d object.
24939     template<typename tp, typename tc, typename tt, typename tx>
24940     const CImg<T>& texturize_object3d(CImgList<tp>& primitives, CImgList<tc>& colors,
24941                                       const CImg<tt>& texture, const CImg<tx>& coords=CImg<tx>::empty()) const {
24942       if (is_empty()) return *this;
24943       if (_height!=3)
24944         throw CImgInstanceException(_cimg_instance
24945                                     "texturize_object3d() : image instance is not a set of 3d points.",
24946                                     cimg_instance);
24947       if (coords && (coords._width!=_width || coords._height!=2))
24948         throw CImgArgumentException(_cimg_instance
24949                                     "texturize_object3d() : Invalid specified texture coordinates (%u,%u,%u,%u,%p).",
24950                                     cimg_instance,
24951                                     coords._width,coords._height,coords._depth,coords._spectrum,coords._data);
24952       CImg<unsigned int> _coords;
24953       if (!coords) { // If no texture coordinates specified, do a default XY-projection.
24954         _coords.assign(_width,2);
24955         float
24956           xmin, xmax = (float)get_shared_line(0).max_min(xmin),
24957           ymin, ymax = (float)get_shared_line(1).max_min(ymin),
24958           dx = xmax>xmin?xmax-xmin:1,
24959           dy = ymax>ymin?ymax-ymin:1;
24960         cimg_forX(*this,p) {
24961           _coords(p,0) = (unsigned int)(((*this)(p,0)-xmin)*(texture._width-1)/dx);
24962           _coords(p,1) = (unsigned int)(((*this)(p,1)-ymin)*(texture._height-1)/dy);
24963         }
24964       } else _coords = coords;
24965 
24966       int texture_ind = -1;
24967       cimglist_for(primitives,l) {
24968         CImg<tp> &p = primitives[l];
24969         const unsigned int siz = p.size();
24970         switch (siz) {
24971         case 1 : { // Point.
24972           const unsigned int
24973             i0 = (unsigned int)p[0],
24974             x0 = (unsigned int)_coords(i0,0), y0 = (unsigned int)_coords(i0,1);
24975           texture.get_vector_at(x0,y0).move_to(colors[l]);
24976         } break;
24977         case 2 : case 6 : { // Line.
24978           const unsigned int
24979             i0 = (unsigned int)p[0], i1 = (unsigned int)p[1],
24980             x0 = (unsigned int)_coords(i0,0), y0 = (unsigned int)_coords(i0,1),
24981             x1 = (unsigned int)_coords(i1,0), y1 = (unsigned int)_coords(i1,1);
24982           if (texture_ind<0) colors[texture_ind=l] = texture; else colors[l].assign(colors[texture_ind],true);
24983           CImg<tp>::vector(i0,i1,x0,y0,x1,y1).move_to(p);
24984         } break;
24985         case 3 : case 9 : { // Triangle.
24986           const unsigned int
24987             i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2],
24988             x0 = (unsigned int)_coords(i0,0), y0 = (unsigned int)_coords(i0,1),
24989             x1 = (unsigned int)_coords(i1,0), y1 = (unsigned int)_coords(i1,1),
24990             x2 = (unsigned int)_coords(i2,0), y2 = (unsigned int)_coords(i2,1);
24991           if (texture_ind<0) colors[texture_ind=l] = texture; else colors[l].assign(colors[texture_ind],true);
24992           CImg<tp>::vector(i0,i1,i2,x0,y0,x1,y1,x2,y2).move_to(p);
24993         } break;
24994         case 4 : case 12 : { // Quadrangle.
24995           const unsigned int
24996             i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2], i3 = (unsigned int)p[3],
24997             x0 = (unsigned int)_coords(i0,0), y0 = (unsigned int)_coords(i0,1),
24998             x1 = (unsigned int)_coords(i1,0), y1 = (unsigned int)_coords(i1,1),
24999             x2 = (unsigned int)_coords(i2,0), y2 = (unsigned int)_coords(i2,1),
25000             x3 = (unsigned int)_coords(i3,0), y3 = (unsigned int)_coords(i3,1);
25001           if (texture_ind<0) colors[texture_ind=l] = texture; else colors[l].assign(colors[texture_ind],true);
25002           CImg<tp>::vector(i0,i1,i2,i3,x0,y0,x1,y1,x2,y2,x3,y3).move_to(p);
25003         } break;
25004         }
25005       }
25006       return *this;
25007     }
25008 
25009     //! Create and return a 3d elevation of the image instance.
25010     /**
25011        \param[out] primitives The returned list of the 3d object primitives
25012                               (template type \e tf should be at least \e unsigned \e int).
25013        \param[out] colors The returned list of the 3d object colors.
25014        \param elevation The input elevation map.
25015        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
25016        \par Sample code :
25017        \code
25018        const CImg<float> img("reference.jpg");
25019        CImgList<unsigned int> faces3d;
25020        CImgList<unsigned char> colors3d;
25021        const CImg<float> points3d = img.get_elevation3d(faces3d,colors,img.get_norm()*0.2);
25022        CImg<unsigned char>().display_object3d("Elevation3d",points3d,faces3d,colors3d);
25023        \endcode
25024        \image html ref_elevation3d.jpg
25025     **/
25026     template<typename tf, typename tc, typename te>
25027     CImg<floatT> get_elevation3d(CImgList<tf>& primitives, CImgList<tc>& colors, const CImg<te>& elevation) const {
25028       if (!is_sameXY(elevation) || elevation._depth>1 || elevation._spectrum>1)
25029         throw CImgArgumentException(_cimg_instance
25030                                     "get_elevation3d() : Instance and specified elevation (%u,%u,%u,%u,%p) "
25031                                     "have incompatible dimensions.",
25032                                     cimg_instance,
25033                                     elevation._width,elevation._height,elevation._depth,elevation._spectrum,elevation._data);
25034       if (is_empty()) return *this;
25035       float m, M = (float)max_min(m);
25036       if (M==m) ++M;
25037       colors.assign();
25038       const unsigned int size_x1 = _width - 1, size_y1 = _height - 1;
25039       for (unsigned int y = 0; y<size_y1; ++y)
25040         for (unsigned int x = 0; x<size_x1; ++x) {
25041           const unsigned char
25042             r = (unsigned char)(((*this)(x,y,0) - m)*255/(M-m)),
25043             g = _spectrum>1?(unsigned char)(((*this)(x,y,1) - m)*255/(M-m)):r,
25044             b = _spectrum>2?(unsigned char)(((*this)(x,y,2) - m)*255/(M-m)):(_spectrum>1?0:r);
25045           CImg<tc>::vector((tc)r,(tc)g,(tc)b).move_to(colors);
25046         }
25047       const typename CImg<te>::_functor2d_int func(elevation);
25048       return elevation3d(primitives,func,0,0,_width-1.0f,_height-1.0f,_width,_height);
25049     }
25050 
25051     //! Create and return the 3d projection planes of the image instance.
25052     template<typename tf, typename tc>
25053     CImg<floatT> get_projections3d(CImgList<tf>& primitives, CImgList<tc>& colors,
25054                                    const unsigned int x0, const unsigned int y0, const unsigned int z0,
25055                                    const bool normalize_colors=false) const {
25056       float m = 0, M = 0, delta = 1;
25057       if (normalize_colors) { m = (float)min_max(M); delta = 255/(m==M?1:M-m); }
25058       const unsigned int
25059         _x0 = (x0>=_width)?_width - 1:x0,
25060         _y0 = (y0>=_height)?_height - 1:y0,
25061         _z0 = (z0>=_depth)?_depth - 1:z0;
25062       CImg<tc> img_xy, img_xz, img_yz;
25063       if (normalize_colors) {
25064         ((get_crop(0,0,_z0,0,_width-1,_height-1,_z0,_spectrum-1)-=m)*=delta).move_to(img_xy);
25065         ((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);
25066         ((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);
25067       } else {
25068         get_crop(0,0,_z0,0,_width-1,_height-1,_z0,_spectrum-1).move_to(img_xy);
25069         get_crop(0,_y0,0,0,_width-1,_y0,_depth-1,_spectrum-1).resize(_width,_depth,1,-100,-1).move_to(img_xz);
25070         get_crop(_x0,0,0,0,_x0,_height-1,_depth-1,_spectrum-1).resize(_height,_depth,1,-100,-1).move_to(img_yz);
25071       }
25072       CImg<floatT> points(12,3,1,1,
25073                           0,_width-1,_width-1,0,   0,_width-1,_width-1,0, _x0,_x0,_x0,_x0,
25074                           0,0,_height-1,_height-1, _y0,_y0,_y0,_y0,       0,_height-1,_height-1,0,
25075                           _z0,_z0,_z0,_z0,         0,0,_depth-1,_depth-1, 0,0,_depth-1,_depth-1);
25076       primitives.assign();
25077       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);
25078       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);
25079       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);
25080       colors.assign();
25081       img_xy.move_to(colors);
25082       img_xz.move_to(colors);
25083       img_yz.move_to(colors);
25084       return points;
25085     }
25086 
25087     //! Create and return a isoline of the image instance as a 3d object.
25088     /**
25089        \param[out] primitives The returned list of the 3d object primitives
25090                               (template type \e tf should be at least \e unsigned \e int).
25091        \param isovalue The returned list of the 3d object colors.
25092        \param size_x The number of subdivisions along the X-axis.
25093        \param size_y The number of subdisivions along the Y-axis.
25094        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
25095        \par Sample code :
25096        \code
25097        const CImg<float> img("reference.jpg");
25098        CImgList<unsigned int> faces3d;
25099        const CImg<float> points3d = img.get_isoline3d(faces3d,100);
25100        CImg<unsigned char>().display_object3d("Isoline3d",points3d,faces3d,colors3d);
25101        \endcode
25102        \image html ref_isoline3d.jpg
25103     **/
25104     template<typename tf>
25105     CImg<floatT> get_isoline3d(CImgList<tf>& primitives, const float isovalue,
25106                                const int size_x=-100, const int size_y=-100) const {
25107       if (_spectrum>1)
25108         throw CImgInstanceException(_cimg_instance
25109                                     "get_isoline3d() : Instance is not a scalar image.",
25110                                     cimg_instance);
25111       if (_depth>1)
25112         throw CImgInstanceException(_cimg_instance
25113                                     "get_isoline3d() : Instance is not a 2d image.",
25114                                     cimg_instance);
25115       primitives.assign();
25116       if (is_empty()) return *this;
25117       CImg<floatT> vertices;
25118       if ((size_x==-100 && size_y==-100) || (size_x==width() && size_y==height())) {
25119         const _functor2d_int func(*this);
25120         vertices = isoline3d(primitives,func,isovalue,0,0,width()-1.0f,height()-1.0f,width(),height());
25121       } else {
25122         const _functor2d_float func(*this);
25123         vertices = isoline3d(primitives,func,isovalue,0,0,width()-1.0f,height()-1.0f,size_x,size_y);
25124       }
25125       return vertices;
25126     }
25127 
25128     //! Create and return a isosurface of the image instance as a 3d object.
25129     /**
25130        \param[out] primitives The returned list of the 3d object primitives
25131                               (template type \e tf should be at least \e unsigned \e int).
25132        \param isovalue The returned list of the 3d object colors.
25133        \param size_x The number of subdivisions along the X-axis.
25134        \param size_y The number of subdisivions along the Y-axis.
25135        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
25136        \par Sample code :
25137        \code
25138        const CImg<float> img = CImg<unsigned char>("reference.jpg").resize(-100,-100,20);
25139        CImgList<unsigned int> faces3d;
25140        const CImg<float> points3d = img.get_isosurface3d(faces3d,100);
25141        CImg<unsigned char>().display_object3d("Isosurface3d",points3d,faces3d,colors3d);
25142        \endcode
25143        \image html ref_isosurface3d.jpg
25144     **/
25145     template<typename tf>
25146     CImg<floatT> get_isosurface3d(CImgList<tf>& primitives, const float isovalue,
25147                                   const int size_x=-100, const int size_y=-100, const int size_z=-100) const {
25148       if (_spectrum>1)
25149         throw CImgInstanceException(_cimg_instance
25150                                     "get_isosurface3d() : Instance is not a scalar image.",
25151                                     cimg_instance);
25152       primitives.assign();
25153       if (is_empty()) return *this;
25154       CImg<floatT> vertices;
25155       if ((size_x==-100 && size_y==-100 && size_z==-100) || (size_x==width() && size_y==height() && size_z==depth())) {
25156         const _functor3d_int func(*this);
25157         vertices = isosurface3d(primitives,func,isovalue,0,0,0,width()-1.0f,height()-1.0f,depth()-1.0f,width(),height(),depth());
25158       } else {
25159         const _functor3d_float func(*this);
25160         vertices = isosurface3d(primitives,func,isovalue,0,0,0,width()-1.0f,height()-1.0f,depth()-1.0f,size_x,size_y,size_z);
25161       }
25162       return vertices;
25163     }
25164 
25165     //! Get elevation3d of a function.
25166     template<typename tf, typename tfunc>
25167     static CImg<floatT> elevation3d(CImgList<tf>& primitives, const tfunc& func,
25168                                     const float x0, const float y0, const float x1, const float y1,
25169                                     const int size_x=256, const int size_y=256) {
25170       const float
25171         nx0 = x0<x1?x0:x1, ny0 = y0<y1?y0:y1,
25172         nx1 = x0<x1?x1:x0, ny1 = y0<y1?y1:y0;
25173       const unsigned int
25174         _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,
25175         _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;
25176       if (nsize_x<2 || nsize_y<2)
25177         throw CImgArgumentException("CImg<%s>::elevation3d() : Invalid specified size (%d,%d).",
25178                                     pixel_type(),
25179                                     nsize_x,nsize_y);
25180 
25181       CImg<floatT> vertices(nsize_x*nsize_y,3);
25182       floatT *ptr_x = vertices.data(0,0), *ptr_y = vertices.data(0,1), *ptr_z = vertices.data(0,2);
25183       for (unsigned int y = 0; y<nsize_y; ++y) {
25184         const float Y = ny0 + y*(ny1-ny0)/nsize_y1;
25185         for (unsigned int x = 0; x<nsize_x; ++x) {
25186           const float X = nx0 + x*(nx1-nx0)/nsize_x1;
25187           *(ptr_x++) = (float)x;
25188           *(ptr_y++) = (float)y;
25189           *(ptr_z++) = (float)func(X,Y);
25190         }
25191       }
25192       primitives.assign(nsize_x1*nsize_y1,1,4);
25193       for (unsigned int p = 0, y = 0; y<nsize_y1; ++y) {
25194         const unsigned int yw = y*nsize_x;
25195         for (unsigned int x = 0; x<nsize_x1; ++x) {
25196           const unsigned int xpyw = x + yw, xpyww = xpyw + nsize_x;
25197           primitives[p++].fill(xpyw,xpyww,xpyww+1,xpyw+1);
25198         }
25199       }
25200       return vertices;
25201     }
25202 
25203     template<typename tf>
25204     static CImg<floatT> elevation3d(CImgList<tf>& primitives, const char *const expression,
25205                                     const float x0, const float y0, const float x1, const float y1,
25206                                     const int sizex=256, const int sizey=256) {
25207       const _functor2d_expr func(expression);
25208       return elevation3d(primitives,func,x0,y0,x1,y1,sizex,sizey);
25209     }
25210 
25211     //! Get isoline as a 3d object.
25212     template<typename tf, typename tfunc>
25213     static CImg<floatT> isoline3d(CImgList<tf>& primitives, const tfunc& func, const float isovalue,
25214                                   const float x0, const float y0, const float x1, const float y1,
25215                                   const int sizex=256, const int sizey=256) {
25216       static const unsigned int edges[16] = { 0x0, 0x9, 0x3, 0xa, 0x6, 0xf, 0x5, 0xc, 0xc, 0x5, 0xf, 0x6, 0xa, 0x3, 0x9, 0x0 };
25217       static const int segments[16][4] = { { -1,-1,-1,-1 }, { 0,3,-1,-1 }, { 0,1,-1,-1 }, { 1,3,-1,-1 },
25218                                            { 1,2,-1,-1 },   { 0,1,2,3 },   { 0,2,-1,-1 }, { 2,3,-1,-1 },
25219                                            { 2,3,-1,-1 },   { 0,2,-1,-1},  { 0,3,1,2 },   { 1,2,-1,-1 },
25220                                            { 1,3,-1,-1 },   { 0,1,-1,-1},  { 0,3,-1,-1},  { -1,-1,-1,-1 } };
25221       const unsigned int
25222         _nx = (unsigned int)(sizex>=0?sizex:cimg::round((x1-x0)*-sizex/100 + 1)),
25223         _ny = (unsigned int)(sizey>=0?sizey:cimg::round((y1-y0)*-sizey/100 + 1)),
25224         nx = _nx?_nx:1,
25225         ny = _ny?_ny:1,
25226         nxm1 = nx - 1,
25227         nym1 = ny - 1;
25228       primitives.assign();
25229       if (!nxm1 || !nym1) return CImg<floatT>();
25230       const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1;
25231       CImgList<floatT> vertices;
25232       CImg<intT> indices1(nx,1,1,2,-1), indices2(nx,1,1,2);
25233       CImg<floatT> values1(nx), values2(nx);
25234       float X = x0, Y = y0, nX = X + dx, nY = Y + dy;
25235 
25236       // Fill first line with values
25237       cimg_forX(values1,x) { values1(x) = (float)func(X,Y); X+=dx; }
25238 
25239       // Run the marching squares algorithm
25240       for (unsigned int yi = 0, nyi = 1; yi<nym1; ++yi, ++nyi, Y=nY, nY+=dy) {
25241         X = x0; nX = X + dx;
25242         indices2.fill(-1);
25243         for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X=nX, nX+=dx) {
25244 
25245           // Determine square configuration
25246           const float
25247             val0 = values1(xi),
25248             val1 = values1(nxi),
25249             val2 = values2(nxi) = (float)func(nX,nY),
25250             val3 = values2(xi) = (float)func(X,nY);
25251           const unsigned int
25252             configuration = (val0<isovalue?1:0)  | (val1<isovalue?2:0)  | (val2<isovalue?4:0)  | (val3<isovalue?8:0),
25253             edge = edges[configuration];
25254 
25255           // Compute intersection vertices
25256           if (edge) {
25257             if ((edge&1) && indices1(xi,0)<0) {
25258               const float Xi = X + (isovalue-val0)*dx/(val1-val0);
25259               indices1(xi,0) = vertices._width;
25260               CImg<floatT>::vector(Xi,Y,0).move_to(vertices);
25261             }
25262             if ((edge&2) && indices1(nxi,1)<0) {
25263               const float Yi = Y + (isovalue-val1)*dy/(val2-val1);
25264               indices1(nxi,1) = vertices._width;
25265               CImg<floatT>::vector(nX,Yi,0).move_to(vertices);
25266             }
25267             if ((edge&4) && indices2(xi,0)<0) {
25268               const float Xi = X + (isovalue-val3)*dx/(val2-val3);
25269               indices2(xi,0) = vertices._width;
25270               CImg<floatT>::vector(Xi,nY,0).move_to(vertices);
25271             }
25272             if ((edge&8) && indices1(xi,1)<0) {
25273               const float Yi = Y + (isovalue-val0)*dy/(val3-val0);
25274               indices1(xi,1) = vertices._width;
25275               CImg<floatT>::vector(X,Yi,0).move_to(vertices);
25276             }
25277 
25278             // Create segments
25279             for (const int *segment = segments[configuration]; *segment!=-1; ) {
25280               const unsigned int p0 = *(segment++), p1 = *(segment++);
25281               const tf
25282                 i0 = (tf)(_isoline3d_indice(p0,indices1,indices2,xi,nxi)),
25283                 i1 = (tf)(_isoline3d_indice(p1,indices1,indices2,xi,nxi));
25284               CImg<tf>::vector(i0,i1).move_to(primitives);
25285             }
25286           }
25287         }
25288         values1.swap(values2);
25289         indices1.swap(indices2);
25290       }
25291       return vertices>'x';
25292     }
25293 
25294     template<typename tf>
25295     static CImg<floatT> isoline3d(CImgList<tf>& primitives, const char *const expression, const float isovalue,
25296                                   const float x0, const float y0, const float x1, const float y1,
25297                                   const int sizex=256, const int sizey=256) {
25298       const _functor2d_expr func(expression);
25299       return isoline3d(primitives,func,isovalue,x0,y0,x1,y1,sizex,sizey);
25300     }
25301 
25302     template<typename t>
25303     static int _isoline3d_indice(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& indices2,
25304                                  const unsigned int x, const unsigned int nx) {
25305       switch (edge) {
25306       case 0 : return (int)indices1(x,0);
25307       case 1 : return (int)indices1(nx,1);
25308       case 2 : return (int)indices2(x,0);
25309       case 3 : return (int)indices1(x,1);
25310       }
25311       return 0;
25312     }
25313 
25314     //! Get isosurface as a 3d object.
25315     template<typename tf, typename tfunc>
25316     static CImg<floatT> isosurface3d(CImgList<tf>& primitives, const tfunc& func, const float isovalue,
25317                                      const float x0, const float y0, const float z0,
25318                                      const float x1, const float y1, const float z1,
25319                                      const int size_x=32, const int size_y=32, const int size_z=32) {
25320       static unsigned int edges[256] = {
25321         0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
25322         0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
25323         0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
25324         0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
25325         0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
25326         0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
25327         0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
25328         0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
25329         0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
25330         0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
25331         0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
25332         0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460,
25333         0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0,
25334         0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230,
25335         0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190,
25336         0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000 };
25337 
25338       static int triangles[256][16] = {
25339         { -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 },
25340         { 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 },
25341         { 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 },
25342         { 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 },
25343         { 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 },
25344         { 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 },
25345         { 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 },
25346         { 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 },
25347         { 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 },
25348         { 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 },
25349         { 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 },
25350         { 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 },
25351         { 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 },
25352         { 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 },
25353         { 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 },
25354         { 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 },
25355         { 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 },
25356         { 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 },
25357         { 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 },
25358         { 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 },
25359         { 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 },
25360         { 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 },
25361         { 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 },
25362         { 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 },
25363         { 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 },
25364         { 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 },
25365         { 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 },
25366         { 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 },
25367         { 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 },
25368         { 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 },
25369         { 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 },
25370         { 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 },
25371         { 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 },
25372         { 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 },
25373         { 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 },
25374         { 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 },
25375         { 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 },
25376         { 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 },
25377         { 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 },
25378         { 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 },
25379         { 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 },
25380         { 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 },
25381         { 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 },
25382         { 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 },
25383         { 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 },
25384         { 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 },
25385         { 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 },
25386         { 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 },
25387         { 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 },
25388         { 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 },
25389         { 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 },
25390         { 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 },
25391         { 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 },
25392         { 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 },
25393         { 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 },
25394         { 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 },
25395         { 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 },
25396         { 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 },
25397         { 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 },
25398         { 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 },
25399         { 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 },
25400         { 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 },
25401         { 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 },
25402         { 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 },
25403         { 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 },
25404         { 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 },
25405         { 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 },
25406         { 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 },
25407         { 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 },
25408         { 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 },
25409         { 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 },
25410         { 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 },
25411         { 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 },
25412         { 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 },
25413         { 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 },
25414         { 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 },
25415         { 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 },
25416         { 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 },
25417         { 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 },
25418         { 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 },
25419         { 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 },
25420         { 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 },
25421         { 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 },
25422         { 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 },
25423         { 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 },
25424         { 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 },
25425         { 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 },
25426         { 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 },
25427         { 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 },
25428         { 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 },
25429         { 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 },
25430         { 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 },
25431         { 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 },
25432         { 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 },
25433         { 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 },
25434         { 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 },
25435         { 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 },
25436         { 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 },
25437         { 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 },
25438         { 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 },
25439         { 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 },
25440         { 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 },
25441         { 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 },
25442         { 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 },
25443         { 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 },
25444         { 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 },
25445         { 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 },
25446         { 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 },
25447         { 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 },
25448         { 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 },
25449         { 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 },
25450         { 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 },
25451         { 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 },
25452         { 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 },
25453         { 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 },
25454         { 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 },
25455         { 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 },
25456         { 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 },
25457         { 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 },
25458         { 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 },
25459         { 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 },
25460         { 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 },
25461         { 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 },
25462         { 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 },
25463         { 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 },
25464         { 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 },
25465         { 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 },
25466         { 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 }
25467       };
25468 
25469       const unsigned int
25470         _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)),
25471         _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)),
25472         _nz = (unsigned int)(size_z>=0?size_y:cimg::round((z1-z0)*-size_z/100 + 1)),
25473         nx = _nx?_nx:1,
25474         ny = _ny?_ny:1,
25475         nz = _nz?_nz:1,
25476         nxm1 = nx - 1,
25477         nym1 = ny - 1,
25478         nzm1 = nz - 1;
25479       primitives.assign();
25480       if (!nxm1 || !nym1 || !nzm1) return CImg<floatT>();
25481       const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1, dz = (z1 - z0)/nzm1;
25482       CImgList<floatT> vertices;
25483       CImg<intT> indices1(nx,ny,1,3,-1), indices2(indices1);
25484       CImg<floatT> values1(nx,ny), values2(nx,ny);
25485       float X = 0, Y = 0, Z = 0, nX = 0, nY = 0, nZ = 0;
25486 
25487       // Fill the first plane with function values
25488       Y = y0;
25489       cimg_forY(values1,y) {
25490         X = x0;
25491         cimg_forX(values1,x) { values1(x,y) = (float)func(X,Y,z0); X+=dx; }
25492         Y+=dy;
25493       }
25494 
25495       // Run Marching Cubes algorithm
25496       Z = z0; nZ = Z + dz;
25497       for (unsigned int zi = 0; zi<nzm1; ++zi, Z = nZ, nZ+=dz) {
25498         Y = y0; nY = Y + dy;
25499         indices2.fill(-1);
25500         for (unsigned int yi = 0, nyi = 1; yi<nym1; ++yi, ++nyi, Y = nY, nY+=dy) {
25501           X = x0; nX = X + dx;
25502           for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X = nX, nX+=dx) {
25503 
25504             // Determine cube configuration
25505             const float
25506               val0 = values1(xi,yi),
25507               val1 = values1(nxi,yi),
25508               val2 = values1(nxi,nyi),
25509               val3 = values1(xi,nyi),
25510               val4 = values2(xi,yi) = (float)func(X,Y,nZ),
25511               val5 = values2(nxi,yi) = (float)func(nX,Y,nZ),
25512               val6 = values2(nxi,nyi) = (float)func(nX,nY,nZ),
25513               val7 = values2(xi,nyi) = (float)func(X,nY,nZ);
25514 
25515             const unsigned int configuration =
25516               (val0<isovalue?1:0)  | (val1<isovalue?2:0)  | (val2<isovalue?4:0)  | (val3<isovalue?8:0) |
25517               (val4<isovalue?16:0) | (val5<isovalue?32:0) | (val6<isovalue?64:0) | (val7<isovalue?128:0),
25518               edge = edges[configuration];
25519 
25520             // Compute intersection vertices
25521             if (edge) {
25522               if ((edge&1) && indices1(xi,yi,0)<0) {
25523                 const float Xi = X + (isovalue-val0)*dx/(val1-val0);
25524                 indices1(xi,yi,0) = vertices._width;
25525                 CImg<floatT>::vector(Xi,Y,Z).move_to(vertices);
25526               }
25527               if ((edge&2) && indices1(nxi,yi,1)<0) {
25528                 const float Yi = Y + (isovalue-val1)*dy/(val2-val1);
25529                 indices1(nxi,yi,1) = vertices._width;
25530                 CImg<floatT>::vector(nX,Yi,Z).move_to(vertices);
25531               }
25532               if ((edge&4) && indices1(xi,nyi,0)<0) {
25533                 const float Xi = X + (isovalue-val3)*dx/(val2-val3);
25534                 indices1(xi,nyi,0) = vertices._width;
25535                 CImg<floatT>::vector(Xi,nY,Z).move_to(vertices);
25536               }
25537               if ((edge&8) && indices1(xi,yi,1)<0) {
25538                 const float Yi = Y + (isovalue-val0)*dy/(val3-val0);
25539                 indices1(xi,yi,1) = vertices._width;
25540                 CImg<floatT>::vector(X,Yi,Z).move_to(vertices);
25541               }
25542               if ((edge&16) && indices2(xi,yi,0)<0) {
25543                 const float Xi = X + (isovalue-val4)*dx/(val5-val4);
25544                 indices2(xi,yi,0) = vertices._width;
25545                 CImg<floatT>::vector(Xi,Y,nZ).move_to(vertices);
25546               }
25547               if ((edge&32) && indices2(nxi,yi,1)<0) {
25548                 const float Yi = Y + (isovalue-val5)*dy/(val6-val5);
25549                 indices2(nxi,yi,1) = vertices._width;
25550                 CImg<floatT>::vector(nX,Yi,nZ).move_to(vertices);
25551               }
25552               if ((edge&64) && indices2(xi,nyi,0)<0) {
25553                 const float Xi = X + (isovalue-val7)*dx/(val6-val7);
25554                 indices2(xi,nyi,0) = vertices._width;
25555                 CImg<floatT>::vector(Xi,nY,nZ).move_to(vertices);
25556               }
25557               if ((edge&128) && indices2(xi,yi,1)<0)  {
25558                 const float Yi = Y + (isovalue-val4)*dy/(val7-val4);
25559                 indices2(xi,yi,1) = vertices._width;
25560                 CImg<floatT>::vector(X,Yi,nZ).move_to(vertices);
25561               }
25562               if ((edge&256) && indices1(xi,yi,2)<0) {
25563                 const float Zi = Z+ (isovalue-val0)*dz/(val4-val0);
25564                 indices1(xi,yi,2) = vertices._width;
25565                 CImg<floatT>::vector(X,Y,Zi).move_to(vertices);
25566               }
25567               if ((edge&512) && indices1(nxi,yi,2)<0)  {
25568                 const float Zi = Z + (isovalue-val1)*dz/(val5-val1);
25569                 indices1(nxi,yi,2) = vertices._width;
25570                 CImg<floatT>::vector(nX,Y,Zi).move_to(vertices);
25571               }
25572               if ((edge&1024) && indices1(nxi,nyi,2)<0) {
25573                 const float Zi = Z + (isovalue-val2)*dz/(val6-val2);
25574                 indices1(nxi,nyi,2) = vertices._width;
25575                 CImg<floatT>::vector(nX,nY,Zi).move_to(vertices);
25576               }
25577               if ((edge&2048) && indices1(xi,nyi,2)<0) {
25578                 const float Zi = Z + (isovalue-val3)*dz/(val7-val3);
25579                 indices1(xi,nyi,2) = vertices._width;
25580                 CImg<floatT>::vector(X,nY,Zi).move_to(vertices);
25581               }
25582 
25583               // Create triangles
25584               for (const int *triangle = triangles[configuration]; *triangle!=-1; ) {
25585                 const unsigned int p0 = *(triangle++), p1 = *(triangle++), p2 = *(triangle++);
25586                 const tf
25587                   i0 = (tf)(_isosurface3d_indice(p0,indices1,indices2,xi,yi,nxi,nyi)),
25588                   i1 = (tf)(_isosurface3d_indice(p1,indices1,indices2,xi,yi,nxi,nyi)),
25589                   i2 = (tf)(_isosurface3d_indice(p2,indices1,indices2,xi,yi,nxi,nyi));
25590                 CImg<tf>::vector(i0,i2,i1).move_to(primitives);
25591               }
25592             }
25593           }
25594         }
25595         cimg::swap(values1,values2);
25596         cimg::swap(indices1,indices2);
25597       }
25598       return vertices>'x';
25599     }
25600 
25601     template<typename tf>
25602     static CImg<floatT> isosurface3d(CImgList<tf>& primitives, const char *const expression, const float isovalue,
25603                                      const float x0, const float y0, const float z0,
25604                                      const float x1, const float y1, const float z1,
25605                                      const int dx=32, const int dy=32, const int dz=32) {
25606       const _functor3d_expr func(expression);
25607       return isosurface3d(primitives,func,isovalue,x0,y0,z0,x1,y1,z1,dx,dy,dz);
25608     }
25609 
25610     template<typename t>
25611     static int _isosurface3d_indice(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& indices2,
25612                                     const unsigned int x, const unsigned int y, const unsigned int nx, const unsigned int ny) {
25613       switch (edge) {
25614       case 0 : return indices1(x,y,0);
25615       case 1 : return indices1(nx,y,1);
25616       case 2 : return indices1(x,ny,0);
25617       case 3 : return indices1(x,y,1);
25618       case 4 : return indices2(x,y,0);
25619       case 5 : return indices2(nx,y,1);
25620       case 6 : return indices2(x,ny,0);
25621       case 7 : return indices2(x,y,1);
25622       case 8 : return indices1(x,y,2);
25623       case 9 : return indices1(nx,y,2);
25624       case 10 : return indices1(nx,ny,2);
25625       case 11 : return indices1(x,ny,2);
25626       }
25627       return 0;
25628     }
25629 
25630     // Define functors for accessing image values (used in previous functions).
25631     struct _functor2d_int {
25632       const CImg<T>& ref;
25633       _functor2d_int(const CImg<T>& pref):ref(pref) {}
25634       float operator()(const float x, const float y) const {
25635         return (float)ref((int)x,(int)y);
25636       }
25637     };
25638 
25639     struct _functor2d_float {
25640       const CImg<T>& ref;
25641       _functor2d_float(const CImg<T>& pref):ref(pref) {}
25642       float operator()(const float x, const float y) const {
25643         return (float)ref._linear_atXY(x,y);
25644       }
25645     };
25646 
25647     struct _functor2d_expr {
25648       _cimg_math_parser *mp;
25649       _functor2d_expr(const char *const expr):mp(0) { mp = new _cimg_math_parser(CImg<T>::empty(),expr,0); }
25650       ~_functor2d_expr() { delete mp; }
25651       float operator()(const float x, const float y) const {
25652         return (float)mp->eval(x,y,0,0);
25653       }
25654     };
25655 
25656     struct _functor3d_int {
25657       const CImg<T>& ref;
25658       _functor3d_int(const CImg<T>& pref):ref(pref) {}
25659       float operator()(const float x, const float y, const float z) const {
25660         return (float)ref((int)x,(int)y,(int)z);
25661       }
25662     };
25663 
25664     struct _functor3d_float {
25665       const CImg<T>& ref;
25666       _functor3d_float(const CImg<T>& pref):ref(pref) {}
25667       float operator()(const float x, const float y, const float z) const {
25668         return (float)ref._linear_atXYZ(x,y,z);
25669       }
25670     };
25671 
25672     struct _functor3d_expr {
25673       _cimg_math_parser *mp;
25674       ~_functor3d_expr() { delete mp; }
25675       _functor3d_expr(const char *const expr):mp(0) { mp = new _cimg_math_parser(CImg<T>::empty(),expr,0); }
25676       float operator()(const float x, const float y, const float z) const {
25677         return (float)mp->eval(x,y,z,0);
25678       }
25679     };
25680 
25681     struct _functor4d_int {
25682       const CImg<T>& ref;
25683       _functor4d_int(const CImg<T>& pref):ref(pref) {}
25684       float operator()(const float x, const float y, const float z, const unsigned int c) const {
25685         return (float)ref((int)x,(int)y,(int)z,c);
25686       }
25687     };
25688 
25689     //! Create and return a 3d box object.
25690     /**
25691        \param[out] primitives The returned list of the 3d object primitives
25692                               (template type \e tf should be at least \e unsigned \e int).
25693        \param size_x The width of the box (dimension along the X-axis).
25694        \param size_y The height of the box (dimension along the Y-axis).
25695        \param size_z The depth of the box (dimension along the Z-axis).
25696        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
25697        \par Sample code :
25698        \code
25699        CImgList<unsigned int> faces3d;
25700        const CImg<float> points3d = CImg<float>::box3d(faces3d,10,20,30);
25701        CImg<unsigned char>().display_object3d("Box3d",points3d,faces3d);
25702        \endcode
25703        \image html ref_box3d.jpg
25704     **/
25705     template<typename tf>
25706     static CImg<floatT> box3d(CImgList<tf>& primitives,
25707                               const float size_x=200, const float size_y=100, const float size_z=100) {
25708       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);
25709       return CImg<floatT>(8,3,1,1,
25710                           0.,size_x,size_x,    0.,    0.,size_x,size_x,    0.,
25711                           0.,    0.,size_y,size_y,    0.,    0.,size_y,size_y,
25712                           0.,    0.,    0.,    0.,size_z,size_z,size_z,size_z);
25713     }
25714 
25715     //! Create and return a 3d cone.
25716     /**
25717        \param[out] primitives The returned list of the 3d object primitives
25718                               (template type \e tf should be at least \e unsigned \e int).
25719        \param radius The radius of the cone basis.
25720        \param size_z The cone's height.
25721        \param subdivisions The number of basis angular subdivisions.
25722        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
25723        \par Sample code :
25724        \code
25725        CImgList<unsigned int> faces3d;
25726        const CImg<float> points3d = CImg<float>::cone3d(faces3d,50);
25727        CImg<unsigned char>().display_object3d("Cone3d",points3d,faces3d);
25728        \endcode
25729        \image html ref_cone3d.jpg
25730     **/
25731     template<typename tf>
25732     static CImg<floatT> cone3d(CImgList<tf>& primitives,
25733                                const float radius=50, const float size_z=100, const unsigned int subdivisions=24) {
25734       primitives.assign();
25735       if (!subdivisions) return CImg<floatT>();
25736       CImgList<floatT> vertices(2,1,3,1,1,
25737                                 0.,0.,size_z,
25738                                 0.,0.,0.);
25739       for (float delta = 360.0f/subdivisions, angle = 0; angle<360; angle+=delta) {
25740         const float a = (float)(angle*cimg::PI/180);
25741         CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0).move_to(vertices);
25742       }
25743       const unsigned int nbr = vertices._width - 2;
25744       for (unsigned int p = 0; p<nbr; ++p) {
25745         const unsigned int curr = 2 + p, next = 2 + ((p+1)%nbr);
25746         CImg<tf>::vector(1,next,curr).move_to(primitives);
25747         CImg<tf>::vector(0,curr,next).move_to(primitives);
25748       }
25749       return vertices>'x';
25750     }
25751 
25752     //! Create and return a 3d cylinder.
25753     /**
25754        \param[out] primitives The returned list of the 3d object primitives
25755                               (template type \e tf should be at least \e unsigned \e int).
25756        \param radius The radius of the cylinder basis.
25757        \param size_z The cylinder's height.
25758        \param subdivisions The number of basis angular subdivisions.
25759        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
25760        \par Sample code :
25761        \code
25762        CImgList<unsigned int> faces3d;
25763        const CImg<float> points3d = CImg<float>::cylinder3d(faces3d,50);
25764        CImg<unsigned char>().display_object3d("Cylinder3d",points3d,faces3d);
25765        \endcode
25766        \image html ref_cylinder3d.jpg
25767     **/
25768     template<typename tf>
25769     static CImg<floatT> cylinder3d(CImgList<tf>& primitives,
25770                                    const float radius=50, const float size_z=100, const unsigned int subdivisions=24) {
25771       primitives.assign();
25772       if (!subdivisions) return CImg<floatT>();
25773       CImgList<floatT> vertices(2,1,3,1,1,
25774                                 0.,0.,0.,
25775                                 0.,0.,size_z);
25776       for (float delta = 360.0f/subdivisions, angle = 0; angle<360; angle+=delta) {
25777         const float a = (float)(angle*cimg::PI/180);
25778         CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0.0f).move_to(vertices);
25779         CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),size_z).move_to(vertices);
25780       }
25781       const unsigned int nbr = (vertices._width - 2)/2;
25782       for (unsigned int p = 0; p<nbr; ++p) {
25783         const unsigned int curr = 2+2*p, next = 2+(2*((p+1)%nbr));
25784         CImg<tf>::vector(0,next,curr).move_to(primitives);
25785         CImg<tf>::vector(1,curr+1,next+1).move_to(primitives);
25786         CImg<tf>::vector(curr,next,next+1,curr+1).move_to(primitives);
25787       }
25788       return vertices>'x';
25789     }
25790 
25791     //! Create and return a 3d torus.
25792     /**
25793        \param[out] primitives The returned list of the 3d object primitives
25794                               (template type \e tf should be at least \e unsigned \e int).
25795        \param radius1 The large radius.
25796        \param radius2 The small radius.
25797        \param subdivisions1 The number of angular subdivisions for the large radius.
25798        \param subdivisions2 The number of angular subdivisions for the small radius.
25799        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
25800        \par Sample code :
25801        \code
25802        CImgList<unsigned int> faces3d;
25803        const CImg<float> points3d = CImg<float>::torus3d(faces3d,20,4);
25804        CImg<unsigned char>().display_object3d("Torus3d",points3d,faces3d);
25805        \endcode
25806        \image html ref_torus3d.jpg
25807     **/
25808     template<typename tf>
25809     static CImg<floatT> torus3d(CImgList<tf>& primitives,
25810                                 const float radius1=100, const float radius2=30,
25811                                 const unsigned int subdivisions1=24, const unsigned int subdivisions2=12) {
25812       primitives.assign();
25813       if (!subdivisions1 || !subdivisions2) return CImg<floatT>();
25814       CImgList<floatT> vertices;
25815       for (unsigned int v = 0; v<subdivisions1; ++v) {
25816         const float
25817           beta = (float)(v*2*cimg::PI/subdivisions1),
25818           xc = radius1*(float)std::cos(beta),
25819           yc = radius1*(float)std::sin(beta);
25820         for (unsigned int u = 0; u<subdivisions2; ++u) {
25821           const float
25822             alpha = (float)(u*2*cimg::PI/subdivisions2),
25823             x = xc + radius2*(float)(std::cos(alpha)*std::cos(beta)),
25824             y = yc + radius2*(float)(std::cos(alpha)*std::sin(beta)),
25825             z = radius2*(float)std::sin(alpha);
25826           CImg<floatT>::vector(x,y,z).move_to(vertices);
25827         }
25828       }
25829       for (unsigned int vv = 0; vv<subdivisions1; ++vv) {
25830         const unsigned int nv = (vv+1)%subdivisions1;
25831         for (unsigned int uu = 0; uu<subdivisions2; ++uu) {
25832           const unsigned int nu = (uu+1)%subdivisions2, svv = subdivisions2*vv, snv = subdivisions2*nv;
25833           CImg<tf>::vector(svv+nu,svv+uu,snv+uu).move_to(primitives);
25834           CImg<tf>::vector(svv+nu,snv+uu,snv+nu).move_to(primitives);
25835         }
25836       }
25837       return vertices>'x';
25838     }
25839 
25840     //! Create and return a 3d XY-plane.
25841     /**
25842        \param[out] primitives The returned list of the 3d object primitives
25843                               (template type \e tf should be at least \e unsigned \e int).
25844        \param size_x The width of the plane (dimension along the X-axis).
25845        \param size_y The height of the plane (dimensions along the Y-axis).
25846        \param subdivisions_x The number of planar subdivisions along the X-axis.
25847        \param subdivisions_y The number of planar subdivisions along the Y-axis.
25848        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
25849        \par Sample code :
25850        \code
25851        CImgList<unsigned int> faces3d;
25852        const CImg<float> points3d = CImg<float>::plane3d(faces3d,100,50);
25853        CImg<unsigned char>().display_object3d("Plane3d",points3d,faces3d);
25854        \endcode
25855        \image html ref_plane3d.jpg
25856     **/
25857     template<typename tf>
25858     static CImg<floatT> plane3d(CImgList<tf>& primitives,
25859                                 const float size_x=100, const float size_y=100,
25860                                 const unsigned int subdivisions_x=10, const unsigned int subdivisions_y=10) {
25861       primitives.assign();
25862       if (!subdivisions_x || !subdivisions_y) return CImg<floatT>();
25863       CImgList<floatT> vertices;
25864       const unsigned int w = subdivisions_x + 1, h = subdivisions_y + 1;
25865       const float fx = (float)size_x/w, fy = (float)size_y/h;
25866       for (unsigned int y = 0; y<h; ++y) for (unsigned int x = 0; x<w; ++x)
25867         CImg<floatT>::vector(fx*x,fy*y,0).move_to(vertices);
25868       for (unsigned int y = 0; y<subdivisions_y; ++y) for (unsigned int x = 0; x<subdivisions_x; ++x) {
25869         const int off1 = x+y*w, off2 = x+1+y*w, off3 = x+1+(y+1)*w, off4 = x+(y+1)*w;
25870         CImg<tf>::vector(off1,off4,off3,off2).move_to(primitives);
25871       }
25872       return vertices>'x';
25873     }
25874 
25875     //! Create and return a 3d sphere.
25876     /**
25877        \param[out] primitives The returned list of the 3d object primitives
25878                               (template type \e tf should be at least \e unsigned \e int).
25879        \param radius The radius of the sphere (dimension along the X-axis).
25880        \param subdivisions The number of recursive subdivisions from an initial icosahedron.
25881        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
25882        \par Sample code :
25883        \code
25884        CImgList<unsigned int> faces3d;
25885        const CImg<float> points3d = CImg<float>::sphere3d(faces3d,100,4);
25886        CImg<unsigned char>().display_object3d("Sphere3d",points3d,faces3d);
25887        \endcode
25888        \image html ref_sphere3d.jpg
25889     **/
25890     template<typename tf>
25891     static CImg<floatT> sphere3d(CImgList<tf>& primitives,
25892                                  const float radius=50, const unsigned int subdivisions=3) {
25893 
25894       // Create initial icosahedron
25895       primitives.assign();
25896       const double tmp = (1+std::sqrt(5.0f))/2, a = 1.0/std::sqrt(1+tmp*tmp), b = tmp*a;
25897       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,
25898                                 -a,0.0,-b, -a,0.0,b, 0.0,b,a, 0.0,-b,a, 0.0,-b,-a, 0.0,b,-a);
25899       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,
25900                         8,0,11, 8,11,1, 9,10,3, 9,2,10, 8,4,0, 11,0,5, 4,9,3,
25901                         5,3,10, 7,8,1, 6,1,11, 7,2,9, 6,10,2);
25902       // edge - length/2
25903       float he = (float)a;
25904 
25905       // Recurse subdivisions
25906       for (unsigned int i = 0; i<subdivisions; ++i) {
25907         const unsigned int L = primitives._width;
25908         he/=2;
25909         const float he2 = he*he;
25910         for (unsigned int l = 0; l<L; ++l) {
25911           const unsigned int
25912             p0 = (unsigned int)primitives(0,0), p1 = (unsigned int)primitives(0,1), p2 = (unsigned int)primitives(0,2);
25913           const float
25914             x0 = vertices(p0,0), y0 = vertices(p0,1), z0 = vertices(p0,2),
25915             x1 = vertices(p1,0), y1 = vertices(p1,1), z1 = vertices(p1,2),
25916             x2 = vertices(p2,0), y2 = vertices(p2,1), z2 = vertices(p2,2),
25917             tnx0 = (x0+x1)/2, tny0 = (y0+y1)/2, tnz0 = (z0+z1)/2, nn0 = (float)std::sqrt(tnx0*tnx0+tny0*tny0+tnz0*tnz0),
25918             tnx1 = (x0+x2)/2, tny1 = (y0+y2)/2, tnz1 = (z0+z2)/2, nn1 = (float)std::sqrt(tnx1*tnx1+tny1*tny1+tnz1*tnz1),
25919             tnx2 = (x1+x2)/2, tny2 = (y1+y2)/2, tnz2 = (z1+z2)/2, nn2 = (float)std::sqrt(tnx2*tnx2+tny2*tny2+tnz2*tnz2),
25920             nx0 = tnx0/nn0, ny0 = tny0/nn0, nz0 = tnz0/nn0,
25921             nx1 = tnx1/nn1, ny1 = tny1/nn1, nz1 = tnz1/nn1,
25922             nx2 = tnx2/nn2, ny2 = tny2/nn2, nz2 = tnz2/nn2;
25923           int i0 = -1, i1 = -1, i2 = -1;
25924           cimglist_for(vertices,p) {
25925             const float x = (float)vertices(p,0), y = (float)vertices(p,1), z = (float)vertices(p,2);
25926             if (cimg::sqr(x-nx0) + cimg::sqr(y-ny0) + cimg::sqr(z-nz0)<he2) i0 = p;
25927             if (cimg::sqr(x-nx1) + cimg::sqr(y-ny1) + cimg::sqr(z-nz1)<he2) i1 = p;
25928             if (cimg::sqr(x-nx2) + cimg::sqr(y-ny2) + cimg::sqr(z-nz2)<he2) i2 = p;
25929           }
25930           if (i0<0) { CImg<floatT>::vector(nx0,ny0,nz0).move_to(vertices); i0 = vertices._width - 1; }
25931           if (i1<0) { CImg<floatT>::vector(nx1,ny1,nz1).move_to(vertices); i1 = vertices._width - 1; }
25932           if (i2<0) { CImg<floatT>::vector(nx2,ny2,nz2).move_to(vertices); i2 = vertices._width - 1; }
25933           primitives.remove(0);
25934           CImg<tf>::vector(p0,i0,i1).move_to(primitives);
25935           CImg<tf>::vector((tf)i0,(tf)p1,(tf)i2).move_to(primitives);
25936           CImg<tf>::vector((tf)i1,(tf)i2,(tf)p2).move_to(primitives);
25937           CImg<tf>::vector((tf)i1,(tf)i0,(tf)i2).move_to(primitives);
25938         }
25939       }
25940       return (vertices>'x')*=radius;
25941     }
25942 
25943     //! Create and return a 3d ellipsoid.
25944     /**
25945        \param[out] primitives The returned list of the 3d object primitives
25946                               (template type \e tf should be at least \e unsigned \e int).
25947        \param tensor The tensor which gives the shape and size of the ellipsoid.
25948        \param subdivisions The number of recursive subdivisions from an initial stretched icosahedron.
25949        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
25950        \par Sample code :
25951        \code
25952        CImgList<unsigned int> faces3d;
25953        const CImg<float> tensor = CImg<float>::diagonal(10,7,3),
25954                          points3d = CImg<float>::ellipsoid3d(faces3d,tensor,4);
25955        CImg<unsigned char>().display_object3d("Ellipsoid3d",points3d,faces3d);
25956        \endcode
25957        \image html ref_ellipsoid3d.jpg
25958     **/
25959     template<typename tf, typename t>
25960     static CImg<floatT> ellipsoid3d(CImgList<tf>& primitives,
25961                                     const CImg<t>& tensor, const unsigned int subdivisions=3) {
25962       primitives.assign();
25963       if (!subdivisions) return CImg<floatT>();
25964       CImg<floatT> S, V;
25965       tensor.symmetric_eigen(S,V);
25966       const float orient =
25967         (V(0,1)*V(1,2) - V(0,2)*V(1,1))*V(2,0) +
25968         (V(0,2)*V(1,0) - V(0,0)*V(1,2))*V(2,1) +
25969         (V(0,0)*V(1,1) - V(0,1)*V(1,0))*V(2,2);
25970       if (orient<0) { V(2,0) = -V(2,0); V(2,1) = -V(2,1); V(2,2) = -V(2,2); }
25971       const float l0 = S[0], l1 = S[1], l2 = S[2];
25972       CImg<floatT> vertices = sphere3d(primitives,1.0,subdivisions);
25973       vertices.get_shared_line(0)*=l0;
25974       vertices.get_shared_line(1)*=l1;
25975       vertices.get_shared_line(2)*=l2;
25976       return V*vertices;
25977     }
25978 
25979     //! Convert a 3d object into a CImg3d.
25980     template<typename tp, typename tc, typename to>
25981     CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives,
25982                               const CImgList<tc>& colors,
25983                               const to& opacities) {
25984       return get_object3dtoCImg3d(primitives,colors,opacities).move_to(*this);
25985     }
25986 
25987     template<typename tp, typename tc>
25988     CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives,
25989                               const CImgList<tc>& colors) {
25990       return get_object3dtoCImg3d(primitives,colors).move_to(*this);
25991     }
25992 
25993     template<typename tp>
25994     CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives) {
25995       return get_object3dtoCImg3d(primitives).move_to(*this);
25996     }
25997 
25998     CImg<T>& object3dtoCImg3d() {
25999       return get_object3dtoCImg3d().move_to(*this);
26000     }
26001 
26002     template<typename tp, typename tc, typename to>
26003     CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives,
26004                                       const CImgList<tc>& colors,
26005                                       const to& opacities) const {
26006       char error_message[1024] = { 0 };
26007       if (!is_object3d(primitives,colors,opacities,true,error_message))
26008         throw CImgInstanceException(_cimg_instance
26009                                     "object3dtoCImg3d() : Invalid specified 3d object (%u,%u) (%s).",
26010                                     cimg_instance,_width,primitives._width,error_message);
26011       CImg<floatT> res(1,_size_object3dtoCImg3d(primitives,colors,opacities));
26012       float *ptrd = res._data;
26013 
26014       // Put magick number.
26015       *(ptrd++) = 'C' + 0.5f; *(ptrd++) = 'I' + 0.5f; *(ptrd++) = 'm' + 0.5f;
26016       *(ptrd++) = 'g' + 0.5f; *(ptrd++) = '3' + 0.5f; *(ptrd++) = 'd' + 0.5f;
26017 
26018       // Put number of vertices and primitives.
26019       *(ptrd++) = cimg::uint2float(_width);
26020       *(ptrd++) = cimg::uint2float(primitives._width);
26021 
26022       // Put vertex data.
26023       if (is_empty() || !primitives) return res;
26024       const T *ptrx = data(0,0), *ptry = data(0,1), *ptrz = data(0,2);
26025       cimg_forX(*this,p) {
26026         *(ptrd++) = (float)*(ptrx++);
26027         *(ptrd++) = (float)*(ptry++);
26028         *(ptrd++) = (float)*(ptrz++);
26029       }
26030 
26031       // Put primitive data.
26032       cimglist_for(primitives,p) {
26033         *(ptrd++) = (float)primitives[p].size();
26034         const tp *ptrp = primitives[p]._data;
26035         cimg_foroff(primitives[p],i) *(ptrd++) = cimg::uint2float((unsigned int)*(ptrp++));
26036       }
26037 
26038       // Put color/texture data.
26039       const unsigned int csiz = cimg::min(colors._width,primitives._width);
26040       for (int c = 0; c<(int)csiz; ++c) {
26041         const CImg<tc>& color = colors[c];
26042         const tc *ptrc = color._data;
26043         if (color.size()==3) { *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*ptrc; }
26044         else {
26045           *(ptrd++) = -128.0f;
26046           int shared_ind = -1;
26047           if (color.is_shared()) for (int i = 0; i<c; ++i) if (ptrc==colors[i]._data) { shared_ind = i; break; }
26048           if (shared_ind<0) {
26049             *(ptrd++) = (float)color._width;
26050             *(ptrd++) = (float)color._height;
26051             *(ptrd++) = (float)color._spectrum;
26052             cimg_foroff(color,l) *(ptrd++) = (float)*(ptrc++);
26053           } else {
26054             *(ptrd++) = (float)shared_ind;
26055             *(ptrd++) = 0;
26056             *(ptrd++) = 0;
26057           }
26058         }
26059       }
26060       const int csiz2 = primitives._width - colors._width;
26061       for (int c = 0; c<csiz2; ++c) { *(ptrd++) = 200.0f; *(ptrd++) = 200.0f; *(ptrd++) = 200.0f; }
26062 
26063       // Put opacity data.
26064       ptrd = _object3dtoCImg3d(opacities,ptrd);
26065       const float *ptre = res.end();
26066       while (ptrd<ptre) *(ptrd++) = 1.0f;
26067       return res;
26068     }
26069 
26070     template<typename to>
26071     float* _object3dtoCImg3d(const CImgList<to>& opacities, float *ptrd) const {
26072       cimglist_for(opacities,o) {
26073         const CImg<to>& opacity = opacities[o];
26074         const to *ptro = opacity._data;
26075         if (opacity.size()==1) *(ptrd++) = (float)*ptro;
26076         else {
26077           *(ptrd++) = -128.0f;
26078           int shared_ind = -1;
26079           if (opacity.is_shared()) for (int i = 0; i<o; ++i) if (ptro==opacities[i]._data) { shared_ind = i; break; }
26080           if (shared_ind<0) {
26081             *(ptrd++) = (float)opacity._width;
26082             *(ptrd++) = (float)opacity._height;
26083             *(ptrd++) = (float)opacity._spectrum;
26084             cimg_foroff(opacity,l) *(ptrd++) = (float)*(ptro++);
26085           } else {
26086             *(ptrd++) = (float)shared_ind;
26087             *(ptrd++) = 0;
26088             *(ptrd++) = 0;
26089           }
26090         }
26091       }
26092       return ptrd;
26093     }
26094 
26095     template<typename to>
26096     float* _object3dtoCImg3d(const CImg<to>& opacities, float *ptrd) const {
26097       const to *ptro = opacities._data;
26098       cimg_foroff(opacities,o) *(ptrd++) = (float)*(ptro++);
26099       return ptrd;
26100     }
26101 
26102     template<typename tp, typename tc, typename to>
26103     unsigned int _size_object3dtoCImg3d(const CImgList<tp>& primitives,
26104                                         const CImgList<tc>& colors,
26105                                         const CImgList<to>& opacities) const {
26106       unsigned int siz = 8 + 3*width();
26107       cimglist_for(primitives,p) siz+=primitives[p].size() + 1;
26108       for (int c = cimg::min(primitives._width,colors._width)-1; c>=0; --c) {
26109         if (colors[c].is_shared()) siz+=4;
26110         else { const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4+csiz:3; }
26111       }
26112       if (colors._width<primitives._width) siz+=3*(primitives._width - colors._width);
26113       cimglist_for(opacities,o) {
26114         if (opacities[o].is_shared()) siz+=4;
26115         else { const unsigned int osiz = opacities[o].size(); siz+=(osiz!=1)?4+osiz:1; }
26116       }
26117       siz+=primitives._width - opacities._width;
26118       return siz;
26119     }
26120 
26121     template<typename tp, typename tc, typename to>
26122     unsigned int _size_object3dtoCImg3d(const CImgList<tp>& primitives,
26123                                         const CImgList<tc>& colors,
26124                                         const CImg<to>& opacities) const {
26125       unsigned int siz = 8 + 3*width();
26126       cimglist_for(primitives,p) siz+=primitives[p].size() + 1;
26127       for (int c = cimg::min(primitives._width,colors._width)-1; c>=0; --c) {
26128         const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4+csiz:3;
26129       }
26130       if (colors._width<primitives._width) siz+=3*(primitives._width - colors._width);
26131       siz+=primitives.size();
26132       cimg::unused(opacities);
26133       return siz;
26134     }
26135 
26136     template<typename tp, typename tc>
26137     CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives,
26138                                       const CImgList<tc>& colors) const {
26139       CImgList<T> opacities;
26140       return get_object3dtoCImg3d(primitives,colors,opacities);
26141     }
26142 
26143     template<typename tp>
26144     CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives) const {
26145       CImgList<T> colors, opacities;
26146       return get_object3dtoCImg3d(primitives,colors,opacities);
26147     }
26148 
26149     CImg<floatT> get_object3dtoCImg3d() const {
26150       CImgList<T> opacities, colors;
26151       CImgList<uintT> primitives(width(),1,1,1,1);
26152       cimglist_for(primitives,p) primitives(p,0) = p;
26153       return get_object3dtoCImg3d(primitives,colors,opacities);
26154     }
26155 
26156     //! Convert a CImg3d (one-column image) into a 3d object.
26157     template<typename tp, typename tc, typename to>
26158     CImg<T> get_CImg3dtoobject3d(CImgList<tp>& primitives, CImgList<tc>& colors, CImgList<to>& opacities) const {
26159       char error_message[1024] = { 0 };
26160       if (!is_CImg3d(true,error_message))
26161         throw CImgInstanceException(_cimg_instance
26162                                     "CImg3dtoobject3d() : image instance is not a CImg3d (%s).",
26163                                     cimg_instance,error_message);
26164       const T *ptrs = _data + 6;
26165       const unsigned int
26166         nb_points = cimg::float2uint((float)*(ptrs++)),
26167         nb_primitives = cimg::float2uint((float)*(ptrs++));
26168       const CImg<T> points = CImg<T>(ptrs,3,nb_points,1,1,true).get_transpose();
26169       ptrs+=3*nb_points;
26170       primitives.assign(nb_primitives);
26171       cimglist_for(primitives,p) {
26172         const unsigned int nb_inds = (unsigned int)*(ptrs++);
26173         primitives[p].assign(1,nb_inds);
26174         tp *ptrp = primitives[p]._data;
26175         for (unsigned int i = 0; i<nb_inds; ++i) *(ptrp++) = (tp)cimg::float2uint((float)*(ptrs++));
26176       }
26177       colors.assign(nb_primitives);
26178       cimglist_for(colors,c) {
26179         if ((int)*ptrs==-128) {
26180           ++ptrs;
26181           const unsigned int w = (unsigned int)*(ptrs++), h = (unsigned int)*(ptrs++), s = (unsigned int)*(ptrs++);
26182           if (!h && !s) colors[c].assign(colors[w],true);
26183           else { colors[c].assign(ptrs,w,h,1,s,false); ptrs+=w*h*s; }
26184         } else { colors[c].assign(ptrs,1,1,1,3,false); ptrs+=3; }
26185       }
26186       opacities.assign(nb_primitives);
26187       cimglist_for(opacities,o) {
26188         if ((int)*ptrs==-128) {
26189           ++ptrs;
26190           const unsigned int w = (unsigned int)*(ptrs++), h = (unsigned int)*(ptrs++), s = (unsigned int)*(ptrs++);
26191           if (!h && !s) opacities[o].assign(opacities[w],true);
26192           else { opacities[o].assign(ptrs,w,h,1,s,false); ptrs+=w*h*s; }
26193         } else opacities[o].assign(1,1,1,1,*(ptrs++));
26194       }
26195       return points;
26196     }
26197 
26198     template<typename tp, typename tc, typename to>
26199     CImg<T>& CImg3dtoobject3d(CImgList<tp>& primitives, CImgList<tc>& colors, CImgList<to>& opacities) {
26200       return get_CImg3dtoobject3d(primitives,colors,opacities).move_to(*this);
26201     }
26202 
26203     //@}
26204     //---------------------------
26205     //
26206     //! \name Drawing Functions
26207     //@{
26208     //---------------------------
26209 
26210     // The following _draw_scanline() routines are *non user-friendly functions*, used only for internal purpose.
26211     // Pre-requisites : x0<x1, y-coordinate is valid, col is valid.
26212     template<typename tc>
26213     CImg<T>& _draw_scanline(const int x0, const int x1, const int y,
26214                             const tc *const color, const float opacity=1,
26215                             const float brightness=1, const bool init=false) {
26216       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
26217       static float nopacity = 0, copacity = 0;
26218       static unsigned int whd = 0;
26219       static const tc *col = 0;
26220       if (init) {
26221         nopacity = cimg::abs(opacity);
26222         copacity = 1 - cimg::max(opacity,0);
26223         whd = _width*_height*_depth;
26224       } else {
26225         const int nx0 = x0>0?x0:0, nx1 = x1<width()?x1:width()-1, dx = nx1 - nx0;
26226         if (dx>=0) {
26227           col = color;
26228           const unsigned int off = whd-dx-1;
26229           T *ptrd = data(nx0,y);
26230           if (opacity>=1) { // ** Opaque drawing **
26231             if (brightness==1) { // Brightness==1
26232               if (sizeof(T)!=1) cimg_forC(*this,c) {
26233                 const T val = (T)*(col++);
26234                 for (int x = dx; x>=0; --x) *(ptrd++) = val;
26235                 ptrd+=off;
26236               } else cimg_forC(*this,c) {
26237                 const T val = (T)*(col++);
26238                 std::memset(ptrd,(int)val,dx+1);
26239                 ptrd+=whd;
26240               }
26241             } else if (brightness<1) { // Brightness<1
26242               if (sizeof(T)!=1) cimg_forC(*this,c) {
26243                 const T val = (T)(*(col++)*brightness);
26244                 for (int x = dx; x>=0; --x) *(ptrd++) = val;
26245                 ptrd+=off;
26246               } else cimg_forC(*this,c) {
26247                 const T val = (T)(*(col++)*brightness);
26248                 std::memset(ptrd,(int)val,dx+1);
26249                 ptrd+=whd;
26250               }
26251             } else { // Brightness>1
26252               if (sizeof(T)!=1) cimg_forC(*this,c) {
26253                 const T val = (T)((2-brightness)**(col++) + (brightness-1)*maxval);
26254                 for (int x = dx; x>=0; --x) *(ptrd++) = val;
26255                 ptrd+=off;
26256               } else cimg_forC(*this,c) {
26257                 const T val = (T)((2-brightness)**(col++) + (brightness-1)*maxval);
26258                 std::memset(ptrd,(int)val,dx+1);
26259                 ptrd+=whd;
26260               }
26261             }
26262           } else { // ** Transparent drawing **
26263             if (brightness==1) { // Brightness==1
26264               cimg_forC(*this,c) {
26265                 const T val = (T)*(col++);
26266                 for (int x = dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ++ptrd; }
26267                 ptrd+=off;
26268               }
26269             } else if (brightness<=1) { // Brightness<1
26270               cimg_forC(*this,c) {
26271                 const T val = (T)(*(col++)*brightness);
26272                 for (int x = dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ++ptrd; }
26273                 ptrd+=off;
26274               }
26275             } else { // Brightness>1
26276               cimg_forC(*this,c) {
26277                 const T val = (T)((2-brightness)**(col++) + (brightness-1)*maxval);
26278                 for (int x = dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ++ptrd; }
26279                 ptrd+=off;
26280               }
26281             }
26282           }
26283         }
26284       }
26285       return *this;
26286     }
26287 
26288     template<typename tc>
26289     CImg<T>& _draw_scanline(const tc *const color, const float opacity=1) {
26290       return _draw_scanline(0,0,0,color,opacity,0,true);
26291     }
26292 
26293     //! Draw a 2d colored point (pixel).
26294     /**
26295        \param x0 X-coordinate of the point.
26296        \param y0 Y-coordinate of the point.
26297        \param color Pointer to \c spectrum() consecutive values, defining the color values.
26298        \param opacity Drawing opacity (optional).
26299        \note
26300        - Clipping is supported.
26301        - To set pixel values without clipping needs, you should use the faster CImg::operator()() function.
26302        \par Example:
26303        \code
26304        CImg<unsigned char> img(100,100,1,3,0);
26305        const unsigned char color[] = { 255,128,64 };
26306        img.draw_point(50,50,color);
26307        \endcode
26308     **/
26309     template<typename tc>
26310     CImg<T>& draw_point(const int x0, const int y0,
26311                         const tc *const color, const float opacity=1) {
26312       return draw_point(x0,y0,0,color,opacity);
26313     }
26314 
26315     //! Draw a 3d colored point (voxel).
26316     template<typename tc>
26317     CImg<T>& draw_point(const int x0, const int y0, const int z0,
26318                         const tc *const color, const float opacity=1) {
26319       if (!color)
26320         throw CImgArgumentException(_cimg_instance
26321                                     "draw_point() : Specified color is (null).",
26322                                     cimg_instance);
26323 
26324       if (is_empty()) return *this;
26325       if (x0>=0 && y0>=0 && z0>=0 && x0<width() && y0<height() && z0<depth()) {
26326         const unsigned int whd = _width*_height*_depth;
26327         const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
26328         T *ptrd = data(x0,y0,z0,0);
26329         const tc *col = color;
26330         if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; }
26331         else cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; }
26332       }
26333       return *this;
26334     }
26335 
26336     // Draw a cloud of colored points.
26337     template<typename t, typename tc>
26338     CImg<T>& draw_point(const CImg<t>& points,
26339                         const tc *const color, const float opacity=1) {
26340       if (is_empty() || !points) return *this;
26341       switch (points._height) {
26342       case 0 : case 1 :
26343         throw CImgArgumentException(_cimg_instance
26344                                     "draw_point() : Invalid specified point set (%u,%u,%u,%u,%p).",
26345                                     cimg_instance,
26346                                     points._width,points._height,points._depth,points._spectrum,points._data);
26347       case 2 : {
26348         cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),color,opacity);
26349       } break;
26350       default : {
26351         cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),(int)points(i,2),color,opacity);
26352       }
26353       }
26354       return *this;
26355     }
26356 
26357     //! Draw a 2d colored line.
26358     /**
26359        \param x0 X-coordinate of the starting line point.
26360        \param y0 Y-coordinate of the starting line point.
26361        \param x1 X-coordinate of the ending line point.
26362        \param y1 Y-coordinate of the ending line point.
26363        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
26364        \param opacity Drawing opacity (optional).
26365        \param pattern An integer whose bits describe the line pattern (optional).
26366        \param init_hatch Flag telling if a reinitialization of the hash state must be done (optional).
26367        \note
26368        - Clipping is supported.
26369        - Line routine uses Bresenham's algorithm.
26370        - Set \p init_hatch = false to draw consecutive hatched segments without breaking the line pattern.
26371        \par Example:
26372        \code
26373        CImg<unsigned char> img(100,100,1,3,0);
26374        const unsigned char color[] = { 255,128,64 };
26375         img.draw_line(40,40,80,70,color);
26376        \endcode
26377     **/
26378     template<typename tc>
26379     CImg<T>& draw_line(const int x0, const int y0,
26380                        const int x1, const int y1,
26381                        const tc *const color, const float opacity=1,
26382                        const unsigned int pattern=~0U, const bool init_hatch=true) {
26383       if (!color)
26384         throw CImgArgumentException(_cimg_instance
26385                                     "draw_line() : Specified color is (null).",
26386                                     cimg_instance);
26387 
26388       if (is_empty()) return *this;
26389       static unsigned int hatch = ~0U - (~0U>>1);
26390       if (init_hatch) hatch = ~0U - (~0U>>1);
26391       const bool xdir = x0<x1, ydir = y0<y1;
26392       int
26393         nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
26394         &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
26395         &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
26396         &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
26397         &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
26398       if (xright<0 || xleft>=width()) return *this;
26399       if (xleft<0) { yleft-=(int)((float)xleft*((float)yright - yleft)/((float)xright - xleft)); xleft = 0; }
26400       if (xright>=width()) { yright-=(int)(((float)xright - width())*((float)yright - yleft)/((float)xright - xleft)); xright = width() - 1; }
26401       if (ydown<0 || yup>=height()) return *this;
26402       if (yup<0) { xup-=(int)((float)yup*((float)xdown - xup)/((float)ydown - yup)); yup = 0; }
26403       if (ydown>=height()) { xdown-=(int)(((float)ydown - height())*((float)xdown - xup)/((float)ydown - yup)); ydown = height() - 1; }
26404       T *ptrd0 = data(nx0,ny0);
26405       int dx = xright - xleft, dy = ydown - yup;
26406       const bool steep = dy>dx;
26407       if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
26408       const int
26409         offx = (nx0<nx1?1:-1)*(steep?_width:1),
26410         offy = (ny0<ny1?1:-1)*(steep?1:_width),
26411         wh = _width*_height;
26412       if (opacity>=1) {
26413         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
26414           if (pattern&hatch) {
26415             T *ptrd = ptrd0; const tc* col = color;
26416             cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
26417           }
26418           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
26419           ptrd0+=offx;
26420           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
26421         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
26422           T *ptrd = ptrd0; const tc* col = color; cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
26423           ptrd0+=offx;
26424           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
26425         }
26426       } else {
26427         const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
26428         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
26429           if (pattern&hatch) {
26430             T *ptrd = ptrd0; const tc* col = color;
26431             cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
26432           }
26433           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
26434           ptrd0+=offx;
26435           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
26436         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
26437           T *ptrd = ptrd0; const tc* col = color; cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
26438           ptrd0+=offx;
26439           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
26440         }
26441       }
26442       return *this;
26443     }
26444 
26445     //! Draw a 2d colored line, with z-buffering.
26446     template<typename tz,typename tc>
26447     CImg<T>& draw_line(CImg<tz>& zbuffer,
26448                        const int x0, const int y0, const float z0,
26449                        const int x1, const int y1, const float z1,
26450                        const tc *const color, const float opacity=1,
26451                        const unsigned int pattern=~0U, const bool init_hatch=true) {
26452       typedef typename cimg::superset<tz,float>::type tzfloat;
26453       if (!color)
26454         throw CImgArgumentException(_cimg_instance
26455                                     "draw_line() : Specified color is (null).",
26456                                     cimg_instance);
26457       if (!is_sameXY(zbuffer))
26458         throw CImgArgumentException(_cimg_instance
26459                                     "draw_line() : Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
26460                                     cimg_instance,
26461                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
26462 
26463       if (is_empty() || z0<=0 || z1<=0) return *this;
26464       static unsigned int hatch = ~0U - (~0U>>1);
26465       if (init_hatch) hatch = ~0U - (~0U>>1);
26466       const bool xdir = x0<x1, ydir = y0<y1;
26467       int
26468         nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
26469         &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
26470         &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
26471         &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
26472         &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
26473       tzfloat
26474         Z0 = 1/(tzfloat)z0, Z1 = 1/(tzfloat)z1, nz0 = Z0, nz1 = Z1, dz = Z1 - Z0,
26475         &zleft = xdir?nz0:nz1,
26476         &zright = xdir?nz1:nz0,
26477         &zup = ydir?nz0:nz1,
26478         &zdown = ydir?nz1:nz0;
26479       if (xright<0 || xleft>=width()) return *this;
26480       if (xleft<0) {
26481         const float D = (float)xright - xleft;
26482         yleft-=(int)((float)xleft*((float)yright - yleft)/D);
26483         zleft-=(tzfloat)xleft*(zright - zleft)/D;
26484         xleft = 0;
26485       }
26486       if (xright>=width()) {
26487         const float d = (float)xright - width(), D = (float)xright - xleft;
26488         yright-=(int)(d*((float)yright - yleft)/D);
26489         zright-=(tzfloat)d*(zright - zleft)/D;
26490         xright = width() - 1;
26491       }
26492       if (ydown<0 || yup>=height()) return *this;
26493       if (yup<0) {
26494         const float D = (float)ydown - yup;
26495         xup-=(int)((float)yup*((float)xdown - xup)/D);
26496         zup-=(tzfloat)yup*(zdown - zup)/D;
26497         yup = 0;
26498       }
26499       if (ydown>=height()) {
26500         const float d = (float)ydown - height(), D = (float)ydown - yup;
26501         xdown-=(int)(d*((float)xdown - xup)/D);
26502         zdown-=(tzfloat)d*(zdown - zup)/D;
26503         ydown = height() - 1;
26504       }
26505       T *ptrd0 = data(nx0,ny0);
26506       tz *ptrz = zbuffer.data(nx0,ny0);
26507       int dx = xright - xleft, dy = ydown - yup;
26508       const bool steep = dy>dx;
26509       if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
26510       const int
26511         offx = (nx0<nx1?1:-1)*(steep?_width:1),
26512         offy = (ny0<ny1?1:-1)*(steep?1:_width),
26513         wh = _width*_height,
26514         ndx = dx>0?dx:1;
26515       if (opacity>=1) {
26516         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
26517           const tzfloat z = Z0 + x*dz/ndx;
26518           if (z>=(tzfloat)*ptrz && pattern&hatch) {
26519             *ptrz = (tz)z;
26520             T *ptrd = ptrd0; const tc *col = color;
26521             cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
26522           }
26523           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
26524           ptrd0+=offx; ptrz+=offx;
26525           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
26526         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
26527           const tzfloat z = Z0 + x*dz/ndx;
26528           if (z>=(tzfloat)*ptrz) {
26529             *ptrz = (tz)z;
26530             T *ptrd = ptrd0; const tc *col = color;
26531             cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
26532           }
26533           ptrd0+=offx; ptrz+=offx;
26534           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
26535         }
26536       } else {
26537         const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
26538         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
26539           const tzfloat z = Z0 + x*dz/ndx;
26540           if (z>=(tzfloat)*ptrz && pattern&hatch) {
26541             *ptrz = (tz)z;
26542             T *ptrd = ptrd0; const tc *col = color;
26543             cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
26544           }
26545           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
26546           ptrd0+=offx; ptrz+=offx;
26547           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
26548         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
26549           const tzfloat z = Z0 + x*dz/ndx;
26550           if (z>=(tzfloat)*ptrz) {
26551             *ptrz = (tz)z;
26552             T *ptrd = ptrd0; const tc *col = color;
26553             cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
26554           }
26555           ptrd0+=offx; ptrz+=offx;
26556           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
26557         }
26558       }
26559       return *this;
26560     }
26561 
26562     //! Draw a 3d colored line.
26563     template<typename tc>
26564     CImg<T>& draw_line(const int x0, const int y0, const int z0,
26565                        const int x1, const int y1, const int z1,
26566                        const tc *const color, const float opacity=1,
26567                        const unsigned int pattern=~0U, const bool init_hatch=true) {
26568       if (!color)
26569         throw CImgArgumentException(_cimg_instance
26570                                     "draw_line() : Specified color is (null).",
26571                                     cimg_instance);
26572 
26573       if (is_empty()) return *this;
26574       static unsigned int hatch = ~0U - (~0U>>1);
26575       if (init_hatch) hatch = ~0U - (~0U>>1);
26576       int nx0 = x0, ny0 = y0, nz0 = z0, nx1 = x1, ny1 = y1, nz1 = z1;
26577       if (nx0>nx1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
26578       if (nx1<0 || nx0>=width()) return *this;
26579       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; }
26580       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; }
26581       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
26582       if (ny1<0 || ny0>=height()) return *this;
26583       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; }
26584       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; }
26585       if (nz0>nz1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
26586       if (nz1<0 || nz0>=depth()) return *this;
26587       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; }
26588       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; }
26589       const unsigned int dmax = cimg::max(cimg::abs(nx1 - nx0),cimg::abs(ny1 - ny0),nz1 - nz0), whd = _width*_height*_depth;
26590       const float px = (nx1 - nx0)/(float)dmax, py = (ny1 - ny0)/(float)dmax, pz = (nz1 - nz0)/(float)dmax;
26591       float x = (float)nx0, y = (float)ny0, z = (float)nz0;
26592       if (opacity>=1) for (unsigned int t = 0; t<=dmax; ++t) {
26593         if (!(~pattern) || (~pattern && pattern&hatch)) {
26594           T* ptrd = data((unsigned int)x,(unsigned int)y,(unsigned int)z);
26595           const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; }
26596         }
26597         x+=px; y+=py; z+=pz; if (pattern) { hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); }
26598       } else {
26599         const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
26600         for (unsigned int t = 0; t<=dmax; ++t) {
26601           if (!(~pattern) || (~pattern && pattern&hatch)) {
26602             T* ptrd = data((unsigned int)x,(unsigned int)y,(unsigned int)z);
26603             const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; }
26604           }
26605           x+=px; y+=py; z+=pz; if (pattern) { hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); }
26606         }
26607       }
26608       return *this;
26609     }
26610 
26611     //! Draw a 2d textured line.
26612     /**
26613        \param x0 X-coordinate of the starting line point.
26614        \param y0 Y-coordinate of the starting line point.
26615        \param x1 X-coordinate of the ending line point.
26616        \param y1 Y-coordinate of the ending line point.
26617        \param texture Texture image defining the pixel colors.
26618        \param tx0 X-coordinate of the starting texture point.
26619        \param ty0 Y-coordinate of the starting texture point.
26620        \param tx1 X-coordinate of the ending texture point.
26621        \param ty1 Y-coordinate of the ending texture point.
26622        \param opacity Drawing opacity (optional).
26623        \param pattern An integer whose bits describe the line pattern (optional).
26624        \param init_hatch Flag telling if the hash variable must be reinitialized (optional).
26625        \note
26626        - Clipping is supported but not for texture coordinates.
26627        - Line routine uses the well known Bresenham's algorithm.
26628        \par Example:
26629        \code
26630        CImg<unsigned char> img(100,100,1,3,0), texture("texture256x256.ppm");
26631        const unsigned char color[] = { 255,128,64 };
26632        img.draw_line(40,40,80,70,texture,0,0,255,255);
26633        \endcode
26634     **/
26635     template<typename tc>
26636     CImg<T>& draw_line(const int x0, const int y0,
26637                        const int x1, const int y1,
26638                        const CImg<tc>& texture,
26639                        const int tx0, const int ty0,
26640                        const int tx1, const int ty1,
26641                        const float opacity=1,
26642                        const unsigned int pattern=~0U, const bool init_hatch=true) {
26643       if (texture._depth>1 || texture._spectrum<_spectrum)
26644         throw CImgArgumentException(_cimg_instance
26645                                     "draw_line() : Invalid specified texture (%u,%u,%u,%u,%p).",
26646                                     cimg_instance,
26647                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
26648 
26649       if (is_empty()) return *this;
26650       if (is_overlapped(texture)) return draw_line(x0,y0,x1,y1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
26651       static unsigned int hatch = ~0U - (~0U>>1);
26652       if (init_hatch) hatch = ~0U - (~0U>>1);
26653       const bool xdir = x0<x1, ydir = y0<y1;
26654       int
26655         dtx = tx1-tx0, dty = ty1-ty0,
26656         nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
26657         tnx0 = tx0, tnx1 = tx1, tny0 = ty0, tny1 = ty1,
26658         &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1, &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
26659         &txleft = xdir?tnx0:tnx1, &tyleft = xdir?tny0:tny1, &txright = xdir?tnx1:tnx0, &tyright = xdir?tny1:tny0,
26660         &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1, &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0,
26661         &txup = ydir?tnx0:tnx1, &tyup = ydir?tny0:tny1, &txdown = ydir?tnx1:tnx0, &tydown = ydir?tny1:tny0;
26662       if (xright<0 || xleft>=width()) return *this;
26663       if (xleft<0) {
26664         const float D = (float)xright - xleft;
26665         yleft-=(int)((float)xleft*((float)yright - yleft)/D);
26666         txleft-=(int)((float)xleft*((float)txright - txleft)/D);
26667         tyleft-=(int)((float)xleft*((float)tyright - tyleft)/D);
26668         xleft = 0;
26669       }
26670       if (xright>=width()) {
26671         const float d = (float)xright - width(), D = (float)xright - xleft;
26672         yright-=(int)(d*((float)yright - yleft)/D);
26673         txright-=(int)(d*((float)txright - txleft)/D);
26674         tyright-=(int)(d*((float)tyright - tyleft)/D);
26675         xright = width() - 1;
26676       }
26677       if (ydown<0 || yup>=height()) return *this;
26678       if (yup<0) {
26679         const float D = (float)ydown - yup;
26680         xup-=(int)((float)yup*((float)xdown - xup)/D);
26681         txup-=(int)((float)yup*((float)txdown - txup)/D);
26682         tyup-=(int)((float)yup*((float)tydown - tyup)/D);
26683         yup = 0;
26684       }
26685       if (ydown>=height()) {
26686         const float d = (float)ydown - height(), D = (float)ydown - yup;
26687         xdown-=(int)(d*((float)xdown - xup)/D);
26688         txdown-=(int)(d*((float)txdown - txup)/D);
26689         tydown-=(int)(d*((float)tydown - tyup)/D);
26690         ydown = height() - 1;
26691       }
26692       T *ptrd0 = data(nx0,ny0);
26693       int dx = xright - xleft, dy = ydown - yup;
26694       const bool steep = dy>dx;
26695       if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
26696       const int
26697         offx = (nx0<nx1?1:-1)*(steep?_width:1),
26698         offy = (ny0<ny1?1:-1)*(steep?1:_width),
26699         wh = _width*_height,
26700         ndx = dx>0?dx:1;
26701       if (opacity>=1) {
26702         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
26703           if (pattern&hatch) {
26704             T *ptrd = ptrd0;
26705             const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
26706             cimg_forC(*this,c) { *ptrd = (T)texture(tx,ty,0,c); ptrd+=wh; }
26707           }
26708           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
26709           ptrd0+=offx;
26710           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
26711         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
26712           T *ptrd = ptrd0;
26713           const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
26714           cimg_forC(*this,c) { *ptrd = (T)texture(tx,ty,0,c); ptrd+=wh; }
26715           ptrd0+=offx;
26716           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
26717         }
26718       } else {
26719         const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
26720         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
26721           T *ptrd = ptrd0;
26722           if (pattern&hatch) {
26723             const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
26724             cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture(tx,ty,0,c) + *ptrd*copacity); ptrd+=wh; }
26725           }
26726           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
26727           ptrd0+=offx;
26728           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
26729         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
26730           T *ptrd = ptrd0;
26731           const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
26732           cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture(tx,ty,0,c) + *ptrd*copacity); ptrd+=wh; }
26733           ptrd0+=offx;
26734           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
26735         }
26736       }
26737       return *this;
26738     }
26739 
26740     //! Draw a 2d textured line, with perspective correction.
26741     template<typename tc>
26742     CImg<T>& draw_line(const int x0, const int y0, const float z0,
26743                        const int x1, const int y1, const float z1,
26744                        const CImg<tc>& texture,
26745                        const int tx0, const int ty0,
26746                        const int tx1, const int ty1,
26747                        const float opacity=1,
26748                        const unsigned int pattern=~0U, const bool init_hatch=true) {
26749       if (texture._depth>1 || texture._spectrum<_spectrum)
26750         throw CImgArgumentException(_cimg_instance
26751                                     "draw_line() : Invalid specified texture (%u,%u,%u,%u,%p).",
26752                                     cimg_instance,
26753                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
26754 
26755       if (is_empty() && z0<=0 && z1<=0) return *this;
26756       if (is_overlapped(texture)) return draw_line(x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
26757       static unsigned int hatch = ~0U - (~0U>>1);
26758       if (init_hatch) hatch = ~0U - (~0U>>1);
26759       const bool xdir = x0<x1, ydir = y0<y1;
26760       int
26761         nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
26762         &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
26763         &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
26764         &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
26765         &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
26766       float
26767         Tx0 = tx0/z0, Tx1 = tx1/z1,
26768         Ty0 = ty0/z0, Ty1 = ty1/z1,
26769         Z0 = 1/z0, Z1 = 1/z1,
26770         dz = Z1 - Z0, dtx = Tx1 - Tx0, dty = Ty1 - Ty0,
26771         tnx0 = Tx0, tnx1 = Tx1, tny0 = Ty0, tny1 = Ty1, nz0 = Z0, nz1 = Z1,
26772         &zleft = xdir?nz0:nz1, &txleft = xdir?tnx0:tnx1, &tyleft = xdir?tny0:tny1,
26773         &zright = xdir?nz1:nz0, &txright = xdir?tnx1:tnx0, &tyright = xdir?tny1:tny0,
26774         &zup = ydir?nz0:nz1, &txup = ydir?tnx0:tnx1, &tyup = ydir?tny0:tny1,
26775         &zdown = ydir?nz1:nz0, &txdown = ydir?tnx1:tnx0, &tydown = ydir?tny1:tny0;
26776       if (xright<0 || xleft>=width()) return *this;
26777       if (xleft<0) {
26778         const float D = (float)xright - xleft;
26779         yleft-=(int)((float)xleft*((float)yright - yleft)/D);
26780         zleft-=(float)xleft*(zright - zleft)/D;
26781         txleft-=(float)xleft*(txright - txleft)/D;
26782         tyleft-=(float)xleft*(tyright - tyleft)/D;
26783         xleft = 0;
26784       }
26785       if (xright>=width()) {
26786         const float d = (float)xright - width(), D = (float)xright - xleft;
26787         yright-=(int)(d*((float)yright - yleft)/D);
26788         zright-=d*(zright - zleft)/D;
26789         txright-=d*(txright - txleft)/D;
26790         tyright-=d*(tyright - tyleft)/D;
26791         xright = width() - 1;
26792       }
26793       if (ydown<0 || yup>=height()) return *this;
26794       if (yup<0) {
26795         const float D = (float)ydown - yup;
26796         xup-=(int)((float)yup*((float)xdown - xup)/D);
26797         zup-=(float)yup*(zdown - zup)/D;
26798         txup-=(float)yup*(txdown - txup)/D;
26799         tyup-=(float)yup*(tydown - tyup)/D;
26800         yup = 0;
26801       }
26802       if (ydown>=height()) {
26803         const float d = (float)ydown - height(), D = (float)ydown - yup;
26804         xdown-=(int)(d*((float)xdown - xup)/D);
26805         zdown-=d*(zdown - zup)/D;
26806         txdown-=d*(txdown - txup)/D;
26807         tydown-=d*(tydown - tyup)/D;
26808         ydown = height() - 1;
26809       }
26810       T *ptrd0 = data(nx0,ny0);
26811       int dx = xright - xleft, dy = ydown - yup;
26812       const bool steep = dy>dx;
26813       if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
26814       const int
26815         offx = (nx0<nx1?1:-1)*(steep?_width:1),
26816         offy = (ny0<ny1?1:-1)*(steep?1:_width),
26817         wh = _width*_height,
26818         ndx = dx>0?dx:1;
26819       if (opacity>=1) {
26820         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
26821           if (pattern&hatch) {
26822             const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
26823             T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); ptrd+=wh; }
26824           }
26825           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
26826           ptrd0+=offx;
26827           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
26828         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
26829           const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
26830           T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); ptrd+=wh; }
26831           ptrd0+=offx;
26832           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
26833         }
26834       } else {
26835         const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
26836         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
26837           if (pattern&hatch) {
26838             const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
26839             T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,c) + *ptrd*copacity); ptrd+=wh; }
26840           }
26841           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
26842           ptrd0+=offx;
26843           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
26844         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
26845           const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
26846           T *ptrd = ptrd0;
26847           cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,c) + *ptrd*copacity); ptrd+=wh; }
26848           ptrd0+=offx;
26849           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
26850         }
26851       }
26852       return *this;
26853     }
26854 
26855     //! Draw a 2d textured line, with z-buffering and perspective correction.
26856     template<typename tz, typename tc>
26857     CImg<T>& draw_line(CImg<tz>& zbuffer,
26858                        const int x0, const int y0, const float z0,
26859                        const int x1, const int y1, const float z1,
26860                        const CImg<tc>& texture,
26861                        const int tx0, const int ty0,
26862                        const int tx1, const int ty1,
26863                        const float opacity=1,
26864                        const unsigned int pattern=~0U, const bool init_hatch=true) {
26865       typedef typename cimg::superset<tz,float>::type tzfloat;
26866       if (!is_sameXY(zbuffer))
26867         throw CImgArgumentException(_cimg_instance
26868                                     "draw_line() : Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
26869                                     cimg_instance,
26870                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
26871 
26872       if (texture._depth>1 || texture._spectrum<_spectrum)
26873         throw CImgArgumentException(_cimg_instance
26874                                     "draw_line() : Invalid specified texture (%u,%u,%u,%u,%p).",
26875                                     cimg_instance,
26876                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
26877 
26878       if (is_empty() || z0<=0 || z1<=0) return *this;
26879       if (is_overlapped(texture)) return draw_line(zbuffer,x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
26880       static unsigned int hatch = ~0U - (~0U>>1);
26881       if (init_hatch) hatch = ~0U - (~0U>>1);
26882       const bool xdir = x0<x1, ydir = y0<y1;
26883       int
26884         nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
26885         &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
26886         &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
26887         &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
26888         &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
26889       float
26890         Tx0 = tx0/z0, Tx1 = tx1/z1,
26891         Ty0 = ty0/z0, Ty1 = ty1/z1,
26892         dtx = Tx1 - Tx0, dty = Ty1 - Ty0,
26893         tnx0 = Tx0, tnx1 = Tx1, tny0 = Ty0, tny1 = Ty1,
26894         &txleft = xdir?tnx0:tnx1, &tyleft = xdir?tny0:tny1,
26895         &txright = xdir?tnx1:tnx0, &tyright = xdir?tny1:tny0,
26896         &txup = ydir?tnx0:tnx1, &tyup = ydir?tny0:tny1,
26897         &txdown = ydir?tnx1:tnx0, &tydown = ydir?tny1:tny0;
26898       tzfloat
26899         Z0 = 1/(tzfloat)z0, Z1 = 1/(tzfloat)z1,
26900         dz = Z1 - Z0,  nz0 = Z0, nz1 = Z1,
26901         &zleft = xdir?nz0:nz1,
26902         &zright = xdir?nz1:nz0,
26903         &zup = ydir?nz0:nz1,
26904         &zdown = ydir?nz1:nz0;
26905       if (xright<0 || xleft>=width()) return *this;
26906       if (xleft<0) {
26907         const float D = (float)xright - xleft;
26908         yleft-=(int)((float)xleft*((float)yright - yleft)/D);
26909         zleft-=(float)xleft*(zright - zleft)/D;
26910         txleft-=(float)xleft*(txright - txleft)/D;
26911         tyleft-=(float)xleft*(tyright - tyleft)/D;
26912         xleft = 0;
26913       }
26914       if (xright>=width()) {
26915         const float d = (float)xright - width(), D = (float)xright - xleft;
26916         yright-=(int)(d*((float)yright - yleft)/D);
26917         zright-=d*(zright - zleft)/D;
26918         txright-=d*(txright - txleft)/D;
26919         tyright-=d*(tyright - tyleft)/D;
26920         xright = width()-1;
26921       }
26922       if (ydown<0 || yup>=height()) return *this;
26923       if (yup<0) {
26924         const float D = (float)ydown - yup;
26925         xup-=(int)((float)yup*((float)xdown - xup)/D);
26926         zup-=yup*(zdown - zup)/D;
26927         txup-=yup*(txdown - txup)/D;
26928         tyup-=yup*(tydown - tyup)/D;
26929         yup = 0;
26930       }
26931       if (ydown>=height()) {
26932         const float d = (float)ydown - height(), D = (float)ydown - yup;
26933         xdown-=(int)(d*((float)xdown - xup)/D);
26934         zdown-=d*(zdown - zup)/D;
26935         txdown-=d*(txdown - txup)/D;
26936         tydown-=d*(tydown - tyup)/D;
26937         ydown = height()-1;
26938       }
26939       T *ptrd0 = data(nx0,ny0);
26940       tz *ptrz = zbuffer.data(nx0,ny0);
26941       int dx = xright - xleft, dy = ydown - yup;
26942       const bool steep = dy>dx;
26943       if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
26944       const int
26945         offx = (nx0<nx1?1:-1)*(steep?_width:1),
26946         offy = (ny0<ny1?1:-1)*(steep?1:_width),
26947         wh = _width*_height,
26948         ndx = dx>0?dx:1;
26949       if (opacity>=1) {
26950         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
26951           if (pattern&hatch) {
26952             const tzfloat z = Z0 + x*dz/ndx;
26953             if (z>=(tzfloat)*ptrz) {
26954               *ptrz = (tz)z;
26955               const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
26956               T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); ptrd+=wh; }
26957             }
26958           }
26959           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
26960           ptrd0+=offx; ptrz+=offx;
26961           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
26962         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
26963           const tzfloat z = Z0 + x*dz/ndx;
26964           if (z>=(tzfloat)*ptrz) {
26965             *ptrz = (tz)z;
26966             const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
26967             T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); ptrd+=wh; }
26968           }
26969           ptrd0+=offx; ptrz+=offx;
26970           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
26971         }
26972       } else {
26973         const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
26974         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
26975           if (pattern&hatch) {
26976             const tzfloat z = Z0 + x*dz/ndx;
26977             if (z>=(tzfloat)*ptrz) {
26978               *ptrz = (tz)z;
26979               const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
26980               T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,c) + *ptrd*copacity); ptrd+=wh; }
26981             }
26982           }
26983           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
26984           ptrd0+=offx; ptrz+=offx;
26985           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
26986         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
26987           const tzfloat z = Z0 + x*dz/ndx;
26988           if (z>=(tzfloat)*ptrz) {
26989             *ptrz = (tz)z;
26990             const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
26991             T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,c) + *ptrd*copacity); ptrd+=wh; }
26992           }
26993           ptrd0+=offx; ptrz+=offx;
26994           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offx; error+=dx; }
26995         }
26996       }
26997       return *this;
26998     }
26999 
27000     //! Draw a set of consecutive colored lines in the image instance.
27001     /**
27002        \param points Coordinates of vertices, stored as a list of vectors.
27003        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
27004        \param opacity Drawing opacity (optional).
27005        \param pattern An integer whose bits describe the line pattern (optional).
27006        \param init_hatch If set to true, init hatch motif.
27007        \note
27008        - This function uses several call to the single CImg::draw_line() procedure,
27009        depending on the vectors size in \p points.
27010        \par Example:
27011        \code
27012        CImg<unsigned char> img(100,100,1,3,0);
27013        const unsigned char color[] = { 255,128,64 };
27014        CImgList<int> points;
27015        points.insert(CImg<int>::vector(0,0)).
27016              .insert(CImg<int>::vector(70,10)).
27017              .insert(CImg<int>::vector(80,60)).
27018              .insert(CImg<int>::vector(10,90));
27019        img.draw_line(points,color);
27020        \endcode
27021     **/
27022     template<typename t, typename tc>
27023     CImg<T>& draw_line(const CImg<t>& points,
27024                        const tc *const color, const float opacity=1,
27025                        const unsigned int pattern=~0U, const bool init_hatch=true) {
27026       if (is_empty() || !points || points._width<2) return *this;
27027       bool ninit_hatch = init_hatch;
27028       switch (points._height) {
27029       case 0 : case 1 :
27030         throw CImgArgumentException(_cimg_instance
27031                                     "draw_line() : Invalid specified point set (%u,%u,%u,%u,%p).",
27032                                     cimg_instance,
27033                                     points._width,points._height,points._depth,points._spectrum,points._data);
27034 
27035       case 2 : {
27036         const int x0 = (int)points(0,0), y0 = (int)points(0,1);
27037         int ox = x0, oy = y0;
27038         for (unsigned int i = 1; i<points._width; ++i) {
27039           const int x = (int)points(i,0), y = (int)points(i,1);
27040           draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch);
27041           ninit_hatch = false;
27042           ox = x; oy = y;
27043         }
27044       } break;
27045       default : {
27046         const int x0 = (int)points(0,0), y0 = (int)points(0,1), z0 = (int)points(0,2);
27047         int ox = x0, oy = y0, oz = z0;
27048         for (unsigned int i = 1; i<points._width; ++i) {
27049           const int x = (int)points(i,0), y = (int)points(i,1), z = (int)points(i,2);
27050           draw_line(ox,oy,oz,x,y,z,color,opacity,pattern,ninit_hatch);
27051           ninit_hatch = false;
27052           ox = x; oy = y; oz = z;
27053         }
27054       }
27055       }
27056       return *this;
27057     }
27058 
27059     //! Draw a colored arrow in the image instance.
27060     /**
27061        \param x0 X-coordinate of the starting arrow point (tail).
27062        \param y0 Y-coordinate of the starting arrow point (tail).
27063        \param x1 X-coordinate of the ending arrow point (head).
27064        \param y1 Y-coordinate of the ending arrow point (head).
27065        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
27066        \param angle Aperture angle of the arrow head (optional).
27067        \param length Length of the arrow head. If negative, describes a percentage of the arrow length (optional).
27068        \param opacity Drawing opacity (optional).
27069        \param pattern An integer whose bits describe the line pattern (optional).
27070        \note
27071        - Clipping is supported.
27072     **/
27073     template<typename tc>
27074     CImg<T>& draw_arrow(const int x0, const int y0,
27075                         const int x1, const int y1,
27076                         const tc *const color, const float opacity=1,
27077                         const float angle=30, const float length=-10,
27078                         const unsigned int pattern=~0U) {
27079       if (is_empty()) return *this;
27080       const float u = (float)(x0 - x1), v = (float)(y0 - y1), sq = u*u + v*v,
27081         deg = (float)(angle*cimg::PI/180), ang = (sq>0)?(float)std::atan2(v,u):0.0f,
27082         l = (length>=0)?length:-length*(float)std::sqrt(sq)/100;
27083       if (sq>0) {
27084         const float
27085             cl = (float)std::cos(ang - deg), sl = (float)std::sin(ang - deg),
27086             cr = (float)std::cos(ang + deg), sr = (float)std::sin(ang + deg);
27087         const int
27088           xl = x1 + (int)(l*cl), yl = y1 + (int)(l*sl),
27089           xr = x1 + (int)(l*cr), yr = y1 + (int)(l*sr),
27090           xc = x1 + (int)((l+1)*(cl+cr))/2, yc = y1 + (int)((l+1)*(sl+sr))/2;
27091         draw_line(x0,y0,xc,yc,color,opacity,pattern).draw_triangle(x1,y1,xl,yl,xr,yr,color,opacity);
27092       } else draw_point(x0,y0,color,opacity);
27093       return *this;
27094     }
27095 
27096     //! Draw a cubic spline curve in the image instance.
27097     /**
27098        \param x0 X-coordinate of the starting curve point
27099        \param y0 Y-coordinate of the starting curve point
27100        \param u0 X-coordinate of the starting velocity
27101        \param v0 Y-coordinate of the starting velocity
27102        \param x1 X-coordinate of the ending curve point
27103        \param y1 Y-coordinate of the ending curve point
27104        \param u1 X-coordinate of the ending velocity
27105        \param v1 Y-coordinate of the ending velocity
27106        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
27107        \param precision Curve drawing precision (optional).
27108        \param opacity Drawing opacity (optional).
27109        \param pattern An integer whose bits describe the line pattern (optional).
27110        \param init_hatch If \c true, init hatch motif.
27111        \note
27112        - The curve is a 2d cubic Bezier spline, from the set of specified starting/ending points
27113        and corresponding velocity vectors.
27114        - The spline is drawn as a serie of connected segments. The \p precision parameter sets the
27115        average number of pixels in each drawn segment.
27116        - 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) }
27117        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
27118        \e control points.
27119        The starting and ending velocities (\p u0,\p v0) and (\p u1,\p v1) can be deduced easily from the control points as
27120        \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).
27121        \par Example:
27122        \code
27123        CImg<unsigned char> img(100,100,1,3,0);
27124        const unsigned char color[] = { 255,255,255 };
27125        img.draw_spline(30,30,0,100,90,40,0,-100,color);
27126        \endcode
27127     **/
27128     template<typename tc>
27129     CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0,
27130                          const int x1, const int y1, const float u1, const float v1,
27131                          const tc *const color, const float opacity=1,
27132                          const float precision=0.25, const unsigned int pattern=~0U,
27133                          const bool init_hatch=true) {
27134       if (!color)
27135         throw CImgArgumentException(_cimg_instance
27136                                     "draw_spline() : Specified color is (null).",
27137                                     cimg_instance);
27138       if (is_empty()) return *this;
27139       if (x0==x1 && y0==y1) return draw_point(x0,y0,color,opacity);
27140       bool ninit_hatch = init_hatch;
27141       const float
27142         ax = u0 + u1 + 2*(x0 - x1),
27143         bx = 3*(x1 - x0) - 2*u0 - u1,
27144         ay = v0 + v1 + 2*(y0 - y1),
27145         by = 3*(y1 - y0) - 2*v0 - v1,
27146         _precision = 1/(std::sqrt(cimg::sqr((float)x0-x1)+cimg::sqr((float)y0-y1))*(precision>0?precision:1));
27147       int ox = x0, oy = y0;
27148       for (float t = 0; t<1; t+=_precision) {
27149         const float t2 = t*t, t3 = t2*t;
27150         const int
27151           nx = (int)(ax*t3 + bx*t2 + u0*t + x0),
27152           ny = (int)(ay*t3 + by*t2 + v0*t + y0);
27153         draw_line(ox,oy,nx,ny,color,opacity,pattern,ninit_hatch);
27154         ninit_hatch = false;
27155         ox = nx; oy = ny;
27156       }
27157       return draw_line(ox,oy,x1,y1,color,opacity,pattern,false);
27158     }
27159 
27160     //! Draw a cubic spline curve in the image instance (for volumetric images).
27161     /**
27162        \note
27163        - Similar to CImg::draw_spline() for a 3d spline in a volumetric image.
27164     **/
27165     template<typename tc>
27166     CImg<T>& draw_spline(const int x0, const int y0, const int z0, const float u0, const float v0, const float w0,
27167                          const int x1, const int y1, const int z1, const float u1, const float v1, const float w1,
27168                          const tc *const color, const float opacity=1,
27169                          const float precision=4, const unsigned int pattern=~0U,
27170                          const bool init_hatch=true) {
27171       if (!color)
27172         throw CImgArgumentException(_cimg_instance
27173                                     "draw_spline() : Specified color is (null).",
27174                                     cimg_instance);
27175       if (is_empty()) return *this;
27176       if (x0==x1 && y0==y1 && z0==z1) return draw_point(x0,y0,z0,color,opacity);
27177       bool ninit_hatch = init_hatch;
27178       const float
27179         ax = u0 + u1 + 2*(x0 - x1),
27180         bx = 3*(x1 - x0) - 2*u0 - u1,
27181         ay = v0 + v1 + 2*(y0 - y1),
27182         by = 3*(y1 - y0) - 2*v0 - v1,
27183         az = w0 + w1 + 2*(z0 - z1),
27184         bz = 3*(z1 - z0) - 2*w0 - w1,
27185         _precision = 1/(std::sqrt(cimg::sqr(x0-x1)+cimg::sqr(y0-y1))*(precision>0?precision:1));
27186       int ox = x0, oy = y0, oz = z0;
27187       for (float t = 0; t<1; t+=_precision) {
27188         const float t2 = t*t, t3 = t2*t;
27189         const int
27190           nx = (int)(ax*t3 + bx*t2 + u0*t + x0),
27191           ny = (int)(ay*t3 + by*t2 + v0*t + y0),
27192           nz = (int)(az*t3 + bz*t2 + w0*t + z0);
27193         draw_line(ox,oy,oz,nx,ny,nz,color,opacity,pattern,ninit_hatch);
27194         ninit_hatch = false;
27195         ox = nx; oy = ny; oz = nz;
27196       }
27197       return draw_line(ox,oy,oz,x1,y1,z1,color,opacity,pattern,false);
27198     }
27199 
27200     //! Draw a cubic spline curve in the image instance.
27201     /**
27202        \param x0 X-coordinate of the starting curve point
27203        \param y0 Y-coordinate of the starting curve point
27204        \param u0 X-coordinate of the starting velocity
27205        \param v0 Y-coordinate of the starting velocity
27206        \param x1 X-coordinate of the ending curve point
27207        \param y1 Y-coordinate of the ending curve point
27208        \param u1 X-coordinate of the ending velocity
27209        \param v1 Y-coordinate of the ending velocity
27210        \param texture Texture image defining line pixel colors.
27211        \param tx0 X-coordinate of the starting texture point.
27212        \param ty0 Y-coordinate of the starting texture point.
27213        \param tx1 X-coordinate of the ending texture point.
27214        \param ty1 Y-coordinate of the ending texture point.
27215        \param precision Curve drawing precision (optional).
27216        \param opacity Drawing opacity (optional).
27217        \param pattern An integer whose bits describe the line pattern (optional).
27218        \param init_hatch if \c true, reinit hatch motif.
27219     **/
27220     template<typename tt>
27221     CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0,
27222                          const int x1, const int y1, const float u1, const float v1,
27223                          const CImg<tt>& texture,
27224                          const int tx0, const int ty0, const int tx1, const int ty1,
27225                          const float opacity=1,
27226                          const float precision=4, const unsigned int pattern=~0U,
27227                          const bool init_hatch=true) {
27228       if (texture._depth>1 || texture._spectrum<_spectrum)
27229         throw CImgArgumentException(_cimg_instance
27230                                     "draw_line() : Invalid specified texture (%u,%u,%u,%u,%p).",
27231                                     cimg_instance,
27232                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
27233       if (is_empty()) return *this;
27234       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);
27235       if (x0==x1 && y0==y1) return draw_point(x0,y0,texture.get_vector_at(x0,y0),opacity);
27236       bool ninit_hatch = init_hatch;
27237       const float
27238         ax = u0 + u1 + 2*(x0 - x1),
27239         bx = 3*(x1 - x0) - 2*u0 - u1,
27240         ay = v0 + v1 + 2*(y0 - y1),
27241         by = 3*(y1 - y0) - 2*v0 - v1,
27242         _precision = 1/(std::sqrt(cimg::sqr(x0-x1)+cimg::sqr(y0-y1))*(precision>0?precision:1));
27243       int ox = x0, oy = y0, otx = tx0, oty = ty0;
27244       for (float t = 0; t<1; t+=_precision) {
27245         const float t2 = t*t, t3 = t2*t;
27246         const int
27247           nx = (int)(ax*t3 + bx*t2 + u0*t + x0),
27248           ny = (int)(ay*t3 + by*t2 + v0*t + y0),
27249           ntx = tx0 + (int)((tx1-tx0)*t),
27250           nty = ty0 + (int)((ty1-ty0)*t);
27251         draw_line(ox,oy,nx,ny,texture,otx,oty,ntx,nty,opacity,pattern,ninit_hatch);
27252         ninit_hatch = false;
27253         ox = nx; oy = ny; otx = ntx; oty = nty;
27254       }
27255       return draw_line(ox,oy,x1,y1,texture,otx,oty,tx1,ty1,opacity,pattern,false);
27256     }
27257 
27258     // Draw a set of connected spline curves in the image instance (internal).
27259     template<typename tp, typename tt, typename tc>
27260     CImg<T>& draw_spline(const CImg<tp>& points, const CImg<tt>& tangents,
27261                          const tc *const color, const float opacity=1,
27262                          const bool close_set=false, const float precision=4,
27263                          const unsigned int pattern=~0U, const bool init_hatch=true) {
27264       if (is_empty() || !points || !tangents || points._width<2 || tangents._width<2) return *this;
27265       bool ninit_hatch = init_hatch;
27266       switch (points._height) {
27267       case 0 : case 1 :
27268         throw CImgArgumentException(_cimg_instance
27269                                     "draw_spline() : Invalid specified point set (%u,%u,%u,%u,%p).",
27270                                     cimg_instance,
27271                                     points._width,points._height,points._depth,points._spectrum,points._data);
27272 
27273       case 2 : {
27274         const int x0 = (int)points(0,0), y0 = (int)points(0,1);
27275         const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1);
27276         int ox = x0, oy = y0;
27277         float ou = u0, ov = v0;
27278         for (unsigned int i = 1; i<points._width; ++i) {
27279           const int x = (int)points(i,0), y = (int)points(i,1);
27280           const float u = (float)tangents(i,0), v = (float)tangents(i,1);
27281           draw_spline(ox,oy,ou,ov,x,y,u,v,color,precision,opacity,pattern,ninit_hatch);
27282           ninit_hatch = false;
27283           ox = x; oy = y; ou = u; ov = v;
27284         }
27285         if (close_set) draw_spline(ox,oy,ou,ov,x0,y0,u0,v0,color,precision,opacity,pattern,false);
27286       } break;
27287       default : {
27288         const int x0 = (int)points(0,0), y0 = (int)points(0,1), z0 = (int)points(0,2);
27289         const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1), w0 = (float)tangents(0,2);
27290         int ox = x0, oy = y0, oz = z0;
27291         float ou = u0, ov = v0, ow = w0;
27292         for (unsigned int i = 1; i<points._width; ++i) {
27293           const int x = (int)points(i,0), y = (int)points(i,1), z = (int)points(i,2);
27294           const float u = (float)tangents(i,0), v = (float)tangents(i,1), w = (float)tangents(i,2);
27295           draw_spline(ox,oy,oz,ou,ov,ow,x,y,z,u,v,w,color,opacity,pattern,ninit_hatch);
27296           ninit_hatch = false;
27297           ox = x; oy = y; oz = z; ou = u; ov = v; ow = w;
27298         }
27299         if (close_set) draw_spline(ox,oy,oz,ou,ov,ow,x0,y0,z0,u0,v0,w0,color,precision,opacity,pattern,false);
27300       }
27301       }
27302       return *this;
27303     }
27304 
27305     //! Draw a set of consecutive colored splines in the image instance.
27306     template<typename tp, typename tc>
27307     CImg<T>& draw_spline(const CImg<tp>& points,
27308                          const tc *const color, const float opacity=1,
27309                          const bool close_set=false, const float precision=4,
27310                          const unsigned int pattern=~0U, const bool init_hatch=true) {
27311       if (is_empty() || !points || points._width<2) return *this;
27312       CImg<Tfloat> tangents;
27313       switch (points._height) {
27314       case 0 : case 1 :
27315         throw CImgArgumentException(_cimg_instance
27316                                     "draw_spline() : Invalid specified point set (%u,%u,%u,%u,%p).",
27317                                     cimg_instance,
27318                                     points._width,points._height,points._depth,points._spectrum,points._data);
27319       case 2 : {
27320         tangents.assign(points._width,points._height);
27321         cimg_forX(points,p) {
27322           const unsigned int
27323             p0 = close_set?(p+points._width-1)%points._width:(p?p-1:0),
27324             p1 = close_set?(p+1)%points._width:(p+1<points._width?p+1:p);
27325           const float
27326             x = (float)points(p,0),
27327             y = (float)points(p,1),
27328             x0 = (float)points(p0,0),
27329             y0 = (float)points(p0,1),
27330             x1 = (float)points(p1,0),
27331             y1 = (float)points(p1,1),
27332             u0 = x - x0,
27333             v0 = y - y0,
27334             n0 = 1e-8f + (float)std::sqrt(u0*u0 + v0*v0),
27335             u1 = x1 - x,
27336             v1 = y1 - y,
27337             n1 = 1e-8f + (float)std::sqrt(u1*u1 + v1*v1),
27338             u = u0/n0 + u1/n1,
27339             v = v0/n0 + v1/n1,
27340             n = 1e-8f + (float)std::sqrt(u*u + v*v),
27341             fact = 0.5f*(n0 + n1);
27342           tangents(p,0) = (Tfloat)(fact*u/n);
27343           tangents(p,1) = (Tfloat)(fact*v/n);
27344         }
27345       } break;
27346       default : {
27347         tangents.assign(points._width,points._height);
27348         cimg_forX(points,p) {
27349           const unsigned int
27350             p0 = close_set?(p+points._width-1)%points._width:(p?p-1:0),
27351             p1 = close_set?(p+1)%points._width:(p+1<points._width?p+1:p);
27352           const float
27353             x = (float)points(p,0),
27354             y = (float)points(p,1),
27355             z = (float)points(p,2),
27356             x0 = (float)points(p0,0),
27357             y0 = (float)points(p0,1),
27358             z0 = (float)points(p0,2),
27359             x1 = (float)points(p1,0),
27360             y1 = (float)points(p1,1),
27361             z1 = (float)points(p1,2),
27362             u0 = x - x0,
27363             v0 = y - y0,
27364             w0 = z - z0,
27365             n0 = 1e-8f + (float)std::sqrt(u0*u0 + v0*v0 + w0*w0),
27366             u1 = x1 - x,
27367             v1 = y1 - y,
27368             w1 = z1 - z,
27369             n1 = 1e-8f + (float)std::sqrt(u1*u1 + v1*v1 + w1*w1),
27370             u = u0/n0 + u1/n1,
27371             v = v0/n0 + v1/n1,
27372             w = w0/n0 + w1/n1,
27373             n = 1e-8f + (float)std::sqrt(u*u + v*v + w*w),
27374             fact = 0.5f*(n0 + n1);
27375           tangents(p,0) = (Tfloat)(fact*u/n);
27376           tangents(p,1) = (Tfloat)(fact*v/n);
27377           tangents(p,2) = (Tfloat)(fact*w/n);
27378         }
27379       }
27380       }
27381       return draw_spline(points,tangents,color,opacity,close_set,precision,pattern,init_hatch);
27382     }
27383 
27384     // Inner macro for drawing triangles.
27385 #define _cimg_for_triangle1(img,xl,xr,y,x0,y0,x1,y1,x2,y2) \
27386         for (int y = y0<0?0:y0, \
27387                xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \
27388                xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \
27389                _sxn=1, \
27390                _sxr=1, \
27391                _sxl=1, \
27392                _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \
27393                _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \
27394                _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \
27395                _dyn = y2-y1, \
27396                _dyr = y2-y0, \
27397                _dyl = y1-y0, \
27398                _counter = (_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
27399                            _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
27400                            _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
27401                            cimg::min((int)(img)._height-y-1,y2-y)), \
27402                _errn = _dyn/2, \
27403                _errr = _dyr/2, \
27404                _errl = _dyl/2, \
27405                _rxn = _dyn?(x2-x1)/_dyn:0, \
27406                _rxr = _dyr?(x2-x0)/_dyr:0, \
27407                _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
27408                                        (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn); \
27409              _counter>=0; --_counter, ++y, \
27410                xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
27411                xl+=(y!=y1)?_rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0): \
27412                            (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
27413 
27414 #define _cimg_for_triangle2(img,xl,cl,xr,cr,y,x0,y0,c0,x1,y1,c1,x2,y2,c2) \
27415         for (int y = y0<0?0:y0, \
27416                xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \
27417                cr = y0>=0?c0:(c0-y0*(c2-c0)/(y2-y0)), \
27418                xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \
27419                cl = y1>=0?(y0>=0?(y0==y1?c1:c0):(c0-y0*(c1-c0)/(y1-y0))):(c1-y1*(c2-c1)/(y2-y1)), \
27420                _sxn=1, _scn=1, \
27421                _sxr=1, _scr=1, \
27422                _sxl=1, _scl=1, \
27423                _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \
27424                _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \
27425                _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \
27426                _dcn = c2>c1?c2-c1:(_scn=-1,c1-c2), \
27427                _dcr = c2>c0?c2-c0:(_scr=-1,c0-c2), \
27428                _dcl = c1>c0?c1-c0:(_scl=-1,c0-c1), \
27429                _dyn = y2-y1, \
27430                _dyr = y2-y0, \
27431                _dyl = y1-y0, \
27432                _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
27433                           _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
27434                           _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
27435                           _dcn-=_dyn?_dyn*(_dcn/_dyn):0, \
27436                           _dcr-=_dyr?_dyr*(_dcr/_dyr):0, \
27437                           _dcl-=_dyl?_dyl*(_dcl/_dyl):0, \
27438                           cimg::min((int)(img)._height-y-1,y2-y)), \
27439                _errn = _dyn/2, _errcn = _errn, \
27440                _errr = _dyr/2, _errcr = _errr, \
27441                _errl = _dyl/2, _errcl = _errl, \
27442                _rxn = _dyn?(x2-x1)/_dyn:0, \
27443                _rcn = _dyn?(c2-c1)/_dyn:0, \
27444                _rxr = _dyr?(x2-x0)/_dyr:0, \
27445                _rcr = _dyr?(c2-c0)/_dyr:0, \
27446                _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
27447                                        (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
27448                _rcl = (y0!=y1 && y1>0)?(_dyl?(c1-c0)/_dyl:0): \
27449                                        (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcn ); \
27450              _counter>=0; --_counter, ++y, \
27451                xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
27452                cr+=_rcr+((_errcr-=_dcr)<0?_errcr+=_dyr,_scr:0), \
27453                xl+=(y!=y1)?(cl+=_rcl+((_errcl-=_dcl)<0?(_errcl+=_dyl,_scl):0), \
27454                            _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
27455                (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcl=_rcn, cl=c1, \
27456                 _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
27457 
27458 #define _cimg_for_triangle3(img,xl,txl,tyl,xr,txr,tyr,y,x0,y0,tx0,ty0,x1,y1,tx1,ty1,x2,y2,tx2,ty2) \
27459         for (int y = y0<0?0:y0, \
27460                xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \
27461                txr = y0>=0?tx0:(tx0-y0*(tx2-tx0)/(y2-y0)), \
27462                tyr = y0>=0?ty0:(ty0-y0*(ty2-ty0)/(y2-y0)), \
27463                xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \
27464                txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0-y0*(tx1-tx0)/(y1-y0))):(tx1-y1*(tx2-tx1)/(y2-y1)), \
27465                tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0-y0*(ty1-ty0)/(y1-y0))):(ty1-y1*(ty2-ty1)/(y2-y1)), \
27466                _sxn=1, _stxn=1, _styn=1, \
27467                _sxr=1, _stxr=1, _styr=1, \
27468                _sxl=1, _stxl=1, _styl=1, \
27469                _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \
27470                _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \
27471                _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \
27472                _dtxn = tx2>tx1?tx2-tx1:(_stxn=-1,tx1-tx2), \
27473                _dtxr = tx2>tx0?tx2-tx0:(_stxr=-1,tx0-tx2), \
27474                _dtxl = tx1>tx0?tx1-tx0:(_stxl=-1,tx0-tx1), \
27475                _dtyn = ty2>ty1?ty2-ty1:(_styn=-1,ty1-ty2), \
27476                _dtyr = ty2>ty0?ty2-ty0:(_styr=-1,ty0-ty2), \
27477                _dtyl = ty1>ty0?ty1-ty0:(_styl=-1,ty0-ty1), \
27478                _dyn = y2-y1, \
27479                _dyr = y2-y0, \
27480                _dyl = y1-y0, \
27481                _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
27482                           _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
27483                           _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
27484                           _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \
27485                           _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \
27486                           _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \
27487                           _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \
27488                           _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \
27489                           _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \
27490                           cimg::min((int)(img)._height-y-1,y2-y)), \
27491                _errn = _dyn/2, _errtxn = _errn, _errtyn = _errn, \
27492                _errr = _dyr/2, _errtxr = _errr, _errtyr = _errr, \
27493                _errl = _dyl/2, _errtxl = _errl, _errtyl = _errl, \
27494                _rxn = _dyn?(x2-x1)/_dyn:0, \
27495                _rtxn = _dyn?(tx2-tx1)/_dyn:0, \
27496                _rtyn = _dyn?(ty2-ty1)/_dyn:0, \
27497                _rxr = _dyr?(x2-x0)/_dyr:0, \
27498                _rtxr = _dyr?(tx2-tx0)/_dyr:0, \
27499                _rtyr = _dyr?(ty2-ty0)/_dyr:0, \
27500                _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
27501                                        (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
27502                _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1-tx0)/_dyl:0): \
27503                                        (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \
27504                _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1-ty0)/_dyl:0): \
27505                                        (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ); \
27506              _counter>=0; --_counter, ++y, \
27507                xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
27508                txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \
27509                tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \
27510                xl+=(y!=y1)?(txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \
27511                             tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \
27512                            _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
27513                (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \
27514                 _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1,\
27515                 _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
27516 
27517 #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) \
27518         for (int y = y0<0?0:y0, \
27519                xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \
27520                cr = y0>=0?c0:(c0-y0*(c2-c0)/(y2-y0)), \
27521                txr = y0>=0?tx0:(tx0-y0*(tx2-tx0)/(y2-y0)), \
27522                tyr = y0>=0?ty0:(ty0-y0*(ty2-ty0)/(y2-y0)), \
27523                xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \
27524                cl = y1>=0?(y0>=0?(y0==y1?c1:c0):(c0-y0*(c1-c0)/(y1-y0))):(c1-y1*(c2-c1)/(y2-y1)), \
27525                txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0-y0*(tx1-tx0)/(y1-y0))):(tx1-y1*(tx2-tx1)/(y2-y1)), \
27526                tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0-y0*(ty1-ty0)/(y1-y0))):(ty1-y1*(ty2-ty1)/(y2-y1)), \
27527                _sxn=1, _scn=1, _stxn=1, _styn=1, \
27528                _sxr=1, _scr=1, _stxr=1, _styr=1, \
27529                _sxl=1, _scl=1, _stxl=1, _styl=1, \
27530                _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \
27531                _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \
27532                _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \
27533                _dcn = c2>c1?c2-c1:(_scn=-1,c1-c2), \
27534                _dcr = c2>c0?c2-c0:(_scr=-1,c0-c2), \
27535                _dcl = c1>c0?c1-c0:(_scl=-1,c0-c1), \
27536                _dtxn = tx2>tx1?tx2-tx1:(_stxn=-1,tx1-tx2), \
27537                _dtxr = tx2>tx0?tx2-tx0:(_stxr=-1,tx0-tx2), \
27538                _dtxl = tx1>tx0?tx1-tx0:(_stxl=-1,tx0-tx1), \
27539                _dtyn = ty2>ty1?ty2-ty1:(_styn=-1,ty1-ty2), \
27540                _dtyr = ty2>ty0?ty2-ty0:(_styr=-1,ty0-ty2), \
27541                _dtyl = ty1>ty0?ty1-ty0:(_styl=-1,ty0-ty1), \
27542                _dyn = y2-y1, \
27543                _dyr = y2-y0, \
27544                _dyl = y1-y0, \
27545                _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
27546                           _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
27547                           _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
27548                           _dcn-=_dyn?_dyn*(_dcn/_dyn):0, \
27549                           _dcr-=_dyr?_dyr*(_dcr/_dyr):0, \
27550                           _dcl-=_dyl?_dyl*(_dcl/_dyl):0, \
27551                           _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \
27552                           _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \
27553                           _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \
27554                           _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \
27555                           _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \
27556                           _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \
27557                           cimg::min((int)(img)._height-y-1,y2-y)), \
27558                _errn = _dyn/2, _errcn = _errn, _errtxn = _errn, _errtyn = _errn, \
27559                _errr = _dyr/2, _errcr = _errr, _errtxr = _errr, _errtyr = _errr, \
27560                _errl = _dyl/2, _errcl = _errl, _errtxl = _errl, _errtyl = _errl, \
27561                _rxn = _dyn?(x2-x1)/_dyn:0, \
27562                _rcn = _dyn?(c2-c1)/_dyn:0, \
27563                _rtxn = _dyn?(tx2-tx1)/_dyn:0, \
27564                _rtyn = _dyn?(ty2-ty1)/_dyn:0, \
27565                _rxr = _dyr?(x2-x0)/_dyr:0, \
27566                _rcr = _dyr?(c2-c0)/_dyr:0, \
27567                _rtxr = _dyr?(tx2-tx0)/_dyr:0, \
27568                _rtyr = _dyr?(ty2-ty0)/_dyr:0, \
27569                _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
27570                                        (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
27571                _rcl = (y0!=y1 && y1>0)?(_dyl?(c1-c0)/_dyl:0): \
27572                                        (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcn ), \
27573                _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1-tx0)/_dyl:0): \
27574                                         (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \
27575                _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1-ty0)/_dyl:0): \
27576                                         (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ); \
27577              _counter>=0; --_counter, ++y, \
27578                xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
27579                cr+=_rcr+((_errcr-=_dcr)<0?_errcr+=_dyr,_scr:0), \
27580                txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \
27581                tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \
27582                xl+=(y!=y1)?(cl+=_rcl+((_errcl-=_dcl)<0?(_errcl+=_dyl,_scl):0), \
27583                             txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \
27584                             tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \
27585                             _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
27586                (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcl=_rcn, cl=c1, \
27587                 _errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \
27588                 _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1, \
27589                 _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
27590 
27591 #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) \
27592         for (int y = y0<0?0:y0, \
27593                xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \
27594                txr = y0>=0?tx0:(tx0-y0*(tx2-tx0)/(y2-y0)), \
27595                tyr = y0>=0?ty0:(ty0-y0*(ty2-ty0)/(y2-y0)), \
27596                lxr = y0>=0?lx0:(lx0-y0*(lx2-lx0)/(y2-y0)), \
27597                lyr = y0>=0?ly0:(ly0-y0*(ly2-ly0)/(y2-y0)), \
27598                xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \
27599                txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0-y0*(tx1-tx0)/(y1-y0))):(tx1-y1*(tx2-tx1)/(y2-y1)), \
27600                tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0-y0*(ty1-ty0)/(y1-y0))):(ty1-y1*(ty2-ty1)/(y2-y1)), \
27601                lxl = y1>=0?(y0>=0?(y0==y1?lx1:lx0):(lx0-y0*(lx1-lx0)/(y1-y0))):(lx1-y1*(lx2-lx1)/(y2-y1)), \
27602                lyl = y1>=0?(y0>=0?(y0==y1?ly1:ly0):(ly0-y0*(ly1-ly0)/(y1-y0))):(ly1-y1*(ly2-ly1)/(y2-y1)), \
27603                _sxn=1, _stxn=1, _styn=1, _slxn=1, _slyn=1, \
27604                _sxr=1, _stxr=1, _styr=1, _slxr=1, _slyr=1, \
27605                _sxl=1, _stxl=1, _styl=1, _slxl=1, _slyl=1, \
27606                _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), _dyn = y2-y1, \
27607                _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), _dyr = y2-y0, \
27608                _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), _dyl = y1-y0, \
27609                _dtxn = tx2>tx1?tx2-tx1:(_stxn=-1,tx1-tx2), \
27610                _dtxr = tx2>tx0?tx2-tx0:(_stxr=-1,tx0-tx2), \
27611                _dtxl = tx1>tx0?tx1-tx0:(_stxl=-1,tx0-tx1), \
27612                _dtyn = ty2>ty1?ty2-ty1:(_styn=-1,ty1-ty2), \
27613                _dtyr = ty2>ty0?ty2-ty0:(_styr=-1,ty0-ty2), \
27614                _dtyl = ty1>ty0?ty1-ty0:(_styl=-1,ty0-ty1), \
27615                _dlxn = lx2>lx1?lx2-lx1:(_slxn=-1,lx1-lx2), \
27616                _dlxr = lx2>lx0?lx2-lx0:(_slxr=-1,lx0-lx2), \
27617                _dlxl = lx1>lx0?lx1-lx0:(_slxl=-1,lx0-lx1), \
27618                _dlyn = ly2>ly1?ly2-ly1:(_slyn=-1,ly1-ly2), \
27619                _dlyr = ly2>ly0?ly2-ly0:(_slyr=-1,ly0-ly2), \
27620                _dlyl = ly1>ly0?ly1-ly0:(_slyl=-1,ly0-ly1), \
27621                _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
27622                           _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
27623                           _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
27624                           _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \
27625                           _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \
27626                           _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \
27627                           _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \
27628                           _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \
27629                           _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \
27630                           _dlxn-=_dyn?_dyn*(_dlxn/_dyn):0, \
27631                           _dlxr-=_dyr?_dyr*(_dlxr/_dyr):0, \
27632                           _dlxl-=_dyl?_dyl*(_dlxl/_dyl):0, \
27633                           _dlyn-=_dyn?_dyn*(_dlyn/_dyn):0, \
27634                           _dlyr-=_dyr?_dyr*(_dlyr/_dyr):0, \
27635                           _dlyl-=_dyl?_dyl*(_dlyl/_dyl):0, \
27636                           cimg::min((int)(img)._height-y-1,y2-y)), \
27637                _errn = _dyn/2, _errtxn = _errn, _errtyn = _errn, _errlxn = _errn, _errlyn = _errn, \
27638                _errr = _dyr/2, _errtxr = _errr, _errtyr = _errr, _errlxr = _errr, _errlyr = _errr, \
27639                _errl = _dyl/2, _errtxl = _errl, _errtyl = _errl, _errlxl = _errl, _errlyl = _errl, \
27640                _rxn = _dyn?(x2-x1)/_dyn:0, \
27641                _rtxn = _dyn?(tx2-tx1)/_dyn:0, \
27642                _rtyn = _dyn?(ty2-ty1)/_dyn:0, \
27643                _rlxn = _dyn?(lx2-lx1)/_dyn:0, \
27644                _rlyn = _dyn?(ly2-ly1)/_dyn:0, \
27645                _rxr = _dyr?(x2-x0)/_dyr:0, \
27646                _rtxr = _dyr?(tx2-tx0)/_dyr:0, \
27647                _rtyr = _dyr?(ty2-ty0)/_dyr:0, \
27648                _rlxr = _dyr?(lx2-lx0)/_dyr:0, \
27649                _rlyr = _dyr?(ly2-ly0)/_dyr:0, \
27650                _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
27651                                        (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
27652                _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1-tx0)/_dyl:0): \
27653                                         (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \
27654                _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1-ty0)/_dyl:0): \
27655                                         (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ), \
27656                _rlxl = (y0!=y1 && y1>0)?(_dyl?(lx1-lx0)/_dyl:0): \
27657                                         (_errlxl=_errlxn, _dlxl=_dlxn, _dyl=_dyn, _slxl=_slxn, _rlxn ), \
27658                _rlyl = (y0!=y1 && y1>0)?(_dyl?(ly1-ly0)/_dyl:0): \
27659                                         (_errlyl=_errlyn, _dlyl=_dlyn, _dyl=_dyn, _slyl=_slyn, _rlyn ); \
27660              _counter>=0; --_counter, ++y, \
27661                xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
27662                txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \
27663                tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \
27664                lxr+=_rlxr+((_errlxr-=_dlxr)<0?_errlxr+=_dyr,_slxr:0), \
27665                lyr+=_rlyr+((_errlyr-=_dlyr)<0?_errlyr+=_dyr,_slyr:0), \
27666                xl+=(y!=y1)?(txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \
27667                             tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \
27668                             lxl+=_rlxl+((_errlxl-=_dlxl)<0?(_errlxl+=_dyl,_slxl):0), \
27669                             lyl+=_rlyl+((_errlyl-=_dlyl)<0?(_errlyl+=_dyl,_slyl):0), \
27670                             _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
27671                (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \
27672                 _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1, \
27673                 _errlxl=_errlxn, _dlxl=_dlxn, _dyl=_dyn, _slxl=_slxn, _rlxl=_rlxn, lxl=lx1, \
27674                 _errlyl=_errlyn, _dlyl=_dlyn, _dyl=_dyn, _slyl=_slyn, _rlyl=_rlyn, lyl=ly1, \
27675                 _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
27676 
27677     // Draw a colored triangle (inner routine, uses bresenham's algorithm).
27678     template<typename tc>
27679     CImg<T>& _draw_triangle(const int x0, const int y0,
27680                             const int x1, const int y1,
27681                             const int x2, const int y2,
27682                             const tc *const color, const float opacity,
27683                             const float brightness) {
27684       _draw_scanline(color,opacity);
27685       const float nbrightness = brightness<0?0:(brightness>2?2:brightness);
27686       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
27687       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1);
27688       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2);
27689       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2);
27690       if (ny0<height() && ny2>=0) {
27691         if ((nx1 - nx0)*(ny2 - ny0) - (nx2 - nx0)*(ny1 - ny0)<0)
27692           _cimg_for_triangle1(*this,xl,xr,y,nx0,ny0,nx1,ny1,nx2,ny2) _draw_scanline(xl,xr,y,color,opacity,nbrightness);
27693         else
27694           _cimg_for_triangle1(*this,xl,xr,y,nx0,ny0,nx1,ny1,nx2,ny2) _draw_scanline(xr,xl,y,color,opacity,nbrightness);
27695       }
27696       return *this;
27697     }
27698 
27699     //! Draw a 2d filled colored triangle.
27700     template<typename tc>
27701     CImg<T>& draw_triangle(const int x0, const int y0,
27702                            const int x1, const int y1,
27703                            const int x2, const int y2,
27704                            const tc *const color, const float opacity=1) {
27705       if (!color)
27706         throw CImgArgumentException(_cimg_instance
27707                                     "draw_triangle : Specified color is (null).",
27708                                     cimg_instance);
27709 
27710       if (is_empty()) return *this;
27711       _draw_triangle(x0,y0,x1,y1,x2,y2,color,opacity,1);
27712       return *this;
27713     }
27714 
27715     //! Draw a 2d outlined colored triangle.
27716     template<typename tc>
27717     CImg<T>& draw_triangle(const int x0, const int y0,
27718                            const int x1, const int y1,
27719                            const int x2, const int y2,
27720                            const tc *const color, const float opacity,
27721                            const unsigned int pattern) {
27722       if (!color)
27723         throw CImgArgumentException(_cimg_instance
27724                                     "draw_triangle : Specified color is (null).",
27725                                     cimg_instance);
27726 
27727       if (is_empty()) return *this;
27728       draw_line(x0,y0,x1,y1,color,opacity,pattern,true).
27729         draw_line(x1,y1,x2,y2,color,opacity,pattern,false).
27730         draw_line(x2,y2,x0,y0,color,opacity,pattern,false);
27731       return *this;
27732     }
27733 
27734     //! Draw a 2d filled colored triangle, with z-buffering.
27735     template<typename tz, typename tc>
27736     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
27737                            const int x0, const int y0, const float z0,
27738                            const int x1, const int y1, const float z1,
27739                            const int x2, const int y2, const float z2,
27740                            const tc *const color, const float opacity=1,
27741                            const float brightness=1) {
27742       typedef typename cimg::superset<tz,float>::type tzfloat;
27743       if (!color)
27744         throw CImgArgumentException(_cimg_instance
27745                                     "draw_triangle() : Specified color is (null).",
27746                                     cimg_instance);
27747       if (!is_sameXY(zbuffer))
27748         throw CImgArgumentException(_cimg_instance
27749                                     "draw_line() : Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
27750                                     cimg_instance,
27751                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
27752 
27753       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
27754       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
27755       const float
27756         nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0),
27757         nbrightness = brightness<0?0:(brightness>2?2:brightness);
27758       const int whd = _width*_height*_depth, offx = _spectrum*whd;
27759       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
27760       tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
27761       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
27762       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nz0,nz2);
27763       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nz1,nz2);
27764       if (ny0>=height() || ny2<0) return *this;
27765       tzfloat
27766         pzl = (nz1 - nz0)/(ny1 - ny0),
27767         pzr = (nz2 - nz0)/(ny2 - ny0),
27768         pzn = (nz2 - nz1)/(ny2 - ny1),
27769         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
27770         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
27771       _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) {
27772         if (y==ny1) { zl = nz1; pzl = pzn; }
27773         int xleft = xleft0, xright = xright0;
27774         tzfloat zleft = zl, zright = zr;
27775         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright);
27776         const int dx = xright - xleft;
27777         const tzfloat pentez = (zright - zleft)/dx;
27778         if (xleft<0 && dx) zleft-=xleft*(zright - zleft)/dx;
27779         if (xleft<0) xleft = 0;
27780         if (xright>=width()-1) xright = width() - 1;
27781         T* ptrd = data(xleft,y,0,0);
27782         tz *ptrz = zbuffer.data(xleft,y);
27783         if (opacity>=1) {
27784           if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
27785               if (zleft>=(tzfloat)*ptrz) {
27786                 *ptrz = (tz)zleft;
27787               const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; }
27788               ptrd-=offx;
27789             }
27790             zleft+=pentez;
27791           } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
27792               if (zleft>=(tzfloat)*ptrz) {
27793                 *ptrz = (tz)zleft;
27794               const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(nbrightness*(*col++)); ptrd+=whd; }
27795               ptrd-=offx;
27796             }
27797             zleft+=pentez;
27798           } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
27799               if (zleft>=(tzfloat)*ptrz) {
27800                 *ptrz = (tz)zleft;
27801               const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval); ptrd+=whd; }
27802               ptrd-=offx;
27803             }
27804             zleft+=pentez;
27805           }
27806         } else {
27807           if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
27808               if (zleft>=(tzfloat)*ptrz) {
27809                 *ptrz = (tz)zleft;
27810               const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=whd; }
27811               ptrd-=offx;
27812             }
27813             zleft+=pentez;
27814           } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
27815               if (zleft>=(tzfloat)*ptrz) {
27816                 *ptrz = (tz)zleft;
27817               const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(nopacity*nbrightness**(col++) + *ptrd*copacity); ptrd+=whd; }
27818               ptrd-=offx;
27819             }
27820             zleft+=pentez;
27821           } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
27822               if (zleft>=(tzfloat)*ptrz) {
27823                 *ptrz = (tz)zleft;
27824               const tc *col = color;
27825               cimg_forC(*this,c) {
27826                 const T val = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval);
27827                 *ptrd = (T)(nopacity*val + *ptrd*copacity);
27828                 ptrd+=whd;
27829               }
27830               ptrd-=offx;
27831             }
27832             zleft+=pentez;
27833           }
27834         }
27835         zr+=pzr; zl+=pzl;
27836       }
27837       return *this;
27838     }
27839 
27840     //! Draw a 2d Gouraud-shaded colored triangle.
27841     /**
27842        \param x0 = X-coordinate of the first corner in the image instance.
27843        \param y0 = Y-coordinate of the first corner in the image instance.
27844        \param x1 = X-coordinate of the second corner in the image instance.
27845        \param y1 = Y-coordinate of the second corner in the image instance.
27846        \param x2 = X-coordinate of the third corner in the image instance.
27847        \param y2 = Y-coordinate of the third corner in the image instance.
27848        \param color = array of spectrum() values of type \c T, defining the global drawing color.
27849        \param brightness0 = brightness of the first corner (in [0,2]).
27850        \param brightness1 = brightness of the second corner (in [0,2]).
27851        \param brightness2 = brightness of the third corner (in [0,2]).
27852        \param opacity = opacity of the drawing.
27853        \note Clipping is supported.
27854     **/
27855     template<typename tc>
27856     CImg<T>& draw_triangle(const int x0, const int y0,
27857                            const int x1, const int y1,
27858                            const int x2, const int y2,
27859                            const tc *const color,
27860                            const float brightness0,
27861                            const float brightness1,
27862                            const float brightness2,
27863                            const float opacity=1) {
27864       if (!color)
27865         throw CImgArgumentException(_cimg_instance
27866                                     "draw_triangle : Specified color is (null).",
27867                                     cimg_instance);
27868 
27869       if (is_empty()) return *this;
27870       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
27871       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
27872       const int whd = _width*_height*_depth, offx = _spectrum*whd-1;
27873       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
27874         nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
27875         nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
27876         nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
27877       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nc0,nc1);
27878       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nc0,nc2);
27879       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nc1,nc2);
27880       if (ny0>=height() || ny2<0) return *this;
27881       _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
27882         int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0;
27883         if (xright<xleft) cimg::swap(xleft,xright,cleft,cright);
27884         const int
27885           dx = xright - xleft,
27886           dc = cright>cleft?cright - cleft:cleft - cright,
27887           rc = dx?(cright - cleft)/dx:0,
27888           sc = cright>cleft?1:-1,
27889           ndc = dc-(dx?dx*(dc/dx):0);
27890         int errc = dx>>1;
27891         if (xleft<0 && dx) cleft-=xleft*(cright - cleft)/dx;
27892         if (xleft<0) xleft = 0;
27893         if (xright>=width()-1) xright = width() - 1;
27894         T* ptrd = data(xleft,y);
27895         if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
27896           const tc *col = color;
27897           cimg_forC(*this,c) {
27898             *ptrd = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256);
27899             ptrd+=whd;
27900           }
27901           ptrd-=offx;
27902           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
27903         } else for (int x = xleft; x<=xright; ++x) {
27904           const tc *col = color;
27905           cimg_forC(*this,c) {
27906             const T val = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256);
27907             *ptrd = (T)(nopacity*val + *ptrd*copacity);
27908             ptrd+=whd;
27909           }
27910           ptrd-=offx;
27911           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
27912         }
27913       }
27914       return *this;
27915     }
27916 
27917     //! Draw a 2d Gouraud-shaded colored triangle, with z-buffering.
27918     template<typename tz, typename tc>
27919     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
27920                            const int x0, const int y0, const float z0,
27921                            const int x1, const int y1, const float z1,
27922                            const int x2, const int y2, const float z2,
27923                            const tc *const color,
27924                            const float brightness0,
27925                            const float brightness1,
27926                            const float brightness2,
27927                            const float opacity=1) {
27928       typedef typename cimg::superset<tz,float>::type tzfloat;
27929       if (!color)
27930         throw CImgArgumentException(_cimg_instance
27931                                     "draw_triangle() : Specified color is (null).",
27932                                     cimg_instance);
27933       if (!is_sameXY(zbuffer))
27934         throw CImgArgumentException(_cimg_instance
27935                                     "draw_line() : Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
27936                                     cimg_instance,
27937                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
27938 
27939       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
27940       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
27941       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
27942       const int whd = _width*_height*_depth, offx = _spectrum*whd;
27943       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
27944         nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
27945         nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
27946         nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
27947       tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
27948       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1,nc0,nc1);
27949       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nz0,nz2,nc0,nc2);
27950       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nz1,nz2,nc1,nc2);
27951       if (ny0>=height() || ny2<0) return *this;
27952       tzfloat
27953         pzl = (nz1 - nz0)/(ny1 - ny0),
27954         pzr = (nz2 - nz0)/(ny2 - ny0),
27955         pzn = (nz2 - nz1)/(ny2 - ny1),
27956         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
27957         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
27958       _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
27959         if (y==ny1) { zl = nz1; pzl = pzn; }
27960         int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0;
27961         tzfloat zleft = zl, zright = zr;
27962         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,cleft,cright);
27963         const int
27964           dx = xright - xleft,
27965           dc = cright>cleft?cright - cleft:cleft - cright,
27966           rc = dx?(cright-cleft)/dx:0,
27967           sc = cright>cleft?1:-1,
27968           ndc = dc-(dx?dx*(dc/dx):0);
27969         const tzfloat pentez = (zright - zleft)/dx;
27970         int errc = dx>>1;
27971         if (xleft<0 && dx) {
27972           cleft-=xleft*(cright - cleft)/dx;
27973           zleft-=xleft*(zright - zleft)/dx;
27974         }
27975         if (xleft<0) xleft = 0;
27976         if (xright>=width()-1) xright = width()-1;
27977         T *ptrd = data(xleft,y);
27978         tz *ptrz = zbuffer.data(xleft,y);
27979         if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
27980             if (zleft>=(tzfloat)*ptrz) {
27981               *ptrz = (tz)zleft;
27982               const tc *col = color;
27983               cimg_forC(*this,c) {
27984                 *ptrd = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256);
27985                 ptrd+=whd;
27986               }
27987               ptrd-=offx;
27988             }
27989             zleft+=pentez;
27990             cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
27991           } else for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
27992             if (zleft>=(tzfloat)*ptrz) {
27993               *ptrz = (tz)zleft;
27994               const tc *col = color;
27995               cimg_forC(*this,c) {
27996                 const T val = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256);
27997                 *ptrd = (T)(nopacity*val + *ptrd*copacity);
27998                 ptrd+=whd;
27999               }
28000               ptrd-=offx;
28001             }
28002             zleft+=pentez;
28003             cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
28004           }
28005         zr+=pzr; zl+=pzl;
28006       }
28007       return *this;
28008     }
28009 
28010     //! Draw a colored triangle with interpolated colors.
28011     template<typename tc1, typename tc2, typename tc3>
28012     CImg<T>& draw_triangle(const int x0, const int y0,
28013                            const int x1, const int y1,
28014                            const int x2, const int y2,
28015                            const tc1 *const color1,
28016                            const tc2 *const color2,
28017                            const tc3 *const color3,
28018                            const float opacity=1) {
28019       const unsigned char one = 1;
28020       cimg_forC(*this,c) get_shared_channel(c).draw_triangle(x0,y0,x1,y1,x2,y2,&one,color1[c],color2[c],color3[c],opacity);
28021       return *this;
28022     }
28023 
28024     //! Draw a 2d textured triangle.
28025     /**
28026        \param x0 = X-coordinate of the first corner in the image instance.
28027        \param y0 = Y-coordinate of the first corner in the image instance.
28028        \param x1 = X-coordinate of the second corner in the image instance.
28029        \param y1 = Y-coordinate of the second corner in the image instance.
28030        \param x2 = X-coordinate of the third corner in the image instance.
28031        \param y2 = Y-coordinate of the third corner in the image instance.
28032        \param texture = texture image used to fill the triangle.
28033        \param tx0 = X-coordinate of the first corner in the texture image.
28034        \param ty0 = Y-coordinate of the first corner in the texture image.
28035        \param tx1 = X-coordinate of the second corner in the texture image.
28036        \param ty1 = Y-coordinate of the second corner in the texture image.
28037        \param tx2 = X-coordinate of the third corner in the texture image.
28038        \param ty2 = Y-coordinate of the third corner in the texture image.
28039        \param opacity = opacity of the drawing.
28040        \param brightness = brightness of the drawing (in [0,2]).
28041        \note Clipping is supported, but texture coordinates do not support clipping.
28042     **/
28043     template<typename tc>
28044     CImg<T>& draw_triangle(const int x0, const int y0,
28045                            const int x1, const int y1,
28046                            const int x2, const int y2,
28047                            const CImg<tc>& texture,
28048                            const int tx0, const int ty0,
28049                            const int tx1, const int ty1,
28050                            const int tx2, const int ty2,
28051                            const float opacity=1,
28052                            const float brightness=1) {
28053       if (texture._depth>1 || texture._spectrum<_spectrum)
28054         throw CImgArgumentException(_cimg_instance
28055                                     "draw_triangle() : Invalid specified texture (%u,%u,%u,%u,%p).",
28056                                     cimg_instance,
28057                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
28058 
28059       if (is_empty()) return *this;
28060       if (is_overlapped(texture)) return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
28061       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
28062       const float
28063         nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0),
28064         nbrightness = brightness<0?0:(brightness>2?2:brightness);
28065       const int whd = _width*_height*_depth, twhd = texture._width*texture._height*texture._depth, offx = _spectrum*whd-1;
28066       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
28067         ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2;
28068       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1);
28069       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2);
28070       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2);
28071       if (ny0>=height() || ny2<0) return *this;
28072       _cimg_for_triangle3(*this,xleft0,txleft0,tyleft0,xright0,txright0,tyright0,y,
28073                           nx0,ny0,ntx0,nty0,nx1,ny1,ntx1,nty1,nx2,ny2,ntx2,nty2) {
28074         int
28075           xleft = xleft0, xright = xright0,
28076           txleft = txleft0, txright = txright0,
28077           tyleft = tyleft0, tyright = tyright0;
28078         if (xright<xleft) cimg::swap(xleft,xright,txleft,txright,tyleft,tyright);
28079         const int
28080           dx = xright - xleft,
28081           dtx = txright>txleft?txright - txleft:txleft - txright,
28082           dty = tyright>tyleft?tyright - tyleft:tyleft - tyright,
28083           rtx = dx?(txright - txleft)/dx:0,
28084           rty = dx?(tyright - tyleft)/dx:0,
28085           stx = txright>txleft?1:-1,
28086           sty = tyright>tyleft?1:-1,
28087           ndtx = dtx - (dx?dx*(dtx/dx):0),
28088           ndty = dty - (dx?dx*(dty/dx):0);
28089         int errtx = dx>>1, errty = errtx;
28090         if (xleft<0 && dx) {
28091           txleft-=xleft*(txright - txleft)/dx;
28092           tyleft-=xleft*(tyright - tyleft)/dx;
28093         }
28094         if (xleft<0) xleft = 0;
28095         if (xright>=width()-1) xright = width()-1;
28096         T* ptrd = data(xleft,y,0,0);
28097         if (opacity>=1) {
28098           if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
28099             const tc *col = texture.data(txleft,tyleft);
28100             cimg_forC(*this,c) {
28101               *ptrd = (T)*col;
28102               ptrd+=whd; col+=twhd;
28103             }
28104             ptrd-=offx;
28105             txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
28106             tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
28107           } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) {
28108             const tc *col = texture.data(txleft,tyleft);
28109             cimg_forC(*this,c) {
28110               *ptrd = (T)(nbrightness**col);
28111               ptrd+=whd; col+=twhd;
28112             }
28113             ptrd-=offx;
28114             txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
28115             tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
28116           } else for (int x = xleft; x<=xright; ++x) {
28117             const tc *col = texture.data(txleft,tyleft);
28118             cimg_forC(*this,c) {
28119               *ptrd = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval);
28120               ptrd+=whd; col+=twhd;
28121             }
28122             ptrd-=offx;
28123             txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
28124             tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
28125           }
28126         } else {
28127           if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
28128             const tc *col = texture.data(txleft,tyleft);
28129             cimg_forC(*this,c) {
28130               *ptrd = (T)(nopacity**col + *ptrd*copacity);
28131               ptrd+=whd; col+=twhd;
28132             }
28133             ptrd-=offx;
28134             txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
28135             tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
28136           } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) {
28137             const tc *col = texture.data(txleft,tyleft);
28138             cimg_forC(*this,c) {
28139               *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity);
28140               ptrd+=whd; col+=twhd;
28141             }
28142             ptrd-=offx;
28143             txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
28144             tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
28145           } else for (int x = xleft; x<=xright; ++x) {
28146             const tc *col = texture.data(txleft,tyleft);
28147             cimg_forC(*this,c) {
28148               const T val = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval);
28149               *ptrd = (T)(nopacity*val + *ptrd*copacity);
28150               ptrd+=whd; col+=twhd;
28151             }
28152             ptrd-=offx;
28153             txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
28154             tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
28155           }
28156         }
28157       }
28158       return *this;
28159     }
28160 
28161     //! Draw a 2d textured triangle, with perspective correction.
28162     template<typename tc>
28163     CImg<T>& draw_triangle(const int x0, const int y0, const float z0,
28164                            const int x1, const int y1, const float z1,
28165                            const int x2, const int y2, const float z2,
28166                            const CImg<tc>& texture,
28167                            const int tx0, const int ty0,
28168                            const int tx1, const int ty1,
28169                            const int tx2, const int ty2,
28170                            const float opacity=1,
28171                            const float brightness=1) {
28172       if (texture._depth>1 || texture._spectrum<_spectrum)
28173         throw CImgArgumentException(_cimg_instance
28174                                     "draw_triangle() : Invalid specified texture (%u,%u,%u,%u,%p).",
28175                                     cimg_instance,
28176                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
28177 
28178       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
28179       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);
28180       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
28181       const float
28182         nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0),
28183         nbrightness = brightness<0?0:(brightness>2?2:brightness);
28184       const int whd = _width*_height*_depth, twhd = texture._width*texture._height*texture._depth, offx = _spectrum*whd-1;
28185       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
28186       float
28187         ntx0 = tx0/z0, nty0 = ty0/z0,
28188         ntx1 = tx1/z1, nty1 = ty1/z1,
28189         ntx2 = tx2/z2, nty2 = ty2/z2,
28190         nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2;
28191       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1);
28192       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2);
28193       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2);
28194       if (ny0>=height() || ny2<0) return *this;
28195       float
28196         ptxl = (ntx1 - ntx0)/(ny1 - ny0),
28197         ptxr = (ntx2 - ntx0)/(ny2 - ny0),
28198         ptxn = (ntx2 - ntx1)/(ny2 - ny1),
28199         ptyl = (nty1 - nty0)/(ny1 - ny0),
28200         ptyr = (nty2 - nty0)/(ny2 - ny0),
28201         ptyn = (nty2 - nty1)/(ny2 - ny1),
28202         pzl = (nz1 - nz0)/(ny1 - ny0),
28203         pzr = (nz2 - nz0)/(ny2 - ny0),
28204         pzn = (nz2 - nz1)/(ny2 - ny1),
28205         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
28206         txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
28207         tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
28208         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))),
28209         txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
28210         tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
28211       _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) {
28212         if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
28213         int xleft = xleft0, xright = xright0;
28214         float
28215           zleft = zl, zright = zr,
28216           txleft = txl, txright = txr,
28217           tyleft = tyl, tyright = tyr;
28218         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright);
28219         const int dx = xright - xleft;
28220         const float
28221           pentez = (zright - zleft)/dx,
28222           pentetx = (txright - txleft)/dx,
28223           pentety = (tyright - tyleft)/dx;
28224         if (xleft<0 && dx) {
28225           zleft-=xleft*(zright - zleft)/dx;
28226           txleft-=xleft*(txright - txleft)/dx;
28227           tyleft-=xleft*(tyright - tyleft)/dx;
28228         }
28229         if (xleft<0) xleft = 0;
28230         if (xright>=width()-1) xright = width()-1;
28231         T* ptrd = data(xleft,y,0,0);
28232         if (opacity>=1) {
28233           if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
28234             const float invz = 1/zleft;
28235             const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
28236             cimg_forC(*this,c) {
28237               *ptrd = (T)*col;
28238               ptrd+=whd; col+=twhd;
28239             }
28240             ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
28241           } else if (nbrightness<1) for (int x=xleft; x<=xright; ++x) {
28242             const float invz = 1/zleft;
28243             const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
28244             cimg_forC(*this,c) {
28245               *ptrd = (T)(nbrightness**col);
28246               ptrd+=whd; col+=twhd;
28247             }
28248             ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
28249           } else for (int x = xleft; x<=xright; ++x) {
28250             const float invz = 1/zleft;
28251             const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
28252             cimg_forC(*this,c) {
28253               *ptrd = (T)((2-nbrightness)**col + (nbrightness-1)*maxval);
28254               ptrd+=whd; col+=twhd;
28255             }
28256             ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
28257           }
28258         } else {
28259           if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
28260             const float invz = 1/zleft;
28261             const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
28262             cimg_forC(*this,c) {
28263               *ptrd = (T)(nopacity**col + *ptrd*copacity);
28264               ptrd+=whd; col+=twhd;
28265             }
28266             ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
28267           } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) {
28268             const float invz = 1/zleft;
28269             const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
28270             cimg_forC(*this,c) {
28271               *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity);
28272               ptrd+=whd; col+=twhd;
28273             }
28274             ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
28275           } else for (int x = xleft; x<=xright; ++x) {
28276             const float invz = 1/zleft;
28277             const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
28278             cimg_forC(*this,c) {
28279               const T val = (T)((2-nbrightness)**col + (nbrightness-1)*maxval);
28280               *ptrd = (T)(nopacity*val + *ptrd*copacity);
28281               ptrd+=whd; col+=twhd;
28282             }
28283             ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
28284           }
28285         }
28286         zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
28287       }
28288       return *this;
28289     }
28290 
28291     //! Draw a 2d textured triangle, with z-buffering and perspective correction.
28292     template<typename tz, typename tc>
28293     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
28294                            const int x0, const int y0, const float z0,
28295                            const int x1, const int y1, const float z1,
28296                            const int x2, const int y2, const float z2,
28297                            const CImg<tc>& texture,
28298                            const int tx0, const int ty0,
28299                            const int tx1, const int ty1,
28300                            const int tx2, const int ty2,
28301                            const float opacity=1,
28302                            const float brightness=1) {
28303       typedef typename cimg::superset<tz,float>::type tzfloat;
28304       if (!is_sameXY(zbuffer))
28305         throw CImgArgumentException(_cimg_instance
28306                                     "draw_line() : Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
28307                                     cimg_instance,
28308                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
28309 
28310       if (texture._depth>1 || texture._spectrum<_spectrum)
28311         throw CImgArgumentException(_cimg_instance
28312                                     "draw_triangle() : Invalid specified texture (%u,%u,%u,%u,%p).",
28313                                     cimg_instance,
28314                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
28315 
28316       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
28317       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);
28318       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
28319       const float
28320         nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0),
28321         nbrightness = brightness<0?0:(brightness>2?2:brightness);
28322       const int whd = _width*_height*_depth, twhd = texture._width*texture._height*texture._depth, offx = _spectrum*whd;
28323       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
28324       float
28325         ntx0 = tx0/z0, nty0 = ty0/z0,
28326         ntx1 = tx1/z1, nty1 = ty1/z1,
28327         ntx2 = tx2/z2, nty2 = ty2/z2;
28328       tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
28329       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1);
28330       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2);
28331       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2);
28332       if (ny0>=height() || ny2<0) return *this;
28333       float
28334         ptxl = (ntx1 - ntx0)/(ny1 - ny0),
28335         ptxr = (ntx2 - ntx0)/(ny2 - ny0),
28336         ptxn = (ntx2 - ntx1)/(ny2 - ny1),
28337         ptyl = (nty1 - nty0)/(ny1 - ny0),
28338         ptyr = (nty2 - nty0)/(ny2 - ny0),
28339         ptyn = (nty2 - nty1)/(ny2 - ny1),
28340         txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
28341         tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
28342         txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
28343         tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
28344       tzfloat
28345         pzl = (nz1 - nz0)/(ny1 - ny0),
28346         pzr = (nz2 - nz0)/(ny2 - ny0),
28347         pzn = (nz2 - nz1)/(ny2 - ny1),
28348         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
28349         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
28350       _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) {
28351         if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
28352         int xleft = xleft0, xright = xright0;
28353         float txleft = txl, txright = txr, tyleft = tyl, tyright = tyr;
28354         tzfloat zleft = zl, zright = zr;
28355         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright);
28356         const int dx = xright - xleft;
28357         const float pentetx = (txright - txleft)/dx, pentety = (tyright - tyleft)/dx;
28358         const tzfloat pentez = (zright - zleft)/dx;
28359         if (xleft<0 && dx) {
28360           zleft-=xleft*(zright - zleft)/dx;
28361           txleft-=xleft*(txright - txleft)/dx;
28362           tyleft-=xleft*(tyright - tyleft)/dx;
28363         }
28364         if (xleft<0) xleft = 0;
28365         if (xright>=width()-1) xright = width()-1;
28366         T *ptrd = data(xleft,y,0,0);
28367         tz *ptrz = zbuffer.data(xleft,y);
28368         if (opacity>=1) {
28369           if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
28370               if (zleft>=(tzfloat)*ptrz) {
28371                 *ptrz = (tz)zleft;
28372                 const tzfloat invz = 1/zleft;
28373                 const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
28374                 cimg_forC(*this,c) {
28375                   *ptrd = (T)*col;
28376                   ptrd+=whd; col+=twhd;
28377                 }
28378                 ptrd-=offx;
28379               }
28380               zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
28381             } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
28382               if (zleft>=(tzfloat)*ptrz) {
28383                 *ptrz = (tz)zleft;
28384                 const tzfloat invz = 1/zleft;
28385                 const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
28386                 cimg_forC(*this,c) {
28387                   *ptrd = (T)(nbrightness**col);
28388                   ptrd+=whd; col+=twhd;
28389                 }
28390                 ptrd-=offx;
28391               }
28392               zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
28393             } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
28394               if (zleft>=(tzfloat)*ptrz) {
28395                 *ptrz = (tz)zleft;
28396                 const tzfloat invz = 1/zleft;
28397                 const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
28398                 cimg_forC(*this,c) {
28399                   *ptrd = (T)((2-nbrightness)**col + (nbrightness-1)*maxval);
28400                   ptrd+=whd; col+=twhd;
28401                 }
28402                 ptrd-=offx;
28403               }
28404               zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
28405             }
28406         } else {
28407           if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
28408               if (zleft>=(tzfloat)*ptrz) {
28409                 *ptrz = (tz)zleft;
28410                 const tzfloat invz = 1/zleft;
28411                 const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
28412                 cimg_forC(*this,c) {
28413                   *ptrd = (T)(nopacity**col + *ptrd*copacity);
28414                   ptrd+=whd; col+=twhd;
28415                 }
28416                 ptrd-=offx;
28417               }
28418               zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
28419             } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
28420               if (zleft>=(tzfloat)*ptrz) {
28421                 *ptrz = (tz)zleft;
28422                 const tzfloat invz = 1/zleft;
28423                 const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
28424                 cimg_forC(*this,c) {
28425                   *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity);
28426                   ptrd+=whd; col+=twhd;
28427                 }
28428                 ptrd-=offx;
28429               }
28430               zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
28431             } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
28432               if (zleft>=(tzfloat)*ptrz) {
28433                 *ptrz = (tz)zleft;
28434                 const tzfloat invz = 1/zleft;
28435                 const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
28436                 cimg_forC(*this,c) {
28437                   const T val = (T)((2-nbrightness)**col + (nbrightness-1)*maxval);
28438                   *ptrd = (T)(nopacity*val + *ptrd*copacity);
28439                   ptrd+=whd; col+=twhd;
28440                 }
28441                 ptrd-=offx;
28442               }
28443               zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
28444             }
28445         }
28446         zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
28447       }
28448       return *this;
28449     }
28450 
28451     //! Draw a 2d Pseudo-Phong-shaded triangle.
28452     /**
28453        \param x0 = X-coordinate of the first corner in the image instance.
28454        \param y0 = Y-coordinate of the first corner in the image instance.
28455        \param x1 = X-coordinate of the second corner in the image instance.
28456        \param y1 = Y-coordinate of the second corner in the image instance.
28457        \param x2 = X-coordinate of the third corner in the image instance.
28458        \param y2 = Y-coordinate of the third corner in the image instance.
28459        \param color = array of spectrum() values of type \c T, defining the global drawing color.
28460        \param light = light image.
28461        \param lx0 = X-coordinate of the first corner in the light image.
28462        \param ly0 = Y-coordinate of the first corner in the light image.
28463        \param lx1 = X-coordinate of the second corner in the light image.
28464        \param ly1 = Y-coordinate of the second corner in the light image.
28465        \param lx2 = X-coordinate of the third corner in the light image.
28466        \param ly2 = Y-coordinate of the third corner in the light image.
28467        \param opacity = opacity of the drawing.
28468        \note Clipping is supported, but texture coordinates do not support clipping.
28469     **/
28470     template<typename tc, typename tl>
28471     CImg<T>& draw_triangle(const int x0, const int y0,
28472                            const int x1, const int y1,
28473                            const int x2, const int y2,
28474                            const tc *const color,
28475                            const CImg<tl>& light,
28476                            const int lx0, const int ly0,
28477                            const int lx1, const int ly1,
28478                            const int lx2, const int ly2,
28479                            const float opacity=1) {
28480       if (!color)
28481         throw CImgArgumentException(_cimg_instance
28482                                     "draw_triangle : Specified color is (null).",
28483                                     cimg_instance);
28484       if (light._depth>1 || light._spectrum<_spectrum)
28485         throw CImgArgumentException(_cimg_instance
28486                                     "draw_triangle() : Invalid specified light texture (%u,%u,%u,%u,%p).",
28487                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
28488 
28489       if (is_empty()) return *this;
28490       if (is_overlapped(light)) return draw_triangle(x0,y0,x1,y1,x2,y2,color,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
28491       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
28492       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
28493       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
28494         nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
28495       const int whd = _width*_height*_depth, offx = _spectrum*whd-1;
28496       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nlx0,nlx1,nly0,nly1);
28497       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nlx0,nlx2,nly0,nly2);
28498       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nlx1,nlx2,nly1,nly2);
28499       if (ny0>=height() || ny2<0) return *this;
28500       _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
28501                           nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
28502         int
28503           xleft = xleft0, xright = xright0,
28504           lxleft = lxleft0, lxright = lxright0,
28505           lyleft = lyleft0, lyright = lyright0;
28506         if (xright<xleft) cimg::swap(xleft,xright,lxleft,lxright,lyleft,lyright);
28507         const int
28508           dx = xright - xleft,
28509           dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
28510           dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
28511           rlx = dx?(lxright - lxleft)/dx:0,
28512           rly = dx?(lyright - lyleft)/dx:0,
28513           slx = lxright>lxleft?1:-1,
28514           sly = lyright>lyleft?1:-1,
28515           ndlx = dlx - (dx?dx*(dlx/dx):0),
28516           ndly = dly - (dx?dx*(dly/dx):0);
28517         int errlx = dx>>1, errly = errlx;
28518         if (xleft<0 && dx) {
28519           lxleft-=xleft*(lxright - lxleft)/dx;
28520           lyleft-=xleft*(lyright - lyleft)/dx;
28521         }
28522         if (xleft<0) xleft = 0;
28523         if (xright>=width()-1) xright = width()-1;
28524         T* ptrd = data(xleft,y,0,0);
28525         if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
28526           const tc *col = color;
28527           cimg_forC(*this,c) {
28528             const tl l = light(lxleft,lyleft,c);
28529             *ptrd = (T)(l<1?l**(col++):((2-l)**(col++)+(l-1)*maxval));
28530             ptrd+=whd;
28531           }
28532           ptrd-=offx;
28533           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
28534           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
28535         } else  for (int x = xleft; x<=xright; ++x) {
28536           const tc *col = color;
28537           cimg_forC(*this,c) {
28538             const tl l = light(lxleft,lyleft,c);
28539             const T val = (T)(l<1?l**(col++):((2-l)**(col++)+(l-1)*maxval));
28540             *ptrd = (T)(nopacity*val + *ptrd*copacity);
28541             ptrd+=whd;
28542           }
28543           ptrd-=offx;
28544           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
28545           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
28546         }
28547       }
28548       return *this;
28549     }
28550 
28551     //! Draw a 2d Pseudo-Phong-shaded triangle, with z-buffering.
28552     template<typename tz, typename tc, typename tl>
28553     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
28554                            const int x0, const int y0, const float z0,
28555                            const int x1, const int y1, const float z1,
28556                            const int x2, const int y2, const float z2,
28557                            const tc *const color,
28558                            const CImg<tl>& light,
28559                            const int lx0, const int ly0,
28560                            const int lx1, const int ly1,
28561                            const int lx2, const int ly2,
28562                            const float opacity=1) {
28563       typedef typename cimg::superset<tz,float>::type tzfloat;
28564       if (!color)
28565         throw CImgArgumentException(_cimg_instance
28566                                     "draw_triangle() : Specified color is (null).",
28567                                     cimg_instance);
28568       if (light._depth>1 || light._spectrum<_spectrum)
28569         throw CImgArgumentException(_cimg_instance
28570                                     "draw_triangle() : Invalid specified light texture (%u,%u,%u,%u,%p).",
28571                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
28572       if (!is_sameXY(zbuffer))
28573         throw CImgArgumentException(_cimg_instance
28574                                     "draw_line() : Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
28575                                     cimg_instance,
28576                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
28577 
28578       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
28579       if (is_overlapped(light)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,
28580                                                      +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
28581       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
28582       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
28583       const int whd = _width*_height*_depth, offx = _spectrum*whd;
28584       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
28585         nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
28586       tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
28587       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nlx0,nlx1,nly0,nly1,nz0,nz1);
28588       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nlx0,nlx2,nly0,nly2,nz0,nz2);
28589       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nlx1,nlx2,nly1,nly2,nz1,nz2);
28590       if (ny0>=height() || ny2<0) return *this;
28591       tzfloat
28592         pzl = (nz1 - nz0)/(ny1 - ny0),
28593         pzr = (nz2 - nz0)/(ny2 - ny0),
28594         pzn = (nz2 - nz1)/(ny2 - ny1),
28595         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
28596         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
28597       _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
28598                           nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
28599         if (y==ny1) { zl = nz1; pzl = pzn; }
28600         int
28601           xleft = xleft0, xright = xright0,
28602           lxleft = lxleft0, lxright = lxright0,
28603           lyleft = lyleft0, lyright = lyright0;
28604         tzfloat zleft = zl, zright = zr;
28605         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,lxleft,lxright,lyleft,lyright);
28606         const int
28607           dx = xright - xleft,
28608           dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
28609           dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
28610           rlx = dx?(lxright - lxleft)/dx:0,
28611           rly = dx?(lyright - lyleft)/dx:0,
28612           slx = lxright>lxleft?1:-1,
28613           sly = lyright>lyleft?1:-1,
28614           ndlx = dlx - (dx?dx*(dlx/dx):0),
28615           ndly = dly - (dx?dx*(dly/dx):0);
28616         const tzfloat pentez = (zright - zleft)/dx;
28617         int errlx = dx>>1, errly = errlx;
28618         if (xleft<0 && dx) {
28619           zleft-=xleft*(zright - zleft)/dx;
28620           lxleft-=xleft*(lxright - lxleft)/dx;
28621           lyleft-=xleft*(lyright - lyleft)/dx;
28622         }
28623         if (xleft<0) xleft = 0;
28624         if (xright>=width()-1) xright = width()-1;
28625         T *ptrd = data(xleft,y,0,0);
28626         tz *ptrz = zbuffer.data(xleft,y);
28627         if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
28628             if (zleft>=(tzfloat)*ptrz) {
28629               *ptrz = (tz)zleft;
28630               const tc *col = color;
28631               cimg_forC(*this,c) {
28632                 const tl l = light(lxleft,lyleft,c);
28633                 const tc cval = *(col++);
28634                 *ptrd = (T)(l<1?l*cval:(2-l)*cval+(l-1)*maxval);
28635                 ptrd+=whd;
28636               }
28637               ptrd-=offx;
28638             }
28639             zleft+=pentez;
28640             lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
28641             lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
28642           } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
28643             if (zleft>=(tzfloat)*ptrz) {
28644               *ptrz = (tz)zleft;
28645               const tc *col = color;
28646               cimg_forC(*this,c) {
28647                 const tl l = light(lxleft,lyleft,c);
28648                 const tc cval = *(col++);
28649                 const T val = (T)(l<1?l*cval:(2-l)*cval+(l-1)*maxval);
28650                 *ptrd = (T)(nopacity*val + *ptrd*copacity);
28651                 ptrd+=whd;
28652               }
28653               ptrd-=offx;
28654             }
28655             zleft+=pentez;
28656             lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
28657             lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
28658           }
28659         zr+=pzr; zl+=pzl;
28660       }
28661       return *this;
28662     }
28663 
28664     //! Draw a 2d Gouraud-shaded textured triangle.
28665     /**
28666        \param x0 = X-coordinate of the first corner in the image instance.
28667        \param y0 = Y-coordinate of the first corner in the image instance.
28668        \param x1 = X-coordinate of the second corner in the image instance.
28669        \param y1 = Y-coordinate of the second corner in the image instance.
28670        \param x2 = X-coordinate of the third corner in the image instance.
28671        \param y2 = Y-coordinate of the third corner in the image instance.
28672        \param texture = texture image used to fill the triangle.
28673        \param tx0 = X-coordinate of the first corner in the texture image.
28674        \param ty0 = Y-coordinate of the first corner in the texture image.
28675        \param tx1 = X-coordinate of the second corner in the texture image.
28676        \param ty1 = Y-coordinate of the second corner in the texture image.
28677        \param tx2 = X-coordinate of the third corner in the texture image.
28678        \param ty2 = Y-coordinate of the third corner in the texture image.
28679        \param brightness0 = brightness value of the first corner.
28680        \param brightness1 = brightness value of the second corner.
28681        \param brightness2 = brightness value of the third corner.
28682        \param opacity = opacity of the drawing.
28683        \note Clipping is supported, but texture coordinates do not support clipping.
28684     **/
28685     template<typename tc>
28686     CImg<T>& draw_triangle(const int x0, const int y0,
28687                            const int x1, const int y1,
28688                            const int x2, const int y2,
28689                            const CImg<tc>& texture,
28690                            const int tx0, const int ty0,
28691                            const int tx1, const int ty1,
28692                            const int tx2, const int ty2,
28693                            const float brightness0,
28694                            const float brightness1,
28695                            const float brightness2,
28696                            const float opacity=1) {
28697       if (texture._depth>1 || texture._spectrum<_spectrum)
28698         throw CImgArgumentException(_cimg_instance
28699                                     "draw_triangle() : Invalid specified texture (%u,%u,%u,%u,%p).",
28700                                     cimg_instance,
28701                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
28702 
28703       if (is_empty()) return *this;
28704       if (is_overlapped(texture))
28705         return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,brightness0,brightness1,brightness2,opacity);
28706       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
28707       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
28708       const int whd = _width*_height*_depth, twhd = texture._width*texture._height*texture._depth, offx = _spectrum*whd-1;
28709       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
28710         ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2,
28711         nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
28712         nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
28713         nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
28714       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nc0,nc1);
28715       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nc0,nc2);
28716       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nc1,nc2);
28717       if (ny0>=height() || ny2<0) return *this;
28718       _cimg_for_triangle4(*this,xleft0,cleft0,txleft0,tyleft0,xright0,cright0,txright0,tyright0,y,
28719                           nx0,ny0,nc0,ntx0,nty0,nx1,ny1,nc1,ntx1,nty1,nx2,ny2,nc2,ntx2,nty2) {
28720         int
28721           xleft = xleft0, xright = xright0,
28722           cleft = cleft0, cright = cright0,
28723           txleft = txleft0, txright = txright0,
28724           tyleft = tyleft0, tyright = tyright0;
28725         if (xright<xleft) cimg::swap(xleft,xright,cleft,cright,txleft,txright,tyleft,tyright);
28726         const int
28727           dx = xright - xleft,
28728           dc = cright>cleft?cright - cleft:cleft - cright,
28729           dtx = txright>txleft?txright - txleft:txleft - txright,
28730           dty = tyright>tyleft?tyright - tyleft:tyleft - tyright,
28731           rc = dx?(cright - cleft)/dx:0,
28732           rtx = dx?(txright - txleft)/dx:0,
28733           rty = dx?(tyright - tyleft)/dx:0,
28734           sc = cright>cleft?1:-1,
28735           stx = txright>txleft?1:-1,
28736           sty = tyright>tyleft?1:-1,
28737           ndc = dc - (dx?dx*(dc/dx):0),
28738           ndtx = dtx - (dx?dx*(dtx/dx):0),
28739           ndty = dty - (dx?dx*(dty/dx):0);
28740         int errc = dx>>1, errtx = errc, errty = errc;
28741         if (xleft<0 && dx) {
28742           cleft-=xleft*(cright - cleft)/dx;
28743           txleft-=xleft*(txright - txleft)/dx;
28744           tyleft-=xleft*(tyright - tyleft)/dx;
28745         }
28746         if (xleft<0) xleft = 0;
28747         if (xright>=width()-1) xright = width()-1;
28748         T* ptrd = data(xleft,y,0,0);
28749         if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
28750           const tc *col = texture.data(txleft,tyleft);
28751           cimg_forC(*this,c) {
28752             *ptrd = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
28753             ptrd+=whd; col+=twhd;
28754           }
28755           ptrd-=offx;
28756           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
28757           txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
28758           tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
28759         } else for (int x = xleft; x<=xright; ++x) {
28760           const tc *col = texture.data(txleft,tyleft);
28761           cimg_forC(*this,c) {
28762             const T val = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
28763             *ptrd = (T)(nopacity*val + *ptrd*copacity);
28764             ptrd+=whd; col+=twhd;
28765           }
28766           ptrd-=offx;
28767           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
28768           txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
28769           tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
28770         }
28771       }
28772       return *this;
28773     }
28774 
28775     //! Draw a 2d Gouraud-shaded textured triangle, with perspective correction.
28776     template<typename tc>
28777     CImg<T>& draw_triangle(const int x0, const int y0, const float z0,
28778                            const int x1, const int y1, const float z1,
28779                            const int x2, const int y2, const float z2,
28780                            const CImg<tc>& texture,
28781                            const int tx0, const int ty0,
28782                            const int tx1, const int ty1,
28783                            const int tx2, const int ty2,
28784                            const float brightness0,
28785                            const float brightness1,
28786                            const float brightness2,
28787                            const float opacity=1) {
28788       if (texture._depth>1 || texture._spectrum<_spectrum)
28789         throw CImgArgumentException(_cimg_instance
28790                                     "draw_triangle() : Invalid specified texture (%u,%u,%u,%u,%p).",
28791                                     cimg_instance,
28792                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
28793 
28794       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
28795       if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
28796                                                        brightness0,brightness1,brightness2,opacity);
28797       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
28798       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
28799       const int whd = _width*_height*_depth, twhd = texture._width*texture._height*texture._depth, offx = _spectrum*whd-1;
28800       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
28801         nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
28802         nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
28803         nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
28804       float
28805         ntx0 = tx0/z0, nty0 = ty0/z0,
28806         ntx1 = tx1/z1, nty1 = ty1/z1,
28807         ntx2 = tx2/z2, nty2 = ty2/z2,
28808         nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2;
28809       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1,nc0,nc1);
28810       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2,nc0,nc2);
28811       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2,nc1,nc2);
28812       if (ny0>=height() || ny2<0) return *this;
28813       float
28814         ptxl = (ntx1 - ntx0)/(ny1 - ny0),
28815         ptxr = (ntx2 - ntx0)/(ny2 - ny0),
28816         ptxn = (ntx2 - ntx1)/(ny2 - ny1),
28817         ptyl = (nty1 - nty0)/(ny1 - ny0),
28818         ptyr = (nty2 - nty0)/(ny2 - ny0),
28819         ptyn = (nty2 - nty1)/(ny2 - ny1),
28820         pzl = (nz1 - nz0)/(ny1 - ny0),
28821         pzr = (nz2 - nz0)/(ny2 - ny0),
28822         pzn = (nz2 - nz1)/(ny2 - ny1),
28823         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
28824         txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
28825         tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
28826         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))),
28827         txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
28828         tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
28829       _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
28830         if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
28831         int
28832           xleft = xleft0, xright = xright0,
28833           cleft = cleft0, cright = cright0;
28834         float
28835           zleft = zl, zright = zr,
28836           txleft = txl, txright = txr,
28837           tyleft = tyl, tyright = tyr;
28838         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,cleft,cright);
28839         const int
28840           dx = xright - xleft,
28841           dc = cright>cleft?cright - cleft:cleft - cright,
28842           rc = dx?(cright - cleft)/dx:0,
28843           sc = cright>cleft?1:-1,
28844           ndc = dc - (dx?dx*(dc/dx):0);
28845         const float
28846           pentez = (zright - zleft)/dx,
28847           pentetx = (txright - txleft)/dx,
28848           pentety = (tyright - tyleft)/dx;
28849         int errc = dx>>1;
28850         if (xleft<0 && dx) {
28851           cleft-=xleft*(cright - cleft)/dx;
28852           zleft-=xleft*(zright - zleft)/dx;
28853           txleft-=xleft*(txright - txleft)/dx;
28854           tyleft-=xleft*(tyright - tyleft)/dx;
28855         }
28856         if (xleft<0) xleft = 0;
28857         if (xright>=width()-1) xright = width()-1;
28858         T* ptrd = data(xleft,y,0,0);
28859         if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
28860           const float invz = 1/zleft;
28861           const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
28862           cimg_forC(*this,c) {
28863             *ptrd = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
28864             ptrd+=whd; col+=twhd;
28865           }
28866           ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
28867           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
28868         } else for (int x = xleft; x<=xright; ++x) {
28869           const float invz = 1/zleft;
28870           const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
28871           cimg_forC(*this,c) {
28872             const T val = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
28873             *ptrd = (T)(nopacity*val + *ptrd*copacity);
28874             ptrd+=whd; col+=twhd;
28875           }
28876           ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
28877           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
28878         }
28879         zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
28880       }
28881       return *this;
28882     }
28883 
28884     //! Draw a 2d Gouraud-shaded textured triangle, with z-buffering and perspective correction.
28885     template<typename tz, typename tc>
28886     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
28887                            const int x0, const int y0, const float z0,
28888                            const int x1, const int y1, const float z1,
28889                            const int x2, const int y2, const float z2,
28890                            const CImg<tc>& texture,
28891                            const int tx0, const int ty0,
28892                            const int tx1, const int ty1,
28893                            const int tx2, const int ty2,
28894                            const float brightness0,
28895                            const float brightness1,
28896                            const float brightness2,
28897                            const float opacity=1) {
28898       typedef typename cimg::superset<tz,float>::type tzfloat;
28899       if (!is_sameXY(zbuffer))
28900         throw CImgArgumentException(_cimg_instance
28901                                     "draw_line() : Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
28902                                     cimg_instance,
28903                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
28904 
28905       if (texture._depth>1 || texture._spectrum<_spectrum)
28906         throw CImgArgumentException(_cimg_instance
28907                                     "draw_triangle() : Invalid specified texture (%u,%u,%u,%u,%p).",
28908                                     cimg_instance,
28909                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
28910 
28911       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
28912       if (is_overlapped(texture)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
28913                                                        brightness0,brightness1,brightness2,opacity);
28914       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
28915       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
28916       const int whd = _width*_height*_depth, twhd = texture._width*texture._height*texture._depth, offx = _spectrum*whd;
28917       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
28918         nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
28919         nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
28920         nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
28921       float
28922         ntx0 = tx0/z0, nty0 = ty0/z0,
28923         ntx1 = tx1/z1, nty1 = ty1/z1,
28924         ntx2 = tx2/z2, nty2 = ty2/z2;
28925       tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
28926       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1,nc0,nc1);
28927       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2,nc0,nc2);
28928       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2,nc1,nc2);
28929       if (ny0>=height() || ny2<0) return *this;
28930       float
28931         ptxl = (ntx1 - ntx0)/(ny1 - ny0),
28932         ptxr = (ntx2 - ntx0)/(ny2 - ny0),
28933         ptxn = (ntx2 - ntx1)/(ny2 - ny1),
28934         ptyl = (nty1 - nty0)/(ny1 - ny0),
28935         ptyr = (nty2 - nty0)/(ny2 - ny0),
28936         ptyn = (nty2 - nty1)/(ny2 - ny1),
28937         txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
28938         tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
28939         txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
28940         tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
28941       tzfloat
28942         pzl = (nz1 - nz0)/(ny1 - ny0),
28943         pzr = (nz2 - nz0)/(ny2 - ny0),
28944         pzn = (nz2 - nz1)/(ny2 - ny1),
28945         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
28946         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
28947       _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
28948         if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
28949         int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0;
28950         float txleft = txl, txright = txr, tyleft = tyl, tyright = tyr;
28951         tzfloat zleft = zl, zright = zr;
28952         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,cleft,cright);
28953         const int
28954           dx = xright - xleft,
28955           dc = cright>cleft?cright - cleft:cleft - cright,
28956           rc = dx?(cright - cleft)/dx:0,
28957           sc = cright>cleft?1:-1,
28958           ndc = dc - (dx?dx*(dc/dx):0);
28959         float pentetx = (txright - txleft)/dx, pentety = (tyright - tyleft)/dx;
28960         const tzfloat pentez = (zright - zleft)/dx;
28961         int errc = dx>>1;
28962         if (xleft<0 && dx) {
28963           cleft-=xleft*(cright - cleft)/dx;
28964           zleft-=xleft*(zright - zleft)/dx;
28965           txleft-=xleft*(txright - txleft)/dx;
28966           tyleft-=xleft*(tyright - tyleft)/dx;
28967         }
28968         if (xleft<0) xleft = 0;
28969         if (xright>=width()-1) xright = width()-1;
28970         T* ptrd = data(xleft,y);
28971         tz *ptrz = zbuffer.data(xleft,y);
28972         if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
28973             if (zleft>=(tzfloat)*ptrz) {
28974               *ptrz = (tz)zleft;
28975               const tzfloat invz = 1/zleft;
28976               const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
28977               cimg_forC(*this,c) {
28978                 *ptrd = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
28979                 ptrd+=whd; col+=twhd;
28980               }
28981               ptrd-=offx;
28982             }
28983             zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
28984             cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
28985           } else for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
28986             if (zleft>=(tzfloat)*ptrz) {
28987               *ptrz = (tz)zleft;
28988               const tzfloat invz = 1/zleft;
28989               const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
28990               cimg_forC(*this,c) {
28991                 const T val = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
28992                 *ptrd = (T)(nopacity*val + *ptrd*copacity);
28993                 ptrd+=whd; col+=twhd;
28994               }
28995               ptrd-=offx;
28996             }
28997             zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
28998             cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
28999           }
29000         zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
29001       }
29002       return *this;
29003     }
29004 
29005     //! Draw a 2d Pseudo-Phong-shaded textured triangle.
29006     /**
29007        \param x0 = X-coordinate of the first corner in the image instance.
29008        \param y0 = Y-coordinate of the first corner in the image instance.
29009        \param x1 = X-coordinate of the second corner in the image instance.
29010        \param y1 = Y-coordinate of the second corner in the image instance.
29011        \param x2 = X-coordinate of the third corner in the image instance.
29012        \param y2 = Y-coordinate of the third corner in the image instance.
29013        \param texture = texture image used to fill the triangle.
29014        \param tx0 = X-coordinate of the first corner in the texture image.
29015        \param ty0 = Y-coordinate of the first corner in the texture image.
29016        \param tx1 = X-coordinate of the second corner in the texture image.
29017        \param ty1 = Y-coordinate of the second corner in the texture image.
29018        \param tx2 = X-coordinate of the third corner in the texture image.
29019        \param ty2 = Y-coordinate of the third corner in the texture image.
29020        \param light = light image.
29021        \param lx0 = X-coordinate of the first corner in the light image.
29022        \param ly0 = Y-coordinate of the first corner in the light image.
29023        \param lx1 = X-coordinate of the second corner in the light image.
29024        \param ly1 = Y-coordinate of the second corner in the light image.
29025        \param lx2 = X-coordinate of the third corner in the light image.
29026        \param ly2 = Y-coordinate of the third corner in the light image.
29027        \param opacity = opacity of the drawing.
29028        \note Clipping is supported, but texture coordinates do not support clipping.
29029     **/
29030     template<typename tc, typename tl>
29031     CImg<T>& draw_triangle(const int x0, const int y0,
29032                            const int x1, const int y1,
29033                            const int x2, const int y2,
29034                            const CImg<tc>& texture,
29035                            const int tx0, const int ty0,
29036                            const int tx1, const int ty1,
29037                            const int tx2, const int ty2,
29038                            const CImg<tl>& light,
29039                            const int lx0, const int ly0,
29040                            const int lx1, const int ly1,
29041                            const int lx2, const int ly2,
29042                            const float opacity=1) {
29043       if (texture._depth>1 || texture._spectrum<_spectrum)
29044         throw CImgArgumentException(_cimg_instance
29045                                     "draw_triangle() : Invalid specified texture (%u,%u,%u,%u,%p).",
29046                                     cimg_instance,
29047                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
29048       if (light._depth>1 || light._spectrum<_spectrum)
29049         throw CImgArgumentException(_cimg_instance
29050                                     "draw_triangle() : Invalid specified light texture (%u,%u,%u,%u,%p).",
29051                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
29052 
29053       if (is_empty()) return *this;
29054       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);
29055       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);
29056       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
29057       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
29058       const int whd = _width*_height*_depth, twhd = texture._width*texture._height*texture._depth, offx = _spectrum*whd-1;
29059       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
29060         ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2,
29061         nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
29062       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1);
29063       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2);
29064       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2);
29065       if (ny0>=height() || ny2<0) return *this;
29066       _cimg_for_triangle5(*this,xleft0,lxleft0,lyleft0,txleft0,tyleft0,xright0,lxright0,lyright0,txright0,tyright0,y,
29067                           nx0,ny0,nlx0,nly0,ntx0,nty0,nx1,ny1,nlx1,nly1,ntx1,nty1,nx2,ny2,nlx2,nly2,ntx2,nty2) {
29068         int
29069           xleft = xleft0, xright = xright0,
29070           lxleft = lxleft0, lxright = lxright0,
29071           lyleft = lyleft0, lyright = lyright0,
29072           txleft = txleft0, txright = txright0,
29073           tyleft = tyleft0, tyright = tyright0;
29074         if (xright<xleft) cimg::swap(xleft,xright,lxleft,lxright,lyleft,lyright,txleft,txright,tyleft,tyright);
29075         const int
29076           dx = xright - xleft,
29077           dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
29078           dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
29079           dtx = txright>txleft?txright - txleft:txleft - txright,
29080           dty = tyright>tyleft?tyright - tyleft:tyleft - tyright,
29081           rlx = dx?(lxright - lxleft)/dx:0,
29082           rly = dx?(lyright - lyleft)/dx:0,
29083           rtx = dx?(txright - txleft)/dx:0,
29084           rty = dx?(tyright - tyleft)/dx:0,
29085           slx = lxright>lxleft?1:-1,
29086           sly = lyright>lyleft?1:-1,
29087           stx = txright>txleft?1:-1,
29088           sty = tyright>tyleft?1:-1,
29089           ndlx = dlx - (dx?dx*(dlx/dx):0),
29090           ndly = dly - (dx?dx*(dly/dx):0),
29091           ndtx = dtx - (dx?dx*(dtx/dx):0),
29092           ndty = dty - (dx?dx*(dty/dx):0);
29093         int errlx = dx>>1, errly = errlx, errtx = errlx, errty = errlx;
29094         if (xleft<0 && dx) {
29095           lxleft-=xleft*(lxright - lxleft)/dx;
29096           lyleft-=xleft*(lyright - lyleft)/dx;
29097           txleft-=xleft*(txright - txleft)/dx;
29098           tyleft-=xleft*(tyright - tyleft)/dx;
29099         }
29100         if (xleft<0) xleft = 0;
29101         if (xright>=width()-1) xright = width()-1;
29102         T* ptrd = data(xleft,y,0,0);
29103         if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
29104           const tc *col = texture.data(txleft,tyleft);
29105           cimg_forC(*this,c) {
29106             const tl l = light(lxleft,lyleft,c);
29107             *ptrd = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
29108             ptrd+=whd; col+=twhd;
29109           }
29110           ptrd-=offx;
29111           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
29112           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
29113           txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
29114           tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
29115         } else for (int x = xleft; x<=xright; ++x) {
29116           const tc *col = texture.data(txleft,tyleft);
29117           cimg_forC(*this,c) {
29118             const tl l = light(lxleft,lyleft,c);
29119             const T val = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
29120             *ptrd = (T)(nopacity*val + *ptrd*copacity);
29121             ptrd+=whd; col+=twhd;
29122           }
29123           ptrd-=offx;
29124           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
29125           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
29126           txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
29127           tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
29128         }
29129       }
29130       return *this;
29131     }
29132 
29133     //! Draw a 2d Pseudo-Phong-shaded textured triangle, with perspective correction.
29134     template<typename tc, typename tl>
29135     CImg<T>& draw_triangle(const int x0, const int y0, const float z0,
29136                            const int x1, const int y1, const float z1,
29137                            const int x2, const int y2, const float z2,
29138                            const CImg<tc>& texture,
29139                            const int tx0, const int ty0,
29140                            const int tx1, const int ty1,
29141                            const int tx2, const int ty2,
29142                            const CImg<tl>& light,
29143                            const int lx0, const int ly0,
29144                            const int lx1, const int ly1,
29145                            const int lx2, const int ly2,
29146                            const float opacity=1) {
29147       if (texture._depth>1 || texture._spectrum<_spectrum)
29148         throw CImgArgumentException(_cimg_instance
29149                                     "draw_triangle() : Invalid specified texture (%u,%u,%u,%u,%p).",
29150                                     cimg_instance,
29151                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
29152       if (light._depth>1 || light._spectrum<_spectrum)
29153         throw CImgArgumentException(_cimg_instance
29154                                     "draw_triangle() : Invalid specified light texture (%u,%u,%u,%u,%p).",
29155                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
29156 
29157       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
29158       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);
29159       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);
29160       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
29161       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
29162       const int whd = _width*_height*_depth, twhd = texture._width*texture._height*texture._depth, offx = _spectrum*whd-1;
29163       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
29164         nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
29165       float
29166         ntx0 = tx0/z0, nty0 = ty0/z0,
29167         ntx1 = tx1/z1, nty1 = ty1/z1,
29168         ntx2 = tx2/z2, nty2 = ty2/z2,
29169         nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2;
29170       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1,nz0,nz1);
29171       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2,nz0,nz2);
29172       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2,nz1,nz2);
29173       if (ny0>=height() || ny2<0) return *this;
29174       float
29175         ptxl = (ntx1 - ntx0)/(ny1 - ny0),
29176         ptxr = (ntx2 - ntx0)/(ny2 - ny0),
29177         ptxn = (ntx2 - ntx1)/(ny2 - ny1),
29178         ptyl = (nty1 - nty0)/(ny1 - ny0),
29179         ptyr = (nty2 - nty0)/(ny2 - ny0),
29180         ptyn = (nty2 - nty1)/(ny2 - ny1),
29181         pzl = (nz1 - nz0)/(ny1 - ny0),
29182         pzr = (nz2 - nz0)/(ny2 - ny0),
29183         pzn = (nz2 - nz1)/(ny2 - ny1),
29184         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
29185         txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
29186         tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
29187         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))),
29188         txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
29189         tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
29190       _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
29191                           nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
29192         if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
29193         int
29194           xleft = xleft0, xright = xright0,
29195           lxleft = lxleft0, lxright = lxright0,
29196           lyleft = lyleft0, lyright = lyright0;
29197         float
29198           zleft = zl, zright = zr,
29199           txleft = txl, txright = txr,
29200           tyleft = tyl, tyright = tyr;
29201         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,lxleft,lxright,lyleft,lyright);
29202         const int
29203           dx = xright - xleft,
29204           dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
29205           dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
29206           rlx = dx?(lxright - lxleft)/dx:0,
29207           rly = dx?(lyright - lyleft)/dx:0,
29208           slx = lxright>lxleft?1:-1,
29209           sly = lyright>lyleft?1:-1,
29210           ndlx = dlx - (dx?dx*(dlx/dx):0),
29211           ndly = dly - (dx?dx*(dly/dx):0);
29212         const float
29213           pentez = (zright - zleft)/dx,
29214           pentetx = (txright - txleft)/dx,
29215           pentety = (tyright - tyleft)/dx;
29216         int errlx = dx>>1, errly = errlx;
29217         if (xleft<0 && dx) {
29218           zleft-=xleft*(zright - zleft)/dx;
29219           lxleft-=xleft*(lxright - lxleft)/dx;
29220           lyleft-=xleft*(lyright - lyleft)/dx;
29221           txleft-=xleft*(txright - txleft)/dx;
29222           tyleft-=xleft*(tyright - tyleft)/dx;
29223         }
29224         if (xleft<0) xleft = 0;
29225         if (xright>=width()-1) xright = width()-1;
29226         T* ptrd = data(xleft,y,0,0);
29227         if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
29228           const float invz = 1/zleft;
29229           const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29230           cimg_forC(*this,c) {
29231             const tl l = light(lxleft,lyleft,c);
29232             *ptrd = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
29233             ptrd+=whd; col+=twhd;
29234           }
29235           ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29236           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
29237           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
29238         } else for (int x = xleft; x<=xright; ++x) {
29239           const float invz = 1/zleft;
29240           const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29241           cimg_forC(*this,c) {
29242             const tl l = light(lxleft,lyleft,c);
29243             const T val = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
29244             *ptrd = (T)(nopacity*val + *ptrd*copacity);
29245             ptrd+=whd; col+=twhd;
29246           }
29247           ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29248           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
29249           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
29250         }
29251         zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
29252       }
29253       return *this;
29254     }
29255 
29256     //! Draw a 2d Pseudo-Phong-shaded textured triangle, with z-buffering and perspective correction.
29257     template<typename tz, typename tc, typename tl>
29258     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
29259                            const int x0, const int y0, const float z0,
29260                            const int x1, const int y1, const float z1,
29261                            const int x2, const int y2, const float z2,
29262                            const CImg<tc>& texture,
29263                            const int tx0, const int ty0,
29264                            const int tx1, const int ty1,
29265                            const int tx2, const int ty2,
29266                            const CImg<tl>& light,
29267                            const int lx0, const int ly0,
29268                            const int lx1, const int ly1,
29269                            const int lx2, const int ly2,
29270                            const float opacity=1) {
29271       typedef typename cimg::superset<tz,float>::type tzfloat;
29272       if (!is_sameXY(zbuffer))
29273         throw CImgArgumentException(_cimg_instance
29274                                     "draw_triangle() : Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
29275                                     cimg_instance,
29276                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
29277       if (texture._depth>1 || texture._spectrum<_spectrum)
29278         throw CImgArgumentException(_cimg_instance
29279                                     "draw_triangle() : Invalid specified texture (%u,%u,%u,%u,%p).",
29280                                     cimg_instance,
29281                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
29282       if (light._depth>1 || light._spectrum<_spectrum)
29283         throw CImgArgumentException(_cimg_instance
29284                                     "draw_triangle() : Invalid specified light texture (%u,%u,%u,%u,%p).",
29285                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
29286 
29287       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
29288       if (is_overlapped(texture)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,
29289                                                        +texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
29290       if (is_overlapped(light)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,
29291                                                      texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
29292       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
29293       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
29294       const int whd = _width*_height*_depth, twhd = texture._width*texture._height*texture._depth, offx = _spectrum*whd;
29295       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
29296         nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
29297       float
29298         ntx0 = tx0/z0, nty0 = ty0/z0,
29299         ntx1 = tx1/z1, nty1 = ty1/z1,
29300         ntx2 = tx2/z2, nty2 = ty2/z2;
29301       tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
29302       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1,nz0,nz1);
29303       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2,nz0,nz2);
29304       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2,nz1,nz2);
29305       if (ny0>=height() || ny2<0) return *this;
29306       float
29307         ptxl = (ntx1 - ntx0)/(ny1 - ny0),
29308         ptxr = (ntx2 - ntx0)/(ny2 - ny0),
29309         ptxn = (ntx2 - ntx1)/(ny2 - ny1),
29310         ptyl = (nty1 - nty0)/(ny1 - ny0),
29311         ptyr = (nty2 - nty0)/(ny2 - ny0),
29312         ptyn = (nty2 - nty1)/(ny2 - ny1),
29313         txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
29314         tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
29315         txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
29316         tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
29317       tzfloat
29318         pzl = (nz1 - nz0)/(ny1 - ny0),
29319         pzr = (nz2 - nz0)/(ny2 - ny0),
29320         pzn = (nz2 - nz1)/(ny2 - ny1),
29321         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
29322         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
29323       _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
29324                           nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
29325         if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
29326         int
29327           xleft = xleft0, xright = xright0,
29328           lxleft = lxleft0, lxright = lxright0,
29329           lyleft = lyleft0, lyright = lyright0;
29330         float txleft = txl, txright = txr, tyleft = tyl, tyright = tyr;
29331         tzfloat zleft = zl, zright = zr;
29332         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,lxleft,lxright,lyleft,lyright);
29333         const int
29334           dx = xright - xleft,
29335           dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
29336           dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
29337           rlx = dx?(lxright - lxleft)/dx:0,
29338           rly = dx?(lyright - lyleft)/dx:0,
29339           slx = lxright>lxleft?1:-1,
29340           sly = lyright>lyleft?1:-1,
29341           ndlx = dlx - (dx?dx*(dlx/dx):0),
29342           ndly = dly - (dx?dx*(dly/dx):0);
29343         float pentetx = (txright - txleft)/dx, pentety = (tyright - tyleft)/dx;
29344         const tzfloat pentez = (zright - zleft)/dx;
29345         int errlx = dx>>1, errly = errlx;
29346         if (xleft<0 && dx) {
29347           zleft-=xleft*(zright - zleft)/dx;
29348           lxleft-=xleft*(lxright - lxleft)/dx;
29349           lyleft-=xleft*(lyright - lyleft)/dx;
29350           txleft-=xleft*(txright - txleft)/dx;
29351           tyleft-=xleft*(tyright - tyleft)/dx;
29352         }
29353         if (xleft<0) xleft = 0;
29354         if (xright>=width()-1) xright = width()-1;
29355         T* ptrd = data(xleft,y);
29356         tz *ptrz = zbuffer.data(xleft,y);
29357         if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29358             if (zleft>=(tzfloat)*ptrz) {
29359               *ptrz = (tz)zleft;
29360               const tzfloat invz = 1/zleft;
29361               const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29362               cimg_forC(*this,c) {
29363                 const tl l = light(lxleft,lyleft,c);
29364                 *ptrd = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
29365                 ptrd+=whd; col+=twhd;
29366               }
29367               ptrd-=offx;
29368             }
29369             zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29370             lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
29371             lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
29372           } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29373             if (zleft>=(tzfloat)*ptrz) {
29374               *ptrz = (tz)zleft;
29375               const tzfloat invz = 1/zleft;
29376               const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29377               cimg_forC(*this,c) {
29378                 const tl l = light(lxleft,lyleft,c);
29379                 const T val = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
29380                 *ptrd = (T)(nopacity*val + *ptrd*copacity);
29381                 ptrd+=whd; col+=twhd;
29382               }
29383               ptrd-=offx;
29384             }
29385             zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29386             lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
29387             lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
29388           }
29389         zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
29390       }
29391       return *this;
29392     }
29393 
29394     //! Draw a 4d filled rectangle in the image instance, at coordinates (\c x0,\c y0,\c z0,\c c0)-(\c x1,\c y1,\c z1,\c c1).
29395     /**
29396        \param x0 X-coordinate of the upper-left rectangle corner.
29397        \param y0 Y-coordinate of the upper-left rectangle corner.
29398        \param z0 Z-coordinate of the upper-left rectangle corner.
29399        \param c0 C-coordinate of the upper-left rectangle corner.
29400        \param x1 X-coordinate of the lower-right rectangle corner.
29401        \param y1 Y-coordinate of the lower-right rectangle corner.
29402        \param z1 Z-coordinate of the lower-right rectangle corner.
29403        \param c1 C-coordinate of the lower-right rectangle corner.
29404        \param val Scalar value used to fill the rectangle area.
29405        \param opacity Drawing opacity (optional).
29406        \note
29407        - Clipping is supported.
29408     **/
29409     CImg<T>& draw_rectangle(const int x0, const int y0, const int z0, const int c0,
29410                             const int x1, const int y1, const int z1, const int c1,
29411                             const T val, const float opacity=1) {
29412       if (is_empty()) return *this;
29413       const bool bx = (x0<x1), by = (y0<y1), bz = (z0<z1), bc = (c0<c1);
29414       const int
29415         nx0 = bx?x0:x1, nx1 = bx?x1:x0,
29416         ny0 = by?y0:y1, ny1 = by?y1:y0,
29417         nz0 = bz?z0:z1, nz1 = bz?z1:z0,
29418         nc0 = bc?c0:c1, nc1 = bc?c1:c0;
29419       const int
29420         lX = (1 + nx1 - nx0) + (nx1>=width()?width() - 1 - nx1:0) + (nx0<0?nx0:0),
29421         lY = (1 + ny1 - ny0) + (ny1>=height()?height() - 1 - ny1:0) + (ny0<0?ny0:0),
29422         lZ = (1 + nz1 - nz0) + (nz1>=depth()?depth() - 1 - nz1:0) + (nz0<0?nz0:0),
29423         lC = (1 + nc1 - nc0) + (nc1>=spectrum()?spectrum() - 1 - nc1:0) + (nc0<0?nc0:0);
29424       const unsigned int offX = _width - lX, offY = _width*(_height - lY), offZ = _width*_height*(_depth - lZ);
29425       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
29426       T *ptrd = data(nx0<0?0:nx0,ny0<0?0:ny0,nz0<0?0:nz0,nc0<0?0:nc0);
29427       if (lX>0 && lY>0 && lZ>0 && lC>0)
29428         for (int v = 0; v<lC; ++v) {
29429           for (int z = 0; z<lZ; ++z) {
29430             for (int y = 0; y<lY; ++y) {
29431               if (opacity>=1) {
29432                 if (sizeof(T)!=1) { for (int x = 0; x<lX; ++x) *(ptrd++) = val; ptrd+=offX; }
29433                 else { std::memset(ptrd,(int)val,lX); ptrd+=_width; }
29434               } else { for (int x = 0; x<lX; ++x) { *ptrd = (T)(nopacity*val + *ptrd*copacity); ++ptrd; } ptrd+=offX; }
29435             }
29436             ptrd+=offY;
29437           }
29438           ptrd+=offZ;
29439         }
29440       return *this;
29441     }
29442 
29443     //! Draw a 3d filled colored rectangle in the image instance, at coordinates (\c x0,\c y0,\c z0)-(\c x1,\c y1,\c z1).
29444     /**
29445        \param x0 X-coordinate of the upper-left rectangle corner.
29446        \param y0 Y-coordinate of the upper-left rectangle corner.
29447        \param z0 Z-coordinate of the upper-left rectangle corner.
29448        \param x1 X-coordinate of the lower-right rectangle corner.
29449        \param y1 Y-coordinate of the lower-right rectangle corner.
29450        \param z1 Z-coordinate of the lower-right rectangle corner.
29451        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
29452        \param opacity Drawing opacity (optional).
29453        \note
29454        - Clipping is supported.
29455     **/
29456     template<typename tc>
29457     CImg<T>& draw_rectangle(const int x0, const int y0, const int z0,
29458                             const int x1, const int y1, const int z1,
29459                             const tc *const color, const float opacity=1) {
29460       if (!color)
29461         throw CImgArgumentException(_cimg_instance
29462                                     "draw_rectangle : Specified color is (null).",
29463                                     cimg_instance);
29464 
29465       if (is_empty()) return *this;
29466       cimg_forC(*this,c) draw_rectangle(x0,y0,z0,c,x1,y1,z1,c,color[c],opacity);
29467       return *this;
29468     }
29469 
29470     //! Draw a 3d outlined colored rectangle in the image instance.
29471     template<typename tc>
29472     CImg<T>& draw_rectangle(const int x0, const int y0, const int z0,
29473                             const int x1, const int y1, const int z1,
29474                             const tc *const color, const float opacity,
29475                             const unsigned int pattern) {
29476       return draw_line(x0,y0,z0,x1,y0,z0,color,opacity,pattern,true).
29477         draw_line(x1,y0,z0,x1,y1,z0,color,opacity,pattern,false).
29478         draw_line(x1,y1,z0,x0,y1,z0,color,opacity,pattern,false).
29479         draw_line(x0,y1,z0,x0,y0,z0,color,opacity,pattern,false).
29480         draw_line(x0,y0,z1,x1,y0,z1,color,opacity,pattern,true).
29481         draw_line(x1,y0,z1,x1,y1,z1,color,opacity,pattern,false).
29482         draw_line(x1,y1,z1,x0,y1,z1,color,opacity,pattern,false).
29483         draw_line(x0,y1,z1,x0,y0,z1,color,opacity,pattern,false).
29484         draw_line(x0,y0,z0,x0,y0,z1,color,opacity,pattern,true).
29485         draw_line(x1,y0,z0,x1,y0,z1,color,opacity,pattern,true).
29486         draw_line(x1,y1,z0,x1,y1,z1,color,opacity,pattern,true).
29487         draw_line(x0,y1,z0,x0,y1,z1,color,opacity,pattern,true);
29488     }
29489 
29490     //! Draw a 2d filled colored rectangle in the image instance, at coordinates (\c x0,\c y0)-(\c x1,\c y1).
29491     /**
29492        \param x0 X-coordinate of the upper-left rectangle corner.
29493        \param y0 Y-coordinate of the upper-left rectangle corner.
29494        \param x1 X-coordinate of the lower-right rectangle corner.
29495        \param y1 Y-coordinate of the lower-right rectangle corner.
29496        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
29497        \param opacity Drawing opacity (optional).
29498        \note
29499        - Clipping is supported.
29500     **/
29501     template<typename tc>
29502     CImg<T>& draw_rectangle(const int x0, const int y0,
29503                             const int x1, const int y1,
29504                             const tc *const color, const float opacity=1) {
29505       return draw_rectangle(x0,y0,0,x1,y1,_depth-1,color,opacity);
29506     }
29507 
29508     //! Draw a 2d outlined colored rectangle.
29509     template<typename tc>
29510     CImg<T>& draw_rectangle(const int x0, const int y0,
29511                             const int x1, const int y1,
29512                             const tc *const color, const float opacity,
29513                             const unsigned int pattern) {
29514       if (is_empty()) return *this;
29515       if (y0==y1) return draw_line(x0,y0,x1,y0,color,opacity,pattern,true);
29516       if (x0==x1) return draw_line(x0,y0,x0,y1,color,opacity,pattern,true);
29517       const bool bx = (x0<x1), by = (y0<y1);
29518       const int
29519         nx0 = bx?x0:x1, nx1 = bx?x1:x0,
29520         ny0 = by?y0:y1, ny1 = by?y1:y0;
29521       if (ny1==ny0+1) return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true).
29522                       draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false);
29523       return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true).
29524         draw_line(nx1,ny0+1,nx1,ny1-1,color,opacity,pattern,false).
29525         draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false).
29526         draw_line(nx0,ny1-1,nx0,ny0+1,color,opacity,pattern,false);
29527     }
29528 
29529     //! Draw a filled polygon in the image instance.
29530     template<typename t, typename tc>
29531     CImg<T>& draw_polygon(const CImg<t>& points,
29532                           const tc *const color, const float opacity=1) {
29533       if (!color)
29534         throw CImgArgumentException(_cimg_instance
29535                                     "draw_polygon() : Specified color is (null).",
29536                                     cimg_instance);
29537 
29538       if (is_empty() || !points || points._width<3) return *this;
29539 
29540       // Normalize 2d input coordinates.
29541       CImg<intT> npoints(points._width,2);
29542       int x = npoints(0,0) = (int)points(0,0), y = npoints(0,1) = (int)points(0,1);
29543       unsigned int nb_points = 1;
29544       for (unsigned int p = 1; p<points._width; ++p) {
29545         const int nx = (int)points(p,0), ny = (int)points(p,1);
29546         if (nx!=x || ny!=y) { npoints(nb_points,0) = nx; npoints(nb_points++,1) = ny; x = nx; y = ny; }
29547       }
29548 
29549       if (nb_points==3) return draw_triangle((int)npoints(0,0),(int)npoints(0,1),
29550                                              (int)npoints(1,0),(int)npoints(1,1),
29551                                              (int)npoints(2,0),(int)npoints(2,1),color,opacity);
29552       // Draw polygon segments.
29553       _draw_scanline(color,opacity);
29554       int
29555         xmax = 0, xmin = (int)npoints.get_shared_points(0,nb_points-1,0).min_max(xmax),
29556         ymax = 0, ymin = (int)npoints.get_shared_points(0,nb_points-1,1).min_max(ymax);
29557       if (xmax<0 || xmin>=width() || ymax<0 || ymin>=height()) return *this;
29558       if (ymin==ymax) return _draw_scanline(xmin,xmax,ymin,color,opacity);
29559       const unsigned int
29560         nymin = ymin<0?0:(unsigned int)ymin,
29561         nymax = ymax>=height()?_height-1:(unsigned int)ymax,
29562         dy = 1 + nymax - nymin;
29563       CImg<intT> X(1+2*nb_points,dy,1,1,0), tmp;
29564       int cx = (int)npoints(0,0), cy = (int)npoints(0,1);
29565       unsigned int cp = 0;
29566       for (unsigned int p = 0; p<nb_points; ++p) {
29567         const unsigned int np = (p!=nb_points-1)?p+1:0, ap = (np!=nb_points-1)?np+1:0;
29568         const int
29569           nx = (int)npoints(np,0), ny = (int)npoints(np,1), ay = (int)npoints(ap,1),
29570           y0 = cy - nymin, y1 = ny - nymin;
29571         if (y0!=y1) {
29572           const int countermin = ((ny<ay && cy<ny) || (ny>ay && cy>ny))?1:0;
29573           for (int x = cx, y = y0, _sx = 1, _sy = 1,
29574                  _dx = nx>cx?nx-cx:((_sx=-1),cx-nx),
29575                  _dy = y1>y0?y1-y0:((_sy=-1),y0-y1),
29576                  _counter = ((_dx-=_dy?_dy*(_dx/_dy):0),_dy),
29577                  _err = _dx>>1,
29578                  _rx = _dy?(nx-cx)/_dy:0;
29579                _counter>=countermin;
29580                --_counter, y+=_sy, x+=_rx + ((_err-=_dx)<0?_err+=_dy,_sx:0))
29581             if (y>=0 && y<(int)dy) X(++X(0,y),y) = x;
29582           cp = np; cx = nx; cy = ny;
29583         } else {
29584           const int pp = (cp?cp-1:nb_points-1), py = (int)npoints(pp,1);
29585           if (y0>=0 && y0<(int)dy && (!p || (cy>py && ay>cy) || (cy<py && ay<cy))) X(++X(0,y0),y0) = nx;
29586           if (cy!=ay) { cp = np; cx = nx; cy = ny; }
29587         }
29588       }
29589 
29590       // Draw polygon scanlines.
29591       for (int y = 0; y<(int)dy; ++y) {
29592         tmp.assign(X.data(1,y),X(0,y),1,1,1,true).sort();
29593         for (int i = 1; i<=X(0,y); ) {
29594           const int xb = X(i++,y), xe = X(i++,y);
29595           _draw_scanline(xb,xe,nymin+y,color,opacity);
29596         }
29597       }
29598 
29599       return *this;
29600     }
29601 
29602     //! Draw a outlined polygon in the image instance.
29603     template<typename t, typename tc>
29604     CImg<T>& draw_polygon(const CImg<t>& points,
29605                           const tc *const color, const float opacity, const unsigned int pattern) {
29606       if (is_empty() || !points || points._width<3) return *this;
29607       bool ninit_hatch = true;
29608       switch (points._height) {
29609       case 0 : case 1 :
29610         throw CImgArgumentException(_cimg_instance
29611                                     "draw_polygon() : Invalid specified point set.",
29612                                     cimg_instance);
29613       case 2 : { // 2d version.
29614         CImg<intT> npoints(points._width,2);
29615         int x = npoints(0,0) = (int)points(0,0), y = npoints(0,1) = (int)points(0,1);
29616         unsigned int nb_points = 1;
29617         for (unsigned int p = 1; p<points._width; ++p) {
29618           const int nx = (int)points(p,0), ny = (int)points(p,1);
29619           if (nx!=x || ny!=y) { npoints(nb_points,0) = nx; npoints(nb_points++,1) = ny; x = nx; y = ny; }
29620         }
29621         const int x0 = (int)npoints(0,0), y0 = (int)npoints(0,1);
29622         int ox = x0, oy = y0;
29623         for (unsigned int i = 1; i<nb_points; ++i) {
29624           const int x = (int)npoints(i,0), y = (int)npoints(i,1);
29625           draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch);
29626           ninit_hatch = false;
29627           ox = x; oy = y;
29628         }
29629         draw_line(ox,oy,x0,y0,color,opacity,pattern,false);
29630       } break;
29631       default : { // 3d version.
29632         CImg<intT> npoints(points._width,3);
29633         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);
29634         unsigned int nb_points = 1;
29635         for (unsigned int p = 1; p<points._width; ++p) {
29636           const int nx = (int)points(p,0), ny = (int)points(p,1), nz = (int)points(p,2);
29637           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; }
29638         }
29639         const int x0 = (int)npoints(0,0), y0 = (int)npoints(0,1), z0 = (int)npoints(0,2);
29640         int ox = x0, oy = y0, oz = z0;
29641         for (unsigned int i = 1; i<nb_points; ++i) {
29642           const int x = (int)npoints(i,0), y = (int)npoints(i,1), z = (int)npoints(i,2);
29643           draw_line(ox,oy,oz,x,y,z,color,opacity,pattern,ninit_hatch);
29644           ninit_hatch = false;
29645           ox = x; oy = y; oz = z;
29646         }
29647         draw_line(ox,oy,oz,x0,y0,z0,color,opacity,pattern,false);
29648       }
29649       }
29650       return *this;
29651     }
29652 
29653     //! Draw a filled circle.
29654     /**
29655        \param x0 X-coordinate of the circle center.
29656        \param y0 Y-coordinate of the circle center.
29657        \param radius  Circle radius.
29658        \param color Array of spectrum() values of type \c T, defining the drawing color.
29659        \param opacity Drawing opacity.
29660        \note
29661        - Circle version of the Bresenham's algorithm is used.
29662     **/
29663     template<typename tc>
29664     CImg<T>& draw_circle(const int x0, const int y0, int radius,
29665                          const tc *const color, const float opacity=1) {
29666       if (!color)
29667         throw CImgArgumentException(_cimg_instance
29668                                     "draw_circle : Specified color is (null).",
29669                                     cimg_instance);
29670 
29671       if (is_empty()) return *this;
29672       _draw_scanline(color,opacity);
29673       if (radius<0 || x0-radius>=width() || y0+radius<0 || y0-radius>=height()) return *this;
29674       if (y0>=0 && y0<height()) _draw_scanline(x0-radius,x0+radius,y0,color,opacity);
29675       for (int f = 1-radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x<y; ) {
29676         if (f>=0) {
29677           const int x1 = x0-x, x2 = x0+x, y1 = y0-y, y2 = y0+y;
29678           if (y1>=0 && y1<height()) _draw_scanline(x1,x2,y1,color,opacity);
29679           if (y2>=0 && y2<height()) _draw_scanline(x1,x2,y2,color,opacity);
29680           f+=(ddFy+=2); --y;
29681         }
29682         const bool no_diag = y!=(x++);
29683         ++(f+=(ddFx+=2));
29684         const int x1 = x0-y, x2 = x0+y, y1 = y0-x, y2 = y0+x;
29685         if (no_diag) {
29686           if (y1>=0 && y1<height()) _draw_scanline(x1,x2,y1,color,opacity);
29687           if (y2>=0 && y2<height()) _draw_scanline(x1,x2,y2,color,opacity);
29688         }
29689       }
29690       return *this;
29691     }
29692 
29693     //! Draw an outlined circle.
29694     /**
29695        \param x0 X-coordinate of the circle center.
29696        \param y0 Y-coordinate of the circle center.
29697        \param radius Circle radius.
29698        \param color Array of spectrum() values of type \c T, defining the drawing color.
29699        \param opacity Drawing opacity.
29700     **/
29701     template<typename tc>
29702     CImg<T>& draw_circle(const int x0, const int y0, int radius,
29703                          const tc *const color, const float opacity,
29704                          const unsigned int) {
29705       if (!color)
29706         throw CImgArgumentException(_cimg_instance
29707                                     "draw_circle : Specified color is (null).",
29708                                     cimg_instance);
29709 
29710       if (is_empty()) return *this;
29711       if (radius<0 || x0-radius>=width() || y0+radius<0 || y0-radius>=height()) return *this;
29712       if (!radius) return draw_point(x0,y0,color,opacity);
29713       draw_point(x0-radius,y0,color,opacity).draw_point(x0+radius,y0,color,opacity).
29714         draw_point(x0,y0-radius,color,opacity).draw_point(x0,y0+radius,color,opacity);
29715       if (radius==1) return *this;
29716       for (int f = 1-radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x<y; ) {
29717         if (f>=0) { f+=(ddFy+=2); --y; }
29718         ++x; ++(f+=(ddFx+=2));
29719         if (x!=y+1) {
29720           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;
29721           draw_point(x1,y1,color,opacity).draw_point(x1,y2,color,opacity).
29722             draw_point(x2,y1,color,opacity).draw_point(x2,y2,color,opacity);
29723           if (x!=y)
29724             draw_point(x3,y3,color,opacity).draw_point(x4,y4,color,opacity).
29725               draw_point(x4,y3,color,opacity).draw_point(x3,y4,color,opacity);
29726         }
29727       }
29728       return *this;
29729     }
29730 
29731     // Draw a 2d ellipse (inner routine).
29732     template<typename tc>
29733     CImg<T>& _draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
29734                            const tc *const color, const float opacity,
29735                            const unsigned int pattern) {
29736       if (!color)
29737         throw CImgArgumentException(_cimg_instance
29738                                     "draw_ellipse : Specified color is (null).",
29739                                     cimg_instance);
29740       if (is_empty()) return *this;
29741       if (r1<=0 || r2<=0) return draw_point(x0,y0,color,opacity);
29742       _draw_scanline(color,opacity);
29743       const float
29744         nr1 = cimg::abs(r1), nr2 = cimg::abs(r2),
29745         nangle = (float)(angle*cimg::PI/180),
29746         u = (float)std::cos(nangle),
29747         v = (float)std::sin(nangle),
29748         rmax = cimg::max(nr1,nr2),
29749         l1 = (float)std::pow(rmax/(nr1>0?nr1:1e-6),2),
29750         l2 = (float)std::pow(rmax/(nr2>0?nr2:1e-6),2),
29751         a = l1*u*u + l2*v*v,
29752         b = u*v*(l1-l2),
29753         c = l1*v*v + l2*u*u;
29754       const int
29755         yb = (int)std::sqrt(a*rmax*rmax/(a*c - b*b)),
29756         tymin = y0 - yb - 1,
29757         tymax = y0 + yb + 1,
29758         ymin = tymin<0?0:tymin,
29759         ymax = tymax>=height()?_height-1:tymax;
29760       int oxmin = 0, oxmax = 0;
29761       bool first_line = true;
29762       for (int y = ymin; y<=ymax; ++y) {
29763         const float
29764           Y = y - y0 + (y<y0?0.5f:-0.5f),
29765           delta = b*b*Y*Y - a*(c*Y*Y - rmax*rmax),
29766           sdelta = delta>0?(float)std::sqrt(delta)/a:0.0f,
29767           bY = b*Y/a,
29768           fxmin = x0 - 0.5f - bY - sdelta,
29769           fxmax = x0 + 0.5f - bY + sdelta;
29770         const int xmin = (int)fxmin, xmax = (int)fxmax;
29771         if (!pattern) _draw_scanline(xmin,xmax,y,color,opacity);
29772         else {
29773           if (first_line) {
29774             if (y0-yb>=0) _draw_scanline(xmin,xmax,y,color,opacity);
29775             else draw_point(xmin,y,color,opacity).draw_point(xmax,y,color,opacity);
29776             first_line = false;
29777           } else {
29778             if (xmin<oxmin) _draw_scanline(xmin,oxmin-1,y,color,opacity);
29779             else _draw_scanline(oxmin+(oxmin==xmin?0:1),xmin,y,color,opacity);
29780             if (xmax<oxmax) _draw_scanline(xmax,oxmax-1,y,color,opacity);
29781             else _draw_scanline(oxmax+(oxmax==xmax?0:1),xmax,y,color,opacity);
29782             if (y==tymax) _draw_scanline(xmin+1,xmax-1,y,color,opacity);
29783           }
29784         }
29785         oxmin = xmin; oxmax = xmax;
29786       }
29787       return *this;
29788     }
29789 
29790     //! Draw a filled ellipse.
29791     /**
29792        \param x0 = X-coordinate of the ellipse center.
29793        \param y0 = Y-coordinate of the ellipse center.
29794        \param r1 = First radius of the ellipse.
29795        \param r2 = Second radius of the ellipse.
29796        \param angle = Angle of the first radius.
29797        \param color = array of spectrum() values of type \c T, defining the drawing color.
29798        \param opacity = opacity of the drawing.
29799     **/
29800     template<typename tc>
29801     CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
29802                           const tc *const color, const float opacity=1) {
29803       return _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,0U);
29804     }
29805 
29806     //! Draw a filled ellipse.
29807     /**
29808        \param x0 = X-coordinate of the ellipse center.
29809        \param y0 = Y-coordinate of the ellipse center.
29810        \param tensor = Diffusion tensor describing the ellipse.
29811        \param color = array of spectrum() values of type \c T, defining the drawing color.
29812        \param opacity = opacity of the drawing.
29813     **/
29814     template<typename t, typename tc>
29815     CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
29816                           const tc *const color, const float opacity=1) {
29817       CImgList<t> eig = tensor.get_symmetric_eigen();
29818       const CImg<t> &val = eig[0], &vec = eig[1];
29819       return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)),
29820                           std::atan2(vec(0,1),vec(0,0))*180/cimg::PI,
29821                           color,opacity);
29822     }
29823 
29824     //! Draw an outlined ellipse.
29825     /**
29826        \param x0 = X-coordinate of the ellipse center.
29827        \param y0 = Y-coordinate of the ellipse center.
29828        \param r1 = First radius of the ellipse.
29829        \param r2 = Second radius of the ellipse.
29830        \param ru = X-coordinate of the orientation vector related to the first radius.
29831        \param rv = Y-coordinate of the orientation vector related to the first radius.
29832        \param color = array of spectrum() values of type \c T, defining the drawing color.
29833        \param pattern = If zero, the ellipse is filled, else pattern is an integer whose bits describe the outline pattern.
29834        \param opacity = opacity of the drawing.
29835     **/
29836     template<typename tc>
29837     CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
29838                           const tc *const color, const float opacity, const unsigned int pattern) {
29839       if (pattern) _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,pattern);
29840       return *this;
29841     }
29842 
29843     //! Draw an outlined ellipse.
29844     /**
29845        \param x0 = X-coordinate of the ellipse center.
29846        \param y0 = Y-coordinate of the ellipse center.
29847        \param tensor = Diffusion tensor describing the ellipse.
29848        \param color = array of spectrum() values of type \c T, defining the drawing color.
29849 
29850        \param opacity = opacity of the drawing.
29851     **/
29852     template<typename t, typename tc>
29853     CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
29854                           const tc *const color, const float opacity,
29855                           const unsigned int pattern) {
29856       CImgList<t> eig = tensor.get_symmetric_eigen();
29857       const CImg<t> &val = eig[0], &vec = eig[1];
29858       return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)),
29859                           std::atan2(vec(0,1),vec(0,0))*180/cimg::PI,
29860                           color,opacity,pattern);
29861     }
29862 
29863     //! Draw an image.
29864     /**
29865        \param sprite Sprite image.
29866        \param x0 X-coordinate of the sprite position.
29867        \param y0 Y-coordinate of the sprite position.
29868        \param z0 Z-coordinate of the sprite position.
29869        \param c0 C-coordinate of the sprite position.
29870        \param opacity Drawing opacity (optional).
29871        \note
29872        - Clipping is supported.
29873     **/
29874     template<typename t>
29875     CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
29876                         const CImg<t>& sprite, const float opacity=1) {
29877       if (!sprite)
29878         throw CImgArgumentException(_cimg_instance
29879                                     "draw_image() : Empty specified sprite.",
29880                                     cimg_instance);
29881       if (is_empty()) return *this;
29882       if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity);
29883       if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1) return assign(sprite,false);
29884       const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bc = (c0<0);
29885       const int
29886         lX = sprite.width() - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0) + (bx?x0:0),
29887         lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0),
29888         lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0),
29889         lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0);
29890       const t
29891         *ptrs = sprite._data -
29892         (bx?x0:0) -
29893         (by?y0*sprite.width():0) -
29894         (bz?z0*sprite.width()*sprite.height():0) -
29895         (bc?c0*sprite.width()*sprite.height()*sprite.depth():0);
29896       const unsigned int
29897         offX = _width - lX, soffX = sprite._width - lX,
29898         offY = _width*(_height - lY), soffY = sprite._width*(sprite._height - lY),
29899         offZ = _width*_height*(_depth - lZ), soffZ = sprite._width*sprite._height*(sprite._depth - lZ);
29900       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
29901       if (lX>0 && lY>0 && lZ>0 && lC>0) {
29902         T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0);
29903         for (int v = 0; v<lC; ++v) {
29904           for (int z = 0; z<lZ; ++z) {
29905             for (int y = 0; y<lY; ++y) {
29906               if (opacity>=1) for (int x = 0; x<lX; ++x) *(ptrd++) = (T)*(ptrs++);
29907               else for (int x = 0; x<lX; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; }
29908               ptrd+=offX; ptrs+=soffX;
29909             }
29910             ptrd+=offY; ptrs+=soffY;
29911           }
29912           ptrd+=offZ; ptrs+=soffZ;
29913         }
29914       }
29915       return *this;
29916     }
29917 
29918     // Optimized version (internal).
29919     CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
29920                         const CImg<T>& sprite, const float opacity=1) {
29921       if (!sprite)
29922         throw CImgArgumentException(_cimg_instance
29923                                     "draw_image() : Empty specified sprite.",
29924                                     cimg_instance);
29925 
29926       if (is_empty()) return *this;
29927       if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity);
29928       if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1) return assign(sprite,false);
29929       const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bc = (c0<0);
29930       const int
29931         lX = sprite.width() - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0) + (bx?x0:0),
29932         lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0),
29933         lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0),
29934         lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0);
29935       const T
29936         *ptrs = sprite._data -
29937         (bx?x0:0) -
29938         (by?y0*sprite.width():0) -
29939         (bz?z0*sprite.width()*sprite.height():0) -
29940         (bc?c0*sprite.width()*sprite.height()*sprite.depth():0);
29941       const unsigned int
29942         offX = _width - lX, soffX = sprite._width - lX,
29943         offY = _width*(_height - lY), soffY = sprite._width*(sprite._height - lY),
29944         offZ = _width*_height*(_depth - lZ), soffZ = sprite._width*sprite._height*(sprite._depth - lZ),
29945         slX = lX*sizeof(T);
29946       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
29947       if (lX>0 && lY>0 && lZ>0 && lC>0) {
29948         T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0);
29949         for (int v = 0; v<lC; ++v) {
29950           for (int z = 0; z<lZ; ++z) {
29951             if (opacity>=1) for (int y = 0; y<lY; ++y) { std::memcpy(ptrd,ptrs,slX); ptrd+=_width; ptrs+=sprite._width; }
29952             else for (int y = 0; y<lY; ++y) {
29953               for (int x = 0; x<lX; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; }
29954               ptrd+=offX; ptrs+=soffX;
29955             }
29956             ptrd+=offY; ptrs+=soffY;
29957           }
29958           ptrd+=offZ; ptrs+=soffZ;
29959         }
29960       }
29961       return *this;
29962     }
29963 
29964     //! Draw an image.
29965     template<typename t>
29966     CImg<T>& draw_image(const int x0, const int y0, const int z0,
29967                         const CImg<t>& sprite, const float opacity=1) {
29968       return draw_image(x0,y0,z0,0,sprite,opacity);
29969     }
29970 
29971     //! Draw an image.
29972     template<typename t>
29973     CImg<T>& draw_image(const int x0, const int y0,
29974                         const CImg<t>& sprite, const float opacity=1) {
29975       return draw_image(x0,y0,0,sprite,opacity);
29976     }
29977 
29978     //! Draw an image.
29979     template<typename t>
29980     CImg<T>& draw_image(const int x0,
29981                         const CImg<t>& sprite, const float opacity=1) {
29982       return draw_image(x0,0,sprite,opacity);
29983     }
29984 
29985     //! Draw an image.
29986     template<typename t>
29987     CImg<T>& draw_image(const CImg<t>& sprite, const float opacity=1) {
29988       return draw_image(0,sprite,opacity);
29989     }
29990 
29991     //! Draw a sprite image in the image instance (masked version).
29992     /**
29993        \param sprite Sprite image.
29994        \param mask Mask image.
29995        \param x0 X-coordinate of the sprite position in the image instance.
29996        \param y0 Y-coordinate of the sprite position in the image instance.
29997        \param z0 Z-coordinate of the sprite position in the image instance.
29998        \param c0 C-coordinate of the sprite position in the image instance.
29999        \param mask_valmax Maximum pixel value of the mask image \c mask (optional).
30000        \param opacity Drawing opacity.
30001        \note
30002        - Pixel values of \c mask set the opacity of the corresponding pixels in \c sprite.
30003        - Clipping is supported.
30004        - Dimensions along x,y and z of \p sprite and \p mask must be the same.
30005     **/
30006     template<typename ti, typename tm>
30007     CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
30008                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
30009                         const float mask_valmax=1) {
30010       if (!sprite)
30011         throw CImgArgumentException(_cimg_instance
30012                                     "draw_image() : Empty specified sprite.",
30013                                     cimg_instance);
30014       if (!mask)
30015         throw CImgArgumentException(_cimg_instance
30016                                     "draw_image() : Empty specified mask.",
30017                                     cimg_instance);
30018 
30019       if (is_empty()) return *this;
30020       if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,mask,opacity,mask_valmax);
30021       if (is_overlapped(mask))   return draw_image(x0,y0,z0,c0,sprite,+mask,opacity,mask_valmax);
30022       if (mask._width!=sprite._width || mask._height!=sprite._height || mask._depth!=sprite._depth)
30023         throw CImgArgumentException(_cimg_instance
30024                                     "draw_image() : Sprite (%u,%u,%u,%u,%p) and mask (%u,%u,%u,%u,%p) have incompatible dimensions.",
30025                                     cimg_instance,
30026                                     sprite._width,sprite._height,sprite._depth,sprite._spectrum,sprite._data,
30027                                     mask._width,mask._height,mask._depth,mask._spectrum,mask._data);
30028 
30029       const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bc = (c0<0);
30030       const int
30031         lX = sprite.width() - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0) + (bx?x0:0),
30032         lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0),
30033         lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0),
30034         lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0);
30035       const int
30036         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),
30037         ssize = mask.width()*mask.height()*mask.depth();
30038       const ti *ptrs = sprite._data + coff;
30039       const tm *ptrm = mask._data   + coff;
30040       const unsigned int
30041         offX = _width - lX, soffX = sprite._width - lX,
30042         offY = _width*(_height - lY), soffY = sprite._width*(sprite._height - lY),
30043         offZ = _width*_height*(_depth - lZ), soffZ = sprite._width*sprite._height*(sprite._depth - lZ);
30044       if (lX>0 && lY>0 && lZ>0 && lC>0) {
30045         T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0);
30046         for (int c = 0; c<lC; ++c) {
30047           ptrm = mask._data + (ptrm - mask._data)%ssize;
30048           for (int z = 0; z<lZ; ++z) {
30049             for (int y = 0; y<lY; ++y) {
30050               for (int x = 0; x<lX; ++x) {
30051                 const float mopacity = (float)(*(ptrm++)*opacity),
30052                   nopacity = cimg::abs(mopacity), copacity = mask_valmax - cimg::max(mopacity,0);
30053                 *ptrd = (T)((nopacity*(*(ptrs++)) + *ptrd*copacity)/mask_valmax);
30054                 ++ptrd;
30055               }
30056               ptrd+=offX; ptrs+=soffX; ptrm+=soffX;
30057             }
30058             ptrd+=offY; ptrs+=soffY; ptrm+=soffY;
30059           }
30060           ptrd+=offZ; ptrs+=soffZ; ptrm+=soffZ;
30061         }
30062       }
30063       return *this;
30064     }
30065 
30066     //! Draw an image.
30067     template<typename ti, typename tm>
30068     CImg<T>& draw_image(const int x0, const int y0, const int z0,
30069                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
30070                         const float mask_valmax=1) {
30071       return draw_image(x0,y0,z0,0,sprite,mask,opacity,mask_valmax);
30072     }
30073 
30074     //! Draw an image.
30075     template<typename ti, typename tm>
30076     CImg<T>& draw_image(const int x0, const int y0,
30077                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
30078                         const float mask_valmax=1) {
30079       return draw_image(x0,y0,0,sprite,mask,opacity,mask_valmax);
30080     }
30081 
30082     //! Draw an image.
30083     template<typename ti, typename tm>
30084     CImg<T>& draw_image(const int x0,
30085                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
30086                         const float mask_valmax=1) {
30087       return draw_image(x0,0,sprite,mask,opacity,mask_valmax);
30088     }
30089 
30090     //! Draw an image.
30091     template<typename ti, typename tm>
30092     CImg<T>& draw_image(const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
30093                         const float mask_valmax=1) {
30094       return draw_image(0,sprite,mask,opacity,mask_valmax);
30095     }
30096 
30097     //! Draw a text.
30098     /**
30099        \param x0 X-coordinate of the text in the image instance.
30100        \param y0 Y-coordinate of the text in the image instance.
30101        \param foreground_color Array of spectrum() values of type \c T, defining the foreground color (0 means 'transparent').
30102        \param background_color Array of spectrum() values of type \c T, defining the background color (0 means 'transparent').
30103        \param font Font used for drawing text.
30104        \param opacity Drawing opacity.
30105        \param format 'printf'-style format string, followed by arguments.
30106        \note Clipping is supported.
30107     **/
30108     template<typename tc1, typename tc2, typename t>
30109     CImg<T>& draw_text(const int x0, const int y0,
30110                        const char *const text,
30111                        const tc1 *const foreground_color, const tc2 *const background_color,
30112                        const float opacity, const CImgList<t>& font, ...) {
30113       if (!font) return *this;
30114       char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font);
30115       cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap);
30116       return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font);
30117     }
30118 
30119     template<typename tc, typename t>
30120     CImg<T>& draw_text(const int x0, const int y0,
30121                        const char *const text,
30122                        const tc *const foreground_color, const int,
30123                        const float opacity, const CImgList<t>& font, ...) {
30124       if (!font) return *this;
30125       char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font);
30126       cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap);
30127       return _draw_text(x0,y0,tmp,foreground_color,(tc*)0,opacity,font);
30128     }
30129 
30130     template<typename tc, typename t>
30131     CImg<T>& draw_text(const int x0, const int y0,
30132                        const char *const text,
30133                        const int, const tc *const background_color,
30134                        const float opacity, const CImgList<t>& font, ...) {
30135       if (!font) return *this;
30136       char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font);
30137       cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap);
30138       return _draw_text(x0,y0,tmp,(tc*)0,background_color,opacity,font);
30139     }
30140 
30141     //! Draw a text.
30142     /**
30143        \param x0 X-coordinate of the text in the image instance.
30144        \param y0 Y-coordinate of the text in the image instance.
30145        \param foreground_color Array of spectrum() values of type \c T, defining the foreground color (0 means 'transparent').
30146        \param background_color Array of spectrum() values of type \c T, defining the background color (0 means 'transparent').
30147        \param font_size Size of the font (exact match for 13,24,32,57).
30148        \param opacity Drawing opacity.
30149        \param format 'printf'-style format string, followed by arguments.
30150        \note Clipping is supported.
30151     **/
30152     template<typename tc1, typename tc2>
30153     CImg<T>& draw_text(const int x0, const int y0,
30154                        const char *const text,
30155                        const tc1 *const foreground_color, const tc2 *const background_color,
30156                        const float opacity=1, const unsigned int font_height=13, ...) {
30157       if (!font_height) return *this;
30158       char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font_height); cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap);
30159       static CImgList<floatT> font;
30160       const unsigned int
30161         ref_height = font_height<=13?13:font_height<=28?24:font_height<=32?32:57,
30162         padding_x = font_height<=18?1:font_height<=32?2:3;
30163       if (!font || font[0]._height!=font_height) {
30164         font = CImgList<floatT>::font(ref_height,true);
30165         font[0].assign(1,font_height);
30166         if (ref_height==font_height) cimglist_for(font,l) font[l].resize(font[l]._width + padding_x,-100,-100,-100,0);
30167       }
30168       if (is_empty()) {
30169         if (font[0]._spectrum!=1) cimglist_for_in(font,0,255,l) font[l].channel(0);
30170       } else if (font[0]._spectrum<_spectrum) cimglist_for_in(font,0,255,l) font[l].resize(-100,-100,1,_spectrum);
30171       if (ref_height!=font_height) for (const char *ptrs = tmp; *ptrs; ++ptrs) {
30172           const unsigned int __c = (unsigned int)(unsigned char)*ptrs, _c = (__c=='\t')?' ':__c;
30173           if (_c<font._width) {
30174             CImg<floatT> &c = font[_c];
30175             if (c._height!=font_height) {
30176               c.resize(cimg::max(1U,c._width*font_height/c._height),font_height,-100,-100,c._height>font_height?2:3);
30177               c.resize(c._width + padding_x,-100,-100,-100,0);
30178             }
30179           }
30180           if (_c+256U<font._width) {
30181             CImg<floatT> &c = font[_c+256];
30182             if (c._height!=font_height) {
30183               c.resize(cimg::max(1U,c._width*font_height/c._height),font_height,-100,-100,c._height>font_height?2:3);
30184               c.resize(c._width + padding_x,-100,-100,-100,0);
30185             }
30186           }
30187         }
30188       return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font);
30189     }
30190 
30191     template<typename tc>
30192     CImg<T>& draw_text(const int x0, const int y0,
30193                        const char *const text,
30194                        const tc *const foreground_color, const int background_color=0,
30195                        const float opacity=1, const unsigned int font_height=13, ...) {
30196       if (!font_height) return *this;
30197       char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font_height); cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap);
30198       return draw_text(x0,y0,"%s",foreground_color,(const tc*)background_color,opacity,font_height,tmp);
30199     }
30200 
30201     template<typename tc>
30202     CImg<T>& draw_text(const int x0, const int y0,
30203                        const char *const text,
30204                        const int, const tc *const background_color,
30205                        const float opacity=1, const unsigned int font_height=13, ...) {
30206       if (!font_height) return *this;
30207       char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font_height); cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap);
30208       return draw_text(x0,y0,"%s",(tc*)0,background_color,opacity,font_height,tmp);
30209     }
30210 
30211     // Draw a text (internal routine).
30212     template<typename tc1, typename tc2, typename t>
30213     CImg<T>& _draw_text(const int x0, const int y0,
30214                         const char *const text,
30215                         const tc1 *const foreground_color, const tc2 *const background_color,
30216                         const float opacity, const CImgList<t>& font) {
30217       if (!text) return *this;
30218       if (!font)
30219         throw CImgArgumentException(_cimg_instance
30220                                     "draw_text() : Empty specified font.",
30221                                     cimg_instance);
30222       const unsigned int text_length = std::strlen(text);
30223       if (is_empty()) {
30224         // If needed, pre-compute necessary size of the image
30225         int x = 0, y = 0, w = 0;
30226         unsigned char c = 0;
30227         for (unsigned int i = 0; i<text_length; ++i) {
30228           c = text[i];
30229           switch (c) {
30230           case '\n' : y+=font[0]._height; if (x>w) w = x; x = 0; break;
30231           case '\t' : x+=4*font[' ']._width; break;
30232           default : if (c<font._width) x+=font[c]._width;
30233           }
30234         }
30235         if (x!=0 || c=='\n') {
30236           if (x>w) w=x;
30237           y+=font[0]._height;
30238         }
30239         assign(x0+w,y0+y,1,font[0]._spectrum,0);
30240         if (background_color) cimg_forC(*this,c) get_shared_channel(c).fill((T)background_color[c]);
30241       }
30242 
30243       int x = x0, y = y0;
30244       CImg<t> letter;
30245       for (unsigned int i = 0; i<text_length; ++i) {
30246         const unsigned char c = text[i];
30247         switch (c) {
30248         case '\n' : y+=font[' ']._height; x = x0; break;
30249         case '\t' : x+=4*font[' ']._width; break;
30250         default : if (c<font._width) {
30251           letter = font[c];
30252           const unsigned int cmin = cimg::min(_spectrum,letter._spectrum);
30253           const CImg<t>& mask = (c+256)<(int)font._width?font[c+256]:font[c];
30254           if (foreground_color)
30255             for (unsigned int p = 0; p<letter._width*letter._height; ++p)
30256               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]);
30257           if (background_color)
30258             for (unsigned int p = 0; p<letter._width*letter._height; ++p)
30259               if (!mask(p)) for (unsigned int c = 0; c<cmin; ++c) letter(p,0,0,c) = (t)background_color[c];
30260           if (!background_color && font._width>=512) draw_image(x,y,letter,mask,opacity,(T)1);
30261           else draw_image(x,y,letter,opacity);
30262           x+=letter._width;
30263           }
30264         }
30265       }
30266       return *this;
30267     }
30268 
30269     //! Draw a vector field in the image instance, using a colormap.
30270     /**
30271        \param flow Image of 2d vectors used as input data.
30272        \param color Image of spectrum()-D vectors corresponding to the color of each arrow.
30273        \param sampling Length (in pixels) between each arrow.
30274        \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length).
30275        \param opacity Opacity of the drawing.
30276        \param pattern Used pattern to draw lines.
30277        \note Clipping is supported.
30278     **/
30279     template<typename t1, typename t2>
30280     CImg<T>& draw_quiver(const CImg<t1>& flow,
30281                          const t2 *const color, const float opacity=1,
30282                          const unsigned int sampling=25, const float factor=-20,
30283                          const bool arrows=true, const unsigned int pattern=~0U) {
30284       return draw_quiver(flow,CImg<t2>(color,_spectrum,1,1,1,true),opacity,sampling,factor,arrows,pattern);
30285     }
30286 
30287     //! Draw a vector field in the image instance, using a colormap.
30288     /**
30289        \param flow Image of 2d vectors used as input data.
30290        \param color Image of spectrum()-D vectors corresponding to the color of each arrow.
30291        \param sampling Length (in pixels) between each arrow.
30292        \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length).
30293        \param opacity Opacity of the drawing.
30294        \param pattern Used pattern to draw lines.
30295        \note Clipping is supported.
30296     **/
30297     template<typename t1, typename t2>
30298     CImg<T>& draw_quiver(const CImg<t1>& flow,
30299                          const CImg<t2>& color, const float opacity=1,
30300                          const unsigned int sampling=25, const float factor=-20,
30301                          const bool arrows=true, const unsigned int pattern=~0U) {
30302 
30303       if (is_empty()) return *this;
30304 
30305       if (!flow || flow._spectrum!=2)
30306         throw CImgArgumentException(_cimg_instance
30307                                     "draw_quiver() : Invalid dimensions of specified flow (%u,%u,%u,%u,%p).",
30308                                     cimg_instance,
30309                                     flow._width,flow._height,flow._depth,flow._spectrum,flow._data);
30310       if (sampling<=0)
30311         throw CImgArgumentException(_cimg_instance
30312                                     "draw_quiver() : Invalid sampling value %g "
30313                                     "(should be >0)",
30314                                     cimg_instance,
30315                                     sampling);
30316 
30317       const bool colorfield = (color._width==flow._width && color._height==flow._height && color._depth==1 && color._spectrum==_spectrum);
30318       if (is_overlapped(flow)) return draw_quiver(+flow,color,opacity,sampling,factor,arrows,pattern);
30319 
30320       float vmax,fact;
30321       if (factor<=0) {
30322         float m, M = (float)flow.get_norm(2).max_min(m);
30323         vmax = (float)cimg::max(cimg::abs(m),cimg::abs(M));
30324         if (!vmax) vmax = 1;
30325         fact = -factor;
30326       } else { fact = factor; vmax = 1; }
30327 
30328       for (unsigned int y = sampling/2; y<_height; y+=sampling)
30329         for (unsigned int x = sampling/2; x<_width; x+=sampling) {
30330           const unsigned int X = x*flow._width/_width, Y = y*flow._height/_height;
30331           float u = (float)flow(X,Y,0,0)*fact/vmax, v = (float)flow(X,Y,0,1)*fact/vmax;
30332           if (arrows) {
30333             const int xx = x+(int)u, yy = y+(int)v;
30334             if (colorfield) draw_arrow(x,y,xx,yy,color.get_vector_at(X,Y)._data,opacity,45,sampling/5.0f,pattern);
30335             else draw_arrow(x,y,xx,yy,color._data,opacity,45,sampling/5.0f,pattern);
30336           } else {
30337             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);
30338             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);
30339           }
30340         }
30341 
30342       return *this;
30343     }
30344 
30345     //! Draw a labeled horizontal axis on the image instance.
30346     /**
30347        \param xvalues Lower bound of the x-range.
30348        \param y Y-coordinate of the horizontal axis in the image instance.
30349        \param color Array of spectrum() values of type \c T, defining the drawing color.
30350        \param opacity Drawing opacity.
30351        \param pattern Drawing pattern.
30352        \param opacity_out Drawing opacity of 'outside' axes.
30353        \note if \c precision==0, precision of the labels is automatically computed.
30354     **/
30355     template<typename t, typename tc>
30356     CImg<T>& draw_axis(const CImg<t>& xvalues, const int y,
30357                        const tc *const color, const float opacity=1,
30358                        const unsigned int pattern=~0U) {
30359       if (!is_empty()) {
30360         int siz = (int)xvalues.size()-1;
30361         if (siz<=0) draw_line(0,y,_width-1,y,color,opacity,pattern);
30362         else {
30363           if (xvalues[0]<xvalues[siz]) draw_arrow(0,y,_width-1,y,color,opacity,30,5,pattern);
30364           else draw_arrow(_width-1,y,0,y,color,opacity,30,5,pattern);
30365           const int yt = (y+14)<height()?(y+3):(y-14);
30366           char txt[32] = { 0 };
30367           cimg_foroff(xvalues,x) {
30368             cimg_snprintf(txt,sizeof(txt),"%g",(double)xvalues(x));
30369             const int xi = (int)(x*(_width-1)/siz), xt = xi - (int)std::strlen(txt)*3;
30370             draw_point(xi,y-1,color,opacity).draw_point(xi,y+1,color,opacity).
30371               draw_text(xt<0?0:xt,yt,txt,color,(tc*)0,opacity,13);
30372           }
30373         }
30374       }
30375       return *this;
30376     }
30377 
30378     //! Draw a labeled vertical axis on the image instance.
30379     template<typename t, typename tc>
30380     CImg<T>& draw_axis(const int x, const CImg<t>& yvalues,
30381                        const tc *const color, const float opacity=1,
30382                        const unsigned int pattern=~0U) {
30383       if (!is_empty()) {
30384         int siz = (int)yvalues.size()-1;
30385         if (siz<=0) draw_line(x,0,x,_height-1,color,opacity,pattern);
30386         else {
30387           if (yvalues[0]<yvalues[siz]) draw_arrow(x,0,x,_height-1,color,opacity,30,5,pattern);
30388           else draw_arrow(x,_height-1,x,0,color,opacity,30,5,pattern);
30389           char txt[32] = { 0 };
30390           cimg_foroff(yvalues,y) {
30391             cimg_snprintf(txt,sizeof(txt),"%g",(double)yvalues(y));
30392             const int
30393               yi = (int)(y*(_height-1)/siz),
30394               tmp = yi - 5,
30395               nyi = tmp<0?0:(tmp>=height()-11?height()-11:tmp),
30396               xt = x - (int)std::strlen(txt)*7;
30397             draw_point(x-1,yi,color,opacity).draw_point(x+1,yi,color,opacity);
30398             if (xt>0) draw_text(xt,nyi,txt,color,(tc*)0,opacity,13);
30399             else draw_text(x+3,nyi,txt,color,(tc*)0,opacity,13);
30400           }
30401         }
30402       }
30403       return *this;
30404     }
30405 
30406     //! Draw a labeled horizontal+vertical axis on the image instance.
30407     template<typename tx, typename ty, typename tc>
30408       CImg<T>& draw_axes(const CImg<tx>& xvalues, const CImg<ty>& yvalues,
30409                          const tc *const color, const float opacity=1,
30410                          const unsigned int patternx=~0U, const unsigned int patterny=~0U) {
30411       if (!is_empty()) {
30412         const CImg<tx> nxvalues(xvalues._data,xvalues.size(),1,1,1,true);
30413         const int sizx = (int)xvalues.size()-1, wm1 = width()-1;
30414         if (sizx>0) {
30415           float ox = (float)nxvalues[0];
30416           for (unsigned int x = 1; x<_width; ++x) {
30417             const float nx = (float)nxvalues._linear_atX((float)x*sizx/wm1);
30418             if (nx*ox<=0) { draw_axis(nx==0?x:x-1,yvalues,color,opacity,patterny); break; }
30419             ox = nx;
30420           }
30421         }
30422         const CImg<ty> nyvalues(yvalues._data,yvalues.size(),1,1,1,true);
30423         const int sizy = (int)yvalues.size()-1, hm1 = height()-1;
30424         if (sizy>0) {
30425           float oy = (float)nyvalues[0];
30426           for (unsigned int y = 1; y<_height; ++y) {
30427             const float ny = (float)nyvalues._linear_atX((float)y*sizy/hm1);
30428             if (ny*oy<=0) { draw_axis(xvalues,ny==0?y:y-1,color,opacity,patternx); break; }
30429             oy = ny;
30430           }
30431         }
30432       }
30433       return *this;
30434     }
30435 
30436     //! Draw a labeled horizontal+vertical axis on the image instance.
30437     template<typename tc>
30438     CImg<T>& draw_axes(const float x0, const float x1, const float y0, const float y1,
30439                        const tc *const color, const float opacity=1,
30440                        const int subdivisionx=-60, const int subdivisiony=-60,
30441                        const float precisionx=0, const float precisiony=0,
30442                        const unsigned int patternx=~0U, const unsigned int patterny=~0U) {
30443       if (!is_empty()) {
30444         const float
30445           dx = cimg::abs(x1-x0), dy = cimg::abs(y1-y0),
30446           px = (precisionx==0)?(float)std::pow(10.0,(int)std::log10(dx)-2.0):precisionx,
30447           py = (precisiony==0)?(float)std::pow(10.0,(int)std::log10(dy)-2.0):precisiony;
30448         if (x0!=x1 && y0!=y1)
30449           draw_axes(CImg<floatT>::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1).round(px),
30450                     CImg<floatT>::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1).round(py),
30451                     color,opacity,patternx,patterny);
30452         else if (x0==x1 && y0!=y1)
30453           draw_axis((int)x0,CImg<floatT>::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1).round(py),
30454                     color,opacity,patterny);
30455         else if (x0!=x1 && y0==y1)
30456           draw_axis(CImg<floatT>::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1).round(px),(int)y0,
30457                     color,opacity,patternx);
30458       }
30459       return *this;
30460     }
30461 
30462     //! Draw grid.
30463     template<typename tx, typename ty, typename tc>
30464     CImg<T>& draw_grid(const CImg<tx>& xvalues, const CImg<ty>& yvalues,
30465                        const tc *const color, const float opacity=1,
30466                        const unsigned int patternx=~0U, const unsigned int patterny=~0U) {
30467       if (!is_empty()) {
30468         if (xvalues) cimg_foroff(xvalues,x) {
30469           const int xi = (int)xvalues[x];
30470           if (xi>=0 && xi<width()) draw_line(xi,0,xi,_height-1,color,opacity,patternx);
30471         }
30472         if (yvalues) cimg_foroff(yvalues,y) {
30473           const int yi = (int)yvalues[y];
30474           if (yi>=0 && yi<height()) draw_line(0,yi,_width-1,yi,color,opacity,patterny);
30475         }
30476       }
30477       return *this;
30478     }
30479 
30480     //! Draw grid.
30481     template<typename tc>
30482     CImg<T>& draw_grid(const float deltax,  const float deltay,
30483                        const float offsetx, const float offsety,
30484                        const bool invertx, const bool inverty,
30485                        const tc *const color, const float opacity=1,
30486                        const unsigned int patternx=~0U, const unsigned int patterny=~0U) {
30487       CImg<uintT> seqx, seqy;
30488       if (deltax!=0) {
30489         const float dx = deltax>0?deltax:_width*-deltax/100;
30490         const unsigned int nx = (unsigned int)(_width/dx);
30491         seqx = CImg<uintT>::sequence(1+nx,0,(unsigned int)(dx*nx));
30492         if (offsetx) cimg_foroff(seqx,x) seqx(x) = (unsigned int)cimg::mod(seqx(x)+offsetx,(float)_width);
30493         if (invertx) cimg_foroff(seqx,x) seqx(x) = _width - 1 - seqx(x);
30494       }
30495 
30496       if (deltay!=0) {
30497         const float dy = deltay>0?deltay:_height*-deltay/100;
30498         const unsigned int ny = (unsigned int)(_height/dy);
30499         seqy = CImg<uintT>::sequence(1+ny,0,(unsigned int)(dy*ny));
30500         if (offsety) cimg_foroff(seqy,y) seqy(y) = (unsigned int)cimg::mod(seqy(y)+offsety,(float)_height);
30501         if (inverty) cimg_foroff(seqy,y) seqy(y) = _height - 1 - seqy(y);
30502      }
30503       return draw_grid(seqx,seqy,color,opacity,patternx,patterny);
30504     }
30505 
30506     //! Draw a 1d graph on the image instance.
30507     /**
30508        \param data Image containing the graph values I = f(x).
30509        \param color Array of spectrum() values of type \c T, defining the drawing color.
30510        \param opacity Drawing opacity.
30511 
30512        \param plot_type Define the type of the plot :
30513                       - 0 = No plot.
30514                       - 1 = Plot using segments.
30515                       - 2 = Plot using cubic splines.
30516                       - 3 = Plot with bars.
30517        \param vertex_type Define the type of points :
30518                       - 0 = No points.
30519                       - 1 = Point.
30520                       - 2 = Straight cross.
30521                       - 3 = Diagonal cross.
30522                       - 4 = Filled circle.
30523                       - 5 = Outlined circle.
30524                       - 6 = Square.
30525                       - 7 = Diamond.
30526        \param ymin Lower bound of the y-range.
30527        \param ymax Upper bound of the y-range.
30528        \param pattern Drawing pattern.
30529        \note
30530          - if \c ymin==ymax==0, the y-range is computed automatically from the input samples.
30531     **/
30532     template<typename t, typename tc>
30533     CImg<T>& draw_graph(const CImg<t>& data,
30534                         const tc *const color, const float opacity=1,
30535                         const unsigned int plot_type=1, const int vertex_type=1,
30536                         const double ymin=0, const double ymax=0, const unsigned int pattern=~0U) {
30537       if (!color)
30538         throw CImgArgumentException(_cimg_instance
30539                                     "draw_graph() : Specified color is (null).",
30540                                     cimg_instance);
30541       if (is_empty() || _height<=1) return *this;
30542 
30543       // Create shaded colors for displaying bar plots.
30544       CImg<tc> color1, color2;
30545       if (plot_type==3) {
30546         color1.assign(_spectrum); color2.assign(_spectrum);
30547         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); }
30548       }
30549 
30550       // Compute min/max and normalization factors.
30551       const unsigned int
30552         siz = data.size(),
30553         _siz1 = siz - (plot_type!=3?1:0),
30554         siz1 = _siz1?_siz1:1,
30555         _width1 = _width - (plot_type!=3?1:0),
30556         width1 = _width1?_width1:1;
30557       double m = ymin, M = ymax;
30558       if (ymin==ymax) m = (double)data.max_min(M);
30559       if (m==M) { --m; ++M; }
30560       const float ca = (float)(M-m)/(_height-1);
30561       bool init_hatch = true;
30562 
30563       // Draw graph edges
30564       switch (plot_type%4) {
30565       case 1 : { // Segments
30566         int oX = 0, oY = (int)((data[0]-m)/ca);
30567         for (unsigned int off = 1; off<siz; ++off) {
30568           const int
30569             X = off*_width1/siz1,
30570             Y = (int)((data[off]-m)/ca);
30571           draw_line(oX,oY,X,Y,color,opacity,pattern,init_hatch);
30572           oX = X; oY = Y;
30573           init_hatch = false;
30574         }
30575       } break;
30576       case 2 : { // Spline
30577         const CImg<t> ndata(data._data,siz,1,1,1,true);
30578         int oY = (int)((data[0]-m)/ca);
30579         cimg_forX(*this,x) {
30580           const int Y = (int)((ndata._cubic_atX((float)x*siz1/width1)-m)/ca);
30581           if (x>0) draw_line(x,oY,x+1,Y,color,opacity,pattern,init_hatch);
30582           init_hatch = false;
30583           oY = Y;
30584         }
30585       } break;
30586       case 3 : { // Bars
30587         const int Y0 = (int)(-m/ca);
30588         int oX = 0;
30589         cimg_foroff(data,off) {
30590           const int
30591             X = (off+1)*_width/siz-1,
30592             Y = (int)((data[off]-m)/ca);
30593           draw_rectangle(oX,Y0,X,Y,color,opacity).
30594             draw_line(oX,Y,oX,Y0,color2.data(),opacity).
30595             draw_line(oX,Y0,X,Y0,Y<=Y0?color2.data():color1.data(),opacity).
30596             draw_line(X,Y,X,Y0,color1.data(),opacity).
30597             draw_line(oX,Y,X,Y,Y<=Y0?color1.data():color2.data(),opacity);
30598           oX = X+1;
30599         }
30600       } break;
30601       default : break; // No edges
30602       }
30603 
30604       // Draw graph points
30605       const unsigned int wb2 = plot_type==3?_width1/(2*siz):0;
30606       switch (vertex_type%8) {
30607       case 1 : { // Point
30608         cimg_foroff(data,off) {
30609           const int
30610             X = off*_width1/siz1 + wb2,
30611             Y = (int)((data[off]-m)/ca);
30612           draw_point(X,Y,color,opacity);
30613         }
30614       } break;
30615       case 2 : { // Straight Cross
30616         cimg_foroff(data,off) {
30617           const int
30618             X = off*_width1/siz1 + wb2,
30619             Y = (int)((data[off]-m)/ca);
30620           draw_line(X-3,Y,X+3,Y,color,opacity).draw_line(X,Y-3,X,Y+3,color,opacity);
30621         }
30622       } break;
30623       case 3 : { // Diagonal Cross
30624         cimg_foroff(data,off) {
30625           const int
30626             X = off*_width1/siz1 + wb2,
30627             Y = (int)((data[off]-m)/ca);
30628           draw_line(X-3,Y-3,X+3,Y+3,color,opacity).draw_line(X-3,Y+3,X+3,Y-3,color,opacity);
30629         }
30630       } break;
30631       case 4 : { // Filled Circle
30632         cimg_foroff(data,off) {
30633           const int
30634             X = off*_width1/siz1 + wb2,
30635             Y = (int)((data[off]-m)/ca);
30636           draw_circle(X,Y,3,color,opacity);
30637         }
30638       } break;
30639       case 5 : { // Outlined circle
30640         cimg_foroff(data,off) {
30641           const int
30642             X = off*_width1/siz1 + wb2,
30643             Y = (int)((data[off]-m)/ca);
30644           draw_circle(X,Y,3,color,opacity,0U);
30645         }
30646       } break;
30647       case 6 : { // Square
30648         cimg_foroff(data,off) {
30649           const int
30650             X = off*_width1/siz1 + wb2,
30651             Y = (int)((data[off]-m)/ca);
30652           draw_rectangle(X-3,Y-3,X+3,Y+3,color,opacity,~0U);
30653         }
30654       } break;
30655       case 7 : { // Diamond
30656         cimg_foroff(data,off) {
30657           const int
30658             X = off*_width1/siz1 + wb2,
30659             Y = (int)((data[off]-m)/ca);
30660           draw_line(X,Y-4,X+4,Y,color,opacity).
30661             draw_line(X+4,Y,X,Y+4,color,opacity).
30662             draw_line(X,Y+4,X-4,Y,color,opacity).
30663             draw_line(X-4,Y,X,Y-4,color,opacity);
30664         }
30665       } break;
30666       default : break; // No points
30667       }
30668       return *this;
30669     }
30670 
30671     //! Draw a 3d filled region starting from a point (\c x,\c y,\ z) in the image instance.
30672     /**
30673        \param x X-coordinate of the starting point of the region to fill.
30674        \param y Y-coordinate of the starting point of the region to fill.
30675        \param z Z-coordinate of the starting point of the region to fill.
30676        \param color An array of spectrum() values of type \c T, defining the drawing color.
30677        \param region Image that will contain the mask of the filled region mask, as an output.
30678        \param sigma Tolerance concerning neighborhood values.
30679        \param opacity Opacity of the drawing.
30680        \param high_connexity Tells if 8-connexity must be used (only for 2d images).
30681        \return \p region is initialized with the binary mask of the filled region.
30682     **/
30683     template<typename tc, typename t>
30684     CImg<T>& draw_fill(const int x, const int y, const int z,
30685                        const tc *const color, const float opacity,
30686                        CImg<t>& region, const float sigma=0,
30687                        const bool high_connexity=false) {
30688 
30689 #define _cimg_draw_fill_test(x,y,z,res) if (region(x,y,z)) res = false; else { \
30690   res = true; \
30691   const T *reference_col = reference_color._data + _spectrum, *ptrs = data(x,y,z) + siz; \
30692   for (unsigned int i = _spectrum; res && i; --i) { ptrs-=whd; res = (cimg::abs(*ptrs - *(--reference_col))<=sigma); } \
30693   region(x,y,z) = (t)(res?1:noregion); \
30694 }
30695 
30696 #define _cimg_draw_fill_set(x,y,z) { \
30697   const tc *col = color; \
30698   T *ptrd = data(x,y,z); \
30699   if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; } \
30700   else cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; } \
30701 }
30702 
30703 #define _cimg_draw_fill_insert(x,y,z) { \
30704   if (posr1>=remaining._height) remaining.resize(3,remaining._height<<1,1,1,0); \
30705   unsigned int *ptrr = remaining.data(0,posr1); \
30706   *(ptrr++) = x; *(ptrr++) = y; *(ptrr++) = z; ++posr1; \
30707 }
30708 
30709 #define _cimg_draw_fill_test_neighbor(x,y,z,cond) if (cond) { \
30710   const unsigned int tx = x, ty = y, tz = z; \
30711   _cimg_draw_fill_test(tx,ty,tz,res); if (res) _cimg_draw_fill_insert(tx,ty,tz); \
30712 }
30713 
30714       if (!color)
30715         throw CImgArgumentException(_cimg_instance
30716                                     "draw_fill() : Specified color is (null).",
30717                                     cimg_instance);
30718 
30719       region.assign(_width,_height,_depth,1,(t)0);
30720       if (x>=0 && x<width() && y>=0 && y<height() && z>=0 && z<depth()) {
30721         const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
30722         const unsigned int whd = _width*_height*_depth, siz = _spectrum*whd, W1 = _width-1, H1 = _height-1, D1 = _depth-1;
30723         const bool is_3d = (_depth>1);
30724         const CImg<T> reference_color = get_vector_at(x,y,z);
30725         CImg<uintT> remaining(3,512,1,1,0);
30726         remaining(0,0) = x; remaining(1,0) = y; remaining(2,0) = z;
30727         unsigned int posr0 = 0, posr1 = 1;
30728         region(x,y,z) = (t)1;
30729         const t noregion = ((t)1==(t)2)?(t)0:(t)(-1);
30730         if (is_3d) do { // 3d version of the filling algorithm
30731           const unsigned int *pcurr = remaining.data(0,posr0++), xc = *(pcurr++), yc = *(pcurr++), zc = *(pcurr++);
30732           if (posr0>=512) { remaining.shift(0,-(int)posr0); posr1-=posr0; posr0 = 0; }
30733           bool cont, res;
30734           unsigned int nxc = xc;
30735           do { // X-backward
30736             _cimg_draw_fill_set(nxc,yc,zc);
30737             _cimg_draw_fill_test_neighbor(nxc,yc-1,zc,yc!=0);
30738             _cimg_draw_fill_test_neighbor(nxc,yc+1,zc,yc<H1);
30739             _cimg_draw_fill_test_neighbor(nxc,yc,zc-1,zc!=0);
30740             _cimg_draw_fill_test_neighbor(nxc,yc,zc+1,zc<D1);
30741             if (nxc) { --nxc; _cimg_draw_fill_test(nxc,yc,zc,cont); } else cont = false;
30742           } while (cont);
30743           nxc = xc;
30744           do { // X-forward
30745             if ((++nxc)<=W1) { _cimg_draw_fill_test(nxc,yc,zc,cont); } else cont = false;
30746             if (cont) {
30747               _cimg_draw_fill_set(nxc,yc,zc);
30748               _cimg_draw_fill_test_neighbor(nxc,yc-1,zc,yc!=0);
30749               _cimg_draw_fill_test_neighbor(nxc,yc+1,zc,yc<H1);
30750               _cimg_draw_fill_test_neighbor(nxc,yc,zc-1,zc!=0);
30751               _cimg_draw_fill_test_neighbor(nxc,yc,zc+1,zc<D1);
30752             }
30753           } while (cont);
30754           unsigned int nyc = yc;
30755           do { // Y-backward
30756             if (nyc) { --nyc; _cimg_draw_fill_test(xc,nyc,zc,cont); } else cont = false;
30757             if (cont) {
30758               _cimg_draw_fill_set(xc,nyc,zc);
30759               _cimg_draw_fill_test_neighbor(xc-1,nyc,zc,xc!=0);
30760               _cimg_draw_fill_test_neighbor(xc+1,nyc,zc,xc<W1);
30761               _cimg_draw_fill_test_neighbor(xc,nyc,zc-1,zc!=0);
30762               _cimg_draw_fill_test_neighbor(xc,nyc,zc+1,zc<D1);
30763             }
30764           } while (cont);
30765           nyc = yc;
30766           do { // Y-forward
30767             if ((++nyc)<=H1) { _cimg_draw_fill_test(xc,nyc,zc,cont); } else cont = false;
30768             if (cont) {
30769               _cimg_draw_fill_set(xc,nyc,zc);
30770               _cimg_draw_fill_test_neighbor(xc-1,nyc,zc,xc!=0);
30771               _cimg_draw_fill_test_neighbor(xc+1,nyc,zc,xc<W1);
30772               _cimg_draw_fill_test_neighbor(xc,nyc,zc-1,zc!=0);
30773               _cimg_draw_fill_test_neighbor(xc,nyc,zc+1,zc<D1);
30774             }
30775           } while (cont);
30776           unsigned int nzc = zc;
30777           do { // Z-backward
30778             if (nzc) { --nzc; _cimg_draw_fill_test(xc,yc,nzc,cont); } else cont = false;
30779             if (cont) {
30780               _cimg_draw_fill_set(xc,yc,nzc);
30781               _cimg_draw_fill_test_neighbor(xc-1,yc,nzc,xc!=0);
30782               _cimg_draw_fill_test_neighbor(xc+1,yc,nzc,xc<W1);
30783               _cimg_draw_fill_test_neighbor(xc,yc-1,nzc,yc!=0);
30784               _cimg_draw_fill_test_neighbor(xc,yc+1,nzc,yc<H1);
30785             }
30786           } while (cont);
30787           nzc = zc;
30788           do { // Z-forward
30789             if ((++nzc)<=D1) { _cimg_draw_fill_test(xc,yc,nzc,cont); } else cont = false;
30790             if (cont) {
30791               _cimg_draw_fill_set(xc,nyc,zc);
30792               _cimg_draw_fill_test_neighbor(xc-1,yc,nzc,xc!=0);
30793               _cimg_draw_fill_test_neighbor(xc+1,yc,nzc,xc<W1);
30794               _cimg_draw_fill_test_neighbor(xc,yc-1,nzc,yc!=0);
30795               _cimg_draw_fill_test_neighbor(xc,yc+1,nzc,yc<H1);
30796             }
30797           } while (cont);
30798         } while (posr1>posr0);
30799         else do { // 2d version of the filling algorithm
30800           const unsigned int *pcurr = remaining.data(0,posr0++), xc = *(pcurr++), yc = *(pcurr++);
30801           if (posr0>=512) { remaining.shift(0,-(int)posr0); posr1-=posr0; posr0 = 0; }
30802           bool cont, res;
30803           unsigned int nxc = xc;
30804           do { // X-backward
30805             _cimg_draw_fill_set(nxc,yc,0);
30806             _cimg_draw_fill_test_neighbor(nxc,yc-1,0,yc!=0);
30807             _cimg_draw_fill_test_neighbor(nxc,yc+1,0,yc<H1);
30808             if (high_connexity) {
30809               _cimg_draw_fill_test_neighbor(nxc-1,yc-1,0,(nxc!=0 && yc!=0));
30810               _cimg_draw_fill_test_neighbor(nxc+1,yc-1,0,(nxc<W1 && yc!=0));
30811               _cimg_draw_fill_test_neighbor(nxc-1,yc+1,0,(nxc!=0 && yc<H1));
30812               _cimg_draw_fill_test_neighbor(nxc+1,yc+1,0,(nxc<W1 && yc<H1));
30813             }
30814             if (nxc) { --nxc; _cimg_draw_fill_test(nxc,yc,0,cont); } else cont = false;
30815           } while (cont);
30816           nxc = xc;
30817           do { // X-forward
30818             if ((++nxc)<=W1) { _cimg_draw_fill_test(nxc,yc,0,cont); } else cont = false;
30819             if (cont) {
30820               _cimg_draw_fill_set(nxc,yc,0);
30821               _cimg_draw_fill_test_neighbor(nxc,yc-1,0,yc!=0);
30822               _cimg_draw_fill_test_neighbor(nxc,yc+1,0,yc<H1);
30823               if (high_connexity) {
30824                 _cimg_draw_fill_test_neighbor(nxc-1,yc-1,0,(nxc!=0 && yc!=0));
30825                 _cimg_draw_fill_test_neighbor(nxc+1,yc-1,0,(nxc<W1 && yc!=0));
30826                 _cimg_draw_fill_test_neighbor(nxc-1,yc+1,0,(nxc!=0 && yc<H1));
30827                 _cimg_draw_fill_test_neighbor(nxc+1,yc+1,0,(nxc<W1 && yc<H1));
30828               }
30829             }
30830           } while (cont);
30831           unsigned int nyc = yc;
30832           do { // Y-backward
30833             if (nyc) { --nyc; _cimg_draw_fill_test(xc,nyc,0,cont); } else cont = false;
30834             if (cont) {
30835               _cimg_draw_fill_set(xc,nyc,0);
30836               _cimg_draw_fill_test_neighbor(xc-1,nyc,0,xc!=0);
30837               _cimg_draw_fill_test_neighbor(xc+1,nyc,0,xc<W1);
30838               if (high_connexity) {
30839                 _cimg_draw_fill_test_neighbor(xc-1,nyc-1,0,(xc!=0 && nyc!=0));
30840                 _cimg_draw_fill_test_neighbor(xc+1,nyc-1,0,(xc<W1 && nyc!=0));
30841                 _cimg_draw_fill_test_neighbor(xc-1,nyc+1,0,(xc!=0 && nyc<H1));
30842                 _cimg_draw_fill_test_neighbor(xc+1,nyc+1,0,(xc<W1 && nyc<H1));
30843               }
30844             }
30845           } while (cont);
30846           nyc = yc;
30847           do { // Y-forward
30848             if ((++nyc)<=H1) { _cimg_draw_fill_test(xc,nyc,0,cont); } else cont = false;
30849             if (cont) {
30850               _cimg_draw_fill_set(xc,nyc,0);
30851               _cimg_draw_fill_test_neighbor(xc-1,nyc,0,xc!=0);
30852               _cimg_draw_fill_test_neighbor(xc+1,nyc,0,xc<W1);
30853               if (high_connexity) {
30854                 _cimg_draw_fill_test_neighbor(xc-1,nyc-1,0,(xc!=0 && nyc!=0));
30855                 _cimg_draw_fill_test_neighbor(xc+1,nyc-1,0,(xc<W1 && nyc!=0));
30856                 _cimg_draw_fill_test_neighbor(xc-1,nyc+1,0,(xc!=0 && nyc<H1));
30857                 _cimg_draw_fill_test_neighbor(xc+1,nyc+1,0,(xc<W1 && nyc<H1));
30858               }
30859             }
30860           } while (cont);
30861         } while (posr1>posr0);
30862         if (noregion) cimg_for(region,ptrd,t) if (*ptrd==noregion) *ptrd = (t)0;
30863       }
30864       return *this;
30865     }
30866 
30867     //! Draw a 3d filled region starting from a point (\c x,\c y,\ z) in the image instance.
30868     /**
30869        \param x = X-coordinate of the starting point of the region to fill.
30870        \param y = Y-coordinate of the starting point of the region to fill.
30871        \param z = Z-coordinate of the starting point of the region to fill.
30872        \param color = an array of spectrum() values of type \c T, defining the drawing color.
30873        \param sigma = tolerance concerning neighborhood values.
30874        \param opacity = opacity of the drawing.
30875     **/
30876     template<typename tc>
30877     CImg<T>& draw_fill(const int x, const int y, const int z,
30878                        const tc *const color, const float opacity=1,
30879                        const float sigma=0, const bool high_connexity=false) {
30880       CImg<boolT> tmp;
30881       return draw_fill(x,y,z,color,opacity,tmp,sigma,high_connexity);
30882     }
30883 
30884     //! Draw a 2d filled region starting from a point (\c x,\c y) in the image instance.
30885     /**
30886        \param x = X-coordinate of the starting point of the region to fill.
30887        \param y = Y-coordinate of the starting point of the region to fill.
30888        \param color = an array of spectrum() values of type \c T, defining the drawing color.
30889        \param sigma = tolerance concerning neighborhood values.
30890        \param opacity = opacity of the drawing.
30891     **/
30892     template<typename tc>
30893     CImg<T>& draw_fill(const int x, const int y,
30894                        const tc *const color, const float opacity=1,
30895                        const float sigma=0, const bool high_connexity=false) {
30896       CImg<boolT> tmp;
30897       return draw_fill(x,y,0,color,opacity,tmp,sigma,high_connexity);
30898     }
30899 
30900     //! Draw a plasma random texture.
30901     /**
30902        \param x0 = X-coordinate of the upper-left corner of the plasma.
30903        \param y0 = Y-coordinate of the upper-left corner of the plasma.
30904        \param x1 = X-coordinate of the lower-right corner of the plasma.
30905        \param y1 = Y-coordinate of the lower-right corner of the plasma.
30906        \param alpha = Alpha-parameter of the plasma.
30907        \param beta = Beta-parameter of the plasma.
30908        \param opacity = opacity of the drawing.
30909     **/
30910     CImg<T>& draw_plasma(const int x0, const int y0, const int x1, const int y1,
30911                          const float alpha=1, const float beta=1,
30912                          const float opacity=1) {
30913       if (!is_empty()) {
30914         const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
30915         int nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1;
30916         if (nx1<nx0) cimg::swap(nx0,nx1);
30917         if (ny1<ny0) cimg::swap(ny0,ny1);
30918         if (nx0<0) nx0 = 0;
30919         if (nx1>=width()) nx1 = _width-1;
30920         if (ny0<0) ny0 = 0;
30921         if (ny1>=height()) ny1 = _height-1;
30922         const int xc = (nx0+nx1)/2, yc = (ny0+ny1)/2, dx = (xc-nx0), dy = (yc-ny0);
30923         const Tfloat
30924           dc = (Tfloat)(std::sqrt((float)(dx*dx+dy*dy))*alpha + beta),
30925           vmin = (Tfloat)cimg::type<T>::min(),
30926           vmax = (Tfloat)cimg::type<T>::max();
30927         Tfloat val = 0;
30928         cimg_forC(*this,c) {
30929           if (opacity>=1) {
30930             const Tfloat
30931               val0 = (Tfloat)((*this)(nx0,ny0,0,c)), val1 = (Tfloat)((*this)(nx1,ny0,0,c)),
30932               val2 = (Tfloat)((*this)(nx0,ny1,0,c)), val3 = (Tfloat)((*this)(nx1,ny1,0,c));
30933             (*this)(xc,ny0,0,c) = (T)((val0+val1)/2);
30934             (*this)(xc,ny1,0,c) = (T)((val2+val3)/2);
30935             (*this)(nx0,yc,0,c) = (T)((val0+val2)/2);
30936             (*this)(nx1,yc,0,c) = (T)((val1+val3)/2);
30937             do {
30938               val = (Tfloat)(0.25f*((Tfloat)((*this)(nx0,ny0,0,c)) +
30939                                     (Tfloat)((*this)(nx1,ny0,0,c)) +
30940                                     (Tfloat)((*this)(nx1,ny1,0,c)) +
30941                                     (Tfloat)((*this)(nx0,ny1,0,c))) +
30942                              dc*cimg::grand());
30943             } while (val<vmin || val>vmax);
30944             (*this)(xc,yc,0,c) = (T)val;
30945           } else {
30946             const Tfloat
30947               val0 = (Tfloat)((*this)(nx0,ny0,0,c)), val1 = (Tfloat)((*this)(nx1,ny0,0,c)),
30948               val2 = (Tfloat)((*this)(nx0,ny1,0,c)), val3 = (Tfloat)((*this)(nx1,ny1,0,c));
30949             (*this)(xc,ny0,0,c) = (T)(((val0+val1)*nopacity + copacity*(*this)(xc,ny0,0,c))/2);
30950             (*this)(xc,ny1,0,c) = (T)(((val2+val3)*nopacity + copacity*(*this)(xc,ny1,0,c))/2);
30951             (*this)(nx0,yc,0,c) = (T)(((val0+val2)*nopacity + copacity*(*this)(nx0,yc,0,c))/2);
30952             (*this)(nx1,yc,0,c) = (T)(((val1+val3)*nopacity + copacity*(*this)(nx1,yc,0,c))/2);
30953             do {
30954               val = (Tfloat)(0.25f*(((Tfloat)((*this)(nx0,ny0,0,c)) +
30955                                      (Tfloat)((*this)(nx1,ny0,0,c)) +
30956                                      (Tfloat)((*this)(nx1,ny1,0,c)) +
30957                                      (Tfloat)((*this)(nx0,ny1,0,c))) +
30958                                     dc*cimg::grand())*nopacity + copacity*(*this)(xc,yc,0,c));
30959             } while (val<vmin || val>vmax);
30960             (*this)(xc,yc,0,c) = (T)val;
30961           }
30962         }
30963         if (xc!=nx0 || yc!=ny0) {
30964           draw_plasma(nx0,ny0,xc,yc,alpha,beta,opacity);
30965           draw_plasma(xc,ny0,nx1,yc,alpha,beta,opacity);
30966           draw_plasma(nx0,yc,xc,ny1,alpha,beta,opacity);
30967           draw_plasma(xc,yc,nx1,ny1,alpha,beta,opacity);
30968         }
30969       }
30970       return *this;
30971     }
30972 
30973     //! Draw a plasma random texture.
30974     /**
30975        \param alpha = Alpha-parameter of the plasma.
30976        \param beta = Beta-parameter of the plasma.
30977        \param opacity = opacity of the drawing.
30978     **/
30979     CImg<T>& draw_plasma(const float alpha=1, const float beta=1,
30980                          const float opacity=1) {
30981       return draw_plasma(0,0,_width-1,_height-1,alpha,beta,opacity);
30982     }
30983 
30984     //! Draw a quadratic Mandelbrot or Julia fractal set, computed using the Escape Time Algorithm.
30985     template<typename tc>
30986     CImg<T>& draw_mandelbrot(const int x0, const int y0, const int x1, const int y1,
30987                              const CImg<tc>& color_palette, const float opacity=1,
30988                              const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2,
30989                              const unsigned int iteration_max=255,
30990                              const bool normalized_iteration=false,
30991                              const bool julia_set=false,
30992                              const double paramr=0, const double parami=0) {
30993       if (is_empty()) return *this;
30994       CImg<tc> palette;
30995       if (color_palette) palette.assign(color_palette._data,color_palette.size()/color_palette._spectrum,1,1,color_palette._spectrum,true);
30996       if (palette && palette._spectrum!=_spectrum)
30997         throw CImgArgumentException(_cimg_instance
30998                                     "draw_mandelbrot() : Instance and specified color palette (%u,%u,%u,%u,%p) have incompatible dimensions.",
30999                                     cimg_instance,
31000                                     color_palette._width,color_palette._height,color_palette._depth,color_palette._spectrum,color_palette._data);
31001 
31002       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0), ln2 = (float)std::log(2.0);
31003       unsigned int iteration = 0;
31004       cimg_for_inXY(*this,x0,y0,x1,y1,p,q) {
31005         const double x = z0r + p*(z1r-z0r)/_width, y = z0i + q*(z1i-z0i)/_height;
31006         double zr, zi, cr, ci;
31007         if (julia_set) { zr = x; zi = y; cr = paramr; ci = parami; }
31008         else { zr = paramr; zi = parami; cr = x; ci = y; }
31009         for (iteration=1; zr*zr + zi*zi<=4 && iteration<=iteration_max; ++iteration) {
31010           const double temp = zr*zr - zi*zi + cr;
31011           zi = 2*zr*zi + ci;
31012           zr = temp;
31013         }
31014         if (iteration>iteration_max) {
31015           if (palette) {
31016             if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette(0,c);
31017             else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(0,c)*nopacity + (*this)(p,q,0,c)*copacity);
31018           } else {
31019             if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)0;
31020             else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)((*this)(p,q,0,c)*copacity);
31021           }
31022         } else if (normalized_iteration) {
31023           const float
31024             normz = (float)cimg::abs(zr*zr+zi*zi),
31025             niteration = (float)(iteration + 1 - std::log(std::log(normz))/ln2);
31026           if (palette) {
31027             if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._linear_atX(niteration,c);
31028             else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette._linear_atX(niteration,c)*nopacity + (*this)(p,q,0,c)*copacity);
31029           } else {
31030             if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)niteration;
31031             else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(niteration*nopacity + (*this)(p,q,0,c)*copacity);
31032           }
31033         } else {
31034           if (palette) {
31035             if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._atX(iteration,c);
31036             else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(iteration,c)*nopacity + (*this)(p,q,0,c)*copacity);
31037           } else {
31038             if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)iteration;
31039             else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(iteration*nopacity + (*this)(p,q,0,c)*copacity);
31040           }
31041         }
31042       }
31043       return *this;
31044     }
31045 
31046     //! Draw a quadratic Mandelbrot or Julia fractal set, computed using the Escape Time Algorithm.
31047     template<typename tc>
31048     CImg<T>& draw_mandelbrot(const CImg<tc>& color_palette, const float opacity=1,
31049                              const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2,
31050                              const unsigned int iteration_max=255,
31051                              const bool normalized_iteration=false,
31052                              const bool julia_set=false,
31053                              const double paramr=0, const double parami=0) {
31054       return draw_mandelbrot(0,0,_width-1,_height-1,color_palette,opacity,
31055                              z0r,z0i,z1r,z1i,iteration_max,normalized_iteration,julia_set,paramr,parami);
31056     }
31057 
31058     //! Draw a 1d gaussian function in the image instance.
31059     /**
31060        \param xc = X-coordinate of the gaussian center.
31061        \param sigma = Standard variation of the gaussian distribution.
31062        \param color = array of spectrum() values of type \c T, defining the drawing color.
31063        \param opacity = opacity of the drawing.
31064     **/
31065     template<typename tc>
31066     CImg<T>& draw_gaussian(const float xc, const float sigma,
31067                            const tc *const color, const float opacity=1) {
31068       if (!color)
31069         throw CImgArgumentException(_cimg_instance
31070                                     "draw_gaussian() : Specified color is (null).",
31071                                     cimg_instance);
31072 
31073       if (is_empty()) return *this;
31074       const float sigma2 = 2*sigma*sigma, nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
31075       const unsigned int whd = _width*_height*_depth;
31076       const tc *col = color;
31077       cimg_forX(*this,x) {
31078         const float dx = (x - xc), val = (float)std::exp(-dx*dx/sigma2);
31079         T *ptrd = data(x,0,0,0);
31080         if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
31081         else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
31082         col-=_spectrum;
31083       }
31084       return *this;
31085     }
31086 
31087     //! Draw an anisotropic 2d gaussian function.
31088     /**
31089        \param xc = X-coordinate of the gaussian center.
31090        \param yc = Y-coordinate of the gaussian center.
31091        \param tensor = 2x2 covariance matrix.
31092        \param color = array of spectrum() values of type \c T, defining the drawing color.
31093        \param opacity = opacity of the drawing.
31094     **/
31095     template<typename t, typename tc>
31096     CImg<T>& draw_gaussian(const float xc, const float yc, const CImg<t>& tensor,
31097                            const tc *const color, const float opacity=1) {
31098       if (tensor._width!=2 || tensor._height!=2 || tensor._depth!=1 || tensor._spectrum!=1)
31099         throw CImgArgumentException(_cimg_instance
31100                                     "draw_gaussian() : Specified tensor (%u,%u,%u,%u,%p) is not a 2x2 matrix.",
31101                                     cimg_instance,
31102                                     tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data);
31103       if (!color)
31104         throw CImgArgumentException(_cimg_instance
31105                                     "draw_gaussian() : Specified color is (null).",
31106                                     cimg_instance);
31107 
31108       if (is_empty()) return *this;
31109       typedef typename CImg<t>::Tfloat tfloat;
31110       const CImg<tfloat> invT = tensor.get_invert(), invT2 = (invT*invT)/(-2.0);
31111       const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = invT2(1,1);
31112       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
31113       const unsigned int whd = _width*_height*_depth;
31114       const tc *col = color;
31115       float dy = -yc;
31116       cimg_forY(*this,y) {
31117         float dx = -xc;
31118         cimg_forX(*this,x) {
31119           const float val = (float)std::exp(a*dx*dx + b*dx*dy + c*dy*dy);
31120           T *ptrd = data(x,y,0,0);
31121           if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
31122           else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
31123           col-=_spectrum;
31124           ++dx;
31125         }
31126         ++dy;
31127       }
31128       return *this;
31129     }
31130 
31131     //! Draw an anisotropic 2d gaussian function.
31132     template<typename tc>
31133     CImg<T>& draw_gaussian(const int xc, const int yc, const float r1, const float r2, const float ru, const float rv,
31134                            const tc *const color, const float opacity=1) {
31135       const double
31136         a = r1*ru*ru + r2*rv*rv,
31137         b = (r1-r2)*ru*rv,
31138         c = r1*rv*rv + r2*ru*ru;
31139       const CImg<Tfloat> tensor(2,2,1,1, a,b,b,c);
31140       return draw_gaussian(xc,yc,tensor,color,opacity);
31141     }
31142 
31143     //! Draw an isotropic 2d gaussian function.
31144     /**
31145        \param xc = X-coordinate of the gaussian center.
31146        \param yc = Y-coordinate of the gaussian center.
31147        \param sigma = standard variation of the gaussian distribution.
31148        \param color = array of spectrum() values of type \c T, defining the drawing color.
31149        \param opacity = opacity of the drawing.
31150     **/
31151     template<typename tc>
31152     CImg<T>& draw_gaussian(const float xc, const float yc, const float sigma,
31153                            const tc *const color, const float opacity=1) {
31154       return draw_gaussian(xc,yc,CImg<floatT>::diagonal(sigma,sigma),color,opacity);
31155     }
31156 
31157     //! Draw an anisotropic 3d gaussian function.
31158     /**
31159        \param xc = X-coordinate of the gaussian center.
31160        \param yc = Y-coordinate of the gaussian center.
31161        \param zc = Z-coordinate of the gaussian center.
31162        \param tensor = 3x3 covariance matrix.
31163        \param color = array of spectrum() values of type \c T, defining the drawing color.
31164        \param opacity = opacity of the drawing.
31165     **/
31166     template<typename t, typename tc>
31167     CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const CImg<t>& tensor,
31168                            const tc *const color, const float opacity=1) {
31169       if (is_empty()) return *this;
31170       typedef typename CImg<t>::Tfloat tfloat;
31171       if (tensor._width!=3 || tensor._height!=3 || tensor._depth!=1 || tensor._spectrum!=1)
31172         throw CImgArgumentException(_cimg_instance
31173                                     "draw_gaussian() : Specified tensor (%u,%u,%u,%u,%p) is not a 3x3 matrix.",
31174                                     cimg_instance,
31175                                     tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data);
31176 
31177       const CImg<tfloat> invT = tensor.get_invert(), invT2 = (invT*invT)/(-2.0);
31178       const tfloat a = invT(0,0), b = 2*invT(1,0), c = 2*invT(2,0), d = invT(1,1), e = 2*invT(2,1), f = invT(2,2);
31179       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
31180       const unsigned int whd = _width*_height*_depth;
31181       const tc *col = color;
31182       cimg_forXYZ(*this,x,y,z) {
31183         const float
31184           dx = (x - xc), dy = (y - yc), dz = (z - zc),
31185           val = (float)std::exp(a*dx*dx + b*dx*dy + c*dx*dz + d*dy*dy + e*dy*dz + f*dz*dz);
31186         T *ptrd = data(x,y,z,0);
31187         if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
31188         else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
31189         col-=_spectrum;
31190       }
31191       return *this;
31192     }
31193 
31194     //! Draw an isotropic 3d gaussian function.
31195    /**
31196        \param xc = X-coordinate of the gaussian center.
31197        \param yc = Y-coordinate of the gaussian center.
31198        \param zc = Z-coordinate of the gaussian center.
31199        \param sigma = standard variation of the gaussian distribution.
31200        \param color = array of spectrum() values of type \c T, defining the drawing color.
31201        \param opacity = opacity of the drawing.
31202     **/
31203     template<typename tc>
31204     CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const float sigma,
31205                            const tc *const color, const float opacity=1) {
31206       return draw_gaussian(xc,yc,zc,CImg<floatT>::diagonal(sigma,sigma,sigma),color,opacity);
31207     }
31208 
31209     //! Draw a 3d object.
31210     /**
31211        \param X = X-coordinate of the 3d object position
31212        \param Y = Y-coordinate of the 3d object position
31213        \param Z = Z-coordinate of the 3d object position
31214        \param vertices = Image Nx3 describing 3d point coordinates
31215        \param primitives = List of P primitives
31216        \param colors = List of P color (or textures)
31217        \param opacities = Image or list of P opacities
31218        \param render_type = Render type (0=Points, 1=Lines, 2=Faces (no light), 3=Faces (flat), 4=Faces(Gouraud)
31219        \param double_sided = Tell if object faces have two sides or are oriented.
31220        \param focale = length of the focale (0 for parallel projection)
31221        \param lightx = X-coordinate of the light
31222        \param lighty = Y-coordinate of the light
31223        \param lightz = Z-coordinate of the light
31224        \param specular_shine = Shininess of the object
31225     **/
31226     template<typename tp, typename tf, typename tc, typename to>
31227     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
31228                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
31229                            const CImgList<tc>& colors, const CImg<to>& opacities,
31230                            const unsigned int render_type=4,
31231                            const bool double_sided=false, const float focale=500,
31232                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
31233                            const float specular_light=0.2f, const float specular_shine=0.1f) {
31234       return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type,double_sided,focale,lightx,lighty,lightz,
31235                            specular_light,specular_shine,CImg<floatT>::empty());
31236     }
31237 
31238     template<typename tp, typename tf, typename tc, typename to, typename tz>
31239     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
31240                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
31241                            const CImgList<tc>& colors, const CImg<to>& opacities,
31242                            const unsigned int render_type,
31243                            const bool double_sided, const float focale,
31244                            const float lightx, const float lighty, const float lightz,
31245                            const float specular_light, const float specular_shine,
31246                            CImg<tz>& zbuffer) {
31247       return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
31248                             render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,1);
31249     }
31250 
31251 #ifdef cimg_use_board
31252     template<typename tp, typename tf, typename tc, typename to>
31253     CImg<T>& draw_object3d(LibBoard::Board& board,
31254                            const float x0, const float y0, const float z0,
31255                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
31256                            const CImgList<tc>& colors, const CImg<to>& opacities,
31257                            const unsigned int render_type=4,
31258                            const bool double_sided=false, const float focale=500,
31259                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
31260                            const float specular_light=0.2f, const float specular_shine=0.1f) {
31261       return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type,double_sided,focale,lightx,lighty,lightz,
31262                            specular_light,specular_shine,CImg<floatT>::empty());
31263     }
31264 
31265     template<typename tp, typename tf, typename tc, typename to, typename tz>
31266     CImg<T>& draw_object3d(LibBoard::Board& board,
31267                            const float x0, const float y0, const float z0,
31268                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
31269                            const CImgList<tc>& colors, const CImg<to>& opacities,
31270                            const unsigned int render_type,
31271                            const bool double_sided, const float focale,
31272                            const float lightx, const float lighty, const float lightz,
31273                            const float specular_light, const float specular_shine,
31274                            CImg<tz>& zbuffer) {
31275       return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
31276                             render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,1);
31277     }
31278 #endif
31279 
31280     template<typename tp, typename tf, typename tc, typename to>
31281     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
31282                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
31283                            const CImgList<tc>& colors, const CImgList<to>& opacities,
31284                            const unsigned int render_type=4,
31285                            const bool double_sided=false, const float focale=500,
31286                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
31287                            const float specular_light=0.2f, const float specular_shine=0.1f) {
31288       return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type,double_sided,focale,lightx,lighty,lightz,
31289                            specular_light,specular_shine,CImg<floatT>::empty());
31290     }
31291 
31292     template<typename tp, typename tf, typename tc, typename to, typename tz>
31293     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
31294                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
31295                            const CImgList<tc>& colors, const CImgList<to>& opacities,
31296                            const unsigned int render_type,
31297                            const bool double_sided, const float focale,
31298                            const float lightx, const float lighty, const float lightz,
31299                            const float specular_light, const float specular_shine,
31300                            CImg<tz>& zbuffer) {
31301       return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
31302                             render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,1);
31303     }
31304 
31305 #ifdef cimg_use_board
31306     template<typename tp, typename tf, typename tc, typename to>
31307     CImg<T>& draw_object3d(LibBoard::Board& board,
31308                            const float x0, const float y0, const float z0,
31309                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
31310                            const CImgList<tc>& colors, const CImgList<to>& opacities,
31311                            const unsigned int render_type=4,
31312                            const bool double_sided=false, const float focale=500,
31313                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
31314                            const float specular_light=0.2f, const float specular_shine=0.1f) {
31315       return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type,double_sided,focale,lightx,lighty,lightz,
31316                            specular_light,specular_shine,CImg<floatT>::empty());
31317     }
31318 
31319     template<typename tp, typename tf, typename tc, typename to, typename tz>
31320     CImg<T>& draw_object3d(LibBoard::Board& board,
31321                            const float x0, const float y0, const float z0,
31322                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
31323                            const CImgList<tc>& colors, const CImgList<to>& opacities,
31324                            const unsigned int render_type,
31325                            const bool double_sided, const float focale,
31326                            const float lightx, const float lighty, const float lightz,
31327                            const float specular_light, const float specular_shine,
31328                            CImg<tz>& zbuffer) {
31329       return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
31330                             render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,1);
31331     }
31332 #endif
31333 
31334     //! Draw a 3d object.
31335     template<typename tp, typename tf, typename tc>
31336     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
31337                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
31338                            const CImgList<tc>& colors,
31339                            const unsigned int render_type=4,
31340                            const bool double_sided=false, const float focale=500,
31341                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
31342                            const float specular_light=0.2f, const float specular_shine=0.1f) {
31343       return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::empty(),
31344                            render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,CImg<floatT>::empty());
31345     }
31346 
31347     template<typename tp, typename tf, typename tc, typename tz>
31348     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
31349                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
31350                            const CImgList<tc>& colors,
31351                            const unsigned int render_type,
31352                            const bool double_sided, const float focale,
31353                            const float lightx, const float lighty, const float lightz,
31354                            const float specular_light, const float specular_shine,
31355                            CImg<tz>& zbuffer) {
31356       return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::empty(),
31357                            render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,zbuffer);
31358     }
31359 
31360 #ifdef cimg_use_board
31361     template<typename tp, typename tf, typename tc, typename to>
31362     CImg<T>& draw_object3d(LibBoard::Board& board,
31363                            const float x0, const float y0, const float z0,
31364                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
31365                            const CImgList<tc>& colors,
31366                            const unsigned int render_type=4,
31367                            const bool double_sided=false, const float focale=500,
31368                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
31369                            const float specular_light=0.2f, const float specular_shine=0.1f) {
31370       return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::empty(),
31371                            render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,CImg<floatT>::empty());
31372     }
31373 
31374     template<typename tp, typename tf, typename tc, typename to, typename tz>
31375     CImg<T>& draw_object3d(LibBoard::Board& board,
31376                            const float x0, const float y0, const float z0,
31377                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
31378                            const CImgList<tc>& colors,
31379                            const unsigned int render_type,
31380                            const bool double_sided, const float focale,
31381                            const float lightx, const float lighty, const float lightz,
31382                            const float specular_light, const float specular_shine,
31383                            CImg<tz>& zbuffer) {
31384       return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::empty(),
31385                            render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,zbuffer);
31386     }
31387 #endif
31388 
31389     template<typename tc, typename to>
31390     void __draw_object3d(const unsigned int n_primitive, const CImgList<to>& opacities, const CImg<tc>& color,
31391                          const int nx0, const int ny0, const CImg<T>& sprite, const float opac) {
31392       if (n_primitive<opacities._width && opacities[n_primitive].is_sameXY(color))
31393         draw_image(nx0,ny0,sprite,opacities[n_primitive].get_resize(sprite._width,sprite._height,1,sprite._spectrum,1));
31394       else draw_image(nx0,ny0,sprite,opac);
31395     }
31396 
31397     template<typename tc, typename to>
31398     void __draw_object3d(const unsigned int, const CImg<to>&, const CImg<tc>&,
31399                          const int nx0, const int ny0, const CImg<T>& sprite, const float opac) {
31400       draw_image(nx0,ny0,sprite,opac);
31401     }
31402 
31403     template<typename tz, typename tp, typename tf, typename tc, typename to>
31404     CImg<T>& _draw_object3d(void *const pboard, CImg<tz>& zbuffer,
31405                             const float X, const float Y, const float Z,
31406                             const CImg<tp>& vertices,
31407                             const CImgList<tf>& primitives,
31408                             const CImgList<tc>& colors,
31409                             const to& opacities,
31410                             const unsigned int render_type,
31411                             const bool double_sided, const float focale,
31412                             const float lightx, const float lighty, const float lightz,
31413                             const float specular_light, const float specular_shine,
31414                             const float sprite_scale) {
31415       typedef typename cimg::superset2<tp,tz,float>::type tpfloat;
31416       if (is_empty() || !vertices || !primitives) return *this;
31417       char error_message[1024] = { 0 };
31418       if (!vertices.is_object3d(primitives,colors,opacities,false,error_message))
31419         throw CImgArgumentException(_cimg_instance
31420                                     "draw_object3d() : Invalid specified 3d object (%u,%u) (%s).",
31421                                     cimg_instance,vertices._width,primitives._width,error_message);
31422 #ifndef cimg_use_board
31423       if (pboard) return *this;
31424 #endif
31425       const float
31426         nspec = 1 - (specular_light<0.0f?0.0f:(specular_light>1.0f?1.0f:specular_light)),
31427         nspec2 = 1 + (specular_shine<0.0f?0.0f:specular_shine),
31428         nsl1 = (nspec2 - 1)/cimg::sqr(nspec - 1),
31429         nsl2 = 1 - 2*nsl1*nspec,
31430         nsl3 = nspec2 - nsl1 - nsl2;
31431 
31432       // Create light texture for phong-like rendering.
31433       CImg<floatT> light_texture;
31434       if (render_type==5) {
31435         if (colors._width>primitives._width) {
31436           static CImg<floatT> default_light_texture;
31437           static const tc *lptr = 0;
31438           static tc ref_values[64] = { 0 };
31439           const CImg<tc>& img = colors.back();
31440           bool is_same_texture = (lptr==img._data);
31441           if (is_same_texture)
31442             for (unsigned int r = 0, j = 0; j<8; ++j)
31443               for (unsigned int i = 0; i<8; ++i)
31444                 if (ref_values[r++]!=img(i*img._width/9,j*img._height/9,0,(i+j)%img._spectrum)) { is_same_texture = false; break; }
31445           if (!is_same_texture || default_light_texture._spectrum<_spectrum) {
31446             (default_light_texture.assign(img,false)/=255).resize(-100,-100,1,_spectrum);
31447             lptr = colors.back().data();
31448             for (unsigned int r = 0, j = 0; j<8; ++j)
31449               for (unsigned int i = 0; i<8; ++i)
31450                 ref_values[r++] = img(i*img._width/9,j*img._height/9,0,(i+j)%img._spectrum);
31451           }
31452           light_texture.assign(default_light_texture,true);
31453         } else {
31454           static CImg<floatT> default_light_texture;
31455           static float olightx = 0, olighty = 0, olightz = 0, ospecular_shine = 0;
31456           if (!default_light_texture ||
31457               lightx!=olightx || lighty!=olighty || lightz!=olightz ||
31458               specular_shine!=ospecular_shine || default_light_texture._spectrum<_spectrum) {
31459             default_light_texture.assign(512,512);
31460             const float
31461               dlx = lightx - X,
31462               dly = lighty - Y,
31463               dlz = lightz - Z,
31464               nl = (float)std::sqrt(dlx*dlx + dly*dly + dlz*dlz),
31465               nlx = default_light_texture._width/2*(1 + dlx/nl),
31466               nly = default_light_texture._height/2*(1 + dly/nl),
31467               white[] = { 1 };
31468             default_light_texture.draw_gaussian(nlx,nly,default_light_texture._width/3.0f,white);
31469             cimg_forXY(default_light_texture,x,y) {
31470               const float factor = default_light_texture(x,y);
31471               if (factor>nspec) default_light_texture(x,y) = cimg::min(2,nsl1*factor*factor + nsl2*factor + nsl3);
31472             }
31473             default_light_texture.resize(-100,-100,1,_spectrum);
31474             olightx = lightx; olighty = lighty; olightz = lightz; ospecular_shine = specular_shine;
31475           }
31476           light_texture.assign(default_light_texture,true);
31477         }
31478       }
31479 
31480       // Compute 3d to 2d projection.
31481       CImg<tpfloat> projections(vertices._width,2);
31482       tpfloat parallzmin = cimg::type<tpfloat>::max();
31483       const float absfocale = focale?cimg::abs(focale):0;
31484       if (absfocale) cimg_forX(projections,l) { // Perspective projection
31485           const tpfloat
31486             x = (tpfloat)vertices(l,0),
31487             y = (tpfloat)vertices(l,1),
31488             z = (tpfloat)vertices(l,2);
31489           const tpfloat projectedz = z + Z + absfocale;
31490           projections(l,1) = Y + absfocale*y/projectedz;
31491           projections(l,0) = X + absfocale*x/projectedz;
31492         } else cimg_forX(projections,l) { // Parallel projection
31493           const tpfloat
31494             x = (tpfloat)vertices(l,0),
31495             y = (tpfloat)vertices(l,1),
31496             z = (tpfloat)vertices(l,2);
31497           if (z<parallzmin) parallzmin = z;
31498           projections(l,1) = Y + y;
31499           projections(l,0) = X + x;
31500         }
31501 
31502       // Compute and sort visible primitives.
31503       CImg<uintT> visibles(primitives._width);
31504       CImg<tpfloat> zrange(primitives._width);
31505       unsigned int nb_visibles = 0;
31506       const tpfloat zmin = absfocale?(tpfloat)(1.5f - absfocale):cimg::type<tpfloat>::min();
31507       cimglist_for(primitives,l) {
31508         const CImg<tf>& primitive = primitives[l];
31509         switch (primitive.size()) {
31510         case 1 : { // Point
31511           const unsigned int i0 = (unsigned int)primitive(0);
31512           const tpfloat z0 = Z + vertices(i0,2);
31513           if (z0>zmin) {
31514             visibles(nb_visibles) = (unsigned int)l;
31515             zrange(nb_visibles++) = z0;
31516           }
31517         } break;
31518         case 5 : { // Sphere
31519           const unsigned int
31520             i0 = (unsigned int)primitive(0),
31521             i1 = (unsigned int)primitive(1);
31522           const tpfloat
31523             x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
31524             x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2);
31525           tpfloat xm, xM, ym, yM;
31526           if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
31527           if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
31528           if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin) {
31529             visibles(nb_visibles) = (unsigned int)l;
31530             zrange(nb_visibles++) = (z0 + z1)/2;
31531           }
31532         } break;
31533         case 2 : // Segment
31534         case 6 : {
31535           const unsigned int
31536             i0 = (unsigned int)primitive(0),
31537             i1 = (unsigned int)primitive(1);
31538           const tpfloat
31539             x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
31540             x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2);
31541           tpfloat xm, xM, ym, yM;
31542           if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
31543           if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
31544           if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin) {
31545             visibles(nb_visibles) = (unsigned int)l;
31546             zrange(nb_visibles++) = (z0 + z1)/2;
31547           }
31548         } break;
31549         case 3 :  // Triangle
31550         case 9 : {
31551           const unsigned int
31552             i0 = (unsigned int)primitive(0),
31553             i1 = (unsigned int)primitive(1),
31554             i2 = (unsigned int)primitive(2);
31555           const tpfloat
31556             x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
31557             x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2),
31558             x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2);
31559           tpfloat xm, xM, ym, yM;
31560           if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
31561           if (x2<xm) xm = x2;
31562           if (x2>xM) xM = x2;
31563           if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
31564           if (y2<ym) ym = y2;
31565           if (y2>yM) yM = y2;
31566           if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin) {
31567             const tpfloat d = (x1-x0)*(y2-y0) - (x2-x0)*(y1-y0);
31568             if (double_sided || d<0) {
31569               visibles(nb_visibles) = (unsigned int)l;
31570               zrange(nb_visibles++) = (z0 + z1 + z2)/3;
31571             }
31572           }
31573         } break;
31574         case 4 : // Rectangle
31575         case 12 : {
31576           const unsigned int
31577             i0 = (unsigned int)primitive(0),
31578             i1 = (unsigned int)primitive(1),
31579             i2 = (unsigned int)primitive(2),
31580             i3 = (unsigned int)primitive(3);
31581           const tpfloat
31582             x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
31583             x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2),
31584             x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2),
31585             x3 = projections(i3,0), y3 = projections(i3,1), z3 = Z + vertices(i3,2);
31586           tpfloat xm, xM, ym, yM;
31587           if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
31588           if (x2<xm) xm = x2;
31589           if (x2>xM) xM = x2;
31590           if (x3<xm) xm = x3;
31591           if (x3>xM) xM = x3;
31592           if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
31593           if (y2<ym) ym = y2;
31594           if (y2>yM) yM = y2;
31595           if (y3<ym) ym = y3;
31596           if (y3>yM) yM = y3;
31597           if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin) {
31598             const float d = (x1 - x0)*(y2 - y0) - (x2 - x0)*(y1 - y0);
31599             if (double_sided || d<0) {
31600               visibles(nb_visibles) = (unsigned int)l;
31601               zrange(nb_visibles++) = (z0 + z1 + z2 + z3)/4;
31602             }
31603           }
31604         } break;
31605         default :
31606           throw CImgArgumentException(_cimg_instance
31607                                       "draw_object3d() : Invalid primitive[%u] with size %u "
31608                                       "(should have size 1,2,3,4,5,6,9 or 12).",
31609                                       cimg_instance,
31610                                       l,primitive.size());
31611         }
31612       }
31613       if (nb_visibles<=0) return *this;
31614       CImg<uintT> permutations;
31615       CImg<tpfloat>(zrange._data,nb_visibles,1,1,1,true).sort(permutations,false);
31616 
31617       // Compute light properties
31618       CImg<floatT> lightprops;
31619       switch (render_type) {
31620       case 3 : { // Flat Shading
31621         lightprops.assign(nb_visibles);
31622         cimg_forX(lightprops,l) {
31623           const CImg<tf>& primitive = primitives(visibles(permutations(l)));
31624           const unsigned int psize = primitive.size();
31625           if (psize==3 || psize==4 || psize==9 || psize==12) {
31626             const unsigned int
31627               i0 = (unsigned int)primitive(0),
31628               i1 = (unsigned int)primitive(1),
31629               i2 = (unsigned int)primitive(2);
31630             const tpfloat
31631               x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2),
31632               x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2),
31633               x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2),
31634               dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0,
31635               dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0,
31636               nx = dy1*dz2 - dz1*dy2,
31637               ny = dz1*dx2 - dx1*dz2,
31638               nz = dx1*dy2 - dy1*dx2,
31639               norm = (tpfloat)std::sqrt(1e-5f + nx*nx + ny*ny + nz*nz),
31640               lx = X + (x0 + x1 + x2)/3 - lightx,
31641               ly = Y + (y0 + y1 + y2)/3 - lighty,
31642               lz = Z + (z0 + z1 + z2)/3 - lightz,
31643               nl = (tpfloat)std::sqrt(1e-5f + lx*lx + ly*ly + lz*lz),
31644               factor = cimg::max(cimg::abs(-lx*nx-ly*ny-lz*nz)/(norm*nl),0);
31645             lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3);
31646           } else lightprops[l] = 1;
31647         }
31648       } break;
31649 
31650       case 4 : // Gouraud Shading
31651       case 5 : { // Phong-Shading
31652         CImg<tpfloat> vertices_normals(vertices._width,3,1,1,0);
31653         for (unsigned int l = 0; l<nb_visibles; ++l) {
31654           const CImg<tf>& primitive = primitives[visibles(l)];
31655           const unsigned int psize = primitive.size();
31656           const bool
31657             triangle_flag = (psize==3) || (psize==9),
31658             rectangle_flag = (psize==4) || (psize==12);
31659           if (triangle_flag || rectangle_flag) {
31660             const unsigned int
31661               i0 = (unsigned int)primitive(0),
31662               i1 = (unsigned int)primitive(1),
31663               i2 = (unsigned int)primitive(2),
31664               i3 = rectangle_flag?(unsigned int)primitive(3):0;
31665             const tpfloat
31666               x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2),
31667               x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2),
31668               x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2),
31669               dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0,
31670               dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0,
31671               nnx = dy1*dz2 - dz1*dy2,
31672               nny = dz1*dx2 - dx1*dz2,
31673               nnz = dx1*dy2 - dy1*dx2,
31674               norm = (tpfloat)(1e-5f + std::sqrt(nnx*nnx + nny*nny + nnz*nnz)),
31675               nx = nnx/norm,
31676               ny = nny/norm,
31677               nz = nnz/norm;
31678             vertices_normals(i0,0)+=nx; vertices_normals(i0,1)+=ny; vertices_normals(i0,2)+=nz;
31679             vertices_normals(i1,0)+=nx; vertices_normals(i1,1)+=ny; vertices_normals(i1,2)+=nz;
31680             vertices_normals(i2,0)+=nx; vertices_normals(i2,1)+=ny; vertices_normals(i2,2)+=nz;
31681             if (rectangle_flag) { vertices_normals(i3,0)+=nx; vertices_normals(i3,1)+=ny; vertices_normals(i3,2)+=nz; }
31682           }
31683         }
31684 
31685         if (double_sided) cimg_forX(vertices_normals,p) if (vertices_normals(p,2)>0) {
31686           vertices_normals(p,0) = -vertices_normals(p,0);
31687           vertices_normals(p,1) = -vertices_normals(p,1);
31688           vertices_normals(p,2) = -vertices_normals(p,2);
31689         }
31690 
31691         if (render_type==4) {
31692           lightprops.assign(vertices._width);
31693           cimg_forX(lightprops,l) {
31694             const tpfloat
31695               nx = vertices_normals(l,0),
31696               ny = vertices_normals(l,1),
31697               nz = vertices_normals(l,2),
31698               norm = (tpfloat)std::sqrt(1e-5f + nx*nx + ny*ny + nz*nz),
31699               lx = X + vertices(l,0) - lightx,
31700               ly = Y + vertices(l,1) - lighty,
31701               lz = Z + vertices(l,2) - lightz,
31702               nl = (tpfloat)std::sqrt(1e-5f + lx*lx + ly*ly + lz*lz),
31703               factor = cimg::max((-lx*nx-ly*ny-lz*nz)/(norm*nl),0);
31704             lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3);
31705           }
31706         } else {
31707           const unsigned int
31708             lw2 = light_texture._width/2 - 1,
31709             lh2 = light_texture._height/2 - 1;
31710           lightprops.assign(vertices._width,2);
31711           cimg_forX(lightprops,l) {
31712             const tpfloat
31713               nx = vertices_normals(l,0),
31714               ny = vertices_normals(l,1),
31715               nz = vertices_normals(l,2),
31716               norm = (tpfloat)std::sqrt(1e-5f + nx*nx + ny*ny + nz*nz),
31717               nnx = nx/norm,
31718               nny = ny/norm;
31719             lightprops(l,0) = lw2*(1 + nnx);
31720             lightprops(l,1) = lh2*(1 + nny);
31721           }
31722         }
31723       } break;
31724       }
31725 
31726       // Draw visible primitives
31727       const float _focale = absfocale?absfocale:(1-parallzmin);
31728       const CImg<tc> default_color(1,_spectrum,1,1,(tc)200);
31729       for (unsigned int l = 0; l<nb_visibles; ++l) {
31730         const unsigned int n_primitive = visibles(permutations(l));
31731         const CImg<tf>& primitive = primitives[n_primitive];
31732         const CImg<tc>
31733           &__color = n_primitive<colors._width?colors[n_primitive]:CImg<tc>(),
31734           _color = (__color && __color.size()!=_spectrum && __color._spectrum<_spectrum)?colors[n_primitive].get_resize(-100,-100,-100,_spectrum,0):CImg<tc>(),
31735           &color = _color?_color:(__color?__color:default_color);
31736         const tc *const pcolor = color._data;
31737         const float opac = n_primitive<opacities.size()?opacities(n_primitive,0):1.0f;
31738 #ifdef cimg_use_board
31739         LibBoard::Board &board = *(LibBoard::Board*)pboard;
31740 #endif
31741 
31742         switch (primitive.size()) {
31743         case 1 : { // Colored point or sprite
31744           const unsigned int n0 = (unsigned int)primitive[0];
31745           const int x0 = (int)projections(n0,0), y0 = (int)projections(n0,1);
31746           if (color.size()==_spectrum) { // Colored point.
31747             draw_point(x0,y0,pcolor,opac);
31748 #ifdef cimg_use_board
31749             if (pboard) {
31750               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
31751               board.fillCircle((float)x0,height()-(float)y0,0);
31752             }
31753 #endif
31754           } else { // Colored sprite.
31755             if (!__color)
31756               throw CImgArgumentException(_cimg_instance
31757                                           "draw_object3d() : Undefined texture for sprite primitive [%u].",
31758                                           cimg_instance,n_primitive);
31759             const tpfloat z = Z + vertices(n0,2);
31760             const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1);
31761             const unsigned int
31762               _sw = (unsigned int)(color._width*factor),
31763               _sh = (unsigned int)(color._height*factor),
31764               sw = _sw?_sw:1,
31765               sh = _sh?_sh:1;
31766             const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2;
31767             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())) {
31768               const CImg<tc>
31769                 _sprite = (sw!=color._width || sh!=color._height)?color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<tc>(),
31770                 &sprite = _sprite?_sprite:color;
31771               __draw_object3d(n_primitive,opacities,color,nx0,ny0,sprite,opac);
31772 #ifdef cimg_use_board
31773               if (pboard) {
31774                 board.setPenColorRGBi(128,128,128);
31775                 board.setFillColor(LibBoard::Color::None);
31776                 board.drawRectangle((float)nx0,height()-(float)ny0,sw,sh);
31777               }
31778 #endif
31779             }
31780           }
31781         } break;
31782         case 2 : { // Colored line
31783           const unsigned int
31784             n0 = (unsigned int)primitive[0],
31785             n1 = (unsigned int)primitive[1];
31786           const int
31787             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
31788             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1);
31789           const float
31790             z0 = vertices(n0,2) + Z + _focale,
31791             z1 = vertices(n1,2) + Z + _focale;
31792           if (render_type) {
31793             if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opac);
31794             else draw_line(x0,y0,x1,y1,pcolor,opac);
31795 #ifdef cimg_use_board
31796             if (pboard) {
31797               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
31798               board.drawLine((float)x0,height()-(float)y0,x1,height()-(float)y1);
31799             }
31800 #endif
31801           } else {
31802             draw_point(x0,y0,pcolor,opac).draw_point(x1,y1,pcolor,opac);
31803 #ifdef cimg_use_board
31804             if (pboard) {
31805               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
31806               board.drawCircle((float)x0,height()-(float)y0,0);
31807               board.drawCircle((float)x1,height()-(float)y1,0);
31808             }
31809 #endif
31810           }
31811         } break;
31812         case 5 : { // Colored sphere
31813           const unsigned int
31814             n0 = (unsigned int)primitive[0],
31815             n1 = (unsigned int)primitive[1];
31816           const float
31817             Xc = 0.5f*((float)vertices(n0,0) + (float)vertices(n1,0)),
31818             Yc = 0.5f*((float)vertices(n0,1) + (float)vertices(n1,1)),
31819             Zc = 0.5f*((float)vertices(n0,2) + (float)vertices(n1,2)),
31820             zc = Z + Zc + _focale,
31821             xc = X + Xc*(absfocale?absfocale/zc:1),
31822             yc = Y + Yc*(absfocale?absfocale/zc:1),
31823             radius = std::sqrt(cimg::sqr(Xc-vertices(n0,0)) + cimg::sqr(Yc-vertices(n0,1)) + cimg::sqr(Zc-vertices(n0,2)))*(absfocale?absfocale/zc:1);
31824           switch (render_type) {
31825           case 0 :
31826             draw_point((int)xc,(int)yc,pcolor,opac);
31827 #ifdef cimg_use_board
31828             if (pboard) {
31829               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
31830               board.fillCircle(xc,height()-yc,0);
31831             }
31832 #endif
31833             break;
31834           case 1 :
31835             draw_circle((int)xc,(int)yc,(int)radius,pcolor,opac,~0U);
31836 #ifdef cimg_use_board
31837             if (pboard) {
31838               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
31839               board.setFillColor(LibBoard::Color::None);
31840               board.drawCircle(xc,height()-yc,radius);
31841             }
31842 #endif
31843             break;
31844           default :
31845             draw_circle((int)xc,(int)yc,(int)radius,pcolor,opac);
31846 #ifdef cimg_use_board
31847             if (pboard) {
31848               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
31849               board.fillCircle(xc,height()-yc,radius);
31850             }
31851 #endif
31852             break;
31853           }
31854         } break;
31855         case 6 : { // Textured line
31856           if (!__color)
31857             throw CImgArgumentException(_cimg_instance
31858                                         "draw_object3d() : Undefined texture for line primitive [%u].",
31859                                         cimg_instance,n_primitive);
31860           const unsigned int
31861             n0 = (unsigned int)primitive[0],
31862             n1 = (unsigned int)primitive[1],
31863             tx0 = (unsigned int)primitive[2],
31864             ty0 = (unsigned int)primitive[3],
31865             tx1 = (unsigned int)primitive[4],
31866             ty1 = (unsigned int)primitive[5];
31867           const int
31868             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
31869             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1);
31870           const float
31871             z0 = vertices(n0,2) + Z + _focale,
31872             z1 = vertices(n1,2) + Z + _focale;
31873           if (render_type) {
31874             if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac);
31875             else draw_line(x0,y0,x1,y1,color,tx0,ty0,tx1,ty1,opac);
31876 #ifdef cimg_use_board
31877             if (pboard) {
31878               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
31879               board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1);
31880             }
31881 #endif
31882           } else {
31883             draw_point(x0,y0,color.get_vector_at(tx0,ty0)._data,opac).
31884               draw_point(x1,y1,color.get_vector_at(tx1,ty1)._data,opac);
31885 #ifdef cimg_use_board
31886             if (pboard) {
31887               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
31888               board.drawCircle((float)x0,height()-(float)y0,0);
31889               board.drawCircle((float)x1,height()-(float)y1,0);
31890             }
31891 #endif
31892           }
31893         } break;
31894         case 3 : { // Colored triangle
31895           const unsigned int
31896             n0 = (unsigned int)primitive[0],
31897             n1 = (unsigned int)primitive[1],
31898             n2 = (unsigned int)primitive[2];
31899           const int
31900             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
31901             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
31902             x2 = (int)projections(n2,0), y2 = (int)projections(n2,1);
31903           const float
31904             z0 = vertices(n0,2) + Z + _focale,
31905             z1 = vertices(n1,2) + Z + _focale,
31906             z2 = vertices(n2,2) + Z + _focale;
31907           switch (render_type) {
31908           case 0 :
31909             draw_point(x0,y0,pcolor,opac).draw_point(x1,y1,pcolor,opac).draw_point(x2,y2,pcolor,opac);
31910 #ifdef cimg_use_board
31911             if (pboard) {
31912               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
31913               board.drawCircle((float)x0,height()-(float)y0,0);
31914               board.drawCircle((float)x1,height()-(float)y1,0);
31915               board.drawCircle((float)x2,height()-(float)y2,0);
31916             }
31917 #endif
31918             break;
31919           case 1 :
31920             if (zbuffer)
31921               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opac).draw_line(zbuffer,x0,y0,z0,x2,y2,z2,pcolor,opac).
31922                 draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opac);
31923             else
31924               draw_line(x0,y0,x1,y1,pcolor,opac).draw_line(x0,y0,x2,y2,pcolor,opac).
31925                 draw_line(x1,y1,x2,y2,pcolor,opac);
31926 #ifdef cimg_use_board
31927             if (pboard) {
31928               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
31929               board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1);
31930               board.drawLine((float)x0,height()-(float)y0,(float)x2,height()-(float)y2);
31931               board.drawLine((float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
31932             }
31933 #endif
31934             break;
31935           case 2 :
31936             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opac);
31937             else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opac);
31938 #ifdef cimg_use_board
31939             if (pboard) {
31940               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
31941               board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
31942             }
31943 #endif
31944             break;
31945           case 3 :
31946             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opac,lightprops(l));
31947             else _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opac,lightprops(l));
31948 #ifdef cimg_use_board
31949             if (pboard) {
31950               const float lp = cimg::min(lightprops(l),1);
31951               board.setPenColorRGBi((unsigned char)(color[0]*lp),
31952                                      (unsigned char)(color[1]*lp),
31953                                      (unsigned char)(color[2]*lp),
31954                                      (unsigned char)(opac*255));
31955               board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
31956             }
31957 #endif
31958             break;
31959           case 4 :
31960             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,lightprops(n0),lightprops(n1),lightprops(n2),opac);
31961             else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprops(n0),lightprops(n1),lightprops(n2),opac);
31962 #ifdef cimg_use_board
31963             if (pboard) {
31964               board.setPenColorRGBi((unsigned char)(color[0]),
31965                                      (unsigned char)(color[1]),
31966                                      (unsigned char)(color[2]),
31967                                      (unsigned char)(opac*255));
31968               board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprops(n0),
31969                                          (float)x1,height()-(float)y1,lightprops(n1),
31970                                          (float)x2,height()-(float)y2,lightprops(n2));
31971             }
31972 #endif
31973             break;
31974           case 5 : {
31975             const unsigned int
31976               lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1),
31977               lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1),
31978               lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1);
31979             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac);
31980             else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac);
31981 #ifdef cimg_use_board
31982             if (pboard) {
31983               const float
31984                 l0 = light_texture((int)(light_texture.width()/2*(1+lightprops(n0,0))), (int)(light_texture.height()/2*(1+lightprops(n0,1)))),
31985                 l1 = light_texture((int)(light_texture.width()/2*(1+lightprops(n1,0))), (int)(light_texture.height()/2*(1+lightprops(n1,1)))),
31986                 l2 = light_texture((int)(light_texture.width()/2*(1+lightprops(n2,0))), (int)(light_texture.height()/2*(1+lightprops(n2,1))));
31987               board.setPenColorRGBi((unsigned char)(color[0]),
31988                                      (unsigned char)(color[1]),
31989                                      (unsigned char)(color[2]),
31990                                      (unsigned char)(opac*255));
31991               board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,
31992                                          (float)x1,height()-(float)y1,l1,
31993                                          (float)x2,height()-(float)y2,l2);
31994             }
31995 #endif
31996           } break;
31997           }
31998         } break;
31999         case 4 : { // Colored rectangle
32000           const unsigned int
32001             n0 = (unsigned int)primitive[0],
32002             n1 = (unsigned int)primitive[1],
32003             n2 = (unsigned int)primitive[2],
32004             n3 = (unsigned int)primitive[3];
32005           const int
32006             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
32007             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
32008             x2 = (int)projections(n2,0), y2 = (int)projections(n2,1),
32009             x3 = (int)projections(n3,0), y3 = (int)projections(n3,1);
32010           const float
32011             z0 = vertices(n0,2) + Z + _focale,
32012             z1 = vertices(n1,2) + Z + _focale,
32013             z2 = vertices(n2,2) + Z + _focale,
32014             z3 = vertices(n3,2) + Z + _focale;
32015           switch (render_type) {
32016           case 0 :
32017             draw_point(x0,y0,pcolor,opac).draw_point(x1,y1,pcolor,opac).
32018               draw_point(x2,y2,pcolor,opac).draw_point(x3,y3,pcolor,opac);
32019 #ifdef cimg_use_board
32020             if (pboard) {
32021               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
32022               board.drawCircle((float)x0,height()-(float)y0,0);
32023               board.drawCircle((float)x1,height()-(float)y1,0);
32024               board.drawCircle((float)x2,height()-(float)y2,0);
32025               board.drawCircle((float)x3,height()-(float)y3,0);
32026             }
32027 #endif
32028             break;
32029           case 1 :
32030             if (zbuffer)
32031               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opac).draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opac).
32032                 draw_line(zbuffer,x2,y2,z2,x3,y3,z3,pcolor,opac).draw_line(zbuffer,x3,y3,z3,x0,y0,z0,pcolor,opac);
32033             else
32034               draw_line(x0,y0,x1,y1,pcolor,opac).draw_line(x1,y1,x2,y2,pcolor,opac).
32035                 draw_line(x2,y2,x3,y3,pcolor,opac).draw_line(x3,y3,x0,y0,pcolor,opac);
32036 #ifdef cimg_use_board
32037             if (pboard) {
32038               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
32039               board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1);
32040               board.drawLine((float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
32041               board.drawLine((float)x2,height()-(float)y2,(float)x3,height()-(float)y3);
32042               board.drawLine((float)x3,height()-(float)y3,(float)x0,height()-(float)y0);
32043             }
32044 #endif
32045             break;
32046           case 2 :
32047             if (zbuffer)
32048               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);
32049             else
32050               draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opac).draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opac);
32051 #ifdef cimg_use_board
32052             if (pboard) {
32053               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
32054               board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
32055               board.fillTriangle((float)x0,height()-(float)y0,(float)x2,height()-(float)y2,(float)x3,height()-(float)y3);
32056             }
32057 #endif
32058             break;
32059           case 3 :
32060             if (zbuffer)
32061               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opac,lightprops(l)).
32062                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opac,lightprops(l));
32063             else
32064               _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opac,lightprops(l)).
32065                 _draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opac,lightprops(l));
32066 #ifdef cimg_use_board
32067             if (pboard) {
32068               const float lp = cimg::min(lightprops(l),1);
32069               board.setPenColorRGBi((unsigned char)(color[0]*lp),
32070                                      (unsigned char)(color[1]*lp),
32071                                      (unsigned char)(color[2]*lp),(unsigned char)(opac*255));
32072               board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
32073               board.fillTriangle((float)x0,height()-(float)y0,(float)x2,height()-(float)y2,(float)x3,height()-(float)y3);
32074             }
32075 #endif
32076             break;
32077           case 4 : {
32078             const float
32079               lightprop0 = lightprops(n0), lightprop1 = lightprops(n1),
32080               lightprop2 = lightprops(n2), lightprop3 = lightprops(n3);
32081             if (zbuffer)
32082               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,lightprop0,lightprop1,lightprop2,opac).
32083                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,lightprop0,lightprop2,lightprop3,opac);
32084             else
32085               draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprop0,lightprop1,lightprop2,opac).
32086                 draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,lightprop0,lightprop2,lightprop3,opac);
32087 #ifdef cimg_use_board
32088             if (pboard) {
32089               board.setPenColorRGBi((unsigned char)(color[0]),
32090                                      (unsigned char)(color[1]),
32091                                      (unsigned char)(color[2]),
32092                                      (unsigned char)(opac*255));
32093               board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprop0,
32094                                          (float)x1,height()-(float)y1,lightprop1,
32095                                          (float)x2,height()-(float)y2,lightprop2);
32096               board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprop0,
32097                                          (float)x2,height()-(float)y2,lightprop2,
32098                                          (float)x3,height()-(float)y3,lightprop3);
32099             }
32100 #endif
32101           } break;
32102           case 5 : {
32103             const unsigned int
32104               lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1),
32105               lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1),
32106               lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1),
32107               lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1);
32108             if (zbuffer)
32109               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac).
32110                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac);
32111             else
32112               draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac).
32113                 draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac);
32114 #ifdef cimg_use_board
32115             if (pboard) {
32116               const float
32117                 l0 = light_texture((int)(light_texture.width()/2*(1+lx0)), (int)(light_texture.height()/2*(1+ly0))),
32118                 l1 = light_texture((int)(light_texture.width()/2*(1+lx1)), (int)(light_texture.height()/2*(1+ly1))),
32119                 l2 = light_texture((int)(light_texture.width()/2*(1+lx2)), (int)(light_texture.height()/2*(1+ly2))),
32120                 l3 = light_texture((int)(light_texture.width()/2*(1+lx3)), (int)(light_texture.height()/2*(1+ly3)));
32121               board.setPenColorRGBi((unsigned char)(color[0]),
32122                                      (unsigned char)(color[1]),
32123                                      (unsigned char)(color[2]),
32124                                      (unsigned char)(opac*255));
32125               board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,
32126                                          (float)x1,height()-(float)y1,l1,
32127                                          (float)x2,height()-(float)y2,l2);
32128               board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,
32129                                          (float)x2,height()-(float)y2,l2,
32130                                          (float)x3,height()-(float)y3,l3);
32131             }
32132 #endif
32133           } break;
32134           }
32135         } break;
32136         case 9 : { // Textured triangle
32137           if (!__color)
32138             throw CImgArgumentException(_cimg_instance
32139                                         "draw_object3d() : Undefined texture for triangle primitive [%u].",
32140                                         cimg_instance,n_primitive);
32141           const unsigned int
32142             n0 = (unsigned int)primitive[0],
32143             n1 = (unsigned int)primitive[1],
32144             n2 = (unsigned int)primitive[2],
32145             tx0 = (unsigned int)primitive[3],
32146             ty0 = (unsigned int)primitive[4],
32147             tx1 = (unsigned int)primitive[5],
32148             ty1 = (unsigned int)primitive[6],
32149             tx2 = (unsigned int)primitive[7],
32150             ty2 = (unsigned int)primitive[8];
32151           const int
32152             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
32153             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
32154             x2 = (int)projections(n2,0), y2 = (int)projections(n2,1);
32155           const float
32156             z0 = vertices(n0,2) + Z + _focale,
32157             z1 = vertices(n1,2) + Z + _focale,
32158             z2 = vertices(n2,2) + Z + _focale;
32159           switch (render_type) {
32160           case 0 :
32161             draw_point(x0,y0,color.get_vector_at(tx0,ty0)._data,opac).
32162               draw_point(x1,y1,color.get_vector_at(tx1,ty1)._data,opac).
32163               draw_point(x2,y2,color.get_vector_at(tx2,ty2)._data,opac);
32164 #ifdef cimg_use_board
32165             if (pboard) {
32166               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
32167               board.drawCircle((float)x0,height()-(float)y0,0);
32168               board.drawCircle((float)x1,height()-(float)y1,0);
32169               board.drawCircle((float)x2,height()-(float)y2,0);
32170             }
32171 #endif
32172             break;
32173           case 1 :
32174             if (zbuffer)
32175               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac).
32176                 draw_line(zbuffer,x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opac).
32177                 draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac);
32178             else
32179               draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac).
32180                 draw_line(x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opac).
32181                 draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac);
32182 #ifdef cimg_use_board
32183             if (pboard) {
32184               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
32185               board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1);
32186               board.drawLine((float)x0,height()-(float)y0,(float)x2,height()-(float)y2);
32187               board.drawLine((float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
32188             }
32189 #endif
32190             break;
32191           case 2 :
32192             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac);
32193             else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac);
32194 #ifdef cimg_use_board
32195             if (pboard) {
32196               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
32197               board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
32198             }
32199 #endif
32200             break;
32201           case 3 :
32202             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l));
32203             else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l));
32204 #ifdef cimg_use_board
32205             if (pboard) {
32206               const float lp = cimg::min(lightprops(l),1);
32207               board.setPenColorRGBi((unsigned char)(128*lp),
32208                                     (unsigned char)(128*lp),
32209                                     (unsigned char)(128*lp),
32210                                     (unsigned char)(opac*255));
32211               board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
32212             }
32213 #endif
32214             break;
32215           case 4 :
32216             if (zbuffer)
32217               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);
32218             else
32219               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);
32220 #ifdef cimg_use_board
32221             if (pboard) {
32222               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
32223               board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprops(n0),
32224                                         (float)x1,height()-(float)y1,lightprops(n1),
32225                                         (float)x2,height()-(float)y2,lightprops(n2));
32226             }
32227 #endif
32228             break;
32229           case 5 :
32230             if (zbuffer)
32231               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,
32232                             (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1),
32233                             (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1),
32234                             (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1),
32235                             opac);
32236             else
32237               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,
32238                             (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1),
32239                             (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1),
32240                             (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1),
32241                             opac);
32242 #ifdef cimg_use_board
32243             if (pboard) {
32244               const float
32245                 l0 = light_texture((int)(light_texture.width()/2*(1+lightprops(n0,0))), (int)(light_texture.height()/2*(1+lightprops(n0,1)))),
32246                 l1 = light_texture((int)(light_texture.width()/2*(1+lightprops(n1,0))), (int)(light_texture.height()/2*(1+lightprops(n1,1)))),
32247                 l2 = light_texture((int)(light_texture.width()/2*(1+lightprops(n2,0))), (int)(light_texture.height()/2*(1+lightprops(n2,1))));
32248               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
32249               board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,(float)x1,height()-(float)y1,l1,(float)x2,height()-(float)y2,l2);
32250             }
32251 #endif
32252             break;
32253           }
32254         } break;
32255         case 12 : { // Textured quadrangle
32256           if (!__color)
32257             throw CImgArgumentException(_cimg_instance
32258                                         "draw_object3d() : Undefined texture for quadrangle primitive [%u].",
32259                                         cimg_instance,n_primitive);
32260           const unsigned int
32261             n0 = (unsigned int)primitive[0],
32262             n1 = (unsigned int)primitive[1],
32263             n2 = (unsigned int)primitive[2],
32264             n3 = (unsigned int)primitive[3],
32265             tx0 = (unsigned int)primitive[4],
32266             ty0 = (unsigned int)primitive[5],
32267             tx1 = (unsigned int)primitive[6],
32268             ty1 = (unsigned int)primitive[7],
32269             tx2 = (unsigned int)primitive[8],
32270             ty2 = (unsigned int)primitive[9],
32271             tx3 = (unsigned int)primitive[10],
32272             ty3 = (unsigned int)primitive[11];
32273           const int
32274             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
32275             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
32276             x2 = (int)projections(n2,0), y2 = (int)projections(n2,1),
32277             x3 = (int)projections(n3,0), y3 = (int)projections(n3,1);
32278           const float
32279             z0 = vertices(n0,2) + Z + _focale,
32280             z1 = vertices(n1,2) + Z + _focale,
32281             z2 = vertices(n2,2) + Z + _focale,
32282             z3 = vertices(n3,2) + Z + _focale;
32283 
32284           switch (render_type) {
32285           case 0 :
32286             draw_point(x0,y0,color.get_vector_at(tx0,ty0)._data,opac).
32287               draw_point(x1,y1,color.get_vector_at(tx1,ty1)._data,opac).
32288               draw_point(x2,y2,color.get_vector_at(tx2,ty2)._data,opac).
32289               draw_point(x3,y3,color.get_vector_at(tx3,ty3)._data,opac);
32290 #ifdef cimg_use_board
32291             if (pboard) {
32292               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
32293               board.drawCircle((float)x0,height()-(float)y0,0);
32294               board.drawCircle((float)x1,height()-(float)y1,0);
32295               board.drawCircle((float)x2,height()-(float)y2,0);
32296               board.drawCircle((float)x3,height()-(float)y3,0);
32297             }
32298 #endif
32299             break;
32300           case 1 :
32301             if (zbuffer)
32302               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac).
32303                 draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac).
32304                 draw_line(zbuffer,x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opac).
32305                 draw_line(zbuffer,x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opac);
32306             else
32307               draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac).
32308                 draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac).
32309                 draw_line(x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opac).
32310                 draw_line(x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opac);
32311 #ifdef cimg_use_board
32312             if (pboard) {
32313               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
32314               board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1);
32315               board.drawLine((float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
32316               board.drawLine((float)x2,height()-(float)y2,(float)x3,height()-(float)y3);
32317               board.drawLine((float)x3,height()-(float)y3,(float)x0,height()-(float)y0);
32318             }
32319 #endif
32320             break;
32321           case 2 :
32322             if (zbuffer)
32323               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac).
32324                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac);
32325             else
32326               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac).
32327                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac);
32328 #ifdef cimg_use_board
32329             if (pboard) {
32330               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
32331               board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
32332               board.fillTriangle((float)x0,height()-(float)y0,(float)x2,height()-(float)y2,(float)x3,height()-(float)y3);
32333             }
32334 #endif
32335             break;
32336           case 3 :
32337             if (zbuffer)
32338               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l)).
32339                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac,lightprops(l));
32340             else
32341               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l)).
32342                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac,lightprops(l));
32343 #ifdef cimg_use_board
32344             if (pboard) {
32345               const float lp = cimg::min(lightprops(l),1);
32346               board.setPenColorRGBi((unsigned char)(128*lp),
32347                                      (unsigned char)(128*lp),
32348                                      (unsigned char)(128*lp),
32349                                      (unsigned char)(opac*255));
32350               board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
32351               board.fillTriangle((float)x0,height()-(float)y0,(float)x2,height()-(float)y2,(float)x3,height()-(float)y3);
32352             }
32353 #endif
32354             break;
32355           case 4 : {
32356             const float
32357               lightprop0 = lightprops(n0), lightprop1 = lightprops(n1),
32358               lightprop2 = lightprops(n2), lightprop3 = lightprops(n3);
32359             if (zbuffer)
32360               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprop0,lightprop1,lightprop2,opac).
32361                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,lightprop0,lightprop2,lightprop3,opac);
32362             else
32363               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprop0,lightprop1,lightprop2,opac).
32364                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,lightprop0,lightprop2,lightprop3,opac);
32365 #ifdef cimg_use_board
32366             if (pboard) {
32367               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
32368               board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprop0,
32369                                          (float)x1,height()-(float)y1,lightprop1,
32370                                          (float)x2,height()-(float)y2,lightprop2);
32371               board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprop0,
32372                                          (float)x2,height()-(float)y2,lightprop2,
32373                                          (float)x3,height()-(float)y3,lightprop3);
32374             }
32375 #endif
32376           } break;
32377           case 5 : {
32378             const unsigned int
32379               lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1),
32380               lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1),
32381               lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1),
32382               lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1);
32383             if (zbuffer)
32384               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).
32385                 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);
32386             else
32387               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).
32388                 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);
32389 #ifdef cimg_use_board
32390             if (pboard) {
32391               const float
32392                 l0 = light_texture((int)(light_texture.width()/2*(1+lx0)), (int)(light_texture.height()/2*(1+ly0))),
32393                 l1 = light_texture((int)(light_texture.width()/2*(1+lx1)), (int)(light_texture.height()/2*(1+ly1))),
32394                 l2 = light_texture((int)(light_texture.width()/2*(1+lx2)), (int)(light_texture.height()/2*(1+ly2))),
32395                 l3 = light_texture((int)(light_texture.width()/2*(1+lx3)), (int)(light_texture.height()/2*(1+ly3)));
32396               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
32397               board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,
32398                                          (float)x1,height()-(float)y1,l1,
32399                                          (float)x2,height()-(float)y2,l2);
32400               board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,
32401                                          (float)x2,height()-(float)y2,l2,
32402                                          (float)x3,height()-(float)y3,l3);
32403             }
32404 #endif
32405           } break;
32406           }
32407         } break;
32408         }
32409       }
32410       return *this;
32411     }
32412 
32413     //@}
32414     //---------------------------
32415     //
32416     //! \name Data Input
32417     //@{
32418     //---------------------------
32419 
32420     //! Simple interface to select a shape from an image.
32421     /**
32422        \param selection  Array of 6 values containing the selection result
32423        \param feature_type Determine feature to select (0=point, 1=vector, 2=rectangle, 3=circle)
32424        \param disp       Display window used to make the selection
32425        \param XYZ        Initial XYZ position (for volumetric images only)
32426        \param color      Color of the shape selector.
32427     **/
32428     CImg<T>& select(CImgDisplay &disp,
32429                     const unsigned int feature_type=2, unsigned int *const XYZ=0) {
32430       return get_select(disp,feature_type,XYZ).move_to(*this);
32431     }
32432 
32433     //! Simple interface to select a shape from an image.
32434     CImg<T>& select(const char *const title,
32435                     const unsigned int feature_type=2, unsigned int *const XYZ=0) {
32436       return get_select(title,feature_type,XYZ).move_to(*this);
32437     }
32438 
32439     //! Simple interface to select a shape from an image.
32440     CImg<intT> get_select(CImgDisplay &disp,
32441                           const unsigned int feature_type=2, unsigned int *const XYZ=0) const {
32442       return _get_select(disp,0,feature_type,XYZ,0,0,0,true);
32443     }
32444 
32445     //! Simple interface to select a shape from an image.
32446     CImg<intT> get_select(const char *const title,
32447                           const unsigned int feature_type=2, unsigned int *const XYZ=0) const {
32448       CImgDisplay disp;
32449       return _get_select(disp,title,feature_type,XYZ,0,0,0,true);
32450     }
32451 
32452     CImg<intT> _get_select(CImgDisplay &disp, const char *const title,
32453                            const unsigned int feature_type, unsigned int *const XYZ,
32454                            const int origX, const int origY, const int origZ,
32455                            const bool reset_view3d=true) const {
32456       if (is_empty())
32457         throw CImgInstanceException(_cimg_instance
32458                                     "select() : Empty instance.",
32459                                     cimg_instance);
32460 
32461       if (!disp) {
32462         disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1);
32463         if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum);
32464       } else if (title) disp.set_title("%s",title);
32465 
32466       const unsigned int old_normalization = disp.normalization();
32467       bool old_is_resized = disp.is_resized();
32468       disp._normalization = 0;
32469       disp.show().set_key(0).set_wheel();
32470 
32471       unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 };
32472 
32473       int area = 0, starting_area = 0, clicked_area = 0, phase = 0,
32474         X0 = (int)((XYZ?XYZ[0]:_width/2)%_width), Y0 = (int)((XYZ?XYZ[1]:_height/2)%_height), Z0 = (int)((XYZ?XYZ[2]:_depth/2)%_depth),
32475         X1 =-1, Y1 = -1, Z1 = -1,
32476         X = -1, Y = -1, Z = -1, X3d = -1, Y3d = -1,
32477         oX = X, oY = Y, oZ = Z, oX3d = X3d, oY3d = -1;
32478       unsigned int old_button = 0, key = 0;
32479 
32480       bool shape_selected = false, text_down = false;
32481       static CImg<floatT> pose3d;
32482       static bool is_view3d = false;
32483       if (reset_view3d) { pose3d.assign(); is_view3d = false; }
32484       CImg<floatT> points3d, opacities3d, sel_opacities3d;
32485       CImgList<uintT> primitives3d, sel_primitives3d;
32486       CImgList<ucharT> colors3d, sel_colors3d;
32487       CImg<ucharT> visu, visu0, view3d;
32488       char text[1024] = { 0 };
32489 
32490       while (!key && !disp.is_closed() && !shape_selected) {
32491 
32492         // Handle mouse motion and selection
32493         oX = X; oY = Y; oZ = Z;
32494         int
32495           mx = disp.mouse_x(),
32496           my = disp.mouse_y();
32497         const int
32498           mX = mx<0?-1:mx*(width()+(depth()>1?depth():0))/disp.width(),
32499           mY = my<0?-1:my*(height()+(depth()>1?depth():0))/disp.height();
32500         area = 0;
32501         if (mX>=0 && mY>=0 && mX<width() && mY<height())  { area = 1; X = mX; Y = mY; Z = phase?Z1:Z0; }
32502         if (mX>=0 && mX<width() && mY>=height()) { area = 2; X = mX; Z = mY - _height; Y = phase?Y1:Y0; }
32503         if (mY>=0 && mX>=width() && mY<height()) { area = 3; Y = mY; Z = mX - _width; X = phase?X1:X0; }
32504         if (mX>=width() && mY>=height()) area = 4;
32505         if (disp.button()) { if (!clicked_area) clicked_area = area; } else clicked_area = 0;
32506 
32507         switch (key = disp.key()) {
32508 #if cimg_OS!=2
32509         case cimg::keyCTRLRIGHT :
32510 #endif
32511         case 0 : case cimg::keyCTRLLEFT : key = 0; break;
32512         case cimg::keyPAGEUP : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(1); key = 0; } break;
32513         case cimg::keyPAGEDOWN : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(-1); key = 0; } break;
32514         case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
32515           disp.set_fullscreen(false).resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
32516                                             CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
32517             _is_resized = true;
32518           disp.set_key(key,false); key = 0; visu0.assign();
32519         } break;
32520         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
32521           disp.set_fullscreen(false).resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
32522           disp.set_key(key,false); key = 0; visu0.assign();
32523         } break;
32524         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
32525           disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true;
32526           disp.set_key(key,false); key = 0; visu0.assign();
32527         } break;
32528         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
32529           disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
32530           disp.set_key(key,false); key = 0; visu0.assign();
32531         } break;
32532         case cimg::keyV : is_view3d = !is_view3d; disp.set_key(key,false); key = 0; visu0.assign(); break;
32533         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
32534           static unsigned int snap_number = 0;
32535           char filename[32] = { 0 };
32536           std::FILE *file;
32537           do {
32538             cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++);
32539             if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
32540           } while (file);
32541           if (visu0) {
32542             visu.draw_text(0,0," Saving snapshot... ",foreground_color,background_color,1,13).display(disp);
32543             visu0.save(filename);
32544             visu.draw_text(0,0," Snapshot '%s' saved. ",foreground_color,background_color,1,13,filename).display(disp);
32545           }
32546           disp.set_key(key,false); key = 0;
32547         } break;
32548         case cimg::keyO :
32549           if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
32550             static unsigned int snap_number = 0;
32551             char filename[32] = { 0 };
32552             std::FILE *file;
32553             do {
32554 #ifdef cimg_use_zlib
32555               cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++);
32556 #else
32557               cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++);
32558 #endif
32559               if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
32560             } while (file);
32561             visu.draw_text(0,0," Saving instance... ",foreground_color,background_color,1,13).display(disp);
32562             save(filename);
32563             visu.draw_text(0,0," Instance '%s' saved. ",foreground_color,background_color,1,13,filename).display(disp);
32564             disp.set_key(key,false); key = 0;
32565           } break;
32566         }
32567 
32568         switch (area) {
32569 
32570         case 0 : // When mouse is out of image range.
32571           mx = my = X = Y = Z = -1;
32572           break;
32573 
32574         case 1 : case 2 : case 3 : // When mouse is over the XY,XZ or YZ projections.
32575           if (disp.button()&1 && phase<2 && clicked_area==area) { // When selection has been started (1st step).
32576             if (_depth>1 && (X1!=X || Y1!=Y || Z1!=Z)) visu0.assign();
32577             X1 = X; Y1 = Y; Z1 = Z;
32578           }
32579           if (!(disp.button()&1) && phase>=2 && clicked_area!=area) { // When selection is at 2nd step (for volumes).
32580             switch (starting_area) {
32581             case 1 : if (Z1!=Z) visu0.assign(); Z1 = Z; break;
32582             case 2 : if (Y1!=Y) visu0.assign(); Y1 = Y; break;
32583             case 3 : if (X1!=X) visu0.assign(); X1 = X; break;
32584             }
32585           }
32586           if (disp.button()&2 && clicked_area==area) { // When moving through the image/volume.
32587             if (phase) {
32588               if (_depth>1 && (X1!=X || Y1!=Y || Z1!=Z)) visu0.assign();
32589               X1 = X; Y1 = Y; Z1 = Z;
32590             } else {
32591               if (_depth>1 && (X0!=X || Y0!=Y || Z0!=Z)) visu0.assign();
32592               X0 = X; Y0 = Y; Z0 = Z;
32593             }
32594           }
32595           if (disp.button()&4) { // Reset positions.
32596             oX = X = X0; oY = Y = Y0; oZ = Z = Z0; phase = area = clicked_area = starting_area = 0; visu0.assign();
32597           }
32598           if (disp.wheel()) { // When moving through the slices of the volume (with mouse wheel).
32599             if (_depth>1 && !disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT() && !disp.is_keySHIFTLEFT() && !disp.is_keySHIFTRIGHT() &&
32600                 !disp.is_keyALT() && !disp.is_keyALTGR()) {
32601               switch (area) {
32602               case 1 :
32603                 if (phase) Z = (Z1+=disp.wheel()); else Z = (Z0+=disp.wheel());
32604                 visu0.assign(); break;
32605               case 2 :
32606                 if (phase) Y = (Y1+=disp.wheel()); else Y = (Y0+=disp.wheel());
32607                 visu0.assign(); break;
32608               case 3 :
32609                 if (phase) X = (X1+=disp.wheel()); else X = (X0+=disp.wheel());
32610                 visu0.assign(); break;
32611               }
32612               disp.set_wheel();
32613             } else key = ~0U;
32614           }
32615           if ((disp.button()&1)!=old_button) { // When left button has just been pressed or released.
32616             switch (phase) {
32617             case 0 :
32618               if (area==clicked_area) {
32619                 X0 = X1 = X; Y0 = Y1 = Y; Z0 = Z1 = Z; starting_area = area; ++phase;
32620               } break;
32621             case 1 :
32622               if (area==starting_area) {
32623                 X1 = X; Y1 = Y; Z1 = Z; ++phase;
32624               } else if (!(disp.button()&1)) { oX = X = X0; oY = Y = Y0; oZ = Z = Z0; phase = 0; visu0.assign(); }
32625               break;
32626             case 2 : ++phase; break;
32627             }
32628             old_button = disp.button()&1;
32629           }
32630           break;
32631 
32632         case 4 : // When mouse is over the 3d view.
32633           if (is_view3d && points3d) {
32634             X3d = mx - _width*disp.width()/(_width+(_depth>1?_depth:0));
32635             Y3d = my - _height*disp.height()/(_height+(_depth>1?_depth:0));
32636             if (oX3d<0) { oX3d = X3d; oY3d = Y3d; }
32637             if ((disp.button()&3)==3) { pose3d.assign(); view3d.assign(); oX3d = oY3d = X3d = Y3d = -1; } // Left + right buttons : reset.
32638             else if (disp.button()&1 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Left button : rotate.
32639               const float
32640                 R = 0.45f*cimg::min(view3d._width,view3d._height),
32641                 R2 = R*R,
32642                 u0 = (float)(oX3d-view3d.width()/2),
32643                 v0 = (float)(oY3d-view3d.height()/2),
32644                 u1 = (float)(X3d-view3d.width()/2),
32645                 v1 = (float)(Y3d-view3d.height()/2),
32646                 n0 = (float)std::sqrt(u0*u0+v0*v0),
32647                 n1 = (float)std::sqrt(u1*u1+v1*v1),
32648                 nu0 = n0>R?(u0*R/n0):u0,
32649                 nv0 = n0>R?(v0*R/n0):v0,
32650                 nw0 = (float)std::sqrt(cimg::max(0,R2-nu0*nu0-nv0*nv0)),
32651                 nu1 = n1>R?(u1*R/n1):u1,
32652                 nv1 = n1>R?(v1*R/n1):v1,
32653                 nw1 = (float)std::sqrt(cimg::max(0,R2-nu1*nu1-nv1*nv1)),
32654                 u = nv0*nw1 - nw0*nv1,
32655                 v = nw0*nu1 - nu0*nw1,
32656                 w = nv0*nu1 - nu0*nv1,
32657                 n = (float)std::sqrt(u*u+v*v+w*w),
32658                 alpha = (float)std::asin(n/R2);
32659               pose3d.draw_image(CImg<floatT>::rotation_matrix(u,v,w,alpha)*pose3d.get_crop(0,0,2,2));
32660               view3d.assign();
32661             } else if (disp.button()&2 && pose3d && oY3d!=Y3d) {  // Right button : zoom.
32662               pose3d(3,2)-=(oY3d - Y3d)*1.5f; view3d.assign();
32663             }
32664             if (disp.wheel()) { // Wheel : zoom
32665               pose3d(3,2)-=disp.wheel()*15; view3d.assign(); disp.set_wheel();
32666             }
32667             if (disp.button()&4 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Middle button : shift.
32668               pose3d(3,0)-=oX3d - X3d; pose3d(3,1)-=oY3d - Y3d; view3d.assign();
32669             }
32670             oX3d = X3d; oY3d = Y3d;
32671           }
32672           mx = my = X = Y = Z = -1;
32673           break;
32674         }
32675 
32676         if (phase) {
32677           if (!feature_type) shape_selected = phase?true:false;
32678           else {
32679             if (_depth>1) shape_selected = (phase==3)?true:false;
32680             else shape_selected = (phase==2)?true:false;
32681           }
32682         }
32683 
32684         if (X0<0) X0 = 0; if (X0>=width()) X0 = width() - 1;
32685         if (Y0<0) Y0 = 0; if (Y0>=height()) Y0 = height() - 1;
32686         if (Z0<0) Z0 = 0; if (Z0>=depth()) Z0 = depth() - 1;
32687         if (X1<1) X1 = 0; if (X1>=width()) X1 = width() - 1;
32688         if (Y1<0) Y1 = 0; if (Y1>=height()) Y1 = height() - 1;
32689         if (Z1<0) Z1 = 0; if (Z1>=depth()) Z1 = depth() - 1;
32690 
32691         // Draw visualization image on the display
32692         if (oX!=X || oY!=Y || oZ!=Z || !visu0 || (_depth>1 && !view3d)) {
32693 
32694           if (!visu0) { // Create image of projected planes.
32695             CImg<Tuchar> tmp, tmp0;
32696             if (_depth!=1) {
32697               tmp0 = get_projections2d(phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0);
32698               tmp = tmp0.get_channels(0,cimg::min(2U,_spectrum - 1));
32699             } else tmp = get_channels(0,cimg::min(2U,_spectrum - 1));
32700             switch (old_normalization) {
32701             case 0 : tmp.move_to(visu0); break;
32702             case 1 : tmp.normalize(0,255).move_to(visu0); break;
32703             case 2 : {
32704               const float m = disp._min, M = disp._max;
32705               ((tmp-=m)*=255.0f/(M-m>0?M-m:1)).move_to(visu0);
32706             }
32707             case 3 :
32708               if (cimg::type<T>::is_float()) (tmp.normalize(0,255)).move_to(visu0);
32709               else {
32710                 const float m = (float)cimg::type<T>::min(), M = (float)cimg::type<T>::max();
32711                 ((tmp-=m)*=255.0f/(M-m)).move_to(visu0);
32712               } break;
32713             }
32714             visu0.resize(disp);
32715             view3d.assign();
32716             points3d.assign();
32717           }
32718 
32719           if (is_view3d && _depth>1 && !view3d) { // Create 3d view for volumetric images.
32720             const unsigned int
32721               _x3d = (unsigned int)cimg::round((float)_width*visu0._width/(_width+_depth),1,1),
32722               _y3d = (unsigned int)cimg::round((float)_height*visu0._height/(_height+_depth),1,1),
32723               x3d = _x3d>=visu0._width?visu0._width-1:_x3d,
32724               y3d = _y3d>=visu0._height?visu0._height-1:_y3d;
32725             CImg<ucharT>(1,2,1,1,64,128).resize(visu0._width-x3d,visu0._height-y3d,1,visu0._spectrum,3).move_to(view3d);
32726             if (!points3d) {
32727               get_projections3d(primitives3d,colors3d,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0,true).move_to(points3d);
32728               points3d.append(CImg<floatT>(8,3,1,1,
32729                                            0,_width-1,_width-1,0,0,_width-1,_width-1,0,
32730                                            0,0,_height-1,_height-1,0,0,_height-1,_height-1,
32731                                            0,0,0,0,_depth-1,_depth-1,_depth-1,_depth-1),'x');
32732               CImg<uintT>::vector(12,13).move_to(primitives3d); CImg<uintT>::vector(13,14).move_to(primitives3d);
32733               CImg<uintT>::vector(14,15).move_to(primitives3d); CImg<uintT>::vector(15,12).move_to(primitives3d);
32734               CImg<uintT>::vector(16,17).move_to(primitives3d); CImg<uintT>::vector(17,18).move_to(primitives3d);
32735               CImg<uintT>::vector(18,19).move_to(primitives3d); CImg<uintT>::vector(19,16).move_to(primitives3d);
32736               CImg<uintT>::vector(12,16).move_to(primitives3d); CImg<uintT>::vector(13,17).move_to(primitives3d);
32737               CImg<uintT>::vector(14,18).move_to(primitives3d); CImg<uintT>::vector(15,19).move_to(primitives3d);
32738               colors3d.insert(12,CImg<ucharT>::vector(255,255,255));
32739               opacities3d.assign(primitives3d.width(),1,1,1,0.5f);
32740               if (!phase) {
32741                 opacities3d[0] = opacities3d[1] = opacities3d[2] = 0.8f;
32742                 sel_primitives3d.assign();
32743                 sel_colors3d.assign();
32744                 sel_opacities3d.assign();
32745               } else {
32746                 if (feature_type==2) {
32747                   points3d.append(CImg<floatT>(8,3,1,1,
32748                                                X0,X1,X1,X0,X0,X1,X1,X0,
32749                                                Y0,Y0,Y1,Y1,Y0,Y0,Y1,Y1,
32750                                                Z0,Z0,Z0,Z0,Z1,Z1,Z1,Z1),'x');
32751                   sel_primitives3d.assign();
32752                   CImg<uintT>::vector(20,21).move_to(sel_primitives3d); CImg<uintT>::vector(21,22).move_to(sel_primitives3d);
32753                   CImg<uintT>::vector(22,23).move_to(sel_primitives3d); CImg<uintT>::vector(23,20).move_to(sel_primitives3d);
32754                   CImg<uintT>::vector(24,25).move_to(sel_primitives3d); CImg<uintT>::vector(25,26).move_to(sel_primitives3d);
32755                   CImg<uintT>::vector(26,27).move_to(sel_primitives3d); CImg<uintT>::vector(27,24).move_to(sel_primitives3d);
32756                   CImg<uintT>::vector(20,24).move_to(sel_primitives3d); CImg<uintT>::vector(21,25).move_to(sel_primitives3d);
32757                   CImg<uintT>::vector(22,26).move_to(sel_primitives3d); CImg<uintT>::vector(23,27).move_to(sel_primitives3d);
32758                 } else {
32759                   points3d.append(CImg<floatT>(2,3,1,1,
32760                                                X0,X1,
32761                                                Y0,Y1,
32762                                                Z0,Z1),'x');
32763                   sel_primitives3d.assign(CImg<uintT>::vector(20,21));
32764                 }
32765                 sel_colors3d.assign(sel_primitives3d._width,CImg<ucharT>::vector(255,255,255));
32766                 sel_opacities3d.assign(sel_primitives3d._width,1,1,1,0.8f);
32767               }
32768               points3d.shift_object3d(-0.5f*_width,-0.5f*_height,-0.5f*_depth).resize_object3d();
32769               points3d*=0.75f*cimg::min(view3d._width,view3d._height);
32770             }
32771 
32772             if (!pose3d) CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose3d);
32773             CImg<floatT> zbuffer3d(view3d._width,view3d._height,1,1,0);
32774             const CImg<floatT> rotated_points3d = pose3d.get_crop(0,0,2,2)*points3d;
32775             if (sel_primitives3d)
32776               view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width,
32777                                    pose3d(3,1) + 0.5f*view3d._height,
32778                                    pose3d(3,2),
32779                                    rotated_points3d,sel_primitives3d,sel_colors3d,sel_opacities3d,
32780                                    2,true,500,0,0,0,0,0,zbuffer3d);
32781             view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width,
32782                                  pose3d(3,1) + 0.5f*view3d._height,
32783                                  pose3d(3,2),
32784                                  rotated_points3d,primitives3d,colors3d,opacities3d,
32785                                  2,true,500,0,0,0,0,0,zbuffer3d);
32786             visu0.draw_image(x3d,y3d,view3d);
32787           }
32788           visu = visu0;
32789 
32790           const int d = (_depth>1)?_depth:0;
32791           if (phase) switch (feature_type) {
32792           case 1 : {
32793             const int
32794               x0 = (int)((X0+0.5f)*disp.width()/(_width+d)),
32795               y0 = (int)((Y0+0.5f)*disp.height()/(_height+d)),
32796               x1 = (int)((X1+0.5f)*disp.width()/(_width+d)),
32797               y1 = (int)((Y1+0.5f)*disp.height()/(_height+d));
32798             visu.draw_arrow(x0,y0,x1,y1,background_color,0.9f,30,5,0x55555555).
32799               draw_arrow(x0,y0,x1,y1,foreground_color,0.9f,30,5,0xAAAAAAAA);
32800             if (d) {
32801               const int
32802                 zx0 = (int)((_width+Z0+0.5f)*disp.width()/(_width+d)),
32803                 zx1 = (int)((_width+Z1+0.5f)*disp.width()/(_width+d)),
32804                 zy0 = (int)((_height+Z0+0.5f)*disp.height()/(_height+d)),
32805                 zy1 = (int)((_height+Z1+0.5f)*disp.height()/(_height+d));
32806               visu.draw_arrow(zx0,y0,zx1,y1,foreground_color,0.9f,30,5,0x55555555).
32807                 draw_arrow(x0,zy0,x1,zy1,foreground_color,0.9f,30,5,0x55555555).
32808                 draw_arrow(zx0,y0,zx1,y1,foreground_color,0.9f,30,5,0xAAAAAAAA).
32809                 draw_arrow(x0,zy0,x1,zy1,foreground_color,0.9f,30,5,0xAAAAAAAA);
32810             }
32811           } break;
32812           case 2 : {
32813             const int
32814               x0 = (X0<X1?X0:X1)*disp.width()/(_width+d),
32815               y0 = (Y0<Y1?Y0:Y1)*disp.height()/(_height+d),
32816               x1 = ((X0<X1?X1:X0)+1)*disp.width()/(_width+d)-1,
32817               y1 = ((Y0<Y1?Y1:Y0)+1)*disp.height()/(_height+d)-1;
32818             visu.draw_rectangle(x0,y0,x1,y1,background_color,0.2f).draw_rectangle(x0,y0,x1,y1,foreground_color,0.6f,0x55555555);
32819             if (d) {
32820               const int
32821                 zx0 = (int)((_width+(Z0<Z1?Z0:Z1))*disp.width()/(_width+d)),
32822                 zy0 = (int)((_height+(Z0<Z1?Z0:Z1))*disp.height()/(_height+d)),
32823                 zx1 = (int)((_width+(Z0<Z1?Z1:Z0)+1)*disp.width()/(_width+d))-1,
32824                 zy1 = (int)((_height+(Z0<Z1?Z1:Z0)+1)*disp.height()/(_height+d))-1;
32825               visu.draw_rectangle(zx0,y0,zx1,y1,background_color,0.2f).draw_rectangle(zx0,y0,zx1,y1,foreground_color,0.6f,0x55555555).
32826                 draw_rectangle(x0,zy0,x1,zy1,background_color,0.2f).draw_rectangle(x0,zy0,x1,zy1,foreground_color,0.6f,0x55555555);
32827             }
32828           } break;
32829           case 3 : {
32830             const int
32831               x0 = X0*disp.width()/(_width+d),
32832               y0 = Y0*disp.height()/(_height+d),
32833               x1 = X1*disp.width()/(_width+d)-1,
32834               y1 = Y1*disp.height()/(_height+d)-1;
32835             visu.draw_ellipse(x0,y0,(float)cimg::abs(x1-x0),(float)cimg::abs(y1-y0),0,background_color,0.2f).
32836               draw_ellipse(x0,y0,(float)cimg::abs(x1-x0),(float)cimg::abs(y1-y0),0,foreground_color,0.6f,0x55555555);
32837             if (d) {
32838               const int
32839                 zx0 = (int)((_width+Z0)*disp.width()/(_width+d)),
32840                 zy0 = (int)((_height+Z0)*disp.height()/(_height+d)),
32841                 zx1 = (int)((_width+Z1+1)*disp.width()/(_width+d))-1,
32842                 zy1 = (int)((_height+Z1+1)*disp.height()/(_height+d))-1;
32843               visu.draw_ellipse(zx0,y0,(float)cimg::abs(zx1-zx0),(float)cimg::abs(y1-y0),0,background_color,0.2f).
32844                 draw_ellipse(zx0,y0,(float)cimg::abs(zx1-zx0),(float)cimg::abs(y1-y0),0,foreground_color,0.6f,0x55555555).
32845                 draw_ellipse(x0,zy0,(float)cimg::abs(x1-x0),(float)cimg::abs(zy1-zy0),0,background_color,0.2f).
32846                 draw_ellipse(x0,zy0,(float)cimg::abs(x1-x0),(float)cimg::abs(zy1-zy0),0,foreground_color,0.6f,0x55555555);
32847             }
32848           } break;
32849           } else {
32850             const int
32851               x0 = X*disp.width()/(_width+d),
32852               y0 = Y*disp.height()/(_height+d),
32853               x1 = (X+1)*disp.width()/(_width+d)-1,
32854               y1 = (Y+1)*disp.height()/(_height+d)-1;
32855             if (x1-x0>=4 && y1-y0>=4) visu.draw_rectangle(x0,y0,x1,y1,background_color,0.2f).
32856                                         draw_rectangle(x0,y0,x1,y1,foreground_color,0.6f,~0U);
32857           }
32858 
32859           if (my>=0 && my<13) text_down = true; else if (my>=visu.height()-13) text_down = false;
32860           if (!feature_type || !phase) {
32861             if (X>=0 && Y>=0 && Z>=0 && X<width() && Y<height() && Z<depth()) {
32862               if (_depth>1) cimg_snprintf(text,sizeof(text)," Point (%d,%d,%d) = [ ",origX+X,origY+Y,origZ+Z);
32863               else cimg_snprintf(text,sizeof(text)," Point (%d,%d) = [ ",origX+X,origY+Y);
32864               char *ctext = text + std::strlen(text), *const ltext = text + 512;
32865               for (unsigned int c = 0; c<_spectrum && ctext<ltext; ++c) {
32866                 cimg_snprintf(ctext,sizeof(text)/2,cimg::type<T>::format(),cimg::type<T>::format((*this)(X,Y,Z,c)));
32867                 ctext = text + std::strlen(text);
32868                 *(ctext++) = ' '; *ctext = 0;
32869               }
32870               std::strcpy(text + std::strlen(text),"] ");
32871             }
32872           } else switch (feature_type) {
32873           case 1 : {
32874             const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1), norm = std::sqrt(dX*dX+dY*dY+dZ*dZ);
32875             if (_depth>1) cimg_snprintf(text,sizeof(text)," Vect (%d,%d,%d)-(%d,%d,%d), Norm = %g ",
32876                                         origX+X0,origY+Y0,origZ+Z0,origX+X1,origY+Y1,origZ+Z1,norm);
32877             else cimg_snprintf(text,sizeof(text)," Vect (%d,%d)-(%d,%d), Norm = %g ",
32878                                origX+X0,origY+Y0,origX+X1,origY+Y1,norm);
32879           } break;
32880           case 2 :
32881             if (_depth>1) cimg_snprintf(text,sizeof(text)," Box (%d,%d,%d)-(%d,%d,%d), Size = (%d,%d,%d) ",
32882                                         origX+(X0<X1?X0:X1),origY+(Y0<Y1?Y0:Y1),origZ+(Z0<Z1?Z0:Z1),
32883                                         origX+(X0<X1?X1:X0),origY+(Y0<Y1?Y1:Y0),origZ+(Z0<Z1?Z1:Z0),
32884                                         1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1),1+cimg::abs(Z0-Z1));
32885             else cimg_snprintf(text,sizeof(text)," Box (%d,%d)-(%d,%d), Size = (%d,%d) ",
32886                                origX+(X0<X1?X0:X1),origY+(Y0<Y1?Y0:Y1),origX+(X0<X1?X1:X0),origY+(Y0<Y1?Y1:Y0),
32887                                1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1));
32888             break;
32889           default :
32890             if (_depth>1) cimg_snprintf(text,sizeof(text)," Ellipse (%d,%d,%d)-(%d,%d,%d), Radii = (%d,%d,%d) ",
32891                                         origX+X0,origY+Y0,origZ+Z0,origX+X1,origY+Y1,origZ+Z1,
32892                                         1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1),1+cimg::abs(Z0-Z1));
32893             else cimg_snprintf(text,sizeof(text)," Ellipse (%d,%d)-(%d,%d), Radii = (%d,%d) ",
32894                                origX+X0,origY+Y0,origX+X1,origY+Y1,1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1));
32895             }
32896           if (phase || (mx>=0 && my>=0)) visu.draw_text(0,text_down?visu.height()-13:0,text,foreground_color,background_color,0.7f,13);
32897           disp.display(visu).wait();
32898         } else if (!shape_selected) disp.wait();
32899         if (disp.is_resized()) { disp.resize(false)._is_resized = false; old_is_resized = true; visu0.assign(); }
32900       }
32901 
32902       // Return result
32903       CImg<intT> res(1,feature_type==0?3:6,1,1,-1);
32904       if (XYZ) { XYZ[0] = (unsigned int)X0; XYZ[1] = (unsigned int)Y0; XYZ[2] = (unsigned int)Z0; }
32905       if (shape_selected) {
32906         if (feature_type==2) {
32907           if (X0>X1) cimg::swap(X0,X1);
32908           if (Y0>Y1) cimg::swap(Y0,Y1);
32909           if (Z0>Z1) cimg::swap(Z0,Z1);
32910         }
32911         if (X1<0 || Y1<0 || Z1<0) X0 = Y0 = Z0 = X1 = Y1 = Z1 = -1;
32912         switch (feature_type) {
32913         case 1 : case 2 : res[3] = X1; res[4] = Y1; res[5] = Z1;
32914         default : res[0] = X0; res[1] = Y0; res[2] = Z0;
32915         }
32916       }
32917       disp.set_button();
32918       disp._normalization = old_normalization;
32919       disp._is_resized = old_is_resized;
32920       if (key!=~0U) disp.set_key(key);
32921       return res;
32922     }
32923 
32924     //! Select sub-graph in a graph.
32925     CImg<intT> get_select_graph(CImgDisplay &disp,
32926                                 const unsigned int plot_type=1, const unsigned int vertex_type=1,
32927                                 const char *const labelx=0, const double xmin=0, const double xmax=0,
32928                                 const char *const labely=0, const double ymin=0, const double ymax=0) const {
32929       if (is_empty())
32930         throw CImgInstanceException(_cimg_instance
32931                                     "select_graph() : Empty instance.",
32932                                     cimg_instance);
32933       if (!disp) disp.assign(cimg_fitscreen(640,480,1),0,0).set_title("CImg<%s>",pixel_type());
32934       const unsigned int siz = _width*_height*_depth, old_normalization = disp.normalization();
32935       disp.show().set_button().set_wheel()._normalization = 0;
32936 
32937       double nymin = ymin, nymax = ymax, nxmin = xmin, nxmax = xmax;
32938       if (nymin==nymax) nymin = (Tfloat)min_max(nymax);
32939       if (nymin==nymax) { --nymin; ++nymax; }
32940       if (nxmin==nxmax && nxmin==0) { nxmin = 0; nxmax = siz - 1.0; }
32941 
32942       const unsigned char black[] = { 0, 0, 0 }, white[] = { 255, 255, 255 }, gray[] = { 220, 220, 220 };
32943       const unsigned char gray2[] = { 110, 110, 110 }, ngray[] = { 35, 35, 35 };
32944       static unsigned int odimv = 0;
32945       static CImg<ucharT> palette;
32946       if (odimv!=_spectrum) {
32947         odimv = _spectrum;
32948         palette = CImg<ucharT>(3,_spectrum,1,1,120).noise(70,1);
32949         if (_spectrum==1) { palette[0] = palette[1] = 120; palette[2] = 200; }
32950         else {
32951           palette(0,0) = 220; palette(1,0) = 10; palette(2,0) = 10;
32952           if (_spectrum>1) { palette(0,1) = 10; palette(1,1) = 220; palette(2,1) = 10; }
32953           if (_spectrum>2) { palette(0,2) = 10; palette(1,2) = 10; palette(2,2) = 220; }
32954         }
32955       }
32956 
32957       CImg<ucharT> visu0, visu, graph, text, axes;
32958       int x0 = -1, x1 = -1, y0 = -1, y1 = -1, omouse_x = -2, omouse_y = -2;
32959       const unsigned int one = plot_type==3?0:1;
32960       unsigned int okey = 0, obutton = 0;
32961       char message[1024] = { 0 };
32962       CImg_3x3(I,unsigned char);
32963 
32964       for (bool selected = false; !selected && !disp.is_closed() && !okey && !disp.wheel(); ) {
32965         const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y();
32966         const unsigned int key = disp.key(), button = disp.button();
32967 
32968         // Generate graph representation.
32969         if (!visu0) {
32970           visu0.assign(disp.width(),disp.height(),1,3,220);
32971           const int gdimx = disp.width() - 32, gdimy = disp.height() - 32;
32972           if (gdimx>0 && gdimy>0) {
32973             graph.assign(gdimx,gdimy,1,3,255);
32974             if (siz<32) graph.draw_grid(gdimx/(float)(siz-one),gdimy/(float)(siz-one),0,0,false,true,black,0.2f,0x33333333,0x33333333);
32975             else graph.draw_grid(-10,-10,0,0,false,true,black,0.2f,0x33333333,0x33333333);
32976             cimg_forC(*this,c) graph.draw_graph(get_shared_channel(c),&palette(0,c),(plot_type!=3 || _spectrum==1)?1:0.6f,
32977                                                 plot_type,vertex_type,nymax,nymin);
32978 
32979             axes.assign(gdimx,gdimy,1,1,0);
32980             const float
32981               dx = (float)cimg::abs(nxmax-nxmin), dy = (float)cimg::abs(nymax-nymin),
32982               px = (float)std::pow(10.0,(int)std::log10(dx)-2.0),
32983               py = (float)std::pow(10.0,(int)std::log10(dy)-2.0);
32984             const CImg<Tdouble>
32985               seqx = CImg<Tdouble>::sequence(1 + gdimx/60,nxmin,one?nxmax:nxmin+(nxmax-nxmin)*(siz+1)/siz).round(px),
32986               seqy = CImg<Tdouble>::sequence(1 + gdimy/60,nymax,nymin).round(py);
32987             axes.draw_axes(seqx,seqy,white);
32988             if (nymin>0) axes.draw_axis(seqx,gdimy-1,gray);
32989             if (nymax<0) axes.draw_axis(seqx,0,gray);
32990             if (nxmin>0) axes.draw_axis(0,seqy,gray);
32991             if (nxmax<0) axes.draw_axis(gdimx-1,seqy,gray);
32992 
32993             cimg_for3x3(axes,x,y,0,0,I,unsigned char)
32994               if (Icc) {
32995                 if (Icc==255) cimg_forC(graph,c) graph(x,y,c) = 0;
32996                 else cimg_forC(graph,c) graph(x,y,c) = (unsigned char)(2*graph(x,y,c)/3);
32997               }
32998               else if (Ipc || Inc || Icp || Icn || Ipp || Inn || Ipn || Inp) cimg_forC(graph,c) graph(x,y,c) = (graph(x,y,c)+255)/2;
32999 
33000             visu0.draw_image(16,16,graph);
33001             visu0.draw_line(15,15,16+gdimx,15,gray2).draw_line(16+gdimx,15,16+gdimx,16+gdimy,gray2).
33002               draw_line(16+gdimx,16+gdimy,15,16+gdimy,white).draw_line(15,16+gdimy,15,15,white);
33003           } else graph.assign();
33004           text.assign().draw_text(0,0,labelx?labelx:"X-axis",white,ngray,1,13).resize(-100,-100,1,3);
33005           visu0.draw_image((visu0.width()-text.width())/2,visu0.height()-14,~text);
33006           text.assign().draw_text(0,0,labely?labely:"Y-axis",white,ngray,1,13).rotate(-90).resize(-100,-100,1,3);
33007           visu0.draw_image(1,(visu0.height()-text.height())/2,~text);
33008           visu.assign();
33009         }
33010 
33011         // Generate and display current view.
33012         if (!visu) {
33013           visu.assign(visu0);
33014           if (graph && x0>=0 && x1>=0) {
33015             const int
33016               nx0 = x0<=x1?x0:x1,
33017               nx1 = x0<=x1?x1:x0,
33018               ny0 = y0<=y1?y0:y1,
33019               ny1 = y0<=y1?y1:y0,
33020               sx0 = 16 + nx0*(visu.width()-32)/(siz-one),
33021               sx1 = 15 + (nx1+1)*(visu.width()-32)/(siz-one),
33022               sy0 = 16 + ny0,
33023               sy1 = 16 + ny1;
33024             if (y0>=0 && y1>=0)
33025               visu.draw_rectangle(sx0,sy0,sx1,sy1,gray,0.5f).draw_rectangle(sx0,sy0,sx1,sy1,black,0.5f,0xCCCCCCCCU);
33026             else visu.draw_rectangle(sx0,0,sx1,visu.height()-17,gray,0.5f).
33027                    draw_line(sx0,16,sx0,visu.height()-17,black,0.5f,0xCCCCCCCCU).
33028                    draw_line(sx1,16,sx1,visu.height()-17,black,0.5f,0xCCCCCCCCU);
33029           }
33030           if (mouse_x>=16 && mouse_y>=16 && mouse_x<visu.width()-16 && mouse_y<visu.height()-16) {
33031             if (graph) visu.draw_line(mouse_x,16,mouse_x,visu.height()-17,black,0.5f,0x55555555U);
33032             const unsigned int x = (unsigned int)cimg::round((mouse_x-16.0f)*(siz-one)/(disp.width()-32),1,one?0:-1);
33033             const double cx = nxmin + x*(nxmax-nxmin)/(siz-1);
33034             if (_spectrum>=7)
33035               cimg_snprintf(message,sizeof(message),"Value[%u:%g] = ( %g %g %g ... %g %g %g )",x,cx,
33036                             (double)(*this)(x,0,0,0),(double)(*this)(x,0,0,1),(double)(*this)(x,0,0,2),
33037                             (double)(*this)(x,0,0,_spectrum-4),(double)(*this)(x,0,0,_spectrum-3),(double)(*this)(x,0,0,_spectrum-1));
33038             else {
33039               cimg_snprintf(message,sizeof(message),"Value[%u:%g] = ( ",x,cx);
33040               cimg_forC(*this,c) std::sprintf(message + std::strlen(message),"%g ",(double)(*this)(x,0,0,c));
33041               std::sprintf(message + std::strlen(message),")");
33042             }
33043             if (x0>=0 && x1>=0) {
33044               const unsigned int
33045                 nx0 = x0<=x1?x0:x1,
33046                 nx1 = x0<=x1?x1:x0,
33047                 ny0 = y0<=y1?y0:y1,
33048                 ny1 = y0<=y1?y1:y0;
33049               const double
33050                 cx0 = nxmin + nx0*(nxmax-nxmin)/(siz-1),
33051                 cx1 = nxmin + (nx1+one)*(nxmax-nxmin)/(siz-1),
33052                 cy0 = nymax - ny0*(nymax-nymin)/(visu._height-32),
33053                 cy1 = nymax - ny1*(nymax-nymin)/(visu._height-32);
33054               if (y0>=0 && y1>=0)
33055                 std::sprintf(message + std::strlen(message)," - Range ( %u:%g, %g ) - ( %u:%g, %g )",x0,cx0,cy0,x1+one,cx1,cy1);
33056               else
33057                 std::sprintf(message + std::strlen(message)," - Range [ %u:%g - %u:%g ]",x0,cx0,x1+one,cx1);
33058             }
33059             text.assign().draw_text(0,0,message,white,ngray,1,13).resize(-100,-100,1,3);
33060             visu.draw_image((visu.width()-text.width())/2,1,~text);
33061           }
33062           visu.display(disp);
33063         }
33064 
33065         // Test keys.
33066         switch (okey = key) {
33067 #if cimg_OS!=2
33068         case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT :
33069 #endif
33070         case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : okey = 0; break;
33071         case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
33072           disp.set_fullscreen(false).resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
33073                                             CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
33074             _is_resized = true;
33075           disp.set_key(key,false); okey = 0;
33076         } break;
33077         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
33078           disp.set_fullscreen(false).resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
33079           disp.set_key(key,false); okey = 0;
33080         } break;
33081         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
33082           disp.set_fullscreen(false).resize(cimg_fitscreen(640,480,1),false)._is_resized = true;
33083           disp.set_key(key,false); okey = 0;
33084         } break;
33085         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
33086           disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
33087           disp.set_key(key,false); okey = 0;
33088         } break;
33089         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
33090           static unsigned int snap_number = 0;
33091           if (visu || visu0) {
33092             CImg<ucharT> &screen = visu?visu:visu0;
33093             char filename[32] = { 0 };
33094             std::FILE *file;
33095             do {
33096               cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++);
33097               if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
33098             } while (file);
33099             (+screen).draw_text(0,0," Saving snapshot... ",black,gray,1,13).display(disp);
33100             screen.save(filename);
33101             screen.draw_text(0,0," Snapshot '%s' saved. ",black,gray,1,13,filename).display(disp);
33102           }
33103           disp.set_key(key,false); okey = 0;
33104         } break;
33105         case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
33106             static unsigned int snap_number = 0;
33107             if (visu || visu0) {
33108               CImg<ucharT> &screen = visu?visu:visu0;
33109               char filename[32] = { 0 };
33110               std::FILE *file;
33111               do {
33112 #ifdef cimg_use_zlib
33113                 cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++);
33114 #else
33115                 cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++);
33116 #endif
33117                 if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
33118               } while (file);
33119               (+screen).draw_text(0,0," Saving instance... ",black,gray,1,13).display(disp);
33120               save(filename);
33121               screen.draw_text(0,0," Instance '%s' saved. ",black,gray,1,13,filename).display(disp);
33122             }
33123             disp.set_key(key,false); okey = 0;
33124           } break;
33125         }
33126 
33127         // Handle mouse motion and mouse buttons
33128         if (obutton!=button || omouse_x!=mouse_x || omouse_y!=mouse_y) {
33129           visu.assign();
33130           if (disp.mouse_x()>=0 && disp.mouse_y()>=0) {
33131             const int
33132               mx = (mouse_x -16)*(int)(siz-one)/(disp.width()-32),
33133               cx = mx<0?0:(mx>=(int)(siz-one)?siz-1-one:mx),
33134               my = mouse_y - 16,
33135               cy = my<=0?0:(my>=(disp.height()-32)?(disp.height()-32):my);
33136             if (button&1) {
33137               if (!obutton) { x0 = cx; y0 = -1; } else { x1 = cx; y1 = -1; }
33138             }
33139             else if (button&2) {
33140               if (!obutton) { x0 = cx; y0 = cy; } else { x1 = cx; y1 = cy; }
33141             }
33142             else if (obutton) { x1 = x1>=0?cx:-1; y1 = y1>=0?cy:-1; selected = true; }
33143           } else if (!button && obutton) selected = true;
33144           obutton = button; omouse_x = mouse_x; omouse_y = mouse_y;
33145         }
33146         if (disp.is_resized()) { disp.resize(false); visu0.assign(); }
33147         if (visu && visu0) disp.wait();
33148       }
33149 
33150       disp._normalization = old_normalization;
33151       if (x1>=0 && x1<x0) cimg::swap(x0,x1);
33152       if (y1<y0) cimg::swap(y0,y1);
33153       disp.set_key(okey);
33154       return CImg<intT>(4,1,1,1,x0,y0,x1>=0?x1+(int)one:-1,y1);
33155     }
33156 
33157     //! Load an image from a file.
33158     /**
33159        \param filename is the name of the image file to load.
33160        \note The extension of \c filename defines the file format. If no filename
33161        extension is provided, CImg<T>::get_load() will try to load a .cimg file.
33162     **/
33163     CImg<T>& load(const char *const filename) {
33164       if (!filename)
33165         throw CImgArgumentException(_cimg_instance
33166                                     "load() : Specified filename is (null).",
33167                                     cimg_instance);
33168 
33169       if (!cimg::strncasecmp(filename,"http://",7)) {
33170         char filename_local[1024] = { 0 };
33171         load(cimg::load_network_external(filename,filename_local));
33172         std::remove(filename_local);
33173         return *this;
33174       }
33175 
33176       const char *const ext = cimg::split_filename(filename);
33177       const unsigned int omode = cimg::exception_mode();
33178       cimg::exception_mode() = 0;
33179       try {
33180 #ifdef cimg_load_plugin
33181         cimg_load_plugin(filename);
33182 #endif
33183 #ifdef cimg_load_plugin1
33184         cimg_load_plugin1(filename);
33185 #endif
33186 #ifdef cimg_load_plugin2
33187         cimg_load_plugin2(filename);
33188 #endif
33189 #ifdef cimg_load_plugin3
33190         cimg_load_plugin3(filename);
33191 #endif
33192 #ifdef cimg_load_plugin4
33193         cimg_load_plugin4(filename);
33194 #endif
33195 #ifdef cimg_load_plugin5
33196         cimg_load_plugin5(filename);
33197 #endif
33198 #ifdef cimg_load_plugin6
33199         cimg_load_plugin6(filename);
33200 #endif
33201 #ifdef cimg_load_plugin7
33202         cimg_load_plugin7(filename);
33203 #endif
33204 #ifdef cimg_load_plugin8
33205         cimg_load_plugin8(filename);
33206 #endif
33207         // ASCII formats
33208         if (!cimg::strcasecmp(ext,"asc")) load_ascii(filename);
33209         else if (!cimg::strcasecmp(ext,"dlm") ||
33210                  !cimg::strcasecmp(ext,"txt")) load_dlm(filename);
33211 
33212         // 2d binary formats
33213         else if (!cimg::strcasecmp(ext,"bmp")) load_bmp(filename);
33214         else if (!cimg::strcasecmp(ext,"jpg") ||
33215                  !cimg::strcasecmp(ext,"jpeg") ||
33216                  !cimg::strcasecmp(ext,"jpe") ||
33217                  !cimg::strcasecmp(ext,"jfif") ||
33218                  !cimg::strcasecmp(ext,"jif")) load_jpeg(filename);
33219         else if (!cimg::strcasecmp(ext,"png")) load_png(filename);
33220         else if (!cimg::strcasecmp(ext,"ppm") ||
33221                  !cimg::strcasecmp(ext,"pgm") ||
33222                  !cimg::strcasecmp(ext,"pnm") ||
33223                  !cimg::strcasecmp(ext,"pbm") ||
33224                  !cimg::strcasecmp(ext,"pnk")) load_pnm(filename);
33225         else if (!cimg::strcasecmp(ext,"pfm")) load_pfm(filename);
33226         else if (!cimg::strcasecmp(ext,"tif") ||
33227                  !cimg::strcasecmp(ext,"tiff")) load_tiff(filename);
33228         else if (!cimg::strcasecmp(ext,"exr")) load_exr(filename);
33229         else if (!cimg::strcasecmp(ext,"cr2") ||
33230                  !cimg::strcasecmp(ext,"crw") ||
33231                  !cimg::strcasecmp(ext,"dcr") ||
33232                  !cimg::strcasecmp(ext,"mrw") ||
33233                  !cimg::strcasecmp(ext,"nef") ||
33234                  !cimg::strcasecmp(ext,"orf") ||
33235                  !cimg::strcasecmp(ext,"pix") ||
33236                  !cimg::strcasecmp(ext,"ptx") ||
33237                  !cimg::strcasecmp(ext,"raf") ||
33238                  !cimg::strcasecmp(ext,"srf")) load_dcraw_external(filename);
33239 
33240         // 3d binary formats
33241         else if (!cimg::strcasecmp(ext,"dcm") ||
33242                  !cimg::strcasecmp(ext,"dicom")) load_medcon_external(filename);
33243         else if (!cimg::strcasecmp(ext,"hdr") ||
33244                  !cimg::strcasecmp(ext,"nii")) load_analyze(filename);
33245         else if (!cimg::strcasecmp(ext,"par") ||
33246                  !cimg::strcasecmp(ext,"rec")) load_parrec(filename);
33247         else if (!cimg::strcasecmp(ext,"mnc")) load_minc2(filename);
33248         else if (!cimg::strcasecmp(ext,"inr")) load_inr(filename);
33249         else if (!cimg::strcasecmp(ext,"pan")) load_pandore(filename);
33250         else if (!cimg::strcasecmp(ext,"cimg") ||
33251                  !cimg::strcasecmp(ext,"cimgz") ||
33252                  !*ext)  return load_cimg(filename);
33253 
33254         // Archive files
33255         else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename);
33256 
33257         // Image sequences
33258         else if (!cimg::strcasecmp(ext,"avi") ||
33259                  !cimg::strcasecmp(ext,"mov") ||
33260                  !cimg::strcasecmp(ext,"asf") ||
33261                  !cimg::strcasecmp(ext,"divx") ||
33262                  !cimg::strcasecmp(ext,"flv") ||
33263                  !cimg::strcasecmp(ext,"mpg") ||
33264                  !cimg::strcasecmp(ext,"m1v") ||
33265                  !cimg::strcasecmp(ext,"m2v") ||
33266                  !cimg::strcasecmp(ext,"m4v") ||
33267                  !cimg::strcasecmp(ext,"mjp") ||
33268                  !cimg::strcasecmp(ext,"mkv") ||
33269                  !cimg::strcasecmp(ext,"mpe") ||
33270                  !cimg::strcasecmp(ext,"movie") ||
33271                  !cimg::strcasecmp(ext,"ogm") ||
33272                  !cimg::strcasecmp(ext,"ogg") ||
33273                  !cimg::strcasecmp(ext,"qt") ||
33274                  !cimg::strcasecmp(ext,"rm") ||
33275                  !cimg::strcasecmp(ext,"vob") ||
33276                  !cimg::strcasecmp(ext,"wmv") ||
33277                  !cimg::strcasecmp(ext,"xvid") ||
33278                  !cimg::strcasecmp(ext,"mpeg")) load_ffmpeg(filename);
33279         else throw CImgIOException("CImg<%s>::load()",
33280                                    pixel_type());
33281       } catch (CImgException& e) {
33282         if (!cimg::strncasecmp(e.what(),"cimg::fopen()",13)) {
33283           cimg::exception_mode() = omode;
33284           throw CImgIOException(_cimg_instance
33285                                 "load() : Failed to open file '%s'.",
33286                                 cimg_instance,
33287                                 filename);
33288         } else try {
33289           const char *const f_type = cimg::file_type(0,filename);
33290           if (!cimg::strcasecmp(f_type,"pnm")) load_pnm(filename);
33291           else if (!cimg::strcasecmp(f_type,"pfm")) load_pfm(filename);
33292           else if (!cimg::strcasecmp(f_type,"bmp")) load_bmp(filename);
33293           else if (!cimg::strcasecmp(f_type,"jpg")) load_jpeg(filename);
33294           else if (!cimg::strcasecmp(f_type,"pan")) load_pandore(filename);
33295           else if (!cimg::strcasecmp(f_type,"png")) load_png(filename);
33296           else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename);
33297           else if (!cimg::strcasecmp(f_type,"inr")) load_inr(filename);
33298           else if (!cimg::strcasecmp(f_type,"dcm")) load_medcon_external(filename);
33299           else throw CImgIOException("CImg<%s>::load()",
33300                                      pixel_type());
33301         } catch (CImgException&) {
33302           try {
33303             load_other(filename);
33304           } catch (CImgException&) {
33305             throw CImgIOException(_cimg_instance
33306                                   "load() : Failed to recognize format of file '%s'.",
33307                                   cimg_instance,
33308                                   filename);
33309           }
33310         }
33311       }
33312       cimg::exception_mode() = omode;
33313       return *this;
33314     }
33315 
33316     static CImg<T> get_load(const char *const filename) {
33317       return CImg<T>().load(filename);
33318     }
33319 
33320     //! Load an image from an ASCII file.
33321     CImg<T>& load_ascii(const char *const filename) {
33322       return _load_ascii(0,filename);
33323     }
33324 
33325     static CImg<T> get_load_ascii(const char *const filename) {
33326       return CImg<T>().load_ascii(filename);
33327     }
33328 
33329     //! Load an image from an ASCII file.
33330     CImg<T>& load_ascii(std::FILE *const file) {
33331       return _load_ascii(file,0);
33332     }
33333 
33334     static CImg<T> get_load_ascii(std::FILE *const file) {
33335       return CImg<T>().load_ascii(file);
33336     }
33337 
33338     CImg<T>& _load_ascii(std::FILE *const file, const char *const filename) {
33339       if (!file && !filename)
33340         throw CImgArgumentException(_cimg_instance
33341                                     "load_ascii() : Specified filename is (null).",
33342                                     cimg_instance);
33343 
33344       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
33345       char line[256] = { 0 };
33346       int err = std::fscanf(nfile,"%255[^\n]",line);
33347       unsigned int off, dx = 0, dy = 1, dz = 1, dc = 1;
33348       std::sscanf(line,"%u%*c%u%*c%u%*c%u",&dx,&dy,&dz,&dc);
33349       err = std::fscanf(nfile,"%*[^0-9.eE+-]");
33350       if (!dx || !dy || !dz || !dc) {
33351         if (!file) cimg::fclose(nfile);
33352         throw CImgIOException(_cimg_instance
33353                               "load_ascii() : Invalid ASCII header in file '%s', image dimensions are set to (%u,%u,%u,%u).",
33354                               cimg_instance,
33355                               filename?filename:"(FILE*)",dx,dy,dz,dc);
33356       }
33357       assign(dx,dy,dz,dc);
33358       const unsigned int siz = size();
33359       double val;
33360       T *ptr = _data;
33361       for (err = 1, off = 0; off<siz && err==1; ++off) {
33362         err = std::fscanf(nfile,"%lf%*[^0-9.eE+-]",&val);
33363         *(ptr++) = (T)val;
33364       }
33365       if (err!=1)
33366         cimg::warn(_cimg_instance
33367                    "load_ascii() : Only %u/%u values read from file '%s'.",
33368                    cimg_instance,
33369                    off-1,siz,filename?filename:"(FILE*)");
33370 
33371       if (!file) cimg::fclose(nfile);
33372       return *this;
33373     }
33374 
33375     //! Load an image from a DLM file.
33376     CImg<T>& load_dlm(const char *const filename) {
33377       return _load_dlm(0,filename);
33378     }
33379 
33380     static CImg<T> get_load_dlm(const char *const filename) {
33381       return CImg<T>().load_dlm(filename);
33382     }
33383 
33384     //! Load an image from a DLM file.
33385     CImg<T>& load_dlm(std::FILE *const file) {
33386       return _load_dlm(file,0);
33387     }
33388 
33389     static CImg<T> get_load_dlm(std::FILE *const file) {
33390       return CImg<T>().load_dlm(file);
33391     }
33392 
33393     CImg<T>& _load_dlm(std::FILE *const file, const char *const filename) {
33394       if (!file && !filename)
33395         throw CImgArgumentException(_cimg_instance
33396                                     "load_dlm() : Specified filename is (null).",
33397                                     cimg_instance);
33398 
33399       std::FILE *const nfile = file?file:cimg::fopen(filename,"r");
33400       char delimiter[256] = { 0 }, tmp[256] = { 0 };
33401       unsigned int cdx = 0, dx = 0, dy = 0;
33402       int err = 0;
33403       double val;
33404       assign(256,256);
33405       while ((err = std::fscanf(nfile,"%lf%255[^0-9.+-]",&val,delimiter))>0) {
33406         if (err>0) (*this)(cdx++,dy) = (T)val;
33407         if (cdx>=_width) resize(3*_width/2,_height,1,1,0);
33408         char c = 0;
33409         if (!std::sscanf(delimiter,"%255[^\n]%c",tmp,&c) || c=='\n') {
33410           dx = cimg::max(cdx,dx);
33411           if (++dy>=_height) resize(_width,3*_height/2,1,1,0);
33412           cdx = 0;
33413         }
33414       }
33415       if (cdx && err==1) { dx = cdx; ++dy; }
33416       if (!dx || !dy) {
33417         if (!file) cimg::fclose(nfile);
33418         throw CImgIOException(_cimg_instance
33419                               "load_dlm() : Invalid DLM file '%s'.",
33420                               cimg_instance,
33421                               filename?filename:"(FILE*)");
33422       }
33423       resize(dx,dy,1,1,0);
33424       if (!file) cimg::fclose(nfile);
33425       return *this;
33426     }
33427 
33428     //! Load an image from a BMP file.
33429     CImg<T>& load_bmp(const char *const filename) {
33430       return _load_bmp(0,filename);
33431     }
33432 
33433     static CImg<T> get_load_bmp(const char *const filename) {
33434       return CImg<T>().load_bmp(filename);
33435     }
33436 
33437     //! Load an image from a BMP file.
33438     CImg<T>& load_bmp(std::FILE *const file) {
33439       return _load_bmp(file,0);
33440     }
33441 
33442     static CImg<T> get_load_bmp(std::FILE *const file) {
33443       return CImg<T>().load_bmp(file);
33444     }
33445 
33446     CImg<T>& _load_bmp(std::FILE *const file, const char *const filename) {
33447       if (!file && !filename)
33448         throw CImgArgumentException(_cimg_instance
33449                                     "load_bmp() : Specified filename is (null).",
33450                                     cimg_instance);
33451 
33452       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
33453       unsigned char header[64] = { 0 };
33454       cimg::fread(header,54,nfile);
33455       if (*header!='B' || header[1]!='M') {
33456         if (!file) cimg::fclose(nfile);
33457         throw CImgIOException(_cimg_instance
33458                               "load_bmp() : Invalid BMP file '%s'.",
33459                               cimg_instance,
33460                               filename?filename:"(FILE*)");
33461       }
33462 
33463       // Read header and pixel buffer
33464       int
33465         file_size = header[0x02] + (header[0x03]<<8) + (header[0x04]<<16) + (header[0x05]<<24),
33466         offset = header[0x0A] + (header[0x0B]<<8) + (header[0x0C]<<16) + (header[0x0D]<<24),
33467         dx = header[0x12] + (header[0x13]<<8) + (header[0x14]<<16) + (header[0x15]<<24),
33468         dy = header[0x16] + (header[0x17]<<8) + (header[0x18]<<16) + (header[0x19]<<24),
33469         compression = header[0x1E] + (header[0x1F]<<8) + (header[0x20]<<16) + (header[0x21]<<24),
33470         nb_colors = header[0x2E] + (header[0x2F]<<8) + (header[0x30]<<16) + (header[0x31]<<24),
33471         bpp = header[0x1C] + (header[0x1D]<<8);
33472       const int
33473         cimg_iobuffer = 12*1024*1024,
33474         dx_bytes = (bpp==1)?(dx/8+(dx%8?1:0)):((bpp==4)?(dx/2+(dx%2?1:0)):(dx*bpp/8)),
33475         align_bytes = (4-dx_bytes%4)%4,
33476         buf_size = cimg::min(cimg::abs(dy)*(dx_bytes + align_bytes),file_size - offset);
33477 
33478       CImg<intT> palette;
33479       if (bpp<16) { if (!nb_colors) nb_colors = 1<<bpp; } else nb_colors = 0;
33480       if (nb_colors) { palette.assign(nb_colors); cimg::fread(palette._data,nb_colors,nfile); }
33481       const int xoffset = offset - 54 - 4*nb_colors;
33482       if (xoffset>0) std::fseek(nfile,xoffset,SEEK_CUR);
33483 
33484       CImg<ucharT> buffer;
33485       if (buf_size<cimg_iobuffer) { buffer.assign(buf_size); cimg::fread(buffer._data,buf_size,nfile); }
33486       else buffer.assign(dx_bytes + align_bytes);
33487       unsigned char *ptrs = buffer;
33488 
33489       // Decompress buffer (if necessary)
33490       if (compression) {
33491         if (file)
33492           throw CImgIOException(_cimg_instance
33493                                 "load_bmp() : Unable to load compressed data from '(*FILE)' inputs.",
33494                                 cimg_instance);
33495         else return load_other(filename);
33496       }
33497 
33498       // Read pixel data
33499       assign(dx,cimg::abs(dy),1,3);
33500       switch (bpp) {
33501       case 1 : { // Monochrome
33502         for (int y = height()-1; y>=0; --y) {
33503           if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); }
33504           unsigned char mask = 0x80, val = 0;
33505           cimg_forX(*this,x) {
33506             if (mask==0x80) val = *(ptrs++);
33507             const unsigned char *col = (unsigned char*)(palette._data + (val&mask?1:0));
33508             (*this)(x,y,2) = (T)*(col++);
33509             (*this)(x,y,1) = (T)*(col++);
33510             (*this)(x,y,0) = (T)*(col++);
33511             mask = cimg::ror(mask);
33512           }
33513           ptrs+=align_bytes;
33514         }
33515       } break;
33516       case 4 : { // 16 colors
33517         for (int y = height()-1; y>=0; --y) {
33518           if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); }
33519           unsigned char mask = 0xF0, val = 0;
33520           cimg_forX(*this,x) {
33521             if (mask==0xF0) val = *(ptrs++);
33522             const unsigned char color = (unsigned char)((mask<16)?(val&mask):((val&mask)>>4));
33523             const unsigned char *col = (unsigned char*)(palette._data + color);
33524             (*this)(x,y,2) = (T)*(col++);
33525             (*this)(x,y,1) = (T)*(col++);
33526             (*this)(x,y,0) = (T)*(col++);
33527             mask = cimg::ror(mask,4);
33528           }
33529           ptrs+=align_bytes;
33530         }
33531       } break;
33532       case 8 : { //  256 colors
33533         for (int y = height()-1; y>=0; --y) {
33534           if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); }
33535           cimg_forX(*this,x) {
33536             const unsigned char *col = (unsigned char*)(palette._data + *(ptrs++));
33537             (*this)(x,y,2) = (T)*(col++);
33538             (*this)(x,y,1) = (T)*(col++);
33539             (*this)(x,y,0) = (T)*(col++);
33540           }
33541           ptrs+=align_bytes;
33542         }
33543       } break;
33544       case 16 : { // 16 bits colors
33545         for (int y = height()-1; y>=0; --y) {
33546           if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); }
33547           cimg_forX(*this,x) {
33548             const unsigned char c1 = *(ptrs++), c2 = *(ptrs++);
33549             const unsigned short col = (unsigned short)(c1|(c2<<8));
33550             (*this)(x,y,2) = (T)(col&0x1F);
33551             (*this)(x,y,1) = (T)((col>>5)&0x1F);
33552             (*this)(x,y,0) = (T)((col>>10)&0x1F);
33553           }
33554           ptrs+=align_bytes;
33555         }
33556       } break;
33557       case 24 : { // 24 bits colors
33558         for (int y = height()-1; y>=0; --y) {
33559           if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); }
33560           cimg_forX(*this,x) {
33561             (*this)(x,y,2) = (T)*(ptrs++);
33562             (*this)(x,y,1) = (T)*(ptrs++);
33563             (*this)(x,y,0) = (T)*(ptrs++);
33564           }
33565           ptrs+=align_bytes;
33566         }
33567       } break;
33568       case 32 : { // 32 bits colors
33569         for (int y = height()-1; y>=0; --y) {
33570           if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); }
33571           cimg_forX(*this,x) {
33572             (*this)(x,y,2) = (T)*(ptrs++);
33573             (*this)(x,y,1) = (T)*(ptrs++);
33574             (*this)(x,y,0) = (T)*(ptrs++);
33575             ++ptrs;
33576           }
33577           ptrs+=align_bytes;
33578         }
33579       } break;
33580       }
33581       if (dy<0) mirror('y');
33582       if (!file) cimg::fclose(nfile);
33583       return *this;
33584     }
33585 
33586     //! Load an image from a JPEG file.
33587     CImg<T>& load_jpeg(const char *const filename) {
33588       return _load_jpeg(0,filename);
33589     }
33590 
33591     static CImg<T> get_load_jpeg(const char *const filename) {
33592       return CImg<T>().load_jpeg(filename);
33593     }
33594 
33595     //! Load an image from a JPEG file.
33596     CImg<T>& load_jpeg(std::FILE *const file) {
33597       return _load_jpeg(file,0);
33598     }
33599 
33600     static CImg<T> get_load_jpeg(std::FILE *const file) {
33601       return CImg<T>().load_jpeg(file);
33602     }
33603 
33604     // Custom error handler for libjpeg.
33605 #ifdef cimg_use_jpeg
33606     struct _cimg_error_mgr {
33607       struct jpeg_error_mgr original;
33608       jmp_buf setjmp_buffer;
33609       char message[JMSG_LENGTH_MAX];
33610     };
33611 
33612     typedef struct _cimg_error_mgr *_cimg_error_ptr;
33613 
33614     METHODDEF(void) _cimg_jpeg_error_exit(j_common_ptr cinfo) {
33615       _cimg_error_ptr c_err = (_cimg_error_ptr) cinfo->err;  // Return control to the setjmp point
33616       (*cinfo->err->format_message)(cinfo,c_err->message);
33617       jpeg_destroy(cinfo);  // Clean memory and temp files.
33618       longjmp(c_err->setjmp_buffer,1);
33619     }
33620 #endif
33621 
33622     CImg<T>& _load_jpeg(std::FILE *const file, const char *const filename) {
33623       if (!file && !filename)
33624         throw CImgArgumentException(_cimg_instance
33625                                     "load_jpeg() : Specified filename is (null).",
33626                                     cimg_instance);
33627 
33628 #ifndef cimg_use_jpeg
33629       if (file)
33630         throw CImgIOException(_cimg_instance
33631                               "load_jpeg() : Unable to load data from '(FILE*)' unless libjpeg is enabled.",
33632                               cimg_instance);
33633       else return load_other(filename);
33634 #else
33635 
33636       struct jpeg_decompress_struct cinfo;
33637       struct _cimg_error_mgr jerr;
33638       cinfo.err = jpeg_std_error(&jerr.original);
33639       jerr.original.error_exit = _cimg_jpeg_error_exit;
33640 
33641       if (setjmp(jerr.setjmp_buffer)) { // JPEG error
33642         throw CImgIOException(_cimg_instance
33643                              "load_jpeg() : Error message returned by libjpeg : %s.",
33644                              cimg_instance,jerr.message);
33645       }
33646 
33647       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
33648       jpeg_create_decompress(&cinfo);
33649       jpeg_stdio_src(&cinfo,nfile);
33650       jpeg_read_header(&cinfo,TRUE);
33651       jpeg_start_decompress(&cinfo);
33652 
33653       if (cinfo.output_components!=1 && cinfo.output_components!=3 && cinfo.output_components!=4) {
33654         if (!file) return load_other(filename);
33655         else
33656           throw CImgIOException(_cimg_instance
33657                                 "load_jpeg() : Failed to load JPEG data from file '%s'.",
33658                                 cimg_instance,filename?filename:"(FILE*)");
33659       }
33660       CImg<ucharT> buffer(cinfo.output_width*cinfo.output_components);
33661       JSAMPROW row_pointer[1];
33662       assign(cinfo.output_width,cinfo.output_height,1,cinfo.output_components);
33663       T *ptr_r = _data, *ptr_g = _data + _width*_height, *ptr_b = _data + 2*_width*_height, *ptr_a = _data + 3*_width*_height;
33664       while (cinfo.output_scanline<cinfo.output_height) {
33665         *row_pointer = buffer._data;
33666         if (jpeg_read_scanlines(&cinfo,row_pointer,1)!=1) {
33667           cimg::warn(_cimg_instance
33668                      "load_jpeg() : Incomplete data in file '%s'.",
33669                      cimg_instance,filename?filename:"(FILE*)");
33670           break;
33671         }
33672         const unsigned char *ptrs = buffer._data;
33673         switch (_spectrum) {
33674         case 1 : {
33675           cimg_forX(*this,x) *(ptr_r++) = (T)*(ptrs++);
33676         } break;
33677         case 3 : {
33678           cimg_forX(*this,x) {
33679             *(ptr_r++) = (T)*(ptrs++);
33680             *(ptr_g++) = (T)*(ptrs++);
33681             *(ptr_b++) = (T)*(ptrs++);
33682           }
33683         } break;
33684         case 4 : {
33685           cimg_forX(*this,x) {
33686             *(ptr_r++) = (T)*(ptrs++);
33687             *(ptr_g++) = (T)*(ptrs++);
33688             *(ptr_b++) = (T)*(ptrs++);
33689             *(ptr_a++) = (T)*(ptrs++);
33690           }
33691         } break;
33692         }
33693       }
33694       jpeg_finish_decompress(&cinfo);
33695       jpeg_destroy_decompress(&cinfo);
33696       if (!file) cimg::fclose(nfile);
33697       return *this;
33698 #endif
33699     }
33700 
33701     //! Load an image from a file, using Magick++ library.
33702     // Added April/may 2006 by Christoph Hormann <chris_hormann@gmx.de>
33703     //   This is experimental code, not much tested, use with care.
33704     CImg<T>& load_magick(const char *const filename) {
33705       if (!filename)
33706         throw CImgArgumentException(_cimg_instance
33707                                     "load_magick() : Specified filename is (null).",
33708                                     cimg_instance);
33709 
33710 #ifdef cimg_use_magick
33711       Magick::Image image(filename);
33712       const unsigned int W = image.size().width(), H = image.size().height();
33713       switch (image.type()) {
33714       case Magick::PaletteMatteType :
33715       case Magick::TrueColorMatteType :
33716       case Magick::ColorSeparationType : {
33717         assign(W,H,1,4);
33718         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);
33719         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
33720         for (unsigned int off = W*H; off; --off) {
33721           *(ptr_r++) = (T)(pixels->red);
33722           *(ptr_g++) = (T)(pixels->green);
33723           *(ptr_b++) = (T)(pixels->blue);
33724           *(ptr_a++) = (T)(pixels->opacity);
33725           ++pixels;
33726         }
33727       } break;
33728       case Magick::PaletteType :
33729       case Magick::TrueColorType : {
33730         assign(W,H,1,3);
33731         T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
33732         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
33733         for (unsigned int off = W*H; off; --off) {
33734           *(ptr_r++) = (T)(pixels->red);
33735           *(ptr_g++) = (T)(pixels->green);
33736           *(ptr_b++) = (T)(pixels->blue);
33737           ++pixels;
33738         }
33739       } break;
33740       case Magick::GrayscaleMatteType : {
33741         assign(W,H,1,2);
33742         T *ptr_r = data(0,0,0,0), *ptr_a = data(0,0,0,1);
33743         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
33744         for (unsigned int off = W*H; off; --off) {
33745           *(ptr_r++) = (T)(pixels->red);
33746           *(ptr_a++) = (T)(pixels->opacity);
33747           ++pixels;
33748         }
33749       } break;
33750       default : {
33751         assign(W,H,1,1);
33752         T *ptr_r = data(0,0,0,0);
33753         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
33754         for (unsigned int off = W*H; off; --off) {
33755           *(ptr_r++) = (T)(pixels->red);
33756           ++pixels;
33757         }
33758       }
33759       }
33760 #else
33761       throw CImgIOException(_cimg_instance
33762                             "load_magick() : Unable to load file '%s' unless libMagick++ is enabled.",
33763                             cimg_instance,
33764                             filename);
33765 #endif
33766       return *this;
33767     }
33768 
33769     static CImg<T> get_load_magick(const char *const filename) {
33770       return CImg<T>().load_magick(filename);
33771     }
33772 
33773     //! Load an image from a PNG file.
33774     CImg<T>& load_png(const char *const filename) {
33775       return _load_png(0,filename);
33776     }
33777 
33778     static CImg<T> get_load_png(const char *const filename) {
33779       return CImg<T>().load_png(filename);
33780     }
33781 
33782     //! Load an image from a PNG file.
33783     CImg<T>& load_png(std::FILE *const file) {
33784       return _load_png(file,0);
33785     }
33786 
33787     static CImg<T> get_load_png(std::FILE *const file) {
33788       return CImg<T>().load_png(file);
33789     }
33790 
33791     // (Note : Most of this function has been written by Eric Fausett)
33792     CImg<T>& _load_png(std::FILE *const file, const char *const filename) {
33793       if (!file && !filename)
33794         throw CImgArgumentException(_cimg_instance
33795                                     "load_png() : Specified filename is (null).",
33796                                     cimg_instance);
33797 
33798 #ifndef cimg_use_png
33799       if (file)
33800         throw CImgIOException(_cimg_instance
33801                               "load_png() : Unable to load data from '(FILE*)' unless libpng is enabled.",
33802                               cimg_instance);
33803 
33804       else return load_other(filename);
33805 #else
33806       // Open file and check for PNG validity
33807       const char *volatile nfilename = filename; // two 'volatile' here to remove a g++ warning due to 'setjmp'.
33808       std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"rb");
33809 
33810       unsigned char pngCheck[8] = { 0 };
33811       cimg::fread(pngCheck,8,(std::FILE*)nfile);
33812       if (png_sig_cmp(pngCheck,0,8)) {
33813         if (!file) cimg::fclose(nfile);
33814         throw CImgIOException(_cimg_instance
33815                               "load_png() : Invalid PNG file '%s'.",
33816                               cimg_instance,
33817                               nfilename?nfilename:"(FILE*)");
33818       }
33819 
33820       // Setup PNG structures for read
33821       png_voidp user_error_ptr = 0;
33822       png_error_ptr user_error_fn = 0, user_warning_fn = 0;
33823       png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,user_error_ptr,user_error_fn,user_warning_fn);
33824       if (!png_ptr) {
33825         if (!file) cimg::fclose(nfile);
33826         throw CImgIOException(_cimg_instance
33827                               "load_png() : Failed to initialize 'png_ptr' structure for file '%s'.",
33828                               cimg_instance,
33829                               nfilename?nfilename:"(FILE*)");
33830       }
33831       png_infop info_ptr = png_create_info_struct(png_ptr);
33832       if (!info_ptr) {
33833         if (!file) cimg::fclose(nfile);
33834         png_destroy_read_struct(&png_ptr,(png_infopp)0,(png_infopp)0);
33835         throw CImgIOException(_cimg_instance
33836                               "load_png() : Failed to initialize 'info_ptr' structure for file '%s'.",
33837                               cimg_instance,
33838                               nfilename?nfilename:"(FILE*)");
33839       }
33840       png_infop end_info = png_create_info_struct(png_ptr);
33841       if (!end_info) {
33842         if (!file) cimg::fclose(nfile);
33843         png_destroy_read_struct(&png_ptr,&info_ptr,(png_infopp)0);
33844         throw CImgIOException(_cimg_instance
33845                               "load_png() : Failed to initialize 'end_info' structure for file '%s'.",
33846                               cimg_instance,
33847                               nfilename?nfilename:"(FILE*)");
33848       }
33849 
33850       // Error handling callback for png file reading
33851       if (setjmp(png_jmpbuf(png_ptr))) {
33852         if (!file) cimg::fclose((std::FILE*)nfile);
33853         png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0);
33854         throw CImgIOException(_cimg_instance
33855                               "load_png() : Encountered unknown fatal error in libpng for file '%s'.",
33856                               cimg_instance,
33857                               nfilename?nfilename:"(FILE*)");
33858       }
33859       png_init_io(png_ptr, nfile);
33860       png_set_sig_bytes(png_ptr, 8);
33861 
33862       // Get PNG Header Info up to data block
33863       png_read_info(png_ptr,info_ptr);
33864       png_uint_32 W, H;
33865       int bit_depth, color_type, interlace_type;
33866       bool is_gray = false;
33867       png_get_IHDR(png_ptr,info_ptr,&W,&H,&bit_depth,&color_type,&interlace_type,(int*)0,(int*)0);
33868 
33869       // Transforms to unify image data
33870       if (color_type==PNG_COLOR_TYPE_PALETTE) {
33871         png_set_palette_to_rgb(png_ptr);
33872         color_type = PNG_COLOR_TYPE_RGB;
33873         bit_depth = 8;
33874       }
33875       if (color_type==PNG_COLOR_TYPE_GRAY && bit_depth<8) {
33876         png_set_expand_gray_1_2_4_to_8(png_ptr);
33877         color_type = PNG_COLOR_TYPE_RGB;
33878         is_gray = true;
33879         bit_depth = 8;
33880       }
33881       if (png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS)) {
33882         png_set_tRNS_to_alpha(png_ptr);
33883         color_type |= PNG_COLOR_MASK_ALPHA;
33884       }
33885       if (color_type==PNG_COLOR_TYPE_GRAY || color_type==PNG_COLOR_TYPE_GRAY_ALPHA) {
33886         png_set_gray_to_rgb(png_ptr);
33887         color_type |= PNG_COLOR_MASK_COLOR;
33888         is_gray = true;
33889       }
33890       if (color_type==PNG_COLOR_TYPE_RGB)
33891         png_set_filler(png_ptr,0xffffU,PNG_FILLER_AFTER);
33892 
33893       png_read_update_info(png_ptr,info_ptr);
33894       if (bit_depth!=8 && bit_depth!=16) {
33895         if (!file) cimg::fclose(nfile);
33896         png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0);
33897         throw CImgIOException(_cimg_instance
33898                               "load_png() : Invalid bit depth %u in file '%s'.",
33899                               cimg_instance,
33900                               bit_depth,nfilename?nfilename:"(FILE*)");
33901       }
33902       const int byte_depth = bit_depth>>3;
33903 
33904       // Allocate Memory for Image Read
33905       png_bytep *const imgData = new png_bytep[H];
33906       for (unsigned int row = 0; row<H; ++row) imgData[row] = new png_byte[byte_depth*4*W];
33907       png_read_image(png_ptr,imgData);
33908       png_read_end(png_ptr,end_info);
33909 
33910       // Read pixel data
33911       if (color_type!=PNG_COLOR_TYPE_RGB && color_type!=PNG_COLOR_TYPE_RGB_ALPHA) {
33912         if (!file) cimg::fclose(nfile);
33913         png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0);
33914         throw CImgIOException(_cimg_instance
33915                               "load_png() : Invalid color coding type %u in file '%s'.",
33916                               cimg_instance,
33917                               color_type,nfilename?nfilename:"(FILE*)");
33918       }
33919       const bool is_alpha = (color_type==PNG_COLOR_TYPE_RGBA);
33920       assign(W,H,1,(is_gray?1:3) + (is_alpha?1:0));
33921       T
33922         *ptr_r = data(0,0,0,0),
33923         *ptr_g = is_gray?0:data(0,0,0,1),
33924         *ptr_b = is_gray?0:data(0,0,0,2),
33925         *ptr_a = !is_alpha?0:data(0,0,0,is_gray?1:3);
33926       switch (bit_depth) {
33927       case 8 : {
33928         cimg_forY(*this,y) {
33929           const unsigned char *ptrs = (unsigned char*)imgData[y];
33930           cimg_forX(*this,x) {
33931             *(ptr_r++) = (T)*(ptrs++);
33932             if (ptr_g) *(ptr_g++) = (T)*(ptrs++); else ++ptrs;
33933             if (ptr_b) *(ptr_b++) = (T)*(ptrs++); else ++ptrs;
33934             if (ptr_a) *(ptr_a++) = (T)*(ptrs++); else ++ptrs;
33935           }
33936         }
33937       } break;
33938       case 16 : {
33939         cimg_forY(*this,y) {
33940           const unsigned short *ptrs = (unsigned short*)(imgData[y]);
33941           if (!cimg::endianness()) cimg::invert_endianness(ptrs,4*_width);
33942           cimg_forX(*this,x) {
33943             *(ptr_r++) = (T)*(ptrs++);
33944             if (ptr_g) *(ptr_g++) = (T)*(ptrs++); else ++ptrs;
33945             if (ptr_b) *(ptr_b++) = (T)*(ptrs++); else ++ptrs;
33946             if (ptr_a) *(ptr_a++) = (T)*(ptrs++); else ++ptrs;
33947           }
33948         }
33949       } break;
33950       }
33951       png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
33952 
33953       // Deallocate Image Read Memory
33954       cimg_forY(*this,n) delete[] imgData[n];
33955       delete[] imgData;
33956       if (!file) cimg::fclose(nfile);
33957       return *this;
33958 #endif
33959     }
33960 
33961     //! Load an image from a PNM file.
33962     CImg<T>& load_pnm(const char *const filename) {
33963       return _load_pnm(0,filename);
33964     }
33965 
33966     static CImg<T> get_load_pnm(const char *const filename) {
33967       return CImg<T>().load_pnm(filename);
33968     }
33969 
33970     //! Load an image from a PNM file.
33971     CImg<T>& load_pnm(std::FILE *const file) {
33972       return _load_pnm(file,0);
33973     }
33974 
33975     static CImg<T> get_load_pnm(std::FILE *const file) {
33976       return CImg<T>().load_pnm(file);
33977     }
33978 
33979     CImg<T>& _load_pnm(std::FILE *const file, const char *const filename) {
33980       if (!file && !filename)
33981         throw CImgArgumentException(_cimg_instance
33982                                     "load_pnm() : Specified filename is (null).",
33983                                     cimg_instance);
33984 
33985       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
33986       unsigned int ppm_type, W, H, D = 1, colormax = 255;
33987       char item[1024] = { 0 };
33988       int err, rval, gval, bval;
33989       const long cimg_iobuffer = 12*1024*1024;
33990       while ((err=std::fscanf(nfile,"%1023[^\n]",item))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
33991       if (std::sscanf(item," P%u",&ppm_type)!=1) {
33992         if (!file) cimg::fclose(nfile);
33993         throw CImgIOException(_cimg_instance
33994                               "load_pnm() : PNM header not found in file '%s'.",
33995                               cimg_instance,
33996                               filename?filename:"(FILE*)");
33997       }
33998       while ((err=std::fscanf(nfile," %1023[^\n]",item))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
33999       if ((err=std::sscanf(item," %u %u %u %u",&W,&H,&D,&colormax))<2) {
34000         if (!file) cimg::fclose(nfile);
34001         throw CImgIOException(_cimg_instance
34002                               "load_pnm() : WIDTH and HEIGHT fields undefined in file '%s'.",
34003                               cimg_instance,
34004                               filename?filename:"(FILE*)");
34005       }
34006       if (ppm_type!=1 && ppm_type!=4) {
34007         if (err==2 || (err==3 && (ppm_type==5 || ppm_type==8 || ppm_type==9))) {
34008           while ((err=std::fscanf(nfile," %1023[^\n]",item))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
34009           if (std::sscanf(item,"%u",&colormax)!=1)
34010             cimg::warn(_cimg_instance
34011                        "load_pnm() : COLORMAX field is undefined in file '%s'.",
34012                        cimg_instance,
34013                        filename?filename:"(FILE*)");
34014         } else { colormax = D; D = 1; }
34015       }
34016       std::fgetc(nfile);
34017 
34018       switch (ppm_type) {
34019       case 1 : { // 2d b&w ascii.
34020         assign(W,H,1,1);
34021         T* ptrd = _data;
34022         cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)(rval?0:255); else break; }
34023       } break;
34024       case 2 : { // 2d grey ascii.
34025         assign(W,H,1,1);
34026         T* ptrd = _data;
34027         cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)rval; else break; }
34028       } break;
34029       case 3 : { // 2d color ascii.
34030         assign(W,H,1,3);
34031         T *ptrd = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
34032         cimg_forXY(*this,x,y) {
34033           if (std::fscanf(nfile,"%d %d %d",&rval,&gval,&bval)==3) { *(ptrd++) = (T)rval; *(ptr_g++) = (T)gval; *(ptr_b++) = (T)bval; }
34034           else break;
34035         }
34036       } break;
34037       case 4 : { // 2d b&w binary (support 3D PINK extension).
34038         CImg<ucharT> raw;
34039         assign(W,H,D,1);
34040         T *ptrd = data(0,0,0,0);
34041         unsigned int w = 0, h = 0, d = 0;
34042         for (long to_read = (long)((W/8 + (W%8?1:0))*H*D); to_read>0; ) {
34043           raw.assign(cimg::min(to_read,cimg_iobuffer));
34044           cimg::fread(raw._data,raw._width,nfile);
34045           to_read-=raw._width;
34046           const unsigned char *ptrs = raw._data;
34047           unsigned char mask = 0, val = 0;
34048           for (unsigned long off = (unsigned long)raw._width; off || mask; mask>>=1) {
34049             if (!mask) { if (off--) val = *(ptrs++); mask = 128; }
34050             *(ptrd++) = (T)((val&mask)?0:255);
34051             if (++w==W) { w = 0; mask = 0; if (++h==H) { h = 0; if (++d==D) break; }}
34052           }
34053         }
34054       } break;
34055       case 5 : { // 2d/3d grey binary (support 3D PINK extension).
34056         if (colormax<256) { // 8 bits.
34057           CImg<ucharT> raw;
34058           assign(W,H,D,1);
34059           T *ptrd = data(0,0,0,0);
34060           for (long to_read = (long)size(); to_read>0; ) {
34061             raw.assign(cimg::min(to_read,cimg_iobuffer));
34062             cimg::fread(raw._data,raw._width,nfile);
34063             to_read-=raw._width;
34064             const unsigned char *ptrs = raw._data;
34065             for (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
34066           }
34067         } else { // 16 bits.
34068           CImg<ushortT> raw;
34069           assign(W,H,D,1);
34070           T *ptrd = data(0,0,0,0);
34071           for (long to_read = (long)size(); to_read>0; ) {
34072             raw.assign(cimg::min(to_read,cimg_iobuffer/2));
34073             cimg::fread(raw._data,raw._width,nfile);
34074             if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width);
34075             to_read-=raw._width;
34076             const unsigned short *ptrs = raw._data;
34077             for (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
34078           }
34079         }
34080       } break;
34081       case 6 : { // 2d color binary.
34082         if (colormax<256) { // 8 bits.
34083           CImg<ucharT> raw;
34084           assign(W,H,1,3);
34085           T
34086             *ptr_r = data(0,0,0,0),
34087             *ptr_g = data(0,0,0,1),
34088             *ptr_b = data(0,0,0,2);
34089           for (long to_read = (long)size(); to_read>0; ) {
34090             raw.assign(cimg::min(to_read,cimg_iobuffer));
34091             cimg::fread(raw._data,raw._width,nfile);
34092             to_read-=raw._width;
34093             const unsigned char *ptrs = raw._data;
34094             for (unsigned long off = (unsigned long)raw._width/3; off; --off) {
34095               *(ptr_r++) = (T)*(ptrs++);
34096               *(ptr_g++) = (T)*(ptrs++);
34097               *(ptr_b++) = (T)*(ptrs++);
34098             }
34099           }
34100         } else { // 16 bits.
34101           CImg<ushortT> raw;
34102           assign(W,H,1,3);
34103           T
34104             *ptr_r = data(0,0,0,0),
34105             *ptr_g = data(0,0,0,1),
34106             *ptr_b = data(0,0,0,2);
34107           for (long to_read = (int)size(); to_read>0; ) {
34108             raw.assign(cimg::min(to_read,cimg_iobuffer/2));
34109             cimg::fread(raw._data,raw._width,nfile);
34110             if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width);
34111             to_read-=raw._width;
34112             const unsigned short *ptrs = raw._data;
34113             for (unsigned long off = (unsigned long)raw._width/3; off; --off) {
34114               *(ptr_r++) = (T)*(ptrs++);
34115               *(ptr_g++) = (T)*(ptrs++);
34116               *(ptr_b++) = (T)*(ptrs++);
34117             }
34118           }
34119         }
34120       } break;
34121       case 8 : { // 2d/3d grey binary with int32 integers (PINK extension).
34122         CImg<intT> raw;
34123         assign(W,H,D,1);
34124         T *ptrd = data(0,0,0,0);
34125         for (long to_read = (long)size(); to_read>0; ) {
34126           raw.assign(cimg::min(to_read,cimg_iobuffer));
34127           cimg::fread(raw._data,raw._width,nfile);
34128           to_read-=raw._width;
34129           const int *ptrs = raw._data;
34130           for (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
34131         }
34132       } break;
34133       case 9 : { // 2d/3d grey binary with float values (PINK extension).
34134         CImg<floatT> raw;
34135         assign(W,H,D,1);
34136         T *ptrd = data(0,0,0,0);
34137         for (long to_read = (long)size(); to_read>0; ) {
34138           raw.assign(cimg::min(to_read,cimg_iobuffer));
34139           cimg::fread(raw._data,raw._width,nfile);
34140           to_read-=raw._width;
34141           const float *ptrs = raw._data;
34142           for (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
34143         }
34144       } break;
34145       default :
34146         assign();
34147         if (!file) cimg::fclose(nfile);
34148         throw CImgIOException(_cimg_instance
34149                               "load_pnm() : PNM type 'P%d' found, but type is not supported.",
34150                               cimg_instance,
34151                               filename?filename:"(FILE*)",ppm_type);
34152       }
34153       if (!file) cimg::fclose(nfile);
34154       return *this;
34155     }
34156 
34157     //! Load an image from a PFM file.
34158     CImg<T>& load_pfm(const char *const filename) {
34159       return _load_pfm(0,filename);
34160     }
34161 
34162     static CImg<T> get_load_pfm(const char *const filename) {
34163       return CImg<T>().load_pfm(filename);
34164     }
34165 
34166     //! Load an image from a PFM file.
34167     CImg<T>& load_pfm(std::FILE *const file) {
34168       return _load_pfm(file,0);
34169     }
34170 
34171     static CImg<T> get_load_pfm(std::FILE *const file) {
34172       return CImg<T>().load_pfm(file);
34173     }
34174 
34175     CImg<T>& _load_pfm(std::FILE *const file, const char *const filename) {
34176       if (!file && !filename)
34177         throw CImgArgumentException(_cimg_instance
34178                                     "load_pfm() : Specified filename is (null).",
34179                                     cimg_instance);
34180 
34181       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
34182       char pfm_type, item[1024] = { 0 };
34183       int W = 0, H = 0, err = 0;
34184       double scale = 0;
34185       while ((err=std::fscanf(nfile,"%1023[^\n]",item))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
34186       if (std::sscanf(item," P%c",&pfm_type)!=1) {
34187         if (!file) cimg::fclose(nfile);
34188         throw CImgIOException(_cimg_instance
34189                               "load_pfm() : PFM header not found in file '%s'.",
34190                               cimg_instance,
34191                               filename?filename:"(FILE*)");
34192       }
34193       while ((err=std::fscanf(nfile," %1023[^\n]",item))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
34194       if ((err=std::sscanf(item," %d %d",&W,&H))<2) {
34195         if (!file) cimg::fclose(nfile);
34196         throw CImgIOException(_cimg_instance
34197                               "load_pfm() : WIDTH and HEIGHT fields are undefined in file '%s'.",
34198                               cimg_instance,
34199                               filename?filename:"(FILE*)");
34200       }
34201       if (err==2) {
34202         while ((err=std::fscanf(nfile," %1023[^\n]",item))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
34203         if (std::sscanf(item,"%lf",&scale)!=1)
34204           cimg::warn(_cimg_instance
34205                      "load_pfm() : SCALE field is undefined in file '%s'.",
34206                      cimg_instance,
34207                      filename?filename:"(FILE*)");
34208       }
34209       std::fgetc(nfile);
34210       const bool is_color = (pfm_type=='F'), is_inverted = (scale>0)!=cimg::endianness();
34211       if (is_color) {
34212         assign(W,H,1,3,0);
34213         CImg<floatT> buf(3*W);
34214         T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
34215         cimg_forY(*this,y) {
34216           cimg::fread(buf._data,3*W,nfile);
34217           if (is_inverted) cimg::invert_endianness(buf._data,3*W);
34218           const float *ptrs = buf._data;
34219           cimg_forX(*this,x) {
34220             *(ptr_r++) = (T)*(ptrs++);
34221             *(ptr_g++) = (T)*(ptrs++);
34222             *(ptr_b++) = (T)*(ptrs++);
34223           }
34224         }
34225       } else {
34226         assign(W,H,1,1,0);
34227         CImg<floatT> buf(W);
34228         T *ptrd = data(0,0,0,0);
34229         cimg_forY(*this,y) {
34230           cimg::fread(buf._data,W,nfile);
34231           if (is_inverted) cimg::invert_endianness(buf._data,W);
34232           const float *ptrs = buf._data;
34233           cimg_forX(*this,x) *(ptrd++) = (T)*(ptrs++);
34234         }
34235       }
34236       if (!file) cimg::fclose(nfile);
34237       return *this;
34238     }
34239 
34240     //! Load an image from a RGB file.
34241     CImg<T>& load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
34242       return _load_rgb(0,filename,dimw,dimh);
34243     }
34244 
34245     static CImg<T> get_load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
34246       return CImg<T>().load_rgb(filename,dimw,dimh);
34247     }
34248 
34249     //! Load an image from a RGB file.
34250     CImg<T>& load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
34251       return _load_rgb(file,0,dimw,dimh);
34252     }
34253 
34254     static CImg<T> get_load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
34255       return CImg<T>().load_rgb(file,dimw,dimh);
34256     }
34257 
34258     CImg<T>& _load_rgb(std::FILE *const file, const char *const filename, const unsigned int dimw, const unsigned int dimh) {
34259       if (!file && !filename)
34260         throw CImgArgumentException(_cimg_instance
34261                                     "load_rgb() : Specified filename is (null).",
34262                                     cimg_instance);
34263 
34264       if (!dimw || !dimh) return assign();
34265       const long cimg_iobuffer = 12*1024*1024;
34266       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
34267       CImg<ucharT> raw;
34268       assign(dimw,dimh,1,3);
34269       T
34270         *ptr_r = data(0,0,0,0),
34271         *ptr_g = data(0,0,0,1),
34272         *ptr_b = data(0,0,0,2);
34273       for (long to_read = (long)size(); to_read>0; ) {
34274         raw.assign(cimg::min(to_read,cimg_iobuffer));
34275         cimg::fread(raw._data,raw._width,nfile);
34276         to_read-=raw._width;
34277         const unsigned char *ptrs = raw._data;
34278         for (unsigned long off = raw._width/3UL; off; --off) {
34279           *(ptr_r++) = (T)*(ptrs++);
34280           *(ptr_g++) = (T)*(ptrs++);
34281           *(ptr_b++) = (T)*(ptrs++);
34282         }
34283       }
34284       if (!file) cimg::fclose(nfile);
34285       return *this;
34286     }
34287 
34288     //! Load an image from a RGBA file.
34289     CImg<T>& load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
34290       return _load_rgba(0,filename,dimw,dimh);
34291     }
34292 
34293     static CImg<T> get_load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
34294       return CImg<T>().load_rgba(filename,dimw,dimh);
34295     }
34296 
34297     //! Load an image from a RGBA file.
34298     CImg<T>& load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
34299       return _load_rgba(file,0,dimw,dimh);
34300     }
34301 
34302     static CImg<T> get_load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
34303       return CImg<T>().load_rgba(file,dimw,dimh);
34304     }
34305 
34306     CImg<T>& _load_rgba(std::FILE *const file, const char *const filename, const unsigned int dimw, const unsigned int dimh) {
34307       if (!file && !filename)
34308         throw CImgArgumentException(_cimg_instance
34309                                     "load_rgba() : Specified filename is (null).",
34310                                     cimg_instance);
34311 
34312       if (!dimw || !dimh) return assign();
34313       const long cimg_iobuffer = 12*1024*1024;
34314       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
34315       CImg<ucharT> raw;
34316       assign(dimw,dimh,1,4);
34317       T
34318         *ptr_r = data(0,0,0,0),
34319         *ptr_g = data(0,0,0,1),
34320         *ptr_b = data(0,0,0,2),
34321         *ptr_a = data(0,0,0,3);
34322       for (long to_read = (long)size(); to_read>0; ) {
34323         raw.assign(cimg::min(to_read,cimg_iobuffer));
34324         cimg::fread(raw._data,raw._width,nfile);
34325         to_read-=raw._width;
34326         const unsigned char *ptrs = raw._data;
34327         for (unsigned long off = raw._width/4UL; off; --off) {
34328           *(ptr_r++) = (T)*(ptrs++);
34329           *(ptr_g++) = (T)*(ptrs++);
34330           *(ptr_b++) = (T)*(ptrs++);
34331           *(ptr_a++) = (T)*(ptrs++);
34332         }
34333       }
34334       if (!file) cimg::fclose(nfile);
34335       return *this;
34336     }
34337 
34338     //! Load an image from a TIFF file.
34339     /**
34340        - libtiff support is enabled by defining the precompilation
34341         directive cimg_use_tif.
34342 
34343        - When libtiff is enabled, 2D and 3D (multipage) several
34344         channel per pixel are supported for
34345         char,uchar,short,ushort,float and double pixel type.
34346 
34347        - If cimg_use_tif is not defined at compilation time the
34348         function uses CImg<T>&load_other(const char*).
34349 
34350        \see CImg<T>& load_other(const char*)
34351        \see CImg<T>& save_tiff(const char*, const unsigned int)
34352      **/
34353     CImg<T>& load_tiff(const char *const filename,
34354                        const unsigned int first_frame=0, const unsigned int last_frame=~0U,
34355                        const unsigned int step_frame=1) {
34356       if (!filename)
34357         throw CImgArgumentException(_cimg_instance
34358                                     "load_tiff() : Specified filename is (null).",
34359                                     cimg_instance);
34360 
34361       const unsigned int
34362         nfirst_frame = first_frame<last_frame?first_frame:last_frame,
34363         nstep_frame = step_frame?step_frame:1;
34364       unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
34365 
34366 #ifndef cimg_use_tiff
34367       if (nfirst_frame || nlast_frame!=~0U || nstep_frame>1)
34368         throw CImgArgumentException(_cimg_instance
34369                                     "load_tiff() : Unable to read sub-images from file '%s' unless libtiff is enabled.",
34370                                     cimg_instance,
34371                                     filename);
34372       return load_other(filename);
34373 #else
34374       TIFF *tif = TIFFOpen(filename,"r");
34375       if (tif) {
34376         unsigned int nb_images = 0;
34377         do ++nb_images; while (TIFFReadDirectory(tif));
34378         if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images))
34379           cimg::warn(_cimg_instance
34380                      "load_tiff() : File '%s' contains %u image(s) while specified frame range is [%u,%u] (step %u).",
34381                      cimg_instance,
34382                      filename,nb_images,nfirst_frame,nlast_frame,nstep_frame);
34383 
34384         if (nfirst_frame>=nb_images) return assign();
34385         if (nlast_frame>=nb_images) nlast_frame = nb_images-1;
34386         TIFFSetDirectory(tif,0);
34387         CImg<T> frame;
34388         for (unsigned int l = nfirst_frame; l<=nlast_frame; l+=nstep_frame) {
34389           frame._load_tiff(tif,l);
34390           if (l==nfirst_frame) assign(frame._width,frame._height,1+(nlast_frame-nfirst_frame)/nstep_frame,frame._spectrum);
34391           if (frame._width>_width || frame._height>_height || frame._spectrum>_spectrum)
34392             resize(cimg::max(frame._width,_width),cimg::max(frame._height,_height),-100,cimg::max(frame._spectrum,_spectrum),0);
34393           draw_image(0,0,(l-nfirst_frame)/nstep_frame,frame);
34394         }
34395         TIFFClose(tif);
34396       } else throw CImgException(_cimg_instance
34397                                  "load_tiff() : Failed to open file '%s'.",
34398                                  cimg_instance,
34399                                  filename);
34400       return *this;
34401 #endif
34402     }
34403 
34404     static CImg<T> get_load_tiff(const char *const filename,
34405                                  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
34406                                  const unsigned int step_frame=1) {
34407       return CImg<T>().load_tiff(filename,first_frame,last_frame,step_frame);
34408     }
34409 
34410     // (Original contribution by Jerome Boulanger).
34411 #ifdef cimg_use_tiff
34412     template<typename t>
34413     void _load_tiff_tiled_contig(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) {
34414       t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif));
34415       if (buf) {
34416         for (unsigned int row = 0; row<ny; row+=th)
34417           for (unsigned int col = 0; col<nx; col+=tw) {
34418             if (TIFFReadTile(tif,buf,col,row,0,0)<0) {
34419               _TIFFfree(buf); TIFFClose(tif);
34420               throw CImgException(_cimg_instance
34421                                   "load_tiff() : Invalid tile in file '%s'.",
34422                                   cimg_instance,
34423                                   TIFFFileName(tif));
34424             }
34425             const t *ptr = buf;
34426             for (unsigned int rr = row; rr<cimg::min((unsigned int)(row+th),(unsigned int)ny); ++rr)
34427               for (unsigned int cc = col; cc<cimg::min((unsigned int)(col+tw),(unsigned int)nx); ++cc)
34428                 for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
34429                   (*this)(cc,rr,vv) = (T)(ptr[(rr-row)*th*samplesperpixel + (cc-col)*samplesperpixel + vv]);
34430           }
34431         _TIFFfree(buf);
34432       }
34433     }
34434 
34435     template<typename t>
34436     void _load_tiff_tiled_separate(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) {
34437       t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif));
34438       if (buf) {
34439         for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
34440           for (unsigned int row = 0; row<ny; row+=th)
34441             for (unsigned int col = 0; col<nx; col+=tw) {
34442               if (TIFFReadTile(tif,buf,col,row,0,vv)<0) {
34443                 _TIFFfree(buf); TIFFClose(tif);
34444                 throw CImgException(_cimg_instance
34445                                     "load_tiff() : Invalid tile in file '%s'.",
34446                                     cimg_instance,
34447                                     TIFFFileName(tif));
34448               }
34449               const t *ptr = buf;
34450               for (unsigned int rr = row; rr<cimg::min((unsigned int)(row+th),(unsigned int)ny); ++rr)
34451                 for (unsigned int cc = col; cc<cimg::min((unsigned int)(col+tw),(unsigned int)nx); ++cc)
34452                   (*this)(cc,rr,vv) = (T)*(ptr++);
34453             }
34454         _TIFFfree(buf);
34455       }
34456     }
34457 
34458     template<typename t>
34459     void _load_tiff_contig(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) {
34460       t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
34461       if (buf) {
34462         uint32 row, rowsperstrip = (uint32)-1;
34463         TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
34464         for (row = 0; row<ny; row+= rowsperstrip) {
34465           uint32 nrow = (row+rowsperstrip>ny?ny-row:rowsperstrip);
34466           tstrip_t strip = TIFFComputeStrip(tif, row, 0);
34467           if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) {
34468             _TIFFfree(buf); TIFFClose(tif);
34469             throw CImgException(_cimg_instance
34470                                 "load_tiff() : Invalid strip in file '%s'.",
34471                                 cimg_instance,
34472                                 TIFFFileName(tif));
34473           }
34474           const t *ptr = buf;
34475           for (unsigned int rr = 0; rr<nrow; ++rr)
34476             for (unsigned int cc = 0; cc<nx; ++cc)
34477               for (unsigned int vv = 0; vv<samplesperpixel; ++vv) (*this)(cc,row+rr,vv) = (T)*(ptr++);
34478         }
34479         _TIFFfree(buf);
34480       }
34481     }
34482 
34483     template<typename t>
34484     void _load_tiff_separate(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) {
34485       t *buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
34486       if (buf) {
34487         uint32 row, rowsperstrip = (uint32)-1;
34488         TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
34489         for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
34490           for (row = 0; row<ny; row+= rowsperstrip) {
34491             uint32 nrow = (row+rowsperstrip>ny?ny-row:rowsperstrip);
34492             tstrip_t strip = TIFFComputeStrip(tif, row, vv);
34493             if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) {
34494               _TIFFfree(buf); TIFFClose(tif);
34495               throw CImgException(_cimg_instance
34496                                   "load_tiff() : Invalid strip in file '%s'.",
34497                                   cimg_instance,
34498                                   TIFFFileName(tif));
34499             }
34500             const t *ptr = buf;
34501             for (unsigned int rr = 0;rr<nrow; ++rr)
34502               for (unsigned int cc = 0; cc<nx; ++cc)
34503                 (*this)(cc,row+rr,vv) = (T)*(ptr++);
34504           }
34505         _TIFFfree(buf);
34506       }
34507     }
34508 
34509     CImg<T>& _load_tiff(TIFF *const tif, const unsigned int directory) {
34510       if (!TIFFSetDirectory(tif,directory)) return assign();
34511       uint16 samplesperpixel, bitspersample;
34512       uint16 sampleformat = SAMPLEFORMAT_UINT;
34513       uint32 nx,ny;
34514       const char *const filename = TIFFFileName(tif);
34515       TIFFGetField(tif,TIFFTAG_IMAGEWIDTH,&nx);
34516       TIFFGetField(tif,TIFFTAG_IMAGELENGTH,&ny);
34517       TIFFGetField(tif,TIFFTAG_SAMPLESPERPIXEL,&samplesperpixel);
34518       TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sampleformat);
34519       TIFFGetFieldDefaulted(tif,TIFFTAG_BITSPERSAMPLE,&bitspersample);
34520       assign(nx,ny,1,samplesperpixel);
34521       if (bitspersample!=8 || !(samplesperpixel==3 || samplesperpixel==4)) {
34522         uint16 photo, config;
34523         TIFFGetField(tif,TIFFTAG_PLANARCONFIG,&config);
34524         TIFFGetField(tif,TIFFTAG_PHOTOMETRIC,&photo);
34525         if (TIFFIsTiled(tif)) {
34526           uint32 tw, th;
34527           TIFFGetField(tif,TIFFTAG_TILEWIDTH,&tw);
34528           TIFFGetField(tif,TIFFTAG_TILELENGTH,&th);
34529           if (config==PLANARCONFIG_CONTIG) switch (bitspersample) {
34530             case 8 : {
34531               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_contig<unsigned char>(tif,samplesperpixel,nx,ny,tw,th);
34532               else _load_tiff_tiled_contig<signed char>(tif,samplesperpixel,nx,ny,tw,th);
34533             } break;
34534             case 16 :
34535               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_contig<unsigned short>(tif,samplesperpixel,nx,ny,tw,th);
34536               else _load_tiff_tiled_contig<short>(tif,samplesperpixel,nx,ny,tw,th);
34537               break;
34538             case 32 :
34539               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_contig<unsigned int>(tif,samplesperpixel,nx,ny,tw,th);
34540               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_tiled_contig<int>(tif,samplesperpixel,nx,ny,tw,th);
34541               else _load_tiff_tiled_contig<float>(tif,samplesperpixel,nx,ny,tw,th);
34542               break;
34543             } else switch (bitspersample) {
34544             case 8 :
34545               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_separate<unsigned char>(tif,samplesperpixel,nx,ny,tw,th);
34546               else _load_tiff_tiled_separate<signed char>(tif,samplesperpixel,nx,ny,tw,th);
34547               break;
34548             case 16 :
34549               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_separate<unsigned short>(tif,samplesperpixel,nx,ny,tw,th);
34550               else _load_tiff_tiled_separate<short>(tif,samplesperpixel,nx,ny,tw,th);
34551               break;
34552             case 32 :
34553               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_separate<unsigned int>(tif,samplesperpixel,nx,ny,tw,th);
34554               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_tiled_separate<int>(tif,samplesperpixel,nx,ny,tw,th);
34555               else _load_tiff_tiled_separate<float>(tif,samplesperpixel,nx,ny,tw,th);
34556               break;
34557             }
34558         } else {
34559           if (config==PLANARCONFIG_CONTIG) switch (bitspersample) {
34560             case 8 :
34561               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned char>(tif,samplesperpixel,nx,ny);
34562               else _load_tiff_contig<signed char>(tif,samplesperpixel,nx,ny);
34563               break;
34564             case 16 :
34565               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned short>(tif,samplesperpixel,nx,ny);
34566               else _load_tiff_contig<short>(tif,samplesperpixel,nx,ny);
34567               break;
34568             case 32 :
34569               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned int>(tif,samplesperpixel,nx,ny);
34570               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig<int>(tif,samplesperpixel,nx,ny);
34571               else _load_tiff_contig<float>(tif,samplesperpixel,nx,ny);
34572               break;
34573             } else switch (bitspersample){
34574             case 8 :
34575               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned char>(tif,samplesperpixel,nx,ny);
34576               else _load_tiff_separate<signed char>(tif,samplesperpixel,nx,ny);
34577               break;
34578             case 16 :
34579               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned short>(tif,samplesperpixel,nx,ny);
34580               else _load_tiff_separate<short>(tif,samplesperpixel,nx,ny);
34581               break;
34582             case 32 :
34583               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned int>(tif,samplesperpixel,nx,ny);
34584               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate<int>(tif,samplesperpixel,nx,ny);
34585               else _load_tiff_separate<float>(tif,samplesperpixel,nx,ny);
34586               break;
34587             }
34588         }
34589       } else {
34590         uint32 *const raster = (uint32*)_TIFFmalloc(nx*ny*sizeof(uint32));
34591         if (!raster) {
34592           _TIFFfree(raster); TIFFClose(tif);
34593           throw CImgException(_cimg_instance
34594                               "load_tiff() : Failed to allocate memory (%s) for file '%s'.",
34595                               cimg_instance,
34596                               cimg::strbuffersize(nx*ny*sizeof(uint32)),filename);
34597         }
34598         TIFFReadRGBAImage(tif,nx,ny,raster,0);
34599         switch (samplesperpixel) {
34600         case 1 : {
34601           cimg_forXY(*this,x,y) (*this)(x,y) = (T)(float)((raster[nx*(ny-1-y)+x] + 128)/257);
34602         } break;
34603         case 3 : {
34604           cimg_forXY(*this,x,y) {
34605             (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny-1-y)+x]);
34606             (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny-1-y)+x]);
34607             (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny-1-y)+x]);
34608           }
34609         } break;
34610         case 4 : {
34611           cimg_forXY(*this,x,y) {
34612             (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny-1-y)+x]);
34613             (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny-1-y)+x]);
34614             (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny-1-y)+x]);
34615             (*this)(x,y,3) = (T)(float)TIFFGetA(raster[nx*(ny-1-y)+x]);
34616           }
34617         } break;
34618         }
34619         _TIFFfree(raster);
34620       }
34621       return *this;
34622     }
34623 #endif
34624 
34625     //! Load an image from a MINC2 file.
34626     // (Original code by Haz-Edine Assemlal).
34627     CImg<T>& load_minc2(const char *const filename) {
34628       if (!filename)
34629         throw CImgArgumentException(_cimg_instance
34630                                     "load_minc2() : Specified filename is (null).",
34631                                     cimg_instance);
34632 #ifndef cimg_use_minc2
34633       return load_other(filename);
34634 #else
34635 
34636       // Open the MINC2 volume in read-only access.
34637       mihandle_t hvol;
34638       int result = miopen_volume(filename,MI2_OPEN_RDWR,&hvol);
34639       if (result!=MI_NOERROR)
34640         throw CImgIOException(_cimg_instance
34641                               "load_minc2() : Invalid MINC2 format for file '%s'.",
34642                               cimg_instance,filename);
34643 
34644       mitype_t volume_data_type;
34645       result = miget_data_type(hvol,&volume_data_type);
34646 
34647       int slice_scaling_flag;
34648       result = miget_slice_scaling_flag(hvol,&slice_scaling_flag);
34649 
34650       double valid_max, valid_min;
34651       result = miget_volume_valid_range(hvol,&valid_max,&valid_min);
34652 
34653       miclass_t volume_data_class;
34654       result = miget_data_class(hvol,&volume_data_class);
34655 
34656       midimhandle_t *const hdims = (midimhandle_t*)std::malloc(4*sizeof(midimhandle_t));
34657       result = miget_volume_dimensions(hvol,MI_DIMCLASS_ANY,MI_DIMATTR_ALL,MI_DIMORDER_FILE,4,hdims);
34658 
34659       CImg<uintT> minc_dims(4,1,1,1,0);
34660       cimg_forX(minc_dims,d) {
34661         miboolean_t irregular;
34662         result = miget_dimension_sampling_flag(hdims[d],&irregular);
34663         if (irregular) {
34664           char* name;
34665           result = miget_dimension_name(hdims[d],&name);
34666           const CImg<charT> _name = CImg<charT>::string(name);
34667           mifree_name(name);
34668           throw CImgIOException(_cimg_instance
34669                                 "load_minc2() : Unsupported dimension (%s) detected in file '%s'.",
34670                                 cimg_instance,name,filename);
34671         }
34672       }
34673 
34674       result = miget_dimension_sizes(hdims,4,minc_dims.data());
34675       miflipping_t file_order;
34676       miflipping_t sign;
34677       result = miget_dimension_apparent_voxel_order(*hdims,&file_order,&sign);
34678       if (file_order)
34679         cimg::warn(_cimg_instance
34680                    "load_minc2() : Unsupported voxel order (%d) detected in file '%s'.",
34681                    cimg_instance,file_order,filename);
34682 
34683       CImg<intT> permutations, permutations_inv;
34684       const CImg<uintT>
34685         minc_dims_sort = minc_dims.get_sort(permutations,true),
34686         minc_dims_sort_inv = minc_dims.get_sort(permutations_inv,false);
34687 
34688       int nb_useful_dims = 0;
34689       if (minc_dims(0)!=0) ++nb_useful_dims;
34690       if (minc_dims(1)!=0) ++nb_useful_dims;
34691       if (minc_dims(2)!=0) ++nb_useful_dims;
34692       if (minc_dims(3)!=0) ++nb_useful_dims;
34693 
34694       // BUG in MINC2 ? count as to be equal to 4, not to nb_useful_dims.
34695       CImg<ulongT> count(4,1,1,1,1);
34696       unsigned int c = 0;
34697       cimg_foroff(minc_dims,d) if (minc_dims(d)) count(c++) = minc_dims(d);
34698       assign(!minc_dims(0)?1:minc_dims(0),
34699              !minc_dims(1)?1:minc_dims(1),
34700              !minc_dims(2)?1:minc_dims(2),
34701              !minc_dims(3)?1:minc_dims(3));
34702       if (nb_useful_dims==2) permute_axes("yxzc");
34703       else if (nb_useful_dims==3) permute_axes("zyxc");
34704       else if (nb_useful_dims==4) permute_axes("czyx");
34705 
34706       // Read the entire file in one operation.
34707       CImg<ulongT> start(4,1,1,1,0);
34708       if (slice_scaling_flag) result = miget_real_value_hyperslab(hvol,MI_TYPE_FLOAT,start.data(),count.data(),data());
34709       else result = miget_voxel_value_hyperslab(hvol,MI_TYPE_FLOAT,start.data(),count.data(),data());
34710 
34711       // Close the MINC2 volume.
34712       result = mifree_dimension_handle(*hdims);
34713       miclose_volume(hvol);
34714       return mirror('y');
34715 #endif
34716     }
34717 
34718     //! Load an image from an ANALYZE7.5/NIFTI file.
34719     CImg<T>& load_analyze(const char *const filename, float *const voxsize=0) {
34720       return _load_analyze(0,filename,voxsize);
34721     }
34722 
34723     static CImg<T> get_load_analyze(const char *const filename, float *const voxsize=0) {
34724       return CImg<T>().load_analyze(filename,voxsize);
34725     }
34726 
34727     //! Load an image from an ANALYZE7.5/NIFTI file.
34728     CImg<T>& load_analyze(std::FILE *const file, float *const voxsize=0) {
34729       return _load_analyze(file,0,voxsize);
34730     }
34731 
34732     static CImg<T> get_load_analyze(std::FILE *const file, float *const voxsize=0) {
34733       return CImg<T>().load_analyze(file,voxsize);
34734     }
34735 
34736     CImg<T>& _load_analyze(std::FILE *const file, const char *const filename, float *const voxsize=0) {
34737       if (!file && !filename)
34738         throw CImgArgumentException(_cimg_instance
34739                                     "load_analyze() : Specified filename is (null).",
34740                                     cimg_instance);
34741 
34742       std::FILE *nfile_header = 0, *nfile = 0;
34743       if (!file) {
34744         char body[1024] = { 0 };
34745         const char *const ext = cimg::split_filename(filename,body);
34746         if (!cimg::strcasecmp(ext,"hdr")) { // File is an Analyze header file.
34747           nfile_header = cimg::fopen(filename,"rb");
34748           std::sprintf(body + std::strlen(body),".img");
34749           nfile = cimg::fopen(body,"rb");
34750         } else if (!cimg::strcasecmp(ext,"img")) { // File is an Analyze data file.
34751           nfile = cimg::fopen(filename,"rb");
34752           std::sprintf(body + std::strlen(body),".hdr");
34753           nfile_header = cimg::fopen(body,"rb");
34754         } else nfile_header = nfile = cimg::fopen(filename,"rb"); // File is a Niftii file.
34755       } else nfile_header = nfile = file; // File is a Niftii file.
34756       if (!nfile || !nfile_header)
34757         throw CImgIOException(_cimg_instance
34758                               "load_analyze() : Invalid Analyze7.5 or NIFTI header in file '%s'.",
34759                               cimg_instance,
34760                               filename?filename:"(FILE*)");
34761 
34762       // Read header.
34763       bool endian = false;
34764       unsigned int header_size;
34765       cimg::fread(&header_size,1,nfile_header);
34766       if (!header_size)
34767         throw CImgIOException(_cimg_instance
34768                               "load_analyze() : Invalid zero-sized header in file '%s'.",
34769                               cimg_instance,
34770                               filename?filename:"(FILE*)");
34771 
34772       if (header_size>=4096) { endian = true; cimg::invert_endianness(header_size); }
34773       unsigned char *const header = new unsigned char[header_size];
34774       cimg::fread(header+4,header_size-4,nfile_header);
34775       if (!file && nfile_header!=nfile) cimg::fclose(nfile_header);
34776       if (endian) {
34777         cimg::invert_endianness((short*)(header+40),5);
34778         cimg::invert_endianness((short*)(header+70),1);
34779         cimg::invert_endianness((short*)(header+72),1);
34780         cimg::invert_endianness((float*)(header+76),4);
34781         cimg::invert_endianness((float*)(header+112),1);
34782       }
34783       unsigned short *dim = (unsigned short*)(header+40), dimx = 1, dimy = 1, dimz = 1, dimv = 1;
34784       if (!dim[0])
34785         cimg::warn(_cimg_instance
34786                    "load_analyze() : File '%s' defines an image with zero dimensions.",
34787                    cimg_instance,
34788                    filename?filename:"(FILE*)");
34789 
34790       if (dim[0]>4)
34791         cimg::warn(_cimg_instance
34792                    "load_analyze() : File '%s' defines an image with %u dimensions, reading only the 4 first.",
34793                    cimg_instance,
34794                    filename?filename:"(FILE*)",dim[0]);
34795 
34796       if (dim[0]>=1) dimx = dim[1];
34797       if (dim[0]>=2) dimy = dim[2];
34798       if (dim[0]>=3) dimz = dim[3];
34799       if (dim[0]>=4) dimv = dim[4];
34800       float scalefactor = *(float*)(header+112); if (scalefactor==0) scalefactor=1;
34801       const unsigned short datatype = *(short*)(header+70);
34802       if (voxsize) {
34803         const float *vsize = (float*)(header+76);
34804         voxsize[0] = vsize[1]; voxsize[1] = vsize[2]; voxsize[2] = vsize[3];
34805       }
34806       delete[] header;
34807 
34808       // Read pixel data.
34809       assign(dimx,dimy,dimz,dimv);
34810       switch (datatype) {
34811       case 2 : {
34812         unsigned char *const buffer = new unsigned char[dimx*dimy*dimz*dimv];
34813         cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile);
34814         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
34815         delete[] buffer;
34816       } break;
34817       case 4 : {
34818         short *const buffer = new short[dimx*dimy*dimz*dimv];
34819         cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile);
34820         if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv);
34821         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
34822         delete[] buffer;
34823       } break;
34824       case 8 : {
34825         int *const buffer = new int[dimx*dimy*dimz*dimv];
34826         cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile);
34827         if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv);
34828         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
34829         delete[] buffer;
34830       } break;
34831       case 16 : {
34832         float *const buffer = new float[dimx*dimy*dimz*dimv];
34833         cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile);
34834         if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv);
34835         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
34836         delete[] buffer;
34837       } break;
34838       case 64 : {
34839         double *const buffer = new double[dimx*dimy*dimz*dimv];
34840         cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile);
34841         if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv);
34842         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
34843         delete[] buffer;
34844       } break;
34845       default :
34846         if (!file) cimg::fclose(nfile);
34847         throw CImgIOException(_cimg_instance
34848                               "load_analyze() : Unable to load datatype %d in file '%s'",
34849                               cimg_instance,
34850                               datatype,filename?filename:"(FILE*)");
34851       }
34852       if (!file) cimg::fclose(nfile);
34853       return *this;
34854     }
34855 
34856     //! Load an image (list) from a .cimg file.
34857     CImg<T>& load_cimg(const char *const filename, const char axis='z', const float align=0) {
34858       CImgList<T> list;
34859       list.load_cimg(filename);
34860       if (list._width==1) return list[0].move_to(*this);
34861       return assign(list.get_append(axis,align));
34862     }
34863 
34864     static CImg<T> get_load_cimg(const char *const filename, const char axis='z', const float align=0) {
34865       return CImg<T>().load_cimg(filename,axis,align);
34866     }
34867 
34868     //! Load an image (list) from a .cimg file.
34869     CImg<T>& load_cimg(std::FILE *const file, const char axis='z', const float align=0) {
34870       CImgList<T> list;
34871       list.load_cimg(file);
34872       if (list._width==1) return list[0].move_to(*this);
34873       return assign(list.get_append(axis,align));
34874     }
34875 
34876     static CImg<T> get_load_cimg(std::FILE *const file, const char axis='z', const float align=0) {
34877       return CImg<T>().load_cimg(file,axis,align);
34878     }
34879 
34880     //! Load a sub-image (list) from a .cimg file.
34881     CImg<T>& load_cimg(const char *const filename,
34882                        const unsigned int n0, const unsigned int n1,
34883                        const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
34884                        const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1,
34885                        const char axis='z', const float align=0) {
34886       CImgList<T> list;
34887       list.load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
34888       if (list._width==1) return list[0].move_to(*this);
34889       return assign(list.get_append(axis,align));
34890     }
34891 
34892     static CImg<T> get_load_cimg(const char *const filename,
34893                                  const unsigned int n0, const unsigned int n1,
34894                                  const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
34895                                  const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1,
34896                                  const char axis='z', const float align=0) {
34897       return CImg<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align);
34898     }
34899 
34900     //! Load a sub-image (list) from a non-compressed .cimg file.
34901     CImg<T>& load_cimg(std::FILE *const file,
34902                        const unsigned int n0, const unsigned int n1,
34903                        const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
34904                        const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1,
34905                        const char axis='z', const float align=0) {
34906       CImgList<T> list;
34907       list.load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
34908       if (list._width==1) return list[0].move_to(*this);
34909       return assign(list.get_append(axis,align));
34910     }
34911 
34912     static CImg<T> get_load_cimg(std::FILE *const file,
34913                                  const unsigned int n0, const unsigned int n1,
34914                                  const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
34915                                  const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1,
34916                                  const char axis='z', const float align=0) {
34917       return CImg<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align);
34918     }
34919 
34920     //! Load an image from an INRIMAGE-4 file.
34921     CImg<T>& load_inr(const char *const filename, float *const voxsize=0) {
34922       return _load_inr(0,filename,voxsize);
34923     }
34924 
34925     static CImg<T> get_load_inr(const char *const filename, float *const voxsize=0) {
34926       return CImg<T>().load_inr(filename,voxsize);
34927     }
34928 
34929     //! Load an image from an INRIMAGE-4 file.
34930     CImg<T>& load_inr(std::FILE *const file, float *const voxsize=0) {
34931       return _load_inr(file,0,voxsize);
34932     }
34933 
34934     static CImg<T> get_load_inr(std::FILE *const file, float *voxsize=0) {
34935       return CImg<T>().load_inr(file,voxsize);
34936     }
34937 
34938     // Load an image from an INRIMAGE-4 file (internal).
34939     static void _load_inr_header(std::FILE *file, int out[8], float *const voxsize) {
34940       char item[1024] = { 0 }, tmp1[64] = { 0 }, tmp2[64] = { 0 };
34941       out[0] = std::fscanf(file,"%63s",item);
34942       out[0] = out[1] = out[2] = out[3] = out[5] = 1; out[4] = out[6] = out[7] = -1;
34943       if(cimg::strncasecmp(item,"#INRIMAGE-4#{",13)!=0)
34944         throw CImgIOException("CImg<%s>::load_inr() : INRIMAGE-4 header not found.",
34945                               pixel_type());
34946 
34947       while (std::fscanf(file," %63[^\n]%*c",item)!=EOF && std::strncmp(item,"##}",3)) {
34948         std::sscanf(item," XDIM%*[^0-9]%d",out);
34949         std::sscanf(item," YDIM%*[^0-9]%d",out+1);
34950         std::sscanf(item," ZDIM%*[^0-9]%d",out+2);
34951         std::sscanf(item," VDIM%*[^0-9]%d",out+3);
34952         std::sscanf(item," PIXSIZE%*[^0-9]%d",out+6);
34953         if (voxsize) {
34954           std::sscanf(item," VX%*[^0-9.+-]%f",voxsize);
34955           std::sscanf(item," VY%*[^0-9.+-]%f",voxsize+1);
34956           std::sscanf(item," VZ%*[^0-9.+-]%f",voxsize+2);
34957         }
34958         if (std::sscanf(item," CPU%*[ =]%s",tmp1)) out[7]=cimg::strncasecmp(tmp1,"sun",3)?0:1;
34959         switch (std::sscanf(item," TYPE%*[ =]%s %s",tmp1,tmp2)) {
34960         case 0 : break;
34961         case 2 : out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0; std::strncpy(tmp1,tmp2,sizeof(tmp1)-1);
34962         case 1 :
34963           if (!cimg::strncasecmp(tmp1,"int",3)   || !cimg::strncasecmp(tmp1,"fixed",5))  out[4] = 0;
34964           if (!cimg::strncasecmp(tmp1,"float",5) || !cimg::strncasecmp(tmp1,"double",6)) out[4] = 1;
34965           if (!cimg::strncasecmp(tmp1,"packed",6))                                       out[4] = 2;
34966           if (out[4]>=0) break;
34967         default :
34968           throw CImgIOException("CImg<%s>::load_inr() : Invalid pixel type '%s' defined in header.",
34969                                 pixel_type(),
34970                                 tmp2);
34971         }
34972       }
34973       if(out[0]<0 || out[1]<0 || out[2]<0 || out[3]<0)
34974         throw CImgIOException("CImg<%s>::load_inr() : Invalid dimensions (%d,%d,%d,%d) defined in header.",
34975                               pixel_type(),
34976                               out[0],out[1],out[2],out[3]);
34977       if(out[4]<0 || out[5]<0)
34978         throw CImgIOException("CImg<%s>::load_inr() : Incomplete pixel type defined in header.",
34979                               pixel_type());
34980       if(out[6]<0)
34981         throw CImgIOException("CImg<%s>::load_inr() : Incomplete PIXSIZE field defined in header.",
34982                               pixel_type());
34983       if(out[7]<0)
34984         throw CImgIOException("CImg<%s>::load_inr() : Big/Little Endian coding type undefined in header.",
34985                               pixel_type());
34986     }
34987 
34988     CImg<T>& _load_inr(std::FILE *const file, const char *const filename, float *const voxsize) {
34989 #define _cimg_load_inr_case(Tf,sign,pixsize,Ts) \
34990      if (!loaded && fopt[6]==pixsize && fopt[4]==Tf && fopt[5]==sign) { \
34991         Ts *xval, *const val = new Ts[fopt[0]*fopt[3]]; \
34992         cimg_forYZ(*this,y,z) { \
34993             cimg::fread(val,fopt[0]*fopt[3],nfile); \
34994             if (fopt[7]!=endian) cimg::invert_endianness(val,fopt[0]*fopt[3]); \
34995             xval = val; cimg_forX(*this,x) cimg_forC(*this,c) (*this)(x,y,z,c) = (T)*(xval++); \
34996           } \
34997         delete[] val; \
34998         loaded = true; \
34999       }
35000 
35001       if (!file && !filename)
35002         throw CImgArgumentException(_cimg_instance
35003                                     "load_inr() : Specified filename is (null).",
35004                                     cimg_instance);
35005 
35006       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
35007       int fopt[8], endian=cimg::endianness()?1:0;
35008       bool loaded = false;
35009       if (voxsize) voxsize[0]=voxsize[1]=voxsize[2]=1;
35010       _load_inr_header(nfile,fopt,voxsize);
35011       assign(fopt[0],fopt[1],fopt[2],fopt[3]);
35012       _cimg_load_inr_case(0,0,8,unsigned char);
35013       _cimg_load_inr_case(0,1,8,char);
35014       _cimg_load_inr_case(0,0,16,unsigned short);
35015       _cimg_load_inr_case(0,1,16,short);
35016       _cimg_load_inr_case(0,0,32,unsigned int);
35017       _cimg_load_inr_case(0,1,32,int);
35018       _cimg_load_inr_case(1,0,32,float);
35019       _cimg_load_inr_case(1,1,32,float);
35020       _cimg_load_inr_case(1,0,64,double);
35021       _cimg_load_inr_case(1,1,64,double);
35022       if (!loaded) {
35023         if (!file) cimg::fclose(nfile);
35024         throw CImgIOException(_cimg_instance
35025                               "load_inr() : Unknown pixel type defined in file '%s'.",
35026                               cimg_instance,
35027                               filename?filename:"(FILE*)");
35028       }
35029       if (!file) cimg::fclose(nfile);
35030       return *this;
35031     }
35032 
35033     //! Load an image from a EXR file.
35034     CImg<T>& load_exr(const char *const filename) {
35035       if (!filename)
35036         throw CImgArgumentException(_cimg_instance
35037                                     "load_exr() : Specified filename is (null).",
35038                                     cimg_instance);
35039 
35040 #ifndef cimg_use_openexr
35041       return load_other(filename);
35042 #else
35043       Imf::RgbaInputFile file(filename);
35044       Imath::Box2i dw = file.dataWindow();
35045       const int
35046         inwidth = dw.max.x - dw.min.x + 1,
35047         inheight = dw.max.y - dw.min.y + 1;
35048       Imf::Array2D<Imf::Rgba> pixels;
35049       pixels.resizeErase(inheight,inwidth);
35050       file.setFrameBuffer(&pixels[0][0] - dw.min.x - dw.min.y*inwidth, 1, inwidth);
35051       file.readPixels(dw.min.y, dw.max.y);
35052       assign(inwidth,inheight,1,4);
35053       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);
35054       cimg_forXY(*this,x,y) {
35055         *(ptr_r++) = (T)pixels[y][x].r;
35056         *(ptr_g++) = (T)pixels[y][x].g;
35057         *(ptr_b++) = (T)pixels[y][x].b;
35058         *(ptr_a++) = (T)pixels[y][x].a;
35059       }
35060       return *this;
35061 #endif
35062     }
35063 
35064     static CImg<T> get_load_exr(const char *const filename) {
35065       return CImg<T>().load_exr(filename);
35066     }
35067 
35068     //! Load an image from a PANDORE file.
35069     CImg<T>& load_pandore(const char *const filename) {
35070       return _load_pandore(0,filename);
35071     }
35072 
35073     static CImg<T> get_load_pandore(const char *const filename) {
35074       return CImg<T>().load_pandore(filename);
35075     }
35076 
35077     //! Load an image from a PANDORE file.
35078     CImg<T>& load_pandore(std::FILE *const file) {
35079       return _load_pandore(file,0);
35080     }
35081 
35082     static CImg<T> get_load_pandore(std::FILE *const file) {
35083       return CImg<T>().load_pandore(file);
35084     }
35085 
35086     CImg<T>& _load_pandore(std::FILE *const file, const char *const filename) {
35087 #define __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,ndim,stype) \
35088         cimg::fread(dims,nbdim,nfile); \
35089         if (endian) cimg::invert_endianness(dims,nbdim); \
35090         assign(nwidth,nheight,ndepth,ndim); \
35091         const unsigned int siz = size(); \
35092         stype *buffer = new stype[siz]; \
35093         cimg::fread(buffer,siz,nfile); \
35094         if (endian) cimg::invert_endianness(buffer,siz); \
35095         T *ptrd = _data; \
35096         cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); \
35097         buffer-=siz; \
35098         delete[] buffer
35099 
35100 #define _cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1,stype2,stype3,ltype) { \
35101         if (sizeof(stype1)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1); } \
35102         else if (sizeof(stype2)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype2); } \
35103         else if (sizeof(stype3)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype3); } \
35104         else throw CImgIOException(_cimg_instance \
35105                                    "load_pandore() : Unknown pixel datatype in file '%s'.", \
35106                                    cimg_instance, \
35107                                    filename?filename:"(FILE*)"); }
35108 
35109       if (!file && !filename)
35110         throw CImgArgumentException(_cimg_instance
35111                                     "load_pandore() : Specified filename is (null).",
35112                                     cimg_instance);
35113 
35114       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
35115       char header[32] = { 0 };
35116       cimg::fread(header,12,nfile);
35117       if (cimg::strncasecmp("PANDORE",header,7)) {
35118         if (!file) cimg::fclose(nfile);
35119         throw CImgIOException(_cimg_instance
35120                               "load_pandore() : PANDORE header not found in file '%s'.",
35121                               cimg_instance,
35122                               filename?filename:"(FILE*)");
35123       }
35124       unsigned int imageid, dims[8] = { 0 };
35125       cimg::fread(&imageid,1,nfile);
35126       const bool endian = (imageid>255);
35127       if (endian) cimg::invert_endianness(imageid);
35128       cimg::fread(header,20,nfile);
35129 
35130       switch (imageid) {
35131       case 2: _cimg_load_pandore_case(2,dims[1],1,1,1,unsigned char,unsigned char,unsigned char,1); break;
35132       case 3: _cimg_load_pandore_case(2,dims[1],1,1,1,long,int,short,4); break;
35133       case 4: _cimg_load_pandore_case(2,dims[1],1,1,1,double,float,float,4); break;
35134       case 5: _cimg_load_pandore_case(3,dims[2],dims[1],1,1,unsigned char,unsigned char,unsigned char,1); break;
35135       case 6: _cimg_load_pandore_case(3,dims[2],dims[1],1,1,long,int,short,4); break;
35136       case 7: _cimg_load_pandore_case(3,dims[2],dims[1],1,1,double,float,float,4); break;
35137       case 8: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,unsigned char,unsigned char,unsigned char,1); break;
35138       case 9: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,long,int,short,4); break;
35139       case 10: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,double,float,float,4); break;
35140       case 11 : { // Region 1d
35141         cimg::fread(dims,3,nfile);
35142         if (endian) cimg::invert_endianness(dims,3);
35143         assign(dims[1],1,1,1);
35144         const unsigned siz = size();
35145         if (dims[2]<256) {
35146           unsigned char *buffer = new unsigned char[siz];
35147           cimg::fread(buffer,siz,nfile);
35148           T *ptrd = _data;
35149           cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
35150           buffer-=siz;
35151           delete[] buffer;
35152         } else {
35153           if (dims[2]<65536) {
35154             unsigned short *buffer = new unsigned short[siz];
35155             cimg::fread(buffer,siz,nfile);
35156             if (endian) cimg::invert_endianness(buffer,siz);
35157             T *ptrd = _data;
35158             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
35159             buffer-=siz;
35160             delete[] buffer;
35161           } else {
35162             unsigned int *buffer = new unsigned int[siz];
35163             cimg::fread(buffer,siz,nfile);
35164             if (endian) cimg::invert_endianness(buffer,siz);
35165             T *ptrd = _data;
35166             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
35167             buffer-=siz;
35168             delete[] buffer;
35169           }
35170         }
35171       }
35172         break;
35173       case 12 : { // Region 2d
35174         cimg::fread(dims,4,nfile);
35175         if (endian) cimg::invert_endianness(dims,4);
35176         assign(dims[2],dims[1],1,1);
35177         const unsigned int siz = size();
35178         if (dims[3]<256) {
35179           unsigned char *buffer = new unsigned char[siz];
35180           cimg::fread(buffer,siz,nfile);
35181           T *ptrd = _data;
35182           cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
35183           buffer-=siz;
35184           delete[] buffer;
35185         } else {
35186           if (dims[3]<65536) {
35187             unsigned short *buffer = new unsigned short[siz];
35188             cimg::fread(buffer,siz,nfile);
35189             if (endian) cimg::invert_endianness(buffer,siz);
35190             T *ptrd = _data;
35191             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
35192             buffer-=siz;
35193             delete[] buffer;
35194           } else {
35195             unsigned long *buffer = new unsigned long[siz];
35196             cimg::fread(buffer,siz,nfile);
35197             if (endian) cimg::invert_endianness(buffer,siz);
35198             T *ptrd = _data;
35199             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
35200             buffer-=siz;
35201             delete[] buffer;
35202           }
35203         }
35204       }
35205         break;
35206       case 13 : { // Region 3d
35207         cimg::fread(dims,5,nfile);
35208         if (endian) cimg::invert_endianness(dims,5);
35209         assign(dims[3],dims[2],dims[1],1);
35210         const unsigned int siz = size();
35211         if (dims[4]<256) {
35212           unsigned char *buffer = new unsigned char[siz];
35213           cimg::fread(buffer,siz,nfile);
35214           T *ptrd = _data;
35215           cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
35216           buffer-=siz;
35217           delete[] buffer;
35218         } else {
35219           if (dims[4]<65536) {
35220             unsigned short *buffer = new unsigned short[siz];
35221             cimg::fread(buffer,siz,nfile);
35222             if (endian) cimg::invert_endianness(buffer,siz);
35223             T *ptrd = _data;
35224             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
35225             buffer-=siz;
35226             delete[] buffer;
35227           } else {
35228             unsigned int *buffer = new unsigned int[siz];
35229             cimg::fread(buffer,siz,nfile);
35230             if (endian) cimg::invert_endianness(buffer,siz);
35231             T *ptrd = _data;
35232             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
35233             buffer-=siz;
35234             delete[] buffer;
35235           }
35236         }
35237       }
35238         break;
35239       case 16: _cimg_load_pandore_case(4,dims[2],dims[1],1,3,unsigned char,unsigned char,unsigned char,1); break;
35240       case 17: _cimg_load_pandore_case(4,dims[2],dims[1],1,3,long,int,short,4); break;
35241       case 18: _cimg_load_pandore_case(4,dims[2],dims[1],1,3,double,float,float,4); break;
35242       case 19: _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,unsigned char,unsigned char,unsigned char,1); break;
35243       case 20: _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,long,int,short,4); break;
35244       case 21: _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,double,float,float,4); break;
35245       case 22: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned char,unsigned char,unsigned char,1); break;
35246       case 23: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],long,int,short,4);
35247       case 24: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned long,unsigned int,unsigned short,4); break;
35248       case 25: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],double,float,float,4); break;
35249       case 26: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned char,unsigned char,unsigned char,1); break;
35250       case 27: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],long,int,short,4); break;
35251       case 28: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned long,unsigned int,unsigned short,4); break;
35252       case 29: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],double,float,float,4); break;
35253       case 30: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned char,unsigned char,unsigned char,1); break;
35254       case 31: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],long,int,short,4); break;
35255       case 32: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned long,unsigned int,unsigned short,4); break;
35256       case 33: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],double,float,float,4); break;
35257       case 34 : { // Points 1d
35258         int ptbuf[4] = { 0 };
35259         cimg::fread(ptbuf,1,nfile);
35260         if (endian) cimg::invert_endianness(ptbuf,1);
35261         assign(1); (*this)(0) = (T)ptbuf[0];
35262       } break;
35263       case 35 : { // Points 2d
35264         int ptbuf[4] = { 0 };
35265         cimg::fread(ptbuf,2,nfile);
35266         if (endian) cimg::invert_endianness(ptbuf,2);
35267         assign(2); (*this)(0) = (T)ptbuf[1]; (*this)(1) = (T)ptbuf[0];
35268       } break;
35269       case 36 : { // Points 3d
35270         int ptbuf[4] = { 0 };
35271         cimg::fread(ptbuf,3,nfile);
35272         if (endian) cimg::invert_endianness(ptbuf,3);
35273         assign(3); (*this)(0) = (T)ptbuf[2]; (*this)(1) = (T)ptbuf[1]; (*this)(2) = (T)ptbuf[0];
35274       } break;
35275       default :
35276         if (!file) cimg::fclose(nfile);
35277         throw CImgIOException(_cimg_instance
35278                               "load_pandore() : Unable to load data with ID_type %u in file '%s'.",
35279                               cimg_instance,
35280                               imageid,filename?filename:"(FILE*)");
35281       }
35282       if (!file) cimg::fclose(nfile);
35283       return *this;
35284     }
35285 
35286     //! Load an image from a PAR-REC (Philips) file.
35287     CImg<T>& load_parrec(const char *const filename, const char axis='c', const float align=0) {
35288       CImgList<T> list;
35289       list.load_parrec(filename);
35290       if (list._width==1) return list[0].move_to(*this);
35291       return assign(list.get_append(axis,align));
35292     }
35293 
35294     static CImg<T> get_load_parrec(const char *const filename, const char axis='c', const float align=0) {
35295       return CImg<T>().load_parrec(filename,axis,align);
35296     }
35297 
35298     //! Load an image from a .RAW file.
35299     CImg<T>& load_raw(const char *const filename,
35300                       const unsigned int sizex=0, const unsigned int sizey=1,
35301                       const unsigned int sizez=1, const unsigned int sizev=1,
35302                       const bool multiplexed=false, const bool invert_endianness=false) {
35303       return _load_raw(0,filename,sizex,sizey,sizez,sizev,multiplexed,invert_endianness);
35304     }
35305 
35306     static CImg<T> get_load_raw(const char *const filename,
35307                                 const unsigned int sizex=0, const unsigned int sizey=1,
35308                                 const unsigned int sizez=1, const unsigned int sizev=1,
35309                                 const bool multiplexed=false, const bool invert_endianness=false) {
35310       return CImg<T>().load_raw(filename,sizex,sizey,sizez,sizev,multiplexed,invert_endianness);
35311     }
35312 
35313     //! Load an image from a .RAW file.
35314     CImg<T>& load_raw(std::FILE *const file,
35315                       const unsigned int sizex=0, const unsigned int sizey=1,
35316                       const unsigned int sizez=1, const unsigned int sizev=1,
35317                       const bool multiplexed=false, const bool invert_endianness=false) {
35318       return _load_raw(file,0,sizex,sizey,sizez,sizev,multiplexed,invert_endianness);
35319     }
35320 
35321     static CImg<T> get_load_raw(std::FILE *const file,
35322                                 const unsigned int sizex=0, const unsigned int sizey=1,
35323                                 const unsigned int sizez=1, const unsigned int sizev=1,
35324                                 const bool multiplexed=false, const bool invert_endianness=false) {
35325       return CImg<T>().load_raw(file,sizex,sizey,sizez,sizev,multiplexed,invert_endianness);
35326     }
35327 
35328     CImg<T>& _load_raw(std::FILE *const file, const char *const filename,
35329                        const unsigned int sizex, const unsigned int sizey,
35330                        const unsigned int sizez, const unsigned int sizev,
35331                        const bool multiplexed, const bool invert_endianness) {
35332       if (!file && !filename)
35333         throw CImgArgumentException(_cimg_instance
35334                                     "load_raw() : Specified filename is (null).",
35335                                     cimg_instance);
35336       unsigned int siz = sizex*sizey*sizez*sizev, _sizex = sizex, _sizey = sizey, _sizez = sizez, _sizev = sizev;
35337       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
35338       if (!siz) {  // Retrieve file size.
35339         const long fpos = std::ftell(nfile);
35340         std::fseek(nfile,0,SEEK_END);
35341         siz = _sizey = (unsigned int)std::ftell(nfile)/sizeof(T);
35342         _sizex = _sizez = _sizev = 1;
35343         std::fseek(nfile,fpos,SEEK_SET);
35344       }
35345       assign(_sizex,_sizey,_sizez,_sizev,0);
35346       if (!multiplexed || sizev==1) {
35347         cimg::fread(_data,siz,nfile);
35348         if (invert_endianness) cimg::invert_endianness(_data,siz);
35349       } else {
35350         CImg<T> buf(1,1,1,_sizev);
35351         cimg_forXYZ(*this,x,y,z) {
35352           cimg::fread(buf._data,_sizev,nfile);
35353           if (invert_endianness) cimg::invert_endianness(buf._data,_sizev);
35354           set_vector_at(buf,x,y,z);
35355         }
35356       }
35357       if (!file) cimg::fclose(nfile);
35358       return *this;
35359     }
35360 
35361     //! Load a video sequence using FFMPEG av's libraries.
35362     CImg<T>& load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
35363                          const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false,
35364                          const char axis='z', const float align=0) {
35365       return get_load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format,resume,axis,align).move_to(*this);
35366     }
35367 
35368     static CImg<T> get_load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
35369                                    const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false,
35370                                    const char axis='z', const float align=0) {
35371       return CImgList<T>().load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format,resume).get_append(axis,align);
35372     }
35373 
35374     //! Load an image sequence from a YUV file.
35375     CImg<T>& load_yuv(const char *const filename,
35376                       const unsigned int sizex, const unsigned int sizey=1,
35377                       const unsigned int first_frame=0, const unsigned int last_frame=~0U,
35378                       const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z', const float align=0) {
35379       return get_load_yuv(filename,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb,axis,align).move_to(*this);
35380     }
35381 
35382     static CImg<T> get_load_yuv(const char *const filename,
35383                                 const unsigned int sizex, const unsigned int sizey=1,
35384                                 const unsigned int first_frame=0, const unsigned int last_frame=~0U,
35385                                 const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z', const float align=0) {
35386       return CImgList<T>().load_yuv(filename,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb).get_append(axis,align);
35387     }
35388 
35389     //! Load an image sequence from a YUV file.
35390     CImg<T>& load_yuv(std::FILE *const file,
35391                       const unsigned int sizex, const unsigned int sizey=1,
35392                       const unsigned int first_frame=0, const unsigned int last_frame=~0U,
35393                       const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z', const float align=0) {
35394       return get_load_yuv(file,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb,axis,align).move_to(*this);
35395     }
35396 
35397     static CImg<T> get_load_yuv(std::FILE *const file,
35398                                 const unsigned int sizex, const unsigned int sizey=1,
35399                                 const unsigned int first_frame=0, const unsigned int last_frame=~0U,
35400                                 const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z', const float align=0) {
35401       return CImgList<T>().load_yuv(file,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb).get_append(axis,align);
35402     }
35403 
35404     //! Load a 3d object from a .OFF file.
35405     template<typename tf, typename tc>
35406     CImg<T>& load_off(const char *const filename, CImgList<tf>& primitives, CImgList<tc>& colors) {
35407       return _load_off(0,filename,primitives,colors);
35408     }
35409 
35410     template<typename tf, typename tc>
35411     static CImg<T> get_load_off(const char *const filename, CImgList<tf>& primitives, CImgList<tc>& colors) {
35412       return CImg<T>().load_off(filename,primitives,colors);
35413     }
35414 
35415     //! Load a 3d object from a .OFF file.
35416     template<typename tf, typename tc>
35417     CImg<T>& load_off(std::FILE *const file, CImgList<tf>& primitives, CImgList<tc>& colors) {
35418       return _load_off(file,0,primitives,colors);
35419     }
35420 
35421     template<typename tf, typename tc>
35422     static CImg<T> get_load_off(std::FILE *const file, CImgList<tf>& primitives, CImgList<tc>& colors) {
35423       return CImg<T>().load_off(file,primitives,colors);
35424     }
35425 
35426     template<typename tf, typename tc>
35427     CImg<T>& _load_off(std::FILE *const file, const char *const filename,
35428                        CImgList<tf>& primitives, CImgList<tc>& colors) {
35429       if (!file && !filename)
35430         throw CImgArgumentException(_cimg_instance
35431                                     "load_off() : Specified filename is (null).",
35432                                     cimg_instance);
35433 
35434       std::FILE *const nfile = file?file:cimg::fopen(filename,"r");
35435       unsigned int nb_points = 0, nb_primitives = 0, nb_read = 0;
35436       char line[256] = { 0 };
35437       int err;
35438 
35439       // Skip comments, and read magic string OFF
35440       do { err = std::fscanf(nfile,"%255[^\n] ",line); } while (!err || (err==1 && *line=='#'));
35441       if (cimg::strncasecmp(line,"OFF",3) && cimg::strncasecmp(line,"COFF",4)) {
35442         if (!file) cimg::fclose(nfile);
35443         throw CImgIOException(_cimg_instance
35444                               "load_off() : OFF header not found in file '%s'.",
35445                               cimg_instance,
35446                               filename?filename:"(FILE*)");
35447       }
35448       do { err = std::fscanf(nfile,"%255[^\n] ",line); } while (!err || (err==1 && *line=='#'));
35449       if ((err = std::sscanf(line,"%u%u%*[^\n] ",&nb_points,&nb_primitives))!=2) {
35450         if (!file) cimg::fclose(nfile);
35451         throw CImgIOException(_cimg_instance
35452                               "load_off() : Invalid number of vertices or primitives specified in file '%s'.",
35453                               cimg_instance,
35454                               filename?filename:"(FILE*)");
35455       }
35456 
35457       // Read points data
35458       assign(nb_points,3);
35459       float X = 0, Y = 0, Z = 0;
35460       cimg_forX(*this,l) {
35461         do { err = std::fscanf(nfile,"%255[^\n] ",line); } while (!err || (err==1 && *line=='#'));
35462         if ((err = std::sscanf(line,"%f%f%f%*[^\n] ",&X,&Y,&Z))!=3) {
35463           if (!file) cimg::fclose(nfile);
35464           throw CImgIOException(_cimg_instance
35465                                 "load_off() : Failed to read vertex %u/%u in file '%s'.",
35466                                 cimg_instance,
35467                                 l+1,nb_points,filename?filename:"(FILE*)");
35468         }
35469         (*this)(l,0) = (T)X; (*this)(l,1) = (T)Y; (*this)(l,2) = (T)Z;
35470       }
35471 
35472       // Read primitive data
35473       primitives.assign();
35474       colors.assign();
35475       bool stopflag = false;
35476       while (!stopflag) {
35477         float c0 = 0.7f, c1 = 0.7f, c2 = 0.7f;
35478         unsigned int prim = 0, i0 = 0, i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, i6 = 0, i7 = 0;
35479         *line = 0;
35480         if ((err = std::fscanf(nfile,"%u",&prim))!=1) stopflag=true;
35481         else {
35482           ++nb_read;
35483           switch (prim) {
35484           case 1 : {
35485             if ((err = std::fscanf(nfile,"%u%255[^\n] ",&i0,line))<2) {
35486               cimg::warn(_cimg_instance
35487                          "load_off() : Failed to read primitive %u/%u from file '%s'.",
35488                          cimg_instance,
35489                          nb_read,nb_primitives,filename?filename:"(FILE*)");
35490 
35491               err = std::fscanf(nfile,"%*[^\n] ");
35492             } else {
35493               err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
35494               CImg<tf>::vector(i0).move_to(primitives);
35495               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
35496             }
35497           } break;
35498           case 2 : {
35499             if ((err = std::fscanf(nfile,"%u%u%255[^\n] ",&i0,&i1,line))<2) {
35500               cimg::warn(_cimg_instance
35501                          "load_off() : Failed to read primitive %u/%u from file '%s'.",
35502                          cimg_instance,
35503                          nb_read,nb_primitives,filename?filename:"(FILE*)");
35504 
35505               err = std::fscanf(nfile,"%*[^\n] ");
35506             } else {
35507               err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
35508               CImg<tf>::vector(i0,i1).move_to(primitives);
35509               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
35510             }
35511           } break;
35512           case 3 : {
35513             if ((err = std::fscanf(nfile,"%u%u%u%255[^\n] ",&i0,&i1,&i2,line))<3) {
35514               cimg::warn(_cimg_instance
35515                          "load_off() : Failed to read primitive %u/%u from file '%s'.",
35516                          cimg_instance,
35517                          nb_read,nb_primitives,filename?filename:"(FILE*)");
35518 
35519               err = std::fscanf(nfile,"%*[^\n] ");
35520             } else {
35521               err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
35522               CImg<tf>::vector(i0,i2,i1).move_to(primitives);
35523               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
35524             }
35525           } break;
35526           case 4 : {
35527             if ((err = std::fscanf(nfile,"%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,line))<4) {
35528               cimg::warn(_cimg_instance
35529                          "load_off() : Failed to read primitive %u/%u from file '%s'.",
35530                          cimg_instance,
35531                          nb_read,nb_primitives,filename?filename:"(FILE*)");
35532 
35533               err = std::fscanf(nfile,"%*[^\n] ");
35534             } else {
35535               err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
35536               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
35537               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
35538             }
35539           } break;
35540           case 5 : {
35541             if ((err = std::fscanf(nfile,"%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,line))<5) {
35542               cimg::warn(_cimg_instance
35543                          "load_off() : Failed to read primitive %u/%u from file '%s'.",
35544                          cimg_instance,
35545                          nb_read,nb_primitives,filename?filename:"(FILE*)");
35546 
35547               err = std::fscanf(nfile,"%*[^\n] ");
35548             } else {
35549               err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
35550               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
35551               CImg<tf>::vector(i0,i4,i3).move_to(primitives);
35552               colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
35553               ++nb_primitives;
35554             }
35555           } break;
35556           case 6 : {
35557             if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,line))<6) {
35558               cimg::warn(_cimg_instance
35559                          "load_off() : Failed to read primitive %u/%u from file '%s'.",
35560                          cimg_instance,
35561                          nb_read,nb_primitives,filename?filename:"(FILE*)");
35562 
35563               err = std::fscanf(nfile,"%*[^\n] ");
35564             } else {
35565               err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
35566               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
35567               CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives);
35568               colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
35569               ++nb_primitives;
35570             }
35571           } break;
35572           case 7 : {
35573             if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,line))<7) {
35574               cimg::warn(_cimg_instance
35575                          "load_off() : Failed to read primitive %u/%u from file '%s'.",
35576                          cimg_instance,
35577                          nb_read,nb_primitives,filename?filename:"(FILE*)");
35578 
35579               err = std::fscanf(nfile,"%*[^\n] ");
35580             } else {
35581               err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
35582               CImg<tf>::vector(i0,i4,i3,i1).move_to(primitives);
35583               CImg<tf>::vector(i0,i6,i5,i4).move_to(primitives);
35584               CImg<tf>::vector(i3,i2,i1).move_to(primitives);
35585               colors.insert(3,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
35586               ++(++nb_primitives);
35587             }
35588           } break;
35589           case 8 : {
35590             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) {
35591               cimg::warn(_cimg_instance
35592                          "load_off() : Failed to read primitive %u/%u from file '%s'.",
35593                          cimg_instance,
35594                          nb_read,nb_primitives,filename?filename:"(FILE*)");
35595 
35596               err = std::fscanf(nfile,"%*[^\n] ");
35597             } else {
35598               err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
35599               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
35600               CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives);
35601               CImg<tf>::vector(i0,i7,i6,i5).move_to(primitives);
35602               colors.insert(3,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
35603               ++(++nb_primitives);
35604             }
35605           } break;
35606           default :
35607             cimg::warn(_cimg_instance
35608                        "load_off() : Failed to read primitive %u/%u (%u vertices) from file '%s'.",
35609                        cimg_instance,
35610                        nb_read,nb_primitives,prim,filename?filename:"(FILE*)");
35611 
35612             err = std::fscanf(nfile,"%*[^\n] ");
35613           }
35614         }
35615       }
35616       if (!file) cimg::fclose(nfile);
35617       if (primitives._width!=nb_primitives)
35618         cimg::warn(_cimg_instance
35619                    "load_off() : Only %u/%u primitives read from file '%s'.",
35620                    cimg_instance,
35621                    primitives._width,nb_primitives,filename?filename:"(FILE*)");
35622       return *this;
35623     }
35624 
35625     //! Load a video sequence using FFMPEG's external tool 'ffmpeg'.
35626     CImg<T>& load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) {
35627       return get_load_ffmpeg_external(filename,axis,align).move_to(*this);
35628     }
35629 
35630     static CImg<T> get_load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) {
35631       return CImgList<T>().load_ffmpeg_external(filename).get_append(axis,align);
35632     }
35633 
35634     //! Load an image using GraphicsMagick's external tool 'gm'.
35635     CImg<T>& load_graphicsmagick_external(const char *const filename) {
35636       if (!filename)
35637         throw CImgArgumentException(_cimg_instance
35638                                     "load_graphicsmagick_external() : Specified filename is (null).",
35639                                     cimg_instance);
35640 
35641       char command[1024] = { 0 }, filetmp[512] = { 0 };
35642       std::FILE *file = 0;
35643 #if cimg_OS==1
35644       cimg_snprintf(command,sizeof(command),"%s convert \"%s\" pnm:-",cimg::graphicsmagick_path(),filename);
35645       file = popen(command,"r");
35646       if (file) {
35647         try { load_pnm(file); } catch (...) {
35648           pclose(file);
35649           throw CImgIOException(_cimg_instance
35650                                 "load_graphicsmagick_external() : Failed to load file '%s' with external command 'gm'.",
35651                                 cimg_instance,
35652                                 filename);
35653         }
35654         pclose(file);
35655         return *this;
35656       }
35657 #endif
35658       do {
35659         cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.pnm",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
35660         if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
35661       } while (file);
35662       cimg_snprintf(command,sizeof(command),"%s convert \"%s\" \"%s\"",cimg::graphicsmagick_path(),filename,filetmp);
35663       cimg::system(command,cimg::graphicsmagick_path());
35664       if (!(file = std::fopen(filetmp,"rb"))) {
35665         cimg::fclose(cimg::fopen(filename,"r"));
35666         throw CImgIOException(_cimg_instance
35667                               "load_graphicsmagick_external() : Failed to load file '%s' with external command 'gm'.",
35668                               cimg_instance,
35669                               filename);
35670 
35671       } else cimg::fclose(file);
35672       load_pnm(filetmp);
35673       std::remove(filetmp);
35674       return *this;
35675     }
35676 
35677     static CImg<T> get_load_graphicsmagick_external(const char *const filename) {
35678       return CImg<T>().load_graphicsmagick_external(filename);
35679     }
35680 
35681     //! Load a gzipped image file, using external tool 'gunzip'.
35682     CImg<T>& load_gzip_external(const char *const filename) {
35683       if (!filename)
35684         throw CImgIOException(_cimg_instance
35685                               "load_gzip_external() : Specified filename is (null).",
35686                               cimg_instance);
35687 
35688       char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
35689       const char
35690         *const ext = cimg::split_filename(filename,body),
35691         *const ext2 = cimg::split_filename(body,0);
35692 
35693       std::FILE *file = 0;
35694       do {
35695         if (!cimg::strcasecmp(ext,"gz")) {
35696           if (*ext2) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
35697           else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
35698         } else {
35699           if (*ext) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
35700           else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
35701         }
35702         if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
35703       } while (file);
35704 
35705       cimg_snprintf(command,sizeof(command),"%s -c \"%s\" > %s",cimg::gunzip_path(),filename,filetmp);
35706       cimg::system(command);
35707       if (!(file = std::fopen(filetmp,"rb"))) {
35708         cimg::fclose(cimg::fopen(filename,"r"));
35709         throw CImgIOException(_cimg_instance
35710                               "load_gzip_external() : Failed to load file '%s' with external command 'gunzip'.",
35711                               cimg_instance,
35712                               filename);
35713 
35714       } else cimg::fclose(file);
35715       load(filetmp);
35716       std::remove(filetmp);
35717       return *this;
35718     }
35719 
35720     static CImg<T> get_load_gzip_external(const char *const filename) {
35721       return CImg<T>().load_gzip_external(filename);
35722     }
35723 
35724     //! Load an image using ImageMagick's external tool 'convert'.
35725     CImg<T>& load_imagemagick_external(const char *const filename) {
35726       if (!filename)
35727         throw CImgArgumentException(_cimg_instance
35728                                     "load_imagemagick_external() : Specified filename is (null).",
35729                                     cimg_instance);
35730       char command[1024] = { 0 }, filetmp[512] = { 0 };
35731       std::FILE *file = 0;
35732 #if cimg_OS==1
35733       cimg_snprintf(command,sizeof(command),"%s \"%s\" pnm:-",cimg::imagemagick_path(),filename);
35734       file = popen(command,"r");
35735       if (file) {
35736         try { load_pnm(file); } catch (...) {
35737           pclose(file);
35738           throw CImgIOException(_cimg_instance
35739                                 "load_imagemagick_external() : Failed to load file '%s' with external command 'convert'.",
35740                                 cimg_instance,
35741                                 filename);
35742         }
35743         pclose(file);
35744         return *this;
35745       }
35746 #endif
35747       do {
35748         cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.pnm",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
35749         if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
35750       } while (file);
35751       cimg_snprintf(command,sizeof(command),"%s \"%s\" \"%s\"",cimg::imagemagick_path(),filename,filetmp);
35752       cimg::system(command,cimg::imagemagick_path());
35753       if (!(file = std::fopen(filetmp,"rb"))) {
35754         cimg::fclose(cimg::fopen(filename,"r"));
35755         throw CImgIOException(_cimg_instance
35756                               "load_imagemagick_external() : Failed to load file '%s' with external command 'convert'.",
35757                               cimg_instance,
35758                               filename);
35759 
35760       } else cimg::fclose(file);
35761       load_pnm(filetmp);
35762       std::remove(filetmp);
35763       return *this;
35764     }
35765 
35766     static CImg<T> get_load_imagemagick_external(const char *const filename) {
35767       return CImg<T>().load_imagemagick_external(filename);
35768     }
35769 
35770     //! Load a DICOM image file, using XMedcon's external tool 'medcon'.
35771     CImg<T>& load_medcon_external(const char *const filename) {
35772       if (!filename)
35773         throw CImgArgumentException(_cimg_instance
35774                                     "load_medcon_external() : Specified filename is (null).",
35775                                     cimg_instance);
35776 
35777       char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
35778       cimg::fclose(cimg::fopen(filename,"r"));
35779       std::FILE *file = 0;
35780       do {
35781         cimg_snprintf(filetmp,sizeof(filetmp),"%s.hdr",cimg::filenamerand());
35782         if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
35783       } while (file);
35784       cimg_snprintf(command,sizeof(command),"%s -w -c anlz -o %s -f %s",cimg::medcon_path(),filetmp,filename);
35785       cimg::system(command);
35786       cimg::split_filename(filetmp,body);
35787 
35788       cimg_snprintf(command,sizeof(command),"%s.hdr",body);
35789       file = std::fopen(command,"rb");
35790       if (!file) {
35791         cimg_snprintf(command,sizeof(command),"m000-%s.hdr",body);
35792         file = std::fopen(command,"rb");
35793         if (!file) {
35794           throw CImgIOException(_cimg_instance
35795                                 "load_medcon_external() : Failed to load file '%s' with external command 'medcon'.",
35796                                 cimg_instance,
35797                                 filename);
35798         }
35799       }
35800       cimg::fclose(file);
35801       load_analyze(command);
35802       std::remove(command);
35803       cimg::split_filename(command,body);
35804       cimg_snprintf(command,sizeof(command),"%s.img",body);
35805       std::remove(command);
35806       return *this;
35807     }
35808 
35809     static CImg<T> get_load_medcon_external(const char *const filename) {
35810       return CImg<T>().load_medcon_external(filename);
35811     }
35812 
35813     //! Load a RAW Color Camera image file, using external tool 'dcraw'.
35814     CImg<T>& load_dcraw_external(const char *const filename) {
35815       if (!filename)
35816         throw CImgArgumentException(_cimg_instance
35817                                     "load_dcraw_external() : Specified filename is (null).",
35818                                     cimg_instance);
35819 
35820       char command[1024] = { 0 }, filetmp[512] = { 0 };
35821       std::FILE *file = 0;
35822 #if cimg_OS==1
35823       cimg_snprintf(command,sizeof(command),"%s -w -4 -c \"%s\"",cimg::dcraw_path(),filename);
35824       file = popen(command,"r");
35825       if (file) {
35826         try { load_pnm(file); } catch (...) {
35827           pclose(file);
35828           throw CImgIOException(_cimg_instance
35829                                 "load_dcraw_external() : Failed to load file '%s' with external command 'dcraw'.",
35830                                 cimg_instance,
35831                                 filename);
35832         }
35833         pclose(file);
35834         return *this;
35835       }
35836 #endif
35837       do {
35838         cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.ppm",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
35839         if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
35840       } while (file);
35841       cimg_snprintf(command,sizeof(command),"%s -w -4 -c \"%s\" > %s",cimg::dcraw_path(),filename,filetmp);
35842       cimg::system(command,cimg::dcraw_path());
35843       if (!(file = std::fopen(filetmp,"rb"))) {
35844         cimg::fclose(cimg::fopen(filename,"r"));
35845         throw CImgIOException(_cimg_instance
35846                               "load_dcraw_external() : Failed to load file '%s' with external command 'dcraw'.",
35847                               cimg_instance,
35848                               filename);
35849 
35850       } else cimg::fclose(file);
35851       load_pnm(filetmp);
35852       std::remove(filetmp);
35853       return *this;
35854     }
35855 
35856     static CImg<T> get_load_dcraw_external(const char *const filename) {
35857       return CImg<T>().load_dcraw_external(filename);
35858     }
35859 
35860     //! Load an image from a camera stream, using OpenCV.
35861     CImg<T>& load_camera(const int camera_index=-1, const unsigned int skip_frames=0, const bool release_camera=false) {
35862 #ifdef cimg_use_opencv
35863       const int ind = camera_index + 1;
35864       if (ind<0 || ind>255)
35865         throw CImgArgumentException(_cimg_instance
35866                                     "load_camera() : Invalid request for camera #%d.",
35867                                     cimg_instance,
35868                                     camera_index);
35869       static CvCapture *capture[256] = { 0 };
35870       if (release_camera) {
35871         if (capture[ind]) cvReleaseCapture(&(capture[ind]));
35872         capture[ind] = 0;
35873         return *this;
35874       }
35875       if (!capture[ind]) {
35876         capture[ind] = cvCreateCameraCapture(camera_index);
35877         if (!capture[ind]) {
35878           if (camera_index>=0)
35879             throw CImgIOException(_cimg_instance
35880                                   "load_camera() : Failed to initialize camera #%d.",
35881                                   cimg_instance,
35882                                   camera_index);
35883           else
35884             throw CImgIOException(_cimg_instance
35885                                   "load_camera() : Failed to initialize default camera.",
35886                                   cimg_instance);
35887         }
35888       }
35889       const IplImage *img = 0;
35890       for (unsigned int i = 0; i<skip_frames; ++i) img = cvQueryFrame(capture[ind]);
35891       img = cvQueryFrame(capture[ind]);
35892       if (img) {
35893         const int step = (int)(img->widthStep - 3*img->width);
35894         assign(img->width,img->height,1,3);
35895         const unsigned char* ptrs = (unsigned char*)img->imageData;
35896         T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
35897         if (step>0) cimg_forY(*this,y) {
35898             cimg_forX(*this,x) { *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++); }
35899             ptrs+=step;
35900           } else for (unsigned int siz = img->width*img->height; siz; --siz) { *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++); }
35901       }
35902 #else
35903       cimg::unused(camera_index,skip_frames,release_camera);
35904       throw CImgIOException(_cimg_instance
35905                             "load_camera() : This function requires the OpenCV library to run "
35906                             "(macro 'cimg_use_opencv' must be defined).",
35907                             cimg_instance);
35908 #endif
35909       return *this;
35910     }
35911 
35912     static CImg<T> get_load_camera(const int camera_index=-1, const unsigned int skip_frames=0, const bool release_camera=false) {
35913       return CImg<T>().load_camera(camera_index,skip_frames,release_camera);
35914     }
35915 
35916     //! Load an image using ImageMagick's or GraphicsMagick's executables. If failed, try to load a .cimg[z] file format.
35917     CImg<T>& load_other(const char *const filename) {
35918       if (!filename)
35919         throw CImgArgumentException(_cimg_instance
35920                                     "load_other() : Specified filename is (null).",
35921                                     cimg_instance);
35922 
35923       const unsigned int omode = cimg::exception_mode();
35924       cimg::exception_mode() = 0;
35925       try { load_magick(filename); }
35926       catch (CImgException&) {
35927         try { load_imagemagick_external(filename); }
35928         catch (CImgException&) {
35929           try { load_graphicsmagick_external(filename); }
35930           catch (CImgException&) {
35931             try { load_cimg(filename); }
35932             catch (CImgException&) {
35933               assign();
35934             }
35935           }
35936         }
35937       }
35938       cimg::exception_mode() = omode;
35939       if (is_empty())
35940         throw CImgIOException(_cimg_instance
35941                               "load_other() : Failed to load file '%s'. Format is not natively supported, and no external commands succeeded.",
35942                               cimg_instance,
35943                               filename);
35944       return *this;
35945     }
35946 
35947     static CImg<T> get_load_other(const char *const filename) {
35948       return CImg<T>().load_other(filename);
35949     }
35950 
35951     //@}
35952     //---------------------------
35953     //
35954     //! \name Data Output
35955     //@{
35956     //---------------------------
35957 
35958     //! Display informations about the image on the standard error output.
35959     /**
35960        \param title Name for the considered image (optional).
35961        \param display_stats Compute and display image statistics (optional).
35962     **/
35963     const CImg<T>& print(const char *const title=0, const bool display_stats=true) const {
35964       int xm = 0, ym = 0, zm = 0, vm = 0, xM = 0, yM = 0, zM = 0, vM = 0;
35965       static CImg<doubleT> st;
35966       if (!is_empty() && display_stats) {
35967         st = get_stats();
35968         xm = (int)st[4]; ym = (int)st[5], zm = (int)st[6], vm = (int)st[7];
35969         xM = (int)st[8]; yM = (int)st[9], zM = (int)st[10], vM = (int)st[11];
35970       }
35971       const unsigned int siz = size(), msiz = siz*sizeof(T), siz1 = siz-1;
35972       const unsigned int mdisp = msiz<8*1024?0:(msiz<8*1024*1024?1:2), width1 = _width-1;
35973 
35974       char _title[64] = { 0 };
35975       if (!title) cimg_snprintf(_title,sizeof(_title),"CImg<%s>",pixel_type());
35976 
35977       std::fprintf(cimg::output(),"%s: this = %p, size = (%u,%u,%u,%u) [%u %s], data = (%s*)%p",
35978                    title?title:_title,(void*)this,_width,_height,_depth,_spectrum,
35979                    mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)),
35980                    mdisp==0?"b":(mdisp==1?"Kb":"Mb"),
35981                    pixel_type(),(void*)begin());
35982       if (_data) std::fprintf(cimg::output(),"..%p (%s) = [ ",(void*)((char*)end()-1),_is_shared?"shared":"non-shared");
35983       else std::fprintf(cimg::output()," (%s) = [ ",_is_shared?"shared":"non-shared");
35984 
35985       if (!is_empty()) cimg_foroff(*this,off) {
35986         std::fprintf(cimg::output(),cimg::type<T>::format(),cimg::type<T>::format(_data[off]));
35987         if (off!=siz1) std::fprintf(cimg::output(),"%s",off%_width==width1?" ; ":" ");
35988         if (off==7 && siz>16) { off = siz1-8; if (off!=7) std::fprintf(cimg::output(),"... "); }
35989       }
35990       if (!is_empty() && display_stats)
35991         std::fprintf(cimg::output()," ], min = %g, max = %g, mean = %g, std = %g, coords(min) = (%u,%u,%u,%u), coords(max) = (%u,%u,%u,%u).\n",
35992                      st[0],st[1],st[2],std::sqrt(st[3]),xm,ym,zm,vm,xM,yM,zM,vM);
35993       else std::fprintf(cimg::output(),"%s].\n",is_empty()?"":" ");
35994       std::fflush(cimg::output());
35995       return *this;
35996     }
35997 
35998     //! Display an image into a CImgDisplay window.
35999     const CImg<T>& display(CImgDisplay& disp) const {
36000       disp.display(*this);
36001       return *this;
36002     }
36003 
36004     //! Display an image in a window with a title \p title, and wait a '_is_closed' or 'keyboard' event.\n
36005     const CImg<T>& display(CImgDisplay &disp, const bool display_info) const {
36006       return _display(disp,0,display_info,false);
36007     }
36008 
36009     //! Display an image in a window with a title \p title, and wait a '_is_closed' or 'keyboard' event.\n
36010     const CImg<T>& display(const char *const title=0, const bool display_info=true) const {
36011       CImgDisplay disp;
36012       return _display(disp,title,display_info,false);
36013     }
36014 
36015     const CImg<T>& _display(CImgDisplay &disp, const char *const title,
36016                             const bool display_info, const bool exit_on_simpleclick) const {
36017       if (is_empty())
36018         throw CImgInstanceException(_cimg_instance
36019                                     "display() : Empty instance.",
36020                                     cimg_instance);
36021 
36022       unsigned int oldw = 0, oldh = 0, XYZ[3], key = 0;
36023       int x0 = 0, y0 = 0, z0 = 0, x1 = width() - 1, y1 = height() - 1, z1 = depth() - 1;
36024 
36025       if (!disp) {
36026         disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1);
36027         if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum);
36028       } else if (title) disp.set_title("%s",title);
36029       disp.show().flush();
36030 
36031       const CImg<char> dtitle = CImg<char>::string(disp.title());
36032       if (display_info) print(dtitle);
36033 
36034       CImg<T> zoom;
36035       for (bool reset_view = true, resize_disp = false, is_first_select = true; !key && !disp.is_closed(); ) {
36036         if (reset_view) {
36037           XYZ[0] = (x0 + x1)/2; XYZ[1] = (y0 + y1)/2; XYZ[2] = (z0 + z1)/2;
36038           x0 = 0; y0 = 0; z0 = 0; x1 = _width - 1; y1 = _height-1; z1 = _depth-1;
36039           oldw = disp.width(); oldh = disp.height();
36040           reset_view = false;
36041         }
36042         if (!x0 && !y0 && !z0 && x1==width()-1 && y1==height()-1 && z1==depth()-1) zoom.assign();
36043         else zoom = get_crop(x0,y0,z0,x1,y1,z1);
36044 
36045         const unsigned int
36046           dx = 1 + x1 - x0, dy = 1 + y1 - y0, dz = 1 + z1 - z0,
36047           tw = dx + (dz>1?dz:0), th = dy + (dz>1?dz:0);
36048         if (!disp.is_fullscreen() && resize_disp) {
36049           const unsigned int
36050             ttw = tw*disp.width()/oldw, tth = th*disp.height()/oldh,
36051             dM = cimg::max(ttw,tth), diM = (unsigned int)cimg::max(disp.width(),disp.height()),
36052             imgw = cimg::max(16U,ttw*diM/dM), imgh = cimg::max(16U,tth*diM/dM);
36053           disp.set_fullscreen(false).resize(cimg_fitscreen(imgw,imgh,1),false);
36054           resize_disp = false;
36055         }
36056         oldw = tw; oldh = th;
36057 
36058         bool
36059           go_up = false, go_down = false, go_left = false, go_right = false,
36060           go_inc = false, go_dec = false, go_in = false, go_out = false,
36061           go_in_center = false;
36062         const CImg<T>& visu = zoom?zoom:*this;
36063         const CImg<intT> selection = visu._get_select(disp,0,2,XYZ,x0,y0,z0,is_first_select);
36064         is_first_select = false;
36065 
36066         if (disp.wheel()) {
36067           if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { go_out = !(go_in = disp.wheel()>0); go_in_center = false; }
36068           else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) { go_right = !(go_left = disp.wheel()>0); }
36069           else if (disp.is_keyALT() || disp.is_keyALTGR() || _depth==1) { go_down = !(go_up = disp.wheel()>0); }
36070           disp.set_wheel();
36071         }
36072 
36073         const int
36074           sx0 = selection(0), sy0 = selection(1), sz0 = selection(2),
36075           sx1 = selection(3), sy1 = selection(4), sz1 = selection(5);
36076         if (sx0>=0 && sy0>=0 && sz0>=0 && sx1>=0 && sy1>=0 && sz1>=0) {
36077           x1 = x0 + sx1; y1 = y0 + sy1; z1 = z0 + sz1; x0+=sx0; y0+=sy0; z0+=sz0;
36078           if (sx0==sx1 && sy0==sy1 && sz0==sz1) {
36079             if (exit_on_simpleclick && !zoom) break; else reset_view = true;
36080           }
36081           resize_disp = true;
36082         } else switch (key = disp.key()) {
36083 #if cimg_OS!=2
36084           case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT :
36085 #endif
36086           case 0 : case cimg::keyCTRLLEFT : case cimg::keyPAD5 : case cimg::keySHIFTLEFT :
36087 #if cimg_OS!=2
36088           case cimg::keyALTGR :
36089 #endif
36090           case cimg::keyALT : key = 0; break;
36091           case cimg::keyP : if (visu._depth>1 && (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT())) { // Special mode : play stack of frames
36092               const unsigned int
36093                 w1 = visu._width*disp.width()/(visu._width+(visu._depth>1?visu._depth:0)),
36094                 h1 = visu._height*disp.height()/(visu._height+(visu._depth>1?visu._depth:0));
36095               float frame_timing = 5;
36096               bool is_stopped = false;
36097               disp.set_key(key,false).set_wheel().resize(cimg_fitscreen(w1,h1,1),false); key = 0;
36098               for (unsigned int timer = 0; !key && !disp.is_closed() && !disp.button(); ) {
36099                 if (disp.is_resized()) disp.resize(false);
36100                 if (!timer) {
36101                   visu.get_slice(XYZ[2]).display(disp.set_title("%s | z=%d",dtitle.data(),XYZ[2]));
36102                   (++XYZ[2])%=visu._depth;
36103                 }
36104                 if (!is_stopped) { if (++timer>(unsigned int)frame_timing) timer = 0; } else timer = ~0U;
36105                 if (disp.wheel()) { frame_timing-=disp.wheel()/3.0f; disp.set_wheel(); }
36106                 switch (key = disp.key()) {
36107 #if cimg_OS!=2
36108                 case cimg::keyCTRLRIGHT :
36109 #endif
36110                 case cimg::keyCTRLLEFT : key = 0; break;
36111                 case cimg::keyPAGEUP : frame_timing-=0.3f; key = 0; break;
36112                 case cimg::keyPAGEDOWN : frame_timing+=0.3f; key = 0; break;
36113                 case cimg::keySPACE : is_stopped = !is_stopped; disp.set_key(key,false); key = 0; break;
36114                 case cimg::keyARROWLEFT : case cimg::keyARROWUP : is_stopped = true; timer = 0; key = 0; break;
36115                 case cimg::keyARROWRIGHT : case cimg::keyARROWDOWN : is_stopped = true; (XYZ[2]+=visu._depth-2)%=visu._depth; timer = 0; key = 0; break;
36116                 case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
36117                     disp.set_fullscreen(false).resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
36118                                                       CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false);
36119                     disp.set_key(key,false); key = 0;
36120                   } break;
36121                 case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
36122                     disp.set_fullscreen(false).resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false).set_key(key,false); key = 0;
36123                   } break;
36124                 case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
36125                     disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false).set_key(key,false); key = 0;
36126                   } break;
36127                 case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
36128                     disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen().set_key(key,false); key = 0;
36129                   } break;
36130                 }
36131                 frame_timing = frame_timing<1?1:(frame_timing>39?39:frame_timing);
36132                 disp.wait(20);
36133               }
36134               const unsigned int
36135                 w2 = (visu._width + (visu._depth>1?visu._depth:0))*disp.width()/visu._width,
36136                 h2 = (visu._height + (visu._depth>1?visu._depth:0))*disp.height()/visu._height;
36137               disp.resize(cimg_fitscreen(w2,h2,1),false).set_title(dtitle.data()).set_key().set_button().set_wheel();
36138               key = 0;
36139             } break;
36140           case cimg::keyHOME : reset_view = resize_disp = true; key = 0; break;
36141           case cimg::keyPADADD : go_in = true; go_in_center = true; key = 0; break;
36142           case cimg::keyPADSUB : go_out = true; key = 0; break;
36143           case cimg::keyARROWLEFT : case cimg::keyPAD4: go_left = true; key = 0; break;
36144           case cimg::keyARROWRIGHT : case cimg::keyPAD6: go_right = true; key = 0; break;
36145           case cimg::keyARROWUP : case cimg::keyPAD8: go_up = true; key = 0; break;
36146           case cimg::keyARROWDOWN : case cimg::keyPAD2: go_down = true; key = 0; break;
36147           case cimg::keyPAD7 : go_up = go_left = true; key = 0; break;
36148           case cimg::keyPAD9 : go_up = go_right = true; key = 0; break;
36149           case cimg::keyPAD1 : go_down = go_left = true; key = 0; break;
36150           case cimg::keyPAD3 : go_down = go_right = true; key = 0; break;
36151           case cimg::keyPAGEUP : go_inc = true; key = 0; break;
36152           case cimg::keyPAGEDOWN : go_dec = true; key = 0; break;
36153           }
36154         if (go_in) {
36155           const int
36156             mx = go_in_center?disp.width()/2:disp.mouse_x(),
36157             my = go_in_center?disp.height()/2:disp.mouse_y(),
36158             mX = mx*(_width+(_depth>1?_depth:0))/disp.width(),
36159             mY = my*(_height+(_depth>1?_depth:0))/disp.height();
36160           int X = XYZ[0], Y = XYZ[1], Z = XYZ[2];
36161           if (mX<width() && mY<height())  { X = x0 + mX*(1+x1-x0)/_width; Y = y0 + mY*(1+y1-y0)/_height; Z = XYZ[2]; }
36162           if (mX<width() && mY>=height()) { X = x0 + mX*(1+x1-x0)/_width; Z = z0 + (mY-_height)*(1+z1-z0)/_depth; Y = XYZ[1]; }
36163           if (mX>=width() && mY<height()) { Y = y0 + mY*(1+y1-y0)/_height; Z = z0 + (mX-_width)*(1+z1-z0)/_depth; X = XYZ[0]; }
36164           if (x1-x0>4) { x0 = X - 7*(X-x0)/8; x1 = X + 7*(x1-X)/8; }
36165           if (y1-y0>4) { y0 = Y - 7*(Y-y0)/8; y1 = Y + 7*(y1-Y)/8; }
36166           if (z1-z0>4) { z0 = Z - 7*(Z-z0)/8; z1 = Z + 7*(z1-Z)/8; }
36167         }
36168         if (go_out) {
36169           const int
36170             deltax = (x1-x0)/8, deltay = (y1-y0)/8, deltaz = (z1-z0)/8,
36171             ndeltax = deltax?deltax:(_width>1?1:0),
36172             ndeltay = deltay?deltay:(_height>1?1:0),
36173             ndeltaz = deltaz?deltaz:(_depth>1?1:0);
36174           x0-=ndeltax; y0-=ndeltay; z0-=ndeltaz;
36175           x1+=ndeltax; y1+=ndeltay; z1+=ndeltaz;
36176           if (x0<0) { x1-=x0; x0 = 0; if (x1>=width()) x1 = width() - 1; }
36177           if (y0<0) { y1-=y0; y0 = 0; if (y1>=height()) y1 = height() - 1; }
36178           if (z0<0) { z1-=z0; z0 = 0; if (z1>=depth()) z1 = depth() - 1; }
36179           if (x1>=width()) { x0-=(x1-width()+1); x1 = width()-1; if (x0<0) x0 = 0; }
36180           if (y1>=height()) { y0-=(y1-height()+1); y1 = height()-1; if (y0<0) y0 = 0; }
36181           if (z1>=depth()) { z0-=(z1-depth()+1); z1 = depth()-1; if (z0<0) z0 = 0; }
36182         }
36183         if (go_left) {
36184           const int delta = (x1-x0)/5, ndelta = delta?delta:(_width>1?1:0);
36185           if (x0-ndelta>=0) { x0-=ndelta; x1-=ndelta; }
36186           else { x1-=x0; x0 = 0; }
36187         }
36188         if (go_right) {
36189           const int delta = (x1-x0)/5, ndelta = delta?delta:(_width>1?1:0);
36190           if (x1+ndelta<width()) { x0+=ndelta; x1+=ndelta; }
36191           else { x0+=(width()-1-x1); x1 = width()-1; }
36192         }
36193         if (go_up) {
36194           const int delta = (y1-y0)/5, ndelta = delta?delta:(_height>1?1:0);
36195           if (y0-ndelta>=0) { y0-=ndelta; y1-=ndelta; }
36196           else { y1-=y0; y0 = 0; }
36197         }
36198         if (go_down) {
36199           const int delta = (y1-y0)/5, ndelta = delta?delta:(_height>1?1:0);
36200           if (y1+ndelta<height()) { y0+=ndelta; y1+=ndelta; }
36201           else { y0+=(height()-1-y1); y1 = height()-1; }
36202         }
36203         if (go_inc) {
36204           const int delta = (z1-z0)/5, ndelta = delta?delta:(_depth>1?1:0);
36205           if (z0-ndelta>=0) { z0-=ndelta; z1-=ndelta; }
36206           else { z1-=z0; z0 = 0; }
36207         }
36208         if (go_dec) {
36209           const int delta = (z1-z0)/5, ndelta = delta?delta:(_depth>1?1:0);
36210           if (z1+ndelta<depth()) { z0+=ndelta; z1+=ndelta; }
36211           else { z0+=(depth()-1-z1); z1 = depth()-1; }
36212         }
36213       }
36214       disp.set_key(key);
36215       return *this;
36216     }
36217 
36218     //! High-level interface for displaying a 3d object.
36219     template<typename tp, typename tf, typename tc, typename to>
36220     const CImg<T>& display_object3d(CImgDisplay& disp,
36221                                     const CImg<tp>& vertices,
36222                                     const CImgList<tf>& primitives,
36223                                     const CImgList<tc>& colors,
36224                                     const to& opacities,
36225                                     const bool centering=true,
36226                                     const int render_static=4, const int render_motion=1,
36227                                     const bool double_sided=true, const float focale=500,
36228                                     const float light_x=0, const float light_y=0, const float light_z=-5000,
36229                                     const float specular_light=0.2f, const float specular_shine=0.1f,
36230                                     const bool display_axes=true, float *const pose_matrix=0) const {
36231       return _display_object3d(disp,0,vertices,primitives,colors,opacities,centering,render_static,
36232                                render_motion,double_sided,focale,
36233                                light_x,light_y,light_z,specular_light,specular_shine,
36234                                display_axes,pose_matrix);
36235     }
36236 
36237     //! High-level interface for displaying a 3d object.
36238     template<typename tp, typename tf, typename tc, typename to>
36239     const CImg<T>& display_object3d(const char *const title,
36240                                     const CImg<tp>& vertices,
36241                                     const CImgList<tf>& primitives,
36242                                     const CImgList<tc>& colors,
36243                                     const to& opacities,
36244                                     const bool centering=true,
36245                                     const int render_static=4, const int render_motion=1,
36246                                     const bool double_sided=true, const float focale=500,
36247                                     const float light_x=0, const float light_y=0, const float light_z=-5000,
36248                                     const float specular_light=0.2f, const float specular_shine=0.1f,
36249                                     const bool display_axes=true, float *const pose_matrix=0) const {
36250       CImgDisplay disp;
36251       return _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,render_static,
36252                                render_motion,double_sided,focale,
36253                                light_x,light_y,light_z,specular_light,specular_shine,
36254                                display_axes,pose_matrix);
36255     }
36256 
36257     //! High-level interface for displaying a 3d object.
36258     template<typename tp, typename tf, typename tc>
36259     const CImg<T>& display_object3d(CImgDisplay &disp,
36260                                     const CImg<tp>& vertices,
36261                                     const CImgList<tf>& primitives,
36262                                     const CImgList<tc>& colors,
36263                                     const bool centering=true,
36264                                     const int render_static=4, const int render_motion=1,
36265                                     const bool double_sided=true, const float focale=500,
36266                                     const float light_x=0, const float light_y=0, const float light_z=-5000,
36267                                     const float specular_light=0.2f, const float specular_shine=0.1f,
36268                                     const bool display_axes=true, float *const pose_matrix=0) const {
36269       return display_object3d(disp,vertices,primitives,colors,CImgList<floatT>(),centering,
36270                               render_static,render_motion,double_sided,focale,
36271                               light_x,light_y,light_z,specular_light,specular_shine,
36272                               display_axes,pose_matrix);
36273     }
36274 
36275     //! High-level interface for displaying a 3d object.
36276     template<typename tp, typename tf, typename tc>
36277     const CImg<T>& display_object3d(const char *const title,
36278                                     const CImg<tp>& vertices,
36279                                     const CImgList<tf>& primitives,
36280                                     const CImgList<tc>& colors,
36281                                     const bool centering=true,
36282                                     const int render_static=4, const int render_motion=1,
36283                                     const bool double_sided=true, const float focale=500,
36284                                     const float light_x=0, const float light_y=0, const float light_z=-5000,
36285                                     const float specular_light=0.2f, const float specular_shine=0.1f,
36286                                     const bool display_axes=true, float *const pose_matrix=0) const {
36287       return display_object3d(title,vertices,primitives,colors,CImgList<floatT>(),centering,
36288                               render_static,render_motion,double_sided,focale,
36289                               light_x,light_y,light_z,specular_light,specular_shine,
36290                               display_axes,pose_matrix);
36291     }
36292 
36293     //! High-level interface for displaying a 3d object.
36294     template<typename tp, typename tf>
36295     const CImg<T>& display_object3d(CImgDisplay &disp,
36296                                     const CImg<tp>& vertices,
36297                                     const CImgList<tf>& primitives,
36298                                     const bool centering=true,
36299                                     const int render_static=4, const int render_motion=1,
36300                                     const bool double_sided=true, const float focale=500,
36301                                     const float light_x=0, const float light_y=0, const float light_z=-5000,
36302                                     const float specular_light=0.2f, const float specular_shine=0.1f,
36303                                     const bool display_axes=true, float *const pose_matrix=0) const {
36304       return display_object3d(disp,vertices,primitives,CImgList<T>(),centering,
36305                               render_static,render_motion,double_sided,focale,
36306                               light_x,light_y,light_z,specular_light,specular_shine,
36307                               display_axes,pose_matrix);
36308     }
36309 
36310     //! High-level interface for displaying a 3d object.
36311     template<typename tp, typename tf>
36312     const CImg<T>& display_object3d(const char *const title,
36313                                     const CImg<tp>& vertices,
36314                                     const CImgList<tf>& primitives,
36315                                     const bool centering=true,
36316                                     const int render_static=4, const int render_motion=1,
36317                                     const bool double_sided=true, const float focale=500,
36318                                     const float light_x=0, const float light_y=0, const float light_z=-5000,
36319                                     const float specular_light=0.2f, const float specular_shine=0.1f,
36320                                     const bool display_axes=true, float *const pose_matrix=0) const {
36321       return display_object3d(title,vertices,primitives,CImgList<T>(),centering,
36322                               render_static,render_motion,double_sided,focale,
36323                               light_x,light_y,light_z,specular_light,specular_shine,
36324                               display_axes,pose_matrix);
36325     }
36326 
36327     //! High-level interface for displaying a 3d object.
36328     template<typename tp>
36329     const CImg<T>& display_object3d(CImgDisplay &disp,
36330                                     const CImg<tp>& vertices,
36331                                     const bool centering=true,
36332                                     const int render_static=4, const int render_motion=1,
36333                                     const bool double_sided=true, const float focale=500,
36334                                     const float light_x=0, const float light_y=0, const float light_z=-5000,
36335                                     const float specular_light=0.2f, const float specular_shine=0.1f,
36336                                     const bool display_axes=true, float *const pose_matrix=0) const {
36337       return display_object3d(disp,vertices,CImgList<uintT>(),centering,
36338                               render_static,render_motion,double_sided,focale,
36339                               light_x,light_y,light_z,specular_light,specular_shine,
36340                               display_axes,pose_matrix);
36341     }
36342 
36343     //! High-level interface for displaying a 3d object.
36344     template<typename tp>
36345     const CImg<T>& display_object3d(const char *const title,
36346                                     const CImg<tp>& vertices,
36347                                     const bool centering=true,
36348                                     const int render_static=4, const int render_motion=1,
36349                                     const bool double_sided=true, const float focale=500,
36350                                     const float light_x=0, const float light_y=0, const float light_z=-5000,
36351                                     const float specular_light=0.2f, const float specular_shine=0.1f,
36352                                     const bool display_axes=true, float *const pose_matrix=0) const {
36353       return display_object3d(title,vertices,CImgList<uintT>(),centering,
36354                               render_static,render_motion,double_sided,focale,
36355                               light_x,light_y,light_z,specular_light,specular_shine,
36356                               display_axes,pose_matrix);
36357     }
36358 
36359     template<typename tp, typename tf, typename tc, typename to>
36360     const CImg<T>& _display_object3d(CImgDisplay& disp, const char *const title,
36361                                      const CImg<tp>& vertices,
36362                                      const CImgList<tf>& primitives,
36363                                      const CImgList<tc>& colors,
36364                                      const to& opacities,
36365                                      const bool centering,
36366                                      const int render_static, const int render_motion,
36367                                      const bool double_sided, const float focale,
36368                                      const float light_x, const float light_y, const float light_z,
36369                                      const float specular_light, const float specular_shine,
36370                                      const bool display_axes, float *const pose_matrix) const {
36371       typedef typename cimg::superset<tp,float>::type tpfloat;
36372 
36373       // Check input arguments
36374       if (is_empty()) {
36375         if (disp) return CImg<T>(disp.width(),disp.height(),1,(colors && colors[0].size()==1)?1:3,0).
36376                     _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,
36377                                       render_static,render_motion,double_sided,focale,
36378                                       light_x,light_y,light_z,specular_light,specular_shine,
36379                                       display_axes,pose_matrix);
36380         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).
36381                _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,
36382                                  render_static,render_motion,double_sided,focale,
36383                                  light_x,light_y,light_z,specular_light,specular_shine,
36384                                  display_axes,pose_matrix);
36385       } else { if (disp) disp.resize(*this,false); }
36386       char error_message[1024] = { 0 };
36387       if (!vertices.is_object3d(primitives,colors,opacities,true,error_message))
36388         throw CImgArgumentException(_cimg_instance
36389                                     "display_object3d() : Invalid specified 3d object (%u,%u) (%s).",
36390                                     cimg_instance,vertices._width,primitives._width,error_message);
36391       if (vertices._width && !primitives) {
36392         CImgList<tf> nprimitives(vertices._width,1,1,1,1);
36393         cimglist_for(nprimitives,l) nprimitives(l,0) = l;
36394         return _display_object3d(disp,title,vertices,nprimitives,colors,opacities,centering,
36395                                  render_static,render_motion,double_sided,focale,
36396                                  light_x,light_y,light_z,specular_light,specular_shine,
36397                                  display_axes,pose_matrix);
36398       }
36399       if (!disp) {
36400         disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,3);
36401         if (!title) disp.set_title("CImg<%s> (%u vertices, %u primitives)",pixel_type(),vertices._width,primitives._width);
36402       } else if (title) disp.set_title("%s",title);
36403 
36404       // Init 3d objects and compute object statistics
36405       CImg<floatT>
36406         pose,
36407         rotated_vertices(vertices._width,3),
36408         bbox_vertices, rotated_bbox_vertices,
36409         axes_vertices, rotated_axes_vertices,
36410         bbox_opacities, axes_opacities;
36411       CImgList<uintT> bbox_primitives, axes_primitives;
36412       CImgList<tf> reverse_primitives;
36413       CImgList<T> bbox_colors, bbox_colors2, axes_colors;
36414       unsigned int ns_width = 0, ns_height = 0;
36415       int _double_sided = (int)double_sided;
36416       bool ndisplay_axes = display_axes;
36417       const CImg<T>
36418         background_color(1,1,1,_spectrum,0),
36419         foreground_color(1,1,1,_spectrum,255);
36420       float
36421         Xoff = 0, Yoff = 0, Zoff = 0, sprite_scale = 1,
36422         xm = 0, xM = vertices?vertices.get_shared_line(0).max_min(xm):0,
36423         ym = 0, yM = vertices?vertices.get_shared_line(1).max_min(ym):0,
36424         zm = 0, zM = vertices?vertices.get_shared_line(2).max_min(zm):0;
36425       const float delta = cimg::max(xM-xm,yM-ym,zM-zm);
36426 
36427       rotated_bbox_vertices = bbox_vertices.assign(8,3,1,1,
36428                                                    xm,xM,xM,xm,xm,xM,xM,xm,
36429                                                    ym,ym,yM,yM,ym,ym,yM,yM,
36430                                                    zm,zm,zm,zm,zM,zM,zM,zM);
36431       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);
36432       bbox_colors.assign(6,_spectrum,1,1,1,background_color[0]);
36433       bbox_colors2.assign(6,_spectrum,1,1,1,foreground_color[0]);
36434       bbox_opacities.assign(bbox_colors._width,1,1,1,0.3f);
36435 
36436       rotated_axes_vertices = axes_vertices.assign(7,3,1,1,
36437                                                    0,20,0,0,22,-6,-6,
36438                                                    0,0,20,0,-6,22,-6,
36439                                                    0,0,0,20,0,0,22);
36440       axes_opacities.assign(3,1,1,1,1);
36441       axes_colors.assign(3,_spectrum,1,1,1,foreground_color[0]);
36442       axes_primitives.assign(3,1,2,1,1, 0,1, 0,2, 0,3);
36443 
36444       // Begin user interaction loop
36445       CImg<T> visu0(*this), visu;
36446       CImg<tpfloat> zbuffer(visu0.width(),visu0.height(),1,1,0);
36447       bool init_pose = true, clicked = false, redraw = true;
36448       unsigned int key = 0;
36449       int
36450         x0 = 0, y0 = 0, x1 = 0, y1 = 0,
36451         nrender_static = render_static,
36452         nrender_motion = render_motion;
36453       disp.show().flush();
36454 
36455       while (!disp.is_closed() && !key) {
36456 
36457         // Init object pose
36458         if (init_pose) {
36459           const float
36460             ratio = delta>0?(2.0f*cimg::min(disp.width(),disp.height())/(3.0f*delta)):1,
36461             dx = (xM + xm)/2, dy = (yM + ym)/2, dz = (zM + zm)/2;
36462           if (centering)
36463             CImg<floatT>(4,3,1,1, ratio,0.,0.,-ratio*dx, 0.,ratio,0.,-ratio*dy, 0.,0.,ratio,-ratio*dz).move_to(pose);
36464           else CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose);
36465           if (pose_matrix) {
36466             CImg<floatT> pose0(pose_matrix,4,3,1,1,false);
36467             pose0.resize(4,4,1,1,0); pose.resize(4,4,1,1,0);
36468             pose0(3,3) = pose(3,3) = 1;
36469             (pose0*pose).get_crop(0,0,3,2).move_to(pose);
36470             Xoff = pose_matrix[12]; Yoff = pose_matrix[13]; Zoff = pose_matrix[14]; sprite_scale = pose_matrix[15];
36471           } else { Xoff = Yoff = Zoff = 0; sprite_scale = 1; }
36472           init_pose = false;
36473           redraw = true;
36474         }
36475 
36476         // Rotate and draw 3d object
36477         if (redraw) {
36478           const float
36479             r00 = pose(0,0), r10 = pose(1,0), r20 = pose(2,0), r30 = pose(3,0),
36480             r01 = pose(0,1), r11 = pose(1,1), r21 = pose(2,1), r31 = pose(3,1),
36481             r02 = pose(0,2), r12 = pose(1,2), r22 = pose(2,2), r32 = pose(3,2);
36482           if ((clicked && nrender_motion>=0) || (!clicked && nrender_static>=0))
36483             cimg_forX(vertices,l) {
36484               const float x = (float)vertices(l,0), y = (float)vertices(l,1), z = (float)vertices(l,2);
36485               rotated_vertices(l,0) = r00*x + r10*y + r20*z + r30;
36486               rotated_vertices(l,1) = r01*x + r11*y + r21*z + r31;
36487               rotated_vertices(l,2) = r02*x + r12*y + r22*z + r32;
36488             }
36489           else cimg_forX(bbox_vertices,l) {
36490               const float x = bbox_vertices(l,0), y = bbox_vertices(l,1), z = bbox_vertices(l,2);
36491               rotated_bbox_vertices(l,0) = r00*x + r10*y + r20*z + r30;
36492               rotated_bbox_vertices(l,1) = r01*x + r11*y + r21*z + r31;
36493               rotated_bbox_vertices(l,2) = r02*x + r12*y + r22*z + r32;
36494             }
36495 
36496           // Draw object
36497           visu = visu0;
36498           if ((clicked && nrender_motion<0) || (!clicked && nrender_static<0))
36499             visu.draw_object3d(Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
36500                                rotated_bbox_vertices,bbox_primitives,bbox_colors,bbox_opacities,2,false,focale).
36501               draw_object3d(Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
36502                             rotated_bbox_vertices,bbox_primitives,bbox_colors2,1,false,focale);
36503           else visu._draw_object3d((void*)0,(!clicked && nrender_static>0)?zbuffer.fill(0):CImg<tpfloat>::empty(),
36504                                    Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
36505                                    rotated_vertices,reverse_primitives?reverse_primitives:primitives,
36506                                    colors,opacities,clicked?nrender_motion:nrender_static,_double_sided==1,focale,
36507                                    width()/2.0f+light_x,height()/2.0f+light_y,light_z,specular_light,specular_shine,
36508                                    sprite_scale);
36509           // Draw axes
36510           if (ndisplay_axes) {
36511             const float
36512               n = (float)std::sqrt(1e-8 + r00*r00 + r01*r01 + r02*r02),
36513               _r00 = r00/n, _r10 = r10/n, _r20 = r20/n,
36514               _r01 = r01/n, _r11 = r11/n, _r21 = r21/n,
36515               _r02 = r01/n, _r12 = r12/n, _r22 = r22/n,
36516               Xaxes = 25, Yaxes = visu._height - 38.0f;
36517             cimg_forX(axes_vertices,l) {
36518               const float
36519                 x = axes_vertices(l,0),
36520                 y = axes_vertices(l,1),
36521                 z = axes_vertices(l,2);
36522               rotated_axes_vertices(l,0) = _r00*x + _r10*y + _r20*z;
36523               rotated_axes_vertices(l,1) = _r01*x + _r11*y + _r21*z;
36524               rotated_axes_vertices(l,2) = _r02*x + _r12*y + _r22*z;
36525             }
36526             axes_opacities(0,0) = (rotated_axes_vertices(1,2)>0)?0.5f:1.0f;
36527             axes_opacities(1,0) = (rotated_axes_vertices(2,2)>0)?0.5f:1.0f;
36528             axes_opacities(2,0) = (rotated_axes_vertices(3,2)>0)?0.5f:1.0f;
36529             visu.draw_object3d(Xaxes,Yaxes,0,rotated_axes_vertices,axes_primitives,axes_colors,axes_opacities,1,false,focale).
36530               draw_text((int)(Xaxes+rotated_axes_vertices(4,0)),
36531                         (int)(Yaxes+rotated_axes_vertices(4,1)),
36532                         "X",axes_colors[0]._data,0,axes_opacities(0,0),13).
36533               draw_text((int)(Xaxes+rotated_axes_vertices(5,0)),
36534                         (int)(Yaxes+rotated_axes_vertices(5,1)),
36535                         "Y",axes_colors[1]._data,0,axes_opacities(1,0),13).
36536               draw_text((int)(Xaxes+rotated_axes_vertices(6,0)),
36537                         (int)(Yaxes+rotated_axes_vertices(6,1)),
36538                         "Z",axes_colors[2]._data,0,axes_opacities(2,0),13);
36539           }
36540           visu.display(disp);
36541           if (!clicked || nrender_motion==nrender_static) redraw = false;
36542         }
36543 
36544         // Handle user interaction
36545         disp.wait();
36546         if ((disp.button() || disp.wheel()) && disp.mouse_x()>=0 && disp.mouse_y()>=0) {
36547           redraw = true;
36548           if (!clicked) { x0 = x1 = disp.mouse_x(); y0 = y1 = disp.mouse_y(); if (!disp.wheel()) clicked = true; }
36549           else { x1 = disp.mouse_x(); y1 = disp.mouse_y(); }
36550           if (disp.button()&1) {
36551             const float
36552               R = 0.45f*cimg::min(disp.width(),disp.height()),
36553               R2 = R*R,
36554               u0 = (float)(x0-disp.width()/2),
36555               v0 = (float)(y0-disp.height()/2),
36556               u1 = (float)(x1-disp.width()/2),
36557               v1 = (float)(y1-disp.height()/2),
36558               n0 = (float)std::sqrt(u0*u0+v0*v0),
36559               n1 = (float)std::sqrt(u1*u1+v1*v1),
36560               nu0 = n0>R?(u0*R/n0):u0,
36561               nv0 = n0>R?(v0*R/n0):v0,
36562               nw0 = (float)std::sqrt(cimg::max(0,R2-nu0*nu0-nv0*nv0)),
36563               nu1 = n1>R?(u1*R/n1):u1,
36564               nv1 = n1>R?(v1*R/n1):v1,
36565               nw1 = (float)std::sqrt(cimg::max(0,R2-nu1*nu1-nv1*nv1)),
36566               u = nv0*nw1-nw0*nv1,
36567               v = nw0*nu1-nu0*nw1,
36568               w = nv0*nu1-nu0*nv1,
36569               n = (float)std::sqrt(u*u+v*v+w*w),
36570               alpha = (float)std::asin(n/R2);
36571             (CImg<floatT>::rotation_matrix(u,v,w,alpha)*pose).move_to(pose);
36572             x0 = x1; y0 = y1;
36573           }
36574           if (disp.button()&2) {
36575             if (focale>0) Zoff-=(y0-y1)*focale/400;
36576             else { const float s = std::exp((y0-y1)/400.0f); pose*=s; sprite_scale*=s; }
36577             x0 = x1; y0 = y1;
36578           }
36579           if (disp.wheel()) {
36580             if (focale>0) Zoff-=disp.wheel()*focale/20;
36581             else { const float s = std::exp(disp.wheel()/20.0f); pose*=s; sprite_scale*=s; }
36582             disp.set_wheel();
36583           }
36584           if (disp.button()&4) { Xoff+=(x1-x0); Yoff+=(y1-y0); x0 = x1; y0 = y1; }
36585           if ((disp.button()&1) && (disp.button()&2)) {
36586             init_pose = true; disp.set_button(); x0 = x1; y0 = y1;
36587             pose = CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0);
36588           }
36589         } else if (clicked) { x0 = x1; y0 = y1; clicked = false; redraw = true; }
36590 
36591         switch (key = disp.key()) {
36592 #if cimg_OS!=2
36593         case cimg::keyCTRLRIGHT :
36594 #endif
36595         case 0 : case cimg::keyCTRLLEFT : key = 0; break;
36596         case cimg::keyD: if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
36597             disp.set_fullscreen(false).resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
36598                                               CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
36599               _is_resized = true;
36600             disp.set_key(key,false); key = 0;
36601           } break;
36602         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
36603             disp.set_fullscreen(false).resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
36604             disp.set_key(key,false); key = 0;
36605           } break;
36606         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
36607             disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true;
36608             disp.set_key(key,false); key = 0;
36609           } break;
36610         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
36611             if (!ns_width || !ns_height ||
36612                 ns_width>(unsigned int)disp.screen_width() || ns_height>(unsigned int)disp.screen_height()) {
36613               ns_width = disp.screen_width()*3U/4;
36614               ns_height = disp.screen_height()*3U/4;
36615             }
36616             if (disp.is_fullscreen()) disp.resize(ns_width,ns_height,false);
36617             else {
36618               ns_width = (unsigned int)disp.width(); ns_height = disp.height();
36619               disp.resize(disp.screen_width(),disp.screen_height(),false);
36620             }
36621             disp.toggle_fullscreen()._is_resized = true;
36622             disp.set_key(key,false); key = 0;
36623           } break;
36624         case cimg::keyT : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Switch single/double-sided primitives.
36625             if (--_double_sided==-2) _double_sided = 1;
36626             if (_double_sided>=0) reverse_primitives.assign();
36627             else primitives.get_reverse_object3d().move_to(reverse_primitives);
36628             disp.set_key(key,false); key = 0; redraw = true;
36629           } break;
36630         case cimg::keyZ : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Enable/disable Z-buffer
36631             if (zbuffer) zbuffer.assign();
36632             else zbuffer.assign(visu0.width(),visu0.height(),1,1,0);
36633             disp.set_key(key,false); key = 0; redraw = true;
36634           } break;
36635         case cimg::keyA : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Show/hide 3d axes.
36636             ndisplay_axes = !ndisplay_axes;
36637             disp.set_key(key,false); key = 0; redraw = true;
36638           } break;
36639         case cimg::keyF1 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to points.
36640             nrender_motion = (nrender_static==0 && nrender_motion!=0)?0:-1; nrender_static = 0;
36641             disp.set_key(key,false); key = 0; redraw = true;
36642           } break;
36643         case cimg::keyF2 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to lines.
36644             nrender_motion = (nrender_static==1 && nrender_motion!=1)?1:-1; nrender_static = 1;
36645             disp.set_key(key,false); key = 0; redraw = true;
36646           } break;
36647         case cimg::keyF3 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat.
36648             nrender_motion = (nrender_static==2 && nrender_motion!=2)?2:-1; nrender_static = 2;
36649             disp.set_key(key,false); key = 0; redraw = true;
36650           } break;
36651         case cimg::keyF4 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat-shaded.
36652             nrender_motion = (nrender_static==3 && nrender_motion!=3)?3:-1; nrender_static = 3;
36653             disp.set_key(key,false); key = 0; redraw = true;
36654           } break;
36655         case cimg::keyF5 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to gouraud-shaded.
36656             nrender_motion = (nrender_static==4 && nrender_motion!=4)?4:-1; nrender_static = 4;
36657             disp.set_key(key,false); key = 0; redraw = true;
36658           } break;
36659         case cimg::keyF6 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to phong-shaded.
36660             nrender_motion = (nrender_static==5 && nrender_motion!=5)?5:-1; nrender_static = 5;
36661             disp.set_key(key,false); key = 0; redraw = true;
36662           } break;
36663         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save snapshot
36664             static unsigned int snap_number = 0;
36665             char filename[32] = { 0 };
36666             std::FILE *file;
36667             do {
36668               cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++);
36669               if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
36670             } while (file);
36671             (+visu).draw_text(0,0," Saving snapshot... ",foreground_color._data,background_color._data,1,13).display(disp);
36672             visu.save(filename);
36673             visu.draw_text(0,0," Snapshot '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp);
36674             disp.set_key(key,false); key = 0;
36675           } break;
36676         case cimg::keyG : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .off file
36677             static unsigned int snap_number = 0;
36678             char filename[32] = { 0 };
36679             std::FILE *file;
36680             do {
36681               cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.off",snap_number++);
36682               if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
36683             } while (file);
36684             visu.draw_text(0,0," Saving object... ",foreground_color._data,background_color._data,1,13).display(disp);
36685             vertices.save_off(filename,reverse_primitives?reverse_primitives:primitives,colors);
36686             visu.draw_text(0,0," Object '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp);
36687             disp.set_key(key,false); key = 0;
36688           } break;
36689         case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .cimg file
36690             static unsigned int snap_number = 0;
36691             char filename[32] = { 0 };
36692             std::FILE *file;
36693             do {
36694 #ifdef cimg_use_zlib
36695               cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++);
36696 #else
36697               cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++);
36698 #endif
36699               if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
36700             } while (file);
36701             visu.draw_text(0,0," Saving object... ",foreground_color._data,background_color._data,1,13).display(disp);
36702             vertices.get_object3dtoCImg3d(reverse_primitives?reverse_primitives:primitives,colors,opacities).save(filename);
36703             visu.draw_text(0,0," Object '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp);
36704             disp.set_key(key,false); key = 0;
36705           } break;
36706 #ifdef cimg_use_board
36707         case cimg::keyP : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .EPS file
36708             static unsigned int snap_number = 0;
36709             char filename[32] = { 0 };
36710             std::FILE *file;
36711             do {
36712               cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.eps",snap_number++);
36713               if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
36714             } while (file);
36715             visu.draw_text(0,0," Saving EPS snapshot... ",foreground_color._data,background_color._data,1,13).display(disp);
36716             LibBoard::Board board;
36717             (+visu)._draw_object3d(&board,zbuffer.fill(0),
36718                                    Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
36719                                    rotated_vertices,reverse_primitives?reverse_primitives:primitives,
36720                                    colors,opacities,clicked?nrender_motion:nrender_static,
36721                                    _double_sided==1,focale,
36722                                    visu.width()/2.0f+light_x,visu.height()/2.0f+light_y,light_z,specular_light,specular_shine,
36723                                    sprite_scale);
36724             board.saveEPS(filename);
36725             visu.draw_text(0,0," Object '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp);
36726             disp.set_key(key,false); key = 0;
36727           } break;
36728         case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .SVG file
36729             static unsigned int snap_number = 0;
36730             char filename[32] = { 0 };
36731             std::FILE *file;
36732             do {
36733               cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.svg",snap_number++);
36734               if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
36735             } while (file);
36736             visu.draw_text(0,0," Saving SVG snapshot... ",foreground_color._data,background_color._data,1,13).display(disp);
36737             LibBoard::Board board;
36738             (+visu)._draw_object3d(&board,zbuffer.fill(0),
36739                                    Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
36740                                    rotated_vertices,reverse_primitives?reverse_primitives:primitives,
36741                                    colors,opacities,clicked?nrender_motion:nrender_static,
36742                                    _double_sided==1,focale,
36743                                    visu.width()/2.0f+light_x,visu.height()/2.0f+light_y,light_z,specular_light,specular_shine,
36744                                    sprite_scale);
36745             board.saveSVG(filename);
36746             visu.draw_text(0,0," Object '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp);
36747             disp.set_key(key,false); key = 0;
36748           } break;
36749 #endif
36750         }
36751         if (disp.is_resized()) {
36752           disp.resize(false); visu0 = get_resize(disp,1);
36753           if (zbuffer) zbuffer.assign(disp.width(),disp.height());
36754           redraw = true;
36755         }
36756       }
36757       if (pose_matrix) {
36758         std::memcpy(pose_matrix,pose._data,12*sizeof(float));
36759         pose_matrix[12] = Xoff; pose_matrix[13] = Yoff; pose_matrix[14] = Zoff; pose_matrix[15] = sprite_scale;
36760       }
36761       disp.set_button().set_key(key);
36762       return *this;
36763     }
36764 
36765     //! High-level interface for displaying a graph.
36766     const CImg<T>& display_graph(CImgDisplay &disp,
36767                                  const unsigned int plot_type=1, const unsigned int vertex_type=1,
36768                                  const char *const labelx=0, const double xmin=0, const double xmax=0,
36769                                  const char *const labely=0, const double ymin=0, const double ymax=0) const {
36770       if (is_empty())
36771         throw CImgInstanceException(_cimg_instance
36772                                     "display_graph() : Empty instance.",
36773                                     cimg_instance);
36774       if (!disp) disp.assign(cimg_fitscreen(640,480,1),0,0).set_title("CImg<%s>",pixel_type());
36775       const unsigned int siz = _width*_height*_depth, old_normalization = disp.normalization();
36776       disp.show().flush()._normalization = 0;
36777 
36778       double y0 = ymin, y1 = ymax, nxmin = xmin, nxmax = xmax;
36779       if (nxmin==nxmax) { nxmin = 0; nxmax = siz-1; }
36780       int x0 = 0, x1 = width()*height()*depth() - 1, key = 0;
36781 
36782       for (bool reset_view = true, resize_disp = false; !key && !disp.is_closed(); ) {
36783         if (reset_view) { x0 = 0; x1 = width()*height()*depth()-1; y0 = ymin; y1 = ymax; reset_view = false; }
36784         CImg<T> zoom(x1-x0+1,1,1,spectrum());
36785         cimg_forC(*this,c) zoom.get_shared_channel(c) = CImg<T>(data(x0,0,0,c),x1-x0+1,1,1,1,true);
36786 
36787         if (y0==y1) y0 = zoom.min_max(y1);
36788         if (y0==y1) { --y0; ++y1; }
36789         const CImg<intT> selection = zoom.get_select_graph(disp,plot_type,vertex_type,
36790                                                            labelx,
36791                                                            nxmin + x0*(nxmax-nxmin)/(siz-1),
36792                                                            nxmin + x1*(nxmax-nxmin)/(siz-1),
36793                                                            labely,y0,y1);
36794         const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y();
36795         if (selection[0]>=0) {
36796           if (selection[2]<0) reset_view = true;
36797           else {
36798             x1 = x0 + selection[2]; x0+=selection[0];
36799             if (selection[1]>=0 && selection[3]>=0) {
36800               y0 = y1 - selection[3]*(y1-y0)/(disp.height()-32);
36801               y1-=selection[1]*(y1-y0)/(disp.height()-32);
36802             }
36803           }
36804         } else {
36805           bool go_in = false, go_out = false, go_left = false, go_right = false, go_up = false, go_down = false;
36806           switch (key = disp.key()) {
36807           case cimg::keyHOME : reset_view = resize_disp = true; key = 0; disp.set_key(); break;
36808           case cimg::keyPADADD : go_in = true; go_out = false; key = 0; disp.set_key(); break;
36809           case cimg::keyPADSUB : go_out = true; go_in = false; key = 0; disp.set_key(); break;
36810           case cimg::keyARROWLEFT : case cimg::keyPAD4 : go_left = true; go_right = false; key = 0; disp.set_key(); break;
36811           case cimg::keyARROWRIGHT : case cimg::keyPAD6 : go_right = true; go_left = false; key = 0; disp.set_key(); break;
36812           case cimg::keyARROWUP : case cimg::keyPAD8 : go_up = true; go_down = false; key = 0; disp.set_key(); break;
36813           case cimg::keyARROWDOWN : case cimg::keyPAD2 : go_down = true; go_up = false; key = 0; disp.set_key(); break;
36814           case cimg::keyPAD7 : go_left = true; go_up = true; key = 0; disp.set_key(); break;
36815           case cimg::keyPAD9 : go_right = true; go_up = true; key = 0; disp.set_key(); break;
36816           case cimg::keyPAD1 : go_left = true; go_down = true; key = 0; disp.set_key(); break;
36817           case cimg::keyPAD3 : go_right = true; go_down = true; key = 0; disp.set_key(); break;
36818           }
36819           if (disp.wheel()) {
36820             if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) go_out = !(go_in = disp.wheel()>0);
36821             else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) go_left = !(go_right = disp.wheel()>0);
36822             else go_up = !(go_down = disp.wheel()<0);
36823             key = 0;
36824           }
36825 
36826           if (go_in) {
36827             const int
36828               xsiz = x1 - x0,
36829               mx = (mouse_x-16)*xsiz/(disp.width()-32),
36830               cx = x0 + (mx<0?0:(mx>=xsiz?xsiz:mx));
36831             if (x1-x0>4) {
36832               x0 = cx - 7*(cx-x0)/8; x1 = cx + 7*(x1-cx)/8;
36833               if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
36834                 const double
36835                   ysiz = y1 - y0,
36836                   my = (mouse_y-16)*ysiz/(disp.height()-32),
36837                   cy = y1 - (my<0?0:(my>=ysiz?ysiz:my));
36838                 y0 = cy - 7*(cy-y0)/8; y1 = cy + 7*(y1-cy)/8;
36839               } else y0 = y1 = 0;
36840             }
36841           }
36842           if (go_out) {
36843             if (x0>0 || x1<(int)siz-1) {
36844               const int deltax = (x1-x0)/8, ndeltax = deltax?deltax:(siz>1?1:0);
36845               const double ndeltay = (y1-y0)/8;
36846               x0-=ndeltax; x1+=ndeltax;
36847               y0-=ndeltay; y1+=ndeltay;
36848               if (x0<0) { x1-=x0; x0 = 0; if (x1>=(int)siz) x1 = (int)siz-1; }
36849               if (x1>=(int)siz) { x0-=(x1-siz+1); x1 = (int)siz-1; if (x0<0) x0 = 0; }
36850             }
36851           }
36852           if (go_left) {
36853             const int delta = (x1-x0)/5, ndelta = delta?delta:1;
36854             if (x0-ndelta>=0) { x0-=ndelta; x1-=ndelta; }
36855             else { x1-=x0; x0 = 0; }
36856             go_left = false;
36857           }
36858           if (go_right) {
36859             const int delta = (x1-x0)/5, ndelta = delta?delta:1;
36860             if (x1+ndelta<(int)siz) { x0+=ndelta; x1+=ndelta; }
36861             else { x0+=(siz-1-x1); x1 = siz-1; }
36862             go_right = false;
36863           }
36864           if (go_up) {
36865             const double delta = (y1-y0)/10, ndelta = delta?delta:1;
36866             y0+=ndelta; y1+=ndelta;
36867             go_up = false;
36868           }
36869           if (go_down) {
36870             const double delta = (y1-y0)/10, ndelta = delta?delta:1;
36871             y0-=ndelta; y1-=ndelta;
36872             go_down = false;
36873           }
36874         }
36875       }
36876       disp._normalization = old_normalization;
36877       return *this;
36878     }
36879 
36880     //! High-level interface for displaying a graph.
36881     const CImg<T>& display_graph(const char *const title=0,
36882                                  const unsigned int plot_type=1, const unsigned int vertex_type=1,
36883                                  const char *const labelx=0, const double xmin=0, const double xmax=0,
36884                                  const char *const labely=0, const double ymin=0, const double ymax=0) const {
36885       if (is_empty())
36886         throw CImgInstanceException(_cimg_instance
36887                                     "display_graph() : Empty instance.",
36888                                     cimg_instance);
36889       CImgDisplay disp;
36890       return display_graph(disp.set_title("%s",title),plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax);
36891     }
36892 
36893     //! Save the image as a file.
36894     /**
36895        The used file format is defined by the file extension in the filename \p filename.
36896        Parameter \p number can be used to add a 6-digit number to the filename before saving.
36897     **/
36898     const CImg<T>& save(const char *const filename, const int number=-1) const {
36899       if (!filename)
36900         throw CImgArgumentException(_cimg_instance
36901                                     "save() : Specified filename is (null).",
36902                                     cimg_instance);
36903       // Do not test for empty instances, since .cimg format is able to manage empty instances.
36904       const char *const ext = cimg::split_filename(filename);
36905       char nfilename[1024] = { 0 };
36906       const char *const fn = (number>=0)?cimg::number_filename(filename,number,6,nfilename):filename;
36907 #ifdef cimg_save_plugin
36908       cimg_save_plugin(fn);
36909 #endif
36910 #ifdef cimg_save_plugin1
36911       cimg_save_plugin1(fn);
36912 #endif
36913 #ifdef cimg_save_plugin2
36914       cimg_save_plugin2(fn);
36915 #endif
36916 #ifdef cimg_save_plugin3
36917       cimg_save_plugin3(fn);
36918 #endif
36919 #ifdef cimg_save_plugin4
36920       cimg_save_plugin4(fn);
36921 #endif
36922 #ifdef cimg_save_plugin5
36923       cimg_save_plugin5(fn);
36924 #endif
36925 #ifdef cimg_save_plugin6
36926       cimg_save_plugin6(fn);
36927 #endif
36928 #ifdef cimg_save_plugin7
36929       cimg_save_plugin7(fn);
36930 #endif
36931 #ifdef cimg_save_plugin8
36932       cimg_save_plugin8(fn);
36933 #endif
36934       // ASCII formats
36935       if (!cimg::strcasecmp(ext,"asc")) return save_ascii(fn);
36936       else if (!cimg::strcasecmp(ext,"dlm") ||
36937                !cimg::strcasecmp(ext,"txt")) return save_dlm(fn);
36938       else if (!cimg::strcasecmp(ext,"cpp") ||
36939                !cimg::strcasecmp(ext,"hpp") ||
36940                !cimg::strcasecmp(ext,"h") ||
36941                !cimg::strcasecmp(ext,"c")) return save_cpp(fn);
36942 
36943       // 2d binary formats
36944       else if (!cimg::strcasecmp(ext,"bmp")) return save_bmp(fn);
36945       else if (!cimg::strcasecmp(ext,"jpg") ||
36946                !cimg::strcasecmp(ext,"jpeg") ||
36947                !cimg::strcasecmp(ext,"jpe") ||
36948                !cimg::strcasecmp(ext,"jfif") ||
36949                !cimg::strcasecmp(ext,"jif")) return save_jpeg(fn);
36950       else if (!cimg::strcasecmp(ext,"rgb")) return save_rgb(fn);
36951       else if (!cimg::strcasecmp(ext,"rgba")) return save_rgba(fn);
36952       else if (!cimg::strcasecmp(ext,"png")) return save_png(fn);
36953       else if (!cimg::strcasecmp(ext,"pgm") ||
36954                !cimg::strcasecmp(ext,"ppm") ||
36955                !cimg::strcasecmp(ext,"pnm")) return save_pnm(fn);
36956       else if (!cimg::strcasecmp(ext,"pnk")) return save_pnk(fn);
36957       else if (!cimg::strcasecmp(ext,"pfm")) return save_pfm(fn);
36958       else if (!cimg::strcasecmp(ext,"exr")) return save_exr(fn);
36959       else if (!cimg::strcasecmp(ext,"tif") ||
36960                !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn);
36961 
36962       // 3d binary formats
36963       else if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true);
36964       else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false);
36965       else if (!cimg::strcasecmp(ext,"dcm")) return save_medcon_external(fn);
36966       else if (!cimg::strcasecmp(ext,"hdr") ||
36967                !cimg::strcasecmp(ext,"nii")) return save_analyze(fn);
36968       else if (!cimg::strcasecmp(ext,"inr")) return save_inr(fn);
36969       else if (!cimg::strcasecmp(ext,"mnc")) return save_minc2(fn);
36970       else if (!cimg::strcasecmp(ext,"pan")) return save_pandore(fn);
36971       else if (!cimg::strcasecmp(ext,"raw")) return save_raw(fn);
36972 
36973       // Archive files
36974       else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn);
36975 
36976       // Image sequences
36977       else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,true);
36978       else if (!cimg::strcasecmp(ext,"avi") ||
36979                !cimg::strcasecmp(ext,"mov") ||
36980                !cimg::strcasecmp(ext,"asf") ||
36981                !cimg::strcasecmp(ext,"divx") ||
36982                !cimg::strcasecmp(ext,"flv") ||
36983                !cimg::strcasecmp(ext,"mpg") ||
36984                !cimg::strcasecmp(ext,"m1v") ||
36985                !cimg::strcasecmp(ext,"m2v") ||
36986                !cimg::strcasecmp(ext,"m4v") ||
36987                !cimg::strcasecmp(ext,"mjp") ||
36988                !cimg::strcasecmp(ext,"mkv") ||
36989                !cimg::strcasecmp(ext,"mpe") ||
36990                !cimg::strcasecmp(ext,"movie") ||
36991                !cimg::strcasecmp(ext,"ogm") ||
36992                !cimg::strcasecmp(ext,"ogg") ||
36993                !cimg::strcasecmp(ext,"qt") ||
36994                !cimg::strcasecmp(ext,"rm") ||
36995                !cimg::strcasecmp(ext,"vob") ||
36996                !cimg::strcasecmp(ext,"wmv") ||
36997                !cimg::strcasecmp(ext,"xvid") ||
36998                !cimg::strcasecmp(ext,"mpeg")) return save_ffmpeg(fn);
36999       return save_other(fn);
37000     }
37001 
37002     // Save the image as an ASCII file (ASCII Raw + simple header) (internal).
37003     const CImg<T>& _save_ascii(std::FILE *const file, const char *const filename) const {
37004       if (!file && !filename)
37005         throw CImgArgumentException(_cimg_instance
37006                                     "save_ascii() : Specified filename is (null).",
37007                                     cimg_instance);
37008       if (is_empty())
37009         throw CImgInstanceException(_cimg_instance
37010                                     "save_ascii() : Empty instance, for file '%s'.",
37011                                     cimg_instance,
37012                                     filename?filename:"(FILE*)");
37013 
37014       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
37015       std::fprintf(nfile,"%u %u %u %u\n",_width,_height,_depth,_spectrum);
37016       const T* ptrs = _data;
37017       cimg_forYZC(*this,y,z,c) {
37018         cimg_forX(*this,x) std::fprintf(nfile,"%g ",(double)*(ptrs++));
37019         std::fputc('\n',nfile);
37020       }
37021       if (!file) cimg::fclose(nfile);
37022       return *this;
37023     }
37024 
37025     //! Save the image as an ASCII file (ASCII Raw + simple header).
37026     const CImg<T>& save_ascii(const char *const filename) const {
37027       return _save_ascii(0,filename);
37028     }
37029 
37030     //! Save the image as an ASCII file (ASCII Raw + simple header).
37031     const CImg<T>& save_ascii(std::FILE *const file) const {
37032       return _save_ascii(file,0);
37033     }
37034 
37035     // Save the image as a C or CPP source file (internal).
37036     const CImg<T>& _save_cpp(std::FILE *const file, const char *const filename) const {
37037       if (!file && !filename)
37038         throw CImgArgumentException(_cimg_instance
37039                                     "save_cpp() : Specified filename is (null).",
37040                                     cimg_instance);
37041       if (is_empty())
37042         throw CImgInstanceException(_cimg_instance
37043                                     "save_cpp() : Empty instance, for file '%s'.",
37044                                     cimg_instance,
37045                                     filename?filename:"(FILE*)");
37046 
37047       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
37048       char varname[1024] = { 0 };
37049       if (filename) std::sscanf(cimg::basename(filename),"%1023[a-zA-Z0-9_]",varname);
37050       if (!*varname) cimg_snprintf(varname,sizeof(varname),"unnamed");
37051       std::fprintf(nfile,
37052                    "/* Define image '%s' of size %ux%ux%ux%u and type '%s' */\n"
37053                    "%s data_%s[] = { \n  ",
37054                    varname,_width,_height,_depth,_spectrum,pixel_type(),pixel_type(),varname);
37055       for (unsigned int off = 0, siz = size()-1; off<=siz; ++off) {
37056         std::fprintf(nfile,cimg::type<T>::format(),cimg::type<T>::format((*this)[off]));
37057         if (off==siz) std::fprintf(nfile," };\n");
37058         else if (!((off+1)%16)) std::fprintf(nfile,",\n  ");
37059         else std::fprintf(nfile,", ");
37060       }
37061       if (!file) cimg::fclose(nfile);
37062       return *this;
37063     }
37064 
37065     //! Save the image as a CPP source file.
37066     const CImg<T>& save_cpp(const char *const filename) const {
37067       return _save_cpp(0,filename);
37068     }
37069 
37070     //! Save the image as a CPP source file.
37071     const CImg<T>& save_cpp(std::FILE *const file) const {
37072       return _save_cpp(file,0);
37073     }
37074 
37075     // Save the image as a DLM file (internal).
37076     const CImg<T>& _save_dlm(std::FILE *const file, const char *const filename) const {
37077       if (!file && !filename)
37078         throw CImgArgumentException(_cimg_instance
37079                                     "save_dlm() : Specified filename is (null).",
37080                                     cimg_instance);
37081       if (is_empty())
37082         throw CImgInstanceException(_cimg_instance
37083                                     "save_dlm() : Empty instance, for file '%s'.",
37084                                     cimg_instance,
37085                                     filename?filename:"(FILE*)");
37086       if (_depth>1)
37087         cimg::warn(_cimg_instance
37088                    "save_dlm() : Instance is volumetric, values along Z will be unrolled in file '%s'.",
37089                    cimg_instance,
37090                    filename?filename:"(FILE*)");
37091 
37092       if (_spectrum>1)
37093         cimg::warn(_cimg_instance
37094                    "save_dlm() : Instance is multispectral, values along C will be unrolled in file '%s'.",
37095                    cimg_instance,
37096                    filename?filename:"(FILE*)");
37097 
37098       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
37099       const T* ptrs = _data;
37100       cimg_forYZC(*this,y,z,c) {
37101         cimg_forX(*this,x) std::fprintf(nfile,"%g%s",(double)*(ptrs++),(x==width()-1)?"":",");
37102         std::fputc('\n',nfile);
37103       }
37104       if (!file) cimg::fclose(nfile);
37105       return *this;
37106     }
37107 
37108     //! Save the image as a DLM file.
37109     const CImg<T>& save_dlm(const char *const filename) const {
37110       return _save_dlm(0,filename);
37111     }
37112 
37113     //! Save the image as a DLM file.
37114     const CImg<T>& save_dlm(std::FILE *const file) const {
37115       return _save_dlm(file,0);
37116     }
37117 
37118    // Save the image as a BMP file (internal).
37119     const CImg<T>& _save_bmp(std::FILE *const file, const char *const filename) const {
37120       if (!file && !filename)
37121         throw CImgArgumentException(_cimg_instance
37122                                     "save_bmp() : Specified filename is (null).",
37123                                     cimg_instance);
37124       if (is_empty())
37125         throw CImgInstanceException(_cimg_instance
37126                                     "save_bmp() : Empty instance, for file '%s'.",
37127                                     cimg_instance,
37128                                     filename?filename:"(FILE*)");
37129       if (_depth>1)
37130         cimg::warn(_cimg_instance
37131                    "save_bmp() : Instance is volumetric, only the first slice will be saved in file '%s'.",
37132                    cimg_instance,
37133                    filename?filename:"(FILE*)");
37134 
37135       if (_spectrum>3)
37136         cimg::warn(_cimg_instance
37137                    "save_bmp() : Instance is multispectral, only the three first channels will be saved in file '%s'.",
37138                    cimg_instance,
37139                    filename?filename:"(FILE*)");
37140 
37141       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
37142       unsigned char header[54] = { 0 }, align_buf[4] = { 0 };
37143       const unsigned int
37144         align = (4 - (3*_width)%4)%4,
37145         buf_size = (3*_width + align)*height(),
37146         file_size = 54 + buf_size;
37147       header[0] = 'B'; header[1] = 'M';
37148       header[0x02] = file_size&0xFF;
37149       header[0x03] = (file_size>>8)&0xFF;
37150       header[0x04] = (file_size>>16)&0xFF;
37151       header[0x05] = (file_size>>24)&0xFF;
37152       header[0x0A] = 0x36;
37153       header[0x0E] = 0x28;
37154       header[0x12] = _width&0xFF;
37155       header[0x13] = (_width>>8)&0xFF;
37156       header[0x14] = (_width>>16)&0xFF;
37157       header[0x15] = (_width>>24)&0xFF;
37158       header[0x16] = _height&0xFF;
37159       header[0x17] = (_height>>8)&0xFF;
37160       header[0x18] = (_height>>16)&0xFF;
37161       header[0x19] = (_height>>24)&0xFF;
37162       header[0x1A] = 1;
37163       header[0x1B] = 0;
37164       header[0x1C] = 24;
37165       header[0x1D] = 0;
37166       header[0x22] = buf_size&0xFF;
37167       header[0x23] = (buf_size>>8)&0xFF;
37168       header[0x24] = (buf_size>>16)&0xFF;
37169       header[0x25] = (buf_size>>24)&0xFF;
37170       header[0x27] = 0x1;
37171       header[0x2B] = 0x1;
37172       cimg::fwrite(header,54,nfile);
37173 
37174       const T
37175         *ptr_r = data(0,_height-1,0,0),
37176         *ptr_g = (_spectrum>=2)?data(0,_height-1,0,1):0,
37177         *ptr_b = (_spectrum>=3)?data(0,_height-1,0,2):0;
37178 
37179       switch (_spectrum) {
37180       case 1 : {
37181         cimg_forY(*this,y) {
37182           cimg_forX(*this,x) {
37183             const unsigned char val = (unsigned char)*(ptr_r++);
37184             std::fputc(val,nfile); std::fputc(val,nfile); std::fputc(val,nfile);
37185           }
37186           cimg::fwrite(align_buf,align,nfile);
37187           ptr_r-=2*_width;
37188         }
37189       } break;
37190       case 2 : {
37191         cimg_forY(*this,y) {
37192           cimg_forX(*this,x) {
37193             std::fputc(0,nfile);
37194             std::fputc((unsigned char)(*(ptr_g++)),nfile);
37195             std::fputc((unsigned char)(*(ptr_r++)),nfile);
37196           }
37197           cimg::fwrite(align_buf,align,nfile);
37198           ptr_r-=2*_width; ptr_g-=2*_width;
37199         }
37200       } break;
37201       default : {
37202         cimg_forY(*this,y) {
37203           cimg_forX(*this,x) {
37204             std::fputc((unsigned char)(*(ptr_b++)),nfile);
37205             std::fputc((unsigned char)(*(ptr_g++)),nfile);
37206             std::fputc((unsigned char)(*(ptr_r++)),nfile);
37207           }
37208           cimg::fwrite(align_buf,align,nfile);
37209           ptr_r-=2*_width; ptr_g-=2*_width; ptr_b-=2*_width;
37210         }
37211       }
37212       }
37213       if (!file) cimg::fclose(nfile);
37214       return *this;
37215     }
37216 
37217     //! Save the image as a BMP file.
37218     const CImg<T>& save_bmp(const char *const filename) const {
37219       return _save_bmp(0,filename);
37220     }
37221 
37222     //! Save the image as a BMP file.
37223     const CImg<T>& save_bmp(std::FILE *const file) const {
37224       return _save_bmp(file,0);
37225     }
37226 
37227     // Save a file in JPEG format (internal).
37228     const CImg<T>& _save_jpeg(std::FILE *const file, const char *const filename, const unsigned int quality) const {
37229       if (!file && !filename)
37230         throw CImgArgumentException(_cimg_instance
37231                                     "save_jpeg() : Specified filename is (null).",
37232                                     cimg_instance);
37233       if (is_empty())
37234         throw CImgInstanceException(_cimg_instance
37235                                     "save_jpeg() : Empty instance, for file '%s'.",
37236                                     cimg_instance,
37237                                     filename?filename:"(FILE*)");
37238       if (_depth>1)
37239         cimg::warn(_cimg_instance
37240                    "save_jpeg() : Instance is volumetric, only the first slice will be saved in file '%s'.",
37241                    cimg_instance,
37242                    filename?filename:"(FILE*)");
37243 
37244 #ifndef cimg_use_jpeg
37245       if (!file) return save_other(filename,quality);
37246       else throw CImgIOException(_cimg_instance
37247                                  "save_jpeg() : Unable to save data in '(*FILE)' unless libjpeg is enabled.",
37248                                  cimg_instance);
37249 #else
37250       unsigned int dimbuf = 0;
37251       J_COLOR_SPACE colortype = JCS_RGB;
37252 
37253       switch(_spectrum) {
37254       case 1 : dimbuf = 1; colortype = JCS_GRAYSCALE; break;
37255       case 2 : dimbuf = 3; colortype = JCS_RGB; break;
37256       case 3 : dimbuf = 3; colortype = JCS_RGB; break;
37257       default: dimbuf = 4; colortype = JCS_CMYK; break;
37258       }
37259 
37260       // Call libjpeg functions
37261       struct jpeg_compress_struct cinfo;
37262       struct jpeg_error_mgr jerr;
37263       cinfo.err = jpeg_std_error(&jerr);
37264       jpeg_create_compress(&cinfo);
37265       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
37266       jpeg_stdio_dest(&cinfo,nfile);
37267       cinfo.image_width = _width;
37268       cinfo.image_height = _height;
37269       cinfo.input_components = dimbuf;
37270       cinfo.in_color_space = colortype;
37271       jpeg_set_defaults(&cinfo);
37272       jpeg_set_quality(&cinfo,quality<100?quality:100,TRUE);
37273       jpeg_start_compress(&cinfo,TRUE);
37274 
37275       JSAMPROW row_pointer[1];
37276       CImg<ucharT> buffer(_width*dimbuf);
37277 
37278       while (cinfo.next_scanline < cinfo.image_height) {
37279         unsigned char *ptrd = buffer._data;
37280 
37281         // Fill pixel buffer
37282         switch (_spectrum) {
37283         case 1 : { // Greyscale images
37284           const T *ptr_g = data(0, cinfo.next_scanline);
37285           for(unsigned int b = 0; b < cinfo.image_width; b++)
37286             *(ptrd++) = (unsigned char)*(ptr_g++);
37287         } break;
37288         case 2 : { // RG images
37289           const T *ptr_r = data(0,cinfo.next_scanline,0,0),
37290             *ptr_g = data(0,cinfo.next_scanline,0,1);
37291           for(unsigned int b = 0; b < cinfo.image_width; ++b) {
37292             *(ptrd++) = (unsigned char)*(ptr_r++);
37293             *(ptrd++) = (unsigned char)*(ptr_g++);
37294             *(ptrd++) = 0;
37295           }
37296         } break;
37297         case 3 : { // RGB images
37298           const T *ptr_r = data(0,cinfo.next_scanline,0,0),
37299             *ptr_g = data(0,cinfo.next_scanline,0,1),
37300             *ptr_b = data(0,cinfo.next_scanline,0,2);
37301           for(unsigned int b = 0; b < cinfo.image_width; ++b) {
37302             *(ptrd++) = (unsigned char)*(ptr_r++);
37303             *(ptrd++) = (unsigned char)*(ptr_g++);
37304             *(ptrd++) = (unsigned char)*(ptr_b++);
37305           }
37306         } break;
37307         default : { // CMYK images
37308           const T *ptr_r = data(0,cinfo.next_scanline,0,0),
37309             *ptr_g = data(0,cinfo.next_scanline,0,1),
37310             *ptr_b = data(0,cinfo.next_scanline,0,2),
37311             *ptr_a = data(0,cinfo.next_scanline,0,3);
37312           for(unsigned int b = 0; b < cinfo.image_width; ++b) {
37313             *(ptrd++) = (unsigned char)*(ptr_r++);
37314             *(ptrd++) = (unsigned char)*(ptr_g++);
37315             *(ptrd++) = (unsigned char)*(ptr_b++);
37316             *(ptrd++) = (unsigned char)*(ptr_a++);
37317           }
37318         }
37319         }
37320         *row_pointer = buffer._data;
37321         jpeg_write_scanlines(&cinfo,row_pointer,1);
37322       }
37323       jpeg_finish_compress(&cinfo);
37324       if (!file) cimg::fclose(nfile);
37325       jpeg_destroy_compress(&cinfo);
37326       return *this;
37327 #endif
37328     }
37329 
37330     //! Save a file in JPEG format.
37331     const CImg<T>& save_jpeg(const char *const filename, const unsigned int quality=100) const {
37332       return _save_jpeg(0,filename,quality);
37333     }
37334 
37335     //! Save a file in JPEG format.
37336     const CImg<T>& save_jpeg(std::FILE *const file, const unsigned int quality=100) const {
37337       return _save_jpeg(file,0,quality);
37338     }
37339 
37340     //! Save the image using built-in ImageMagick++ library.
37341     const CImg<T>& save_magick(const char *const filename, const unsigned int bytes_per_pixel=0) const {
37342       if (!filename)
37343         throw CImgArgumentException(_cimg_instance
37344                                     "save_magick() : Specified filename is (null).",
37345                                     cimg_instance);
37346       if (is_empty())
37347         throw CImgInstanceException(_cimg_instance
37348                                     "save_magick() : Empty instance, for file '%s'.",
37349                                     cimg_instance,
37350                                     filename);
37351 #ifdef cimg_use_magick
37352       double stmin, stmax = (double)max_min(stmin);
37353       if (_depth>1)
37354         cimg::warn(_cimg_instance
37355                    "save_magick() : Instance is volumetric, only the first slice will be saved in file '%s'.",
37356                    cimg_instance,
37357                    filename);
37358 
37359       if (_spectrum>3)
37360         cimg::warn(_cimg_instance
37361                    "save_magick() : Instance is multispectral, only the three first channels will be saved in file '%s'.",
37362                    cimg_instance,
37363                    filename);
37364 
37365       if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
37366         cimg::warn(_cimg_instance
37367                    "save_magick() : Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
37368                    cimg_instance,
37369                    filename,stmin,stmax);
37370 
37371       Magick::Image image(Magick::Geometry(_width,_height),"black");
37372       image.type(Magick::TrueColorType);
37373       image.depth(bytes_per_pixel?(8*bytes_per_pixel):(stmax>=256?16:8));
37374       const T
37375         *ptr_r = data(0,0,0,0),
37376         *ptr_g = _spectrum>1?data(0,0,0,1):0,
37377         *ptr_b = _spectrum>2?data(0,0,0,2):0;
37378       Magick::PixelPacket *pixels = image.getPixels(0,0,_width,_height);
37379       switch (_spectrum) {
37380       case 1 : // Scalar images
37381         for (unsigned int off = _width*_height; off; --off) {
37382           pixels->red = pixels->green = pixels->blue = (Magick::Quantum)*(ptr_r++);
37383           ++pixels;
37384         }
37385         break;
37386       case 2 : // RG images
37387         for (unsigned int off = _width*_height; off; --off) {
37388           pixels->red = (Magick::Quantum)*(ptr_r++);
37389           pixels->green = (Magick::Quantum)*(ptr_g++);
37390           pixels->blue = 0; ++pixels;
37391         }
37392         break;
37393       default : // RGB images
37394         for (unsigned int off = _width*_height; off; --off) {
37395           pixels->red = (Magick::Quantum)*(ptr_r++);
37396           pixels->green = (Magick::Quantum)*(ptr_g++);
37397           pixels->blue = (Magick::Quantum)*(ptr_b++);
37398           ++pixels;
37399         }
37400       }
37401       image.syncPixels();
37402       image.write(filename);
37403 #else
37404       cimg::unused(bytes_per_pixel);
37405       throw CImgIOException(_cimg_instance
37406                             "save_magick() : Unable to save file '%s' unless libMagick++ is enabled.",
37407                             cimg_instance,
37408                             filename);
37409 #endif
37410       return *this;
37411     }
37412 
37413     // Save an image to a PNG file (internal).
37414     // Most of this function has been written by Eric Fausett
37415     const CImg<T>& _save_png(std::FILE *const file, const char *const filename, const unsigned int bytes_per_pixel=0) const {
37416       if (!file && !filename)
37417         throw CImgArgumentException(_cimg_instance
37418                                     "save_png() : Specified filename is (null).",
37419                                     cimg_instance);
37420       if (is_empty())
37421         throw CImgInstanceException(_cimg_instance
37422                                     "save_png() : Empty image, for file '%s'.",
37423                                     cimg_instance,
37424                                     filename?filename:"(FILE*)");
37425 #ifndef cimg_use_png
37426       cimg::unused(bytes_per_pixel);
37427       if (!file) return save_other(filename);
37428       else throw CImgIOException(_cimg_instance
37429                                  "save_png() : Unable to save data in '(*FILE)' unless libpng is enabled.",
37430                                  cimg_instance);
37431 #else
37432       const char *volatile nfilename = filename; // two 'volatile' here to remove a g++ warning due to 'setjmp'.
37433       std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"wb");
37434 
37435       double stmin, stmax = (double)max_min(stmin);
37436       if (_depth>1)
37437         cimg::warn(_cimg_instance
37438                    "save_png() : Instance is volumetric, only the first slice will be saved in file '%s'.",
37439                    cimg_instance,
37440                    filename);
37441 
37442       if (_spectrum>4)
37443         cimg::warn(_cimg_instance
37444                    "save_png() : Instance is multispectral, only the three first channels will be saved in file '%s'.",
37445                    cimg_instance,
37446                    filename);
37447 
37448       if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
37449         cimg::warn(_cimg_instance
37450                    "save_png() : Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
37451                    cimg_instance,
37452                    filename,stmin,stmax);
37453 
37454       // Setup PNG structures for write
37455       png_voidp user_error_ptr = 0;
37456       png_error_ptr user_error_fn = 0, user_warning_fn = 0;
37457       png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,user_error_ptr, user_error_fn, user_warning_fn);
37458       if(!png_ptr){
37459         if (!file) cimg::fclose(nfile);
37460         throw CImgIOException(_cimg_instance
37461                               "save_png() : Failed to initialize 'png_ptr' structure when saving file '%s'.",
37462                               cimg_instance,
37463                               nfilename?nfilename:"(FILE*)");
37464       }
37465       png_infop info_ptr = png_create_info_struct(png_ptr);
37466       if (!info_ptr) {
37467         png_destroy_write_struct(&png_ptr,(png_infopp)0);
37468         if (!file) cimg::fclose(nfile);
37469         throw CImgIOException(_cimg_instance
37470                               "save_png() : Failed to initialize 'info_ptr' structure when saving file '%s'.",
37471                               cimg_instance,
37472                               nfilename?nfilename:"(FILE*)");
37473       }
37474       if (setjmp(png_jmpbuf(png_ptr))) {
37475         png_destroy_write_struct(&png_ptr, &info_ptr);
37476         if (!file) cimg::fclose(nfile);
37477         throw CImgIOException(_cimg_instance
37478                               "save_png() : Encountered unknown fatal error in libpng when saving file '%s'.",
37479                               cimg_instance,
37480                               nfilename?nfilename:"(FILE*)");
37481       }
37482       png_init_io(png_ptr, nfile);
37483       const int bit_depth = bytes_per_pixel?(bytes_per_pixel*8):(stmax>=256?16:8);
37484       int color_type;
37485       switch (spectrum()) {
37486       case 1 : color_type = PNG_COLOR_TYPE_GRAY; break;
37487       case 2 : color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break;
37488       case 3 : color_type = PNG_COLOR_TYPE_RGB; break;
37489       default : color_type = PNG_COLOR_TYPE_RGB_ALPHA;
37490       }
37491       const int interlace_type = PNG_INTERLACE_NONE;
37492       const int compression_type = PNG_COMPRESSION_TYPE_DEFAULT;
37493       const int filter_method = PNG_FILTER_TYPE_DEFAULT;
37494       png_set_IHDR(png_ptr,info_ptr,_width,_height,bit_depth,color_type,interlace_type,compression_type,filter_method);
37495       png_write_info(png_ptr,info_ptr);
37496       const int byte_depth = bit_depth>>3;
37497       const int numChan = spectrum()>4?4:spectrum();
37498       const int pixel_bit_depth_flag = numChan * (bit_depth-1);
37499 
37500       // Allocate Memory for Image Save and Fill pixel data
37501       png_bytep *const imgData = new png_byte*[_height];
37502       for (unsigned int row = 0; row<_height; ++row) imgData[row] = new png_byte[byte_depth*numChan*_width];
37503       const T *pC0 = data(0,0,0,0);
37504       switch (pixel_bit_depth_flag) {
37505       case 7 :  { // Gray 8-bit
37506         cimg_forY(*this,y) {
37507           unsigned char *ptrd = imgData[y];
37508           cimg_forX(*this,x) *(ptrd++) = (unsigned char)*(pC0++);
37509         }
37510       } break;
37511       case 14 : { // Gray w/ Alpha 8-bit
37512         const T *pC1 = data(0,0,0,1);
37513         cimg_forY(*this,y) {
37514           unsigned char *ptrd = imgData[y];
37515           cimg_forX(*this,x) {
37516             *(ptrd++) = (unsigned char)*(pC0++);
37517             *(ptrd++) = (unsigned char)*(pC1++);
37518           }
37519         }
37520       } break;
37521       case 21 :  { // RGB 8-bit
37522         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2);
37523         cimg_forY(*this,y) {
37524           unsigned char *ptrd = imgData[y];
37525           cimg_forX(*this,x) {
37526             *(ptrd++) = (unsigned char)*(pC0++);
37527             *(ptrd++) = (unsigned char)*(pC1++);
37528             *(ptrd++) = (unsigned char)*(pC2++);
37529           }
37530         }
37531       } break;
37532       case 28 : { // RGB x/ Alpha 8-bit
37533         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3);
37534         cimg_forY(*this,y){
37535           unsigned char *ptrd = imgData[y];
37536           cimg_forX(*this,x){
37537             *(ptrd++) = (unsigned char)*(pC0++);
37538             *(ptrd++) = (unsigned char)*(pC1++);
37539             *(ptrd++) = (unsigned char)*(pC2++);
37540             *(ptrd++) = (unsigned char)*(pC3++);
37541           }
37542         }
37543       } break;
37544       case 15 : { // Gray 16-bit
37545         cimg_forY(*this,y){
37546           unsigned short *ptrd = (unsigned short*)(imgData[y]);
37547           cimg_forX(*this,x) *(ptrd++) = (unsigned short)*(pC0++);
37548           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],_width);
37549         }
37550       } break;
37551       case 30 : { // Gray w/ Alpha 16-bit
37552         const T *pC1 = data(0,0,0,1);
37553         cimg_forY(*this,y){
37554           unsigned short *ptrd = (unsigned short*)(imgData[y]);
37555           cimg_forX(*this,x) {
37556             *(ptrd++) = (unsigned short)*(pC0++);
37557             *(ptrd++) = (unsigned short)*(pC1++);
37558           }
37559           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],2*_width);
37560         }
37561       } break;
37562       case 45 : { // RGB 16-bit
37563         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2);
37564         cimg_forY(*this,y) {
37565           unsigned short *ptrd = (unsigned short*)(imgData[y]);
37566           cimg_forX(*this,x) {
37567             *(ptrd++) = (unsigned short)*(pC0++);
37568             *(ptrd++) = (unsigned short)*(pC1++);
37569             *(ptrd++) = (unsigned short)*(pC2++);
37570           }
37571           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],3*_width);
37572         }
37573       } break;
37574       case 60 : { // RGB w/ Alpha 16-bit
37575         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3);
37576         cimg_forY(*this,y) {
37577           unsigned short *ptrd = (unsigned short*)(imgData[y]);
37578           cimg_forX(*this,x) {
37579             *(ptrd++) = (unsigned short)*(pC0++);
37580             *(ptrd++) = (unsigned short)*(pC1++);
37581             *(ptrd++) = (unsigned short)*(pC2++);
37582             *(ptrd++) = (unsigned short)*(pC3++);
37583           }
37584           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],4*_width);
37585         }
37586       } break;
37587       default :
37588         if (!file) cimg::fclose(nfile);
37589         throw CImgIOException(_cimg_instance
37590                               "save_png() : Encountered unknown fatal error in libpng when saving file '%s'.",
37591                               cimg_instance,
37592                               nfilename?nfilename:"(FILE*)");
37593       }
37594       png_write_image(png_ptr,imgData);
37595       png_write_end(png_ptr,info_ptr);
37596       png_destroy_write_struct(&png_ptr, &info_ptr);
37597 
37598       // Deallocate Image Write Memory
37599       cimg_forY(*this,n) delete[] imgData[n];
37600       delete[] imgData;
37601       if (!file) cimg::fclose(nfile);
37602       return *this;
37603 #endif
37604     }
37605 
37606     //! Save a file in PNG format
37607     const CImg<T>& save_png(const char *const filename, const unsigned int bytes_per_pixel=0) const {
37608       return _save_png(0,filename,bytes_per_pixel);
37609     }
37610 
37611     //! Save a file in PNG format
37612     const CImg<T>& save_png(std::FILE *const file, const unsigned int bytes_per_pixel=0) const {
37613       return _save_png(file,0,bytes_per_pixel);
37614     }
37615 
37616     //! Save the image as a PNM file.
37617     const CImg<T>& save_pnm(const char *const filename, const unsigned int bytes_per_pixel=0) const {
37618       return _save_pnm(0,filename,bytes_per_pixel);
37619     }
37620 
37621     //! Save the image as a PNM file.
37622     const CImg<T>& save_pnm(std::FILE *const file, const unsigned int bytes_per_pixel=0) const {
37623       return _save_pnm(file,0,bytes_per_pixel);
37624     }
37625 
37626     // Save the image as a PNM file (internal function).
37627     const CImg<T>& _save_pnm(std::FILE *const file, const char *const filename, const unsigned int bytes_per_pixel=0) const {
37628       if (!file && !filename)
37629         throw CImgArgumentException(_cimg_instance
37630                                     "save_pnm() : Specified filename is (null).",
37631                                     cimg_instance);
37632       if (is_empty())
37633         throw CImgInstanceException(_cimg_instance
37634                                     "save_pnm() : Empty instance, for file '%s'.",
37635                                     cimg_instance,
37636                                     filename?filename:"(FILE*)");
37637 
37638       double stmin, stmax = (double)max_min(stmin);
37639       if (_depth>1)
37640         cimg::warn(_cimg_instance
37641                    "save_pnm() : Instance is volumetric, only the first slice will be saved in file '%s'.",
37642                    cimg_instance,
37643                    filename?filename:"(FILE*)");
37644 
37645       if (_spectrum>3)
37646         cimg::warn(_cimg_instance
37647                    "save_pnm() : Instance is multispectral, only the three first channels will be saved in file '%s'.",
37648                    cimg_instance,
37649                    filename?filename:"(FILE*)");
37650 
37651       if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
37652         cimg::warn(_cimg_instance
37653                    "save_pnm() : Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
37654                    cimg_instance,
37655                    stmin,stmax,filename?filename:"(FILE*)");
37656 
37657       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
37658       const T
37659         *ptr_r = data(0,0,0,0),
37660         *ptr_g = (_spectrum>=2)?data(0,0,0,1):0,
37661         *ptr_b = (_spectrum>=3)?data(0,0,0,2):0;
37662       const unsigned int buf_size = cimg::min(1024*1024U,_width*_height*(_spectrum==1?1:3));
37663 
37664       std::fprintf(nfile,"P%c\n%u %u\n%u\n",
37665                    (_spectrum==1?'5':'6'),_width,_height,stmax<256?255:(stmax<4096?4095:65535));
37666 
37667       switch (_spectrum) {
37668       case 1 : { // Scalar image
37669         if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PGM 8 bits
37670           CImg<ucharT> buf(buf_size);
37671           for (long to_write = _width*_height; to_write>0; ) {
37672             const unsigned long N = cimg::min((unsigned long)to_write,buf_size);
37673             unsigned char *ptrd = buf._data;
37674             for (long i = (long)N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr_r++);
37675             cimg::fwrite(buf._data,N,nfile);
37676             to_write-=N;
37677           }
37678         } else { // Binary PGM 16 bits
37679           CImg<ushortT> buf(buf_size);
37680           for (long to_write = _width*_height; to_write>0; ) {
37681             const unsigned long N = cimg::min((unsigned long)to_write,buf_size);
37682             unsigned short *ptrd = buf._data;
37683             for (long i = (long)N; i>0; --i) *(ptrd++) = (unsigned short)*(ptr_r++);
37684             if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
37685             cimg::fwrite(buf._data,N,nfile);
37686             to_write-=N;
37687           }
37688         }
37689       } break;
37690       case 2 : { // RG image
37691         if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits
37692           CImg<ucharT> buf(buf_size);
37693           for (long to_write = _width*_height; to_write>0; ) {
37694             const unsigned long N = cimg::min((unsigned long)to_write,buf_size/3);
37695             unsigned char *ptrd = buf._data;
37696             for (long i = (long)N; i>0; --i) {
37697               *(ptrd++) = (unsigned char)*(ptr_r++);
37698               *(ptrd++) = (unsigned char)*(ptr_g++);
37699               *(ptrd++) = 0;
37700             }
37701             cimg::fwrite(buf._data,3*N,nfile);
37702             to_write-=N;
37703           }
37704         } else {             // Binary PPM 16 bits
37705           CImg<ushortT> buf(buf_size);
37706           for (long to_write = _width*_height; to_write>0; ) {
37707             const unsigned long N = cimg::min((unsigned long)to_write,buf_size/3);
37708             unsigned short *ptrd = buf._data;
37709             for (long i = (long)N; i>0; --i) {
37710               *(ptrd++) = (unsigned short)*(ptr_r++);
37711               *(ptrd++) = (unsigned short)*(ptr_g++);
37712               *(ptrd++) = 0;
37713             }
37714             if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
37715             cimg::fwrite(buf._data,3*N,nfile);
37716             to_write-=N;
37717           }
37718         }
37719       } break;
37720       default : { // RGB image
37721         if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits
37722           CImg<ucharT> buf(buf_size);
37723           for (long to_write = _width*_height; to_write>0; ) {
37724             const unsigned long N = cimg::min((unsigned long)to_write,buf_size/3);
37725             unsigned char *ptrd = buf._data;
37726             for (long i = (long)N; i>0; --i) {
37727               *(ptrd++) = (unsigned char)*(ptr_r++);
37728               *(ptrd++) = (unsigned char)*(ptr_g++);
37729               *(ptrd++) = (unsigned char)*(ptr_b++);
37730             }
37731             cimg::fwrite(buf._data,3*N,nfile);
37732             to_write-=N;
37733           }
37734         } else {             // Binary PPM 16 bits
37735           CImg<ushortT> buf(buf_size);
37736           for (long to_write = _width*_height; to_write>0; ) {
37737             const unsigned long N = cimg::min((unsigned long)to_write,buf_size/3);
37738             unsigned short *ptrd = buf._data;
37739             for (long i = (long)N; i>0; --i) {
37740               *(ptrd++) = (unsigned short)*(ptr_r++);
37741               *(ptrd++) = (unsigned short)*(ptr_g++);
37742               *(ptrd++) = (unsigned short)*(ptr_b++);
37743             }
37744             if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
37745             cimg::fwrite(buf._data,3*N,nfile);
37746             to_write-=N;
37747           }
37748         }
37749       }
37750       }
37751       if (!file) cimg::fclose(nfile);
37752       return *this;
37753     }
37754 
37755     //! Save the image as a PNK file (PINK library extension of PGM).
37756     const CImg<T>& save_pnk(const char *const filename) const {
37757       return _save_pnk(0,filename);
37758     }
37759 
37760     //! Save the image as a PNk file (PINK library extension of PGM).
37761     const CImg<T>& save_pnk(std::FILE *const file) const {
37762       return _save_pnk(file,0);
37763     }
37764 
37765     // Save the image as a PNK file (internal function).
37766     const CImg<T>& _save_pnk(std::FILE *const file, const char *const filename) const {
37767       if (!file && !filename)
37768         throw CImgArgumentException(_cimg_instance
37769                                     "save_pnk() : Specified filename is (null).",
37770                                     cimg_instance);
37771       if (is_empty())
37772         throw CImgInstanceException(_cimg_instance
37773                                     "save_pnk() : Empty instance, for file '%s'.",
37774                                     cimg_instance,
37775                                     filename?filename:"(FILE*)");
37776       if (_spectrum>1)
37777         cimg::warn(_cimg_instance
37778                    "save_pnk() : Instance is multispectral, only the first channel will be saved in file '%s'.",
37779                    cimg_instance,
37780                    filename?filename:"(FILE*)");
37781 
37782       const unsigned long buf_size = cimg::min(1024*1024LU,_width*_height*_depth);
37783       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
37784       const T *ptr = data(0,0,0,0);
37785 
37786       if (!cimg::type<T>::is_float() && sizeof(T)==1 && _depth<2) _save_pnm(file,filename,0); // Can be saved as regular PNM file.
37787       else if (!cimg::type<T>::is_float() && sizeof(T)==1) { // Save as extended P5 file : Binary byte-valued 3d.
37788         std::fprintf(nfile,"P5\n%u %u %u\n255\n",_width,_height,_depth);
37789         CImg<ucharT> buf(buf_size);
37790         for (long to_write = _width*_height*_depth; to_write>0; ) {
37791           const unsigned long N = cimg::min((unsigned long)to_write,buf_size);
37792           unsigned char *ptrd = buf._data;
37793           for (long i = (long)N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr++);
37794           cimg::fwrite(buf._data,N,nfile);
37795           to_write-=N;
37796         }
37797       } else if (!cimg::type<T>::is_float()) { // Save as P8 : Binary int32-valued 3d.
37798         if (_depth>1) std::fprintf(nfile,"P8\n%u %u %u\n%d\n",_width,_height,_depth,(int)max());
37799         else std::fprintf(nfile,"P8\n%u %u\n%d\n",_width,_height,(int)max());
37800         CImg<intT> buf(buf_size);
37801         for (long to_write = _width*_height*_depth; to_write>0; ) {
37802           const unsigned long N = cimg::min((unsigned long)to_write,buf_size);
37803           int *ptrd = buf._data;
37804           for (long i = (long)N; i>0; --i) *(ptrd++) = (int)*(ptr++);
37805           cimg::fwrite(buf._data,N,nfile);
37806           to_write-=N;
37807         }
37808       } else { // Save as P9 : Binary float-valued 3d.
37809         if (_depth>1) std::fprintf(nfile,"P9\n%u %u %u\n%g\n",_width,_height,_depth,(double)max());
37810         else std::fprintf(nfile,"P9\n%u %u\n%g\n",_width,_height,(double)max());
37811         CImg<floatT> buf(buf_size);
37812         for (long to_write = _width*_height*_depth; to_write>0; ) {
37813           const unsigned long N = cimg::min((unsigned long)to_write,buf_size);
37814           float *ptrd = buf._data;
37815           for (long i = (long)N; i>0; --i) *(ptrd++) = (float)*(ptr++);
37816           cimg::fwrite(buf._data,N,nfile);
37817           to_write-=N;
37818         }
37819       }
37820 
37821       if (!file) cimg::fclose(nfile);
37822       return *this;
37823     }
37824 
37825     //! Save the image as a PFM file.
37826     const CImg<T>& save_pfm(const char *const filename) const {
37827       return _save_pfm(0,filename);
37828     }
37829 
37830     //! Save the image as a PFM file.
37831     const CImg<T>& save_pfm(std::FILE *const file) const {
37832       return _save_pfm(file,0);
37833     }
37834 
37835     // Save the image as a PFM file (internal function).
37836     const CImg<T>& _save_pfm(std::FILE *const file, const char *const filename) const {
37837       if (!file && !filename)
37838         throw CImgArgumentException(_cimg_instance
37839                                     "save_pfm() : Specified filename is (null).",
37840                                     cimg_instance);
37841       if (is_empty())
37842         throw CImgInstanceException(_cimg_instance
37843                                     "save_pfm() : Empty instance, for file '%s'.",
37844                                     cimg_instance,
37845                                     filename?filename:"(FILE*)");
37846       if (_depth>1)
37847         cimg::warn(_cimg_instance
37848                    "save_pfm() : Instance is volumetric, only the first slice will be saved in file '%s'.",
37849                    cimg_instance,
37850                    filename?filename:"(FILE*)");
37851 
37852       if (_spectrum>3)
37853         cimg::warn(_cimg_instance
37854                    "save_pfm() : image instance is multispectral, only the three first channels will be saved in file '%s'.",
37855                    cimg_instance,
37856                    filename?filename:"(FILE*)");
37857 
37858       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
37859       const T
37860         *ptr_r = data(0,0,0,0),
37861         *ptr_g = (_spectrum>=2)?data(0,0,0,1):0,
37862         *ptr_b = (_spectrum>=3)?data(0,0,0,2):0;
37863       const unsigned int buf_size = cimg::min(1024*1024U,_width*_height*(_spectrum==1?1:3));
37864 
37865       std::fprintf(nfile,"P%c\n%u %u\n1.0\n",
37866                    (_spectrum==1?'f':'F'),_width,_height);
37867 
37868       switch (_spectrum) {
37869       case 1 : { // Scalar image
37870         CImg<floatT> buf(buf_size);
37871         for (int to_write = _width*_height; to_write>0; ) {
37872           const unsigned int N = cimg::min((unsigned int)to_write,buf_size);
37873           float *ptrd = buf._data;
37874           for (int i = (int)N; i>0; --i) *(ptrd++) = (float)*(ptr_r++);
37875           if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
37876           cimg::fwrite(buf._data,N,nfile);
37877           to_write-=N;
37878         }
37879       } break;
37880       case 2 : { // RG image
37881         CImg<floatT> buf(buf_size);
37882         for (int to_write = _width*_height; to_write>0; ) {
37883           const unsigned int N = cimg::min((unsigned int)to_write,buf_size/3);
37884           float *ptrd = buf._data;
37885           for (int i = (int)N; i>0; --i) {
37886             *(ptrd++) = (float)*(ptr_r++);
37887             *(ptrd++) = (float)*(ptr_g++);
37888             *(ptrd++) = 0;
37889           }
37890           if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
37891           cimg::fwrite(buf._data,3*N,nfile);
37892           to_write-=N;
37893         }
37894       } break;
37895       default : { // RGB image
37896         CImg<floatT> buf(buf_size);
37897         for (int to_write = _width*_height; to_write>0; ) {
37898           const unsigned int N = cimg::min((unsigned int)to_write,buf_size/3);
37899           float *ptrd = buf._data;
37900           for (int i = (int)N; i>0; --i) {
37901             *(ptrd++) = (float)*(ptr_r++);
37902             *(ptrd++) = (float)*(ptr_g++);
37903             *(ptrd++) = (float)*(ptr_b++);
37904           }
37905           if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
37906           cimg::fwrite(buf._data,3*N,nfile);
37907           to_write-=N;
37908         }
37909       }
37910       }
37911       if (!file) cimg::fclose(nfile);
37912       return *this;
37913     }
37914 
37915     // Save the image as a RGB file (internal).
37916     const CImg<T>& _save_rgb(std::FILE *const file, const char *const filename) const {
37917       if (!file && !filename)
37918         throw CImgArgumentException(_cimg_instance
37919                                     "save_rgb() : Specified filename is (null).",
37920                                     cimg_instance);
37921       if (is_empty())
37922         throw CImgInstanceException(_cimg_instance
37923                                     "save_rgb() : Empty instance, for file '%s'.",
37924                                     cimg_instance,
37925                                     filename?filename:"(FILE*)");
37926       if (_spectrum!=3)
37927         cimg::warn(_cimg_instance
37928                    "save_rgb() : image instance has not exactly 3 channels, for file '%s'.",
37929                    cimg_instance,
37930                    filename?filename:"(FILE*)");
37931 
37932       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
37933       const unsigned int wh = _width*_height;
37934       unsigned char *const buffer = new unsigned char[3*wh], *nbuffer = buffer;
37935       const T
37936         *ptr1 = data(0,0,0,0),
37937         *ptr2 = _spectrum>1?data(0,0,0,1):0,
37938         *ptr3 = _spectrum>2?data(0,0,0,2):0;
37939       switch (_spectrum) {
37940       case 1 : { // Scalar image
37941         for (unsigned int k = 0; k<wh; ++k) {
37942           const unsigned char val = (unsigned char)*(ptr1++);
37943           *(nbuffer++) = val;
37944           *(nbuffer++) = val;
37945           *(nbuffer++) = val;
37946         }
37947       } break;
37948       case 2 : { // RG image
37949         for (unsigned int k = 0; k<wh; ++k) {
37950           *(nbuffer++) = (unsigned char)(*(ptr1++));
37951           *(nbuffer++) = (unsigned char)(*(ptr2++));
37952           *(nbuffer++) = 0;
37953         }
37954       } break;
37955       default : { // RGB image
37956         for (unsigned int k = 0; k<wh; ++k) {
37957           *(nbuffer++) = (unsigned char)(*(ptr1++));
37958           *(nbuffer++) = (unsigned char)(*(ptr2++));
37959           *(nbuffer++) = (unsigned char)(*(ptr3++));
37960         }
37961       }
37962       }
37963       cimg::fwrite(buffer,3*wh,nfile);
37964       if (!file) cimg::fclose(nfile);
37965       delete[] buffer;
37966       return *this;
37967     }
37968 
37969     //! Save the image as a RGB file.
37970     const CImg<T>& save_rgb(const char *const filename) const {
37971       return _save_rgb(0,filename);
37972     }
37973 
37974     //! Save the image as a RGB file.
37975     const CImg<T>& save_rgb(std::FILE *const file) const {
37976       return _save_rgb(file,0);
37977     }
37978 
37979     // Save the image as a RGBA file (internal).
37980     const CImg<T>& _save_rgba(std::FILE *const file, const char *const filename) const {
37981       if (!file && !filename)
37982         throw CImgArgumentException(_cimg_instance
37983                                     "save_rgba() : Specified filename is (null).",
37984                                     cimg_instance);
37985       if (is_empty())
37986         throw CImgInstanceException(_cimg_instance
37987                                     "save_rgba() : Empty instance, for file '%s'.",
37988                                     cimg_instance,
37989                                     filename?filename:"(FILE*)");
37990       if (_spectrum!=4)
37991         cimg::warn(_cimg_instance
37992                    "save_rgba() : image instance has not exactly 4 channels, for file '%s'.",
37993                    cimg_instance,
37994                    filename?filename:"(FILE*)");
37995 
37996       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
37997       const unsigned int wh = _width*_height;
37998       unsigned char *const buffer = new unsigned char[4*wh], *nbuffer = buffer;
37999       const T
38000         *ptr1 = data(0,0,0,0),
38001         *ptr2 = _spectrum>1?data(0,0,0,1):0,
38002         *ptr3 = _spectrum>2?data(0,0,0,2):0,
38003         *ptr4 = _spectrum>3?data(0,0,0,3):0;
38004       switch (_spectrum) {
38005       case 1 : { // Scalar images
38006         for (unsigned int k = 0; k<wh; ++k) {
38007           const unsigned char val = (unsigned char)*(ptr1++);
38008           *(nbuffer++) = val;
38009           *(nbuffer++) = val;
38010           *(nbuffer++) = val;
38011           *(nbuffer++) = 255;
38012         }
38013       } break;
38014       case 2 : { // RG images
38015         for (unsigned int k = 0; k<wh; ++k) {
38016           *(nbuffer++) = (unsigned char)(*(ptr1++));
38017           *(nbuffer++) = (unsigned char)(*(ptr2++));
38018           *(nbuffer++) = 0;
38019           *(nbuffer++) = 255;
38020         }
38021       } break;
38022       case 3 : { // RGB images
38023         for (unsigned int k = 0; k<wh; ++k) {
38024           *(nbuffer++) = (unsigned char)(*(ptr1++));
38025           *(nbuffer++) = (unsigned char)(*(ptr2++));
38026           *(nbuffer++) = (unsigned char)(*(ptr3++));
38027           *(nbuffer++) = 255;
38028         }
38029       } break;
38030       default : { // RGBA images
38031         for (unsigned int k = 0; k<wh; ++k) {
38032           *(nbuffer++) = (unsigned char)(*(ptr1++));
38033           *(nbuffer++) = (unsigned char)(*(ptr2++));
38034           *(nbuffer++) = (unsigned char)(*(ptr3++));
38035           *(nbuffer++) = (unsigned char)(*(ptr4++));
38036         }
38037       }
38038       }
38039       cimg::fwrite(buffer,4*wh,nfile);
38040       if (!file) cimg::fclose(nfile);
38041       delete[] buffer;
38042       return *this;
38043     }
38044 
38045     //! Save the image as a RGBA file.
38046     const CImg<T>& save_rgba(const char *const filename) const {
38047       return _save_rgba(0,filename);
38048     }
38049 
38050     //! Save the image as a RGBA file.
38051     const CImg<T>& save_rgba(std::FILE *const file) const {
38052       return _save_rgba(file,0);
38053     }
38054 
38055     // Save a plane into a tiff file
38056 #ifdef cimg_use_tiff
38057 
38058 #define _cimg_save_tiff(types,typed,compression) \
38059     if (!std::strcmp(types,pixel_type())) { const typed foo = (typed)0; return _save_tiff(tif,directory,foo,compression); }
38060 
38061     template<typename t>
38062     const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory, const t& pixel_t, const unsigned int compression) const {
38063       if (is_empty() || !tif || pixel_t) return *this;
38064       const char *const filename = TIFFFileName(tif);
38065       uint32 rowsperstrip = (uint32)-1;
38066       uint16 spp = _spectrum, bpp = sizeof(t)*8, photometric;
38067       if (spp==3 || spp==4) photometric = PHOTOMETRIC_RGB;
38068       else photometric = PHOTOMETRIC_MINISBLACK;
38069       TIFFSetDirectory(tif,directory);
38070       TIFFSetField(tif,TIFFTAG_IMAGEWIDTH,_width);
38071       TIFFSetField(tif,TIFFTAG_IMAGELENGTH,_height);
38072       TIFFSetField(tif,TIFFTAG_ORIENTATION,ORIENTATION_TOPLEFT);
38073       TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,spp);
38074       if (cimg::type<t>::is_float()) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,3);
38075       else if (cimg::type<t>::min()==0) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,1);
38076       else TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,2);
38077       TIFFSetField(tif,TIFFTAG_BITSPERSAMPLE,bpp);
38078       TIFFSetField(tif,TIFFTAG_PLANARCONFIG,PLANARCONFIG_CONTIG);
38079       TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,photometric);
38080       TIFFSetField(tif,TIFFTAG_COMPRESSION,compression?(compression-1):COMPRESSION_NONE);
38081       rowsperstrip = TIFFDefaultStripSize(tif,rowsperstrip);
38082       TIFFSetField(tif,TIFFTAG_ROWSPERSTRIP,rowsperstrip);
38083       TIFFSetField(tif,TIFFTAG_FILLORDER,FILLORDER_MSB2LSB);
38084       TIFFSetField(tif,TIFFTAG_SOFTWARE,"CImg");
38085       t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
38086       if (buf) {
38087         for (unsigned int row = 0; row<_height; row+=rowsperstrip) {
38088           uint32 nrow = (row + rowsperstrip>_height?_height-row:rowsperstrip);
38089           tstrip_t strip = TIFFComputeStrip(tif,row,0);
38090           tsize_t i = 0;
38091           for (unsigned int rr = 0; rr<nrow; ++rr)
38092             for (unsigned int cc = 0; cc<_width; ++cc)
38093               for (unsigned int vv = 0; vv<spp; ++vv)
38094                 buf[i++] = (t)(*this)(cc,row + rr,0,vv);
38095           if (TIFFWriteEncodedStrip(tif,strip,buf,i*sizeof(t))<0)
38096             throw CImgException(_cimg_instance
38097                                 "save_tiff() : Invalid strip writing when saving file '%s'.",
38098                                 cimg_instance,
38099                                 filename?filename:"(FILE*)");
38100         }
38101         _TIFFfree(buf);
38102       }
38103       TIFFWriteDirectory(tif);
38104       return (*this);
38105     }
38106 
38107     const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int compression) const {
38108       _cimg_save_tiff("bool",unsigned char,compression);
38109       _cimg_save_tiff("char",char,compression);
38110       _cimg_save_tiff("unsigned char",unsigned char,compression);
38111       _cimg_save_tiff("short",short,compression);
38112       _cimg_save_tiff("unsigned short",unsigned short,compression);
38113       _cimg_save_tiff("int",int,compression);
38114       _cimg_save_tiff("unsigned int",unsigned int,compression);
38115       _cimg_save_tiff("long",int,compression);
38116       _cimg_save_tiff("unsigned long",unsigned int,compression);
38117       _cimg_save_tiff("float",float,compression);
38118       _cimg_save_tiff("double",float,compression);
38119       const char *const filename = TIFFFileName(tif);
38120       throw CImgException(_cimg_instance
38121                           "save_tiff() : Unsupported pixel type '%s' for file '%s'.",
38122                           cimg_instance,
38123                           pixel_type(),filename?filename:"(FILE*)");
38124       return *this;
38125     }
38126 #endif
38127 
38128     //! Save a file in TIFF format.
38129     /**
38130        - libtiff support is enabled by defining the precompilation
38131         directive cimg_use_tif.
38132 
38133        - When libtiff is enabled, 2D and 3D (multipage) several
38134         channel per pixel are supported for
38135         char,uchar,short,ushort,float and double pixel type.
38136 
38137        - If cimg_use_tif is not defined at compilation time the
38138         function uses CImg<T>&save_other(const char*).
38139 
38140        \param compression 1:None, 2:CCITTRLE, 3:CCITTFAX3, 4:CCITTFAX4, 5:LZW, 6:JPEG
38141 
38142        \see CImg<T>& save_other(const char*)
38143        \see CImg<T>& load_tiff(const char*)
38144      **/
38145     const CImg<T>& save_tiff(const char *const filename, const unsigned int compression=0) const {
38146       if (!filename)
38147         throw CImgArgumentException(_cimg_instance
38148                                     "save_tiff() : Specified filename is (null).",
38149                                     cimg_instance);
38150       if (is_empty())
38151         throw CImgInstanceException(_cimg_instance
38152                                     "save_tiff() : Empty instance, for file '%s'.",
38153                                     cimg_instance,
38154                                     filename);
38155 
38156 #ifdef cimg_use_tiff
38157       TIFF *tif = TIFFOpen(filename,"w");
38158       if (tif) {
38159         cimg_forZ(*this,z) get_slice(z)._save_tiff(tif,z,compression);
38160         TIFFClose(tif);
38161       } else throw CImgException(_cimg_instance
38162                                  "save_tiff() : Failed to open file '%s' for writing.",
38163                                  cimg_instance,
38164                                  filename);
38165 #else
38166       cimg::unused(compression);
38167       return save_other(filename);
38168 #endif
38169       return *this;
38170     }
38171 
38172     //! Save the image as a MINC2 file.
38173     // (Original code by Haz-Edine Assemlal).
38174     const CImg<T>& save_minc2(const char *const filename) const {
38175       if (!filename)
38176         throw CImgArgumentException(_cimg_instance
38177                                     "save_minc2() : Specified filename is (null).",
38178                                     cimg_instance);
38179       if (is_empty())
38180         throw CImgInstanceException(_cimg_instance
38181                                     "save_minc2() : Empty instance, for file '%s'.",
38182                                     cimg_instance,
38183                                     filename);
38184 #ifndef cimg_use_minc2
38185       return save_other(filename);
38186 #else
38187       int nb_useful_dims = 0;
38188       if (width()!=1) ++nb_useful_dims;
38189       if (height()!=1) ++nb_useful_dims;
38190       if (depth()!=1) ++nb_useful_dims;
38191       if (spectrum()!=1) ++nb_useful_dims;
38192 
38193       mihandle_t hvol;
38194       midimhandle_t *const hdims = (midimhandle_t*)std::malloc(nb_useful_dims*sizeof(midimhandle_t));
38195       CImg<ulongT>
38196         start(nb_useful_dims,1,1,1,0),
38197         count(nb_useful_dims,1,1,1,0);
38198       CImg<floatT> output(*this);
38199       const char* dim_name[] = {"xspace", "yspace", "zspace", "time"};
38200       const midimclass_t dim_class[] = { MI_DIMCLASS_SPATIAL,MI_DIMCLASS_SPATIAL,MI_DIMCLASS_SPATIAL,MI_DIMCLASS_TIME };
38201       const midimattr_t dim_attr = MI_DIMATTR_REGULARLY_SAMPLED;
38202 
38203       // Strange stuffs here
38204       /* The conversion from CImg data to Minc works only for
38205        * specific orders (increasing dimensions and data in decreasing)
38206        */
38207       CImg<charT> permutations, permutations_inv;
38208       CImg<uintT>
38209         dim_vec = CImg<uintT>::vector(width(),height(),depth(),spectrum()),
38210         dim_sort = dim_vec.get_sort(permutations,true);
38211       dim_vec.get_sort(permutations_inv,false);
38212 
38213       int d = 0;
38214       cimg_foroff(dim_sort,dv) {
38215         if (dim_sort(dv)<=1) continue;
38216         micreate_dimension(dim_name[(int)permutations(dv)],dim_class[(int)permutations(dv)],
38217                            dim_attr,dim_sort(dv),&hdims[d]);
38218         count(d++) = dim_sort(dv);
38219       }
38220       cimg_for(permutations_inv,ptr,char) if (*ptr<3) (*ptr)+='x'; else *ptr='c';
38221       output.permute_axes(permutations_inv.append(CImg<uintT>::vector(0),'y').data()).mirror('y');
38222 
38223       // End of strange stuffs.
38224       micreate_volume(filename,nb_useful_dims,hdims,MI_TYPE_FLOAT,MI_CLASS_REAL,0,&hvol);
38225       micreate_volume_image(hvol);
38226       miset_volume_range(hvol,max(),min());
38227       miset_voxel_value_hyperslab(hvol,MI_TYPE_FLOAT,start,count,output.data());
38228       miclose_volume(hvol);
38229       mifree_dimension_handle(*hdims);
38230       return *this;
38231 #endif
38232     }
38233 
38234     //! Save the image as an ANALYZE7.5 or NIFTI file.
38235     const CImg<T>& save_analyze(const char *const filename, const float *const voxsize=0) const {
38236       if (!filename)
38237         throw CImgArgumentException(_cimg_instance
38238                                     "save_analyze() : Specified filename is (null).",
38239                                     cimg_instance);
38240       if (is_empty())
38241         throw CImgInstanceException(_cimg_instance
38242                                     "save_analyze() : Empty instance, for file '%s'.",
38243                                     cimg_instance,
38244                                     filename);
38245 
38246       std::FILE *file;
38247       char header[348] = { 0 }, hname[1024] = { 0 }, iname[1024] = { 0 };
38248       const char *const ext = cimg::split_filename(filename);
38249       short datatype=-1;
38250       std::memset(header,0,348);
38251       if (!*ext) { cimg_snprintf(hname,sizeof(hname),"%s.hdr",filename); cimg_snprintf(iname,sizeof(iname),"%s.img",filename); }
38252       if (!cimg::strncasecmp(ext,"hdr",3)) {
38253         std::strcpy(hname,filename); std::strncpy(iname,filename,sizeof(iname)-1); std::sprintf(iname + std::strlen(iname)-3,"img");
38254       }
38255       if (!cimg::strncasecmp(ext,"img",3)) {
38256         std::strcpy(hname,filename); std::strncpy(iname,filename,sizeof(iname)-1); std::sprintf(hname + std::strlen(iname)-3,"hdr");
38257       }
38258       if (!cimg::strncasecmp(ext,"nii",3)) {
38259         std::strncpy(hname,filename,sizeof(hname)-1); *iname = 0;
38260       }
38261       int *const iheader = (int*)header;
38262       *iheader = 348;
38263       std::strcpy(header + 4,"CImg");
38264       std::strcpy(header + 14," ");
38265       ((short*)(header + 36))[0] = 4096;
38266       ((char*)(header + 38))[0] = 114;
38267       ((short*)(header + 40))[0] = 4;
38268       ((short*)(header + 40))[1] = _width;
38269       ((short*)(header + 40))[2] = _height;
38270       ((short*)(header + 40))[3] = _depth;
38271       ((short*)(header + 40))[4] = _spectrum;
38272       if (!cimg::strcasecmp(pixel_type(),"bool")) datatype = 2;
38273       if (!cimg::strcasecmp(pixel_type(),"unsigned char")) datatype = 2;
38274       if (!cimg::strcasecmp(pixel_type(),"char")) datatype = 2;
38275       if (!cimg::strcasecmp(pixel_type(),"unsigned short")) datatype = 4;
38276       if (!cimg::strcasecmp(pixel_type(),"short")) datatype = 4;
38277       if (!cimg::strcasecmp(pixel_type(),"unsigned int")) datatype = 8;
38278       if (!cimg::strcasecmp(pixel_type(),"int")) datatype = 8;
38279       if (!cimg::strcasecmp(pixel_type(),"unsigned long")) datatype = 8;
38280       if (!cimg::strcasecmp(pixel_type(),"long")) datatype = 8;
38281       if (!cimg::strcasecmp(pixel_type(),"float")) datatype = 16;
38282       if (!cimg::strcasecmp(pixel_type(),"double")) datatype = 64;
38283       if (datatype<0)
38284         throw CImgIOException(_cimg_instance
38285                               "save_analyze() : Unsupported pixel type '%s' for file '%s'.",
38286                               cimg_instance,
38287                               pixel_type(),filename);
38288 
38289       ((short*)(header+70))[0] = datatype;
38290       ((short*)(header+72))[0] = sizeof(T);
38291       ((float*)(header+112))[0] = 1;
38292       ((float*)(header+76))[0] = 0;
38293       if (voxsize) {
38294         ((float*)(header+76))[1] = voxsize[0];
38295         ((float*)(header+76))[2] = voxsize[1];
38296         ((float*)(header+76))[3] = voxsize[2];
38297       } else ((float*)(header+76))[1] = ((float*)(header+76))[2] = ((float*)(header+76))[3] = 1;
38298       file = cimg::fopen(hname,"wb");
38299       cimg::fwrite(header,348,file);
38300       if (*iname) { cimg::fclose(file); file = cimg::fopen(iname,"wb"); }
38301       cimg::fwrite(_data,size(),file);
38302       cimg::fclose(file);
38303       return *this;
38304     }
38305 
38306     //! Save the image as a .cimg file.
38307     const CImg<T>& save_cimg(const char *const filename, const bool compression=false) const {
38308       CImgList<T>(*this,true).save_cimg(filename,compression);
38309       return *this;
38310     }
38311 
38312     // Save the image as a .cimg file.
38313     const CImg<T>& save_cimg(std::FILE *const file, const bool compression=false) const {
38314       CImgList<T>(*this,true).save_cimg(file,compression);
38315       return *this;
38316     }
38317 
38318     //! Insert the image into an existing .cimg file, at specified coordinates.
38319     const CImg<T>& save_cimg(const char *const filename,
38320                              const unsigned int n0,
38321                              const unsigned int x0, const unsigned int y0,
38322                              const unsigned int z0, const unsigned int c0) const {
38323       CImgList<T>(*this,true).save_cimg(filename,n0,x0,y0,z0,c0);
38324       return *this;
38325     }
38326 
38327     //! Insert the image into an existing .cimg file, at specified coordinates.
38328     const CImg<T>& save_cimg(std::FILE *const file,
38329                              const unsigned int n0,
38330                              const unsigned int x0, const unsigned int y0,
38331                              const unsigned int z0, const unsigned int c0) const {
38332       CImgList<T>(*this,true).save_cimg(file,n0,x0,y0,z0,c0);
38333       return *this;
38334     }
38335 
38336     //! Save an empty .cimg file with specified dimensions.
38337     static void save_empty_cimg(const char *const filename,
38338                                 const unsigned int dx, const unsigned int dy=1,
38339                                 const unsigned int dz=1, const unsigned int dc=1) {
38340       return CImgList<T>::save_empty_cimg(filename,1,dx,dy,dz,dc);
38341     }
38342 
38343     //! Save an empty .cimg file with specified dimensions.
38344     static void save_empty_cimg(std::FILE *const file,
38345                                 const unsigned int dx, const unsigned int dy=1,
38346                                 const unsigned int dz=1, const unsigned int dc=1) {
38347       return CImgList<T>::save_empty_cimg(file,1,dx,dy,dz,dc);
38348     }
38349 
38350     // Save the image as an INRIMAGE-4 file (internal).
38351     const CImg<T>& _save_inr(std::FILE *const file, const char *const filename, const float *const voxsize) const {
38352       if (!file && !filename)
38353         throw CImgArgumentException(_cimg_instance
38354                                     "save_inr() : Specified filename is (null).",
38355                                     cimg_instance);
38356       if (is_empty())
38357         throw CImgInstanceException(_cimg_instance
38358                                     "save_inr() : Empty instance, for file '%s'.",
38359                                     cimg_instance,
38360                                     filename?filename:"(FILE*)");
38361 
38362       int inrpixsize=-1;
38363       const char *inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0";
38364       if (!cimg::strcasecmp(pixel_type(),"unsigned char"))  { inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1; }
38365       if (!cimg::strcasecmp(pixel_type(),"char"))           { inrtype = "fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1; }
38366       if (!cimg::strcasecmp(pixel_type(),"unsigned short")) { inrtype = "unsigned fixed\nPIXSIZE=16 bits\nSCALE=2**0";inrpixsize = 2; }
38367       if (!cimg::strcasecmp(pixel_type(),"short"))          { inrtype = "fixed\nPIXSIZE=16 bits\nSCALE=2**0"; inrpixsize = 2; }
38368       if (!cimg::strcasecmp(pixel_type(),"unsigned int"))   { inrtype = "unsigned fixed\nPIXSIZE=32 bits\nSCALE=2**0";inrpixsize = 4; }
38369       if (!cimg::strcasecmp(pixel_type(),"int"))            { inrtype = "fixed\nPIXSIZE=32 bits\nSCALE=2**0"; inrpixsize = 4; }
38370       if (!cimg::strcasecmp(pixel_type(),"float"))          { inrtype = "float\nPIXSIZE=32 bits"; inrpixsize = 4; }
38371       if (!cimg::strcasecmp(pixel_type(),"double"))         { inrtype = "float\nPIXSIZE=64 bits"; inrpixsize = 8; }
38372       if (inrpixsize<=0)
38373         throw CImgIOException(_cimg_instance
38374                               "save_inr() : Unsupported pixel type '%s' for file '%s'",
38375                               cimg_instance,
38376                               pixel_type(),filename?filename:"(FILE*)");
38377 
38378       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
38379       char header[257] = { 0 };
38380       int err = cimg_snprintf(header,sizeof(header),"#INRIMAGE-4#{\nXDIM=%u\nYDIM=%u\nZDIM=%u\nVDIM=%u\n",_width,_height,_depth,_spectrum);
38381       if (voxsize) err+=std::sprintf(header + err,"VX=%g\nVY=%g\nVZ=%g\n",voxsize[0],voxsize[1],voxsize[2]);
38382       err+=std::sprintf(header + err,"TYPE=%s\nCPU=%s\n",inrtype,cimg::endianness()?"sun":"decm");
38383       std::memset(header + err,'\n',252 - err);
38384       std::memcpy(header + 252,"##}\n",4);
38385       cimg::fwrite(header,256,nfile);
38386       cimg_forXYZ(*this,x,y,z) cimg_forC(*this,c) cimg::fwrite(&((*this)(x,y,z,c)),1,nfile);
38387       if (!file) cimg::fclose(nfile);
38388       return *this;
38389     }
38390 
38391     //! Save the image as an INRIMAGE-4 file.
38392     const CImg<T>& save_inr(const char *const filename, const float *const voxsize=0) const {
38393       return _save_inr(0,filename,voxsize);
38394     }
38395 
38396     //! Save the image as an INRIMAGE-4 file.
38397     const CImg<T>& save_inr(std::FILE *const file, const float *const voxsize=0) const {
38398       return _save_inr(file,0,voxsize);
38399     }
38400 
38401     //! Save the image as a EXR file.
38402     const CImg<T>& save_exr(const char *const filename) const {
38403       if (!filename)
38404         throw CImgArgumentException(_cimg_instance
38405                                     "save_exr() : Specified filename is (null).",
38406                                     cimg_instance);
38407       if (is_empty())
38408         throw CImgInstanceException(_cimg_instance
38409                                     "save_exr() : Empty instance, for file '%s'.",
38410                                     cimg_instance,
38411                                     filename);
38412       if (_depth>1)
38413         cimg::warn(_cimg_instance
38414                    "save_exr() : Instance is volumetric, only the first slice will be saved in file '%s'.",
38415                    cimg_instance,
38416                    filename);
38417 
38418 #ifndef cimg_use_openexr
38419       return save_other(filename);
38420 #else
38421       Imf::Rgba *const ptrd0 = new Imf::Rgba[_width*_height], *ptrd = ptrd0, rgba;
38422       switch (_spectrum) {
38423       case 1 : { // Grayscale image.
38424         for (const T *ptr_r = data(), *const ptr_e = ptr_r + _width*_height; ptr_r<ptr_e;) {
38425           rgba.r = rgba.g = rgba.b = (half)(*(ptr_r++));
38426           rgba.a = (half)1;
38427           *(ptrd++) = rgba;
38428         }
38429       } break;
38430       case 2 : { // RG image.
38431         for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1), *const ptr_e = ptr_r + _width*_height; ptr_r<ptr_e; ) {
38432           rgba.r = (half)(*(ptr_r++));
38433           rgba.g = (half)(*(ptr_g++));
38434           rgba.b = (half)0;
38435           rgba.a = (half)1;
38436           *(ptrd++) = rgba;
38437         }
38438       } break;
38439       case 3 : { // RGB image.
38440         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 + _width*_height; ptr_r<ptr_e;) {
38441           rgba.r = (half)(*(ptr_r++));
38442           rgba.g = (half)(*(ptr_g++));
38443           rgba.b = (half)(*(ptr_b++));
38444           rgba.a = (half)1;
38445           *(ptrd++) = rgba;
38446         }
38447       } break;
38448       default : { // RGBA image.
38449         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),
38450                *const ptr_e = ptr_r + _width*_height; ptr_r<ptr_e;) {
38451           rgba.r = (half)(*(ptr_r++));
38452           rgba.g = (half)(*(ptr_g++));
38453           rgba.b = (half)(*(ptr_b++));
38454           rgba.a = (half)(*(ptr_a++));
38455           *(ptrd++) = rgba;
38456         }
38457       } break;
38458       }
38459       Imf::RgbaOutputFile outFile(filename,_width,_height,
38460                                   _spectrum==1?Imf::WRITE_Y:_spectrum==2?Imf::WRITE_YA:_spectrum==3?Imf::WRITE_RGB:Imf::WRITE_RGBA);
38461       outFile.setFrameBuffer(ptrd0,1,_width);
38462       outFile.writePixels(_height);
38463       delete[] ptrd0;
38464       return *this;
38465 #endif
38466     }
38467 
38468     // Save the image as a PANDORE-5 file (internal).
38469     unsigned int _save_pandore_header_length(unsigned int id, unsigned int *dims, const unsigned int colorspace) const {
38470       unsigned int nbdims = 0;
38471       if (id==2 || id==3 || id==4) { dims[0] = 1; dims[1] = _width;  nbdims = 2; }
38472       if (id==5 || id==6 || id==7) { dims[0] = 1; dims[1] = _height; dims[2] = _width;  nbdims=3; }
38473       if (id==8 || id==9 || id==10) { dims[0] = _spectrum; dims[1] = _depth;  dims[2] = _height; dims[3] = _width; nbdims = 4; }
38474       if (id==16 || id==17 || id==18) { dims[0] = 3; dims[1] = _height; dims[2] = _width;  dims[3] = colorspace; nbdims = 4; }
38475       if (id==19 || id==20 || id==21) { dims[0] = 3; dims[1] = _depth;  dims[2] = _height; dims[3] = _width; dims[4] = colorspace; nbdims = 5; }
38476       if (id==22 || id==23 || id==25) { dims[0] = _spectrum; dims[1] = _width;  nbdims = 2; }
38477       if (id==26 || id==27 || id==29) { dims[0] = _spectrum; dims[1] = _height; dims[2] = _width;  nbdims=3; }
38478       if (id==30 || id==31 || id==33) { dims[0] = _spectrum; dims[1] = _depth;  dims[2] = _height; dims[3] = _width; nbdims = 4; }
38479       return nbdims;
38480     }
38481 
38482     const CImg<T>& _save_pandore(std::FILE *const file, const char *const filename, const unsigned int colorspace) const {
38483 
38484 #define __cimg_save_pandore_case(dtype) \
38485        dtype *buffer = new dtype[size()]; \
38486        const T *ptrs = _data; \
38487        cimg_foroff(*this,off) *(buffer++) = (dtype)(*(ptrs++)); \
38488        buffer-=size(); \
38489        cimg::fwrite(buffer,size(),nfile); \
38490        delete[] buffer
38491 
38492 #define _cimg_save_pandore_case(sy,sz,sv,stype,id) \
38493       if (!saved && (sy?(sy==_height):true) && (sz?(sz==_depth):true) && (sv?(sv==_spectrum):true) && !std::strcmp(stype,pixel_type())) { \
38494         unsigned int *iheader = (unsigned int*)(header+12); \
38495         nbdims = _save_pandore_header_length((*iheader=id),dims,colorspace); \
38496         cimg::fwrite(header,36,nfile); \
38497         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); } \
38498         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); } \
38499         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); } \
38500         else throw CImgIOException(_cimg_instance \
38501                                    "save_pandore() : Unsupported datatype for file '%s'.",\
38502                                    cimg_instance, \
38503                                    filename?filename:"(FILE*)"); \
38504         if (id==2 || id==5 || id==8 || id==16 || id==19 || id==22 || id==26 || id==30) { \
38505           __cimg_save_pandore_case(unsigned char); \
38506         } else if (id==3 || id==6 || id==9 || id==17 || id==20 || id==23 || id==27 || id==31) { \
38507           if (sizeof(unsigned long)==4) { __cimg_save_pandore_case(unsigned long); } \
38508           else if (sizeof(unsigned int)==4) { __cimg_save_pandore_case(unsigned int); } \
38509           else if (sizeof(unsigned short)==4) { __cimg_save_pandore_case(unsigned short); } \
38510           else throw CImgIOException(_cimg_instance \
38511                                      "save_pandore() : Unsupported datatype for file '%s'.",\
38512                                      cimg_instance, \
38513                                      filename?filename:"(FILE*)"); \
38514         } else if (id==4 || id==7 || id==10 || id==18 || id==21 || id==25 || id==29 || id==33) { \
38515           if (sizeof(double)==4) { __cimg_save_pandore_case(double); } \
38516           else if (sizeof(float)==4) { __cimg_save_pandore_case(float); } \
38517           else throw CImgIOException(_cimg_instance \
38518                                      "save_pandore() : Unsupported datatype for file '%s'.",\
38519                                      cimg_instance, \
38520                                      filename?filename:"(FILE*)"); \
38521         } \
38522         saved = true; \
38523       }
38524 
38525       if (!file && !filename)
38526         throw CImgArgumentException(_cimg_instance
38527                                     "save_pandore() : Specified filename is (null).",
38528                                     cimg_instance);
38529       if (is_empty())
38530         throw CImgInstanceException(_cimg_instance
38531                                     "save_pandore() : Empty instance, for file '%s'.",
38532                                     cimg_instance,
38533                                     filename?filename:"(FILE*)");
38534 
38535       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
38536       unsigned char header[36] = { 'P','A','N','D','O','R','E','0','4',0,0,0,
38537                                    0,0,0,0,'C','I','m','g',0,0,0,0,0,'N','o',' ','d','a','t','e',0,0,0,0 };
38538       unsigned int nbdims, dims[5] = { 0 };
38539       bool saved = false;
38540       _cimg_save_pandore_case(1,1,1,"unsigned char",2);
38541       _cimg_save_pandore_case(1,1,1,"char",3);
38542       _cimg_save_pandore_case(1,1,1,"short",3);
38543       _cimg_save_pandore_case(1,1,1,"unsigned short",3);
38544       _cimg_save_pandore_case(1,1,1,"unsigned int",3);
38545       _cimg_save_pandore_case(1,1,1,"int",3);
38546       _cimg_save_pandore_case(1,1,1,"unsigned long",4);
38547       _cimg_save_pandore_case(1,1,1,"long",3);
38548       _cimg_save_pandore_case(1,1,1,"float",4);
38549       _cimg_save_pandore_case(1,1,1,"double",4);
38550 
38551       _cimg_save_pandore_case(0,1,1,"unsigned char",5);
38552       _cimg_save_pandore_case(0,1,1,"char",6);
38553       _cimg_save_pandore_case(0,1,1,"short",6);
38554       _cimg_save_pandore_case(0,1,1,"unsigned short",6);
38555       _cimg_save_pandore_case(0,1,1,"unsigned int",6);
38556       _cimg_save_pandore_case(0,1,1,"int",6);
38557       _cimg_save_pandore_case(0,1,1,"unsigned long",7);
38558       _cimg_save_pandore_case(0,1,1,"long",6);
38559       _cimg_save_pandore_case(0,1,1,"float",7);
38560       _cimg_save_pandore_case(0,1,1,"double",7);
38561 
38562       _cimg_save_pandore_case(0,0,1,"unsigned char",8);
38563       _cimg_save_pandore_case(0,0,1,"char",9);
38564       _cimg_save_pandore_case(0,0,1,"short",9);
38565       _cimg_save_pandore_case(0,0,1,"unsigned short",9);
38566       _cimg_save_pandore_case(0,0,1,"unsigned int",9);
38567       _cimg_save_pandore_case(0,0,1,"int",9);
38568       _cimg_save_pandore_case(0,0,1,"unsigned long",10);
38569       _cimg_save_pandore_case(0,0,1,"long",9);
38570       _cimg_save_pandore_case(0,0,1,"float",10);
38571       _cimg_save_pandore_case(0,0,1,"double",10);
38572 
38573       _cimg_save_pandore_case(0,1,3,"unsigned char",16);
38574       _cimg_save_pandore_case(0,1,3,"char",17);
38575       _cimg_save_pandore_case(0,1,3,"short",17);
38576       _cimg_save_pandore_case(0,1,3,"unsigned short",17);
38577       _cimg_save_pandore_case(0,1,3,"unsigned int",17);
38578       _cimg_save_pandore_case(0,1,3,"int",17);
38579       _cimg_save_pandore_case(0,1,3,"unsigned long",18);
38580       _cimg_save_pandore_case(0,1,3,"long",17);
38581       _cimg_save_pandore_case(0,1,3,"float",18);
38582       _cimg_save_pandore_case(0,1,3,"double",18);
38583 
38584       _cimg_save_pandore_case(0,0,3,"unsigned char",19);
38585       _cimg_save_pandore_case(0,0,3,"char",20);
38586       _cimg_save_pandore_case(0,0,3,"short",20);
38587       _cimg_save_pandore_case(0,0,3,"unsigned short",20);
38588       _cimg_save_pandore_case(0,0,3,"unsigned int",20);
38589       _cimg_save_pandore_case(0,0,3,"int",20);
38590       _cimg_save_pandore_case(0,0,3,"unsigned long",21);
38591       _cimg_save_pandore_case(0,0,3,"long",20);
38592       _cimg_save_pandore_case(0,0,3,"float",21);
38593       _cimg_save_pandore_case(0,0,3,"double",21);
38594 
38595       _cimg_save_pandore_case(1,1,0,"unsigned char",22);
38596       _cimg_save_pandore_case(1,1,0,"char",23);
38597       _cimg_save_pandore_case(1,1,0,"short",23);
38598       _cimg_save_pandore_case(1,1,0,"unsigned short",23);
38599       _cimg_save_pandore_case(1,1,0,"unsigned int",23);
38600       _cimg_save_pandore_case(1,1,0,"int",23);
38601       _cimg_save_pandore_case(1,1,0,"unsigned long",25);
38602       _cimg_save_pandore_case(1,1,0,"long",23);
38603       _cimg_save_pandore_case(1,1,0,"float",25);
38604       _cimg_save_pandore_case(1,1,0,"double",25);
38605 
38606       _cimg_save_pandore_case(0,1,0,"unsigned char",26);
38607       _cimg_save_pandore_case(0,1,0,"char",27);
38608       _cimg_save_pandore_case(0,1,0,"short",27);
38609       _cimg_save_pandore_case(0,1,0,"unsigned short",27);
38610       _cimg_save_pandore_case(0,1,0,"unsigned int",27);
38611       _cimg_save_pandore_case(0,1,0,"int",27);
38612       _cimg_save_pandore_case(0,1,0,"unsigned long",29);
38613       _cimg_save_pandore_case(0,1,0,"long",27);
38614       _cimg_save_pandore_case(0,1,0,"float",29);
38615       _cimg_save_pandore_case(0,1,0,"double",29);
38616 
38617       _cimg_save_pandore_case(0,0,0,"unsigned char",30);
38618       _cimg_save_pandore_case(0,0,0,"char",31);
38619       _cimg_save_pandore_case(0,0,0,"short",31);
38620       _cimg_save_pandore_case(0,0,0,"unsigned short",31);
38621       _cimg_save_pandore_case(0,0,0,"unsigned int",31);
38622       _cimg_save_pandore_case(0,0,0,"int",31);
38623       _cimg_save_pandore_case(0,0,0,"unsigned long",33);
38624       _cimg_save_pandore_case(0,0,0,"long",31);
38625       _cimg_save_pandore_case(0,0,0,"float",33);
38626       _cimg_save_pandore_case(0,0,0,"double",33);
38627 
38628       if (!file) cimg::fclose(nfile);
38629       return *this;
38630     }
38631 
38632     //! Save the image as a PANDORE-5 file.
38633     const CImg<T>& save_pandore(const char *const filename, const unsigned int colorspace=0) const {
38634       return _save_pandore(0,filename,colorspace);
38635     }
38636 
38637     //! Save the image as a PANDORE-5 file.
38638     const CImg<T>& save_pandore(std::FILE *const file, const unsigned int colorspace=0) const {
38639       return _save_pandore(file,0,colorspace);
38640     }
38641 
38642    // Save the image as a RAW file (internal).
38643     const CImg<T>& _save_raw(std::FILE *const file, const char *const filename, const bool multiplexed) const {
38644       if (!file && !filename)
38645         throw CImgArgumentException(_cimg_instance
38646                                     "save_raw() : Specified filename is (null).",
38647                                     cimg_instance);
38648       if (is_empty())
38649         throw CImgInstanceException(_cimg_instance
38650                                     "save_raw() : empty instance, for file '%s'.",
38651                                     cimg_instance,
38652                                     filename?filename:"(FILE*)");
38653 
38654       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
38655       if (!multiplexed) cimg::fwrite(_data,size(),nfile);
38656       else {
38657         CImg<T> buf(_spectrum);
38658         cimg_forXYZ(*this,x,y,z) {
38659           cimg_forC(*this,c) buf[c] = (*this)(x,y,z,c);
38660           cimg::fwrite(buf._data,_spectrum,nfile);
38661         }
38662       }
38663       if (!file) cimg::fclose(nfile);
38664       return *this;
38665     }
38666 
38667     //! Save the image as a RAW file.
38668     const CImg<T>& save_raw(const char *const filename, const bool multiplexed=false) const {
38669       return _save_raw(0,filename,multiplexed);
38670     }
38671 
38672     //! Save the image as a RAW file.
38673     const CImg<T>& save_raw(std::FILE *const file, const bool multiplexed=false) const {
38674       return _save_raw(file,0,multiplexed);
38675     }
38676 
38677     //! Save the image as a video sequence file, using FFMPEG library.
38678     const CImg<T>& save_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
38679                                const unsigned int fps=25, const unsigned int bitrate=2048) const {
38680       if (!filename)
38681         throw CImgArgumentException(_cimg_instance
38682                                     "save_ffmpeg() : Specified filename is (null).",
38683                                     cimg_instance);
38684       if (is_empty())
38685         throw CImgInstanceException(_cimg_instance
38686                                     "save_ffmpeg() : Empty instance, for file '%s'.",
38687                                     cimg_instance,
38688                                     filename);
38689       if (!fps)
38690         throw CImgArgumentException(_cimg_instance
38691                                     "save_ffmpeg() : Invalid specified framerate 0, for file '%s'.",
38692                                     cimg_instance,
38693                                     filename);
38694 
38695 #ifndef cimg_use_ffmpeg
38696       return save_ffmpeg_external(filename,first_frame,last_frame,"mpeg2video",fps,bitrate);
38697 #else
38698       get_split('z').save_ffmpeg(filename,first_frame,last_frame,fps,bitrate);
38699 #endif
38700       return *this;
38701     }
38702 
38703     //! Save the image as a YUV video sequence file.
38704     const CImg<T>& save_yuv(const char *const filename, const bool rgb2yuv=true) const {
38705       get_split('z').save_yuv(filename,rgb2yuv);
38706       return *this;
38707     }
38708 
38709     //! Save the image as a YUV video sequence file.
38710     const CImg<T>& save_yuv(std::FILE *const file, const bool rgb2yuv=true) const {
38711       get_split('z').save_yuv(file,rgb2yuv);
38712       return *this;
38713     }
38714 
38715     // Save OFF files (internal).
38716     template<typename tf, typename tc>
38717     const CImg<T>& _save_off(std::FILE *const file, const char *const filename,
38718                              const CImgList<tf>& primitives, const CImgList<tc>& colors) const {
38719       if (!file && !filename)
38720         throw CImgArgumentException(_cimg_instance
38721                                     "save_off() : Specified filename is (null).",
38722                                     cimg_instance);
38723       if (is_empty())
38724         throw CImgInstanceException(_cimg_instance
38725                                     "save_off() : Empty instance, for file '%s'.",
38726                                     cimg_instance,
38727                                     filename?filename:"(FILE*)");
38728 
38729       CImgList<T> opacities;
38730       char error_message[1024] = { 0 };
38731       if (!is_object3d(primitives,colors,opacities,true,error_message))
38732         throw CImgInstanceException(_cimg_instance
38733                                     "save_off() : Invalid specified 3d object, for file '%s' (%s).",
38734                                     cimg_instance,
38735                                     filename?filename:"(FILE*)",error_message);
38736 
38737       const CImg<tc> default_color(1,3,1,1,200);
38738       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
38739       unsigned int supported_primitives = 0;
38740       cimglist_for(primitives,l) if (primitives[l].size()!=5) ++supported_primitives;
38741       std::fprintf(nfile,"OFF\n%u %u %u\n",_width,supported_primitives,3*primitives._width);
38742       cimg_forX(*this,i) std::fprintf(nfile,"%f %f %f\n",(float)((*this)(i,0)),(float)((*this)(i,1)),(float)((*this)(i,2)));
38743       cimglist_for(primitives,l) {
38744         const CImg<tc>& color = l<colors.width()?colors[l]:default_color;
38745         const unsigned int psiz = primitives[l].size(), csiz = color.size();
38746         const float r = color[0]/255.0f, g = (csiz>1?color[1]:r)/255.0f, b = (csiz>2?color[2]:g)/255.0f;
38747         switch (psiz) {
38748         case 1 : std::fprintf(nfile,"1 %u %f %f %f\n",(unsigned int)primitives(l,0),r,g,b); break;
38749         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;
38750         case 3 : std::fprintf(nfile,"3 %u %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,2),
38751                               (unsigned int)primitives(l,1),r,g,b); break;
38752         case 4 : std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,3),
38753                               (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),r,g,b); break;
38754         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;
38755         case 6 : {
38756           const unsigned int xt = (unsigned int)primitives(l,2), yt = (unsigned int)primitives(l,3);
38757           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;
38758           std::fprintf(nfile,"2 %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,1),rt,gt,bt);
38759         } break;
38760         case 9 : {
38761           const unsigned int xt = (unsigned int)primitives(l,3), yt = (unsigned int)primitives(l,4);
38762           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;
38763           std::fprintf(nfile,"3 %u %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,2),
38764                        (unsigned int)primitives(l,1),rt,gt,bt);
38765         } break;
38766         case 12 : {
38767           const unsigned int xt = (unsigned int)primitives(l,4), yt = (unsigned int)primitives(l,5);
38768           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;
38769           std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,3),
38770                        (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),rt,gt,bt);
38771         } break;
38772         }
38773       }
38774       if (!file) cimg::fclose(nfile);
38775       return *this;
38776     }
38777 
38778     //! Save OFF files.
38779     template<typename tf, typename tc>
38780     const CImg<T>& save_off(const char *const filename,
38781                             const CImgList<tf>& primitives, const CImgList<tc>& colors) const {
38782       return _save_off(0,filename,primitives,colors);
38783     }
38784 
38785     //! Save OFF files.
38786     template<typename tf, typename tc>
38787     const CImg<T>& save_off(std::FILE *const file,
38788                             const CImgList<tf>& primitives, const CImgList<tc>& colors) const {
38789       return _save_off(file,0,primitives,colors);
38790     }
38791 
38792     //! Save the image as a video sequence file, using the external tool 'ffmpeg'.
38793     const CImg<T>& save_ffmpeg_external(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
38794                                         const char *const codec="mpeg2video", const unsigned int fps=25, const unsigned int bitrate=2048) const {
38795       if (!filename)
38796         throw CImgArgumentException(_cimg_instance
38797                                     "save_ffmpeg_external() : Specified filename is (null).",
38798                                     cimg_instance);
38799       if (is_empty())
38800         throw CImgInstanceException(_cimg_instance
38801                                     "save_ffmpeg_external() : Empty instance, for file '%s'.",
38802                                     cimg_instance,
38803                                     filename);
38804 
38805       get_split('z').save_ffmpeg_external(filename,first_frame,last_frame,codec,fps,bitrate);
38806       return *this;
38807     }
38808 
38809     //! Save the image using GraphicsMagick's gm.
38810     /** Function that saves the image for other file formats that are not natively handled by CImg,
38811         using the tool 'gm' from the GraphicsMagick package.\n
38812         This is the case for all compressed image formats (GIF,PNG,JPG,TIF, ...). You need to install
38813         the GraphicsMagick package in order to get
38814         this function working properly (see http://www.graphicsmagick.org ).
38815     **/
38816     const CImg<T>& save_graphicsmagick_external(const char *const filename, const unsigned int quality=100) const {
38817       if (!filename)
38818         throw CImgArgumentException(_cimg_instance
38819                                     "save_graphicsmagick_external() : Specified filename is (null).",
38820                                     cimg_instance);
38821       if (is_empty())
38822         throw CImgInstanceException(_cimg_instance
38823                                     "save_graphicsmagick_external() : Empty instance, for file '%s'.",
38824                                     cimg_instance,
38825                                     filename);
38826 
38827       char command[1024] = { 0 }, filetmp[512] = { 0 };
38828       std::FILE *file;
38829       do {
38830         cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),_spectrum==1?"pgm":"ppm");
38831         if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
38832       } while (file);
38833       save_pnm(filetmp);
38834       cimg_snprintf(command,sizeof(command),"%s -quality %u%% \"%s\" \"%s\"",cimg::graphicsmagick_path(),quality,filetmp,filename);
38835       cimg::system(command);
38836       file = std::fopen(filename,"rb");
38837       if (!file)
38838         throw CImgIOException(_cimg_instance
38839                               "save_graphicsmagick_external() : Failed to save file '%s' with external command 'gm'.",
38840                               cimg_instance,
38841                               filename);
38842 
38843       if (file) cimg::fclose(file);
38844       std::remove(filetmp);
38845       return *this;
38846     }
38847 
38848     //! Save an image as a gzipped file, using external tool 'gzip'.
38849     const CImg<T>& save_gzip_external(const char *const filename) const {
38850       if (!filename)
38851         throw CImgArgumentException(_cimg_instance
38852                                     "save_gzip_external() : Specified filename is (null).",
38853                                     cimg_instance);
38854       if (is_empty())
38855         throw CImgInstanceException(_cimg_instance
38856                                     "save_gzip_external() : Empty instance, for file '%s'.",
38857                                     cimg_instance,
38858                                     filename);
38859 
38860       char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
38861       const char
38862         *ext = cimg::split_filename(filename,body),
38863         *ext2 = cimg::split_filename(body,0);
38864       std::FILE *file;
38865       do {
38866         if (!cimg::strcasecmp(ext,"gz")) {
38867           if (*ext2) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
38868           else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.cimg",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
38869         } else {
38870           if (*ext) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
38871           else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.cimg",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
38872         }
38873         if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
38874       } while (file);
38875       save(filetmp);
38876       cimg_snprintf(command,sizeof(command),"%s -c %s > \"%s\"",cimg::gzip_path(),filetmp,filename);
38877       cimg::system(command);
38878       file = std::fopen(filename,"rb");
38879       if (!file)
38880         throw CImgIOException(_cimg_instance
38881                               "save_gzip_external() : Failed to save file '%s' with external command 'gzip'.",
38882                               cimg_instance,
38883                               filename);
38884 
38885       else cimg::fclose(file);
38886       std::remove(filetmp);
38887       return *this;
38888     }
38889 
38890     //! Save the image using ImageMagick's convert.
38891     /** Function that saves the image for other file formats that are not natively handled by CImg,
38892         using the tool 'convert' from the ImageMagick package.\n
38893         This is the case for all compressed image formats (GIF,PNG,JPG,TIF, ...). You need to install
38894         the ImageMagick package in order to get
38895         this function working properly (see http://www.imagemagick.org ).
38896     **/
38897     const CImg<T>& save_imagemagick_external(const char *const filename, const unsigned int quality=100) const {
38898       if (!filename)
38899         throw CImgArgumentException(_cimg_instance
38900                                     "save_imagemagick_external() : Specified filename is (null).",
38901                                     cimg_instance);
38902       if (is_empty())
38903         throw CImgInstanceException(_cimg_instance
38904                                     "save_imagemagick_external() : Empty instance, for file '%s'.",
38905                                     cimg_instance,
38906                                     filename);
38907 
38908       char command[1024] = { 0 }, filetmp[512] = { 0 };
38909       std::FILE *file;
38910       do {
38911         cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),_spectrum==1?"pgm":"ppm");
38912         if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
38913       } while (file);
38914       save_pnm(filetmp);
38915       cimg_snprintf(command,sizeof(command),"%s -quality %u%% \"%s\" \"%s\"",cimg::imagemagick_path(),quality,filetmp,filename);
38916       cimg::system(command);
38917       file = std::fopen(filename,"rb");
38918       if (!file)
38919         throw CImgIOException(_cimg_instance
38920                               "save_imagemagick_external() : Failed to save file '%s' with external command 'convert'.",
38921                               cimg_instance,
38922                               filename);
38923 
38924       if (file) cimg::fclose(file);
38925       std::remove(filetmp);
38926       return *this;
38927     }
38928 
38929     //! Save an image as a Dicom file (need '(X)Medcon' : http://xmedcon.sourceforge.net )
38930     const CImg<T>& save_medcon_external(const char *const filename) const {
38931       if (!filename)
38932         throw CImgArgumentException(_cimg_instance
38933                                     "save_medcon_external() : Specified filename is (null).",
38934                                     cimg_instance);
38935       if (is_empty())
38936         throw CImgInstanceException(_cimg_instance
38937                                     "save_medcon_external() : Empty instance, for file '%s'.",
38938                                     cimg_instance,
38939                                     filename);
38940 
38941       char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
38942       std::FILE *file;
38943       do {
38944         cimg_snprintf(filetmp,sizeof(filetmp),"%s.hdr",cimg::filenamerand());
38945         if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
38946       } while (file);
38947       save_analyze(filetmp);
38948       cimg_snprintf(command,sizeof(command),"%s -w -c dicom -o %s -f %s",cimg::medcon_path(),filename,filetmp);
38949       cimg::system(command);
38950       std::remove(filetmp);
38951       cimg::split_filename(filetmp,body);
38952       cimg_snprintf(filetmp,sizeof(filetmp),"%s.img",body);
38953       std::remove(filetmp);
38954 
38955       file = std::fopen(filename,"rb");
38956       if (!file) {
38957         cimg_snprintf(command,sizeof(command),"m000-%s",filename);
38958         file = std::fopen(command,"rb");
38959         if (!file) {
38960           cimg::fclose(cimg::fopen(filename,"r"));
38961           throw CImgIOException(_cimg_instance
38962                                 "save_medcon_external() : Failed to save file '%s' with external command 'medcon'.",
38963                                 cimg_instance,
38964                                 filename);
38965         }
38966       }
38967       cimg::fclose(file);
38968       std::rename(command,filename);
38969       return *this;
38970     }
38971 
38972     // Try to save the image if other extension is provided.
38973     const CImg<T>& save_other(const char *const filename, const unsigned int quality=100) const {
38974       if (!filename)
38975         throw CImgArgumentException(_cimg_instance
38976                                     "save_other() : Specified filename is (null).",
38977                                     cimg_instance);
38978       if (is_empty())
38979         throw CImgInstanceException(_cimg_instance
38980                                     "save_other() : Empty instance, for file '%s'.",
38981                                     cimg_instance,
38982                                     filename);
38983 
38984       const unsigned int omode = cimg::exception_mode();
38985       bool is_saved = true;
38986       cimg::exception_mode() = 0;
38987       try { save_magick(filename); }
38988       catch (CImgException&) {
38989         try { save_imagemagick_external(filename,quality); }
38990         catch (CImgException&) {
38991           try { save_graphicsmagick_external(filename,quality); }
38992           catch (CImgException&) {
38993             is_saved = false;
38994           }
38995         }
38996       }
38997       cimg::exception_mode() = omode;
38998       if (!is_saved)
38999         throw CImgIOException(_cimg_instance
39000                               "save_other() : Failed to save file '%s'. Format is not natively supported, and no external commands succeeded.",
39001                               cimg_instance,
39002                               filename);
39003       return *this;
39004     }
39005 
39006     // Get a 40x38 color logo of a 'danger' item (internal).
39007     static CImg<T> logo40x38() {
39008       static bool first_time = true;
39009       static CImg<T> res(40,38,1,3);
39010       if (first_time) {
39011         const unsigned char *ptrs = cimg::logo40x38;
39012         T *ptr1 = res.data(0,0,0,0), *ptr2 = res.data(0,0,0,1), *ptr3 = res.data(0,0,0,2);
39013         for (unsigned int off = 0; off<res._width*res._height;) {
39014           const unsigned char n = *(ptrs++), r = *(ptrs++), g = *(ptrs++), b = *(ptrs++);
39015           for (unsigned int l = 0; l<n; ++off, ++l) { *(ptr1++) = (T)r; *(ptr2++) = (T)g; *(ptr3++) = (T)b; }
39016         }
39017         first_time = false;
39018       }
39019       return res;
39020     }
39021 
39022     //@}
39023   };
39024 
39025   /*
39026    #-----------------------------------------
39027    #
39028    #
39029    #
39030    # Definition of the CImgList<T> structure
39031    #
39032    #
39033    #
39034    #------------------------------------------
39035    */
39036 
39037   //! Class representing list of images CImg<T>.
39038   template<typename T>
39039   struct CImgList {
39040 
39041     //! Size of the list (number of images).
39042     unsigned int _width;
39043 
39044     //! Allocation size of the list.
39045     unsigned int _allocated_width;
39046 
39047     //! Pointer to the first list element.
39048     CImg<T> *_data;
39049 
39050     //! Define a CImgList<T>::iterator.
39051     typedef CImg<T>* iterator;
39052 
39053     //! Define a CImgList<T>::const_iterator.
39054     typedef const CImg<T>* const_iterator;
39055 
39056     //! Value type.
39057     typedef T value_type;
39058 
39059     // Define common T-dependant types.
39060     typedef typename cimg::superset<T,bool>::type Tbool;
39061     typedef typename cimg::superset<T,unsigned char>::type Tuchar;
39062     typedef typename cimg::superset<T,char>::type Tchar;
39063     typedef typename cimg::superset<T,unsigned short>::type Tushort;
39064     typedef typename cimg::superset<T,short>::type Tshort;
39065     typedef typename cimg::superset<T,unsigned int>::type Tuint;
39066     typedef typename cimg::superset<T,int>::type Tint;
39067     typedef typename cimg::superset<T,unsigned long>::type Tulong;
39068     typedef typename cimg::superset<T,long>::type Tlong;
39069     typedef typename cimg::superset<T,float>::type Tfloat;
39070     typedef typename cimg::superset<T,double>::type Tdouble;
39071     typedef typename cimg::last<T,bool>::type boolT;
39072     typedef typename cimg::last<T,unsigned char>::type ucharT;
39073     typedef typename cimg::last<T,char>::type charT;
39074     typedef typename cimg::last<T,unsigned short>::type ushortT;
39075     typedef typename cimg::last<T,short>::type shortT;
39076     typedef typename cimg::last<T,unsigned int>::type uintT;
39077     typedef typename cimg::last<T,int>::type intT;
39078     typedef typename cimg::last<T,unsigned long>::type ulongT;
39079     typedef typename cimg::last<T,long>::type longT;
39080     typedef typename cimg::last<T,float>::type floatT;
39081     typedef typename cimg::last<T,double>::type doubleT;
39082 
39083     //@}
39084     //---------------------------
39085     //
39086     //! \name Plugins
39087     //@{
39088     //---------------------------
39089 #ifdef cimglist_plugin
39090 #include cimglist_plugin
39091 #endif
39092 #ifdef cimglist_plugin1
39093 #include cimglist_plugin1
39094 #endif
39095 #ifdef cimglist_plugin2
39096 #include cimglist_plugin2
39097 #endif
39098 #ifdef cimglist_plugin3
39099 #include cimglist_plugin3
39100 #endif
39101 #ifdef cimglist_plugin4
39102 #include cimglist_plugin4
39103 #endif
39104 #ifdef cimglist_plugin5
39105 #include cimglist_plugin5
39106 #endif
39107 #ifdef cimglist_plugin6
39108 #include cimglist_plugin6
39109 #endif
39110 #ifdef cimglist_plugin7
39111 #include cimglist_plugin7
39112 #endif
39113 #ifdef cimglist_plugin8
39114 #include cimglist_plugin8
39115 #endif
39116 
39117     //@}
39118     //--------------------------------------------------------
39119     //
39120     //! \name Constructors / Destructor / Instance Management
39121     //@{
39122     //--------------------------------------------------------
39123 
39124     //! Destructor.
39125     ~CImgList() {
39126       delete[] _data;
39127     }
39128 
39129     //! Default constructor.
39130     CImgList():
39131       _width(0),_allocated_width(0),_data(0) {}
39132 
39133     //! Construct an image list containing n empty images.
39134     explicit CImgList(const unsigned int n):_width(n) {
39135       if (n) _data = new CImg<T>[_allocated_width = cimg::max(16UL,cimg::nearest_pow2(n))];
39136       else { _allocated_width = 0; _data = 0; }
39137     }
39138 
39139     //! Construct an image list containing n images with specified size.
39140     CImgList(const unsigned int n, const unsigned int width, const unsigned int height=1,
39141              const unsigned int depth=1, const unsigned int spectrum=1):
39142       _width(0),_allocated_width(0),_data(0) {
39143       assign(n);
39144       cimglist_apply(*this,assign)(width,height,depth,spectrum);
39145     }
39146 
39147     //! Construct an image list containing n images with specified size, filled with specified value.
39148     CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
39149              const unsigned int depth, const unsigned int spectrum, const T val):
39150       _width(0),_allocated_width(0),_data(0) {
39151       assign(n);
39152       cimglist_apply(*this,assign)(width,height,depth,spectrum,val);
39153     }
39154 
39155     //! Construct an image list containing n images with specified size and specified pixel values (int version).
39156     CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
39157              const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...):
39158       _width(0),_allocated_width(0),_data(0) {
39159 #define _CImgList_stdarg(t) { \
39160         assign(n,width,height,depth,spectrum); \
39161         const unsigned int siz = width*height*depth*spectrum, nsiz = siz*n; \
39162         T *ptrd = _data->_data; \
39163         va_list ap; \
39164         va_start(ap,val1); \
39165         for (unsigned int l = 0, s = 0, i = 0; i<nsiz; ++i) { \
39166           *(ptrd++) = (T)(i==0?val0:(i==1?val1:va_arg(ap,t))); \
39167           if ((++s)==siz) { ptrd = _data[++l]._data; s = 0; } \
39168         } \
39169         va_end(ap); \
39170       }
39171       _CImgList_stdarg(int);
39172     }
39173 
39174     //! Construct an image list containing n images with specified size and specified pixel values (double version).
39175     CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
39176              const unsigned int depth, const unsigned int spectrum, const double val0, const double val1, ...):
39177       _width(0),_allocated_width(0),_data(0) {
39178       _CImgList_stdarg(double);
39179     }
39180 
39181     //! Construct a list containing n copies of the image img.
39182     template<typename t>
39183     CImgList(const unsigned int n, const CImg<t>& img, const bool shared=false):
39184       _width(0),_allocated_width(0),_data(0) {
39185       assign(n);
39186       cimglist_apply(*this,assign)(img,shared);
39187     }
39188 
39189     //! Construct an image list from one image.
39190     template<typename t>
39191     explicit CImgList(const CImg<t>& img, const bool shared=false):
39192       _width(0),_allocated_width(0),_data(0) {
39193       assign(1);
39194       _data[0].assign(img,shared);
39195     }
39196 
39197     //! Construct an image list from two images.
39198     template<typename t1, typename t2>
39199     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const bool shared=false):
39200       _width(0),_allocated_width(0),_data(0) {
39201       assign(2);
39202       _data[0].assign(img1,shared); _data[1].assign(img2,shared);
39203     }
39204 
39205     //! Construct an image list from three images.
39206     template<typename t1, typename t2, typename t3>
39207     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool shared=false):
39208       _width(0),_allocated_width(0),_data(0) {
39209       assign(3);
39210       _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared);
39211     }
39212 
39213     //! Construct an image list from four images.
39214     template<typename t1, typename t2, typename t3, typename t4>
39215     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4, const bool shared=false):
39216       _width(0),_allocated_width(0),_data(0) {
39217       assign(4);
39218       _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared);
39219     }
39220 
39221     //! Construct an image list from five images.
39222     template<typename t1, typename t2, typename t3, typename t4, typename t5>
39223     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
39224              const CImg<t5>& img5, const bool shared=false):
39225       _width(0),_allocated_width(0),_data(0) {
39226       assign(5);
39227       _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared);
39228       _data[4].assign(img5,shared);
39229     }
39230 
39231     //! Construct an image list from six images.
39232     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
39233     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
39234              const CImg<t5>& img5, const CImg<t6>& img6, const bool shared=false):
39235       _width(0),_allocated_width(0),_data(0) {
39236       assign(6);
39237       _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared);
39238       _data[4].assign(img5,shared); _data[5].assign(img6,shared);
39239     }
39240 
39241     //! Construct an image list from seven images.
39242     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
39243     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
39244              const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool shared=false):
39245       _width(0),_allocated_width(0),_data(0) {
39246       assign(7);
39247       _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared);
39248       _data[4].assign(img5,shared); _data[5].assign(img6,shared); _data[6].assign(img7,shared);
39249     }
39250 
39251     //! Construct an image list from eight images.
39252     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
39253     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
39254              const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8, const bool shared=false):
39255       _width(0),_allocated_width(0),_data(0) {
39256       assign(8);
39257       _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared);
39258       _data[4].assign(img5,shared); _data[5].assign(img6,shared); _data[6].assign(img7,shared); _data[7].assign(img8,shared);
39259     }
39260 
39261     //! Default copy constructor.
39262     template<typename t>
39263     CImgList(const CImgList<t>& list):_width(0),_allocated_width(0),_data(0) {
39264       assign(list._width);
39265       cimglist_for(*this,l) _data[l].assign(list[l],false);
39266     }
39267 
39268     CImgList(const CImgList<T>& list):_width(0),_allocated_width(0),_data(0) {
39269       assign(list._width);
39270       cimglist_for(*this,l) _data[l].assign(list[l],list[l]._is_shared);
39271     }
39272 
39273     //! Advanced copy constructor.
39274     template<typename t>
39275     CImgList(const CImgList<t>& list, const bool shared):_width(0),_allocated_width(0),_data(0) {
39276       assign(list._width);
39277       cimglist_for(*this,l) _data[l].assign(list[l],shared);
39278     }
39279 
39280     //! Construct an image list from a filename.
39281     explicit CImgList(const char *const filename):_width(0),_allocated_width(0),_data(0) {
39282       assign(filename);
39283     }
39284 
39285     //! Construct an image list from a display.
39286     explicit CImgList(const CImgDisplay &disp):_width(0),_allocated_width(0),_data(0) {
39287       assign(disp);
39288     }
39289 
39290     //! Return a shared instance of the list.
39291     CImgList<T> get_shared() {
39292       CImgList<T> res(_width);
39293       cimglist_for(*this,l) res[l].assign(_data[l],true);
39294       return res;
39295     }
39296 
39297     //! Return a shared instance of the list.
39298     const CImgList<T> get_shared() const {
39299       CImgList<T> res(_width);
39300       cimglist_for(*this,l) res[l].assign(_data[l],true);
39301       return res;
39302     }
39303 
39304     //! In-place version of the default constructor and default destructor.
39305     CImgList<T>& assign() {
39306       delete[] _data;
39307       _width = _allocated_width = 0;
39308       _data = 0;
39309       return *this;
39310     }
39311 
39312     //! In-place version of the corresponding constructor.
39313     CImgList<T>& assign(const unsigned int n) {
39314       if (n) {
39315         if (_allocated_width<n || _allocated_width>(n<<2)) {
39316           delete[] _data;
39317           _data = new CImg<T>[_allocated_width=cimg::max(16UL,cimg::nearest_pow2(n))];
39318         }
39319         _width = n;
39320       } else assign();
39321       return *this;
39322     }
39323 
39324     //! In-place version of the corresponding constructor.
39325     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height=1,
39326                         const unsigned int depth=1, const unsigned int spectrum=1) {
39327       assign(n);
39328       cimglist_apply(*this,assign)(width,height,depth,spectrum);
39329       return *this;
39330     }
39331 
39332     //! In-place version of the corresponding constructor.
39333     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
39334                         const unsigned int depth, const unsigned int spectrum, const T val) {
39335       assign(n);
39336       cimglist_apply(*this,assign)(width,height,depth,spectrum,val);
39337       return *this;
39338     }
39339 
39340     //! In-place version of the corresponding constructor.
39341     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
39342                         const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...) {
39343       _CImgList_stdarg(int);
39344       return *this;
39345     }
39346 
39347     //! In-place version of the corresponding constructor.
39348     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
39349                         const unsigned int depth, const unsigned int spectrum, const double val0, const double val1, ...) {
39350       _CImgList_stdarg(double);
39351       return *this;
39352     }
39353 
39354     //! In-place version of the copy constructor.
39355     CImgList<T>& assign(const CImgList<T>& list, const bool shared=false) {
39356       if (this==&list) return *this;
39357       CImgList<T> res(list._width);
39358       cimglist_for(res,l) res[l].assign(list[l],shared);
39359       return res.move_to(*this);
39360     }
39361 
39362     template<typename t>
39363     CImgList<T>& assign(const CImgList<t>& list, const bool shared=false) {
39364       assign(list._width);
39365       cimglist_for(*this,l) _data[l].assign(list[l],shared);
39366       return *this;
39367     }
39368 
39369     //! In-place version of the corresponding constructor.
39370     template<typename t>
39371     CImgList<T>& assign(const unsigned int n, const CImg<t>& img, const bool shared=false) {
39372       assign(n);
39373       cimglist_apply(*this,assign)(img,shared);
39374       return *this;
39375     }
39376 
39377     //! In-place version of the corresponding constructor.
39378     template<typename t>
39379     CImgList<T>& assign(const CImg<t>& img, const bool shared=false) {
39380       assign(1);
39381       _data[0].assign(img,shared);
39382       return *this;
39383     }
39384 
39385     //! In-place version of the corresponding constructor.
39386     template<typename t1, typename t2>
39387     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const bool shared=false) {
39388       assign(2);
39389       _data[0].assign(img1,shared); _data[1].assign(img2,shared);
39390       return *this;
39391     }
39392 
39393     //! In-place version of the corresponding constructor.
39394     template<typename t1, typename t2, typename t3>
39395     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool shared=false) {
39396       assign(3);
39397       _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared);
39398       return *this;
39399     }
39400 
39401     //! In-place version of the corresponding constructor.
39402     template<typename t1, typename t2, typename t3, typename t4>
39403     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
39404                         const bool shared=false) {
39405       assign(4);
39406       _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared);
39407       return *this;
39408     }
39409 
39410     //! In-place version of the corresponding constructor.
39411     template<typename t1, typename t2, typename t3, typename t4, typename t5>
39412     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
39413                         const CImg<t5>& img5, const bool shared=false) {
39414       assign(5);
39415       _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared);
39416       _data[4].assign(img5,shared);
39417       return *this;
39418     }
39419 
39420     //! In-place version of the corresponding constructor.
39421     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
39422     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
39423                         const CImg<t5>& img5, const CImg<t6>& img6, const bool shared=false) {
39424       assign(6);
39425       _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared);
39426       _data[4].assign(img5,shared); _data[5].assign(img6,shared);
39427       return *this;
39428     }
39429 
39430     //! In-place version of the corresponding constructor.
39431     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
39432     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
39433                         const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool shared=false) {
39434       assign(7);
39435       _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared);
39436       _data[4].assign(img5,shared); _data[5].assign(img6,shared); _data[6].assign(img7,shared);
39437       return *this;
39438     }
39439 
39440     //! In-place version of the corresponding constructor.
39441     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
39442     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
39443                         const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8,
39444                         const bool shared=false) {
39445       assign(8);
39446       _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared);
39447       _data[4].assign(img5,shared); _data[5].assign(img6,shared); _data[6].assign(img7,shared); _data[7].assign(img8,shared);
39448       return *this;
39449     }
39450 
39451     //! In-place version of the corresponding constructor.
39452     CImgList<T>& assign(const char *const filename) {
39453       return load(filename);
39454     }
39455 
39456     //! In-place version of the corresponding constructor.
39457     CImgList<T>& assign(const CImgDisplay &disp) {
39458       return assign(CImg<T>(disp));
39459     }
39460 
39461     //! In-place version of the default constructor.
39462     /**
39463        Equivalent to assign().
39464        \note
39465        - It has been defined for compatibility with STL naming conventions.
39466        \sa assign().
39467     **/
39468     CImgList<T>& clear() {
39469       return assign();
39470     }
39471 
39472     //! Move the content of the image instance list into another one.
39473     template<typename t>
39474     CImgList<t>& move_to(CImgList<t>& list) {
39475       list.assign(_width);
39476       bool is_one_shared_element = false;
39477       cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared;
39478       if (is_one_shared_element) cimglist_for(*this,l) list[l].assign(_data[l]);
39479       else cimglist_for(*this,l) _data[l].move_to(list[l]);
39480       assign();
39481       return list;
39482     }
39483 
39484     template<typename t>
39485     CImgList<t>& move_to(CImgList<t>& list, const unsigned int pos) {
39486       if (is_empty()) return list;
39487       const unsigned int npos = pos>list._width?list._width:pos;
39488       list.insert(_width,npos);
39489       bool is_one_shared_element = false;
39490       cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared;
39491       if (is_one_shared_element) cimglist_for(*this,l) list[npos+l].assign(_data[l]);
39492       else cimglist_for(*this,l) _data[l].move_to(list[npos+l]);
39493       assign();
39494       return list;
39495     }
39496 
39497     //! Swap all fields of two CImgList instances (use with care !)
39498     CImgList<T>& swap(CImgList<T>& list) {
39499       cimg::swap(_width,list._width);
39500       cimg::swap(_allocated_width,list._allocated_width);
39501       cimg::swap(_data,list._data);
39502       return list;
39503     }
39504 
39505     //! Return a reference to an empty list.
39506     static CImgList<T>& empty() {
39507       static CImgList<T> _empty;
39508       return _empty.assign();
39509     }
39510 
39511     //@}
39512     //------------------------------------------
39513     //
39514     //! \name Overloaded Operators
39515     //@{
39516     //------------------------------------------
39517 
39518     //! Return a reference to the i-th element of the image list.
39519     CImg<T>& operator()(const unsigned int pos) {
39520 #if cimg_verbosity>=3
39521       if (pos>=_width) {
39522         cimg::warn(_cimglist_instance
39523                    "operator() : Invalid image request, at position [%u].",
39524                    cimglist_instance,
39525                    pos);
39526         return *_data;
39527       }
39528 #endif
39529       return _data[pos];
39530     }
39531 
39532     const CImg<T>& operator()(const unsigned int pos) const {
39533       return const_cast<CImgList<T>*>(this)->operator()(pos);
39534     }
39535 
39536     //! Return a reference to (x,y,z,c) pixel of the pos-th image of the list
39537     T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0,
39538                   const unsigned int z=0, const unsigned int c=0) {
39539       return (*this)[pos](x,y,z,c);
39540     }
39541     const T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0,
39542                         const unsigned int z=0, const unsigned int c=0) const {
39543       return (*this)[pos](x,y,z,c);
39544     }
39545 
39546     //! Return address of the image vector.
39547     operator const CImg<T>*() const {
39548       return _data;
39549     }
39550 
39551     operator CImg<T>*() {
39552       return _data;
39553     }
39554 
39555     //! Operator=().
39556     template<typename t>
39557     CImgList<T>& operator=(const CImg<t>& img) {
39558       return assign(img);
39559     }
39560 
39561     //! Operator=().
39562     CImgList<T>& operator=(const CImgDisplay& disp) {
39563       return assign(disp);
39564     }
39565 
39566     //! Operator=().
39567     template<typename t>
39568     CImgList<T>& operator=(const CImgList<t>& list) {
39569       return assign(list);
39570     }
39571 
39572     CImgList<T>& operator=(const CImgList<T>& list) {
39573       return assign(list);
39574     }
39575 
39576     //! Operator=().
39577     CImgList<T>& operator=(const char *const filename) {
39578       return assign(filename);
39579     }
39580 
39581     //! Operator+() (unary).
39582     /**
39583        Writing '+list' is a convenient shortcut to 'CImgList<T>(list,false)'
39584        (forces a copy with non-shared elements).
39585      **/
39586     CImgList<T> operator+() const {
39587       return CImgList<T>(*this,false);
39588     }
39589 
39590     //! Operator,().
39591     template<typename t>
39592     CImgList<T>& operator,(const CImg<t>& img) {
39593       return insert(img);
39594     }
39595 
39596     //! Operator,().
39597     template<typename t>
39598     CImgList<T>& operator,(const CImgList<t>& list) {
39599       return insert(list);
39600     }
39601 
39602     //! Operator>().
39603     CImg<T> operator>(const char axis) const {
39604       return get_append(axis,0);
39605     }
39606 
39607     //! Operator<().
39608     CImgList<T> operator<(const char axis) const {
39609       return get_split(axis);
39610     }
39611 
39612     //@}
39613     //-------------------------------------
39614     //
39615     //! \name Instance Characteristics
39616     //@{
39617     //-------------------------------------
39618 
39619     //! Return a string describing the type of the image pixels in the list (template parameter \p T).
39620     static const char* pixel_type() {
39621       return cimg::type<T>::string();
39622     }
39623 
39624     //! Return the size of the list.
39625     int width() const {
39626       return (int)_width;
39627     }
39628 
39629     //! Return the size of the list.
39630     unsigned int size() const {
39631       return _width;
39632     }
39633 
39634     //! Return a pointer to the image buffer.
39635     CImg<T> *data() {
39636       return _data;
39637     }
39638 
39639     const CImg<T> *data() const {
39640       return _data;
39641     }
39642 
39643     //! Return a pointer to the image buffer.
39644 #if cimg_verbosity>=3
39645     CImg<T> *data(const unsigned int l) {
39646       if (l>=size()) {
39647         cimg::warn(_cimglist_instance
39648                    "data() : Invalid pointer request, at position [%u].",
39649                    cimglist_instance,
39650                    l);
39651         return _data;
39652       }
39653       return _data + l;
39654     }
39655 
39656     const CImg<T> *data(const unsigned int l) const {
39657       return const_cast<CImgList<T>*>(this)->data(l);
39658     }
39659 #else
39660     CImg<T> *data(const unsigned int l) {
39661       return _data + l;
39662     }
39663 
39664     const CImg<T> *data(const unsigned int l) const {
39665       return _data + l;
39666     }
39667 #endif
39668 
39669     //! Returns an iterator to the beginning of the vector (STL-compliant name).
39670     iterator begin() {
39671       return _data;
39672     }
39673 
39674     const_iterator begin() const {
39675       return _data;
39676     }
39677 
39678     //! Returns an iterator just past the last element (STL-compliant name).
39679     iterator end() {
39680       return _data + _width;
39681     }
39682 
39683     const_iterator end() const {
39684       return _data + _width;
39685     }
39686 
39687     //! Returns a reference to the first element (STL-compliant name).
39688     CImg<T>& front() {
39689       return *_data;
39690     }
39691 
39692     const CImg<T>& front() const {
39693       return *_data;
39694     }
39695 
39696     //! Return a reference to the last image (STL-compliant name).
39697     const CImg<T>& back() const {
39698       return *(_data + _width - 1);
39699     }
39700 
39701     CImg<T>& back() {
39702       return *(_data + _width - 1);
39703     }
39704 
39705     //! Read an image in specified position.
39706     CImg<T>& at(const int pos) {
39707       if (is_empty())
39708         throw CImgInstanceException(_cimglist_instance
39709                                     "at() : Empty instance.",
39710                                     cimglist_instance);
39711 
39712       return _data[pos<0?0:pos>=(int)_width?(int)_width-1:pos];
39713     }
39714 
39715     //! Get pixel value with Dirichlet boundary conditions.
39716     T& atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T out_value) {
39717       return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atXYZC(x,y,z,c,out_value);
39718     }
39719 
39720     T atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T out_value) const {
39721       return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atXYZC(x,y,z,c,out_value);
39722     }
39723 
39724     //! Get pixel value with Neumann boundary conditions.
39725     T& atNXYZC(const int pos, const int x, const int y, const int z, const int c) {
39726       if (is_empty())
39727         throw CImgInstanceException(_cimglist_instance
39728                                     "atNXYZC() : Empty instance.",
39729                                     cimglist_instance);
39730 
39731       return _atNXYZC(pos,x,y,z,c);
39732     }
39733 
39734     T atNXYZC(const int pos, const int x, const int y, const int z, const int c) const {
39735       if (is_empty())
39736         throw CImgInstanceException(_cimglist_instance
39737                                     "atNXYZC() : Empty instance.",
39738                                     cimglist_instance);
39739 
39740       return _atNXYZC(pos,x,y,z,c);
39741     }
39742 
39743     T& _atNXYZC(const int pos, const int x, const int y, const int z, const int c) {
39744       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZC(x,y,z,c);
39745     }
39746 
39747     T _atNXYZC(const int pos, const int x, const int y, const int z, const int c) const {
39748       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZC(x,y,z,c);
39749     }
39750 
39751     //! Get pixel value with Dirichlet boundary conditions for the four first coordinates (\c pos, \c x,\c y,\c z).
39752     T& atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T out_value) {
39753       return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atXYZ(x,y,z,c,out_value);
39754     }
39755 
39756     T atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T out_value) const {
39757       return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atXYZ(x,y,z,c,out_value);
39758     }
39759 
39760     //! Get pixel value with Neumann boundary conditions for the four first coordinates (\c pos, \c x,\c y,\c z).
39761     T& atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) {
39762       if (is_empty())
39763         throw CImgInstanceException(_cimglist_instance
39764                                     "atNXYZ() : Empty instance.",
39765                                     cimglist_instance);
39766 
39767       return _atNXYZ(pos,x,y,z,c);
39768     }
39769 
39770     T atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const {
39771       if (is_empty())
39772         throw CImgInstanceException(_cimglist_instance
39773                                     "atNXYZ() : Empty instance.",
39774                                     cimglist_instance);
39775 
39776       return _atNXYZ(pos,x,y,z,c);
39777     }
39778 
39779     T& _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) {
39780       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZ(x,y,z,c);
39781     }
39782 
39783     T _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const {
39784       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZ(x,y,z,c);
39785     }
39786 
39787     //! Get pixel value with Dirichlet boundary conditions for the three first coordinates (\c pos, \c x,\c y).
39788     T& atNXY(const int pos, const int x, const int y, const int z, const int c, const T out_value) {
39789       return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atXY(x,y,z,c,out_value);
39790     }
39791 
39792     T atNXY(const int pos, const int x, const int y, const int z, const int c, const T out_value) const {
39793       return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atXY(x,y,z,c,out_value);
39794     }
39795 
39796     //! Get pixel value with Neumann boundary conditions for the three first coordinates (\c pos, \c x,\c y).
39797     T& atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) {
39798       if (is_empty())
39799         throw CImgInstanceException(_cimglist_instance
39800                                     "atNXY() : Empty instance.",
39801                                     cimglist_instance);
39802 
39803       return _atNXY(pos,x,y,z,c);
39804     }
39805 
39806     T atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const {
39807       if (is_empty())
39808         throw CImgInstanceException(_cimglist_instance
39809                                     "atNXY() : Empty instance.",
39810                                     cimglist_instance);
39811 
39812       return _atNXY(pos,x,y,z,c);
39813     }
39814 
39815     T& _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) {
39816       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXY(x,y,z,c);
39817     }
39818 
39819     T _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const {
39820       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXY(x,y,z,c);
39821     }
39822 
39823     //! Get pixel value with Dirichlet boundary conditions for the two first coordinates (\c pos,\c x).
39824     T& atNX(const int pos, const int x, const int y, const int z, const int c, const T out_value) {
39825       return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atX(x,y,z,c,out_value);
39826     }
39827 
39828     T atNX(const int pos, const int x, const int y, const int z, const int c, const T out_value) const {
39829       return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atX(x,y,z,c,out_value);
39830     }
39831 
39832     //! Get pixel value with Neumann boundary conditions for the two first coordinates (\c pos, \c x).
39833     T& atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) {
39834       if (is_empty())
39835         throw CImgInstanceException(_cimglist_instance
39836                                     "atNX() : Empty instance.",
39837                                     cimglist_instance);
39838 
39839       return _atNX(pos,x,y,z,c);
39840     }
39841 
39842     T atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const {
39843       if (is_empty())
39844         throw CImgInstanceException(_cimglist_instance
39845                                     "atNX() : Empty instance.",
39846                                     cimglist_instance);
39847 
39848       return _atNX(pos,x,y,z,c);
39849     }
39850 
39851     T& _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) {
39852       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atX(x,y,z,c);
39853     }
39854 
39855     T _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const {
39856       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atX(x,y,z,c);
39857     }
39858 
39859     //! Get pixel value with Dirichlet boundary conditions for the first coordinates (\c pos).
39860     T& atN(const int pos, const int x, const int y, const int z, const int c, const T out_value) {
39861       return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):(*this)(pos,x,y,z,c);
39862     }
39863 
39864     T atN(const int pos, const int x, const int y, const int z, const int c, const T out_value) const {
39865       return (pos<0 || pos>=(int)_width)?out_value:(*this)(pos,x,y,z,c);
39866     }
39867 
39868     //! Get pixel value with Neumann boundary conditions for the first coordinates (\c pos).
39869     T& atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) {
39870       if (is_empty())
39871         throw CImgInstanceException(_cimglist_instance
39872                                     "atN() : Empty instance.",
39873                                     cimglist_instance);
39874       return _atN(pos,x,y,z,c);
39875     }
39876 
39877     T atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const {
39878       if (is_empty())
39879         throw CImgInstanceException(_cimglist_instance
39880                                     "atN() : Empty instance.",
39881                                     cimglist_instance);
39882       return _atN(pos,x,y,z,c);
39883     }
39884 
39885     T& _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) {
39886       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)](x,y,z,c);
39887     }
39888 
39889     T _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const {
39890       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)](x,y,z,c);
39891     }
39892 
39893     //! Return a C-string containing the values of all images in the instance list.
39894     CImg<charT> value_string(const char separator=',', const unsigned int max_size=0) const {
39895       if (is_empty()) return CImg<ucharT>(1,1,1,1,0);
39896       CImgList<charT> items;
39897       for (unsigned int l = 0; l<_width-1; ++l) {
39898         CImg<charT> item = _data[l].value_string(separator,0);
39899         item.back() = separator;
39900         item.move_to(items);
39901       }
39902       _data[_width-1].value_string(separator,0).move_to(items);
39903       CImg<charT> res; (items>'x').move_to(res);
39904       if (max_size) { res.crop(0,max_size); res(max_size) = 0; }
39905       return res;
39906     }
39907 
39908     //@}
39909     //-------------------------------------
39910     //
39911     //! \name Instance Checking
39912     //@{
39913     //-------------------------------------
39914 
39915     //! Return \p true if list is empty.
39916     bool is_empty() const {
39917       return (!_data || !_width);
39918     }
39919 
39920     //! Return \p true if list if of specified size.
39921     bool is_sameN(const unsigned int n) const {
39922       return (_width==n);
39923     }
39924 
39925     //! Return \p true if list if of specified size.
39926     template<typename t>
39927     bool is_sameN(const CImgList<t>& list) const {
39928       return (_width==list._width);
39929     }
39930 
39931     // Define useful dimension check functions.
39932     // (not documented because they are macro-generated).
39933 #define _cimglist_def_is_same1(axis) \
39934     bool is_same##axis(const unsigned int val) const { \
39935       bool res = true; for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(val); return res; \
39936     } \
39937     bool is_sameN##axis(const unsigned int n, const unsigned int val) const { \
39938       return is_sameN(n) && is_same##axis(val); \
39939     } \
39940 
39941 #define _cimglist_def_is_same2(axis1,axis2) \
39942     bool is_same##axis1##axis2(const unsigned int val1, const unsigned int val2) const { \
39943       bool res = true; for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2(val1,val2); return res; \
39944     } \
39945     bool is_sameN##axis1##axis2(const unsigned int n, const unsigned int val1, const unsigned int val2) const { \
39946       return is_sameN(n) && is_same##axis1##axis2(val1,val2); \
39947     } \
39948 
39949 #define _cimglist_def_is_same3(axis1,axis2,axis3) \
39950     bool is_same##axis1##axis2##axis3(const unsigned int val1, const unsigned int val2, const unsigned int val3) const { \
39951       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; \
39952     } \
39953     bool is_sameN##axis1##axis2##axis3(const unsigned int n, const unsigned int val1, const unsigned int val2, const unsigned int val3) const { \
39954       return is_sameN(n) && is_same##axis1##axis2##axis3(val1,val2,val3); \
39955     } \
39956 
39957 #define _cimglist_def_is_same(axis) \
39958     template<typename t> bool is_same##axis(const CImg<t>& img) const { \
39959       bool res = true; for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(img); return res; \
39960     } \
39961     template<typename t> bool is_same##axis(const CImgList<t>& list) const { \
39962       const unsigned int lmin = cimg::min(_width,list._width); \
39963       bool res = true; for (unsigned int l = 0; l<lmin && res; ++l) res = _data[l].is_same##axis(list[l]); return res; \
39964     } \
39965     template<typename t> bool is_sameN##axis(const unsigned int n, const CImg<t>& img) const { \
39966       return (is_sameN(n) && is_same##axis(img)); \
39967     } \
39968     template<typename t> bool is_sameN##axis(const CImgList<t>& list) const { \
39969       return (is_sameN(list) && is_same##axis(list)); \
39970     }
39971 
39972     _cimglist_def_is_same(XY)
39973     _cimglist_def_is_same(XZ)
39974     _cimglist_def_is_same(XC)
39975     _cimglist_def_is_same(YZ)
39976     _cimglist_def_is_same(YC)
39977     _cimglist_def_is_same(XYZ)
39978     _cimglist_def_is_same(XYC)
39979     _cimglist_def_is_same(YZC)
39980     _cimglist_def_is_same(XYZC)
39981     _cimglist_def_is_same1(X)
39982     _cimglist_def_is_same1(Y)
39983     _cimglist_def_is_same1(Z)
39984     _cimglist_def_is_same1(C)
39985     _cimglist_def_is_same2(X,Y)
39986     _cimglist_def_is_same2(X,Z)
39987     _cimglist_def_is_same2(X,C)
39988     _cimglist_def_is_same2(Y,Z)
39989     _cimglist_def_is_same2(Y,C)
39990     _cimglist_def_is_same2(Z,C)
39991     _cimglist_def_is_same3(X,Y,Z)
39992     _cimglist_def_is_same3(X,Y,C)
39993     _cimglist_def_is_same3(X,Z,C)
39994     _cimglist_def_is_same3(Y,Z,C)
39995 
39996     bool is_sameXYZC(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dc) const {
39997       bool res = true;
39998       for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_sameXYZC(dx,dy,dz,dc);
39999       return res;
40000     }
40001 
40002     bool is_sameNXYZC(const unsigned int n, const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dc) const {
40003       return is_sameN(n) && is_sameXYZC(dx,dy,dz,dc);
40004     }
40005 
40006     //! Return \c true if the list contains the pixel (n,x,y,z,c).
40007     bool containsNXYZC(const int n, const int x=0, const int y=0, const int z=0, const int c=0) const {
40008       if (is_empty()) return false;
40009       return n>=0 && n<(int)_width && x>=0 && x<_data[n].width() && y>=0 && y<_data[n].height() &&
40010         z>=0 && z<_data[n].depth() && c>=0 && c<_data[n].spectrum();
40011     }
40012 
40013     //! Return \c true if the list contains the image (n).
40014     bool containsN(const int n) const {
40015       if (is_empty()) return false;
40016       return n>=0 && n<(int)_width;
40017     }
40018 
40019     //! Return \c true if one of the image list contains the specified referenced value. If true, set coordinates (n,x,y,z,c).
40020     template<typename t>
40021     bool contains(const T& pixel, t& n, t& x, t&y, t& z, t& c) const {
40022       if (is_empty()) return false;
40023       cimglist_for(*this,l) if (_data[l].contains(pixel,x,y,z,c)) { n = (t)l; return true; }
40024       return false;
40025     }
40026 
40027     //! Return \c true if one of the image list contains the specified referenced value. If true, set coordinates (n,x,y,z).
40028     template<typename t>
40029     bool contains(const T& pixel, t& n, t& x, t&y, t& z) const {
40030       t c;
40031       return contains(pixel,n,x,y,z,c);
40032     }
40033 
40034     //! Return \c true if one of the image list contains the specified referenced value. If true, set coordinates (n,x,y).
40035     template<typename t>
40036     bool contains(const T& pixel, t& n, t& x, t&y) const {
40037       t z, c;
40038       return contains(pixel,n,x,y,z,c);
40039     }
40040 
40041     //! Return \c true if one of the image list contains the specified referenced value. If true, set coordinates (n,x).
40042     template<typename t>
40043     bool contains(const T& pixel, t& n, t& x) const {
40044       t y, z, c;
40045       return contains(pixel,n,x,y,z,c);
40046     }
40047 
40048     //! Return \c true if one of the image list contains the specified referenced value. If true, set coordinates (n).
40049     template<typename t>
40050     bool contains(const T& pixel, t& n) const {
40051       t x, y, z, c;
40052       return contains(pixel,n,x,y,z,c);
40053     }
40054 
40055     //! Return \c true if one of the image list contains the specified referenced value.
40056     bool contains(const T& pixel) const {
40057       unsigned int n, x, y, z, c;
40058       return contains(pixel,n,x,y,z,c);
40059     }
40060 
40061     //! Return \c true if the list contains the image 'img'. If true, returns the position (n) of the image in the list.
40062     template<typename t>
40063     bool contains(const CImg<T>& img, t& n) const {
40064       if (is_empty()) return false;
40065       const CImg<T> *const ptr = &img;
40066       cimglist_for(*this,i) if (_data+i==ptr) { n = (t)i; return true; }
40067       return false;
40068     }
40069 
40070     //! Return \c true if the list contains the image img.
40071     bool contains(const CImg<T>& img) const {
40072       unsigned int n;
40073       return contains(img,n);
40074     }
40075 
40076     //@}
40077     //-------------------------------------
40078     //
40079     //! \name Mathematical Functions
40080     //@{
40081     //-------------------------------------
40082 
40083     //! Return a reference to the minimum pixel value of the instance list.
40084     T& min() {
40085       if (is_empty())
40086         throw CImgInstanceException(_cimglist_instance
40087                                     "min() : Empty instance.",
40088                                     cimglist_instance);
40089       T *ptr_min = _data->_data;
40090       T min_value = *ptr_min;
40091       cimglist_for(*this,l) {
40092         const CImg<T>& img = _data[l];
40093         cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
40094       }
40095       return *ptr_min;
40096     }
40097 
40098     const T& min() const {
40099       if (is_empty())
40100         throw CImgInstanceException(_cimglist_instance
40101                                     "min() : Empty instance.",
40102                                     cimglist_instance);
40103       const T *ptr_min = _data->_data;
40104       T min_value = *ptr_min;
40105       cimglist_for(*this,l) {
40106         const CImg<T>& img = _data[l];
40107         cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
40108       }
40109       return *ptr_min;
40110     }
40111 
40112     //! Return a reference to the maximum pixel value of the instance list.
40113     T& max() {
40114       if (is_empty())
40115         throw CImgInstanceException(_cimglist_instance
40116                                     "max() : Empty instance.",
40117                                     cimglist_instance);
40118       T *ptr_max = _data->_data;
40119       T max_value = *ptr_max;
40120       cimglist_for(*this,l) {
40121         const CImg<T>& img = _data[l];
40122         cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
40123       }
40124       return *ptr_max;
40125     }
40126 
40127     const T& max() const {
40128       if (is_empty())
40129         throw CImgInstanceException(_cimglist_instance
40130                                     "max() : Empty instance.",
40131                                     cimglist_instance);
40132       const T *ptr_max = _data->_data;
40133       T max_value = *ptr_max;
40134       cimglist_for(*this,l) {
40135         const CImg<T>& img = _data[l];
40136         cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
40137       }
40138       return *ptr_max;
40139     }
40140 
40141     //! Return a reference to the minimum pixel value of the instance list.
40142     template<typename t>
40143     T& min_max(t& max_val) {
40144       if (is_empty())
40145         throw CImgInstanceException(_cimglist_instance
40146                                     "min_max() : Empty instance.",
40147                                     cimglist_instance);
40148       T *ptr_min = _data->_data;
40149       T min_value = *ptr_min, max_value = min_value;
40150       cimglist_for(*this,l) {
40151         const CImg<T>& img = _data[l];
40152         cimg_for(img,ptrs,T) {
40153           const T val = *ptrs;
40154           if (val<min_value) { min_value = val; ptr_min = ptrs; }
40155           if (val>max_value) max_value = val;
40156         }
40157       }
40158       max_val = (t)max_value;
40159       return *ptr_min;
40160     }
40161 
40162     template<typename t>
40163     const T& min_max(t& max_val) const {
40164       if (is_empty())
40165         throw CImgInstanceException(_cimglist_instance
40166                                     "min_max() : Empty instance.",
40167                                     cimglist_instance);
40168       const T *ptr_min = _data->_data;
40169       T min_value = *ptr_min, max_value = min_value;
40170       cimglist_for(*this,l) {
40171         const CImg<T>& img = _data[l];
40172         cimg_for(img,ptrs,T) {
40173           const T val = *ptrs;
40174           if (val<min_value) { min_value = val; ptr_min = ptrs; }
40175           if (val>max_value) max_value = val;
40176         }
40177       }
40178       max_val = (t)max_value;
40179       return *ptr_min;
40180     }
40181 
40182     //! Return a reference to the minimum pixel value of the instance list.
40183     template<typename t>
40184     T& max_min(t& min_val) {
40185       if (is_empty())
40186         throw CImgInstanceException(_cimglist_instance
40187                                     "max_min() : Empty instance.",
40188                                     cimglist_instance);
40189       T *ptr_max = _data->_data;
40190       T min_value = *ptr_max, max_value = min_value;
40191       cimglist_for(*this,l) {
40192         const CImg<T>& img = _data[l];
40193         cimg_for(img,ptrs,T) {
40194           const T val = *ptrs;
40195           if (val>max_value) { max_value = val; ptr_max = ptrs; }
40196           if (val<min_value) min_value = val;
40197         }
40198       }
40199       min_val = (t)min_value;
40200       return *ptr_max;
40201     }
40202 
40203     template<typename t>
40204     const T& max_min(t& min_val) const {
40205       if (is_empty())
40206         throw CImgInstanceException(_cimglist_instance
40207                                     "max_min() : Empty instance.",
40208                                     cimglist_instance);
40209       const T *ptr_max = _data->_data;
40210       T min_value = *ptr_max, max_value = min_value;
40211       cimglist_for(*this,l) {
40212         const CImg<T>& img = _data[l];
40213         cimg_for(img,ptrs,T) {
40214           const T val = *ptrs;
40215           if (val>max_value) { max_value = val; ptr_max = ptrs; }
40216           if (val<min_value) min_value = val;
40217         }
40218       }
40219       min_val = (t)min_value;
40220       return *ptr_max;
40221     }
40222 
40223     //@}
40224     //---------------------------
40225     //
40226     //! \name List Manipulation
40227     //@{
40228     //---------------------------
40229 
40230     //! Insert a copy of the image \p img into the current image list, at position \p pos.
40231     template<typename t>
40232     CImgList<T>& insert(const CImg<t>& img, const unsigned int pos=~0U, const bool shared=false) {
40233       const unsigned int npos = pos==~0U?_width:pos;
40234       if (npos>_width)
40235         throw CImgArgumentException(_cimglist_instance
40236                                     "insert() : Invalid insertion request of specified image (%u,%u,%u,%u,%p) at position %u.",
40237                                     cimglist_instance,
40238                                     img._width,img._height,img._depth,img._spectrum,img._data,npos);
40239       if (shared)
40240         throw CImgArgumentException(_cimglist_instance
40241                                     "insert() : Invalid insertion request of specified shared image CImg<%s>(%u,%u,%u,%u,%p) at position %u "
40242                                     "(pixel types are different).",
40243                                     cimglist_instance,
40244                                     img.pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data,npos);
40245 
40246       CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1):(_allocated_width=16)]:0;
40247       if (!_width || !_data) {
40248         _data = new_data;
40249         *_data = img;
40250       } else {
40251         if (new_data) {
40252           if (npos) std::memcpy(new_data,_data,sizeof(CImg<T>)*npos);
40253           if (npos!=_width-1) std::memcpy(new_data+npos+1,_data+npos,sizeof(CImg<T>)*(_width-1-npos));
40254           std::memset(_data,0,sizeof(CImg<T>)*(_width-1));
40255           delete[] _data;
40256           _data = new_data;
40257         } else if (npos!=_width-1) std::memmove(_data+npos+1,_data+npos,sizeof(CImg<T>)*(_width-1-npos));
40258         _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0; _data[npos]._data = 0;
40259         _data[npos] = img;
40260       }
40261       return *this;
40262     }
40263 
40264     CImgList<T>& insert(const CImg<T>& img, const unsigned int pos=~0U, const bool shared=false) {
40265       const unsigned int npos = pos==~0U?_width:pos;
40266       if (npos>_width)
40267         throw CImgArgumentException(_cimglist_instance
40268                                     "insert() : Invalid insertion request of specified image (%u,%u,%u,%u,%p) at position %u.",
40269                                     cimglist_instance,
40270                                     img._width,img._height,img._depth,img._spectrum,img._data,npos);
40271       CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1):(_allocated_width=16)]:0;
40272       if (!_width || !_data) {
40273         _data = new_data;
40274         if (shared && img) {
40275           _data->_width = img._width; _data->_height = img._height; _data->_depth = img._depth; _data->_spectrum = img._spectrum;
40276           _data->_is_shared = true; _data->_data = img._data;
40277         } else *_data = img;
40278       }
40279       else {
40280         if (new_data) {
40281           if (npos) std::memcpy(new_data,_data,sizeof(CImg<T>)*npos);
40282           if (npos!=_width-1) std::memcpy(new_data+npos+1,_data+npos,sizeof(CImg<T>)*(_width-1-npos));
40283           if (shared && img) {
40284             new_data[npos]._width = img._width; new_data[npos]._height = img._height; new_data[npos]._depth = img._depth;
40285             new_data[npos]._spectrum = img._spectrum; new_data[npos]._is_shared = true; new_data[npos]._data = img._data;
40286           } else {
40287             new_data[npos]._width = new_data[npos]._height = new_data[npos]._depth = new_data[npos]._spectrum = 0; new_data[npos]._data = 0;
40288             new_data[npos] = img;
40289           }
40290           std::memset(_data,0,sizeof(CImg<T>)*(_width-1));
40291           delete[] _data;
40292           _data = new_data;
40293         } else {
40294           if (npos!=_width-1) std::memmove(_data+npos+1,_data+npos,sizeof(CImg<T>)*(_width-1-npos));
40295           if (shared && img) {
40296             _data[npos]._width = img._width; _data[npos]._height = img._height; _data[npos]._depth = img._depth; _data[npos]._spectrum = img._spectrum;
40297             _data[npos]._is_shared = true; _data[npos]._data = img._data;
40298           } else {
40299             _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0; _data[npos]._data = 0;
40300             _data[npos] = img;
40301           }
40302         }
40303       }
40304       return *this;
40305     }
40306 
40307     template<typename t>
40308     CImgList<T> get_insert(const CImg<t>& img, const unsigned int pos=~0U, const bool shared=false) const {
40309       return (+*this).insert(img,pos,shared);
40310     }
40311 
40312     //! Insert n empty images img into the current image list, at position \p pos.
40313     CImgList<T>& insert(const unsigned int n, const unsigned int pos=~0U) {
40314       CImg<T> empty;
40315       if (!n) return *this;
40316       const unsigned int npos = pos==~0U?_width:pos;
40317       for (unsigned int i = 0; i<n; ++i) insert(empty,npos+i);
40318       return *this;
40319     }
40320 
40321     CImgList<T> get_insert(const unsigned int n, const unsigned int pos=~0U) const {
40322       return (+*this).insert(n,pos);
40323     }
40324 
40325     //! Insert n copies of the image \p img into the current image list, at position \p pos.
40326     template<typename t>
40327     CImgList<T>& insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U, const bool shared=false) {
40328       if (!n) return *this;
40329       const unsigned int npos = pos==~0U?_width:pos;
40330       insert(img,npos,shared);
40331       for (unsigned int i = 1; i<n; ++i) insert(_data[npos],npos+i,shared);
40332       return *this;
40333     }
40334 
40335     template<typename t>
40336     CImgList<T> get_insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U, const bool shared=false) const {
40337       return (+*this).insert(n,img,pos,shared);
40338     }
40339 
40340     //! Insert a copy of the image list \p list into the current image list, starting from position \p pos.
40341     template<typename t>
40342     CImgList<T>& insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool shared=false) {
40343       const unsigned int npos = pos==~0U?_width:pos;
40344       if ((void*)this!=(void*)&list) cimglist_for(list,l) insert(list[l],npos+l,shared);
40345       else insert(CImgList<T>(list),npos,shared);
40346       return *this;
40347     }
40348 
40349     template<typename t>
40350     CImgList<T> get_insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool shared=false) const {
40351       return (+*this).insert(list,pos,shared);
40352     }
40353 
40354     //! Insert n copies of the list \p list at position \p pos of the current list.
40355     template<typename t>
40356     CImgList<T>& insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U, const bool shared=false) {
40357       if (!n) return *this;
40358       const unsigned int npos = pos==~0U?_width:pos;
40359       for (unsigned int i = 0; i<n; ++i) insert(list,npos,shared);
40360       return *this;
40361     }
40362 
40363     template<typename t>
40364     CImgList<T> get_insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U, const bool shared=false) const {
40365       return (+*this).insert(n,list,pos,shared);
40366     }
40367 
40368     //! Remove the images from positions \p pos1 to \p pos2.
40369     CImgList<T>& remove(const unsigned int pos1, const unsigned int pos2) {
40370       const unsigned int
40371         npos1 = pos1<pos2?pos1:pos2,
40372         tpos2 = pos1<pos2?pos2:pos1,
40373         npos2 = tpos2<_width?tpos2:_width-1;
40374       if (npos1>=_width)
40375         throw CImgArgumentException(_cimglist_instance
40376                                     "remove() : Invalid remove request at positions %u->%u.",
40377                                     cimglist_instance,
40378                                     npos1,tpos2);
40379       else {
40380         if (tpos2>=_width)
40381           throw CImgArgumentException(_cimglist_instance
40382                                       "remove() : Invalid remove request at positions %u->%u.",
40383                                       cimglist_instance,
40384                                       npos1,tpos2);
40385 
40386         for (unsigned int k = npos1; k<=npos2; ++k) _data[k].assign();
40387         const unsigned int nb = 1 + npos2 - npos1;
40388         if (!(_width-=nb)) return assign();
40389         if (_width>(_allocated_width>>2) || _allocated_width<=8) { // Removing items without reallocation.
40390           if (npos1!=_width) std::memmove(_data+npos1,_data+npos2+1,sizeof(CImg<T>)*(_width - npos1));
40391           std::memset(_data + _width,0,sizeof(CImg<T>)*nb);
40392         } else { // Removing items with reallocation.
40393           _allocated_width>>=2;
40394           while (_allocated_width>8 && _width<(_allocated_width>>1)) _allocated_width>>=1;
40395           CImg<T> *const new_data = new CImg<T>[_allocated_width];
40396           if (npos1) std::memcpy(new_data,_data,sizeof(CImg<T>)*npos1);
40397           if (npos1!=_width) std::memcpy(new_data+npos1,_data+npos2+1,sizeof(CImg<T>)*(_width-npos1));
40398           if (_width!=_allocated_width) std::memset(new_data+_width,0,sizeof(_allocated_width - _width));
40399           std::memset(_data,0,sizeof(CImg<T>)*(_width+nb));
40400           delete[] _data;
40401           _data = new_data;
40402         }
40403       }
40404       return *this;
40405     }
40406 
40407     CImgList<T> get_remove(const unsigned int pos1, const unsigned int pos2) const {
40408       return (+*this).remove(pos1,pos2);
40409     }
40410 
40411     //! Remove the image at position \p pos from the image list.
40412     CImgList<T>& remove(const unsigned int pos) {
40413       return remove(pos,pos);
40414     }
40415 
40416     CImgList<T> get_remove(const unsigned int pos) const {
40417       return (+*this).remove(pos);
40418     }
40419 
40420     //! Remove the last image from the image list.
40421     CImgList<T>& remove() {
40422       return remove(_width-1);
40423     }
40424 
40425     CImgList<T> get_remove() const {
40426       return (+*this).remove();
40427     }
40428 
40429     //! Reverse list order.
40430     CImgList<T>& reverse() {
40431       for (unsigned int l = 0; l<_width/2; ++l) (*this)[l].swap((*this)[_width-1-l]);
40432       return *this;
40433     }
40434 
40435     CImgList<T> get_reverse() const {
40436       return (+*this).reverse();
40437     }
40438 
40439     //! Get a sub-list.
40440     CImgList<T>& images(const unsigned int i0, const unsigned int i1) {
40441       return get_images(i0,i1).move_to(*this);
40442     }
40443 
40444     CImgList<T> get_images(const unsigned int i0, const unsigned int i1) const {
40445       if (i0>i1 || i1>=_width)
40446         throw CImgArgumentException(_cimglist_instance
40447                                     "images() : Specified sub-list indices (%u->%u) are out of bounds.",
40448                                     cimglist_instance,
40449                                     i0,i1);
40450       CImgList<T> res(i1-i0+1);
40451       cimglist_for(res,l) res[l].assign(_data[i0+l]);
40452       return res;
40453     }
40454 
40455     //! Get a shared sub-list.
40456     CImgList<T> get_shared_images(const unsigned int i0, const unsigned int i1) {
40457       if (i0>i1 || i1>=_width)
40458         throw CImgArgumentException(_cimglist_instance
40459                                     "get_shared_images() : Specified sub-list indices (%u->%u) are out of bounds.",
40460                                     cimglist_instance,
40461                                     i0,i1);
40462       CImgList<T> res(i1-i0+1);
40463       cimglist_for(res,l) res[l].assign(_data[i0+l],true);
40464       return res;
40465     }
40466 
40467     const CImgList<T> get_shared_images(const unsigned int i0, const unsigned int i1) const {
40468       if (i0>i1 || i1>=_width)
40469         throw CImgArgumentException(_cimglist_instance
40470                                     "get_shared_images() : Specified sub-list indices (%u->%u) are out of bounds.",
40471                                     cimglist_instance,
40472                                     i0,i1);
40473       CImgList<T> res(i1-i0+1);
40474       cimglist_for(res,l) res[l].assign(_data[i0+l],true);
40475       return res;
40476     }
40477 
40478     //! Return a single image which is the concatenation of all images of the current CImgList instance.
40479     /**
40480        \param axis : specify the axis for image concatenation. Can be 'x','y','z' or 'c'.
40481        \param align : specify the alignment for image concatenation. Can be '0' (top), '0.5' (center) or '1' (bottom) for instance.
40482        \return A CImg<T> image corresponding to the concatenation is returned.
40483     **/
40484     CImg<T> get_append(const char axis, const float align=0) const {
40485       if (is_empty()) return CImg<T>();
40486       if (_width==1) return +((*this)[0]);
40487       unsigned int dx = 0, dy = 0, dz = 0, dc = 0, pos = 0;
40488       CImg<T> res;
40489       switch (cimg::uncase(axis)) {
40490       case 'x' : { // Along the X-axis.
40491         cimglist_for(*this,l) {
40492           const CImg<T>& img = (*this)[l];
40493           dx+=img._width; dy = cimg::max(dy,img._height); dz = cimg::max(dz,img._depth); dc = cimg::max(dc,img._spectrum);
40494         }
40495         res.assign(dx,dy,dz,dc,0);
40496         if (res) cimglist_for(*this,l) {
40497             res.draw_image(pos,
40498                            (int)(align*(dy-(*this)[l]._height)),
40499                            (int)(align*(dz-(*this)[l]._depth)),
40500                            (int)(align*(dc-(*this)[l]._spectrum)),
40501                            (*this)[l]);
40502             pos+=(*this)[l]._width;
40503           }
40504       } break;
40505       case 'y' : { // Along the Y-axis.
40506         cimglist_for(*this,l) {
40507           const CImg<T>& img = (*this)[l];
40508           dx = cimg::max(dx,img._width); dy+=img._height; dz = cimg::max(dz,img._depth); dc = cimg::max(dc,img._spectrum);
40509         }
40510         res.assign(dx,dy,dz,dc,0);
40511         if (res) cimglist_for(*this,l) {
40512             res.draw_image((int)(align*(dx-(*this)[l]._width)),
40513                            pos,
40514                            (int)(align*(dz-(*this)[l]._depth)),
40515                            (int)(align*(dc-(*this)[l]._spectrum)),
40516                            (*this)[l]);
40517             pos+=(*this)[l]._height;
40518           }
40519       } break;
40520       case 'z' : { // Along the Z-axis.
40521         cimglist_for(*this,l) {
40522           const CImg<T>& img = (*this)[l];
40523           dx = cimg::max(dx,img._width); dy = cimg::max(dy,img._height); dz+=img._depth; dc = cimg::max(dc,img._spectrum);
40524         }
40525         res.assign(dx,dy,dz,dc,0);
40526         if (res) cimglist_for(*this,l) {
40527             res.draw_image((int)(align*(dx-(*this)[l]._width)),
40528                            (int)(align*(dy-(*this)[l]._height)),
40529                            pos,
40530                            (int)(align*(dc-(*this)[l]._spectrum)),
40531                            (*this)[l]);
40532             pos+=(*this)[l]._depth;
40533           }
40534       } break;
40535       default : { // Along the C-axis.
40536         cimglist_for(*this,l) {
40537           const CImg<T>& img = (*this)[l];
40538           dx = cimg::max(dx,img._width); dy = cimg::max(dy,img._height); dz = cimg::max(dz,img._depth); dc+=img._spectrum;
40539         }
40540         res.assign(dx,dy,dz,dc,0);
40541         if (res) cimglist_for(*this,l) {
40542             res.draw_image((int)(align*(dx-(*this)[l]._width)),
40543                            (int)(align*(dy-(*this)[l]._height)),
40544                            (int)(align*(dz-(*this)[l]._depth)),
40545                            pos,
40546                            (*this)[l]);
40547             pos+=(*this)[l]._spectrum;
40548           }
40549       }
40550       }
40551       return res;
40552     }
40553 
40554     //! Return a list where each image has been split along the specified axis.
40555     CImgList<T>& split(const char axis, const int nb=0) {
40556       return get_split(axis,nb).move_to(*this);
40557     }
40558 
40559     CImgList<T> get_split(const char axis, const int nb=0) const {
40560       CImgList<T> res;
40561       cimglist_for(*this,l) _data[l].get_split(axis,nb).move_to(res,~0U);
40562       return res;
40563     }
40564 
40565     //! Insert image \p img at the end of the list (STL-compliant name).
40566     template<typename t>
40567     CImgList<T>& push_back(const CImg<t>& img) {
40568       return insert(img);
40569     }
40570 
40571     //! Insert image \p img at the front of the list (STL-compliant name).
40572     template<typename t>
40573     CImgList<T>& push_front(const CImg<t>& img) {
40574       return insert(img,0);
40575     }
40576 
40577     //! Insert list \p list at the end of the current list (STL-compliant name).
40578     template<typename t>
40579     CImgList<T>& push_back(const CImgList<t>& list) {
40580       return insert(list);
40581     }
40582 
40583     //! Insert list \p list at the front of the current list (STL-compliant name).
40584     template<typename t>
40585     CImgList<T>& push_front(const CImgList<t>& list) {
40586       return insert(list,0);
40587     }
40588 
40589     //! Remove last element of the list (STL-compliant name).
40590     CImgList<T>& pop_back() {
40591       return remove(_width-1);
40592     }
40593 
40594     //! Remove first element of the list (STL-compliant name).
40595     CImgList<T>& pop_front() {
40596       return remove(0);
40597     }
40598 
40599     //! Remove the element pointed by iterator \p iter (STL-compliant name).
40600     CImgList<T>& erase(const iterator iter) {
40601       return remove(iter-_data);
40602     }
40603 
40604     //@}
40605     //----------------------------------
40606     //
40607     //! \name Data Input
40608     //@{
40609     //----------------------------------
40610 
40611     //! Simple interface to select sub-lists or single images in a list.
40612     CImg<intT> get_select(CImgDisplay &disp, const bool feature_type=true,
40613                           const char axis='x', const float align=0) const {
40614       return _get_select(disp,0,feature_type,axis,align,0,false,false,false);
40615     }
40616 
40617     CImg<intT> get_select(const char *const title, const bool feature_type=true,
40618                           const char axis='x', const float align=0) const {
40619       CImgDisplay disp;
40620       return _get_select(disp,title,feature_type,axis,align,0,false,false,false);
40621     }
40622 
40623     CImg<intT> _get_select(CImgDisplay &disp, const char *const title, const bool feature_type,
40624                            const char axis, const float align,
40625                            const unsigned int orig, const bool resize_disp,
40626                            const bool exit_on_rightbutton, const bool exit_on_wheel) const {
40627       if (is_empty())
40628         throw CImgInstanceException(_cimglist_instance
40629                                     "select() : Empty instance.",
40630                                     cimglist_instance);
40631 
40632       // Create image correspondence table and get list dimensions for visualization.
40633       CImgList<uintT> _indices;
40634       unsigned int max_width = 0, max_height = 0, sum_width = 0, sum_height = 0;
40635       cimglist_for(*this,l) if (_data[l]) {
40636         const CImg<T>& img = _data[l];
40637         const unsigned int
40638           w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
40639           h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
40640         if (w>max_width) max_width = w;
40641         if (h>max_height) max_height = h;
40642         sum_width+=w; sum_height+=h;
40643         if (axis=='x') CImg<uintT>(w,1,1,1,(unsigned int)l).move_to(_indices);
40644         else CImg<uintT>(h,1,1,1,(unsigned int)l).move_to(_indices);
40645       }
40646       const CImg<uintT> indices0 = _indices>'x';
40647 
40648       // Create display window.
40649       if (!disp) {
40650         if (axis=='x') disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:0,1);
40651         else disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:0,1);
40652         if (!title) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width);
40653       } else if (title) disp.set_title("%s",title);
40654       if (resize_disp) {
40655         if (axis=='x') disp.resize(cimg_fitscreen(sum_width,max_height,1),false);
40656         else disp.resize(cimg_fitscreen(max_width,sum_height,1),false);
40657       }
40658 
40659       const unsigned int old_normalization = disp.normalization();
40660       bool old_is_resized = disp.is_resized();
40661       disp._normalization = 0;
40662       disp.show().set_key(0);
40663       const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 };
40664 
40665       // Enter event loop.
40666       CImg<ucharT> visu0, visu;
40667       CImg<uintT> indices;
40668       CImg<intT> positions(_width,4,1,1,-1);
40669       int oindice0 = -1, oindice1 = -1, indice0 = -1, indice1 = -1;
40670       bool is_clicked = false, is_selected = false, text_down = false, update_display = true;
40671       unsigned int key = 0;
40672       while (!is_selected && !disp.is_closed() && !key) {
40673 
40674         // Create background image.
40675         if (!visu0) {
40676           visu0.assign(disp._width,disp._height,1,3,0); visu.assign();
40677           (indices0.get_resize(axis=='x'?visu0._width:visu0._height,1)).move_to(indices);
40678           unsigned int ind = 0;
40679           if (axis=='x') for (unsigned int x = 0; x<visu0._width; ) {
40680               const unsigned int x0 = x;
40681               ind = indices[x];
40682               while (x<indices._width && indices[++x]==ind) {}
40683               const CImg<T>
40684                 &src = _data[ind],
40685                 _img2d = src._depth>1?src.get_projections2d(src._width/2,src._height/2,src._depth/2):CImg<T>(),
40686                 &img2d = _img2d?_img2d:src;
40687               CImg<ucharT> res = old_normalization==1?CImg<ucharT>(img2d.get_normalize(0,255)):CImg<ucharT>(img2d);
40688               if (res._spectrum>3) res.channels(0,2);
40689               const unsigned int h = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,true);
40690               res.resize(x - x0,cimg::max(32U,h*disp._height/max_height),1,res._spectrum==1?3:-100);
40691               positions(ind,0) = positions(ind,2) = (int)x0;
40692               positions(ind,1) = positions(ind,3) = (int)(align*(visu0.height()-res.height()));
40693               positions(ind,2)+=res._width;
40694               positions(ind,3)+=res._height - 1;
40695               visu0.draw_image(positions(ind,0),positions(ind,1),res);
40696             } else for (unsigned int y = 0; y<visu0._height; ) {
40697               const unsigned int y0 = y;
40698               ind = indices[y];
40699               while (y<visu0._height && indices[++y]==ind) {}
40700               const CImg<T>
40701                 &src = _data[ind],
40702                 _img2d = src._depth>1?src.get_projections2d(src._width/2,src._height/2,src._depth/2):CImg<T>(),
40703                 &img2d = _img2d?_img2d:src;
40704               CImg<ucharT> res = old_normalization==1?CImg<ucharT>(img2d.get_normalize(0,255)):CImg<ucharT>(img2d);
40705               if (res._spectrum>3) res.channels(0,2);
40706               const unsigned int w = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,false);
40707               res.resize(cimg::max(32U,w*disp._width/max_width),y - y0,1,res._spectrum==1?3:-100);
40708               positions(ind,0) = positions(ind,2) = (int)(align*(visu0.width()-res.width()));
40709               positions(ind,1) = positions(ind,3) = (int)y0;
40710               positions(ind,2)+=res._width - 1;
40711               positions(ind,3)+=res._height;
40712               visu0.draw_image(positions(ind,0),positions(ind,1),res);
40713             }
40714           if (axis=='x') --positions(ind,2); else --positions(ind,3);
40715           update_display = true;
40716         }
40717 
40718         if (!visu || oindice0!=indice0 || oindice1!=indice1) {
40719           if (indice0>=0 && indice1>=0) {
40720             visu.assign(visu0,false);
40721             const int indm = cimg::min(indice0,indice1), indM = cimg::max(indice0,indice1);
40722             for (int ind = indm; ind<=indM; ++ind) if (positions(ind,0)>=0) {
40723                 visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),background_color,0.2f);
40724                 if ((axis=='x' && positions(ind,2) - positions(ind,0)>=8) ||
40725                     (axis!='x' && positions(ind,3) - positions(ind,1)>=8))
40726                   visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),foreground_color,0.9f,0x55555555);
40727               }
40728             const int yt = (int)text_down?visu.height()-13:0;
40729             if (is_clicked) visu.draw_text(0,yt," Images %u - %u, Size = %u",foreground_color,background_color,0.7f,13,
40730                                            orig + indm,orig + indM,indM - indm + 1);
40731             else visu.draw_text(0,yt," Image %u",foreground_color,background_color,0.7f,13,
40732                                 orig + indice0);
40733             update_display = true;
40734           } else visu.assign();
40735         }
40736         if (!visu) { visu.assign(visu0,true); update_display = true; }
40737         if (update_display) { visu.display(disp); update_display = false; }
40738         disp.wait();
40739 
40740         // Manage user events.
40741         const int xm = disp.mouse_x(), ym = disp.mouse_y();
40742         int indice = -1;
40743 
40744         if (xm>=0) {
40745           indice = (int)indices(axis=='x'?xm:ym);
40746           if (disp.button()&1) {
40747             if (!is_clicked) { is_clicked = true; oindice0 = indice0; indice0 = indice; }
40748             oindice1 = indice1; indice1 = indice;
40749             if (!feature_type) is_selected = true;
40750           } else {
40751             if (!is_clicked) { oindice0 = oindice1 = indice0; indice0 = indice1 = indice; }
40752             else is_selected = true;
40753           }
40754         } else {
40755           if (is_clicked) {
40756             if (!(disp.button()&1)) { is_clicked = is_selected = false; indice0 = indice1 = -1; }
40757             else indice1 = -1;
40758           } else indice0 = indice1 = -1;
40759         }
40760 
40761         if (disp.button()&4) { is_clicked = is_selected = false; indice0 = indice1 = -1; }
40762         if (disp.button()&2 && exit_on_rightbutton) { is_selected = true; indice1 = indice0 = -1; }
40763         if (disp.wheel() && exit_on_wheel) is_selected = true;
40764 
40765         switch (key = disp.key()) {
40766 #if cimg_OS!=2
40767         case cimg::keyCTRLRIGHT :
40768 #endif
40769         case 0 : case cimg::keyCTRLLEFT : key = 0; break;
40770         case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
40771             disp.set_fullscreen(false).resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
40772                                               CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
40773               _is_resized = true;
40774             disp.set_key(key,false); key = 0; visu0.assign();
40775           } break;
40776         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
40777             disp.set_fullscreen(false).resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
40778             disp.set_key(key,false); key = 0; visu0.assign();
40779           } break;
40780         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
40781             disp.set_fullscreen(false).resize(cimg_fitscreen(axis=='x'?sum_width:max_width,axis=='x'?max_height:sum_height,1),false)._is_resized = true;
40782             disp.set_key(key,false); key = 0; visu0.assign();
40783           } break;
40784         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
40785             disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
40786             disp.set_key(key,false); key = 0; visu0.assign();
40787           } break;
40788         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
40789             static unsigned int snap_number = 0;
40790             char filename[32] = { 0 };
40791             std::FILE *file;
40792             do {
40793               cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++);
40794               if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
40795             } while (file);
40796             if (visu0) {
40797               visu.draw_text(0,0," Saving snapshot... ",foreground_color,background_color,1,13).display(disp);
40798               visu0.save(filename);
40799               visu.draw_text(0,0," Snapshot '%s' saved. ",foreground_color,background_color,1,13,filename).display(disp);
40800             }
40801             disp.set_key(key,false).wait(); key = 0;
40802           } break;
40803         case cimg::keyO :
40804           if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
40805             static unsigned int snap_number = 0;
40806             char filename[32] = { 0 };
40807             std::FILE *file;
40808             do {
40809 #ifdef cimg_use_zlib
40810               cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++);
40811 #else
40812               cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++);
40813 #endif
40814               if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
40815             } while (file);
40816             visu.draw_text(0,0," Saving instance... ",foreground_color,background_color,1,13).display(disp);
40817             save(filename);
40818             visu.draw_text(0,0," Instance '%s' saved. ",foreground_color,background_color,1,13,filename).display(disp);
40819             disp.set_key(key,false).wait(); key = 0;
40820           } break;
40821         }
40822         if (disp.is_resized()) { disp.resize(false); visu0.assign(); }
40823         if (ym>=0 && ym<13) { if (!text_down) { visu.assign(); text_down = true; }}
40824         else if (ym>=visu.height()-13) { if(text_down) { visu.assign(); text_down = false; }}
40825       }
40826       CImg<intT> res(1,2,1,1,-1);
40827       if (is_selected) { if (feature_type) res.fill(cimg::min(indice0,indice1),cimg::max(indice0,indice1)); else res.fill(indice0); }
40828       if (!(disp.button()&2)) disp.set_button();
40829       disp._normalization = old_normalization;
40830       disp._is_resized = old_is_resized;
40831       disp.set_key(key);
40832       return res;
40833     }
40834 
40835     //! Load an image list from a file.
40836     CImgList<T>& load(const char *const filename) {
40837       if (!filename)
40838         throw CImgArgumentException(_cimglist_instance
40839                                     "load() : Specified filename is (null).",
40840                                     cimglist_instance);
40841 
40842       const char *const ext = cimg::split_filename(filename);
40843       const unsigned int omode = cimg::exception_mode();
40844       cimg::exception_mode() = 0;
40845       try {
40846 #ifdef cimglist_load_plugin
40847         cimglist_load_plugin(filename);
40848 #endif
40849 #ifdef cimglist_load_plugin1
40850         cimglist_load_plugin1(filename);
40851 #endif
40852 #ifdef cimglist_load_plugin2
40853         cimglist_load_plugin2(filename);
40854 #endif
40855 #ifdef cimglist_load_plugin3
40856         cimglist_load_plugin3(filename);
40857 #endif
40858 #ifdef cimglist_load_plugin4
40859         cimglist_load_plugin4(filename);
40860 #endif
40861 #ifdef cimglist_load_plugin5
40862         cimglist_load_plugin5(filename);
40863 #endif
40864 #ifdef cimglist_load_plugin6
40865         cimglist_load_plugin6(filename);
40866 #endif
40867 #ifdef cimglist_load_plugin7
40868         cimglist_load_plugin7(filename);
40869 #endif
40870 #ifdef cimglist_load_plugin8
40871         cimglist_load_plugin8(filename);
40872 #endif
40873         if (!cimg::strcasecmp(ext,"tif") ||
40874             !cimg::strcasecmp(ext,"tiff")) load_tiff(filename);
40875         else if (!cimg::strcasecmp(ext,"cimg") ||
40876                  !cimg::strcasecmp(ext,"cimgz") ||
40877                  !*ext) load_cimg(filename);
40878         else if (!cimg::strcasecmp(ext,"rec") ||
40879                  !cimg::strcasecmp(ext,"par")) load_parrec(filename);
40880         else if (!cimg::strcasecmp(ext,"avi") ||
40881                  !cimg::strcasecmp(ext,"mov") ||
40882                  !cimg::strcasecmp(ext,"asf") ||
40883                  !cimg::strcasecmp(ext,"divx") ||
40884                  !cimg::strcasecmp(ext,"flv") ||
40885                  !cimg::strcasecmp(ext,"mpg") ||
40886                  !cimg::strcasecmp(ext,"m1v") ||
40887                  !cimg::strcasecmp(ext,"m2v") ||
40888                  !cimg::strcasecmp(ext,"m4v") ||
40889                  !cimg::strcasecmp(ext,"mjp") ||
40890                  !cimg::strcasecmp(ext,"mkv") ||
40891                  !cimg::strcasecmp(ext,"mpe") ||
40892                  !cimg::strcasecmp(ext,"movie") ||
40893                  !cimg::strcasecmp(ext,"ogm") ||
40894                  !cimg::strcasecmp(ext,"ogg") ||
40895                  !cimg::strcasecmp(ext,"qt") ||
40896                  !cimg::strcasecmp(ext,"rm") ||
40897                  !cimg::strcasecmp(ext,"vob") ||
40898                  !cimg::strcasecmp(ext,"wmv") ||
40899                  !cimg::strcasecmp(ext,"xvid") ||
40900                  !cimg::strcasecmp(ext,"mpeg")) load_ffmpeg(filename);
40901         else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename);
40902         else throw CImgIOException("CImgList<%s>::load()",
40903                                    pixel_type());
40904       } catch (CImgIOException& e) {
40905         if (!cimg::strncasecmp(e.what(),"cimg::fopen()",13)) {
40906           cimg::exception_mode() = omode;
40907           throw CImgIOException(_cimglist_instance
40908                                 "load() : Failed to open file '%s'.",
40909                                 cimglist_instance,
40910                                 filename);
40911         } else try {
40912           assign(1);
40913           _data->load(filename);
40914         } catch (CImgException&) {
40915           throw CImgIOException(_cimglist_instance
40916                                 "load() : Failed to recognize format of file '%s'.",
40917                                 cimglist_instance,
40918                                 filename);
40919         }
40920       }
40921       cimg::exception_mode() = omode;
40922       return *this;
40923     }
40924 
40925     static CImgList<T> get_load(const char *const filename) {
40926       return CImgList<T>().load(filename);
40927     }
40928 
40929     //! Load an image list from a .cimg file.
40930     CImgList<T>& load_cimg(const char *const filename) {
40931       return _load_cimg(0,filename);
40932     }
40933 
40934     static CImgList<T> get_load_cimg(const char *const filename) {
40935       return CImgList<T>().load_cimg(filename);
40936     }
40937 
40938     //! Load an image list from a .cimg file.
40939     CImgList<T>& load_cimg(std::FILE *const file) {
40940       return _load_cimg(file,0);
40941     }
40942 
40943     static CImgList<T> get_load_cimg(std::FILE *const file) {
40944       return CImgList<T>().load_cimg(file);
40945     }
40946 
40947     CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename) {
40948 #ifdef cimg_use_zlib
40949 #define _cimgz_load_cimg_case(Tss) { \
40950    Bytef *const cbuf = new Bytef[csiz]; \
40951    cimg::fread(cbuf,csiz,nfile); \
40952    raw.assign(W,H,D,C); \
40953    unsigned long destlen = (unsigned long)raw.size()*sizeof(Tss); \
40954    uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \
40955    delete[] cbuf; \
40956    const Tss *ptrs = raw._data; \
40957    for (unsigned int off = raw.size(); off; --off) *(ptrd++) = (T)*(ptrs++); \
40958 }
40959 #else
40960 #define _cimgz_load_cimg_case(Tss) \
40961    throw CImgIOException(_cimglist_instance \
40962                          "load_cimg() : Unable to load compressed data from file '%s' unless zlib is enabled.", \
40963                          cimglist_instance, \
40964                          filename?filename:"(FILE*)");
40965 #endif
40966 
40967 #define _cimg_load_cimg_case(Ts,Tss) \
40968       if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
40969         for (unsigned int l = 0; l<N; ++l) { \
40970           j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0) tmp[j++] = (char)i; tmp[j] = 0; \
40971           W = H = D = C = 0; csiz = 0; \
40972           if ((err = std::sscanf(tmp,"%u %u %u %u #%u",&W,&H,&D,&C,&csiz))<4) \
40973             throw CImgIOException(_cimglist_instance \
40974                                   "load_cimg() : Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'", \
40975                                   cimglist_instance, \
40976                                   W,H,D,C,l,filename?filename:("(FILE*)")); \
40977           if (W*H*D*C>0) { \
40978             CImg<Tss> raw; \
40979             CImg<T> &img = _data[l]; \
40980             img.assign(W,H,D,C); \
40981             T *ptrd = img._data; \
40982             if (err==5) _cimgz_load_cimg_case(Tss) \
40983             else for (long to_read = (long)img.size(); to_read>0; ) { \
40984               raw.assign(cimg::min(to_read,cimg_iobuffer)); \
40985               cimg::fread(raw._data,raw._width,nfile); \
40986               if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); \
40987               to_read-=raw._width; \
40988               const Tss *ptrs = raw._data; \
40989               for (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
40990             } \
40991           } \
40992         } \
40993         loaded = true; \
40994       }
40995 
40996       if (!filename && !file)
40997         throw CImgArgumentException(_cimglist_instance
40998                                     "load_cimg() : Specified filename is (null).",
40999                                     cimglist_instance);
41000 
41001       const int cimg_iobuffer = 12*1024*1024;
41002       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
41003       bool loaded = false, endian = cimg::endianness();
41004       char tmp[256] = { 0 }, str_pixeltype[256] = { 0 }, str_endian[256] = { 0 };
41005       unsigned int j, err, N = 0, W, H, D, C, csiz;
41006       int i;
41007       do {
41008         j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
41009       } while (*tmp=='#' && i!=EOF);
41010       err = std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype,str_endian);
41011       if (err<2) {
41012         if (!file) cimg::fclose(nfile);
41013         throw CImgIOException(_cimglist_instance
41014                               "load_cimg() : CImg header not found in file '%s'.",
41015                               cimglist_instance,
41016                               filename?filename:"(FILE*)");
41017       }
41018       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
41019       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
41020       assign(N);
41021       _cimg_load_cimg_case("bool",bool);
41022       _cimg_load_cimg_case("unsigned_char",unsigned char);
41023       _cimg_load_cimg_case("uchar",unsigned char);
41024       _cimg_load_cimg_case("char",char);
41025       _cimg_load_cimg_case("unsigned_short",unsigned short);
41026       _cimg_load_cimg_case("ushort",unsigned short);
41027       _cimg_load_cimg_case("short",short);
41028       _cimg_load_cimg_case("unsigned_int",unsigned int);
41029       _cimg_load_cimg_case("uint",unsigned int);
41030       _cimg_load_cimg_case("int",int);
41031       _cimg_load_cimg_case("unsigned_long",unsigned long);
41032       _cimg_load_cimg_case("ulong",unsigned long);
41033       _cimg_load_cimg_case("long",long);
41034       _cimg_load_cimg_case("float",float);
41035       _cimg_load_cimg_case("double",double);
41036       if (!loaded) {
41037         if (!file) cimg::fclose(nfile);
41038         throw CImgIOException(_cimglist_instance
41039                               "load_cimg() : Unsupported pixel type '%s' for file '%s'.",
41040                               cimglist_instance,
41041                               str_pixeltype,filename?filename:"(FILE*)");
41042       }
41043       if (!file) cimg::fclose(nfile);
41044       return *this;
41045     }
41046 
41047     //! Load a sub-image list from a non compressed .cimg file.
41048     CImgList<T>& load_cimg(const char *const filename,
41049                            const unsigned int n0, const unsigned int n1,
41050                            const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
41051                            const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1) {
41052       return _load_cimg(0,filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
41053     }
41054 
41055     static CImgList<T> get_load_cimg(const char *const filename,
41056                                      const unsigned int n0, const unsigned int n1,
41057                                      const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
41058                                      const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1) {
41059       return CImgList<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
41060     }
41061 
41062     //! Load a sub-image list from a non compressed .cimg file.
41063     CImgList<T>& load_cimg(std::FILE *const file,
41064                            const unsigned int n0, const unsigned int n1,
41065                            const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
41066                            const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1) {
41067       return _load_cimg(file,0,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
41068     }
41069 
41070     static CImgList<T> get_load_cimg(std::FILE *const file,
41071                                      const unsigned int n0, const unsigned int n1,
41072                                      const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
41073                                      const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1) {
41074       return CImgList<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
41075     }
41076 
41077     CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename,
41078                             const unsigned int n0, const unsigned int n1,
41079                             const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
41080                             const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1) {
41081 #define _cimg_load_cimg_case2(Ts,Tss) \
41082       if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
41083         for (unsigned int l = 0; l<=nn1; ++l) { \
41084           j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0) tmp[j++] = (char)i; tmp[j] = 0; \
41085           W = H = D = C = 0; \
41086           if (std::sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
41087             throw CImgIOException(_cimglist_instance \
41088                                   "load_cimg() : Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'", \
41089                                   cimglist_instance, \
41090                                   W,H,D,C,l,filename?filename:"(FILE*)"); \
41091           if (W*H*D*C>0) { \
41092             if (l<n0 || x0>=W || y0>=H || z0>=D || c0>=D) std::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
41093             else { \
41094               const unsigned int \
41095                 nx1 = x1>=W?W-1:x1, \
41096                 ny1 = y1>=H?H-1:y1, \
41097                 nz1 = z1>=D?D-1:z1, \
41098                 nc1 = c1>=C?C-1:c1; \
41099               CImg<Tss> raw(1 + nx1 - x0); \
41100               CImg<T> &img = _data[l - n0]; \
41101               img.assign(1 + nx1 - x0,1 + ny1 - y0,1 + nz1 - z0,1 + nc1 - c0); \
41102               T *ptrd = img._data; \
41103               const unsigned int skipvb = c0*W*H*D*sizeof(Tss); \
41104               if (skipvb) std::fseek(nfile,skipvb,SEEK_CUR); \
41105               for (unsigned int v = 1 + nc1 - c0; v; --v) { \
41106                 const unsigned int skipzb = z0*W*H*sizeof(Tss); \
41107                 if (skipzb) std::fseek(nfile,skipzb,SEEK_CUR); \
41108                 for (unsigned int z = 1 + nz1 - z0; z; --z) { \
41109                   const unsigned int skipyb = y0*W*sizeof(Tss); \
41110                   if (skipyb) std::fseek(nfile,skipyb,SEEK_CUR); \
41111                   for (unsigned int y = 1 + ny1 - y0; y; --y) { \
41112                     const unsigned int skipxb = x0*sizeof(Tss); \
41113                     if (skipxb) std::fseek(nfile,skipxb,SEEK_CUR); \
41114                     cimg::fread(raw._data,raw._width,nfile); \
41115                     if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); \
41116                     const Tss *ptrs = raw._data; \
41117                     for (unsigned int off = raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
41118                     const unsigned int skipxe = (W-1-nx1)*sizeof(Tss); \
41119                     if (skipxe) std::fseek(nfile,skipxe,SEEK_CUR); \
41120                   } \
41121                   const unsigned int skipye = (H-1-ny1)*W*sizeof(Tss); \
41122                   if (skipye) std::fseek(nfile,skipye,SEEK_CUR); \
41123                 } \
41124                 const unsigned int skipze = (D-1-nz1)*W*H*sizeof(Tss); \
41125                 if (skipze) std::fseek(nfile,skipze,SEEK_CUR); \
41126               } \
41127               const unsigned int skipve = (C-1-nc1)*W*H*D*sizeof(Tss); \
41128               if (skipve) std::fseek(nfile,skipve,SEEK_CUR); \
41129             } \
41130           } \
41131         } \
41132         loaded = true; \
41133       }
41134 
41135       if (!filename && !file)
41136         throw CImgArgumentException(_cimglist_instance
41137                                     "load_cimg() : Specified filename is (null).",
41138                                     cimglist_instance);
41139 
41140       if (n1<n0 || x1<x0 || y1<y0 || z1<z0 || c1<c0)
41141         throw CImgArgumentException(_cimglist_instance
41142                                     "load_cimg() : Invalid specified sub-region coordinates [%u->%u] (%u,%u,%u,%u)->(%u,%u,%u,%u) for file '%s'.",
41143                                     cimglist_instance,
41144                                     n0,n1,x0,y0,z0,c0,x1,y1,z1,filename?filename:"(FILE*)");
41145 
41146       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
41147       bool loaded = false, endian = cimg::endianness();
41148       char tmp[256] = { 0 }, str_pixeltype[256] = { 0 }, str_endian[256] = { 0 };
41149       unsigned int j, err, N, W, H, D, C;
41150       int i;
41151       j = 0; while((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
41152       err = std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype,str_endian);
41153       if (err<2) {
41154         if (!file) cimg::fclose(nfile);
41155         throw CImgIOException(_cimglist_instance
41156                               "load_cimg() : CImg header not found in file '%s'.",
41157                               cimglist_instance,
41158                               filename?filename:"(FILE*)");
41159       }
41160       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
41161       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
41162       const unsigned int nn1 = n1>=N?N-1:n1;
41163       assign(1+nn1-n0);
41164       _cimg_load_cimg_case2("bool",bool);
41165       _cimg_load_cimg_case2("unsigned_char",unsigned char);
41166       _cimg_load_cimg_case2("uchar",unsigned char);
41167       _cimg_load_cimg_case2("char",char);
41168       _cimg_load_cimg_case2("unsigned_short",unsigned short);
41169       _cimg_load_cimg_case2("ushort",unsigned short);
41170       _cimg_load_cimg_case2("short",short);
41171       _cimg_load_cimg_case2("unsigned_int",unsigned int);
41172       _cimg_load_cimg_case2("uint",unsigned int);
41173       _cimg_load_cimg_case2("int",int);
41174       _cimg_load_cimg_case2("unsigned_long",unsigned long);
41175       _cimg_load_cimg_case2("ulong",unsigned long);
41176       _cimg_load_cimg_case2("long",long);
41177       _cimg_load_cimg_case2("float",float);
41178       _cimg_load_cimg_case2("double",double);
41179       if (!loaded) {
41180         if (!file) cimg::fclose(nfile);
41181         throw CImgIOException(_cimglist_instance
41182                               "load_cimg() : Unsupported pixel type '%s' for file '%s'.",
41183                               cimglist_instance,
41184                               str_pixeltype,filename?filename:"(FILE*)");
41185       }
41186       if (!file) cimg::fclose(nfile);
41187       return *this;
41188     }
41189 
41190     //! Load an image list from a PAR/REC (Philips) file.
41191     CImgList<T>& load_parrec(const char *const filename) {
41192       if (!filename)
41193         throw CImgArgumentException(_cimglist_instance
41194                                     "load_parrec() : Specified filename is (null).",
41195                                     cimglist_instance);
41196 
41197       char body[1024] = { 0 }, filenamepar[1024] = { 0 }, filenamerec[1024] = { 0 };
41198       const char *const ext = cimg::split_filename(filename,body);
41199       if (!std::strcmp(ext,"par")) { std::strncpy(filenamepar,filename,sizeof(filenamepar)-1); cimg_snprintf(filenamerec,sizeof(filenamerec),"%s.rec",body); }
41200       if (!std::strcmp(ext,"PAR")) { std::strncpy(filenamepar,filename,sizeof(filenamepar)-1); cimg_snprintf(filenamerec,sizeof(filenamerec),"%s.REC",body); }
41201       if (!std::strcmp(ext,"rec")) { std::strncpy(filenamerec,filename,sizeof(filenamerec)-1); cimg_snprintf(filenamepar,sizeof(filenamepar),"%s.par",body); }
41202       if (!std::strcmp(ext,"REC")) { std::strncpy(filenamerec,filename,sizeof(filenamerec)-1); cimg_snprintf(filenamepar,sizeof(filenamepar),"%s.PAR",body); }
41203       std::FILE *file = cimg::fopen(filenamepar,"r");
41204 
41205       // Parse header file
41206       CImgList<floatT> st_slices;
41207       CImgList<uintT> st_global;
41208       int err;
41209       char line[256] = { 0 };
41210       do { err=std::fscanf(file,"%255[^\n]%*c",line); } while (err!=EOF && (*line=='#' || *line=='.'));
41211       do {
41212         unsigned int sn,sizex,sizey,pixsize;
41213         float rs,ri,ss;
41214         err = std::fscanf(file,"%u%*u%*u%*u%*u%*u%*u%u%*u%u%u%g%g%g%*[^\n]",&sn,&pixsize,&sizex,&sizey,&ri,&rs,&ss);
41215         if (err==7) {
41216           CImg<floatT>::vector((float)sn,(float)pixsize,(float)sizex,(float)sizey,ri,rs,ss,0).move_to(st_slices);
41217           unsigned int i; for (i = 0; i<st_global._width && sn<=st_global[i][2]; ++i) {}
41218           if (i==st_global._width) CImg<uintT>::vector(sizex,sizey,sn).move_to(st_global);
41219           else {
41220             CImg<uintT> &vec = st_global[i];
41221             if (sizex>vec[0]) vec[0] = sizex;
41222             if (sizey>vec[1]) vec[1] = sizey;
41223             vec[2] = sn;
41224           }
41225           st_slices[st_slices._width-1][7] = (float)i;
41226         }
41227       } while (err==7);
41228 
41229       // Read data
41230       std::FILE *file2 = cimg::fopen(filenamerec,"rb");
41231       cimglist_for(st_global,l) {
41232         const CImg<uintT>& vec = st_global[l];
41233         CImg<T>(vec[0],vec[1],vec[2]).move_to(*this);
41234       }
41235 
41236       cimglist_for(st_slices,l) {
41237         const CImg<floatT>& vec = st_slices[l];
41238         const unsigned int
41239           sn = (unsigned int)vec[0] - 1,
41240           pixsize = (unsigned int)vec[1],
41241           sizex = (unsigned int)vec[2],
41242           sizey = (unsigned int)vec[3],
41243           imn = (unsigned int)vec[7];
41244         const float ri = vec[4], rs = vec[5], ss = vec[6];
41245         switch (pixsize) {
41246         case 8 : {
41247           CImg<ucharT> buf(sizex,sizey);
41248           cimg::fread(buf._data,sizex*sizey,file2);
41249           if (cimg::endianness()) cimg::invert_endianness(buf._data,sizex*sizey);
41250           CImg<T>& img = (*this)[imn];
41251           cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
41252         } break;
41253         case 16 : {
41254           CImg<ushortT> buf(sizex,sizey);
41255           cimg::fread(buf._data,sizex*sizey,file2);
41256           if (cimg::endianness()) cimg::invert_endianness(buf._data,sizex*sizey);
41257           CImg<T>& img = (*this)[imn];
41258           cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
41259         } break;
41260         case 32 : {
41261           CImg<uintT> buf(sizex,sizey);
41262           cimg::fread(buf._data,sizex*sizey,file2);
41263           if (cimg::endianness()) cimg::invert_endianness(buf._data,sizex*sizey);
41264           CImg<T>& img = (*this)[imn];
41265           cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
41266         } break;
41267         default :
41268           cimg::fclose(file);
41269           cimg::fclose(file2);
41270           throw CImgIOException(_cimglist_instance
41271                                 "load_parrec() : Unsupported %d-bits pixel type for file '%s'.",
41272                                 cimglist_instance,
41273                                 pixsize,filename);
41274         }
41275       }
41276       cimg::fclose(file);
41277       cimg::fclose(file2);
41278       if (!_width)
41279         throw CImgIOException(_cimglist_instance
41280                               "load_parrec() : Failed to recognize valid PAR-REC data in file '%s'.",
41281                               cimglist_instance,
41282                               filename);
41283       return *this;
41284     }
41285 
41286     static CImgList<T> get_load_parrec(const char *const filename) {
41287       return CImgList<T>().load_parrec(filename);
41288     }
41289 
41290     //! Load an image sequence from a YUV file.
41291     CImgList<T>& load_yuv(const char *const filename,
41292                           const unsigned int sizex, const unsigned int sizey,
41293                           const unsigned int first_frame=0, const unsigned int last_frame=~0U,
41294                           const unsigned int step_frame=1, const bool yuv2rgb=true) {
41295       return _load_yuv(0,filename,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb);
41296     }
41297 
41298     static CImgList<T> get_load_yuv(const char *const filename,
41299                                     const unsigned int sizex, const unsigned int sizey=1,
41300                                     const unsigned int first_frame=0, const unsigned int last_frame=~0U,
41301                                     const unsigned int step_frame=1, const bool yuv2rgb=true) {
41302       return CImgList<T>().load_yuv(filename,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb);
41303     }
41304 
41305     //! Load an image sequence from a YUV file.
41306     CImgList<T>& load_yuv(std::FILE *const file,
41307                           const unsigned int sizex, const unsigned int sizey,
41308                           const unsigned int first_frame=0, const unsigned int last_frame=~0U,
41309                           const unsigned int step_frame=1, const bool yuv2rgb=true) {
41310       return _load_yuv(file,0,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb);
41311     }
41312 
41313     static CImgList<T> get_load_yuv(std::FILE *const file,
41314                                     const unsigned int sizex, const unsigned int sizey=1,
41315                                     const unsigned int first_frame=0, const unsigned int last_frame=~0U,
41316                                     const unsigned int step_frame=1, const bool yuv2rgb=true) {
41317       return CImgList<T>().load_yuv(file,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb);
41318     }
41319 
41320     CImgList<T>& _load_yuv(std::FILE *const file, const char *const filename,
41321                            const unsigned int sizex, const unsigned int sizey,
41322                            const unsigned int first_frame, const unsigned int last_frame,
41323                            const unsigned int step_frame, const bool yuv2rgb) {
41324       if (!filename && !file)
41325         throw CImgArgumentException(_cimglist_instance
41326                                     "load_yuv() : Specified filename is (null).",
41327                                     cimglist_instance);
41328       if (sizex%2 || sizey%2)
41329         throw CImgArgumentException(_cimglist_instance
41330                                     "load_yuv() : Invalid odd XY dimensions %ux%u in file '%s'.",
41331                                     cimglist_instance,
41332                                     sizex,sizey,filename?filename:"(FILE*)");
41333       if (!sizex || !sizey)
41334         throw CImgArgumentException(_cimglist_instance
41335                                     "load_yuv() : Invalid sequence size (%u,%u) in file '%s'.",
41336                                     cimglist_instance,
41337                                     sizex,sizey,filename?filename:"(FILE*)");
41338 
41339       const unsigned int
41340         nfirst_frame = first_frame<last_frame?first_frame:last_frame,
41341         nlast_frame = first_frame<last_frame?last_frame:first_frame,
41342         nstep_frame = step_frame?step_frame:1;
41343 
41344       CImg<ucharT> tmp(sizex,sizey,1,3), UV(sizex/2,sizey/2,1,2);
41345       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
41346       bool stopflag = false;
41347       int err;
41348       if (nfirst_frame) {
41349         err = std::fseek(nfile,nfirst_frame*(sizex*sizey + sizex*sizey/2),SEEK_CUR);
41350         if (err) {
41351           if (!file) cimg::fclose(nfile);
41352           throw CImgIOException(_cimglist_instance
41353                                 "load_yuv() : File '%s' doesn't contain frame number %u.",
41354                                 cimglist_instance,
41355                                 filename?filename:"(FILE*)",nfirst_frame);
41356         }
41357       }
41358       unsigned int frame;
41359       for (frame = nfirst_frame; !stopflag && frame<=nlast_frame; frame+=nstep_frame) {
41360         tmp.fill(0);
41361         // *TRY* to read the luminance part, do not replace by cimg::fread !
41362         err = (int)std::fread((void*)(tmp._data),1,(size_t)(tmp._width*tmp._height),nfile);
41363         if (err!=(int)(tmp._width*tmp._height)) {
41364           stopflag = true;
41365           if (err>0)
41366             cimg::warn(_cimglist_instance
41367                        "load_yuv() : File '%s' contains incomplete data or given image dimensions (%u,%u) are incorrect.",
41368                        cimglist_instance,
41369                        filename?filename:"(FILE*)",sizex,sizey);
41370         } else {
41371           UV.fill(0);
41372           // *TRY* to read the luminance part, do not replace by cimg::fread !
41373           err = (int)std::fread((void*)(UV._data),1,(size_t)(UV.size()),nfile);
41374           if (err!=(int)(UV.size())) {
41375             stopflag = true;
41376             if (err>0)
41377               cimg::warn(_cimglist_instance
41378                          "load_yuv() : File '%s' contains incomplete data or given image dimensions (%u,%u) are incorrect.",
41379                          cimglist_instance,
41380                          filename?filename:"(FILE*)",sizex,sizey);
41381           } else {
41382             cimg_forXY(UV,x,y) {
41383               const int x2 = x*2, y2 = y*2;
41384               tmp(x2,y2,1) = tmp(x2+1,y2,1) = tmp(x2,y2+1,1) = tmp(x2+1,y2+1,1) = UV(x,y,0);
41385               tmp(x2,y2,2) = tmp(x2+1,y2,2) = tmp(x2,y2+1,2) = tmp(x2+1,y2+1,2) = UV(x,y,1);
41386             }
41387             if (yuv2rgb) tmp.YCbCrtoRGB();
41388             insert(tmp);
41389             if (nstep_frame>1) std::fseek(nfile,(nstep_frame-1)*(sizex*sizey + sizex*sizey/2),SEEK_CUR);
41390           }
41391         }
41392       }
41393       if (stopflag && nlast_frame!=~0U && frame!=nlast_frame)
41394         cimg::warn(_cimglist_instance
41395                    "load_yuv() : Frame %d not reached since only %u frames were found in file '%s'.",
41396                    cimglist_instance,
41397                    nlast_frame,frame-1,filename?filename:"(FILE*)");
41398 
41399       if (!file) cimg::fclose(nfile);
41400       return *this;
41401     }
41402 
41403     //! Load an image from a video file, using ffmpeg libraries.
41404     // This piece of code has been firstly created by David Starweather (starkdg(at)users(dot)sourceforge(dot)net)
41405     // I modified it afterwards for direct inclusion in the library core.
41406     CImgList<T>& load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
41407                              const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false) {
41408       if (!filename)
41409         throw CImgArgumentException(_cimglist_instance
41410                                     "load_ffmpeg() : Specified filename is (null).",
41411                                     cimglist_instance);
41412 
41413       const unsigned int
41414         nfirst_frame = first_frame<last_frame?first_frame:last_frame,
41415         nlast_frame = first_frame<last_frame?last_frame:first_frame,
41416         nstep_frame = step_frame?step_frame:1;
41417       assign();
41418 
41419 #ifndef cimg_use_ffmpeg
41420       if ((nfirst_frame || nlast_frame!=~0U || nstep_frame>1) || (resume && (pixel_format || !pixel_format)))
41421         throw CImgArgumentException(_cimglist_instance
41422                                     "load_ffmpeg() : Unable to load sub-frames from file '%s' unless libffmpeg is enabled.",
41423                                     cimglist_instance,
41424                                     filename);
41425 
41426       return load_ffmpeg_external(filename);
41427 #else
41428       const PixelFormat ffmpeg_pixfmt = pixel_format?PIX_FMT_RGB24:PIX_FMT_GRAY8;
41429       avcodec_register_all();
41430       av_register_all();
41431       static AVFormatContext *format_ctx = 0;
41432       static AVCodecContext *codec_ctx = 0;
41433       static AVCodec *codec = 0;
41434       static AVFrame *avframe = avcodec_alloc_frame(), *converted_frame = avcodec_alloc_frame();
41435       static int vstream = 0;
41436 
41437       if (resume) {
41438         if (!format_ctx || !codec_ctx || !codec || !avframe || !converted_frame)
41439           throw CImgArgumentException(_cimglist_instance
41440                                       "load_ffmpeg() : Failed to resume loading of file '%s', due to unallocated FFMPEG structures.",
41441                                       cimglist_instance,
41442                                       filename);
41443       } else {
41444         // Open video file, find main video stream and codec.
41445         if (format_ctx) av_close_input_file(format_ctx);
41446         if (av_open_input_file(&format_ctx,filename,0,0,0)!=0)
41447           throw CImgIOException(_cimglist_instance
41448                                 "load_ffmpeg() : Failed to open file '%s'.",
41449                                 cimglist_instance,
41450                                 filename);
41451 
41452         if (!avframe || !converted_frame || av_find_stream_info(format_ctx)<0) {
41453           av_close_input_file(format_ctx); format_ctx = 0;
41454           return load_ffmpeg_external(filename);
41455         }
41456 #if cimg_verbosity>=3
41457         dump_format(format_ctx,0,0,0);
41458 #endif
41459 
41460         // Special command : Return informations on main video stream.
41461         // as a vector 1x4 containing : (nb_frames,width,height,fps).
41462         if (!first_frame && !last_frame && !step_frame) {
41463           for (vstream = 0; vstream<(int)(format_ctx->nb_streams); ++vstream)
41464             if (format_ctx->streams[vstream]->codec->codec_type==CODEC_TYPE_VIDEO) break;
41465           if (vstream==(int)format_ctx->nb_streams) assign();
41466           else {
41467             CImgList<doubleT> timestamps;
41468             int nb_frames;
41469             AVPacket packet;
41470             // Count frames and store timestamps.
41471             for (nb_frames = 0; av_read_frame(format_ctx,&packet)>=0; av_free_packet(&packet))
41472               if (packet.stream_index==vstream) {
41473                 CImg<doubleT>::vector((double)packet.pts).move_to(timestamps);
41474                 ++nb_frames;
41475               }
41476             // Get frame with, height and fps.
41477             const int
41478               framew = format_ctx->streams[vstream]->codec->width,
41479               frameh = format_ctx->streams[vstream]->codec->height;
41480             const float
41481               num = (float)(format_ctx->streams[vstream]->r_frame_rate).num,
41482               den = (float)(format_ctx->streams[vstream]->r_frame_rate).den,
41483               fps = num/den;
41484             // Return infos as a list.
41485             assign(2);
41486             (*this)[0].assign(1,4).fill((T)nb_frames,(T)framew,(T)frameh,(T)fps);
41487             (*this)[1] = (timestamps>'y');
41488           }
41489           av_close_input_file(format_ctx); format_ctx = 0;
41490           return *this;
41491         }
41492 
41493         for (vstream = 0; vstream<(int)(format_ctx->nb_streams) &&
41494                format_ctx->streams[vstream]->codec->codec_type!=CODEC_TYPE_VIDEO; ) ++vstream;
41495         if (vstream==(int)format_ctx->nb_streams) {
41496           av_close_input_file(format_ctx); format_ctx = 0;
41497           return load_ffmpeg_external(filename);
41498         }
41499         codec_ctx = format_ctx->streams[vstream]->codec;
41500         codec = avcodec_find_decoder(codec_ctx->codec_id);
41501         if (!codec) {
41502           return load_ffmpeg_external(filename);
41503         }
41504         if (avcodec_open(codec_ctx,codec)<0) { // Open codec
41505           return load_ffmpeg_external(filename);
41506         }
41507       }
41508 
41509       // Read video frames
41510       const unsigned int numBytes = avpicture_get_size(ffmpeg_pixfmt,codec_ctx->width,codec_ctx->height);
41511       uint8_t *const buffer = new uint8_t[numBytes];
41512       avpicture_fill((AVPicture *)converted_frame,buffer,ffmpeg_pixfmt,codec_ctx->width,codec_ctx->height);
41513       const T foo = (T)0;
41514       AVPacket packet;
41515       for (unsigned int frame = 0, next_frame = nfirst_frame; frame<=nlast_frame && av_read_frame(format_ctx,&packet)>=0; ) {
41516         if (packet.stream_index==(int)vstream) {
41517           int decoded = 0;
41518 #if defined(AV_VERSION_INT)
41519 #if LIBAVCODEC_VERSION_INT<AV_VERSION_INT(52,26,0)
41520           avcodec_decode_video(codec_ctx,avframe,&decoded,packet.data, packet.size);
41521 #else
41522           avcodec_decode_video2(codec_ctx,avframe,&decoded,&packet);
41523 #endif
41524 #else
41525           avcodec_decode_video(codec_ctx,avframe,&decoded,packet.data, packet.size);
41526 #endif
41527           if (decoded) {
41528             if (frame==next_frame) {
41529               SwsContext *c = sws_getContext(codec_ctx->width,codec_ctx->height,codec_ctx->pix_fmt,codec_ctx->width,
41530                                              codec_ctx->height,ffmpeg_pixfmt,1,0,0,0);
41531               sws_scale(c,avframe->data,avframe->linesize,0,codec_ctx->height,converted_frame->data,converted_frame->linesize);
41532               if (ffmpeg_pixfmt==PIX_FMT_RGB24) {
41533                 CImg<ucharT> next_image(*converted_frame->data,3,codec_ctx->width,codec_ctx->height,1,true);
41534                 next_image._get_permute_axes("yzcx",foo).move_to(*this);
41535               } else {
41536                 CImg<ucharT> next_image(*converted_frame->data,1,codec_ctx->width,codec_ctx->height,1,true);
41537                 next_image._get_permute_axes("yzcx",foo).move_to(*this);
41538               }
41539               next_frame+=nstep_frame;
41540             }
41541             ++frame;
41542           }
41543           av_free_packet(&packet);
41544           if (next_frame>nlast_frame) break;
41545         }
41546       }
41547       delete[] buffer;
41548 #endif
41549       return *this;
41550     }
41551 
41552     static CImgList<T> get_load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
41553                                        const unsigned int step_frame=1, const bool pixel_format=true) {
41554       return CImgList<T>().load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format);
41555     }
41556 
41557     //! Load an image from a video file (MPEG,AVI) using the external tool 'ffmpeg'.
41558     CImgList<T>& load_ffmpeg_external(const char *const filename) {
41559       if (!filename)
41560         throw CImgArgumentException(_cimglist_instance
41561                                     "load_ffmpeg_external() : Specified filename is (null).",
41562                                     cimglist_instance);
41563 
41564       char command[1024] = { 0 }, filetmp[512] = { 0 }, filetmp2[512] = { 0 };
41565       std::FILE *file = 0;
41566       do {
41567         cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
41568         cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_000001.ppm",filetmp);
41569         if ((file=std::fopen(filetmp2,"rb"))!=0) cimg::fclose(file);
41570       } while (file);
41571       cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_%%6d.ppm",filetmp);
41572 #if cimg_OS!=2
41573       cimg_snprintf(command,sizeof(command),"%s -i \"%s\" %s >/dev/null 2>&1",cimg::ffmpeg_path(),filename,filetmp2);
41574 #else
41575       cimg_snprintf(command,sizeof(command),"\"%s -i \"%s\" %s\" >NUL 2>&1",cimg::ffmpeg_path(),filename,filetmp2);
41576 #endif
41577       cimg::system(command,0);
41578       const unsigned int omode = cimg::exception_mode();
41579       cimg::exception_mode() = 0;
41580       assign();
41581       unsigned int i = 1;
41582       for (bool stopflag = false; !stopflag; ++i) {
41583         cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_%.6u.ppm",filetmp,i);
41584         CImg<T> img;
41585         try { img.load_pnm(filetmp2); }
41586         catch (CImgException&) { stopflag = true; }
41587         if (img) { img.move_to(*this); std::remove(filetmp2); }
41588       }
41589       cimg::exception_mode() = omode;
41590       if (is_empty())
41591         throw CImgIOException(_cimglist_instance
41592                               "load_ffmpeg_external() : Failed to open file '%s' with external command 'ffmpeg'.",
41593                               cimglist_instance,
41594                               filename);
41595       return *this;
41596     }
41597 
41598     static CImgList<T> get_load_ffmpeg_external(const char *const filename) {
41599       return CImgList<T>().load_ffmpeg_external(filename);
41600     }
41601 
41602     //! Load a gzipped list, using external tool 'gunzip'.
41603     CImgList<T>& load_gzip_external(const char *const filename) {
41604       if (!filename)
41605         throw CImgIOException(_cimglist_instance
41606                               "load_gzip_external() : Specified filename is (null).",
41607                               cimglist_instance);
41608 
41609       char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
41610       const char
41611         *ext = cimg::split_filename(filename,body),
41612         *ext2 = cimg::split_filename(body,0);
41613       std::FILE *file = 0;
41614       do {
41615         if (!cimg::strcasecmp(ext,"gz")) {
41616           if (*ext2) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
41617           else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
41618         } else {
41619           if (*ext) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
41620           else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
41621         }
41622         if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
41623       } while (file);
41624       cimg_snprintf(command,sizeof(command),"%s -c \"%s\" > %s",cimg::gunzip_path(),filename,filetmp);
41625       cimg::system(command);
41626       if (!(file = std::fopen(filetmp,"rb"))) {
41627         cimg::fclose(cimg::fopen(filename,"r"));
41628         throw CImgIOException(_cimglist_instance
41629                               "load_gzip_external() : Failed to open file '%s'.",
41630                               cimglist_instance,
41631                               filename);
41632 
41633       } else cimg::fclose(file);
41634       load(filetmp);
41635       std::remove(filetmp);
41636       return *this;
41637     }
41638 
41639     static CImgList<T> get_load_gzip_external(const char *const filename) {
41640       return CImgList<T>().load_gzip_external(filename);
41641     }
41642 
41643     //! Load a 3d object from a .OFF file.
41644     template<typename tf, typename tc>
41645     CImgList<T>& load_off(const char *const filename,
41646                           CImgList<tf>& primitives, CImgList<tc>& colors) {
41647       return get_load_off(filename,primitives,colors).move_to(*this);
41648     }
41649 
41650     template<typename tf, typename tc>
41651       static CImgList<T> get_load_off(const char *const filename,
41652                                       CImgList<tf>& primitives, CImgList<tc>& colors) {
41653       return CImg<T>().load_off(filename,primitives,colors)<'x';
41654     }
41655 
41656     //! Load a TIFF file.
41657     CImgList<T>& load_tiff(const char *const filename,
41658                            const unsigned int first_frame=0, const unsigned int last_frame=~0U,
41659                            const unsigned int step_frame=1) {
41660       const unsigned int
41661         nfirst_frame = first_frame<last_frame?first_frame:last_frame,
41662         nstep_frame = step_frame?step_frame:1;
41663       unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
41664 #ifndef cimg_use_tiff
41665       if (nfirst_frame || nlast_frame!=~0U || nstep_frame!=1)
41666         throw CImgArgumentException(_cimglist_instance
41667                                     "load_tiff() : Unable to load sub-images from file '%s' unless libtiff is enabled.",
41668                                     cimglist_instance,
41669                                     filename);
41670 
41671       return assign(CImg<T>::get_load_tiff(filename));
41672 #else
41673       TIFF *tif = TIFFOpen(filename,"r");
41674       if (tif) {
41675         unsigned int nb_images = 0;
41676         do ++nb_images; while (TIFFReadDirectory(tif));
41677         if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images))
41678           cimg::warn(_cimglist_instance
41679                      "load_tiff() : Invalid specified frame range is [%u,%u] (step %u) since file '%s' contains %u image(s).",
41680                      cimglist_instance,
41681                      nfirst_frame,nlast_frame,nstep_frame,filename,nb_images);
41682 
41683         if (nfirst_frame>=nb_images) return assign();
41684         if (nlast_frame>=nb_images) nlast_frame = nb_images-1;
41685         assign(1+(nlast_frame-nfirst_frame)/nstep_frame);
41686         TIFFSetDirectory(tif,0);
41687 #if cimg_verbosity>=3
41688         TIFFSetWarningHandler(0);
41689         TIFFSetErrorHandler(0);
41690 #endif
41691         cimglist_for(*this,l) _data[l]._load_tiff(tif,nfirst_frame + l*nstep_frame);
41692         TIFFClose(tif);
41693       } else throw CImgException(_cimglist_instance
41694                                  "load_tiff() : Failed to open file '%s'.",
41695                                  cimglist_instance,
41696                                  filename);
41697       return *this;
41698 #endif
41699     }
41700 
41701     static CImgList<T> get_load_tiff(const char *const filename,
41702                                      const unsigned int first_frame=0, const unsigned int last_frame=~0U,
41703                                      const unsigned int step_frame=1) {
41704       return CImgList<T>().load_tiff(filename,first_frame,last_frame,step_frame);
41705     }
41706 
41707     //@}
41708     //----------------------------------
41709     //
41710     //! \name Data Output
41711     //@{
41712     //----------------------------------
41713 
41714     //! Print informations about the list on the standard output.
41715     const CImgList<T>& print(const char *const title=0, const bool display_stats=true) const {
41716       unsigned int msiz = 0;
41717       cimglist_for(*this,l) msiz+=_data[l].size();
41718       msiz*=sizeof(T);
41719       const unsigned int mdisp = msiz<8*1024?0:(msiz<8*1024*1024?1:2);
41720       char _title[64] = { 0 };
41721       if (!title) cimg_snprintf(_title,sizeof(_title),"CImgList<%s>",pixel_type());
41722       std::fprintf(cimg::output(),"%s: this = %p, size = %u [%u %s], data = (CImg<%s>*)%p",
41723                    title?title:_title,(void*)this,_width,
41724                    mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)),
41725                    mdisp==0?"b":(mdisp==1?"Kb":"Mb"),
41726                    pixel_type(),(void*)begin());
41727       if (_data) std::fprintf(cimg::output(),"..%p.\n",(void*)((char*)end()-1));
41728       else std::fprintf(cimg::output(),".\n");
41729 
41730       char tmp[16] = { 0 };
41731       cimglist_for(*this,ll) {
41732         cimg_snprintf(tmp,sizeof(tmp),"[%d]",ll);
41733         std::fprintf(cimg::output(),"  ");
41734         _data[ll].print(tmp,display_stats);
41735         if (ll==3 && _width>8) { ll = _width-5; std::fprintf(cimg::output(),"  ...\n"); }
41736       }
41737       std::fflush(cimg::output());
41738       return *this;
41739     }
41740 
41741     //! Display the current CImgList instance in an existing CImgDisplay window (by reference).
41742     /**
41743        This function displays the list images of the current CImgList instance into an existing CImgDisplay window.
41744        Images of the list are concatenated in a single temporarly image for visualization purposes.
41745        The function returns immediately.
41746        \param disp : reference to an existing CImgDisplay instance, where the current image list will be displayed.
41747        \param axis : specify the axis for image concatenation. Can be 'x','y','z' or 'c'.
41748        \param align : specify the alignment for image concatenation.
41749        \return A reference to the current CImgList instance is returned.
41750     **/
41751     const CImgList<T>& display(CImgDisplay &disp, const char axis='x', const float align=0) const {
41752       get_append(axis,align).display(disp);
41753       return *this;
41754     }
41755 
41756     //! Display the current CImgList instance in a new display window.
41757     /**
41758        This function opens a new window with a specific title and displays the list images of the current CImgList instance into it.
41759        Images of the list are concatenated in a single temporarly image for visualization purposes.
41760        The function returns when a key is pressed or the display window is closed by the user.
41761        \param title : specify the title of the opening display window.
41762        \param axis : specify the axis for image concatenation. Can be 'x','y','z' or 'c'.
41763        \param align : specify the alignment for image concatenation.
41764        \return A reference to the current CImgList instance is returned.
41765     **/
41766     const CImgList<T>& display(CImgDisplay &disp, const bool display_info,
41767                                const char axis='x', const float align=0) const {
41768       bool is_exit = false;
41769       return _display(disp,0,display_info,axis,align,0,true,is_exit);
41770     }
41771 
41772     //! Display the current CImgList instance in a new display window.
41773     const CImgList<T>& display(const char *const title=0, const bool display_info=true,
41774                                const char axis='x', const float align=0) const {
41775       CImgDisplay disp;
41776       bool is_exit = false;
41777       return _display(disp,title,display_info,axis,align,0,true,is_exit);
41778     }
41779 
41780     const CImgList<T>& _display(CImgDisplay &disp, const char *const title, const bool display_info,
41781                                 const char axis, const float align,
41782                                 const unsigned int orig, const bool is_first_call, bool &is_exit) const {
41783       if (is_empty())
41784         throw CImgInstanceException(_cimglist_instance
41785                                     "display() : Empty instance.",
41786                                     cimglist_instance);
41787       if (!disp) {
41788         if (axis=='x') {
41789           unsigned int sum_width = 0, max_height = 0;
41790           cimglist_for(*this,l) {
41791             const CImg<T> &img = _data[l];
41792             const unsigned int
41793               w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
41794               h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
41795             sum_width+=w;
41796             if (h>max_height) max_height = h;
41797           }
41798           disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:0,1);
41799         } else {
41800           unsigned int max_width = 0, sum_height = 0;
41801           cimglist_for(*this,l) {
41802             const CImg<T> &img = _data[l];
41803             const unsigned int
41804               w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
41805               h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
41806             if (w>max_width) max_width = w;
41807             sum_height+=h;
41808           }
41809           disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:0,1);
41810         }
41811         if (!title) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width);
41812       } else if (title) disp.set_title("%s",title);
41813       const CImg<char> dtitle = CImg<char>::string(disp.title());
41814       if (display_info) print(disp.title());
41815       disp.show().flush();
41816 
41817       if (_width==1) {
41818         if (!is_first_call)
41819           disp.resize(cimg_fitscreen(_data[0]._width,_data[0]._height,_data[0]._depth),false).
41820             set_title("%s (%ux%ux%ux%u)",dtitle.data(),_data[0]._width,_data[0]._height,_data[0]._depth,_data[0]._spectrum);
41821         _data[0]._display(disp,0,false,!is_first_call);
41822         if (disp.key()) is_exit = true;
41823         disp.set_title("%s",dtitle.data());
41824       } else {
41825         bool disp_resize = !is_first_call;
41826         while (!disp.is_closed() && !is_exit) {
41827           const CImg<intT> s = _get_select(disp,0,true,axis,align,orig,disp_resize,!is_first_call,true);
41828           disp_resize = true;
41829           if (s[0]<0) { // No selections done.
41830             if (disp.button()&2) { disp.flush(); break; }
41831             is_exit = true;
41832           } else if (disp.wheel()) { // Zoom in/out.
41833             const int wheel = disp.wheel();
41834             disp.set_wheel();
41835             if (!is_first_call && wheel<0) break;
41836             if (wheel>0 && _width>=4) {
41837               const unsigned int
41838                 delta = cimg::max(1U,(unsigned int)cimg::round(0.3*_width)),
41839                 ind0 = (unsigned int)cimg::max(0,s[0] - (int)delta),
41840                 ind1 = (unsigned int)cimg::min(width() - 1,s[0] + (int)delta);
41841               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);
41842             }
41843           } 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);
41844         }
41845       }
41846       return *this;
41847     }
41848 
41849     //! Save an image list into a file.
41850     /**
41851        Depending on the extension of the given filename, a file format is chosen for the output file.
41852     **/
41853     const CImgList<T>& save(const char *const filename, const int number=-1) const {
41854       if (!filename)
41855         throw CImgArgumentException(_cimglist_instance
41856                                     "save() : Specified filename is (null).",
41857                                     cimglist_instance);
41858       // Do not test for empty instances, since .cimg format is able to manage empty instances.
41859       const char *const ext = cimg::split_filename(filename);
41860       char nfilename[1024] = { 0 };
41861       const char *const fn = (number>=0)?cimg::number_filename(filename,number,6,nfilename):filename;
41862 #ifdef cimglist_save_plugin
41863       cimglist_save_plugin(fn);
41864 #endif
41865 #ifdef cimglist_save_plugin1
41866       cimglist_save_plugin1(fn);
41867 #endif
41868 #ifdef cimglist_save_plugin2
41869       cimglist_save_plugin2(fn);
41870 #endif
41871 #ifdef cimglist_save_plugin3
41872       cimglist_save_plugin3(fn);
41873 #endif
41874 #ifdef cimglist_save_plugin4
41875       cimglist_save_plugin4(fn);
41876 #endif
41877 #ifdef cimglist_save_plugin5
41878       cimglist_save_plugin5(fn);
41879 #endif
41880 #ifdef cimglist_save_plugin6
41881       cimglist_save_plugin6(fn);
41882 #endif
41883 #ifdef cimglist_save_plugin7
41884       cimglist_save_plugin7(fn);
41885 #endif
41886 #ifdef cimglist_save_plugin8
41887       cimglist_save_plugin8(fn);
41888 #endif
41889       if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true);
41890       else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false);
41891       else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,true);
41892       else if (!cimg::strcasecmp(ext,"avi") ||
41893                !cimg::strcasecmp(ext,"mov") ||
41894                !cimg::strcasecmp(ext,"asf") ||
41895                !cimg::strcasecmp(ext,"divx") ||
41896                !cimg::strcasecmp(ext,"flv") ||
41897                !cimg::strcasecmp(ext,"mpg") ||
41898                !cimg::strcasecmp(ext,"m1v") ||
41899                !cimg::strcasecmp(ext,"m2v") ||
41900                !cimg::strcasecmp(ext,"m4v") ||
41901                !cimg::strcasecmp(ext,"mjp") ||
41902                !cimg::strcasecmp(ext,"mkv") ||
41903                !cimg::strcasecmp(ext,"mpe") ||
41904                !cimg::strcasecmp(ext,"movie") ||
41905                !cimg::strcasecmp(ext,"ogm") ||
41906                !cimg::strcasecmp(ext,"ogg") ||
41907                !cimg::strcasecmp(ext,"qt") ||
41908                !cimg::strcasecmp(ext,"rm") ||
41909                !cimg::strcasecmp(ext,"vob") ||
41910                !cimg::strcasecmp(ext,"wmv") ||
41911                !cimg::strcasecmp(ext,"xvid") ||
41912                !cimg::strcasecmp(ext,"mpeg")) return save_ffmpeg(fn);
41913 #ifdef cimg_use_tiff
41914       else if (!cimg::strcasecmp(ext,"tif") ||
41915           !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn);
41916 #endif
41917       else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn);
41918       else { if (_width==1) _data[0].save(fn,-1); else cimglist_for(*this,l) _data[l].save(fn,l); }
41919       return *this;
41920     }
41921 
41922     // Tell if a CImgList can be saved as one single file.
41923     static bool is_saveable(const char *const filename) {
41924       const char *const ext = cimg::split_filename(filename);
41925       if (!cimg::strcasecmp(ext,"cimgz") ||
41926 #ifdef cimg_use_tiff
41927           !cimg::strcasecmp(ext,"tif") ||
41928           !cimg::strcasecmp(ext,"tiff") ||
41929 #endif
41930           !cimg::strcasecmp(ext,"yuv") ||
41931           !cimg::strcasecmp(ext,"avi") ||
41932           !cimg::strcasecmp(ext,"mov") ||
41933           !cimg::strcasecmp(ext,"asf") ||
41934           !cimg::strcasecmp(ext,"divx") ||
41935           !cimg::strcasecmp(ext,"flv") ||
41936           !cimg::strcasecmp(ext,"mpg") ||
41937           !cimg::strcasecmp(ext,"m1v") ||
41938           !cimg::strcasecmp(ext,"m2v") ||
41939           !cimg::strcasecmp(ext,"m4v") ||
41940           !cimg::strcasecmp(ext,"mjp") ||
41941           !cimg::strcasecmp(ext,"mkv") ||
41942           !cimg::strcasecmp(ext,"mpe") ||
41943           !cimg::strcasecmp(ext,"movie") ||
41944           !cimg::strcasecmp(ext,"ogm") ||
41945           !cimg::strcasecmp(ext,"ogg") ||
41946           !cimg::strcasecmp(ext,"qt") ||
41947           !cimg::strcasecmp(ext,"rm") ||
41948           !cimg::strcasecmp(ext,"vob") ||
41949           !cimg::strcasecmp(ext,"wmv") ||
41950           !cimg::strcasecmp(ext,"xvid") ||
41951           !cimg::strcasecmp(ext,"mpeg")) return true;
41952       return false;
41953     }
41954 
41955     //! Save an image sequence, using FFMPEG library.
41956     // This piece of code has been originally written by David. G. Starkweather.
41957     const CImgList<T>& save_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
41958                                    const unsigned int fps=25, const unsigned int bitrate=2048) const {
41959       if (!filename)
41960         throw CImgArgumentException(_cimglist_instance
41961                                     "save_ffmpeg() : Specified filename is (null).",
41962                                     cimglist_instance);
41963       if (is_empty())
41964         throw CImgInstanceException(_cimglist_instance
41965                                     "save_ffmpeg() : Empty instance, for file '%s'.",
41966                                     cimglist_instance,
41967                                     filename);
41968       if (!fps)
41969         throw CImgArgumentException(_cimglist_instance
41970                                     "save_ffmpeg() : Invalid specified framerate 0, for file '%s'.",
41971                                     cimglist_instance,
41972                                     filename);
41973 
41974       const unsigned int nlast_frame = last_frame==~0U?_width-1:last_frame;
41975       if (first_frame>=_width || nlast_frame>=_width)
41976         throw CImgArgumentException(_cimglist_instance
41977                                     "save_ffmpeg() : Out of range specified frames [%u,%u], for file '%s'.",
41978                                     cimglist_instance,
41979                                     first_frame,last_frame,filename);
41980 
41981       for (unsigned int ll = first_frame; ll<=nlast_frame; ++ll) if (!_data[ll].is_sameXYZ(_data[0]))
41982         throw CImgInstanceException(_cimglist_instance
41983                                     "save_ffmpeg() : Invalid instance dimensions, for file '%s'.",
41984                                     cimglist_instance,
41985                                     filename);
41986 
41987 #ifndef cimg_use_ffmpeg
41988       return save_ffmpeg_external(filename,first_frame,last_frame,"mpeg2video",fps,bitrate);
41989 #else
41990       avcodec_register_all();
41991       av_register_all();
41992       const int
41993         frame_dimx = _data[first_frame].width(),
41994         frame_dimy = _data[first_frame].height(),
41995         frame_dimv = _data[first_frame].spectrum();
41996       if (frame_dimv!=1 && frame_dimv!=3)
41997         throw CImgInstanceException(_cimglist_instance
41998                                     "save_ffmpeg() : Image[0] (%u,%u,%u,%u,%p) has not 1 or 3 channels, for file '%s'.",
41999                                     cimglist_instance,
42000                                     _data[0]._width,_data[0]._height,_data[0]._depth,_data[0]._spectrum,_data,filename);
42001 
42002       PixelFormat dest_pxl_fmt = PIX_FMT_YUV420P;
42003       PixelFormat src_pxl_fmt  = (frame_dimv==3)?PIX_FMT_RGB24:PIX_FMT_GRAY8;
42004 
42005       int sws_flags = SWS_FAST_BILINEAR; // Interpolation method (keeping same size images for now).
42006       AVOutputFormat *fmt = 0;
42007 #if defined(AV_VERSION_INT)
42008 #if LIBAVFORMAT_VERSION_INT<AV_VERSION_INT(52,45,0)
42009       fmt = guess_format(0,filename,0);
42010       if (!fmt) fmt = guess_format("mpeg",0,0); // Default format "mpeg".
42011 #else
42012       fmt = av_guess_format(0,filename,0);
42013       if (!fmt) fmt = av_guess_format("mpeg",0,0); // Default format "mpeg".
42014 #endif
42015 #else
42016       fmt = guess_format(0,filename,0);
42017       if (!fmt) fmt = guess_format("mpeg",0,0); // Default format "mpeg".
42018 #endif
42019 
42020       if (!fmt)
42021         throw CImgArgumentException(_cimglist_instance
42022                                     "save_ffmpeg() : Unable to determine codec for file '%s'.",
42023                                     cimglist_instance,
42024                                     filename);
42025 
42026       AVFormatContext *oc = 0;
42027 #if defined(AV_VERSION_INT)
42028 #if LIBAVFORMAT_VERSION_INT<AV_VERSION_INT(52,36,0)
42029       oc = av_alloc_format_context();
42030 #else
42031       oc = avformat_alloc_context();
42032 #endif
42033 #else
42034       oc = av_alloc_format_context();
42035 #endif
42036       if (!oc) // Failed to allocate format context.
42037         throw CImgIOException(_cimglist_instance
42038                               "save_ffmpeg() : Failed to allocate FFMPEG structure for format context, for file '%s'.",
42039                               cimglist_instance,
42040                               filename);
42041 
42042       AVCodec *codec = 0;
42043       AVFrame *picture = 0;
42044       AVFrame *tmp_pict = 0;
42045       oc->oformat = fmt;
42046       std::sprintf(oc->filename,"%s",filename);
42047 
42048       // Add video stream.
42049       int stream_index = 0;
42050       AVStream *video_str = 0;
42051       if (fmt->video_codec!=CODEC_ID_NONE) {
42052         video_str = av_new_stream(oc,stream_index);
42053         if (!video_str) { // Failed to allocate stream.
42054           av_free(oc);
42055           throw CImgIOException(_cimglist_instance
42056                                 "save_ffmpeg() : Failed to allocate FFMPEG structure for video stream, for file '%s'.",
42057                                 cimglist_instance,
42058                                 filename);
42059         }
42060       } else { // No codec identified.
42061         av_free(oc);
42062         throw CImgIOException(_cimglist_instance
42063                               "save_ffmpeg() : Failed to identify proper codec, for file '%s'.",
42064                               cimglist_instance,
42065                               filename);
42066       }
42067 
42068       AVCodecContext *c = video_str->codec;
42069       c->codec_id = fmt->video_codec;
42070       c->codec_type = CODEC_TYPE_VIDEO;
42071       c->bit_rate = 1024*bitrate;
42072       c->width = frame_dimx;
42073       c->height = frame_dimy;
42074       c->time_base.num = 1;
42075       c->time_base.den = fps;
42076       c->gop_size = 12;
42077       c->pix_fmt = dest_pxl_fmt;
42078       if (c->codec_id==CODEC_ID_MPEG2VIDEO) c->max_b_frames = 2;
42079       if (c->codec_id==CODEC_ID_MPEG1VIDEO) c->mb_decision = 2;
42080 
42081       if (av_set_parameters(oc,0)<0) { // Parameters not properly set.
42082         av_free(oc);
42083         throw CImgIOException(_cimglist_instance
42084                               "save_ffmpeg() : Invalid parameters set for avcodec, for file '%s'.",
42085                               cimglist_instance,
42086                               filename);
42087       }
42088 
42089       // Open codecs and alloc buffers.
42090       codec = avcodec_find_encoder(c->codec_id);
42091       if (!codec) { // Failed to find codec.
42092         av_free(oc);
42093         throw CImgIOException(_cimglist_instance
42094                               "save_ffmpeg() : No valid codec found for file '%s'.",
42095                               cimglist_instance,
42096                               filename);
42097       }
42098       if (avcodec_open(c,codec)<0) // Failed to open codec.
42099         throw CImgIOException(_cimglist_instance
42100                               "save_ffmpeg() : Failed to open codec for file '%s'.",
42101                               cimglist_instance,
42102                               filename);
42103 
42104       tmp_pict = avcodec_alloc_frame();
42105       if (!tmp_pict) { // Failed to allocate memory for tmp_pict frame.
42106         avcodec_close(video_str->codec);
42107         av_free(oc);
42108         throw CImgIOException(_cimglist_instance
42109                               "save_ffmpeg() : Failed to allocate memory for file '%s'.",
42110                               cimglist_instance,
42111                               filename);
42112       }
42113       tmp_pict->linesize[0] = (src_pxl_fmt==PIX_FMT_RGB24)?3*frame_dimx:frame_dimx;
42114       tmp_pict->type = FF_BUFFER_TYPE_USER;
42115       int tmp_size = avpicture_get_size(src_pxl_fmt,frame_dimx,frame_dimy);
42116       uint8_t *tmp_buffer = (uint8_t*)av_malloc(tmp_size);
42117       if (!tmp_buffer) { // Failed to allocate memory for tmp buffer.
42118         av_free(tmp_pict);
42119         avcodec_close(video_str->codec);
42120         av_free(oc);
42121         throw CImgIOException(_cimglist_instance
42122                               "save_ffmpeg() : Failed to allocate memory for file '%s'.",
42123                               cimglist_instance,
42124                               filename);
42125       }
42126 
42127       // Associate buffer with tmp_pict.
42128       avpicture_fill((AVPicture*)tmp_pict,tmp_buffer,src_pxl_fmt,frame_dimx,frame_dimy);
42129       picture = avcodec_alloc_frame();
42130       if (!picture) { // Failed to allocate picture frame.
42131         av_free(tmp_pict->data[0]);
42132         av_free(tmp_pict);
42133         avcodec_close(video_str->codec);
42134         av_free(oc);
42135         throw CImgIOException(_cimglist_instance
42136                               "save_ffmpeg() : Failed to allocate memory for file '%s'.",
42137                               cimglist_instance,
42138                               filename);
42139       }
42140 
42141       int size = avpicture_get_size(c->pix_fmt,frame_dimx,frame_dimy);
42142       uint8_t *buffer = (uint8_t*)av_malloc(size);
42143       if (!buffer) { // Failed to allocate picture frame buffer.
42144         av_free(picture);
42145         av_free(tmp_pict->data[0]);
42146         av_free(tmp_pict);
42147         avcodec_close(video_str->codec);
42148         av_free(oc);
42149         throw CImgIOException(_cimglist_instance
42150                               "save_ffmpeg() : Failed to allocate memory for file '%s'.",
42151                               cimglist_instance,
42152                               filename);
42153       }
42154 
42155       // Associate the buffer with picture.
42156       avpicture_fill((AVPicture*)picture,buffer,c->pix_fmt,frame_dimx,frame_dimy);
42157 
42158       // Open file.
42159       if (!(fmt->flags&AVFMT_NOFILE)) {
42160         if (url_fopen(&oc->pb,filename,URL_WRONLY)<0)
42161           throw CImgIOException(_cimglist_instance
42162                                 "save_ffmpeg() : Failed to open file '%s'.",
42163                                 cimglist_instance,
42164                                 filename);
42165       }
42166 
42167       if (av_write_header(oc)<0)
42168         throw CImgIOException(_cimglist_instance
42169                               "save_ffmpeg() : Failed to write header in file '%s'.",
42170                               cimglist_instance,
42171                               filename);
42172 
42173       double video_pts;
42174       SwsContext *img_convert_context = 0;
42175       img_convert_context = sws_getContext(frame_dimx,frame_dimy,src_pxl_fmt,
42176                                            c->width,c->height,c->pix_fmt,sws_flags,0,0,0);
42177       if (!img_convert_context) { // Failed to get swscale context.
42178         // if (!(fmt->flags & AVFMT_NOFILE)) url_fclose(&oc->pb);
42179         av_free(picture->data);
42180         av_free(picture);
42181         av_free(tmp_pict->data[0]);
42182         av_free(tmp_pict);
42183         avcodec_close(video_str->codec);
42184         av_free(oc);
42185         throw CImgIOException(_cimglist_instance
42186                               "save_ffmpeg() : Failed to get conversion context for file '%s'.",
42187                               cimglist_instance,
42188                               filename);
42189       }
42190       int ret = 0, out_size;
42191       uint8_t *video_outbuf = 0;
42192       int video_outbuf_size = 1000000;
42193       video_outbuf = (uint8_t*)av_malloc(video_outbuf_size);
42194       if (!video_outbuf) {
42195         // if (!(fmt->flags & AVFMT_NOFILE)) url_fclose(&oc->pb);
42196         av_free(picture->data);
42197         av_free(picture);
42198         av_free(tmp_pict->data[0]);
42199         av_free(tmp_pict);
42200         avcodec_close(video_str->codec);
42201         av_free(oc);
42202         throw CImgIOException(_cimglist_instance
42203                               "save_ffmpeg() : Failed to allocate memory, for file '%s'.",
42204                               cimglist_instance,
42205                               filename);
42206       }
42207 
42208       // Loop through each desired image in list.
42209       for (unsigned int i = first_frame; i<=nlast_frame; ++i) {
42210         CImg<uint8_t> currentIm = _data[i], red, green, blue, gray;
42211         if (src_pxl_fmt==PIX_FMT_RGB24) {
42212           red = currentIm.get_shared_channel(0);
42213           green = currentIm.get_shared_channel(1);
42214           blue = currentIm.get_shared_channel(2);
42215           cimg_forXY(currentIm,X,Y) { // Assign pizel values to data buffer in interlaced RGBRGB ... format.
42216             tmp_pict->data[0][Y*tmp_pict->linesize[0] + 3*X] = red(X,Y);
42217             tmp_pict->data[0][Y*tmp_pict->linesize[0] + 3*X + 1] = green(X,Y);
42218             tmp_pict->data[0][Y*tmp_pict->linesize[0] + 3*X + 2] = blue(X,Y);
42219           }
42220         } else {
42221           gray = currentIm.get_shared_channel(0);
42222           cimg_forXY(currentIm,X,Y) tmp_pict->data[0][Y*tmp_pict->linesize[0] + X] = gray(X,Y);
42223         }
42224 
42225         if (video_str) video_pts = (video_str->pts.val * video_str->time_base.num)/(video_str->time_base.den);
42226         else video_pts = 0.0;
42227         if (!video_str) break;
42228         if (sws_scale(img_convert_context,tmp_pict->data,tmp_pict->linesize,0,c->height,picture->data,picture->linesize)<0) break;
42229         out_size = avcodec_encode_video(c,video_outbuf,video_outbuf_size,picture);
42230         if (out_size>0) {
42231           AVPacket pkt;
42232           av_init_packet(&pkt);
42233           pkt.pts = av_rescale_q(c->coded_frame->pts,c->time_base,video_str->time_base);
42234           if (c->coded_frame->key_frame) pkt.flags|=PKT_FLAG_KEY;
42235           pkt.stream_index = video_str->index;
42236           pkt.data = video_outbuf;
42237           pkt.size = out_size;
42238           ret = av_write_frame(oc,&pkt);
42239         } else if (out_size<0) break;
42240         if (ret) break; // Error occured in writing frame.
42241       }
42242 
42243       // Close codec.
42244       if (video_str) {
42245         avcodec_close(video_str->codec);
42246         av_free(picture->data[0]);
42247         av_free(picture);
42248         av_free(tmp_pict->data[0]);
42249         av_free(tmp_pict);
42250       }
42251       if (av_write_trailer(oc)<0)
42252         throw CImgIOException(_cimglist_instance
42253                               "save_ffmpeg() : Failed to write trailer for file '%s'.",
42254                               cimglist_instance,
42255                               filename);
42256 
42257       av_freep(&oc->streams[stream_index]->codec);
42258       av_freep(&oc->streams[stream_index]);
42259       if (!(fmt->flags&AVFMT_NOFILE)) {
42260         /*if (url_fclose(oc->pb)<0)
42261           throw CImgIOException(_cimglist_instance
42262                                 "save_ffmpeg() : File '%s', failed to close file.",
42263                                 cimglist_instance,
42264                                 filename);
42265         */
42266       }
42267       av_free(oc);
42268       av_free(video_outbuf);
42269 #endif
42270       return *this;
42271     }
42272 
42273     // Save an image sequence into a YUV file (internal).
42274     const CImgList<T>& _save_yuv(std::FILE *const file, const char *const filename, const bool rgb2yuv) const {
42275       if (!file && !filename)
42276         throw CImgArgumentException(_cimglist_instance
42277                                     "save_yuv() : Specified filename is (null).",
42278                                     cimglist_instance);
42279       if (is_empty())
42280         throw CImgInstanceException(_cimglist_instance
42281                                     "save_yuv() : Empty instance, for file '%s'.",
42282                                     cimglist_instance,
42283                                     filename?filename:"(FILE*)");
42284 
42285       if ((*this)[0].width()%2 || (*this)[0].height()%2)
42286         throw CImgInstanceException(_cimglist_instance
42287                                     "save_yuv() : Invalid odd instance dimensions (%u,%u) for file '%s'.",
42288                                     cimglist_instance,
42289                                     (*this)[0].width(),(*this)[0].height(),
42290                                     filename?filename:"(FILE*)");
42291 
42292       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
42293       cimglist_for(*this,l) {
42294         CImg<ucharT> YCbCr((*this)[l]);
42295         if (rgb2yuv) YCbCr.RGBtoYCbCr();
42296         cimg::fwrite(YCbCr._data,YCbCr._width*YCbCr._height,nfile);
42297         cimg::fwrite(YCbCr.get_resize(YCbCr._width/2, YCbCr._height/2,1,3,3).data(0,0,0,1),
42298                      YCbCr._width*YCbCr._height/2,nfile);
42299       }
42300       if (!file) cimg::fclose(nfile);
42301       return *this;
42302     }
42303 
42304     //! Save an image sequence into a YUV file.
42305     const CImgList<T>& save_yuv(const char *const filename=0, const bool rgb2yuv=true) const {
42306       return _save_yuv(0,filename,rgb2yuv);
42307     }
42308 
42309     //! Save an image sequence into a YUV file.
42310     const CImgList<T>& save_yuv(std::FILE *const file, const bool rgb2yuv=true) const {
42311       return _save_yuv(file,0,rgb2yuv);
42312     }
42313 
42314     //! Save an image list into a .cimg file.
42315     /**
42316        A CImg RAW file is a simple uncompressed binary file that may be used to save list of CImg<T> images.
42317        \param filename : name of the output file.
42318        \return A reference to the current CImgList instance is returned.
42319     **/
42320     const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename, const bool compression) const {
42321       if (!file && !filename)
42322         throw CImgArgumentException(_cimglist_instance
42323                                     "save_cimg() : Specified filename is (null).",
42324                                     cimglist_instance);
42325 #ifndef cimg_use_zlib
42326       if (compression)
42327         cimg::warn(_cimglist_instance
42328                    "save_cimg() : Unable to save compressed data in file '%s' unless zlib is enabled, saving them uncompressed.",
42329                    cimglist_instance,
42330                    filename?filename:"(FILE*)");
42331 #endif
42332       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
42333       const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little";
42334       if (std::strstr(ptype,"unsigned")==ptype) std::fprintf(nfile,"%u unsigned_%s %s_endian\n",_width,ptype+9,etype);
42335       else std::fprintf(nfile,"%u %s %s_endian\n",_width,ptype,etype);
42336       cimglist_for(*this,l) {
42337         const CImg<T>& img = _data[l];
42338         std::fprintf(nfile,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum);
42339         if (img._data) {
42340           CImg<T> tmp;
42341           if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); }
42342           const CImg<T>& ref = cimg::endianness()?tmp:img;
42343           bool compressed = false;
42344           if (compression) {
42345 #ifdef cimg_use_zlib
42346             const unsigned long siz = sizeof(T)*ref.size();
42347             unsigned long csiz = siz + siz/100 + 16;
42348             Bytef *const cbuf = new Bytef[csiz];
42349             if (compress(cbuf,&csiz,(Bytef*)ref._data,siz)) {
42350               cimg::warn(_cimglist_instance
42351                          "save_cimg() : Failed to save compressed data for file '%s', saving them uncompressed.",
42352                          cimglist_instance,
42353                          filename?filename:"(FILE*)");
42354 
42355               compressed = false;
42356             } else {
42357               std::fprintf(nfile," #%lu\n",csiz);
42358               cimg::fwrite(cbuf,csiz,nfile);
42359               delete[] cbuf;
42360               compressed = true;
42361             }
42362 #else
42363             compressed = false;
42364 #endif
42365           }
42366           if (!compressed) {
42367             std::fputc('\n',nfile);
42368             cimg::fwrite(ref._data,ref.size(),nfile);
42369           }
42370         } else std::fputc('\n',nfile);
42371       }
42372       if (!file) cimg::fclose(nfile);
42373       return *this;
42374     }
42375 
42376     //! Save an image list into a CImg file (RAW binary file + simple header)
42377     const CImgList<T>& save_cimg(std::FILE *file, const bool compress=false) const {
42378       return _save_cimg(file,0,compress);
42379     }
42380 
42381     //! Save an image list into a CImg file (RAW binary file + simple header)
42382     const CImgList<T>& save_cimg(const char *const filename, const bool compress=false) const {
42383       return _save_cimg(0,filename,compress);
42384     }
42385 
42386     // Insert the image instance into into an existing .cimg file, at specified coordinates.
42387     const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename,
42388                                  const unsigned int n0,
42389                                  const unsigned int x0, const unsigned int y0,
42390                                  const unsigned int z0, const unsigned int c0) const {
42391 #define _cimg_save_cimg_case(Ts,Tss) \
42392       if (!saved && !cimg::strcasecmp(Ts,str_pixeltype)) { \
42393         for (unsigned int l = 0; l<lmax; ++l) { \
42394           j = 0; while((i=std::fgetc(nfile))!='\n') tmp[j++]=(char)i; tmp[j] = 0; \
42395           W = H = D = C = 0; \
42396           if (std::sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
42397             throw CImgIOException(_cimglist_instance \
42398                                   "save_cimg() : Invalid size (%u,%u,%u,%u) of image[%u], for file '%s'.", \
42399                                   cimglist_instance, \
42400                                   W,H,D,C,l,filename?filename:"(FILE*)"); \
42401           if (W*H*D*C>0) { \
42402             if (l<n0 || x0>=W || y0>=H || z0>=D || c0>=D) std::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
42403             else { \
42404               const CImg<T>& img = (*this)[l - n0]; \
42405               const T *ptrs = img._data; \
42406               const unsigned int \
42407                 x1 = x0 + img._width - 1, \
42408                 y1 = y0 + img._height - 1, \
42409                 z1 = z0 + img._depth - 1, \
42410                 c1 = c0 + img._spectrum - 1, \
42411                 nx1 = x1>=W?W-1:x1, \
42412                 ny1 = y1>=H?H-1:y1, \
42413                 nz1 = z1>=D?D-1:z1, \
42414                 nc1 = c1>=C?C-1:c1; \
42415               CImg<Tss> raw(1+nx1-x0); \
42416               const unsigned int skipvb = c0*W*H*D*sizeof(Tss); \
42417               if (skipvb) std::fseek(nfile,skipvb,SEEK_CUR); \
42418               for (unsigned int v = 1 + nc1 - c0; v; --v) { \
42419                 const unsigned int skipzb = z0*W*H*sizeof(Tss); \
42420                 if (skipzb) std::fseek(nfile,skipzb,SEEK_CUR); \
42421                 for (unsigned int z = 1 + nz1 - z0; z; --z) { \
42422                   const unsigned int skipyb = y0*W*sizeof(Tss); \
42423                   if (skipyb) std::fseek(nfile,skipyb,SEEK_CUR); \
42424                   for (unsigned int y = 1 + ny1 - y0; y; --y) { \
42425                     const unsigned int skipxb = x0*sizeof(Tss); \
42426                     if (skipxb) std::fseek(nfile,skipxb,SEEK_CUR); \
42427                     raw.assign(ptrs, raw._width); \
42428                     ptrs+=img._width; \
42429                     if (endian) cimg::invert_endianness(raw._data,raw._width); \
42430                     cimg::fwrite(raw._data,raw._width,nfile); \
42431                     const unsigned int skipxe = (W - 1 - nx1)*sizeof(Tss); \
42432                     if (skipxe) std::fseek(nfile,skipxe,SEEK_CUR); \
42433                   } \
42434                   const unsigned int skipye = (H - 1 - ny1)*W*sizeof(Tss); \
42435                   if (skipye) std::fseek(nfile,skipye,SEEK_CUR); \
42436                 } \
42437                 const unsigned int skipze = (D - 1 - nz1)*W*H*sizeof(Tss); \
42438                 if (skipze) std::fseek(nfile,skipze,SEEK_CUR); \
42439               } \
42440               const unsigned int skipve = (C - 1 - nc1)*W*H*D*sizeof(Tss); \
42441               if (skipve) std::fseek(nfile,skipve,SEEK_CUR); \
42442             } \
42443           } \
42444         } \
42445         saved = true; \
42446       }
42447 
42448       if (!file && !filename)
42449         throw CImgArgumentException(_cimglist_instance
42450                                     "save_cimg() : Specified filename is (null).",
42451                                     cimglist_instance);
42452       if (is_empty())
42453         throw CImgInstanceException(_cimglist_instance
42454                                     "save_cimg() : Empty instance, for file '%s'.",
42455                                     cimglist_instance,
42456                                     filename?filename:"(FILE*)");
42457 
42458       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb+");
42459       bool saved = false, endian = cimg::endianness();
42460       char tmp[256] = { 0 }, str_pixeltype[256] = { 0 }, str_endian[256] = { 0 };
42461       unsigned int j, err, N, W, H, D, C;
42462       int i;
42463       j = 0; while((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
42464       err = std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype,str_endian);
42465       if (err<2) {
42466         if (!file) cimg::fclose(nfile);
42467         throw CImgIOException(_cimglist_instance
42468                               "save_cimg() : CImg header not found in file '%s'.",
42469                               cimglist_instance,
42470                               filename?filename:"(FILE*)");
42471       }
42472       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
42473       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
42474       const unsigned int lmax = cimg::min(N,n0+_width);
42475       _cimg_save_cimg_case("bool",bool);
42476       _cimg_save_cimg_case("unsigned_char",unsigned char);
42477       _cimg_save_cimg_case("uchar",unsigned char);
42478       _cimg_save_cimg_case("char",char);
42479       _cimg_save_cimg_case("unsigned_short",unsigned short);
42480       _cimg_save_cimg_case("ushort",unsigned short);
42481       _cimg_save_cimg_case("short",short);
42482       _cimg_save_cimg_case("unsigned_int",unsigned int);
42483       _cimg_save_cimg_case("uint",unsigned int);
42484       _cimg_save_cimg_case("int",int);
42485       _cimg_save_cimg_case("unsigned_long",unsigned long);
42486       _cimg_save_cimg_case("ulong",unsigned long);
42487       _cimg_save_cimg_case("long",long);
42488       _cimg_save_cimg_case("float",float);
42489       _cimg_save_cimg_case("double",double);
42490       if (!saved) {
42491         if (!file) cimg::fclose(nfile);
42492         throw CImgIOException(_cimglist_instance
42493                               "save_cimg() : Unsupported data type '%s' for file '%s'.",
42494                               cimglist_instance,
42495                               filename?filename:"(FILE*)",str_pixeltype);
42496       }
42497       if (!file) cimg::fclose(nfile);
42498       return *this;
42499     }
42500 
42501     //! Insert the image instance into into an existing .cimg file, at specified coordinates.
42502     const CImgList<T>& save_cimg(const char *const filename,
42503                                  const unsigned int n0,
42504                                  const unsigned int x0, const unsigned int y0,
42505                                  const unsigned int z0, const unsigned int c0) const {
42506       return _save_cimg(0,filename,n0,x0,y0,z0,c0);
42507     }
42508 
42509     //! Insert the image instance into into an existing .cimg file, at specified coordinates.
42510     const CImgList<T>& save_cimg(std::FILE *const file,
42511                                  const unsigned int n0,
42512                                  const unsigned int x0, const unsigned int y0,
42513                                  const unsigned int z0, const unsigned int c0) const {
42514       return _save_cimg(file,0,n0,x0,y0,z0,c0);
42515     }
42516 
42517     // Create an empty .cimg file with specified dimensions (internal)
42518     static void _save_empty_cimg(std::FILE *const file, const char *const filename,
42519                                 const unsigned int nb,
42520                                 const unsigned int dx, const unsigned int dy,
42521                                 const unsigned int dz, const unsigned int dc) {
42522       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
42523       const unsigned int siz = dx*dy*dz*dc*sizeof(T);
42524       std::fprintf(nfile,"%u %s\n",nb,pixel_type());
42525       for (unsigned int i=nb; i; --i) {
42526         std::fprintf(nfile,"%u %u %u %u\n",dx,dy,dz,dc);
42527         for (unsigned int off=siz; off; --off) std::fputc(0,nfile);
42528       }
42529       if (!file) cimg::fclose(nfile);
42530     }
42531 
42532     //! Create an empty .cimg file with specified dimensions.
42533     static void save_empty_cimg(const char *const filename,
42534                                 const unsigned int nb,
42535                                 const unsigned int dx, const unsigned int dy=1,
42536                                 const unsigned int dz=1, const unsigned int dc=1) {
42537       return _save_empty_cimg(0,filename,nb,dx,dy,dz,dc);
42538     }
42539 
42540     //! Create an empty .cimg file with specified dimensions.
42541     static void save_empty_cimg(std::FILE *const file,
42542                                 const unsigned int nb,
42543                                 const unsigned int dx, const unsigned int dy=1,
42544                                 const unsigned int dz=1, const unsigned int dc=1) {
42545       return _save_empty_cimg(file,0,nb,dx,dy,dz,dc);
42546     }
42547 
42548     //! Save a file in TIFF format.
42549     const CImgList<T>& save_tiff(const char *const filename, const unsigned int compression=0) const {
42550       if (!filename)
42551         throw CImgArgumentException(_cimglist_instance
42552                                     "save_tiff() : Specified filename is (null).",
42553                                     cimglist_instance);
42554       if (is_empty())
42555         throw CImgInstanceException(_cimglist_instance
42556                                     "save_tiff() : Empty instance, for file '%s'.",
42557                                     cimglist_instance,
42558                                     filename);
42559 #ifndef cimg_use_tiff
42560       if (_width==1) _data[0].save_tiff(filename,compression);
42561       else cimglist_for(*this,l) {
42562           char nfilename[1024] = { 0 };
42563           cimg::number_filename(filename,l,6,nfilename);
42564           _data[l].save_tiff(nfilename,compression);
42565         }
42566 #else
42567       TIFF *tif = TIFFOpen(filename,"w");
42568       if (tif) {
42569         for (unsigned int dir = 0, l = 0; l<_width; ++l) {
42570           const CImg<T>& img = (*this)[l];
42571           if (img) {
42572             if (img._depth==1) img._save_tiff(tif,dir++,compression);
42573             else cimg_forZ(img,z) img.get_slice(z)._save_tiff(tif,dir++,compression);
42574           }
42575         }
42576         TIFFClose(tif);
42577       } else
42578         throw CImgException(_cimglist_instance
42579                             "save_tiff() : Failed to open stream for file '%s'.",
42580                             cimglist_instance,
42581                             filename);
42582 #endif
42583       return *this;
42584     }
42585 
42586 
42587     //! Save an image list as a gzipped file, using external tool 'gzip'.
42588     const CImgList<T>& save_gzip_external(const char *const filename) const {
42589       if (!filename)
42590         throw CImgIOException(_cimglist_instance
42591                               "save_gzip_external() : Specified filename is (null).",
42592                               cimglist_instance);
42593 
42594       char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
42595       const char
42596         *ext = cimg::split_filename(filename,body),
42597         *ext2 = cimg::split_filename(body,0);
42598       std::FILE *file;
42599       do {
42600         if (!cimg::strcasecmp(ext,"gz")) {
42601           if (*ext2) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
42602           else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.cimg",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
42603         } else {
42604           if (*ext) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
42605           else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.cimg",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
42606         }
42607         if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
42608       } while (file);
42609 
42610       if (is_saveable(body)) {
42611         save(filetmp);
42612         cimg_snprintf(command,sizeof(command),"%s -c %s > \"%s\"",cimg::gzip_path(),filetmp,filename);
42613         cimg::system(command);
42614         file = std::fopen(filename,"rb");
42615         if (!file)
42616           throw CImgIOException(_cimglist_instance
42617                                 "save_gzip_external() : Failed to save file '%s' with external command 'gzip'.",
42618                                 cimglist_instance,
42619                                 filename);
42620         else cimg::fclose(file);
42621         std::remove(filetmp);
42622       } else {
42623         char nfilename[1024] = { 0 };
42624         cimglist_for(*this,l) {
42625           cimg::number_filename(body,l,6,nfilename);
42626           if (*ext) std::sprintf(nfilename + std::strlen(nfilename),".%s",ext);
42627           _data[l].save_gzip_external(nfilename);
42628         }
42629       }
42630       return *this;
42631     }
42632 
42633     //! Save an image sequence using the external tool 'ffmpeg'.
42634     const CImgList<T>& save_ffmpeg_external(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
42635                                             const char *const codec="mpeg2video", const unsigned int fps=25, const unsigned int bitrate=2048) const {
42636       if (!filename)
42637         throw CImgArgumentException(_cimglist_instance
42638                                     "save_ffmpeg_external() : Specified filename is (null).",
42639                                     cimglist_instance);
42640       if (is_empty())
42641         throw CImgInstanceException(_cimglist_instance
42642                                     "save_ffmpeg_external() : Empty instance, for file '%s'.",
42643                                     cimglist_instance,
42644                                     filename);
42645 
42646       char command[1024] = { 0 }, filetmp[512] = { 0 }, filetmp2[512] = { 0 };
42647       std::FILE *file = 0;
42648       const unsigned int nlast_frame = last_frame==~0U?_width-1:last_frame;
42649       if (first_frame>=_width || nlast_frame>=_width)
42650         throw CImgArgumentException(_cimglist_instance
42651                                     "save_ffmpeg_external() : Out of range specified frames [%u,%u] for file '%s'.",
42652                                     cimglist_instance,
42653                                     filename,first_frame,last_frame);
42654 
42655       for (unsigned int ll = first_frame; ll<=nlast_frame; ++ll) if (!_data[ll].is_sameXYZ(_data[0]))
42656         throw CImgInstanceException(_cimglist_instance
42657                                     "save_ffmpeg_external() : Invalid instance dimensions for file '%s'.",
42658                                     cimglist_instance,
42659                                     filename);
42660       do {
42661         cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
42662         cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_000001.ppm",filetmp);
42663         if ((file=std::fopen(filetmp2,"rb"))!=0) cimg::fclose(file);
42664       } while (file);
42665       for (unsigned int l = first_frame; l<=nlast_frame; ++l) {
42666         cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_%.6u.ppm",filetmp,l+1);
42667         if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save_pnm(filetmp2);
42668         else _data[l].save_pnm(filetmp2);
42669       }
42670 #if cimg_OS!=2
42671       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);
42672 #else
42673       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);
42674 #endif
42675       cimg::system(command);
42676       file = std::fopen(filename,"rb");
42677       if (!file)
42678         throw CImgIOException(_cimglist_instance
42679                               "save_ffmpeg_external() : Failed to save file '%s' with external command 'ffmpeg'.",
42680                               cimglist_instance,
42681                               filename);
42682       else cimg::fclose(file);
42683       cimglist_for(*this,lll) { cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_%.6u.ppm",filetmp,lll+1); std::remove(filetmp2); }
42684       return *this;
42685     }
42686 
42687     //@}
42688     //----------------------------------
42689     //
42690     //! \name Others
42691     //@{
42692     //----------------------------------
42693 
42694     //! Create an auto-cropped font (along the X axis) from a input font \p font.
42695     CImgList<T>& crop_font() {
42696       return get_crop_font().move_to(*this);
42697     }
42698 
42699     CImgList<T> get_crop_font() const {
42700       CImgList<T> res;
42701       cimglist_for(*this,l) {
42702         const CImg<T>& letter = (*this)[l];
42703         int xmin = letter._width, xmax = 0;
42704         cimg_forXY(letter,x,y) if (letter(x,y)) { if (x<xmin) xmin = x; if (x>xmax) xmax = x; }
42705         if (xmin>xmax) CImg<T>(letter._width,letter._height,1,letter._spectrum,0).move_to(res);
42706         else letter.get_crop(xmin,0,xmax,letter._height-1).move_to(res);
42707       }
42708       res[' '].resize(res['f']._width,-100,-100,-100,0);
42709       if (' '+256<res.size()) res[' '+256].resize(res['f']._width,-100,-100,-100,0);
42710       return res;
42711     }
42712 
42713 
42714     //! Return a CImg pre-defined font with desired size.
42715     /**
42716        \param font_height = height of the desired font (exact match for 11,13,17,19,24,32,38,57)
42717        \param fixed_size = tell if the font has a fixed or variable width.
42718     **/
42719     static const CImgList<T>& font(const unsigned int font_height, const bool variable_size=true) {
42720 
42721 #define _cimg_font(sx,sy) \
42722       if (!variable_size && (!font || font[0]._height!=sy)) font = _font(cimg::font##sx##x##sy,sx,sy,false); \
42723       if (variable_size  && (!vfont || vfont[0]._height!=sy)) vfont = _font(cimg::font##sx##x##sy,sx,sy,true); \
42724       if (font_height==sy) return variable_size?vfont:font; \
42725       if (variable_size) { \
42726         if (cvfont && font_height==cvfont[0]._height) return cvfont; \
42727         cvfont = vfont; \
42728         cimglist_for(cvfont,l) \
42729           cvfont[l].resize(cimg::max(1U,cvfont[l]._width*font_height/cvfont[l]._height),font_height,-100,-100, \
42730                            cvfont[0]._height>font_height?2:5); \
42731         return cvfont; \
42732       } else { \
42733         if (cfont && font_height==cfont[0]._height) return cfont; \
42734         cfont = font; \
42735         cimglist_for(cfont,l) \
42736           cfont[l].resize(cimg::max(1U,cfont[l]._width*font_height/cfont[l]._height),font_height,-100,-100, \
42737                           cfont[0]._height>font_height?2:5); \
42738         return cfont; \
42739       } \
42740 
42741       static CImgList<T> font, vfont, cfont, cvfont;
42742       if (!font_height) return CImgList<T>::empty();
42743       if (font_height<=13) { _cimg_font(10,13); } // [1,13] -> ref 13
42744       if (font_height<=28) { _cimg_font(12,24); } // [14,28] -> ref 24
42745       if (font_height<=32) { _cimg_font(16,32); } // [29,32] -> ref 32
42746       _cimg_font(29,57);                          // [33,+inf] -> ref 57
42747     }
42748 
42749     static CImgList<T> _font(const unsigned int *const font, const unsigned int w, const unsigned int h, const bool variable_size) {
42750       CImgList<T> res(256,w,h,1,1);
42751       const unsigned int *ptr = font;
42752       unsigned int m = 0, val = 0;
42753       for (unsigned int y = 0; y<h; ++y)
42754         for (unsigned int x = 0; x<256*w; ++x) {
42755           m>>=1; if (!m) { m = 0x80000000; val = *(ptr++); }
42756           CImg<T>& img = res[x/w];
42757           unsigned int xm = x%w;
42758           img(xm,y) = (T)((val&m)?1:0);
42759         }
42760       if (variable_size) res.crop_font();
42761       return  res.insert(res);
42762     }
42763 
42764     //! Compute a 1-D Fast Fourier Transform, along specified axis.
42765     CImgList<T>& FFT(const char axis, const bool invert=false) {
42766       if (is_empty()) return *this;
42767       if (_width==1) insert(1);
42768       if (_width>2)
42769         cimg::warn(_cimglist_instance
42770                    "FFT() : Instance has more than 2 images",
42771                    cimglist_instance);
42772 
42773       CImg<T>::FFT(_data[0],_data[1],axis,invert);
42774       return *this;
42775     }
42776 
42777     CImgList<Tfloat> get_FFT(const char axis, const bool invert=false) const {
42778       return CImgList<Tfloat>(*this,false).FFT(axis,invert);
42779     }
42780 
42781     //! Compute a n-d Fast Fourier Transform.
42782     CImgList<T>& FFT(const bool invert=false) {
42783       if (is_empty()) return *this;
42784       if (_width==1) insert(1);
42785       if (_width>2)
42786         cimg::warn(_cimglist_instance
42787                    "FFT() : Instance has more than 2 images",
42788                    cimglist_instance);
42789 
42790       CImg<T>::FFT(_data[0],_data[1],invert);
42791       return *this;
42792     }
42793 
42794     CImgList<Tfloat> get_FFT(const bool invert=false) const {
42795       return CImgList<Tfloat>(*this,false).FFT(invert);
42796     }
42797 
42798     //! Invert primitives orientation of a 3d object.
42799     CImgList<T>& reverse_object3d() {
42800       cimglist_for(*this,l) {
42801         CImg<T>& p = _data[l];
42802         switch (p.size()) {
42803         case 2: case 3: cimg::swap(p[0],p[1]); break;
42804         case 6: cimg::swap(p[0],p[1],p[2],p[4],p[3],p[5]); break;
42805         case 9: cimg::swap(p[0],p[1],p[3],p[5],p[4],p[6]); break;
42806         case 4: cimg::swap(p[0],p[1],p[2],p[3]); break;
42807         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;
42808         }
42809       }
42810       return *this;
42811     }
42812 
42813     CImgList<T> get_reverse_object3d() const {
42814       return (+*this).reverse_object3d();
42815     }
42816 
42817     //@}
42818   };
42819 
42820   /*
42821   #---------------------------------------------
42822   #
42823    # Completion of previously declared functions
42824    #
42825    #----------------------------------------------
42826   */
42827 
42828 namespace cimg {
42829 
42830   //! Display a dialog box, where a user can click standard buttons.
42831   /**
42832      Up to 6 buttons can be defined in the dialog window.
42833      This function returns when a user clicked one of the button or closed the dialog window.
42834      \param title = Title of the dialog window.
42835      \param msg = Main message displayed inside the dialog window.
42836      \param button1_label = Label of the 1st button.
42837      \param button2_label = Label of the 2nd button.
42838      \param button3_label = Label of the 3rd button.
42839      \param button4_label = Label of the 4th button.
42840      \param button5_label = Label of the 5th button.
42841      \param button6_label = Label of the 6th button.
42842      \param logo = Logo image displayed at the left of the main message. This parameter is optional.
42843      \param centering = Tell to center the dialog window on the screen.
42844      \return The button number (from 0 to 5), or -1 if the dialog window has been closed by the user.
42845      \note If a button text is set to 0, then the corresponding button (and the followings) won't appear in
42846      the dialog box. At least one button is necessary.
42847   **/
42848   template<typename t>
42849   inline int dialog(const char *const title, const char *const msg,
42850                     const char *const button1_label, const char *const button2_label,
42851                     const char *const button3_label, const char *const button4_label,
42852                     const char *const button5_label, const char *const button6_label,
42853                     const CImg<t>& logo, const bool centering = false) {
42854 #if cimg_display==0
42855     cimg::unused(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label,logo._data,centering);
42856     throw CImgIOException("cimg::dialog() : No display available.");
42857 #else
42858     const unsigned char
42859       black[] = { 0,0,0 }, white[] = { 255,255,255 }, gray[] = { 200,200,200 }, gray2[] = { 150,150,150 };
42860 
42861     // Create buttons and canvas graphics
42862     CImgList<unsigned char> buttons, cbuttons, sbuttons;
42863     if (button1_label) { CImg<unsigned char>().draw_text(0,0,button1_label,black,gray,1,13).move_to(buttons);
42864       if (button2_label) { CImg<unsigned char>().draw_text(0,0,button2_label,black,gray,1,13).move_to(buttons);
42865         if (button3_label) { CImg<unsigned char>().draw_text(0,0,button3_label,black,gray,1,13).move_to(buttons);
42866           if (button4_label) { CImg<unsigned char>().draw_text(0,0,button4_label,black,gray,1,13).move_to(buttons);
42867             if (button5_label) { CImg<unsigned char>().draw_text(0,0,button5_label,black,gray,1,13).move_to(buttons);
42868               if (button6_label) { CImg<unsigned char>().draw_text(0,0,button6_label,black,gray,1,13).move_to(buttons);
42869               }}}}}}
42870     if (!buttons._width)
42871       throw CImgArgumentException("cimg::dialog() : No buttons have been defined.");
42872     cimglist_for(buttons,l) buttons[l].resize(-100,-100,1,3);
42873 
42874     unsigned int bw = 0, bh = 0;
42875     cimglist_for(buttons,l) { bw = cimg::max(bw,buttons[l]._width); bh = cimg::max(bh,buttons[l]._height); }
42876     bw+=8; bh+=8;
42877     if (bw<64) bw = 64;
42878     if (bw>128) bw = 128;
42879     if (bh<24) bh = 24;
42880     if (bh>48) bh = 48;
42881 
42882     CImg<unsigned char> button(bw,bh,1,3);
42883     button.draw_rectangle(0,0,bw-1,bh-1,gray);
42884     button.draw_line(0,0,bw-1,0,white).draw_line(0,bh-1,0,0,white);
42885     button.draw_line(bw-1,0,bw-1,bh-1,black).draw_line(bw-1,bh-1,0,bh-1,black);
42886     button.draw_line(1,bh-2,bw-2,bh-2,gray2).draw_line(bw-2,bh-2,bw-2,1,gray2);
42887     CImg<unsigned char> sbutton(bw,bh,1,3);
42888     sbutton.draw_rectangle(0,0,bw-1,bh-1,gray);
42889     sbutton.draw_line(0,0,bw-1,0,black).draw_line(bw-1,0,bw-1,bh-1,black);
42890     sbutton.draw_line(bw-1,bh-1,0,bh-1,black).draw_line(0,bh-1,0,0,black);
42891     sbutton.draw_line(1,1,bw-2,1,white).draw_line(1,bh-2,1,1,white);
42892     sbutton.draw_line(bw-2,1,bw-2,bh-2,black).draw_line(bw-2,bh-2,1,bh-2,black);
42893     sbutton.draw_line(2,bh-3,bw-3,bh-3,gray2).draw_line(bw-3,bh-3,bw-3,2,gray2);
42894     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);
42895     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);
42896     CImg<unsigned char> cbutton(bw,bh,1,3);
42897     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);
42898     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);
42899     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);
42900 
42901     cimglist_for(buttons,ll) {
42902       CImg<unsigned char>(cbutton).draw_image(1+(bw-buttons[ll].width())/2,1+(bh-buttons[ll].height())/2,buttons[ll]).
42903         move_to(cbuttons);
42904       CImg<unsigned char>(sbutton).draw_image((bw-buttons[ll].width())/2,(bh-buttons[ll].height())/2,buttons[ll]).
42905         move_to(sbuttons);
42906       CImg<unsigned char>(button).draw_image((bw-buttons[ll].width())/2,(bh-buttons[ll].height())/2,buttons[ll]).
42907         move_to(buttons[ll]);
42908     }
42909 
42910     CImg<unsigned char> canvas;
42911     if (msg) CImg<unsigned char>().draw_text(0,0,"%s",black,gray,1,13,msg).resize(-100,-100,1,3).move_to(canvas);
42912     const unsigned int
42913       bwall = (buttons._width-1)*(12+bw) + bw,
42914       w = cimg::max(196U,36+logo._width+canvas._width,24+bwall),
42915       h = cimg::max(96U,36+canvas._height+bh,36+logo._height+bh),
42916       lx = 12 + (canvas._data?0:((w-24-logo._width)/2)),
42917       ly = (h-12-bh-logo._height)/2,
42918       tx = lx+logo._width+12,
42919       ty = (h-12-bh-canvas._height)/2,
42920       bx = (w-bwall)/2,
42921       by = h-12-bh;
42922 
42923     if (canvas._data)
42924       canvas = CImg<unsigned char>(w,h,1,3).
42925         draw_rectangle(0,0,w-1,h-1,gray).
42926         draw_line(0,0,w-1,0,white).draw_line(0,h-1,0,0,white).
42927         draw_line(w-1,0,w-1,h-1,black).draw_line(w-1,h-1,0,h-1,black).
42928         draw_image(tx,ty,canvas);
42929     else
42930       canvas = CImg<unsigned char>(w,h,1,3).
42931         draw_rectangle(0,0,w-1,h-1,gray).
42932         draw_line(0,0,w-1,0,white).draw_line(0,h-1,0,0,white).
42933         draw_line(w-1,0,w-1,h-1,black).draw_line(w-1,h-1,0,h-1,black);
42934     if (logo._data) canvas.draw_image(lx,ly,logo);
42935 
42936     unsigned int xbuttons[6] = { 0 };
42937     cimglist_for(buttons,lll) { xbuttons[lll] = bx+(bw+12)*lll; canvas.draw_image(xbuttons[lll],by,buttons[lll]); }
42938 
42939     // Open window and enter events loop
42940     CImgDisplay disp(canvas,title?title:" ",0,false,centering?true:false);
42941     if (centering) disp.move((CImgDisplay::screen_width() - disp.width())/2,
42942                              (CImgDisplay::screen_height() - disp.height())/2);
42943     bool stopflag = false, refresh = false;
42944     int oselected = -1, oclicked = -1, selected = -1, clicked = -1;
42945     while (!disp.is_closed() && !stopflag) {
42946       if (refresh) {
42947         if (clicked>=0) CImg<unsigned char>(canvas).draw_image(xbuttons[clicked],by,cbuttons[clicked]).display(disp);
42948         else {
42949           if (selected>=0) CImg<unsigned char>(canvas).draw_image(xbuttons[selected],by,sbuttons[selected]).display(disp);
42950           else canvas.display(disp);
42951         }
42952         refresh = false;
42953       }
42954       disp.wait(15);
42955       if (disp.is_resized()) disp.resize(disp,false);
42956 
42957       if (disp.button()&1)  {
42958         oclicked = clicked;
42959         clicked = -1;
42960         cimglist_for(buttons,l)
42961           if (disp.mouse_y()>=(int)by && disp.mouse_y()<(int)(by+bh) &&
42962               disp.mouse_x()>=(int)xbuttons[l] && disp.mouse_x()<(int)(xbuttons[l]+bw)) {
42963             clicked = selected = l;
42964             refresh = true;
42965           }
42966         if (clicked!=oclicked) refresh = true;
42967       } else if (clicked>=0) stopflag = true;
42968 
42969       if (disp.key()) {
42970         oselected = selected;
42971         switch (disp.key()) {
42972         case cimg::keyESC : selected=-1; stopflag=true; break;
42973         case cimg::keyENTER : if (selected<0) selected = 0; stopflag = true; break;
42974         case cimg::keyTAB :
42975         case cimg::keyARROWRIGHT :
42976         case cimg::keyARROWDOWN : selected = (selected+1)%buttons._width; break;
42977         case cimg::keyARROWLEFT :
42978         case cimg::keyARROWUP : selected = (selected+buttons._width-1)%buttons._width; break;
42979         }
42980         disp.set_key();
42981         if (selected!=oselected) refresh = true;
42982       }
42983     }
42984     if (!disp) selected = -1;
42985     return selected;
42986 #endif
42987   }
42988 
42989   inline int dialog(const char *const title, const char *const msg,
42990                     const char *const button1_label, const char *const button2_label, const char *const button3_label,
42991                     const char *const button4_label, const char *const button5_label, const char *const button6_label,
42992                     const bool centering) {
42993     return dialog(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label,
42994                   CImg<unsigned char>::logo40x38(),centering);
42995   }
42996 
42997   //! Evaluate math expression.
42998   inline double eval(const char *const expression, const double x, const double y, const double z, const double c) {
42999     static const CImg<float> empty;
43000     return empty.eval(expression,x,y,z,c);
43001   }
43002 
43003   // End of cimg:: namespace
43004 }
43005 
43006   // End of cimg_library:: namespace
43007 }
43008 
43009 #ifdef _cimg_redefine_None
43010 #define None 0
43011 #endif
43012 #ifdef _cimg_redefine_min
43013 #define min(a,b) (((a)<(b))?(a):(b))
43014 #endif
43015 #ifdef _cimg_redefine_max
43016 #define max(a,b) (((a)>(b))?(a):(b))
43017 #endif
43018 #ifdef _cimg_redefine_PI
43019 #define PI 3.141592653589793238462643383
43020 #endif
43021 
43022 #endif
43023 // Local Variables:
43024 // mode: c++
43025 // End:
43026