Sim-LIT  2.0
 All Classes Namespaces Files Functions Variables Typedefs
CImg.h
1 /*
2  #
3  # File : CImg.h
4  # ( C++ header file )
5  #
6  # Description : The C++ Template Image Processing Toolkit.
7  # This file is the main component of the CImg Library project.
8  # ( http://cimg.sourceforge.net )
9  #
10  # Project manager : David Tschumperle.
11  # ( http://tschumperle.users.greyc.fr/ )
12  #
13  # A complete list of contributors is available in file 'README.txt'
14  # distributed within the CImg package.
15  #
16  # Licenses : This file is 'dual-licensed', you have to choose one
17  # of the two licenses below to apply.
18  #
19  # CeCILL-C
20  # The CeCILL-C license is close to the GNU LGPL.
21  # ( http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html )
22  #
23  # or CeCILL v2.0
24  # The CeCILL license is compatible with the GNU GPL.
25  # ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html )
26  #
27  # This software is governed either by the CeCILL or the CeCILL-C license
28  # under French law and abiding by the rules of distribution of free software.
29  # You can use, modify and or redistribute the software under the terms of
30  # the CeCILL or CeCILL-C licenses as circulated by CEA, CNRS and INRIA
31  # at the following URL: "http://www.cecill.info".
32  #
33  # As a counterpart to the access to the source code and rights to copy,
34  # modify and redistribute granted by the license, users are provided only
35  # with a limited warranty and the software's author, the holder of the
36  # economic rights, and the successive licensors have only limited
37  # liability.
38  #
39  # In this respect, the user's attention is drawn to the risks associated
40  # with loading, using, modifying and/or developing or reproducing the
41  # software by the user in light of its specific status of free software,
42  # that may mean that it is complicated to manipulate, and that also
43  # therefore means that it is reserved for developers and experienced
44  # professionals having in-depth computer knowledge. Users are therefore
45  # encouraged to load and test the software's suitability as regards their
46  # requirements in conditions enabling the security of their systems and/or
47  # data to be ensured and, more generally, to use and operate it in the
48  # same conditions as regards security.
49  #
50  # The fact that you are presently reading this means that you have had
51  # knowledge of the CeCILL and CeCILL-C licenses and that you accept its terms.
52  #
53 */
54 
55 // Set version number of the library.
56 #ifndef cimg_version
57 #define cimg_version 160
58 
59 /*-----------------------------------------------------------
60  #
61  # Test and possibly auto-set CImg configuration variables
62  # and include required headers.
63  #
64  # If you find that the default configuration variables are
65  # not adapted to your system, you can override their values
66  # before including the header file "CImg.h"
67  # (use the #define directive).
68  #
69  ------------------------------------------------------------*/
70 
71 // Include standard C++ headers.
72 // This is the minimal set of required headers to make CImg-based codes compile.
73 #include <cstdio>
74 #include <cstdlib>
75 #include <cstdarg>
76 #include <cstring>
77 #include <cmath>
78 #include <ctime>
79 #include <exception>
80 
81 // Detect/configure OS variables.
82 //
83 // Define 'cimg_OS' to: '0' for an unknown OS (will try to minize library dependencies).
84 // '1' for a Unix-like OS (Linux, Solaris, BSD, MacOSX, Irix, ...).
85 // '2' for Microsoft Windows.
86 // (auto-detection is performed if 'cimg_OS' is not set by the user).
87 #ifndef cimg_OS
88 #if defined(unix) || defined(__unix) || defined(__unix__) \
89  || defined(linux) || defined(__linux) || defined(__linux__) \
90  || defined(sun) || defined(__sun) \
91  || defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) \
92  || defined(__FreeBSD__) || defined (__DragonFly__) \
93  || defined(sgi) || defined(__sgi) \
94  || defined(__MACOSX__) || defined(__APPLE__) \
95  || defined(__CYGWIN__)
96 #define cimg_OS 1
97 #elif defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) \
98  || defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
99 #define cimg_OS 2
100 #else
101 #define cimg_OS 0
102 #endif
103 #elif !(cimg_OS==0 || cimg_OS==1 || cimg_OS==2)
104 #error CImg Library: Invalid configuration variable 'cimg_OS'.
105 #error (correct values are '0 = unknown OS', '1 = Unix-like OS', '2 = Microsoft Windows').
106 #endif
107 
108 // Disable silly warnings on some Microsoft VC++ compilers.
109 #ifdef _MSC_VER
110 #pragma warning(push)
111 #pragma warning(disable:4311)
112 #pragma warning(disable:4312)
113 #pragma warning(disable:4800)
114 #pragma warning(disable:4804)
115 #pragma warning(disable:4996)
116 #define _CRT_SECURE_NO_DEPRECATE 1
117 #define _CRT_NONSTDC_NO_DEPRECATE 1
118 #endif
119 
120 // Include OS-specific headers.
121 #if cimg_OS==1
122 #include <sys/types.h>
123 #include <sys/time.h>
124 #include <unistd.h>
125 #elif cimg_OS==2
126 #ifndef NOMINMAX
127 #define NOMINMAX
128 #endif
129 #include <windows.h>
130 #ifndef _WIN32_IE
131 #define _WIN32_IE 0x0400
132 #endif
133 #include <shlobj.h>
134 #include <process.h>
135 #include <io.h>
136 #define cimg_snprintf _snprintf
137 #define cimg_vsnprintf _vsnprintf
138 #endif
139 #ifndef cimg_snprintf
140 #include <stdio.h>
141 #define cimg_snprintf snprintf
142 #define cimg_vsnprintf vsnprintf
143 #endif
144 
145 // Look for C++11 features
146 #if !defined(cimg_use_cpp11) && __cplusplus>201100
147 #define cimg_use_cpp11
148 #endif
149 #ifdef cimg_use_cpp11
150 #include <initializer_list>
151 #include <utility>
152 #endif
153 
154 // Configure filename separator.
155 //
156 // Filename separator is set by default to '/', except for Windows where it is '\'.
157 #ifndef cimg_file_separator
158 #if cimg_OS==2
159 #define cimg_file_separator '\\'
160 #else
161 #define cimg_file_separator '/'
162 #endif
163 #endif
164 
165 // Configure verbosity of output messages.
166 //
167 // Define 'cimg_verbosity' to: '0' to hide library messages (quiet mode).
168 // '1' to output library messages on the console.
169 // '2' to output library messages on a basic dialog window (default behavior).
170 // '3' to do as '1' + add extra warnings (may slow down the code!).
171 // '4' to do as '2' + add extra warnings (may slow down the code!).
172 //
173 // Define 'cimg_strict_warnings' to replace warning messages by exception throwns.
174 //
175 // Define 'cimg_use_vt100' to allow output of color messages on VT100-compatible terminals.
176 #ifndef cimg_verbosity
177 #define cimg_verbosity 2
178 #elif !(cimg_verbosity==0 || cimg_verbosity==1 || cimg_verbosity==2 || cimg_verbosity==3 || cimg_verbosity==4)
179 #error CImg Library: Configuration variable 'cimg_verbosity' is badly defined.
180 #error (should be { 0=quiet | 1=console | 2=dialog | 3=console+warnings | 4=dialog+warnings }).
181 #endif
182 
183 // Configure display framework.
184 //
185 // Define 'cimg_display' to: '0' to disable display capabilities.
186 // '1' to use the X-Window framework (X11).
187 // '2' to use the Microsoft GDI32 framework.
188 #ifndef cimg_display
189 #if cimg_OS==0
190 #define cimg_display 0
191 #elif cimg_OS==1
192 #if defined(__MACOSX__) || defined(__APPLE__)
193 #define cimg_display 1
194 #else
195 #define cimg_display 1
196 #endif
197 #elif cimg_OS==2
198 #define cimg_display 2
199 #endif
200 #elif !(cimg_display==0 || cimg_display==1 || cimg_display==2)
201 #error CImg Library: Configuration variable 'cimg_display' is badly defined.
202 #error (should be { 0=none | 1=X-Window (X11) | 2=Microsoft GDI32 }).
203 #endif
204 
205 // Include display-specific headers.
206 #if cimg_display==1
207 #include <X11/Xlib.h>
208 #include <X11/Xutil.h>
209 #include <X11/keysym.h>
210 #include <pthread.h>
211 #ifdef cimg_use_xshm
212 #include <sys/ipc.h>
213 #include <sys/shm.h>
214 #include <X11/extensions/XShm.h>
215 #endif
216 #ifdef cimg_use_xrandr
217 #include <X11/extensions/Xrandr.h>
218 #endif
219 #endif
220 #ifndef cimg_appname
221 #define cimg_appname "CImg"
222 #endif
223 
224 // Configure OpenMP support.
225 // (http://www.openmp.org)
226 //
227 // Define 'cimg_use_openmp' to enable OpenMP support.
228 //
229 // OpenMP directives may be used in a (very) few CImg functions to get
230 // advantages of multi-core CPUs.
231 #ifdef cimg_use_openmp
232 #include <omp.h>
233 #endif
234 
235 // Configure OpenCV support.
236 // (http://opencv.willowgarage.com/wiki/)
237 //
238 // Define 'cimg_use_opencv' to enable OpenCV support.
239 //
240 // OpenCV library may be used to access images from cameras
241 // (see method 'CImg<T>::load_camera()').
242 #ifdef cimg_use_opencv
243 #ifdef True
244 #undef True
245 #define _cimg_redefine_True
246 #endif
247 #ifdef False
248 #undef False
249 #define _cimg_redefine_False
250 #endif
251 #include <cstddef>
252 #include "cv.h"
253 #include "highgui.h"
254 #endif
255 
256 // Configure LibPNG support.
257 // (http://www.libpng.org)
258 //
259 // Define 'cimg_use_png' to enable LibPNG support.
260 //
261 // PNG library may be used to get a native support of '.png' files.
262 // (see methods 'CImg<T>::{load,save}_png()'.
263 #ifdef cimg_use_png
264 extern "C" {
265 #include "png.h"
266 }
267 #endif
268 
269 // Configure LibJPEG support.
270 // (http://en.wikipedia.org/wiki/Libjpeg)
271 //
272 // Define 'cimg_use_jpeg' to enable LibJPEG support.
273 //
274 // JPEG library may be used to get a native support of '.jpg' files.
275 // (see methods 'CImg<T>::{load,save}_jpeg()').
276 #ifdef cimg_use_jpeg
277 extern "C" {
278 #include "jpeglib.h"
279 #include "setjmp.h"
280 }
281 #endif
282 
283 // Configure LibTIFF support.
284 // (http://www.libtiff.org)
285 //
286 // Define 'cimg_use_tiff' to enable LibTIFF support.
287 //
288 // TIFF library may be used to get a native support of '.tif' files.
289 // (see methods 'CImg[List]<T>::{load,save}_tiff()').
290 #ifdef cimg_use_tiff
291 extern "C" {
292 #define uint64 uint64_hack_
293 #define int64 int64_hack_
294 #include "tiffio.h"
295 #undef uint64
296 #undef int64
297 }
298 #endif
299 
300 // Configure LibMINC2 support.
301 // (http://en.wikibooks.org/wiki/MINC/Reference/MINC2.0_File_Format_Reference)
302 //
303 // Define 'cimg_use_minc2' to enable LibMINC2 support.
304 //
305 // MINC2 library may be used to get a native support of '.mnc' files.
306 // (see methods 'CImg<T>::{load,save}_minc2()').
307 #ifdef cimg_use_minc2
308 #include "minc_io_simple_volume.h"
309 #include "minc_1_simple.h"
310 #include "minc_1_simple_rw.h"
311 #endif
312 
313 // Configure FFMPEG support.
314 // (http://www.ffmpeg.org)
315 //
316 // Define 'cimg_use_ffmpeg' to enable FFMPEG lib support.
317 //
318 // Avcodec and Avformat libraries from FFMPEG may be used
319 // to get a native support of various video file formats.
320 // (see methods 'CImg[List]<T>::load_ffmpeg()').
321 #ifdef cimg_use_ffmpeg
322 #if (defined(_STDINT_H) || defined(_STDINT_H_)) && !defined(UINT64_C)
323 #warning "__STDC_CONSTANT_MACROS has to be defined before including <stdint.h>, this file will probably not compile."
324 #endif
325 #ifndef __STDC_CONSTANT_MACROS
326 #define __STDC_CONSTANT_MACROS // ...or stdint.h wont' define UINT64_C, needed by libavutil
327 #endif
328 extern "C" {
329 #include <libavformat/avformat.h>
330 #include <libavcodec/avcodec.h>
331 #include <libswscale/swscale.h>
332 }
333 #endif
334 
335 // Configure Zlib support.
336 // (http://www.zlib.net)
337 //
338 // Define 'cimg_use_zlib' to enable Zlib support.
339 //
340 // Zlib library may be used to allow compressed data in '.cimgz' files
341 // (see methods 'CImg[List]<T>::{load,save}_cimg()').
342 #ifdef cimg_use_zlib
343 extern "C" {
344 #include "zlib.h"
345 }
346 #endif
347 
348 // Configure Magick++ support.
349 // (http://www.imagemagick.org/Magick++)
350 //
351 // Define 'cimg_use_magick' to enable Magick++ support.
352 //
353 // Magick++ library may be used to get a native support of various image file formats.
354 // (see methods 'CImg<T>::{load,save}()').
355 #ifdef cimg_use_magick
356 #include "Magick++.h"
357 #endif
358 
359 // Configure FFTW3 support.
360 // (http://www.fftw.org)
361 //
362 // Define 'cimg_use_fftw3' to enable libFFTW3 support.
363 //
364 // FFTW3 library may be used to efficiently compute the Fast Fourier Transform
365 // of image data, without restriction on the image size.
366 // (see method 'CImg[List]<T>::FFT()').
367 #ifdef cimg_use_fftw3
368 extern "C" {
369 #include "fftw3.h"
370 }
371 #endif
372 
373 // Configure LibBoard support.
374 // (http://libboard.sourceforge.net/)
375 //
376 // Define 'cimg_use_board' to enable Board support.
377 //
378 // Board library may be used to draw 3d objects in vector-graphics canvas
379 // that can be saved as '.ps' or '.svg' files afterwards.
380 // (see method 'CImg<T>::draw_object3d()').
381 #ifdef cimg_use_board
382 #ifdef None
383 #undef None
384 #define _cimg_redefine_None
385 #endif
386 #include "Board.h"
387 #endif
388 
389 // Configure OpenEXR support.
390 // (http://www.openexr.com/)
391 //
392 // Define 'cimg_use_openexr' to enable OpenEXR support.
393 //
394 // OpenEXR library may be used to get a native support of '.exr' files.
395 // (see methods 'CImg<T>::{load,save}_exr()').
396 #ifdef cimg_use_openexr
397 #include "ImfRgbaFile.h"
398 #include "ImfInputFile.h"
399 #include "ImfChannelList.h"
400 #include "ImfMatrixAttribute.h"
401 #include "ImfArray.h"
402 #endif
403 
404 // Lapack configuration.
405 // (http://www.netlib.org/lapack)
406 //
407 // Define 'cimg_use_lapack' to enable LAPACK support.
408 //
409 // Lapack library may be used in several CImg methods to speed up
410 // matrix computations (eigenvalues, inverse, ...).
411 #ifdef cimg_use_lapack
412 extern "C" {
413  extern void sgetrf_(int*, int*, float*, int*, int*, int*);
414  extern void sgetri_(int*, float*, int*, int*, float*, int*, int*);
415  extern void sgetrs_(char*, int*, int*, float*, int*, int*, float*, int*, int*);
416  extern void sgesvd_(char*, char*, int*, int*, float*, int*, float*, float*, int*, float*, int*, float*, int*, int*);
417  extern void ssyev_(char*, char*, int*, float*, int*, float*, float*, int*, int*);
418  extern void dgetrf_(int*, int*, double*, int*, int*, int*);
419  extern void dgetri_(int*, double*, int*, int*, double*, int*, int*);
420  extern void dgetrs_(char*, int*, int*, double*, int*, int*, double*, int*, int*);
421  extern void dgesvd_(char*, char*, int*, int*, double*, int*, double*, double*,
422  int*, double*, int*, double*, int*, int*);
423  extern void dsyev_(char*, char*, int*, double*, int*, double*, double*, int*, int*);
424  extern void dgels_(char*, int*,int*,int*,double*,int*,double*,int*,double*,int*,int*);
425  extern void sgels_(char*, int*,int*,int*,float*,int*,float*,int*,float*,int*,int*);
426 }
427 #endif
428 
429 // Check if min/max/PI macros are defined.
430 //
431 // CImg does not compile if macros 'min', 'max' or 'PI' are defined,
432 // because it redefines functions min(), max() and const variable PI in the cimg:: namespace.
433 // so it '#undef' these macros if necessary, and restore them to reasonable
434 // values at the end of this file.
435 #ifdef min
436 #undef min
437 #define _cimg_redefine_min
438 #endif
439 #ifdef max
440 #undef max
441 #define _cimg_redefine_max
442 #endif
443 #ifdef PI
444 #undef PI
445 #define _cimg_redefine_PI
446 #endif
447 
448 // Define 'cimg_library' namespace suffix.
449 //
450 // You may want to add a suffix to the 'cimg_library' namespace, for instance if you need to work
451 // with several versions of the library at the same time.
452 #ifdef cimg_namespace_suffix
453 #define __cimg_library_suffixed(s) cimg_library_##s
454 #define _cimg_library_suffixed(s) __cimg_library_suffixed(s)
455 #define cimg_library_suffixed _cimg_library_suffixed(cimg_namespace_suffix)
456 #else
457 #define cimg_library_suffixed cimg_library
458 #endif
459 
460 /*------------------------------------------------------------------------------
461  #
462  # Define user-friendly macros.
463  #
464  # These CImg macros are prefixed by 'cimg_' and can be used safely in your own
465  # code. They are useful to parse command line options, or to write image loops.
466  #
467  ------------------------------------------------------------------------------*/
468 
469 // Macros to define program usage, and retrieve command line arguments.
470 #define cimg_usage(usage) cimg_library_suffixed::cimg::option((char*)0,argc,argv,(char*)0,usage,false)
471 #define cimg_help(str) cimg_library_suffixed::cimg::option((char*)0,argc,argv,str,(char*)0)
472 #define cimg_option(name,defaut,usage) cimg_library_suffixed::cimg::option(name,argc,argv,defaut,usage)
473 #define cimg_argument(pos) \
474  cimg_library_suffixed::cimg::argument(pos,argc,argv)
475 #define cimg_argument1(pos,s0) \
476  cimg_library_suffixed::cimg::argument(pos,argc,argv,1,s0)
477 #define cimg_argument2(pos,s0,s1) \
478  cimg_library_suffixed::cimg::argument(pos,argc,argv,2,s0,s1)
479 #define cimg_argument3(pos,s0,s1,s2) \
480  cimg_library_suffixed::cimg::argument(pos,argc,argv,3,s0,s1,s2)
481 #define cimg_argument4(pos,s0,s1,s2,s3) \
482  cimg_library_suffixed::cimg::argument(pos,argc,argv,4,s0,s1,s2,s3)
483 #define cimg_argument5(pos,s0,s1,s2,s3,s4) \
484  cimg_library_suffixed::cimg::argument(pos,argc,argv,5,s0,s1,s2,s3,s4)
485 #define cimg_argument6(pos,s0,s1,s2,s3,s4,s5) \
486  cimg_library_suffixed::cimg::argument(pos,argc,argv,6,s0,s1,s2,s3,s4,s5)
487 #define cimg_argument7(pos,s0,s1,s2,s3,s4,s5,s6) \
488  cimg_library_suffixed::cimg::argument(pos,argc,argv,7,s0,s1,s2,s3,s4,s5,s6)
489 #define cimg_argument8(pos,s0,s1,s2,s3,s4,s5,s6,s7) \
490  cimg_library_suffixed::cimg::argument(pos,argc,argv,8,s0,s1,s2,s3,s4,s5,s6,s7)
491 #define cimg_argument9(pos,s0,s1,s2,s3,s4,s5,s6,s7,s8) \
492  cimg_library_suffixed::cimg::argument(pos,argc,argv,9,s0,s1,s2,s3,s4,s5,s6,s7,s8)
493 
494 // Macros to define and manipulate local neighborhoods.
495 #define CImg_2x2(I,T) T I[4]; \
496  T& I##cc = I[0]; T& I##nc = I[1]; \
497  T& I##cn = I[2]; T& I##nn = I[3]; \
498  I##cc = I##nc = \
499  I##cn = I##nn = 0
500 
501 #define CImg_3x3(I,T) T I[9]; \
502  T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; \
503  T& I##pc = I[3]; T& I##cc = I[4]; T& I##nc = I[5]; \
504  T& I##pn = I[6]; T& I##cn = I[7]; T& I##nn = I[8]; \
505  I##pp = I##cp = I##np = \
506  I##pc = I##cc = I##nc = \
507  I##pn = I##cn = I##nn = 0
508 
509 #define CImg_4x4(I,T) T I[16]; \
510  T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; T& I##ap = I[3]; \
511  T& I##pc = I[4]; T& I##cc = I[5]; T& I##nc = I[6]; T& I##ac = I[7]; \
512  T& I##pn = I[8]; T& I##cn = I[9]; T& I##nn = I[10]; T& I##an = I[11]; \
513  T& I##pa = I[12]; T& I##ca = I[13]; T& I##na = I[14]; T& I##aa = I[15]; \
514  I##pp = I##cp = I##np = I##ap = \
515  I##pc = I##cc = I##nc = I##ac = \
516  I##pn = I##cn = I##nn = I##an = \
517  I##pa = I##ca = I##na = I##aa = 0
518 
519 #define CImg_5x5(I,T) T I[25]; \
520  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]; \
521  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]; \
522  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]; \
523  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]; \
524  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]; \
525  I##bb = I##pb = I##cb = I##nb = I##ab = \
526  I##bp = I##pp = I##cp = I##np = I##ap = \
527  I##bc = I##pc = I##cc = I##nc = I##ac = \
528  I##bn = I##pn = I##cn = I##nn = I##an = \
529  I##ba = I##pa = I##ca = I##na = I##aa = 0
530 
531 #define CImg_2x2x2(I,T) T I[8]; \
532  T& I##ccc = I[0]; T& I##ncc = I[1]; \
533  T& I##cnc = I[2]; T& I##nnc = I[3]; \
534  T& I##ccn = I[4]; T& I##ncn = I[5]; \
535  T& I##cnn = I[6]; T& I##nnn = I[7]; \
536  I##ccc = I##ncc = \
537  I##cnc = I##nnc = \
538  I##ccn = I##ncn = \
539  I##cnn = I##nnn = 0
540 
541 #define CImg_3x3x3(I,T) T I[27]; \
542  T& I##ppp = I[0]; T& I##cpp = I[1]; T& I##npp = I[2]; \
543  T& I##pcp = I[3]; T& I##ccp = I[4]; T& I##ncp = I[5]; \
544  T& I##pnp = I[6]; T& I##cnp = I[7]; T& I##nnp = I[8]; \
545  T& I##ppc = I[9]; T& I##cpc = I[10]; T& I##npc = I[11]; \
546  T& I##pcc = I[12]; T& I##ccc = I[13]; T& I##ncc = I[14]; \
547  T& I##pnc = I[15]; T& I##cnc = I[16]; T& I##nnc = I[17]; \
548  T& I##ppn = I[18]; T& I##cpn = I[19]; T& I##npn = I[20]; \
549  T& I##pcn = I[21]; T& I##ccn = I[22]; T& I##ncn = I[23]; \
550  T& I##pnn = I[24]; T& I##cnn = I[25]; T& I##nnn = I[26]; \
551  I##ppp = I##cpp = I##npp = \
552  I##pcp = I##ccp = I##ncp = \
553  I##pnp = I##cnp = I##nnp = \
554  I##ppc = I##cpc = I##npc = \
555  I##pcc = I##ccc = I##ncc = \
556  I##pnc = I##cnc = I##nnc = \
557  I##ppn = I##cpn = I##npn = \
558  I##pcn = I##ccn = I##ncn = \
559  I##pnn = I##cnn = I##nnn = 0
560 
561 #define cimg_get2x2(img,x,y,z,c,I,T) \
562  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), \
563  I[3] = (T)(img)(_n1##x,_n1##y,z,c)
564 
565 #define cimg_get3x3(img,x,y,z,c,I,T) \
566  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), \
567  I[3] = (T)(img)(_p1##x,y,z,c), I[4] = (T)(img)(x,y,z,c), I[5] = (T)(img)(_n1##x,y,z,c), \
568  I[6] = (T)(img)(_p1##x,_n1##y,z,c), I[7] = (T)(img)(x,_n1##y,z,c), I[8] = (T)(img)(_n1##x,_n1##y,z,c)
569 
570 #define cimg_get4x4(img,x,y,z,c,I,T) \
571  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), \
572  I[3] = (T)(img)(_n2##x,_p1##y,z,c), I[4] = (T)(img)(_p1##x,y,z,c), I[5] = (T)(img)(x,y,z,c), \
573  I[6] = (T)(img)(_n1##x,y,z,c), I[7] = (T)(img)(_n2##x,y,z,c), I[8] = (T)(img)(_p1##x,_n1##y,z,c), \
574  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), \
575  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), \
576  I[15] = (T)(img)(_n2##x,_n2##y,z,c)
577 
578 #define cimg_get5x5(img,x,y,z,c,I,T) \
579  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), \
580  I[3] = (T)(img)(_n1##x,_p2##y,z,c), I[4] = (T)(img)(_n2##x,_p2##y,z,c), I[5] = (T)(img)(_p2##x,_p1##y,z,c), \
581  I[6] = (T)(img)(_p1##x,_p1##y,z,c), I[7] = (T)(img)(x,_p1##y,z,c), I[8] = (T)(img)(_n1##x,_p1##y,z,c), \
582  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), \
583  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), \
584  I[15] = (T)(img)(_p2##x,_n1##y,z,c), I[16] = (T)(img)(_p1##x,_n1##y,z,c), I[17] = (T)(img)(x,_n1##y,z,c), \
585  I[18] = (T)(img)(_n1##x,_n1##y,z,c), I[19] = (T)(img)(_n2##x,_n1##y,z,c), I[20] = (T)(img)(_p2##x,_n2##y,z,c), \
586  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), \
587  I[24] = (T)(img)(_n2##x,_n2##y,z,c)
588 
589 #define cimg_get6x6(img,x,y,z,c,I,T) \
590  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), \
591  I[3] = (T)(img)(_n1##x,_p2##y,z,c), I[4] = (T)(img)(_n2##x,_p2##y,z,c), I[5] = (T)(img)(_n3##x,_p2##y,z,c), \
592  I[6] = (T)(img)(_p2##x,_p1##y,z,c), I[7] = (T)(img)(_p1##x,_p1##y,z,c), I[8] = (T)(img)(x,_p1##y,z,c), \
593  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), \
594  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), \
595  I[15] = (T)(img)(_n1##x,y,z,c), I[16] = (T)(img)(_n2##x,y,z,c), I[17] = (T)(img)(_n3##x,y,z,c), \
596  I[18] = (T)(img)(_p2##x,_n1##y,z,c), I[19] = (T)(img)(_p1##x,_n1##y,z,c), I[20] = (T)(img)(x,_n1##y,z,c), \
597  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), \
598  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), \
599  I[27] = (T)(img)(_n1##x,_n2##y,z,c), I[28] = (T)(img)(_n2##x,_n2##y,z,c), I[29] = (T)(img)(_n3##x,_n2##y,z,c), \
600  I[30] = (T)(img)(_p2##x,_n3##y,z,c), I[31] = (T)(img)(_p1##x,_n3##y,z,c), I[32] = (T)(img)(x,_n3##y,z,c), \
601  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)
602 
603 #define cimg_get7x7(img,x,y,z,c,I,T) \
604  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), \
605  I[3] = (T)(img)(x,_p3##y,z,c), I[4] = (T)(img)(_n1##x,_p3##y,z,c), I[5] = (T)(img)(_n2##x,_p3##y,z,c), \
606  I[6] = (T)(img)(_n3##x,_p3##y,z,c), I[7] = (T)(img)(_p3##x,_p2##y,z,c), I[8] = (T)(img)(_p2##x,_p2##y,z,c), \
607  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), \
608  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), \
609  I[15] = (T)(img)(_p2##x,_p1##y,z,c), I[16] = (T)(img)(_p1##x,_p1##y,z,c), I[17] = (T)(img)(x,_p1##y,z,c), \
610  I[18] = (T)(img)(_n1##x,_p1##y,z,c), I[19] = (T)(img)(_n2##x,_p1##y,z,c), I[20] = (T)(img)(_n3##x,_p1##y,z,c), \
611  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), \
612  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), \
613  I[27] = (T)(img)(_n3##x,y,z,c), I[28] = (T)(img)(_p3##x,_n1##y,z,c), I[29] = (T)(img)(_p2##x,_n1##y,z,c), \
614  I[30] = (T)(img)(_p1##x,_n1##y,z,c), I[31] = (T)(img)(x,_n1##y,z,c), I[32] = (T)(img)(_n1##x,_n1##y,z,c), \
615  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), \
616  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), \
617  I[39] = (T)(img)(_n1##x,_n2##y,z,c), I[40] = (T)(img)(_n2##x,_n2##y,z,c), I[41] = (T)(img)(_n3##x,_n2##y,z,c), \
618  I[42] = (T)(img)(_p3##x,_n3##y,z,c), I[43] = (T)(img)(_p2##x,_n3##y,z,c), I[44] = (T)(img)(_p1##x,_n3##y,z,c), \
619  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), \
620  I[48] = (T)(img)(_n3##x,_n3##y,z,c)
621 
622 #define cimg_get8x8(img,x,y,z,c,I,T) \
623  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), \
624  I[3] = (T)(img)(x,_p3##y,z,c), I[4] = (T)(img)(_n1##x,_p3##y,z,c), I[5] = (T)(img)(_n2##x,_p3##y,z,c), \
625  I[6] = (T)(img)(_n3##x,_p3##y,z,c), I[7] = (T)(img)(_n4##x,_p3##y,z,c), I[8] = (T)(img)(_p3##x,_p2##y,z,c), \
626  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), \
627  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), \
628  I[15] = (T)(img)(_n4##x,_p2##y,z,c), I[16] = (T)(img)(_p3##x,_p1##y,z,c), I[17] = (T)(img)(_p2##x,_p1##y,z,c), \
629  I[18] = (T)(img)(_p1##x,_p1##y,z,c), I[19] = (T)(img)(x,_p1##y,z,c), I[20] = (T)(img)(_n1##x,_p1##y,z,c), \
630  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), \
631  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), \
632  I[27] = (T)(img)(x,y,z,c), I[28] = (T)(img)(_n1##x,y,z,c), I[29] = (T)(img)(_n2##x,y,z,c), \
633  I[30] = (T)(img)(_n3##x,y,z,c), I[31] = (T)(img)(_n4##x,y,z,c), I[32] = (T)(img)(_p3##x,_n1##y,z,c), \
634  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), \
635  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), \
636  I[39] = (T)(img)(_n4##x,_n1##y,z,c), I[40] = (T)(img)(_p3##x,_n2##y,z,c), I[41] = (T)(img)(_p2##x,_n2##y,z,c), \
637  I[42] = (T)(img)(_p1##x,_n2##y,z,c), I[43] = (T)(img)(x,_n2##y,z,c), I[44] = (T)(img)(_n1##x,_n2##y,z,c), \
638  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), \
639  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), \
640  I[51] = (T)(img)(x,_n3##y,z,c), I[52] = (T)(img)(_n1##x,_n3##y,z,c), I[53] = (T)(img)(_n2##x,_n3##y,z,c), \
641  I[54] = (T)(img)(_n3##x,_n3##y,z,c), I[55] = (T)(img)(_n4##x,_n3##y,z,c), I[56] = (T)(img)(_p3##x,_n4##y,z,c), \
642  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), \
643  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), \
644  I[63] = (T)(img)(_n4##x,_n4##y,z,c);
645 
646 #define cimg_get9x9(img,x,y,z,c,I,T) \
647  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), \
648  I[3] = (T)(img)(_p1##x,_p4##y,z,c), I[4] = (T)(img)(x,_p4##y,z,c), I[5] = (T)(img)(_n1##x,_p4##y,z,c), \
649  I[6] = (T)(img)(_n2##x,_p4##y,z,c), I[7] = (T)(img)(_n3##x,_p4##y,z,c), I[8] = (T)(img)(_n4##x,_p4##y,z,c), \
650  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), \
651  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), \
652  I[15] = (T)(img)(_n2##x,_p3##y,z,c), I[16] = (T)(img)(_n3##x,_p3##y,z,c), I[17] = (T)(img)(_n4##x,_p3##y,z,c), \
653  I[18] = (T)(img)(_p4##x,_p2##y,z,c), I[19] = (T)(img)(_p3##x,_p2##y,z,c), I[20] = (T)(img)(_p2##x,_p2##y,z,c), \
654  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), \
655  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), \
656  I[27] = (T)(img)(_p4##x,_p1##y,z,c), I[28] = (T)(img)(_p3##x,_p1##y,z,c), I[29] = (T)(img)(_p2##x,_p1##y,z,c), \
657  I[30] = (T)(img)(_p1##x,_p1##y,z,c), I[31] = (T)(img)(x,_p1##y,z,c), I[32] = (T)(img)(_n1##x,_p1##y,z,c), \
658  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), \
659  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), \
660  I[39] = (T)(img)(_p1##x,y,z,c), I[40] = (T)(img)(x,y,z,c), I[41] = (T)(img)(_n1##x,y,z,c), \
661  I[42] = (T)(img)(_n2##x,y,z,c), I[43] = (T)(img)(_n3##x,y,z,c), I[44] = (T)(img)(_n4##x,y,z,c), \
662  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), \
663  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), \
664  I[51] = (T)(img)(_n2##x,_n1##y,z,c), I[52] = (T)(img)(_n3##x,_n1##y,z,c), I[53] = (T)(img)(_n4##x,_n1##y,z,c), \
665  I[54] = (T)(img)(_p4##x,_n2##y,z,c), I[55] = (T)(img)(_p3##x,_n2##y,z,c), I[56] = (T)(img)(_p2##x,_n2##y,z,c), \
666  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), \
667  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), \
668  I[63] = (T)(img)(_p4##x,_n3##y,z,c), I[64] = (T)(img)(_p3##x,_n3##y,z,c), I[65] = (T)(img)(_p2##x,_n3##y,z,c), \
669  I[66] = (T)(img)(_p1##x,_n3##y,z,c), I[67] = (T)(img)(x,_n3##y,z,c), I[68] = (T)(img)(_n1##x,_n3##y,z,c), \
670  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), \
671  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), \
672  I[75] = (T)(img)(_p1##x,_n4##y,z,c), I[76] = (T)(img)(x,_n4##y,z,c), I[77] = (T)(img)(_n1##x,_n4##y,z,c), \
673  I[78] = (T)(img)(_n2##x,_n4##y,z,c), I[79] = (T)(img)(_n3##x,_n4##y,z,c), I[80] = (T)(img)(_n4##x,_n4##y,z,c)
674 
675 #define cimg_get2x2x2(img,x,y,z,c,I,T) \
676  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), \
677  I[3] = (T)(img)(_n1##x,_n1##y,z,c), I[4] = (T)(img)(x,y,_n1##z,c), I[5] = (T)(img)(_n1##x,y,_n1##z,c), \
678  I[6] = (T)(img)(x,_n1##y,_n1##z,c), I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)
679 
680 #define cimg_get3x3x3(img,x,y,z,c,I,T) \
681  I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c), I[1] = (T)(img)(x,_p1##y,_p1##z,c), \
682  I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c), I[3] = (T)(img)(_p1##x,y,_p1##z,c), I[4] = (T)(img)(x,y,_p1##z,c), \
683  I[5] = (T)(img)(_n1##x,y,_p1##z,c), I[6] = (T)(img)(_p1##x,_n1##y,_p1##z,c), I[7] = (T)(img)(x,_n1##y,_p1##z,c), \
684  I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c), I[9] = (T)(img)(_p1##x,_p1##y,z,c), I[10] = (T)(img)(x,_p1##y,z,c), \
685  I[11] = (T)(img)(_n1##x,_p1##y,z,c), I[12] = (T)(img)(_p1##x,y,z,c), I[13] = (T)(img)(x,y,z,c), \
686  I[14] = (T)(img)(_n1##x,y,z,c), I[15] = (T)(img)(_p1##x,_n1##y,z,c), I[16] = (T)(img)(x,_n1##y,z,c), \
687  I[17] = (T)(img)(_n1##x,_n1##y,z,c), I[18] = (T)(img)(_p1##x,_p1##y,_n1##z,c), I[19] = (T)(img)(x,_p1##y,_n1##z,c), \
688  I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c), I[21] = (T)(img)(_p1##x,y,_n1##z,c), I[22] = (T)(img)(x,y,_n1##z,c), \
689  I[23] = (T)(img)(_n1##x,y,_n1##z,c), I[24] = (T)(img)(_p1##x,_n1##y,_n1##z,c), I[25] = (T)(img)(x,_n1##y,_n1##z,c), \
690  I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)
691 
692 // Macros to perform various image loops.
693 //
694 // These macros are simpler to use than loops with C++ iterators.
695 #define cimg_for(img,ptrs,T_ptrs) \
696  for (T_ptrs *ptrs = (img)._data, *_max##ptrs = (img)._data + (img).size(); ptrs<_max##ptrs; ++ptrs)
697 #define cimg_rof(img,ptrs,T_ptrs) for (T_ptrs *ptrs = (img)._data + (img).size() - 1; ptrs>=(img)._data; --ptrs)
698 #define cimg_foroff(img,off) for (unsigned long off = 0, _max##off = (img).size(); off<_max##off; ++off)
699 
700 #define cimg_for1(bound,i) for (int i = 0; i<(int)(bound); ++i)
701 #define cimg_forX(img,x) cimg_for1((img)._width,x)
702 #define cimg_forY(img,y) cimg_for1((img)._height,y)
703 #define cimg_forZ(img,z) cimg_for1((img)._depth,z)
704 #define cimg_forC(img,c) cimg_for1((img)._spectrum,c)
705 #define cimg_forXY(img,x,y) cimg_forY(img,y) cimg_forX(img,x)
706 #define cimg_forXZ(img,x,z) cimg_forZ(img,z) cimg_forX(img,x)
707 #define cimg_forYZ(img,y,z) cimg_forZ(img,z) cimg_forY(img,y)
708 #define cimg_forXC(img,x,c) cimg_forC(img,c) cimg_forX(img,x)
709 #define cimg_forYC(img,y,c) cimg_forC(img,c) cimg_forY(img,y)
710 #define cimg_forZC(img,z,c) cimg_forC(img,c) cimg_forZ(img,z)
711 #define cimg_forXYZ(img,x,y,z) cimg_forZ(img,z) cimg_forXY(img,x,y)
712 #define cimg_forXYC(img,x,y,c) cimg_forC(img,c) cimg_forXY(img,x,y)
713 #define cimg_forXZC(img,x,z,c) cimg_forC(img,c) cimg_forXZ(img,x,z)
714 #define cimg_forYZC(img,y,z,c) cimg_forC(img,c) cimg_forYZ(img,y,z)
715 #define cimg_forXYZC(img,x,y,z,c) cimg_forC(img,c) cimg_forXYZ(img,x,y,z)
716 
717 #define cimg_rof1(bound,i) for (int i = (int)(bound)-1; i>=0; --i)
718 #define cimg_rofX(img,x) cimg_rof1((img)._width,x)
719 #define cimg_rofY(img,y) cimg_rof1((img)._height,y)
720 #define cimg_rofZ(img,z) cimg_rof1((img)._depth,z)
721 #define cimg_rofC(img,c) cimg_rof1((img)._spectrum,c)
722 #define cimg_rofXY(img,x,y) cimg_rofY(img,y) cimg_rofX(img,x)
723 #define cimg_rofXZ(img,x,z) cimg_rofZ(img,z) cimg_rofX(img,x)
724 #define cimg_rofYZ(img,y,z) cimg_rofZ(img,z) cimg_rofY(img,y)
725 #define cimg_rofXC(img,x,c) cimg_rofC(img,c) cimg_rofX(img,x)
726 #define cimg_rofYC(img,y,c) cimg_rofC(img,c) cimg_rofY(img,y)
727 #define cimg_rofZC(img,z,c) cimg_rofC(img,c) cimg_rofZ(img,z)
728 #define cimg_rofXYZ(img,x,y,z) cimg_rofZ(img,z) cimg_rofXY(img,x,y)
729 #define cimg_rofXYC(img,x,y,c) cimg_rofC(img,c) cimg_rofXY(img,x,y)
730 #define cimg_rofXZC(img,x,z,c) cimg_rofC(img,c) cimg_rofXZ(img,x,z)
731 #define cimg_rofYZC(img,y,z,c) cimg_rofC(img,c) cimg_rofYZ(img,y,z)
732 #define cimg_rofXYZC(img,x,y,z,c) cimg_rofC(img,c) cimg_rofXYZ(img,x,y,z)
733 
734 #define cimg_for_in1(bound,i0,i1,i) \
735  for (int i = (int)(i0)<0?0:(int)(i0), _max##i = (int)(i1)<(int)(bound)?(int)(i1):(int)(bound)-1; i<=_max##i; ++i)
736 #define cimg_for_inX(img,x0,x1,x) cimg_for_in1((img)._width,x0,x1,x)
737 #define cimg_for_inY(img,y0,y1,y) cimg_for_in1((img)._height,y0,y1,y)
738 #define cimg_for_inZ(img,z0,z1,z) cimg_for_in1((img)._depth,z0,z1,z)
739 #define cimg_for_inC(img,c0,c1,c) cimg_for_in1((img)._spectrum,c0,c1,c)
740 #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)
741 #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)
742 #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)
743 #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)
744 #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)
745 #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)
746 #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)
747 #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)
748 #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)
749 #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)
750 #define cimg_for_inXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
751  cimg_for_inC(img,c0,c1,c) cimg_for_inXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
752 #define cimg_for_insideX(img,x,n) cimg_for_inX(img,n,(img)._width-1-(n),x)
753 #define cimg_for_insideY(img,y,n) cimg_for_inY(img,n,(img)._height-1-(n),y)
754 #define cimg_for_insideZ(img,z,n) cimg_for_inZ(img,n,(img)._depth-1-(n),z)
755 #define cimg_for_insideC(img,c,n) cimg_for_inC(img,n,(img)._spectrum-1-(n),c)
756 #define cimg_for_insideXY(img,x,y,n) cimg_for_inXY(img,n,n,(img)._width-1-(n),(img)._height-1-(n),x,y)
757 #define cimg_for_insideXYZ(img,x,y,z,n) \
758  cimg_for_inXYZ(img,n,n,n,(img)._width-1-(n),(img)._height-1-(n),(img)._depth-1-(n),x,y,z)
759 #define cimg_for_insideXYZC(img,x,y,z,c,n) \
760  cimg_for_inXYZ(img,n,n,n,(img)._width-1-(n),(img)._height-1-(n),(img)._depth-1-(n),x,y,z)
761 
762 #define cimg_for_out1(boundi,i0,i1,i) \
763  for (int i = (int)(i0)>0?0:(int)(i1)+1; i<(int)(boundi); ++i, i = i==(int)(i0)?(int)(i1)+1:i)
764 #define cimg_for_out2(boundi,boundj,i0,j0,i1,j1,i,j) \
765  for (int j = 0; j<(int)(boundj); ++j) \
766  for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j?0:(int)(i0)>0?0:(int)(i1)+1; i<(int)(boundi); \
767  ++i, i = _n1j?i:(i==(int)(i0)?(int)(i1)+1:i))
768 #define cimg_for_out3(boundi,boundj,boundk,i0,j0,k0,i1,j1,k1,i,j,k) \
769  for (int k = 0; k<(int)(boundk); ++k) \
770  for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \
771  for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k?0:(int)(i0)>0?0:(int)(i1)+1; i<(int)(boundi); \
772  ++i, i = _n1j || _n1k?i:(i==(int)(i0)?(int)(i1)+1:i))
773 #define cimg_for_out4(boundi,boundj,boundk,boundl,i0,j0,k0,l0,i1,j1,k1,l1,i,j,k,l) \
774  for (int l = 0; l<(int)(boundl); ++l) \
775  for (int _n1l = (int)(l<(int)(l0) || l>(int)(l1)), k = 0; k<(int)(boundk); ++k) \
776  for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \
777  for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k || _n1l?0:(int)(i0)>0?0:(int)(i1)+1; \
778  i<(int)(boundi); ++i, i = _n1j || _n1k || _n1l?i:(i==(int)(i0)?(int)(i1)+1:i))
779 #define cimg_for_outX(img,x0,x1,x) cimg_for_out1((img)._width,x0,x1,x)
780 #define cimg_for_outY(img,y0,y1,y) cimg_for_out1((img)._height,y0,y1,y)
781 #define cimg_for_outZ(img,z0,z1,z) cimg_for_out1((img)._depth,z0,z1,z)
782 #define cimg_for_outC(img,c0,c1,c) cimg_for_out1((img)._spectrum,c0,c1,c)
783 #define cimg_for_outXY(img,x0,y0,x1,y1,x,y) cimg_for_out2((img)._width,(img)._height,x0,y0,x1,y1,x,y)
784 #define cimg_for_outXZ(img,x0,z0,x1,z1,x,z) cimg_for_out2((img)._width,(img)._depth,x0,z0,x1,z1,x,z)
785 #define cimg_for_outXC(img,x0,c0,x1,c1,x,c) cimg_for_out2((img)._width,(img)._spectrum,x0,c0,x1,c1,x,c)
786 #define cimg_for_outYZ(img,y0,z0,y1,z1,y,z) cimg_for_out2((img)._height,(img)._depth,y0,z0,y1,z1,y,z)
787 #define cimg_for_outYC(img,y0,c0,y1,c1,y,c) cimg_for_out2((img)._height,(img)._spectrum,y0,c0,y1,c1,y,c)
788 #define cimg_for_outZC(img,z0,c0,z1,c1,z,c) cimg_for_out2((img)._depth,(img)._spectrum,z0,c0,z1,c1,z,c)
789 #define cimg_for_outXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) \
790  cimg_for_out3((img)._width,(img)._height,(img)._depth,x0,y0,z0,x1,y1,z1,x,y,z)
791 #define cimg_for_outXYC(img,x0,y0,c0,x1,y1,c1,x,y,c) \
792  cimg_for_out3((img)._width,(img)._height,(img)._spectrum,x0,y0,c0,x1,y1,c1,x,y,c)
793 #define cimg_for_outXZC(img,x0,z0,c0,x1,z1,c1,x,z,c) \
794  cimg_for_out3((img)._width,(img)._depth,(img)._spectrum,x0,z0,c0,x1,z1,c1,x,z,c)
795 #define cimg_for_outYZC(img,y0,z0,c0,y1,z1,c1,y,z,c) \
796  cimg_for_out3((img)._height,(img)._depth,(img)._spectrum,y0,z0,c0,y1,z1,c1,y,z,c)
797 #define cimg_for_outXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
798  cimg_for_out4((img)._width,(img)._height,(img)._depth,(img)._spectrum,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c)
799 #define cimg_for_borderX(img,x,n) cimg_for_outX(img,n,(img)._width-1-(n),x)
800 #define cimg_for_borderY(img,y,n) cimg_for_outY(img,n,(img)._height-1-(n),y)
801 #define cimg_for_borderZ(img,z,n) cimg_for_outZ(img,n,(img)._depth-1-(n),z)
802 #define cimg_for_borderC(img,c,n) cimg_for_outC(img,n,(img)._spectrum-1-(n),c)
803 #define cimg_for_borderXY(img,x,y,n) cimg_for_outXY(img,n,n,(img)._width-1-(n),(img)._height-1-(n),x,y)
804 #define cimg_for_borderXYZ(img,x,y,z,n) \
805  cimg_for_outXYZ(img,n,n,n,(img)._width-1-(n),(img)._height-1-(n),(img)._depth-1-(n),x,y,z)
806 #define cimg_for_borderXYZC(img,x,y,z,c,n) \
807  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)
808 
809 #define cimg_for_spiralXY(img,x,y) \
810  for (int x = 0, y = 0, _n1##x = 1, _n1##y = (img).width()*(img).height(); _n1##y; \
811  --_n1##y, _n1##x+=(_n1##x>>2)-((!(_n1##x&3)?--y:((_n1##x&3)==1?(img)._width-1-++x:\
812  ((_n1##x&3)==2?(img)._height-1-++y:--x))))?0:1)
813 
814 #define cimg_for_lineXY(x,y,x0,y0,x1,y1) \
815  for (int x = (int)(x0), y = (int)(y0), _sx = 1, _sy = 1, _steep = 0, \
816  _dx=(x1)>(x0)?(int)(x1)-(int)(x0):(_sx=-1,(int)(x0)-(int)(x1)), \
817  _dy=(y1)>(y0)?(int)(y1)-(int)(y0):(_sy=-1,(int)(y0)-(int)(y1)), \
818  _counter = _dx, \
819  _err = _dx>_dy?(_dy>>1):((_steep=1),(_counter=_dy),(_dx>>1)); \
820  _counter>=0; \
821  --_counter, x+=_steep? \
822  (y+=_sy,(_err-=_dx)<0?_err+=_dy,_sx:0): \
823  (y+=(_err-=_dy)<0?_err+=_dx,_sy:0,_sx))
824 
825 #define cimg_for2(bound,i) \
826  for (int i = 0, _n1##i = 1>=(bound)?(int)(bound)-1:1; \
827  _n1##i<(int)(bound) || i==--_n1##i; \
828  ++i, ++_n1##i)
829 #define cimg_for2X(img,x) cimg_for2((img)._width,x)
830 #define cimg_for2Y(img,y) cimg_for2((img)._height,y)
831 #define cimg_for2Z(img,z) cimg_for2((img)._depth,z)
832 #define cimg_for2C(img,c) cimg_for2((img)._spectrum,c)
833 #define cimg_for2XY(img,x,y) cimg_for2Y(img,y) cimg_for2X(img,x)
834 #define cimg_for2XZ(img,x,z) cimg_for2Z(img,z) cimg_for2X(img,x)
835 #define cimg_for2XC(img,x,c) cimg_for2C(img,c) cimg_for2X(img,x)
836 #define cimg_for2YZ(img,y,z) cimg_for2Z(img,z) cimg_for2Y(img,y)
837 #define cimg_for2YC(img,y,c) cimg_for2C(img,c) cimg_for2Y(img,y)
838 #define cimg_for2ZC(img,z,c) cimg_for2C(img,c) cimg_for2Z(img,z)
839 #define cimg_for2XYZ(img,x,y,z) cimg_for2Z(img,z) cimg_for2XY(img,x,y)
840 #define cimg_for2XZC(img,x,z,c) cimg_for2C(img,c) cimg_for2XZ(img,x,z)
841 #define cimg_for2YZC(img,y,z,c) cimg_for2C(img,c) cimg_for2YZ(img,y,z)
842 #define cimg_for2XYZC(img,x,y,z,c) cimg_for2C(img,c) cimg_for2XYZ(img,x,y,z)
843 
844 #define cimg_for_in2(bound,i0,i1,i) \
845  for (int i = (int)(i0)<0?0:(int)(i0), \
846  _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1; \
847  i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \
848  ++i, ++_n1##i)
849 #define cimg_for_in2X(img,x0,x1,x) cimg_for_in2((img)._width,x0,x1,x)
850 #define cimg_for_in2Y(img,y0,y1,y) cimg_for_in2((img)._height,y0,y1,y)
851 #define cimg_for_in2Z(img,z0,z1,z) cimg_for_in2((img)._depth,z0,z1,z)
852 #define cimg_for_in2C(img,c0,c1,c) cimg_for_in2((img)._spectrum,c0,c1,c)
853 #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)
854 #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)
855 #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)
856 #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)
857 #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)
858 #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)
859 #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)
860 #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)
861 #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)
862 #define cimg_for_in2XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
863  cimg_for_in2C(img,c0,c1,c) cimg_for_in2XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
864 
865 #define cimg_for3(bound,i) \
866  for (int i = 0, _p1##i = 0, \
867  _n1##i = 1>=(bound)?(int)(bound)-1:1; \
868  _n1##i<(int)(bound) || i==--_n1##i; \
869  _p1##i = i++, ++_n1##i)
870 #define cimg_for3X(img,x) cimg_for3((img)._width,x)
871 #define cimg_for3Y(img,y) cimg_for3((img)._height,y)
872 #define cimg_for3Z(img,z) cimg_for3((img)._depth,z)
873 #define cimg_for3C(img,c) cimg_for3((img)._spectrum,c)
874 #define cimg_for3XY(img,x,y) cimg_for3Y(img,y) cimg_for3X(img,x)
875 #define cimg_for3XZ(img,x,z) cimg_for3Z(img,z) cimg_for3X(img,x)
876 #define cimg_for3XC(img,x,c) cimg_for3C(img,c) cimg_for3X(img,x)
877 #define cimg_for3YZ(img,y,z) cimg_for3Z(img,z) cimg_for3Y(img,y)
878 #define cimg_for3YC(img,y,c) cimg_for3C(img,c) cimg_for3Y(img,y)
879 #define cimg_for3ZC(img,z,c) cimg_for3C(img,c) cimg_for3Z(img,z)
880 #define cimg_for3XYZ(img,x,y,z) cimg_for3Z(img,z) cimg_for3XY(img,x,y)
881 #define cimg_for3XZC(img,x,z,c) cimg_for3C(img,c) cimg_for3XZ(img,x,z)
882 #define cimg_for3YZC(img,y,z,c) cimg_for3C(img,c) cimg_for3YZ(img,y,z)
883 #define cimg_for3XYZC(img,x,y,z,c) cimg_for3C(img,c) cimg_for3XYZ(img,x,y,z)
884 
885 #define cimg_for_in3(bound,i0,i1,i) \
886  for (int i = (int)(i0)<0?0:(int)(i0), \
887  _p1##i = i-1<0?0:i-1, \
888  _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1; \
889  i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \
890  _p1##i = i++, ++_n1##i)
891 #define cimg_for_in3X(img,x0,x1,x) cimg_for_in3((img)._width,x0,x1,x)
892 #define cimg_for_in3Y(img,y0,y1,y) cimg_for_in3((img)._height,y0,y1,y)
893 #define cimg_for_in3Z(img,z0,z1,z) cimg_for_in3((img)._depth,z0,z1,z)
894 #define cimg_for_in3C(img,c0,c1,c) cimg_for_in3((img)._spectrum,c0,c1,c)
895 #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)
896 #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)
897 #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)
898 #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)
899 #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)
900 #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)
901 #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)
902 #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)
903 #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)
904 #define cimg_for_in3XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
905  cimg_for_in3C(img,c0,c1,c) cimg_for_in3XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
906 
907 #define cimg_for4(bound,i) \
908  for (int i = 0, _p1##i = 0, _n1##i = 1>=(bound)?(int)(bound)-1:1, \
909  _n2##i = 2>=(bound)?(int)(bound)-1:2; \
910  _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \
911  _p1##i = i++, ++_n1##i, ++_n2##i)
912 #define cimg_for4X(img,x) cimg_for4((img)._width,x)
913 #define cimg_for4Y(img,y) cimg_for4((img)._height,y)
914 #define cimg_for4Z(img,z) cimg_for4((img)._depth,z)
915 #define cimg_for4C(img,c) cimg_for4((img)._spectrum,c)
916 #define cimg_for4XY(img,x,y) cimg_for4Y(img,y) cimg_for4X(img,x)
917 #define cimg_for4XZ(img,x,z) cimg_for4Z(img,z) cimg_for4X(img,x)
918 #define cimg_for4XC(img,x,c) cimg_for4C(img,c) cimg_for4X(img,x)
919 #define cimg_for4YZ(img,y,z) cimg_for4Z(img,z) cimg_for4Y(img,y)
920 #define cimg_for4YC(img,y,c) cimg_for4C(img,c) cimg_for4Y(img,y)
921 #define cimg_for4ZC(img,z,c) cimg_for4C(img,c) cimg_for4Z(img,z)
922 #define cimg_for4XYZ(img,x,y,z) cimg_for4Z(img,z) cimg_for4XY(img,x,y)
923 #define cimg_for4XZC(img,x,z,c) cimg_for4C(img,c) cimg_for4XZ(img,x,z)
924 #define cimg_for4YZC(img,y,z,c) cimg_for4C(img,c) cimg_for4YZ(img,y,z)
925 #define cimg_for4XYZC(img,x,y,z,c) cimg_for4C(img,c) cimg_for4XYZ(img,x,y,z)
926 
927 #define cimg_for_in4(bound,i0,i1,i) \
928  for (int i = (int)(i0)<0?0:(int)(i0), \
929  _p1##i = i-1<0?0:i-1, \
930  _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
931  _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2; \
932  i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \
933  _p1##i = i++, ++_n1##i, ++_n2##i)
934 #define cimg_for_in4X(img,x0,x1,x) cimg_for_in4((img)._width,x0,x1,x)
935 #define cimg_for_in4Y(img,y0,y1,y) cimg_for_in4((img)._height,y0,y1,y)
936 #define cimg_for_in4Z(img,z0,z1,z) cimg_for_in4((img)._depth,z0,z1,z)
937 #define cimg_for_in4C(img,c0,c1,c) cimg_for_in4((img)._spectrum,c0,c1,c)
938 #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)
939 #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)
940 #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)
941 #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)
942 #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)
943 #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)
944 #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)
945 #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)
946 #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)
947 #define cimg_for_in4XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
948  cimg_for_in4C(img,c0,c1,c) cimg_for_in4XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
949 
950 #define cimg_for5(bound,i) \
951  for (int i = 0, _p2##i = 0, _p1##i = 0, \
952  _n1##i = 1>=(bound)?(int)(bound)-1:1, \
953  _n2##i = 2>=(bound)?(int)(bound)-1:2; \
954  _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \
955  _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i)
956 #define cimg_for5X(img,x) cimg_for5((img)._width,x)
957 #define cimg_for5Y(img,y) cimg_for5((img)._height,y)
958 #define cimg_for5Z(img,z) cimg_for5((img)._depth,z)
959 #define cimg_for5C(img,c) cimg_for5((img)._spectrum,c)
960 #define cimg_for5XY(img,x,y) cimg_for5Y(img,y) cimg_for5X(img,x)
961 #define cimg_for5XZ(img,x,z) cimg_for5Z(img,z) cimg_for5X(img,x)
962 #define cimg_for5XC(img,x,c) cimg_for5C(img,c) cimg_for5X(img,x)
963 #define cimg_for5YZ(img,y,z) cimg_for5Z(img,z) cimg_for5Y(img,y)
964 #define cimg_for5YC(img,y,c) cimg_for5C(img,c) cimg_for5Y(img,y)
965 #define cimg_for5ZC(img,z,c) cimg_for5C(img,c) cimg_for5Z(img,z)
966 #define cimg_for5XYZ(img,x,y,z) cimg_for5Z(img,z) cimg_for5XY(img,x,y)
967 #define cimg_for5XZC(img,x,z,c) cimg_for5C(img,c) cimg_for5XZ(img,x,z)
968 #define cimg_for5YZC(img,y,z,c) cimg_for5C(img,c) cimg_for5YZ(img,y,z)
969 #define cimg_for5XYZC(img,x,y,z,c) cimg_for5C(img,c) cimg_for5XYZ(img,x,y,z)
970 
971 #define cimg_for_in5(bound,i0,i1,i) \
972  for (int i = (int)(i0)<0?0:(int)(i0), \
973  _p2##i = i-2<0?0:i-2, \
974  _p1##i = i-1<0?0:i-1, \
975  _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
976  _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2; \
977  i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \
978  _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i)
979 #define cimg_for_in5X(img,x0,x1,x) cimg_for_in5((img)._width,x0,x1,x)
980 #define cimg_for_in5Y(img,y0,y1,y) cimg_for_in5((img)._height,y0,y1,y)
981 #define cimg_for_in5Z(img,z0,z1,z) cimg_for_in5((img)._depth,z0,z1,z)
982 #define cimg_for_in5C(img,c0,c1,c) cimg_for_in5((img)._spectrum,c0,c1,c)
983 #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)
984 #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)
985 #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)
986 #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)
987 #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)
988 #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)
989 #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)
990 #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)
991 #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)
992 #define cimg_for_in5XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
993  cimg_for_in5C(img,c0,c1,c) cimg_for_in5XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
994 
995 #define cimg_for6(bound,i) \
996  for (int i = 0, _p2##i = 0, _p1##i = 0, \
997  _n1##i = 1>=(bound)?(int)(bound)-1:1, \
998  _n2##i = 2>=(bound)?(int)(bound)-1:2, \
999  _n3##i = 3>=(bound)?(int)(bound)-1:3; \
1000  _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \
1001  _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
1002 #define cimg_for6X(img,x) cimg_for6((img)._width,x)
1003 #define cimg_for6Y(img,y) cimg_for6((img)._height,y)
1004 #define cimg_for6Z(img,z) cimg_for6((img)._depth,z)
1005 #define cimg_for6C(img,c) cimg_for6((img)._spectrum,c)
1006 #define cimg_for6XY(img,x,y) cimg_for6Y(img,y) cimg_for6X(img,x)
1007 #define cimg_for6XZ(img,x,z) cimg_for6Z(img,z) cimg_for6X(img,x)
1008 #define cimg_for6XC(img,x,c) cimg_for6C(img,c) cimg_for6X(img,x)
1009 #define cimg_for6YZ(img,y,z) cimg_for6Z(img,z) cimg_for6Y(img,y)
1010 #define cimg_for6YC(img,y,c) cimg_for6C(img,c) cimg_for6Y(img,y)
1011 #define cimg_for6ZC(img,z,c) cimg_for6C(img,c) cimg_for6Z(img,z)
1012 #define cimg_for6XYZ(img,x,y,z) cimg_for6Z(img,z) cimg_for6XY(img,x,y)
1013 #define cimg_for6XZC(img,x,z,c) cimg_for6C(img,c) cimg_for6XZ(img,x,z)
1014 #define cimg_for6YZC(img,y,z,c) cimg_for6C(img,c) cimg_for6YZ(img,y,z)
1015 #define cimg_for6XYZC(img,x,y,z,c) cimg_for6C(img,c) cimg_for6XYZ(img,x,y,z)
1016 
1017 #define cimg_for_in6(bound,i0,i1,i) \
1018  for (int i = (int)(i0)<0?0:(int)(i0), \
1019  _p2##i = i-2<0?0:i-2, \
1020  _p1##i = i-1<0?0:i-1, \
1021  _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
1022  _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \
1023  _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3; \
1024  i<=(int)(i1) && \
1025  (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \
1026  _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
1027 #define cimg_for_in6X(img,x0,x1,x) cimg_for_in6((img)._width,x0,x1,x)
1028 #define cimg_for_in6Y(img,y0,y1,y) cimg_for_in6((img)._height,y0,y1,y)
1029 #define cimg_for_in6Z(img,z0,z1,z) cimg_for_in6((img)._depth,z0,z1,z)
1030 #define cimg_for_in6C(img,c0,c1,c) cimg_for_in6((img)._spectrum,c0,c1,c)
1031 #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)
1032 #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)
1033 #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)
1034 #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)
1035 #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)
1036 #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)
1037 #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)
1038 #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)
1039 #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)
1040 #define cimg_for_in6XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
1041  cimg_for_in6C(img,c0,c1,c) cimg_for_in6XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
1042 
1043 #define cimg_for7(bound,i) \
1044  for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
1045  _n1##i = 1>=(bound)?(int)(bound)-1:1, \
1046  _n2##i = 2>=(bound)?(int)(bound)-1:2, \
1047  _n3##i = 3>=(bound)?(int)(bound)-1:3; \
1048  _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \
1049  _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
1050 #define cimg_for7X(img,x) cimg_for7((img)._width,x)
1051 #define cimg_for7Y(img,y) cimg_for7((img)._height,y)
1052 #define cimg_for7Z(img,z) cimg_for7((img)._depth,z)
1053 #define cimg_for7C(img,c) cimg_for7((img)._spectrum,c)
1054 #define cimg_for7XY(img,x,y) cimg_for7Y(img,y) cimg_for7X(img,x)
1055 #define cimg_for7XZ(img,x,z) cimg_for7Z(img,z) cimg_for7X(img,x)
1056 #define cimg_for7XC(img,x,c) cimg_for7C(img,c) cimg_for7X(img,x)
1057 #define cimg_for7YZ(img,y,z) cimg_for7Z(img,z) cimg_for7Y(img,y)
1058 #define cimg_for7YC(img,y,c) cimg_for7C(img,c) cimg_for7Y(img,y)
1059 #define cimg_for7ZC(img,z,c) cimg_for7C(img,c) cimg_for7Z(img,z)
1060 #define cimg_for7XYZ(img,x,y,z) cimg_for7Z(img,z) cimg_for7XY(img,x,y)
1061 #define cimg_for7XZC(img,x,z,c) cimg_for7C(img,c) cimg_for7XZ(img,x,z)
1062 #define cimg_for7YZC(img,y,z,c) cimg_for7C(img,c) cimg_for7YZ(img,y,z)
1063 #define cimg_for7XYZC(img,x,y,z,c) cimg_for7C(img,c) cimg_for7XYZ(img,x,y,z)
1064 
1065 #define cimg_for_in7(bound,i0,i1,i) \
1066  for (int i = (int)(i0)<0?0:(int)(i0), \
1067  _p3##i = i-3<0?0:i-3, \
1068  _p2##i = i-2<0?0:i-2, \
1069  _p1##i = i-1<0?0:i-1, \
1070  _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
1071  _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \
1072  _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3; \
1073  i<=(int)(i1) && \
1074  (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \
1075  _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
1076 #define cimg_for_in7X(img,x0,x1,x) cimg_for_in7((img)._width,x0,x1,x)
1077 #define cimg_for_in7Y(img,y0,y1,y) cimg_for_in7((img)._height,y0,y1,y)
1078 #define cimg_for_in7Z(img,z0,z1,z) cimg_for_in7((img)._depth,z0,z1,z)
1079 #define cimg_for_in7C(img,c0,c1,c) cimg_for_in7((img)._spectrum,c0,c1,c)
1080 #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)
1081 #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)
1082 #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)
1083 #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)
1084 #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)
1085 #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)
1086 #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)
1087 #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)
1088 #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)
1089 #define cimg_for_in7XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
1090  cimg_for_in7C(img,c0,c1,c) cimg_for_in7XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
1091 
1092 #define cimg_for8(bound,i) \
1093  for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
1094  _n1##i = 1>=(bound)?(int)(bound)-1:1, \
1095  _n2##i = 2>=(bound)?(int)(bound)-1:2, \
1096  _n3##i = 3>=(bound)?(int)(bound)-1:3, \
1097  _n4##i = 4>=(bound)?(int)(bound)-1:4; \
1098  _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
1099  i==(_n4##i = _n3##i = _n2##i = --_n1##i); \
1100  _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
1101 #define cimg_for8X(img,x) cimg_for8((img)._width,x)
1102 #define cimg_for8Y(img,y) cimg_for8((img)._height,y)
1103 #define cimg_for8Z(img,z) cimg_for8((img)._depth,z)
1104 #define cimg_for8C(img,c) cimg_for8((img)._spectrum,c)
1105 #define cimg_for8XY(img,x,y) cimg_for8Y(img,y) cimg_for8X(img,x)
1106 #define cimg_for8XZ(img,x,z) cimg_for8Z(img,z) cimg_for8X(img,x)
1107 #define cimg_for8XC(img,x,c) cimg_for8C(img,c) cimg_for8X(img,x)
1108 #define cimg_for8YZ(img,y,z) cimg_for8Z(img,z) cimg_for8Y(img,y)
1109 #define cimg_for8YC(img,y,c) cimg_for8C(img,c) cimg_for8Y(img,y)
1110 #define cimg_for8ZC(img,z,c) cimg_for8C(img,c) cimg_for8Z(img,z)
1111 #define cimg_for8XYZ(img,x,y,z) cimg_for8Z(img,z) cimg_for8XY(img,x,y)
1112 #define cimg_for8XZC(img,x,z,c) cimg_for8C(img,c) cimg_for8XZ(img,x,z)
1113 #define cimg_for8YZC(img,y,z,c) cimg_for8C(img,c) cimg_for8YZ(img,y,z)
1114 #define cimg_for8XYZC(img,x,y,z,c) cimg_for8C(img,c) cimg_for8XYZ(img,x,y,z)
1115 
1116 #define cimg_for_in8(bound,i0,i1,i) \
1117  for (int i = (int)(i0)<0?0:(int)(i0), \
1118  _p3##i = i-3<0?0:i-3, \
1119  _p2##i = i-2<0?0:i-2, \
1120  _p1##i = i-1<0?0:i-1, \
1121  _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
1122  _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \
1123  _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3, \
1124  _n4##i = i+4>=(int)(bound)?(int)(bound)-1:i+4; \
1125  i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
1126  i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \
1127  _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
1128 #define cimg_for_in8X(img,x0,x1,x) cimg_for_in8((img)._width,x0,x1,x)
1129 #define cimg_for_in8Y(img,y0,y1,y) cimg_for_in8((img)._height,y0,y1,y)
1130 #define cimg_for_in8Z(img,z0,z1,z) cimg_for_in8((img)._depth,z0,z1,z)
1131 #define cimg_for_in8C(img,c0,c1,c) cimg_for_in8((img)._spectrum,c0,c1,c)
1132 #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)
1133 #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)
1134 #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)
1135 #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)
1136 #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)
1137 #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)
1138 #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)
1139 #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)
1140 #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)
1141 #define cimg_for_in8XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
1142  cimg_for_in8C(img,c0,c1,c) cimg_for_in8XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
1143 
1144 #define cimg_for9(bound,i) \
1145  for (int i = 0, _p4##i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
1146  _n1##i = 1>=(int)(bound)?(int)(bound)-1:1, \
1147  _n2##i = 2>=(int)(bound)?(int)(bound)-1:2, \
1148  _n3##i = 3>=(int)(bound)?(int)(bound)-1:3, \
1149  _n4##i = 4>=(int)(bound)?(int)(bound)-1:4; \
1150  _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
1151  i==(_n4##i = _n3##i = _n2##i = --_n1##i); \
1152  _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
1153 #define cimg_for9X(img,x) cimg_for9((img)._width,x)
1154 #define cimg_for9Y(img,y) cimg_for9((img)._height,y)
1155 #define cimg_for9Z(img,z) cimg_for9((img)._depth,z)
1156 #define cimg_for9C(img,c) cimg_for9((img)._spectrum,c)
1157 #define cimg_for9XY(img,x,y) cimg_for9Y(img,y) cimg_for9X(img,x)
1158 #define cimg_for9XZ(img,x,z) cimg_for9Z(img,z) cimg_for9X(img,x)
1159 #define cimg_for9XC(img,x,c) cimg_for9C(img,c) cimg_for9X(img,x)
1160 #define cimg_for9YZ(img,y,z) cimg_for9Z(img,z) cimg_for9Y(img,y)
1161 #define cimg_for9YC(img,y,c) cimg_for9C(img,c) cimg_for9Y(img,y)
1162 #define cimg_for9ZC(img,z,c) cimg_for9C(img,c) cimg_for9Z(img,z)
1163 #define cimg_for9XYZ(img,x,y,z) cimg_for9Z(img,z) cimg_for9XY(img,x,y)
1164 #define cimg_for9XZC(img,x,z,c) cimg_for9C(img,c) cimg_for9XZ(img,x,z)
1165 #define cimg_for9YZC(img,y,z,c) cimg_for9C(img,c) cimg_for9YZ(img,y,z)
1166 #define cimg_for9XYZC(img,x,y,z,c) cimg_for9C(img,c) cimg_for9XYZ(img,x,y,z)
1167 
1168 #define cimg_for_in9(bound,i0,i1,i) \
1169  for (int i = (int)(i0)<0?0:(int)(i0), \
1170  _p4##i = i-4<0?0:i-4, \
1171  _p3##i = i-3<0?0:i-3, \
1172  _p2##i = i-2<0?0:i-2, \
1173  _p1##i = i-1<0?0:i-1, \
1174  _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
1175  _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \
1176  _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3, \
1177  _n4##i = i+4>=(int)(bound)?(int)(bound)-1:i+4; \
1178  i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
1179  i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \
1180  _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
1181 #define cimg_for_in9X(img,x0,x1,x) cimg_for_in9((img)._width,x0,x1,x)
1182 #define cimg_for_in9Y(img,y0,y1,y) cimg_for_in9((img)._height,y0,y1,y)
1183 #define cimg_for_in9Z(img,z0,z1,z) cimg_for_in9((img)._depth,z0,z1,z)
1184 #define cimg_for_in9C(img,c0,c1,c) cimg_for_in9((img)._spectrum,c0,c1,c)
1185 #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)
1186 #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)
1187 #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)
1188 #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)
1189 #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)
1190 #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)
1191 #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)
1192 #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)
1193 #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)
1194 #define cimg_for_in9XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
1195  cimg_for_in9C(img,c0,c1,c) cimg_for_in9XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
1196 
1197 #define cimg_for2x2(img,x,y,z,c,I,T) \
1198  cimg_for2((img)._height,y) for (int x = 0, \
1199  _n1##x = (int)( \
1200  (I[0] = (T)(img)(0,y,z,c)), \
1201  (I[2] = (T)(img)(0,_n1##y,z,c)), \
1202  1>=(img)._width?(img).width()-1:1); \
1203  (_n1##x<(img).width() && ( \
1204  (I[1] = (T)(img)(_n1##x,y,z,c)), \
1205  (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
1206  x==--_n1##x; \
1207  I[0] = I[1], \
1208  I[2] = I[3], \
1209  ++x, ++_n1##x)
1210 
1211 #define cimg_for_in2x2(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1212  cimg_for_in2((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1213  _n1##x = (int)( \
1214  (I[0] = (T)(img)(x,y,z,c)), \
1215  (I[2] = (T)(img)(x,_n1##y,z,c)), \
1216  x+1>=(int)(img)._width?(img).width()-1:x+1); \
1217  x<=(int)(x1) && ((_n1##x<(img).width() && ( \
1218  (I[1] = (T)(img)(_n1##x,y,z,c)), \
1219  (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
1220  x==--_n1##x); \
1221  I[0] = I[1], \
1222  I[2] = I[3], \
1223  ++x, ++_n1##x)
1224 
1225 #define cimg_for3x3(img,x,y,z,c,I,T) \
1226  cimg_for3((img)._height,y) for (int x = 0, \
1227  _p1##x = 0, \
1228  _n1##x = (int)( \
1229  (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
1230  (I[3] = I[4] = (T)(img)(0,y,z,c)), \
1231  (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)), \
1232  1>=(img)._width?(img).width()-1:1); \
1233  (_n1##x<(img).width() && ( \
1234  (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
1235  (I[5] = (T)(img)(_n1##x,y,z,c)), \
1236  (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
1237  x==--_n1##x; \
1238  I[0] = I[1], I[1] = I[2], \
1239  I[3] = I[4], I[4] = I[5], \
1240  I[6] = I[7], I[7] = I[8], \
1241  _p1##x = x++, ++_n1##x)
1242 
1243 #define cimg_for_in3x3(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1244  cimg_for_in3((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1245  _p1##x = x-1<0?0:x-1, \
1246  _n1##x = (int)( \
1247  (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \
1248  (I[3] = (T)(img)(_p1##x,y,z,c)), \
1249  (I[6] = (T)(img)(_p1##x,_n1##y,z,c)), \
1250  (I[1] = (T)(img)(x,_p1##y,z,c)), \
1251  (I[4] = (T)(img)(x,y,z,c)), \
1252  (I[7] = (T)(img)(x,_n1##y,z,c)), \
1253  x+1>=(int)(img)._width?(img).width()-1:x+1); \
1254  x<=(int)(x1) && ((_n1##x<(img).width() && ( \
1255  (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
1256  (I[5] = (T)(img)(_n1##x,y,z,c)), \
1257  (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
1258  x==--_n1##x); \
1259  I[0] = I[1], I[1] = I[2], \
1260  I[3] = I[4], I[4] = I[5], \
1261  I[6] = I[7], I[7] = I[8], \
1262  _p1##x = x++, ++_n1##x)
1263 
1264 #define cimg_for4x4(img,x,y,z,c,I,T) \
1265  cimg_for4((img)._height,y) for (int x = 0, \
1266  _p1##x = 0, \
1267  _n1##x = 1>=(img)._width?(img).width()-1:1, \
1268  _n2##x = (int)( \
1269  (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
1270  (I[4] = I[5] = (T)(img)(0,y,z,c)), \
1271  (I[8] = I[9] = (T)(img)(0,_n1##y,z,c)), \
1272  (I[12] = I[13] = (T)(img)(0,_n2##y,z,c)), \
1273  (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
1274  (I[6] = (T)(img)(_n1##x,y,z,c)), \
1275  (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \
1276  (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \
1277  2>=(img)._width?(img).width()-1:2); \
1278  (_n2##x<(img).width() && ( \
1279  (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \
1280  (I[7] = (T)(img)(_n2##x,y,z,c)), \
1281  (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \
1282  (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
1283  _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \
1284  I[0] = I[1], I[1] = I[2], I[2] = I[3], \
1285  I[4] = I[5], I[5] = I[6], I[6] = I[7], \
1286  I[8] = I[9], I[9] = I[10], I[10] = I[11], \
1287  I[12] = I[13], I[13] = I[14], I[14] = I[15], \
1288  _p1##x = x++, ++_n1##x, ++_n2##x)
1289 
1290 #define cimg_for_in4x4(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1291  cimg_for_in4((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1292  _p1##x = x-1<0?0:x-1, \
1293  _n1##x = x+1>=(int)(img)._width?(img).width()-1:x+1, \
1294  _n2##x = (int)( \
1295  (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \
1296  (I[4] = (T)(img)(_p1##x,y,z,c)), \
1297  (I[8] = (T)(img)(_p1##x,_n1##y,z,c)), \
1298  (I[12] = (T)(img)(_p1##x,_n2##y,z,c)), \
1299  (I[1] = (T)(img)(x,_p1##y,z,c)), \
1300  (I[5] = (T)(img)(x,y,z,c)), \
1301  (I[9] = (T)(img)(x,_n1##y,z,c)), \
1302  (I[13] = (T)(img)(x,_n2##y,z,c)), \
1303  (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
1304  (I[6] = (T)(img)(_n1##x,y,z,c)), \
1305  (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \
1306  (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \
1307  x+2>=(int)(img)._width?(img).width()-1:x+2); \
1308  x<=(int)(x1) && ((_n2##x<(img).width() && ( \
1309  (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \
1310  (I[7] = (T)(img)(_n2##x,y,z,c)), \
1311  (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \
1312  (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
1313  _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \
1314  I[0] = I[1], I[1] = I[2], I[2] = I[3], \
1315  I[4] = I[5], I[5] = I[6], I[6] = I[7], \
1316  I[8] = I[9], I[9] = I[10], I[10] = I[11], \
1317  I[12] = I[13], I[13] = I[14], I[14] = I[15], \
1318  _p1##x = x++, ++_n1##x, ++_n2##x)
1319 
1320 #define cimg_for5x5(img,x,y,z,c,I,T) \
1321  cimg_for5((img)._height,y) for (int x = 0, \
1322  _p2##x = 0, _p1##x = 0, \
1323  _n1##x = 1>=(img)._width?(img).width()-1:1, \
1324  _n2##x = (int)( \
1325  (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \
1326  (I[5] = I[6] = I[7] = (T)(img)(0,_p1##y,z,c)), \
1327  (I[10] = I[11] = I[12] = (T)(img)(0,y,z,c)), \
1328  (I[15] = I[16] = I[17] = (T)(img)(0,_n1##y,z,c)), \
1329  (I[20] = I[21] = I[22] = (T)(img)(0,_n2##y,z,c)), \
1330  (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
1331  (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \
1332  (I[13] = (T)(img)(_n1##x,y,z,c)), \
1333  (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \
1334  (I[23] = (T)(img)(_n1##x,_n2##y,z,c)), \
1335  2>=(img)._width?(img).width()-1:2); \
1336  (_n2##x<(img).width() && ( \
1337  (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
1338  (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \
1339  (I[14] = (T)(img)(_n2##x,y,z,c)), \
1340  (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \
1341  (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
1342  _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \
1343  I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \
1344  I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \
1345  I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \
1346  I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \
1347  I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \
1348  _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x)
1349 
1350 #define cimg_for_in5x5(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1351  cimg_for_in5((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1352  _p2##x = x-2<0?0:x-2, \
1353  _p1##x = x-1<0?0:x-1, \
1354  _n1##x = x+1>=(int)(img)._width?(img).width()-1:x+1, \
1355  _n2##x = (int)( \
1356  (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \
1357  (I[5] = (T)(img)(_p2##x,_p1##y,z,c)), \
1358  (I[10] = (T)(img)(_p2##x,y,z,c)), \
1359  (I[15] = (T)(img)(_p2##x,_n1##y,z,c)), \
1360  (I[20] = (T)(img)(_p2##x,_n2##y,z,c)), \
1361  (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \
1362  (I[6] = (T)(img)(_p1##x,_p1##y,z,c)), \
1363  (I[11] = (T)(img)(_p1##x,y,z,c)), \
1364  (I[16] = (T)(img)(_p1##x,_n1##y,z,c)), \
1365  (I[21] = (T)(img)(_p1##x,_n2##y,z,c)), \
1366  (I[2] = (T)(img)(x,_p2##y,z,c)), \
1367  (I[7] = (T)(img)(x,_p1##y,z,c)), \
1368  (I[12] = (T)(img)(x,y,z,c)), \
1369  (I[17] = (T)(img)(x,_n1##y,z,c)), \
1370  (I[22] = (T)(img)(x,_n2##y,z,c)), \
1371  (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
1372  (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \
1373  (I[13] = (T)(img)(_n1##x,y,z,c)), \
1374  (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \
1375  (I[23] = (T)(img)(_n1##x,_n2##y,z,c)), \
1376  x+2>=(int)(img)._width?(img).width()-1:x+2); \
1377  x<=(int)(x1) && ((_n2##x<(img).width() && ( \
1378  (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
1379  (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \
1380  (I[14] = (T)(img)(_n2##x,y,z,c)), \
1381  (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \
1382  (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
1383  _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \
1384  I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \
1385  I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \
1386  I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \
1387  I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \
1388  I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \
1389  _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x)
1390 
1391 #define cimg_for6x6(img,x,y,z,c,I,T) \
1392  cimg_for6((img)._height,y) for (int x = 0, \
1393  _p2##x = 0, _p1##x = 0, \
1394  _n1##x = 1>=(img)._width?(img).width()-1:1, \
1395  _n2##x = 2>=(img)._width?(img).width()-1:2, \
1396  _n3##x = (int)( \
1397  (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \
1398  (I[6] = I[7] = I[8] = (T)(img)(0,_p1##y,z,c)), \
1399  (I[12] = I[13] = I[14] = (T)(img)(0,y,z,c)), \
1400  (I[18] = I[19] = I[20] = (T)(img)(0,_n1##y,z,c)), \
1401  (I[24] = I[25] = I[26] = (T)(img)(0,_n2##y,z,c)), \
1402  (I[30] = I[31] = I[32] = (T)(img)(0,_n3##y,z,c)), \
1403  (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
1404  (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \
1405  (I[15] = (T)(img)(_n1##x,y,z,c)), \
1406  (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \
1407  (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \
1408  (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \
1409  (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
1410  (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \
1411  (I[16] = (T)(img)(_n2##x,y,z,c)), \
1412  (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \
1413  (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \
1414  (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \
1415  3>=(img)._width?(img).width()-1:3); \
1416  (_n3##x<(img).width() && ( \
1417  (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \
1418  (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \
1419  (I[17] = (T)(img)(_n3##x,y,z,c)), \
1420  (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \
1421  (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \
1422  (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
1423  _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x); \
1424  I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \
1425  I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \
1426  I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \
1427  I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \
1428  I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \
1429  I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \
1430  _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
1431 
1432 #define cimg_for_in6x6(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1433  cimg_for_in6((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)x0, \
1434  _p2##x = x-2<0?0:x-2, \
1435  _p1##x = x-1<0?0:x-1, \
1436  _n1##x = x+1>=(int)(img)._width?(img).width()-1:x+1, \
1437  _n2##x = x+2>=(int)(img)._width?(img).width()-1:x+2, \
1438  _n3##x = (int)( \
1439  (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \
1440  (I[6] = (T)(img)(_p2##x,_p1##y,z,c)), \
1441  (I[12] = (T)(img)(_p2##x,y,z,c)), \
1442  (I[18] = (T)(img)(_p2##x,_n1##y,z,c)), \
1443  (I[24] = (T)(img)(_p2##x,_n2##y,z,c)), \
1444  (I[30] = (T)(img)(_p2##x,_n3##y,z,c)), \
1445  (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \
1446  (I[7] = (T)(img)(_p1##x,_p1##y,z,c)), \
1447  (I[13] = (T)(img)(_p1##x,y,z,c)), \
1448  (I[19] = (T)(img)(_p1##x,_n1##y,z,c)), \
1449  (I[25] = (T)(img)(_p1##x,_n2##y,z,c)), \
1450  (I[31] = (T)(img)(_p1##x,_n3##y,z,c)), \
1451  (I[2] = (T)(img)(x,_p2##y,z,c)), \
1452  (I[8] = (T)(img)(x,_p1##y,z,c)), \
1453  (I[14] = (T)(img)(x,y,z,c)), \
1454  (I[20] = (T)(img)(x,_n1##y,z,c)), \
1455  (I[26] = (T)(img)(x,_n2##y,z,c)), \
1456  (I[32] = (T)(img)(x,_n3##y,z,c)), \
1457  (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
1458  (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \
1459  (I[15] = (T)(img)(_n1##x,y,z,c)), \
1460  (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \
1461  (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \
1462  (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \
1463  (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
1464  (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \
1465  (I[16] = (T)(img)(_n2##x,y,z,c)), \
1466  (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \
1467  (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \
1468  (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \
1469  x+3>=(int)(img)._width?(img).width()-1:x+3); \
1470  x<=(int)(x1) && ((_n3##x<(img).width() && ( \
1471  (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \
1472  (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \
1473  (I[17] = (T)(img)(_n3##x,y,z,c)), \
1474  (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \
1475  (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \
1476  (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
1477  _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x)); \
1478  I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \
1479  I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \
1480  I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \
1481  I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \
1482  I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \
1483  I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \
1484  _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
1485 
1486 #define cimg_for7x7(img,x,y,z,c,I,T) \
1487  cimg_for7((img)._height,y) for (int x = 0, \
1488  _p3##x = 0, _p2##x = 0, _p1##x = 0, \
1489  _n1##x = 1>=(img)._width?(img).width()-1:1, \
1490  _n2##x = 2>=(img)._width?(img).width()-1:2, \
1491  _n3##x = (int)( \
1492  (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \
1493  (I[7] = I[8] = I[9] = I[10] = (T)(img)(0,_p2##y,z,c)), \
1494  (I[14] = I[15] = I[16] = I[17] = (T)(img)(0,_p1##y,z,c)), \
1495  (I[21] = I[22] = I[23] = I[24] = (T)(img)(0,y,z,c)), \
1496  (I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_n1##y,z,c)), \
1497  (I[35] = I[36] = I[37] = I[38] = (T)(img)(0,_n2##y,z,c)), \
1498  (I[42] = I[43] = I[44] = I[45] = (T)(img)(0,_n3##y,z,c)), \
1499  (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
1500  (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \
1501  (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \
1502  (I[25] = (T)(img)(_n1##x,y,z,c)), \
1503  (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \
1504  (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \
1505  (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \
1506  (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
1507  (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \
1508  (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \
1509  (I[26] = (T)(img)(_n2##x,y,z,c)), \
1510  (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \
1511  (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \
1512  (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \
1513  3>=(img)._width?(img).width()-1:3); \
1514  (_n3##x<(img).width() && ( \
1515  (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
1516  (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \
1517  (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \
1518  (I[27] = (T)(img)(_n3##x,y,z,c)), \
1519  (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \
1520  (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \
1521  (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
1522  _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x); \
1523  I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \
1524  I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \
1525  I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \
1526  I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \
1527  I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \
1528  I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \
1529  I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \
1530  _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
1531 
1532 #define cimg_for_in7x7(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1533  cimg_for_in7((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1534  _p3##x = x-3<0?0:x-3, \
1535  _p2##x = x-2<0?0:x-2, \
1536  _p1##x = x-1<0?0:x-1, \
1537  _n1##x = x+1>=(int)(img)._width?(img).width()-1:x+1, \
1538  _n2##x = x+2>=(int)(img)._width?(img).width()-1:x+2, \
1539  _n3##x = (int)( \
1540  (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \
1541  (I[7] = (T)(img)(_p3##x,_p2##y,z,c)), \
1542  (I[14] = (T)(img)(_p3##x,_p1##y,z,c)), \
1543  (I[21] = (T)(img)(_p3##x,y,z,c)), \
1544  (I[28] = (T)(img)(_p3##x,_n1##y,z,c)), \
1545  (I[35] = (T)(img)(_p3##x,_n2##y,z,c)), \
1546  (I[42] = (T)(img)(_p3##x,_n3##y,z,c)), \
1547  (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \
1548  (I[8] = (T)(img)(_p2##x,_p2##y,z,c)), \
1549  (I[15] = (T)(img)(_p2##x,_p1##y,z,c)), \
1550  (I[22] = (T)(img)(_p2##x,y,z,c)), \
1551  (I[29] = (T)(img)(_p2##x,_n1##y,z,c)), \
1552  (I[36] = (T)(img)(_p2##x,_n2##y,z,c)), \
1553  (I[43] = (T)(img)(_p2##x,_n3##y,z,c)), \
1554  (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \
1555  (I[9] = (T)(img)(_p1##x,_p2##y,z,c)), \
1556  (I[16] = (T)(img)(_p1##x,_p1##y,z,c)), \
1557  (I[23] = (T)(img)(_p1##x,y,z,c)), \
1558  (I[30] = (T)(img)(_p1##x,_n1##y,z,c)), \
1559  (I[37] = (T)(img)(_p1##x,_n2##y,z,c)), \
1560  (I[44] = (T)(img)(_p1##x,_n3##y,z,c)), \
1561  (I[3] = (T)(img)(x,_p3##y,z,c)), \
1562  (I[10] = (T)(img)(x,_p2##y,z,c)), \
1563  (I[17] = (T)(img)(x,_p1##y,z,c)), \
1564  (I[24] = (T)(img)(x,y,z,c)), \
1565  (I[31] = (T)(img)(x,_n1##y,z,c)), \
1566  (I[38] = (T)(img)(x,_n2##y,z,c)), \
1567  (I[45] = (T)(img)(x,_n3##y,z,c)), \
1568  (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
1569  (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \
1570  (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \
1571  (I[25] = (T)(img)(_n1##x,y,z,c)), \
1572  (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \
1573  (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \
1574  (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \
1575  (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
1576  (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \
1577  (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \
1578  (I[26] = (T)(img)(_n2##x,y,z,c)), \
1579  (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \
1580  (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \
1581  (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \
1582  x+3>=(int)(img)._width?(img).width()-1:x+3); \
1583  x<=(int)(x1) && ((_n3##x<(img).width() && ( \
1584  (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
1585  (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \
1586  (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \
1587  (I[27] = (T)(img)(_n3##x,y,z,c)), \
1588  (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \
1589  (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \
1590  (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
1591  _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x)); \
1592  I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \
1593  I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \
1594  I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \
1595  I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \
1596  I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \
1597  I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \
1598  I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \
1599  _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
1600 
1601 #define cimg_for8x8(img,x,y,z,c,I,T) \
1602  cimg_for8((img)._height,y) for (int x = 0, \
1603  _p3##x = 0, _p2##x = 0, _p1##x = 0, \
1604  _n1##x = 1>=((img)._width)?(img).width()-1:1, \
1605  _n2##x = 2>=((img)._width)?(img).width()-1:2, \
1606  _n3##x = 3>=((img)._width)?(img).width()-1:3, \
1607  _n4##x = (int)( \
1608  (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \
1609  (I[8] = I[9] = I[10] = I[11] = (T)(img)(0,_p2##y,z,c)), \
1610  (I[16] = I[17] = I[18] = I[19] = (T)(img)(0,_p1##y,z,c)), \
1611  (I[24] = I[25] = I[26] = I[27] = (T)(img)(0,y,z,c)), \
1612  (I[32] = I[33] = I[34] = I[35] = (T)(img)(0,_n1##y,z,c)), \
1613  (I[40] = I[41] = I[42] = I[43] = (T)(img)(0,_n2##y,z,c)), \
1614  (I[48] = I[49] = I[50] = I[51] = (T)(img)(0,_n3##y,z,c)), \
1615  (I[56] = I[57] = I[58] = I[59] = (T)(img)(0,_n4##y,z,c)), \
1616  (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
1617  (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \
1618  (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \
1619  (I[28] = (T)(img)(_n1##x,y,z,c)), \
1620  (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \
1621  (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \
1622  (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \
1623  (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \
1624  (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
1625  (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \
1626  (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \
1627  (I[29] = (T)(img)(_n2##x,y,z,c)), \
1628  (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \
1629  (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \
1630  (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \
1631  (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \
1632  (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
1633  (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \
1634  (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \
1635  (I[30] = (T)(img)(_n3##x,y,z,c)), \
1636  (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \
1637  (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \
1638  (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \
1639  (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \
1640  4>=((img)._width)?(img).width()-1:4); \
1641  (_n4##x<(img).width() && ( \
1642  (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \
1643  (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \
1644  (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \
1645  (I[31] = (T)(img)(_n4##x,y,z,c)), \
1646  (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \
1647  (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \
1648  (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \
1649  (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
1650  _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \
1651  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], \
1652  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], \
1653  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], \
1654  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], \
1655  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], \
1656  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], \
1657  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], \
1658  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], \
1659  _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
1660 
1661 #define cimg_for_in8x8(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1662  cimg_for_in8((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1663  _p3##x = x-3<0?0:x-3, \
1664  _p2##x = x-2<0?0:x-2, \
1665  _p1##x = x-1<0?0:x-1, \
1666  _n1##x = x+1>=(img).width()?(img).width()-1:x+1, \
1667  _n2##x = x+2>=(img).width()?(img).width()-1:x+2, \
1668  _n3##x = x+3>=(img).width()?(img).width()-1:x+3, \
1669  _n4##x = (int)( \
1670  (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \
1671  (I[8] = (T)(img)(_p3##x,_p2##y,z,c)), \
1672  (I[16] = (T)(img)(_p3##x,_p1##y,z,c)), \
1673  (I[24] = (T)(img)(_p3##x,y,z,c)), \
1674  (I[32] = (T)(img)(_p3##x,_n1##y,z,c)), \
1675  (I[40] = (T)(img)(_p3##x,_n2##y,z,c)), \
1676  (I[48] = (T)(img)(_p3##x,_n3##y,z,c)), \
1677  (I[56] = (T)(img)(_p3##x,_n4##y,z,c)), \
1678  (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \
1679  (I[9] = (T)(img)(_p2##x,_p2##y,z,c)), \
1680  (I[17] = (T)(img)(_p2##x,_p1##y,z,c)), \
1681  (I[25] = (T)(img)(_p2##x,y,z,c)), \
1682  (I[33] = (T)(img)(_p2##x,_n1##y,z,c)), \
1683  (I[41] = (T)(img)(_p2##x,_n2##y,z,c)), \
1684  (I[49] = (T)(img)(_p2##x,_n3##y,z,c)), \
1685  (I[57] = (T)(img)(_p2##x,_n4##y,z,c)), \
1686  (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \
1687  (I[10] = (T)(img)(_p1##x,_p2##y,z,c)), \
1688  (I[18] = (T)(img)(_p1##x,_p1##y,z,c)), \
1689  (I[26] = (T)(img)(_p1##x,y,z,c)), \
1690  (I[34] = (T)(img)(_p1##x,_n1##y,z,c)), \
1691  (I[42] = (T)(img)(_p1##x,_n2##y,z,c)), \
1692  (I[50] = (T)(img)(_p1##x,_n3##y,z,c)), \
1693  (I[58] = (T)(img)(_p1##x,_n4##y,z,c)), \
1694  (I[3] = (T)(img)(x,_p3##y,z,c)), \
1695  (I[11] = (T)(img)(x,_p2##y,z,c)), \
1696  (I[19] = (T)(img)(x,_p1##y,z,c)), \
1697  (I[27] = (T)(img)(x,y,z,c)), \
1698  (I[35] = (T)(img)(x,_n1##y,z,c)), \
1699  (I[43] = (T)(img)(x,_n2##y,z,c)), \
1700  (I[51] = (T)(img)(x,_n3##y,z,c)), \
1701  (I[59] = (T)(img)(x,_n4##y,z,c)), \
1702  (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
1703  (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \
1704  (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \
1705  (I[28] = (T)(img)(_n1##x,y,z,c)), \
1706  (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \
1707  (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \
1708  (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \
1709  (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \
1710  (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
1711  (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \
1712  (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \
1713  (I[29] = (T)(img)(_n2##x,y,z,c)), \
1714  (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \
1715  (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \
1716  (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \
1717  (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \
1718  (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
1719  (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \
1720  (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \
1721  (I[30] = (T)(img)(_n3##x,y,z,c)), \
1722  (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \
1723  (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \
1724  (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \
1725  (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \
1726  x+4>=(img).width()?(img).width()-1:x+4); \
1727  x<=(int)(x1) && ((_n4##x<(img).width() && ( \
1728  (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \
1729  (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \
1730  (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \
1731  (I[31] = (T)(img)(_n4##x,y,z,c)), \
1732  (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \
1733  (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \
1734  (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \
1735  (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
1736  _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \
1737  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], \
1738  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], \
1739  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], \
1740  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], \
1741  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], \
1742  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], \
1743  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], \
1744  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], \
1745  _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
1746 
1747 #define cimg_for9x9(img,x,y,z,c,I,T) \
1748  cimg_for9((img)._height,y) for (int x = 0, \
1749  _p4##x = 0, _p3##x = 0, _p2##x = 0, _p1##x = 0, \
1750  _n1##x = 1>=((img)._width)?(img).width()-1:1, \
1751  _n2##x = 2>=((img)._width)?(img).width()-1:2, \
1752  _n3##x = 3>=((img)._width)?(img).width()-1:3, \
1753  _n4##x = (int)( \
1754  (I[0] = I[1] = I[2] = I[3] = I[4] = (T)(img)(_p4##x,_p4##y,z,c)), \
1755  (I[9] = I[10] = I[11] = I[12] = I[13] = (T)(img)(0,_p3##y,z,c)), \
1756  (I[18] = I[19] = I[20] = I[21] = I[22] = (T)(img)(0,_p2##y,z,c)), \
1757  (I[27] = I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_p1##y,z,c)), \
1758  (I[36] = I[37] = I[38] = I[39] = I[40] = (T)(img)(0,y,z,c)), \
1759  (I[45] = I[46] = I[47] = I[48] = I[49] = (T)(img)(0,_n1##y,z,c)), \
1760  (I[54] = I[55] = I[56] = I[57] = I[58] = (T)(img)(0,_n2##y,z,c)), \
1761  (I[63] = I[64] = I[65] = I[66] = I[67] = (T)(img)(0,_n3##y,z,c)), \
1762  (I[72] = I[73] = I[74] = I[75] = I[76] = (T)(img)(0,_n4##y,z,c)), \
1763  (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \
1764  (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \
1765  (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \
1766  (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \
1767  (I[41] = (T)(img)(_n1##x,y,z,c)), \
1768  (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \
1769  (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \
1770  (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \
1771  (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \
1772  (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \
1773  (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \
1774  (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \
1775  (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \
1776  (I[42] = (T)(img)(_n2##x,y,z,c)), \
1777  (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \
1778  (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \
1779  (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \
1780  (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \
1781  (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \
1782  (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \
1783  (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \
1784  (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \
1785  (I[43] = (T)(img)(_n3##x,y,z,c)), \
1786  (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \
1787  (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \
1788  (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \
1789  (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \
1790  4>=((img)._width)?(img).width()-1:4); \
1791  (_n4##x<(img).width() && ( \
1792  (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \
1793  (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \
1794  (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \
1795  (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \
1796  (I[44] = (T)(img)(_n4##x,y,z,c)), \
1797  (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \
1798  (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \
1799  (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \
1800  (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
1801  _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \
1802  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], \
1803  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], \
1804  I[16] = I[17], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \
1805  I[24] = I[25], I[25] = I[26], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], \
1806  I[32] = I[33], I[33] = I[34], I[34] = I[35], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], \
1807  I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[45] = I[46], I[46] = I[47], I[47] = I[48], \
1808  I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[54] = I[55], I[55] = I[56], \
1809  I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[63] = I[64], \
1810  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], \
1811  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], \
1812  I[79] = I[80], \
1813  _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
1814 
1815 #define cimg_for_in9x9(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1816  cimg_for_in9((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1817  _p4##x = x-4<0?0:x-4, \
1818  _p3##x = x-3<0?0:x-3, \
1819  _p2##x = x-2<0?0:x-2, \
1820  _p1##x = x-1<0?0:x-1, \
1821  _n1##x = x+1>=(img).width()?(img).width()-1:x+1, \
1822  _n2##x = x+2>=(img).width()?(img).width()-1:x+2, \
1823  _n3##x = x+3>=(img).width()?(img).width()-1:x+3, \
1824  _n4##x = (int)( \
1825  (I[0] = (T)(img)(_p4##x,_p4##y,z,c)), \
1826  (I[9] = (T)(img)(_p4##x,_p3##y,z,c)), \
1827  (I[18] = (T)(img)(_p4##x,_p2##y,z,c)), \
1828  (I[27] = (T)(img)(_p4##x,_p1##y,z,c)), \
1829  (I[36] = (T)(img)(_p4##x,y,z,c)), \
1830  (I[45] = (T)(img)(_p4##x,_n1##y,z,c)), \
1831  (I[54] = (T)(img)(_p4##x,_n2##y,z,c)), \
1832  (I[63] = (T)(img)(_p4##x,_n3##y,z,c)), \
1833  (I[72] = (T)(img)(_p4##x,_n4##y,z,c)), \
1834  (I[1] = (T)(img)(_p3##x,_p4##y,z,c)), \
1835  (I[10] = (T)(img)(_p3##x,_p3##y,z,c)), \
1836  (I[19] = (T)(img)(_p3##x,_p2##y,z,c)), \
1837  (I[28] = (T)(img)(_p3##x,_p1##y,z,c)), \
1838  (I[37] = (T)(img)(_p3##x,y,z,c)), \
1839  (I[46] = (T)(img)(_p3##x,_n1##y,z,c)), \
1840  (I[55] = (T)(img)(_p3##x,_n2##y,z,c)), \
1841  (I[64] = (T)(img)(_p3##x,_n3##y,z,c)), \
1842  (I[73] = (T)(img)(_p3##x,_n4##y,z,c)), \
1843  (I[2] = (T)(img)(_p2##x,_p4##y,z,c)), \
1844  (I[11] = (T)(img)(_p2##x,_p3##y,z,c)), \
1845  (I[20] = (T)(img)(_p2##x,_p2##y,z,c)), \
1846  (I[29] = (T)(img)(_p2##x,_p1##y,z,c)), \
1847  (I[38] = (T)(img)(_p2##x,y,z,c)), \
1848  (I[47] = (T)(img)(_p2##x,_n1##y,z,c)), \
1849  (I[56] = (T)(img)(_p2##x,_n2##y,z,c)), \
1850  (I[65] = (T)(img)(_p2##x,_n3##y,z,c)), \
1851  (I[74] = (T)(img)(_p2##x,_n4##y,z,c)), \
1852  (I[3] = (T)(img)(_p1##x,_p4##y,z,c)), \
1853  (I[12] = (T)(img)(_p1##x,_p3##y,z,c)), \
1854  (I[21] = (T)(img)(_p1##x,_p2##y,z,c)), \
1855  (I[30] = (T)(img)(_p1##x,_p1##y,z,c)), \
1856  (I[39] = (T)(img)(_p1##x,y,z,c)), \
1857  (I[48] = (T)(img)(_p1##x,_n1##y,z,c)), \
1858  (I[57] = (T)(img)(_p1##x,_n2##y,z,c)), \
1859  (I[66] = (T)(img)(_p1##x,_n3##y,z,c)), \
1860  (I[75] = (T)(img)(_p1##x,_n4##y,z,c)), \
1861  (I[4] = (T)(img)(x,_p4##y,z,c)), \
1862  (I[13] = (T)(img)(x,_p3##y,z,c)), \
1863  (I[22] = (T)(img)(x,_p2##y,z,c)), \
1864  (I[31] = (T)(img)(x,_p1##y,z,c)), \
1865  (I[40] = (T)(img)(x,y,z,c)), \
1866  (I[49] = (T)(img)(x,_n1##y,z,c)), \
1867  (I[58] = (T)(img)(x,_n2##y,z,c)), \
1868  (I[67] = (T)(img)(x,_n3##y,z,c)), \
1869  (I[76] = (T)(img)(x,_n4##y,z,c)), \
1870  (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \
1871  (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \
1872  (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \
1873  (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \
1874  (I[41] = (T)(img)(_n1##x,y,z,c)), \
1875  (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \
1876  (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \
1877  (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \
1878  (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \
1879  (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \
1880  (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \
1881  (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \
1882  (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \
1883  (I[42] = (T)(img)(_n2##x,y,z,c)), \
1884  (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \
1885  (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \
1886  (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \
1887  (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \
1888  (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \
1889  (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \
1890  (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \
1891  (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \
1892  (I[43] = (T)(img)(_n3##x,y,z,c)), \
1893  (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \
1894  (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \
1895  (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \
1896  (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \
1897  x+4>=(img).width()?(img).width()-1:x+4); \
1898  x<=(int)(x1) && ((_n4##x<(img).width() && ( \
1899  (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \
1900  (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \
1901  (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \
1902  (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \
1903  (I[44] = (T)(img)(_n4##x,y,z,c)), \
1904  (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \
1905  (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \
1906  (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \
1907  (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
1908  _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \
1909  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], \
1910  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], \
1911  I[16] = I[17], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \
1912  I[24] = I[25], I[25] = I[26], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], \
1913  I[32] = I[33], I[33] = I[34], I[34] = I[35], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], \
1914  I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[45] = I[46], I[46] = I[47], I[47] = I[48], \
1915  I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[54] = I[55], I[55] = I[56], \
1916  I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[63] = I[64], \
1917  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], \
1918  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], \
1919  I[79] = I[80], \
1920  _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
1921 
1922 #define cimg_for2x2x2(img,x,y,z,c,I,T) \
1923  cimg_for2((img)._depth,z) cimg_for2((img)._height,y) for (int x = 0, \
1924  _n1##x = (int)( \
1925  (I[0] = (T)(img)(0,y,z,c)), \
1926  (I[2] = (T)(img)(0,_n1##y,z,c)), \
1927  (I[4] = (T)(img)(0,y,_n1##z,c)), \
1928  (I[6] = (T)(img)(0,_n1##y,_n1##z,c)), \
1929  1>=(img)._width?(img).width()-1:1); \
1930  (_n1##x<(img).width() && ( \
1931  (I[1] = (T)(img)(_n1##x,y,z,c)), \
1932  (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \
1933  (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \
1934  (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
1935  x==--_n1##x; \
1936  I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \
1937  ++x, ++_n1##x)
1938 
1939 #define cimg_for_in2x2x2(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \
1940  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), \
1941  _n1##x = (int)( \
1942  (I[0] = (T)(img)(x,y,z,c)), \
1943  (I[2] = (T)(img)(x,_n1##y,z,c)), \
1944  (I[4] = (T)(img)(x,y,_n1##z,c)), \
1945  (I[6] = (T)(img)(x,_n1##y,_n1##z,c)), \
1946  x+1>=(int)(img)._width?(img).width()-1:x+1); \
1947  x<=(int)(x1) && ((_n1##x<(img).width() && ( \
1948  (I[1] = (T)(img)(_n1##x,y,z,c)), \
1949  (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \
1950  (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \
1951  (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
1952  x==--_n1##x); \
1953  I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \
1954  ++x, ++_n1##x)
1955 
1956 #define cimg_for3x3x3(img,x,y,z,c,I,T) \
1957  cimg_for3((img)._depth,z) cimg_for3((img)._height,y) for (int x = 0, \
1958  _p1##x = 0, \
1959  _n1##x = (int)( \
1960  (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \
1961  (I[3] = I[4] = (T)(img)(0,y,_p1##z,c)), \
1962  (I[6] = I[7] = (T)(img)(0,_n1##y,_p1##z,c)), \
1963  (I[9] = I[10] = (T)(img)(0,_p1##y,z,c)), \
1964  (I[12] = I[13] = (T)(img)(0,y,z,c)), \
1965  (I[15] = I[16] = (T)(img)(0,_n1##y,z,c)), \
1966  (I[18] = I[19] = (T)(img)(0,_p1##y,_n1##z,c)), \
1967  (I[21] = I[22] = (T)(img)(0,y,_n1##z,c)), \
1968  (I[24] = I[25] = (T)(img)(0,_n1##y,_n1##z,c)), \
1969  1>=(img)._width?(img).width()-1:1); \
1970  (_n1##x<(img).width() && ( \
1971  (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \
1972  (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \
1973  (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \
1974  (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \
1975  (I[14] = (T)(img)(_n1##x,y,z,c)), \
1976  (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \
1977  (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \
1978  (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \
1979  (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
1980  x==--_n1##x; \
1981  I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \
1982  I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \
1983  I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \
1984  _p1##x = x++, ++_n1##x)
1985 
1986 #define cimg_for_in3x3x3(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \
1987  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), \
1988  _p1##x = x-1<0?0:x-1, \
1989  _n1##x = (int)( \
1990  (I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \
1991  (I[3] = (T)(img)(_p1##x,y,_p1##z,c)), \
1992  (I[6] = (T)(img)(_p1##x,_n1##y,_p1##z,c)), \
1993  (I[9] = (T)(img)(_p1##x,_p1##y,z,c)), \
1994  (I[12] = (T)(img)(_p1##x,y,z,c)), \
1995  (I[15] = (T)(img)(_p1##x,_n1##y,z,c)), \
1996  (I[18] = (T)(img)(_p1##x,_p1##y,_n1##z,c)), \
1997  (I[21] = (T)(img)(_p1##x,y,_n1##z,c)), \
1998  (I[24] = (T)(img)(_p1##x,_n1##y,_n1##z,c)), \
1999  (I[1] = (T)(img)(x,_p1##y,_p1##z,c)), \
2000  (I[4] = (T)(img)(x,y,_p1##z,c)), \
2001  (I[7] = (T)(img)(x,_n1##y,_p1##z,c)), \
2002  (I[10] = (T)(img)(x,_p1##y,z,c)), \
2003  (I[13] = (T)(img)(x,y,z,c)), \
2004  (I[16] = (T)(img)(x,_n1##y,z,c)), \
2005  (I[19] = (T)(img)(x,_p1##y,_n1##z,c)), \
2006  (I[22] = (T)(img)(x,y,_n1##z,c)), \
2007  (I[25] = (T)(img)(x,_n1##y,_n1##z,c)), \
2008  x+1>=(int)(img)._width?(img).width()-1:x+1); \
2009  x<=(int)(x1) && ((_n1##x<(img).width() && ( \
2010  (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \
2011  (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \
2012  (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \
2013  (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \
2014  (I[14] = (T)(img)(_n1##x,y,z,c)), \
2015  (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \
2016  (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \
2017  (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \
2018  (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
2019  x==--_n1##x); \
2020  I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \
2021  I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \
2022  I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \
2023  _p1##x = x++, ++_n1##x)
2024 
2025 #define cimglist_for(list,l) for (int l = 0; l<(int)(list)._width; ++l)
2026 #define cimglist_for_in(list,l0,l1,l) \
2027  for (int l = (int)(l0)<0?0:(int)(l0), _max##l = (unsigned int)l1<(list)._width?(int)(l1):(int)(list)._width-1; \
2028  l<=_max##l; ++l)
2029 
2030 #define cimglist_apply(list,fn) cimglist_for(list,__##fn) (list)[__##fn].fn
2031 
2032 // Macros used to display error messages when exceptions are thrown.
2033 // You should not use these macros is your own code.
2034 #define _cimgdisplay_instance "[instance(%u,%u,%u,%c%s%c)] CImgDisplay::"
2035 #define cimgdisplay_instance _width,_height,_normalization,_title?'\"':'[',_title?_title:"untitled",_title?'\"':']'
2036 #define _cimg_instance "[instance(%u,%u,%u,%u,%p,%sshared)] CImg<%s>::"
2037 #define cimg_instance _width,_height,_depth,_spectrum,_data,_is_shared?"":"non-",pixel_type()
2038 #define _cimglist_instance "[instance(%u,%u,%p)] CImgList<%s>::"
2039 #define cimglist_instance _width,_allocated_width,_data,pixel_type()
2040 
2041 /*------------------------------------------------
2042  #
2043  #
2044  # Define cimg_library:: namespace
2045  #
2046  #
2047  -------------------------------------------------*/
2049 
2061 
2062  // Declare the four classes of the CImg Library.
2063  template<typename T=float> struct CImg;
2064  template<typename T=float> struct CImgList;
2065  struct CImgDisplay;
2066  struct CImgException;
2067 
2068  // Declare cimg:: namespace.
2069  // This is an uncomplete namespace definition here. It only contains some
2070  // necessary stuffs to ensure a correct declaration order of the classes and functions
2071  // defined afterwards.
2072  namespace cimg {
2073 
2074  // Define ascii sequences for colored terminal output.
2075 #ifdef cimg_use_vt100
2076  const char t_normal[] = { 0x1b, '[', '0', ';', '0', ';', '0', 'm', 0 };
2077  const char t_black[] = { 0x1b, '[', '0', ';', '3', '0', ';', '5', '9', 'm', 0 };
2078  const char t_red[] = { 0x1b, '[', '0', ';', '3', '1', ';', '5', '9', 'm', 0 };
2079  const char t_green[] = { 0x1b, '[', '0', ';', '3', '2', ';', '5', '9', 'm', 0 };
2080  const char t_yellow[] = { 0x1b, '[', '0', ';', '3', '3', ';', '5', '9', 'm', 0 };
2081  const char t_blue[] = { 0x1b, '[', '0', ';', '3', '4', ';', '5', '9', 'm', 0 };
2082  const char t_magenta[] = { 0x1b, '[', '0', ';', '3', '5', ';', '5', '9', 'm', 0 };
2083  const char t_cyan[] = { 0x1b, '[', '0', ';', '3', '6', ';', '5', '9', 'm', 0 };
2084  const char t_white[] = { 0x1b, '[', '0', ';', '3', '7', ';', '5', '9', 'm', 0 };
2085  const char t_bold[] = { 0x1b, '[', '1', 'm', 0 };
2086  const char t_underscore[] = { 0x1b, '[', '4', 'm', 0 };
2087 #else
2088  const char t_normal[] = { 0 };
2089  const char *const t_black = cimg::t_normal,
2090  *const t_red = cimg::t_normal,
2091  *const t_green = cimg::t_normal,
2092  *const t_yellow = cimg::t_normal,
2093  *const t_blue = cimg::t_normal,
2094  *const t_magenta = cimg::t_normal,
2095  *const t_cyan = cimg::t_normal,
2096  *const t_white = cimg::t_normal,
2097  *const t_bold = cimg::t_normal,
2098  *const t_underscore = cimg::t_normal;
2099 #endif
2100 
2101  inline std::FILE* output(std::FILE *file=0);
2102  inline void info();
2103 
2105  template<typename T>
2106  inline void unused(const T&, ...) {}
2107 
2108  // [internal] Lock/unlock a mutex for managing concurrent threads.
2109  // 'lock_mode' can be { 0=unlock | 1=lock | 2=trylock }.
2110  // 'n' can be in [0,31] but mutex range [0,16] is reserved by CImg.
2111  inline int mutex(const unsigned int n, const int lock_mode=1);
2112 
2113  inline unsigned int& _exception_mode(const unsigned int value, const bool is_set) {
2114  static unsigned int mode = cimg_verbosity;
2115  cimg::mutex(0);
2116  if (is_set) mode = value;
2117  cimg::mutex(0,0);
2118  return mode;
2119  }
2120 
2122 
2131  inline unsigned int& exception_mode(const unsigned int mode) {
2132  return _exception_mode(mode,true);
2133  }
2134 
2136 
2139  inline unsigned int& exception_mode() {
2140  return _exception_mode(0,false);
2141  }
2142 
2143  inline int dialog(const char *const title, const char *const msg, const char *const button1_label="OK",
2144  const char *const button2_label=0, const char *const button3_label=0,
2145  const char *const button4_label=0, const char *const button5_label=0,
2146  const char *const button6_label=0, const bool centering=false);
2147 
2148  inline double eval(const char *const expression,
2149  const double x=0, const double y=0, const double z=0, const double c=0);
2150  }
2151 
2152  /*---------------------------------------
2153  #
2154  # Define the CImgException structures
2155  #
2156  --------------------------------------*/
2158 
2219  struct CImgException : public std::exception {
2220 #define _cimg_exception_err(etype,disp_flag) \
2221  std::va_list ap; va_start(ap,format); cimg_vsnprintf(_message,sizeof(_message),format,ap); va_end(ap); \
2222  if (cimg::exception_mode()) { \
2223  std::fprintf(cimg::output(),"\n%s[CImg] *** %s ***%s %s\n",cimg::t_red,etype,cimg::t_normal,_message); \
2224  if (cimg_display && disp_flag && !(cimg::exception_mode()%2)) try { cimg::dialog(etype,_message,"Abort"); } \
2225  catch (CImgException&) {} \
2226  if (cimg::exception_mode()>=3) cimg_library_suffixed::cimg::info(); \
2227  }
2228 
2229  char _message[16384];
2230  CImgException() { *_message = 0; }
2231  CImgException(const char *const format, ...) { _cimg_exception_err("CImgException",true); }
2233  const char *what() const throw() { return _message; }
2234  };
2235 
2236  // The CImgInstanceException class is used to throw an exception related
2237  // to an invalid instance encountered in a library function call.
2239  CImgInstanceException(const char *const format, ...) { _cimg_exception_err("CImgInstanceException",true); }
2240  };
2241 
2242  // The CImgArgumentException class is used to throw an exception related
2243  // to invalid arguments encountered in a library function call.
2245  CImgArgumentException(const char *const format, ...) { _cimg_exception_err("CImgArgumentException",true); }
2246  };
2247 
2248  // The CImgIOException class is used to throw an exception related
2249  // to input/output file problems encountered in a library function call.
2250  struct CImgIOException : public CImgException {
2251  CImgIOException(const char *const format, ...) { _cimg_exception_err("CImgIOException",true); }
2252  };
2253 
2254  // The CImgDisplayException class is used to throw an exception related
2255  // to display problems encountered in a library function call.
2257  CImgDisplayException(const char *const format, ...) { _cimg_exception_err("CImgDisplayException",false); }
2258  };
2259 
2260  // The CImgWarningException class is used to throw an exception for warnings
2261  // encountered in a library function call.
2263  CImgWarningException(const char *const format, ...) { _cimg_exception_err("CImgWarningException",false); }
2264  };
2265 
2266  /*-------------------------------------
2267  #
2268  # Define cimg:: namespace
2269  #
2270  -----------------------------------*/
2272 
2279  namespace cimg {
2280 
2281  // Define traits that will be used to determine the best data type to work in CImg functions.
2282  //
2283  template<typename T> struct type {
2284  static const char* string() {
2285  static const char* s[] = { "unknown", "unknown8", "unknown16", "unknown24",
2286  "unknown32", "unknown40", "unknown48", "unknown56",
2287  "unknown64", "unknown72", "unknown80", "unknown88",
2288  "unknown96", "unknown104", "unknown112", "unknown120",
2289  "unknown128" };
2290  return s[(sizeof(T)<17)?sizeof(T):0];
2291  }
2292  static bool is_float() { return false; }
2293  static bool is_inf(const T) { return false; }
2294  static bool is_nan(const T) { return false; }
2295  static T min() { return (T)-1>0?(T)0:(T)-1<<(8*sizeof(T)-1); }
2296  static T max() { return (T)-1>0?(T)-1:~((T)-1<<(8*sizeof(T)-1)); }
2297  static T inf() { return max(); }
2298  static T cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(T)val; }
2299  static const char* format() { return "%s"; }
2300  static const char* format(const T val) { static const char *const s = "unknown"; cimg::unused(val); return s; }
2301  };
2302 
2303  template<> struct type<bool> {
2304  static const char* string() { static const char *const s = "bool"; return s; }
2305  static bool is_float() { return false; }
2306  static bool is_inf(const bool) { return false; }
2307  static bool is_nan(const bool) { return false; }
2308  static bool min() { return false; }
2309  static bool max() { return true; }
2310  static bool inf() { return max(); }
2311  static bool is_inf() { return false; }
2312  static bool cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(bool)val; }
2313  static const char* format() { return "%s"; }
2314  static const char* format(const bool val) { static const char* s[] = { "false", "true" }; return s[val?1:0]; }
2315  };
2316 
2317  template<> struct type<unsigned char> {
2318  static const char* string() { static const char *const s = "unsigned char"; return s; }
2319  static bool is_float() { return false; }
2320  static bool is_inf(const unsigned char) { return false; }
2321  static bool is_nan(const unsigned char) { return false; }
2322  static unsigned char min() { return 0; }
2323  static unsigned char max() { return (unsigned char)~0U; }
2324  static unsigned char inf() { return max(); }
2325  static unsigned char cut(const double val) {
2326  return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; }
2327  static const char* format() { return "%u"; }
2328  static unsigned int format(const unsigned char val) { return (unsigned int)val; }
2329  };
2330 
2331  template<> struct type<char> {
2332  static const char* string() { static const char *const s = "char"; return s; }
2333  static bool is_float() { return false; }
2334  static bool is_inf(const char) { return false; }
2335  static bool is_nan(const char) { return false; }
2336  static char min() { return (char)(-1L<<(8*sizeof(char)-1)); }
2337  static char max() { return (char)~((char)(-1L<<(8*sizeof(char)-1))); }
2338  static char inf() { return max(); }
2339  static char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(char)val; }
2340  static const char* format() { return "%d"; }
2341  static int format(const char val) { return (int)val; }
2342  };
2343 
2344  template<> struct type<signed char> {
2345  static const char* string() { static const char *const s = "signed char"; return s; }
2346  static bool is_float() { return false; }
2347  static bool is_inf(const signed char) { return false; }
2348  static bool is_nan(const signed char) { return false; }
2349  static signed char min() { return (signed char)(-1L<<(8*sizeof(signed char)-1)); }
2350  static signed char max() { return ~((signed char)(-1L<<(8*sizeof(signed char)-1))); }
2351  static signed char inf() { return max(); }
2352  static signed char cut(const double val) {
2353  return val<(double)min()?min():val>(double)max()?max():(signed char)val; }
2354  static const char* format() { return "%d"; }
2355  static unsigned int format(const signed char val) { return (int)val; }
2356  };
2357 
2358  template<> struct type<unsigned short> {
2359  static const char* string() { static const char *const s = "unsigned short"; return s; }
2360  static bool is_float() { return false; }
2361  static bool is_inf(const unsigned short) { return false; }
2362  static bool is_nan(const unsigned short) { return false; }
2363  static unsigned short min() { return 0; }
2364  static unsigned short max() { return (unsigned short)~0U; }
2365  static unsigned short inf() { return max(); }
2366  static unsigned short cut(const double val) {
2367  return val<(double)min()?min():val>(double)max()?max():(unsigned short)val; }
2368  static const char* format() { return "%u"; }
2369  static unsigned int format(const unsigned short val) { return (unsigned int)val; }
2370  };
2371 
2372  template<> struct type<short> {
2373  static const char* string() { static const char *const s = "short"; return s; }
2374  static bool is_float() { return false; }
2375  static bool is_inf(const short) { return false; }
2376  static bool is_nan(const short) { return false; }
2377  static short min() { return (short)(-1L<<(8*sizeof(short)-1)); }
2378  static short max() { return ~((short)(-1L<<(8*sizeof(short)-1))); }
2379  static short inf() { return max(); }
2380  static short cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(short)val; }
2381  static const char* format() { return "%d"; }
2382  static int format(const short val) { return (int)val; }
2383  };
2384 
2385  template<> struct type<unsigned int> {
2386  static const char* string() { static const char *const s = "unsigned int"; return s; }
2387  static bool is_float() { return false; }
2388  static bool is_inf(const unsigned int) { return false; }
2389  static bool is_nan(const unsigned int) { return false; }
2390  static unsigned int min() { return 0; }
2391  static unsigned int max() { return (unsigned int)~0U; }
2392  static unsigned int inf() { return max(); }
2393  static unsigned int cut(const double val) {
2394  return val<(double)min()?min():val>(double)max()?max():(unsigned int)val; }
2395  static const char* format() { return "%u"; }
2396  static unsigned int format(const unsigned int val) { return val; }
2397  };
2398 
2399  template<> struct type<int> {
2400  static const char* string() { static const char *const s = "int"; return s; }
2401  static bool is_float() { return false; }
2402  static bool is_inf(const int) { return false; }
2403  static bool is_nan(const int) { return false; }
2404  static int min() { return (int)(-1L<<(8*sizeof(int)-1)); }
2405  static int max() { return ~((int)(-1L<<(8*sizeof(int)-1))); }
2406  static int inf() { return max(); }
2407  static int cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(int)val; }
2408  static const char* format() { return "%d"; }
2409  static int format(const int val) { return val; }
2410  };
2411 
2412  template<> struct type<unsigned long> {
2413  static const char* string() { static const char *const s = "unsigned long"; return s; }
2414  static bool is_float() { return false; }
2415  static bool is_inf(const unsigned long) { return false; }
2416  static bool is_nan(const unsigned long) { return false; }
2417  static unsigned long min() { return 0; }
2418  static unsigned long max() { return (unsigned long)~0UL; }
2419  static unsigned long inf() { return max(); }
2420  static unsigned long cut(const double val) {
2421  return val<(double)min()?min():val>(double)max()?max():(unsigned long)val; }
2422  static const char* format() { return "%lu"; }
2423  static unsigned long format(const unsigned long val) { return val; }
2424  };
2425 
2426  template<> struct type<long> {
2427  static const char* string() { static const char *const s = "long"; return s; }
2428  static bool is_float() { return false; }
2429  static bool is_inf(const long) { return false; }
2430  static bool is_nan(const long) { return false; }
2431  static long min() { return (long)(-1L<<(8*sizeof(long)-1)); }
2432  static long max() { return ~((long)(-1L<<(8*sizeof(long)-1))); }
2433  static long inf() { return max(); }
2434  static long cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(long)val; }
2435  static const char* format() { return "%ld"; }
2436  static long format(const long val) { return val; }
2437  };
2438 
2439  template<> struct type<double> {
2440  static const char* string() { static const char *const s = "double"; return s; }
2441  static bool is_float() { return true; }
2442  static bool is_inf(const double val) {
2443 #ifdef isinf
2444  return (bool)isinf(val);
2445 #else
2446  return !is_nan(val) && (val<cimg::type<double>::min() || val>cimg::type<double>::max());
2447 #endif
2448  }
2449  static bool is_nan(const double val) {
2450 #ifdef isnan
2451  return (bool)isnan(val);
2452 #else
2453  return !(val==val);
2454 #endif
2455  }
2456  static double min() { return -1.7E308; }
2457  static double max() { return 1.7E308; }
2458  static double inf() { return max()*max(); }
2459  static double nan() { static const double val_nan = -std::sqrt(-1.0); return val_nan; }
2460  static double cut(const double val) { return val<min()?min():val>max()?max():val; }
2461  static const char* format() { return "%.16g"; }
2462  static double format(const double val) { return val; }
2463  };
2464 
2465  template<> struct type<float> {
2466  static const char* string() { static const char *const s = "float"; return s; }
2467  static bool is_float() { return true; }
2468  static bool is_inf(const float val) {
2469 #ifdef isinf
2470  return (bool)isinf(val);
2471 #else
2472  return !is_nan(val) && (val<cimg::type<float>::min() || val>cimg::type<float>::max());
2473 #endif
2474  }
2475  static bool is_nan(const float val) {
2476 #ifdef isnan
2477  return (bool)isnan(val);
2478 #else
2479  return !(val==val);
2480 #endif
2481  }
2482  static float min() { return -3.4E38f; }
2483  static float max() { return 3.4E38f; }
2484  static float inf() { return (float)cimg::type<double>::inf(); }
2485  static float nan() { return (float)cimg::type<double>::nan(); }
2486  static float cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(float)val; }
2487  static const char* format() { return "%.16g"; }
2488  static double format(const float val) { return (double)val; }
2489  };
2490 
2491  template<typename T, typename t> struct superset { typedef T type; };
2492  template<> struct superset<bool,unsigned char> { typedef unsigned char type; };
2493  template<> struct superset<bool,char> { typedef char type; };
2494  template<> struct superset<bool,signed char> { typedef signed char type; };
2495  template<> struct superset<bool,unsigned short> { typedef unsigned short type; };
2496  template<> struct superset<bool,short> { typedef short type; };
2497  template<> struct superset<bool,unsigned int> { typedef unsigned int type; };
2498  template<> struct superset<bool,int> { typedef int type; };
2499  template<> struct superset<bool,unsigned long> { typedef unsigned long type; };
2500  template<> struct superset<bool,long> { typedef long type; };
2501  template<> struct superset<bool,float> { typedef float type; };
2502  template<> struct superset<bool,double> { typedef double type; };
2503  template<> struct superset<unsigned char,char> { typedef short type; };
2504  template<> struct superset<unsigned char,signed char> { typedef short type; };
2505  template<> struct superset<unsigned char,unsigned short> { typedef unsigned short type; };
2506  template<> struct superset<unsigned char,short> { typedef short type; };
2507  template<> struct superset<unsigned char,unsigned int> { typedef unsigned int type; };
2508  template<> struct superset<unsigned char,int> { typedef int type; };
2509  template<> struct superset<unsigned char,unsigned long> { typedef unsigned long type; };
2510  template<> struct superset<unsigned char,long> { typedef long type; };
2511  template<> struct superset<unsigned char,float> { typedef float type; };
2512  template<> struct superset<unsigned char,double> { typedef double type; };
2513  template<> struct superset<signed char,unsigned char> { typedef short type; };
2514  template<> struct superset<signed char,char> { typedef short type; };
2515  template<> struct superset<signed char,unsigned short> { typedef int type; };
2516  template<> struct superset<signed char,short> { typedef short type; };
2517  template<> struct superset<signed char,unsigned int> { typedef long type; };
2518  template<> struct superset<signed char,int> { typedef int type; };
2519  template<> struct superset<signed char,unsigned long> { typedef long type; };
2520  template<> struct superset<signed char,long> { typedef long type; };
2521  template<> struct superset<signed char,float> { typedef float type; };
2522  template<> struct superset<signed char,double> { typedef double type; };
2523  template<> struct superset<char,unsigned char> { typedef short type; };
2524  template<> struct superset<char,signed char> { typedef short type; };
2525  template<> struct superset<char,unsigned short> { typedef int type; };
2526  template<> struct superset<char,short> { typedef short type; };
2527  template<> struct superset<char,unsigned int> { typedef long type; };
2528  template<> struct superset<char,int> { typedef int type; };
2529  template<> struct superset<char,unsigned long> { typedef long type; };
2530  template<> struct superset<char,long> { typedef long type; };
2531  template<> struct superset<char,float> { typedef float type; };
2532  template<> struct superset<char,double> { typedef double type; };
2533  template<> struct superset<unsigned short,char> { typedef int type; };
2534  template<> struct superset<unsigned short,signed char> { typedef int type; };
2535  template<> struct superset<unsigned short,short> { typedef int type; };
2536  template<> struct superset<unsigned short,unsigned int> { typedef unsigned int type; };
2537  template<> struct superset<unsigned short,int> { typedef int type; };
2538  template<> struct superset<unsigned short,unsigned long> { typedef unsigned long type; };
2539  template<> struct superset<unsigned short,long> { typedef long type; };
2540  template<> struct superset<unsigned short,float> { typedef float type; };
2541  template<> struct superset<unsigned short,double> { typedef double type; };
2542  template<> struct superset<short,unsigned short> { typedef int type; };
2543  template<> struct superset<short,unsigned int> { typedef long type; };
2544  template<> struct superset<short,int> { typedef int type; };
2545  template<> struct superset<short,unsigned long> { typedef long type; };
2546  template<> struct superset<short,long> { typedef long type; };
2547  template<> struct superset<short,float> { typedef float type; };
2548  template<> struct superset<short,double> { typedef double type; };
2549  template<> struct superset<unsigned int,char> { typedef long type; };
2550  template<> struct superset<unsigned int,signed char> { typedef long type; };
2551  template<> struct superset<unsigned int,short> { typedef long type; };
2552  template<> struct superset<unsigned int,int> { typedef long type; };
2553  template<> struct superset<unsigned int,unsigned long> { typedef unsigned long type; };
2554  template<> struct superset<unsigned int,long> { typedef long type; };
2555  template<> struct superset<unsigned int,float> { typedef float type; };
2556  template<> struct superset<unsigned int,double> { typedef double type; };
2557  template<> struct superset<int,unsigned int> { typedef long type; };
2558  template<> struct superset<int,unsigned long> { typedef long type; };
2559  template<> struct superset<int,long> { typedef long type; };
2560  template<> struct superset<int,float> { typedef float type; };
2561  template<> struct superset<int,double> { typedef double type; };
2562  template<> struct superset<unsigned long,char> { typedef long type; };
2563  template<> struct superset<unsigned long,signed char> { typedef long type; };
2564  template<> struct superset<unsigned long,short> { typedef long type; };
2565  template<> struct superset<unsigned long,int> { typedef long type; };
2566  template<> struct superset<unsigned long,long> { typedef long type; };
2567  template<> struct superset<unsigned long,float> { typedef double type; };
2568  template<> struct superset<unsigned long,double> { typedef double type; };
2569  template<> struct superset<long,float> { typedef double type; };
2570  template<> struct superset<long,double> { typedef double type; };
2571  template<> struct superset<float,double> { typedef double type; };
2572 
2573  template<typename t1, typename t2, typename t3> struct superset2 {
2574  typedef typename superset<t1, typename superset<t2,t3>::type>::type type;
2575  };
2576 
2577  template<typename t1, typename t2, typename t3, typename t4> struct superset3 {
2578  typedef typename superset<t1, typename superset2<t2,t3,t4>::type>::type type;
2579  };
2580 
2581  template<typename t1, typename t2> struct last { typedef t2 type; };
2582 
2583 #define _cimg_Tt typename cimg::superset<T,t>::type
2584 #define _cimg_Tfloat typename cimg::superset<T,float>::type
2585 #define _cimg_Ttfloat typename cimg::superset2<T,t,float>::type
2586 #define _cimg_Ttdouble typename cimg::superset2<T,t,double>::type
2587 
2588  // Define variables used internally by CImg.
2589 #if cimg_display==1
2590  struct X11_info {
2591  volatile unsigned int nb_wins;
2592  pthread_t* events_thread;
2593  pthread_cond_t wait_event;
2594  pthread_mutex_t wait_event_mutex;
2595  CImgDisplay* wins[1024];
2596  Display* display;
2597  unsigned int nb_bits;
2598  bool is_blue_first;
2599  bool is_shm_enabled;
2600  bool byte_order;
2601 #ifdef cimg_use_xrandr
2602  XRRScreenSize *resolutions;
2603  Rotation curr_rotation;
2604  unsigned int curr_resolution;
2605  unsigned int nb_resolutions;
2606 #endif
2607  X11_info():nb_wins(0),events_thread(0),display(0),
2608  nb_bits(0),is_blue_first(false),is_shm_enabled(false),byte_order(false) {
2609  XInitThreads();
2610  pthread_mutex_init(&wait_event_mutex,0);
2611  pthread_cond_init(&wait_event,0);
2612 #ifdef cimg_use_xrandr
2613  resolutions = 0;
2614  curr_rotation = 0;
2615  curr_resolution = nb_resolutions = 0;
2616 #endif
2617  }
2618 
2619  ~X11_info() {
2620  if (events_thread) {
2621  pthread_cancel(*events_thread);
2622  delete events_thread;
2623  }
2624  if (display) {} // XCloseDisplay(display);
2625  pthread_cond_destroy(&wait_event);
2626  pthread_mutex_unlock(&wait_event_mutex);
2627  pthread_mutex_destroy(&wait_event_mutex);
2628  }
2629  };
2630 #if defined(cimg_module)
2631  X11_info& X11_attr();
2632 #elif defined(cimg_main)
2633  X11_info& X11_attr() { static X11_info val; return val; }
2634 #else
2635  inline X11_info& X11_attr() { static X11_info val; return val; }
2636 #endif
2637 
2638 #elif cimg_display==2
2639  struct Win32_info {
2640  HANDLE wait_event;
2641  Win32_info() { wait_event = CreateEvent(0,FALSE,FALSE,0); }
2642  };
2643 #if defined(cimg_module)
2644  Win32_info& Win32_attr();
2645 #elif defined(cimg_main)
2646  Win32_info& Win32_attr() { static Win32_info val; return val; }
2647 #else
2648  inline Win32_info& Win32_attr() { static Win32_info val; return val; }
2649 #endif
2650 #endif
2651 
2652  struct Mutex_info {
2653 #if cimg_OS==2
2654  HANDLE mutex[32];
2655  Mutex_info() { for (unsigned int i = 0; i<32; ++i) mutex[i] = CreateMutex(0,FALSE,0); }
2656  void lock(const unsigned int n) { WaitForSingleObject(mutex[n],INFINITE); }
2657  void unlock(const unsigned int n) { ReleaseMutex(mutex[n]); }
2658  int trylock(const unsigned int) { return 0; }
2659 #elif defined(_PTHREAD_H)
2660  pthread_mutex_t mutex[32];
2661  Mutex_info() { for (unsigned int i = 0; i<32; ++i) pthread_mutex_init(&mutex[i],0); }
2662  void lock(const unsigned int n) { pthread_mutex_lock(&mutex[n]); }
2663  void unlock(const unsigned int n) { pthread_mutex_unlock(&mutex[n]); }
2664  int trylock(const unsigned int n) { return pthread_mutex_trylock(&mutex[n]); }
2665 #else
2666  Mutex_info() {}
2667  void lock(const unsigned int) {}
2668  void unlock(const unsigned int) {}
2669  int trylock(const unsigned int) { return 0; }
2670 #endif
2671  };
2672 #if defined(cimg_module)
2673  Mutex_info& Mutex_attr();
2674 #elif defined(cimg_main)
2675  Mutex_info& Mutex_attr() { static Mutex_info val; return val; }
2676 #else
2677  inline Mutex_info& Mutex_attr() { static Mutex_info val; return val; }
2678 #endif
2679 
2680 #if defined(cimg_use_magick)
2681  static struct Magick_info {
2682  Magick_info() {
2683  Magick::InitializeMagick("");
2684  }
2685  } _Magick_info;
2686 #endif
2687 
2688 #if cimg_display==1
2689  // Define keycodes for X11-based graphical systems.
2690  const unsigned int keyESC = XK_Escape;
2691  const unsigned int keyF1 = XK_F1;
2692  const unsigned int keyF2 = XK_F2;
2693  const unsigned int keyF3 = XK_F3;
2694  const unsigned int keyF4 = XK_F4;
2695  const unsigned int keyF5 = XK_F5;
2696  const unsigned int keyF6 = XK_F6;
2697  const unsigned int keyF7 = XK_F7;
2698  const unsigned int keyF8 = XK_F8;
2699  const unsigned int keyF9 = XK_F9;
2700  const unsigned int keyF10 = XK_F10;
2701  const unsigned int keyF11 = XK_F11;
2702  const unsigned int keyF12 = XK_F12;
2703  const unsigned int keyPAUSE = XK_Pause;
2704  const unsigned int key1 = XK_1;
2705  const unsigned int key2 = XK_2;
2706  const unsigned int key3 = XK_3;
2707  const unsigned int key4 = XK_4;
2708  const unsigned int key5 = XK_5;
2709  const unsigned int key6 = XK_6;
2710  const unsigned int key7 = XK_7;
2711  const unsigned int key8 = XK_8;
2712  const unsigned int key9 = XK_9;
2713  const unsigned int key0 = XK_0;
2714  const unsigned int keyBACKSPACE = XK_BackSpace;
2715  const unsigned int keyINSERT = XK_Insert;
2716  const unsigned int keyHOME = XK_Home;
2717  const unsigned int keyPAGEUP = XK_Page_Up;
2718  const unsigned int keyTAB = XK_Tab;
2719  const unsigned int keyQ = XK_q;
2720  const unsigned int keyW = XK_w;
2721  const unsigned int keyE = XK_e;
2722  const unsigned int keyR = XK_r;
2723  const unsigned int keyT = XK_t;
2724  const unsigned int keyY = XK_y;
2725  const unsigned int keyU = XK_u;
2726  const unsigned int keyI = XK_i;
2727  const unsigned int keyO = XK_o;
2728  const unsigned int keyP = XK_p;
2729  const unsigned int keyDELETE = XK_Delete;
2730  const unsigned int keyEND = XK_End;
2731  const unsigned int keyPAGEDOWN = XK_Page_Down;
2732  const unsigned int keyCAPSLOCK = XK_Caps_Lock;
2733  const unsigned int keyA = XK_a;
2734  const unsigned int keyS = XK_s;
2735  const unsigned int keyD = XK_d;
2736  const unsigned int keyF = XK_f;
2737  const unsigned int keyG = XK_g;
2738  const unsigned int keyH = XK_h;
2739  const unsigned int keyJ = XK_j;
2740  const unsigned int keyK = XK_k;
2741  const unsigned int keyL = XK_l;
2742  const unsigned int keyENTER = XK_Return;
2743  const unsigned int keySHIFTLEFT = XK_Shift_L;
2744  const unsigned int keyZ = XK_z;
2745  const unsigned int keyX = XK_x;
2746  const unsigned int keyC = XK_c;
2747  const unsigned int keyV = XK_v;
2748  const unsigned int keyB = XK_b;
2749  const unsigned int keyN = XK_n;
2750  const unsigned int keyM = XK_m;
2751  const unsigned int keySHIFTRIGHT = XK_Shift_R;
2752  const unsigned int keyARROWUP = XK_Up;
2753  const unsigned int keyCTRLLEFT = XK_Control_L;
2754  const unsigned int keyAPPLEFT = XK_Super_L;
2755  const unsigned int keyALT = XK_Alt_L;
2756  const unsigned int keySPACE = XK_space;
2757  const unsigned int keyALTGR = XK_Alt_R;
2758  const unsigned int keyAPPRIGHT = XK_Super_R;
2759  const unsigned int keyMENU = XK_Menu;
2760  const unsigned int keyCTRLRIGHT = XK_Control_R;
2761  const unsigned int keyARROWLEFT = XK_Left;
2762  const unsigned int keyARROWDOWN = XK_Down;
2763  const unsigned int keyARROWRIGHT = XK_Right;
2764  const unsigned int keyPAD0 = XK_KP_0;
2765  const unsigned int keyPAD1 = XK_KP_1;
2766  const unsigned int keyPAD2 = XK_KP_2;
2767  const unsigned int keyPAD3 = XK_KP_3;
2768  const unsigned int keyPAD4 = XK_KP_4;
2769  const unsigned int keyPAD5 = XK_KP_5;
2770  const unsigned int keyPAD6 = XK_KP_6;
2771  const unsigned int keyPAD7 = XK_KP_7;
2772  const unsigned int keyPAD8 = XK_KP_8;
2773  const unsigned int keyPAD9 = XK_KP_9;
2774  const unsigned int keyPADADD = XK_KP_Add;
2775  const unsigned int keyPADSUB = XK_KP_Subtract;
2776  const unsigned int keyPADMUL = XK_KP_Multiply;
2777  const unsigned int keyPADDIV = XK_KP_Divide;
2778 
2779 #elif cimg_display==2
2780  // Define keycodes for Windows.
2781  const unsigned int keyESC = VK_ESCAPE;
2782  const unsigned int keyF1 = VK_F1;
2783  const unsigned int keyF2 = VK_F2;
2784  const unsigned int keyF3 = VK_F3;
2785  const unsigned int keyF4 = VK_F4;
2786  const unsigned int keyF5 = VK_F5;
2787  const unsigned int keyF6 = VK_F6;
2788  const unsigned int keyF7 = VK_F7;
2789  const unsigned int keyF8 = VK_F8;
2790  const unsigned int keyF9 = VK_F9;
2791  const unsigned int keyF10 = VK_F10;
2792  const unsigned int keyF11 = VK_F11;
2793  const unsigned int keyF12 = VK_F12;
2794  const unsigned int keyPAUSE = VK_PAUSE;
2795  const unsigned int key1 = '1';
2796  const unsigned int key2 = '2';
2797  const unsigned int key3 = '3';
2798  const unsigned int key4 = '4';
2799  const unsigned int key5 = '5';
2800  const unsigned int key6 = '6';
2801  const unsigned int key7 = '7';
2802  const unsigned int key8 = '8';
2803  const unsigned int key9 = '9';
2804  const unsigned int key0 = '0';
2805  const unsigned int keyBACKSPACE = VK_BACK;
2806  const unsigned int keyINSERT = VK_INSERT;
2807  const unsigned int keyHOME = VK_HOME;
2808  const unsigned int keyPAGEUP = VK_PRIOR;
2809  const unsigned int keyTAB = VK_TAB;
2810  const unsigned int keyQ = 'Q';
2811  const unsigned int keyW = 'W';
2812  const unsigned int keyE = 'E';
2813  const unsigned int keyR = 'R';
2814  const unsigned int keyT = 'T';
2815  const unsigned int keyY = 'Y';
2816  const unsigned int keyU = 'U';
2817  const unsigned int keyI = 'I';
2818  const unsigned int keyO = 'O';
2819  const unsigned int keyP = 'P';
2820  const unsigned int keyDELETE = VK_DELETE;
2821  const unsigned int keyEND = VK_END;
2822  const unsigned int keyPAGEDOWN = VK_NEXT;
2823  const unsigned int keyCAPSLOCK = VK_CAPITAL;
2824  const unsigned int keyA = 'A';
2825  const unsigned int keyS = 'S';
2826  const unsigned int keyD = 'D';
2827  const unsigned int keyF = 'F';
2828  const unsigned int keyG = 'G';
2829  const unsigned int keyH = 'H';
2830  const unsigned int keyJ = 'J';
2831  const unsigned int keyK = 'K';
2832  const unsigned int keyL = 'L';
2833  const unsigned int keyENTER = VK_RETURN;
2834  const unsigned int keySHIFTLEFT = VK_SHIFT;
2835  const unsigned int keyZ = 'Z';
2836  const unsigned int keyX = 'X';
2837  const unsigned int keyC = 'C';
2838  const unsigned int keyV = 'V';
2839  const unsigned int keyB = 'B';
2840  const unsigned int keyN = 'N';
2841  const unsigned int keyM = 'M';
2842  const unsigned int keySHIFTRIGHT = VK_SHIFT;
2843  const unsigned int keyARROWUP = VK_UP;
2844  const unsigned int keyCTRLLEFT = VK_CONTROL;
2845  const unsigned int keyAPPLEFT = VK_LWIN;
2846  const unsigned int keyALT = VK_LMENU;
2847  const unsigned int keySPACE = VK_SPACE;
2848  const unsigned int keyALTGR = VK_CONTROL;
2849  const unsigned int keyAPPRIGHT = VK_RWIN;
2850  const unsigned int keyMENU = VK_APPS;
2851  const unsigned int keyCTRLRIGHT = VK_CONTROL;
2852  const unsigned int keyARROWLEFT = VK_LEFT;
2853  const unsigned int keyARROWDOWN = VK_DOWN;
2854  const unsigned int keyARROWRIGHT = VK_RIGHT;
2855  const unsigned int keyPAD0 = 0x60;
2856  const unsigned int keyPAD1 = 0x61;
2857  const unsigned int keyPAD2 = 0x62;
2858  const unsigned int keyPAD3 = 0x63;
2859  const unsigned int keyPAD4 = 0x64;
2860  const unsigned int keyPAD5 = 0x65;
2861  const unsigned int keyPAD6 = 0x66;
2862  const unsigned int keyPAD7 = 0x67;
2863  const unsigned int keyPAD8 = 0x68;
2864  const unsigned int keyPAD9 = 0x69;
2865  const unsigned int keyPADADD = VK_ADD;
2866  const unsigned int keyPADSUB = VK_SUBTRACT;
2867  const unsigned int keyPADMUL = VK_MULTIPLY;
2868  const unsigned int keyPADDIV = VK_DIVIDE;
2869 
2870 #else
2871  // Define random keycodes when no display is available.
2872  // (should rarely be used then!).
2873  const unsigned int keyESC = 1U;
2874  const unsigned int keyF1 = 2U;
2875  const unsigned int keyF2 = 3U;
2876  const unsigned int keyF3 = 4U;
2877  const unsigned int keyF4 = 5U;
2878  const unsigned int keyF5 = 6U;
2879  const unsigned int keyF6 = 7U;
2880  const unsigned int keyF7 = 8U;
2881  const unsigned int keyF8 = 9U;
2882  const unsigned int keyF9 = 10U;
2883  const unsigned int keyF10 = 11U;
2884  const unsigned int keyF11 = 12U;
2885  const unsigned int keyF12 = 13U;
2886  const unsigned int keyPAUSE = 14U;
2887  const unsigned int key1 = 15U;
2888  const unsigned int key2 = 16U;
2889  const unsigned int key3 = 17U;
2890  const unsigned int key4 = 18U;
2891  const unsigned int key5 = 19U;
2892  const unsigned int key6 = 20U;
2893  const unsigned int key7 = 21U;
2894  const unsigned int key8 = 22U;
2895  const unsigned int key9 = 23U;
2896  const unsigned int key0 = 24U;
2897  const unsigned int keyBACKSPACE = 25U;
2898  const unsigned int keyINSERT = 26U;
2899  const unsigned int keyHOME = 27U;
2900  const unsigned int keyPAGEUP = 28U;
2901  const unsigned int keyTAB = 29U;
2902  const unsigned int keyQ = 30U;
2903  const unsigned int keyW = 31U;
2904  const unsigned int keyE = 32U;
2905  const unsigned int keyR = 33U;
2906  const unsigned int keyT = 34U;
2907  const unsigned int keyY = 35U;
2908  const unsigned int keyU = 36U;
2909  const unsigned int keyI = 37U;
2910  const unsigned int keyO = 38U;
2911  const unsigned int keyP = 39U;
2912  const unsigned int keyDELETE = 40U;
2913  const unsigned int keyEND = 41U;
2914  const unsigned int keyPAGEDOWN = 42U;
2915  const unsigned int keyCAPSLOCK = 43U;
2916  const unsigned int keyA = 44U;
2917  const unsigned int keyS = 45U;
2918  const unsigned int keyD = 46U;
2919  const unsigned int keyF = 47U;
2920  const unsigned int keyG = 48U;
2921  const unsigned int keyH = 49U;
2922  const unsigned int keyJ = 50U;
2923  const unsigned int keyK = 51U;
2924  const unsigned int keyL = 52U;
2925  const unsigned int keyENTER = 53U;
2926  const unsigned int keySHIFTLEFT = 54U;
2927  const unsigned int keyZ = 55U;
2928  const unsigned int keyX = 56U;
2929  const unsigned int keyC = 57U;
2930  const unsigned int keyV = 58U;
2931  const unsigned int keyB = 59U;
2932  const unsigned int keyN = 60U;
2933  const unsigned int keyM = 61U;
2934  const unsigned int keySHIFTRIGHT = 62U;
2935  const unsigned int keyARROWUP = 63U;
2936  const unsigned int keyCTRLLEFT = 64U;
2937  const unsigned int keyAPPLEFT = 65U;
2938  const unsigned int keyALT = 66U;
2939  const unsigned int keySPACE = 67U;
2940  const unsigned int keyALTGR = 68U;
2941  const unsigned int keyAPPRIGHT = 69U;
2942  const unsigned int keyMENU = 70U;
2943  const unsigned int keyCTRLRIGHT = 71U;
2944  const unsigned int keyARROWLEFT = 72U;
2945  const unsigned int keyARROWDOWN = 73U;
2946  const unsigned int keyARROWRIGHT = 74U;
2947  const unsigned int keyPAD0 = 75U;
2948  const unsigned int keyPAD1 = 76U;
2949  const unsigned int keyPAD2 = 77U;
2950  const unsigned int keyPAD3 = 78U;
2951  const unsigned int keyPAD4 = 79U;
2952  const unsigned int keyPAD5 = 80U;
2953  const unsigned int keyPAD6 = 81U;
2954  const unsigned int keyPAD7 = 82U;
2955  const unsigned int keyPAD8 = 83U;
2956  const unsigned int keyPAD9 = 84U;
2957  const unsigned int keyPADADD = 85U;
2958  const unsigned int keyPADSUB = 86U;
2959  const unsigned int keyPADMUL = 87U;
2960  const unsigned int keyPADDIV = 88U;
2961 #endif
2962 
2963  const double PI = 3.14159265358979323846;
2964 
2965  // Define a 12x13 font (small size).
2966  const char *const data_font12x13 =
2967 " .wjwlwmyuw>wjwkwbwjwkwRxuwmwjwkwmyuwJwjwlx`w Fw mwlwlwuwnwuynwuwmyTwlwkwuwmwuwnwlwkwuwmwuw_wuxl"
2968 "wlwkwuwnwuynwuwTwlwlwtwnwtwnw my Qw +wlw b{ \\w Wx`xTw_w[wbxawSwkw nynwky<x1w `y ,w Xwuw CxlwiwlwmyuwbwuwUwiwlwbwiwrwqw^wuwmxuwnwiwlwmy"
2969 "uwJwiwlw^wnwEymymymymy1w^wkxnxtxnw<| gybwkwuwjwtwowmxswnxnwkxlxkw:wlymxlymykwn{myo{nymy2ykwqwqwm{myozn{o{mzpwrwpwkwkwswowkwqwqxswnyozlyozmzp}pwrwqwqwq"
2970 "wswswsxsxqwqwp}qwlwiwjybw`w[wcw_wkwkwkwkw mw\"wlwiw=wtw`xIw awuwlwm{o{mylwn|pwtwtwoy`w_w_wbwiwkxcwqwpwkznwuwjzpyGzqymyaxlylw_zWxkxaxrwqxrwqyswowkwkwkwk"
2971 "wkwkwk}qyo{o{o{o{owkwkwkwkznxswnymymymymyayuwqwrwpwrwpwrwpwrwqwqwpwkwtwlwkwlwuwnwuynwuwmyTwkwlwuwmwuwnwkwlwuwmwuwkxlwuxmwkwlwuwnwuynwuwTwkwlwuwmwuwlwm"
2972 "wkwtwUwuwuwowswowswowswowsw;wqwtw_ymzp~py>w bwswcwkwuwjwuwozpwtwuwnwtwowkwjwmwuwuwkwIxmxuxowuwmwswowswmxnwjwhwowswowsw0wmwowswuwnwrwowswpwswowkwjwrwqw"
2973 "rwpwkwkwtwnwkxsxqxswowswpwswnwswpwswowrwnwmwrwqwqwqwswswrwswowswjwpwlxjwkxuxLw[wcw_wSwkw mw\"wlwiw=wtwmxlwFw cwswnwuwnwkwjwswo{pwrwpwtwtwpwswby`w`yUwlw"
2974 "twpwqwpwswowlw\\wrwrxuwHwrwfwuwjwlwlwTyuwVwlwtwawswowswowswcwuwmwuwmwuwmwuwmwuwlwkwuwnwswpwkwkwkwkwkwkwkwkwswoxswowswowswowswowswowswowrwpwswpwrwpwrwpw"
2975 "rwpwrwpwswoznwtw Ww (wGwtwtwqwqwqwuwuwuwqwswuwqwqw=wqxtw`{nzp~q{ozowrwnxmwtwow bzawkwuwl}rwuwnwtwuwnwtwowkwjwlyjwIwlwswmwiwkwnwuwnwkwhwnwswowswowkwew"
2976 "ewixnwsytwswuwnwrwpwkwrwpwkwkwkwrwpwkwkwuwmwkxsxqwuwtwpwqwqwswowqwqwswowiwmwrwpwswowtwtwpwuwmwuwjwowkwjwlxsxXynzmymznyozlzoznwkwkwtwnwkzuyrzmynzmzowux"
2977 "myozmwswpwrwowtwtwrwrwpwrwp{mwlwiwHyuwpwtwkwmxlynzoxswmwmwswnwswowtxq|owtwtwpym{p{owswnwuwmwlwkwqwqxuwuxqwrwpwtwtwqwqwowlwuwuwkwmwlwtwowuwuwdwjznwl{nw"
2978 "uwnwkx_wtxtwswtwlwtwWwuytwgyjwmwjwawswoyuwVwlwtwnwtwmwtwnwtwmwuwmwlwuwmwuwmwuwmwuwmwuwmwuwmxuwowkwkwkwkwkwkwkwkwkwrwpwuwtwpwqwqwqwqwqwqwqwqwqwowtwpwsw"
2979 "uwqwrwpwrwpwrwpwrwowuwnwswowuwlymymymymymymyuyqymymymymynwkwkwkwjynzmymymymymykwmzowswowswowswowswpwrwozowrwW}q}qwtwtwqwtwtwqwtwtwA}rwuw_{p~r~r}pwtwow"
2980 "rwnxmwtwow aw_w]wtwpwuwmxuwmybwjwlyjwIwlwswmwiwnynwtwnznzkwmynwswTyp}pylwmwtwtwtwswuwn{owkwrwp{o{owk|pwkwkxlwkwuwuwuwqwuwtwpwqwqwswowqwqwswoykwmwrwpws"
2981 "wowuwuwuwowkwjwnwkwjwDwowswowkwswowswowkwswowswowkwkwuwmwkwswswswswowswowswowswoxlwswowkwswpwrwowtwtwqwtwowrwlwoxkwhxVxuxpwtypwuwjwnwtwnwkwswowtxnxmws"
2982 "wowqwqwtwuxqwtwnwtwtwqwswowswmwm{nwuwlxnwkwqwqwtwtwqwrwpwtwtwqwuyuwpwiwhwnwmwrwnwbwkwuwlwlwswoxuxowlwtw`wuwrwszmwtwo}dwuwtwuw[}qymx`wswoyuwow_ylxlwtwo"
2983 "yuwoyuwoyuwmwlwuwmwuwmwuwmwuwmwuwmwuwmwt{swk{o{o{o{owkwkwkwlztwpwuwtwpwqwqwqwqwqwqwqwqwqwnxowtwtwqwrwpwrwpwrwpwrwnwmwswowuwiwkwkwkwkwkwkwswswkwswowswo"
2984 "wswowswowkwkwkwkwswowswowswowswowswowswowswcwtxowswowswowswowswpwrwowswpwrwWwtwtwqwqwqwuwuwuwqwuwswqwqw>wowuw`}q~q|q}qwrwpwrwowtwnwtwo~ izaw]wtwoykwux"
2985 "qwtwswfwjwmwuwuwn}eyaxlwswmwjwjwpwswjwowswmwmwswnzWy]ypwlwtwtwuwswswowrwpwkwrwpwkwkwsyqwrwpwkwkwuwmwkwuwuwuwqwtwuwpwqwqznwqwqzkynwmwrwowuwnwuwuwuwowkw"
2986 "jwnwkxkwGzowswowkwswo{owkwswowswowkwkxlwkwswswswswowswowswowswowjxmwkwswowtwnwuwuwuwpxmwtwlwlwlwiwlytwewtwtwqwswowtxoznwswnxmwswnwuwmwuwnwswowtwtwqwtw"
2987 "twqwtwnwtwtwqwswowswmwmwswowswmwmwkwqwqwtwtwqwrwowuwuwpwuyuwq~own~own~owbwkwuwmznwswmwbwswawuwrwgwtwhwdwuytwXwJwswnxuw=wtwmwswowtxowswqxmwswowswowswow"
2988 "swowswowswnwtwowkwkwkwkwkwkwkwkwkwrwpwtwuwpwqwqwqwqwqwqwqwqwqwnxowtwtwqwrwpwrwpwrwpwrwnwmwswowtwmznznznznznzn~swk{o{o{o{owkwkwkwkwswowswowswowswowswow"
2989 "swowswo}qwuwuwowswowswowswowswowtwnwswowtwUwuwuwowswowswowswowsw@}qx`}q~pzo{pwrwpwrwowtwnwtwow aw_w_}owuwmwuwtwrwswuwewjwkwiwJwkwswmwkwiwp|kwowswmwmws"
2990 "wkwWym}mypwlwszr{owrwpwkwrwpwkwkwqwqwrwpwkwkwtwnwkwtwtwqwtwuwpwqwqwkwqwqwtwiwnwmwrwowuwnwuwuwuwpwuwlwkwmwjwkwHwswowswowkwswowkwkwswowswowkwkwuwmwkwsws"
2991 "wswswowswowswowswowhwnwkwswowtwnwuwuwuwpxmwtwmwkwlwiwmwtydwtwtwqwswowswowtwnwswowkwswnwuwnwtwnwswowtwtwqwtwtwqwtwnwtwtwqwswowswmwmwswowswnwlwkwqwqxuwu"
2992 "xqwrwnyowqwpwiwhwpwuwuwowrwpwuwuwdwkwuwlwlwswo{owkxuwawtxtwszmwtwiwdwuwtwuwXwJwswmwuwKzmwtwlwtxowrwpwtxrxl{o{o{o{o{o{o{owkwkwkwkwkwkwkwkwkwrwpwtwuwpwq"
2993 "wqwqwqwqwqwqwqwqwowtwpwuwswqwrwpwrwpwrwpwrwnwmznwswowswowswowswowswowswowswowswowkwkwkwkwkwkwkwkwkwswowswowswowswowswowswowswcwuwuwowswowswowswowswowt"
2994 "wnwswowtwTymymymymy=wmw^wuwuwmxlxmyowrwowtwnwtwmxmw bwswIwuwmwuwmwuwtwrxswdwjw]wJwkxuxmwlwlwswlwjwowswmwmwswlwSycyawlwswowrwowswpwswowkwjwrwqwrwpwkwkw"
2995 "swowkwqwqwsxowswpwjwswpwswowrwnwmxtxnwlwswpwswmwlwlwjwkwHwswowswowkwswowswowkwswowswowkwkwtwnwkwswswswswowswowswowswowkwswowkwswnxlwswpwtwmxmwjwlwiwTx"
2996 "uxpwtxowswowtwnwswowkwswnynwtwnwswowtwtwqxuwuxqwtwnwtwtwqwswowswmwlwuwnwswowkwjwswo{pwrwmwmwswnwjwiwnymwtwnycwkwuwlwl{mwmwiw_wrwdwtwVwrw*wswmwuw?wtwlw"
2997 "tzqwrwpwtzswkwswowswowswowswowswowswowswnwswpwkwkwkwkwkwkwkwkwswowsxowswowswowswowswowswowrwpwswpxtxpxtxpxtxpxtxnwmwkwswowswowswowswowswowswowswowtxow"
2998 "kwswowswowswowswowkwkwkwkwswowswowswowswowswowswowswlwnxtwowswowswowswowswnxmwswnx >wlw\\wkx`wnwrwoznwtwmxl| gybw^wtwozmwsxpzuxfxlx]wnw_wlxjyn{o{nykwnz"
2999 "mymwkynymwkwewewjwjwrwswqwp{myozn{owizpwrwpwkwkwrwp{owqwqwsxnyowiyowrwozmwlzmwlwswqxsxnwm}qwjxlwGzozmymznynwjzowswowkwkwswowkwswswswswnynzmzowjymxlznx"
3000 "lwswqwrwnwm{mwlwiwHxuxpzmxlymynwswmwnwrwozmxuxo{pwtxn{pzmykwmyo}p{owkyuynwnwrwmwly`w_w_wbwjzo{pwqwnwmwhw_z>zY}M|nwuw2wqwqwryrwqwqyowqwqwqwqwqwqwqwqwqw"
3001 "qwqwqwr{qyo{o{o{o{owkwkwkwkznwsxnymymymymycwuynznznznzmwmwkwuynznznznznznznyuzrymymymymynwkwkwkwjynwswnymymymymybzmznznznznwlzmw hwHwlwSwTw <w8z ]"
3002 "x tx Zxjwmx RwWw/wgw pw_ynwky=wCwmwaw\\w_wnw 1wIwlz 'wiwuwaw mw Pw swlwjw hw f| pyWx/wgw rxSw/wCwmwaw\\w_wnw 1w AwRx nw Pw txk"
3003 "wlxm";
3004 
3005  // Define a 20x23 font (normal size).
3006  const char *const data_font20x23 =
3007 " 9q\\q^r_rnp`qnq`plp7q\\q^q_qmqbq\\q^q_qmqHqmp_q\\q^r_rnp`qnq7q\\q^q_qmq_q \"r "
3008 " Mq^q^qnq`pnr`qnq`plp6q^q^pmp`qmqaq^q^pmp`qmqIpmq]q^q^qnq`pnr`qnq6q^q^pmp`qmq`q \"plp 'q 5qmq Vq "
3009 " Xq [plp 3qYq_p^rnpLplp8qYq_qNqYq_q4rmpaqYq_q_rmp%qYq^pGq Irc|!pKp]raqjq`p HtNq_qmq\\plqbp_shpdscq[q^q[p [q]s_r`uau]rbv`tcxbua"
3010 "t LsZucrav_udwcxdw`udqiqeq]q]qjreq]sksgrjqbtcv_tcvaud{eqiqgqfqgqjsjqlrjrhrirfzfs`q[sZqMqJqCqNsLq]q]q]q]q .scq]s \\sKt%r [s^raxdxat_qazgqlqlqctJqIqIq"
3011 "LqHsOqiqOtaqmq\\uft nufu`sLs`t\\qKv<r\\rLrepirepitgpeq]r^r^r^r^r^r^{gudxdxdxdxdq]q]q]q]wcrjqbt`t`t`t`tLtlpgqiqeqiqeqiqeqiqgrireq[s_q[q_pnp_pnr`qnq`plp7q["
3012 "q_s`qmqcq[q_s`qmq]pkpbpmr`q[q_s`pmraqmq8q[q^pnp_qnq^qaq\\qnq !pnqd{!pJp^tdunucr _y dvOq_qmq\\plpap_pmpipdudq[p\\p_plplp _q^ubtawcw^rbvavdxcwcw Ou]yerawb"
3013 "xeyexdwbxeqiqeq]q]qkrdq]sksgrjqdxewbxewcwe{eqiqfqhqfqjsjqkqjqfqiqezfs`q[s[sMpJqCqOtLq]q]q]q]q q 1tcq]t ^vaq_w&r \\u_raxdxcxcuczgqlqlqexMsJqJsMq[p^uPq"
3014 "iqdq]uaqmq]qkqcq!qkqguaqmqNpkp\\p]pKtmp:p]plpKpfpfpfpcpipdq]r^r^r^r^r^r^{ixexdxdxdxdq]q]q]q]yerjqdxdxdxdxdxPwnpfqiqeqiqeqiqeqiqfqiqdq\\u_p[p^pnpKqnq_r5p"
3015 "[p^pmp`qmqbp[p^pmp`qmq]tKp[p^pmpLqmq7p[p]pnp_qnq^p`q\\qnq5uauauauaucq`qhq4p]pKr_ueunucr `q \\rkpOq_qmq\\plpctbqmqkqerlpdq\\q\\q_qnpnq\\q%q^qkqcqnqapjrdpjr`"
3016 "sbq]rkp^qcrkrerkq Oplr`sirgtbqkrdripeqjsfq]q]ripeqiqeq]q]qlrcq]sksgskqerjrfqkrdrjrfqkrerjp`q`qiqfqhqeqkskqiqlqdqkq\\qeq]qZq\\qmqNqKqCqOqIq5q]q q 1q`qZq"
3017 " _rlqbtaqjp$q ^qkqatbr^q]rjrewdqhqgqlqlqfrjrOuKqKu8p_rlpOqkqcq]qFpgpcp\"pgpTpkp\\q^p\\p^qLump:p^pjpLpgpepgpbpjpPt`t`t`t`t`qnq_qnqcripeq]q]q]q]q]q]q]q]qj"
3018 "sfskqerjrfrjrfrjrfrjrfrjrRrjrfqiqeqiqeqiqeqiqeqkqcvbrlq`q]q_plp Iq]q_qmqNq]q_qmqKtIq]q_qmq ^q]q^plpKq`q mqkqcqkqcqkqcqkqcqkqdq`qhq5q^qLt`ueunudtasbqip"
3019 "`q`pipcq [qIq_qmq`{gvcqmqkpdq_q\\q\\q]rZq%q_rkraqZq]qaqnqbq]qXqcqiqeqiq1pSpXq`qfrhqnqbqjqdq]qhqfq]q]q]qiqeq]q]qmrbq]qnqmqnqgskqeqhqfqjqdqhqfqjqeqYq`qiq"
3020 "frjreqkskqirnrdrmr]qdq]qZq]qkq)qCqOqIq5q]q q 1q`qZq _qkq_qaq mq ^qkqaqnqar_q]qhqfrnqnreqhqgqlqlqfqhqPwLqLw9p_q_phqdqkqcq]qGplslpiu#pmtlpUpkp\\q_q_r8u"
3021 "mp:p^pjpLpgpepgperipcq^qnq`qnq`qnq`qnq`qnq`qnq`qmqcq]q]q]q]q]q]q]q]q]qhqfskqeqhqfqhqfqhqfqhqfqhqdphpfqirfqiqeqiqeqiqeqiqermrcwcqkq [q 3qZp Oq nqmqm"
3022 "qeqiqeqiqeqiqeqiqeq_piq4q^pLvatd|evdvcqipasaqkqdq [qHq_qmq`{hrnpmpcqmqlpcq_q\\pZp]rZq%q_qiqaqZq]qapmqbq^qWqcqiqeqiqdq]qUsSs[qaqdqhqnqbqjqeq\\qgqgq]q^q\\"
3023 "qiqeq]q]qnraq]qnqmqnqgqnqlqfqfqgqjqeqfqgqjqeqYq`qiqeqjqdqlqmqlqhqnqbqmq]rdq]qZq^pgp=taqns`s`snqatdv_snqcqnsbq]q]qkqcq]qnsmshqns`saqnsasnqcqnr`tbvaqjqe"
3024 "qiqdqkqkqjrkreqiqdw`q`qZq#tnreqkq^qatauaqnsdqiq`raqjqdqiqdpmrcxdqmqmqatbxfyeqiqbqnq`r`q^qfqhrmqmrfqhqgqlqlqgqfqep[pnqnp[p`q`pipbpnqnpNq]taq^qnqnqbqmqb"
3025 "q\\qIqmpkpmqkqkp$qmpkpmqVqmq\\q`q[pLqjqeump:p^pjpLphpdphpapkpbq^qnq`qnq`qnq`qnq`qnq`qnq`qmqdq\\q]q]q]q]q]q]q]q]qgqgqnqlqfqfqhqfqhqfqhqfqhqfqfrjrhqiqnqgqi"
3026 "qeqiqeqiqeqiqdqmqbqkrdqmsbt`t`t`t`t`t`tlsfs_t`t`t`tbq]q]q]q[tbqns`s_s_s_s_s\\q`smpdqjqdqjqdqjqdqjqeqiqdqnscqiq;qlqlqgqgqgqnqmqnqgqjqnqgqgqfq_qjq<{fpjpL"
3027 "vatd|fxeqkqdqipasaqkqdp \\yNqGplqeqmp`qmqmqcrLqZq`qnpnq\\q%q_qiqaqZq^rbqmqbubqms^qaqkqdqiqdq]qXuf{fu_q`qlrnqlqjqlqcqkreq\\qgqgq]q^q\\qiqeq]q]t`q]qnqmqnqg"
3028 "qnqlqfqfqgqkreqfqgqkres[q`qiqeqjqdqlqmqlqhs`s]rcq]qZq#vbwcvbwcwev`wcwcq]q]qlqbq]vnthwcwcwcwcubwcvaqjqdqkqcqkqkqiqkqdqiqdw`q`qZq7smsfxdqlr^qavdvawdqkq_"
3029 "raqjqdpgpeqntdxdqmqmqcwdyfyeqiqcqlq`raq^qfqhqlqlqfqhqgqlqlqgqfqfrZqZraqarkraqLq^vbq^wbqmqbq]tKpmpfpkpjp_plp9plpkplpUs[qaqZpLqjqeump:p^pjpaplp_piqdpiqa"
3030 "plqbq_qlqbqlqbqlqbqlqbqlqbqlqbrmqdq\\q]q]q]q]q]q]q]q]qgqgqnqlqfqfqhqfqhqfqhqfqhqfqerlrgqjqmqgqiqeqiqeqiqeqiqcsaqjqdqnq`vbvbvbvbvbvbvnuivbwcwcwcwcq]q]q]"
3031 "q]wcwcwcwcwcwcwOwcqjqdqjqdqjqdqjqeqiqdwdqiq;pkqkpgpepgpmumpgpjrmpgpepfq_qkq;{hrkpLxdxf|fxepipdqipas`pkpcp ZqHqGplpdt_pmplpmshsMqZqaplplp]q&q^qiqaq[qa"
3032 "t`plqbvcx_q`ucrkr:uc{cucq`qlvlqjqlqcwdq\\qgqgxdvcqjtfyeq]q]s_q]qmsmqgqmqmqfqfqgwdqfqgwcv_q`qiqdqlqbqmqmqmqfr`s]qbq\\q[q#pjqcrlrdqkpcrlrcqkrdq^rlrcrlrdq]"
3033 "q]qmqaq]rlrlqirlrdqkqcrlrerlrcr_qjpbq]qjqdqkqcqlslqhqmqbqkq^q_q`qZq_tjpSqmsmpgrlsdqnsaqmqbqkqdq\\rlrdqlq_raqjqeqgqgrnqnrdqlqcqmqmqcqkqerkq`qaycqlq_rbq^"
3034 "qfqhqlqlqfqhqgqlqlqgqnvnqgrYqYrbqbrirbqLq_rnpmpdwaqmqcydq^qlqLpmpfpkpkq`plpa{RpltkpB{gpXpLqjqdtmpcqHp]plp_plp`pipjpipipmsfplpjphr_qlqbqlqbqlqbqlqbqlqb"
3035 "qlqbqlxkq\\xdxdxdxdq]q]q]q_vjqgqmqmqfqfqhqfqhqfqhqfqhqfqdrnrfqkqlqgqiqeqiqeqiqeqiqcsaqjqdqnq`pjqcpjqcpjqcpjqcpjqcpjqcpjrlrjqkpbqkrdqkrdqkrdqkrdq]q]q]q]"
3036 "qkrdrlrdqkqcqkqcqkqcqkqcqkqOqkqcqjqdqjqdqjqdqjqdqkqcrlrdqkq:pnwnpgpnwnpgplslpgpkrlpgpkqkpfq^qlq6qaqlpMzfzfzfzgqipdqipbqmp`qmqc| fqHqHqlpcuasmplpmpiul"
3037 "qSqZq]p^{+q^qiqaq\\q`ubqlqbpkrdrkrarawcx<tEteq`qlqlqlqjqlqcwdq\\qgqgxdvcqjtfyeq]q]t`q]qmsmqgqmqmqfqfqgvcqfqgv_t`q`qiqdqlqbqmqmqmqgs_q]qaq\\q[q\"vcqjqeq]qj"
3038 "qdqiqdq^qjqcqjqdq]q]qnq`q]qkqkqiqjqeqiqdqjqeqjqcq^s^q]qjqdqkqbqmsmqgqmqbqkq_qas_qYsc{Spkqkphqkrcqntcvcqiqeq\\qjqdqmr`tbqjqeqgqgqmqmqdqlqcqmqmqdqiqfqiqa"
3039 "qaycqlq_qaq^qfqhqlqlqfqhqfqmqmqfqnvnqh}cqc}cqc}cqLq_qmpawbqkqasaq^qkqMpmpfpjsnpaplp`{RplpmqkpB{huatKqjqbrmpcqJt^r]plpctlpjqktlpmpkpltlpjqhq^qlqbqlqbql"
3040 "qbqlqbqlqcrlrcqlxkq\\xdxdxdxdq]q]q]q_vjqgqmqmqfqfqhqfqhqfqhqfqhqfqcteqlqkqgqiqeqiqeqiqeqiqbq`qkrdqmravbvbvbvbvbvbvjqkq]qiqeqiqeqiqeqiqdq]q]q]q^qiqdqjqe"
3041 "qiqeqiqeqiqeqiqeqiqd{hqkpnqdqjqdqjqdqjqdqjqdqkqcqjqdqkq:pnwnpgpnwnpgplslpgplrkpgpkqkpfq^qlq6qaqmqMzg|fxdxfqipdqipbqmqaqmqcp \\wLqK{dt]qmqmqkrmrnrSqZqK"
3042 "{TtKq^qiqaq]r\\rdqkq\\qdqiqaqarkrcsmq<tEtfq_qlqlqlqkqjqdqjqeq\\qgqgq]q^qgqfqiqeq]q]qnraq]qmsmqgqlqnqfqfqgq^qfqgqkq]raq`qiqdqlqbqnqkqnqgt`q^raq\\q[q#wcqjqe"
3043 "q]qjqdydq^qjqcqjqdq]q]s_q]qkqkqiqjqeqiqdqjqeqjqcq]uaq]qjqcqmqaqmpmpmqfs`qmq_ras_qYscpjtRpkqkphqkrcqkreqlrcqiqcr_qjqdqmq_qnqbqjqeqlqlqgqmqmqdqlqcqmqmqd"
3044 "qiqfqiqaqaqiqdqjqaq`q^qfqhqlqlqfqhqfrnqnrfqfqh}cqc}cqc}cqLq_qmp_q^qkq`qMrlqMpmpfpWplpUqRplplqlp=q&qjq`pmp _plp]qkpnpdqhpeqkpnpiq^qjqdqjqdqjqdqjqdqjqdq"
3045 "jqdqkqdq\\q]q]q]q]q]q]q]q]qgqgqlqnqfqfqhqfqhqfqhqfqhqfqbrdqmqjqgqiqeqiqeqiqeqiqbq`wcqlrcwcwcwcwcwcwc~kq]yeyeyeydq]q]q]q^qiqdqjqeqiqeqiqeqiqeqiqeqiqd{hq"
3046 "lpmqdqjqdqjqdqjqdqjqcqmqbqjqcqmq9pkqkpgpepgpmumpgpmrjpgpepfq]pmq:{epmpLzg|evbveqipdqipbqmqaqmpbq [qHqK{cpmq^plqmqkqktRqZqFqOtKq^qiqaq^rZqdy^qdqiqaqaq"
3047 "iq]q:uc{cudq_qlqlqmqjxdqiqfq\\qgqgq]q^qgqfqiqeq]q]qmrbq]qlqlqgqlqnqfqfqgq^qfqgqkr]qaq`qiqcqnqaqnqkqnqhrnq`q_r`q\\q[q$qjqcqjqeq]qjqdydq^qjqcqjqdq]q]s_q]q"
3048 "kqkqiqjqeqiqdqjqeqjqcqZsbq]qjqcqmqaqnqmqnqfs`qmq`r^r`qZr9pkqkphqkrcqjqeqkqcqiqet_qjqcqnq`rnqbqjqeqlqlqgqmqmqdqlqcqmqmqdqiqfqiqaqaqiqdqjqbr`q]qhqgrmqmr"
3049 "fqhqeweqfqgrYqYrdpnqnpdrirdpnqnpNq_qmp_q]qmqcyPrmqMqmpkpmqkvaplpVqRqmpkpmq=q&qjq`pmp(v_plp\\pkpmpdphqepkpmpjq]xdxdxdxdxdxdwdq\\q]q]q]q]q]q]q]q]qgqgqlqnq"
3050 "fqfqhqfqhqfqhqfqhqfqcteqnqiqgqiqeqiqeqiqeqiqbq`vbqjqeqjqdqjqdqjqdqjqdqjqdqjqdqjxkq]yeyeyeydq]q]q]q^qiqdqjqeqiqeqiqeqiqeqiqeqiqQqmplqdqjqdqjqdqjqdqjqcq"
3051 "mqbqjqcqmq9qlqlqgqgqgqnqmqnqgqnqjqgqgqfq]qnq:{eqnpLzg|dt`tdqipcpipbpkp`sbq Zq plq`pmq_pkqmqkqjrQqZqFq'q]rkraq_rYqdy^qdqiqbq`qiq^q6uf{fuaq_qlyjzeqiqeq"
3052 "]qhqfq]q]qhqfqiqeq]q]qlrcq]qlqlqgqkseqhqfq]qhqfqjq]qaq`qiqcqnq`skshrmraq_q_q[q\\q$qjqcqjqeq]qjqdq\\q^qjqcqjqdq]q]qnq`q]qkqkqiqjqeqiqdqjqeqjqcqXqbq]qjqcq"
3053 "mqaqnqmqnqgqmq`s_q\\q`qZq7pmpnqmpgqkrcqjqeqkpbqiqeq\\qjqcs_qlqcqjqeqlqlqgqmqmqdqlqcqmqmqdqiqfqiqaq`qkqdrjrdr_q]riqfrnqnreqhqducqhqerZqZrdwdrkrdwOq_qmp_q"
3054 "^w`q`q[sKplslpTplpWqQpmpkqnp<q&qjq`pmp aplp\\pkplpephqepkplpjq^zfzfzfzfzfzfxcq]q]q]q]q]q]q]q]q]qhqfqkseqhqfqhqfqhqfqhqfqhqcrnreriqfqiqeqiqeqiqeqiqbq`q]"
3055 "qjqeqjqdqjqdqjqdqjqdqjqdqjqdqjqdq]q]q]q]q\\q]q]q]q^qiqdqjqeqiqeqiqeqiqeqiqeqiqQqnpkqdqjqdqjqdqjqdqjqbsaqjqbs7qmqmqeqiqeqiqeqiqeqiqeq]qnp7q]rJrnpnresnpn"
3056 "sct_rcqipcqkqcqkqasaq [rkp&plpcplpnr`qkqmqkrltRqZqFq'q\\qkq`q`r_pjr^qcpjrcqkrbq`rkrdpkr3sSsLrlrnrhqhqeqjreripeqjsfq]q]riqfqiqeq]q]qkrdq]qgqgqkserjrfq]"
3057 "rjrfqjrfpiraq_qkqbt`skshqkqaq`q^q[q\\q$qkrcrlrdqkpcrlrcqipdq^rlrcqjqdq]q]qmqaq]qkqkqiqjqdqkqcrlrerlrcq^pjqbq]rlrbs_rkrfqmq`s`r\\q`qZq6qlrfrmscrlrepkqbrk"
3058 "qdqkpaqjqcs`rlqcrlrernsnrgrnqnrdqlqcrnqnrdrkqdqkraq`qkqdqhqer^q\\rkqdwdqhqbqarjrdpYqYpbubpipbuNq_rnpmpbq^qnqnq`q`qZqIpgpRplp7pgp;q&rlr`pmp bplp[pkufpiq"
3059 "dpkukrlpcqhqfqhqfqhqfqhqfqhqfqhqfqjqcripeq]q]q]q]q]q]q]q]qjsfqkserjrfrjrfrjrfrjrfrjrdrlrfrjreqkqcqkqcqkqcqkqaq`q]qnplqeqkrdqkrdqkrdqkrdqkrdqkrdqksjpjq"
3060 "kpbqipdqipdqipdqipdq]q]q]q]qkqcqjqdqkqcqkqcqkqcqkqcqkq^qbqkqcrlrdrlrdrlrdrlrbsarlrbs6qkqcqkqcqkqcqkqcqkqdq\\r7q\\qFp\\p]r^rcqipcvbqkqas`r \\vOqIqlpcw_pip"
3061 "mpivnrRpZpEqbqIq^q[ubwdxdw]qcwbwaq_wcvbq]qRpSp[q^q^qhqexcxeyexdq\\xeqiqeq]q]qjrexdqgqgqjrdxeq\\xeqiqfx`q_war_ririqiqbqazfq[q\\q$xcwcvbwcxdq]wcqjqdq]q]qlq"
3062 "bq]qkqkqiqjqdwcwcwcq^wbu`wbs_rkrgqkq`q`w`q`qZq$yewdqmq`wdvaqjqbr`qkqcyeyewcqlsdwcxdw`sauczexdq^umteucqhqbq`xLqJsKsMq^vdxdpgpaq`qYqIqkq bqkq?{+yapmp Jp"
3063 "fpfpipcpfpiucqhqfqhqfqhqfqhqfqhqfqhqfqjxixexdxdxdxdq]q]q]q]yeqjrdxdxdxdxdxdrjrgpnwdwcwcwcwaq`q]qnuexdxdxdxdxdxdvnwjvbxdxdxdxdq]q]q]q]wcqjqdwcwcwcwcw^q"
3064 "bwbwcwcwcwaq`w`q4uauauauaucq\\r7p[qFp\\p\\p\\pbqipasapip`q^y ctNqIqmqbu_phsgslrSq\\qEqbqIq^qZsawdxcu\\qbt^taq]uataq]q q]qgpiqfqfw`udwcxdqZudqiqeq]q]qirfxdq"
3065 "gqgqjrbtcqZtcqirfv_q]s_r_rirjrircqazfq[q\\q#tnqcqns`s`snqaucq\\snqcqjqdq]q]qkqcq]qkqkqiqjqbsaqnsasnqcq]t_t_snqaq^rkrhrkraq`w`q`qZq#smrevbs^t`s`qjqbq`qiq"
3066 "dqnrmqdrmrcubqkrcubqntat^r`sc|fxdq^umtcqaqhqbq^tJqIqIqLq]tcxLq`qYqHu `u>{+qnrmqapmp Kpepgpiuhpephscqfqhqfqhqfqhqfqhqfqhqfqhqixgudxdxdxdxdq]q]q]q]wcqjr"
3067 "bt`t`t`t`taphpgplt`s_s_s_s_q`q]qmsctnqctnqctnqctnqctnqctnqbsktgs_uauauaucq]q]q]q[saqjqbs_s_s_s_sNpms_snqbsnqbsnqbsnqaq`qns_q !p Zp jp#q\\q6q7q l"
3068 "q [sjq Qq -q OqZq]q Cq;q HqWq $rIq`qZq _q iqbqKqFqIq`q hp$q]u JqYpmpLp .p jp ]p Xr`q[r !p Tp\"p\\p6q6q mq Yx Qr -r Ps\\q_s"
3069 " Ipkq:q HqWq $qHq`qZq _q iqbqKqFqIq`q hp$q]t IqYpmpLq /q kq Fq_q[q #s Tp\"q^q6p 1p Vu Rs YsJsMy &v<s HqWq &sHtcq]t _q i"
3070 "qbqKqFqIq`q hp$q 2q2q /q kq Hs_q]s \"q (r Xy %t;r GqWq &rFscq]s ^q iqbqKqFqIq`q ,q4r 0r lr G"
3071 "r^q *q kr i";
3072 
3073  // Define a 47x53 font (extra-large size).
3074  const char *const data_font47x53 =
3075 " "
3076 " 9])]2_2]T\\8^U^3] E])]2`4^U^>])]2_4^U^ 6^T\\5])]1_2]T\\8^U^ K])]2`4^V^3] "
3077 " U]*\\2a4`V\\8^U^5a F]*\\1\\X\\4^U^=]*\\"
3078 "2a5^U^ 7aV\\4]*\\1a4`V\\8^U^ J]*\\1\\X\\4^V^3\\ "
3079 " S],\\1\\W\\5g8^U^6c F],\\1\\V\\5^U^<],\\2]W]6^U^ 8h3],\\0\\W\\5g8^U^ I],\\1\\V\\5^V"
3080 "^4\\ ;] "
3081 " :\\-]2\\U\\6\\V`7^U^7]U] F\\-]2\\T\\6^U^;\\-]3]U]7^U^ 8\\Va1\\-]1\\U\\6\\V`7^U^ H\\-]2\\T\\6^V^5] =a "
3082 " J] "
3083 " N\\/]2\\S\\7\\T]6^U^7\\S\\ E\\/]2\\R\\7^U^:\\/]3]S]8^U^ 8\\T^/\\/]1\\S\\7\\T]6^U^ G\\/]2\\R\\7^V^6] =c L^ "
3084 " *^ U` O^ )\\S\\ "
3085 " !^$^3\\ E]U\\ K^$^4^ G^$^4] J^$^3\\ #^$^3\\ 4^ B[ "
3086 "&^ Xe S^ (\\S\\ )Z Q^&^3^2]S\\ A\\S\\ K^&^3^ F^&^4_ >]S"
3087 "\\9^&^3^2]S\\ W^&^3^ 6^ Q] M[ ?` ![1^H]?` =]4](\\ %` >b4c Bb ?`2a .a Ib Pb Aa <a @b Fb =b F^ :] '] Da A"
3088 "].].].].] <_:]._ Xh ?c W^ @` La Pa Sa Va5^U^ @` \"f4_ >`0`*^ $^.` <^F]F^F]G`G] F\\S\\ ;b %a2a2a2a2a <bR"
3089 "\\ D`4^(^3`4`U\\8^V^6\\S\\ J^(^3`4^U^@^(^3_4^U^/^/`U\\8^(^3`4`U\\8^V^ K^(^3`4^V^1^9]+^V^ ?` O\\ D\\6]M] We D]1]T] 9[3bJ\\@e<])]2])\\ "
3090 " T]0d3_7h9i/_;k5f?n:f7e 3g :_8i3h@h9n?l5iB]H]C].].]J^B].`I`H_J]<g?g1g?g4hAuB]H]G]C]F]K_K]S^J^F^G^CrBb7]*b'_ D] :] '] Fc A].].].].] >a:]"
3091 ".a !^T_ Bg ` Dd2_8n?m7g3]:rD]P]P]@g <] 8] 8] B] 3e J^K^ If7^U^+b@d Fb@f5a Ad4e-] :f Ra0d AaF\\HaF\\HeJ\\?]._0_0_0_0_2\\U\\0tHh@n?n?n?n?].].].]"
3092 "-h:_J]<g8g8g8g8g BhV]G]H]C]H]C]H]C]H]G^G^B]*d5](]2\\X\\4aW]8^V^6\\S\\ I](]3]X]5^U^?](]3\\W\\5^U^.^R[9aW]7](]2\\X\\4aW]8^V^ J](]2\\X\\4^V^1]8]+^V^ ?a>w "
3093 "P[ 9[/a:aQa7[ Wl \"h E]1]T]+\\R\\;[4dL]Ag=])]2])\\ U^1f8c8k;j1`;k7h?n;h9g 5i*b:_8k6kBl=n?l7mD]H]C].].]L_A].`I`H`K]>kAj6kAj9kBuB]H]F]E]E^L_L^"
3094 "R^L^D^I^BrBb7^+b(a D] ;] '] Gd A].].].].] ;] (b:].b #^Q] Dj !a Ff3_8n?m8i4]:rD]P]P]Bk ?_ 9] 9_ C]&[0f I]K]=]0g7^U^-fC\\S] IfBf6c B["
3095 "S]5[S].] <i R\\W\\1]T] B\\W\\G]H\\W\\G]H[S]K]?]._0_0_0_0_2c1uIkBn?n?n?n?].].].]-l>`K]>k<k<k<k<k EoF]H]C]H]C]H]C]H]F^I^A],h6]*]2\\V\\6]Wa7^V^6\\S\\ H]*]2\\V]6^U"
3096 "^>]*]3]W]6^U^._V_;]Wa5]*]2\\V\\6]Wa7^V^ I]*]2\\V\\5^V^2]7]+^V^ @]W\\=v P[ 9\\1c<cSd:] \"o #_S^ F]1]T],]S];[5^V^N]A_T]=]*]0]*\\ U]1^T^;e8`S_<"
3097 "^R_2`;k8^R]?n<_T_;^S^ 6^S_.i>_8m:`R`Cn?n?l9`QaE]H]C].].]M_@].aKaH`K]?`S`Bk8`S`Bk;_R_BuB]H]F]E]D]MaM]P]L]B^K^ArB]1]&])c D] <] '] G] :].].].].] "
3098 ";] (^6]*^ #]P^ E^P\\ V^ H^T^4_8n?m:`S`6]:rD]P]P]C`S` Aa :] :a D]&[1^S\\ I^M^=]0^R[7^U^/^R^EZO\\ L^R^ N]U] :],\\0] <j M\\2]R] >\\H]B\\H]=\\M]>"
3099 "]._0_0_0_0_0_/uK`R`Cn?n?n?n?].].].]-n@`K]?`S`>`S`>`S`>`S`>`S` H`ScE]H]C]H]C]H]C]H]E^K^@],^T^5],]1\\V\\6\\U`7^V^6]U\\ F],]2\\T\\6^U^=],]2\\U\\6^U^-e9\\U`4],]1\\"
3100 "V\\6\\U`7^V^ H],]1\\V\\5^V^3]6]+^V^ B`1`1`1`1`6]W]>u P[ 9]2e>eUf;^ %q $^O\\ F]1]T],]S];[5]T]N\\@]P[=]*]0]2ZR\\RZ $]2]P]<_W]8]N]<ZL^4a;]+]MZ/]<^P"
3101 "^=^Q^ 7\\O]1nAa9]N_<_M]C]NaA].]+_L^E]H]C].].]N_?].aKaHaL]@^M^C]P_:^M^C]P_=^M\\6]6]H]F^G^D]MaM]P^N^B^K^-^B]1]&]*e D] =] '] H] 9].].].].] ;] )"
3102 "^5])^ %^O]8^3]LZ U] I^R^6a9_0]+^M^7]:]H]D]P]P]D^M^ Cc ;] ;c E]&[2^PZ H]M]<]1^-^U^1]L];[ N]L] Q]S] :\\,\\1] <dU\\ M\\2\\P\\ >\\H\\A\\H\\<\\M\\=]/a2a2a"
3103 "2a2a1_/]V];_M]C].].].].].].].]-]ObBaL]@^M^@^M^@^M^@^M^@^M^ J^N`D]H]C]H]C]H]C]H]E^K^@]-^Q]5].]1\\T\\7\\S]6^V^5c E].]2]S\\7^U^<].]2\\S\\7^U^,a6\\S]2].]1\\T\\7\\S"
3104 "]6^V^ G].]1\\T\\6^V^4]5]+^V^ De6e6e6e6e9\\U\\>u P[ :_3f@gVf<_ &r $]M[ F]1]T],\\R]>d<^T^P]A^OZ=]+].]4]T\\T] &^3^P^=[S]8[K].]4\\X];],]!]<]N]>^O^ "
3105 " 8ZM^3`P`Ba9]M^=^J\\C]K_B].],^H\\E]H]C].].]O_>].aKaHaL]A^K^D]N^<^K^D]N^>]JZ6]6]H]E]G]C]MaM]O^P^@^M^-^A]1]&]+_W_ D] >] '] H] 9] B].] ;] )]4](]"
3106 " %]N]:c6] G] J^P^7a8_1],^K^;c=]H]D]P]P]E^K^ Ee <] <e F]&[2] =^O^<]1] 0\\H\\<\\ P\\H\\ R\\Q\\+]3\\,\\2] <eU\\ M\\3]P\\ >\\I]A\\I]<\\N]=]/a2a2a2a2a2a1]U]<"
3107 "^J\\C].].].].].].].]-]K_CaL]A^K^B^K^B^K^B^K^B^K^ K]K^D]H]C]H]C]H]C]H]D^M^?]-]P]4]0]1\\R\\ Ha C]0]2]R] E]0]2\\Q\\ 9c 9]0]1\\R\\ !]0]1\\R\\ ?]4] Di:i:i:i:i"
3108 ";\\6]G] P\\ :`5g@gWh>a (_ J]KZ F]1]T],\\R\\?h>]R]P\\@]1]+].]3^V\\V^.] T]2]N]5]8ZJ]-]6]X];]-]!^=]L]?]M] *]5_J_Ec:]L^>]H[C]I^C].],]F[E]H]C].].]"
3109 "P_=].]X]M]X]HbM]A]I]D]M]<]I]D]M]?]%]6]H]E]G]C^NaN^N]Q^>^O^-^@]0]'],_U_ &] '] H] 9] B].] ;] )]4](] %]N]:d7] F] K]N]8c8^1],]I]>i@]H"
3110 "]D]P]P]E]I] Fg =] =g G]&[2] <]O];]1] 1\\F\\=\\ Q\\F\\ S\\Q\\+]3\\.] IeU\\ M\\3\\N\\ ?\\I\\@\\I\\=]M\\<]0c4c4c4c4c3a1]U]<]H[C].].].].].].].]-]J_DbM]A]I]B]I]B]I]B]I]"
3111 "B]I] L]J_E]H]C]H]C]H]C]H]C^O^>].]N] .] '`X_ I] FbWa=bWa=bWa=bWa=bWa<\\6^I^ ?Z2[ :a5gAiXh?c *^ H] 7]1]T]-]S]Aj>]R]Q]@]1],"
3112 "],\\1^X\\X^,] T]3]L]6]'].]7]W];]-]!]<]L]?]M^ +]6^F^F]W]:]K]?]FZC]H^D].]-]DZE]H]C].].]Q_<].]X]M]X]H]X]M]B]G]E]M^>]G]E]M^@]%]6]H]E^I^B]O^X]O]M^R^=]O^"
3113 "-^@]0]']-_S_ '] '] H] 9] B].] ;] )]4](] %]N]:e8_ H] L]M]8]W]7^2]-]G]AmB]H]D]P]P]F]G] Hi >] >i J[3] ;^Q^;]1] 2\\RbT\\Ge R\\VdR\\ T\\"
3114 "Q\\+]4\\2a IfU\\ M\\3\\N\\ ?\\J\\?\\J\\AaM\\ G]W]4]W]4]W]4]W]4]W]4c3^U]=]FZC].].].].].].].]-]H]D]X]M]B]G]D]G]D]G]D]G]D]G]A[H[B]J`E]H]C]H]C]H]C]H]B]O^>g8]N] "
3115 " 1]T_ 3[ 9] G_O^?_O^?_O^?_O^?_O^=\\5]I^ @\\3[ ;c6gAy?d7`8]L]7^7]L]>^ H] 6]1]T]-]S]B_W[U]>]R]R]?]1],],]0d*] T]3]L]6]'].]7\\V];]"
3116 ".] ]<]L]@]K] 7Z PZ X]7^D^G]W]:]K]?]/]G]D].]-]/]H]C].].]R_;].]X^O^X]H]X^N]B]G]E]L]>]G]E]L]@]%]6]H]D]I]A]O]W]O]L^T^<^Q^-^?]0]'].^O^ Sb7]U`2b4`U]8a8])`"
3117 "7]T_ M].]%_O_@_2`0`3`/_3c9] )]4](] N_6]N]3^7a/c0_ <^ D[U^ Ga N]L]9]W]6^3]-]G]B`W]W`C]H]D]P]P]F]G] I_X]X_ ?] ?_X]X_ Nb7]2ZFZ=]Q]:]0] 3[SfU[I"
3118 "g R[UfS[ T\\Q\\+]5]2a IfU\\ M\\3\\N\\ ?\\K]?\\K]AaN] G]W]4]W]4]W]4]W]4]W]4]W]3]T]=]/].].].].].].].]-]G]E]X^N]B]G]D]G]D]G]D]G]D]G]B]J]C]KbF]H]C]H]C]H]C]H]B"
3119 "^Q^=j;]P_9b3b3b3b3b3b3bN`Bb3a2a2a2a V_2_2`1`1`1`1` ;aU] :]U` S^T]U^A^L^A^L^A^L^A^L^?]5]I] @^5\\ <e7gAy@f;e:]L]8`8^N^?^ G] 6]1]T]-\\R\\A]U["
3120 "RZ>]R]R\\>]1],],].`(] U^3]L]6]'].]8]V];].]!^<]L]@]K] :] P]#^8^A]I^W^;]K]@].]G^E].].].]H]C].].]S_:].]W]O]W]H]W]N]C]E]F]L]?]E]F]L]@]%]6]H]D]J^A]O]W]O]"
3121 "L^U^:^S^-^>]0^(]/^M^ Wh:]Wd6f8dW]:e>h2dW]?]Vd<].].]O_>].]WdScK]Vd8f;]Wd7dW]?]Wa6h>h6]L]B]I]A]P`P]K^L^B^K^@l4]4](] PdU]A]N]2^8e5g;]Vd?^J^8]6]L] E]V`"
3122 ">pA]S]S]:e6kDo>]L]:^W^6^4].]E]D_U]U_D]H]D]P]P]G]E] K_W]W_ @] @_W]W_ Qf9]3\\H\\>^S^:]0_ 6[ThT[K]Q\\ S[T\\R]S[ U]S]+]6],] ?]L]@fU\\ M\\3\\N\\ ?\\K\\>\\K\\;]O\\ G"
3123 "^W^6^W^6^W^6^W^6^W^5]W]4^T]>].].].].].].].].]-]G^F]W]N]C]E]F]E]F]E]F]E]F]E]D_L_E]K]W]F]H]C]H]C]H]C]H]A^S^<k<]Ra<h9h9h9h9h9h9hTeFf7e6e6e6e;].].].]\"^;]V"
3124 "d8f7f7f7f7f/^6eX]@]L]?]L]?]L]?]L]B^K^?]Wd>^K^ O]S]S]B]I]B]I]B]I]B]I]@]5^K^ @]4[ ;f8gAyAg<h<]L]8`7]N]>] F] 6]1]T]-\\R\\B]T[6]R]S]>^2]-]*\\.`(] U"
3125 "]2]L]6]'].]9]U];].]!];]L]@]K] =` P`'^7]?\\I]U];]K]@].]F]E].].].]H]C].].]T_9].]W]O]W]H]W^O]C]E]F]L]?]E]F]L]@]%]6]H]C]K]@^P]W]P^K^V^9]S]-^=]/](]0^K^ Xi"
3126 ";]Xf9h9fX]<h?h3fX]?]Xg=].].]P_=].]XfVfL]Xg:h<]Xf9fX]?]Xb7i>h6]L]A]K]@^Q`Q^J^N^@]K]?l4]4](] QfW^A]O^1]6f9h;]Xg@_K]7]6]L]=]G]C^Wc@pA]S]S]<h9mDo>]L]:]U"
3127 "]5^5].]E]E^S]S^E]H]D]P]P]G]E]@Z+]V]V^-Z4]5ZKZ:]V]V^ Sh9]4^J^>]S]9]._ 8[U_Q[T[L]P\\ S[T\\Q]T[ T]U]*]7]*] @]L]@fU\\ M\\3\\N\\ ?\\L]>\\L]:]Q]:]1]U]6]U]6]U]6]"
3128 "U]6]U]6^W^5]S]>].].].].].].].].]-]F]F]W^O]C]E]F]E]F]E]F]E]F]E]C_N_D]L^W]F]H]C]H]C]H]C]H]@]S];]P_=]S^8i:i:i:i:i:i:iVgIh9h9h9h9h<].].].]'d<]Xg:h9h9h9h9h"
3129 "0^8k?]L]?]L]?]L]?]L]A]K]>]Xf>]K] O]R]R]D]G]D]VZOZV]D]KZV]D]G]A]4]K] @]3[ <g7fAyBi>j=]L]8`7]N]?] F^ 6]1]T]5uI]T[6]R]S\\<^3]-]*]1d*] U]3]J]7]']"
3130 ".]9\\T];].\\Ua-^;]L]@]K^?].] Uc Pc+_8]>]J]U];]K]@].]F]E].].].]H]C].].]U_8].]W^Q^W]H]V]O]C]E]F]L]?]E]F]L]@^&]6]H]C]K]?]Q^V]Q]I^X^8^U^.^<]/](]1^I^ ]R_<aT"
3131 "_;_R\\:^Tb=_S^@h4_Ub?bT^=].].]Q_<].aT_X]T^LbT^;_T_=aT_;^Tb?aTZ8_R]>h6]L]A]K]?]Q`Q]H^P^?]K]?l4]4](] R^U^W]@]O]0^7g;_S];bT^@`L]8_7]L]>]E]E^W]V]@pA]S]S]"
3132 "=_T_<oDo?]K^;]U]5_6].\\D]E]R]R]E]H]D]P]P]G]E]A\\+[U]U\\,\\6]6\\L\\;[U]U\\ S_W[V\\9]3^V`V^=^U^9]/a :[T]G[M\\O\\1ZQZ M[S\\P\\S[ Ud)]8](\\ @]L]@fU\\ M\\3\\N\\9ZQZ0\\L\\="
3133 "\\L\\8\\Q\\9]1]U]6]U]6]U]6]U]6]U]6]U]5]S]>].].].].].].].].]-]F]F]V]O]C]E]F]E]F]E]F]E]F]E]B_P_C]L]V^G]H]C]H]C]H]C]H]@^U^;]N^>]T]6]R_;]R_;]R_;]R_;]R_;]R_;]R"
3134 "_X_T^K_R\\:_S^;_S^;_S^;_S^=].].].]*h=bT^;_T_;_T_;_T_;_T_;_T_1^9_T`>]L]?]L]?]L]?]L]A]K]>aT_?]K] P]Q]R]E]F]E]V\\Q\\W]E]K\\W]E]F]A]4^L] A^@ZN\\ =i8e@yCk?^R^"
3135 "=]L]9b8]O^?] Im B]1]T]5uI]T[6]S^T]<^3]-]*]3^X\\X^,] V^3]J]7](^/]9]T];e7]We/]9]N]?]K^?].] Wd Nd._8]O`U\\T\\K]S]<]L^A]-]F^F].]/]-]H]C].].]V_7].]V]Q"
3136 "]V]H]V^P]D]C]G]L]@]C]G]L]?^']6]H]C^M^?]Q]U]Q]Ic6^W^._<]/^)]2^G^ !ZM^=`Q^=^NZ;^Q`>^P^=].^Q`?`Q^>].].]R_;].`R^X\\R^M`Q^=^P^>`Q^=^Q`?`1]MZ;].]L]A^M^?]Q`Q]"
3137 "G^R^>^M^1^4]4](] D]P^A]R^X]@]P^/]9^Vb=^NZ;`Q^AaN^8_7]L]>]E]F^V]U]>]P]>]S]S]>^P^>`T`7]6]J]<]S]5^6]/]C]G]Q]Q]F]H]D]P]P]H]C]C^&]TZ,^7]7^N^6]TZ H]/^U[TZ9"
3138 "]2n;]U]8]0d <[U]F[M\\P]2[R[ M[S\\P\\S[ Tb(]9]'\\ @]L]@fU\\ M\\3]P]9[R[1\\M\\<\\M\\7\\R\\8]2]S]8]S]8]S]8]S]8]S]7]U]6]R]?]-].].].].].].].]-]F]F]V^P]D]C]H]C]H]C]H]"
3139 "C]H]C]B_R_C]L]T]G]H]C]H]C]H]C]H]?^W^:]M]>]U^6ZM^<ZM^<ZM^<ZM^<ZM^<ZM^<ZMbP]M^NZ;^P^=^P^=^P^=^P^>].].].]+i=`Q^=^P^=^P^=^P^=^P^=^P^2^:^P^>]L]?]L]?]L]?]L]"
3140 "A^M^>`Q^@^M^ P]Q]Q]F]E]F]W^S^W]F]L^W]F]E]B]3]M^ B^B^O[ =k8d?xClA^P^>]L]9]X]8^P]>\\ Hl A] 9uI]T[5]T]T]:^ =]*]5^V\\V^.] V]2]J]7](]/^:]S];h:]Xg0]"
3141 "9^P^?]K^?].]!e Je2_7\\PdW\\S\\L]S]<]M^@]-]E]F].]/]-]H]C].].]X_5].]V]Q]V]H]U^Q]D]C]G]L]@]C]G]M^?`)]6]H]B]M]>]Q]U]Q]Hb5c-^;].])] B]=_O]=].]O_>]N^>].]O_?_"
3142 "O]>].].]S_:]._P`P]M_O]=]N]>_O]=]O_?_1]-].]L]@]M]>]RbR]G^R^=]M]1^3]4](] FaSaD^Qa?]R_.]9]R`>]._O]>^N]8`7]L]>]E]G^U]U^?]P]>]S]S]>]N]>^P^7]6]J]<]S]4^7]/]"
3143 "C]G]Q]Q]F]H]D]P]P]H]C]D_&]&_8]8_N_7] B]/]T[3]1l:^W^8]1]W` >\\U\\E\\N\\P]3\\S\\ N\\S\\P\\S\\ S_']:]&\\ @]L]@fU\\ M\\2\\P\\8\\S\\2\\N]<\\N]7\\S]8]2]S]8]S]8]S]8]S]8]S]8]S]"
3144 "7]R]?]-].].].].].].].]-]E]G]U^Q]D]C]H]C]H]C]H]C]H]C]A_T_B]M]S]G]H]C]H]C]H]C]H]>c9]M^?]U]'].].].].].].`O^N].]N^>]N^>]N^>]N^?].].].],_R^>_O]=]N]=]N]=]N]"
3145 "=]N]=]N]2^:]O_?]L]?]L]?]L]?]L]@]M]=_O]?]M] O\\P]Q]F\\D]F\\U^U^V]F\\L^V]F\\D]B]3]M] RuJ`O[ >m9c>wCmA]N]>]L]9]X]7]P]?] Im A] 2\\R\\A]T[5^V^T\\:` ?](\\6]T"
3146 "\\T]/] V]2]J]7])^1_9]S];i;bS^2^8^S_>]K^?].]$e@u@e6_7]QfX\\S\\M^S^=]N^?]-]E]F].]/]-]H]C].].c4].]U]S]U]H]T]Q]D]C]G]M^@]C]G]M]=c-]6]H]B]M]>^R]U]R^G`4c.^:]"
3147 ".])] B]=^M]?^/]M^?]L]>]/]M^?^N^?].].]T_9].^O_O^N^N^?]M^?^M]?]M^?^0]-].]L]@]M]>^S]X]S^F^T^<^O^2_3]4](] GcUcE]Pa?]Vb-]:]O_?].^N^>]O^8a8]L]?]C]H]T]T]?"
3148 "]P]>]S]S]?]L]@^N^8]6]J]=^S^4^8]/]C]H^Q]Q^G]H]D]P]P]H]C]E_%]%_9]9_L_8] B]0^T[3]0_T_>cWc=]1]U_ ?[U\\C[N]R^4]T] N[R\\Q]R[ 'uG]&] @]L]?eU\\ M\\2]R]8]T]3\\N\\;"
3149 "\\N\\7]S\\7]3^S^:^S^:^S^:^S^:^S^9]S]8^R]?]-].].].].].].].]-]E]G]T]Q]D]C]H]C]H]C]H]C]H]C]@_V_A]N]R]G]H]C]H]C]H]C]H]>c9]L]?]U]'].].].].].]._M]O^/]L]?]L]?]L"
3150 "]?]L]?].].].]-^O]>^N^?]M^?]M^?]M^?]M^?]M^ I]O`?]L]?]L]?]L]?]L]@^O^=^M]@^O^ P]P]P\\G]C\\G]T^W^T\\G]M^T\\G]C\\B]3^O^ RuJ[X]P[ >o=\\XaX]BwDoC]L\\>]L]:^X^8]P]?"
3151 "] E] 5] 3]S]A^U[4dT];b @](]6ZR\\RZ.] V]2]J]7]*^7d8]R];]R_<aQ^3]5f<^M_?].]'e=u=e:_6\\Q^S`S]N]Q]=l>]-]E]Fm>k=]-rC].].b3].]U]S]U]H]T^R]D]C]G]M]?]C]"
3152 "G]N^<f1]6]H]B^O^=]S^U^S]F_2a.^9].])] A]>^M]?].]M^?]L]>]/]M^?^M]?].].]U_8].^N^N]N^M]?]L]?^M]?]M^?^0]-].]L]@^O^=]S]X]S]D^V^:]O]2_2]4](] H\\U^W]U\\E]Pa?"
3153 "]Vb-];]M^?].^M]>^P]7a8]L]?]C]H]T]T]?]P]>]S]S]?]L]@]L]8]6p=]Q]3^9]/]C]H]P]P]G]H]C]Q]Q]G]ViV]F_$]$_:]:_J_9] B]0]S[3]0]P]>o=]2]S_ @[U\\C[M]T_5^U^;u O[R\\R]"
3154 "Q[ 'uH]/ZQ] ?]L]?eU\\ M\\1]T]7^U^4\\O]O]I\\O]T`MZQ]S]O]E]3]Q]:]Q]:]Q]:]Q]:]Q]:^S^9]QmO]-m>m>m>m>].].].]1hL]G]T^R]D]C]H]C]H]C]H]C]H]C]?_X_@]O]Q]G]H]C]H]C]"
3155 "H]C]H]=a8]L]?]U]&].].].].].].^M]O].]L]?]L]?]L]?]L]?].].].].^M]?^M]?]L]?]L]?]L]?]L]?]L] I]Pa?]L]?]L]?]L]?]L]?]O]<^M]?]O] O]P]P\\G]C\\G]ScS\\G]N^S\\G]P]P\\B"
3156 "]2]O] QuF]Q[ >oAqDuDqD]L]?]L]:^X^8^R^?\\ D] 5] 3]S]@`X[3bS\\R^G]W^N] P](].\\&] W]1]J]7]*^7c8]Q];ZM^=`O^4]4d:]M_?].])d:u:d=_5\\R]O^R\\N]Q]=j<]-]E]F"
3157 "m>k=]-rC].].a2].]U^U^U]H]S]R]D]C]G]N^?]C]G]P_:g3]6]H]A]O]<]S]S]S]E^1_.^8]-]*] A]>^M]?]/^M^?]K]?]0^M^?]L]?].].]V_7].]M]M]N]L]@^L]?^M]@^M^?]/]-].]L]?]"
3158 "O]<]S]X]S]C^X^9]O]2^1]4](]0_IZ O[R\\X]S\\G^O_>]Vd9_U];]L]?].]L]=]P]8]X^9]L]?]C]I^T]S]@]P]>]S]S]?]L]@]L^9]6p=]Q]3^9]/]C]H]P]P]G]H]C]Q]Q]G]ViV]G_#]#_;];_H"
3159 "_:] B]0]S[3]0\\N\\>o=]2]Q^ A[U\\C[LcX\\6]T]9u O[RfP[ 'uIf7e >]L]>dU\\<] :f5d4]T]:fT\\O^NfT\\UdOeR\\O^F^3]Q]:]Q]:]Q]:]Q]:]Q]:]Q]:^QmO]-m>m>m>m>].].].]1hL]G]S]R"
3160 "]D]C]H]C]H]C]H]C]H]C]>d?]P^Q]G]H]C]H]C]H]C]H]<_7]L]?]U^'].].].].].].^L]P].]K]@]K]@]K]@]K]@].].].].]L]?]L]@^L]@^L]@^L]@^L]@^L] I]Q]X^@]L]?]L]?]L]?]L]?]"
3161 "O]<^M]?]O] O\\WmX]H\\WmX]H\\QaR]H\\N^R]H\\O]P]C]2]O] QuF]R\\ ?qCsDtDrE]L]?]L]:]V]7]R]>x '] 5] 3\\R\\?e3^R\\SbJ^V^O] P](].\\&] W]1]J]7]+^6e:]Q]-^>_M]5^6"
3162 "h<^O` Qe8u8e@^5]R\\M]R\\O^Q^>m?]-]E]Fm>k=]KdFrC].].b3].]T]U]T]H]S^S]D]C]G]P_>]C]Gk6f5]6]H]A^Q^<]S]S]S]F_1_/_8]-]*] A]>]K]A].]K]@]J]?]0]K]?]L]?].].]W_"
3163 "6].]M]M]N]L]@]J]@]K]A]K]?]/^.].]L]?]O]<]T^W]T]C^X^9^Q^3^1]3]']3dN\\ P\\R`Q[G]N_>]Q`;bW];\\K^?]/]L]=]Q^8]W]9]L]?]C]I]S]S]@]P]>]S]S]@]J]B^L^9]6p>^Q^4^9]/]C"
3164 "]H]P]P]G]H]C]Q]Q]G]ViV]H_\"]\"_<]<_F_;] B]1]R[3]1]N]8a6]2]P^ B[U\\C[K`V\\7]T]8u O[RdN[ 'uIf5a <]L]=cU\\<] :f3`1]T];fU\\N^NfU\\T[S]NaQ\\N^G^3^Q^<^Q^<^Q^<^Q^<^Q"
3165 "^;]Q]:]PmO]-m>m>m>m>].].].]1hL]G]S^S]D]C]H]C]H]C]H]C]H]C]=b>]P]P]G]H]C]H]C]H]C]H]<_7]L]?]U_(].].].].].].]K]Q].]J]A]J]A]J]A]J]@].].].].]L]?]L]@]J]A]J]A"
3166 "]J]A]J]A]J] K]P\\V]@]L]?]L]?]L]?]L]?^Q^<]K]@^Q^ O\\WmX]H\\WmX]H\\P_Q]H\\O^Q]H\\O]P]C]2^Q^ D^<]R[ >qDuEsCqD]L]?]L]:]V]7]R]>x '] 5] 3\\R\\=f+]TdL^T^P] P]"
3167 "(].\\2u *]1]J]7],^-_=]P],]>_M]5]7_R^<^Qa Sd .dC^4\\R]M]R\\O]O]>]N_@]-]E]F].]/]KdF]H]C].].]X^4].]T]U]T]H]R]S]D]C]Gk=]C]Gj1c6]6]H]@]Q];^T]S]T^Ga1].^7]-]*"
3168 "] Lh>]K]A].]K]@]J]?]0]K]?]L]?].].]X_5].]M]M]N]L]@]J]@]K]A]K]?]._0].]L]>]Q];^U]V]U^Bb7]Q]3^1^3]'^6iS^ P[P^P[G]N_>]N^=dX]<]J]>^1]L]=^R]8^W]9]L]@]A]J]S"
3169 "]S]@]P]>]S]S]@]J]B]J]9]6]J]>]O]5^8]/]C]H]P]P]G]H]B]R]R]F]C]Iz<]<z=]=z<] B]1]R[7j:\\L\\7_5]2]P^ B[U\\C[ V]T]7u O[R\\U^O[ T] ]L];aU\\<] I]T],]O[X\\>]K]@]"
3170 "O[X\\I`3]O]<]O]<]O]<]O]<]O]<]O];]P]?]-].].].].].].].]-]E]G]R]S]D]C]H]C]H]C]H]C]H]C]<`=]Q]O]G]H]C]H]C]H]C]H];]6]L]?]T_4h9h9h9h9h9h9hK]Q].]J]A]J]A]J]A]J]"
3171 "@].].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J]?tG]Q\\U]@]L]?]L]?]L]?]L]>]Q];]K]?]Q] N\\WmX]H\\WmX]H\\P_Q]H\\P^P]H\\O]P]C]1]Q] C]:]S[ ?sEvEqAoC]L]?]L];^V^8^T^>x "
3172 " '] 5] 4]S]<g-\\T^V^M]S_Q\\ O](].\\2u Se =^1]J]7]-^*^?]O],^?^K]7^7]N]<^Sb Sa (aC]3\\R\\K\\R\\P^O^?]L^A]-]E]F].]/]KdF]H]C].].]W^5].]T^W^T]H]R^T]D]C]Gj<]C]Gj-"
3173 "`7]6]H]@]Q]:]U^S^U]Fb2]/^6]-^+] Nj>]K]A].]K]@p?]0]K]?]L]?].].b3].]M]M]N]L]@]J]@]K]A]K]?].c4].]L]>]Q]:]U]V]U]@`6^S^4^5b2]&b<u P[O]P\\H]N^=]M]>^Ua<]J]="
3174 "c7]L]<]S^8]V^:]L]@]A]J]S]S]@]P]>]S]S]@]J]B]J]9]6]J]?^O^7^7]/]C]H]P]P]G]H]B]R]R]F]C]Iz<]<z=]=z<] B]1]R[7j:\\L\\7_ C^P] B[U\\C[ W]T] W] O[R\\T^P[ T] ]L]7"
3175 "]U\\<] H]T]-\\O\\X\\>\\I\\@\\O\\X\\J`3^O^>^O^>^O^>^O^>^O^=]O]<^P]?]-].].].].].].].]-]E]G]R^T]D]C]H]C]H]C]H]C]H]C];^<]R]N]G]H]C]H]C]H]C]H];]6]L]?]S`8j;j;j;j;j"
3176 ";j;|Q].pApApAp@].].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J]?tG]R]U]@]L]?]L]?]L]?]L]>^S^;]K]?^S^ N\\WmX]H\\WmX]H\\QaR]H\\Q^O]H\\O]P]C]1^S^ D]9]T\\ ?sFwDo?nC]L]?]L];"
3177 "]T]7]T]=] Hj ?] 4]S]8d/]T]T]N^R_R\\ O](] =u Se =]0]J]7].^(]?]O]+]?^K]7]7]L]<gX] Sa (aC]3\\R\\K\\R\\P]M]?]K]A]-]E]F].]/]D]F]H]C].].]V^6].]S]W]S]H]Q]T"
3178 "]D]C]Gg9]C]G]Q_,^7]6]H]@^S^:]U]Q]U]G^X]2]0^5],]+] Pl>]K]A].]K]@p?]0]K]?]L]?].].a2].]M]M]N]L]@]J]@]K]A]K]?]-f8].]L]>^S^:]U]V]U]?^4]S]4^4`0]$`<^Si O[O"
3179 "\\O\\H]N^=]M^@^S`<]J]=c7]L]<]S]8^U]:]L]@]O]O]J]S]S]@]P]>]S]S]@]J]B]J]9]6]J]?]M]7]6]/^E^H]P]P]G]H]A]S]S]E]C]Iz<]<z=]=z<] B]1]R[7j:\\L\\6] A^Q] B[U\\C[Ni:]T]"
3180 " V] O[R\\S]P[ T] ]L]6\\U\\<] Dh2]T]/]P\\W\\?]I\\A]P\\W\\K`2]M]>]M]>]M]>]M]>]M]>^O^=]O]?]-].].].].].].].]-]E]G]Q]T]D]C]H]C]H]C]H]C]H]C]<`=]S]M]G]H]C]H]C]H]"
3181 "C]H];]6]M^?]R`;l=l=l=l=l=l=~Q].pApApAp@].].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J]?tG]S]T]@]L]?]L]?]L]?]L]=]S]:]K]>]S] M]P]P\\G]C\\G]ScS\\G]S^N\\G]P]P\\B]0]S] D]"
3182 "7\\T[ >sFwCn?mB]L]?]L];]T]7]T]=] Hi >] 4]S]7[Xa1]T^T^O]P_T] O](] =u Se =]0]J]7]/^'^A]N]+]?^K]7]8^L^<eW] Sd .dC]3\\R\\K\\R\\P]M]?]K]A]-]E]F].]/]D]F]H"
3183 "]C].].]U^7].]ScS]H]Q^U]D]C]G]/]C]G]O^,^8]6]H]?]S]9]U]Q]U]H^W^3]1^4],]+] Q`P]>]K]A].]K]@p?]0]K]?]L]?].].b3].]M]M]N]L]@]J]@]K]A]K]?]+e9].]L]=]S]9]V]T]"
3184 "V]@_4]S]5_4b2]&b<\\Nd M[O]P\\H]N^=]L]@]Q_<]J]?e7]L];]T]8]T]:]L]@]O]O]J]S]S]@]P]>]S]S]@]J]B]J]9]6]J]?]M]8^6].]E]G]P]Q^G]H]A^T]T^E]C]Iz<]<z=]=z<] B]1]R[3]"
3185 "1\\L\\6] A_R] B\\U\\E\\Ni:]T] V] O\\S\\R]R\\ T] ]L]6\\U\\<] Dh2]T]/\\O[V\\?\\H\\A\\O[V\\L`1]M]>]M]>]M]>]M]>]M]>]M]>^O]?]-].].].].].].].]-]E]G]Q^U]D]C]H]C]H]C]H]C]"
3186 "H]C]=b>]T]L]G]H]C]H]C]H]C]H];]6]M]>]Qa>`P]>`P]>`P]>`P]>`P]>`P]>`PoQ].pApApAp@].].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J]?tG]T]S]@]L]?]L]?]L]?]L]=]S]:]K]>]S] "
3187 "L\\P]P\\F\\C\\F\\T^W^T\\F\\T^M\\F\\C\\B]0]S] E^7]U[ >sFwBl=kA]L]?]L]<^T^8^V^=] Ij >] <u=[U^1\\S]R]O]O_U\\ N](] 1] Ge =]0]J]7]0_&]A]N]+]?^K]8^8]J]:aU\\ Pe 4"
3188 "eA]3\\R\\K\\R\\Qo@]J]A].]F^F].].]E]F]H]C].].]T^8].]RaR]H]P]U]C]E]F].]E]F]N^,]8]6]H]?]S]9^V]Q]V^H^V^4]2_4],]+] Q]M]>]K]A].]K]@],]0]K]?]L]?].].c4].]M]M]N]"
3189 "L]@]J]@]K]A]K]?](d;].]L]=]S]9^W]T]W^@`5^U^5^/_3]'_8ZJ` K[O]P\\H]N^=]L]@]P];]J]@_0]L];]U^9^T^;]L]@]O]O]J]S]S]@]P]>]S]S]@]J]B]J]9]6]J]@^M^:^5].]E]F]Q]Q]F"
3190 "]H]@^U]U^C]E]G_\"]\"_BZT]TZB_F_;] B]1]R[3]1\\L\\?o I_S] A[U]F[ V]T] W] N[S\\R]R[ S] ]L]6\\U\\ ']T]/\\O\\V\\@\\H\\A\\O\\V\\M_0o@o@o@o@o?m>l>].].].].].].].].]-]F^"
3191 "G]P]U]C]E]F]E]F]E]F]E]F]E]=d?^V]L]F]H]C]H]C]H]C]H];]6]N^>]O`?]M]>]M]>]M]>]M]>]M]>]M]>]M]?].].].].]-].].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J] K]U]R]@]L]?]L]?"
3192 "]L]?]L]=^U^:]K]>^U^ L\\P]Q]F\\D]F\\U^U^V]F\\U^M]F\\D]B\\/^U^ OuD]V[ =sFwBk;i@]L]?]L]<]R]7]V];] F^ Nu=[T^3]S]R]O]N_V\\ N](] 1] ].]L]6]1_%]Aq0]>]K]"
3193 "8]7]J]/] Md:u:d>]3\\R\\K\\S\\Po@]J]A].]F]E].].]E]F]H]C].].]S^9].]RaR]H]P^V]C]E]F].]E]F]M],]8]6]H]>]U^8]W^Q^W]H^U^4]2^3]+],] R^M]>]K]A].]K]@],]0]K]?]L]?"
3194 "].].]X_5].]M]M]N]L]@]J]@]K]A]K]?]$`;].]L]=^U^8]W]T]W]@b5]U]5^,]3]'] J\\Q_Q[G]N^=]L]A]O];]J]@].]L];]U]8]R];]L]@]O]O]J]S]S]@]P]>]S]S]@]J]B]J]9]5]L]?]K];"
3195 "^4].^G^F]Q]Q]F]H]?_W]W_B]E]F_#]#_B\\U]U\\B_H_A\\U]U[ H]1]R[3]1]N]?o H`V] @[T]G[ U]T] X] N[S\\Q]S[ S] ]L]6\\U\\ (]T]/]P\\U\\A]I]B]P\\U\\M^/o@o@o@o@o@o@m>].]"
3196 ".].].].].].].]-]F]F]P^V]C]E]F]E]F]E]F]E]F]E]>_X_?]W^L]F]H]C]H]C]H]C]H];]6]P_=]M^@^M]?^M]?^M]?^M]?^M]?^M]?^M]?].].].].]-].].].]/]J]@]L]@]J]A]J]A]J]A]J]"
3197 "A]J] K]U\\Q]@]L]?]L]?]L]?]L]<]U]9]K]=]U] K]Q]Q]F]E]F]W^S^W]F]W^L]F]E]B\\.]U] NuC\\V[ =eXZXdFgXhAi9h@]L]?]L]<]R]7]V];] E] Nu=[S]3\\R]R]O]M_X\\ M]("
3198 "] 1] ].]L]6]2_$]Aq0]>]K]8]7]J]/] Ke=u=e<]3\\R\\K\\S\\Po@]J]A].]F]E].].]E]F]H]C].].]R^:].]RaR]H]O^W]C]E]F].]E]F]M^-]8]6]H]>]U]7]W]O]W]I^S^5]3^2]+],] R"
3199 "]L]>]K]A].]K]@],]0]K]?]L]?].].]W_6].]M]M]N]L]@]J]@]K]A]K]?]\"_<].]L]<]U]7]W]T]W]Ac5^W^6^+^4](] H[R\\X]S\\G]N^=]L]A]O];]J]A^.]L]:]W^9^R];]L]@]O]O]J]S]S]@"
3200 "]P]>]S]S]@]J]B]J]9]5]L]?]K];^4]-]G]D]R]R]E]H]>kA]E]E_$]$_B^V]V^B_J_A^V]V] I]1]R[3]0\\N\\>o G`X] ?\\U_Q[T\\ T]T] ] N\\T\\Q]T\\ S] ]L]6\\U\\ )]T].\\P\\T\\A\\I]A"
3201 "\\P\\T\\N^.o@o@o@o@o@o@m>].].].].].].].].]-]F]F]O^W]C]E]F]E]F]E]F]E]F]E]?_V_@]W]K]F]H]C]H]C]H]C]H];]6k<]L^A]L]?]L]?]L]?]L]?]L]?]L]?]L]?].].].].]-].].].]/"
3202 "]J]@]L]@]J]A]J]A]J]A]J]A]J] K]V\\P]@]L]?]L]?]L]?]L]<^W^9]K]=^W^ J]R]R]D]G]D]W\\Q\\W]D]W\\L]D]G]A\\.^V] NuC]W[ <cWZXdEfXh@g8g?]L]?]L]=^R^8^X^:] F] "
3203 " G\\R\\5[S]4]R]R]O]Lb M](\\ 0] ].]L]6]3_#]Aq0]>]K]9]6]J]/] He@u@e H\\R]M]T]Q^J]A]J]@]/]G^E].]-]F]F]H]C].].]Q^;].]Q_Q]H]N]W]B]G]E]-]G^F]L]-]8]6]I^>^W^7]"
3204 "W]O]W]I^R^6]4^1]+],] R]M^>^M^@]/^M^?]-]0^M^?]L]?].].]V_7].]M]M]N]L]@^L]?^M^A^M^?] ]<].]L]<]U]7]X]R]X]B^W^5]W]6^)]4](] H\\T]W]U\\F]O_=]L]A]P^;^L^A]-]L"
3205 "]:]W]8]P]<]L]@]O]O]J^T]T]?]P]>]S]S]@^L]A^L]8]5]L]@^J]=^3]-^I^D^S]S^E]H]<g>]G]C_%]%_A_W]W_A_L_@_W]W_ J]0]S[3]0]P]5]4],b =[ThT[ R]T]!] M[T\\P]U[ R] ]L"
3206 "]6\\U\\ *]T].]P[S\\B]J]A]P[S\\N].^J]B^J]B^J]B^J]B^J]B^K^A]M]=]/].].].].].].].]-]G^F]N]W]B]G]D]G]D]G]D]G]D]G]?_T_AbK]E]I^C]I^C]I^C]I^;]6j;]K]A]M^?]M^?]M^"
3207 "?]M^?]M^?]M^?]M_?].].].].].].].].]/]J]@]L]@^L]@^L]@^L]@^L]@^L] J^X]Q]?]L]?]L]?]L]?]L];]W]8^M^<]W] I]R]S]C]H]C]VZOZW]C]VZL]C]H]@\\-]W] MuC]X[ ;cWZWbDe"
3208 "WZXe>e6e>]L]?]L]=]P]8^X^:] F^ H\\R\\5[S]5]Q]R]O^L` K]*] 0] !^.]L]6]4_\"]2],^>^M]8]6]J]0] DeCuCe E]R\\M]T\\P]I]A]J]@]/]G]D].]-]F]F]H]C].].]P^<].]Q"
3209 "_Q]H]N^X]B]G]E]-]G]E]L^.]8]5]J]<]W]6^X]O]X^J^Q^6]5^0]+^-] R]M^>^M]?].]M^?]-]/]M^?]L]?].].]U_8].]M]M]N]L]?]L]?^M]?]M^?] ]<].]M^<^W^6aRbB^V^6]W]7^(]4]"
3210 "(] GcUcE]P_=]L]A]P]9]L]@]-]L]:^X]9^P]<]M^@]P^O]I]T]T]?]P]>]S]S]@^L]@]L]8]5]M]?]I]>^2],]I]B_U]U_D]H]:c<]G]B_&]&_?_X]X_?_N_>_X]X_ I]0]S[3]0_T_5]4]+` ;["
3211 "SfU[ P^U^#] L[U\\P]V[ Q] ]M^6\\U\\ ,^U^-\\P\\S\\B\\J]@\\P\\S\\N].]I]B]I]B]I]B]I]B]I]B]I]B^M]=]/].].].].].].].]-]G]E]N^X]B]G]D]G]D]G]D]G]D]G]@_R_A`J]D]J]A]J"
3212 "]A]J]A]J]:]6g8]K]A]M^?]M^?]M^?]M^?]M^?]M^?]M_?].].].].].].].].].]L]?]L]?]L]?]L]?]L]?]L]?]L]3^;aP]?]M^?]M^?]M^?]M^;]W]8^M];]W] H]S]T^B]J^B]J^B]J^B]J^@"
3213 "\\-]W] G^1_ :aW[V`BcW[Wc<d5c=]L]>]N]<]P]7]X]8] F]KZ X]S]5[S]5\\P]R]N]K_ K]*] 0] !],]N]5]5_\"]1],]<]M]9^6^L^0] Ad Nd A\\R]O^U\\P^I^B]K^?]H[C]H^D]"
3214 ".],]G]F]H]C].].]O^=].]P^Q]H]M]X]A]I]D],]I^E]K]AZH^8]5]J]<]W]5bObJ^O^7]6_0]*]-] R]M^>^M]?^/]M^?^.]/]M^?]L]?].].]T_9].]M]M]N]L]?]L]?^M]?]M^?] ]<].]M^;"
3215 "]W]5aRaB^U^6c8_(]4](] FaSaD]P_=]M]@]P]9]L]@]-]L]9b9]O^=^N^?\\P_Q]H]T]T]?]P]=]T]T]?^L]@]L]8]4]N]@^I^?]1],^K^A`W]W`C]H]7]8]I]@^&]&^=i=^N^<i H]0^T[3]1l6]"
3216 "4])_ <\\RbT\\ O]T]#] L\\V\\O]X\\ M^N^6\\U\\ ,]T]-\\OhF\\J]@\\OhQ]/^I^D^I^D^I^D^I^D^I^C]I]B]L]<]H[C].].].].].].].]-]H]D]M]X]A]I]B]I]B]I]B]I]B]I]@_P_B_J]C]J"
3217 "]A]J]A]J]A]J]:]6].]K]A]M^?]M^?]M^?]M^?]M^?]M^?]M_?^/^/^/^/^/].].].].]L]?]L]?]L]?]L]?]L]?]L]?]L]3^;`O]?]M^?]M^?]M^?]M^;c8^M];c G^U]U^@^M^@^M^@^M^@^M^?"
3218 "\\-c H^0_ 9^U[U^@aV[Va:b3a<]L]>^P^=^P]7]X]8_ H^M[ F] 6]S]>ZQ[T^6]P]S^N^K^ K]*] 0]:] 8]0],]O^5]6_2ZI]1]-^<^O^9]4]L]0]<].] Uc Pc1]2\\Q^S`W^P]G]B]K]"
3219 ">^J\\C]I^C].],^H]F]H]C].].]N^>].]C]H]MbA^K^D],^K^D]K^B[I]7]5^L^<c5aMaJ^N]7]6^/]*]-] R^O_>_O]=].]O_>].].]O_?]L]?].].]S_:].]M]M]N]L]>]N]>_O]=]O_?] ]<]-"
3220 "]O_;]X^5aRaC^S^6a8_']4](] D]P^B^Ra>^N]@]Q]7]N]?^.]L]9a8]N]=^N^?]Q_Q]G]U]U]>]P]=]T]T]?_N]>]N]7]4^P^@]G]@^1]+^M^?mB]H]7]8^K^?\\%]%\\;g;\\L\\:g G]/]T[3]2n7]"
3221 "4]'^ <\\F\\ M\\S\\ J\\F\\ L^N^6\\U\\ ,\\S\\-]OhG]K]@]OhQ]LZ=]G]D]G]D]G]D]G]D]G]D]G]D^L]<^J\\C].].].].].].].]-]J_D]MbA^K^B^K^B^K^B^K^B^K^A_N_B^K]B^L^A^L^A^"
3222 "L^A^L^:]6].]K]A^O_?^O_?^O_?^O_?^O_?^O_?^Oa?].].].].]/].].].]-]N]>]L]>]N]=]N]=]N]=]N]=]N]2^;_O]=]O_>]O_>]O_>]O_:a7_O]9a E^P_>^P_>^P_>^P_>^P_>\\,a H^.]"
3223 " /[5]T[S\\8a1`<]L]=^R^<]O^8b7_ H^O\\ F] 6\\R\\=[R[U^5\\N]T]L^M` L]*] 0]:] 8]1^+]P]4]7_1[L_1]<ZL^:^Q^8]4^N^>ZM];].] R` P`.]2]QfXaN]G]B]L^=^L]C]K_B].]+"
3224 "_J]F]H]C].].]M^?].]C]H]La@^M^C]+^M^C]J]B]L^7]4^N^:a4aMaK^M^8]7^.]*^.] Q]P`>`Q^=^NZ;^Q`>_LZ>].^Q`?]L]?].].]Q^;].]M]M]N]L]>^P^>`Q^=^Q`?]/ZL];]-^Q`:a4`"
3225 "P`D^Q^7a8^&]4](] S]Sb>_P^@]R^7^P^>^MZ<]L]9a9]M]=_P`XZB]Q_Q]G^V]V^>]P]=^U]U^?`P^>^P^6]4]Q^?]G]A^0]*^O^<i@]H]7]7^M^=Z$]%Z8e9ZKZ7e F]/^U[TZ9]3^V`V^8]4]"
3226 "&^ <\\H\\ K[R[ I\\H\\ K_P`XZ9\\U\\ ,[R[,\\E\\D\\K]?\\E\\M]O\\=]G]D]G]D]G]D]G]D]G]D]G]D]K];^L]C].].].].].].].]-]K_C]La@^M^@^M^@^M^@^M^@^M^A_L_C`N^A^N^?^N^?^"
3227 "N^?^N^9]6].]L]?]P`>]P`>]P`>]P`>]P`>]P`>]P]X^LZN^NZ;_LZ>_LZ>_LZ>_LZ?].].].]-^P^>]L]>^P^=^P^=^P^=^P^=^P^2^:^P^=^Q`>^Q`>^Q`>^Q`:a7`Q^9a Dk<k<k<k<k>],a "
3228 "H]-] /[,[._0_;]L]=j<]N]7`5a J_S^ F] 6\\R\\=^U[W_5]N^V^K_Rd L],] /]:] 8]1])^T^3]8_0^Q`0]<]Q_8^S^8^3_R_=]R^:].] O] P]+]1\\PdW`N^G^C]N_;`R`C]NaA].]*`O"
3229 "`F]H]C].].]L^@].]C]H]La?`S`B]*`S`B]J]B`Q_6]3_R_9a4aMaL^K^9]8^-])].] Q_Tb>aS^;_R\\:^Sa=`Q]>]-^Sa?]L]?].].]P^<].]M]M]N]L]=_T_=aS^;^Sa?]/^R_:]-^Sa:a3_P_"
3230 "C^P^7_8^%]4](] S_V^X^?aS^>]T^5_T_=`R]<]L]8_8]M^>`SdA]SaS]E^W]W^=]P^=_W]W_>]X]T_<_T_5^4^T^?^G^C^/])^Q^8c=]H]7]6`S` ?] ;c >c E]._W[V\\9]4^J^9]4]%] ;]L]"
3231 " IZQZ H]L] !u ,`Sd9\\U\\ ,ZQZ,]E\\E]L]?]E\\M_S^>^G^F^G^F^G^F^G^F^G^F^G^F^K]:`R`C].].].].].].].]-]ObB]La?`S`>`S`>`S`>`S`>`S`?]J]CcS`?_R_=_R_=_R_=_R_8]6"
3232 "].]V[R^?_Tb>_Tb>_Tb>_Tb>_Tb>_Tb>_T^V_Q]M_R\\:`Q]=`Q]=`Q]=`Q]?].].].],_T_=]L]=_T_;_T_;_T_;_T_;_T_1^:`T_;^Sa=^Sa=^Sa=^Sa9_6aS^7_ Bi:i:i:i:i=]+` I],] /["
3233 ",[-].]:]L]<h;]N]7`3q \"h E] 7]S]=k5]LdIjW^ M],] /]:] 8]1](f9k?n?l/]<j6g7]1j<h9].] LZ PZ(]1]O`U]K]E]Cm8kBn?n?](nE]H]C].].]K^Am>]C]H]K`>kA])kA]J^Cm5"
3234 "]2j7_2`M`K^J]9]8tC])].] PgX]>]Xf9h9fX]<k>],fX]?]L]?].].]O^=].]M]M]N]L]<h<]Xf9fX]?]/j9d4gX]:a3_P_D^O^7_8m4]4](] RfXaBk=^V^3h;j<]L]8_9^L]>qA^U]W]U^D"
3235 "i<]O`?k=]Xg:h3a7f>uCn?]/eSe;]:]H]7]5k >] :a <a D]-h>n?\\H\\8]4]%] 9^R^ *^R^ Xu ,q9\\U\\ /]D\\F]LfH]D\\Li>]E]F]E]F]E]F]E]F]E]F]E]F]JnIkBn?n?n?n?].].]."
3236 "]-n@]K`>k<k<k<k<k=[H[Co<j;j;j;j7]6].]Vf=gX]=gX]=gX]=gX]=gX]=gX]=gTjLh9k<k<k<k?].].].]+h<]L]<h9h9h9h9h Fk:gX]=gX]=gX]=gX]9_6]Xf6_ @e6e6e6e6e;]+_ G\\+["
3237 " /].]-[,[9]L];e:^N^8`2p e D] 7]S]<i4\\JbGgT^ M\\,\\ .]:] 8]1]'d8k?n>i-]<i4e6]0h;g8].] I]0]3]E]Cl6h@l=n?]&jC]H]C].].]J^Bm>]C]H]K`<g?]'g?]I]Bj3]1h6"
3238 "_2_K_L^I^:]8tC])].] OdV]>]Wd6f8dW]:i>]+dW]?]L]?].].]N^>].]M]M]N]L];f;]Wd7dW]?]/i7c3dV]9_2_P_E^M^8_8m4]4](] QdV`B]Xe;d1f8h<]L]8_9]K]>]XdW_@eWeBg;]O"
3239 "`=g;]Vd8f1`6d=uCn?]/eSe;]:]H]7]3g <] 9_ :_ C]+f>n>ZFZ7]4]%] 7f &f Vu ,]XdW_9\\U\\ /\\C\\F\\KfH\\C\\Kg=]E]F]E]F]E]F]E]F]E]F]E]F]JnHh@n?n?n?n?].].].]-l>"
3240 "]K`<g8g8g8g8g J]Vh:h9h9h9h6]6].]Ve;dV]<dV]<dV]<dV]<dV]<dV]<eRiJf7i:i:i:i?].].].]*f;]L];f7f7f7f7f F]Xe7dV]<dV]<dV]<dV]9_6]Wd5_ <\\-\\-\\-\\-\\6]+_ FZ*[ /]"
3241 ".],Z+Z9]L]8`8]L]7^.m W` A] 7\\R\\7b2]H^BaP_ O].] .]:\\ 7]2^%`6k?n:b*]9c/a5],b6b5].\\ H]/\\4]C]Di0b=h9n?]#c?]H]C].].]I_Dm>]C]H]J_9a<]$d?]I^?c0].b3_2"
3242 "_K_M^G^;]8tC](]/] M`T]>]U`2b4`U]7c;])`U]?]L]?].].]M^?].]M]M]N]L]8`8]U`3`U]?],c2a0_T]9_2^N^F^K^8]7m4]4](] O`R^B]Va8b-`3d:]L]7]9^J]?]V`T]>cUc?c9]N_:"
3243 "a8]T`3`-_4`<wDn?]/eSe;]:]H]7]0a 9] 8] 8] B])b<n @]4]&^ 5b \"b Tu ,]V`T]8\\U\\ 0].].]0b;]C]H]C]H]C]H]C]H]C]H^E^H^JnEb=n?n?n?n?].].].]-h:]J_9a2a2a2a"
3244 "2a G\\Rb4b3b3b3b3]6].]Vc7`T]:`T]:`T]:`T]:`T]:`T]:aMcEb2c4c4c4c<].].].]'`8]L]8`1`1`1`1` D]Ua2_T]9_T]9_T]9_T]8]5]U`2] =] &[ "
3245 "O].] E] E] '] S] R] ^ (](]/] C] S] '] V] F^ 7]4](] %])[ 4]7] @])_Q_:] 9]6] 6[ S]0[R"
3246 "^ H]%\\U\\ A\\ @\\ /Z <\\ ,[ M^5](^ =] &[ N]0] D\\ D] '\\ "
3247 " Q^DZ 1] _ )](]/] D^ S] '] V] F] 6]4](] %] ;]7] @] /] 9]6] 6[ S]0g H]%\\U\\ @\\ "
3248 " @\\ J\\ X]4](] <] &[ N]0] D\\ E^ '\\ P^G] 2] X^ )]"
3249 "(^0] D] R] '] V] G^ 6]4](] %] ;]7] @] /] 9]6] 6[ S]0e F]%\\U\\ ?[ ?[ "
3250 "I[ ^4])^ @ZV] &[ M]2] D] E] '] O_K_ 3] V^ *b,]5b E^ R] '] V] "
3251 " G^ 6^5])^ %] ;]7] @] /] 9]6] 6[ S].a D]%\\U\\ ?\\ @\\ J\\ !^4])^ "
3252 " B\\V] &[ M]2] D\\ G\\ L`P` 2] U^ +b =b RZN^ R^ '] V] H^ 4^6]*^ $] ;]7] @] /]"
3253 " 9]6] 6[ S] J] :\\ @\\ J\\ \"^3]*^ A\\V\\ %[ L"
3254 "]4] Vm 2^ S^ ,b =b R\\Q_ R] &] V] I^ 3b:].b $] ;]7] @] /] 9]6] 6[ S] "
3255 " J] @ZU] FZU] PZU] #^2]+^ @b %[ Si 4b "
3256 " %i Ua &] V] Mb 2a:].a #] ;]7] @] /] 9]6] .] J] @b Fb "
3257 "Pb 'b2] E` Qb 1a $g S` %] V] Ma /_:]._ !] "
3258 " ;]7] @] /] 9]6] .] J] @a Ea Oa &a1] D^ "
3259 " X^ Ip Fc Q^ #] V] M_ A] )] ;]7] @] /] 9]6] T] @` "
3260 " D` N` %_/] BZ Ap "
3261 " 6] "
3262 " p 6] "
3263 " "
3264 " F]']2] +]']2^ D]']3_ E]']1] \"]']2^ 8] H";
3265 
3266  // Define a 90x103 font (huge size).
3267  const char *const _data_font90x103[] = { // Defined as an array to avoid MS compiler limit about constant string (65Kb).
3268  // Start of first string.
3269 " "
3270 " "
3271 " HX 4V >X IX *W FW "
3272 " "
3273 " "
3274 " HX W 4Z 3VCT <Z >X W 4Z "
3275 " HX W 4Z 'VCT ;X W 3Y 2UCT KX W 3Y 0W "
3276 " "
3277 " "
3278 " @W !W 4\\ 5YET ?XHX 8] >W !W 4\\ 7XGX KW !W 4\\ 7XHX +YET :W !W 3[ 5ZFT ?XGX EW !W 3[ 7XGX 5W "
3279 " "
3280 " "
3281 " >W \"V 3\\ 7]HU ?XHX 9` ?W \""
3282 "V 3\\ 7XGX JW \"V 3\\ 7XHX -]HU 9W \"V 3] 7]HT ?XGX DW \"V 3] 8XGX 5V "
3283 " "
3284 " "
3285 " <W $V 3VNV 8_KV ?XHX 9` >W $V 3VNV 8XGX IW $V 3VNV 8XHX -_KV 8W $V 2] 7_KU ?XGX CW $V "
3286 "2] 8XGX 6V "
3287 " "
3288 " :W &W "
3289 "4VLV :j >XHX :VJV >W &W 4VLV 9XGX HW &W 4VLV 9XHX .j 6W &W 3VMV 9i >XGX BW &W 3VMV 9XGX 7W MW "
3290 " "
3291 " "
3292 " CV 'W 4VJV ;j >XHX ;UGV >V 'W 4VJV :XGX GV 'W 4VJV :XHX .j"
3293 " 5V 'W 3VKV :i >XGX AV 'W 3VKV :XGX 8W N[ "
3294 " "
3295 " "
3296 " DV )W 4VHU <VK_ =XHX ;TEU =V )W 4VHU :XGX FV )W 4VHU :XHX /VK_ 3V )W 3VIV <UK_ =XGX @V )W 3VIV ;XGX 9W "
3297 " N] "
3298 " "
3299 " DV *V 3UFU =UH\\ <XHX <UD"
3300 "T <V *V 3UFU ;XGX EV *V 3UFU ;XHX /UH\\ 1V *V 2UGU <TH] =XGX ?V *V 2UGU ;XGX 9V a "
3301 " "
3302 " "
3303 " EV ,V 3UDU >TEY ;XHX <TBT <V ,V 3UDU <XGX DV ,V 3UDU <XHX /TEY /V ,V 2U"
3304 "EU =TFZ <XGX >V ,V 2UEU <XGX :V Na "
3305 " "
3306 " "
3307 " DU -V 3VDV ?TCV :XHX <TBT ;U -V 3VDV =XGX CU -V 3VDV =XHX /TCV -U -V 2UCU >TCU :XGX =U -V 2UCU =XGX ;V NV"
3308 "IV \"W "
3309 " "
3310 " JU /V 3VBV ETBT :U /"
3311 "V 3VBV FU /V 3VBV (U /V 2UAU DU /V 2UAU @V NVGV "
3312 " $X "
3313 " *X "
3314 " JX GTBT MX GX 7V :UEU DX GX 7V "
3315 " JX GX 7W 4X GX 6V GX GX 5V (X &X "
3316 " )X 8V "
3317 " ;X FTBT "
3318 " LX IX 7X <UCU DX IX 7X JX IX 6W 3X IX 6X GX IX 5X *X "
3319 " &Y "
3320 " (X 9V "
3321 " <X ETBT KX KX 6X 1TBT BTAT CX KX 6"
3322 "Y JX KX 6Y (TBT BX KX 5X 1TBT LX KX 4X +X %T #W 9W "
3323 " 3a :a <W 2W >W E\\ AW ,W ,W ,W ,W "
3324 " HY GV +Y 4Z NX @X %W "
3325 " DUDU =Y 7W KW 6Z 4XDT BTAT BW KW 6Z IW KW 6[ ,Y )XDT AW KW 5Z 4XDT "
3326 " KW KW 4Z ,W BW 8V (S <S 9V 7V "
3327 " 3a :a ;W 3W >W H_ AW ,W ,W ,W ,W L] GV +] ;a "
3328 " #[ F^ 8XGX +W BTEU "
3329 " *R 9a :W MW 6\\ 6ZET ?XHX <TAT AW MW 6\\ 7XGX LW MW 5[ 7XGX .Y +ZET @W MW 5\\ 6ZET ?XHX DW MW 4\\ 7XHX 0W AW"
3330 " &XHX MZ +T $Y BS 1W,V MY 8W 7W T 9X 5Z /[ 0Z 8Z /Y GY "
3331 " .\\ <\\ [ 4[ :\\ -a :a :W 4W >W Ja AW ,W ,W ,W ,W N_ GV +_ "
3332 "?e 8] J] Jb 8[ <[ $Y FY 7XGX =Z Di 5W 8Z .Y !W FW *Y 4W)V*W)V-Y(V "
3333 " <UFU 3\\ +[ 0[ 0[ 0[ 0[ 4[=T <e ;W W 5\\ 7\\FT ?XHX <TAT @W W 6^ 8XGX KW W 5] 8XGX .Z@R ?\\FT ?W W 4\\ 7\\"
3334 "FT ?XHX CW W 3\\ 7XHX 1W @W &XHX N\\ ,T :U :U5U ` EX 2VFV .S 4]0W\"b DV V 5V T 7W"
3335 " .` 3[ 7c 8d )Z Dq 8b Hy Bb 7` Na /Z @k .d Kj ?x Mt 7f MX/X'X -X -X2Z&X -]0]0[3X Dc Ii -c Ij 4f N~W$X/X.X&X.X4Y4XDY/Y/Y,Y'~S%a >W $a MY "
3336 " EW 5W >W Kb AW ,W ,W ,W ,W !a GV +a Ch =f ^ Mf 2Z @x Mx <c 3X C~Q)X?X?X Kc 2T "
3337 " .V .T CX $a !W.W N` ;XGX ![ Lb &Z Mi 7[ >a 5a &W 0g #\\ -_ <\\*V.\\*V0a-V\"X )Z /Z /Z /Z /Z 4WJV 1~U+d Kx Mx Mx Mx MX -X -X -X ,j"
3338 " @[3X Dc 8c 8c 8c 8c <cBV.X/X'X/X'X/X'X/X/Y,Y$X &h ;W \"W 5VNV 8]HU ?XHX <TAT ?W \"W 5VNV 8XGX JW \"W 5VMV 9XGX -ZDV @]HU >W \"W 4VNV 8]HU ?XHX "
3339 "BW \"W 3VNV 8XHX 2W ?W &XHX ^ K~\\ >S 3Q +[ @[;[ ;Q ;e HX 2VFV #VBV FS 6`1V#g GV !V 3V !T 7W 0d"
3340 " :` ;j ?k -[ Dq :g Ky Df ;d $f 1Z @o 5j Np Ex Mt :m\"X/X'X -X -X3Z%X -]0]0\\4X Gi Lm 4i Ln ;m#~W$X/X-X(X-X4Y4XCY1Y-Y.Y&~S%a >W $a N[ EV "
3341 "5W >W Lc AW ,W ,W ,W ,W \"b GV +a Dk Aj \"_ h 3Z @x Mx ?i 6X C~Q)X?X?X Ni 6V /V /"
3342 "V DX &f #W0W e >XGX %c#e +b\"i 9_ Be 9d 'V 3k %^ /c @^*V0^*V2d.V\"X )Z /Z /Z /Z /Z 3b 1~U.j Nx Mx Mx Mx MX -X -X -X ,p F\\4X Gi >i "
3343 ">i >i >i BiEV.X/X'X/X'X/X'X/X.Y.Y#X 'j ;V \"V 5VLV :_IT >XHX <TAT >V \"V 5VLV 9XGX IV \"V 4VMV 9XGX ,ZHY A_IT <V \"V 4VLV :_IT >XHX AV \"V 3VLV 9"
3344 "XHX 2V >W &XHX !_ K~[ >T 4R -_ D_?_ >S =t Fh IX 2VFV #VBV FS 7c4V#i HV \"W 3V !T 7V 0f @e >o Co 0"
3345 "\\ Dq <j Ly Fj ?h (i \\ ?Z @r :o\"s Hx Mt <q$X/X'X -X -X4Z$X -]0]0\\4X Im Np 9m Np ?q%~W$X/X-X(X,W5[6XAX1X+X.X%~S%a =V $a ] EV 6W >W M"
3346 "d AW ,W ,W ,W ,W HW 1b GV +b Fm Dm #` \"j 4Z @x Mx Am 8X C~Q)X?X?X!m 9X 0V 0X EX 'h"
3347 " $W0W \"h ?XGX 'g%g 0h%i :a Cf :f *V 4m %^ 0e A^+V/^+V1f1V!X )Z /Z /Z /Z /Z 2` 1~V0o\"x Mx Mx Mx MX -X -X -X ,t J\\4X Im Bm Bm Bm Bm F"
3348 "mHV-X/X'X/X'X/X'X/X-X.X\"X (l ;V $V 4UJU :ULXLU >XHX <UCU =V $V 5VJV :XGX HV $V 4VKV :XGX +ZL\\ AULXLU ;V $V 3UJU :ULXLU >XHX @V $V 2UJU 9XHX 3V"
3349 " =W &XHX !` K~Z >T 4S /a FaAa @T @w Hl KX 2VFV $WCV ES 8e5V$j HV \"V 1V \"T 7V 2j Eh ?q Dp 1\\ Dq >"
3350 "l Ly Hn Bj +l %e E\\ At >s$v Kx Mt >u&X/X'X -X -X5Z#X -^2^0]5X Jo q ;o r Br%~W$X/X-X(X,X6[6XAY3Y+Y0Y%~S%W 3V IW !_ FW 7W >W Md AW "
3351 ",W ,W ,W ,W HW 2[ ?V #[ Hn En #` #l 6\\ Ax Mx Cp 9X C~Q)X?X?X\"o ;Z 1V 1Z FX KS 0i #W2"
3352 "W LV ,i ?XGX *l'h 3l'i ;c Dg ;g ,W 6o %^ 1g B^,V.^,V0g3V X *\\ 1\\ 1\\ 1\\ 1\\ 2^ 0~V2s$x Mx Mx Mx MX -X -X -X ,v L]5X Jo Do Do Do Do HpKW"
3353 "-X/X'X/X'X/X'X/X-Y0Y\"X )n <W &W 5VJV ;TI_ >XHX ;UEU <W &W 5VIV ;XGX HW &W 5VIV ;XGX *g ?TI_ ;W &W 4VJV ;TI_ >XHX @W &W 3VJV :XHX 4W =W &XHX "
3354 " 1\\ 1\\ 1\\ 1\\ 1\\ =XMV K~Y =S 4U 1c IdCc AU Dz In LX 2VFV $VBV ES 9g7V$k HV #W 1W #T 8W 3l Fh ?r Eq 3] Dq ?m L"
3355 "y Ip Em -n )k H\\ Au Av%x Mx Mt ?x(X/X'X -X -X6Z\"X -^2^0]5X Ls\"s ?s\"s Et%~W$X/X,X*X+X6[6X@Y5Y)Y2Y$~S%W 3W JW \"a FW 8W >W NZ 6W ,W "
3356 ",W ,W ,W HW 2X <V X H[G[ Go KZ %[H[ 7\\ Ax Mx Ds ;X C~Q)X?X?X$s >\\ 2V 2\\ GX KS 1j #"
3357 "W2W LV -j ?XGX +ZEZ)VGY 5ZDZ)i <e EUFY <UFX -W 7q %VMU 2YIY CVMU,V.VMU,V0UFX3V X *\\ 1\\ 1\\ 1\\ 1\\ 1\\ 0~W4v%x Mx Mx Mx MX -X -X -X ,x N]5X"
3358 " Ls Hs Hs Hs Hs LsMW,X/X'X/X'X/X'X/X,Y2Y!X *\\G[ <W (W 4UHU <UH] =XHX ;VGV ;W (W 5VHV ;XGX GW (W 4UGU ;XGX )c =UH] 9W (W 3UHU <UH] =XHX ?W (W"
3359 " 2UHU :XHX 5W <W &XHX 5c 8c 8c 8c 8c @WKU J~X >T 5V 2e KfEe CW G| Jp MX 2VFV $VBV ES 9XIX8V$l HV #V /V #T "
3360 " 8V 3n Gh ?s Fr 5^ Dq @n Lx Ir Go .o -q L^ Bv Cx&z x Mt A{)X/X'X -X -X7Z!X -^2^0^6X Mu#t Au#t Gu%~W$X/X,X*X+X6[6X?X5X'X2X#~S%W 2V JW #c FW"
3361 " 9W >W NX 4W ,W ,W ,W ,W HW 2W ;V NW IZCY Hp JY &ZDZ 9^ Bx Mx Eu <X C~Q)X?X?X%u @"
3362 "^ 3V 3^ HX KS 2k \"W4W KV -ZGW ?XGX -X=X+R@W 8X<X .XIX FQ@W <Q@W /W 7dGU %QHU 3XEX DQHU-V-QHU-V/Q@W5V NX +^ 3^ 3^ 3^ 3^ 2\\ 0~W5"
3363 "x&x Mx Mx Mx MX -X -X -X ,z!^6X Mu Ju Ju Ju Ju N}+X/X'X/X'X/X'X/X+X2X X +ZBY ;W *W 4UFU =TF\\ =XHX :VIV 9W *W 5VFV <XGX FW *W 4VGV <XGX (_ :TF\\ 8"
3364 "W *W 3UFU =TF\\ =XHX >W *W 2UFU ;XHX 6W ;W &XHX 7h =h =h =h =h DWJV K~X >T 5W 4g MgFg EY J~ K]FZ MX 2VFV $VBV "
3365 "ES :XGX9V%\\GX HV $W /W 3PATAP GV 3[H[ Gh ?]F] GZE^ 6^ Dq A]FX Lx I\\F\\ G\\G[ /[H] 0u N^ Bw E_D^&{!x Mt B`C_)X/X'X -X -X8Z X -_4_0_7X N^"
3366 "E^$u C^E^$u H^E\\%~W$X/X,Y,Y*W7]8X>Y7Y'Y4Y#~S%W 2V JW $e FV 9W >W NW 3W ,W ,W ,W ,W HW 2W ;V NW IY@X >X "
3367 "4[AV IX &X@X 9^ Bx Mx F^E^ =X C~Q)X?X?X&^E^ B` 4V 4` IX KS 3\\GW \"W4W KV .YBT ?XGX .V7V,P=W :W8W /VEV 3V +V /V "
3368 " 7eGU KU 3WCW ;U-V$U-V LV5V NX +^ 3^ 3^ 3^ 3^ 3^ 1~W6_D^&x Mx Mx Mx MX -X -X -X ,{\"_7X N^E^ L^E^ L^E^ L^E^ L^E^ !^Ed*X/X'X/X'X/X'X/X+Y4Y X +Y?"
3369 "X ;V *V 4UDU >TEZ <XHX 9a 7V *V 4UDV =XGX EV *V 4VEV =XGX )] 7TEZ 6V *V 3UDU >TEZ <XHX =V *V 2UDU <XHX 6V :W &XHX 9k @k @k @k @k EWJV K~W "
3370 " >T 5Y 5g MhHi G[ M~Q L\\AW MX 2VFV $VCV DS :WEW:V%ZAU HV $V -V 3RCTCR HW 4ZDZ H\\LX ?Y?[ HV>\\ 8_ DX )[?T -Y J[B"
3371 "[ I[CZ 0WAZ 2x ^ BX>^ G]=Z&X=b#X -X '];[)X/X'X -X -X:[ NX -_4_0_7X \\?\\%X@^ E\\?\\%X?] J[=X =X <X/X+X,X)X8]8X=Y9Y%Y6Y )Y$W 2W KW %ZMZ FV :W"
3372 " >W X 3W 4W ,W HW 3X ;V NX KY?X Ca 9Y:R HX (X>X :VNV BZ /X '\\?\\ A^ FX0X)X?X?X'\\?\\ "
3373 " Db 5V 5b JX KS 3ZBT !W6W JV .X?R 4V4U HV ;V4V 1VCV 4V *U 0V 7fGU KU 4WAW <U.V#U.V JU6V MX +^ 3^ 3^ 3^ 3^ 3^ 2XIX F]=Z&X -X"
3374 " -X -X -X -X -X -X ,X=b$_7X \\?\\ N\\?\\ N\\?\\ N\\?\\ N\\?\\ #\\?`)X/X'X/X'X/X'X/X*Y6Y NX ,Y=W :V ,V 3UDU >TDX ;a 6V ,V 4UBU GV ,V 3UCU 0` 6TDX 4V ,V"
3375 " 2UDU >TDX >V ,V 1UDU :V 9W (o Do Do Do Do GWIU J~V >T 6Z 6i jIj I\\ N~R M[=U MX 2VFV %VBV H] AWCW;V%Y=R"
3376 " HV %W -V 4UETEU IV 4ZBZ IWGX ?V;[ IS9Z 9VNX DX *Z;R -X JZ>Y JZ?Y 1U>Z 5`C_#` CX;[ H[7W&X9_$X -X (\\6X)X/X'X -X -X;[ MX -_4_0`8X![;[&X"
3377 "=[ F[;[&X<[ LZ8U =X <X/X+X,X)X8]8X<X9X#X6X )Z$W 1V KW &ZKZ FV ;W >W W 2W 4W ,W HW 3W :V MW KX=W Cc "
3378 ";X7P HX (W<W ;WNW BY /X ([;[ Gg JX0X)X?X?X([;[ Fd 6V 6d KX KS 4Y>R !X8X JV /X<P 6V1U IV <U0U 2UAU 3U *U 1V "
3379 " 6fGU KU 4V?V <U/V\"U/V IU7V LX ,` 5` 5` 5` 5` 5` 3XIX G[7W&X -X -X -X -X -X -X -X ,X9_%`8X![;[![;[![;[![;[![;[ %[;](X/X'X/X'X/X'X/X)X6X MX ,X;W"
3380 " :V .V 3UBU ?TBT 7] 3V .V 4VAU GV .V 3UAU 4d 7TBT 1V .V 2UBU ?TBT ;V .V 1UBU <V 8W )r Gr Gr Gr Gr IVHR GX+W =S 5[ 7i!k"
3381 "Jk I] !^ )Y:T MX 2VFV %VBV Le EVAV<V$X:P HV %W -W 6WFTFV IV 4X?Y IRBX ?T7Y IP5Z :VNX DX +Z8P .Y JY<Y KY=X 1S;"
3382 "Y 6];\\$WNW CX9Z J[4U&X6]%X -X )[2V)X/X'X -X -X<[ LX -XNV6VNX0`8X\"Z7Z'X;Z HZ7Z'X;Z LY4R =X <X/X*X.X(X8]8X<Y;Y#Y8Y *Z#W 1V KW 'ZIZ FV <W >W W "
3383 " 2W 4W ,W HW 3W :V MW KW<X Dd <W -W )W;X <WNW AY 0X )Z7Z Jl MX0X)X?X?X)Z7Z Hf 7V 7f"
3384 " LX KS 4X;P W8W IV /W \"V.U JV >U.U 4VAV &V 5U *U 2V 6gGU KU 5W?W =U/V\"U/V IU7V LX ,WNW 5WNW 5WNW 5WNW 5WNW 5WNW 4XHX H[4U&X -X -"
3385 "X -X -X -X -X -X ,X6]&`8X\"Z7Z#Z7Z#Z7Z#Z7Z#Z7Z 'Z8['X/X'X/X'X/X'X/X)Y8Y MX ,W:W 9V 0V 3U@U ?[ 1V 0V 3U@V GV 0V 3U?U 8h 1V 0V 2U@U "
3386 " CV 0V 1U@U >V 7W *`L` I`L` I`L` I`L` I`L` JV =X,X >T 6] 9k\"lKl K_ #\\ 'Y8S MX 2VFV %VBV Nk IVAV=V$X 1V %V +V "
3387 "6YHTHY -V EW 5Y>Y :X ?R5Z .Y ;VMX DX +Y DX IY<Y LY;X 2Q8Y 8[5[&WNW CX8Y KZ1T&X4\\&X -X *Z.T)X/X'X -X -X=[ KX -XNV6VNX0a9X#Z5Z(X:Y IZ5Z("
3388 "X:Z NY1P =X <X/X*X.X'W9WNV:X:Y=Y!Y:Y *Z\"W 1W LW (ZGZ -W >W W 2W 4W ,W HW 3W :V MW KW;W De =W "
3389 " -X *W:W <VLV @Y 1X *Z5Z Mp X0X)X?X?X*Z5Z Jh 8V 8h MX KS 5Y :X:X IV /W #U+T JV ?U+T 5U?U &V 5U +V AgGU KU 5"
3390 "V=V =U0V!U0V IV8V KX ,WNW 5WNW 5WNW 5WNW 5WNW 5WNW 4XHX IZ1T&X -X -X -X -X -X -X -X ,X4\\'a9X#Z5Z%Z5Z%Z5Z%Z5Z%Z5Z )Z5Z(X/X'X/X'X/X'X/X(Y:Y LX -X:W "
3391 " !W 2\\LZ EW +[@[ K[@[ K[@[ K[@[ K[@[ KV <X-X /P 0T 7^ 9k\"lLm La %Z "
3392 " %Z6Q MX 2VFV %VCV n KWAW>V$X 1V &W +W 5XITIX +V EV 4X<X :X ?P2Y -X <WMX DX ,Y CX JY:Y MX9W 2P7Y :Z0Z(WLW DX7X KY.R&X2Z&X -X *Y+R)X"
3393 "/X'X -X -X>[ JX -XNW8WNX0a9X#Y3Y(X9Y JY3Y(X9Y NX LX <X/X*X.X'X:VMV:X9X=X NX:X *Z!W 0V LW )ZEZ .W >W W 2W 4W ,W HW "
3394 " 3W :V MW LX;W Df >W ,W +W8W >WLW @Y 2X +Z3Z!t\"X0X)X?X?X*Y3Y Kj 9V 9j AS 5X 8W:W HV /W #T)T KV "
3395 " @T(T 6U?U &V 5T +V AhGU KU 5V=V =U0V!U0V JV7V WLW 7WLW 7WLW 7WLW 7WLW 7XNX 6XGX IY.R&X -X -X -X -X -X -X -X ,X2Z'a9X#Y3Y%Y3Y%Y3Y%Y3Y%Y3"
3396 "Y )Y3Z)X/X'X/X'X/X'X/X'X:X Ki >W8V *XHZ FW ,Z<Z MZ<Z MZ<Z MZ<Z MZ<Z LV <X.X .R 2S "
3397 "7` :k#nMm Mb &Z $Y4P MX 2VFV &VBV!o KV?V?V#W 0V &V )V 3XKTKX )V EV 5X:X ;X X -Y =VLX DX -Y CY JY:Y NY9X HX ;"
3398 "Z-Y)WLW DX7Y MY,Q&X1Z'X -X +Y)Q)X/X'X -X -X?[ IX -XMV8VMX0XNX:X$Y1Y)X9Y KY1Y)X8X NX LX <X/X)X0X&X:VMV:X9Y?Y NY<Y *Y W 0V LW *ZCZ /W >W W "
3399 " 2W 4W ,W HW 3W :V MW LW:W Dg ?W ,X ,W8W >WLW ?Y 3X +Y1Y\"v#X0X)X?X?X+Y1Y MYNVNY :V :"
3400 "YNVNY BS 5X 8X<X HV /W $T?ZBT*c AT&T 7U?U &V 6U -W @hGU KU 6V;V >U1V U1V KW7V NWLW 7WLW 7WLW 7WLW 7WLW 7WLW 6XGX JY,Q&X -X "
3401 "-X -X -X -X -X -X ,X1Z(XNX:X$Y1Y'Y1Y'Y1Y'Y1Y'Y1Y P)P$Y3[)X/X'X/X'X/X'X/X'Y<Y Km BW8W +UDZ 7P 1W "
3402 " -Y8Y Y8Y Y8Y Y8Y Y8Y MV ;W.X /T 4T 7a ;k#nMn Nc 6P :W4W ?Z ?X6X KY #Y 0X 2VFV &VBV\"p KV?V?V#W 0V 'W )W 2XMTMX 'V FW "
3403 "5X:X ;X Y -X >VKX DX -X BX IX8X NX7W KP 1P =X <Y)X+XLX EX6X NY*P&X0Z(X -X ,Y'P)X/X'X -X -X@Z GX -XMV8VMX0XNX:X%Y/Y*X8X LY/Y*X8Y!X KX <X/X)X0"
3404 "X&X:VMV:X8YAY LY>Y *Z W 0W MW +ZAZ 0W >W W 2W 4W ,W HW 3W :V MW LW:W DSF[ @X -X "
3405 " -X8W ?WJW ?Y 4X ,Y/Y%z%X0X)X?X?X,Y/Y YMVMY ;V ;YMVMY CS 5X 5P*Q JW<W GV /W %TBbET/g BTGb?T 8U?U &V 7U 5_ ?hGU KU 6V;V "
3406 ">U2V NU2V$_7V NXLX 9XLX 9XLX 9XLX 9XLX 8WLW 6XGX KY*P&X -X -X -X -X -X -X -X ,X0Z)XNX:X%Y/Y)Y/Y)Y/Y)Y/Y)Y/Y\"R+R&Y3]*X/X'X/X'X/X'X/X&Y>Y Jp EW:Y "
3407 " +R@Y 7Q 2W .XEVFY\"X5Y\"X5Y\"X5Y\"X5Y NV ;X/X 0V 5T 8c <k#nNo e >^ AW4W ?Z >W6W KY "
3408 " \"Y 0X 2VFV &VCW#[LSKZ KV?V@V\"W 0V 'W )W 1XNTNX &V FW 6Y:Y <X NX -X ?WKX DX .Y CY IX8X NX7W NS 1S @X =X&X,WJW EX6X NY /X/Y(X -X ,"
3409 "Y /X/X'X -X -XAZ FX -XMW:WMX0XMX;X%Y/Y*X8Y MY/Y*X8Y!X KX <X/X)Y1X%W;WMW;W6XAX JX>X *Z NW 0W MW ,Z?Z 1W >W W 2W 4W ,W H"
3410 "W 3W :V MW LW:W DPAY ?Y .W -W6W @WJW >Y 5X ,X-X&_MXM_&X0X)X?X?X,Y/Y !YLVLY <V <YLVLY DS 6Y 6R,R JX>"
3411 "W FV /X 'TCfFT2i CUGfBT 9U?U &V 7U 5] >iGU KU 6V;V >U2V NU2V$]5V NWJW 9WJW 9WJW 9WJW 9WJW 9WJW 8XFX KY /X -X -X -X -X -X -X -X ,X"
3412 "/Y)XMX;X%Y/Y)Y/Y)Y/Y)Y/Y)Y/Y#T-T'Y3]*X/X'X/X'X/X'X/X%X>X Ir GW=\\ GY 9S 3W /XDVDX$X2X$X2X$X"
3413 "2X$X2X V ;X0X 0X 7T 8d <k#~`!g Bd DW4W ?[ ?X7W LY !X /X 2VFV &VCV#ZJSGV KV?VAV!W 0V 'V 'V /d $V FV 5X8X <X NX -X ?VJX DX"
3414 " .X BX HX8X Y7X #V 1V CX >X$X-WJW EX6X Y .X.Y)X -X -Y .X/X'X -X -XBZ EX -XLV:VLX0XMX;X&Y-Y+X7X NY-Y+X7X!X KX <X/X(X2X$X<VKV<X6YCY JY@Y +Z MW /"
3415 "V MW -Y;Y \"Z ;WDX 0Z 2XDW >Z <W !X :WDY IW ,W HX8X MY 3Z *X 3X &X 7] <W 3W :V MW ;X :W:W 4Y @[ )\\ (Y 6X 8QEV :[ "
3416 " JW6W @VIW =Y 6X -Y-Y(]JXJ]'X0X)X?X?X-Y-Y #YKVKY =V =YKVKY IZ 9X 6T.T JW>W FV .X (TDgFT3j CTFhDT 9U?U &V 8U 4\\ =iGU KU 6V"
3417 ";V >U3V MU3V#\\5V MWJW 9WJW 9WJW 9WJW 9WJW 9WJW 8XFX LY .X -X -X -X -X -X -X -X ,X.Y*XMX;X&Y-Y+Y-Y+Y-Y+Y-Y+Y-Y%V/V)Y3_+X/X'X/X'X/X'X/X%Y@Y Is HW?^ "
3418 "?Z /Z /Z /Z /Z /Z /Z6Y NZ 0Z /Z /Z /Z 8Y 1Y 3Z /Z /Z /Z /Z 3ZCV 5WDX DXCVCW%X0W%X0W%X0W%X0W V :X1X 0X 7T 9f =k#~`\"h Cf "
3419 "EW4W @\\ ?X8X LX !Y /X 2VFV 'VBV#XHSET KV?VAV!W 0V (W 'W .` \"V GW 5X8X <X NX -X @VIX DX .X BX HX8X X5W &Y 1Y FX >W\"W.XJX"
3420 " FX6X X -X.Y)X -X -X -X/X'X -X -XCZ DX -XLV:VLX0XLX<X&X+X+X7X NX+X+X7X!X KX <X/X(X2X$X<VKV<X5YEY HYBY +Z LW /W NW .Y9Y 'b ?WG^ 7b 9^GW A` Gl 2_GW"
3421 " MWG_ DW ,W ,W8Y MW ,WG^>^4WG_ 9` @WG^ 9^GW MWG\\ ;f Gm <W6W#X2X#W;X;W5Y7Y#W1X\"u 6W :V MW >^BV\"W:W 3X ?^ 0e AWG_ KV.X ?X <W6W HTG[ K}!WCWCW Ca"
3422 " 7p&{ NW6W AWHW >Z 7X -X+X)\\HXH\\(X0X)X?X?X-X+X $YJVJY >V >YJVJY Ma =X 7V0V JW@W EV .Y *TEiET5k DTEiDT :VAV &V 9U 3_ ;W6W NiGU "
3423 "KU 6V;V >U3V MU3V#_8V NXJX ;XJX ;XJX ;XJX ;XJX ;XJX :XEX LX -X -X -X -X -X -X -X -X ,X.Y*XLX<X&X+X+X+X+X+X+X+X+X+X&X1X*X3`+X/X'X/X'X/X'X/X$YBY Ht "
3424 "IW@_ Cb 7b 7b 7b 7b 7b 7b>a'b 7` 5` 5` 5` AW ,W ,W ,W DY EWG_ 9` 5` 5` 5` 5` (Z <`GV W6W MW6W MW6W MW6W#W1X NWG^ HW1X NWBVBW&W.W&WJP:PJW&W4PJW&W."
3425 "W!V :X2X 0X 6S 8g >k#~`#j Fj GW4W @\\ >W8W LX X .X 2VFV 'VBV$XGSCR KV?VBV X 1V (W 'W ,\\ V GW 5X8X <X NX -X AWIX DX /X "
3426 "BY HX8X X5W ([ 1[ HX ?W W/WHW FX6X!Y -X-Y*X -X .Y -X/X'X -X -XDZ CX -XLW<WLX0XKW<X'Y+X+X7X Y+X+X7X!X KX <X/X'X4X#X<VKV<X4XFY FXBX *Y KW /W NW "
3427 "/Y7Y +g AWIb ;f =bIW De Il 3bIW MWIc FW ,W ,W9Y LW ,WIbBb6WIc >f CWIb =bIW MWI^ =j Im <W6W\"W2W\"W<Z<W4X7X!W2W!u 6W :V MW @bEW\"W:W 2X @c 8j CW"
3428 "Ic MX0W =W <W6W IW/W\"VI^ L}!WCWCW Ee =t&{ W4W BWHW =Y 7X .X*Y*ZFXFZ(X0X)X?X?X.Y+X #WIVIW =V =WIVIW f ?X 8X2X KW@W EV .Z +SE[GVDS6ZDV "
3429 "DSDVDXDS 9UAU %V :U 2` <W6W NiGU KU 6V;V >U4V LU4V\"`:V GX /WHW ;WHW ;WHW ;WHW ;WHW ;WHW :XEX MY -X -X -X -X -X -X -X -X ,X-Y+XKW<X'Y+X,Y+X,Y+"
3430 "X,Y+X,Y+X'Z3Z,Y4WNY,X/X'X/X'X/X'X/X#XBX Gu JWB\\ Ag <g <g <g <g <g <gBe+f <e :e :e :e CW ,W ,W ,W Mc FWIc >f ;f ;f ;f ;f +Z >eJU NW6W MW6W MW6W MW6W\"W"
3431 "2W MWIb IW2W NWAVAW(W,W(WJR<RJW(W4RJW(W,W\"V 9W2X 1X 6T 9i ?k#~`#k Hl HW4W @] ?X9W LW NX .X 2VFV 'VCW$WFSAP KV?VBV NW 1V (V"
3432 " &W *X MV GV 5X6X =X NX -X AVHX DX /X BX GX8X X5X ,^ 1^ LX ?W MW0WHW FX6X!X ,X-Y*X -X .X ,X/X'X -X -XEZ BX -XKV<VKX0XKX=X'Y+Y,X7X Y+Y,X"
3433 "7X!X KX <X/X'X4X\"W=WKV<W3YGY FYDY +Z KW .V NW 0Y5Y /l CWJe ?j AeJW Eh Kl 5eJW MWJe GW ,W ,W:Y KW ,WJdDd7WJe @h DWJe AeJW MWJ_ ?l Im <W6W\"W2W!W=Z="
3434 "W2X9X W2W!u 6W :V MW BeFV!W;X 1W ?f =k CWJe NY2X =X =W6W JW-W$WI` N}!WCWCW Gi Av&{ W4W BVGW <Y 8X .X)X+ZEXEZ)X0X)X?X?X.Y+Y #UHVHU <V <UHVH"
3435 "U !j AX 9Z4Z KWBW DV -Z -TFY@RDT8XAV ETDVBWET :VCV %V ;V )X =W6W NiGU KU 6V;V >U5V KU5V GX<V FX /WHW ;WHW ;WHW ;WHW ;WHW ;WHW :WDX"
3436 " MX ,X -X -X -X -X -X -X -X ,X-Y+XKX=X'Y+Y-Y+Y-Y+Y-Y+Y-Y+Y'Z5Z+Y5WMY,X/X'X/X'X/X'X/X#YDY GX@^ KWCZ Al Al Al Al Al Al AlFh.j ?h =h =h =h EW ,W ,W ,W !g"
3437 " GWJe @h =h =h =h =h ,Z @hLV NW6W MW6W MW6W MW6W\"W2W MWJe KW2W W@VAW)W+W)WJT>TKW)W4TKW)W+W\"V 9X3X 2X 5T :k ?i\"~`$m Jn IW4W A^ ?X:X MW "
3438 " NY .X 2VFV 7~X2XFS <V?VCV MX 2V )W %W +X MV GV 5X6X =X NX -X BVGX DX /X BX GX8X X5X LX -X 7a 1a X @W KW2XHX GX6X!X ,X,X*X -X ."
3439 "X ,X/X'X -X -XFZ AX -XKV<VKX0XJW=X'X)X,X7X X)X,X7X!X KX <X/X'X4X\"X>VIV>X2YIY DYFY +Z JW .V NW 1Y3Y 1n DWLh Bm ChLW Gk Ll 6hLW MWKg HW ,W ,W;Y JW "
3440 ",WKfGg8WKg Cl FWLh ChLW MWK` @m Im <W6W\"X4X!W=Z=W1X;X NW3X!u 6W :V MW CgGV!W;W 0X ?g Am CWKg [4X >Y =W6W JW-W&YJb }!WCWCW Hk Dx&{ W4W CWFW <Y 9"
3441 "X /Y)X,ZDXDZ*X0X)X?X?X.X)X P #SGVGS %P 7V 9P0P CSGVGS !l BX 8ZGWFZ JWCX DV ,Z .SEW<PCS8V?V .P>P JSCVAVDS :WEV $V <V &W >W6W NiGU KU 6V"
3442 ";V BP>P /U5V KU5V EW=V FX 0XHX =XHX =XHX =XHX =XHX =XHX <XDX MX ,X -X -X -X -X -X -X -X ,X,X+XJW=X'X)X-X)X-X)X-X)X-X)X&Z7Z*X5WKX,X/X'X/X'X/X'X/X\"YFY F"
3443 "X=[ KWDY @n Cn Cn Cn Cn Cn CnHj1m Bk @k @k @k FW ,W ,W ,W $j GWKg Cl Al Al Al Al .Z Bs MW6W MW6W MW6W MW6W\"W3X MWLh LW3X V?V@W*V)W*VJV@VKW*V4VKW*V"
3444 ")W#V 9X4X 2X 4S :l ?i\"~`%o Lp JW4W A^ >W:X MW NX -X 2VFV 7~X2WES <V?VDV LX 2V )W %W -\\ V HW 5X6X =X NX .X BWGX DX 0X "
3445 "BY FX:X NX5X LX -X :d 1d $Y @V IV2WFW GX6X\"Y ,X,Y+X -X /Y ,X/X'X -X -XH[ @X -XKW>WKX0XJX>X(Y)X,X7X!Y)X,X7X!Y LX <X/X&X6X!X>VIV>X1YKY BXFX +Z IW .W "
3446 " W 2Y1Y 2o EWMj Dn DjMW Hn Nl 7jMW MWLi IW ,W ,W<Y IW ,WLhIi9WLi En GWMj EjMW MWLa An Im <W6W!W4W W=Z=W1Y=Y MW4W u 6W :V MW DiIV W;W /W =g C"
3447 "m CWLi![4W =Y =W6W KW+W(ZKd!}!WCWCW Im Fy&{ W4W CWFW ;Y :X /X'X-YCXCY*X0X)X?X?X/Y)X!R #QFVFQ $R 9V :R1R DQFVFQ \"n BX 7ZJ\\JZ HWDW CV +[ 1TFW.T:W?V"
3448 " /Q?Q KTCVAWET :XIX $V =V #U >W6W NiGU KU 6V;V BQ?Q 0U6V JU6V BU>V EX 0WFW =WFW =WFW =WFW =WFW =WFW <XDX NY ,X -X -X -X -X -X -X -X ,X,Y,XJ"
3449 "X>X(Y)X.Y)X.Y)X.Y)X.Y)X%Z9Z*Y6WJX,X/X'X/X'X/X'X/X!XFX EX;Z LWDX ?o Do Do Do Do Do DoKn4n Cn Cn Cn Cn HW ,W ,W ,W %l HWLi En Cn Cn Cn Cn /Z Cs LW6W MW6"
3450 "W MW6W MW6W!W4W LWMj LW4W W?V?V+W(V+WKXBXKV+W5XKV+W(V$W 8W4X 2X 5T ;n ?g!~_%p LZDZ JW4W A^ >W:W MW MX -X 2VFV 7~X2WES <WAW"
3451 "DV KX 3V )W %W /` \"V HV 4X6X =X Y .X BVFX DX 0X BX EX:X NX5X LX -X <e /e 'Y @V GV4XFX HX7X!X +X+X+X -X /X +X/X'X -X -XI[ ?X -XJV>VJX0XIW>X(X"
3452 "'X-X7X!X'X-X7X!Y LX <X/X&X6X!X>VIV>X1YKY AXHX +Z HW -V W 3Y/Y 3p FWMk Fo EkMW Io Nl 8kMW MWMk JW ,W ,W=Y HW ,WMjJj:WMk Gp HWMk GkMW MWMb Bo Im <W"
3453 "6W!W4W W>\\>W0X=X LW5X u 6W :V MW EkJV W<X /W >j Fn CWMk\"\\6X =Z >W6W KW+W)[Ke\"}!WCWCW Jo Hz&{ W4W DWDW ;Y ;X /X'X.YBXBY+X0X)X?X?X/X'X#T HV IT "
3454 ":V ;T3T :V CV +o BX 6ZM`MZ GXFX CV *\\ 3SFW,S:V>V 0R@R KSBV@VDS 9e #V ?W \"V ?W6W NiGU KU 6V;V BR@R 1U6V JU6V BV?V EX 1XFX ?XFX ?XFX ?XFX"
3455 " ?XFX ?XFW =XCX NX +X -X -X -X -X -X -X -X ,X+X,XIW>X(X'X/X'X/X'X/X'X/X'X%Z;Z)X5VHX-X/X'X/X'X/X'X/X XHX DX:Y LWEX >p Ep Ep Ep Ep Ep EpMp6o Do Do Do Do"
3456 " HW ,W ,W ,W 'o IWMk Gp Ep Ep Ep Ep 0Z Ds KW6W MW6W MW6W MW6W!W5X LWMk MW5X V>V?W,V'W,VKZDYKW,V5YKW,V'W%W 8X5W 2X 4T ;o @g ~^%q NY@Y KW4W B`"
3457 " ?X<X MV LX -X 2VFV 7~X2WES ;VAVDV JY 4V )V $W 1d $V HV 4X6X =X X .Y CWFX DXLY =XEX 'Y EY<X MX5X LX -X ?e )e +Y ?V:X6V4WDW "
3458 "HX7X!X +X+X+X -X /X +X/X'X -X -XJ[ >X -XJW@WJX0XIX?X(X'X-X7X!X'X-X8Y Y MX <X/X%W6X W?WIV>W/YMY @YJY +Y GW -V W 4X+X 4YE\\ FWNXG\\ H]EX F\\GXNW J\\F[ "
3459 "GW ,\\GXNW MWNXG[ JW ,W ,W?Z GW ,WNXH[KXH[:WNXG[ H]H] IWNXG\\ I\\GXNW MWNXFQ C\\CW CW ,W6W!X6X NW?\\?W.X?X JW6W 1X 6W :V MW 9X=X\"[IZKW W=Y /W @m H]DV "
3460 "CWNXG[\"\\6W =[ >W6W LW)W*ZJWKY\"}!WCWCW K\\H] J{&{ V3W DWDW :Y <X /X'X.XAXAX+X0X)X?X?X/X'X$V IV JV ;V <V5V ;V CV ,^MSKW BX 5x EWFW BV ,_ 5TFW,S:V?W"
3461 " 1SAS LTBV@VDS 9d \"V @W U ?W6W NiGU KU 5V=V ASAS 2U7V IU7V @U@V DX 1WDW ?WDW ?WDW ?WDW ?WDW ?XFX >XCX NX +X -X -X -X -X -X -X -X ,X+X,XIX?"
3462 "X(X'X/X'X/X'X/X'X/X'X$Z=Z(X6WHX-X/X'X/X'X/X'X/X YJY DX9Y MWEW =YE\\ EYE\\ EYE\\ EYE\\ EYE\\ EYE\\ EYE]N\\G[7]EX E\\F[ F\\F[ F\\F[ F\\F[ IW ,W ,W ,W (p IWNXG[ H]H"
3463 "] G]H] G]H] G]H] G]H] 1Z E]H^ JW6W MW6W MW6W MW6W W6W KWNXG\\ MW6W NV>V>V,V&V,VJZFYIV,V6YIV,V&V%W 7W6X 3X LR:T ;q @e N~^&s!Y>Y LW4W B` >W<X N"
3464 "W $x FX 2VFV 7~X2WES ;VAVEW IY 5V *W #W 4XNTNX &V IW 5X5X =X X .X CWEX Di AXH_ +X CX<X MX5X LX -X Be #e /Z @V<^IUDV5WDW HX8Y"
3465 "!X +X+X+X -X /X +X/X'X -X -XK[ =X -XIV@VIX0XHW?X(X'X-X7X!X'X-X8X NZ NX <X/X%X8X NX@VGV@X.c >XJX +Z GW -W !W 5X)X 5U>Z G_CZ I[>T FZC_ KZAZ HW -ZB_ "
3466 "M^BZ KW ,W ,W@Z FW ,^CZMVCZ;^BZ IZBZ I_CZ IZC_ M^ 5Y<S CW ,W6W W6W MW?\\?W.YAY JW6W 2Y 6W :V MW ;\\A\\%YDYLV NW>Y .W AXJa IZ<Q C^BZ MX8X =\\ ?W6W LW)"
3467 "W+YIXJY LW=W JWCWCW LZBZ K]F] ;W >W2W EWDW 9Y =X /X'X/YAXAY,X0X)X?X?X/X'X%X JV KX <V =X7X <V CV -\\JSHT BX 4v DXHX BV -b 7SEV*S;V?W 2TBT LSAV"
3468 "@VCS 9b !V AV MU ?W6W MhGU KU 5V=V ATBT 3U8V HU8V ?UAV CX 1WDW ?WDW ?WDW ?WDW ?WDW ?WDW ?XBX NX +X -X -X -X -X -X -X -X ,X+X,XHW?X(X'X/X'X/X'X"
3469 "/X'X/X'X#Z?Z'X7WGX-X/X'X/X'X/X'X/X NXJX CX9Y MWFW <U>Z FU>Z FU>Z FU>Z FU>Z FU>Z FU>eBZ9[>T FZAZ HZAZ HZAZ HZAZ JW ,W ,W ,W )r J^BZ IZBZ GZBZ GZBZ GZBZ"
3470 " GZBZ 1Z EZB[ JW6W MW6W MW6W MW6W W6W K_CZ MW6W V=V>V-V%V-VHZHYHV-V6YHV-V%V%W 7X7X 4X NU:T <s Ae N~^'u\"X<X LW4W BWNW >W<W MW "
3471 "$w EX 2~X2WES ;WCWEV GY 9W #W 5XMTMX 'V IV 4X4X >X !Y 0Y BVDX Dk CXJc -X BX>X LX5Y MX -X Ee Le 3Z ?U=bKUCU6XDX IX9Y X +X+X+X -X /X +X/X"
3472 "'X -X -XL[ <X -XIV@VIX0XHX@X(X'X-X8Y!X'X-X8X N[ X <X/X%X8X NX@VGV@X.c =XLX +Z FW ,V !W AR9Y H]?Y KZ:R GY?] LY=Y IW -Y?] M]@Y KW ,W ,WAY DW ,]@X"
3473 "NV@X;]@Y JY>Y J]?Y KY?] M] 4X8P CW ,W6W X8X MW?\\?W-XAX IW7X 3Y 5W :V MW =_C_(YBXLV NW?Z -W CXC\\ KY ,]@Y LW8X >] ?W6W LW)W,YHWHY MW=W JWCWCW MY>Y "
3474 "L[B[ ;W >W2W FWBW 9Y >X 0X%X0X@X@X,X0X)X?X?X/X'X&Y JV KY =V >Y7Y =V CV .[HSFR BX 3t BWHW AV .WN\\ 9SFV)S;V?W 3UCU LSAV@VCS 7_ V BV LU ?W"
3475 "6W MhGU KU 5W?W AUCU 4U8V HU8V ?UAV CX 2XDX AXDX AXDX AXDX AXDX AXDX @XBX NX +X -X -X -X -X -X -X -X ,X+X,XHX@X(X'X/X'X/X'X/X'X/X'X\"ZAZ&X8WFX-X/X'"
3476 "X/X'X/X'X/X MXLX BX8X MWFW <R9Y GR9Y GR9Y GR9Y GR9Y GR9Y GR9a>Y;Z:R GY=Y JY=Y JY=Y JY=Y KW ,W ,W ,W *]E[ J]@Y JY>Y IY>Y IY>Y IY>Y IY>Y 2Z FY>Y JW6W MW"
3477 "6W MW6W MW6W W7X K]?Y NW7X V=V=U-V$U-VGZJYFU-V7YFU-V$U%W 7X8X &~X/X:T =t @c L~\\'v\"W:W LW4W CXNX ?X>X MV $x EX 2~X2WES :VDW"
3478 "EV FZ :W #W 7XKTKX )V IV 4X4X >X !X 0Y BWDX Dm FXKf /Y AYBY KX5Y MX -X Gd ~X d 5Y ?V>dLUCU6WBW IX;Z Y +X+Y,X -X 0Y +X/X'X -X -XM[ ;X -XIWBWIX"
3479 "0XGW@X)Y'Y.X8X!Y'Y.X9Y M] #X <X/X$X:X MX@VGV@X-a <YNY ,Z EW ,V !W AP6X H\\=Y LY7P HY=\\ LX;X IW .Y=\\ M[=X KW ,W ,WBY CW ,[=]=W;[=X KY<Y K\\=Y MY=\\ "
3480 "M\\ 4X *W ,W6W NW8X MW@VNV@W,XCX GW8W 3Y 4W :V MW >aEa)X@XNW NWA[ ,W DW?[ LX +[=X KW:X =] ?W6W MW'W-XGWGX MW=W JWCWCW MX<Y NZ>Z <W >W2W FWBW 9Z ?X"
3481 " 0X%X0X@X@X,X0X(X@X@X/Y'Y(Y IV JY >V ?Y5Y >V CV .YFSDP BX 2q @XJX AV /WK[ :SFV)S;V@X 4VDV LSAV@VCS 6\\ MV CV KU ?W6W MhGU KU 4V?V @V"
3482 "DV 5U9V GU9V >UBV BX 2WBW AWBW AWBW AWBW AWBW AXDX @XBX Y +X -X -X -X -X -X -X -X ,X+Y-XGW@X)Y'Y1Y'Y1Y'Y1Y'Y1Y'Y\"ZCZ&Y9WEY.X/X'X/X'X/X'X/X MYNY BX8Y N"
3483 "WFW <P6X GP6X GP6X GP6X GP6X GP6X GP6_<X;Y7P GX;X JX;X JX;X JX;X KW ,W ,W ,W *Z?Y K[=X KY<Y KY<Y KY<Y KY<Y KY<Y 3Z GY<Y KW6W MW6W MW6W MW6W NW8W J\\=Y "
3484 "NW8W NV=V=V.V$V.VFZLYEV.V8YEV.V$V&W 6W8X &~X2\\<T =v Ab K~\\(x$W8W MW4W CXNX ?X>X NW $w DX $VBV#XFS :WFXEV H] ;W #W 9XITIX"
3485 " +V JW 4X4X >X \"Y 3[ BWCX Dn GXLi 1X ?ZFZ JY7Z MX -X Je M~X Me 9Y >U?gMUCV7WBW IX>\\ NX *X*X,X -X 0X *X/X'X -X -XNZ 9X -XHVBVHX0XGXAX)X%X.X9Y!X%"
3486 "X.X:Y La 'X <X/X$X:X LWAWGV@W+_ :XNX ,Z DW ,W \"W &W H[;X MY .X;[ MX9X JW .X;[ M[<X LW ,W ,WCY BW ,Z<\\<X<[<X LX:X K[;X MX;[ M[ 3W )W ,W6W NW8W KW"
3487 "AVNVAW*XEX FW9X 4Y 3W :V MW ?cGc+Y?WNV MWD] +W DV=Z LX +Z;X LW:X >_ @W6W MW'W.YGWFX NW=W JWCWCW NX:X NY<Y <W >W2W FWBW 8Z @X 0X%X0X@X@X,X0X(X@X@X"
3488 "/X%X)Y HV IY ?V @Y3Y ?V CV /YES 6X 1\\H[ JcJc LV 0WI\\ =TFV)S;WAX 5WEW MTAVAWCS 3W 4~W.W KV ?W6W LgGU KU 4WAW @WEW 6U9V GU9V ?VBV BX 2"
3489 "WBW AWBW AWBW AWBW AWBW AWBW AXAX X *X -X -X -X -X -X -X -X ,X*X-XGXAX)X%X1X%X1X%X1X%X1X%X!ZEZ%X9WCX.X/X'X/X'X/X'X/X LXNX AX7X NWFW !W ,W ,W ,W ,W ,W "
3490 ",]:X=Y .X9X LX9X LX9X LX9X LW ,W ,W ,W +Z=X K[<X LX:X KX:X KX:X KX:X KX:X 3Z GX<Z KW6W MW6W MW6W MW6W NW9X J[;X NW9X NU<V=V.U#V.UDZNYDV.U8YDV.U#V&"
3491 "V 5X9W %~X3]<T >x A` J~\\(y%W8W MW4W CXMW >W>W MV $x DX $VCV\"XFS 9XIXEV H_ <W #W ;YHTHY -V JV 3X4X >X #Y ?g AVBX Do HXM"
3492 "k 3Y >l HX7Z MX -X Me J~X Je =Y >V?hNUBU8XBX Ju MX *X*X,w Lq IX *~R'X -X -c 8X -XHVBVHX0XFWAX)X%X.X9Y!X%X.X;Z Ke ,X <X/X$X:X LXBVEVBX+_ 9` +Y CW +V \""
3493 "W %W IZ9X NX .X9Z MW7W JW /X9Z MZ;X LW ,W ,WDY AW ,Z;[;W<Z;X MY:Y LZ9X X9Z MZ 2W )W ,W6W NX:X KWAVNVAW*YGY EW:W 4Z 3W :V MW ?XMYIe,X>WNV MW"
3494 "Ib +W EW;Y MW *Z;X KV:W =_ @W6W NW%W/XFWFX NW=W JWCWCW NW8X!Y:Y =W >| GW@W 8Y @X 0X%X1Y@X@Y-X0X(X@X@X/XImIX*Y GV HY @V AY1Y @V CV /XDS 6X 0YDY JdL"
3495 "d LV 1WF[ >SFV'S<WBY 6XFX MS@VAVAS @~W/W JU >W6W LgGU KU 3WCW ?XFX 7U:V FU:V >UBV AX 3XBX CXBX CXBX CXBX CXBX CXBX BXAw?X *w Lw Lw Lw "
3496 "LX -X -X -X ,X*X-XFWAX)X%X1X%X1X%X1X%X1X%X ZGZ$X:WBX.X/X'X/X'X/X'X/X K` @X7X NWFW W ,W ,W ,W ,W ,W ,[8W=X -W7W LW7W LW7W LW7W LW ,W ,W ,W ,Y:X LZ;X M"
3497 "Y:Y MY:Y MY:Y MY:Y MY:Y \"Y=\\ LW6W MW6W MW6W MW6W MW:W IZ9X NW:W NV<V=V/V#V/VCcCV/V9YCV/V=X>V&V 4W:X %~X2TNV<S =y KWM^LW$~Z({&W7V MW4W CWLX ?"
3498 "X?W MV KX ,X %VBV!XGS 9gFV Ha >W \"W ;WFTFW -V JV 3X4X >X #Y ?f AWBX Dp IXNm 4X <j GX7Z MX -X !e G~X Ge AY =U?ZH^BU8W@W Jt "
3499 "LX *X*X,w Lq IX *~R'X -X -b 7X -XHWDWHX0XFXBX)X%X.X:Y X%X.X<Z Ih 0X <X/X#X<X KXBVEVBX*] 8` ,Z CW +V \"W %W IZ9X X -X9Z NX7X KW /X9Z MY9W LW ,W ,W"
3500 "EY @W ,Y:Z:W<Y9W MX8X LZ9X X9Z MY 1W )W ,W6W MW:W JWAVNVAW)XGX DW:W 4Y 3X :V MW @VHXKWGV,W<^ MWIa *W FW9Y NW *Y9W KW<X >` @W6W NW%W/WEWEW NW=W JW"
3501 "CWCW X8X!X8X =W >| GW@W 7Y AX 0X%X1X?X?X-X0X(X@X@X/XImIX+Y FV GY AV BY/Y AV DX 1XCS 6X 0W@X KdLd LV 1VCZ ?SFV'S;WE[ 7XFX G~X .S@VBWAS @~W0W "
3502 ".P>W >W6W KfGU KU 3XEX >XFX 8U;V:W3U;VCZ9P>WCV:W/Y 3W@W CW@W CW@W CW@W CW@W CXBX CX@w?X *w Lw Lw Lw LX -X -X -X 5p9X-XFXBX)X%X1X%X1X%X1X%X1X%X N"
3503 "ZIZ#X:VAX.X/X'X/X'X/X'X/X K` @X7X NWFW W ,W ,W ,W ,W ,W ,[8X?X -X7X NX7X NX7X NX7X MW ,W ,W ,W ,X9X LY9W MX8X MX8X MX8X MX8X MX8X \"X=] LW6W MW6W MW6"
3504 "W MW6W MW:W IZ9X NW:W NVLuKU/VLuKU/VBaAU/V:YAU/V=X=U&V 4X;X %~X2RLW>T >{!z'~Z)}(W6W NW4W DXLX ?X@X MV KX ,X %VBV!YHS 8eEV"
3505 " Ic ?W !W ;UETEU ,V KW 3X4X >X $Y >c ?WAX DWD^ JbG] 5X 9d DY9[ MX -X #d D~X Dd DY <U@YD\\BU9X@X Kq IX *X*X,w Lq IX *~R'X -X -a 6X -XGVDVGX0XEWB"
3506 "X)X%X.X;Z X%X.X?\\ Gk 4X <X/X#X<X KXBVEVBX)[ 6^ ,Z BW +W #W %W IY7W X -W7Y NW5W KW 0X7Y MY9W LW ,W ,WFY ?W ,Y:Z:W<Y9W MW6W LY7W W7Y MY 1W )W ,W6W"
3507 " MW:W JWBVLVBW(XIX CW;X 5Y 2X :V MX BUDVKVDU.X<] LWI_ :WEW FV7X NW *Y9W JV<X >a AW6W NW%W0XEWEX W=W JWCWCW W6W!X8X =W >| HX@X 7Y BX 0X%X1X?X?X-X0"
3508 "X(X@X@X/XImIX,Y EV FY BV CY-Y BV DX 1XCS 6X 1W>W KeNe LV 1VB[ ASFV'S;YI] 9YGY F~X .S@VDX@S @~W1V ,TEZ >W6W JeGU IX +U 2YIY <YGY :U;V:W3U"
3509 ";VGa<TEZCV:W/X 3X@X EX@X EX@X EX@X EX@X EX@X DX@w?X *w Lw Lw Lw LX -X -X -X 5p9X-XEWBX)X%X1X%X1X%X1X%X1X%X MZKZ\"X;WAX.X/X'X/X'X/X'X/X J^ ?X7X NWFX !W "
3510 ",W ,W ,W ,W ,W ,Z6W?X -W5W NW5W NW5W NW5W MW ,W ,W ,W -X7W LY9W MW6W MW6W MW6W MW6W MW6W \"W=^ LW6W MW6W MW6W MW6W MW;X IY7W NW;X NVLuKU/VLuKU/VA_"
3511 "@U/V;Y@U/V=X=U&V 4X<X $~X,W>T ?|\"}(~X)~(W6W NW4W DXKW >W@X MV KX ,X %VBV!ZIS 7cEV IYNZ8W 0W !W :RCTCR +V KW 3X4X >X %Y"
3512 " =b >V@X DS=\\ K`C[ 6Y 8b BX9[ Nd A~X Ad HY <VAX@ZBV:X?W Kq IX *X*X,w Lq IX *~R'X -X -a 6X -XGVDVGX0XEXCX)X%X.X=[ NX%X.u Fl 6X <X/X\"W<W IWCWEVBW([ "
3513 "5\\ ,Z AW +W #W $V IY7X\"X -X7Y NW5W KW 0X7Y MX8X MW ,W ,WHZ >W ,X8X8W=X8X X6X MY7X\"X7Y MX 0W )W ,W6W MX<X IWCVLVCW&XKX AW<W 5Y 1W 9V LW 4P /TB"
3514 "VMVBT.X;\\ LWI` =\\HW GW7X NW *X8X KV=X >XMW AW6W NW%W0XEWDW W=W JWCWCW!X6X#X6X >W >| HW>W 6Y CX 0X%X1X?X?X-X0X'XAXAX.XImIX-Y DV EY CV DY+Y CV DX 2X"
3515 "BS 6X 1V<V KeNe LV 2V?Y ASFV'S:dNV :XFY E~X .S@i?S @~W2i >h =W6W JeGU IX 4g :g :YFX DgEV:X<gEVHe>hCV:X/X 3X?W EX?W EX?W EX?W EX?W EX@X EX?w?"
3516 "X *w Lw Lw Lw LX -X -X -X 5p9X-XEXCX)X%X1X%X1X%X1X%X1X%X LZMZ!X<W@X.X/X'X/X'X/X'X/X I\\ >X7X NWFY !V +V +V +V +V +V +Y6W@X ,W5W NW5W NW5W NW5W MW ,W ,W"
3517 " ,W -X7X MX8X X6X X6X X6X X6X X6X $X=_ MW6W MW6W MW6W MW6W LW<W HY7X NW<W MVLuKU/VLuKU/V@]?U/V<Y?U/V=X=U&V 3W<X $~X+V>S >}%~R)~V(~P)W6W NW4W"
3518 " DWJX ?XAW L~^ $X ,X %VCV N\\LS 6aDVAW0XLZ9W 0W !W :PATAP +V KV 2X4X >X &Z =e BW@X DP8[ L^?Z 7X :h EY;\\ \"d >~X ?e LY ;U@W>Y"
3519 "AU:W>W Ks KX *X*X,w Lq IX6f+~R'X -X -b 7X -XGWFWGX0XDWCX)X%X.X@^ NX%X.s Bl 8X <X/X\"X>X IXDVCVDX)[ 4\\ -Z @W *V #W $W JX5W\"X -W5X W4W KW 0W5X MX7W"
3520 " MW ,W ,WIZ =W ,X8X8W=X7W W4W MX5W\"W5X MX 0X *W ,W6W LW<W HWCVLVCW&YMY AW=X 6Y 1X 9V LX 1X.Q /TA]AU/W:\\ LWIb A`JW GV5X NW +X7W KW>X >XMX BW6W W#W1WD"
3521 "WDW W=W JWCWCW!W4W#X6X >W >| HW>W 7Y BX 0X%X1X?X?X-X0X'XAXAX.XImIX.Y CV DY DV EY)Y DV DX 2XBS 6X 2W<W =^ =V 2V>Y BSFV'S9bMV ;XFY D~X .S@h>S "
3522 "@~W2i >g <W6W HcGU IX 4g 9e 8YFX EgEV;Y<gEVHf?gBV;Y0Y 3W>W EW>W EW>W EW>W EW>W EW>W EX?w?X *w Lw Lw Lw LX -X -X -X 5p9X-XDWCX)X%X1X%X1X%X1X%X1X%X "
3523 "Ke X=W?X.X/X'X/X'X/X'X/X I\\ >X7X NWEY \"W ,W ,W ,W ,W ,W ,X5W@X -W4W W4W W4W W4W MW ,W ,W ,W -W6X MX7W W4W W4W W4W W4W W4W $W=VMW MW6W MW6W MW6W MW6W "
3524 "LW=X HX5W NW=X MVLuKU/VLuKU/V?[>U/V=Y>U/V=X=U&V 3X=W 7X FW@T ?~&~T*~V)~R*W5V NW4W EXJX ?XBX L~^ $X ,X &VBV Mb 4]CVC]4XJZ:W"
3525 " 0W !W +T KV KV 2X4X >X 'Z <g EW?X +Z L]=Z 9Y <l GZ=] %e e!Y :UAW<XAU;X>X Lu MX *X*X,w Lq IX6f+~R'X -X -c 8X -XFVFVFX0XDXDX)X%X.u MX%X.r"
3526 " ?l :X <X/X\"X>X IXDVCVDX)\\ 4Z ,Y ?W *V #W $W JX5W\"W ,W5X W3W LW 0W5X MX7W MW ,W ,WJY ;W ,X8X8W=X7W W4W MX5W\"W5X MX 0X *W ,W6W LW<W HWCVKUCW%XMX "
3527 "?W>W 6Y 0X 9V LX 5`3R 0T?[?T/W:[ KWId DbKW HW5X NW +X7W JV>W =WLX BW6W W#W1WDWDW W=W JWCWCW!W4W#W4W >W >| IX>X 9Y AX 0X%X1X?X?X-X0X'XAXAX.XImIX/Y B"
3528 "V CY EV FY'Y EV DX 2WAS ?r CV:V =^ =V 2V=Y CSFV'S8`LV <XFX B~X .S@e;S @~W2i >e :W6W GbGU IX 4g 8c 5XFX FgFV:Y<gFVGg@eAV:Y1Y 3X>X GX>X GX>"
3529 "X GX>X GX>X GX>X FX?w?X *w Lw Lw Lw LX -X -X -X 5p9X-XDXDX)X%X1X%X1X%X1X%X1X%X Jc NX>W>X.X/X'X/X'X/X'X/X HZ =X7X NWEZ #W ,W ,W ,W ,W ,W ,X4WAW ,W3W!W3"
3530 "W!W3W!W3W NW ,W ,W ,W .X5W MX7W W4W W4W W4W W4W W4W $W>VLW MW6W MW6W MW6W MW6W KW>W GX5W MW>W LVLuKU/VLuKU/V>Z>U/V>Y=U/V=X=U&V 2W>X 8Y FW@T "
3531 " ?~P(~V*~T(~Q)V4V NW4W EXJX >WBX L~^ $X ,X &VBV Ld 4WAVD`6XHZ;W 0W !W +T KV LW 2X4X >X 'Y ;i GV>X *Z M\\;Y 9X =p HZ?^ 'd "
3532 " Id$Y 9UAW<XAU;W<W Lw X *X*X,w Lq IX6f+~R'X -X -d 9X -XFVFVFX0XCWDX)X%X.t LX%X.p ;k ;X <X/X!X@X HXDVCVDX*^ 4X ,Z ?W *W $W $W JX5W\"W ,W5X W3W LW"
3533 " 0W5X MW6W MW ,W ,WKY :W ,W7W7W=W6W W4W MX5W\"W5X MX /Y ,W ,W6W LX>X GWEVJVEW#a >W>W 7Y 1Y 8V KY 9e8T 0T?Z>T0X:[ KWIf GdLW HW4W MW ,W6W JV?X >XKW BW6"
3534 "W W#W2XDWDX!W=W JWCWCW!W4W#W4W >W >| IW<W :Y @X 0X%X1X?X?X-X0X&XBXBX-XImIX0Y AV BY FV GY%Y FV DX 2WAS ?r DW:W =\\ <V 2V;W CSFV'S7]JV =XFX A~X .S"
3535 "@d:S (V Ii <a 8W6W FaGU IX 4g 6_ 2XFX GgGV:Z<gGVFUFY?a@V:Z2Y 2W<W GW<W GW<W GW<W GW<W GX>X GX>w?X *w Lw Lw Lw LX -X -X -X 5p9X-XCWDX)X%X1X%X1X%",
3536 // Start of second string.
3537 "X1X%X1X%X Ia MX?W=X.X/X'X/X'X/X'X/X GX <X7X NWDZ $W ,W ,W ,W ,W ,W ,X4WAW ,W3W!W3W!W3W!W3W NW ,W ,W ,W .W4W MW6W W4W W4W W4W W4W W4W $W?VKW MW6W MW6W"
3538 " MW6W MW6W KW>W GX5W MW>W LVLuKU/VLuKU/V?\\?U/V?Y<U/V=X=U&V 2W>X 8X DWBT ?~Q)~W)~R&~(V4V NW4W EWHW >WBW K~^ $X ,X &VBV Kg \""
3539 "VEc8WFZ=W /W !W +T 4~W 5V 1X4X >X (Y -] IW>X )Y M[9X 9X >\\F\\ H[C` 'a Ca$Y 9UAV:WAU;W<W LX<\\!X *X*X,X -X 0X6f+X/X'X -X -XN[ :X -XEVHVEX0XCX"
3540 "EX)X%X.s KX%X.o 6h <X <X/X!X@X GWDVCVDW*_ 4X -Z >W )V $W 6i JX5X$X -X5X V2W LW 1W3W MW6W MW ,W ,WLY 9W ,W7W7W=W6W!X4X NX5X$X5X MW .[ .W ,W6W KW>"
3541 "W FWEVJVEW#a >W?X 8Z 4\\ 8V K[ =i<V 0S=Y=S0X:[ KW@^ IfMW HW4W MY .W6W JW@W =XKX CW6W W#W2WCWCW!W=W JWCWCW\"X4X%X4X ?W >W2W IW<W :Y @X 0X%X1X?X?X-X0X&X"
3542 "BXBX-X%X1~` GV H~` GV H~` GV DX 3XAS ?r DV8V =\\ <V 2V;X DSFV'S4W /XFX @~X .S@VIX;S (V Ii 8Z 5W6W D_GU IX 4g 3Y .XFX HgGV;TNU<gGVFQ@W;Z=V;T"
3543 "NU3Y 1W<W GW<W GW<W GW<W GW<W GW<W GX>X X *X -X -X -X -X -X -X -X ,X*X-XCXEX)X%X1X%X1X%X1X%X1X%X H_ LX@W<X.X/X'X/X'X/X'X/X GX <X7X NWD\\ 8i >i >i >i >i"
3544 " >i >i3WBX ,V2W!V2W!V2W!V2W NW ,W ,W ,W .W4W MW6W!X4X\"X4X\"X4X\"X4X\"X4X M~Y2X@VIW NW6W MW6W MW6W MW6W KW?X GX5X NW?X LVLuKU/VLuKU/V@^@U/V@Y;U/V=X=U&"
3545 "V 2X?W 8X CWBT ?~R*~X)~Q%}(V4W W4W FXHX ?XDX K~^ $X ,X 'WCV Ii &VEe:XEZ>W /W !W +T 4~W 5V 1X4X >X )Y )[ KW=X (Y N[9Y ;Y "
3546 "?Z@Z I]Gb '^ =^$X 9U@V:WAU<X<X MX9Z\"X *X*X,X -X 0X6f+X/X'X -X -XM[ ;X -XEVHVEX0XBWEX)X%X.r JX%X.q 4e =X <X/X!X@X GXFVAVFX*` 5X .Z =W )V $W "
3547 " :m JW3W$W ,W3W!W2W LW 1W3W MW6W MW ,W ,WMY 8W ,W7W7W=W6W!W2W NW3W$W3W MW -^ 2W ,W6W KX@X FWEVJVEW\"_ <W@W 7Y :b 7V Jb FmAX 0S<W<S0W8Y JW<[ KYHVMV GV"
3548 "3X MZ 0W6W IVAX >XIW CW6W!W!W3WCWCW!W=W JWCWCW\"W2W%W3X ?W >W2W JW;X <Y ?X 0X&Y1X?X?X-X0X&YCXCY-X%X2~a GV H~a HV I~b HV DX 3W@S ?r DV8V <Z ;V 2W;W "
3549 "DSFV'S <XFX =V .S@VGW<S (V \"W6W A\\GU IX 2XFX *V;TMU LV2V V;TMU4Z 2X<X IX<X IX<X IX<X IX<X IX<X IX=X X *X -X -X -X -X -X -X -X ,X*X-XB"
3550 "WEX)X%X1X%X1X%X1X%X1X%X G] KX@V;X.X/X'X/X'X/X'X/X GX <X8Y NWC\\ =m Bm Bm Bm Bm Bm Bm3WBW ,W2W\"W2W\"W2W\"W2W NW ,W ,W ,W /X4X NW6W!W2W\"W2W\"W2W\"W2W\"W2W M~Y"
3551 "2W@VHW NW6W MW6W MW6W MW6W JW@W FW3W MW@W KVLuKU/VLuKU/VA`AU/VAY:U/V=X=U&V 1W@X 9X BWBS >~R+~Z*~P#{'V4W W4W FXHX ?XDX K~^ $X "
3552 " ,X 'VBV Gi (VFg;WCZ?W /W !W +T 4~W 6W 1X4X >X *Y &Z LW=X (Y NZ7X ;X ?Z>Z ImNX '[ 8\\%Y 9UAW:WAU<W:W MX7Y#X *X*X,X -X 0X6f+X/X'X -X -XL[ "
3553 "<X -XEWJWEX0XBXFX)X%X.p HX%X.r 0a >X <X/X XBX FXFVAVFX+b 6X /Z <W )W %W =p JW3W$W ,W3W!| LW 1W3W MW6W MW ,W ,WNY 7W ,W7W7W=W6W!W2W NW3W$W3W MW -"
3554 "b 6W ,W6W JW@W EWFVHVFW!] ;WAX 8Y 9` 5V H` HrG[ 0S<W<S0W8Y JW:Y KXF^ HW2W Kc ;W6W IVAX >XIW CW6W!W!W3WCWCW!W=W JWCWCW\"W2W%W2W ?W >W2W JW:W =Y >X 0Y'"
3555 "X0X?X?X-X0X%XCXCX,X%X2~a GV H~a HV I~b HV DX 3W@S ?r DV8V <Z FW;W DSFV'S =XFX <V .S@VFW=S (V \"W6W <WGU IX 1XFX +V;SLU LV2V V;SL"
3556 "U5Z 1W:W IW:W IW:W IW:W IW:W IX<X IX=X X *X -X -X -X -X -X -X -X ,X*X-XBXFX)X%X1X%X1X%X1X%X1X%X F[ JXAW;X.X/X'X/X'X/X'X/X GX <X8X MWB] Bp Ep Ep Ep Ep "
3557 "Ep E~eBW ,|\"|\"|\"| NW ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W M~Y2WAWHW NW6W MW6W MW6W MW6W JWAX FW3W MWAX KV<V=V/V#V/VBbCV/VBY:V/V=X>V&V 1XAW 9"
3558 "X @WDT ?~S+~Z)}!y'W4W W4W FWFW >WDW J~^ *r ?V &VBV Eh *VEXIX<XBZ@W /W !W +T 4~W 5f 8V 0X4X >X +Y $Z NW<X 'X NZ7X ;X ?X:X HkMX "
3559 " '[ 7[%X 8UAV8VAU=X:X NX6X#X *X*X,X -X 0X6f+X/X'X -X -XK[ =X -XDVJVDX0XAWFX)X%X.m EX%X.XA\\ -^ ?X <X/X XBX FXFVAVFX,c 6X /Y ;W (V %W ?r JW3W"
3560 "$W ,W3W!| LW 1W3W MW6W MW ,W ,a 6W ,W7W7W=W6W!W2W NW3W$W3W MW ,e :W ,W6W JW@W DWGVHVGW N[ 9WBW 8Y 8^ 3V F^ I~X 0S;U;T1W8Y JW8X MXC\\ HW2W Ia ;W6W IWB"
3561 "W >XHX DW6W!W<W<W3WCWCW!W=W JWCWCW\"W2W%W2W ?W >W2W KX:X ?Y =X /X'X0Y@X@Y-X0X%YDXDY,X%X2~a GV H~a HV I~b HV DX 3W@S ?r DV8V ;X DW;V DSFV'S >XFX "
3562 " ;V .S@VFW=S (V \"W6W :UGU IX 0XFX -V;TLU MV0U!V;TLU6Y 0X:X KX:X KX:X KX:X KX:X KX:X JW<X X *X -X -X -X -X -X -X -X ,X*X-XAWFX)X%X1X%X1X"
3563 "%X1X%X1X%X F[ JXBW:X.X/X'X/X'X/X'X/X GX <X9Y MWA] Er Gr Gr Gr Gr Gr G~gBW ,|\"|\"|\"| NW ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W M~Y2WBWGW NW6W MW6W MW6W "
3564 "MW6W IWBW EW3W LWBW IU<V=V.U#V.UCdDV.UCY9V.U=X>V&V 1XBX :X ?WDT ?~S,~[({ x&W4W W4W FWFX ?XFX JV \"q >V &VBV Af -VEXGX=W@ZB"
3565 "W .W !W +T 4~W 5f 8V 0X4X >X ,Y \"Y W;X 'X NZ7X <Y @Y:Y HiLX '^ =^%X 8UAV8VAU=X:X NX5X$X *X*X,X -X 0X(X+X/X'X -X -XJ[ >X -XDVJVDX0XAXGX)X%X.i"
3566 " AX%X.X>Z ,\\ ?X <X/X NWBW DWFVAVFW+XMY 7X 0Z ;W (V %W @s JW3W$W ,W3W!| LW 1W3W MW6W MW ,W ,` 5W ,W7W7W=W6W!W2W NW3W$W3W MW +g =W ,W6W JXBX DWGVH"
3567 "VGW N[ 9WBW 9Y 7^ 3V F^ I[Gr /S;U;T1W8X IW7X NWA[ HW2W F^ ;W6W HVCX >XGW DW6W!W<W<W3WCWCW!W=W JWCWCW\"W2W%W2W ?W >W2W KW9X ?Y =X /X'X/X@X@X,X0X$YEXEY"
3568 "+X%X2~a GV H~a HV I~b HV DX 3W@S 6X 3V8V ;X DX<V DTFV)T >WEW :V .TAVEW?T (V \"W6W :UGU IX /WEW .V;TKU NV/U\"V;TKU7Y /X:X KX:X KX:"
3569 "X KX:X KX:X KX:X KX<X X *X -X -X -X -X -X -X -X ,X*X-XAXGX)X%X1X%X1X%X1X%X1X%X G] KXCW9X.X/X'X/X'X/X'X/X GX <X9Y MW?] Hs Hs Hs Hs Hs Hs H~hBW ,|\"|\"|\"|"
3570 " NW ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W M~Y2WBVFW NW6W MW6W MW6W MW6W IWBW EW3W LWBW IU<V=V.U#V.UDYMZEV.UDY8V.U#V&V 0WBX ;X >WDS >~T-~\\(y"
3571 " Mw&W4W W4W GXFX ?XFX JV #r >V 'WCV <c .VEWEW=W?ZCW .W !W :~W 5f 9W 0X4X >X -Y Y!W;X 'Y Y5X =X @Y8Y HgKX 'a Ca%X 8UAV8"
3572 "VAU=W8W NX4X%X *X+Y,X -X 0X(X+X/X'X -X -XI[ ?X -XDWLWDX0X@WGX)X&Y.X 0X&Y.X=Y *[ @X <X/X NXDX DXHW@VHX,YMZ 8X 1Z :W (W &W At JW3W$W ,W3W!| LW 1W3"
3573 "W MW6W MW ,W ,` 5W ,W7W7W=W6W!W2W NW3W$W3W MW )g ?W ,W6W IWBW CWGVHVGW MY 8WCX :Y 6` 5V H` IW@m -S;V<T1W8X IW7X W@[ HW2W Ia ;W6W HVCW >XFX EW6W!W<W<"
3574 "W3WCWCW!W=W JWCWCW\"W2W%W2W ?W >W2W KW8W @Y <X /X'X/X@X@X,X0X#YFXFY*X&Y2~a GV H~a HV I~b HV DX 3W@S 6X 3V8V ;X CX=V CSFV)S =WEW :V -SAVDW@S "
3575 " 'V \"W6W :UGU IX /WEW .V<TJU NV/U\"V<TJU8Z /W8W KW8W KW8W KW8W KW8W KX:X KX<X X *X -X -X -X -X -X -X -X ,X+Y-X@WGX)X&Y1X&Y1X&Y1X&Y1X&Y H_ LX"
3576 "DW9Y.X/X'X/X'X/X'X/X GX <X:Y LW>] Jt It It It It It I~iBW ,|\"|\"|\"| NW ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W M~Y2WCVEW NW6W MW6W MW6W MW6W IWCX EW3W L"
3577 "WCX IV=V=V.V$V.VFYKZFV.VFY7V.V$V&V 0XCW ;Y =WFT >~T-~\\'w Ku%W4W W4W GXEW >WFW IV #q =V 6~X JSN^ /VEWCW?W=ZDW .W !W :~W"
3578 " 5f 9V /X4X >X .Y MX\"W:X &X Y5X >Y @X6X FcJX &d Id%X 8UAV8VAU>X8X X4X$X +X+X+X -X /X)X+X/X'X -X -XH[ @X -XCVLVCX0X@XHX(X'X-X /X'X-X<Y *Z @X "
3579 "<X/X NXDX DXHV?VHX-YKY 8X 2Z 9W 'V &W B]?W JW3W$W ,W3W!| LW 1W3W MW6W MW ,W ,a 6W ,W7W7W=W6W!W2W NW3W$W3W MW 'g AW ,W6W IWBW CWHVFVHW NZ 7WDW :Z"
3580 " 6a 6V Jb IU;i ,S;V<S0W7W IW6W W?Z HW2W Kc ;W6W HWEX >XFX EW6W!W<W<W3WCWCW!W=W JWCWCW\"W2W%W2W ?W =V2V KX8X BY ;X /Y)Y/X@X@X,X0X#YFXGZ)X'X0~` GV H~` "
3581 "GV H~` GV DX 3W@S 6X 3V8V M| &Z?V CSFV)S:m AXFX ;V -SAVDW@S 'V \"W6W :UGU *m 5XFX /V;SIU V.T\"V;SIU9Z /X8X MX8X MX8X MX8X MX8X MX8X "
3582 "MX;X NX +X -X -X -X -X -X -X -X ,X+X,X@XHX(X'X/X'X/X'X/X'X/X'X Ha LXFW8X-X/X'X/X'X/X'X/X GX <X;Z LW<\\ L]?W J]?W J]?W J]?W J]?W J]?W J]?{BW ,|\"|\"|\"| NW"
3583 " ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W M~Y2WDVDW NW6W MW6W MW6W MW6W HWDW DW3W KWDW HV=V>V-V%V-VGYIZHV-VGY7V-V%V%V /WDX ;X <WFT >~T-~\\'v Is"
3584 "$W4W W4W GWDX ?XGW HV %r =V 6~X JSJ[ 0VEVAV?W<ZFW -W !W \"V Lf 9V /X5X =X /Z MX\"V9X &X NX5X >X ?X6X D`IX $d Ne#X 8UAV8"
3585 "VBU=x X4X$X +X+X+X -X /X)X+X/X'X -X -XG[ AX -XCVLVCX0X?WHX(X'X-X /X'X-X;Y *Y @X <X/X MXFX CXHV?VHX-XIY 9X 3Z 8W 'V &W CZ;W JW3W$W ,W3W!| LW 1W3W"
3586 " MW6W MW ,W ,b 7W ,W7W7W=W6W!W2W NW3W$W3W MW %f BW ,W6W IXDX BWIVFVIW N\\ 8WEX :Y .[ 7V K\\ BT8e *S<X=S0W7V HW6X\"W=X GW2W Me ;W6W GVEX >WDW EW6W!W<W<W"
3587 "3WCWCW!W=W JWCWCW\"W2W%W2W ?W =W4W KW6W CY :X .X)X.YAXAY,X0X\"ZHXHZ(X'X/Y AV BY FV GY%Y FV DX 3W@S 6X 2V:V L| %ZAV BSEV*S:m @XFX <V -SAVCWAS "
3588 " 'V \"W6W :UGU *m 6XFX .V<TIU V/U\"V<TIU9Y .x Mx Mx Mx Mx Mx Mu NX +X -X -X -X -X -X -X -X ,X+X,X?WHX(X'X/X'X/X'X/X'X/X'X Ic MXGW7X-X/X'X/X'X/"
3589 "X'X/X GX <X=[ KW:[ NZ;W KZ;W KZ;W KZ;W KZ;W KZ;W KZ;{BW ,|\"|\"|\"| NW ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W &WEVCW NW6W MW6W MW6W MW6W HWEX DW3W KWEX "
3590 " GV>V>V,V&V,VIYGZIV,VIY6V,V&V&W /XEW N~X'VGT =~T-~\\&u Ir#W4W NV4W HXDX ?XHX HV KX ,V 6~X JSHZ 2VDVAV?W;ZGW -W !W \"V "
3591 "Lf :W .X6X =X 0Z LY#~ /X NX5X >X @X5Y AYFX !d >~X >d X 8UAV8VBU>z!X3X%X +X+X+X -X /X)X+X/X'X -X -XF[ BX -XCWNWCX0X?XIX(X'X-X /X'X-X:X )Y AX <X/X"
3592 " MXFX BWHV?VHW-YIY 9X 3Y 7W 'W 'W CX9W JW3W$W ,W3W!W 'W 1W3W MW6W MW ,W ,WNZ 8W ,W7W7W=W6W!W2W NW3W$W3W MW !c CW ,W6W HWDW AWIVFVIW N] 8WFW :Y *"
3593 "Y 8V KY ?R3` (S<X=S0W7V HW5W\"W=X GW2W N[ 0W6W GWFW >XDX FW6W!W<W<W3WCWCW!W=W JWCWCW\"W2W%W2W ?W =W4W LX6X DY :X .X)X-XAXAX+X0X!ZIXIZ'X'X.Y BV CY EV"
3594 " FY'Y EV DX 3W@S 6X 2V:V L| $[CV BTFW,T:m ?XFX =V -TBVBVBT 'V \"W6W :UGU *m 7XFX .V<THU!V/U\"V<THU:Y .z z z z z Nx Nv NX +X -X -X -X"
3595 " -X -X -X -X ,X+X,X?XIX(X'X/X'X/X'X/X'X/X'X Je NXGV6X-X/X'X/X'X/X'X/X GX <X@^ KW9[ X9W KX9W KX9W KX9W KX9W KX9W KX9W MW ,W ,W ,W ,W )W ,W ,W ,W /W2W N"
3596 "W6W!W2W\"W2W\"W2W\"W2W\"W2W &WFVBW NW6W MW6W MW6W MW6W GWFW CW3W JWFW FV>V?W,V'W,VJYEZKW,VJY6W,V'W&W /XFX N~X'WHT =~T-~\\%s Gp\"W4W NV4V GXCW >WH"
3597 "X HW LX ,V 6~X JSGY 3VDWAW@W:ZIW ,W !W \"V Lf :W .X6X =X 1Z JX#~ /X NX5X ?Y @X4X .X Md A~X Ad LX 8UAV8VBU>z!X3X%X +X+X+"
3598 "X -X /X)X+X/X'X -X -XE[ CX -XBVNVBX0X>WIX(X'X-X /X'X-X9X *Y AX <X/X MXFX BXJW?WJX.YGY :X 4Z 7W 'W 'W DX8W JW3W$W ,W3W!W 'W 1W3W MW6W MW ,W ,WLY "
3599 "9W ,W7W7W=W6W!W2W NW3W$W3W MW K_ DW ,W6W HXFX AWIVFVIW ^ 8WFW ;Y (Y 9V LY >Q.X $T>Z?T0W8W HW5W\"W<W GW2W Y -W6W GWGX >WCX FW6W!W<W<W3WCWCW!W=W JWCWC"
3600 "W\"W2W%W2W ?W =W4W LX6X EY 9X .Y+Y-YBXBY+X0X ZJXJZ&X'X-Y CV DY DV EY)Y DV DX 3W@S 6X 2W<W L| #\\FW ASFW,S9m >XFX >V ,SBVBWCS &V \"W6W :U"
3601 "GU *m 8XFX .V<TGU\"V.U#V<TGU;Y -z z z z z z v NX +X -X -X -X -X -X -X -X ,X+X,X>WIX(X'X/X'X/X'X/X'X/X'X KZMZ XHW6X-X/X'X/X'X/X'X/X GX <u JW7Y!X8W "
3602 "LX8W LX8W LX8W LX8W LX8W LX8W MW ,W ,W ,W ,W )W ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W &WGWBW NW6W MW6W MW6W MW6W GWFW CW3W JWFW FW?V?V+W(V+WKXCY"
3603 "KV+WKX5V+W(V%W .WFX N~X'WHT =~T-~\\$q Eo\"W4W NV4V GWBW >XIW GW LX ;~X JSFX 3VDV?V@W9ZJW +V \"W !V V -X6X =X 2Z IX#"
3604 "~ /X NX5X ?X ?X4X .X Jd D~X Dd IX 8UAV8VCV>z!X3X%Y ,X,Y+X -X /Y*X+X/X'X -X -XD[ DX -XBVNVBX0X>XJX(Y)X,X /Y)X,X9Y *X AX <X/X LXHX AXJV=VJX.XEY ;X 5"
3605 "Z 6W &V 'W DW7W JW3W$W ,W3W!W 'W 1W3W MW6W MW ,W ,WKY :W ,W7W7W=W6W!W2W NW3W$W3W MW H\\ DW ,W6W GWFW @WJVDVJW!` 9WGX <Y &X 9V LX =P (T?\\@T0W8"
3606 "X IW5W\"W<W GW2W X ,W6W FVGW >XBW FW6W!W<W<W3WCWCW!W=W JWCWCW\"W2W%W2W ?W =W4W LW4W FY 8X -X+X+YCXCY*X0X N\\MXM\\%Y)X+Y DV EY NQFVFQ Y+Y CV DX 3W@S 6X"
3607 " 1V<V K| ![HW @TFW.T9m =XFX ?V ,TCVAVDT &V \"W6W :UGU *m 9XFX -V<SFU\"V/U\"V<SFU;X ,z z z z z z v NY ,X -X -X -X -X -X -X -X ,X,Y,X>XJX"
3608 "(Y)X.Y)X.Y)X.Y)X.Y)X KZKZ!YJW6X,X/X'X/X'X/X'X/X GX <t IW6Y\"W7W LW7W LW7W LW7W LW7W LW7W LW7W MW ,W ,W ,W ,W )W ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W "
3609 " &WHWAW NW6W MW6W MW6W MW6W GWGX CW3W JWGX EV?V@W*V)W*VJVAWKW*VJV5W*V)W%W .XGW M~X&WJT <~S,kNn#o Cm!W4W NV4V HXBX ?XJX FW MY"
3610 " <~X JSEX 5VCV?V@W8ZLW *W #W !V V -X6X =X 3Z HX#~ /X NX5X @Y ?X4X /X Ge G~X Ge GX 8UAV9WCU>|\"X3X$X ,X,X*X -X .X*X+X/X'X -X -XC[ EX"
3611 " -XA\\AX0X=WJX'X)X,X .X)X,X8X *X AX <X/X LXHX AXJV=VJX/YEY ;X 6Z 5W &V 'W DW7W JW3W$W ,W3W!W 'W 1W3W MW6W MW ,W ,WJY ;W ,W7W7W=W6W!W2W NW3W$W3W M"
3612 "W EZ EW ,W6W GWFW ?WKVDVKW!b 9WHW <Y $W 9V LW BTAVNUAT/W8X IW5W#W;V FW2W!X +W6W FWIX >XBX GW6W!W<W<W3WCWCW!W=W JWCWCW\"W2W%W2W ?W =W4W MX4X HY 7X"
3613 " -Y-Y+ZDXDZ*X0X Mt#X)X*Y EV FY NSGVGS Y-Y MQFVFQ X 3W@S 6X 1W>W 9X =\\KW >SEW<PCS 6XFX @V +SCVAWES %V \"W6W :UGU &XFX -V<TFU#V"
3614 "/U\"V<TFU<X ,|\"|\"|\"|\"|\"|\"w MX ,X -X -X -X -X -X -X -X ,X,X+X=WJX'X)X-X)X-X)X-X)X-X)X LZIZ!XKW5X,X/X'X/X'X/X'X/X GX <s HW5X\"W7W LW7W LW7W LW7W LW7W LW7W"
3615 " LW7W MW ,W ,W ,W ,W )W ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W &WIW@W NW6W MW6W MW6W MW6W FWHW BW3W IWHW DW@VAW)W+W)WJT?UKW)WJT5W)W+W$W -WHX "
3616 "M~X&WJT ;eMQMe+jNQNj!m Bl W4W NW6W HXBX >WJX FW LX <~X JSEX 6WCV?V@W7ZMW *W #W !V !W -X6X =X 4Z GX#~ /X NX5X @X >X4X "
3617 "/X De J~X Je DX 8U@V:WDV>|\"X3X$X ,X-Y*X -X .X*X+X/X'X -X -XB[ FX -XA\\AX0X=XKX'X*Y,X .X*Y,X8Y +X AX <Y1Y KWHW ?WJV=VJW/YCY <X 7Z 4W &W (W EW6"
3618 "W JX5X$X -X5X!X (W 0W5X MW6W MW ,W ,WIY <W ,W7W7W=W6W!X4X NX5X$X5X MW CX EW ,W6W GXHX ?WKVDVKW!XNY :WIX =Y #X :V MX BUCVMVBT/W9Y IW5W#W<W FW3X!W"
3619 " *W6W EVIX ?X@W GW6W!W=Y=W3XDWDX!W=W JWCWCW\"X4X%X4W >W <W6W LX4X HY 7X ,X-X)ZEXEZ)X0X Lr\"X)X)Y FV GY NUHVHU Y/Y MSGVGS !X 3XAS 6X 0W@W 8X ;\\NW "
3620 "=TEX@RDT 5XFY BV +TDV@WGT %V \"W6W :UGU (YFX ,V=TEU#V0U!V=TEU<X ,|\"|\"|\"|\"|\"|\"w MX ,X -X -X -X -X -X -X -X ,X-Y+X=XKX'X*Y-X*Y-X*Y-X*Y-"
3621 "X*Y MZGZ\"XLW5Y,Y1Y'Y1Y'Y1Y'Y1Y GX <r GW4X$W6W MW6W MW6W MW6W MW6W MW6W MW6X NX -X -X -X -X *W ,W ,W ,W /W2W NW6W!X4X\"X4X\"X4X\"X4X\"X4X &WIV@X NW6W MW6W"
3622 " MW6W MW6W FWIX BX5X IWIX CWAVAW(W,W(WJR=SJW(WJR4W(W,W$W -XIX M~X&WJS :dLQLd+iMQNj!l @j NW4W NW6W HW@W >WJW DW MX .VCV"
3623 " :SDW 6VBV?V@W6b )W #W !V !V +X8X <X 5Z FX#~ /X MW5X @X >X4X /X Ad L~X Ld AX 8VAV:WDU=|\"X3X$Y -X-Y*X -X .Y+X+X/X'X -X -XA[ GX -XA\\AX0X<WK"
3624 "X'Y+X+X .Y+Y,X7X +X AX ;X1X JXJX ?XLW=WLX/XAY =X 7Y 3W %V (W EW7X JX5W\"W ,W5X W (W 0W5X MW6W MW ,W ,WHY =W ,W7W7W=W6W W4W MX5W\"W5X MW BX FW ,W6"
3625 "W FWHW >WKVDVKW\"XLX 9WJW =Z #X :V MX AUEVKVDU/X:Y IW5W#W<W EW4W!X *W6W EVJX >X@W GW6W!W=Y=W2WDWDW W=W JWCWCW\"X4W#W4W >W <W6W LW2W IY 6X ,Y/Y(ZFXF"
3626 "Z(X0X Kp!Y+X'Y GV HY NWIVIW Y1Y MUHVHU \"X 2WAS 6X 0YDY 8X :c <TE[FUDS 3XFY CV *SDV@WGS $V \"W6W :UGU )YFX ,V=TDU$V0V\"V=TDU=X +"
3627 "|\"|\"|\"|\"|\"|#x MY -X -X -X -X -X -X -X -X ,X-Y+X<WKX'Y+X,Y+X,Y+X,Y+X,Y+X MZEZ#YNW4X*X1X%X1X%X1X%X1X FX <p EW4X$W7X MW7X MW7X MW7X MW7X MW7X MW7Y MW ,W "
3628 ",W ,W ,W *W ,W ,W ,W .W4W MW6W W4W W4W W4W W4W W4W $WKV?W MW6W MW6W MW6W MW6W EWJW AX5W GWJW BXBVBW'X.W'XJP;QJW'XJP4W'X.W#V ,XIW L~X%WLT :d"
3629 "LQLc*iMQMi k ?i NW4W NW6W IX@X ?XLX DW MY 0VBV :SDW 7VAV?V@X6a )W #W !V !V +X8X <X 6Z EX#~ 0Y MW5X AY >X4X 0X =d ~X"
3630 " d LUAW<XEV>X2X#X3X#X -X.Y)X -X -X+X+X/X'X -X -X@[ HX -X@Z@X0X<XLX&X+X+X -X+X+X7Y ,X AX ;X1X JXJX ?XLV;VLX0YAY =X 8Z 3W %V (W EW7X JX5W\"W ,W5X"
3631 " W (W 0W5X MW6W MW ,W ,WGY >W ,W7W7W=W6W W4W MX5W\"W5X MW BX FW ,W7X FWHW >WLVBVLW#YKX :WJW =Y !W :V MW @VHXJWHV-W:Y IW5W#W<W EW4W!W )W6W EWKX ?X"
3632 "?X HW6W!X>Y>W1WDWDW W=W JWCWCW\"X4W#W4W >W <W6W MX2X KY 5X +Y1Y'[GXH\\(X0X Jn NX+X&Y HV IY NYJVJY Y3Y MWIVIW #X 2WAS 6X 0[H[ 8X :V %` :TEiET 2YGY "
3633 " DV *TEV?WIT $V \"W6W :UGU *YGY ,V<SCU%V0V\"V<SCU=X ,X2X$X2X$X2X$X2X$X2X$X2X$X8X LX -X -X -X -X -X -X -X -X ,X.Y*X<XLX&X+X+X+X+X+X+X+X+X"
3634 "+X NZCZ#`3X*X1X%X1X%X1X%X1X FX <m BW3W$W7X MW7X MW7X MW7X MW7X MW7X MW7Y MW ,W ,W ,W ,W *W ,W ,W ,W .W4W MW6W W4W W4W W4W W4W W4W 5Z IWLV>W MW7X MW7X "
3635 "MW7X MW7X EWJW AX5W GWJW AXCVCW%X0W%X0W%X0W%X0W\"V +WJX ?X 2WLT 9bKQKb)gLQMh Mi =g MW4W MV6W IX@X ?XLX CW MX 0VBV :SDW "
3636 "7VAV?V@X5_ (W #W !V \"W +X8X <X 7Z DX 5X 'X LX7X @X =X4X 0X ;e Le JUAW<XFV=X1W#X3X#Y .X.Y)X -X -Y,X+X/X'X -X -X?[ IX -X@Z@X0X;XMX&Y-Y+"
3637 "X -Y-Y+X6X ,X AX ;X1X IXLX >XLV;VLX1Y?Y >X 9Z 2W %W )W EW7X JX5W\"X -W5X X )W 0X7Y MW6W MW ,W ,WFY ?W ,W7W7W=W6W W4W MX5W\"W5X MW AW FW ,W7X FXJX"
3638 " =WMVBVMW#YJY ;WKX >Y W :V MW ?dId,W;Z IW5W#W=W DW4W!W )W6W DVKW >X>W HW6W W>Y>W1WDWDW W=W JWCWDX\"X4W#W4W >W ;V7W LX2X LY 4X *X1X%]JXJ]'X0X Hj L"
3639 "Y-Y%Y IV JY LYKVKY MY5Y MYJVJY $X 2XBS 6X 2q 9X :V #\\ 7TDgFT /XFX EV )TFV>VJT #V \"W6W :UGU +XFX *V=TCU%V1V!V=TCU=X ,X1W$X1W$X1W"
3640 "$X1W$X1W$X2X%X7X LY .X -X -X -X -X -X -X -X ,X.Y*X;XMX&Y-Y+Y-Y+Y-Y+Y-Y+Y-Y ZAZ$_3Y*X1X%X1X%X1X%X1X FX <i >W3W$W7X MW7X MW7X MW7X MW7X MW7X MW7Z NX -X "
3641 "-X -X -X +W ,W ,W ,W .W4W MW6W W4W W4W W4W W4W W4W 5Z IWMV=W MW7X MW7X MW7X MW7X EWKX AX5W GWKX @XDVDX$X2X$X2X$X2X$X2X\"V +XKW ?X 1WMT 7`JQKa"
3642 "'fLQLf Kg <f LW4W MW8W HW>W >WLW BX NY 1VBV :SDW 8V@V?V?W4] &V $W V \"V *Y:Y <X 8Z DY 5X 'X KW7X @X =X5Y 1Y 8e #e "
3643 " GU@W>YGW>X0X$X4Y\"Y /X/Y(X -X ,Y-X+X/X'X -X -X>[ JX -X@Z@X0X;XMX%Y/Y*X ,Y/Y*X6Y -X AX ;Y3Y IXLX =WLV;VLW0X=Y ?X :Z 1W $V )W EW8Y JY7X\"X -X7Y X "
3644 ")W 0X7Y MW6W MW ,W ,WEY @W ,W7W7W=W6W X6X MY7X\"X7Y MW AW FW ,X8X EWJW <WMVBVMW#XHX :WLW >Y NW :V MW >bGc,W;[ JW6X#W=W DX6X!W )W6W DVLX >W=X IW7"
3645 "X W>Y>W1XEWEX W=W IWDWDW!Y6X#X6X >W ;W8W MX0X MY 4X *Y3Y$^LXL^&X0X Ff IY/Y#Y JV KY JYLVLY KY7Y KYKVKY #X 2XBS 6X 3t ;X :V ![ 8TCfFT .XFX FV )U"
3646 "GV>WKT MW7X :UGU ,XFX *V=TBU&V2W!V=TBU=X -X0X&X0X&X0X&X0X&X0X&X0W%X7X KY /X -X -X -X -X -X -X -X ,X/Y)X;XMX%Y/Y)Y/Y)Y/Y)Y/Y)Y/Y Z?Z$"
3647 "^4Y)Y3Y%Y3Y%Y3Y%Y3Y FX <X -W3W$W8Y MW8Y MW8Y MW8Y MW8Y MW8Y MW8[ NX -X -X -X -X +W ,W ,W ,W .X6X MW6W X6X X6X X6X X6X X6X 5Z I_=X MX8X MX8X MX8X MX8X "
3648 "DWLW @Y7X FWLW >XEVFY\"X5Y\"X5Y\"X5Y\"X5Y!V *WLX @X /WNT 7`JQJ_&eKQKe Je :d KW4W MW8W HW>X ?XNX AX Y 1VCV 9SDW 9V?V?V?X4\\ "
3649 " &W %W V \"V )X:X ;X 9Z CX 4X (Y KW7X AX <Y6Y 1X 4e )e DVAX@ZHW=X0X$X4Y\"Y*P&X0Z(X -X ,Y-X+X/X'X -X -X=[ KX -X?X?X0X:XNX%Y/Y*X ,Y/Y*X5X "
3650 ".Y AX :X3X HXLX =XNW;WNX1Y=Y ?X ;Z 0W $V )W EW8Y JY7W W ,W7Y NX *W /W8Z MW6W MW ,W ,WDY AW ,W7W7W=W6W NW6W LY7W W7Y MW AW FW ,X9Y EWJW <WMVBVMW"
3651 "$XFX ;WMX ?Y MW :V MW =`Ea+X<[ JW6W\"W>W BW6W W )W6W DWMX ?X=X IX8X W?[?W0WEWEW NW=W IWDWDW!Y6W!W6W =W ;W8W MX0X NY 3X )Y5Y\"z%X0X C` FY/Y\"X JV "
3652 "KX HYMVMY IX7X IYLVLY \"X 1XCS 6X 4v <X :V [ 8TBbET ,WEW FV (T$T LX8X :UGU ,WEW )V=m,V3W V=mCX -X0X&X0X&X0X&X0X&X0X&X0X&X7X KY"
3653 "*P&X -X -X -X -X -X -X -X ,X0Z)X:XNX%Y/Y)Y/Y)Y/Y)Y/Y)Y/Y!Z=Z%]3Y(X3X#X3X#X3X#X3X EX <X -W3W$W8Y MW8Y MW8Y MW8Y MW8Y MW8Y MW8[ MW ,X -X -X -X ,W ,W ,W "
3654 ",W -W6W LW6W NW6W MW6W MW6W MW6W MW6W 4Z H^=W LX9Y MX9Y MX9Y MX9Y DWMX @Y7W EWMX =Y8Y Y8Y Y8Y Y8Y Y8Y V *WLX AX .WNT 6^IQI]$cKRJc Id 8c KW4W"
3655 " MX:X IX>X ?XNX AY Y4P VBV 9SDW 9V?V?V?Y4Z %W %W V #W )X:X ;X :Z CY 4X (Y KX9Y AX ;X6X 1Y 1e /e @U@XB[JX<X/W$X4"
3656 "X Y,Q&X1Z'X -X +Y.X+X/X'X -X -X<[ LX -X?X?X0X:XNX$Y1Y)X +Y1Y)X5Y /X @X :X4Y GXNX <XNV9VNX2Y;Y @X ;Y /W $W *W EW9Z JZ9X X -X9Z NX *W /X9Z MW6W MW"
3657 " ,W ,WCY BW ,W7W7W=W6W NX8X LZ9X X9Z MW AW FW +W9Y EXLX <WNV@VNW%YEX ;WNW ?Y LW :V MW <^C_)W=\\ JX7W\"W>W BX8X W )W6W CVNX >W;W IX8X X@[@X0XFWEW "
3658 "NW=W IWDWEX!Z8X!X8X =W :W:W LX0X Y 2X (Y7Y Nv#X0X ?X AY1Y V IV JV FYNVNY GV5V GYMVMY !X 1XCS 6X 5x =X :V MZ 8T?ZBT *VDV FV 'T&T KX"
3659 "8X :UGU ,VDV )V<m-V3V NV<mCX -X/W&X/W&X/W&X/W&X/W&X0X'X6X JY,Q&X -X -X -X -X -X -X -X ,X1Z(X:XNX$Y1Y'Y1Y'Y1Y'Y1Y'Y1Y!Z;Z%[3Y'X4Y#X4Y#X4Y#X4Y EX"
3660 " <X -W3W$W9Z MW9Z MW9Z MW9Z MW9Z MW9Z MW9] NX -X -X -X -X ,W ,W ,W ,W -X8X LW6W NX8X MX8X MX8X MX8X MX8X 4Z H]=X KW9Y LW9Y LW9Y LW9Y CWNW ?Z9X DWNW "
3661 " ;Y;Z MY;Z MY;Z MY;Z MY;Z NV *XMW AY -[ 3ZHRH[\"aJRI` Fb 6a JW4W LW:W HX=W >WNX @Y !Z6Q VBV KP>SEW 9V>WAW>X3Z &W %W V "
3662 " #V 'X<X :X ;Z BY 4X )Y IW9X AY ;Y8Y 2Y .d 1d >U?ZH^MZ<X.X%X5Y NY.R&X2Z&X -X *Y/X+X/X'X -X -X;[ MX -X&X0X9a$Z3Y(X *Y3Y(X4X$P-Y @X :Y5Y GXNX"
3663 " <XNV9VNX2X9Y AX <Z /W #V *W EX:Z JZ9X NX .X9Z MX +W .X;[ MW6W MW ,W ,WBY CW ,W7W7W=W6W NX9Y LZ9X X9Z MW AW FW +W:Z DWLW :^@^$XDY <WNW @Z LW :"
3664 "V MW ;\\@['X>\\ JX8X\"W?W AX9Y X *W6W CVNX ?X;X JX9Y NW@[@W/XFWFX NW=W IXEWEX!Z8X!X8W ;W ;W;X MX.X\"Y 1X 'Y9Y Lt\"X0X ?X @Y3Y MT HV IT Dj ET3T EYNVN"
3665 "Y X 0XDS 6X 6ZM`LY >X :V LY 7T)T (UCU ET(T JX9Y :UGU ,UCU )V;m.V3V NV;mCY7P HX.X(X.X(X.X(X.X(X.X(X.X(X6X IY.R&X -X -X -X -"
3666 "X -X -X -X ,X2Z'X9a$Z3Y&Z3Y&Z3Y&Z3Y&Z3Y!Z9Z&Z3Y&Y5Y#Y5Y#Y5Y#Y5Y EX <X -W3W$X:Z MX:Z MX:Z MX:Z MX:Z MX:Z MX:^ NX -X -X -X -X -W ,W ,W ,W -X8X LW6W NX9Y"
3667 " MX9Y MX9Y MX9Y MX9Y 4Z H\\=Y KW:Z LW:Z LW:Z LW:Z CWNW ?Z9X DWNW :[@[ K[@[ K[@[ K[@[ K[@[ MV )WNX AX ,[ 1WGRFW N_IRH^ Da 5_ IW4W LX<X HW<W >`"
3668 " >Y !Y8S MX +VBV KQ?SFX 9V=VAV=Y6] &V &W NV BX 1X 1V 'Y>Y :X <Z BY 3X GP3Z IX;Y AX :Y9Z 2X GX -X 7a 1a .X 6V@iNa;X.X%X6Z N"
3669 "Z1T&X4\\&X -X *Z0X+X/X'X -X -X:[ NX -X&X0X9a#Z5Z(X *Z5Z(X4Y%R/Y @X 9Y7Y EWNW :WNV9VNW2Y9Y AX =Z .W #V *W EX;[ J[;X MY .X;[ MY2P JW .Y=\\ MW6W MW ,"
3670 "W ,WAY DW ,W7W7W=W6W MX:X K[;X MX;[ MW /P4X FX ,X<[ DXNX :^@^%XBX <` @Y KW :V MW 8V;W%X?^ KY9X!V@X @X:X NX *W6W C_ >X:W JY;Z NXB]BX.XGWGX MW=W H"
3671 "XFWFX [:X NX:X ;W :W<W LX.X\"Y 1X &Y;Y Ip X0X ?X @Z5Z LR GV HR Bh CR1R Cj NX 0YES 6X 7ZJ\\IY ?X :V KY 8U+U 'TBT DU+T IY;Z :UGU "
3672 " ,TBT (V;m.V4V MV;mCY8Q HX.X(X.X(X.X(X.X(X.X(X.X)X5X IZ1T&X -X -X -X -X -X -X -X ,X4\\'X9a#Z5Z%Z5Z%Z5Z%Z5Z%Z5Z\"Z7Z&Z5Z%Y7Y!Y7Y!Y7Y!Y7Y DX <X -W4X$X"
3673 ";[ MX;[ MX;[ MX;[ MX;[ MX;[ MX;`3P=Y .Y2P LY2P LY2P LY2P LW ,W ,W ,W ,X:X KW6W MX:X KX:X KX:X KX:X KX:X 3Z GZ<X JX<[ LX<[ LX<[ LX<[ C` ?[;X C` 9_J"
3674 "_ I_J_ I_J_ I_J_ I_J_ LV )` AX +Z S <[GRFZ A_ 4^ HW4W KX>X HX<X ?` =Z \"Y:T MX +VCV JSASFX :V<VAV<Y8_ 'W 'W NV BX 1X 2W"
3675 " &X>X 9X =Z 1P2Z 3X GQ5Z GX=Y @X 9Y:Y KP8Z GX -X 4^ 1^ +X 5U?gM_9W,W%X7Z L[4U&X6]%X -X )[2X+X/X'X -X -X9[ X -X&X0X8`\"Z7Z'X )Z7Z'X3X%T2Y ?X 9Z9Z E` :"
3676 "_9_3Y7Y BX >Z -W #W +W DX=\\ J\\=Y LY7P HY=\\ LY5R JW -Y?] MW6W MW ,W ,W@Y EW ,W7W7W=W6W MY<Y K\\=Y MY=\\ MW /R6W DW ,Y=[ CWNW 9^@^&X@X <^ @Y JW :V "
3677 " MW HXA` LZ;X V@W ?Y<Y MX +W6W B^ ?X9W JZ<Z NXB]BX.YHWHY MW=W HYGWGY \\<Y NY<X :W :X>X LX.X#Y 0X %Y=Z Gl MX0X ?X ?Z7Z JP FV GP @f AP/P Ah MX "
3678 "/YFSDP BX 8ZFVEY @X :V JX 7V.U %SAS CU.U HZ<Z :UGU ,SAS (V:m/V5W MV:mBY;S HW,W(W,W(W,W(W,W(W,W(X.X)X5X H[4U&X -X -X -X -X -X"
3679 " -X -X ,X6]&X8`\"Z7Z#Z7Z#Z7Z#Z7Z#Z7Z\"Z5Z&[8Z$Z9Z!Z9Z!Z9Z!Z9Z DX <X -W4W\"X=\\ LX=\\ LX=\\ LX=\\ LX=\\ LX=\\ LX=b6R<Y7P GY5R KY5R KY5R KY5R LW ,W ,W ,W ,Y<Y KW"
3680 "6W MY<Y KY<Y KY<Y KY<Y KY<Y 3Z GY<Y JY=[ LY=[ LY=[ LY=[ B^ >\\=Y B^ 7r Gr Gr Gr Gr KV (_ BX )Y S 8RBSCR <] 2\\ GW4W KZBZ HX;W >_ <[ "
3681 " $[=U MX ,VBV JUCSHY :V;WCW<[<b (W 'W NV BX 1X 2W &Y@Y 9X >Z 0R5Z 2X GT9[ GY?Z AY 9[>[ KR;Z FX -X 1[ 1[ (X 5V>dL^9X,X&X9[ J[7W&X9_$X "
3682 "-X (\\6Z+X/X'X -X -X8[!X -X&X0X8`![;[&X ([;[&X3Y&W7[ ?X 8Z;Z D` :^7^3X5Y CX ?Z ,W #W +W DY?] J]?Y KZ:R GY?] LZ8T JW -ZA^ MW6W MW ,W ,W?Y FW ,W7W7"
3683 "W=W6W LY>Y J]?Y KY?] MW /T9X DX ,Y@] CWNW 9]>]'Y@Y =^ AY IW :V MW HYCXNW L\\>Y VAX >Y>Y LY ,W6W B] >X9X K[>[ MXDVMVDX,YIWIY LW=W GYHWHY N]>Y LY"
3684 ">Y :X :X@X LX,X%Y /X $ZAZ Ch KX0X ?X >[;[ ?V 6d >f LX /[HSFR BX 9Z3Y AX :V IX 7V1V #R@R BU0U G[>[ :UGU ,R@R 'V(U)V6W"
3685 " LV(U<Z>U IX,X*X,X*X,X*X,X*X,X*X,X*W4X G[7W&X -X -X -X -X -X -X -X ,X9_%X8`![;[![;[![;[![;[![;[\"Z3Z(];[\"Z;Z NZ;Z NZ;Z NZ;Z CX <X -WJP;X\"Y?] LY?] LY?] "
3686 "LY?] LY?] LY?] LY?XNZ9T<Z:R GZ8T KZ8T KZ8T KZ8T LW ,W ,W ,W +Y>Y JW6W LY>Y IY>Y IY>Y IY>Y IY>Y 2Z FY>Y HY@] KY@] KY@] KY@] B^ >]?Y A^ 6o Do Do Do "
3687 "Do IV (_ CX (Y S (S ,[ 0[ GW4W J\\H\\ GW:W >^ :\\ %[@W MX ,VBV JXFSIZ :V:WEW:\\@e (V 'V MV BX 1X 2V $ZDZ 8X ?Z /U;] 2X GV="
3688 "\\ EZC[ @X 7[@[ JT?[ EX -X /Y 1Y &X 5V=bK\\7X,X&X<^ I]=Z&X=b#X -X ']:\\+X/X'X -X -X7[\"X -X&X0X7_ \\?\\%X '\\?\\%X2X&Z<\\ >X 7[?[ B^ 9^7^4Y5Y CX ?Y +W \"V +W "
3689 " DZB_ J_CZ I[>T G[C_ K[=W JW ,\\GXNW MW6W MW ,W ,W>Y GW ,W7W7W=W6W KZBZ I_CZ J[C_ MW /W>Z DZ .ZB^ C` 8\\>\\&X>Y =\\ AY HW :V MW GZFYNY N]AZ N"
3690 "WCX <ZBZ JZ:Q EW6W B] ?X7W K\\A^ NYFWMWFY,ZJWJY KW=X H[JWJ[ N_BZ JZBZ 8Y <ZDZ LX,X&Y .X #ZCZ >_ FX0X ?X =\\?\\ >V 5b <d KX .\\JSHT BX 8X2X @X :V "
3691 "IX 5V4U Q?Q AV4V F\\A^ ;UGU ,Q?Q 'V'U*V6W LV'U<[AW IX,X*X,X*X,X*X,X*X,X*X,X+X4X F]=Z&X -X -X -X -X -X -X -X ,X=b$X7_ \\?\\ N\\?\\"
3692 " N\\?\\ N\\?\\ N\\?\\ X1X(`?\\ [?[ L[?[ L[?[ L[?[ BX <X -WJS@Z\"ZB_ LZB_ LZB_ LZB_ LZB_ LZB_ LZBYM\\>W;[>T F[=W J[=W J[=W J[=W LW ,W ,W ,W *ZBZ IW6W KZBZ GZBZ "
3693 "GZBZ GZBZ GZBZ 1Z F[BZ GZB^ KZB^ KZB^ KZB^ A\\ =_CZ ?\\ 3l Al Al Al Al HV (^ BX (X NS (S ,Z .Y FW4W In GX:X ?^ 9_ (]FZ MX "
3694 ",VBV J[ISL\\ :V9XGX9^Fi )W )W MV BX 1X 3W #[H[ Et Mx MZC_ 1X GZD^ C[G\\ @Y 7^F] IXF] DX -X ,V 1V #X 4V<^IY5X*X'y G_D^&{!y NX &`B`+X/X'X -X -X6[#"
3695 "w LX&X0X7_ N^E^$X &^E^$X2Y'^C^ =X 7^E^ B^ 8]7]4Y3Y DX @~U&W \"W ,W C\\HYNW JWNXG\\ H]EX F\\GXNW J]D[ JW +kMW MW6W MW ,W ,W=Y HW ,W7W7W=W6W K]H] IWNX"
3696 "G\\ I\\GXNW MW /[E\\ Be 9[GXNW B^ 7\\>\\'X<X =\\ AX GW :V MW G\\IYM^$`F\\ MWEX ;]H] J]BV EW6W A\\ ?X7X L_GaKP#ZJYMYJZ*[LWL[ KW=Y H\\LWL\\ MWNXG] J]H\\ 7a "
3697 "C[H[ L~W'x MX 1iEi HX CX0X ?X <^E^ =V 4` :b JX -^MSLX Lz V0V ?X :V HW 4V7V MP>P @W8W 3~W :_GaKP @UGU ,P>P 'V&U+V6V KV&"
3698 "U;]GZ JX*X,X*X,X*X,X*X,X*X,Y,Y,X4y7_D^&y Ny Ny Ny NX -X -X -X ,{\"X7_ N^E^ L^E^ L^E^ L^E^ L^E^ MV/V(dE^ N^E^ L^E^ L^E^ L^E^ BX <X -WJWF[ \\HYNW K\\HYNW K"
3699 "\\HYNW K\\HYNW K\\HYNW K\\HYNW K\\H[K^E[:]EX E]D[ I]D[ I]D[ I]D[ LW ,W ,W ,W )[F[ HW6W K]H] G]H] G]H] G]H] G]H] 1Z F]G] F[GXNW J[GXNW J[GXNW J[GXNW A\\ =WNX"
3700 "G\\ ?\\ 1h =h =h =h =h FV '] AV &W T )T +X -X EW4W Hl FX9W ?^ 8~R Jp MX ,VCV It 9V8XIX7sLZ *W )W MV BX 1X 3W #n Et M"
3701 "x Mu 0X Gs Ao @X 5t In CX -X )S 1S X 4V9XFU1X*X'x Ex&z y NX %|*X/X'X -X -X5[$w LX&X0X6^ Mu#X %u#X1X'y =X 6u A^ 8]7]4X1X DX @~U&W \"W ,W ClMW J"
3702 "WMk Fo EkMW Is JW *jMW MW6W MW ,W ,W<Y IW ,W7W7W=W6W Jp HWMk GkMW MW /q Ae 9kMW B^ 7\\=[(Y;X >\\ Av 6W :V MW FkL]$u LXGX 9p Hp EW6W A[ ?X6X LpN\\#"
3703 "hKh)s JW<] Lu LWNm Hp 6` Bl K~W'x MX 1iEi HX CX0X ?X ;u <V 3^ 8` IX ,o Lz NT.T >X :V HW 3X=X )X<X 2~W :pN\\ @UGU V&"
3704 "U+V7i.V&U:o JX*X,X*X,X*X,X*X,X*X,X*X-X3y6x&y Ny Ny Ny NX -X -X -X ,z!X6^ Mu Ju Ju Ju Ju KT-T(} Lu Ju Ju Ju AX <X -WJk NlMW KlMW KlMW KlMW KlMW KlMW Kn"
3705 "Is9o Ds Hs Hs Hs LW ,W ,W ,W )p HW6W Jp Ep Ep Ep Ep Ls EkMW JkMW JkMW JkMW A\\ =WMk >\\ /c 8c 8c 8c 8c CV '\\ ?T %W U *T *W ,V DW4W Gj EW8W "
3706 ">\\ 5~P In LX -VBV Is 9V7g6qJZ *V )V LV BX 1X 3V !l Dt Mx Mt /X Gr ?m ?X 4r Hm BX -X &P 1P LX 3V 3X*X'w Cv%x My NX #x(X/X'X"
3707 " -X -X4[%w LX&X0X5] Ls\"X $s\"X1Y(w ;X 5s ?\\ 7\\5\\5Y1Y EX @~U&W !V ,W BjLW JWMj Dn DjMW Hr JW )hLW MW6W MW ,W ,W;Y JW ,W7W7W=W6W In GWMj EjMW MW /p"
3708 " ?d 8iLW B^ 6Z<[)Y:Y >Z @v 6W :V MW EiK]$t JYLZ 7n Fo EW6W A[ ?X5W LWNfM\\\"gKg'q IW<] Ks KWMk Fn 5` Aj J~W'x MX 1iEi HX CX0X ?X :s ;V 2\\ 6"
3709 "^ HX +n Lz MR,R =X :V HW 1ZEZ %ZDZ 0~W :WNfM\\ @UGU !V%U,V6i/V%U9n JX*X,X*X,X*X,X*X,X*X,X*X-X3y5v%y Ny Ny Ny NX -X -X -X ,"
3710 "x NX5] Ls Hs Hs Hs Hs IR+R(WMs Js Hs Hs Hs @X <X -WJk MjLW JjLW JjLW JjLW JjLW JjLW JmHr8n Cr Gr Gr Gr LW ,W ,W ,W (n GW6W In Cn Cn Cn Cn Ls CiLW Ii"
3711 "LW IiLW IiLW @Z <WMj <Z +] 2] 2] 2] 2] @V &[ >R $V NU *U *U *U DW4W Fh DW8X ?\\ 4~ Hl KX -VBV Hp 8V5e4nGZ +W +W LV BX"
3712 " 1X 3V j Ct Mx Mr -X Gq =j >Y 3p Gl AX -X 2X 3W 5X(X(u ?s$v Ky NX \"v'X/X'X -X -X3[&w LX&X0X5] Kq!X #p X0X(v :X 4p =\\ 7\\5\\6Y/Y FX @~U&W !V ,W "
3713 " AhKW JWLh Bm ChLW Gq JW (eJW MW6W MW ,W ,W:Y KW ,W7W7W=W6W Hl FWLh ChLW MW /o >d 7gKW A\\ 5Z<Z(X8X >Z @v 6W :V MW DgI\\$s He 5l Dn EW6W @Y "
3714 ">W4X MWMeM\\!eIe%o HW<] Jq JWLi Dk 2_ @h J~Y(x MX 1iEi HX CX0X ?X 9q :V 1Z 4\\ GX *m Lz LP*P <X :V HW 0m \"l .~W :WMeM\\ @UGU "
3715 " !V%U,V6i/V%U8l JX(X.X(X.X(X.X(X.X(X.Y)X/X2y3s$y Ny Ny Ny NX -X -X -X ,v LX5] Kq Fq Fq Fq Fq GP)P'VKp Gp Ep Ep Ep >X <X -WJj KhKW IhKW IhKW IhKW"
3716 " IhKW IhKW IjEq7m Bq Fq Fq Fq LW ,W ,W ,W &j EW6W Hl Al Al Al Al Ls AgKW HgKW HgKW HgKW @Z <WLh ;Z MV &[ =P \"U V +V )S (S CW4W "
3717 "De DX8X ?\\ 2| Fh IX -VBV Ek 6V4c1kEZ +V +V KV BW 0X 4W Mf At Mx Mq ,X Go :h =X 0l Ej ?X -W 1X 2W 6X(X(s ;o\"s Hy NX r%"
3718 "X/X'X -X -X2['w LX&X0X4\\ Im NX !m NX0Y(t 9X 2m ;Z 5[5[5X-X FX @~U&W !W -W @fJW JWJe ?j AeJW En IW 'cIW MW6W MW ,W ,W9Y LW ,W7W7W=W6W Fh DWJe AeJ"
3719 "W MW .m ;b 6eJW A\\ 5Z<Z)X6X >X ?v 6W :V MW CeG[$r Fc 2h Am EW6W @Y ?X3W MWMdL\\ cGc#m GW;\\ Hm HWKg Ah /] ?f I~Y(x MX 1iEi HX CX0X ?X 7m 8V 0"
3720 "X 2Z FX (j Kz AX :V HW -g Lh ,~W :WMdL\\ @UGU \"V$U-V5i0V$U7i HX(X.X(X.X(X.X(X.X(X.X(X/X2y1o\"y Ny Ny Ny NX -X -X -X ,t"
3721 " JX4\\ Im Bm Bm Bm Bm %VHm Dm Bm Bm Bm =X <X -WJh HfJW HfJW HfJW HfJW HfJW HfJW HhBn4j ?n Cn Cn Cn KW ,W ,W ,W %h DW6W Fh =h =h =h =h KVMi >eJW GeJW"
3722 " GeJW GeJW ?X ;WJe 9X MW &Z =U W ,W *R &Q BW4W B` AW6W >[ /y Dd GX -VCV Af 5V2a.gBZ ,W -W KV CX 0X 4V "
3723 " Kd @t Mx Km *X Ek 6d ;X .h Bh >X .X 1X 1W 7X(X(q 7j Np Ey NX Mm\"X/X'X -X -X1[(w LX&X0X4\\ Gi LX Ni LX/X$n 7X 0i 9Z 5[5[6Y-Y GX @~U&W V -W "
3724 " >cIW JWIb <g =bIW Ci FW %_GW MW6W MW ,W ,W8Y MW ,W7W7W=W6W Ef CWIb =bIW MW +h 8a 5cIW @Z 4Y:Y*Y5X ?X ?v 6W :V MW AbDY$WMf Ca 0f >k EW6W @Y ?"
3725 "W2W MWK`I[ NaEa i EW;\\ Fi FWIc >e ,\\ =b G~Y(x MX 1iEi HX CX0X ?X 5i 6V /V 0X EX &f Iz AX :V /P;W *c Gb )~W :WK`I[ @UGU "
3726 " #V#U.V4i1V#U6f FX(X.X(X.X(X.X(X.X(X.X(X/X2y/j Ny Ny Ny Ny NX -X -X -X ,p FX4\\ Gi >i >i >i >i $VEi @i >i >i >i ;X <X -WIf EcIW FcIW FcIW FcIW Fc"
3727 "IW FcIW Fd>i0g ;i >i >i >i HW ,W ,W ,W #d BW6W Ef ;f ;f ;f ;f JUJe ;cIW FcIW FcIW FcIW ?X ;WIb 7X MW %Y =T X -X )P %P AW4W ?Z"
3728 " >W6X ?Z ,w B` EX .VBV <] 1V0]*b?[ -W -W KV CW /X 4V I` >t Mx Hg 'X Bf 2` :X +d =b ;X .W 0X 1X 9X&X)m 0d Kj ?y NX Jg "
3729 "NX/X'X -X -X0[)w LX&X0X3[ Dc IX Kf LX/Y!g 4X .e 7Z 5Z3Z7Y+Y HX @~U&W V -W =`GW JWG^ 7b 9^GW Ad CW \"YDW MW6W MW ,W ,W7Y NW ,W7W7W=W6W B` @WG^ 9"
3730 "^GW MW (c 2] 3_GW @Z 3X:X*Y4Y @X ?v 6W :V MW ?_AW$WKb @^ +` 9g CW6W ?W ?X2X NWJ^GY K]B^ Ke CW:[ Dd CWG_ 9` 'Y ;^ F~[)x MX 1iEi HX CX0X ?X 2c "
3731 "3V .T .V DX $b Gz AX :V /R>X &[ ?Z %~W :WJ^GY ?UGU #V +V +V 1b EX&X0X&X0X&X0X&X0X&X0Y'X1X1y,d Ky Ny Ny Ny NX -X -X "
3732 "-X ,j @X3[ Dc 8c 8c 8c 8c !VBc ;e :e :e :e 9X <X -WFa B`GW E`GW E`GW E`GW E`GW E`GW D`:d*b 7d 9d 9d 9d EW ,W ,W ,W !` @W6W B` 5` 5` 5` 5` HVHa 7_GW"
3733 " D_GW D_GW D_GW ?X ;WG^ 5X MW 7S @r >Y BS .V,W#Z ;V -V 7W ;W EX ;\\ 6] "
3734 "+Z 5\\ 5Z <W 7X %\\ <] \"X ([ 4c E] /[ (W W .W :Y #X 0Z 2X *\\ $W &W .Z =WDX 3XDW I["
3735 " 0Y 8W -W :V MW <Z ;WH[ 9Y &Z 1] LW ?W >WGXBU FX=X E` \"W >] @WDY 3Z 2X C[ >T :[ KV /TAY "
3736 " EWGXBU =UGU BT 6V +V +V ,Y ?\\ +[ 0[ 0[ 0[ 0[ KT=[ 2[ 0[ 0[ 0[ 7Z ;Y .Y .Y .Y .Y .Y -"
3737 "Y2\\\"Z /\\ 1\\ 1\\ 1\\ CZ 3Z /Z /Z /Z /Z FVCZ 1Y .Y .Y .Y ,W :WDX 2W LW 7R #S"
3738 " >W /W 8W :V \"W 5X )X &Z CW NV .W :W %W"
3739 " @W :W -X -W :V MW LW FW ?W >W NW 0W =W 3S GV /XGZ "
3740 " DW HUGU AT %T 'R JT "
3741 " #T (X :W NX LW 7S =V /V 7W :V \"W"
3742 " 4X'Q &Y %Z DW NV .W :W %W @W :W -W ,W :V MW "
3743 " LW FW ?W >W NW 0W =W 3S GV /j CW HUGU @T "
3744 " %T 'P HT \"Q 'W 9W NW KW "
3745 " 7S =W 1W 7V :W \"V 2X)R &X #Z "
3746 " EW NW /W :W %W @W :W -W ,X ;V NX LW FW ?W >W NW 0W =W "
3747 " 3S GV /j CW HUGU @U &U "
3748 " U \"P 'W 9W NW KV 6S "
3749 " <V 1V 6V :V !V 1Y-U 'X \"Z FW MV /W ;X %W "
3750 " @W :W .X +W ;V NW KW FW ?W >W NW 0W =W 3S GV /h "
3751 " AW HUGU ?T %T NT "
3752 " )X 9W X KV 6S <W 3V 6V 9V \"V "
3753 " /Z1X (X !Z Ga (V 9a ;W $W @W :W .W *W ;V NW KW "
3754 " FW ?W >W NW 0W =W 3S GV .f @W HUGU ?U &"
3755 "U U *W 8W W JV "
3756 " 6S ;V 3V 6V :W \"V .[5[ *Y Z "
3757 " Ha (W :a <X $W @W :W /X *X <V X KW FW ?W >W NW 0W =W "
3758 " 3S GV +a >W HUGU >T %T "
3759 " NT +X 8W !X (VIV 6S :V 5V 5U"
3760 " 9W \"U +\\;] )X MZ Ia (W :a =Y %W ?W :W "
3761 " /W )[ ?V #[ KW FW ?W >W NW 0W =W 3S GV 'Z ;W "
3762 " HUGU >U &U U ,W 7W !"
3763 "W 'VIV 6S :V 6W 6V 4V *_C` "
3764 " )Y LZ Ja :a (P7Y $W ?W :W 0X (b GV +b JW FW ?W >W "
3765 " NW 0W =W 3S GV 7W HUGU >U &U "
3766 " U -X 7W \"X 'VJW "
3767 " 6S 9V 7V 5U 3U 'x (Z KZ Ka :a "
3768 " (R:Z $W ?W :W 0X (b GV +b JW FW ?W >W NW 0W =W 3S "
3769 " GV 7W #U &U U "
3770 " -X 7W \"X &UJW 6S 9W 9W "
3771 " Bu ([ IZ La :a (T>[ $X ?W :W 1X &a GV +a "
3772 " IW FW ?W >W NW 0W =W 3S GV 7W $V "
3773 " 'V !V .X 6W #X %VLW "
3774 " 5S 2p -a "
3775 " 8XE] %Y >W :W 3Z $_ GV +_ GW FW ?W >W NW 0W =W "
3776 " 3S GV 7W /QGW 2QGW ,QG"
3777 "W 0Z 6W %Z %a 5S "
3778 " 0l +a 8p +_ >W :W ;a !] G"
3779 "V +] EW FW ?W >W NW 0W =W 3S GV 7W /` "
3780 " 1` +` 7a 5W -a #` "
3781 " >e '` "
3782 " 7o *^ =W :W ;` KY GV +Y AW FW ?W >W NW 0W =W "
3783 " 3S GV 7W /` 1` +` "
3784 " 7` 4W -` \"_ "
3785 " 8\\ #_ \"} 3n )^ =W :W ;` 9V "
3786 " BW FW ?W >W NW 0W =W 'V 7W /_ "
3787 " 0_ *_ 6` 4W -` !] "
3788 " -] "
3789 " } 3l '] <W :W ;_ 8V BW FW ?W >W NW 0W =W "
3790 " 'V 7W /^ /^ )^ "
3791 " 5_ 3W -_ N[ "
3792 " ,[ M} 2j &\\ ;W :W ;^ 7V BW "
3793 " FW ?W >W NW 0W =W 7W -Y *Y "
3794 " $Y 2^ 2W -^ LX "
3795 " *X J} "
3796 " /d #Z 9W :W ;\\ 5V BW FW ?W >W NW 0W =W "
3797 " 7W "
3798 " /\\ 0W HT "
3799 " I} *[ NW 6W :W ;Z 3V BW FW ?W >W"
3800 " NW 0W =W 7W "
3801 " /Z .W "
3802 " =} "
3803 " "
3804 " D" };
3805 
3806  // Define a 40x38 'danger' color logo (used by cimg::dialog()).
3807  const unsigned char logo40x38[4576] = {
3808  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,
3809  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,
3810  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,
3811  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,
3812  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,
3813  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,
3814  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,
3815  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,
3816  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,
3817  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,
3818  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,
3819  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,
3820  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,
3821  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,
3822  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,
3823  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,
3824  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,
3825  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,
3826  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,
3827  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,
3828  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,
3829  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,
3830  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,
3831  200,200,200,91,49,124,118,124,71,32,124,95,49,56,114,52,82,121,0};
3832 
3834 
3838  inline std::FILE* output(std::FILE *file) {
3839  cimg::mutex(1);
3840  static std::FILE *res = stderr;
3841  if (file) res = file;
3842  cimg::mutex(1,0);
3843  return res;
3844  }
3845 
3846  // Return number of available CPU cores.
3847  inline unsigned int nb_cpus() {
3848  unsigned int res = 1;
3849 #if cimg_OS==2
3850  SYSTEM_INFO sysinfo;
3851  GetSystemInfo(&sysinfo);
3852  res = (unsigned int)sysinfo.dwNumberOfProcessors;
3853 #else
3854  res = (unsigned int)sysconf(_SC_NPROCESSORS_ONLN);
3855 #endif
3856  return res?res:1U;
3857  }
3858 
3859  // Lock/unlock mutex for CImg multi-thread programming.
3860  inline int mutex(const unsigned int n, const int lock_mode) {
3861  switch (lock_mode) {
3862  case 0 : cimg::Mutex_attr().unlock(n); return 0;
3863  case 1 : cimg::Mutex_attr().lock(n); return 0;
3864  default : return cimg::Mutex_attr().trylock(n);
3865  }
3866  }
3867 
3869 
3883  inline void warn(const char *const format, ...) {
3884  if (cimg::exception_mode()>=1) {
3885  char message[16384] = { 0 };
3886  std::va_list ap;
3887  va_start(ap,format);
3888  cimg_vsnprintf(message,sizeof(message),format,ap);
3889  va_end(ap);
3890 #ifdef cimg_strict_warnings
3891  throw CImgWarningException(message);
3892 #else
3893  std::fprintf(cimg::output(),"\n%s[CImg] *** Warning ***%s%s",cimg::t_red,cimg::t_normal,message);
3894 #endif
3895  }
3896  }
3897 
3898  // Execute an external system command.
3907  inline int system(const char *const command, const char *const module_name=0) {
3908  cimg::unused(module_name);
3909 #ifdef cimg_no_system_calls
3910  return -1;
3911 #else
3912 #if cimg_OS==1
3913  const unsigned int l = std::strlen(command);
3914  if (l) {
3915  char *const ncommand = new char[l+16];
3916  std::strncpy(ncommand,command,l);
3917  std::strcpy(ncommand+l," 2> /dev/null"); // Make command silent.
3918  const int out_val = std::system(ncommand);
3919  delete[] ncommand;
3920  return out_val;
3921  } else return -1;
3922 #elif cimg_OS==2
3923  PROCESS_INFORMATION pi;
3924  STARTUPINFO si;
3925  std::memset(&pi,0,sizeof(PROCESS_INFORMATION));
3926  std::memset(&si,0,sizeof(STARTUPINFO));
3927  GetStartupInfo(&si);
3928  si.cb = sizeof(si);
3929  si.wShowWindow = SW_HIDE;
3930  si.dwFlags |= SW_HIDE | STARTF_USESHOWWINDOW;
3931  const BOOL res = CreateProcess((LPCTSTR)module_name,(LPTSTR)command,0,0,FALSE,0,0,0,&si,&pi);
3932  if (res) {
3933  WaitForSingleObject(pi.hProcess, INFINITE);
3934  CloseHandle(pi.hThread);
3935  CloseHandle(pi.hProcess);
3936  return 0;
3937  } else return std::system(command);
3938 #endif
3939 #endif
3940  }
3941 
3943  template<typename T>
3944  inline T& temporary(const T&) {
3945  static T temp;
3946  return temp;
3947  }
3948 
3950  template<typename T>
3951  inline void swap(T& a, T& b) { T t = a; a = b; b = t; }
3952 
3954  template<typename T1, typename T2>
3955  inline void swap(T1& a1, T1& b1, T2& a2, T2& b2) {
3956  cimg::swap(a1,b1); cimg::swap(a2,b2);
3957  }
3958 
3960  template<typename T1, typename T2, typename T3>
3961  inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3) {
3962  cimg::swap(a1,b1,a2,b2); cimg::swap(a3,b3);
3963  }
3964 
3966  template<typename T1, typename T2, typename T3, typename T4>
3967  inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4) {
3968  cimg::swap(a1,b1,a2,b2,a3,b3); cimg::swap(a4,b4);
3969  }
3970 
3972  template<typename T1, typename T2, typename T3, typename T4, typename T5>
3973  inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5) {
3974  cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4); cimg::swap(a5,b5);
3975  }
3976 
3978  template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
3979  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) {
3980  cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5); cimg::swap(a6,b6);
3981  }
3982 
3984  template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
3985  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,
3986  T7& a7, T7& b7) {
3987  cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6); cimg::swap(a7,b7);
3988  }
3989 
3991  template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
3992  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,
3993  T7& a7, T7& b7, T8& a8, T8& b8) {
3994  cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6,a7,b7); cimg::swap(a8,b8);
3995  }
3996 
3998 
4001  inline bool endianness() {
4002  const int x = 1;
4003  return ((unsigned char*)&x)[0]?false:true;
4004  }
4005 
4007 
4011  template<typename T>
4012  inline void invert_endianness(T* const buffer, const unsigned long size) {
4013  if (size) switch (sizeof(T)) {
4014  case 1 : break;
4015  case 2 : { for (unsigned short *ptr = (unsigned short*)buffer+size; ptr>(unsigned short*)buffer; ) {
4016  const unsigned short val = *(--ptr);
4017  *ptr = (unsigned short)((val>>8)|((val<<8)));
4018  }
4019  } break;
4020  case 4 : { for (unsigned int *ptr = (unsigned int*)buffer+size; ptr>(unsigned int*)buffer; ) {
4021  const unsigned int val = *(--ptr);
4022  *ptr = (val>>24)|((val>>8)&0xff00)|((val<<8)&0xff0000)|(val<<24);
4023  }
4024  } break;
4025  default : { for (T* ptr = buffer+size; ptr>buffer; ) {
4026  unsigned char *pb = (unsigned char*)(--ptr), *pe = pb + sizeof(T);
4027  for (int i = 0; i<(int)sizeof(T)/2; ++i) swap(*(pb++),*(--pe));
4028  }
4029  }
4030  }
4031  }
4032 
4034 
4038  template<typename T>
4039  inline T& invert_endianness(T& a) {
4040  invert_endianness(&a,1);
4041  return a;
4042  }
4043 
4044  // Conversion functions to get more precision when trying to store unsigned ints values 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  // use memcpy instead of assignment to avoid undesired optimizations by C++-compiler.
4051  std::memcpy(&u,&f,sizeof(float));
4052  return ((u)<<1)>>1; // set sign bit to 0.
4053  }
4054 
4055  inline float uint2float(const unsigned int u) {
4056  if (u<(1U<<19)) return (float)u; // Consider safe storage of unsigned int as floats until 19bits (i.e 524287).
4057  float f;
4058  const unsigned int v = u|(1U<<(8*sizeof(unsigned int)-1)); // set sign bit to 1.
4059  // use memcpy instead of simple assignment to avoid undesired optimizations by C++-compiler.
4060  std::memcpy(&f,&v,sizeof(float));
4061  return f;
4062  }
4063 
4065 
4068  inline unsigned long time() {
4069 #if cimg_OS==1
4070  struct timeval st_time;
4071  gettimeofday(&st_time,0);
4072  return (unsigned long)(st_time.tv_usec/1000 + st_time.tv_sec*1000);
4073 #elif cimg_OS==2
4074  SYSTEMTIME st_time;
4075  GetSystemTime(&st_time);
4076  return (unsigned long)(st_time.wMilliseconds + 1000*(st_time.wSecond + 60*(st_time.wMinute + 60*st_time.wHour)));
4077 #else
4078  return 0;
4079 #endif
4080  }
4081 
4082  // Implement a tic/toc mechanism to display elapsed time of algorithms.
4083  inline unsigned long tictoc(const bool is_tic);
4084 
4086 
4089  inline unsigned long tic() {
4090  return cimg::tictoc(true);
4091  }
4092 
4094 
4097  inline unsigned long toc() {
4098  return cimg::tictoc(false);
4099  }
4100 
4102 
4107  inline void sleep(const unsigned int milliseconds) {
4108 #if cimg_OS==1
4109  struct timespec tv;
4110  tv.tv_sec = milliseconds/1000;
4111  tv.tv_nsec = (milliseconds%1000)*1000000;
4112  nanosleep(&tv,0);
4113 #elif cimg_OS==2
4114  Sleep(milliseconds);
4115 #endif
4116  }
4117 
4118  inline unsigned int _wait(const unsigned int milliseconds, unsigned long& timer) {
4119  if (!timer) timer = cimg::time();
4120  const unsigned long current_time = cimg::time();
4121  if (current_time>=timer+milliseconds) { timer = current_time; return 0; }
4122  const unsigned long time_diff = timer + milliseconds - current_time;
4123  timer = current_time + time_diff;
4124  cimg::sleep(time_diff);
4125  return (unsigned int)time_diff;
4126  }
4127 
4129 
4135  inline unsigned int wait(const unsigned int milliseconds) {
4136  cimg::mutex(3);
4137  static unsigned long timer = 0;
4138  if (!timer) timer = cimg::time();
4139  cimg::mutex(3,0);
4140  return _wait(milliseconds,timer);
4141  }
4142 
4143  // Random number generators.
4144  // CImg may use its own Random Number Generator (RNG) if configuration macro 'cimg_use_rng' is set.
4145  // Use it for instance when you have to deal with concurrent threads trying to call std::srand()
4146  // at the same time!
4147 #ifdef cimg_use_rng
4148 
4149 #include <stdint.h>
4150 
4151  // Use a custom RNG.
4152  inline unsigned int _rand(const unsigned int seed=0, const bool set_seed=false) {
4153  static unsigned long next = 1;
4154  cimg::mutex(4);
4155  if (set_seed) next = (unsigned long)seed;
4156  next = next*1103515245 + 12345 + rand();
4157  cimg::mutex(4,0);
4158  return (unsigned int)((next>>16)&0x7FFF);
4159  }
4160 
4161  inline void srand() {
4162  const unsigned int t = (unsigned int)cimg::time();
4163 #if cimg_OS==1
4164  cimg::_rand(t+(unsigned int)getpid(),true);
4165 #elif cimg_OS==2
4166  cimg::_rand(t+(unsigned int)_getpid(),true);
4167 #else
4168  cimg::_rand(t,true);
4169 #endif
4170  }
4171 
4172  inline void srand(const unsigned int seed) {
4173  _rand(seed,true);
4174  }
4175 
4176  inline double rand() {
4177  return cimg::_rand()/32767.;
4178  }
4179 
4180 #else
4181 
4182  // Use the system RNG.
4183  inline void srand() {
4184  const unsigned int t = (unsigned int)cimg::time();
4185 #if cimg_OS==1
4186  std::srand(t+(unsigned int)getpid());
4187 #elif cimg_OS==2
4188  std::srand(t+(unsigned int)_getpid());
4189 #else
4190  std::srand(t);
4191 #endif
4192  }
4193 
4194  inline void srand(const unsigned int seed) {
4195  std::srand(seed);
4196  }
4197 
4199 
4201  inline double rand() {
4202  return (double)std::rand()/RAND_MAX;
4203  }
4204 #endif
4205 
4207 
4209  inline double crand() {
4210  return 1-2*cimg::rand();
4211  }
4212 
4214 
4216  inline double grand() {
4217  double x1, w;
4218  do {
4219  const double x2 = 2*cimg::rand() - 1.0;
4220  x1 = 2*cimg::rand()-1.0;
4221  w = x1*x1 + x2*x2;
4222  } while (w<=0 || w>=1.0);
4223  return x1*std::sqrt((-2*std::log(w))/w);
4224  }
4225 
4227 
4229  inline unsigned int prand(const double z) {
4230  if (z<=1.0e-10) return 0;
4231  if (z>100) return (unsigned int)((std::sqrt(z) * cimg::grand()) + z);
4232  unsigned int k = 0;
4233  const double y = std::exp(-z);
4234  for (double s = 1.0; s>=y; ++k) s*=cimg::rand();
4235  return k-1;
4236  }
4237 
4239  template<typename T>
4240  inline T rol(const T a, const unsigned int n=1) {
4241  return n?(T)((a<<n)|(a>>((sizeof(T)<<3)-n))):a;
4242  }
4243 
4244  inline float rol(const float a, const unsigned int n=1) {
4245  return (float)rol((int)a,n);
4246  }
4247 
4248  inline double rol(const double a, const unsigned int n=1) {
4249  return (double)rol((long)a,n);
4250  }
4251 
4253  template<typename T>
4254  inline T ror(const T a, const unsigned int n=1) {
4255  return n?(T)((a>>n)|(a<<((sizeof(T)<<3)-n))):a;
4256  }
4257 
4258  inline float ror(const float a, const unsigned int n=1) {
4259  return (float)ror((int)a,n);
4260  }
4261 
4262  inline double ror(const double a, const unsigned int n=1) {
4263  return (double)ror((long)a,n);
4264  }
4265 
4267  template<typename T>
4268  inline T abs(const T a) {
4269  return a>=0?a:-a;
4270  }
4271  inline bool abs(const bool a) {
4272  return a;
4273  }
4274  inline unsigned char abs(const unsigned char a) {
4275  return a;
4276  }
4277  inline unsigned short abs(const unsigned short a) {
4278  return a;
4279  }
4280  inline unsigned int abs(const unsigned int a) {
4281  return a;
4282  }
4283  inline unsigned long abs(const unsigned long a) {
4284  return a;
4285  }
4286  inline double abs(const double a) {
4287  return std::fabs(a);
4288  }
4289  inline float abs(const float a) {
4290  return (float)std::fabs((double)a);
4291  }
4292  inline int abs(const int a) {
4293  return std::abs(a);
4294  }
4295 
4297  template<typename T>
4298  inline T sqr(const T val) {
4299  return val*val;
4300  }
4301 
4303  inline int xln(const int x) {
4304  return x>0?(int)(1+std::log10((double)x)):1;
4305  }
4306 
4308  template<typename t1, typename t2>
4309  inline typename cimg::superset<t1,t2>::type min(const t1& a, const t2& b) {
4310  typedef typename cimg::superset<t1,t2>::type t1t2;
4311  return (t1t2)(a<=b?a:b);
4312  }
4313 
4315  template<typename t1, typename t2, typename t3>
4316  inline typename cimg::superset2<t1,t2,t3>::type min(const t1& a, const t2& b, const t3& c) {
4317  typedef typename cimg::superset2<t1,t2,t3>::type t1t2t3;
4318  return (t1t2t3)cimg::min(cimg::min(a,b),c);
4319  }
4320 
4322  template<typename t1, typename t2, typename t3, typename t4>
4323  inline typename cimg::superset3<t1,t2,t3,t4>::type min(const t1& a, const t2& b, const t3& c, const t4& d) {
4324  typedef typename cimg::superset3<t1,t2,t3,t4>::type t1t2t3t4;
4325  return (t1t2t3t4)cimg::min(cimg::min(a,b,c),d);
4326  }
4327 
4329  template<typename t1, typename t2>
4330  inline typename cimg::superset<t1,t2>::type max(const t1& a, const t2& b) {
4331  typedef typename cimg::superset<t1,t2>::type t1t2;
4332  return (t1t2)(a>=b?a:b);
4333  }
4334 
4336  template<typename t1, typename t2, typename t3>
4337  inline typename cimg::superset2<t1,t2,t3>::type max(const t1& a, const t2& b, const t3& c) {
4338  typedef typename cimg::superset2<t1,t2,t3>::type t1t2t3;
4339  return (t1t2t3)cimg::max(cimg::max(a,b),c);
4340  }
4341 
4343  template<typename t1, typename t2, typename t3, typename t4>
4344  inline typename cimg::superset3<t1,t2,t3,t4>::type max(const t1& a, const t2& b, const t3& c, const t4& d) {
4345  typedef typename cimg::superset3<t1,t2,t3,t4>::type t1t2t3t4;
4346  return (t1t2t3t4)cimg::max(cimg::max(a,b,c),d);
4347  }
4348 
4350  template<typename T>
4351  inline T sign(const T x) {
4352  return (x<0)?(T)(-1):(x==0?(T)0:(T)1);
4353  }
4354 
4356  template<typename T>
4357  inline unsigned long nearest_pow2(const T x) {
4358  unsigned long i = 1;
4359  while (x>i) i<<=1;
4360  return i;
4361  }
4362 
4364  inline double sinc(const double x) {
4365  return x?std::sin(x)/x:1;
4366  }
4367 
4369 
4374  template<typename T>
4375  inline T mod(const T& x, const T& m) {
4376  const double dx = (double)x, dm = (double)m;
4377  return (T)(dx - dm * std::floor(dx / dm));
4378  }
4379  inline int mod(const bool x, const bool m) {
4380  return m?(x?1:0):0;
4381  }
4382  inline int mod(const char x, const char m) {
4383  return x>=0?x%m:(x%m?m+x%m:0);
4384  }
4385  inline int mod(const short x, const short m) {
4386  return x>=0?x%m:(x%m?m+x%m:0);
4387  }
4388  inline int mod(const int x, const int m) {
4389  return x>=0?x%m:(x%m?m+x%m:0);
4390  }
4391  inline int mod(const long x, const long m) {
4392  return x>=0?x%m:(x%m?m+x%m:0);
4393  }
4394  inline int mod(const unsigned char x, const unsigned char m) {
4395  return x%m;
4396  }
4397  inline int mod(const unsigned short x, const unsigned short m) {
4398  return x%m;
4399  }
4400  inline int mod(const unsigned int x, const unsigned int m) {
4401  return x%m;
4402  }
4403  inline int mod(const unsigned long x, const unsigned long m) {
4404  return x%m;
4405  }
4406 
4408 
4413  template<typename T>
4414  inline T minmod(const T a, const T b) {
4415  return a*b<=0?0:(a>0?(a<b?a:b):(a<b?b:a));
4416  }
4417 
4419  inline double log2(const double x) {
4420  static const double base = std::log(2.0);
4421  return std::log(x)/base;
4422  }
4423 
4425 
4431  template<typename T>
4432  inline T round(const T x, const double y=1, const int rounding_type=0) {
4433  if (y<=0) return x;
4434  const double sx = (double)x/y, floor = std::floor(sx), delta = sx - floor;
4435  return (T)(y*(rounding_type<0?floor:rounding_type>0?std::ceil(sx):delta<0.5?floor:std::ceil(sx)));
4436  }
4437 
4438  inline double _pythagore(double a, double b) {
4439  const double absa = cimg::abs(a), absb = cimg::abs(b);
4440  if (absa>absb) { const double tmp = absb/absa; return absa*std::sqrt(1.0+tmp*tmp); }
4441  else { const double tmp = absa/absb; return absb==0?0:absb*std::sqrt(1.0+tmp*tmp); }
4442  }
4443 
4444  inline bool _is_self_expr(const char *expression) {
4445  if (!expression || *expression=='>' || *expression=='<') return false;
4446  for (const char *s = expression; *s; ++s)
4447  if ((*s=='i' || *s=='j') && (s[1]=='(' || s[1]=='[')) return true;
4448  return false;
4449  }
4450 
4452  inline char uncase(const char x) {
4453  return (char)((x<'A'||x>'Z')?x:x-'A'+'a');
4454  }
4455 
4457  inline void uncase(char *const str) {
4458  if (str) for (char *ptr = str; *ptr; ++ptr) *ptr = uncase(*ptr);
4459  }
4460 
4462 
4468  inline double atof(const char *const str) {
4469  double x = 0, y = 1;
4470  if (!str) return 0; else { std::sscanf(str,"%lf/%lf",&x,&y); return x/y; }
4471  }
4472 
4474 
4481  inline int strncasecmp(const char *const str1, const char *const str2, const int l) {
4482  if (!l) return 0;
4483  if (!str1) return str2?-1:0;
4484  const char *nstr1 = str1, *nstr2 = str2;
4485  int k, diff = 0; for (k = 0; k<l && !(diff = uncase(*nstr1)-uncase(*nstr2)); ++k) { ++nstr1; ++nstr2; }
4486  return k!=l?diff:0;
4487  }
4488 
4490 
4496  inline int strcasecmp(const char *const str1, const char *const str2) {
4497  if (!str1) return str2?-1:0;
4498  const unsigned int
4499  l1 = (unsigned int)std::strlen(str1),
4500  l2 = (unsigned int)std::strlen(str2);
4501  return cimg::strncasecmp(str1,str2,1+(l1<l2?l1:l2));
4502  }
4503 
4505 
4513  inline bool strpare(char *const str, const char delimiter=' ',
4514  const bool is_symmetric=false, const bool is_iterative=false) {
4515  if (!str) return false;
4516  const int l = (int)std::strlen(str);
4517  int p, q;
4518  if (is_symmetric) for (p = 0, q = l-1; p<q && str[p]==delimiter && str[q]==delimiter; ) {
4519  --q; ++p; if (!is_iterative) break;
4520  } else {
4521  for (p = 0; p<l && str[p]==delimiter; ) { ++p; if (!is_iterative) break; }
4522  for (q = l-1; q>p && str[q]==delimiter; ) { --q; if (!is_iterative) break; }
4523  }
4524  const int n = q - p + 1;
4525  if (n!=l) { std::memmove(str,str+p,n); str[n] = 0; return true; }
4526  return false;
4527  }
4528 
4530 
4533  inline void strunescape(char *const str) {
4534 #define cimg_strunescape(ci,co) case ci: *nd = co; ++ns; break;
4535  unsigned int val = 0;
4536  for (char *ns = str, *nd = str; *ns || (bool)(*nd=0); ++nd) if (*ns=='\\') switch (*(++ns)) {
4537  cimg_strunescape('n','\n');
4538  cimg_strunescape('t','\t');
4539  cimg_strunescape('v','\v');
4540  cimg_strunescape('b','\b');
4541  cimg_strunescape('r','\r');
4542  cimg_strunescape('f','\f');
4543  cimg_strunescape('a','\a');
4544  cimg_strunescape('\\','\\');
4545  cimg_strunescape('\?','\?');
4546  cimg_strunescape('\'','\'');
4547  cimg_strunescape('\"','\"');
4548  case 0 : *nd = 0; break;
4549  case '0' : case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' :
4550  std::sscanf(ns,"%o",&val); while (*ns>='0' && *ns<='7') ++ns;
4551  *nd = val; break;
4552  case 'x':
4553  std::sscanf(++ns,"%x",&val);
4554  while ((*ns>='0' && *ns<='7') || (*ns>='a' && *ns<='f') || (*ns>='A' && *ns<='F')) ++ns;
4555  *nd = val; break;
4556  default : *nd = *(ns++);
4557  } else *nd = *(ns++);
4558  }
4559 
4560  // Return a temporary string describing the size of a memory buffer.
4561  inline const char *strbuffersize(const unsigned long size) {
4562  static char res[256] = { 0 };
4563  cimg::mutex(5);
4564  if (size<1024LU) cimg_snprintf(res,sizeof(res),"%lu byte%s",size,size>1?"s":"");
4565  else if (size<1024*1024LU) { const float nsize = size/1024.0f; cimg_snprintf(res,sizeof(res),"%.1f Kio",nsize); }
4566  else if (size<1024*1024*1024LU) {
4567  const float nsize = size/(1024*1024.0f); cimg_snprintf(res,sizeof(res),"%.1f Mio",nsize);
4568  } else { const float nsize = size/(1024*1024*1024.0f); cimg_snprintf(res,sizeof(res),"%.1f Gio",nsize); }
4569  cimg::mutex(5,0);
4570  return res;
4571  }
4572 
4573  // Return string that identifies the running OS.
4574  inline const char *stros() {
4575 #if defined(linux) || defined(__linux) || defined(__linux__)
4576  const char *const str = "Linux";
4577 #elif defined(sun) || defined(__sun)
4578  const char *const str = "Sun OS";
4579 #elif defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined (__DragonFly__)
4580  const char *const str = "BSD";
4581 #elif defined(sgi) || defined(__sgi)
4582  const char *const str = "Irix";
4583 #elif defined(__MACOSX__) || defined(__APPLE__)
4584  const char *const str = "Mac OS";
4585 #elif defined(unix) || defined(__unix) || defined(__unix__)
4586  const char *const str = "Generic Unix";
4587 #elif defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || \
4588  defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
4589  const char *const str = "Windows";
4590 #else
4591  const char
4592  *const _str1 = std::getenv("OSTYPE"),
4593  *const _str2 = _str1?_str1:std::getenv("OS"),
4594  *const str = _str2?_str2:"Unknown OS";
4595 #endif
4596  return str;
4597  }
4598 
4600  inline const char* basename(const char *const str) {
4601  const char *p = 0;
4602  for (const char *np = str; np>=str && (p=np); np = std::strchr(np,cimg_file_separator)+1) {}
4603  return p;
4604  }
4605 
4606  // Return a random filename.
4607  inline const char* filenamerand() {
4608  cimg::mutex(6);
4609  static char randomid[9] = { 0 };
4610  cimg::srand();
4611  for (unsigned int k = 0; k<8; ++k) {
4612  const int v = (int)std::rand()%3;
4613  randomid[k] = (char)(v==0?('0'+(std::rand()%10)):(v==1?('a'+(std::rand()%26)):('A'+(std::rand()%26))));
4614  }
4615  cimg::mutex(6,0);
4616  return randomid;
4617  }
4618 
4619  // Convert filename as a Windows-style filename (short path name).
4620  inline void winformat_string(char *const str) {
4621  if (str && *str) {
4622 #if cimg_OS==2
4623  char *const nstr = new char[MAX_PATH];
4624  if (GetShortPathNameA(str,nstr,MAX_PATH)) std::strcpy(str,nstr);
4625 #endif
4626  }
4627  }
4628 
4630 
4637  inline std::FILE *fopen(const char *const path, const char *const mode) {
4638  if (!path)
4639  throw CImgArgumentException("cimg::fopen(): Specified file path is (null).");
4640  if (!mode)
4641  throw CImgArgumentException("cimg::fopen(): File '%s', specified mode is (null).",
4642  path);
4643  std::FILE *res = 0;
4644  if (*path=='-' && (!path[1] || path[1]=='.')) {
4645  res = (*mode=='r')?stdin:stdout;
4646 #if cimg_OS==2
4647  if (*mode && mode[1]=='b') { // Force stdin/stdout to be in binary mode.
4648  if (_setmode(_fileno(res),0x8000)==-1) res = 0;
4649  }
4650 #endif
4651  } else res = std::fopen(path,mode);
4652  if (!res) throw CImgIOException("cimg::fopen(): Failed to open file '%s' with mode '%s'.",
4653  path,mode);
4654  return res;
4655  }
4656 
4658 
4664  inline int fclose(std::FILE *file) {
4665  if (!file) warn("cimg::fclose(): Specified file is (null).");
4666  if (!file || file==stdin || file==stdout) return 0;
4667  const int errn = std::fclose(file);
4668  if (errn!=0) warn("cimg::fclose(): Error code %d returned during file closing.",
4669  errn);
4670  return errn;
4671  }
4672 
4674 
4679  inline const char* temporary_path(const char *const user_path=0, const bool reinit_path=false) {
4680 #define _cimg_test_temporary_path(p) \
4681  if (!path_found) { \
4682  cimg_snprintf(s_path,1024,"%s",p); \
4683  cimg_snprintf(tmp,sizeof(tmp),"%s%c%s",s_path,cimg_file_separator,filetmp); \
4684  if ((file=std::fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } \
4685  }
4686  static char *s_path = 0;
4687  cimg::mutex(7);
4688  if (reinit_path) { delete[] s_path; s_path = 0; }
4689  if (user_path) {
4690  if (!s_path) s_path = new char[1024];
4691  std::memset(s_path,0,1024);
4692  std::strncpy(s_path,user_path,1023);
4693  } else if (!s_path) {
4694  s_path = new char[1024];
4695  std::memset(s_path,0,1024);
4696  bool path_found = false;
4697  char tmp[1024] = { 0 }, filetmp[512] = { 0 };
4698  std::FILE *file = 0;
4699  cimg_snprintf(filetmp,sizeof(filetmp),"%s.tmp",cimg::filenamerand());
4700  char *tmpPath = std::getenv("TMP");
4701  if (!tmpPath) { tmpPath = std::getenv("TEMP"); winformat_string(tmpPath); }
4702  if (tmpPath) _cimg_test_temporary_path(tmpPath);
4703 #if cimg_OS==2
4704  _cimg_test_temporary_path("C:\\WINNT\\Temp");
4705  _cimg_test_temporary_path("C:\\WINDOWS\\Temp");
4706  _cimg_test_temporary_path("C:\\Temp");
4707  _cimg_test_temporary_path("C:");
4708  _cimg_test_temporary_path("D:\\WINNT\\Temp");
4709  _cimg_test_temporary_path("D:\\WINDOWS\\Temp");
4710  _cimg_test_temporary_path("D:\\Temp");
4711  _cimg_test_temporary_path("D:");
4712 #else
4713  _cimg_test_temporary_path("/tmp");
4714  _cimg_test_temporary_path("/var/tmp");
4715 #endif
4716  if (!path_found) {
4717  *s_path = 0;
4718  std::strncpy(tmp,filetmp,sizeof(tmp)-1);
4719  if ((file=std::fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; }
4720  }
4721  if (!path_found) {
4722  cimg::mutex(7,0);
4723  throw CImgIOException("cimg::temporary_path(): Failed to locate path for writing temporary files.\n");
4724  }
4725  }
4726  cimg::mutex(7,0);
4727  return s_path;
4728  }
4729 
4731 
4736 #if cimg_OS==2
4737  inline const char* programfiles_path(const char *const user_path=0, const bool reinit_path=false) {
4738  static char *s_path = 0;
4739  cimg::mutex(7);
4740  if (reinit_path) { delete[] s_path; s_path = 0; }
4741  if (user_path) {
4742  if (!s_path) s_path = new char[1024];
4743  std::memset(s_path,0,1024);
4744  std::strncpy(s_path,user_path,1023);
4745  } else if (!s_path) {
4746  s_path = new char[MAX_PATH];
4747  std::memset(s_path,0,MAX_PATH);
4748  // Note: in the following line, 0x26 = CSIDL_PROGRAM_FILES (not defined on every compiler).
4749 #if !defined(__INTEL_COMPILER)
4750  if (!SHGetSpecialFolderPathA(0,s_path,0x0026,false)) {
4751  const char *const pfPath = std::getenv("PROGRAMFILES");
4752  if (pfPath) std::strncpy(s_path,pfPath,MAX_PATH-1);
4753  else std::strcpy(s_path,"C:\\PROGRA~1");
4754  }
4755 #else
4756  std::strcpy(s_path,"C:\\PROGRA~1");
4757 #endif
4758  }
4759  cimg::mutex(7,0);
4760  return s_path;
4761  }
4762 #endif
4763 
4765 
4770  inline const char* imagemagick_path(const char *const user_path=0, const bool reinit_path=false) {
4771  static char *s_path = 0;
4772  cimg::mutex(7);
4773  if (reinit_path) { delete[] s_path; s_path = 0; }
4774  if (user_path) {
4775  if (!s_path) s_path = new char[1024];
4776  std::memset(s_path,0,1024);
4777  std::strncpy(s_path,user_path,1023);
4778  } else if (!s_path) {
4779  s_path = new char[1024];
4780  std::memset(s_path,0,1024);
4781  bool path_found = false;
4782  std::FILE *file = 0;
4783 #if cimg_OS==2
4784  const char *const pf_path = programfiles_path();
4785  if (!path_found) {
4786  std::strcpy(s_path,".\\convert.exe");
4787  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4788  }
4789  for (int k = 32; k>=10 && !path_found; --k) {
4790  cimg_snprintf(s_path,sizeof(s_path),"%s\\IMAGEM~1.%.2d-\\convert.exe",pf_path,k);
4791  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4792  }
4793  for (int k = 9; k>=0 && !path_found; --k) {
4794  cimg_snprintf(s_path,sizeof(s_path),"%s\\IMAGEM~1.%d-Q\\convert.exe",pf_path,k);
4795  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4796  }
4797  for (int k = 32; k>=0 && !path_found; --k) {
4798  cimg_snprintf(s_path,sizeof(s_path),"%s\\IMAGEM~1.%d\\convert.exe",pf_path,k);
4799  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4800  }
4801  for (int k = 32; k>=10 && !path_found; --k) {
4802  cimg_snprintf(s_path,sizeof(s_path),"%s\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",pf_path,k);
4803  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4804  }
4805  for (int k = 9; k>=0 && !path_found; --k) {
4806  cimg_snprintf(s_path,sizeof(s_path),"%s\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",pf_path,k);
4807  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4808  }
4809  for (int k = 32; k>=0 && !path_found; --k) {
4810  cimg_snprintf(s_path,sizeof(s_path),"%s\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",pf_path,k);
4811  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4812  }
4813  for (int k = 32; k>=10 && !path_found; --k) {
4814  cimg_snprintf(s_path,sizeof(s_path),"C:\\IMAGEM~1.%.2d-\\convert.exe",k);
4815  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4816  }
4817  for (int k = 9; k>=0 && !path_found; --k) {
4818  cimg_snprintf(s_path,sizeof(s_path),"C:\\IMAGEM~1.%d-Q\\convert.exe",k);
4819  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4820  }
4821  for (int k = 32; k>=0 && !path_found; --k) {
4822  cimg_snprintf(s_path,sizeof(s_path),"C:\\IMAGEM~1.%d\\convert.exe",k);
4823  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4824  }
4825  for (int k = 32; k>=10 && !path_found; --k) {
4826  cimg_snprintf(s_path,sizeof(s_path),"C:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",k);
4827  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4828  }
4829  for (int k = 9; k>=0 && !path_found; --k) {
4830  cimg_snprintf(s_path,sizeof(s_path),"C:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",k);
4831  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4832  }
4833  for (int k = 32; k>=0 && !path_found; --k) {
4834  cimg_snprintf(s_path,sizeof(s_path),"C:\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",k);
4835  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4836  }
4837  for (int k = 32; k>=10 && !path_found; --k) {
4838  cimg_snprintf(s_path,sizeof(s_path),"D:\\IMAGEM~1.%.2d-\\convert.exe",k);
4839  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4840  }
4841  for (int k = 9; k>=0 && !path_found; --k) {
4842  cimg_snprintf(s_path,sizeof(s_path),"D:\\IMAGEM~1.%d-Q\\convert.exe",k);
4843  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4844  }
4845  for (int k = 32; k>=0 && !path_found; --k) {
4846  cimg_snprintf(s_path,sizeof(s_path),"D:\\IMAGEM~1.%d\\convert.exe",k);
4847  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4848  }
4849  for (int k = 32; k>=10 && !path_found; --k) {
4850  cimg_snprintf(s_path,sizeof(s_path),"D:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",k);
4851  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4852  }
4853  for (int k = 9; k>=0 && !path_found; --k) {
4854  cimg_snprintf(s_path,sizeof(s_path),"D:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",k);
4855  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4856  }
4857  for (int k = 32; k>=0 && !path_found; --k) {
4858  cimg_snprintf(s_path,sizeof(s_path),"D:\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",k);
4859  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4860  }
4861  if (!path_found) std::strcpy(s_path,"convert.exe");
4862 #else
4863  if (!path_found) {
4864  std::strcpy(s_path,"./convert");
4865  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4866  }
4867  if (!path_found) std::strcpy(s_path,"convert");
4868 #endif
4869  winformat_string(s_path);
4870  }
4871  cimg::mutex(7,0);
4872  return s_path;
4873  }
4874 
4876 
4881  inline const char* graphicsmagick_path(const char *const user_path=0, const bool reinit_path=false) {
4882  static char *s_path = 0;
4883  cimg::mutex(7);
4884  if (reinit_path) { delete[] s_path; s_path = 0; }
4885  if (user_path) {
4886  if (!s_path) s_path = new char[1024];
4887  std::memset(s_path,0,1024);
4888  std::strncpy(s_path,user_path,1023);
4889  } else if (!s_path) {
4890  s_path = new char[1024];
4891  std::memset(s_path,0,1024);
4892  bool path_found = false;
4893  std::FILE *file = 0;
4894 #if cimg_OS==2
4895  const char *const pf_path = programfiles_path();
4896  if (!path_found) {
4897  std::strcpy(s_path,".\\gm.exe");
4898  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4899  }
4900  for (int k = 32; k>=10 && !path_found; --k) {
4901  cimg_snprintf(s_path,sizeof(s_path),"%s\\GRAPHI~1.%.2d-\\gm.exe",pf_path,k);
4902  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4903  }
4904  for (int k = 9; k>=0 && !path_found; --k) {
4905  cimg_snprintf(s_path,sizeof(s_path),"%s\\GRAPHI~1.%d-Q\\gm.exe",pf_path,k);
4906  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4907  }
4908  for (int k = 32; k>=0 && !path_found; --k) {
4909  cimg_snprintf(s_path,sizeof(s_path),"%s\\GRAPHI~1.%d\\gm.exe",pf_path,k);
4910  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4911  }
4912  for (int k = 32; k>=10 && !path_found; --k) {
4913  cimg_snprintf(s_path,sizeof(s_path),"%s\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",pf_path,k);
4914  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4915  }
4916  for (int k = 9; k>=0 && !path_found; --k) {
4917  cimg_snprintf(s_path,sizeof(s_path),"%s\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",pf_path,k);
4918  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4919  }
4920  for (int k = 32; k>=0 && !path_found; --k) {
4921  cimg_snprintf(s_path,sizeof(s_path),"%s\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",pf_path,k);
4922  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4923  }
4924  for (int k = 32; k>=10 && !path_found; --k) {
4925  cimg_snprintf(s_path,sizeof(s_path),"C:\\GRAPHI~1.%.2d-\\gm.exe",k);
4926  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4927  }
4928  for (int k = 9; k>=0 && !path_found; --k) {
4929  cimg_snprintf(s_path,sizeof(s_path),"C:\\GRAPHI~1.%d-Q\\gm.exe",k);
4930  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4931  }
4932  for (int k = 32; k>=0 && !path_found; --k) {
4933  cimg_snprintf(s_path,sizeof(s_path),"C:\\GRAPHI~1.%d\\gm.exe",k);
4934  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4935  }
4936  for (int k = 32; k>=10 && !path_found; --k) {
4937  cimg_snprintf(s_path,sizeof(s_path),"C:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k);
4938  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4939  }
4940  for (int k = 9; k>=0 && !path_found; --k) {
4941  cimg_snprintf(s_path,sizeof(s_path),"C:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k);
4942  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4943  }
4944  for (int k = 32; k>=0 && !path_found; --k) {
4945  cimg_snprintf(s_path,sizeof(s_path),"C:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k);
4946  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4947  }
4948  for (int k = 32; k>=10 && !path_found; --k) {
4949  cimg_snprintf(s_path,sizeof(s_path),"D:\\GRAPHI~1.%.2d-\\gm.exe",k);
4950  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4951  }
4952  for (int k = 9; k>=0 && !path_found; --k) {
4953  cimg_snprintf(s_path,sizeof(s_path),"D:\\GRAPHI~1.%d-Q\\gm.exe",k);
4954  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4955  }
4956  for (int k = 32; k>=0 && !path_found; --k) {
4957  cimg_snprintf(s_path,sizeof(s_path),"D:\\GRAPHI~1.%d\\gm.exe",k);
4958  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4959  }
4960  for (int k = 32; k>=10 && !path_found; --k) {
4961  cimg_snprintf(s_path,sizeof(s_path),"D:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k);
4962  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4963  }
4964  for (int k = 9; k>=0 && !path_found; --k) {
4965  cimg_snprintf(s_path,sizeof(s_path),"D:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k);
4966  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4967  }
4968  for (int k = 32; k>=0 && !path_found; --k) {
4969  cimg_snprintf(s_path,sizeof(s_path),"D:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k);
4970  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4971  }
4972  if (!path_found) std::strcpy(s_path,"gm.exe");
4973 #else
4974  if (!path_found) {
4975  std::strcpy(s_path,"./gm");
4976  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4977  }
4978  if (!path_found) std::strcpy(s_path,"gm");
4979 #endif
4980  winformat_string(s_path);
4981  }
4982  cimg::mutex(7,0);
4983  return s_path;
4984  }
4985 
4987 
4992  inline const char* medcon_path(const char *const user_path=0, const bool reinit_path=false) {
4993  static char *s_path = 0;
4994  cimg::mutex(7);
4995  if (reinit_path) { delete[] s_path; s_path = 0; }
4996  if (user_path) {
4997  if (!s_path) s_path = new char[1024];
4998  std::memset(s_path,0,1024);
4999  std::strncpy(s_path,user_path,1023);
5000  } else if (!s_path) {
5001  s_path = new char[1024];
5002  std::memset(s_path,0,1024);
5003  bool path_found = false;
5004  std::FILE *file = 0;
5005 #if cimg_OS==2
5006  const char *const pf_path = programfiles_path();
5007  if (!path_found) {
5008  std::strcpy(s_path,".\\medcon.exe");
5009  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5010  }
5011  if (!path_found) {
5012  cimg_snprintf(s_path,sizeof(s_path),"%s\\XMedCon\\bin\\medcon.bat",pf_path);
5013  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5014  }
5015  if (!path_found) {
5016  cimg_snprintf(s_path,sizeof(s_path),"%s\\XMedCon\\bin\\medcon.exe",pf_path);
5017  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5018  }
5019  if (!path_found) {
5020  std::strcpy(s_path,"C:\\XMedCon\\bin\\medcon.exe");
5021  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5022  }
5023  if (!path_found) std::strcpy(s_path,"medcon.exe");
5024 #else
5025  if (!path_found) {
5026  std::strcpy(s_path,"./medcon");
5027  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5028  }
5029  if (!path_found) std::strcpy(s_path,"medcon");
5030 #endif
5031  winformat_string(s_path);
5032  }
5033  cimg::mutex(7,0);
5034  return s_path;
5035  }
5036 
5038 
5043  inline const char *ffmpeg_path(const char *const user_path=0, const bool reinit_path=false) {
5044  static char *s_path = 0;
5045  cimg::mutex(7);
5046  if (reinit_path) { delete[] s_path; s_path = 0; }
5047  if (user_path) {
5048  if (!s_path) s_path = new char[1024];
5049  std::memset(s_path,0,1024);
5050  std::strncpy(s_path,user_path,1023);
5051  } else if (!s_path) {
5052  s_path = new char[1024];
5053  std::memset(s_path,0,1024);
5054  bool path_found = false;
5055  std::FILE *file = 0;
5056 #if cimg_OS==2
5057  if (!path_found) {
5058  std::strcpy(s_path,".\\ffmpeg.exe");
5059  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5060  }
5061  if (!path_found) std::strcpy(s_path,"ffmpeg.exe");
5062 #else
5063  if (!path_found) {
5064  std::strcpy(s_path,"./ffmpeg");
5065  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5066  }
5067  if (!path_found) std::strcpy(s_path,"ffmpeg");
5068 #endif
5069  winformat_string(s_path);
5070  }
5071  cimg::mutex(7,0);
5072  return s_path;
5073  }
5074 
5076 
5081  inline const char *gzip_path(const char *const user_path=0, const bool reinit_path=false) {
5082  static char *s_path = 0;
5083  cimg::mutex(7);
5084  if (reinit_path) { delete[] s_path; s_path = 0; }
5085  if (user_path) {
5086  if (!s_path) s_path = new char[1024];
5087  std::memset(s_path,0,1024);
5088  std::strncpy(s_path,user_path,1023);
5089  } else if (!s_path) {
5090  s_path = new char[1024];
5091  std::memset(s_path,0,1024);
5092  bool path_found = false;
5093  std::FILE *file = 0;
5094 #if cimg_OS==2
5095  if (!path_found) {
5096  std::strcpy(s_path,".\\gzip.exe");
5097  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5098  }
5099  if (!path_found) std::strcpy(s_path,"gzip.exe");
5100 #else
5101  if (!path_found) {
5102  std::strcpy(s_path,"./gzip");
5103  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5104  }
5105  if (!path_found) std::strcpy(s_path,"gzip");
5106 #endif
5107  winformat_string(s_path);
5108  }
5109  cimg::mutex(7,0);
5110  return s_path;
5111  }
5112 
5114 
5119  inline const char *gunzip_path(const char *const user_path=0, const bool reinit_path=false) {
5120  static char *s_path = 0;
5121  cimg::mutex(7);
5122  if (reinit_path) { delete[] s_path; s_path = 0; }
5123  if (user_path) {
5124  if (!s_path) s_path = new char[1024];
5125  std::memset(s_path,0,1024);
5126  std::strncpy(s_path,user_path,1023);
5127  } else if (!s_path) {
5128  s_path = new char[1024];
5129  std::memset(s_path,0,1024);
5130  bool path_found = false;
5131  std::FILE *file = 0;
5132 #if cimg_OS==2
5133  if (!path_found) {
5134  std::strcpy(s_path,".\\gunzip.exe");
5135  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5136  }
5137  if (!path_found) std::strcpy(s_path,"gunzip.exe");
5138 #else
5139  if (!path_found) {
5140  std::strcpy(s_path,"./gunzip");
5141  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5142  }
5143  if (!path_found) std::strcpy(s_path,"gunzip");
5144 #endif
5145  winformat_string(s_path);
5146  }
5147  cimg::mutex(7,0);
5148  return s_path;
5149  }
5150 
5152 
5157  inline const char *dcraw_path(const char *const user_path=0, const bool reinit_path=false) {
5158  static char *s_path = 0;
5159  cimg::mutex(7);
5160  if (reinit_path) { delete[] s_path; s_path = 0; }
5161  if (user_path) {
5162  if (!s_path) s_path = new char[1024];
5163  std::memset(s_path,0,1024);
5164  std::strncpy(s_path,user_path,1023);
5165  } else if (!s_path) {
5166  s_path = new char[1024];
5167  std::memset(s_path,0,1024);
5168  bool path_found = false;
5169  std::FILE *file = 0;
5170 #if cimg_OS==2
5171  if (!path_found) {
5172  std::strcpy(s_path,".\\dcraw.exe");
5173  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5174  }
5175  if (!path_found) std::strcpy(s_path,"dcraw.exe");
5176 #else
5177  if (!path_found) {
5178  std::strcpy(s_path,"./dcraw");
5179  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5180  }
5181  if (!path_found) std::strcpy(s_path,"dcraw");
5182 #endif
5183  winformat_string(s_path);
5184  }
5185  cimg::mutex(7,0);
5186  return s_path;
5187  }
5188 
5190 
5195  inline const char *wget_path(const char *const user_path=0, const bool reinit_path=false) {
5196  static char *s_path = 0;
5197  cimg::mutex(7);
5198  if (reinit_path) { delete[] s_path; s_path = 0; }
5199  if (user_path) {
5200  if (!s_path) s_path = new char[1024];
5201  std::memset(s_path,0,1024);
5202  std::strncpy(s_path,user_path,1023);
5203  } else if (!s_path) {
5204  s_path = new char[1024];
5205  std::memset(s_path,0,1024);
5206  bool path_found = false;
5207  std::FILE *file = 0;
5208 #if cimg_OS==2
5209  if (!path_found) {
5210  std::strcpy(s_path,".\\wget.exe");
5211  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5212  }
5213  if (!path_found) std::strcpy(s_path,"wget.exe");
5214 #else
5215  if (!path_found) {
5216  std::strcpy(s_path,"./wget");
5217  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5218  }
5219  if (!path_found) std::strcpy(s_path,"wget");
5220 #endif
5221  winformat_string(s_path);
5222  }
5223  cimg::mutex(7,0);
5224  return s_path;
5225  }
5226 
5228 
5233  inline const char *curl_path(const char *const user_path=0, const bool reinit_path=false) {
5234  static char *s_path = 0;
5235  cimg::mutex(7);
5236  if (reinit_path) { delete[] s_path; s_path = 0; }
5237  if (user_path) {
5238  if (!s_path) s_path = new char[1024];
5239  std::memset(s_path,0,1024);
5240  std::strncpy(s_path,user_path,1023);
5241  } else if (!s_path) {
5242  s_path = new char[1024];
5243  std::memset(s_path,0,1024);
5244  bool path_found = false;
5245  std::FILE *file = 0;
5246 #if cimg_OS==2
5247  if (!path_found) {
5248  std::strcpy(s_path,".\\curl.exe");
5249  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5250  }
5251  if (!path_found) std::strcpy(s_path,"curl.exe");
5252 #else
5253  if (!path_found) {
5254  std::strcpy(s_path,"./curl");
5255  if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5256  }
5257  if (!path_found) std::strcpy(s_path,"curl");
5258 #endif
5259  winformat_string(s_path);
5260  }
5261  cimg::mutex(7,0);
5262  return s_path;
5263  }
5264 
5266  inline const char *split_filename(const char *const filename, char *const body=0) {
5267  if (!filename) { if (body) *body = 0; return 0; }
5268  const char *p = 0; for (const char *np = filename; np>=filename && (p=np); np = std::strchr(np,'.')+1) {}
5269  if (p==filename) {
5270  if (body) std::strcpy(body,filename);
5271  return filename + std::strlen(filename);
5272  }
5273  const unsigned int l = (unsigned int)(p - filename - 1);
5274  if (body) { std::memcpy(body,filename,l); body[l] = 0; }
5275  return p;
5276  }
5277 
5279  inline char* number_filename(const char *const filename, const int number,
5280  const unsigned int digits, char *const str) {
5281  if (!filename) { if (str) *str = 0; return 0; }
5282  char format[1024] = { 0 }, body[1024] = { 0 };
5283  const char *const ext = cimg::split_filename(filename,body);
5284  if (*ext) cimg_snprintf(format,sizeof(format),"%%s_%%.%ud.%%s",digits);
5285  else cimg_snprintf(format,sizeof(format),"%%s_%%.%ud",digits);
5286  std::sprintf(str,format,body,number,ext);
5287  return str;
5288  }
5289 
5291 
5296  inline const char *file_type(std::FILE *const file, const char *const filename) {
5297  if (!file && !filename)
5298  throw CImgArgumentException("cimg::file_type(): Specified filename is (null).");
5299  static const char
5300  *const _pnm = "pnm",
5301  *const _pfm = "pfm",
5302  *const _bmp = "bmp",
5303  *const _gif = "gif",
5304  *const _jpg = "jpg",
5305  *const _off = "off",
5306  *const _pan = "pan",
5307  *const _png = "png",
5308  *const _tif = "tif",
5309  *const _inr = "inr",
5310  *const _dcm = "dcm";
5311  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
5312  const char *f_type = 0, *head;
5313  char header[2048] = { 0 }, item[1024] = { 0 };
5314  const unsigned char *const uheader = (unsigned char*)header;
5315  int err; char cerr;
5316  const unsigned int siz = (unsigned int)std::fread(header,2048,1,nfile); // Read first 2048 bytes.
5317  if (!file) cimg::fclose(nfile);
5318 
5319  if (!std::strncmp(header,"OFF\n",4)) f_type = _off; // OFF.
5320  else if (!std::strncmp(header,"#INRIMAGE",9)) f_type = _inr; // INRIMAGE.
5321  else if (!std::strncmp(header,"PANDORE",7)) f_type = _pan; // PANDORE.
5322  else if (!std::strncmp(header+128,"DICM",4)) f_type = _dcm; // DICOM.
5323  else if (uheader[0]==0xFF && uheader[1]==0xD8 && uheader[2]==0xFF) f_type = _jpg; // JPEG.
5324  else if (header[0]=='B' && header[1]=='M') f_type = _bmp; // BMP.
5325  else if (header[0]=='G' && header[1]=='I' && header[2]=='F' && header[3]=='8' && header[5]=='a' && // GIF.
5326  (header[4]=='7' || header[4]=='9')) f_type = _gif;
5327  else if (uheader[0]==0x89 && uheader[1]==0x50 && uheader[2]==0x4E && uheader[3]==0x47 && // PNG.
5328  uheader[4]==0x0D && uheader[5]==0x0A && uheader[6]==0x1A && uheader[7]==0x0A) f_type = _png;
5329  else if ((uheader[0]==0x49 && uheader[1]==0x49) || (uheader[0]==0x4D && uheader[1]==0x4D)) f_type = _tif; // TIFF.
5330  else { // PNM or PFM.
5331  head = header;
5332  while (head<header+siz && (err=std::sscanf(head,"%1023[^\n]",item))!=EOF && (*item=='#' || !err))
5333  head+=1+(err?std::strlen(item):0);
5334  if (std::sscanf(item," P%d",&err)==1) f_type = _pnm;
5335  else if (std::sscanf(item," P%c",&cerr)==1 && (cerr=='f' || cerr=='F')) f_type = _pfm;
5336  }
5337  return f_type;
5338  }
5339 
5341 
5348  template<typename T>
5349  inline int fread(T *const ptr, const unsigned long nmemb, std::FILE *stream) {
5350  if (!ptr || nmemb<=0 || !stream)
5351  throw CImgArgumentException("cimg::fread(): Invalid reading request of %u %s%s from file %p to buffer %p.",
5352  nmemb,cimg::type<T>::string(),nmemb>1?"s":"",stream,ptr);
5353 
5354  const unsigned long wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T);
5355  unsigned long to_read = nmemb, al_read = 0, l_to_read = 0, l_al_read = 0;
5356  do {
5357  l_to_read = (to_read*sizeof(T))<wlimitT?to_read:wlimit;
5358  l_al_read = (unsigned long)std::fread((void*)(ptr+al_read),sizeof(T),l_to_read,stream);
5359  al_read+=l_al_read;
5360  to_read-=l_al_read;
5361  } while (l_to_read==l_al_read && to_read>0);
5362  if (to_read>0)
5363  warn("cimg::fread(): Only %u/%u elements could be read from file.",
5364  al_read,nmemb);
5365  return al_read;
5366  }
5367 
5369 
5376  template<typename T>
5377  inline int fwrite(const T *ptr, const unsigned long nmemb, std::FILE *stream) {
5378  if (!ptr || !stream)
5379  throw CImgArgumentException("cimg::fwrite(): Invalid writing request of %u %s%s from buffer %p to file %p.",
5380  nmemb,cimg::type<T>::string(),nmemb>1?"s":"",ptr,stream);
5381  if (nmemb<=0) return 0;
5382  const unsigned long wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T);
5383  unsigned long to_write = nmemb, al_write = 0, l_to_write = 0, l_al_write = 0;
5384  do {
5385  l_to_write = (to_write*sizeof(T))<wlimitT?to_write:wlimit;
5386  l_al_write = (unsigned long)std::fwrite((void*)(ptr+al_write),sizeof(T),l_to_write,stream);
5387  al_write+=l_al_write;
5388  to_write-=l_al_write;
5389  } while (l_to_write==l_al_write && to_write>0);
5390  if (to_write>0)
5391  warn("cimg::fwrite(): Only %u/%u elements could be written in file.",
5392  al_write,nmemb);
5393  return al_write;
5394  }
5395 
5397 
5401  inline void fempty(std::FILE *const file, const char *const filename) {
5402  if (!file && !filename)
5403  throw CImgArgumentException("cimg::file_type(): Specified filename is (null).");
5404  std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
5405  if (!file) cimg::fclose(nfile);
5406  }
5407 
5409 
5416  inline char *load_network_external(const char *const filename, char *const filename_local) {
5417  if (!filename)
5418  throw CImgArgumentException("cimg::load_network_external(): Specified filename is (null).");
5419  if (!filename_local)
5420  throw CImgArgumentException("cimg::load_network_external(): Specified destination string is (null).");
5421  const char *const _ext = cimg::split_filename(filename), *const ext = (*_ext && _ext>filename)?_ext-1:_ext;
5422  char command[1024] = { 0 };
5423  std::FILE *file = 0;
5424  *filename_local = 0;
5425  do {
5426  cimg_snprintf(filename_local,512,"%s%c%s%s",
5427  cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
5428  if ((file=std::fopen(filename_local,"rb"))!=0) cimg::fclose(file);
5429  } while (file);
5430 
5431  // Try with 'curl' first.
5432  cimg_snprintf(command,sizeof(command),"%s -f --silent --compressed -o \"%s\" \"%s\"",
5433  cimg::curl_path(),filename_local,filename);
5434  cimg::system(command);
5435  if (!(file = std::fopen(filename_local,"rb"))) {
5436 
5437  // Try with 'wget' else.
5438  cimg_snprintf(command,sizeof(command),"%s -q -r -l 0 --no-cache -O \"%s\" \"%s\"",
5439  cimg::wget_path(),filename_local,filename);
5440  cimg::system(command);
5441  if (!(file = std::fopen(filename_local,"rb")))
5442  throw CImgIOException("cimg::load_network_external(): Failed to load file '%s' with external commands "
5443  "'wget' or 'curl'.",filename);
5444  cimg::fclose(file);
5445 
5446  // Try gunzip it.
5447  cimg_snprintf(command,sizeof(command),"%s.gz",filename_local);
5448  std::rename(filename_local,command);
5449  cimg_snprintf(command,sizeof(command),"%s --quiet \"%s.gz\"",
5450  gunzip_path(),filename_local);
5451  cimg::system(command);
5452  file = std::fopen(filename_local,"rb");
5453  if (!file) {
5454  cimg_snprintf(command,sizeof(command),"%s.gz",filename_local);
5455  std::rename(command,filename_local);
5456  file = std::fopen(filename_local,"rb");
5457  }
5458  }
5459  std::fseek(file,0,SEEK_END); // Check if file size is 0.
5460  if (std::ftell(file)<=0)
5461  throw CImgIOException("cimg::load_network_external(): Failed to load file '%s' with external commands "
5462  "'wget' or 'curl'.",filename);
5463  cimg::fclose(file);
5464  return filename_local;
5465  }
5466 
5468  inline const char* option(const char *const name, const int argc, const char *const *const argv,
5469  const char *const defaut, const char *const usage, const bool reset_static) {
5470  static bool first = true, visu = false;
5471  if (reset_static) { first = true; return 0; }
5472  const char *res = 0;
5473  if (first) {
5474  first = false;
5475  visu = cimg::option("-h",argc,argv,(char*)0,(char*)0,false)!=0;
5476  visu |= cimg::option("-help",argc,argv,(char*)0,(char*)0,false)!=0;
5477  visu |= cimg::option("--help",argc,argv,(char*)0,(char*)0,false)!=0;
5478  }
5479  if (!name && visu) {
5480  if (usage) {
5481  std::fprintf(cimg::output(),"\n %s%s%s",cimg::t_red,cimg::basename(argv[0]),cimg::t_normal);
5482  std::fprintf(cimg::output(),": %s",usage);
5483  std::fprintf(cimg::output()," (%s, %s)\n\n",__DATE__,__TIME__);
5484  }
5485  if (defaut) std::fprintf(cimg::output(),"%s\n",defaut);
5486  }
5487  if (name) {
5488  if (argc>0) {
5489  int k = 0;
5490  while (k<argc && std::strcmp(argv[k],name)) ++k;
5491  res = (k++==argc?defaut:(k==argc?argv[--k]:argv[k]));
5492  } else res = defaut;
5493  if (visu && usage) std::fprintf(cimg::output()," %s%-16s%s %-24s %s%s%s\n",
5494  cimg::t_bold,name,cimg::t_normal,res?res:"0",
5495  cimg::t_green,usage,cimg::t_normal);
5496  }
5497  return res;
5498  }
5499 
5500  inline const char* option(const char *const name, const int argc, const char *const *const argv,
5501  const char *const defaut, const char *const usage=0) {
5502  return option(name,argc,argv,defaut,usage,false);
5503  }
5504 
5505  inline bool option(const char *const name, const int argc, const char *const *const argv,
5506  const bool defaut, const char *const usage=0) {
5507  const char *const s = cimg::option(name,argc,argv,(char*)0);
5508  const bool res = s?(cimg::strcasecmp(s,"false") && cimg::strcasecmp(s,"off") && cimg::strcasecmp(s,"0")):defaut;
5509  cimg::option(name,0,0,res?"true":"false",usage);
5510  return res;
5511  }
5512 
5513  inline int option(const char *const name, const int argc, const char *const *const argv,
5514  const int defaut, const char *const usage=0) {
5515  const char *const s = cimg::option(name,argc,argv,(char*)0);
5516  const int res = s?std::atoi(s):defaut;
5517  char tmp[256] = { 0 };
5518  cimg_snprintf(tmp,sizeof(tmp),"%d",res);
5519  cimg::option(name,0,0,tmp,usage);
5520  return res;
5521  }
5522 
5523  inline char option(const char *const name, const int argc, const char *const *const argv,
5524  const char defaut, const char *const usage=0) {
5525  const char *const s = cimg::option(name,argc,argv,(char*)0);
5526  const char res = s?*s:defaut;
5527  char tmp[8] = { 0 };
5528  *tmp = res;
5529  cimg::option(name,0,0,tmp,usage);
5530  return res;
5531  }
5532 
5533  inline float option(const char *const name, const int argc, const char *const *const argv,
5534  const float defaut, const char *const usage=0) {
5535  const char *const s = cimg::option(name,argc,argv,(char*)0);
5536  const float res = s?(float)cimg::atof(s):defaut;
5537  char tmp[256] = { 0 };
5538  cimg_snprintf(tmp,sizeof(tmp),"%g",res);
5539  cimg::option(name,0,0,tmp,usage);
5540  return res;
5541  }
5542 
5543  inline double option(const char *const name, const int argc, const char *const *const argv,
5544  const double defaut, const char *const usage=0) {
5545  const char *const s = cimg::option(name,argc,argv,(char*)0);
5546  const double res = s?cimg::atof(s):defaut;
5547  char tmp[256] = { 0 };
5548  cimg_snprintf(tmp,sizeof(tmp),"%g",res);
5549  cimg::option(name,0,0,tmp,usage);
5550  return res;
5551  }
5552 
5553  inline const char* argument(const unsigned int nb, const int argc, const char *const *const argv,
5554  const unsigned int nb_singles=0, ...) {
5555  for (int k = 1, pos = 0; k<argc;) {
5556  const char *const item = argv[k];
5557  bool option = (*item=='-'), single_option = false;
5558  if (option) {
5559  va_list ap;
5560  va_start(ap,nb_singles);
5561  for (unsigned int i = 0; i<nb_singles; ++i) if (!cimg::strcasecmp(item,va_arg(ap,char*))) {
5562  single_option = true; break;
5563  }
5564  va_end(ap);
5565  }
5566  if (option) { ++k; if (!single_option) ++k; }
5567  else { if (pos++==(int)nb) return item; else ++k; }
5568  }
5569  return 0;
5570  }
5571 
5573 
5576  inline void info() {
5577  char tmp[1024] = { 0 };
5578  std::fprintf(cimg::output(),"\n %s%sCImg Library %u.%u.%u%s, compiled %s ( %s ) with the following flags:\n\n",
5579  cimg::t_red,cimg::t_bold,cimg_version/100,(cimg_version/10)%10,cimg_version%10,
5580  cimg::t_normal,__DATE__,__TIME__);
5581 
5582  std::fprintf(cimg::output()," > Operating System: %s%-13s%s %s('cimg_OS'=%d)%s\n",
5583  cimg::t_bold,
5584  cimg_OS==1?"Unix":(cimg_OS==2?"Windows":"Unknow"),
5585  cimg::t_normal,cimg::t_green,
5586  cimg_OS,
5587  cimg::t_normal);
5588 
5589  std::fprintf(cimg::output()," > CPU endianness: %s%s Endian%s\n",
5590  cimg::t_bold,
5591  cimg::endianness()?"Big":"Little",
5592  cimg::t_normal);
5593 
5594  std::fprintf(cimg::output()," > Verbosity mode: %s%-13s%s %s('cimg_verbosity'=%d)%s\n",
5595  cimg::t_bold,
5596  cimg_verbosity==0?"Quiet":
5597  cimg_verbosity==1?"Console":
5598  cimg_verbosity==2?"Dialog":
5599  cimg_verbosity==3?"Console+Warnings":"Dialog+Warnings",
5600  cimg::t_normal,cimg::t_green,
5601  cimg_verbosity,
5602  cimg::t_normal);
5603 
5604  std::fprintf(cimg::output()," > Stricts warnings: %s%-13s%s %s('cimg_strict_warnings' %s)%s\n",
5605  cimg::t_bold,
5606 #ifdef cimg_strict_warnings
5607  "Yes",cimg::t_normal,cimg::t_green,"defined",
5608 #else
5609  "No",cimg::t_normal,cimg::t_green,"undefined",
5610 #endif
5611  cimg::t_normal);
5612 
5613  std::fprintf(cimg::output()," > Using VT100 messages: %s%-13s%s %s('cimg_use_vt100' %s)%s\n",
5614  cimg::t_bold,
5615 #ifdef cimg_use_vt100
5616  "Yes",cimg::t_normal,cimg::t_green,"defined",
5617 #else
5618  "No",cimg::t_normal,cimg::t_green,"undefined",
5619 #endif
5620  cimg::t_normal);
5621 
5622  std::fprintf(cimg::output()," > Display type: %s%-13s%s %s('cimg_display'=%d)%s\n",
5623  cimg::t_bold,
5624  cimg_display==0?"No display":cimg_display==1?"X11":cimg_display==2?"Windows GDI":"Unknown",
5625  cimg::t_normal,cimg::t_green,
5626  cimg_display,
5627  cimg::t_normal);
5628 
5629 #if cimg_display==1
5630  std::fprintf(cimg::output()," > Using XShm for X11: %s%-13s%s %s('cimg_use_xshm' %s)%s\n",
5631  cimg::t_bold,
5632 #ifdef cimg_use_xshm
5633  "Yes",cimg::t_normal,cimg::t_green,"defined",
5634 #else
5635  "No",cimg::t_normal,cimg::t_green,"undefined",
5636 #endif
5637  cimg::t_normal);
5638 
5639  std::fprintf(cimg::output()," > Using XRand for X11: %s%-13s%s %s('cimg_use_xrandr' %s)%s\n",
5640  cimg::t_bold,
5641 #ifdef cimg_use_xrandr
5642  "Yes",cimg::t_normal,cimg::t_green,"defined",
5643 #else
5644  "No",cimg::t_normal,cimg::t_green,"undefined",
5645 #endif
5646  cimg::t_normal);
5647 #endif
5648  std::fprintf(cimg::output()," > Using OpenMP: %s%-13s%s %s('cimg_use_openmp' %s)%s\n",
5649  cimg::t_bold,
5650 #ifdef cimg_use_openmp
5651  "Yes",cimg::t_normal,cimg::t_green,"defined",
5652 #else
5653  "No",cimg::t_normal,cimg::t_green,"undefined",
5654 #endif
5655  cimg::t_normal);
5656  std::fprintf(cimg::output()," > Using PNG library: %s%-13s%s %s('cimg_use_png' %s)%s\n",
5657  cimg::t_bold,
5658 #ifdef cimg_use_png
5659  "Yes",cimg::t_normal,cimg::t_green,"defined",
5660 #else
5661  "No",cimg::t_normal,cimg::t_green,"undefined",
5662 #endif
5663  cimg::t_normal);
5664  std::fprintf(cimg::output()," > Using JPEG library: %s%-13s%s %s('cimg_use_jpeg' %s)%s\n",
5665  cimg::t_bold,
5666 #ifdef cimg_use_jpeg
5667  "Yes",cimg::t_normal,cimg::t_green,"defined",
5668 #else
5669  "No",cimg::t_normal,cimg::t_green,"undefined",
5670 #endif
5671  cimg::t_normal);
5672 
5673  std::fprintf(cimg::output()," > Using TIFF library: %s%-13s%s %s('cimg_use_tiff' %s)%s\n",
5674  cimg::t_bold,
5675 #ifdef cimg_use_tiff
5676  "Yes",cimg::t_normal,cimg::t_green,"defined",
5677 #else
5678  "No",cimg::t_normal,cimg::t_green,"undefined",
5679 #endif
5680  cimg::t_normal);
5681 
5682  std::fprintf(cimg::output()," > Using Magick++ library: %s%-13s%s %s('cimg_use_magick' %s)%s\n",
5683  cimg::t_bold,
5684 #ifdef cimg_use_magick
5685  "Yes",cimg::t_normal,cimg::t_green,"defined",
5686 #else
5687  "No",cimg::t_normal,cimg::t_green,"undefined",
5688 #endif
5689  cimg::t_normal);
5690 
5691  std::fprintf(cimg::output()," > Using FFTW3 library: %s%-13s%s %s('cimg_use_fftw3' %s)%s\n",
5692  cimg::t_bold,
5693 #ifdef cimg_use_fftw3
5694  "Yes",cimg::t_normal,cimg::t_green,"defined",
5695 #else
5696  "No",cimg::t_normal,cimg::t_green,"undefined",
5697 #endif
5698  cimg::t_normal);
5699 
5700  std::fprintf(cimg::output()," > Using LAPACK library: %s%-13s%s %s('cimg_use_lapack' %s)%s\n",
5701  cimg::t_bold,
5702 #ifdef cimg_use_lapack
5703  "Yes",cimg::t_normal,cimg::t_green,"defined",
5704 #else
5705  "No",cimg::t_normal,cimg::t_green,"undefined",
5706 #endif
5707  cimg::t_normal);
5708 
5709  cimg_snprintf(tmp,sizeof(tmp),"\"%.1020s\"",cimg::imagemagick_path());
5710  std::fprintf(cimg::output()," > Path of ImageMagick: %s%-13s%s\n",
5711  cimg::t_bold,
5712  tmp,
5713  cimg::t_normal);
5714 
5715  cimg_snprintf(tmp,sizeof(tmp),"\"%.1020s\"",cimg::graphicsmagick_path());
5716  std::fprintf(cimg::output()," > Path of GraphicsMagick: %s%-13s%s\n",
5717  cimg::t_bold,
5718  tmp,
5719  cimg::t_normal);
5720 
5721  cimg_snprintf(tmp,sizeof(tmp),"\"%.1020s\"",cimg::medcon_path());
5722  std::fprintf(cimg::output()," > Path of 'medcon': %s%-13s%s\n",
5723  cimg::t_bold,
5724  tmp,
5725  cimg::t_normal);
5726 
5727  cimg_snprintf(tmp,sizeof(tmp),"\"%.1020s\"",cimg::temporary_path());
5728  std::fprintf(cimg::output()," > Temporary path: %s%-13s%s\n",
5729  cimg::t_bold,
5730  tmp,
5731  cimg::t_normal);
5732 
5733  std::fprintf(cimg::output(),"\n");
5734  }
5735 
5736  // Declare LAPACK function signatures if LAPACK support is enabled.
5737 #ifdef cimg_use_lapack
5738  template<typename T>
5739  inline void getrf(int &N, T *lapA, int *IPIV, int &INFO) {
5740  dgetrf_(&N,&N,lapA,&N,IPIV,&INFO);
5741  }
5742 
5743  inline void getrf(int &N, float *lapA, int *IPIV, int &INFO) {
5744  sgetrf_(&N,&N,lapA,&N,IPIV,&INFO);
5745  }
5746 
5747  template<typename T>
5748  inline void getri(int &N, T *lapA, int *IPIV, T* WORK, int &LWORK, int &INFO) {
5749  dgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO);
5750  }
5751 
5752  inline void getri(int &N, float *lapA, int *IPIV, float* WORK, int &LWORK, int &INFO) {
5753  sgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO);
5754  }
5755 
5756  template<typename T>
5757  inline void gesvd(char &JOB, int &M, int &N, T *lapA, int &MN,
5758  T *lapS, T *lapU, T *lapV, T *WORK, int &LWORK, int &INFO) {
5759  dgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO);
5760  }
5761 
5762  inline void gesvd(char &JOB, int &M, int &N, float *lapA, int &MN,
5763  float *lapS, float *lapU, float *lapV, float *WORK, int &LWORK, int &INFO) {
5764  sgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO);
5765  }
5766 
5767  template<typename T>
5768  inline void getrs(char &TRANS, int &N, T *lapA, int *IPIV, T *lapB, int &INFO) {
5769  int one = 1;
5770  dgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO);
5771  }
5772 
5773  inline void getrs(char &TRANS, int &N, float *lapA, int *IPIV, float *lapB, int &INFO) {
5774  int one = 1;
5775  sgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO);
5776  }
5777 
5778  template<typename T>
5779  inline void syev(char &JOB, char &UPLO, int &N, T *lapA, T *lapW, T *WORK, int &LWORK, int &INFO) {
5780  dsyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO);
5781  }
5782 
5783  inline void syev(char &JOB, char &UPLO, int &N, float *lapA, float *lapW, float *WORK, int &LWORK, int &INFO) {
5784  ssyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO);
5785  }
5786 
5787  template<typename T>
5788  inline void sgels(char & TRANS, int &M, int &N, int &NRHS, T* lapA, int &LDA,
5789  T* lapB, int &LDB, T* WORK, int &LWORK, int &INFO){
5790  dgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO);
5791  }
5792 
5793  inline void sgels(char & TRANS, int &M, int &N, int &NRHS, float* lapA, int &LDA,
5794  float* lapB, int &LDB, float* WORK, int &LWORK, int &INFO){
5795  sgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO);
5796  }
5797 
5798 #endif
5799 
5800  // End of the 'cimg' namespace
5801  }
5802 
5803  /*------------------------------------------------
5804  #
5805  #
5806  # Definition of mathematical operators and
5807  # external functions.
5808  #
5809  #
5810  -------------------------------------------------*/
5811 
5812 #define _cimg_create_ext_operators(typ) \
5813  template<typename T> \
5814  inline CImg<typename cimg::superset<T,typ>::type> operator+(const typ val, const CImg<T>& img) { \
5815  return img + val; \
5816  } \
5817  template<typename T> \
5818  inline CImg<typename cimg::superset<T,typ>::type> operator-(const typ val, const CImg<T>& img) { \
5819  typedef typename cimg::superset<T,typ>::type Tt; \
5820  return CImg<Tt>(img._width,img._height,img._depth,img._spectrum,val)-=img; \
5821  } \
5822  template<typename T> \
5823  inline CImg<typename cimg::superset<T,typ>::type> operator*(const typ val, const CImg<T>& img) { \
5824  return img*val; \
5825  } \
5826  template<typename T> \
5827  inline CImg<typename cimg::superset<T,typ>::type> operator/(const typ val, const CImg<T>& img) { \
5828  return val*img.get_invert(); \
5829  } \
5830  template<typename T> \
5831  inline CImg<typename cimg::superset<T,typ>::type> operator&(const typ val, const CImg<T>& img) { \
5832  return img & val; \
5833  } \
5834  template<typename T> \
5835  inline CImg<typename cimg::superset<T,typ>::type> operator|(const typ val, const CImg<T>& img) { \
5836  return img | val; \
5837  } \
5838  template<typename T> \
5839  inline CImg<typename cimg::superset<T,typ>::type> operator^(const typ val, const CImg<T>& img) { \
5840  return img ^ val; \
5841  } \
5842  template<typename T> \
5843  inline bool operator==(const typ val, const CImg<T>& img) { \
5844  return img == val; \
5845  } \
5846  template<typename T> \
5847  inline bool operator!=(const typ val, const CImg<T>& img) { \
5848  return img != val; \
5849  }
5850 
5851  _cimg_create_ext_operators(bool)
5852  _cimg_create_ext_operators(unsigned char)
5853  _cimg_create_ext_operators(char)
5854  _cimg_create_ext_operators(signed char)
5855  _cimg_create_ext_operators(unsigned short)
5856  _cimg_create_ext_operators(short)
5857  _cimg_create_ext_operators(unsigned int)
5858  _cimg_create_ext_operators(int)
5859  _cimg_create_ext_operators(unsigned long)
5860  _cimg_create_ext_operators(long)
5861  _cimg_create_ext_operators(float)
5862  _cimg_create_ext_operators(double)
5863 
5864  template<typename T>
5865  inline CImg<_cimg_Tfloat> operator+(const char *const expression, const CImg<T>& img) {
5866  return img + expression;
5867  }
5868 
5869  template<typename T>
5870  inline CImg<_cimg_Tfloat> operator-(const char *const expression, const CImg<T>& img) {
5871  return CImg<_cimg_Tfloat>(img._width,img._height,img._depth,img._spectrum,expression,true)-=img;
5872  }
5873 
5874  template<typename T>
5875  inline CImg<_cimg_Tfloat> operator*(const char *const expression, const CImg<T>& img) {
5876  return img*expression;
5877  }
5878 
5879  template<typename T>
5880  inline CImg<_cimg_Tfloat> operator/(const char *const expression, const CImg<T>& img) {
5881  return expression*img.get_invert();
5882  }
5883 
5884  template<typename T>
5885  inline CImg<T> operator&(const char *const expression, const CImg<T>& img) {
5886  return img & expression;
5887  }
5888 
5889  template<typename T>
5890  inline CImg<T> operator|(const char *const expression, const CImg<T>& img) {
5891  return img | expression;
5892  }
5893 
5894  template<typename T>
5895  inline CImg<T> operator^(const char *const expression, const CImg<T>& img) {
5896  return img ^ expression;
5897  }
5898 
5899  template<typename T>
5900  inline bool operator==(const char *const expression, const CImg<T>& img) {
5901  return img == expression;
5902  }
5903 
5904  template<typename T>
5905  inline bool operator!=(const char *const expression, const CImg<T>& img) {
5906  return img != expression;
5907  }
5908 
5909  template<typename T>
5910  inline CImg<_cimg_Tfloat> sqr(const CImg<T>& instance) {
5911  return instance.get_sqr();
5912  }
5913 
5914  template<typename T>
5915  inline CImg<_cimg_Tfloat> sqrt(const CImg<T>& instance) {
5916  return instance.get_sqrt();
5917  }
5918 
5919  template<typename T>
5920  inline CImg<_cimg_Tfloat> exp(const CImg<T>& instance) {
5921  return instance.get_exp();
5922  }
5923 
5924  template<typename T>
5925  inline CImg<_cimg_Tfloat> log(const CImg<T>& instance) {
5926  return instance.get_log();
5927  }
5928 
5929  template<typename T>
5930  inline CImg<_cimg_Tfloat> log2(const CImg<T>& instance) {
5931  return instance.get_log2();
5932  }
5933 
5934  template<typename T>
5935  inline CImg<_cimg_Tfloat> log10(const CImg<T>& instance) {
5936  return instance.get_log10();
5937  }
5938 
5939  template<typename T>
5940  inline CImg<_cimg_Tfloat> abs(const CImg<T>& instance) {
5941  return instance.get_abs();
5942  }
5943 
5944  template<typename T>
5945  inline CImg<_cimg_Tfloat> sign(const CImg<T>& instance) {
5946  return instance.get_sign();
5947  }
5948 
5949  template<typename T>
5950  inline CImg<_cimg_Tfloat> cos(const CImg<T>& instance) {
5951  return instance.get_cos();
5952  }
5953 
5954  template<typename T>
5955  inline CImg<_cimg_Tfloat> sin(const CImg<T>& instance) {
5956  return instance.get_sin();
5957  }
5958 
5959  template<typename T>
5960  inline CImg<_cimg_Tfloat> sinc(const CImg<T>& instance) {
5961  return instance.get_sinc();
5962  }
5963 
5964  template<typename T>
5965  inline CImg<_cimg_Tfloat> tan(const CImg<T>& instance) {
5966  return instance.get_tan();
5967  }
5968 
5969  template<typename T>
5970  inline CImg<_cimg_Tfloat> acos(const CImg<T>& instance) {
5971  return instance.get_acos();
5972  }
5973 
5974  template<typename T>
5975  inline CImg<_cimg_Tfloat> asin(const CImg<T>& instance) {
5976  return instance.get_asin();
5977  }
5978 
5979  template<typename T>
5980  inline CImg<_cimg_Tfloat> atan(const CImg<T>& instance) {
5981  return instance.get_atan();
5982  }
5983 
5984  template<typename T>
5985  inline CImg<_cimg_Tfloat> cosh(const CImg<T>& instance) {
5986  return instance.get_cosh();
5987  }
5988 
5989  template<typename T>
5990  inline CImg<_cimg_Tfloat> sinh(const CImg<T>& instance) {
5991  return instance.get_sinh();
5992  }
5993 
5994  template<typename T>
5995  inline CImg<_cimg_Tfloat> tanh(const CImg<T>& instance) {
5996  return instance.get_tanh();
5997  }
5998 
5999  template<typename T>
6000  inline CImg<T> transpose(const CImg<T>& instance) {
6001  return instance.get_transpose();
6002  }
6003 
6004  template<typename T>
6005  inline CImg<_cimg_Tfloat> invert(const CImg<T>& instance) {
6006  return instance.get_invert();
6007  }
6008 
6009  template<typename T>
6010  inline CImg<_cimg_Tfloat> pseudoinvert(const CImg<T>& instance) {
6011  return instance.get_pseudoinvert();
6012  }
6013 
6014  /*-----------------------------------
6015  #
6016  # Define the CImgDisplay structure
6017  #
6018  ----------------------------------*/
6020 
6036  struct CImgDisplay {
6037  unsigned long _timer, _fps_frames, _fps_timer;
6038  unsigned int _width, _height, _normalization;
6039  float _fps_fps, _min, _max;
6040  bool _is_fullscreen;
6041  char *_title;
6042  volatile unsigned int _window_width, _window_height, _button, _keys[128], _released_keys[128];
6043  volatile int _window_x, _window_y, _mouse_x, _mouse_y, _wheel;
6044  volatile bool _is_closed, _is_resized, _is_moved, _is_event,
6045  _is_keyESC, _is_keyF1, _is_keyF2, _is_keyF3, _is_keyF4, _is_keyF5, _is_keyF6, _is_keyF7,
6046  _is_keyF8, _is_keyF9, _is_keyF10, _is_keyF11, _is_keyF12, _is_keyPAUSE, _is_key1, _is_key2,
6047  _is_key3, _is_key4, _is_key5, _is_key6, _is_key7, _is_key8, _is_key9, _is_key0,
6048  _is_keyBACKSPACE, _is_keyINSERT, _is_keyHOME, _is_keyPAGEUP, _is_keyTAB, _is_keyQ, _is_keyW, _is_keyE,
6049  _is_keyR, _is_keyT, _is_keyY, _is_keyU, _is_keyI, _is_keyO, _is_keyP, _is_keyDELETE,
6050  _is_keyEND, _is_keyPAGEDOWN, _is_keyCAPSLOCK, _is_keyA, _is_keyS, _is_keyD, _is_keyF, _is_keyG,
6051  _is_keyH, _is_keyJ, _is_keyK, _is_keyL, _is_keyENTER, _is_keySHIFTLEFT, _is_keyZ, _is_keyX,
6052  _is_keyC, _is_keyV, _is_keyB, _is_keyN, _is_keyM, _is_keySHIFTRIGHT, _is_keyARROWUP, _is_keyCTRLLEFT,
6053  _is_keyAPPLEFT, _is_keyALT, _is_keySPACE, _is_keyALTGR, _is_keyAPPRIGHT, _is_keyMENU, _is_keyCTRLRIGHT,
6054  _is_keyARROWLEFT, _is_keyARROWDOWN, _is_keyARROWRIGHT, _is_keyPAD0, _is_keyPAD1, _is_keyPAD2, _is_keyPAD3,
6055  _is_keyPAD4, _is_keyPAD5, _is_keyPAD6, _is_keyPAD7, _is_keyPAD8, _is_keyPAD9, _is_keyPADADD, _is_keyPADSUB,
6056  _is_keyPADMUL, _is_keyPADDIV;
6057 
6059  //---------------------------
6060  //
6062 
6063  //---------------------------
6064 
6065 #ifdef cimgdisplay_plugin
6066 #include cimgdisplay_plugin
6067 #endif
6068 #ifdef cimgdisplay_plugin1
6069 #include cimgdisplay_plugin1
6070 #endif
6071 #ifdef cimgdisplay_plugin2
6072 #include cimgdisplay_plugin2
6073 #endif
6074 #ifdef cimgdisplay_plugin3
6075 #include cimgdisplay_plugin3
6076 #endif
6077 #ifdef cimgdisplay_plugin4
6078 #include cimgdisplay_plugin4
6079 #endif
6080 #ifdef cimgdisplay_plugin5
6081 #include cimgdisplay_plugin5
6082 #endif
6083 #ifdef cimgdisplay_plugin6
6084 #include cimgdisplay_plugin6
6085 #endif
6086 #ifdef cimgdisplay_plugin7
6087 #include cimgdisplay_plugin7
6088 #endif
6089 #ifdef cimgdisplay_plugin8
6090 #include cimgdisplay_plugin8
6091 #endif
6092 
6094  //--------------------------------------------------------
6095  //
6097 
6098  //--------------------------------------------------------
6099 
6101 
6105  assign();
6106  }
6107 
6109 
6120  _width(0),_height(0),_normalization(0),
6121  _min(0),_max(0),
6122  _is_fullscreen(false),
6123  _title(0),
6124  _window_width(0),_window_height(0),_button(0),
6125  _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0),
6126  _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
6127  assign();
6128  }
6129 
6131 
6140  CImgDisplay(const unsigned int width, const unsigned int height,
6141  const char *const title=0, const unsigned int normalization=3,
6142  const bool is_fullscreen=false, const bool is_closed=false):
6143  _width(0),_height(0),_normalization(0),
6144  _min(0),_max(0),
6145  _is_fullscreen(false),
6146  _title(0),
6147  _window_width(0),_window_height(0),_button(0),
6148  _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0),
6149  _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
6151  }
6152 
6154 
6162  template<typename T>
6163  explicit CImgDisplay(const CImg<T>& img,
6164  const char *const title=0, const unsigned int normalization=3,
6165  const bool is_fullscreen=false, const bool is_closed=false):
6166  _width(0),_height(0),_normalization(0),
6167  _min(0),_max(0),
6168  _is_fullscreen(false),
6169  _title(0),
6170  _window_width(0),_window_height(0),_button(0),
6171  _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0),
6172  _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
6174  }
6175 
6177 
6185  template<typename T>
6186  explicit CImgDisplay(const CImgList<T>& list,
6187  const char *const title=0, const unsigned int normalization=3,
6188  const bool is_fullscreen=false, const bool is_closed=false):
6189  _width(0),_height(0),_normalization(0),
6190  _min(0),_max(0),
6191  _is_fullscreen(false),
6192  _title(0),
6193  _window_width(0),_window_height(0),_button(0),
6194  _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0),
6195  _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
6197  }
6198 
6200 
6204  CImgDisplay(const CImgDisplay& disp):
6205  _width(0),_height(0),_normalization(0),
6206  _min(0),_max(0),
6207  _is_fullscreen(false),
6208  _title(0),
6209  _window_width(0),_window_height(0),_button(0),
6210  _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0),
6211  _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
6212  assign(disp);
6213  }
6214 
6215 #if cimg_display==0
6216 
6217  static void _no_display_exception() {
6218  throw CImgDisplayException("CImgDisplay(): No display available.");
6219  }
6220 
6222 
6226  return flush();
6227  }
6228 
6230 
6232  CImgDisplay& assign(const unsigned int width, const unsigned int height,
6233  const char *const title=0, const unsigned int normalization=3,
6234  const bool is_fullscreen=false, const bool is_closed=false) {
6236  _no_display_exception();
6237  return assign();
6238  }
6239 
6241 
6243  template<typename T>
6245  const char *const title=0, const unsigned int normalization=3,
6246  const bool is_fullscreen=false, const bool is_closed=false) {
6247  _no_display_exception();
6248  return assign(img._width,img._height,title,normalization,is_fullscreen,is_closed);
6249  }
6250 
6252 
6254  template<typename T>
6256  const char *const title=0, const unsigned int normalization=3,
6257  const bool is_fullscreen=false, const bool is_closed=false) {
6258  _no_display_exception();
6259  return assign(list._width,list._width,title,normalization,is_fullscreen,is_closed);
6260  }
6261 
6263 
6266  _no_display_exception();
6267  return assign(disp._width,disp._height);
6268  }
6269 
6270 #endif
6271 
6273 
6281  static CImgDisplay& empty() {
6282  static CImgDisplay _empty;
6283  return _empty.assign();
6284  }
6285 
6286 #define cimg_fitscreen(dx,dy,dz) CImgDisplay::_fitscreen(dx,dy,dz,128,-85,false), \
6287  CImgDisplay::_fitscreen(dx,dy,dz,128,-85,true)
6288  static unsigned int _fitscreen(const unsigned int dx, const unsigned int dy, const unsigned int dz,
6289  const int dmin, const int dmax,const bool return_y) {
6290  const unsigned int _nw = dx + (dz>1?dz:0), _nh = dy + (dz>1?dz:0);
6291  unsigned int nw = _nw?_nw:1, nh = _nh?_nh:1;
6292  const unsigned int
6294  mw = dmin<0?(unsigned int)(sw*-dmin/100):(unsigned int)dmin,
6295  mh = dmin<0?(unsigned int)(sh*-dmin/100):(unsigned int)dmin,
6296  Mw = dmax<0?(unsigned int)(sw*-dmax/100):(unsigned int)dmax,
6297  Mh = dmax<0?(unsigned int)(sh*-dmax/100):(unsigned int)dmax;
6298  if (nw<mw) { nh = nh*mw/nw; nh+=(nh==0?1:0); nw = mw; }
6299  if (nh<mh) { nw = nw*mh/nh; nw+=(nw==0?1:0); nh = mh; }
6300  if (nw>Mw) { nh = nh*Mw/nw; nh+=(nh==0?1:0); nw = Mw; }
6301  if (nh>Mh) { nw = nw*Mh/nh; nw+=(nw==0?1:0); nh = Mh; }
6302  if (nw<mw) nw = mw;
6303  if (nh<mh) nh = mh;
6304  return return_y?nh:nw;
6305  }
6306 
6308  //------------------------------------------
6309  //
6311 
6312  //------------------------------------------
6313 
6315 
6318  template<typename t>
6320  return display(img);
6321  }
6322 
6324 
6327  template<typename t>
6329  return display(list);
6330  }
6331 
6333 
6337  return assign(disp);
6338  }
6339 
6341 
6344  operator bool() const {
6345  return !is_empty();
6346  }
6347 
6349  //------------------------------------------
6350  //
6352 
6353  //------------------------------------------
6354 
6356 
6358  bool is_empty() const {
6359  return !(_width && _height);
6360  }
6361 
6363 
6368  bool is_closed() const {
6369  return _is_closed;
6370  }
6371 
6373 
6375  bool is_resized() const {
6376  return _is_resized;
6377  }
6378 
6380 
6382  bool is_moved() const {
6383  return _is_moved;
6384  }
6385 
6387 
6389  bool is_event() const {
6390  return _is_event;
6391  }
6392 
6394 
6396  bool is_fullscreen() const {
6397  return _is_fullscreen;
6398  }
6399 
6401 
6404  bool is_key() const {
6405  return _is_keyESC || _is_keyF1 || _is_keyF2 || _is_keyF3 ||
6406  _is_keyF4 || _is_keyF5 || _is_keyF6 || _is_keyF7 ||
6407  _is_keyF8 || _is_keyF9 || _is_keyF10 || _is_keyF11 ||
6408  _is_keyF12 || _is_keyPAUSE || _is_key1 || _is_key2 ||
6409  _is_key3 || _is_key4 || _is_key5 || _is_key6 ||
6410  _is_key7 || _is_key8 || _is_key9 || _is_key0 ||
6411  _is_keyBACKSPACE || _is_keyINSERT || _is_keyHOME ||
6412  _is_keyPAGEUP || _is_keyTAB || _is_keyQ || _is_keyW ||
6413  _is_keyE || _is_keyR || _is_keyT || _is_keyY ||
6414  _is_keyU || _is_keyI || _is_keyO || _is_keyP ||
6415  _is_keyDELETE || _is_keyEND || _is_keyPAGEDOWN ||
6416  _is_keyCAPSLOCK || _is_keyA || _is_keyS || _is_keyD ||
6417  _is_keyF || _is_keyG || _is_keyH || _is_keyJ ||
6418  _is_keyK || _is_keyL || _is_keyENTER ||
6419  _is_keySHIFTLEFT || _is_keyZ || _is_keyX || _is_keyC ||
6420  _is_keyV || _is_keyB || _is_keyN || _is_keyM ||
6421  _is_keySHIFTRIGHT || _is_keyARROWUP || _is_keyCTRLLEFT ||
6422  _is_keyAPPLEFT || _is_keyALT || _is_keySPACE || _is_keyALTGR ||
6423  _is_keyAPPRIGHT || _is_keyMENU || _is_keyCTRLRIGHT ||
6424  _is_keyARROWLEFT || _is_keyARROWDOWN || _is_keyARROWRIGHT ||
6425  _is_keyPAD0 || _is_keyPAD1 || _is_keyPAD2 ||
6426  _is_keyPAD3 || _is_keyPAD4 || _is_keyPAD5 ||
6427  _is_keyPAD6 || _is_keyPAD7 || _is_keyPAD8 ||
6428  _is_keyPAD9 || _is_keyPADADD || _is_keyPADSUB ||
6429  _is_keyPADMUL || _is_keyPADDIV;
6430  }
6431 
6433 
6446  bool is_key(const unsigned int keycode) const {
6447 #define _cimg_iskey_test(k) if (keycode==cimg::key##k) return _is_key##k;
6448  _cimg_iskey_test(ESC); _cimg_iskey_test(F1); _cimg_iskey_test(F2); _cimg_iskey_test(F3);
6449  _cimg_iskey_test(F4); _cimg_iskey_test(F5); _cimg_iskey_test(F6); _cimg_iskey_test(F7);
6450  _cimg_iskey_test(F8); _cimg_iskey_test(F9); _cimg_iskey_test(F10); _cimg_iskey_test(F11);
6451  _cimg_iskey_test(F12); _cimg_iskey_test(PAUSE); _cimg_iskey_test(1); _cimg_iskey_test(2);
6452  _cimg_iskey_test(3); _cimg_iskey_test(4); _cimg_iskey_test(5); _cimg_iskey_test(6);
6453  _cimg_iskey_test(7); _cimg_iskey_test(8); _cimg_iskey_test(9); _cimg_iskey_test(0);
6454  _cimg_iskey_test(BACKSPACE); _cimg_iskey_test(INSERT); _cimg_iskey_test(HOME);
6455  _cimg_iskey_test(PAGEUP); _cimg_iskey_test(TAB); _cimg_iskey_test(Q); _cimg_iskey_test(W);
6456  _cimg_iskey_test(E); _cimg_iskey_test(R); _cimg_iskey_test(T); _cimg_iskey_test(Y);
6457  _cimg_iskey_test(U); _cimg_iskey_test(I); _cimg_iskey_test(O); _cimg_iskey_test(P);
6458  _cimg_iskey_test(DELETE); _cimg_iskey_test(END); _cimg_iskey_test(PAGEDOWN);
6459  _cimg_iskey_test(CAPSLOCK); _cimg_iskey_test(A); _cimg_iskey_test(S); _cimg_iskey_test(D);
6460  _cimg_iskey_test(F); _cimg_iskey_test(G); _cimg_iskey_test(H); _cimg_iskey_test(J);
6461  _cimg_iskey_test(K); _cimg_iskey_test(L); _cimg_iskey_test(ENTER);
6462  _cimg_iskey_test(SHIFTLEFT); _cimg_iskey_test(Z); _cimg_iskey_test(X); _cimg_iskey_test(C);
6463  _cimg_iskey_test(V); _cimg_iskey_test(B); _cimg_iskey_test(N); _cimg_iskey_test(M);
6464  _cimg_iskey_test(SHIFTRIGHT); _cimg_iskey_test(ARROWUP); _cimg_iskey_test(CTRLLEFT);
6465  _cimg_iskey_test(APPLEFT); _cimg_iskey_test(ALT); _cimg_iskey_test(SPACE); _cimg_iskey_test(ALTGR);
6466  _cimg_iskey_test(APPRIGHT); _cimg_iskey_test(MENU); _cimg_iskey_test(CTRLRIGHT);
6467  _cimg_iskey_test(ARROWLEFT); _cimg_iskey_test(ARROWDOWN); _cimg_iskey_test(ARROWRIGHT);
6468  _cimg_iskey_test(PAD0); _cimg_iskey_test(PAD1); _cimg_iskey_test(PAD2);
6469  _cimg_iskey_test(PAD3); _cimg_iskey_test(PAD4); _cimg_iskey_test(PAD5);
6470  _cimg_iskey_test(PAD6); _cimg_iskey_test(PAD7); _cimg_iskey_test(PAD8);
6471  _cimg_iskey_test(PAD9); _cimg_iskey_test(PADADD); _cimg_iskey_test(PADSUB);
6472  _cimg_iskey_test(PADMUL); _cimg_iskey_test(PADDIV);
6473  return false;
6474  }
6475 
6477 
6490  volatile bool& is_key(const char *const keycode) {
6491  static bool f = false;
6492  f = false;
6493 #define _cimg_iskey_test2(k) if (!cimg::strcasecmp(keycode,#k)) return _is_key##k;
6494  _cimg_iskey_test2(ESC); _cimg_iskey_test2(F1); _cimg_iskey_test2(F2); _cimg_iskey_test2(F3);
6495  _cimg_iskey_test2(F4); _cimg_iskey_test2(F5); _cimg_iskey_test2(F6); _cimg_iskey_test2(F7);
6496  _cimg_iskey_test2(F8); _cimg_iskey_test2(F9); _cimg_iskey_test2(F10); _cimg_iskey_test2(F11);
6497  _cimg_iskey_test2(F12); _cimg_iskey_test2(PAUSE); _cimg_iskey_test2(1); _cimg_iskey_test2(2);
6498  _cimg_iskey_test2(3); _cimg_iskey_test2(4); _cimg_iskey_test2(5); _cimg_iskey_test2(6);
6499  _cimg_iskey_test2(7); _cimg_iskey_test2(8); _cimg_iskey_test2(9); _cimg_iskey_test2(0);
6500  _cimg_iskey_test2(BACKSPACE); _cimg_iskey_test2(INSERT); _cimg_iskey_test2(HOME);
6501  _cimg_iskey_test2(PAGEUP); _cimg_iskey_test2(TAB); _cimg_iskey_test2(Q); _cimg_iskey_test2(W);
6502  _cimg_iskey_test2(E); _cimg_iskey_test2(R); _cimg_iskey_test2(T); _cimg_iskey_test2(Y);
6503  _cimg_iskey_test2(U); _cimg_iskey_test2(I); _cimg_iskey_test2(O); _cimg_iskey_test2(P);
6504  _cimg_iskey_test2(DELETE); _cimg_iskey_test2(END); _cimg_iskey_test2(PAGEDOWN);
6505  _cimg_iskey_test2(CAPSLOCK); _cimg_iskey_test2(A); _cimg_iskey_test2(S); _cimg_iskey_test2(D);
6506  _cimg_iskey_test2(F); _cimg_iskey_test2(G); _cimg_iskey_test2(H); _cimg_iskey_test2(J);
6507  _cimg_iskey_test2(K); _cimg_iskey_test2(L); _cimg_iskey_test2(ENTER);
6508  _cimg_iskey_test2(SHIFTLEFT); _cimg_iskey_test2(Z); _cimg_iskey_test2(X); _cimg_iskey_test2(C);
6509  _cimg_iskey_test2(V); _cimg_iskey_test2(B); _cimg_iskey_test2(N); _cimg_iskey_test2(M);
6510  _cimg_iskey_test2(SHIFTRIGHT); _cimg_iskey_test2(ARROWUP); _cimg_iskey_test2(CTRLLEFT);
6511  _cimg_iskey_test2(APPLEFT); _cimg_iskey_test2(ALT); _cimg_iskey_test2(SPACE); _cimg_iskey_test2(ALTGR);
6512  _cimg_iskey_test2(APPRIGHT); _cimg_iskey_test2(MENU); _cimg_iskey_test2(CTRLRIGHT);
6513  _cimg_iskey_test2(ARROWLEFT); _cimg_iskey_test2(ARROWDOWN); _cimg_iskey_test2(ARROWRIGHT);
6514  _cimg_iskey_test2(PAD0); _cimg_iskey_test2(PAD1); _cimg_iskey_test2(PAD2);
6515  _cimg_iskey_test2(PAD3); _cimg_iskey_test2(PAD4); _cimg_iskey_test2(PAD5);
6516  _cimg_iskey_test2(PAD6); _cimg_iskey_test2(PAD7); _cimg_iskey_test2(PAD8);
6517  _cimg_iskey_test2(PAD9); _cimg_iskey_test2(PADADD); _cimg_iskey_test2(PADSUB);
6518  _cimg_iskey_test2(PADMUL); _cimg_iskey_test2(PADDIV);
6519  return f;
6520  }
6521 
6523 
6539  bool is_key_sequence(const unsigned int *const keycodes_sequence, const unsigned int length,
6540  const bool remove_sequence=false) {
6541  if (keycodes_sequence && length) {
6542  const unsigned int
6543  *const ps_end = keycodes_sequence + length - 1,
6544  *const pk_end = (unsigned int*)_keys + 1 + sizeof(_keys)/sizeof(unsigned int) - length,
6545  k = *ps_end;
6546  for (unsigned int *pk = (unsigned int*)_keys; pk<pk_end; ) {
6547  if (*(pk++)==k) {
6548  bool res = true;
6549  const unsigned int *ps = ps_end, *pk2 = pk;
6550  for (unsigned int i = 1; i<length; ++i) res = (*(--ps)==*(pk2++));
6551  if (res) {
6552  if (remove_sequence) std::memset((void*)(pk-1),0,sizeof(unsigned int)*length);
6553  return true;
6554  }
6555  }
6556  }
6557  }
6558  return false;
6559  }
6560 
6561 #define _cimg_iskey_def(k) \
6562  bool is_key##k() const { \
6563  return _is_key##k; \
6564  }
6565 
6567 
6576  _cimg_iskey_def(BACKSPACE); _cimg_iskey_def(INSERT); _cimg_iskey_def(HOME);
6580  _cimg_iskey_def(DELETE); _cimg_iskey_def(END); _cimg_iskey_def(PAGEDOWN);
6586  _cimg_iskey_def(SHIFTRIGHT); _cimg_iskey_def(ARROWUP); _cimg_iskey_def(CTRLLEFT);
6587  _cimg_iskey_def(APPLEFT); _cimg_iskey_def(ALT); _cimg_iskey_def(SPACE); _cimg_iskey_def(ALTGR);
6588  _cimg_iskey_def(APPRIGHT); _cimg_iskey_def(MENU); _cimg_iskey_def(CTRLRIGHT);
6589  _cimg_iskey_def(ARROWLEFT); _cimg_iskey_def(ARROWDOWN); _cimg_iskey_def(ARROWRIGHT);
6590  _cimg_iskey_def(PAD0); _cimg_iskey_def(PAD1); _cimg_iskey_def(PAD2);
6591  _cimg_iskey_def(PAD3); _cimg_iskey_def(PAD4); _cimg_iskey_def(PAD5);
6592  _cimg_iskey_def(PAD6); _cimg_iskey_def(PAD7); _cimg_iskey_def(PAD8);
6593  _cimg_iskey_def(PAD9); _cimg_iskey_def(PADADD); _cimg_iskey_def(PADSUB);
6594  _cimg_iskey_def(PADMUL); _cimg_iskey_def(PADDIV);
6595 
6597  //------------------------------------------
6598  //
6600 
6601  //------------------------------------------
6602 
6603 #if cimg_display==0
6604 
6606 
6608  static int screen_width() {
6609  _no_display_exception();
6610  return 0;
6611  }
6612 
6614 
6616  static int screen_height() {
6617  _no_display_exception();
6618  return 0;
6619  }
6620 
6621 #endif
6622 
6624 
6628  int width() const {
6629  return (int)_width;
6630  }
6631 
6633 
6637  int height() const {
6638  return (int)_height;
6639  }
6640 
6642 
6664  unsigned int normalization() const {
6665  return _normalization;
6666  }
6667 
6669 
6673  const char *title() const {
6674  return _title;
6675  }
6676 
6678 
6682  int window_width() const {
6683  return (int)_window_width;
6684  }
6685 
6687 
6691  int window_height() const {
6692  return (int)_window_height;
6693  }
6694 
6696 
6699  int window_x() const {
6700  return _window_x;
6701  }
6702 
6704 
6707  int window_y() const {
6708  return _window_y;
6709  }
6710 
6712 
6717  int mouse_x() const {
6718  return _mouse_x;
6719  }
6720 
6722 
6727  int mouse_y() const {
6728  return _mouse_y;
6729  }
6730 
6732 
6757  unsigned int button() const {
6758  return _button;
6759  }
6760 
6762 
6786  int wheel() const {
6787  return _wheel;
6788  }
6789 
6791 
6803  unsigned int key(const unsigned int pos=0) const {
6804  return pos<(sizeof(_keys)/sizeof(unsigned int))?_keys[pos]:0;
6805  }
6806 
6808 
6820  unsigned int released_key(const unsigned int pos=0) const {
6821  return pos<(sizeof(_released_keys)/sizeof(unsigned int))?_released_keys[pos]:0;
6822  }
6823 
6825 
6833  static unsigned int keycode(const char *const keycode) {
6834 #define _cimg_keycode(k) if (!cimg::strcasecmp(keycode,#k)) return cimg::key##k;
6835  _cimg_keycode(ESC); _cimg_keycode(F1); _cimg_keycode(F2); _cimg_keycode(F3);
6836  _cimg_keycode(F4); _cimg_keycode(F5); _cimg_keycode(F6); _cimg_keycode(F7);
6837  _cimg_keycode(F8); _cimg_keycode(F9); _cimg_keycode(F10); _cimg_keycode(F11);
6838  _cimg_keycode(F12); _cimg_keycode(PAUSE); _cimg_keycode(1); _cimg_keycode(2);
6839  _cimg_keycode(3); _cimg_keycode(4); _cimg_keycode(5); _cimg_keycode(6);
6840  _cimg_keycode(7); _cimg_keycode(8); _cimg_keycode(9); _cimg_keycode(0);
6841  _cimg_keycode(BACKSPACE); _cimg_keycode(INSERT); _cimg_keycode(HOME);
6842  _cimg_keycode(PAGEUP); _cimg_keycode(TAB); _cimg_keycode(Q); _cimg_keycode(W);
6843  _cimg_keycode(E); _cimg_keycode(R); _cimg_keycode(T); _cimg_keycode(Y);
6844  _cimg_keycode(U); _cimg_keycode(I); _cimg_keycode(O); _cimg_keycode(P);
6845  _cimg_keycode(DELETE); _cimg_keycode(END); _cimg_keycode(PAGEDOWN);
6846  _cimg_keycode(CAPSLOCK); _cimg_keycode(A); _cimg_keycode(S); _cimg_keycode(D);
6847  _cimg_keycode(F); _cimg_keycode(G); _cimg_keycode(H); _cimg_keycode(J);
6848  _cimg_keycode(K); _cimg_keycode(L); _cimg_keycode(ENTER);
6849  _cimg_keycode(SHIFTLEFT); _cimg_keycode(Z); _cimg_keycode(X); _cimg_keycode(C);
6850  _cimg_keycode(V); _cimg_keycode(B); _cimg_keycode(N); _cimg_keycode(M);
6851  _cimg_keycode(SHIFTRIGHT); _cimg_keycode(ARROWUP); _cimg_keycode(CTRLLEFT);
6852  _cimg_keycode(APPLEFT); _cimg_keycode(ALT); _cimg_keycode(SPACE); _cimg_keycode(ALTGR);
6853  _cimg_keycode(APPRIGHT); _cimg_keycode(MENU); _cimg_keycode(CTRLRIGHT);
6854  _cimg_keycode(ARROWLEFT); _cimg_keycode(ARROWDOWN); _cimg_keycode(ARROWRIGHT);
6855  _cimg_keycode(PAD0); _cimg_keycode(PAD1); _cimg_keycode(PAD2);
6856  _cimg_keycode(PAD3); _cimg_keycode(PAD4); _cimg_keycode(PAD5);
6857  _cimg_keycode(PAD6); _cimg_keycode(PAD7); _cimg_keycode(PAD8);
6858  _cimg_keycode(PAD9); _cimg_keycode(PADADD); _cimg_keycode(PADSUB);
6859  _cimg_keycode(PADMUL); _cimg_keycode(PADDIV);
6860  return 0;
6861  }
6862 
6864 
6869  if (!_fps_timer) _fps_timer = cimg::time();
6870  const float delta = (cimg::time()-_fps_timer)/1000.0f;
6871  ++_fps_frames;
6872  if (delta>=1) {
6873  _fps_fps = _fps_frames/delta;
6874  _fps_frames = 0;
6875  _fps_timer = cimg::time();
6876  }
6877  return _fps_fps;
6878  }
6879 
6881  //---------------------------------------
6882  //
6884 
6885  //---------------------------------------
6886 
6887 #if cimg_display==0
6888 
6890 
6894  template<typename T>
6895  CImgDisplay& display(const CImg<T>& img) {
6896  return assign(img);
6897  }
6898 
6899 #endif
6900 
6902 
6909  template<typename T>
6910  CImgDisplay& display(const CImgList<T>& list, const char axis='x', const float align=0) {
6911  CImgList<typename CImg<T>::ucharT> visu(list._width);
6912  cimglist_for(list,l) {
6913  const CImg<T>& img = list._data[l];
6914  img.__get_select(*this,_normalization,(img._width-1)/2,(img._height-1)/2,(img._depth-1)/2).move_to(visu[l]);
6915  }
6916  visu.get_append(axis,align).display(*this);
6917  return *this;
6918  }
6919 
6920 #if cimg_display==0
6921 
6923 
6929  return assign();
6930  }
6931 
6933 
6941  return assign();
6942  }
6943 
6945 
6951  CImgDisplay& move(const int pos_x, const int pos_y) {
6952  return assign(pos_x,pos_y);
6953  }
6954 
6955 #endif
6956 
6958 
6965  CImgDisplay& resize(const bool force_redraw=true) {
6966  resize(_window_width,_window_height,force_redraw);
6967  return *this;
6968  }
6969 
6970 #if cimg_display==0
6971 
6973 
6979  CImgDisplay& resize(const int width, const int height, const bool force_redraw=true) {
6980  return assign(width,height,0,3,force_redraw);
6981  }
6982 
6983 #endif
6984 
6986 
6994  template<typename T>
6995  CImgDisplay& resize(const CImg<T>& img, const bool force_redraw=true) {
6996  return resize(img._width,img._height,force_redraw);
6997  }
6998 
7000 
7008  CImgDisplay& resize(const CImgDisplay& disp, const bool force_redraw=true) {
7009  return resize(disp._width,disp._height,force_redraw);
7010  }
7011 
7012  // [internal] Render pixel buffer with size (wd,hd) from source buffer of size (ws,hs).
7013  template<typename t, typename T>
7014  static void _render_resize(const T *ptrs, const unsigned int ws, const unsigned int hs,
7015  t *ptrd, const unsigned int wd, const unsigned int hd) {
7016  unsigned int *const offx = new unsigned int[wd], *const offy = new unsigned int[hd+1], *poffx, *poffy;
7017  float s, curr, old;
7018  s = (float)ws/wd;
7019  poffx = offx; curr = 0; for (unsigned int x = 0; x<wd; ++x) {
7020  old = curr; curr+=s; *(poffx++) = (unsigned int)curr - (unsigned int)old;
7021  }
7022  s = (float)hs/hd;
7023  poffy = offy; curr = 0; for (unsigned int y = 0; y<hd; ++y) {
7024  old = curr; curr+=s; *(poffy++) = ws*((unsigned int)curr - (unsigned int)old);
7025  }
7026  *poffy = 0;
7027  poffy = offy;
7028  for (unsigned int y = 0; y<hd; ) {
7029  const T *ptr = ptrs;
7030  poffx = offx;
7031  for (unsigned int x = 0; x<wd; ++x) { *(ptrd++) = *ptr; ptr+=*(poffx++); }
7032  ++y;
7033  unsigned int dy = *(poffy++);
7034  for ( ; !dy && y<hd; std::memcpy(ptrd,ptrd - wd,sizeof(t)*wd), ++y, ptrd+=wd, dy = *(poffy++)) {}
7035  ptrs+=dy;
7036  }
7037  delete[] offx; delete[] offy;
7038  }
7039 
7041 
7045  _normalization = normalization;
7046  _min = _max = 0;
7047  return *this;
7048  }
7049 
7050 #if cimg_display==0
7051 
7053 
7065  CImgDisplay& set_title(const char *const format, ...) {
7066  return assign(0,0,format);
7067  }
7068 
7069 #endif
7070 
7072 
7083  CImgDisplay& set_fullscreen(const bool is_fullscreen, const bool force_redraw=true) {
7084  if (is_empty() || _is_fullscreen==is_fullscreen) return *this;
7085  return toggle_fullscreen(force_redraw);
7086  }
7087 
7088 #if cimg_display==0
7089 
7091 
7095  CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
7096  return assign(_width,_height,0,3,force_redraw);
7097  }
7098 
7100 
7105  return assign();
7106  }
7107 
7109 
7114  return assign();
7115  }
7116 
7118 
7122  CImgDisplay& set_mouse(const int pos_x, const int pos_y) {
7123  return assign(pos_x,pos_y);
7124  }
7125 
7126 #endif
7127 
7129 
7133  _button = 0;
7134  _is_event = true;
7135 #if cimg_display==1
7136  pthread_cond_broadcast(&cimg::X11_attr().wait_event);
7137 #elif cimg_display==2
7138  SetEvent(cimg::Win32_attr().wait_event);
7139 #endif
7140  return *this;
7141  }
7142 
7144 
7148  CImgDisplay& set_button(const unsigned int button, const bool is_pressed=true) {
7149  const unsigned int buttoncode = button==1?1:button==2?2:button==3?4:0;
7150  if (is_pressed) _button |= buttoncode; else _button &= ~buttoncode;
7151  _is_event = buttoncode?true:false;
7152  if (buttoncode) {
7153 #if cimg_display==1
7154  pthread_cond_broadcast(&cimg::X11_attr().wait_event);
7155 #elif cimg_display==2
7156  SetEvent(cimg::Win32_attr().wait_event);
7157 #endif
7158  }
7159  return *this;
7160  }
7161 
7163 
7167  _wheel = 0;
7168  _is_event = true;
7169 #if cimg_display==1
7170  pthread_cond_broadcast(&cimg::X11_attr().wait_event);
7171 #elif cimg_display==2
7172  SetEvent(cimg::Win32_attr().wait_event);
7173 #endif
7174  return *this;
7175  }
7176 
7178 
7182  CImgDisplay& set_wheel(const int amplitude) {
7183  _wheel+=amplitude;
7184  _is_event = amplitude?true:false;
7185  if (amplitude) {
7186 #if cimg_display==1
7187  pthread_cond_broadcast(&cimg::X11_attr().wait_event);
7188 #elif cimg_display==2
7189  SetEvent(cimg::Win32_attr().wait_event);
7190 #endif
7191  }
7192  return *this;
7193  }
7194 
7196 
7200  std::memset((void*)_keys,0,sizeof(_keys));
7201  std::memset((void*)_released_keys,0,sizeof(_released_keys));
7202  _is_keyESC = _is_keyF1 = _is_keyF2 = _is_keyF3 = _is_keyF4 = _is_keyF5 = _is_keyF6 = _is_keyF7 = _is_keyF8 =
7203  _is_keyF9 = _is_keyF10 = _is_keyF11 = _is_keyF12 = _is_keyPAUSE = _is_key1 = _is_key2 = _is_key3 = _is_key4 =
7204  _is_key5 = _is_key6 = _is_key7 = _is_key8 = _is_key9 = _is_key0 = _is_keyBACKSPACE = _is_keyINSERT =
7205  _is_keyHOME = _is_keyPAGEUP = _is_keyTAB = _is_keyQ = _is_keyW = _is_keyE = _is_keyR = _is_keyT = _is_keyY =
7206  _is_keyU = _is_keyI = _is_keyO = _is_keyP = _is_keyDELETE = _is_keyEND = _is_keyPAGEDOWN = _is_keyCAPSLOCK =
7207  _is_keyA = _is_keyS = _is_keyD = _is_keyF = _is_keyG = _is_keyH = _is_keyJ = _is_keyK = _is_keyL =
7208  _is_keyENTER = _is_keySHIFTLEFT = _is_keyZ = _is_keyX = _is_keyC = _is_keyV = _is_keyB = _is_keyN =
7209  _is_keyM = _is_keySHIFTRIGHT = _is_keyARROWUP = _is_keyCTRLLEFT = _is_keyAPPLEFT = _is_keyALT = _is_keySPACE =
7210  _is_keyALTGR = _is_keyAPPRIGHT = _is_keyMENU = _is_keyCTRLRIGHT = _is_keyARROWLEFT = _is_keyARROWDOWN =
7211  _is_keyARROWRIGHT = _is_keyPAD0 = _is_keyPAD1 = _is_keyPAD2 = _is_keyPAD3 = _is_keyPAD4 = _is_keyPAD5 =
7212  _is_keyPAD6 = _is_keyPAD7 = _is_keyPAD8 = _is_keyPAD9 = _is_keyPADADD = _is_keyPADSUB = _is_keyPADMUL =
7213  _is_keyPADDIV = false;
7214  _is_event = true;
7215 #if cimg_display==1
7216  pthread_cond_broadcast(&cimg::X11_attr().wait_event);
7217 #elif cimg_display==2
7218  SetEvent(cimg::Win32_attr().wait_event);
7219 #endif
7220  return *this;
7221  }
7222 
7224 
7230  CImgDisplay& set_key(const unsigned int keycode, const bool is_pressed=true) {
7231 #define _cimg_set_key(k) if (keycode==cimg::key##k) _is_key##k = is_pressed;
7232  _cimg_set_key(ESC); _cimg_set_key(F1); _cimg_set_key(F2); _cimg_set_key(F3);
7233  _cimg_set_key(F4); _cimg_set_key(F5); _cimg_set_key(F6); _cimg_set_key(F7);
7234  _cimg_set_key(F8); _cimg_set_key(F9); _cimg_set_key(F10); _cimg_set_key(F11);
7235  _cimg_set_key(F12); _cimg_set_key(PAUSE); _cimg_set_key(1); _cimg_set_key(2);
7236  _cimg_set_key(3); _cimg_set_key(4); _cimg_set_key(5); _cimg_set_key(6);
7237  _cimg_set_key(7); _cimg_set_key(8); _cimg_set_key(9); _cimg_set_key(0);
7238  _cimg_set_key(BACKSPACE); _cimg_set_key(INSERT); _cimg_set_key(HOME);
7239  _cimg_set_key(PAGEUP); _cimg_set_key(TAB); _cimg_set_key(Q); _cimg_set_key(W);
7240  _cimg_set_key(E); _cimg_set_key(R); _cimg_set_key(T); _cimg_set_key(Y);
7241  _cimg_set_key(U); _cimg_set_key(I); _cimg_set_key(O); _cimg_set_key(P);
7242  _cimg_set_key(DELETE); _cimg_set_key(END); _cimg_set_key(PAGEDOWN);
7243  _cimg_set_key(CAPSLOCK); _cimg_set_key(A); _cimg_set_key(S); _cimg_set_key(D);
7244  _cimg_set_key(F); _cimg_set_key(G); _cimg_set_key(H); _cimg_set_key(J);
7245  _cimg_set_key(K); _cimg_set_key(L); _cimg_set_key(ENTER);
7246  _cimg_set_key(SHIFTLEFT); _cimg_set_key(Z); _cimg_set_key(X); _cimg_set_key(C);
7247  _cimg_set_key(V); _cimg_set_key(B); _cimg_set_key(N); _cimg_set_key(M);
7248  _cimg_set_key(SHIFTRIGHT); _cimg_set_key(ARROWUP); _cimg_set_key(CTRLLEFT);
7249  _cimg_set_key(APPLEFT); _cimg_set_key(ALT); _cimg_set_key(SPACE); _cimg_set_key(ALTGR);
7250  _cimg_set_key(APPRIGHT); _cimg_set_key(MENU); _cimg_set_key(CTRLRIGHT);
7251  _cimg_set_key(ARROWLEFT); _cimg_set_key(ARROWDOWN); _cimg_set_key(ARROWRIGHT);
7252  _cimg_set_key(PAD0); _cimg_set_key(PAD1); _cimg_set_key(PAD2);
7253  _cimg_set_key(PAD3); _cimg_set_key(PAD4); _cimg_set_key(PAD5);
7254  _cimg_set_key(PAD6); _cimg_set_key(PAD7); _cimg_set_key(PAD8);
7255  _cimg_set_key(PAD9); _cimg_set_key(PADADD); _cimg_set_key(PADSUB);
7256  _cimg_set_key(PADMUL); _cimg_set_key(PADDIV);
7257  if (is_pressed) {
7258  if (*_keys)
7259  std::memmove((void*)(_keys+1),(void*)_keys,sizeof(_keys) - sizeof(unsigned int));
7260  *_keys = keycode;
7261  if (*_released_keys) {
7262  std::memmove((void*)(_released_keys+1),(void*)_released_keys,sizeof(_released_keys) - sizeof(unsigned int));
7263  *_released_keys = 0;
7264  }
7265  } else {
7266  if (*_keys) {
7267  std::memmove((void*)(_keys+1),(void*)_keys,sizeof(_keys) - sizeof(unsigned int));
7268  *_keys = 0;
7269  }
7270  if (*_released_keys)
7271  std::memmove((void*)(_released_keys+1),(void*)_released_keys,sizeof(_released_keys) - sizeof(unsigned int));
7272  *_released_keys = keycode;
7273  }
7274  _is_event = keycode?true:false;
7275  if (keycode) {
7276 #if cimg_display==1
7277  pthread_cond_broadcast(&cimg::X11_attr().wait_event);
7278 #elif cimg_display==2
7279  SetEvent(cimg::Win32_attr().wait_event);
7280 #endif
7281  }
7282  return *this;
7283  }
7284 
7286 
7291  _is_resized = _is_moved = _is_event = false;
7292  _fps_timer = _fps_frames = _timer = 0;
7293  _fps_fps = 0;
7294  return *this;
7295  }
7296 
7299  wait(*this);
7300  return *this;
7301  }
7302 
7304 
7308  CImgDisplay& wait(const unsigned int milliseconds) {
7309  cimg::_wait(milliseconds,_timer);
7310  return *this;
7311  }
7312 
7314  static void wait(CImgDisplay& disp1) {
7315  disp1._is_event = false;
7316  while (!disp1._is_closed && !disp1._is_event) wait_all();
7317  }
7318 
7320  static void wait(CImgDisplay& disp1, CImgDisplay& disp2) {
7321  disp1._is_event = disp2._is_event = false;
7322  while ((!disp1._is_closed || !disp2._is_closed) &&
7323  !disp1._is_event && !disp2._is_event) wait_all();
7324  }
7325 
7327  static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3) {
7328  disp1._is_event = disp2._is_event = disp3._is_event = false;
7329  while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed) &&
7330  !disp1._is_event && !disp2._is_event && !disp3._is_event) wait_all();
7331  }
7332 
7334  static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4) {
7335  disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = false;
7336  while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed) &&
7337  !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event) wait_all();
7338  }
7339 
7341  static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4,
7342  CImgDisplay& disp5) {
7343  disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = false;
7344  while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed) &&
7345  !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event)
7346  wait_all();
7347  }
7348 
7350  static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
7351  CImgDisplay& disp6) {
7352  disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
7353  disp6._is_event = false;
7354  while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
7355  !disp6._is_closed) &&
7356  !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
7357  !disp6._is_event) wait_all();
7358  }
7359 
7361  static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
7362  CImgDisplay& disp6, CImgDisplay& disp7) {
7363  disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
7364  disp6._is_event = disp7._is_event = false;
7365  while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
7366  !disp6._is_closed || !disp7._is_closed) &&
7367  !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
7368  !disp6._is_event && !disp7._is_event) wait_all();
7369  }
7370 
7372  static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
7373  CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8) {
7374  disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
7375  disp6._is_event = disp7._is_event = disp8._is_event = false;
7376  while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
7377  !disp6._is_closed || !disp7._is_closed || !disp8._is_closed) &&
7378  !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
7379  !disp6._is_event && !disp7._is_event && !disp8._is_event) wait_all();
7380  }
7381 
7383  static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
7384  CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9) {
7385  disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
7386  disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = false;
7387  while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
7388  !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed) &&
7389  !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
7390  !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event) wait_all();
7391  }
7392 
7394  static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
7395  CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9,
7396  CImgDisplay& disp10) {
7397  disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
7398  disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = disp10._is_event = false;
7399  while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
7400  !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed || !disp10._is_closed) &&
7401  !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
7402  !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event && !disp10._is_event)
7403  wait_all();
7404  }
7405 
7406 #if cimg_display==0
7407 
7409  static void wait_all() {
7410  return _no_display_exception();
7411  }
7412 
7414 
7421  template<typename T>
7422  CImgDisplay& render(const CImg<T>& img) {
7423  return assign(img);
7424  }
7425 
7427 
7433  return assign();
7434  }
7435 
7437 
7440  template<typename T>
7441  const CImgDisplay& snapshot(CImg<T>& img) const {
7442  cimg::unused(img);
7443  _no_display_exception();
7444  return *this;
7445  }
7446 #endif
7447 
7448  // X11-based implementation
7449  //--------------------------
7450 #if cimg_display==1
7451 
7452  Atom _wm_window_atom, _wm_protocol_atom;
7453  Window _window, _background_window;
7454  Colormap _colormap;
7455  XImage *_image;
7456  void *_data;
7457 #ifdef cimg_use_xshm
7458  XShmSegmentInfo *_shminfo;
7459 #endif
7460 
7461  static int screen_width() {
7462  Display *const dpy = cimg::X11_attr().display;
7463  int res = 0;
7464  if (!dpy) {
7465  Display *const _dpy = XOpenDisplay(0);
7466  if (!_dpy)
7467  throw CImgDisplayException("CImgDisplay::screen_width(): Failed to open X11 display.");
7468  res = DisplayWidth(_dpy,DefaultScreen(_dpy));
7469  XCloseDisplay(_dpy);
7470  } else {
7471 #ifdef cimg_use_xrandr
7472  if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution)
7473  res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width;
7474  else res = DisplayWidth(dpy,DefaultScreen(dpy));
7475 #else
7476  res = DisplayWidth(dpy,DefaultScreen(dpy));
7477 #endif
7478  }
7479  return res;
7480  }
7481 
7482  static int screen_height() {
7483  Display *const dpy = cimg::X11_attr().display;
7484  int res = 0;
7485  if (!dpy) {
7486  Display *const _dpy = XOpenDisplay(0);
7487  if (!_dpy)
7488  throw CImgDisplayException("CImgDisplay::screen_height(): Failed to open X11 display.");
7489  res = DisplayHeight(_dpy,DefaultScreen(_dpy));
7490  XCloseDisplay(_dpy);
7491  } else {
7492 #ifdef cimg_use_xrandr
7493  if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution)
7494  res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height;
7495  else res = DisplayHeight(dpy,DefaultScreen(dpy));
7496 #else
7497  res = DisplayHeight(dpy,DefaultScreen(dpy));
7498 #endif
7499  }
7500  return res;
7501  }
7502 
7503  static void wait_all() {
7504  if (!cimg::X11_attr().display) return;
7505  if (cimg::mutex(13,2)) { cimg::sleep(10); return; }
7506  pthread_cond_wait(&cimg::X11_attr().wait_event,&cimg::X11_attr().wait_event_mutex);
7507  cimg::mutex(13,0);
7508  }
7509 
7510  void _handle_events(const XEvent *const pevent) {
7511  Display *const dpy = cimg::X11_attr().display;
7512  XEvent event = *pevent;
7513  switch (event.type) {
7514  case ClientMessage : {
7515  if ((int)event.xclient.message_type==(int)_wm_protocol_atom &&
7516  (int)event.xclient.data.l[0]==(int)_wm_window_atom) {
7517  XUnmapWindow(cimg::X11_attr().display,_window);
7518  _is_closed = _is_event = true;
7519  pthread_cond_broadcast(&cimg::X11_attr().wait_event);
7520  }
7521  } break;
7522  case ConfigureNotify : {
7523  while (XCheckWindowEvent(dpy,_window,StructureNotifyMask,&event)) {}
7524  const unsigned int nw = event.xconfigure.width, nh = event.xconfigure.height;
7525  const int nx = event.xconfigure.x, ny = event.xconfigure.y;
7526  if (nw && nh && (nw!=_window_width || nh!=_window_height)) {
7527  _window_width = nw; _window_height = nh; _mouse_x = _mouse_y = -1;
7528  XResizeWindow(dpy,_window,_window_width,_window_height);
7529  _is_resized = _is_event = true;
7530  pthread_cond_broadcast(&cimg::X11_attr().wait_event);
7531  }
7532  if (nx!=_window_x || ny!=_window_y) {
7533  _window_x = nx; _window_y = ny; _is_moved = _is_event = true;
7534  pthread_cond_broadcast(&cimg::X11_attr().wait_event);
7535  }
7536  } break;
7537  case Expose : {
7538  while (XCheckWindowEvent(dpy,_window,ExposureMask,&event)) {}
7539  _paint(false);
7540  if (_is_fullscreen) {
7541  XWindowAttributes attr;
7542  XGetWindowAttributes(dpy,_window,&attr);
7543  while (attr.map_state!=IsViewable) XSync(dpy,0);
7544  XSetInputFocus(dpy,_window,RevertToParent,CurrentTime);
7545  }
7546  } break;
7547  case ButtonPress : {
7548  do {
7549  _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y;
7550  if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
7551  switch (event.xbutton.button) {
7552  case 1 : set_button(1); break;
7553  case 3 : set_button(2); break;
7554  case 2 : set_button(3); break;
7555  }
7556  } while (XCheckWindowEvent(dpy,_window,ButtonPressMask,&event));
7557  } break;
7558  case ButtonRelease : {
7559  do {
7560  _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y;
7561  if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
7562  switch (event.xbutton.button) {
7563  case 1 : set_button(1,false); break;
7564  case 3 : set_button(2,false); break;
7565  case 2 : set_button(3,false); break;
7566  case 4 : set_wheel(1); break;
7567  case 5 : set_wheel(-1); break;
7568  }
7569  } while (XCheckWindowEvent(dpy,_window,ButtonReleaseMask,&event));
7570  } break;
7571  case KeyPress : {
7572  char tmp = 0; KeySym ksym;
7573  XLookupString(&event.xkey,&tmp,1,&ksym,0);
7574  set_key((unsigned int)ksym,true);
7575  } break;
7576  case KeyRelease : {
7577  char keys_return[32]; // Check that the key has been physically unpressed.
7578  XQueryKeymap(dpy,keys_return);
7579  const unsigned int kc = event.xkey.keycode, kc1 = kc/8, kc2 = kc%8;
7580  const bool is_key_pressed = kc1>=32?false:(keys_return[kc1]>>kc2)&1;
7581  if (!is_key_pressed) {
7582  char tmp = 0; KeySym ksym;
7583  XLookupString(&event.xkey,&tmp,1,&ksym,0);
7584  set_key((unsigned int)ksym,false);
7585  }
7586  } break;
7587  case EnterNotify: {
7588  while (XCheckWindowEvent(dpy,_window,EnterWindowMask,&event)) {}
7589  _mouse_x = event.xmotion.x;
7590  _mouse_y = event.xmotion.y;
7591  if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
7592  } break;
7593  case LeaveNotify : {
7594  while (XCheckWindowEvent(dpy,_window,LeaveWindowMask,&event)) {}
7595  _mouse_x = _mouse_y =-1; _is_event = true;
7596  pthread_cond_broadcast(&cimg::X11_attr().wait_event);
7597  } break;
7598  case MotionNotify : {
7599  while (XCheckWindowEvent(dpy,_window,PointerMotionMask,&event)) {}
7600  _mouse_x = event.xmotion.x;
7601  _mouse_y = event.xmotion.y;
7602  if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
7603  _is_event = true;
7604  pthread_cond_broadcast(&cimg::X11_attr().wait_event);
7605  } break;
7606  }
7607  }
7608 
7609  static void* _events_thread(void *arg) { // Thread to manage events for all opened display windows.
7610  Display *const dpy = cimg::X11_attr().display;
7611  XEvent event;
7612  pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0);
7613  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0);
7614  if (!arg) for (;;) {
7615  XLockDisplay(dpy);
7616  bool event_flag = XCheckTypedEvent(dpy,ClientMessage,&event);
7617  if (!event_flag) event_flag = XCheckMaskEvent(dpy,
7618  ExposureMask | StructureNotifyMask | ButtonPressMask |
7619  KeyPressMask | PointerMotionMask | EnterWindowMask |
7620  LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask,&event);
7621  if (event_flag)
7622  for (unsigned int i = 0; i<cimg::X11_attr().nb_wins; ++i)
7623  if (!cimg::X11_attr().wins[i]->_is_closed && event.xany.window==cimg::X11_attr().wins[i]->_window)
7624  cimg::X11_attr().wins[i]->_handle_events(&event);
7625  XUnlockDisplay(dpy);
7626  pthread_testcancel();
7627  cimg::sleep(8);
7628  }
7629  return 0;
7630  }
7631 
7632  void _set_colormap(Colormap& _colormap, const unsigned int dim) {
7633  XColor colormap[256];
7634  switch (dim) {
7635  case 1 : { // colormap for greyscale images
7636  for (unsigned int index = 0; index<256; ++index) {
7637  colormap[index].pixel = index;
7638  colormap[index].red = colormap[index].green = colormap[index].blue = (unsigned short)(index<<8);
7639  colormap[index].flags = DoRed | DoGreen | DoBlue;
7640  }
7641  } break;
7642  case 2 : { // colormap for RG images
7643  for (unsigned int index = 0, r = 8; r<256; r+=16)
7644  for (unsigned int g = 8; g<256; g+=16) {
7645  colormap[index].pixel = index;
7646  colormap[index].red = colormap[index].blue = (unsigned short)(r<<8);
7647  colormap[index].green = (unsigned short)(g<<8);
7648  colormap[index++].flags = DoRed | DoGreen | DoBlue;
7649  }
7650  } break;
7651  default : { // colormap for RGB images
7652  for (unsigned int index = 0, r = 16; r<256; r+=32)
7653  for (unsigned int g = 16; g<256; g+=32)
7654  for (unsigned int b = 32; b<256; b+=64) {
7655  colormap[index].pixel = index;
7656  colormap[index].red = (unsigned short)(r<<8);
7657  colormap[index].green = (unsigned short)(g<<8);
7658  colormap[index].blue = (unsigned short)(b<<8);
7659  colormap[index++].flags = DoRed | DoGreen | DoBlue;
7660  }
7661  }
7662  }
7663  XStoreColors(cimg::X11_attr().display,_colormap,colormap,256);
7664  }
7665 
7666  void _map_window() {
7667  Display *const dpy = cimg::X11_attr().display;
7668  bool is_exposed = false, is_mapped = false;
7669  XWindowAttributes attr;
7670  XEvent event;
7671  XMapRaised(dpy,_window);
7672  do { // Wait for the window to be mapped.
7673  XWindowEvent(dpy,_window,StructureNotifyMask | ExposureMask,&event);
7674  switch (event.type) {
7675  case MapNotify : is_mapped = true; break;
7676  case Expose : is_exposed = true; break;
7677  }
7678  } while (!is_exposed || !is_mapped);
7679  do { // Wait for the window to be visible.
7680  XGetWindowAttributes(dpy,_window,&attr);
7681  if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); }
7682  } while (attr.map_state!=IsViewable);
7683  _window_x = attr.x;
7684  _window_y = attr.y;
7685  }
7686 
7687  void _paint(const bool wait_expose=true) {
7688  if (_is_closed || !_image) return;
7689  Display *const dpy = cimg::X11_attr().display;
7690  if (wait_expose) { // Send an expose event sticked to display window to force repaint.
7691  XEvent event;
7692  event.xexpose.type = Expose;
7693  event.xexpose.serial = 0;
7694  event.xexpose.send_event = 1;
7695  event.xexpose.display = dpy;
7696  event.xexpose.window = _window;
7697  event.xexpose.x = 0;
7698  event.xexpose.y = 0;
7699  event.xexpose.width = width();
7700  event.xexpose.height = height();
7701  event.xexpose.count = 0;
7702  XSendEvent(dpy,_window,0,0,&event);
7703  } else { // Repaint directly (may be called from the expose event).
7704  GC gc = DefaultGC(dpy,DefaultScreen(dpy));
7705 #ifdef cimg_use_xshm
7706  if (_shminfo) XShmPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height,1);
7707  else XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height);
7708 #else
7709  XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height);
7710 #endif
7711  }
7712  }
7713 
7714  template<typename T>
7715  void _resize(T pixel_type, const unsigned int ndimx, const unsigned int ndimy, const bool force_redraw) {
7716  Display *const dpy = cimg::X11_attr().display;
7717  cimg::unused(pixel_type);
7718 
7719 #ifdef cimg_use_xshm
7720  if (_shminfo) {
7721  XShmSegmentInfo *const nshminfo = new XShmSegmentInfo;
7722  XImage *const nimage = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),
7723  cimg::X11_attr().nb_bits,ZPixmap,0,nshminfo,ndimx,ndimy);
7724  if (!nimage) { delete nshminfo; return; }
7725  else {
7726  nshminfo->shmid = shmget(IPC_PRIVATE,ndimx*ndimy*sizeof(T),IPC_CREAT | 0777);
7727  if (nshminfo->shmid==-1) { XDestroyImage(nimage); delete nshminfo; return; }
7728  else {
7729  nshminfo->shmaddr = nimage->data = (char*)shmat(nshminfo->shmid,0,0);
7730  if (nshminfo->shmaddr==(char*)-1) {
7731  shmctl(nshminfo->shmid,IPC_RMID,0); XDestroyImage(nimage); delete nshminfo; return;
7732  } else {
7733  nshminfo->readOnly = 0;
7734  cimg::X11_attr().is_shm_enabled = true;
7735  XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm);
7736  XShmAttach(dpy,nshminfo);
7737  XFlush(dpy);
7738  XSetErrorHandler(oldXErrorHandler);
7739  if (!cimg::X11_attr().is_shm_enabled) {
7740  shmdt(nshminfo->shmaddr);
7741  shmctl(nshminfo->shmid,IPC_RMID,0);
7742  XDestroyImage(nimage);
7743  delete nshminfo;
7744  return;
7745  } else {
7746  T *const ndata = (T*)nimage->data;
7747  if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy);
7748  else std::memset(ndata,0,sizeof(T)*ndimx*ndimy);
7749  XShmDetach(dpy,_shminfo);
7750  XDestroyImage(_image);
7751  shmdt(_shminfo->shmaddr);
7752  shmctl(_shminfo->shmid,IPC_RMID,0);
7753  delete _shminfo;
7754  _shminfo = nshminfo;
7755  _image = nimage;
7756  _data = (void*)ndata;
7757  }
7758  }
7759  }
7760  }
7761  } else
7762 #endif
7763  {
7764  T *ndata = (T*)std::malloc(ndimx*ndimy*sizeof(T));
7765  if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy);
7766  else std::memset(ndata,0,sizeof(T)*ndimx*ndimy);
7767  _data = (void*)ndata;
7768  XDestroyImage(_image);
7769  _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),
7770  cimg::X11_attr().nb_bits,ZPixmap,0,(char*)_data,ndimx,ndimy,8,0);
7771  }
7772  }
7773 
7774  void _init_fullscreen() {
7775  if (!_is_fullscreen || _is_closed) return;
7776  Display *const dpy = cimg::X11_attr().display;
7777  _background_window = 0;
7778 
7779 #ifdef cimg_use_xrandr
7780  int foo;
7781  if (XRRQueryExtension(dpy,&foo,&foo)) {
7782  XRRRotations(dpy,DefaultScreen(dpy),&cimg::X11_attr().curr_rotation);
7783  if (!cimg::X11_attr().resolutions) {
7784  cimg::X11_attr().resolutions = XRRSizes(dpy,DefaultScreen(dpy),&foo);
7785  cimg::X11_attr().nb_resolutions = (unsigned int)foo;
7786  }
7787  if (cimg::X11_attr().resolutions) {
7788  cimg::X11_attr().curr_resolution = 0;
7789  for (unsigned int i = 0; i<cimg::X11_attr().nb_resolutions; ++i) {
7790  const unsigned int
7791  nw = (unsigned int)(cimg::X11_attr().resolutions[i].width),
7792  nh = (unsigned int)(cimg::X11_attr().resolutions[i].height);
7793  if (nw>=_width && nh>=_height &&
7794  nw<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width) &&
7795  nh<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height))
7796  cimg::X11_attr().curr_resolution = i;
7797  }
7798  if (cimg::X11_attr().curr_resolution>0) {
7799  XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy));
7800  XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),
7801  cimg::X11_attr().curr_resolution,cimg::X11_attr().curr_rotation,CurrentTime);
7802  XRRFreeScreenConfigInfo(config);
7803  XSync(dpy,0);
7804  }
7805  }
7806  }
7807  if (!cimg::X11_attr().resolutions)
7808  cimg::warn(_cimgdisplay_instance
7809  "init_fullscreen(): Xrandr extension not supported by the X server.",
7810  cimgdisplay_instance);
7811 #endif
7812 
7813  const unsigned int sx = screen_width(), sy = screen_height();
7814  if (sx==_width && sy==_height) return;
7815  XSetWindowAttributes winattr;
7816  winattr.override_redirect = 1;
7817  _background_window = XCreateWindow(dpy,DefaultRootWindow(dpy),0,0,sx,sy,0,0,
7818  InputOutput,CopyFromParent,CWOverrideRedirect,&winattr);
7819  const unsigned long buf_size = (unsigned long)sx*sy*(cimg::X11_attr().nb_bits==8?1:
7820  (cimg::X11_attr().nb_bits==16?2:4));
7821  void *background_data = std::malloc(buf_size);
7822  std::memset(background_data,0,buf_size);
7823  XImage *background_image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,
7824  ZPixmap,0,(char*)background_data,sx,sy,8,0);
7825  XEvent event;
7826  XSelectInput(dpy,_background_window,StructureNotifyMask);
7827  XMapRaised(dpy,_background_window);
7828  do XWindowEvent(dpy,_background_window,StructureNotifyMask,&event);
7829  while (event.type!=MapNotify);
7830  GC gc = DefaultGC(dpy,DefaultScreen(dpy));
7831 #ifdef cimg_use_xshm
7832  if (_shminfo) XShmPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy,0);
7833  else XPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy);
7834 #else
7835  XPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy);
7836 #endif
7837  XWindowAttributes attr;
7838  XGetWindowAttributes(dpy,_background_window,&attr);
7839  while (attr.map_state!=IsViewable) XSync(dpy,0);
7840  XDestroyImage(background_image);
7841  }
7842 
7843  void _desinit_fullscreen() {
7844  if (!_is_fullscreen) return;
7845  Display *const dpy = cimg::X11_attr().display;
7846  XUngrabKeyboard(dpy,CurrentTime);
7847 #ifdef cimg_use_xrandr
7848  if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) {
7849  XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy));
7850  XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),0,cimg::X11_attr().curr_rotation,CurrentTime);
7851  XRRFreeScreenConfigInfo(config);
7852  XSync(dpy,0);
7853  cimg::X11_attr().curr_resolution = 0;
7854  }
7855 #endif
7856  if (_background_window) XDestroyWindow(dpy,_background_window);
7857  _background_window = 0;
7858  _is_fullscreen = false;
7859  }
7860 
7861  static int _assign_xshm(Display *dpy, XErrorEvent *error) {
7862  cimg::unused(dpy,error);
7863  cimg::X11_attr().is_shm_enabled = false;
7864  return 0;
7865  }
7866 
7867  void _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0,
7868  const unsigned int normalization_type=3,
7869  const bool fullscreen_flag=false, const bool closed_flag=false) {
7870  cimg::mutex(14);
7871 
7872  // Allocate space for window title
7873  const char *const nptitle = ptitle?ptitle:"";
7874  const unsigned int s = std::strlen(nptitle) + 1;
7875  char *const tmp_title = s?new char[s]:0;
7876  if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char));
7877 
7878  // Destroy previous display window if existing
7879  if (!is_empty()) assign();
7880 
7881  // Open X11 display and retrieve graphical properties.
7882  Display* &dpy = cimg::X11_attr().display;
7883  if (!dpy) {
7884  dpy = XOpenDisplay(0);
7885  if (!dpy)
7886  throw CImgDisplayException(_cimgdisplay_instance
7887  "assign(): Failed to open X11 display.",
7888  cimgdisplay_instance);
7889 
7890  cimg::X11_attr().nb_bits = DefaultDepth(dpy,DefaultScreen(dpy));
7891  if (cimg::X11_attr().nb_bits!=8 && cimg::X11_attr().nb_bits!=16 &&
7892  cimg::X11_attr().nb_bits!=24 && cimg::X11_attr().nb_bits!=32)
7893  throw CImgDisplayException(_cimgdisplay_instance
7894  "assign(): Invalid %u bits screen mode detected "
7895  "(only 8, 16, 24 and 32 bits modes are managed).",
7896  cimgdisplay_instance,
7897  cimg::X11_attr().nb_bits);
7898  XVisualInfo vtemplate;
7899  vtemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,DefaultScreen(dpy)));
7900  int nb_visuals;
7901  XVisualInfo *vinfo = XGetVisualInfo(dpy,VisualIDMask,&vtemplate,&nb_visuals);
7902  if (vinfo && vinfo->red_mask<vinfo->blue_mask) cimg::X11_attr().is_blue_first = true;
7903  cimg::X11_attr().byte_order = ImageByteOrder(dpy);
7904  XFree(vinfo);
7905 
7906  XLockDisplay(dpy);
7907  cimg::X11_attr().events_thread = new pthread_t;
7908  pthread_create(cimg::X11_attr().events_thread,0,_events_thread,0);
7909  } else XLockDisplay(dpy);
7910 
7911  // Set display variables.
7912  _width = cimg::min(dimw,(unsigned int)screen_width());
7913  _height = cimg::min(dimh,(unsigned int)screen_height());
7914  _normalization = normalization_type<4?normalization_type:3;
7915  _is_fullscreen = fullscreen_flag;
7916  _window_x = _window_y = 0;
7917  _is_closed = closed_flag;
7918  _title = tmp_title;
7919  flush();
7920 
7921  // Create X11 window (and LUT, if 8bits display)
7922  if (_is_fullscreen) {
7923  if (!_is_closed) _init_fullscreen();
7924  const unsigned int sx = screen_width(), sy = screen_height();
7925  XSetWindowAttributes winattr;
7926  winattr.override_redirect = 1;
7927  _window = XCreateWindow(dpy,DefaultRootWindow(dpy),(sx-_width)/2,(sy-_height)/2,_width,_height,0,0,
7928  InputOutput,CopyFromParent,CWOverrideRedirect,&winattr);
7929  } else
7930  _window = XCreateSimpleWindow(dpy,DefaultRootWindow(dpy),0,0,_width,_height,0,0L,0L);
7931 
7932  XSelectInput(dpy,_window,
7933  ExposureMask | StructureNotifyMask | ButtonPressMask | KeyPressMask | PointerMotionMask |
7934  EnterWindowMask | LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask);
7935 
7936  XStoreName(dpy,_window,_title?_title:" ");
7937  if (cimg::X11_attr().nb_bits==8) {
7938  _colormap = XCreateColormap(dpy,_window,DefaultVisual(dpy,DefaultScreen(dpy)),AllocAll);
7939  _set_colormap(_colormap,3);
7940  XSetWindowColormap(dpy,_window,_colormap);
7941  }
7942 
7943  static const char *const _window_class = cimg_appname;
7944  XClassHint *const window_class = XAllocClassHint();
7945  window_class->res_name = (char*)_window_class;
7946  window_class->res_class = (char*)_window_class;
7947  XSetClassHint(dpy,_window,window_class);
7948  XFree(window_class);
7949 
7950  _window_width = _width;
7951  _window_height = _height;
7952 
7953  // Create XImage
7954 #ifdef cimg_use_xshm
7955  _shminfo = 0;
7956  if (XShmQueryExtension(dpy)) {
7957  _shminfo = new XShmSegmentInfo;
7958  _image = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,
7959  ZPixmap,0,_shminfo,_width,_height);
7960  if (!_image) { delete _shminfo; _shminfo = 0; }
7961  else {
7962  _shminfo->shmid = shmget(IPC_PRIVATE,_image->bytes_per_line*_image->height,IPC_CREAT|0777);
7963  if (_shminfo->shmid==-1) { XDestroyImage(_image); delete _shminfo; _shminfo = 0; }
7964  else {
7965  _shminfo->shmaddr = _image->data = (char*)(_data = shmat(_shminfo->shmid,0,0));
7966  if (_shminfo->shmaddr==(char*)-1) {
7967  shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image); delete _shminfo; _shminfo = 0;
7968  } else {
7969  _shminfo->readOnly = 0;
7970  cimg::X11_attr().is_shm_enabled = true;
7971  XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm);
7972  XShmAttach(dpy,_shminfo);
7973  XSync(dpy,0);
7974  XSetErrorHandler(oldXErrorHandler);
7975  if (!cimg::X11_attr().is_shm_enabled) {
7976  shmdt(_shminfo->shmaddr); shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image);
7977  delete _shminfo; _shminfo = 0;
7978  }
7979  }
7980  }
7981  }
7982  }
7983  if (!_shminfo)
7984 #endif
7985  {
7986  const unsigned long buf_size = (unsigned long)_width*_height*(cimg::X11_attr().nb_bits==8?1:
7987  (cimg::X11_attr().nb_bits==16?2:4));
7988  _data = std::malloc(buf_size);
7989  _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,
7990  ZPixmap,0,(char*)_data,_width,_height,8,0);
7991  }
7992 
7993  _wm_window_atom = XInternAtom(dpy,"WM_DELETE_WINDOW",0);
7994  _wm_protocol_atom = XInternAtom(dpy,"WM_PROTOCOLS",0);
7995  XSetWMProtocols(dpy,_window,&_wm_window_atom,1);
7996 
7997  if (_is_fullscreen) XGrabKeyboard(dpy,_window,1,GrabModeAsync,GrabModeAsync,CurrentTime);
7998  cimg::X11_attr().wins[cimg::X11_attr().nb_wins++]=this;
7999  if (!_is_closed) _map_window(); else { _window_x = _window_y = cimg::type<int>::min(); }
8000  XUnlockDisplay(dpy);
8001  cimg::mutex(14,0);
8002  }
8003 
8004  CImgDisplay& assign() {
8005  if (is_empty()) return flush();
8006  Display *const dpy = cimg::X11_attr().display;
8007  XLockDisplay(dpy);
8008 
8009  // Remove display window from event thread list.
8010  unsigned int i;
8011  for (i = 0; i<cimg::X11_attr().nb_wins && cimg::X11_attr().wins[i]!=this; ++i) {}
8012  for (; i<cimg::X11_attr().nb_wins-1; ++i) cimg::X11_attr().wins[i] = cimg::X11_attr().wins[i+1];
8013  --cimg::X11_attr().nb_wins;
8014 
8015  // Destroy window, image, colormap and title.
8016  if (_is_fullscreen && !_is_closed) _desinit_fullscreen();
8017  XDestroyWindow(dpy,_window);
8018  _window = 0;
8019 #ifdef cimg_use_xshm
8020  if (_shminfo) {
8021  XShmDetach(dpy,_shminfo);
8022  XDestroyImage(_image);
8023  shmdt(_shminfo->shmaddr);
8024  shmctl(_shminfo->shmid,IPC_RMID,0);
8025  delete _shminfo;
8026  _shminfo = 0;
8027  } else
8028 #endif
8029  XDestroyImage(_image);
8030  _data = 0; _image = 0;
8031  if (cimg::X11_attr().nb_bits==8) XFreeColormap(dpy,_colormap);
8032  _colormap = 0;
8033  XSync(dpy,0);
8034 
8035  // Reset display variables.
8036  delete[] _title;
8037  _width = _height = _normalization = _window_width = _window_height = 0;
8038  _window_x = _window_y = 0;
8039  _is_fullscreen = false;
8040  _is_closed = true;
8041  _min = _max = 0;
8042  _title = 0;
8043  flush();
8044 
8045  XUnlockDisplay(dpy);
8046  return *this;
8047  }
8048 
8049  CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0,
8050  const unsigned int normalization_type=3,
8051  const bool fullscreen_flag=false, const bool closed_flag=false) {
8052  if (!dimw || !dimh) return assign();
8053  _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag);
8054  _min = _max = 0;
8055  std::memset(_data,0,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char):
8056  (cimg::X11_attr().nb_bits==16?sizeof(unsigned short):sizeof(unsigned int)))*
8057  (unsigned long)_width*_height);
8058  return paint();
8059  }
8060 
8061  template<typename T>
8062  CImgDisplay& assign(const CImg<T>& img, const char *const title=0,
8063  const unsigned int normalization_type=3,
8064  const bool fullscreen_flag=false, const bool closed_flag=false) {
8065  if (!img) return assign();
8066  CImg<T> tmp;
8067  const CImg<T>& nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width-1)/2,
8068  (img._height-1)/2,
8069  (img._depth-1)/2));
8070  _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
8071  if (_normalization==2) _min = (float)nimg.min_max(_max);
8072  return render(nimg).paint();
8073  }
8074 
8075  template<typename T>
8076  CImgDisplay& assign(const CImgList<T>& list, const char *const title=0,
8077  const unsigned int normalization_type=3,
8078  const bool fullscreen_flag=false, const bool closed_flag=false) {
8079  if (!list) return assign();
8080  CImg<T> tmp;
8081  const CImg<T> img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width-1)/2,
8082  (img._height-1)/2,
8083  (img._depth-1)/2));
8084  _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
8085  if (_normalization==2) _min = (float)nimg.min_max(_max);
8086  return render(nimg).paint();
8087  }
8088 
8089  CImgDisplay& assign(const CImgDisplay& disp) {
8090  if (!disp) return assign();
8091  _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed);
8092  std::memcpy(_data,disp._data,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char):
8093  cimg::X11_attr().nb_bits==16?sizeof(unsigned short):
8094  sizeof(unsigned int))*(unsigned long)_width*_height);
8095  return paint();
8096  }
8097 
8098  CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) {
8099  if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign();
8100  if (is_empty()) return assign(nwidth,nheight);
8101  Display *const dpy = cimg::X11_attr().display;
8102  const unsigned int
8103  tmpdimx = (nwidth>0)?nwidth:(-nwidth*width()/100),
8104  tmpdimy = (nheight>0)?nheight:(-nheight*height()/100),
8105  dimx = tmpdimx?tmpdimx:1,
8106  dimy = tmpdimy?tmpdimy:1;
8107  XLockDisplay(dpy);
8108  if (_window_width!=dimx || _window_height!=dimy) {
8109  XWindowAttributes attr;
8110  for (unsigned int i = 0; i<10; ++i) {
8111  XResizeWindow(dpy,_window,dimx,dimy);
8112  XGetWindowAttributes(dpy,_window,&attr);
8113  if (attr.width==(int)dimx && attr.height==(int)dimy) break;
8114  cimg::wait(5);
8115  }
8116  }
8117  if (_width!=dimx || _height!=dimy) switch (cimg::X11_attr().nb_bits) {
8118  case 8 : { unsigned char pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break;
8119  case 16 : { unsigned short pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break;
8120  default : { unsigned int pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); }
8121  }
8122  _window_width = _width = dimx; _window_height = _height = dimy;
8123  _is_resized = false;
8124  XUnlockDisplay(dpy);
8125  if (_is_fullscreen) move((screen_width()-_width)/2,(screen_height()-_height)/2);
8126  if (force_redraw) return paint();
8127  return *this;
8128  }
8129 
8130  CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
8131  if (is_empty()) return *this;
8132  if (force_redraw) {
8133  const unsigned long buf_size = (unsigned long)_width*_height*
8134  (cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4));
8135  void *image_data = std::malloc(buf_size);
8136  std::memcpy(image_data,_data,buf_size);
8137  assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
8138  std::memcpy(_data,image_data,buf_size);
8139  std::free(image_data);
8140  return paint();
8141  }
8142  return assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
8143  }
8144 
8145  CImgDisplay& show() {
8146  if (is_empty() || !_is_closed) return *this;
8147  Display *const dpy = cimg::X11_attr().display;
8148  XLockDisplay(dpy);
8149  if (_is_fullscreen) _init_fullscreen();
8150  _map_window();
8151  _is_closed = false;
8152  XUnlockDisplay(dpy);
8153  return paint();
8154  }
8155 
8156  CImgDisplay& close() {
8157  if (is_empty() || _is_closed) return *this;
8158  Display *const dpy = cimg::X11_attr().display;
8159  XLockDisplay(dpy);
8160  if (_is_fullscreen) _desinit_fullscreen();
8161  XUnmapWindow(dpy,_window);
8162  _window_x = _window_y = -1;
8163  _is_closed = true;
8164  XUnlockDisplay(dpy);
8165  return *this;
8166  }
8167 
8168  CImgDisplay& move(const int posx, const int posy) {
8169  if (is_empty()) return *this;
8170  show();
8171  Display *const dpy = cimg::X11_attr().display;
8172  XLockDisplay(dpy);
8173  XMoveWindow(dpy,_window,posx,posy);
8174  _window_x = posx; _window_y = posy;
8175  _is_moved = false;
8176  XUnlockDisplay(dpy);
8177  return paint();
8178  }
8179 
8180  CImgDisplay& show_mouse() {
8181  if (is_empty()) return *this;
8182  Display *const dpy = cimg::X11_attr().display;
8183  XLockDisplay(dpy);
8184  XUndefineCursor(dpy,_window);
8185  XUnlockDisplay(dpy);
8186  return *this;
8187  }
8188 
8189  CImgDisplay& hide_mouse() {
8190  if (is_empty()) return *this;
8191  Display *const dpy = cimg::X11_attr().display;
8192  XLockDisplay(dpy);
8193  const char pix_data[8] = { 0 };
8194  XColor col;
8195  col.red = col.green = col.blue = 0;
8196  Pixmap pix = XCreateBitmapFromData(dpy,_window,pix_data,8,8);
8197  Cursor cur = XCreatePixmapCursor(dpy,pix,pix,&col,&col,0,0);
8198  XFreePixmap(dpy,pix);
8199  XDefineCursor(dpy,_window,cur);
8200  XUnlockDisplay(dpy);
8201  return *this;
8202  }
8203 
8204  CImgDisplay& set_mouse(const int posx, const int posy) {
8205  if (is_empty() || _is_closed) return *this;
8206  Display *const dpy = cimg::X11_attr().display;
8207  XLockDisplay(dpy);
8208  XWarpPointer(dpy,0L,_window,0,0,0,0,posx,posy);
8209  _mouse_x = posx; _mouse_y = posy;
8210  _is_moved = false;
8211  XSync(dpy,0);
8212  XUnlockDisplay(dpy);
8213  return *this;
8214  }
8215 
8216  CImgDisplay& set_title(const char *const format, ...) {
8217  if (is_empty()) return *this;
8218  char tmp[1024] = { 0 };
8219  va_list ap;
8220  va_start(ap, format);
8221  cimg_vsnprintf(tmp,sizeof(tmp),format,ap);
8222  va_end(ap);
8223  if (!std::strcmp(_title,tmp)) return *this;
8224  delete[] _title;
8225  const unsigned int s = std::strlen(tmp) + 1;
8226  _title = new char[s];
8227  std::memcpy(_title,tmp,s*sizeof(char));
8228  Display *const dpy = cimg::X11_attr().display;
8229  XLockDisplay(dpy);
8230  XStoreName(dpy,_window,tmp);
8231  XUnlockDisplay(dpy);
8232  return *this;
8233  }
8234 
8235  template<typename T>
8236  CImgDisplay& display(const CImg<T>& img) {
8237  if (!img)
8238  throw CImgArgumentException(_cimgdisplay_instance
8239  "display(): Empty specified image.",
8240  cimgdisplay_instance);
8241  if (is_empty()) return assign(img);
8242  return render(img).paint(false);
8243  }
8244 
8245  CImgDisplay& paint(const bool wait_expose=true) {
8246  if (is_empty()) return *this;
8247  Display *const dpy = cimg::X11_attr().display;
8248  XLockDisplay(dpy);
8249  _paint(wait_expose);
8250  XUnlockDisplay(dpy);
8251  return *this;
8252  }
8253 
8254  template<typename T>
8255  CImgDisplay& render(const CImg<T>& img, const bool flag8=false) {
8256  if (!img)
8257  throw CImgArgumentException(_cimgdisplay_instance
8258  "render(): Empty specified image.",
8259  cimgdisplay_instance);
8260  if (is_empty()) return *this;
8261  if (img._depth!=1) return render(img.get_projections2d((img._width-1)/2,(img._height-1)/2,(img._depth-1)/2));
8262  if (cimg::X11_attr().nb_bits==8 && (img._width!=_width || img._height!=_height))
8263  return render(img.get_resize(_width,_height,1,-100,1));
8264  if (cimg::X11_attr().nb_bits==8 && !flag8 && img._spectrum==3) {
8265  static const CImg<typename CImg<T>::ucharT> default_colormap = CImg<typename CImg<T>::ucharT>::default_LUT256();
8266  return render(img.get_index(default_colormap,1,false));
8267  }
8268 
8269  Display *const dpy = cimg::X11_attr().display;
8270  const T
8271  *data1 = img._data,
8272  *data2 = (img._spectrum>1)?img.data(0,0,0,1):data1,
8273  *data3 = (img._spectrum>2)?img.data(0,0,0,2):data1;
8274 
8275  if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3);
8276  XLockDisplay(dpy);
8277 
8278  if (!_normalization || (_normalization==3 && cimg::type<T>::string()==cimg::type<unsigned char>::string())) {
8279  _min = _max = 0;
8280  switch (cimg::X11_attr().nb_bits) {
8281  case 8 : { // 256 colormap, no normalization
8282  _set_colormap(_colormap,img._spectrum);
8283  unsigned char *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data:new unsigned char[(unsigned long)img._width*img._height];
8284  unsigned char *ptrd = (unsigned char*)ndata;
8285  switch (img._spectrum) {
8286  case 1 : for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) (*ptrd++) = (unsigned char)*(data1++);
8287  break;
8288  case 2 : for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8289  const unsigned char R = (unsigned char)*(data1++), G = (unsigned char)*(data2++);
8290  (*ptrd++) = (R&0xf0) | (G>>4);
8291  } break;
8292  default : for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8293  const unsigned char R = (unsigned char)*(data1++), G = (unsigned char)*(data2++), B = (unsigned char)*(data3++);
8294  (*ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6);
8295  }
8296  }
8297  if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height); delete[] ndata; }
8298  } break;
8299  case 16 : { // 16 bits colors, no normalization
8300  unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data:new unsigned short[(unsigned long)img._width*img._height];
8301  unsigned char *ptrd = (unsigned char*)ndata;
8302  const unsigned int M = 248;
8303  switch (img._spectrum) {
8304  case 1 :
8305  if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8306  const unsigned char val = (unsigned char)*(data1++), G = val>>2;
8307  *(ptrd++) = (val&M) | (G>>3);
8308  *(ptrd++) = (G<<5) | (G>>1);
8309  } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8310  const unsigned char val = (unsigned char)*(data1++), G = val>>2;
8311  *(ptrd++) = (G<<5) | (G>>1);
8312  *(ptrd++) = (val&M) | (G>>3);
8313  }
8314  break;
8315  case 2 :
8316  if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8317  const unsigned char G = (unsigned char)*(data2++)>>2;
8318  *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3);
8319  *(ptrd++) = (G<<5);
8320  } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8321  const unsigned char G = (unsigned char)*(data2++)>>2;
8322  *(ptrd++) = (G<<5);
8323  *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3);
8324  }
8325  break;
8326  default :
8327  if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8328  const unsigned char G = (unsigned char)*(data2++)>>2;
8329  *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3);
8330  *(ptrd++) = (G<<5) | ((unsigned char)*(data3++)>>3);
8331  } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8332  const unsigned char G = (unsigned char)*(data2++)>>2;
8333  *(ptrd++) = (G<<5) | ((unsigned char)*(data3++)>>3);
8334  *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3);
8335  }
8336  }
8337  if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height); delete[] ndata; }
8338  } break;
8339  default : { // 24 bits colors, no normalization
8340  unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data:new unsigned int[(unsigned long)img._width*img._height];
8341  if (sizeof(int)==4) { // 32 bits int uses optimized version
8342  unsigned int *ptrd = ndata;
8343  switch (img._spectrum) {
8344  case 1 :
8345  if (cimg::X11_attr().byte_order==cimg::endianness())
8346  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8347  const unsigned char val = (unsigned char)*(data1++);
8348  *(ptrd++) = (val<<16) | (val<<8) | val;
8349  }
8350  else
8351  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8352  const unsigned char val = (unsigned char)*(data1++);
8353  *(ptrd++) = (val<<16) | (val<<8) | val;
8354  }
8355  break;
8356  case 2 :
8357  if (cimg::X11_attr().byte_order==cimg::endianness())
8358  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
8359  *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8);
8360  else
8361  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
8362  *(ptrd++) = ((unsigned char)*(data2++)<<16) | ((unsigned char)*(data1++)<<8);
8363  break;
8364  default :
8365  if (cimg::X11_attr().byte_order==cimg::endianness())
8366  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
8367  *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) | (unsigned char)*(data3++);
8368  else
8369  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
8370  *(ptrd++) = ((unsigned char)*(data3++)<<24) | ((unsigned char)*(data2++)<<16) | ((unsigned char)*(data1++)<<8);
8371  }
8372  } else {
8373  unsigned char *ptrd = (unsigned char*)ndata;
8374  switch (img._spectrum) {
8375  case 1 :
8376  if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8377  *(ptrd++) = 0;
8378  *(ptrd++) = (unsigned char)*(data1++);
8379  *(ptrd++) = 0;
8380  *(ptrd++) = 0;
8381  } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8382  *(ptrd++) = 0;
8383  *(ptrd++) = 0;
8384  *(ptrd++) = (unsigned char)*(data1++);
8385  *(ptrd++) = 0;
8386  }
8387  break;
8388  case 2 :
8389  if (cimg::X11_attr().byte_order) cimg::swap(data1,data2);
8390  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8391  *(ptrd++) = 0;
8392  *(ptrd++) = (unsigned char)*(data2++);
8393  *(ptrd++) = (unsigned char)*(data1++);
8394  *(ptrd++) = 0;
8395  }
8396  break;
8397  default :
8398  if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8399  *(ptrd++) = 0;
8400  *(ptrd++) = (unsigned char)*(data1++);
8401  *(ptrd++) = (unsigned char)*(data2++);
8402  *(ptrd++) = (unsigned char)*(data3++);
8403  } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8404  *(ptrd++) = (unsigned char)*(data3++);
8405  *(ptrd++) = (unsigned char)*(data2++);
8406  *(ptrd++) = (unsigned char)*(data1++);
8407  *(ptrd++) = 0;
8408  }
8409  }
8410  }
8411  if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height); delete[] ndata; }
8412  }
8413  }
8414  } else {
8415  if (_normalization==3) {
8416  if (cimg::type<T>::is_float()) _min = (float)img.min_max(_max);
8417  else { _min = (float)cimg::type<T>::min(); _max = (float)cimg::type<T>::max(); }
8418  } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max);
8419  const float delta = _max - _min, mm = 255/(delta?delta:1.0f);
8420  switch (cimg::X11_attr().nb_bits) {
8421  case 8 : { // 256 colormap, with normalization
8422  _set_colormap(_colormap,img._spectrum);
8423  unsigned char *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data:new unsigned char[(unsigned long)img._width*img._height];
8424  unsigned char *ptrd = (unsigned char*)ndata;
8425  switch (img._spectrum) {
8426  case 1 : for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8427  const unsigned char R = (unsigned char)((*(data1++)-_min)*mm);
8428  *(ptrd++) = R;
8429  } break;
8430  case 2 : for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8431  const unsigned char
8432  R = (unsigned char)((*(data1++)-_min)*mm),
8433  G = (unsigned char)((*(data2++)-_min)*mm);
8434  (*ptrd++) = (R&0xf0) | (G>>4);
8435  } break;
8436  default :
8437  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8438  const unsigned char
8439  R = (unsigned char)((*(data1++)-_min)*mm),
8440  G = (unsigned char)((*(data2++)-_min)*mm),
8441  B = (unsigned char)((*(data3++)-_min)*mm);
8442  *(ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6);
8443  }
8444  }
8445  if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height); delete[] ndata; }
8446  } break;
8447  case 16 : { // 16 bits colors, with normalization
8448  unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data:new unsigned short[(unsigned long)img._width*img._height];
8449  unsigned char *ptrd = (unsigned char*)ndata;
8450  const unsigned int M = 248;
8451  switch (img._spectrum) {
8452  case 1 :
8453  if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8454  const unsigned char val = (unsigned char)((*(data1++)-_min)*mm), G = val>>2;
8455  *(ptrd++) = (val&M) | (G>>3);
8456  *(ptrd++) = (G<<5) | (val>>3);
8457  } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8458  const unsigned char val = (unsigned char)((*(data1++)-_min)*mm), G = val>>2;
8459  *(ptrd++) = (G<<5) | (val>>3);
8460  *(ptrd++) = (val&M) | (G>>3);
8461  }
8462  break;
8463  case 2 :
8464  if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8465  const unsigned char G = (unsigned char)((*(data2++)-_min)*mm)>>2;
8466  *(ptrd++) = ((unsigned char)((*(data1++)-_min)*mm)&M) | (G>>3);
8467  *(ptrd++) = (G<<5);
8468  } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8469  const unsigned char G = (unsigned char)((*(data2++)-_min)*mm)>>2;
8470  *(ptrd++) = (G<<5);
8471  *(ptrd++) = ((unsigned char)((*(data1++)-_min)*mm)&M) | (G>>3);
8472  }
8473  break;
8474  default :
8475  if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8476  const unsigned char G = (unsigned char)((*(data2++)-_min)*mm)>>2;
8477  *(ptrd++) = ((unsigned char)((*(data1++)-_min)*mm)&M) | (G>>3);
8478  *(ptrd++) = (G<<5) | ((unsigned char)((*(data3++)-_min)*mm)>>3);
8479  } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8480  const unsigned char G = (unsigned char)((*(data2++)-_min)*mm)>>2;
8481  *(ptrd++) = (G<<5) | ((unsigned char)((*(data3++)-_min)*mm)>>3);
8482  *(ptrd++) = ((unsigned char)((*(data1++)-_min)*mm)&M) | (G>>3);
8483  }
8484  }
8485  if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height); delete[] ndata; }
8486  } break;
8487  default : { // 24 bits colors, with normalization
8488  unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data:new unsigned int[(unsigned long)img._width*img._height];
8489  if (sizeof(int)==4) { // 32 bits int uses optimized version
8490  unsigned int *ptrd = ndata;
8491  switch (img._spectrum) {
8492  case 1 :
8493  if (cimg::X11_attr().byte_order==cimg::endianness())
8494  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8495  const unsigned char val = (unsigned char)((*(data1++)-_min)*mm);
8496  *(ptrd++) = (val<<16) | (val<<8) | val;
8497  }
8498  else
8499  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8500  const unsigned char val = (unsigned char)((*(data1++)-_min)*mm);
8501  *(ptrd++) = (val<<24) | (val<<16) | (val<<8);
8502  }
8503  break;
8504  case 2 :
8505  if (cimg::X11_attr().byte_order==cimg::endianness())
8506  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
8507  *(ptrd++) =
8508  ((unsigned char)((*(data1++)-_min)*mm)<<16) |
8509  ((unsigned char)((*(data2++)-_min)*mm)<<8);
8510  else
8511  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
8512  *(ptrd++) =
8513  ((unsigned char)((*(data2++)-_min)*mm)<<16) |
8514  ((unsigned char)((*(data1++)-_min)*mm)<<8);
8515  break;
8516  default :
8517  if (cimg::X11_attr().byte_order==cimg::endianness())
8518  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
8519  *(ptrd++) =
8520  ((unsigned char)((*(data1++)-_min)*mm)<<16) |
8521  ((unsigned char)((*(data2++)-_min)*mm)<<8) |
8522  (unsigned char)((*(data3++)-_min)*mm);
8523  else
8524  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
8525  *(ptrd++) =
8526  ((unsigned char)((*(data3++)-_min)*mm)<<24) |
8527  ((unsigned char)((*(data2++)-_min)*mm)<<16) |
8528  ((unsigned char)((*(data1++)-_min)*mm)<<8);
8529  }
8530  } else {
8531  unsigned char *ptrd = (unsigned char*)ndata;
8532  switch (img._spectrum) {
8533  case 1 :
8534  if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8535  const unsigned char val = (unsigned char)((*(data1++)-_min)*mm);
8536  (*ptrd++) = 0;
8537  (*ptrd++) = val;
8538  (*ptrd++) = val;
8539  (*ptrd++) = val;
8540  } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8541  const unsigned char val = (unsigned char)((*(data1++)-_min)*mm);
8542  (*ptrd++) = val;
8543  (*ptrd++) = val;
8544  (*ptrd++) = val;
8545  (*ptrd++) = 0;
8546  }
8547  break;
8548  case 2 :
8549  if (cimg::X11_attr().byte_order) cimg::swap(data1,data2);
8550  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8551  (*ptrd++) = 0;
8552  (*ptrd++) = (unsigned char)((*(data2++)-_min)*mm);
8553  (*ptrd++) = (unsigned char)((*(data1++)-_min)*mm);
8554  (*ptrd++) = 0;
8555  }
8556  break;
8557  default :
8558  if (cimg::X11_attr().byte_order)
8559  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8560  (*ptrd++) = 0;
8561  (*ptrd++) = (unsigned char)((*(data1++)-_min)*mm);
8562  (*ptrd++) = (unsigned char)((*(data2++)-_min)*mm);
8563  (*ptrd++) = (unsigned char)((*(data3++)-_min)*mm);
8564  } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8565  (*ptrd++) = (unsigned char)((*(data3++)-_min)*mm);
8566  (*ptrd++) = (unsigned char)((*(data2++)-_min)*mm);
8567  (*ptrd++) = (unsigned char)((*(data1++)-_min)*mm);
8568  (*ptrd++) = 0;
8569  }
8570  }
8571  }
8572  if (ndata!=_data) {
8573  _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height); delete[] ndata;
8574  }
8575  }
8576  }
8577  }
8578  XUnlockDisplay(dpy);
8579  return *this;
8580  }
8581 
8582  template<typename T>
8583  const CImgDisplay& snapshot(CImg<T>& img) const {
8584  if (is_empty()) { img.assign(); return *this; }
8585  const unsigned char *ptrs = (unsigned char*)_data;
8586  img.assign(_width,_height,1,3);
8587  T
8588  *data1 = img.data(0,0,0,0),
8589  *data2 = img.data(0,0,0,1),
8590  *data3 = img.data(0,0,0,2);
8591  if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3);
8592  switch (cimg::X11_attr().nb_bits) {
8593  case 8 : {
8594  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8595  const unsigned char val = *(ptrs++);
8596  *(data1++) = (T)(val&0xe0);
8597  *(data2++) = (T)((val&0x1c)<<3);
8598  *(data3++) = (T)(val<<6);
8599  }
8600  } break;
8601  case 16 : {
8602  if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8603  const unsigned char val0 = *(ptrs++), val1 = *(ptrs++);
8604  *(data1++) = (T)(val0&0xf8);
8605  *(data2++) = (T)((val0<<5) | ((val1&0xe0)>>5));
8606  *(data3++) = (T)(val1<<3);
8607  } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8608  const unsigned short val0 = *(ptrs++), val1 = *(ptrs++);
8609  *(data1++) = (T)(val1&0xf8);
8610  *(data2++) = (T)((val1<<5) | ((val0&0xe0)>>5));
8611  *(data3++) = (T)(val0<<3);
8612  }
8613  } break;
8614  default : {
8615  if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8616  ++ptrs;
8617  *(data1++) = (T)*(ptrs++);
8618  *(data2++) = (T)*(ptrs++);
8619  *(data3++) = (T)*(ptrs++);
8620  } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8621  *(data3++) = (T)*(ptrs++);
8622  *(data2++) = (T)*(ptrs++);
8623  *(data1++) = (T)*(ptrs++);
8624  ++ptrs;
8625  }
8626  }
8627  }
8628  return *this;
8629  }
8630 
8631  // Windows-based implementation.
8632  //-------------------------------
8633 #elif cimg_display==2
8634 
8635  bool _is_mouse_tracked, _is_cursor_visible;
8636  HANDLE _thread, _is_created, _mutex;
8637  HWND _window, _background_window;
8638  CLIENTCREATESTRUCT _ccs;
8639  unsigned int *_data;
8640  DEVMODE _curr_mode;
8641  BITMAPINFO _bmi;
8642  HDC _hdc;
8643 
8644  static int screen_width() {
8645  DEVMODE mode;
8646  mode.dmSize = sizeof(DEVMODE);
8647  mode.dmDriverExtra = 0;
8648  EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode);
8649  return mode.dmPelsWidth;
8650  }
8651 
8652  static int screen_height() {
8653  DEVMODE mode;
8654  mode.dmSize = sizeof(DEVMODE);
8655  mode.dmDriverExtra = 0;
8656  EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode);
8657  return mode.dmPelsHeight;
8658  }
8659 
8660  static void wait_all() {
8661  WaitForSingleObject(cimg::Win32_attr().wait_event,INFINITE);
8662  }
8663 
8664  static LRESULT APIENTRY _handle_events(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) {
8665 #ifdef _WIN64
8666  CImgDisplay *const disp = (CImgDisplay*)GetWindowLongPtr(window,GWLP_USERDATA);
8667 #else
8668  CImgDisplay *const disp = (CImgDisplay*)GetWindowLong(window,GWL_USERDATA);
8669 #endif
8670  MSG st_msg;
8671  switch (msg) {
8672  case WM_CLOSE :
8673  disp->_mouse_x = disp->_mouse_y = -1;
8674  disp->_window_x = disp->_window_y = 0;
8675  disp->set_button().set_key(0).set_key(0,false)._is_closed = true;
8676  ReleaseMutex(disp->_mutex);
8677  ShowWindow(disp->_window,SW_HIDE);
8678  disp->_is_event = true;
8679  SetEvent(cimg::Win32_attr().wait_event);
8680  return 0;
8681  case WM_SIZE : {
8682  while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {}
8683  WaitForSingleObject(disp->_mutex,INFINITE);
8684  const unsigned int nw = LOWORD(lParam),nh = HIWORD(lParam);
8685  if (nw && nh && (nw!=disp->_width || nh!=disp->_height)) {
8686  disp->_window_width = nw;
8687  disp->_window_height = nh;
8688  disp->_mouse_x = disp->_mouse_y = -1;
8689  disp->_is_resized = disp->_is_event = true;
8690  SetEvent(cimg::Win32_attr().wait_event);
8691  }
8692  ReleaseMutex(disp->_mutex);
8693  } break;
8694  case WM_MOVE : {
8695  while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {}
8696  WaitForSingleObject(disp->_mutex,INFINITE);
8697  const int nx = (int)(short)(LOWORD(lParam)), ny = (int)(short)(HIWORD(lParam));
8698  if (nx!=disp->_window_x || ny!=disp->_window_y) {
8699  disp->_window_x = nx;
8700  disp->_window_y = ny;
8701  disp->_is_moved = disp->_is_event = true;
8702  SetEvent(cimg::Win32_attr().wait_event);
8703  }
8704  ReleaseMutex(disp->_mutex);
8705  } break;
8706  case WM_PAINT :
8707  disp->paint();
8708  if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE)>=0);
8709  break;
8710  case WM_KEYDOWN :
8711  disp->set_key((unsigned int)wParam);
8712  SetEvent(cimg::Win32_attr().wait_event);
8713  break;
8714  case WM_KEYUP :
8715  disp->set_key((unsigned int)wParam,false);
8716  SetEvent(cimg::Win32_attr().wait_event);
8717  break;
8718  case WM_MOUSEMOVE : {
8719  while (PeekMessage(&st_msg,window,WM_MOUSEMOVE,WM_MOUSEMOVE,PM_REMOVE)) {}
8720  disp->_mouse_x = LOWORD(lParam);
8721  disp->_mouse_y = HIWORD(lParam);
8722 #if (_WIN32_WINNT>=0x0400) && !defined(NOTRACKMOUSEEVENT)
8723  if (!disp->_is_mouse_tracked) {
8724  TRACKMOUSEEVENT tme;
8725  tme.cbSize = sizeof(TRACKMOUSEEVENT);
8726  tme.dwFlags = TME_LEAVE;
8727  tme.hwndTrack = disp->_window;
8728  if (TrackMouseEvent(&tme)) disp->_is_mouse_tracked = true;
8729  }
8730 #endif
8731  if (disp->_mouse_x<0 || disp->_mouse_y<0 || disp->_mouse_x>=disp->width() || disp->_mouse_y>=disp->height())
8732  disp->_mouse_x = disp->_mouse_y = -1;
8733  disp->_is_event = true;
8734  SetEvent(cimg::Win32_attr().wait_event);
8735  if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE)>=0);
8736  } break;
8737  case WM_MOUSELEAVE : {
8738  disp->_mouse_x = disp->_mouse_y = -1;
8739  disp->_is_mouse_tracked = false;
8740  while (ShowCursor(TRUE)<0);
8741  } break;
8742  case WM_LBUTTONDOWN :
8743  disp->set_button(1);
8744  SetEvent(cimg::Win32_attr().wait_event);
8745  break;
8746  case WM_RBUTTONDOWN :
8747  disp->set_button(2);
8748  SetEvent(cimg::Win32_attr().wait_event);
8749  break;
8750  case WM_MBUTTONDOWN :
8751  disp->set_button(3);
8752  SetEvent(cimg::Win32_attr().wait_event);
8753  break;
8754  case WM_LBUTTONUP :
8755  disp->set_button(1,false);
8756  SetEvent(cimg::Win32_attr().wait_event);
8757  break;
8758  case WM_RBUTTONUP :
8759  disp->set_button(2,false);
8760  SetEvent(cimg::Win32_attr().wait_event);
8761  break;
8762  case WM_MBUTTONUP :
8763  disp->set_button(3,false);
8764  SetEvent(cimg::Win32_attr().wait_event);
8765  break;
8766  case 0x020A : // WM_MOUSEWHEEL:
8767  disp->set_wheel((int)((short)HIWORD(wParam))/120);
8768  SetEvent(cimg::Win32_attr().wait_event);
8769  }
8770  return DefWindowProc(window,msg,wParam,lParam);
8771  }
8772 
8773  static DWORD WINAPI _events_thread(void* arg) {
8774  CImgDisplay *const disp = (CImgDisplay*)(((void**)arg)[0]);
8775  const char *const title = (const char*)(((void**)arg)[1]);
8776  MSG msg;
8777  delete[] (void**)arg;
8778  disp->_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
8779  disp->_bmi.bmiHeader.biWidth = disp->width();
8780  disp->_bmi.bmiHeader.biHeight = -disp->height();
8781  disp->_bmi.bmiHeader.biPlanes = 1;
8782  disp->_bmi.bmiHeader.biBitCount = 32;
8783  disp->_bmi.bmiHeader.biCompression = BI_RGB;
8784  disp->_bmi.bmiHeader.biSizeImage = 0;
8785  disp->_bmi.bmiHeader.biXPelsPerMeter = 1;
8786  disp->_bmi.bmiHeader.biYPelsPerMeter = 1;
8787  disp->_bmi.bmiHeader.biClrUsed = 0;
8788  disp->_bmi.bmiHeader.biClrImportant = 0;
8789  disp->_data = new unsigned int[(unsigned long)disp->_width*disp->_height];
8790  if (!disp->_is_fullscreen) { // Normal window
8791  RECT rect;
8792  rect.left = rect.top = 0; rect.right = disp->_width-1; rect.bottom = disp->_height-1;
8793  AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
8794  const int
8795  border1 = (rect.right - rect.left + 1 - disp->_width)/2,
8796  border2 = rect.bottom - rect.top + 1 - disp->_height - border1;
8797  disp->_window = CreateWindowA("MDICLIENT",title?title:" ",
8798  WS_OVERLAPPEDWINDOW | (disp->_is_closed?0:WS_VISIBLE), CW_USEDEFAULT,CW_USEDEFAULT,
8799  disp->_width + 2*border1, disp->_height + border1 + border2,
8800  0,0,0,&(disp->_ccs));
8801  if (!disp->_is_closed) {
8802  GetWindowRect(disp->_window,&rect);
8803  disp->_window_x = rect.left + border1;
8804  disp->_window_y = rect.top + border2;
8805  } else disp->_window_x = disp->_window_y = 0;
8806  } else { // Fullscreen window
8807  const unsigned int sx = screen_width(), sy = screen_height();
8808  disp->_window = CreateWindowA("MDICLIENT",title?title:" ",
8809  WS_POPUP | (disp->_is_closed?0:WS_VISIBLE), (sx-disp->_width)/2,
8810  (sy-disp->_height)/2,
8811  disp->_width,disp->_height,0,0,0,&(disp->_ccs));
8812  disp->_window_x = disp->_window_y = 0;
8813  }
8814  SetForegroundWindow(disp->_window);
8815  disp->_hdc = GetDC(disp->_window);
8816  disp->_window_width = disp->_width;
8817  disp->_window_height = disp->_height;
8818  disp->flush();
8819 #ifdef _WIN64
8820  SetWindowLongPtr(disp->_window,GWLP_USERDATA,(LONG_PTR)disp);
8821  SetWindowLongPtr(disp->_window,GWLP_WNDPROC,(LONG_PTR)_handle_events);
8822 #else
8823  SetWindowLong(disp->_window,GWL_USERDATA,(LONG)disp);
8824  SetWindowLong(disp->_window,GWL_WNDPROC,(LONG)_handle_events);
8825 #endif
8826  SetEvent(disp->_is_created);
8827  while (GetMessage(&msg,0,0,0)) DispatchMessage(&msg);
8828  return 0;
8829  }
8830 
8831  CImgDisplay& _update_window_pos() {
8832  if (_is_closed) _window_x = _window_y = -1;
8833  else {
8834  RECT rect;
8835  rect.left = rect.top = 0; rect.right = _width-1; rect.bottom = _height-1;
8836  AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
8837  const int
8838  border1 = (rect.right - rect.left + 1 - _width)/2,
8839  border2 = rect.bottom - rect.top + 1 - _height - border1;
8840  GetWindowRect(_window,&rect);
8841  _window_x = rect.left + border1;
8842  _window_y = rect.top + border2;
8843  }
8844  return *this;
8845  }
8846 
8847  void _init_fullscreen() {
8848  _background_window = 0;
8849  if (!_is_fullscreen || _is_closed) _curr_mode.dmSize = 0;
8850  else {
8851  DEVMODE mode;
8852  unsigned int imode = 0, ibest = 0, bestbpp = 0, bw = ~0U, bh = ~0U;
8853  for (mode.dmSize = sizeof(DEVMODE), mode.dmDriverExtra = 0; EnumDisplaySettings(0,imode,&mode); ++imode) {
8854  const unsigned int nw = mode.dmPelsWidth, nh = mode.dmPelsHeight;
8855  if (nw>=_width && nh>=_height && mode.dmBitsPerPel>=bestbpp && nw<=bw && nh<=bh) {
8856  bestbpp = mode.dmBitsPerPel;
8857  ibest = imode;
8858  bw = nw; bh = nh;
8859  }
8860  }
8861  if (bestbpp) {
8862  _curr_mode.dmSize = sizeof(DEVMODE); _curr_mode.dmDriverExtra = 0;
8863  EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&_curr_mode);
8864  EnumDisplaySettings(0,ibest,&mode);
8865  ChangeDisplaySettings(&mode,0);
8866  } else _curr_mode.dmSize = 0;
8867 
8868  const unsigned int sx = screen_width(), sy = screen_height();
8869  if (sx!=_width || sy!=_height) {
8870  CLIENTCREATESTRUCT background_ccs;
8871  _background_window = CreateWindowA("MDICLIENT","",WS_POPUP | WS_VISIBLE, 0,0,sx,sy,0,0,0,&background_ccs);
8872  SetForegroundWindow(_background_window);
8873  }
8874  }
8875  }
8876 
8877  void _desinit_fullscreen() {
8878  if (!_is_fullscreen) return;
8879  if (_background_window) DestroyWindow(_background_window);
8880  _background_window = 0;
8881  if (_curr_mode.dmSize) ChangeDisplaySettings(&_curr_mode,0);
8882  _is_fullscreen = false;
8883  }
8884 
8885  CImgDisplay& _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0,
8886  const unsigned int normalization_type=3,
8887  const bool fullscreen_flag=false, const bool closed_flag=false) {
8888 
8889  // Allocate space for window title
8890  const char *const nptitle = ptitle?ptitle:"";
8891  const unsigned int s = (unsigned int)std::strlen(nptitle) + 1;
8892  char *const tmp_title = s?new char[s]:0;
8893  if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char));
8894 
8895  // Destroy previous window if existing
8896  if (!is_empty()) assign();
8897 
8898  // Set display variables
8899  _width = cimg::min(dimw,(unsigned int)screen_width());
8900  _height = cimg::min(dimh,(unsigned int)screen_height());
8901  _normalization = normalization_type<4?normalization_type:3;
8902  _is_fullscreen = fullscreen_flag;
8903  _window_x = _window_y = 0;
8904  _is_closed = closed_flag;
8905  _is_cursor_visible = true;
8906  _is_mouse_tracked = false;
8907  _title = tmp_title;
8908  flush();
8909  if (_is_fullscreen) _init_fullscreen();
8910 
8911  // Create event thread
8912  void *const arg = (void*)(new void*[2]);
8913  ((void**)arg)[0] = (void*)this;
8914  ((void**)arg)[1] = (void*)_title;
8915  _mutex = CreateMutex(0,FALSE,0);
8916  _is_created = CreateEvent(0,FALSE,FALSE,0);
8917  _thread = CreateThread(0,0,_events_thread,arg,0,0);
8918  WaitForSingleObject(_is_created,INFINITE);
8919  return *this;
8920  }
8921 
8922  CImgDisplay& assign() {
8923  if (is_empty()) return flush();
8924  DestroyWindow(_window);
8925  TerminateThread(_thread,0);
8926  delete[] _data;
8927  delete[] _title;
8928  _data = 0;
8929  _title = 0;
8930  if (_is_fullscreen) _desinit_fullscreen();
8931  _width = _height = _normalization = _window_width = _window_height = 0;
8932  _window_x = _window_y = 0;
8933  _is_fullscreen = false;
8934  _is_closed = true;
8935  _min = _max = 0;
8936  _title = 0;
8937  flush();
8938  return *this;
8939  }
8940 
8941  CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0,
8942  const unsigned int normalization_type=3,
8943  const bool fullscreen_flag=false, const bool closed_flag=false) {
8944  if (!dimw || !dimh) return assign();
8945  _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag);
8946  _min = _max = 0;
8947  std::memset(_data,0,sizeof(unsigned int)*_width*_height);
8948  return paint();
8949  }
8950 
8951  template<typename T>
8952  CImgDisplay& assign(const CImg<T>& img, const char *const title=0,
8953  const unsigned int normalization_type=3,
8954  const bool fullscreen_flag=false, const bool closed_flag=false) {
8955  if (!img) return assign();
8956  CImg<T> tmp;
8957  const CImg<T>& nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width-1)/2,
8958  (img._height-1)/2,
8959  (img._depth-1)/2));
8960  _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
8961  if (_normalization==2) _min = (float)nimg.min_max(_max);
8962  return display(nimg);
8963  }
8964 
8965  template<typename T>
8966  CImgDisplay& assign(const CImgList<T>& list, const char *const title=0,
8967  const unsigned int normalization_type=3,
8968  const bool fullscreen_flag=false, const bool closed_flag=false) {
8969  if (!list) return assign();
8970  CImg<T> tmp;
8971  const CImg<T> img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width-1)/2,
8972  (img._height-1)/2,
8973  (img._depth-1)/2));
8974  _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
8975  if (_normalization==2) _min = (float)nimg.min_max(_max);
8976  return display(nimg);
8977  }
8978 
8979  CImgDisplay& assign(const CImgDisplay& disp) {
8980  if (!disp) return assign();
8981  _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed);
8982  std::memcpy(_data,disp._data,sizeof(unsigned int)*_width*_height);
8983  return paint();
8984  }
8985 
8986  CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) {
8987  if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign();
8988  if (is_empty()) return assign(nwidth,nheight);
8989  const unsigned int
8990  tmpdimx = (nwidth>0)?nwidth:(-nwidth*_width/100),
8991  tmpdimy = (nheight>0)?nheight:(-nheight*_height/100),
8992  dimx = tmpdimx?tmpdimx:1,
8993  dimy = tmpdimy?tmpdimy:1;
8994  if (_window_width!=dimx || _window_height!=dimy) {
8995  RECT rect; rect.left = rect.top = 0; rect.right = dimx - 1; rect.bottom = dimy - 1;
8996  AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
8997  const int cwidth = rect.right - rect.left + 1, cheight = rect.bottom - rect.top + 1;
8998  SetWindowPos(_window,0,0,0,cwidth,cheight,SWP_NOMOVE | SWP_NOZORDER | SWP_NOCOPYBITS);
8999  }
9000  if (_width!=dimx || _height!=dimy) {
9001  unsigned int *const ndata = new unsigned int[dimx*dimy];
9002  if (force_redraw) _render_resize(_data,_width,_height,ndata,dimx,dimy);
9003  else std::memset(ndata,0x80,sizeof(unsigned int)*dimx*dimy);
9004  delete[] _data;
9005  _data = ndata;
9006  _bmi.bmiHeader.biWidth = dimx;
9007  _bmi.bmiHeader.biHeight = -(int)dimy;
9008  _width = dimx;
9009  _height = dimy;
9010  }
9011  _window_width = dimx; _window_height = dimy;
9012  _is_resized = false;
9013  if (_is_fullscreen) move((screen_width()-_width)/2,(screen_height()-_height)/2);
9014  if (force_redraw) return paint();
9015  return *this;
9016  }
9017 
9018  CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
9019  if (is_empty()) return *this;
9020  if (force_redraw) {
9021  const unsigned long buf_size = _width*_height*4UL;
9022  void *odata = std::malloc(buf_size);
9023  std::memcpy(odata,_data,buf_size);
9024  assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
9025  std::memcpy(_data,odata,buf_size);
9026  std::free(odata);
9027  return paint();
9028  }
9029  return assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
9030  }
9031 
9032  CImgDisplay& show() {
9033  if (is_empty() || !_is_closed) return *this;
9034  _is_closed = false;
9035  if (_is_fullscreen) _init_fullscreen();
9036  ShowWindow(_window,SW_SHOW);
9037  _update_window_pos();
9038  return paint();
9039  }
9040 
9041  CImgDisplay& close() {
9042  if (is_empty() || _is_closed) return *this;
9043  _is_closed = true;
9044  if (_is_fullscreen) _desinit_fullscreen();
9045  ShowWindow(_window,SW_HIDE);
9046  _window_x = _window_y = 0;
9047  return *this;
9048  }
9049 
9050  CImgDisplay& move(const int posx, const int posy) {
9051  if (is_empty()) return *this;
9052  if (!_is_fullscreen) {
9053  RECT rect; rect.left = rect.top = 0; rect.right = _window_width-1; rect.bottom = _window_height-1;
9054  AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
9055  const int border1 = (rect.right-rect.left+1-_width)/2, border2 = rect.bottom-rect.top+1-_height-border1;
9056  SetWindowPos(_window,0,posx-border1,posy-border2,0,0,SWP_NOSIZE | SWP_NOZORDER);
9057  } else SetWindowPos(_window,0,posx,posy,0,0,SWP_NOSIZE | SWP_NOZORDER);
9058  _window_x = posx;
9059  _window_y = posy;
9060  _is_moved = false;
9061  return show();
9062  }
9063 
9064  CImgDisplay& show_mouse() {
9065  if (is_empty()) return *this;
9066  _is_cursor_visible = true;
9067  return *this;
9068  }
9069 
9070  CImgDisplay& hide_mouse() {
9071  if (is_empty()) return *this;
9072  _is_cursor_visible = false;
9073  return *this;
9074  }
9075 
9076  CImgDisplay& set_mouse(const int posx, const int posy) {
9077  if (_is_closed || posx<0 || posy<0) return *this;
9078  _update_window_pos();
9079  const int res = (int)SetCursorPos(_window_x + posx,_window_y + posy);
9080  if (res) { _mouse_x = posx; _mouse_y = posy; }
9081  return *this;
9082  }
9083 
9084  CImgDisplay& set_title(const char *const format, ...) {
9085  if (is_empty()) return *this;
9086  char tmp[1024] = { 0 };
9087  va_list ap;
9088  va_start(ap, format);
9089  cimg_vsnprintf(tmp,sizeof(tmp),format,ap);
9090  va_end(ap);
9091  if (!std::strcmp(_title,tmp)) return *this;
9092  delete[] _title;
9093  const unsigned int s = (unsigned int)std::strlen(tmp) + 1;
9094  _title = new char[s];
9095  std::memcpy(_title,tmp,s*sizeof(char));
9096  SetWindowTextA(_window, tmp);
9097  return *this;
9098  }
9099 
9100  template<typename T>
9101  CImgDisplay& display(const CImg<T>& img) {
9102  if (!img)
9103  throw CImgArgumentException(_cimgdisplay_instance
9104  "display(): Empty specified image.",
9105  cimgdisplay_instance);
9106  if (is_empty()) return assign(img);
9107  return render(img).paint();
9108  }
9109 
9110  CImgDisplay& paint() {
9111  if (_is_closed) return *this;
9112  WaitForSingleObject(_mutex,INFINITE);
9113  SetDIBitsToDevice(_hdc,0,0,_width,_height,0,0,0,_height,_data,&_bmi,DIB_RGB_COLORS);
9114  ReleaseMutex(_mutex);
9115  return *this;
9116  }
9117 
9118  template<typename T>
9119  CImgDisplay& render(const CImg<T>& img) {
9120  if (!img)
9121  throw CImgArgumentException(_cimgdisplay_instance
9122  "render(): Empty specified image.",
9123  cimgdisplay_instance);
9124 
9125  if (is_empty()) return *this;
9126  if (img._depth!=1) return render(img.get_projections2d((img._width-1)/2,(img._height-1)/2,(img._depth-1)/2));
9127 
9128  const T
9129  *data1 = img._data,
9130  *data2 = (img._spectrum>=2)?img.data(0,0,0,1):data1,
9131  *data3 = (img._spectrum>=3)?img.data(0,0,0,2):data1;
9132 
9133  WaitForSingleObject(_mutex,INFINITE);
9134  unsigned int
9135  *const ndata = (img._width==_width && img._height==_height)?_data:
9136  new unsigned int[(unsigned long)img._width*img._height],
9137  *ptrd = ndata;
9138 
9139  if (!_normalization || (_normalization==3 && cimg::type<T>::string()==cimg::type<unsigned char>::string())) {
9140  _min = _max = 0;
9141  switch (img._spectrum) {
9142  case 1 : {
9143  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
9144  const unsigned char val = (unsigned char)*(data1++);
9145  *(ptrd++) = (val<<16) | (val<<8) | val;
9146  }
9147  } break;
9148  case 2 : {
9149  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
9150  *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8);
9151  } break;
9152  default : {
9153  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
9154  *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) | (unsigned char)*(data3++);
9155  }
9156  }
9157  } else {
9158  if (_normalization==3) {
9159  if (cimg::type<T>::is_float()) _min = (float)img.min_max(_max);
9160  else { _min = (float)cimg::type<T>::min(); _max = (float)cimg::type<T>::max(); }
9161  } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max);
9162  const float delta = _max - _min, mm = 255/(delta?delta:1.0f);
9163  switch (img._spectrum) {
9164  case 1 : {
9165  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
9166  const unsigned char val = (unsigned char)((*(data1++)-_min)*mm);
9167  *(ptrd++) = (val<<16) | (val<<8) | val;
9168  }
9169  } break;
9170  case 2 : {
9171  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
9172  const unsigned char
9173  R = (unsigned char)((*(data1++)-_min)*mm),
9174  G = (unsigned char)((*(data2++)-_min)*mm);
9175  *(ptrd++) = (R<<16) | (G<<8);
9176  }
9177  } break;
9178  default : {
9179  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
9180  const unsigned char
9181  R = (unsigned char)((*(data1++)-_min)*mm),
9182  G = (unsigned char)((*(data2++)-_min)*mm),
9183  B = (unsigned char)((*(data3++)-_min)*mm);
9184  *(ptrd++) = (R<<16) | (G<<8) | B;
9185  }
9186  }
9187  }
9188  }
9189  if (ndata!=_data) { _render_resize(ndata,img._width,img._height,_data,_width,_height); delete[] ndata; }
9190  ReleaseMutex(_mutex);
9191  return *this;
9192  }
9193 
9194  template<typename T>
9195  const CImgDisplay& snapshot(CImg<T>& img) const {
9196  if (is_empty()) { img.assign(); return *this; }
9197  const unsigned int *ptrs = _data;
9198  img.assign(_width,_height,1,3);
9199  T
9200  *data1 = img.data(0,0,0,0),
9201  *data2 = img.data(0,0,0,1),
9202  *data3 = img.data(0,0,0,2);
9203  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
9204  const unsigned int val = *(ptrs++);
9205  *(data1++) = (T)(unsigned char)(val>>16);
9206  *(data2++) = (T)(unsigned char)((val>>8)&0xFF);
9207  *(data3++) = (T)(unsigned char)(val&0xFF);
9208  }
9209  return *this;
9210  }
9211 #endif
9212 
9214  };
9215 
9216  /*
9217  #--------------------------------------
9218  #
9219  #
9220  #
9221  # Definition of the CImg<T> structure
9222  #
9223  #
9224  #
9225  #--------------------------------------
9226  */
9227 
9229 
9321  template<typename T>
9322  struct CImg {
9323 
9324  unsigned int _width, _height, _depth, _spectrum;
9325  bool _is_shared;
9326  T *_data;
9327 
9329 
9341  typedef T* iterator;
9342 
9344 
9357  typedef const T* const_iterator;
9358 
9360 
9367  typedef T value_type;
9368 
9369  // Define common types related to template type T.
9370  typedef typename cimg::superset<T,bool>::type Tbool;
9371  typedef typename cimg::superset<T,unsigned char>::type Tuchar;
9372  typedef typename cimg::superset<T,char>::type Tchar;
9373  typedef typename cimg::superset<T,unsigned short>::type Tushort;
9374  typedef typename cimg::superset<T,short>::type Tshort;
9375  typedef typename cimg::superset<T,unsigned int>::type Tuint;
9376  typedef typename cimg::superset<T,int>::type Tint;
9377  typedef typename cimg::superset<T,unsigned long>::type Tulong;
9378  typedef typename cimg::superset<T,long>::type Tlong;
9379  typedef typename cimg::superset<T,float>::type Tfloat;
9380  typedef typename cimg::superset<T,double>::type Tdouble;
9381  typedef typename cimg::last<T,bool>::type boolT;
9382  typedef typename cimg::last<T,unsigned char>::type ucharT;
9383  typedef typename cimg::last<T,char>::type charT;
9384  typedef typename cimg::last<T,unsigned short>::type ushortT;
9385  typedef typename cimg::last<T,short>::type shortT;
9386  typedef typename cimg::last<T,unsigned int>::type uintT;
9387  typedef typename cimg::last<T,int>::type intT;
9388  typedef typename cimg::last<T,unsigned long>::type ulongT;
9389  typedef typename cimg::last<T,long>::type longT;
9390  typedef typename cimg::last<T,float>::type floatT;
9391  typedef typename cimg::last<T,double>::type doubleT;
9392 
9394  //---------------------------
9395  //
9397 
9398  //---------------------------
9399 #ifdef cimg_plugin
9400 #include cimg_plugin
9401 #endif
9402 #ifdef cimg_plugin1
9403 #include cimg_plugin1
9404 #endif
9405 #ifdef cimg_plugin2
9406 #include cimg_plugin2
9407 #endif
9408 #ifdef cimg_plugin3
9409 #include cimg_plugin3
9410 #endif
9411 #ifdef cimg_plugin4
9412 #include cimg_plugin4
9413 #endif
9414 #ifdef cimg_plugin5
9415 #include cimg_plugin5
9416 #endif
9417 #ifdef cimg_plugin6
9418 #include cimg_plugin6
9419 #endif
9420 #ifdef cimg_plugin7
9421 #include cimg_plugin7
9422 #endif
9423 #ifdef cimg_plugin8
9424 #include cimg_plugin8
9425 #endif
9426 
9428  //---------------------------------------------------------
9429  //
9431 
9432  //---------------------------------------------------------
9433 
9435 
9444  ~CImg() {
9445  if (!_is_shared) delete[] _data;
9446  }
9447 
9449 
9465  CImg():_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {}
9466 
9468 
9490  explicit CImg(const unsigned int size_x, const unsigned int size_y=1,
9491  const unsigned int size_z=1, const unsigned int size_c=1):
9492  _is_shared(false) {
9493  const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c;
9494  if (siz) {
9495  _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
9496  try { _data = new T[siz]; } catch (...) {
9497  _width = _height = _depth = _spectrum = 0; _data = 0;
9498  throw CImgInstanceException(_cimg_instance
9499  "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9500  cimg_instance,
9501  cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
9502  size_x,size_y,size_z,size_c);
9503  }
9504  } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
9505  }
9506 
9508 
9522  CImg(const unsigned int size_x, const unsigned int size_y,
9523  const unsigned int size_z, const unsigned int size_c, const T value):
9524  _is_shared(false) {
9525  const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c;
9526  if (siz) {
9527  _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
9528  try { _data = new T[siz]; } catch (...) {
9529  _width = _height = _depth = _spectrum = 0; _data = 0;
9530  throw CImgInstanceException(_cimg_instance
9531  "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9532  cimg_instance,
9533  cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
9534  size_x,size_y,size_z,size_c);
9535  }
9536  fill(value);
9537  } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
9538  }
9539 
9541 
9568  CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
9569  const int value0, const int value1, ...):
9570  _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
9571 #define _CImg_stdarg(img,a0,a1,N,t) { \
9572  unsigned long _siz = (unsigned long)N; \
9573  if (_siz--) { \
9574  va_list ap; \
9575  va_start(ap,a1); \
9576  T *ptrd = (img)._data; \
9577  *(ptrd++) = (T)a0; \
9578  if (_siz--) { \
9579  *(ptrd++) = (T)a1; \
9580  for (; _siz; --_siz) *(ptrd++) = (T)va_arg(ap,t); \
9581  } \
9582  va_end(ap); \
9583  } \
9584  }
9585  assign(size_x,size_y,size_z,size_c);
9586  _CImg_stdarg(*this,value0,value1,(unsigned long)size_x*size_y*size_z*size_c,int);
9587  }
9588 
9589 #ifdef cimg_use_cpp11
9590 
9615  template<typename t>
9616  CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
9617  const std::initializer_list<t> values,
9618  const bool repeat_values=true):
9619  _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
9620 #define _cimg_constructor_cpp11(repeat_values) \
9621  auto it = values.begin(); \
9622  unsigned long siz = size(); \
9623  if (repeat_values) for (T *ptrd = _data; siz--; ) { \
9624  *(ptrd++) = (T)(*(it++)); if (it==values.end()) it = values.begin(); } \
9625  else { siz = cimg::min(siz,values.size()); for (T *ptrd = _data; siz--; ) *(ptrd++) = (T)(*(it++)); }
9626  assign(size_x,size_y,size_z,size_c);
9627  _cimg_constructor_cpp11(repeat_values);
9628  }
9629 
9630  template<typename t>
9631  CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z,
9632  std::initializer_list<t> values,
9633  const bool repeat_values=true):
9634  _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
9635  assign(size_x,size_y,size_z);
9636  _cimg_constructor_cpp11(repeat_values);
9637  }
9638 
9639  template<typename t>
9640  CImg(const unsigned int size_x, const unsigned int size_y,
9641  std::initializer_list<t> values,
9642  const bool repeat_values=true):
9643  _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
9644  assign(size_x,size_y);
9645  _cimg_constructor_cpp11(repeat_values);
9646  }
9647 
9648  template<typename t>
9649  CImg(const unsigned int size_x,
9650  std::initializer_list<t> values,
9651  const bool repeat_values=true):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
9652  assign(size_x);
9653  _cimg_constructor_cpp11(repeat_values);
9654  }
9655 
9657 
9673  template<typename t>
9674  CImg(const std::initializer_list<t> values):
9675  _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
9676  assign(values.size(),1,1,1);
9677  auto it = values.begin();
9678  unsigned long siz = _width;
9679  for (T *ptrd = _data; siz--; ) *(ptrd++) = (T)(*(it++));
9680  }
9681 
9682  template<typename t>
9683  CImg<T> & operator=(std::initializer_list<t> values) {
9684  _cimg_constructor_cpp11(siz>values.size());
9685  return *this;
9686  }
9687 #endif
9688 
9690 
9711  CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
9712  const double value0, const double value1, ...):
9713  _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
9714  assign(size_x,size_y,size_z,size_c);
9715  _CImg_stdarg(*this,value0,value1,(unsigned long)size_x*size_y*size_z*size_c,double);
9716  }
9717 
9719 
9748  CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
9749  const char *const values, const bool repeat_values):_is_shared(false) {
9750  const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c;
9751  if (siz) {
9752  _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
9753  try { _data = new T[siz]; } catch (...) {
9754  _width = _height = _depth = _spectrum = 0; _data = 0;
9755  throw CImgInstanceException(_cimg_instance
9756  "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9757  cimg_instance,
9758  cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
9759  size_x,size_y,size_z,size_c);
9760  }
9761  fill(values,repeat_values);
9762  } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
9763  }
9764 
9766 
9795  template<typename t>
9796  CImg(const t *const values, const unsigned int size_x, const unsigned int size_y=1,
9797  const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false):_is_shared(false) {
9798  if (is_shared) {
9799  _width = _height = _depth = _spectrum = 0; _data = 0;
9800  throw CImgArgumentException(_cimg_instance
9801  "CImg(): Invalid construction request of a (%u,%u,%u,%u) shared instance "
9802  "from a (%s*) buffer (pixel types are different).",
9803  cimg_instance,
9804  size_x,size_y,size_z,size_c,CImg<t>::pixel_type());
9805  }
9806  const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c;
9807  if (values && siz) {
9808  _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
9809  try { _data = new T[siz]; } catch (...) {
9810  _width = _height = _depth = _spectrum = 0; _data = 0;
9811  throw CImgInstanceException(_cimg_instance
9812  "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9813  cimg_instance,
9814  cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
9815  size_x,size_y,size_z,size_c);
9816 
9817  }
9818  const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
9819  } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
9820  }
9821 
9823  CImg(const T *const values, const unsigned int size_x, const unsigned int size_y=1,
9824  const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false) {
9825  const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c;
9826  if (values && siz) {
9827  _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = is_shared;
9828  if (_is_shared) _data = const_cast<T*>(values);
9829  else {
9830  try { _data = new T[siz]; } catch (...) {
9831  _width = _height = _depth = _spectrum = 0; _data = 0;
9832  throw CImgInstanceException(_cimg_instance
9833  "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9834  cimg_instance,
9835  cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
9836  size_x,size_y,size_z,size_c);
9837  }
9838  std::memcpy(_data,values,siz*sizeof(T)); }
9839  } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
9840  }
9841 
9843 
9863  explicit CImg(const char *const filename):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
9864  assign(filename);
9865  }
9866 
9868 
9886  template<typename t>
9887  CImg(const CImg<t>& img):_is_shared(false) {
9888  const unsigned long siz = img.size();
9889  if (img._data && siz) {
9890  _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
9891  try { _data = new T[siz]; } catch (...) {
9892  _width = _height = _depth = _spectrum = 0; _data = 0;
9893  throw CImgInstanceException(_cimg_instance
9894  "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9895  cimg_instance,
9896  cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
9897  img._width,img._height,img._depth,img._spectrum);
9898  }
9899  const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
9900  } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
9901  }
9902 
9904  CImg(const CImg<T>& img) {
9905  const unsigned long siz = img.size();
9906  if (img._data && siz) {
9907  _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
9908  _is_shared = img._is_shared;
9909  if (_is_shared) _data = const_cast<T*>(img._data);
9910  else {
9911  try { _data = new T[siz]; } catch (...) {
9912  _width = _height = _depth = _spectrum = 0; _data = 0;
9913  throw CImgInstanceException(_cimg_instance
9914  "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9915  cimg_instance,
9916  cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
9917  img._width,img._height,img._depth,img._spectrum);
9918 
9919  }
9920  std::memcpy(_data,img._data,siz*sizeof(T));
9921  }
9922  } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
9923  }
9924 
9926 
9940  template<typename t>
9941  CImg(const CImg<t>& img, const bool is_shared):_is_shared(false) {
9942  if (is_shared) {
9943  _width = _height = _depth = _spectrum = 0; _data = 0;
9944  throw CImgArgumentException(_cimg_instance
9945  "CImg(): Invalid construction request of a shared instance from a "
9946  "CImg<%s> image (%u,%u,%u,%u,%p) (pixel types are different).",
9947  cimg_instance,
9948  CImg<t>::pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data);
9949  }
9950  const unsigned long siz = img.size();
9951  if (img._data && siz) {
9952  _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
9953  try { _data = new T[siz]; } catch (...) {
9954  _width = _height = _depth = _spectrum = 0; _data = 0;
9955  throw CImgInstanceException(_cimg_instance
9956  "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9957  cimg_instance,
9958  cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
9959  img._width,img._height,img._depth,img._spectrum);
9960  }
9961  const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
9962  } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
9963  }
9964 
9966  CImg(const CImg<T>& img, const bool is_shared) {
9967  const unsigned long siz = img.size();
9968  if (img._data && siz) {
9969  _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
9970  _is_shared = is_shared;
9971  if (_is_shared) _data = const_cast<T*>(img._data);
9972  else {
9973  try { _data = new T[siz]; } catch (...) {
9974  _width = _height = _depth = _spectrum = 0; _data = 0;
9975  throw CImgInstanceException(_cimg_instance
9976  "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9977  cimg_instance,
9978  cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
9979  img._width,img._height,img._depth,img._spectrum);
9980  }
9981  std::memcpy(_data,img._data,siz*sizeof(T));
9982  }
9983  } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
9984  }
9985 
9987 
10006  template<typename t>
10007  CImg(const CImg<t>& img, const char *const dimensions):
10008  _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
10009  assign(img,dimensions);
10010  }
10011 
10013 
10022  template<typename t>
10023  CImg(const CImg<t>& img, const char *const dimensions, const T value):
10024  _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
10025  assign(img,dimensions).fill(value);
10026  }
10027 
10029 
10038  explicit CImg(const CImgDisplay &disp):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
10039  disp.snapshot(*this);
10040  }
10041 
10042  // Constructor and assignment operator for rvalue references (c++11).
10043  // This avoids an additional image copy for methods returning new images. Can save RAM for big images !
10044 #ifdef cimg_use_cpp11
10045  CImg(CImg<T>&& img):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
10046  swap(img);
10047  }
10048  CImg<T>& operator=(CImg<T>&& img) {
10049  if (_is_shared) return assign(img);
10050  return img.swap(*this);
10051  }
10052 #endif
10053 
10055 
10059  if (!_is_shared) delete[] _data;
10060  _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0;
10061  return *this;
10062  }
10063 
10065 
10068  CImg<T>& assign(const unsigned int size_x, const unsigned int size_y=1,
10069  const unsigned int size_z=1, const unsigned int size_c=1) {
10070  const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c;
10071  if (!siz) return assign();
10072  const unsigned long curr_siz = size();
10073  if (siz!=curr_siz) {
10074  if (_is_shared)
10075  throw CImgArgumentException(_cimg_instance
10076  "assign(): Invalid assignement request of shared instance from specified "
10077  "image (%u,%u,%u,%u).",
10078  cimg_instance,
10079  size_x,size_y,size_z,size_c);
10080  else {
10081  delete[] _data;
10082  try { _data = new T[siz]; } catch (...) {
10083  _width = _height = _depth = _spectrum = 0; _data = 0;
10084  throw CImgInstanceException(_cimg_instance
10085  "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
10086  cimg_instance,
10087  cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
10088  size_x,size_y,size_z,size_c);
10089  }
10090  }
10091  }
10092  _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
10093  return *this;
10094  }
10095 
10097 
10100  CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
10101  const unsigned int size_z, const unsigned int size_c, const T value) {
10102  return assign(size_x,size_y,size_z,size_c).fill(value);
10103  }
10104 
10106 
10109  CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
10110  const unsigned int size_z, const unsigned int size_c,
10111  const int value0, const int value1, ...) {
10112  assign(size_x,size_y,size_z,size_c);
10113  _CImg_stdarg(*this,value0,value1,(unsigned long)size_x*size_y*size_z*size_c,int);
10114  return *this;
10115  }
10116 
10118 
10121  CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
10122  const unsigned int size_z, const unsigned int size_c,
10123  const double value0, const double value1, ...) {
10124  assign(size_x,size_y,size_z,size_c);
10125  _CImg_stdarg(*this,value0,value1,(unsigned long)size_x*size_y*size_z*size_c,double);
10126  return *this;
10127  }
10128 
10130 
10133  CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
10134  const unsigned int size_z, const unsigned int size_c,
10135  const char *const values, const bool repeat_values) {
10136  return assign(size_x,size_y,size_z,size_c).fill(values,repeat_values);
10137  }
10138 
10140 
10143  template<typename t>
10144  CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y=1,
10145  const unsigned int size_z=1, const unsigned int size_c=1) {
10146  const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c;
10147  if (!values || !siz) return assign();
10148  assign(size_x,size_y,size_z,size_c);
10149  const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
10150  return *this;
10151  }
10152 
10154  CImg<T>& assign(const T *const values, const unsigned int size_x, const unsigned int size_y=1,
10155  const unsigned int size_z=1, const unsigned int size_c=1) {
10156  const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c;
10157  if (!values || !siz) return assign();
10158  const unsigned long curr_siz = size();
10159  if (values==_data && siz==curr_siz) return assign(size_x,size_y,size_z,size_c);
10160  if (_is_shared || values+siz<_data || values>=_data+size()) {
10161  assign(size_x,size_y,size_z,size_c);
10162  if (_is_shared) std::memmove(_data,values,siz*sizeof(T));
10163  else std::memcpy(_data,values,siz*sizeof(T));
10164  } else {
10165  T *new_data = 0;
10166  try { new_data = new T[siz]; } catch (...) {
10167  _width = _height = _depth = _spectrum = 0; _data = 0;
10168  throw CImgInstanceException(_cimg_instance
10169  "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
10170  cimg_instance,
10171  cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
10172  size_x,size_y,size_z,size_c);
10173  }
10174  std::memcpy(new_data,values,siz*sizeof(T));
10175  delete[] _data; _data = new_data; _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
10176  }
10177  return *this;
10178  }
10179 
10181  template<typename t>
10182  CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y,
10183  const unsigned int size_z, const unsigned int size_c, const bool is_shared) {
10184  if (is_shared)
10185  throw CImgArgumentException(_cimg_instance
10186  "assign(): Invalid assignment request of shared instance from (%s*) buffer"
10187  "(pixel types are different).",
10188  cimg_instance,
10190  return assign(values,size_x,size_y,size_z,size_c);
10191  }
10192 
10194  CImg<T>& assign(const T *const values, const unsigned int size_x, const unsigned int size_y,
10195  const unsigned int size_z, const unsigned int size_c, const bool is_shared) {
10196  const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c;
10197  if (!values || !siz) {
10198  if (is_shared)
10199  throw CImgArgumentException(_cimg_instance
10200  "assign(): Invalid assignment request of shared instance from (null) or "
10201  "empty buffer.",
10202  cimg_instance);
10203  else return assign();
10204  }
10205  if (!is_shared) { if (_is_shared) assign(); assign(values,size_x,size_y,size_z,size_c); }
10206  else {
10207  if (!_is_shared) {
10208  if (values+siz<_data || values>=_data+size()) assign();
10209  else cimg::warn(_cimg_instance
10210  "assign(): Shared image instance has overlapping memory.",
10211  cimg_instance);
10212  }
10213  _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = true;
10214  _data = const_cast<T*>(values);
10215  }
10216  return *this;
10217  }
10218 
10220 
10223  CImg<T>& assign(const char *const filename) {
10224  return load(filename);
10225  }
10226 
10228 
10231  template<typename t>
10232  CImg<T>& assign(const CImg<t>& img) {
10233  return assign(img._data,img._width,img._height,img._depth,img._spectrum);
10234  }
10235 
10237 
10240  template<typename t>
10241  CImg<T>& assign(const CImg<t>& img, const bool is_shared) {
10242  return assign(img._data,img._width,img._height,img._depth,img._spectrum,is_shared);
10243  }
10244 
10246 
10249  template<typename t>
10250  CImg<T>& assign(const CImg<t>& img, const char *const dimensions) {
10251  if (!dimensions || !*dimensions) return assign(img._width,img._height,img._depth,img._spectrum);
10252  unsigned int siz[4] = { 0,1,1,1 }, k = 0;
10253  for (const char *s = dimensions; *s && k<4; ++k) {
10254  char item[256] = { 0 };
10255  if (std::sscanf(s,"%255[^0-9%xyzvwhdcXYZVWHDC]",item)>0) s+=std::strlen(item);
10256  if (*s) {
10257  unsigned int val = 0; char sep = 0;
10258  if (std::sscanf(s,"%u%c",&val,&sep)>0) {
10259  if (sep=='%') siz[k] = val*(k==0?_width:k==1?_height:k==2?_depth:_spectrum)/100;
10260  else siz[k] = val;
10261  while (*s>='0' && *s<='9') ++s; if (sep=='%') ++s;
10262  } else switch (cimg::uncase(*s)) {
10263  case 'x' : case 'w' : siz[k] = img._width; ++s; break;
10264  case 'y' : case 'h' : siz[k] = img._height; ++s; break;
10265  case 'z' : case 'd' : siz[k] = img._depth; ++s; break;
10266  case 'c' : case 's' : siz[k] = img._spectrum; ++s; break;
10267  default :
10268  throw CImgArgumentException(_cimg_instance
10269  "assign(): Invalid character '%c' detected in specified dimension string '%s'.",
10270  cimg_instance,
10271  *s,dimensions);
10272  }
10273  }
10274  }
10275  return assign(siz[0],siz[1],siz[2],siz[3]);
10276  }
10277 
10279 
10282  template<typename t>
10283  CImg<T>& assign(const CImg<t>& img, const char *const dimensions, const T value) {
10284  return assign(img,dimensions).fill(value);
10285  }
10286 
10288 
10291  CImg<T>& assign(const CImgDisplay &disp) {
10292  disp.snapshot(*this);
10293  return *this;
10294  }
10295 
10297 
10303  return assign();
10304  }
10305 
10307 
10322  template<typename t>
10324  img.assign(*this);
10325  assign();
10326  return img;
10327  }
10328 
10331  if (_is_shared || img._is_shared) img.assign(*this);
10332  else swap(img);
10333  assign();
10334  return img;
10335  }
10336 
10338 
10355  template<typename t>
10356  CImgList<t>& move_to(CImgList<t>& list, const unsigned int pos=~0U) {
10357  const unsigned int npos = pos>list._width?list._width:pos;
10358  move_to(list.insert(1,npos)[npos]);
10359  return list;
10360  }
10361 
10363 
10376  cimg::swap(_width,img._width);
10377  cimg::swap(_height,img._height);
10378  cimg::swap(_depth,img._depth);
10379  cimg::swap(_spectrum,img._spectrum);
10380  cimg::swap(_data,img._data);
10381  cimg::swap(_is_shared,img._is_shared);
10382  return img;
10383  }
10384 
10386 
10394  static CImg<T>& empty() {
10395  static CImg<T> _empty;
10396  return _empty.assign();
10397  }
10398 
10400  //------------------------------------------
10401  //
10403 
10404  //------------------------------------------
10405 
10407 
10439 #if cimg_verbosity>=3
10440  T& operator()(const unsigned int x, const unsigned int y=0,
10441  const unsigned int z=0, const unsigned int c=0) {
10442  const unsigned long off = (unsigned long)offset(x,y,z,c);
10443  if (!_data || off>=size()) {
10444  cimg::warn(_cimg_instance
10445  "operator(): Invalid pixel request, at coordinates (%u,%u,%u,%u) [offset=%u].",
10446  cimg_instance,
10447  x,y,z,c,off);
10448  return *_data;
10449  }
10450  else return _data[off];
10451  }
10452 
10454  const T& operator()(const unsigned int x, const unsigned int y=0,
10455  const unsigned int z=0, const unsigned int c=0) const {
10456  return const_cast<CImg<T>*>(this)->operator()(x,y,z,c);
10457  }
10458 
10460 
10472  T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
10473  const unsigned long wh, const unsigned long whd=0) {
10474  cimg::unused(wh,whd);
10475  return (*this)(x,y,z,c);
10476  }
10477 
10479  const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
10480  const unsigned long wh, const unsigned long whd=0) const {
10481  cimg::unused(wh,whd);
10482  return (*this)(x,y,z,c);
10483  }
10484 #else
10485  T& operator()(const unsigned int x) {
10486  return _data[x];
10487  }
10488 
10489  const T& operator()(const unsigned int x) const {
10490  return _data[x];
10491  }
10492 
10493  T& operator()(const unsigned int x, const unsigned int y) {
10494  return _data[x + y*_width];
10495  }
10496 
10497  const T& operator()(const unsigned int x, const unsigned int y) const {
10498  return _data[x + y*_width];
10499  }
10500 
10501  T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) {
10502  return _data[x + y*(unsigned long)_width + z*(unsigned long)_width*_height];
10503  }
10504 
10505  const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) const {
10506  return _data[x + y*(unsigned long)_width + z*(unsigned long)_width*_height];
10507  }
10508 
10509  T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) {
10510  return _data[x + y*(unsigned long)_width + z*(unsigned long)_width*_height +
10511  c*(unsigned long)_width*_height*_depth];
10512  }
10513 
10514  const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) const {
10515  return _data[x + y*(unsigned long)_width + z*(unsigned long)_width*_height +
10516  c*(unsigned long)_width*_height*_depth];
10517  }
10518 
10519  T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int,
10520  const unsigned long wh) {
10521  return _data[x + y*_width + z*wh];
10522  }
10523 
10524  const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int,
10525  const unsigned long wh) const {
10526  return _data[x + y*_width + z*wh];
10527  }
10528 
10529  T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
10530  const unsigned long wh, const unsigned long whd) {
10531  return _data[x + y*_width + z*wh + c*whd];
10532  }
10533 
10534  const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
10535  const unsigned long wh, const unsigned long whd) const {
10536  return _data[x + y*_width + z*wh + c*whd];
10537  }
10538 #endif
10539 
10541 
10562  operator T*() {
10563  return _data;
10564  }
10565 
10567  operator const T*() const {
10568  return _data;
10569  }
10570 
10572 
10585  CImg<T>& operator=(const T value) {
10586  return fill(value);
10587  }
10588 
10590 
10609  CImg<T>& operator=(const char *const expression) {
10610  const unsigned int omode = cimg::exception_mode();
10611  cimg::exception_mode() = 0;
10612  try {
10613  fill(expression,true);
10614  } catch (CImgException&) {
10615  cimg::exception_mode() = omode;
10616  load(expression);
10617  }
10618  cimg::exception_mode() = omode;
10619  return *this;
10620  }
10621 
10623 
10626  template<typename t>
10627  CImg<T>& operator=(const CImg<t>& img) {
10628  return assign(img);
10629  }
10630 
10632  CImg<T>& operator=(const CImg<T>& img) {
10633  return assign(img);
10634  }
10635 
10637 
10641  disp.snapshot(*this);
10642  return *this;
10643  }
10644 
10646 
10672  template<typename t>
10673  CImg<T>& operator+=(const t value) {
10674  if (is_empty()) return *this;
10675 #ifdef cimg_use_openmp
10676 #pragma omp parallel for if (size()>=524288)
10677 #endif
10678  cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd + value);
10679  return *this;
10680  }
10681 
10683 
10690  CImg<T>& operator+=(const char *const expression) {
10691  if (is_empty()) return *this;
10692  const unsigned int omode = cimg::exception_mode();
10693  cimg::exception_mode() = 0;
10694  try {
10695  const CImg<T> _base = cimg::_is_self_expr(expression)?+*this:CImg<T>(), &base = _base?_base:*this;
10696  _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"operator+=");
10697  T *ptrd = *expression=='<'?end()-1:_data;
10698  if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd + mp(x,y,z,c)); --ptrd; }
10699  else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd + mp(x,y,z,c)); ++ptrd; }
10700  else {
10701 #ifdef cimg_use_openmp
10702  if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)
10703 #pragma omp parallel
10704  {
10705  _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp;
10706 #pragma omp for collapse(3)
10707  cimg_forYZC(*this,y,z,c) {
10708  T *ptrd = data(0,y,z,c);
10709  cimg_forX(*this,x) { *ptrd = (T)(*ptrd + lmp(x,y,z,c)); ++ptrd; }
10710  }
10711  }
10712  else
10713 #endif
10714  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd + mp(x,y,z,c)); ++ptrd; }
10715  }
10716  } catch (CImgException&) {
10717  cimg::exception_mode() = omode;
10718  *this+=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
10719  }
10720  cimg::exception_mode() = omode;
10721  return *this;
10722  }
10723 
10725 
10744  template<typename t>
10745  CImg<T>& operator+=(const CImg<t>& img) {
10746  const unsigned long siz = size(), isiz = img.size();
10747  if (siz && isiz) {
10748  if (is_overlapped(img)) return *this+=+img;
10749  T *ptrd = _data, *const ptre = _data + siz;
10750  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
10751  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
10752  *ptrd = (T)(*ptrd + *(ptrs++));
10753  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd + *(ptrs++));
10754  }
10755  return *this;
10756  }
10757 
10759 
10765  if (is_empty()) return *this;
10766 #ifdef cimg_use_openmp
10767 #pragma omp parallel for if (size()>=524288)
10768 #endif
10769  cimg_rof(*this,ptrd,T) ++*ptrd;
10770  return *this;
10771  }
10772 
10774 
10781  const CImg<T> copy(*this,false);
10782  ++*this;
10783  return copy;
10784  }
10785 
10787 
10795  CImg<T> operator+() const {
10796  return CImg<T>(*this,false);
10797  }
10798 
10800 
10804  template<typename t>
10805  CImg<_cimg_Tt> operator+(const t value) const {
10806  return CImg<_cimg_Tt>(*this,false)+=value;
10807  }
10808 
10810 
10814  CImg<Tfloat> operator+(const char *const expression) const {
10815  return CImg<Tfloat>(*this,false)+=expression;
10816  }
10817 
10819 
10823  template<typename t>
10824  CImg<_cimg_Tt> operator+(const CImg<t>& img) const {
10825  return CImg<_cimg_Tt>(*this,false)+=img;
10826  }
10827 
10829 
10832  template<typename t>
10833  CImg<T>& operator-=(const t value) {
10834  if (is_empty()) return *this;
10835 #ifdef cimg_use_openmp
10836 #pragma omp parallel for if (size()>=524288)
10837 #endif
10838  cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd - value);
10839  return *this;
10840  }
10841 
10843 
10846  CImg<T>& operator-=(const char *const expression) {
10847  if (is_empty()) return *this;
10848  const unsigned int omode = cimg::exception_mode();
10849  cimg::exception_mode() = 0;
10850  try {
10851  const CImg<T> _base = cimg::_is_self_expr(expression)?+*this:CImg<T>(), &base = _base?_base:*this;
10852  _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"operator-=");
10853  T *ptrd = *expression=='<'?end()-1:_data;
10854  if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd - mp(x,y,z,c)); --ptrd; }
10855  else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd - mp(x,y,z,c)); ++ptrd; }
10856  else {
10857 #ifdef cimg_use_openmp
10858  if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)
10859 #pragma omp parallel
10860  {
10861  _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp;
10862 #pragma omp for collapse(3)
10863  cimg_forYZC(*this,y,z,c) {
10864  T *ptrd = data(0,y,z,c);
10865  cimg_forX(*this,x) { *ptrd = (T)(*ptrd - lmp(x,y,z,c)); ++ptrd; }
10866  }
10867  }
10868  else
10869 #endif
10870  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd + mp(x,y,z,c)); ++ptrd; }
10871  }
10872  } catch (CImgException&) {
10873  cimg::exception_mode() = omode;
10874  *this-=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
10875  }
10876  cimg::exception_mode() = omode;
10877  return *this;
10878  }
10879 
10881 
10884  template<typename t>
10885  CImg<T>& operator-=(const CImg<t>& img) {
10886  const unsigned long siz = size(), isiz = img.size();
10887  if (siz && isiz) {
10888  if (is_overlapped(img)) return *this-=+img;
10889  T *ptrd = _data, *const ptre = _data + siz;
10890  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
10891  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
10892  *ptrd = (T)(*ptrd - *(ptrs++));
10893  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd - *(ptrs++));
10894  }
10895  return *this;
10896  }
10897 
10899 
10903  if (is_empty()) return *this;
10904 #ifdef cimg_use_openmp
10905 #pragma omp parallel for if (size()>=524288)
10906 #endif
10907  cimg_rof(*this,ptrd,T) *ptrd = *ptrd-(T)1;
10908  return *this;
10909  }
10910 
10912 
10916  const CImg<T> copy(*this,false);
10917  --*this;
10918  return copy;
10919  }
10920 
10922 
10935  CImg<T> operator-() const {
10936  return CImg<T>(_width,_height,_depth,_spectrum,(T)0)-=*this;
10937  }
10938 
10940 
10944  template<typename t>
10945  CImg<_cimg_Tt> operator-(const t value) const {
10946  return CImg<_cimg_Tt>(*this,false)-=value;
10947  }
10948 
10950 
10954  CImg<Tfloat> operator-(const char *const expression) const {
10955  return CImg<Tfloat>(*this,false)-=expression;
10956  }
10957 
10959 
10963  template<typename t>
10964  CImg<_cimg_Tt> operator-(const CImg<t>& img) const {
10965  return CImg<_cimg_Tt>(*this,false)-=img;
10966  }
10967 
10969 
10972  template<typename t>
10973  CImg<T>& operator*=(const t value) {
10974  if (is_empty()) return *this;
10975 #ifdef cimg_use_openmp
10976 #pragma omp parallel for if (size()>=262144)
10977 #endif
10978  cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd * value);
10979  return *this;
10980  }
10981 
10983 
10986  CImg<T>& operator*=(const char *const expression) {
10987  if (is_empty()) return *this;
10988  const unsigned int omode = cimg::exception_mode();
10989  cimg::exception_mode() = 0;
10990  try {
10991  const CImg<T> _base = cimg::_is_self_expr(expression)?+*this:CImg<T>(), &base = _base?_base:*this;
10992  _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"operator*=");
10993  T *ptrd = *expression=='<'?end()-1:_data;
10994  if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd * mp(x,y,z,c)); --ptrd; }
10995  else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd * mp(x,y,z,c)); ++ptrd; }
10996  else {
10997 #ifdef cimg_use_openmp
10998  if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)
10999 #pragma omp parallel
11000  {
11001  _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp;
11002 #pragma omp for collapse(3)
11003  cimg_forYZC(*this,y,z,c) {
11004  T *ptrd = data(0,y,z,c);
11005  cimg_forX(*this,x) { *ptrd = (T)(*ptrd * lmp(x,y,z,c)); ++ptrd; }
11006  }
11007  }
11008  else
11009 #endif
11010  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd * mp(x,y,z,c)); ++ptrd; }
11011  }
11012  } catch (CImgException&) {
11013  cimg::exception_mode() = omode;
11014  mul(CImg<T>(_width,_height,_depth,_spectrum,expression,true));
11015  }
11016  cimg::exception_mode() = omode;
11017  return *this;
11018  }
11019 
11021 
11037  template<typename t>
11038  CImg<T>& operator*=(const CImg<t>& img) {
11039  return ((*this)*img).move_to(*this);
11040  }
11041 
11043 
11047  template<typename t>
11048  CImg<_cimg_Tt> operator*(const t value) const {
11049  return CImg<_cimg_Tt>(*this,false)*=value;
11050  }
11051 
11053 
11057  CImg<Tfloat> operator*(const char *const expression) const {
11058  return CImg<Tfloat>(*this,false)*=expression;
11059  }
11060 
11062 
11066  template<typename t>
11067  CImg<_cimg_Tt> operator*(const CImg<t>& img) const {
11068  if (_width!=img._height || _depth!=1 || _spectrum!=1)
11069  throw CImgArgumentException(_cimg_instance
11070  "operator*(): Invalid multiplication of instance by specified "
11071  "matrix (%u,%u,%u,%u,%p)",
11072  cimg_instance,
11073  img._width,img._height,img._depth,img._spectrum,img._data);
11074  CImg<_cimg_Tt> res(img._width,_height);
11075 #ifdef cimg_use_openmp
11076 #pragma omp parallel for if (size()>1024 && img.size()>1024) collapse(2)
11077  cimg_forXY(res,i,j) {
11078  _cimg_Ttdouble value = 0; cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k); res(i,j) = (_cimg_Tt)value;
11079  }
11080 #else
11081  _cimg_Tt *ptrd = res._data;
11082  cimg_forXY(res,i,j) {
11083  _cimg_Ttdouble value = 0; cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k); *(ptrd++) = (_cimg_Tt)value;
11084  }
11085 #endif
11086  return res;
11087  }
11088 
11090 
11093  template<typename t>
11094  CImg<T>& operator/=(const t value) {
11095  if (is_empty()) return *this;
11096 #ifdef cimg_use_openmp
11097 #pragma omp parallel for if (size()>=32768)
11098 #endif
11099  cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd / value);
11100  return *this;
11101  }
11102 
11104 
11107  CImg<T>& operator/=(const char *const expression) {
11108  if (is_empty()) return *this;
11109  const unsigned int omode = cimg::exception_mode();
11110  cimg::exception_mode() = 0;
11111  try {
11112  const CImg<T> _base = cimg::_is_self_expr(expression)?+*this:CImg<T>(), &base = _base?_base:*this;
11113  _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"operator/=");
11114  T *ptrd = *expression=='<'?end()-1:_data;
11115  if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd / mp(x,y,z,c)); --ptrd; }
11116  else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd / mp(x,y,z,c)); ++ptrd; }
11117  else {
11118 #ifdef cimg_use_openmp
11119  if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)
11120 #pragma omp parallel
11121  {
11122  _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp;
11123 #pragma omp for collapse(3)
11124  cimg_forYZC(*this,y,z,c) {
11125  T *ptrd = data(0,y,z,c);
11126  cimg_forX(*this,x) { *ptrd = (T)(*ptrd / lmp(x,y,z,c)); ++ptrd; }
11127  }
11128  }
11129  else
11130 #endif
11131  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd / mp(x,y,z,c)); ++ptrd; }
11132  }
11133  } catch (CImgException&) {
11134  cimg::exception_mode() = omode;
11135  div(CImg<T>(_width,_height,_depth,_spectrum,expression,true));
11136  }
11137  cimg::exception_mode() = omode;
11138  return *this;
11139  }
11140 
11142 
11152  template<typename t>
11153  CImg<T>& operator/=(const CImg<t>& img) {
11154  return (*this*img.get_invert()).move_to(*this);
11155  }
11156 
11158 
11162  template<typename t>
11163  CImg<_cimg_Tt> operator/(const t value) const {
11164  return CImg<_cimg_Tt>(*this,false)/=value;
11165  }
11166 
11168 
11172  CImg<Tfloat> operator/(const char *const expression) const {
11173  return CImg<Tfloat>(*this,false)/=expression;
11174  }
11175 
11177 
11181  template<typename t>
11182  CImg<_cimg_Tt> operator/(const CImg<t>& img) const {
11183  return (*this)*img.get_invert();
11184  }
11185 
11187 
11190  template<typename t>
11191  CImg<T>& operator%=(const t value) {
11192  if (is_empty()) return *this;
11193 #ifdef cimg_use_openmp
11194 #pragma omp parallel for if (size()>=16384)
11195 #endif
11196  cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::mod(*ptrd,(T)value);
11197  return *this;
11198  }
11199 
11201 
11204  CImg<T>& operator%=(const char *const expression) {
11205  if (is_empty()) return *this;
11206  const unsigned int omode = cimg::exception_mode();
11207  cimg::exception_mode() = 0;
11208  try {
11209  const CImg<T> _base = cimg::_is_self_expr(expression)?+*this:CImg<T>(), &base = _base?_base:*this;
11210  _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"operator%=");
11211  T *ptrd = *expression=='<'?end()-1:_data;
11212  if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::mod(*ptrd,(T)mp(x,y,z,c)); --ptrd; }
11213  else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::mod(*ptrd,(T)mp(x,y,z,c)); ++ptrd; }
11214  else {
11215 #ifdef cimg_use_openmp
11216  if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)
11217 #pragma omp parallel
11218  {
11219  _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp;
11220 #pragma omp for collapse(3)
11221  cimg_forYZC(*this,y,z,c) {
11222  T *ptrd = data(0,y,z,c);
11223  cimg_forX(*this,x) { *ptrd = (T)cimg::mod(*ptrd,(T)lmp(x,y,z,c)); ++ptrd; }
11224  }
11225  }
11226  else
11227 #endif
11228  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::mod(*ptrd,(T)mp(x,y,z,c)); ++ptrd; }
11229  }
11230  } catch (CImgException&) {
11231  cimg::exception_mode() = omode;
11232  *this%=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
11233  }
11234  cimg::exception_mode() = omode;
11235  return *this;
11236  }
11237 
11239 
11242  template<typename t>
11243  CImg<T>& operator%=(const CImg<t>& img) {
11244  const unsigned long siz = size(), isiz = img.size();
11245  if (siz && isiz) {
11246  if (is_overlapped(img)) return *this%=+img;
11247  T *ptrd = _data, *const ptre = _data + siz;
11248  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
11249  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
11250  *ptrd = cimg::mod(*ptrd,(T)*(ptrs++));
11251  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::mod(*ptrd,(T)*(ptrs++));
11252  }
11253  return *this;
11254  }
11255 
11257 
11261  template<typename t>
11262  CImg<_cimg_Tt> operator%(const t value) const {
11263  return CImg<_cimg_Tt>(*this,false)%=value;
11264  }
11265 
11267 
11271  CImg<Tfloat> operator%(const char *const expression) const {
11272  return CImg<Tfloat>(*this,false)%=expression;
11273  }
11274 
11276 
11280  template<typename t>
11281  CImg<_cimg_Tt> operator%(const CImg<t>& img) const {
11282  return CImg<_cimg_Tt>(*this,false)%=img;
11283  }
11284 
11286 
11289  template<typename t>
11290  CImg<T>& operator&=(const t value) {
11291  if (is_empty()) return *this;
11292 #ifdef cimg_use_openmp
11293 #pragma omp parallel for if (size()>=32768)
11294 #endif
11295  cimg_rof(*this,ptrd,T) *ptrd = (T)((unsigned long)*ptrd & (unsigned long)value);
11296  return *this;
11297  }
11298 
11300 
11303  CImg<T>& operator&=(const char *const expression) {
11304  if (is_empty()) return *this;
11305  const unsigned int omode = cimg::exception_mode();
11306  cimg::exception_mode() = 0;
11307  try {
11308  const CImg<T> _base = cimg::_is_self_expr(expression)?+*this:CImg<T>(), &base = _base?_base:*this;
11309  _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"operator&=");
11310  T *ptrd = *expression=='<'?end()-1:_data;
11311  if (*expression=='<')
11312  cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd & (unsigned long)mp(x,y,z,c)); --ptrd; }
11313  else if (*expression=='>')
11314  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd & (unsigned long)mp(x,y,z,c)); ++ptrd; }
11315  else {
11316 #ifdef cimg_use_openmp
11317  if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)
11318 #pragma omp parallel
11319  {
11320  _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp;
11321 #pragma omp for collapse(3)
11322  cimg_forYZC(*this,y,z,c) {
11323  T *ptrd = data(0,y,z,c);
11324  cimg_forX(*this,x) { *ptrd = (T)((unsigned long)*ptrd & (unsigned long)lmp(x,y,z,c)); ++ptrd; }
11325  }
11326  }
11327  else
11328 #endif
11329  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd & (unsigned long)mp(x,y,z,c)); ++ptrd; }
11330  }
11331  } catch (CImgException&) {
11332  cimg::exception_mode() = omode;
11333  *this&=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
11334  }
11335  cimg::exception_mode() = omode;
11336  return *this;
11337  }
11338 
11340 
11343  template<typename t>
11344  CImg<T>& operator&=(const CImg<t>& img) {
11345  const unsigned long siz = size(), isiz = img.size();
11346  if (siz && isiz) {
11347  if (is_overlapped(img)) return *this&=+img;
11348  T *ptrd = _data, *const ptre = _data + siz;
11349  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
11350  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
11351  *ptrd = (T)((unsigned long)*ptrd & (unsigned long)*(ptrs++));
11352  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((unsigned long)*ptrd & (unsigned long)*(ptrs++));
11353  }
11354  return *this;
11355  }
11356 
11358 
11362  template<typename t>
11363  CImg<T> operator&(const t value) const {
11364  return (+*this)&=value;
11365  }
11366 
11368 
11372  CImg<T> operator&(const char *const expression) const {
11373  return (+*this)&=expression;
11374  }
11375 
11377 
11381  template<typename t>
11382  CImg<T> operator&(const CImg<t>& img) const {
11383  return (+*this)&=img;
11384  }
11385 
11387 
11390  template<typename t>
11391  CImg<T>& operator|=(const t value) {
11392  if (is_empty()) return *this;
11393 #ifdef cimg_use_openmp
11394 #pragma omp parallel for if (size()>=32768)
11395 #endif
11396  cimg_rof(*this,ptrd,T) *ptrd = (T)((unsigned long)*ptrd | (unsigned long)value);
11397  return *this;
11398  }
11399 
11401 
11404  CImg<T>& operator|=(const char *const expression) {
11405  if (is_empty()) return *this;
11406  const unsigned int omode = cimg::exception_mode();
11407  cimg::exception_mode() = 0;
11408  try {
11409  const CImg<T> _base = cimg::_is_self_expr(expression)?+*this:CImg<T>(), &base = _base?_base:*this;
11410  _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"operator|=");
11411  T *ptrd = *expression=='<'?end()-1:_data;
11412  if (*expression=='<')
11413  cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd | (unsigned long)mp(x,y,z,c)); --ptrd; }
11414  else if (*expression=='>')
11415  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd | (unsigned long)mp(x,y,z,c)); ++ptrd; }
11416  else {
11417 #ifdef cimg_use_openmp
11418  if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)
11419 #pragma omp parallel
11420  {
11421  _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp;
11422 #pragma omp for collapse(3)
11423  cimg_forYZC(*this,y,z,c) {
11424  T *ptrd = data(0,y,z,c);
11425  cimg_forX(*this,x) { *ptrd = (T)((unsigned long)*ptrd | (unsigned long)lmp(x,y,z,c)); ++ptrd; }
11426  }
11427  }
11428  else
11429 #endif
11430  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd | (unsigned long)mp(x,y,z,c)); ++ptrd; }
11431  }
11432  } catch (CImgException&) {
11433  cimg::exception_mode() = omode;
11434  *this|=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
11435  }
11436  cimg::exception_mode() = omode;
11437  return *this;
11438  }
11439 
11441 
11444  template<typename t>
11445  CImg<T>& operator|=(const CImg<t>& img) {
11446  const unsigned long siz = size(), isiz = img.size();
11447  if (siz && isiz) {
11448  if (is_overlapped(img)) return *this|=+img;
11449  T *ptrd = _data, *const ptre = _data + siz;
11450  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
11451  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
11452  *ptrd = (T)((unsigned long)*ptrd | (unsigned long)*(ptrs++));
11453  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((unsigned long)*ptrd | (unsigned long)*(ptrs++));
11454  }
11455  return *this;
11456  }
11457 
11459 
11463  template<typename t>
11464  CImg<T> operator|(const t value) const {
11465  return (+*this)|=value;
11466  }
11467 
11469 
11473  CImg<T> operator|(const char *const expression) const {
11474  return (+*this)|=expression;
11475  }
11476 
11478 
11482  template<typename t>
11483  CImg<T> operator|(const CImg<t>& img) const {
11484  return (+*this)|=img;
11485  }
11486 
11488 
11493  template<typename t>
11494  CImg<T>& operator^=(const t value) {
11495  if (is_empty()) return *this;
11496 #ifdef cimg_use_openmp
11497 #pragma omp parallel for if (size()>=32768)
11498 #endif
11499  cimg_rof(*this,ptrd,T) *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)value);
11500  return *this;
11501  }
11502 
11504 
11509  CImg<T>& operator^=(const char *const expression) {
11510  if (is_empty()) return *this;
11511  const unsigned int omode = cimg::exception_mode();
11512  cimg::exception_mode() = 0;
11513  try {
11514  const CImg<T> _base = cimg::_is_self_expr(expression)?+*this:CImg<T>(), &base = _base?_base:*this;
11515  _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"operator^=");
11516  T *ptrd = *expression=='<'?end()-1:_data;
11517  if (*expression=='<')
11518  cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)mp(x,y,z,c)); --ptrd; }
11519  else if (*expression=='>')
11520  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)mp(x,y,z,c)); ++ptrd; }
11521  else {
11522 #ifdef cimg_use_openmp
11523  if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)
11524 #pragma omp parallel
11525  {
11526  _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp;
11527 #pragma omp for collapse(3)
11528  cimg_forYZC(*this,y,z,c) {
11529  T *ptrd = data(0,y,z,c);
11530  cimg_forX(*this,x) { *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)lmp(x,y,z,c)); ++ptrd; }
11531  }
11532  }
11533  else
11534 #endif
11535  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)mp(x,y,z,c)); ++ptrd; }
11536  }
11537  } catch (CImgException&) {
11538  cimg::exception_mode() = omode;
11539  *this^=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
11540  }
11541  cimg::exception_mode() = omode;
11542  return *this;
11543  }
11544 
11546 
11551  template<typename t>
11552  CImg<T>& operator^=(const CImg<t>& img) {
11553  const unsigned long siz = size(), isiz = img.size();
11554  if (siz && isiz) {
11555  if (is_overlapped(img)) return *this^=+img;
11556  T *ptrd = _data, *const ptre = _data + siz;
11557  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
11558  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
11559  *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)*(ptrs++));
11560  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)*(ptrs++));
11561  }
11562  return *this;
11563  }
11564 
11566 
11570  template<typename t>
11571  CImg<T> operator^(const t value) const {
11572  return (+*this)^=value;
11573  }
11574 
11576 
11580  CImg<T> operator^(const char *const expression) const {
11581  return (+*this)^=expression;
11582  }
11583 
11585 
11589  template<typename t>
11590  CImg<T> operator^(const CImg<t>& img) const {
11591  return (+*this)^=img;
11592  }
11593 
11595 
11598  template<typename t>
11599  CImg<T>& operator<<=(const t value) {
11600  if (is_empty()) return *this;
11601 #ifdef cimg_use_openmp
11602 #pragma omp parallel for if (size()>=65536)
11603 #endif
11604  cimg_rof(*this,ptrd,T) *ptrd = (T)(((long)*ptrd) << (int)value);
11605  return *this;
11606  }
11607 
11609 
11612  CImg<T>& operator<<=(const char *const expression) {
11613  if (is_empty()) return *this;
11614  const unsigned int omode = cimg::exception_mode();
11615  cimg::exception_mode() = 0;
11616  try {
11617  const CImg<T> _base = cimg::_is_self_expr(expression)?+*this:CImg<T>(), &base = _base?_base:*this;
11618  _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"operator<<=");
11619  T *ptrd = *expression=='<'?end()-1:_data;
11620  if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd << (int)mp(x,y,z,c)); --ptrd; }
11621  else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd << (int)mp(x,y,z,c)); ++ptrd; }
11622  else {
11623 #ifdef cimg_use_openmp
11624  if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)
11625 #pragma omp parallel
11626  {
11627  _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp;
11628 #pragma omp for collapse(3)
11629  cimg_forYZC(*this,y,z,c) {
11630  T *ptrd = data(0,y,z,c);
11631  cimg_forX(*this,x) { *ptrd = (T)((long)*ptrd << (int)lmp(x,y,z,c)); ++ptrd; }
11632  }
11633  }
11634  else
11635 #endif
11636  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd << (int)mp(x,y,z,c)); ++ptrd; }
11637  }
11638  } catch (CImgException&) {
11639  cimg::exception_mode() = omode;
11640  *this<<=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
11641  }
11642  cimg::exception_mode() = omode;
11643  return *this;
11644  }
11645 
11647 
11650  template<typename t>
11651  CImg<T>& operator<<=(const CImg<t>& img) {
11652  const unsigned long siz = size(), isiz = img.size();
11653  if (siz && isiz) {
11654  if (is_overlapped(img)) return *this^=+img;
11655  T *ptrd = _data, *const ptre = _data + siz;
11656  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
11657  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
11658  *ptrd = (T)((long)*ptrd << (int)*(ptrs++));
11659  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((long)*ptrd << (int)*(ptrs++));
11660  }
11661  return *this;
11662  }
11663 
11665 
11669  template<typename t>
11670  CImg<T> operator<<(const t value) const {
11671  return (+*this)<<=value;
11672  }
11673 
11675 
11679  CImg<T> operator<<(const char *const expression) const {
11680  return (+*this)<<=expression;
11681  }
11682 
11684 
11689  template<typename t>
11690  CImg<T> operator<<(const CImg<t>& img) const {
11691  return (+*this)<<=img;
11692  }
11693 
11695 
11698  template<typename t>
11699  CImg<T>& operator>>=(const t value) {
11700  if (is_empty()) return *this;
11701 #ifdef cimg_use_openmp
11702 #pragma omp parallel for if (size()>=65536)
11703 #endif
11704  cimg_rof(*this,ptrd,T) *ptrd = (T)(((long)*ptrd) >> (int)value);
11705  return *this;
11706  }
11707 
11709 
11712  CImg<T>& operator>>=(const char *const expression) {
11713  if (is_empty()) return *this;
11714  const unsigned int omode = cimg::exception_mode();
11715  cimg::exception_mode() = 0;
11716  try {
11717  const CImg<T> _base = cimg::_is_self_expr(expression)?+*this:CImg<T>(), &base = _base?_base:*this;
11718  _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"operator<<=");
11719  T *ptrd = *expression=='<'?end()-1:_data;
11720  if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd >> (int)mp(x,y,z,c)); --ptrd; }
11721  else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd >> (int)mp(x,y,z,c)); ++ptrd; }
11722  else {
11723 #ifdef cimg_use_openmp
11724  if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)
11725 #pragma omp parallel
11726  {
11727  _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp;
11728 #pragma omp for collapse(3)
11729  cimg_forYZC(*this,y,z,c) {
11730  T *ptrd = data(0,y,z,c);
11731  cimg_forX(*this,x) { *ptrd = (T)((long)*ptrd >> (int)lmp(x,y,z,c)); ++ptrd; }
11732  }
11733  }
11734  else
11735 #endif
11736  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd >> (int)mp(x,y,z,c)); ++ptrd; }
11737  }
11738  } catch (CImgException&) {
11739  cimg::exception_mode() = omode;
11740  *this>>=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
11741  }
11742  cimg::exception_mode() = omode;
11743  return *this;
11744  }
11745 
11747 
11750  template<typename t>
11751  CImg<T>& operator>>=(const CImg<t>& img) {
11752  const unsigned long siz = size(), isiz = img.size();
11753  if (siz && isiz) {
11754  if (is_overlapped(img)) return *this^=+img;
11755  T *ptrd = _data, *const ptre = _data + siz;
11756  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
11757  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
11758  *ptrd = (T)((long)*ptrd >> (int)*(ptrs++));
11759  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((long)*ptrd >> (int)*(ptrs++));
11760  }
11761  return *this;
11762  }
11763 
11765 
11769  template<typename t>
11770  CImg<T> operator>>(const t value) const {
11771  return (+*this)>>=value;
11772  }
11773 
11775 
11779  CImg<T> operator>>(const char *const expression) const {
11780  return (+*this)>>=expression;
11781  }
11782 
11784 
11789  template<typename t>
11790  CImg<T> operator>>(const CImg<t>& img) const {
11791  return (+*this)>>=img;
11792  }
11793 
11795 
11798  CImg<T> operator~() const {
11799  CImg<T> res(_width,_height,_depth,_spectrum);
11800  const T *ptrs = _data;
11801  cimg_for(res,ptrd,T) { const unsigned long value = (unsigned long)*(ptrs++); *ptrd = (T)~value; }
11802  return res;
11803  }
11804 
11806 
11810  template<typename t>
11811  bool operator==(const t value) const {
11812  if (is_empty()) return false;
11813  typedef _cimg_Tt Tt;
11814  bool is_equal = true;
11815  for (T *ptrd = _data + size(); is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)value)) {}
11816  return is_equal;
11817  }
11818 
11820 
11824  bool operator==(const char *const expression) const {
11825  if (is_empty()) return !*expression;
11826  const unsigned int omode = cimg::exception_mode();
11827  cimg::exception_mode() = 0;
11828  bool is_equal = true;
11829  try {
11830  const CImg<T> _base = cimg::_is_self_expr(expression)?+*this:CImg<T>(), &base = _base?_base:*this;
11831  _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"operator<<=");
11832  const T *ptrs = *expression=='<'?end()-1:_data;
11833  if (*expression=='<')
11834  cimg_rofXYZC(*this,x,y,z,c) { if (!is_equal) break; is_equal = ((double)*(ptrs--)==mp(x,y,z,c)); }
11835  else if (*expression=='>')
11836  cimg_forXYZC(*this,x,y,z,c) { if (!is_equal) break; is_equal = ((double)*(ptrs++)==mp(x,y,z,c)); }
11837  else cimg_forXYZC(*this,x,y,z,c) { if (!is_equal) break; is_equal = ((double)*(ptrs++)==mp(x,y,z,c)); }
11838  } catch (CImgException&) {
11839  cimg::exception_mode() = omode;
11840  is_equal = (*this==CImg<T>(_width,_height,_depth,_spectrum,expression,true));
11841  }
11842  cimg::exception_mode() = omode;
11843  return is_equal;
11844  }
11845 
11847 
11865  template<typename t>
11866  bool operator==(const CImg<t>& img) const {
11867  typedef _cimg_Tt Tt;
11868  const unsigned long siz = size();
11869  bool is_equal = true;
11870  if (siz!=img.size()) return false;
11871  t *ptrs = img._data + siz;
11872  for (T *ptrd = _data + siz; is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)*(--ptrs))) {}
11873  return is_equal;
11874  }
11875 
11877 
11881  template<typename t>
11882  bool operator!=(const t value) const {
11883  return !((*this)==value);
11884  }
11885 
11887 
11891  bool operator!=(const char *const expression) const {
11892  return !((*this)==expression);
11893  }
11894 
11896 
11903  template<typename t>
11904  bool operator!=(const CImg<t>& img) const {
11905  return !((*this)==img);
11906  }
11907 
11909 
11938  template<typename t>
11940  return CImgList<_cimg_Tt>(*this,img);
11941  }
11942 
11944 
11953  template<typename t>
11955  return CImgList<_cimg_Tt>(list,false).insert(*this,0);
11956  }
11957 
11959 
11973  CImgList<T> operator<(const char axis) const {
11974  return get_split(axis);
11975  }
11976 
11978  //-------------------------------------
11979  //
11981 
11982  //-------------------------------------
11983 
11985 
11992  static const char* pixel_type() {
11993  return cimg::type<T>::string();
11994  }
11995 
11997 
12008  int width() const {
12009  return (int)_width;
12010  }
12011 
12013 
12023  int height() const {
12024  return (int)_height;
12025  }
12026 
12028 
12040  int depth() const {
12041  return (int)_depth;
12042  }
12043 
12045 
12059  int spectrum() const {
12060  return (int)_spectrum;
12061  }
12062 
12064 
12079  unsigned long size() const {
12080  return (unsigned long)_width*_height*_depth*_spectrum;
12081  }
12082 
12084 
12094  T* data() {
12095  return _data;
12096  }
12097 
12099  const T* data() const {
12100  return _data;
12101  }
12102 
12104 
12116 #if cimg_verbosity>=3
12117  T *data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
12118  const unsigned long off = (unsigned long)offset(x,y,z,c);
12119  if (off>=size())
12120  cimg::warn(_cimg_instance
12121  "data(): Invalid pointer request, at coordinates (%u,%u,%u,%u) [offset=%u].",
12122  cimg_instance,
12123  x,y,z,c,off);
12124  return _data + off;
12125  }
12126 
12128  const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
12129  return const_cast<CImg<T>*>(this)->data(x,y,z,c);
12130  }
12131 #else
12132  T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
12133  return _data + x + y*(unsigned long)_width + z*(unsigned long)_width*_height +
12134  c*(unsigned long)_width*_height*_depth;
12135  }
12136 
12137  const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
12138  return _data + x + y*_width + z*(unsigned long)_width*_height + c*(unsigned long)_width*_height*_depth;
12139  }
12140 #endif
12141 
12143 
12158  long offset(const int x, const int y=0, const int z=0, const int c=0) const {
12159  return x + y*(long)_width + z*(long)_width*_height + c*(long)_width*_height*_depth;
12160  }
12161 
12163 
12168  iterator begin() {
12169  return _data;
12170  }
12171 
12173  const_iterator begin() const {
12174  return _data;
12175  }
12176 
12178 
12193  iterator end() {
12194  return _data + size();
12195  }
12196 
12198  const_iterator end() const {
12199  return _data + size();
12200  }
12201 
12203 
12208  T& front() {
12209  return *_data;
12210  }
12211 
12213  const T& front() const {
12214  return *_data;
12215  }
12216 
12218 
12224  T& back() {
12225  return *(_data + size() - 1);
12226  }
12227 
12229  const T& back() const {
12230  return *(_data + size() - 1);
12231  }
12232 
12234 
12246  T& at(const int offset, const T out_value) {
12247  return (offset<0 || offset>=(int)size())?(cimg::temporary(out_value)=out_value):(*this)[offset];
12248  }
12249 
12251  T at(const int offset, const T out_value) const {
12252  return (offset<0 || offset>=(int)size())?out_value:(*this)[offset];
12253  }
12254 
12256 
12269  T& at(const int offset) {
12270  if (is_empty())
12271  throw CImgInstanceException(_cimg_instance
12272  "at(): Empty instance.",
12273  cimg_instance);
12274  return _at(offset);
12275  }
12276 
12277  T& _at(const int offset) {
12278  const unsigned int siz = (unsigned int)size();
12279  return (*this)[offset<0?0:(unsigned int)offset>=siz?siz-1:offset];
12280  }
12281 
12283  T at(const int offset) const {
12284  if (is_empty())
12285  throw CImgInstanceException(_cimg_instance
12286  "at(): Empty instance.",
12287  cimg_instance);
12288  return _at(offset);
12289  }
12290 
12291  T _at(const int offset) const {
12292  const unsigned int siz = (unsigned int)size();
12293  return (*this)[offset<0?0:(unsigned int)offset>=siz?siz-1:offset];
12294  }
12295 
12297 
12313  T& atX(const int x, const int y, const int z, const int c, const T out_value) {
12314  return (x<0 || x>=width())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
12315  }
12316 
12318  T atX(const int x, const int y, const int z, const int c, const T out_value) const {
12319  return (x<0 || x>=width())?out_value:(*this)(x,y,z,c);
12320  }
12321 
12323 
12340  T& atX(const int x, const int y=0, const int z=0, const int c=0) {
12341  if (is_empty())
12342  throw CImgInstanceException(_cimg_instance
12343  "atX(): Empty instance.",
12344  cimg_instance);
12345  return _atX(x,y,z,c);
12346  }
12347 
12348  T& _atX(const int x, const int y=0, const int z=0, const int c=0) {
12349  return (*this)(x<0?0:(x>=width()?width()-1:x),y,z,c);
12350  }
12351 
12353  T atX(const int x, const int y=0, const int z=0, const int c=0) const {
12354  if (is_empty())
12355  throw CImgInstanceException(_cimg_instance
12356  "atX(): Empty instance.",
12357  cimg_instance);
12358  return _atX(x,y,z,c);
12359  }
12360 
12361  T _atX(const int x, const int y=0, const int z=0, const int c=0) const {
12362  return (*this)(x<0?0:(x>=width()?width()-1:x),y,z,c);
12363  }
12364 
12366 
12369  T& atXY(const int x, const int y, const int z, const int c, const T out_value) {
12370  return (x<0 || y<0 || x>=width() || y>=height())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
12371  }
12372 
12374  T atXY(const int x, const int y, const int z, const int c, const T out_value) const {
12375  return (x<0 || y<0 || x>=width() || y>=height())?out_value:(*this)(x,y,z,c);
12376  }
12377 
12379 
12385  T& atXY(const int x, const int y, const int z=0, const int c=0) {
12386  if (is_empty())
12387  throw CImgInstanceException(_cimg_instance
12388  "atXY(): Empty instance.",
12389  cimg_instance);
12390  return _atXY(x,y,z,c);
12391  }
12392 
12393  T& _atXY(const int x, const int y, const int z=0, const int c=0) {
12394  return (*this)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1:y),z,c);
12395  }
12396 
12398  T atXY(const int x, const int y, const int z=0, const int c=0) const {
12399  if (is_empty())
12400  throw CImgInstanceException(_cimg_instance
12401  "atXY(): Empty instance.",
12402  cimg_instance);
12403  return _atXY(x,y,z,c);
12404  }
12405 
12406  T _atXY(const int x, const int y, const int z=0, const int c=0) const {
12407  return (*this)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1:y),z,c);
12408  }
12409 
12411 
12415  T& atXYZ(const int x, const int y, const int z, const int c, const T out_value) {
12416  return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?
12417  (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
12418  }
12419 
12421  T atXYZ(const int x, const int y, const int z, const int c, const T out_value) const {
12422  return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?out_value:(*this)(x,y,z,c);
12423  }
12424 
12426 
12432  T& atXYZ(const int x, const int y, const int z, const int c=0) {
12433  if (is_empty())
12434  throw CImgInstanceException(_cimg_instance
12435  "atXYZ(): Empty instance.",
12436  cimg_instance);
12437  return _atXYZ(x,y,z,c);
12438  }
12439 
12440  T& _atXYZ(const int x, const int y, const int z, const int c=0) {
12441  return (*this)(x<0?0:(x>=width()?width()-1:x),y<0?0:(y>=height()?height()-1:y),
12442  z<0?0:(z>=depth()?depth()-1:z),c);
12443  }
12444 
12446  T atXYZ(const int x, const int y, const int z, const int c=0) const {
12447  if (is_empty())
12448  throw CImgInstanceException(_cimg_instance
12449  "atXYZ(): Empty instance.",
12450  cimg_instance);
12451  return _atXYZ(x,y,z,c);
12452  }
12453 
12454  T _atXYZ(const int x, const int y, const int z, const int c=0) const {
12455  return (*this)(x<0?0:(x>=width()?width()-1:x),y<0?0:(y>=height()?height()-1:y),
12456  z<0?0:(z>=depth()?depth()-1:z),c);
12457  }
12458 
12460 
12464  T& atXYZC(const int x, const int y, const int z, const int c, const T out_value) {
12465  return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?
12466  (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
12467  }
12468 
12470  T atXYZC(const int x, const int y, const int z, const int c, const T out_value) const {
12471  return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?out_value:
12472  (*this)(x,y,z,c);
12473  }
12474 
12476 
12482  T& atXYZC(const int x, const int y, const int z, const int c) {
12483  if (is_empty())
12484  throw CImgInstanceException(_cimg_instance
12485  "atXYZC(): Empty instance.",
12486  cimg_instance);
12487  return _atXYZC(x,y,z,c);
12488  }
12489 
12490  T& _atXYZC(const int x, const int y, const int z, const int c) {
12491  return (*this)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1:y),
12492  z<0?0:(z>=depth()?depth()-1:z), c<0?0:(c>=spectrum()?spectrum()-1:c));
12493  }
12494 
12496  T atXYZC(const int x, const int y, const int z, const int c) const {
12497  if (is_empty())
12498  throw CImgInstanceException(_cimg_instance
12499  "atXYZC(): Empty instance.",
12500  cimg_instance);
12501  return _atXYZC(x,y,z,c);
12502  }
12503 
12504  T _atXYZC(const int x, const int y, const int z, const int c) const {
12505  return (*this)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1:y),
12506  z<0?0:(z>=depth()?depth()-1:z), c<0?0:(c>=spectrum()?spectrum()-1:c));
12507  }
12508 
12510 
12525  Tfloat linear_atX(const float fx, const int y, const int z, const int c, const T out_value) const {
12526  const int
12527  x = (int)fx - (fx>=0?0:1), nx = x + 1;
12528  const float
12529  dx = fx - x;
12530  const Tfloat
12531  Ic = (Tfloat)atX(x,y,z,c,out_value), In = (Tfloat)atXY(nx,y,z,c,out_value);
12532  return Ic + dx*(In-Ic);
12533  }
12534 
12536 
12552  Tfloat linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
12553  if (is_empty())
12554  throw CImgInstanceException(_cimg_instance
12555  "linear_atX(): Empty instance.",
12556  cimg_instance);
12557 
12558  return _linear_atX(fx,y,z,c);
12559  }
12560 
12561  Tfloat _linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
12562  const float
12563  nfx = fx<0?0:(fx>_width-1?_width-1:fx);
12564  const unsigned int
12565  x = (unsigned int)nfx;
12566  const float
12567  dx = nfx - x;
12568  const unsigned int
12569  nx = dx>0?x+1:x;
12570  const Tfloat
12571  Ic = (Tfloat)(*this)(x,y,z,c), In = (Tfloat)(*this)(nx,y,z,c);
12572  return Ic + dx*(In-Ic);
12573  }
12574 
12576 
12580  Tfloat linear_atXY(const float fx, const float fy, const int z, const int c, const T out_value) const {
12581  const int
12582  x = (int)fx - (fx>=0?0:1), nx = x + 1,
12583  y = (int)fy - (fy>=0?0:1), ny = y + 1;
12584  const float
12585  dx = fx - x,
12586  dy = fy - y;
12587  const Tfloat
12588  Icc = (Tfloat)atXY(x,y,z,c,out_value), Inc = (Tfloat)atXY(nx,y,z,c,out_value),
12589  Icn = (Tfloat)atXY(x,ny,z,c,out_value), Inn = (Tfloat)atXY(nx,ny,z,c,out_value);
12590  return Icc + dx*(Inc-Icc + dy*(Icc+Inn-Icn-Inc)) + dy*(Icn-Icc);
12591  }
12592 
12594 
12601  Tfloat linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
12602  if (is_empty())
12603  throw CImgInstanceException(_cimg_instance
12604  "linear_atXY(): Empty instance.",
12605  cimg_instance);
12606 
12607  return _linear_atXY(fx,fy,z,c);
12608  }
12609 
12610  Tfloat _linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
12611  const float
12612  nfx = fx<0?0:(fx>_width-1?_width-1:fx),
12613  nfy = fy<0?0:(fy>_height-1?_height-1:fy);
12614  const unsigned int
12615  x = (unsigned int)nfx,
12616  y = (unsigned int)nfy;
12617  const float
12618  dx = nfx - x,
12619  dy = nfy - y;
12620  const unsigned int
12621  nx = dx>0?x+1:x,
12622  ny = dy>0?y+1:y;
12623  const Tfloat
12624  Icc = (Tfloat)(*this)(x,y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c),
12625  Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c);
12626  return Icc + dx*(Inc-Icc + dy*(Icc+Inn-Icn-Inc)) + dy*(Icn-Icc);
12627  }
12628 
12630 
12634  Tfloat linear_atXYZ(const float fx, const float fy, const float fz, const int c, const T out_value) const {
12635  const int
12636  x = (int)fx - (fx>=0?0:1), nx = x + 1,
12637  y = (int)fy - (fy>=0?0:1), ny = y + 1,
12638  z = (int)fz - (fz>=0?0:1), nz = z + 1;
12639  const float
12640  dx = fx - x,
12641  dy = fy - y,
12642  dz = fz - z;
12643  const Tfloat
12644  Iccc = (Tfloat)atXYZ(x,y,z,c,out_value), Incc = (Tfloat)atXYZ(nx,y,z,c,out_value),
12645  Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value), Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value),
12646  Iccn = (Tfloat)atXYZ(x,y,nz,c,out_value), Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value),
12647  Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value), Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value);
12648  return Iccc +
12649  dx*(Incc-Iccc +
12650  dy*(Iccc+Innc-Icnc-Incc +
12651  dz*(Iccn+Innn+Icnc+Incc-Icnn-Incn-Iccc-Innc)) +
12652  dz*(Iccc+Incn-Iccn-Incc)) +
12653  dy*(Icnc-Iccc +
12654  dz*(Iccc+Icnn-Iccn-Icnc)) +
12655  dz*(Iccn-Iccc);
12656  }
12657 
12659 
12666  Tfloat linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const {
12667  if (is_empty())
12668  throw CImgInstanceException(_cimg_instance
12669  "linear_atXYZ(): Empty instance.",
12670  cimg_instance);
12671 
12672  return _linear_atXYZ(fx,fy,fz,c);
12673  }
12674 
12675  Tfloat _linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const {
12676  const float
12677  nfx = fx<0?0:(fx>_width-1?_width-1:fx),
12678  nfy = fy<0?0:(fy>_height-1?_height-1:fy),
12679  nfz = fz<0?0:(fz>_depth-1?_depth-1:fz);
12680  const unsigned int
12681  x = (unsigned int)nfx,
12682  y = (unsigned int)nfy,
12683  z = (unsigned int)nfz;
12684  const float
12685  dx = nfx - x,
12686  dy = nfy - y,
12687  dz = nfz - z;
12688  const unsigned int
12689  nx = dx>0?x+1:x,
12690  ny = dy>0?y+1:y,
12691  nz = dz>0?z+1:z;
12692  const Tfloat
12693  Iccc = (Tfloat)(*this)(x,y,z,c), Incc = (Tfloat)(*this)(nx,y,z,c),
12694  Icnc = (Tfloat)(*this)(x,ny,z,c), Innc = (Tfloat)(*this)(nx,ny,z,c),
12695  Iccn = (Tfloat)(*this)(x,y,nz,c), Incn = (Tfloat)(*this)(nx,y,nz,c),
12696  Icnn = (Tfloat)(*this)(x,ny,nz,c), Innn = (Tfloat)(*this)(nx,ny,nz,c);
12697  return Iccc +
12698  dx*(Incc-Iccc +
12699  dy*(Iccc+Innc-Icnc-Incc +
12700  dz*(Iccn+Innn+Icnc+Incc-Icnn-Incn-Iccc-Innc)) +
12701  dz*(Iccc+Incn-Iccn-Incc)) +
12702  dy*(Icnc-Iccc +
12703  dz*(Iccc+Icnn-Iccn-Icnc)) +
12704  dz*(Iccn-Iccc);
12705  }
12706 
12708 
12712  Tfloat linear_atXYZC(const float fx, const float fy, const float fz, const float fc, const T out_value) const {
12713  const int
12714  x = (int)fx - (fx>=0?0:1), nx = x + 1,
12715  y = (int)fy - (fy>=0?0:1), ny = y + 1,
12716  z = (int)fz - (fz>=0?0:1), nz = z + 1,
12717  c = (int)fc - (fc>=0?0:1), nc = c + 1;
12718  const float
12719  dx = fx - x,
12720  dy = fy - y,
12721  dz = fz - z,
12722  dc = fc - c;
12723  const Tfloat
12724  Icccc = (Tfloat)atXYZC(x,y,z,c,out_value), Inccc = (Tfloat)atXYZC(nx,y,z,c,out_value),
12725  Icncc = (Tfloat)atXYZC(x,ny,z,c,out_value), Inncc = (Tfloat)atXYZC(nx,ny,z,c,out_value),
12726  Iccnc = (Tfloat)atXYZC(x,y,nz,c,out_value), Incnc = (Tfloat)atXYZC(nx,y,nz,c,out_value),
12727  Icnnc = (Tfloat)atXYZC(x,ny,nz,c,out_value), Innnc = (Tfloat)atXYZC(nx,ny,nz,c,out_value),
12728  Icccn = (Tfloat)atXYZC(x,y,z,nc,out_value), Inccn = (Tfloat)atXYZC(nx,y,z,nc,out_value),
12729  Icncn = (Tfloat)atXYZC(x,ny,z,nc,out_value), Inncn = (Tfloat)atXYZC(nx,ny,z,nc,out_value),
12730  Iccnn = (Tfloat)atXYZC(x,y,nz,nc,out_value), Incnn = (Tfloat)atXYZC(nx,y,nz,nc,out_value),
12731  Icnnn = (Tfloat)atXYZC(x,ny,nz,nc,out_value), Innnn = (Tfloat)atXYZC(nx,ny,nz,nc,out_value);
12732  return Icccc +
12733  dx*(Inccc-Icccc +
12734  dy*(Icccc+Inncc-Icncc-Inccc +
12735  dz*(Iccnc+Innnc+Icncc+Inccc-Icnnc-Incnc-Icccc-Inncc +
12736  dc*(Iccnn+Innnn+Icncn+Inccn+Icnnc+Incnc+Icccc+Inncc-
12737  Icnnn-Incnn-Icccn-Inncn-Iccnc-Innnc-Icncc-Inccc)) +
12738  dc*(Icccn+Inncn+Icncc+Inccc-Icncn-Inccn-Icccc-Inncc)) +
12739  dz*(Icccc+Incnc-Iccnc-Inccc +
12740  dc*(Icccn+Incnn+Iccnc+Inccc-Iccnn-Inccn-Icccc-Incnc)) +
12741  dc*(Icccc+Inccn-Inccc-Icccn)) +
12742  dy*(Icncc-Icccc +
12743  dz*(Icccc+Icnnc-Iccnc-Icncc +
12744  dc*(Icccn+Icnnn+Iccnc+Icncc-Iccnn-Icncn-Icccc-Icnnc)) +
12745  dc*(Icccc+Icncn-Icncc-Icccn)) +
12746  dz*(Iccnc-Icccc +
12747  dc*(Icccc+Iccnn-Iccnc-Icccn)) +
12748  dc*(Icccn-Icccc);
12749  }
12750 
12752 
12759  Tfloat linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
12760  if (is_empty())
12761  throw CImgInstanceException(_cimg_instance
12762  "linear_atXYZC(): Empty instance.",
12763  cimg_instance);
12764 
12765  return _linear_atXYZC(fx,fy,fz,fc);
12766  }
12767 
12768  Tfloat _linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
12769  const float
12770  nfx = fx<0?0:(fx>_width-1?_width-1:fx),
12771  nfy = fy<0?0:(fy>_height-1?_height-1:fy),
12772  nfz = fz<0?0:(fz>_depth-1?_depth-1:fz),
12773  nfc = fc<0?0:(fc>_spectrum-1?_spectrum-1:fc);
12774  const unsigned int
12775  x = (unsigned int)nfx,
12776  y = (unsigned int)nfy,
12777  z = (unsigned int)nfz,
12778  c = (unsigned int)nfc;
12779  const float
12780  dx = nfx - x,
12781  dy = nfy - y,
12782  dz = nfz - z,
12783  dc = nfc - c;
12784  const unsigned int
12785  nx = dx>0?x+1:x,
12786  ny = dy>0?y+1:y,
12787  nz = dz>0?z+1:z,
12788  nc = dc>0?c+1:c;
12789  const Tfloat
12790  Icccc = (Tfloat)(*this)(x,y,z,c), Inccc = (Tfloat)(*this)(nx,y,z,c),
12791  Icncc = (Tfloat)(*this)(x,ny,z,c), Inncc = (Tfloat)(*this)(nx,ny,z,c),
12792  Iccnc = (Tfloat)(*this)(x,y,nz,c), Incnc = (Tfloat)(*this)(nx,y,nz,c),
12793  Icnnc = (Tfloat)(*this)(x,ny,nz,c), Innnc = (Tfloat)(*this)(nx,ny,nz,c),
12794  Icccn = (Tfloat)(*this)(x,y,z,nc), Inccn = (Tfloat)(*this)(nx,y,z,nc),
12795  Icncn = (Tfloat)(*this)(x,ny,z,nc), Inncn = (Tfloat)(*this)(nx,ny,z,nc),
12796  Iccnn = (Tfloat)(*this)(x,y,nz,nc), Incnn = (Tfloat)(*this)(nx,y,nz,nc),
12797  Icnnn = (Tfloat)(*this)(x,ny,nz,nc), Innnn = (Tfloat)(*this)(nx,ny,nz,nc);
12798  return Icccc +
12799  dx*(Inccc-Icccc +
12800  dy*(Icccc+Inncc-Icncc-Inccc +
12801  dz*(Iccnc+Innnc+Icncc+Inccc-Icnnc-Incnc-Icccc-Inncc +
12802  dc*(Iccnn+Innnn+Icncn+Inccn+Icnnc+Incnc+Icccc+Inncc-
12803  Icnnn-Incnn-Icccn-Inncn-Iccnc-Innnc-Icncc-Inccc)) +
12804  dc*(Icccn+Inncn+Icncc+Inccc-Icncn-Inccn-Icccc-Inncc)) +
12805  dz*(Icccc+Incnc-Iccnc-Inccc +
12806  dc*(Icccn+Incnn+Iccnc+Inccc-Iccnn-Inccn-Icccc-Incnc)) +
12807  dc*(Icccc+Inccn-Inccc-Icccn)) +
12808  dy*(Icncc-Icccc +
12809  dz*(Icccc+Icnnc-Iccnc-Icncc +
12810  dc*(Icccn+Icnnn+Iccnc+Icncc-Iccnn-Icncn-Icccc-Icnnc)) +
12811  dc*(Icccc+Icncn-Icncc-Icccn)) +
12812  dz*(Iccnc-Icccc +
12813  dc*(Icccc+Iccnn-Iccnc-Icccn)) +
12814  dc*(Icccn-Icccc);
12815  }
12816 
12818 
12834  Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T out_value) const {
12835  const int
12836  x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2;
12837  const float
12838  dx = fx - x;
12839  const Tfloat
12840  Ip = (Tfloat)atX(px,y,z,c,out_value), Ic = (Tfloat)atX(x,y,z,c,out_value),
12841  In = (Tfloat)atX(nx,y,z,c,out_value), Ia = (Tfloat)atX(ax,y,z,c,out_value);
12842  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));
12843  }
12844 
12846 
12850  Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T out_value,
12851  const Tfloat min_value, const Tfloat max_value) const {
12852  const Tfloat val = cubic_atX(fx,y,z,c,out_value);
12853  return val<min_value?min_value:val>max_value?max_value:val;
12854  }
12855 
12857 
12873  Tfloat cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
12874  if (is_empty())
12875  throw CImgInstanceException(_cimg_instance
12876  "cubic_atX(): Empty instance.",
12877  cimg_instance);
12878  return _cubic_atX(fx,y,z,c);
12879  }
12880 
12881  Tfloat _cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
12882  const float
12883  nfx = fx<0?0:(fx>_width-1?_width-1:fx);
12884  const int
12885  x = (int)nfx;
12886  const float
12887  dx = nfx - x;
12888  const int
12889  px = x-1<0?0:x-1, nx = dx>0?x+1:x, ax = x+2>=width()?width()-1:x+2;
12890  const Tfloat
12891  Ip = (Tfloat)(*this)(px,y,z,c), Ic = (Tfloat)(*this)(x,y,z,c),
12892  In = (Tfloat)(*this)(nx,y,z,c), Ia = (Tfloat)(*this)(ax,y,z,c);
12893  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));
12894  }
12895 
12897 
12901  Tfloat cubic_atX(const float fx, const int y, const int z, const int c,
12902  const Tfloat min_value, const Tfloat max_value) const {
12903  const Tfloat val = cubic_atX(fx,y,z,c);
12904  return val<min_value?min_value:val>max_value?max_value:val;
12905  }
12906 
12907  Tfloat _cubic_atX(const float fx, const int y, const int z, const int c,
12908  const Tfloat min_value, const Tfloat max_value) const {
12909  const Tfloat val = _cubic_atX(fx,y,z,c);
12910  return val<min_value?min_value:val>max_value?max_value:val;
12911  }
12912 
12914 
12918  Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T out_value) const {
12919  const int
12920  x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2,
12921  y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2;
12922  const float dx = fx - x, dy = fy - y;
12923  const Tfloat
12924  Ipp = (Tfloat)atXY(px,py,z,c,out_value), Icp = (Tfloat)atXY(x,py,z,c,out_value),
12925  Inp = (Tfloat)atXY(nx,py,z,c,out_value), Iap = (Tfloat)atXY(ax,py,z,c,out_value),
12926  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)),
12927  Ipc = (Tfloat)atXY(px,y,z,c,out_value), Icc = (Tfloat)atXY(x, y,z,c,out_value),
12928  Inc = (Tfloat)atXY(nx,y,z,c,out_value), Iac = (Tfloat)atXY(ax,y,z,c,out_value),
12929  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)),
12930  Ipn = (Tfloat)atXY(px,ny,z,c,out_value), Icn = (Tfloat)atXY(x,ny,z,c,out_value),
12931  Inn = (Tfloat)atXY(nx,ny,z,c,out_value), Ian = (Tfloat)atXY(ax,ny,z,c,out_value),
12932  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)),
12933  Ipa = (Tfloat)atXY(px,ay,z,c,out_value), Ica = (Tfloat)atXY(x,ay,z,c,out_value),
12934  Ina = (Tfloat)atXY(nx,ay,z,c,out_value), Iaa = (Tfloat)atXY(ax,ay,z,c,out_value),
12935  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));
12936  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));
12937  }
12938 
12940 
12944  Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T out_value,
12945  const Tfloat min_value, const Tfloat max_value) const {
12946  const Tfloat val = cubic_atXY(fx,fy,z,c,out_value);
12947  return val<min_value?min_value:val>max_value?max_value:val;
12948  }
12949 
12951 
12958  Tfloat cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
12959  if (is_empty())
12960  throw CImgInstanceException(_cimg_instance
12961  "cubic_atXY(): Empty instance.",
12962  cimg_instance);
12963  return _cubic_atXY(fx,fy,z,c);
12964  }
12965 
12966  Tfloat _cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
12967  const float
12968  nfx = fx<0?0:(fx>_width-1?_width-1:fx),
12969  nfy = fy<0?0:(fy>_height-1?_height-1:fy);
12970  const int x = (int)nfx, y = (int)nfy;
12971  const float dx = nfx - x, dy = nfy - y;
12972  const int
12973  px = x-1<0?0:x-1, nx = dx>0?x+1:x, ax = x+2>=width()?width()-1:x+2,
12974  py = y-1<0?0:y-1, ny = dy>0?y+1:y, ay = y+2>=height()?height()-1:y+2;
12975  const Tfloat
12976  Ipp = (Tfloat)(*this)(px,py,z,c), Icp = (Tfloat)(*this)(x,py,z,c), Inp = (Tfloat)(*this)(nx,py,z,c),
12977  Iap = (Tfloat)(*this)(ax,py,z,c),
12978  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)),
12979  Ipc = (Tfloat)(*this)(px,y,z,c), Icc = (Tfloat)(*this)(x, y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c),
12980  Iac = (Tfloat)(*this)(ax,y,z,c),
12981  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)),
12982  Ipn = (Tfloat)(*this)(px,ny,z,c), Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c),
12983  Ian = (Tfloat)(*this)(ax,ny,z,c),
12984  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)),
12985  Ipa = (Tfloat)(*this)(px,ay,z,c), Ica = (Tfloat)(*this)(x,ay,z,c), Ina = (Tfloat)(*this)(nx,ay,z,c),
12986  Iaa = (Tfloat)(*this)(ax,ay,z,c),
12987  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));
12988  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));
12989  }
12990 
12992 
12996  Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c,
12997  const Tfloat min_value, const Tfloat max_value) const {
12998  const Tfloat val = cubic_atXY(fx,fy,z,c);
12999  return val<min_value?min_value:val>max_value?max_value:val;
13000  }
13001 
13002  Tfloat _cubic_atXY(const float fx, const float fy, const int z, const int c,
13003  const Tfloat min_value, const Tfloat max_value) const {
13004  const Tfloat val = _cubic_atXY(fx,fy,z,c);
13005  return val<min_value?min_value:val>max_value?max_value:val;
13006  }
13007 
13009 
13013  Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T out_value) const {
13014  const int
13015  x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2,
13016  y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2,
13017  z = (int)fz - (fz>=0?0:1), pz = z - 1, nz = z + 1, az = z + 2;
13018  const float dx = fx - x, dy = fy - y, dz = fz - z;
13019  const Tfloat
13020  Ippp = (Tfloat)atXYZ(px,py,pz,c,out_value), Icpp = (Tfloat)atXYZ(x,py,pz,c,out_value),
13021  Inpp = (Tfloat)atXYZ(nx,py,pz,c,out_value), Iapp = (Tfloat)atXYZ(ax,py,pz,c,out_value),
13022  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)),
13023  Ipcp = (Tfloat)atXYZ(px,y,pz,c,out_value), Iccp = (Tfloat)atXYZ(x, y,pz,c,out_value),
13024  Incp = (Tfloat)atXYZ(nx,y,pz,c,out_value), Iacp = (Tfloat)atXYZ(ax,y,pz,c,out_value),
13025  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)),
13026  Ipnp = (Tfloat)atXYZ(px,ny,pz,c,out_value), Icnp = (Tfloat)atXYZ(x,ny,pz,c,out_value),
13027  Innp = (Tfloat)atXYZ(nx,ny,pz,c,out_value), Ianp = (Tfloat)atXYZ(ax,ny,pz,c,out_value),
13028  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)),
13029  Ipap = (Tfloat)atXYZ(px,ay,pz,c,out_value), Icap = (Tfloat)atXYZ(x,ay,pz,c,out_value),
13030  Inap = (Tfloat)atXYZ(nx,ay,pz,c,out_value), Iaap = (Tfloat)atXYZ(ax,ay,pz,c,out_value),
13031  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)),
13032  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)),
13033  Ippc = (Tfloat)atXYZ(px,py,z,c,out_value), Icpc = (Tfloat)atXYZ(x,py,z,c,out_value),
13034  Inpc = (Tfloat)atXYZ(nx,py,z,c,out_value), Iapc = (Tfloat)atXYZ(ax,py,z,c,out_value),
13035  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)),
13036  Ipcc = (Tfloat)atXYZ(px,y,z,c,out_value), Iccc = (Tfloat)atXYZ(x, y,z,c,out_value),
13037  Incc = (Tfloat)atXYZ(nx,y,z,c,out_value), Iacc = (Tfloat)atXYZ(ax,y,z,c,out_value),
13038  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)),
13039  Ipnc = (Tfloat)atXYZ(px,ny,z,c,out_value), Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value),
13040  Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value), Ianc = (Tfloat)atXYZ(ax,ny,z,c,out_value),
13041  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)),
13042  Ipac = (Tfloat)atXYZ(px,ay,z,c,out_value), Icac = (Tfloat)atXYZ(x,ay,z,c,out_value),
13043  Inac = (Tfloat)atXYZ(nx,ay,z,c,out_value), Iaac = (Tfloat)atXYZ(ax,ay,z,c,out_value),
13044  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)),
13045  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)),
13046  Ippn = (Tfloat)atXYZ(px,py,nz,c,out_value), Icpn = (Tfloat)atXYZ(x,py,nz,c,out_value),
13047  Inpn = (Tfloat)atXYZ(nx,py,nz,c,out_value), Iapn = (Tfloat)atXYZ(ax,py,nz,c,out_value),
13048  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)),
13049  Ipcn = (Tfloat)atXYZ(px,y,nz,c,out_value), Iccn = (Tfloat)atXYZ(x, y,nz,c,out_value),
13050  Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value), Iacn = (Tfloat)atXYZ(ax,y,nz,c,out_value),
13051  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)),
13052  Ipnn = (Tfloat)atXYZ(px,ny,nz,c,out_value), Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value),
13053  Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value), Iann = (Tfloat)atXYZ(ax,ny,nz,c,out_value),
13054  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)),
13055  Ipan = (Tfloat)atXYZ(px,ay,nz,c,out_value), Ican = (Tfloat)atXYZ(x,ay,nz,c,out_value),
13056  Inan = (Tfloat)atXYZ(nx,ay,nz,c,out_value), Iaan = (Tfloat)atXYZ(ax,ay,nz,c,out_value),
13057  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)),
13058  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)),
13059  Ippa = (Tfloat)atXYZ(px,py,az,c,out_value), Icpa = (Tfloat)atXYZ(x,py,az,c,out_value),
13060  Inpa = (Tfloat)atXYZ(nx,py,az,c,out_value), Iapa = (Tfloat)atXYZ(ax,py,az,c,out_value),
13061  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)),
13062  Ipca = (Tfloat)atXYZ(px,y,az,c,out_value), Icca = (Tfloat)atXYZ(x, y,az,c,out_value),
13063  Inca = (Tfloat)atXYZ(nx,y,az,c,out_value), Iaca = (Tfloat)atXYZ(ax,y,az,c,out_value),
13064  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)),
13065  Ipna = (Tfloat)atXYZ(px,ny,az,c,out_value), Icna = (Tfloat)atXYZ(x,ny,az,c,out_value),
13066  Inna = (Tfloat)atXYZ(nx,ny,az,c,out_value), Iana = (Tfloat)atXYZ(ax,ny,az,c,out_value),
13067  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)),
13068  Ipaa = (Tfloat)atXYZ(px,ay,az,c,out_value), Icaa = (Tfloat)atXYZ(x,ay,az,c,out_value),
13069  Inaa = (Tfloat)atXYZ(nx,ay,az,c,out_value), Iaaa = (Tfloat)atXYZ(ax,ay,az,c,out_value),
13070  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)),
13071  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));
13072  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));
13073  }
13074 
13076 
13080  Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T out_value,
13081  const Tfloat min_value, const Tfloat max_value) const {
13082  const Tfloat val = cubic_atXYZ(fx,fy,fz,c,out_value);
13083  return val<min_value?min_value:val>max_value?max_value:val;
13084  }
13085 
13087 
13094  Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const {
13095  if (is_empty())
13096  throw CImgInstanceException(_cimg_instance
13097  "cubic_atXYZ(): Empty instance.",
13098  cimg_instance);
13099  return _cubic_atXYZ(fx,fy,fz,c);
13100  }
13101 
13102  Tfloat _cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const {
13103  const float
13104  nfx = fx<0?0:(fx>_width-1?_width-1:fx),
13105  nfy = fy<0?0:(fy>_height-1?_height-1:fy),
13106  nfz = fz<0?0:(fz>_depth-1?_depth-1:fz);
13107  const int x = (int)nfx, y = (int)nfy, z = (int)nfz;
13108  const float dx = nfx - x, dy = nfy - y, dz = nfz - z;
13109  const int
13110  px = x-1<0?0:x-1, nx = dx>0?x+1:x, ax = x+2>=width()?width()-1:x+2,
13111  py = y-1<0?0:y-1, ny = dy>0?y+1:y, ay = y+2>=height()?height()-1:y+2,
13112  pz = z-1<0?0:z-1, nz = dz>0?z+1:z, az = z+2>=depth()?depth()-1:z+2;
13113  const Tfloat
13114  Ippp = (Tfloat)(*this)(px,py,pz,c), Icpp = (Tfloat)(*this)(x,py,pz,c),
13115  Inpp = (Tfloat)(*this)(nx,py,pz,c), Iapp = (Tfloat)(*this)(ax,py,pz,c),
13116  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)),
13117  Ipcp = (Tfloat)(*this)(px,y,pz,c), Iccp = (Tfloat)(*this)(x, y,pz,c),
13118  Incp = (Tfloat)(*this)(nx,y,pz,c), Iacp = (Tfloat)(*this)(ax,y,pz,c),
13119  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)),
13120  Ipnp = (Tfloat)(*this)(px,ny,pz,c), Icnp = (Tfloat)(*this)(x,ny,pz,c),
13121  Innp = (Tfloat)(*this)(nx,ny,pz,c), Ianp = (Tfloat)(*this)(ax,ny,pz,c),
13122  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)),
13123  Ipap = (Tfloat)(*this)(px,ay,pz,c), Icap = (Tfloat)(*this)(x,ay,pz,c),
13124  Inap = (Tfloat)(*this)(nx,ay,pz,c), Iaap = (Tfloat)(*this)(ax,ay,pz,c),
13125  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)),
13126  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)),
13127  Ippc = (Tfloat)(*this)(px,py,z,c), Icpc = (Tfloat)(*this)(x,py,z,c),
13128  Inpc = (Tfloat)(*this)(nx,py,z,c), Iapc = (Tfloat)(*this)(ax,py,z,c),
13129  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)),
13130  Ipcc = (Tfloat)(*this)(px,y,z,c), Iccc = (Tfloat)(*this)(x, y,z,c),
13131  Incc = (Tfloat)(*this)(nx,y,z,c), Iacc = (Tfloat)(*this)(ax,y,z,c),
13132  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)),
13133  Ipnc = (Tfloat)(*this)(px,ny,z,c), Icnc = (Tfloat)(*this)(x,ny,z,c),
13134  Innc = (Tfloat)(*this)(nx,ny,z,c), Ianc = (Tfloat)(*this)(ax,ny,z,c),
13135  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)),
13136  Ipac = (Tfloat)(*this)(px,ay,z,c), Icac = (Tfloat)(*this)(x,ay,z,c),
13137  Inac = (Tfloat)(*this)(nx,ay,z,c), Iaac = (Tfloat)(*this)(ax,ay,z,c),
13138  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)),
13139  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)),
13140  Ippn = (Tfloat)(*this)(px,py,nz,c), Icpn = (Tfloat)(*this)(x,py,nz,c),
13141  Inpn = (Tfloat)(*this)(nx,py,nz,c), Iapn = (Tfloat)(*this)(ax,py,nz,c),
13142  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)),
13143  Ipcn = (Tfloat)(*this)(px,y,nz,c), Iccn = (Tfloat)(*this)(x, y,nz,c),
13144  Incn = (Tfloat)(*this)(nx,y,nz,c), Iacn = (Tfloat)(*this)(ax,y,nz,c),
13145  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)),
13146  Ipnn = (Tfloat)(*this)(px,ny,nz,c), Icnn = (Tfloat)(*this)(x,ny,nz,c),
13147  Innn = (Tfloat)(*this)(nx,ny,nz,c), Iann = (Tfloat)(*this)(ax,ny,nz,c),
13148  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)),
13149  Ipan = (Tfloat)(*this)(px,ay,nz,c), Ican = (Tfloat)(*this)(x,ay,nz,c),
13150  Inan = (Tfloat)(*this)(nx,ay,nz,c), Iaan = (Tfloat)(*this)(ax,ay,nz,c),
13151  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)),
13152  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)),
13153  Ippa = (Tfloat)(*this)(px,py,az,c), Icpa = (Tfloat)(*this)(x,py,az,c),
13154  Inpa = (Tfloat)(*this)(nx,py,az,c), Iapa = (Tfloat)(*this)(ax,py,az,c),
13155  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)),
13156  Ipca = (Tfloat)(*this)(px,y,az,c), Icca = (Tfloat)(*this)(x, y,az,c),
13157  Inca = (Tfloat)(*this)(nx,y,az,c), Iaca = (Tfloat)(*this)(ax,y,az,c),
13158  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)),
13159  Ipna = (Tfloat)(*this)(px,ny,az,c), Icna = (Tfloat)(*this)(x,ny,az,c),
13160  Inna = (Tfloat)(*this)(nx,ny,az,c), Iana = (Tfloat)(*this)(ax,ny,az,c),
13161  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)),
13162  Ipaa = (Tfloat)(*this)(px,ay,az,c), Icaa = (Tfloat)(*this)(x,ay,az,c),
13163  Inaa = (Tfloat)(*this)(nx,ay,az,c), Iaaa = (Tfloat)(*this)(ax,ay,az,c),
13164  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)),
13165  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));
13166  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));
13167  }
13168 
13170 
13174  Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c,
13175  const Tfloat min_value, const Tfloat max_value) const {
13176  const Tfloat val = cubic_atXYZ(fx,fy,fz,c);
13177  return val<min_value?min_value:val>max_value?max_value:val;
13178  }
13179 
13180  Tfloat _cubic_atXYZ(const float fx, const float fy, const float fz, const int c,
13181  const Tfloat min_value, const Tfloat max_value) const {
13182  const Tfloat val = _cubic_atXYZ(fx,fy,fz,c);
13183  return val<min_value?min_value:val>max_value?max_value:val;
13184  }
13185 
13187 
13201  CImg<T>& set_linear_atXY(const T& value, const float fx, const float fy=0, const int z=0, const int c=0,
13202  const bool is_added=false) {
13203  const int
13204  x = (int)fx - (fx>=0?0:1), nx = x + 1,
13205  y = (int)fy - (fy>=0?0:1), ny = y + 1;
13206  const float
13207  dx = fx - x,
13208  dy = fy - y;
13209  if (z>=0 && z<depth() && c>=0 && c<spectrum()) {
13210  if (y>=0 && y<height()) {
13211  if (x>=0 && x<width()) {
13212  const float w1 = (1-dx)*(1-dy), w2 = is_added?1:(1-w1);
13213  (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
13214  }
13215  if (nx>=0 && nx<width()) {
13216  const float w1 = dx*(1-dy), w2 = is_added?1:(1-w1);
13217  (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
13218  }
13219  }
13220  if (ny>=0 && ny<height()) {
13221  if (x>=0 && x<width()) {
13222  const float w1 = (1-dx)*dy, w2 = is_added?1:(1-w1);
13223  (*this)(x,ny,z,c) = (T)(w1*value + w2*(*this)(x,ny,z,c));
13224  }
13225  if (nx>=0 && nx<width()) {
13226  const float w1 = dx*dy, w2 = is_added?1:(1-w1);
13227  (*this)(nx,ny,z,c) = (T)(w1*value + w2*(*this)(nx,ny,z,c));
13228  }
13229  }
13230  }
13231  return *this;
13232  }
13233 
13235 
13239  CImg<T>& set_linear_atXYZ(const T& value, const float fx, const float fy=0, const float fz=0, const int c=0,
13240  const bool is_added=false) {
13241  const int
13242  x = (int)fx - (fx>=0?0:1), nx = x + 1,
13243  y = (int)fy - (fy>=0?0:1), ny = y + 1,
13244  z = (int)fz - (fz>=0?0:1), nz = z + 1;
13245  const float
13246  dx = fx - x,
13247  dy = fy - y,
13248  dz = fz - z;
13249  if (c>=0 && c<spectrum()) {
13250  if (z>=0 && z<depth()) {
13251  if (y>=0 && y<height()) {
13252  if (x>=0 && x<width()) {
13253  const float w1 = (1-dx)*(1-dy)*(1-dz), w2 = is_added?1:(1-w1);
13254  (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
13255  }
13256  if (nx>=0 && nx<width()) {
13257  const float w1 = dx*(1-dy)*(1-dz), w2 = is_added?1:(1-w1);
13258  (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
13259  }
13260  }
13261  if (ny>=0 && ny<height()) {
13262  if (x>=0 && x<width()) {
13263  const float w1 = (1-dx)*dy*(1-dz), w2 = is_added?1:(1-w1);
13264  (*this)(x,ny,z,c) = (T)(w1*value + w2*(*this)(x,ny,z,c));
13265  }
13266  if (nx>=0 && nx<width()) {
13267  const float w1 = dx*dy*(1-dz), w2 = is_added?1:(1-w1);
13268  (*this)(nx,ny,z,c) = (T)(w1*value + w2*(*this)(nx,ny,z,c));
13269  }
13270  }
13271  }
13272  if (nz>=0 && nz<depth()) {
13273  if (y>=0 && y<height()) {
13274  if (x>=0 && x<width()) {
13275  const float w1 = (1-dx)*(1-dy)*dz, w2 = is_added?1:(1-w1);
13276  (*this)(x,y,nz,c) = (T)(w1*value + w2*(*this)(x,y,nz,c));
13277  }
13278  if (nx>=0 && nx<width()) {
13279  const float w1 = dx*(1-dy)*dz, w2 = is_added?1:(1-w1);
13280  (*this)(nx,y,nz,c) = (T)(w1*value + w2*(*this)(nx,y,nz,c));
13281  }
13282  }
13283  if (ny>=0 && ny<height()) {
13284  if (x>=0 && x<width()) {
13285  const float w1 = (1-dx)*dy*dz, w2 = is_added?1:(1-w1);
13286  (*this)(x,ny,nz,c) = (T)(w1*value + w2*(*this)(x,ny,nz,c));
13287  }
13288  if (nx>=0 && nx<width()) {
13289  const float w1 = dx*dy*dz, w2 = is_added?1:(1-w1);
13290  (*this)(nx,ny,nz,c) = (T)(w1*value + w2*(*this)(nx,ny,nz,c));
13291  }
13292  }
13293  }
13294  }
13295  return *this;
13296  }
13297 
13299 
13311  CImg<charT> value_string(const char separator=',', const unsigned int max_size=0) const {
13312  if (is_empty()) return CImg<charT>::string("");
13313  CImgList<charT> items;
13314  char s_item[256] = { 0 };
13315  const T *ptrs = _data;
13316  unsigned int string_size = 0;
13317  for (unsigned long off = 0, siz = (unsigned int)size(); off<siz && string_size<=max_size; ++off) {
13318  const unsigned int printed_size = 1U + cimg_snprintf(s_item,sizeof(s_item),
13320  CImg<charT> item(s_item,printed_size);
13321  item[printed_size-1] = separator;
13322  item.move_to(items);
13323  if (max_size) string_size+=printed_size;
13324  }
13325  CImg<charT> res;
13326  (items>'x').move_to(res);
13327  if (max_size && res._width>max_size) res.crop(0,max_size);
13328  res.back() = 0;
13329  return res;
13330  }
13331 
13333  //-------------------------------------
13334  //
13336 
13337  //-------------------------------------
13338 
13340 
13347  bool is_shared() const {
13348  return _is_shared;
13349  }
13350 
13352 
13356  bool is_empty() const {
13357  return !(_data && _width && _height && _depth && _spectrum);
13358  }
13359 
13361 
13364  bool is_inf() const {
13365  if (cimg::type<T>::is_float()) cimg_for(*this,p,T) if (cimg::type<T>::is_inf((float)*p)) return true;
13366  return false;
13367  }
13368 
13370 
13373  bool is_nan() const {
13374  if (cimg::type<T>::is_float()) cimg_for(*this,p,T) if (cimg::type<T>::is_nan((float)*p)) return true;
13375  return false;
13376  }
13377 
13379  bool is_sameX(const unsigned int size_x) const {
13380  return _width==size_x;
13381  }
13382 
13384  template<typename t>
13385  bool is_sameX(const CImg<t>& img) const {
13386  return is_sameX(img._width);
13387  }
13388 
13390  bool is_sameX(const CImgDisplay& disp) const {
13391  return is_sameX(disp._width);
13392  }
13393 
13395  bool is_sameY(const unsigned int size_y) const {
13396  return _height==size_y;
13397  }
13398 
13400  template<typename t>
13401  bool is_sameY(const CImg<t>& img) const {
13402  return is_sameY(img._height);
13403  }
13404 
13406  bool is_sameY(const CImgDisplay& disp) const {
13407  return is_sameY(disp._height);
13408  }
13409 
13411  bool is_sameZ(const unsigned int size_z) const {
13412  return _depth==size_z;
13413  }
13414 
13416  template<typename t>
13417  bool is_sameZ(const CImg<t>& img) const {
13418  return is_sameZ(img._depth);
13419  }
13420 
13422  bool is_sameC(const unsigned int size_c) const {
13423  return _spectrum==size_c;
13424  }
13425 
13427  template<typename t>
13428  bool is_sameC(const CImg<t>& img) const {
13429  return is_sameC(img._spectrum);
13430  }
13431 
13433 
13436  bool is_sameXY(const unsigned int size_x, const unsigned int size_y) const {
13437  return _width==size_x && _height==size_y;
13438  }
13439 
13441 
13444  template<typename t>
13445  bool is_sameXY(const CImg<t>& img) const {
13446  return is_sameXY(img._width,img._height);
13447  }
13448 
13450 
13453  bool is_sameXY(const CImgDisplay& disp) const {
13454  return is_sameXY(disp._width,disp._height);
13455  }
13456 
13458 
13461  bool is_sameXZ(const unsigned int size_x, const unsigned int size_z) const {
13462  return _width==size_x && _depth==size_z;
13463  }
13464 
13466 
13469  template<typename t>
13470  bool is_sameXZ(const CImg<t>& img) const {
13471  return is_sameXZ(img._width,img._depth);
13472  }
13473 
13475 
13478  bool is_sameXC(const unsigned int size_x, const unsigned int size_c) const {
13479  return _width==size_x && _spectrum==size_c;
13480  }
13481 
13483 
13486  template<typename t>
13487  bool is_sameXC(const CImg<t>& img) const {
13488  return is_sameXC(img._width,img._spectrum);
13489  }
13490 
13492 
13495  bool is_sameYZ(const unsigned int size_y, const unsigned int size_z) const {
13496  return _height==size_y && _depth==size_z;
13497  }
13498 
13500 
13503  template<typename t>
13504  bool is_sameYZ(const CImg<t>& img) const {
13505  return is_sameYZ(img._height,img._depth);
13506  }
13507 
13509 
13512  bool is_sameYC(const unsigned int size_y, const unsigned int size_c) const {
13513  return _height==size_y && _spectrum==size_c;
13514  }
13515 
13517 
13520  template<typename t>
13521  bool is_sameYC(const CImg<t>& img) const {
13522  return is_sameYC(img._height,img._spectrum);
13523  }
13524 
13526 
13529  bool is_sameZC(const unsigned int size_z, const unsigned int size_c) const {
13530  return _depth==size_z && _spectrum==size_c;
13531  }
13532 
13534 
13537  template<typename t>
13538  bool is_sameZC(const CImg<t>& img) const {
13539  return is_sameZC(img._depth,img._spectrum);
13540  }
13541 
13543 
13546  bool is_sameXYZ(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z) const {
13547  return is_sameXY(size_x,size_y) && _depth==size_z;
13548  }
13549 
13551 
13554  template<typename t>
13555  bool is_sameXYZ(const CImg<t>& img) const {
13556  return is_sameXYZ(img._width,img._height,img._depth);
13557  }
13558 
13560 
13563  bool is_sameXYC(const unsigned int size_x, const unsigned int size_y, const unsigned int size_c) const {
13564  return is_sameXY(size_x,size_y) && _spectrum==size_c;
13565  }
13566 
13568 
13571  template<typename t>
13572  bool is_sameXYC(const CImg<t>& img) const {
13573  return is_sameXYC(img._width,img._height,img._spectrum);
13574  }
13575 
13577 
13580  bool is_sameXZC(const unsigned int size_x, const unsigned int size_z, const unsigned int size_c) const {
13581  return is_sameXZ(size_x,size_z) && _spectrum==size_c;
13582  }
13583 
13585 
13588  template<typename t>
13589  bool is_sameXZC(const CImg<t>& img) const {
13590  return is_sameXZC(img._width,img._depth,img._spectrum);
13591  }
13592 
13594 
13597  bool is_sameYZC(const unsigned int size_y, const unsigned int size_z, const unsigned int size_c) const {
13598  return is_sameYZ(size_y,size_z) && _spectrum==size_c;
13599  }
13600 
13602 
13605  template<typename t>
13606  bool is_sameYZC(const CImg<t>& img) const {
13607  return is_sameYZC(img._height,img._depth,img._spectrum);
13608  }
13609 
13611 
13615  bool is_sameXYZC(const unsigned int size_x, const unsigned int size_y,
13616  const unsigned int size_z, const unsigned int size_c) const {
13617  return is_sameXYZ(size_x,size_y,size_z) && _spectrum==size_c;
13618  }
13619 
13621 
13624  template<typename t>
13625  bool is_sameXYZC(const CImg<t>& img) const {
13626  return is_sameXYZC(img._width,img._height,img._depth,img._spectrum);
13627  }
13628 
13630 
13645  bool containsXYZC(const int x, const int y=0, const int z=0, const int c=0) const {
13646  return !is_empty() && x>=0 && x<width() && y>=0 && y<height() && z>=0 && z<depth() && c>=0 && c<spectrum();
13647  }
13648 
13650 
13670  template<typename t>
13671  bool contains(const T& pixel, t& x, t& y, t& z, t& c) const {
13672  const unsigned long wh = (unsigned long)_width*_height, whd = wh*_depth, siz = whd*_spectrum;
13673  const T *const ppixel = &pixel;
13674  if (is_empty() || ppixel<_data || ppixel>=_data+siz) return false;
13675  unsigned long off = (unsigned long)(ppixel - _data);
13676  const unsigned long nc = off/whd;
13677  off%=whd;
13678  const unsigned long nz = off/wh;
13679  off%=wh;
13680  const unsigned long ny = off/_width, nx = off%_width;
13681  x = (t)nx; y = (t)ny; z = (t)nz; c = (t)nc;
13682  return true;
13683  }
13684 
13686 
13689  template<typename t>
13690  bool contains(const T& pixel, t& x, t& y, t& z) const {
13691  const unsigned long wh = (unsigned long)_width*_height, whd = wh*_depth, siz = whd*_spectrum;
13692  const T *const ppixel = &pixel;
13693  if (is_empty() || ppixel<_data || ppixel>=_data+siz) return false;
13694  unsigned long off = ((unsigned long)(ppixel - _data))%whd;
13695  const unsigned long nz = off/wh;
13696  off%=wh;
13697  const unsigned long ny = off/_width, nx = off%_width;
13698  x = (t)nx; y = (t)ny; z = (t)nz;
13699  return true;
13700  }
13701 
13703 
13706  template<typename t>
13707  bool contains(const T& pixel, t& x, t& y) const {
13708  const unsigned long wh = (unsigned long)_width*_height, siz = wh*_depth*_spectrum;
13709  const T *const ppixel = &pixel;
13710  if (is_empty() || ppixel<_data || ppixel>=_data+siz) return false;
13711  unsigned long off = ((unsigned int)(ppixel - _data))%wh;
13712  const unsigned long ny = off/_width, nx = off%_width;
13713  x = (t)nx; y = (t)ny;
13714  return true;
13715  }
13716 
13718 
13721  template<typename t>
13722  bool contains(const T& pixel, t& x) const {
13723  const T *const ppixel = &pixel;
13724  if (is_empty() || ppixel<_data || ppixel>=_data+size()) return false;
13725  x = (t)(((unsigned long)(ppixel - _data))%_width);
13726  return true;
13727  }
13728 
13730 
13733  bool contains(const T& pixel) const {
13734  const T *const ppixel = &pixel;
13735  return !is_empty() && ppixel>=_data && ppixel<_data + size();
13736  }
13737 
13739 
13757  template<typename t>
13758  bool is_overlapped(const CImg<t>& img) const {
13759  const unsigned long csiz = size(), isiz = img.size();
13760  return !((void*)(_data + csiz)<=(void*)img._data || (void*)_data>=(void*)(img._data + isiz));
13761  }
13762 
13764 
13777  template<typename tp, typename tc, typename to>
13778  bool is_object3d(const CImgList<tp>& primitives,
13779  const CImgList<tc>& colors,
13780  const to& opacities,
13781  const bool full_check=true,
13782  char *const error_message=0) const {
13783  if (error_message) *error_message = 0;
13784 
13785  // Check consistency for the particular case of an empty 3d object.
13786  if (is_empty()) {
13787  if (primitives || colors || opacities) {
13788  if (error_message) std::sprintf(error_message,
13789  "3d object (%u,%u) defines no vertices but %u primitives, "
13790  "%u colors and %lu opacities",
13791  _width,primitives._width,primitives._width,
13792  colors._width,(unsigned long)opacities.size());
13793  return false;
13794  }
13795  return true;
13796  }
13797 
13798  // Check consistency of vertices.
13799  if (_height!=3 || _depth>1 || _spectrum>1) { // Check vertices dimensions.
13800  if (error_message) std::sprintf(error_message,
13801  "3d object (%u,%u) has invalid vertex dimensions (%u,%u,%u,%u)",
13802  _width,primitives._width,_width,_height,_depth,_spectrum);
13803  return false;
13804  }
13805  if (colors._width>primitives._width+1) {
13806  if (error_message) std::sprintf(error_message,
13807  "3d object (%u,%u) defines %u colors",
13808  _width,primitives._width,colors._width);
13809  return false;
13810  }
13811  if (opacities.size()>primitives._width) {
13812  if (error_message) std::sprintf(error_message,
13813  "3d object (%u,%u) defines %lu opacities",
13814  _width,primitives._width,(unsigned long)opacities.size());
13815  return false;
13816  }
13817  if (!full_check) return true;
13818 
13819  // Check consistency of primitives.
13820  cimglist_for(primitives,l) {
13821  const CImg<tp>& primitive = primitives[l];
13822  const unsigned long psiz = primitive.size();
13823  switch (psiz) {
13824  case 1 : { // Point.
13825  const unsigned int i0 = (unsigned int)primitive(0);
13826  if (i0>=_width) {
13827  if (error_message) std::sprintf(error_message,
13828  "3d object (%u,%u) refers to invalid vertex indice %u in "
13829  "point primitive [%u]",
13830  _width,primitives._width,i0,l);
13831  return false;
13832  }
13833  } break;
13834  case 5 : { // Sphere.
13835  const unsigned int
13836  i0 = (unsigned int)primitive(0),
13837  i1 = (unsigned int)primitive(1);
13838  if (i0>=_width || i1>=_width) {
13839  if (error_message) std::sprintf(error_message,
13840  "3d object (%u,%u) refers to invalid vertex indices (%u,%u) in "
13841  "sphere primitive [%u]",
13842  _width,primitives._width,i0,i1,l);
13843  return false;
13844  }
13845  } break;
13846  case 2 : // Segment.
13847  case 6 : {
13848  const unsigned int
13849  i0 = (unsigned int)primitive(0),
13850  i1 = (unsigned int)primitive(1);
13851  if (i0>=_width || i1>=_width) {
13852  if (error_message) std::sprintf(error_message,
13853  "3d object (%u,%u) refers to invalid vertex indices (%u,%u) in "
13854  "segment primitive [%u]",
13855  _width,primitives._width,i0,i1,l);
13856  return false;
13857  }
13858  } break;
13859  case 3 : // Triangle.
13860  case 9 : {
13861  const unsigned int
13862  i0 = (unsigned int)primitive(0),
13863  i1 = (unsigned int)primitive(1),
13864  i2 = (unsigned int)primitive(2);
13865  if (i0>=_width || i1>=_width || i2>=_width) {
13866  if (error_message) std::sprintf(error_message,
13867  "3d object (%u,%u) refers to invalid vertex indices (%u,%u,%u) in "
13868  "triangle primitive [%u]",
13869  _width,primitives._width,i0,i1,i2,l);
13870  return false;
13871  }
13872  } break;
13873  case 4 : // Quadrangle.
13874  case 12 : {
13875  const unsigned int
13876  i0 = (unsigned int)primitive(0),
13877  i1 = (unsigned int)primitive(1),
13878  i2 = (unsigned int)primitive(2),
13879  i3 = (unsigned int)primitive(3);
13880  if (i0>=_width || i1>=_width || i2>=_width || i3>=_width) {
13881  if (error_message) std::sprintf(error_message,
13882  "3d object (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in "
13883  "quadrangle primitive [%u]",
13884  _width,primitives._width,i0,i1,i2,i3,l);
13885  return false;
13886  }
13887  } break;
13888  default :
13889  if (error_message) std::sprintf(error_message,
13890  "3d object (%u,%u) defines an invalid primitive [%u] of size %u",
13891  _width,primitives._width,l,(unsigned int)psiz);
13892  return false;
13893  }
13894  }
13895 
13896  // Check consistency of colors.
13897  cimglist_for(colors,c) {
13898  const CImg<tc>& color = colors[c];
13899  if (!color) {
13900  if (error_message) std::sprintf(error_message,
13901  "3d object (%u,%u) defines no color for primitive [%u]",
13902  _width,primitives._width,c);
13903  return false;
13904  }
13905  }
13906 
13907  // Check consistency of light texture.
13908  if (colors._width>primitives._width) {
13909  const CImg<tc> &light = colors.back();
13910  if (!light || light._depth>1) {
13911  if (error_message) std::sprintf(error_message,
13912  "3d object (%u,%u) defines an invalid light texture (%u,%u,%u,%u)",
13913  _width,primitives._width,light._width,
13914  light._height,light._depth,light._spectrum);
13915  return false;
13916  }
13917  }
13918 
13919  return true;
13920  }
13921 
13923 
13932  bool is_CImg3d(const bool full_check=true, char *const error_message=0) const {
13933  if (error_message) *error_message = 0;
13934 
13935  // Check instance dimension and header.
13936  if (_width!=1 || _height<8 || _depth!=1 || _spectrum!=1) {
13937  if (error_message) std::sprintf(error_message,
13938  "CImg3d has invalid dimensions (%u,%u,%u,%u)",
13939  _width,_height,_depth,_spectrum);
13940  return false;
13941  }
13942  const T *ptrs = _data, *const ptre = end();
13943  if (!_is_CImg3d(*(ptrs++),'C') || !_is_CImg3d(*(ptrs++),'I') || !_is_CImg3d(*(ptrs++),'m') ||
13944  !_is_CImg3d(*(ptrs++),'g') || !_is_CImg3d(*(ptrs++),'3') || !_is_CImg3d(*(ptrs++),'d')) {
13945  if (error_message) std::sprintf(error_message,
13946  "CImg3d header not found");
13947  return false;
13948  }
13949  const unsigned int
13950  nb_points = cimg::float2uint((float)*(ptrs++)),
13951  nb_primitives = cimg::float2uint((float)*(ptrs++));
13952 
13953  // Check consistency of number of vertices / primitives.
13954  if (!full_check) {
13955  const unsigned long minimal_size = 8UL + 3*nb_points + 6*nb_primitives;
13956  if (_data + minimal_size>ptre) {
13957  if (error_message) std::sprintf(error_message,
13958  "CImg3d (%u,%u) has only %lu values, while at least %lu values were expected",
13959  nb_points,nb_primitives,size(),minimal_size);
13960  return false;
13961  }
13962  }
13963 
13964  // Check consistency of vertex data.
13965  if (!nb_points) {
13966  if (nb_primitives) {
13967  if (error_message) std::sprintf(error_message,
13968  "CImg3d (%u,%u) defines no vertices but %u primitives",
13969  nb_points,nb_primitives,nb_primitives);
13970  return false;
13971  }
13972  if (ptrs!=ptre) {
13973  if (error_message) std::sprintf(error_message,
13974  "CImg3d (%u,%u) is an empty object but contains %u value%s "
13975  "more than expected",
13976  nb_points,nb_primitives,(unsigned int)(ptre-ptrs),(ptre-ptrs)>1?"s":"");
13977  return false;
13978  }
13979  return true;
13980  }
13981  if (ptrs+3*nb_points>ptre) {
13982  if (error_message) std::sprintf(error_message,
13983  "CImg3d (%u,%u) defines only %u vertices data",
13984  nb_points,nb_primitives,(unsigned int)(ptre-ptrs)/3);
13985  return false;
13986  }
13987  ptrs+=3*nb_points;
13988 
13989  // Check consistency of primitive data.
13990  if (ptrs==ptre) {
13991  if (error_message) std::sprintf(error_message,
13992  "CImg3d (%u,%u) defines %u vertices but no primitive",
13993  nb_points,nb_primitives,nb_points);
13994  return false;
13995  }
13996 
13997  if (!full_check) return true;
13998 
13999  for (unsigned int p = 0; p<nb_primitives; ++p) {
14000  const unsigned int nb_inds = (unsigned int)*(ptrs++);
14001  switch (nb_inds) {
14002  case 1 : { // Point.
14003  const unsigned int i0 = cimg::float2uint((float)*(ptrs++));
14004  if (i0>=nb_points) {
14005  if (error_message) std::sprintf(error_message,
14006  "CImg3d (%u,%u) refers to invalid vertex indice %u in point primitive [%u]",
14007  nb_points,nb_primitives,i0,p);
14008  return false;
14009  }
14010  } break;
14011  case 5 : { // Sphere.
14012  const unsigned int
14013  i0 = cimg::float2uint((float)*(ptrs++)),
14014  i1 = cimg::float2uint((float)*(ptrs++));
14015  ptrs+=3;
14016  if (i0>=nb_points || i1>=nb_points) {
14017  if (error_message) std::sprintf(error_message,
14018  "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in "
14019  "sphere primitive [%u]",
14020  nb_points,nb_primitives,i0,i1,p);
14021  return false;
14022  }
14023  } break;
14024  case 2 : case 6 : { // Segment.
14025  const unsigned int
14026  i0 = cimg::float2uint((float)*(ptrs++)),
14027  i1 = cimg::float2uint((float)*(ptrs++));
14028  if (nb_inds==6) ptrs+=4;
14029  if (i0>=nb_points || i1>=nb_points) {
14030  if (error_message) std::sprintf(error_message,
14031  "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in "
14032  "segment primitive [%u]",
14033  nb_points,nb_primitives,i0,i1,p);
14034  return false;
14035  }
14036  } break;
14037  case 3 : case 9 : { // Triangle.
14038  const unsigned int
14039  i0 = cimg::float2uint((float)*(ptrs++)),
14040  i1 = cimg::float2uint((float)*(ptrs++)),
14041  i2 = cimg::float2uint((float)*(ptrs++));
14042  if (nb_inds==9) ptrs+=6;
14043  if (i0>=nb_points || i1>=nb_points || i2>=nb_points) {
14044  if (error_message) std::sprintf(error_message,
14045  "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u) in "
14046  "triangle primitive [%u]",
14047  nb_points,nb_primitives,i0,i1,i2,p);
14048  return false;
14049  }
14050  } break;
14051  case 4 : case 12 : { // Quadrangle.
14052  const unsigned int
14053  i0 = cimg::float2uint((float)*(ptrs++)),
14054  i1 = cimg::float2uint((float)*(ptrs++)),
14055  i2 = cimg::float2uint((float)*(ptrs++)),
14056  i3 = cimg::float2uint((float)*(ptrs++));
14057  if (nb_inds==12) ptrs+=8;
14058  if (i0>=nb_points || i1>=nb_points || i2>=nb_points || i3>=nb_points) {
14059  if (error_message) std::sprintf(error_message,
14060  "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in "
14061  "quadrangle primitive [%u]",
14062  nb_points,nb_primitives,i0,i1,i2,i3,p);
14063  return false;
14064  }
14065  } break;
14066  default :
14067  if (error_message) std::sprintf(error_message,
14068  "CImg3d (%u,%u) defines an invalid primitive [%u] of size %u",
14069  nb_points,nb_primitives,p,nb_inds);
14070  return false;
14071  }
14072  if (ptrs>ptre) {
14073  if (error_message) std::sprintf(error_message,
14074  "CImg3d (%u,%u) has incomplete primitive data for primitive [%u], "
14075  "%u values missing",
14076  nb_points,nb_primitives,p,(unsigned int)(ptrs-ptre));
14077  return false;
14078  }
14079  }
14080 
14081  // Check consistency of color data.
14082  if (ptrs==ptre) {
14083  if (error_message) std::sprintf(error_message,
14084  "CImg3d (%u,%u) defines no color/texture data",
14085  nb_points,nb_primitives);
14086  return false;
14087  }
14088  for (unsigned int c = 0; c<nb_primitives; ++c) {
14089  if (*(ptrs++)!=(T)-128) ptrs+=2;
14090  else if ((ptrs+=3)<ptre) {
14091  const unsigned int w = (unsigned int)*(ptrs-3), h = (unsigned int)*(ptrs-2), s = (unsigned int)*(ptrs-1);
14092  if (!h && !s) {
14093  if (w>=c) {
14094  if (error_message) std::sprintf(error_message,
14095  "CImg3d (%u,%u) refers to invalid shared sprite/texture indice %u "
14096  "for primitive [%u]",
14097  nb_points,nb_primitives,w,c);
14098  return false;
14099  }
14100  } else ptrs+=w*h*s;
14101  }
14102  if (ptrs>ptre) {
14103  if (error_message) std::sprintf(error_message,
14104  "CImg3d (%u,%u) has incomplete color/texture data for primitive [%u], "
14105  "%u values missing",
14106  nb_points,nb_primitives,c,(unsigned int)(ptrs-ptre));
14107  return false;
14108  }
14109  }
14110 
14111  // Check consistency of opacity data.
14112  if (ptrs==ptre) {
14113  if (error_message) std::sprintf(error_message,
14114  "CImg3d (%u,%u) defines no opacity data",
14115  nb_points,nb_primitives);
14116  return false;
14117  }
14118  for (unsigned int o = 0; o<nb_primitives; ++o) {
14119  if (*(ptrs++)==(T)-128 && (ptrs+=3)<ptre) {
14120  const unsigned int w = (unsigned int)*(ptrs-3), h = (unsigned int)*(ptrs-2), s = (unsigned int)*(ptrs-1);
14121  if (!h && !s) {
14122  if (w>=o) {
14123  if (error_message) std::sprintf(error_message,
14124  "CImg3d (%u,%u) refers to invalid shared opacity indice %u "
14125  "for primitive [%u]",
14126  nb_points,nb_primitives,w,o);
14127  return false;
14128  }
14129  } else ptrs+=w*h*s;
14130  }
14131  if (ptrs>ptre) {
14132  if (error_message) std::sprintf(error_message,
14133  "CImg3d (%u,%u) has incomplete opacity data for primitive [%u]",
14134  nb_points,nb_primitives,o);
14135  return false;
14136  }
14137  }
14138 
14139  // Check end of data.
14140  if (ptrs<ptre) {
14141  if (error_message) std::sprintf(error_message,
14142  "CImg3d (%u,%u) contains %u value%s more than expected",
14143  nb_points,nb_primitives,(unsigned int)(ptre-ptrs),(ptre-ptrs)>1?"s":"");
14144  return false;
14145  }
14146  return true;
14147  }
14148 
14149  static bool _is_CImg3d(const T val, const char c) {
14150  return val>=(T)c && val<(T)(c+1);
14151  }
14152 
14154  //-------------------------------------
14155  //
14157 
14158  //-------------------------------------
14159 
14160  // Define the math formula parser/compiler and evaluator.
14162  CImgList<longT> code;
14163  CImg<longT> opcode;
14164  const CImg<longT>* p_code;
14165  CImgList<charT> labelM;
14166  CImg<uintT> level, labelMpos, label1pos;
14167  CImg<doubleT> mem;
14168  CImg<charT> expr;
14169  const CImg<T>& reference;
14170  CImg<Tdouble> reference_stats;
14171  unsigned int mempos, result;
14172  const char *const calling_function;
14173  typedef double (*mp_func)(_cimg_math_parser&);
14174 
14175 #define _cimg_mp_return(x) { *se = saved_char; return x; }
14176 #define _cimg_mp_opcode0(op) _cimg_mp_return(opcode0(op));
14177 #define _cimg_mp_opcode1(op,i1) _cimg_mp_return(opcode1(op,i1));
14178 #define _cimg_mp_opcode2(op,i1,i2) { const unsigned int _i1 = i1, _i2 = i2; _cimg_mp_return(opcode2(op,_i1,_i2)); }
14179 #define _cimg_mp_opcode3(op,i1,i2,i3) \
14180  { const unsigned int _i1 = i1, _i2 = i2, _i3 = i3; _cimg_mp_return(opcode3(op,_i1,_i2,_i3)); }
14181 #define _cimg_mp_opcode6(op,i1,i2,i3,i4,i5,i6) \
14182  { const unsigned int _i1 = i1, _i2 = i2, _i3 = i3, _i4 = i4, _i5 = i5, _i6 = i6; \
14183  _cimg_mp_return(opcode6(op,_i1,_i2,_i3,_i4,_i5,_i6)); }
14184 
14185 #if defined(_WIN64)
14186  // On Win64 and gcc 4.7, sizeof(long)!=sizeof(pointer), so a workaround is needed..
14187 #define _cimg_mp_enfunc(op) (long)((char*)(op)-(char*)mp_u)
14188 #define _cimg_mp_defunc(mp) (*(mp_func)((char*)mp_u+(mp).opcode[0]))(mp)
14189 #else
14190 #define _cimg_mp_enfunc(op) (long)(op)
14191 #define _cimg_mp_defunc(mp) (*(mp_func)((mp).opcode[0]))(mp)
14192 #endif
14193 
14194  // Constructors.
14195  _cimg_math_parser():reference(CImg<T>::empty()),calling_function(0) {}
14196 
14197  _cimg_math_parser(const CImg<T>& img, const char *const expression, const char *const funcname=0):
14198  reference(img),calling_function(funcname?funcname:"cimg_math_parser") {
14199  unsigned int l = 0;
14200  if (expression) {
14201  l = (unsigned int)std::strlen(expression);
14202  expr.assign(expression,l+1);
14203  if (*expr._data) {
14204  char *d = expr._data;
14205  for (const char *s = expr._data; *s || (bool)(*d=0); ++s) if (*s!=' ') *(d++) = *s;
14206  l = (unsigned int)(d - expr._data);
14207  }
14208  }
14209  if (!l) throw CImgArgumentException("[_cimg_math_parser] "
14210  "CImg<%s>::%s(): Empty specified expression.",
14211  pixel_type(),calling_function);
14212 
14213  int lv = 0; // Count parentheses/brackets level of expression.
14214  level.assign(l);
14215  unsigned int *pd = level._data;
14216  for (const char *ps = expr._data; *ps && lv>=0; ++ps)
14217  *(pd++) = (unsigned int)(*ps=='('||*ps=='['?lv++:*ps==')'||*ps==']'?--lv:lv);
14218  if (lv!=0) {
14219  throw CImgArgumentException("[_cimg_math_parser] "
14220  "CImg<%s>::%s(): Unbalanced parentheses/brackets in specified expression '%s'.",
14221  pixel_type(),calling_function,
14222  expr._data);
14223  }
14224  // Init constant values.
14225  mem.assign(512);
14226  mem[0] = 0;
14227  mem[1] = 1;
14228  mem[2] = 2;
14229  mem[3] = (double)reference._width;
14230  mem[4] = (double)reference._height;
14231  mem[5] = (double)reference._depth;
14232  mem[6] = (double)reference._spectrum;
14233  mem[7] = cimg::PI;
14234  mem[8] = std::exp(1.0); // Then [9] = x, [10] = y, [11] = z, [12] = c
14235  mempos = 13;
14236  labelMpos.assign(8);
14237  label1pos.assign(128,1,1,1,~0U);
14238  label1pos['w'] = 3;
14239  label1pos['h'] = 4;
14240  label1pos['d'] = 5;
14241  label1pos['s'] = 6;
14242  label1pos[0] = 7; // pi
14243  label1pos['e'] = 8;
14244  label1pos['x'] = 9;
14245  label1pos['y'] = 10;
14246  label1pos['z'] = 11;
14247  label1pos['c'] = 12;
14248  result = compile(expr._data,expr._data+l); // Compile formula into a serie of opcodes.
14249  }
14250 
14251  // Insert code instructions.
14252  unsigned int opcode0(const mp_func op) {
14253  if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
14254  const unsigned int pos = mempos++;
14255  CImg<longT>::vector(_cimg_mp_enfunc(op),pos).move_to(code);
14256  return pos;
14257  }
14258 
14259  unsigned int opcode1(const mp_func op, const unsigned int arg1) {
14260  if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
14261  const unsigned int pos = mempos++;
14262  CImg<longT>::vector(_cimg_mp_enfunc(op),pos,arg1).move_to(code);
14263  return pos;
14264  }
14265 
14266  unsigned int opcode2(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
14267  if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
14268  const unsigned int pos = mempos++;
14269  CImg<longT>::vector(_cimg_mp_enfunc(op),pos,arg1,arg2).move_to(code);
14270  return pos;
14271  }
14272 
14273  unsigned int opcode3(const mp_func op,
14274  const unsigned int arg1, const unsigned int arg2, const unsigned int arg3) {
14275  if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
14276  const unsigned int pos = mempos++;
14277  CImg<longT>::vector(_cimg_mp_enfunc(op),pos,arg1,arg2,arg3).move_to(code);
14278  return pos;
14279  }
14280 
14281  unsigned int opcode6(const mp_func op,
14282  const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
14283  const unsigned int arg4, const unsigned int arg5, const unsigned int arg6) {
14284  if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
14285  const unsigned int pos = mempos++;
14286  CImg<longT>::vector(_cimg_mp_enfunc(op),pos,arg1,arg2,arg3,arg4,arg5,arg6).move_to(code);
14287  return pos;
14288  }
14289 
14290  // Compilation procedure.
14291  unsigned int compile(char *const ss, char *const se) {
14292  if (!ss || se<=ss || !*ss) {
14293  throw CImgArgumentException("[_cimg_math_parser] "
14294  "CImg<%s>::%s(): Missing item in specified expression '%s'.",
14295  pixel_type(),calling_function,
14296  expr._data);
14297  }
14298  char
14299  *const se1 = se-1, *const se2 = se-2, *const se3 = se-3, *const se4 = se-4,
14300  *const ss1 = ss+1, *const ss2 = ss+2, *const ss3 = ss+3, *const ss4 = ss+4,
14301  *const ss5 = ss+5, *const ss6 = ss+6, *const ss7 = ss+7;
14302  const char saved_char = *se; *se = 0;
14303  const unsigned int clevel = level[ss-expr._data], clevel1 = clevel+1;
14304  if (*se1==';') return compile(ss,se1);
14305 
14306  // Look for a single value, variable or variable assignment.
14307  char end = 0, sep = 0; double val = 0;
14308  const int nb = std::sscanf(ss,"%lf%c%c",&val,&sep,&end);
14309  if (nb==1) {
14310  if (val==0 || val==1 || val==2) _cimg_mp_return((int)val);
14311  if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
14312  const unsigned int pos = mempos++;
14313  mem[pos] = val;
14314  _cimg_mp_return(pos);
14315  }
14316  if (nb==2 && sep=='%') {
14317  if (val==0 || val==100 || val==200) _cimg_mp_return((int)(val/100));
14318  if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
14319  const unsigned int pos = mempos++;
14320  mem[pos] = val/100;
14321  _cimg_mp_return(pos);
14322  }
14323  if (ss1==se) switch (*ss) {
14324  case 'w' : case 'h' : case 'd' : case 's' :
14325  case 'x' : case 'y' : case 'z' : case 'c' : case 'e' : _cimg_mp_return(label1pos[*ss]);
14326  case 'u' : if (label1pos['u']!=~0U) _cimg_mp_return(label1pos['u']); _cimg_mp_opcode2(mp_u,0,1);
14327  case 'g' : if (label1pos['g']!=~0U) _cimg_mp_return(label1pos['g']); _cimg_mp_opcode0(mp_g);
14328  case 'i' : if (label1pos['i']!=~0U) _cimg_mp_return(label1pos['i']); _cimg_mp_opcode0(mp_i);
14329  case '?' : _cimg_mp_opcode2(mp_u,0,1);
14330  }
14331  if (ss1==se1) {
14332  if (*ss=='p' && *ss1=='i') _cimg_mp_return(label1pos[0]); // pi
14333  if (*ss=='i') {
14334  if (*ss1=='m') { if (label1pos[1]!=~0U) _cimg_mp_return(label1pos[1]); _cimg_mp_opcode0(mp_im); } // im
14335  if (*ss1=='M') { if (label1pos[2]!=~0U) _cimg_mp_return(label1pos[2]); _cimg_mp_opcode0(mp_iM); } // iM
14336  if (*ss1=='a') { if (label1pos[3]!=~0U) _cimg_mp_return(label1pos[3]); _cimg_mp_opcode0(mp_ia); } // ia
14337  if (*ss1=='v') { if (label1pos[4]!=~0U) _cimg_mp_return(label1pos[4]); _cimg_mp_opcode0(mp_iv); } // iv
14338  }
14339  if (*ss1=='m') {
14340  if (*ss=='x') { if (label1pos[5]!=~0U) _cimg_mp_return(label1pos[5]); _cimg_mp_opcode0(mp_xm); } // xm
14341  if (*ss=='y') { if (label1pos[6]!=~0U) _cimg_mp_return(label1pos[6]); _cimg_mp_opcode0(mp_ym); } // ym
14342  if (*ss=='z') { if (label1pos[7]!=~0U) _cimg_mp_return(label1pos[7]); _cimg_mp_opcode0(mp_zm); } // zm
14343  if (*ss=='c') { if (label1pos[8]!=~0U) _cimg_mp_return(label1pos[8]); _cimg_mp_opcode0(mp_cm); } // cm
14344  }
14345  if (*ss1=='M') {
14346  if (*ss=='x') { if (label1pos[9]!=~0U) _cimg_mp_return(label1pos[9]); _cimg_mp_opcode0(mp_xM); } // xM
14347  if (*ss=='y') { if (label1pos[10]!=~0U) _cimg_mp_return(label1pos[10]); _cimg_mp_opcode0(mp_yM); } // yM
14348  if (*ss=='z') { if (label1pos[11]!=~0U) _cimg_mp_return(label1pos[11]); _cimg_mp_opcode0(mp_zM); } // zM
14349  if (*ss=='c') { if (label1pos[12]!=~0U) _cimg_mp_return(label1pos[12]); _cimg_mp_opcode0(mp_cM); } // cM
14350  }
14351  }
14352 
14353  // Look for variable declarations.
14354  for (char *s = se2; s>ss; --s)
14355  if (*s==';' && level[s-expr._data]==clevel) { compile(ss,s); _cimg_mp_return(compile(s+1,se)); }
14356  for (char *s = ss1, *ps = ss, *ns = ss2; s<se1; ++s, ++ps, ++ns)
14357  if (*s=='=' && *ns!='=' && *ps!='=' && *ps!='>' && *ps!='<' && *ps!='!' && level[s-expr._data]==clevel) {
14358  CImg<charT> variable_name(ss,(unsigned int)(s-ss+1));
14359  variable_name.back() = 0;
14360  bool is_valid_name = true;
14361  if (*ss>='0' && *ss<='9') is_valid_name = false;
14362  else for (const char *ns = ss+1; ns<s; ++ns)
14363  if ((*ns<'a' || *ns>'z') && (*ns<'A' || *ns>'Z') && (*ns<'0' || *ns>'9') && *ns!='_') {
14364  is_valid_name = false; break;
14365  }
14366  if (!is_valid_name) {
14367  *se = saved_char;
14368  throw CImgArgumentException("[_cimg_math_parser] "
14369  "CImg<%s>::%s(): Invalid variable name '%s' in specified expression "
14370  "'%s%s%s'.",
14371  pixel_type(),calling_function,
14372  variable_name._data,
14373  (ss-8)>expr._data?"...":"",
14374  (ss-8)>expr._data?ss-8:expr._data,
14375  se<&expr.back()?"...":"");
14376  }
14377  const unsigned int pos = compile(s+1,se);
14378 
14379  // Check for particular case of a reserved variable.
14380  if (variable_name[0] && variable_name[1] && !variable_name[2]) {
14381  const char c1 = variable_name[0], c2 = variable_name[1];
14382  if (c1=='p' && c2=='i') variable_name.fill((char)0,(char)0); // pi
14383  else if (c1=='i') {
14384  if (c2=='m') variable_name.fill(1,0); // im
14385  else if (c2=='M') variable_name.fill(2,0); // iM
14386  else if (c2=='a') variable_name.fill(3,0); // ia
14387  else if (c2=='v') variable_name.fill(4,0); // iv
14388  } else if (c2=='m') {
14389  if (c1=='x') variable_name.fill(5,0); // xm
14390  else if (c1=='y') variable_name.fill(6,0); // ym
14391  else if (c1=='z') variable_name.fill(7,0); // zm
14392  else if (c1=='c') variable_name.fill(8,0); // cm
14393  } else if (c2=='M') {
14394  if (c1=='x') variable_name.fill(9,0); // xM
14395  else if (c1=='y') variable_name.fill(10,0); // yM
14396  else if (c1=='z') variable_name.fill(11,0); // zM
14397  else if (c1=='c') variable_name.fill(12,0); // cM
14398  }
14399  }
14400  if (variable_name[1]) { // Multi-char variable.
14401  int label_pos = -1;
14402  cimglist_for(labelM,i) // Check for existing variable with same name.
14403  if (!std::strcmp(variable_name,labelM[i])) { label_pos = i; break; }
14404  if (label_pos<0) { // If new variable.
14405  if (labelM._width>=labelMpos._width) labelMpos.resize(-200,1,1,1,0);
14406  label_pos = labelM._width;
14407  variable_name.move_to(labelM);
14408  }
14409  labelMpos[label_pos] = pos;
14410  } else label1pos[*variable_name] = pos; // Single-char variable.
14411  _cimg_mp_return(pos);
14412  }
14413 
14414  // Look for unary/binary operators. The operator precedences is defined as in C++.
14415  for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='|' && *ns=='|' && level[s-expr._data]==clevel) {
14416  const unsigned int mem_A = compile(ss,s), bp1 = code._width, mem_B = compile(s+2,se);
14417  if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
14418  const unsigned int pos = mempos++;
14419  CImg<longT>::vector(_cimg_mp_enfunc(mp_logical_or),pos,mem_A,mem_B,code._width-bp1).move_to(code,bp1);
14420  _cimg_mp_return(pos);
14421  }
14422  for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='&' && *ns=='&' && level[s-expr._data]==clevel) {
14423  const unsigned int mem_A = compile(ss,s), bp1 = code._width, mem_B = compile(s+2,se);
14424  if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
14425  const unsigned int pos = mempos++;
14426  CImg<longT>::vector(_cimg_mp_enfunc(mp_logical_and),pos,mem_A,mem_B,code._width-bp1).move_to(code,bp1);
14427  _cimg_mp_return(pos);
14428  }
14429  for (char *s = se2; s>ss; --s)
14430  if (*s=='|' && level[s-expr._data]==clevel)
14431  _cimg_mp_opcode2(mp_bitwise_or,compile(ss,s),compile(s+1,se));
14432  for (char *s = se2; s>ss; --s)
14433  if (*s=='&' && level[s-expr._data]==clevel)
14434  _cimg_mp_opcode2(mp_bitwise_and,compile(ss,s),compile(s+1,se));
14435  for (char *s = se3, *ns = se2; s>ss; --s, --ns)
14436  if (*s=='!' && *ns=='=' && level[s-expr._data]==clevel)
14437  _cimg_mp_opcode2(mp_noteq,compile(ss,s),compile(s+2,se));
14438  for (char *s = se3, *ns = se2; s>ss; --s, --ns)
14439  if (*s=='=' && *ns=='=' && level[s-expr._data]==clevel)
14440  _cimg_mp_opcode2(mp_eqeq,compile(ss,s),compile(s+2,se));
14441  for (char *s = se3, *ns = se2; s>ss; --s, --ns)
14442  if (*s=='<' && *ns=='=' && level[s-expr._data]==clevel)
14443  _cimg_mp_opcode2(mp_infeq,compile(ss,s),compile(s+2,se));
14444  for (char *s = se3, *ns = se2; s>ss; --s, --ns)
14445  if (*s=='>' && *ns=='=' && level[s-expr._data]==clevel)
14446  _cimg_mp_opcode2(mp_supeq,compile(ss,s),compile(s+2,se));
14447  for (char *s = se2, *ns = se1, *ps = se3; s>ss; --s, --ns, --ps)
14448  if (*s=='<' && *ns!='<' && *ps!='<' && level[s-expr._data]==clevel)
14449  _cimg_mp_opcode2(mp_inf,compile(ss,s),compile(s+1,se));
14450  for (char *s = se2, *ns = se1, *ps = se3; s>ss; --s, --ns, --ps)
14451  if (*s=='>' && *ns!='>' && *ps!='>' && level[s-expr._data]==clevel)
14452  _cimg_mp_opcode2(mp_sup,compile(ss,s),compile(s+1,se));
14453  for (char *s = se3, *ns = se2; s>ss; --s, --ns)
14454  if (*s=='<' && *ns=='<' && level[s-expr._data]==clevel)
14455  _cimg_mp_opcode2(mp_lsl,compile(ss,s),compile(s+2,se));
14456  for (char *s = se3, *ns = se2; s>ss; --s, --ns)
14457  if (*s=='>' && *ns=='>' && level[s-expr._data]==clevel)
14458  _cimg_mp_opcode2(mp_lsr,compile(ss,s),compile(s+2,se));
14459  for (char *s = se2, *ps = se3; s>ss; --s, --ps)
14460  if (*s=='+' && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' &&
14461  *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' &&
14462  (*ps!='e' || !(ps>ss && (*(ps-1)=='.' || (*(ps-1)>='0' && *(ps-1)<='9')))) && level[s-expr._data]==clevel)
14463  _cimg_mp_opcode2(mp_add,compile(ss,s),compile(s+1,se));
14464  for (char *s = se2, *ps = se3; s>ss; --s, --ps)
14465  if (*s=='-' && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' &&
14466  *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' &&
14467  (*ps!='e' || !(ps>ss && (*(ps-1)=='.' || (*(ps-1)>='0' && *(ps-1)<='9')))) && level[s-expr._data]==clevel)
14468  _cimg_mp_opcode2(mp_sub,compile(ss,s),compile(s+1,se));
14469  for (char *s = se2; s>ss; --s) if (*s=='*' && level[s-expr._data]==clevel) {
14470  const unsigned int mem_A = compile(ss,s), bp1 = code._width, mem_B = compile(s+1,se);
14471  if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
14472  const unsigned int pos = mempos++;
14473  CImg<longT>::vector(_cimg_mp_enfunc(mp_mul),pos,mem_A,mem_B,code._width-bp1).move_to(code,bp1);
14474  _cimg_mp_return(pos);
14475  }
14476  for (char *s = se2; s>ss; --s)
14477  if (*s=='/' && level[s-expr._data]==clevel)
14478  _cimg_mp_opcode2(mp_div,compile(ss,s),compile(s+1,se));
14479  for (char *s = se2, *ns = se1; s>ss; --s, --ns)
14480  if (*s=='%' && *ns!='^' && level[s-expr._data]==clevel)
14481  _cimg_mp_opcode2(mp_modulo,compile(ss,s),compile(s+1,se));
14482  if (ss<se1) {
14483  if (*ss=='+') _cimg_mp_return(compile(ss1,se));
14484  if (*ss=='-') _cimg_mp_opcode1(mp_minus,compile(ss1,se));
14485  if (*ss=='!') _cimg_mp_opcode1(mp_logical_not,compile(ss1,se));
14486  if (*ss=='~') _cimg_mp_opcode1(mp_bitwise_not,compile(ss1,se));
14487  }
14488  for (char *s = se2; s>ss; --s)
14489  if (*s=='^' && level[s-expr._data]==clevel)
14490  _cimg_mp_opcode2(mp_pow,compile(ss,s),compile(s+1,se));
14491 
14492  // Look for a function call or a parenthesis.
14493  if (*se1==']') {
14494  const bool is_relative = *ss=='j';
14495  if ((*ss=='i' || is_relative) && *ss1=='[') {
14496  if (*ss2==']') _cimg_mp_opcode0(mp_i);
14497  _cimg_mp_opcode1(is_relative?mp_joff:mp_ioff,compile(ss2,se1));
14498  }
14499  }
14500  if (*se1==')') {
14501  if (*ss=='(') _cimg_mp_return(compile(ss1,se1));
14502  if (!std::strncmp(ss,"sin(",4)) _cimg_mp_opcode1(mp_sin,compile(ss4,se1));
14503  if (!std::strncmp(ss,"cos(",4)) _cimg_mp_opcode1(mp_cos,compile(ss4,se1));
14504  if (!std::strncmp(ss,"tan(",4)) _cimg_mp_opcode1(mp_tan,compile(ss4,se1));
14505  if (!std::strncmp(ss,"asin(",5)) _cimg_mp_opcode1(mp_asin,compile(ss5,se1));
14506  if (!std::strncmp(ss,"acos(",5)) _cimg_mp_opcode1(mp_acos,compile(ss5,se1));
14507  if (!std::strncmp(ss,"atan(",5)) _cimg_mp_opcode1(mp_atan,compile(ss5,se1));
14508  if (!std::strncmp(ss,"sinh(",5)) _cimg_mp_opcode1(mp_sinh,compile(ss5,se1));
14509  if (!std::strncmp(ss,"cosh(",5)) _cimg_mp_opcode1(mp_cosh,compile(ss5,se1));
14510  if (!std::strncmp(ss,"tanh(",5)) _cimg_mp_opcode1(mp_tanh,compile(ss5,se1));
14511  if (!std::strncmp(ss,"log10(",6)) _cimg_mp_opcode1(mp_log10,compile(ss6,se1));
14512  if (!std::strncmp(ss,"log2(",5)) _cimg_mp_opcode1(mp_log2,compile(ss5,se1));
14513  if (!std::strncmp(ss,"log(",4)) _cimg_mp_opcode1(mp_log,compile(ss4,se1));
14514  if (!std::strncmp(ss,"exp(",4)) _cimg_mp_opcode1(mp_exp,compile(ss4,se1));
14515  if (!std::strncmp(ss,"sqrt(",5)) _cimg_mp_opcode1(mp_sqrt,compile(ss5,se1));
14516  if (!std::strncmp(ss,"sign(",5)) _cimg_mp_opcode1(mp_sign,compile(ss5,se1));
14517  if (!std::strncmp(ss,"abs(",4)) _cimg_mp_opcode1(mp_abs,compile(ss4,se1));
14518  if (!std::strncmp(ss,"atan2(",6)) {
14519  char *s1 = ss6; while (s1<se2 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
14520  _cimg_mp_opcode2(mp_atan2,compile(ss6,s1),compile(s1+1,se1));
14521  }
14522  if (*ss=='i' && *ss1=='f' && *ss2=='(') {
14523  char *s1 = ss3; while (s1<se4 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
14524  char *s2 = s1+1; while (s2<se2 && (*s2!=',' || level[s2-expr._data]!=clevel1)) ++s2;
14525  const unsigned int mem_cond = compile(ss3,s1), bp1 = code._width, mem_A = compile(s1+1,s2),
14526  bp2 = code._width, mem_B = compile(s2+1,se1);
14527  if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
14528  const unsigned int pos = mempos++;
14529  CImg<longT>::vector(_cimg_mp_enfunc(mp_if),pos,mem_cond,mem_A,mem_B,bp2-bp1,code._width-bp2).
14530  move_to(code,bp1);
14531  _cimg_mp_return(pos);
14532  }
14533  if (!std::strncmp(ss,"round(",6)) {
14534  unsigned int value = 0, round = 1, direction = 0;
14535  char *s1 = ss6; while (s1<se2 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
14536  value = compile(ss6,s1==se2?++s1:s1);
14537  if (s1<se1) {
14538  char *s2 = s1+1; while (s2<se2 && (*s2!=',' || level[s2-expr._data]!=clevel1)) ++s2;
14539  round = compile(s1+1,s2==se2?++s2:s2);
14540  if (s2<se1) direction = compile(s2+1,se1);
14541  }
14542  _cimg_mp_opcode3(mp_round,value,round,direction);
14543  }
14544  if ((*ss=='?' || *ss=='u') && *ss1=='(') {
14545  if (*ss2==')') _cimg_mp_opcode2(mp_u,0,1);
14546  char *s1 = ss2; while (s1<se1 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
14547  if (s1<se1) _cimg_mp_opcode2(mp_u,compile(ss2,s1),compile(s1+1,se1));
14548  _cimg_mp_opcode2(mp_u,0,compile(ss2,s1));
14549  }
14550  const bool is_relative = *ss=='j';
14551  if ((*ss=='i' || is_relative) && *ss1=='(') {
14552  if (*ss2==')') _cimg_mp_opcode0(mp_i);
14553  unsigned int
14554  indx = is_relative?0:9, indy = is_relative?0:10,
14555  indz = is_relative?0:11, indc = is_relative?0:12,
14556  borders = 0, interpolation = 0;
14557  if (ss2!=se1) {
14558  char *s1 = ss2; while (s1<se2 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
14559  indx = compile(ss2,s1==se2?++s1:s1);
14560  if (s1<se1) {
14561  char *s2 = s1+1; while (s2<se2 && (*s2!=',' || level[s2-expr._data]!=clevel1)) ++s2;
14562  indy = compile(s1+1,s2==se2?++s2:s2);
14563  if (s2<se1) {
14564  char *s3 = s2+1; while (s3<se2 && (*s3!=',' || level[s3-expr._data]!=clevel1)) ++s3;
14565  indz = compile(s2+1,s3==se2?++s3:s3);
14566  if (s3<se1) {
14567  char *s4 = s3+1; while (s4<se2 && (*s4!=',' || level[s4-expr._data]!=clevel1)) ++s4;
14568  indc = compile(s3+1,s4==se2?++s4:s4);
14569  if (s4<se1) {
14570  char *s5 = s4+1; while (s5<se2 && (*s5!=',' || level[s5-expr._data]!=clevel1)) ++s5;
14571  interpolation = compile(s4+1,s5==se2?++s5:s5);
14572  if (s5<se1) borders = compile(s5+1,se1);
14573  }
14574  }
14575  }
14576  }
14577  }
14578  _cimg_mp_opcode6(is_relative?mp_jxyzc:mp_ixyzc,indx,indy,indz,indc,interpolation,borders);
14579  }
14580  if (!std::strncmp(ss,"min(",4) || !std::strncmp(ss,"max(",4) ||
14581  !std::strncmp(ss,"med(",4) || !std::strncmp(ss,"kth(",4)) {
14582  CImgList<longT> opcode;
14583  if (mempos>=mem.size()) mem.resize(-200,1,1,1,0);
14584  const unsigned int pos = mempos++;
14585  CImg<longT>::vector(_cimg_mp_enfunc(*ss=='k'?mp_kth:ss[1]=='i'?mp_min:ss[1]=='a'?mp_max:mp_med),pos).
14586  move_to(opcode);
14587  for (char *s = ss4; s<se; ++s) {
14588  char *ns = s; while (ns<se && (*ns!=',' || level[ns-expr._data]!=clevel1) &&
14589  (*ns!=')' || level[ns-expr._data]!=clevel)) ++ns;
14590  CImg<longT>::vector(compile(s,ns)).move_to(opcode);
14591  s = ns;
14592  }
14593  (opcode>'y').move_to(code);
14594  _cimg_mp_return(pos);
14595  }
14596  if (!std::strncmp(ss,"arg(",4)) {
14597  CImgList<longT> opcode;
14598  if (mempos>=mem.size()) mem.resize(-200,1,1,1,0);
14599  const unsigned int pos = mempos++;
14600  CImg<longT>::vector(_cimg_mp_enfunc(mp_arg),pos).move_to(opcode);
14601  for (char *s = ss4; s<se; ++s) {
14602  char *ns = s; while (ns<se && (*ns!=',' || level[ns-expr._data]!=clevel1) &&
14603  (*ns!=')' || level[ns-expr._data]!=clevel)) ++ns;
14604  CImg<longT>::vector(compile(s,ns)).move_to(opcode);
14605  s = ns;
14606  }
14607  (opcode>'y').move_to(code);
14608  _cimg_mp_return(pos);
14609  }
14610  if (!std::strncmp(ss,"narg(",5)) {
14611  if (*ss5==')') _cimg_mp_return(0);
14612  unsigned int nb_args = 0;
14613  for (char *s = ss5; s<se; ++s) {
14614  char *ns = s; while (ns<se && (*ns!=',' || level[ns-expr._data]!=clevel1) &&
14615  (*ns!=')' || level[ns-expr._data]!=clevel)) ++ns;
14616  ++nb_args; s = ns;
14617  }
14618  if (nb_args==0 || nb_args==1) _cimg_mp_return(nb_args);
14619  if (mempos>=mem.size()) mem.resize(-200,1,1,1,0);
14620  const unsigned int pos = mempos++;
14621  mem[pos] = nb_args;
14622  _cimg_mp_return(pos);
14623  }
14624  if (!std::strncmp(ss,"isval(",6)) {
14625  char sep = 0, end = 0; double val = 0;
14626  if (std::sscanf(ss6,"%lf%c%c",&val,&sep,&end)==2 && sep==')') _cimg_mp_return(1);
14627  _cimg_mp_return(0);
14628  }
14629  if (!std::strncmp(ss,"isnan(",6)) _cimg_mp_opcode1(mp_isnan,compile(ss6,se1));
14630  if (!std::strncmp(ss,"isinf(",6)) _cimg_mp_opcode1(mp_isinf,compile(ss6,se1));
14631  if (!std::strncmp(ss,"isint(",6)) _cimg_mp_opcode1(mp_isint,compile(ss6,se1));
14632  if (!std::strncmp(ss,"isbool(",7)) _cimg_mp_opcode1(mp_isbool,compile(ss7,se1));
14633  if (!std::strncmp(ss,"rol(",4) || !std::strncmp(ss,"ror(",4)) {
14634  unsigned int value = 0, nb = 1;
14635  char *s1 = ss4; while (s1<se2 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
14636  value = compile(ss4,s1==se2?++s1:s1);
14637  if (s1<se1) {
14638  char *s2 = s1+1; while (s2<se2 && (*s2!=',' || level[s2-expr._data]!=clevel1)) ++s2;
14639  nb = compile(s1+1,se1);
14640  }
14641  _cimg_mp_opcode2(*ss2=='l'?mp_rol:mp_ror,value,nb);
14642  }
14643 
14644  if (!std::strncmp(ss,"sinc(",5)) _cimg_mp_opcode1(mp_sinc,compile(ss5,se1));
14645  if (!std::strncmp(ss,"int(",4)) _cimg_mp_opcode1(mp_int,compile(ss4,se1));
14646  }
14647 
14648  // No known item found, assuming this is an already initialized variable.
14649  CImg<charT> variable_name(ss,(unsigned int)(se-ss+1));
14650  variable_name.back() = 0;
14651  if (variable_name[1]) { // Multi-char variable.
14652  cimglist_for(labelM,i) if (!std::strcmp(variable_name,labelM[i])) _cimg_mp_return(labelMpos[i]);
14653  } else if (label1pos[*variable_name]!=~0U) _cimg_mp_return(label1pos[*variable_name]); // Single-char variable.
14654  *se = saved_char;
14655  throw CImgArgumentException("[_cimg_math_parser] "
14656  "CImg<%s>::%s(): Invalid item '%s' in specified expression '%s%s%s'.\n",
14657  pixel_type(),calling_function,
14658  variable_name._data,
14659  (ss-8)>expr._data?"...":"",
14660  (ss-8)>expr._data?ss-8:expr._data,
14661  se<&expr.back()?"...":"");
14662  }
14663 
14664  // Evaluation functions, known by the parser.
14665  // Defining these functions 'static' ensures that sizeof(mp_func)==sizeof(ulong), so we can store pointers to them
14666  // directly in the opcode vectors.
14667  static double mp_u(_cimg_math_parser& mp) {
14668  return mp.mem[mp.opcode(2)] + cimg::rand()*(mp.mem[mp.opcode(3)]-mp.mem[mp.opcode(2)]);
14669  }
14670  static double mp_g(_cimg_math_parser& mp) {
14671  cimg::unused(mp);
14672  return cimg::grand();
14673  }
14674  static double mp_i(_cimg_math_parser& mp) {
14675  return (double)mp.reference.atXYZC((int)mp.mem[9],(int)mp.mem[10],(int)mp.mem[11],(int)mp.mem[12],0);
14676  }
14677  static double mp_logical_and(_cimg_math_parser& mp) {
14678  const bool is_A = (bool)mp.mem[mp.opcode(2)];
14679  const CImg<longT> *const pE = ++mp.p_code + mp.opcode(4);
14680  if (!is_A) { mp.p_code = pE - 1; return 0; }
14681  const unsigned int mem_B = (unsigned int)mp.opcode(3);
14682  for ( ; mp.p_code<pE; ++mp.p_code) {
14683  const CImg<longT> &op = *mp.p_code;
14684  mp.opcode._data = op._data; mp.opcode._height = op._height;
14685  const unsigned int target = (unsigned int)mp.opcode[1];
14686  mp.mem[target] = _cimg_mp_defunc(mp);
14687  }
14688  --mp.p_code;
14689  return (double)(bool)mp.mem[mem_B];
14690  }
14691  static double mp_logical_or(_cimg_math_parser& mp) {
14692  const bool is_A = (bool)mp.mem[mp.opcode(2)];
14693  const CImg<longT> *const pE = ++mp.p_code + mp.opcode(4);
14694  if (is_A) { mp.p_code = pE - 1; return 1; }
14695  const unsigned int mem_B = (unsigned int)mp.opcode(3);
14696  for ( ; mp.p_code<pE; ++mp.p_code) {
14697  const CImg<longT> &op = *mp.p_code;
14698  mp.opcode._data = op._data; mp.opcode._height = op._height;
14699  const unsigned int target = (unsigned int)mp.opcode[1];
14700  mp.mem[target] = _cimg_mp_defunc(mp);
14701  }
14702  --mp.p_code;
14703  return (double)(bool)mp.mem[mem_B];
14704  }
14705  static double mp_infeq(_cimg_math_parser& mp) {
14706  return (double)(mp.mem[mp.opcode(2)]<=mp.mem[mp.opcode(3)]);
14707  }
14708  static double mp_supeq(_cimg_math_parser& mp) {
14709  return (double)(mp.mem[mp.opcode(2)]>=mp.mem[mp.opcode(3)]);
14710  }
14711  static double mp_noteq(_cimg_math_parser& mp) {
14712  return (double)(mp.mem[mp.opcode(2)]!=mp.mem[mp.opcode(3)]);
14713  }
14714  static double mp_eqeq(_cimg_math_parser& mp) {
14715  return (double)(mp.mem[mp.opcode(2)]==mp.mem[mp.opcode(3)]);
14716  }
14717  static double mp_inf(_cimg_math_parser& mp) {
14718  return (double)(mp.mem[mp.opcode(2)]<mp.mem[mp.opcode(3)]);
14719  }
14720  static double mp_sup(_cimg_math_parser& mp) {
14721  return (double)(mp.mem[mp.opcode(2)]>mp.mem[mp.opcode(3)]);
14722  }
14723  static double mp_add(_cimg_math_parser& mp) {
14724  return mp.mem[mp.opcode(2)] + mp.mem[mp.opcode(3)];
14725  }
14726  static double mp_sub(_cimg_math_parser& mp) {
14727  return mp.mem[mp.opcode(2)] - mp.mem[mp.opcode(3)];
14728  }
14729  static double mp_mul(_cimg_math_parser& mp) {
14730  const double A = mp.mem[mp.opcode(2)];
14731  const CImg<longT> *const pE = ++mp.p_code + mp.opcode(4);
14732  if (!A) { mp.p_code = pE - 1; return 0; }
14733  const unsigned int mem_B = (unsigned int)mp.opcode(3);
14734  for ( ; mp.p_code<pE; ++mp.p_code) {
14735  const CImg<longT> &op = *mp.p_code;
14736  mp.opcode._data = op._data; mp.opcode._height = op._height;
14737  const unsigned int target = (unsigned int)mp.opcode[1];
14738  mp.mem[target] = _cimg_mp_defunc(mp);
14739  }
14740  --mp.p_code;
14741  return A*(double)mp.mem[mem_B];
14742  }
14743  static double mp_div(_cimg_math_parser& mp) {
14744  return mp.mem[mp.opcode(2)] / mp.mem[mp.opcode(3)];
14745  }
14746  static double mp_minus(_cimg_math_parser& mp) {
14747  return -mp.mem[mp.opcode(2)];
14748  }
14749  static double mp_not(_cimg_math_parser& mp) {
14750  return !mp.mem[mp.opcode(2)];
14751  }
14752  static double mp_logical_not(_cimg_math_parser& mp) {
14753  return !mp.mem[mp.opcode(2)];
14754  }
14755  static double mp_bitwise_not(_cimg_math_parser& mp) {
14756  return ~(unsigned long)mp.mem[mp.opcode(2)];
14757  }
14758  static double mp_modulo(_cimg_math_parser& mp) {
14759  return cimg::mod(mp.mem[mp.opcode(2)],mp.mem[mp.opcode(3)]);
14760  }
14761  static double mp_bitwise_and(_cimg_math_parser& mp) {
14762  return ((unsigned long)mp.mem[mp.opcode(2)] & (unsigned long)mp.mem[mp.opcode(3)]);
14763  }
14764  static double mp_bitwise_or(_cimg_math_parser& mp) {
14765  return ((unsigned long)mp.mem[mp.opcode(2)] | (unsigned long)mp.mem[mp.opcode(3)]);
14766  }
14767  static double mp_pow(_cimg_math_parser& mp) {
14768  const double v = mp.mem[mp.opcode(2)], p = mp.mem[mp.opcode(3)];
14769  if (p==0) return 1;
14770  if (p==0.5) return std::sqrt(v);
14771  if (p==1) return v;
14772  if (p==2) return v*v;
14773  if (p==3) return v*v*v;
14774  if (p==4) return v*v*v*v;
14775  return std::pow(v,p);
14776  }
14777  static double mp_sin(_cimg_math_parser& mp) {
14778  return std::sin(mp.mem[mp.opcode(2)]);
14779  }
14780  static double mp_cos(_cimg_math_parser& mp) {
14781  return std::cos(mp.mem[mp.opcode(2)]);
14782  }
14783  static double mp_tan(_cimg_math_parser& mp) {
14784  return std::tan(mp.mem[mp.opcode(2)]);
14785  }
14786  static double mp_asin(_cimg_math_parser& mp) {
14787  return std::asin(mp.mem[mp.opcode(2)]);
14788  }
14789  static double mp_acos(_cimg_math_parser& mp) {
14790  return std::acos(mp.mem[mp.opcode(2)]);
14791  }
14792  static double mp_atan(_cimg_math_parser& mp) {
14793  return std::atan(mp.mem[mp.opcode(2)]);
14794  }
14795  static double mp_sinh(_cimg_math_parser& mp) {
14796  return std::sinh(mp.mem[mp.opcode(2)]);
14797  }
14798  static double mp_cosh(_cimg_math_parser& mp) {
14799  return std::cosh(mp.mem[mp.opcode(2)]);
14800  }
14801  static double mp_tanh(_cimg_math_parser& mp) {
14802  return std::tanh(mp.mem[mp.opcode(2)]);
14803  }
14804  static double mp_log10(_cimg_math_parser& mp) {
14805  return std::log10(mp.mem[mp.opcode(2)]);
14806  }
14807  static double mp_log2(_cimg_math_parser& mp) {
14808  return cimg::log2(mp.mem[mp.opcode(2)]);
14809  }
14810  static double mp_log(_cimg_math_parser& mp) {
14811  return std::log(mp.mem[mp.opcode(2)]);
14812  }
14813  static double mp_exp(_cimg_math_parser& mp) {
14814  return std::exp(mp.mem[mp.opcode(2)]);
14815  }
14816  static double mp_sqrt(_cimg_math_parser& mp) {
14817  return std::sqrt(mp.mem[mp.opcode(2)]);
14818  }
14819  static double mp_sign(_cimg_math_parser& mp) {
14820  return cimg::sign(mp.mem[mp.opcode(2)]);
14821  }
14822  static double mp_abs(_cimg_math_parser& mp) {
14823  return cimg::abs(mp.mem[mp.opcode(2)]);
14824  }
14825  static double mp_atan2(_cimg_math_parser& mp) {
14826  return std::atan2(mp.mem[mp.opcode(2)],mp.mem[mp.opcode(3)]);
14827  }
14828  static double mp_if(_cimg_math_parser& mp) {
14829  const bool is_cond = (bool)mp.mem[mp.opcode(2)];
14830  const unsigned int mem_A = (unsigned int)mp.opcode(3), mem_B = (unsigned int)mp.opcode(4);
14831  const CImg<longT>
14832  *const pB = ++mp.p_code + mp.opcode(5),
14833  *const pE = pB + mp.opcode(6);
14834  if (is_cond) { // Evaluate on-the-fly only the correct argument.
14835  for ( ; mp.p_code<pB; ++mp.p_code) {
14836  const CImg<longT> &op = *mp.p_code;
14837  mp.opcode._data = op._data; mp.opcode._height = op._height;
14838  const unsigned int target = (unsigned int)mp.opcode[1];
14839  mp.mem[target] = _cimg_mp_defunc(mp);
14840  }
14841  mp.p_code = pE - 1;
14842  return mp.mem[mem_A];
14843  }
14844  for (mp.p_code = pB; mp.p_code<pE; ++mp.p_code) {
14845  const CImg<longT> &op = *mp.p_code;
14846  mp.opcode._data = op._data; mp.opcode._height = op._height;
14847  const unsigned int target = (unsigned int)mp.opcode[1];
14848  mp.mem[target] = _cimg_mp_defunc(mp);
14849  }
14850  --mp.p_code;
14851  return mp.mem[mem_B];
14852  }
14853  static double mp_round(_cimg_math_parser& mp) {
14854  return cimg::round(mp.mem[mp.opcode(2)],mp.mem[mp.opcode(3)],(int)mp.mem[mp.opcode(4)]);
14855  }
14856  static double mp_ixyzc(_cimg_math_parser& mp) {
14857  const int i = (int)mp.mem[mp.opcode(6)], b = (int)mp.mem[mp.opcode(7)];
14858  if (i==0) { // Nearest neighbor interpolation.
14859  if (b==2) return (double)mp.reference.atXYZC(cimg::mod((int)mp.mem[mp.opcode(2)],mp.reference.width()),
14860  cimg::mod((int)mp.mem[mp.opcode(3)],mp.reference.height()),
14861  cimg::mod((int)mp.mem[mp.opcode(4)],mp.reference.depth()),
14862  cimg::mod((int)mp.mem[mp.opcode(5)],mp.reference.spectrum()));
14863  if (b==1) return (double)mp.reference.atXYZC((int)mp.mem[mp.opcode(2)],
14864  (int)mp.mem[mp.opcode(3)],
14865  (int)mp.mem[mp.opcode(4)],
14866  (int)mp.mem[mp.opcode(5)]);
14867  return (double)mp.reference.atXYZC((int)mp.mem[mp.opcode(2)],
14868  (int)mp.mem[mp.opcode(3)],
14869  (int)mp.mem[mp.opcode(4)],
14870  (int)mp.mem[mp.opcode(5)],0);
14871  } else { // Linear interpolation.
14872  if (b==2) return (double)mp.reference.linear_atXYZC(cimg::mod((float)mp.mem[mp.opcode(2)],(float)mp.reference.width()),
14873  cimg::mod((float)mp.mem[mp.opcode(3)],(float)mp.reference.height()),
14874  cimg::mod((float)mp.mem[mp.opcode(4)],(float)mp.reference.depth()),
14875  cimg::mod((float)mp.mem[mp.opcode(5)],(float)mp.reference.spectrum()));
14876  if (b==1) return (double)mp.reference.linear_atXYZC((float)mp.mem[mp.opcode(2)],
14877  (float)mp.mem[mp.opcode(3)],
14878  (float)mp.mem[mp.opcode(4)],
14879  (float)mp.mem[mp.opcode(5)]);
14880  return (double)mp.reference.linear_atXYZC((float)mp.mem[mp.opcode(2)],
14881  (float)mp.mem[mp.opcode(3)],
14882  (float)mp.mem[mp.opcode(4)],
14883  (float)mp.mem[mp.opcode(5)],0);
14884  }
14885  }
14886  static double mp_jxyzc(_cimg_math_parser& mp) {
14887  const double x = mp.mem[9], y = mp.mem[10], z = mp.mem[11], c = mp.mem[12];
14888  const int i = (int)mp.mem[mp.opcode(6)], b = (int)mp.mem[mp.opcode(7)];
14889  if (i==0) { // Nearest neighbor interpolation.
14890  if (b==2) return (double)mp.reference.atXYZC(cimg::mod((int)(x+mp.mem[mp.opcode(2)]),mp.reference.width()),
14891  cimg::mod((int)(y+mp.mem[mp.opcode(3)]),mp.reference.height()),
14892  cimg::mod((int)(z+mp.mem[mp.opcode(4)]),mp.reference.depth()),
14893  cimg::mod((int)(c+mp.mem[mp.opcode(5)]),mp.reference.spectrum()));
14894  if (b==1) return (double)mp.reference.atXYZC((int)(x+mp.mem[mp.opcode(2)]),
14895  (int)(y+mp.mem[mp.opcode(3)]),
14896  (int)(z+mp.mem[mp.opcode(4)]),
14897  (int)(c+mp.mem[mp.opcode(5)]));
14898  return (double)mp.reference.atXYZC((int)(x+mp.mem[mp.opcode(2)]),
14899  (int)(y+mp.mem[mp.opcode(3)]),
14900  (int)(z+mp.mem[mp.opcode(4)]),
14901  (int)(c+mp.mem[mp.opcode(5)]),0);
14902  } else { // Linear interpolation.
14903  if (b==2) return (double)mp.reference.linear_atXYZC(cimg::mod((float)(x+mp.mem[mp.opcode(2)]),(float)mp.reference.width()),
14904  cimg::mod((float)(y+mp.mem[mp.opcode(3)]),(float)mp.reference.height()),
14905  cimg::mod((float)(z+mp.mem[mp.opcode(4)]),(float)mp.reference.depth()),
14906  cimg::mod((float)(c+mp.mem[mp.opcode(5)]),(float)mp.reference.spectrum()));
14907  if (b==1) return (double)mp.reference.linear_atXYZC((float)(x+mp.mem[mp.opcode(2)]),
14908  (float)(y+mp.mem[mp.opcode(3)]),
14909  (float)(z+mp.mem[mp.opcode(4)]),
14910  (float)(c+mp.mem[mp.opcode(5)]));
14911  return (double)mp.reference.linear_atXYZC((float)(x+mp.mem[mp.opcode(2)]),
14912  (float)(y+mp.mem[mp.opcode(3)]),
14913  (float)(z+mp.mem[mp.opcode(4)]),
14914  (float)(c+mp.mem[mp.opcode(5)]),0);
14915  }
14916  }
14917  static double mp_min(_cimg_math_parser& mp) {
14918  double val = mp.mem[mp.opcode(2)];
14919  for (unsigned int i = 3; i<mp.opcode._height; ++i) val = cimg::min(val,mp.mem[mp.opcode(i)]);
14920  return val;
14921  }
14922  static double mp_max(_cimg_math_parser& mp) {
14923  double val = mp.mem[mp.opcode(2)];
14924  for (unsigned int i = 3; i<mp.opcode._height; ++i) val = cimg::max(val,mp.mem[mp.opcode(i)]);
14925  return val;
14926  }
14927  static double mp_med(_cimg_math_parser& mp) {
14928  CImg<doubleT> values(mp.opcode._height-2);
14929  double *p = values.data();
14930  for (unsigned int i = 2; i<mp.opcode._height; ++i) *(p++) = mp.mem[mp.opcode(i)];
14931  return values.median();
14932  }
14933  static double mp_kth(_cimg_math_parser& mp) {
14934  CImg<doubleT> values(mp.opcode._height-3);
14935  double *p = values.data();
14936  for (unsigned int i = 3; i<mp.opcode._height; ++i) *(p++) = mp.mem[mp.opcode(i)];
14937  int ind = (int)cimg::round(mp.mem[mp.opcode(2)]);
14938  if (ind<0) ind+=1+values.width();
14939  ind = cimg::max(1,cimg::min(values.width(),ind));
14940  return values.kth_smallest(ind-1);
14941  }
14942  static double mp_isnan(_cimg_math_parser& mp) {
14943  const double val = mp.mem[mp.opcode(2)];
14944  return cimg::type<double>::is_nan(val);
14945  }
14946  static double mp_isinf(_cimg_math_parser& mp) {
14947  const double val = mp.mem[mp.opcode(2)];
14948  return cimg::type<double>::is_inf(val);
14949  }
14950  static double mp_isint(_cimg_math_parser& mp) {
14951  const double val = mp.mem[mp.opcode(2)];
14952  return (double)(cimg::mod(val,1.0)==0);
14953  }
14954  static double mp_isbool(_cimg_math_parser& mp) {
14955  const double val = mp.mem[mp.opcode(2)];
14956  return (val==0.0 || val==1.0);
14957  }
14958  static double mp_rol(_cimg_math_parser& mp) {
14959  return cimg::rol(mp.mem[mp.opcode(2)],(unsigned int)mp.mem[mp.opcode(3)]);
14960  }
14961  static double mp_ror(_cimg_math_parser& mp) {
14962  return cimg::ror(mp.mem[mp.opcode(2)],(unsigned int)mp.mem[mp.opcode(3)]);
14963  }
14964  static double mp_lsl(_cimg_math_parser& mp) {
14965  return (long)mp.mem[mp.opcode(2)]<<(unsigned int)mp.mem[mp.opcode(3)];
14966  }
14967  static double mp_lsr(_cimg_math_parser& mp) {
14968  return (long)mp.mem[mp.opcode(2)]>>(unsigned int)mp.mem[mp.opcode(3)];
14969  }
14970  static double mp_sinc(_cimg_math_parser& mp) {
14971  return cimg::sinc(mp.mem[mp.opcode(2)]);
14972  }
14973  static double mp_im(_cimg_math_parser& mp) {
14974  if (!mp.reference_stats) mp.reference.get_stats().move_to(mp.reference_stats);
14975  return mp.reference_stats?mp.reference_stats[0]:0;
14976  }
14977  static double mp_iM(_cimg_math_parser& mp) {
14978  if (!mp.reference_stats) mp.reference.get_stats().move_to(mp.reference_stats);
14979  return mp.reference_stats?mp.reference_stats[1]:0;
14980  }
14981  static double mp_ia(_cimg_math_parser& mp) {
14982  if (!mp.reference_stats) mp.reference.get_stats().move_to(mp.reference_stats);
14983  return mp.reference_stats?mp.reference_stats[2]:0;
14984  }
14985  static double mp_iv(_cimg_math_parser& mp) {
14986  if (!mp.reference_stats) mp.reference.get_stats().move_to(mp.reference_stats);
14987  return mp.reference_stats?mp.reference_stats[3]:0;
14988  }
14989  static double mp_xm(_cimg_math_parser& mp) {
14990  if (!mp.reference_stats) mp.reference.get_stats().move_to(mp.reference_stats);
14991  return mp.reference_stats?mp.reference_stats[4]:0;
14992  }
14993  static double mp_ym(_cimg_math_parser& mp) {
14994  if (!mp.reference_stats) mp.reference.get_stats().move_to(mp.reference_stats);
14995  return mp.reference_stats?mp.reference_stats[5]:0;
14996  }
14997  static double mp_zm(_cimg_math_parser& mp) {
14998  if (!mp.reference_stats) mp.reference.get_stats().move_to(mp.reference_stats);
14999  return mp.reference_stats?mp.reference_stats[6]:0;
15000  }
15001  static double mp_cm(_cimg_math_parser& mp) {
15002  if (!mp.reference_stats) mp.reference.get_stats().move_to(mp.reference_stats);
15003  return mp.reference_stats?mp.reference_stats[7]:0;
15004  }
15005  static double mp_xM(_cimg_math_parser& mp) {
15006  if (!mp.reference_stats) mp.reference.get_stats().move_to(mp.reference_stats);
15007  return mp.reference_stats?mp.reference_stats[8]:0;
15008  }
15009  static double mp_yM(_cimg_math_parser& mp) {
15010  if (!mp.reference_stats) mp.reference.get_stats().move_to(mp.reference_stats);
15011  return mp.reference_stats?mp.reference_stats[9]:0;
15012  }
15013  static double mp_zM(_cimg_math_parser& mp) {
15014  if (!mp.reference_stats) mp.reference.get_stats().move_to(mp.reference_stats);
15015  return mp.reference_stats?mp.reference_stats[10]:0;
15016  }
15017  static double mp_cM(_cimg_math_parser& mp) {
15018  if (!mp.reference_stats) mp.reference.get_stats().move_to(mp.reference_stats);
15019  return mp.reference_stats?mp.reference_stats[11]:0;
15020  }
15021  static double mp_arg(_cimg_math_parser& mp) {
15022  const int _ind = (int)mp.mem[mp.opcode(2)];
15023  const unsigned int nb_args = mp.opcode._height-2, ind = _ind<0?_ind+nb_args:(unsigned int)_ind;
15024  if (ind>=nb_args) return 0;
15025  return mp.mem[mp.opcode(ind+2)];
15026  }
15027  static double mp_int(_cimg_math_parser& mp) {
15028  return (double)(long)mp.mem[mp.opcode(2)];
15029  }
15030  static double mp_ioff(_cimg_math_parser& mp) {
15031  const unsigned long off = (unsigned long)mp.mem[mp.opcode(2)];
15032  if (off>=mp.reference.size()) return 0;
15033  return (double)mp.reference[off];
15034  }
15035  static double mp_joff(_cimg_math_parser& mp) {
15036  const int x = (int)mp.mem[9], y = (int)mp.mem[10], z = (int)mp.mem[11], c = (int)mp.mem[12];
15037  const unsigned long off = mp.reference.offset(x,y,z,c) + (unsigned long)(mp.mem[mp.opcode(2)]);
15038  if (off>=mp.reference.size()) return 0;
15039  return (double)mp.reference[off];
15040  }
15041 
15042  // Evaluation procedure, with image data.
15043  double operator()(const double x, const double y, const double z, const double c) {
15044  if (!mem) return 0;
15045  mem[9] = x; mem[10] = y; mem[11] = z; mem[12] = c;
15046  opcode._is_shared = true; opcode._width = opcode._depth = opcode._spectrum = 1;
15047 
15048  for (p_code = code._data; p_code<code.end(); ++p_code) {
15049  const CImg<longT> &op = *p_code;
15050  // Allows to avoid parameter passing to evaluation functions.
15051  opcode._data = op._data; opcode._height = op._height;
15052  const unsigned int target = (unsigned int)opcode[1];
15053  mem[target] = _cimg_mp_defunc(*this);
15054  }
15055  return mem[result];
15056  }
15057  };
15058 
15060 
15073  if (is_empty()) return *this;
15074 #ifdef cimg_use_openmp
15075 #pragma omp parallel for if (size()>=524288)
15076 #endif
15077  cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(val*val); };
15078  return *this;
15079  }
15080 
15083  return CImg<Tfloat>(*this,false).sqr();
15084  }
15085 
15087 
15100  if (is_empty()) return *this;
15101 #ifdef cimg_use_openmp
15102 #pragma omp parallel for if (size()>=8192)
15103 #endif
15104  cimg_rof(*this,ptrd,T) *ptrd = (T)std::sqrt((double)*ptrd);
15105  return *this;
15106  }
15107 
15110  return CImg<Tfloat>(*this,false).sqrt();
15111  }
15112 
15114 
15121  if (is_empty()) return *this;
15122 #ifdef cimg_use_openmp
15123 #pragma omp parallel for if (size()>=4096)
15124 #endif
15125  cimg_rof(*this,ptrd,T) *ptrd = (T)std::exp((double)*ptrd);
15126  return *this;
15127  }
15128 
15131  return CImg<Tfloat>(*this,false).exp();
15132  }
15133 
15135 
15143  if (is_empty()) return *this;
15144 #ifdef cimg_use_openmp
15145 #pragma omp parallel for if (size()>=262144)
15146 #endif
15147  cimg_rof(*this,ptrd,T) *ptrd = (T)std::log((double)*ptrd);
15148  return *this;
15149  }
15150 
15153  return CImg<Tfloat>(*this,false).log();
15154  }
15155 
15157 
15165  if (is_empty()) return *this;
15166 #ifdef cimg_use_openmp
15167 #pragma omp parallel for if (size()>=4096)
15168 #endif
15169  cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::log2((double)*ptrd);
15170  return *this;
15171  }
15172 
15175  return CImg<Tfloat>(*this,false).log2();
15176  }
15177 
15179 
15187  if (is_empty()) return *this;
15188 #ifdef cimg_use_openmp
15189 #pragma omp parallel for if (size()>=4096)
15190 #endif
15191  cimg_rof(*this,ptrd,T) *ptrd = (T)std::log10((double)*ptrd);
15192  return *this;
15193  }
15194 
15197  return CImg<Tfloat>(*this,false).log10();
15198  }
15199 
15201 
15208  if (is_empty()) return *this;
15209 #ifdef cimg_use_openmp
15210 #pragma omp parallel for if (size()>=524288)
15211 #endif
15212  cimg_rof(*this,ptrd,T) *ptrd = cimg::abs(*ptrd);
15213  return *this;
15214  }
15215 
15218  return CImg<Tfloat>(*this,false).abs();
15219  }
15220 
15222 
15234  if (is_empty()) return *this;
15235 #ifdef cimg_use_openmp
15236 #pragma omp parallel for if (size()>=32768)
15237 #endif
15238  cimg_rof(*this,ptrd,T) *ptrd = cimg::sign(*ptrd);
15239  return *this;
15240  }
15241 
15244  return CImg<Tfloat>(*this,false).sign();
15245  }
15246 
15248 
15256  if (is_empty()) return *this;
15257 #ifdef cimg_use_openmp
15258 #pragma omp parallel for if (size()>=8192)
15259 #endif
15260  cimg_rof(*this,ptrd,T) *ptrd = (T)std::cos((double)*ptrd);
15261  return *this;
15262  }
15263 
15266  return CImg<Tfloat>(*this,false).cos();
15267  }
15268 
15270 
15278  if (is_empty()) return *this;
15279 #ifdef cimg_use_openmp
15280 #pragma omp parallel for if (size()>=8192)
15281 #endif
15282  cimg_rof(*this,ptrd,T) *ptrd = (T)std::sin((double)*ptrd);
15283  return *this;
15284  }
15285 
15288  return CImg<Tfloat>(*this,false).sin();
15289  }
15290 
15292 
15301  if (is_empty()) return *this;
15302 #ifdef cimg_use_openmp
15303 #pragma omp parallel for if (size()>=2048)
15304 #endif
15305  cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::sinc((double)*ptrd);
15306  return *this;
15307  }
15308 
15311  return CImg<Tfloat>(*this,false).sinc();
15312  }
15313 
15315 
15323  if (is_empty()) return *this;
15324 #ifdef cimg_use_openmp
15325 #pragma omp parallel for if (size()>=2048)
15326 #endif
15327  cimg_rof(*this,ptrd,T) *ptrd = (T)std::tan((double)*ptrd);
15328  return *this;
15329  }
15330 
15333  return CImg<Tfloat>(*this,false).tan();
15334  }
15335 
15337 
15345  if (is_empty()) return *this;
15346 #ifdef cimg_use_openmp
15347 #pragma omp parallel for if (size()>=2048)
15348 #endif
15349  cimg_rof(*this,ptrd,T) *ptrd = (T)std::cosh((double)*ptrd);
15350  return *this;
15351  }
15352 
15355  return CImg<Tfloat>(*this,false).cosh();
15356  }
15357 
15359 
15367  if (is_empty()) return *this;
15368 #ifdef cimg_use_openmp
15369 #pragma omp parallel for if (size()>=2048)
15370 #endif
15371  cimg_rof(*this,ptrd,T) *ptrd = (T)std::sinh((double)*ptrd);
15372  return *this;
15373  }
15374 
15377  return CImg<Tfloat>(*this,false).sinh();
15378  }
15379 
15381 
15389  if (is_empty()) return *this;
15390 #ifdef cimg_use_openmp
15391 #pragma omp parallel for if (size()>=2048)
15392 #endif
15393  cimg_rof(*this,ptrd,T) *ptrd = (T)std::tanh((double)*ptrd);
15394  return *this;
15395  }
15396 
15399  return CImg<Tfloat>(*this,false).tanh();
15400  }
15401 
15403 
15411  if (is_empty()) return *this;
15412 #ifdef cimg_use_openmp
15413 #pragma omp parallel for if (size()>=8192)
15414 #endif
15415  cimg_rof(*this,ptrd,T) *ptrd = (T)std::acos((double)*ptrd);
15416  return *this;
15417  }
15418 
15421  return CImg<Tfloat>(*this,false).acos();
15422  }
15423 
15425 
15433  if (is_empty()) return *this;
15434 #ifdef cimg_use_openmp
15435 #pragma omp parallel for if (size()>=8192)
15436 #endif
15437  cimg_rof(*this,ptrd,T) *ptrd = (T)std::asin((double)*ptrd);
15438  return *this;
15439  }
15440 
15443  return CImg<Tfloat>(*this,false).asin();
15444  }
15445 
15447 
15455  if (is_empty()) return *this;
15456 #ifdef cimg_use_openmp
15457 #pragma omp parallel for if (size()>=8192)
15458 #endif
15459  cimg_rof(*this,ptrd,T) *ptrd = (T)std::atan((double)*ptrd);
15460  return *this;
15461  }
15462 
15465  return CImg<Tfloat>(*this,false).atan();
15466  }
15467 
15469 
15485  template<typename t>
15486  CImg<T>& atan2(const CImg<t>& img) {
15487  const unsigned long siz = size(), isiz = img.size();
15488  if (siz && isiz) {
15489  if (is_overlapped(img)) return atan2(+img);
15490  T *ptrd = _data, *const ptre = _data + siz;
15491  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
15492  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
15493  *ptrd = (T)std::atan2((double)*ptrd,(double)*(ptrs++));
15494  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)std::atan2((double)*ptrd,(double)*(ptrs++));
15495  }
15496  return *this;
15497  }
15498 
15500  template<typename t>
15501  CImg<Tfloat> get_atan2(const CImg<t>& img) const {
15502  return CImg<Tfloat>(*this,false).atan2(img);
15503  }
15504 
15506 
15522  template<typename t>
15523  CImg<T>& mul(const CImg<t>& img) {
15524  const unsigned long siz = size(), isiz = img.size();
15525  if (siz && isiz) {
15526  if (is_overlapped(img)) return mul(+img);
15527  T *ptrd = _data, *const ptre = _data + siz;
15528  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
15529  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
15530  *ptrd = (T)(*ptrd * *(ptrs++));
15531  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd * *(ptrs++));
15532  }
15533  return *this;
15534  }
15535 
15537  template<typename t>
15538  CImg<_cimg_Tt> get_mul(const CImg<t>& img) const {
15539  return CImg<_cimg_Tt>(*this,false).mul(img);
15540  }
15541 
15543 
15546  template<typename t>
15547  CImg<T>& div(const CImg<t>& img) {
15548  const unsigned long siz = size(), isiz = img.size();
15549  if (siz && isiz) {
15550  if (is_overlapped(img)) return div(+img);
15551  T *ptrd = _data, *const ptre = _data + siz;
15552  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
15553  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
15554  *ptrd = (T)(*ptrd / *(ptrs++));
15555  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd / *(ptrs++));
15556  }
15557  return *this;
15558  }
15559 
15561  template<typename t>
15562  CImg<_cimg_Tt> get_div(const CImg<t>& img) const {
15563  return CImg<_cimg_Tt>(*this,false).div(img);
15564  }
15565 
15567 
15582  CImg<T>& pow(const double p) {
15583  if (is_empty()) return *this;
15584  if (p==-4) {
15585 #ifdef cimg_use_openmp
15586 #pragma omp parallel for if (size()>=32768)
15587 #endif
15588  cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/(val*val*val*val)); }
15589  return *this;
15590  }
15591  if (p==-3) {
15592 #ifdef cimg_use_openmp
15593 #pragma omp parallel for if (size()>=32768)
15594 #endif
15595  cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/(val*val*val)); }
15596  return *this;
15597  }
15598  if (p==-2) {
15599 #ifdef cimg_use_openmp
15600 #pragma omp parallel for if (size()>=32768)
15601 #endif
15602  cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/(val*val)); }
15603  return *this;
15604  }
15605  if (p==-1) {
15606 #ifdef cimg_use_openmp
15607 #pragma omp parallel for if (size()>=32768)
15608 #endif
15609  cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/val); }
15610  return *this;
15611  }
15612  if (p==-0.5) {
15613 #ifdef cimg_use_openmp
15614 #pragma omp parallel for if (size()>=8192)
15615 #endif
15616  cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1/std::sqrt((double)val)); }
15617  return *this;
15618  }
15619  if (p==0) return fill(1);
15620  if (p==0.5) return sqrt();
15621  if (p==1) return *this;
15622  if (p==2) return sqr();
15623  if (p==3) {
15624 #ifdef cimg_use_openmp
15625 #pragma omp parallel for if (size()>=262144)
15626 #endif
15627  cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val; }
15628  return *this;
15629  }
15630  if (p==4) {
15631 #ifdef cimg_use_openmp
15632 #pragma omp parallel for if (size()>=131072)
15633 #endif
15634  cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val*val; }
15635  return *this;
15636  }
15637 #ifdef cimg_use_openmp
15638 #pragma omp parallel for if (size()>=1024)
15639 #endif
15640  cimg_rof(*this,ptrd,T) *ptrd = (T)std::pow((double)*ptrd,p);
15641  return *this;
15642  }
15643 
15645  CImg<Tfloat> get_pow(const double p) const {
15646  return CImg<Tfloat>(*this,false).pow(p);
15647  }
15648 
15650 
15653  CImg<T>& pow(const char *const expression) {
15654  if (is_empty()) return *this;
15655  const unsigned int omode = cimg::exception_mode();
15656  cimg::exception_mode() = 0;
15657  try {
15658  const CImg<T> _base = cimg::_is_self_expr(expression)?+*this:CImg<T>(), &base = _base?_base:*this;
15659  _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"pow");
15660  T *ptrd = *expression=='<'?end()-1:_data;
15661  if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)std::pow((double)*ptrd,mp(x,y,z,c)); --ptrd; }
15662  else if (*expression=='>')
15663  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)std::pow((double)*ptrd,mp(x,y,z,c)); ++ptrd; }
15664  else {
15665 #ifdef cimg_use_openmp
15666  if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)
15667 #pragma omp parallel
15668  {
15669  _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp;
15670 #pragma omp for collapse(3)
15671  cimg_forYZC(*this,y,z,c) {
15672  T *ptrd = data(0,y,z,c);
15673  cimg_forX(*this,x) { *ptrd = (T)std::pow((double)*ptrd,lmp(x,y,z,c)); ++ptrd; }
15674  }
15675  }
15676  else
15677 #endif
15678  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)std::pow((double)*ptrd,mp(x,y,z,c)); ++ptrd; }
15679  }
15680  } catch (CImgException&) {
15681  CImg<Tfloat> values(_width,_height,_depth,_spectrum);
15682  try {
15683  values.fill(expression,true);
15684  } catch (CImgException&) {
15685  cimg::exception_mode() = omode;
15686  values.load(expression);
15687  }
15688  pow(values);
15689  }
15690  cimg::exception_mode() = omode;
15691  return *this;
15692  }
15693 
15695  CImg<Tfloat> get_pow(const char *const expression) const {
15696  return CImg<Tfloat>(*this,false).pow(expression);
15697  }
15698 
15700 
15703  template<typename t>
15704  CImg<T>& pow(const CImg<t>& img) {
15705  const unsigned long siz = size(), isiz = img.size();
15706  if (siz && isiz) {
15707  if (is_overlapped(img)) return pow(+img);
15708  T *ptrd = _data, *const ptre = _data + siz;
15709  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
15710  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
15711  *ptrd = (T)std::pow((double)*ptrd,(double)(*(ptrs++)));
15712  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)std::pow((double)*ptrd,(double)(*(ptrs++)));
15713  }
15714  return *this;
15715  }
15716 
15718  template<typename t>
15719  CImg<Tfloat> get_pow(const CImg<t>& img) const {
15720  return CImg<Tfloat>(*this,false).pow(img);
15721  }
15722 
15724 
15727  CImg<T>& rol(const unsigned int n=1) {
15728  if (is_empty()) return *this;
15729 #ifdef cimg_use_openmp
15730 #pragma omp parallel for if (size()>=32768)
15731 #endif
15732  cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::rol(*ptrd,n);
15733  return *this;
15734  }
15735 
15737  CImg<T> get_rol(const unsigned int n=1) const {
15738  return (+*this).rol(n);
15739  }
15740 
15742 
15745  CImg<T>& rol(const char *const expression) {
15746  if (is_empty()) return *this;
15747  const unsigned int omode = cimg::exception_mode();
15748  cimg::exception_mode() = 0;
15749  try {
15750  const CImg<T> _base = cimg::_is_self_expr(expression)?+*this:CImg<T>(), &base = _base?_base:*this;
15751  _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"rol");
15752  T *ptrd = *expression=='<'?end()-1:_data;
15753  if (*expression=='<')
15754  cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::rol(*ptrd,(unsigned int)mp(x,y,z,c)); --ptrd; }
15755  else if (*expression=='>')
15756  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::rol(*ptrd,(unsigned int)mp(x,y,z,c)); ++ptrd; }
15757  else {
15758 #ifdef cimg_use_openmp
15759  if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)
15760 #pragma omp parallel
15761  {
15762  _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp;
15763 #pragma omp for collapse(3)
15764  cimg_forYZC(*this,y,z,c) {
15765  T *ptrd = data(0,y,z,c);
15766  cimg_forX(*this,x) { *ptrd = (T)cimg::rol(*ptrd,(unsigned int)lmp(x,y,z,c)); ++ptrd; }
15767  }
15768  }
15769  else
15770 #endif
15771  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::rol(*ptrd,(unsigned int)mp(x,y,z,c)); ++ptrd; }
15772  }
15773  } catch (CImgException&) {
15774  CImg<Tfloat> values(_width,_height,_depth,_spectrum);
15775  try {
15776  values.fill(expression,true);
15777  } catch (CImgException&) {
15778  cimg::exception_mode() = omode;
15779  values.load(expression);
15780  }
15781  rol(values);
15782  }
15783  cimg::exception_mode() = omode;
15784  return *this;
15785  }
15786 
15788  CImg<T> get_rol(const char *const expression) const {
15789  return (+*this).rol(expression);
15790  }
15791 
15793 
15796  template<typename t>
15797  CImg<T>& rol(const CImg<t>& img) {
15798  const unsigned long siz = size(), isiz = img.size();
15799  if (siz && isiz) {
15800  if (is_overlapped(img)) return rol(+img);
15801  T *ptrd = _data, *const ptre = _data + siz;
15802  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
15803  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
15804  *ptrd = (T)cimg::rol(*ptrd,(unsigned int)(*(ptrs++)));
15805  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)cimg::rol(*ptrd,(unsigned int)(*(ptrs++)));
15806  }
15807  return *this;
15808  }
15809 
15811  template<typename t>
15812  CImg<T> get_rol(const CImg<t>& img) const {
15813  return (+*this).rol(img);
15814  }
15815 
15817 
15820  CImg<T>& ror(const unsigned int n=1) {
15821  if (is_empty()) return *this;
15822 #ifdef cimg_use_openmp
15823 #pragma omp parallel for if (size()>=32768)
15824 #endif
15825  cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::ror(*ptrd,n);
15826  return *this;
15827  }
15828 
15830  CImg<T> get_ror(const unsigned int n=1) const {
15831  return (+*this).ror(n);
15832  }
15833 
15835 
15838  CImg<T>& ror(const char *const expression) {
15839  if (is_empty()) return *this;
15840  const unsigned int omode = cimg::exception_mode();
15841  cimg::exception_mode() = 0;
15842  try {
15843  const CImg<T> _base = cimg::_is_self_expr(expression)?+*this:CImg<T>(), &base = _base?_base:*this;
15844  _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"ror");
15845  T *ptrd = *expression=='<'?end()-1:_data;
15846  if (*expression=='<')
15847  cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::ror(*ptrd,(unsigned int)mp(x,y,z,c)); --ptrd; }
15848  else if (*expression=='>')
15849  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::ror(*ptrd,(unsigned int)mp(x,y,z,c)); ++ptrd; }
15850  else {
15851 #ifdef cimg_use_openmp
15852  if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)
15853 #pragma omp parallel
15854  {
15855  _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp;
15856 #pragma omp for collapse(3)
15857  cimg_forYZC(*this,y,z,c) {
15858  T *ptrd = data(0,y,z,c);
15859  cimg_forX(*this,x) { *ptrd = (T)cimg::ror(*ptrd,(unsigned int)lmp(x,y,z,c)); ++ptrd; }
15860  }
15861  }
15862  else
15863 #endif
15864  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::ror(*ptrd,(unsigned int)mp(x,y,z,c)); ++ptrd; }
15865  }
15866  } catch (CImgException&) {
15867  CImg<Tfloat> values(_width,_height,_depth,_spectrum);
15868  try {
15869  values.fill(expression,true);
15870  } catch (CImgException&) {
15871  cimg::exception_mode() = omode;
15872  values.load(expression);
15873  }
15874  ror(values);
15875  }
15876  cimg::exception_mode() = omode;
15877  return *this;
15878  }
15879 
15881  CImg<T> get_ror(const char *const expression) const {
15882  return (+*this).ror(expression);
15883  }
15884 
15886 
15889  template<typename t>
15890  CImg<T>& ror(const CImg<t>& img) {
15891  const unsigned long siz = size(), isiz = img.size();
15892  if (siz && isiz) {
15893  if (is_overlapped(img)) return ror(+img);
15894  T *ptrd = _data, *const ptre = _data + siz;
15895  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
15896  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
15897  *ptrd = (T)cimg::ror(*ptrd,(unsigned int)(*(ptrs++)));
15898  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)cimg::ror(*ptrd,(unsigned int)(*(ptrs++)));
15899  }
15900  return *this;
15901  }
15902 
15904  template<typename t>
15905  CImg<T> get_ror(const CImg<t>& img) const {
15906  return (+*this).ror(img);
15907  }
15908 
15910 
15915  CImg<T>& min(const T val) {
15916  if (is_empty()) return *this;
15917 #ifdef cimg_use_openmp
15918 #pragma omp parallel for if (size()>=65536)
15919 #endif
15920  cimg_rof(*this,ptrd,T) *ptrd = cimg::min(*ptrd,val);
15921  return *this;
15922  }
15923 
15925  CImg<T> get_min(const T val) const {
15926  return (+*this).min(val);
15927  }
15928 
15930 
15935  template<typename t>
15936  CImg<T>& min(const CImg<t>& img) {
15937  const unsigned long siz = size(), isiz = img.size();
15938  if (siz && isiz) {
15939  if (is_overlapped(img)) return min(+img);
15940  T *ptrd = _data, *const ptre = _data + siz;
15941  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
15942  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
15943  *ptrd = cimg::min((T)*(ptrs++),*ptrd);
15944  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::min((T)*(ptrs++),*ptrd);
15945  }
15946  return *this;
15947  }
15948 
15950  template<typename t>
15951  CImg<_cimg_Tt> get_min(const CImg<t>& img) const {
15952  return CImg<_cimg_Tt>(*this,false).min(img);
15953  }
15954 
15956 
15961  CImg<T>& min(const char *const expression) {
15962  if (is_empty()) return *this;
15963  const unsigned int omode = cimg::exception_mode();
15964  cimg::exception_mode() = 0;
15965  try {
15966  const CImg<T> _base = cimg::_is_self_expr(expression)?+*this:CImg<T>(), &base = _base?_base:*this;
15967  _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"min");
15968  T *ptrd = *expression=='<'?end()-1:_data;
15969  if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::min(*ptrd,(T)mp(x,y,z,c)); --ptrd; }
15970  else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::min(*ptrd,(T)mp(x,y,z,c)); ++ptrd; }
15971  else {
15972 #ifdef cimg_use_openmp
15973  if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)
15974 #pragma omp parallel
15975  {
15976  _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp;
15977 #pragma omp for collapse(3)
15978  cimg_forYZC(*this,y,z,c) {
15979  T *ptrd = data(0,y,z,c);
15980  cimg_forX(*this,x) { *ptrd = (T)cimg::min(*ptrd,(T)lmp(x,y,z,c)); ++ptrd; }
15981  }
15982  }
15983  else
15984 #endif
15985  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::min(*ptrd,(T)mp(x,y,z,c)); ++ptrd; }
15986  }
15987  } catch (CImgException&) {
15988  CImg<T> values(_width,_height,_depth,_spectrum);
15989  try {
15990  values.fill(expression,true);
15991  } catch (CImgException&) {
15992  cimg::exception_mode() = omode;
15993  values.load(expression);
15994  }
15995  min(values);
15996  }
15997  cimg::exception_mode() = omode;
15998  return *this;
15999  }
16000 
16002  CImg<Tfloat> get_min(const char *const expression) const {
16003  return CImg<Tfloat>(*this,false).min(expression);
16004  }
16005 
16007 
16012  CImg<T>& max(const T val) {
16013  if (is_empty()) return *this;
16014 #ifdef cimg_use_openmp
16015 #pragma omp parallel for if (size()>=65536)
16016 #endif
16017  cimg_rof(*this,ptrd,T) *ptrd = cimg::max(*ptrd,val);
16018  return *this;
16019  }
16020 
16022  CImg<T> get_max(const T val) const {
16023  return (+*this).max(val);
16024  }
16025 
16027 
16032  template<typename t>
16033  CImg<T>& max(const CImg<t>& img) {
16034  const unsigned long siz = size(), isiz = img.size();
16035  if (siz && isiz) {
16036  if (is_overlapped(img)) return max(+img);
16037  T *ptrd = _data, *const ptre = _data + siz;
16038  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
16039  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
16040  *ptrd = cimg::max((T)*(ptrs++),*ptrd);
16041  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::max((T)*(ptrs++),*ptrd);
16042  }
16043  return *this;
16044  }
16045 
16047  template<typename t>
16048  CImg<_cimg_Tt> get_max(const CImg<t>& img) const {
16049  return CImg<_cimg_Tt>(*this,false).max(img);
16050  }
16051 
16053 
16058  CImg<T>& max(const char *const expression) {
16059  if (is_empty()) return *this;
16060  const unsigned int omode = cimg::exception_mode();
16061  cimg::exception_mode() = 0;
16062  try {
16063  const CImg<T> _base = cimg::_is_self_expr(expression)?+*this:CImg<T>(), &base = _base?_base:*this;
16064  _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"max");
16065  T *ptrd = *expression=='<'?end()-1:_data;
16066  if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::max(*ptrd,(T)mp(x,y,z,c)); --ptrd; }
16067  else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::max(*ptrd,(T)mp(x,y,z,c)); ++ptrd; }
16068  else {
16069 #ifdef cimg_use_openmp
16070  if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)
16071 #pragma omp parallel
16072  {
16073  _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp;
16074 #pragma omp for collapse(3)
16075  cimg_forYZC(*this,y,z,c) {
16076  T *ptrd = data(0,y,z,c);
16077  cimg_forX(*this,x) { *ptrd = (T)cimg::max(*ptrd,(T)lmp(x,y,z,c)); ++ptrd; }
16078  }
16079  }
16080  else
16081 #endif
16082  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::max(*ptrd,(T)mp(x,y,z,c)); ++ptrd; }
16083  }
16084  } catch (CImgException&) {
16085  CImg<T> values(_width,_height,_depth,_spectrum);
16086  try {
16087  values.fill(expression,true);
16088  } catch (CImgException&) {
16089  cimg::exception_mode() = omode;
16090  values.load(expression);
16091  }
16092  max(values);
16093  }
16094  cimg::exception_mode() = omode;
16095  return *this;
16096  }
16097 
16099  CImg<Tfloat> get_max(const char *const expression) const {
16100  return CImg<Tfloat>(*this,false).max(expression);
16101  }
16102 
16104 
16106  T& min() {
16107  if (is_empty())
16108  throw CImgInstanceException(_cimg_instance
16109  "min(): Empty instance.",
16110  cimg_instance);
16111  T *ptr_min = _data;
16112  T min_value = *ptr_min;
16113  cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
16114  return *ptr_min;
16115  }
16116 
16118  const T& min() const {
16119  if (is_empty())
16120  throw CImgInstanceException(_cimg_instance
16121  "min(): Empty instance.",
16122  cimg_instance);
16123  const T *ptr_min = _data;
16124  T min_value = *ptr_min;
16125  cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
16126  return *ptr_min;
16127  }
16128 
16130 
16132  T& max() {
16133  if (is_empty())
16134  throw CImgInstanceException(_cimg_instance
16135  "max(): Empty instance.",
16136  cimg_instance);
16137  T *ptr_max = _data;
16138  T max_value = *ptr_max;
16139  cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
16140  return *ptr_max;
16141  }
16142 
16144  const T& max() const {
16145  if (is_empty())
16146  throw CImgInstanceException(_cimg_instance
16147  "max(): Empty instance.",
16148  cimg_instance);
16149  const T *ptr_max = _data;
16150  T max_value = *ptr_max;
16151  cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
16152  return *ptr_max;
16153  }
16154 
16156 
16159  template<typename t>
16160  T& min_max(t& max_val) {
16161  if (is_empty())
16162  throw CImgInstanceException(_cimg_instance
16163  "min_max(): Empty instance.",
16164  cimg_instance);
16165  T *ptr_min = _data;
16166  T min_value = *ptr_min, max_value = min_value;
16167  cimg_for(*this,ptrs,T) {
16168  const T val = *ptrs;
16169  if (val<min_value) { min_value = val; ptr_min = ptrs; }
16170  if (val>max_value) max_value = val;
16171  }
16172  max_val = (t)max_value;
16173  return *ptr_min;
16174  }
16175 
16177  template<typename t>
16178  const T& min_max(t& max_val) const {
16179  if (is_empty())
16180  throw CImgInstanceException(_cimg_instance
16181  "min_max(): Empty instance.",
16182  cimg_instance);
16183  const T *ptr_min = _data;
16184  T min_value = *ptr_min, max_value = min_value;
16185  cimg_for(*this,ptrs,T) {
16186  const T val = *ptrs;
16187  if (val<min_value) { min_value = val; ptr_min = ptrs; }
16188  if (val>max_value) max_value = val;
16189  }
16190  max_val = (t)max_value;
16191  return *ptr_min;
16192  }
16193 
16195 
16198  template<typename t>
16199  T& max_min(t& min_val) {
16200  if (is_empty())
16201  throw CImgInstanceException(_cimg_instance
16202  "max_min(): Empty instance.",
16203  cimg_instance);
16204  T *ptr_max = _data;
16205  T max_value = *ptr_max, min_value = max_value;
16206  cimg_for(*this,ptrs,T) {
16207  const T val = *ptrs;
16208  if (val>max_value) { max_value = val; ptr_max = ptrs; }
16209  if (val<min_value) min_value = val;
16210  }
16211  min_val = (t)min_value;
16212  return *ptr_max;
16213  }
16214 
16216  template<typename t>
16217  const T& max_min(t& min_val) const {
16218  if (is_empty())
16219  throw CImgInstanceException(_cimg_instance
16220  "max_min(): Empty instance.",
16221  cimg_instance);
16222  const T *ptr_max = _data;
16223  T max_value = *ptr_max, min_value = max_value;
16224  cimg_for(*this,ptrs,T) {
16225  const T val = *ptrs;
16226  if (val>max_value) { max_value = val; ptr_max = ptrs; }
16227  if (val<min_value) min_value = val;
16228  }
16229  min_val = (t)min_value;
16230  return *ptr_max;
16231  }
16232 
16234 
16237  T kth_smallest(const unsigned int k) const {
16238  if (is_empty())
16239  throw CImgInstanceException(_cimg_instance
16240  "kth_smallest(): Empty instance.",
16241  cimg_instance);
16242  CImg<T> arr(*this);
16243  unsigned int l = 0, ir = size() - 1;
16244  for (;;) {
16245  if (ir<=l+1) {
16246  if (ir==l+1 && arr[ir]<arr[l]) cimg::swap(arr[l],arr[ir]);
16247  return arr[k];
16248  } else {
16249  const unsigned int mid = (l + ir)>>1;
16250  cimg::swap(arr[mid],arr[l+1]);
16251  if (arr[l]>arr[ir]) cimg::swap(arr[l],arr[ir]);
16252  if (arr[l+1]>arr[ir]) cimg::swap(arr[l+1],arr[ir]);
16253  if (arr[l]>arr[l+1]) cimg::swap(arr[l],arr[l+1]);
16254  unsigned int i = l + 1, j = ir;
16255  const T pivot = arr[l+1];
16256  for (;;) {
16257  do ++i; while (arr[i]<pivot);
16258  do --j; while (arr[j]>pivot);
16259  if (j<i) break;
16260  cimg::swap(arr[i],arr[j]);
16261  }
16262  arr[l+1] = arr[j];
16263  arr[j] = pivot;
16264  if (j>=k) ir = j - 1;
16265  if (j<=k) l = i;
16266  }
16267  }
16268  }
16269 
16271 
16273  T median() const {
16274  if (is_empty())
16275  throw CImgInstanceException(_cimg_instance
16276  "median(): Empty instance.",
16277  cimg_instance);
16278  const unsigned int s = size();
16279  const T res = kth_smallest(s>>1);
16280  return (s%2)?res:((res+kth_smallest((s>>1)-1))/2);
16281  }
16282 
16284 
16286  Tdouble sum() const {
16287  if (is_empty())
16288  throw CImgInstanceException(_cimg_instance
16289  "sum(): Empty instance.",
16290  cimg_instance);
16291  Tdouble res = 0;
16292  cimg_for(*this,ptrs,T) res+=(Tdouble)*ptrs;
16293  return res;
16294  }
16295 
16297 
16299  Tdouble mean() const {
16300  if (is_empty())
16301  throw CImgInstanceException(_cimg_instance
16302  "mean(): Empty instance.",
16303  cimg_instance);
16304  Tdouble res = 0;
16305  cimg_for(*this,ptrs,T) res+=(Tdouble)*ptrs;
16306  return res/size();
16307  }
16308 
16310 
16320  Tdouble variance(const unsigned int variance_method=1) const {
16321  Tdouble foo;
16322  return variance_mean(variance_method,foo);
16323  }
16324 
16326 
16330  template<typename t>
16331  Tdouble variance_mean(const unsigned int variance_method, t& mean) const {
16332  if (is_empty())
16333  throw CImgInstanceException(_cimg_instance
16334  "variance_mean(): Empty instance.",
16335  cimg_instance);
16336 
16337  Tdouble variance = 0, average = 0;
16338  const unsigned long siz = size();
16339  switch (variance_method) {
16340  case 0 :{ // Least mean square (standard definition)
16341  Tdouble S = 0, S2 = 0;
16342  cimg_for(*this,ptrs,T) { const Tdouble val = (Tdouble)*ptrs; S+=val; S2+=val*val; }
16343  variance = (S2 - S*S/siz)/siz;
16344  average = S;
16345  } break;
16346  case 1 : { // Least mean square (robust definition)
16347  Tdouble S = 0, S2 = 0;
16348  cimg_for(*this,ptrs,T) { const Tdouble val = (Tdouble)*ptrs; S+=val; S2+=val*val; }
16349  variance = siz>1?(S2 - S*S/siz)/(siz - 1):0;
16350  average = S;
16351  } break;
16352  case 2 : { // Least Median of Squares (MAD)
16353  CImg<Tfloat> buf(*this,false);
16354  buf.sort();
16355  const unsigned long siz2 = siz>>1;
16356  const Tdouble med_i = (double)buf[siz2];
16357  cimg_for(buf,ptrs,Tfloat) {
16358  const Tdouble val = (Tdouble)*ptrs; *ptrs = (Tfloat)cimg::abs(val - med_i); average+=val;
16359  }
16360  buf.sort();
16361  const Tdouble sig = (Tdouble)(1.4828*buf[siz2]);
16362  variance = sig*sig;
16363  } break;
16364  default : { // Least trimmed of Squares
16365  CImg<Tfloat> buf(*this,false);
16366  const unsigned long siz2 = siz>>1;
16367  cimg_for(buf,ptrs,Tfloat) {
16368  const Tdouble val = (Tdouble)*ptrs; (*ptrs)=(Tfloat)((*ptrs)*val); average+=val;
16369  }
16370  buf.sort();
16371  Tdouble a = 0;
16372  const Tfloat *ptrs = buf._data;
16373  for (unsigned long j = 0; j<siz2; ++j) a+=(Tdouble)*(ptrs++);
16374  const Tdouble sig = (Tdouble)(2.6477*std::sqrt(a/siz2));
16375  variance = sig*sig;
16376  }
16377  }
16378  mean = (t)(average/siz);
16379  return variance>0?variance:0;
16380  }
16381 
16383 
16391  Tdouble variance_noise(const unsigned int variance_method=2) const {
16392  if (is_empty())
16393  throw CImgInstanceException(_cimg_instance
16394  "variance_noise(): Empty instance.",
16395  cimg_instance);
16396 
16397  const unsigned long siz = size();
16398  if (!siz || !_data) return 0;
16399  if (variance_method>1) { // Compute a scaled version of the Laplacian.
16400  CImg<Tdouble> tmp(*this);
16401  if (_depth==1) {
16402  const Tdouble cste = 1.0/std::sqrt(20.0); // Depends on how the Laplacian is computed.
16403 #ifdef cimg_use_openmp
16404 #pragma omp parallel for if (_width*_height>=262144 && _spectrum>=2)
16405 #endif
16406  cimg_forC(*this,c) {
16407  CImg_3x3(I,T);
16408  cimg_for3x3(*this,x,y,0,c,I,T) {
16409  tmp(x,y,c) = cste*((Tdouble)Inc + (Tdouble)Ipc + (Tdouble)Icn +
16410  (Tdouble)Icp - 4*(Tdouble)Icc);
16411  }
16412  }
16413  } else {
16414  const Tdouble cste = 1.0/std::sqrt(42.0); // Depends on how the Laplacian is computed.
16415 #ifdef cimg_use_openmp
16416 #pragma omp parallel for if (_width*_height*_depth>=262144 && _spectrum>=2)
16417 #endif
16418  cimg_forC(*this,c) {
16419  CImg_3x3x3(I,T);
16420  cimg_for3x3x3(*this,x,y,z,c,I,T) {
16421  tmp(x,y,z,c) = cste*(
16422  (Tdouble)Incc + (Tdouble)Ipcc + (Tdouble)Icnc + (Tdouble)Icpc +
16423  (Tdouble)Iccn + (Tdouble)Iccp - 6*(Tdouble)Iccc);
16424  }
16425  }
16426  }
16427  return tmp.variance(variance_method);
16428  }
16429 
16430  // Version that doesn't need intermediate images.
16431  Tdouble variance = 0, S = 0, S2 = 0;
16432  if (_depth==1) {
16433  const Tdouble cste = 1.0/std::sqrt(20.0);
16434  CImg_3x3(I,T);
16435  cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) {
16436  const Tdouble val = cste*((Tdouble)Inc + (Tdouble)Ipc +
16437  (Tdouble)Icn + (Tdouble)Icp - 4*(Tdouble)Icc);
16438  S+=val; S2+=val*val;
16439  }
16440  } else {
16441  const Tdouble cste = 1.0/std::sqrt(42.0);
16442  CImg_3x3x3(I,T);
16443  cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,T) {
16444  const Tdouble val = cste *
16445  ((Tdouble)Incc + (Tdouble)Ipcc + (Tdouble)Icnc +
16446  (Tdouble)Icpc +
16447  (Tdouble)Iccn + (Tdouble)Iccp - 6*(Tdouble)Iccc);
16448  S+=val; S2+=val*val;
16449  }
16450  }
16451  if (variance_method) variance = siz>1?(S2 - S*S/siz)/(siz - 1):0;
16452  else variance = (S2 - S*S/siz)/siz;
16453  return variance>0?variance:0;
16454  }
16455 
16457 
16460  template<typename t>
16461  Tdouble MSE(const CImg<t>& img) const {
16462  if (img.size()!=size())
16463  throw CImgArgumentException(_cimg_instance
16464  "MSE(): Instance and specified image (%u,%u,%u,%u,%p) have different dimensions.",
16465  cimg_instance,
16466  img._width,img._height,img._depth,img._spectrum,img._data);
16467  Tdouble vMSE = 0;
16468  const t* ptr2 = img._data;
16469  cimg_for(*this,ptr1,T) {
16470  const Tdouble diff = (Tdouble)*ptr1 - (Tdouble)*(ptr2++);
16471  vMSE+=diff*diff;
16472  }
16473  const unsigned long siz = img.size();
16474  if (siz) vMSE/=siz;
16475  return vMSE;
16476  }
16477 
16479 
16483  template<typename t>
16484  Tdouble PSNR(const CImg<t>& img, const Tdouble max_value=255) const {
16485  const Tdouble vMSE = (Tdouble)std::sqrt(MSE(img));
16486  return (vMSE!=0)?(Tdouble)(20*std::log10(max_value/vMSE)):(Tdouble)(cimg::type<Tdouble>::max());
16487  }
16488 
16490 
16497  double eval(const char *const expression,
16498  const double x=0, const double y=0, const double z=0, const double c=0) const {
16499  if (!expression) return 0;
16500  return _cimg_math_parser(*this,expression,"eval")(x,y,z,c);
16501  }
16502 
16504 
16508  template<typename t>
16509  CImg<doubleT> eval(const char *const expression, const CImg<t>& xyzc) const {
16510  CImg<doubleT> res(1,xyzc.size()/4);
16511  if (!expression) return res.fill(0);
16512  _cimg_math_parser mp(*this,expression,"eval");
16513 #ifdef cimg_use_openmp
16514 #pragma omp parallel if (res._height>=512 && std::strlen(expression)>=6)
16515  {
16516  _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp;
16517 #pragma omp for
16518  for (unsigned int i = 0; i<res._height; ++i) {
16519  const unsigned int i4 = 4*i;
16520  const double x = (double)xyzc[i4], y = (double)xyzc[i4+1], z = (double)xyzc[i4+2], c = (double)xyzc[i4+3];
16521  res[i] = lmp(x,y,z,c);
16522  }
16523  }
16524 #else
16525  const t *ps = xyzc._data;
16526  cimg_for(res,pd,double) {
16527  const double x = (double)*(ps++), y = (double)*(ps++), z = (double)*(ps++), c = (double)*(ps++);
16528  *pd = mp(x,y,z,c);
16529  }
16530 #endif
16531  return res;
16532  }
16533 
16535  /*
16536  \param variance_method Method used to compute the variance (see variance(const unsigned int) const).
16537  \return Statistics vector as <tt>[min; max; mean; variance; xmin; ymin; zmin; cmin; xmax; ymax; zmax; cmax]</tt>.
16538  **/
16539  CImg<Tdouble> get_stats(const unsigned int variance_method=1) const {
16540  if (is_empty()) return CImg<doubleT>();
16541  const unsigned long siz = size();
16542  const T *const odata = _data;
16543  const T *pm = odata, *pM = odata;
16544  Tdouble S = 0, S2 = 0;
16545  T m = *pm, M = m;
16546  cimg_for(*this,ptrs,T) {
16547  const T val = *ptrs;
16548  const Tdouble _val = (Tdouble)val;
16549  if (val<m) { m = val; pm = ptrs; }
16550  if (val>M) { M = val; pM = ptrs; }
16551  S+=_val;
16552  S2+=_val*_val;
16553  }
16554  const Tdouble
16555  mean_value = S/siz,
16556  _variance_value = variance_method==0?(S2 - S*S/siz)/siz:
16557  (variance_method==1?(siz>1?(S2 - S*S/siz)/(siz - 1):0):
16558  variance(variance_method)),
16559  variance_value = _variance_value>0?_variance_value:0;
16560  int
16561  xm = 0, ym = 0, zm = 0, cm = 0,
16562  xM = 0, yM = 0, zM = 0, cM = 0;
16563  contains(*pm,xm,ym,zm,cm);
16564  contains(*pM,xM,yM,zM,cM);
16565  return CImg<Tdouble>(1,12).fill((Tdouble)m,(Tdouble)M,mean_value,variance_value,
16566  (Tdouble)xm,(Tdouble)ym,(Tdouble)zm,(Tdouble)cm,
16567  (Tdouble)xM,(Tdouble)yM,(Tdouble)zM,(Tdouble)cM);
16568  }
16569 
16571  CImg<T>& stats(const unsigned int variance_method=1) {
16572  return get_stats(variance_method).move_to(*this);
16573  }
16574 
16576  //-------------------------------------
16577  //
16579 
16580  //-------------------------------------
16581 
16583 
16589  Tdouble magnitude(const int magnitude_type=2) const {
16590  if (is_empty())
16591  throw CImgInstanceException(_cimg_instance
16592  "magnitude(): Empty instance.",
16593  cimg_instance);
16594  Tdouble res = 0;
16595  switch (magnitude_type) {
16596  case -1 : {
16597  cimg_for(*this,ptrs,T) { const Tdouble val = (Tdouble)cimg::abs(*ptrs); if (val>res) res = val; }
16598  } break;
16599  case 1 : {
16600  cimg_for(*this,ptrs,T) res+=(Tdouble)cimg::abs(*ptrs);
16601  } break;
16602  default : {
16603  cimg_for(*this,ptrs,T) res+=(Tdouble)cimg::sqr(*ptrs);
16604  res = (Tdouble)std::sqrt(res);
16605  }
16606  }
16607  return res;
16608  }
16609 
16611 
16613  Tdouble trace() const {
16614  if (is_empty())
16615  throw CImgInstanceException(_cimg_instance
16616  "trace(): Empty instance.",
16617  cimg_instance);
16618  Tdouble res = 0;
16619  cimg_forX(*this,k) res+=(Tdouble)(*this)(k,k);
16620  return res;
16621  }
16622 
16624 
16626  Tdouble det() const {
16627  if (is_empty() || _width!=_height || _depth!=1 || _spectrum!=1)
16628  throw CImgInstanceException(_cimg_instance
16629  "det(): Instance is not a square matrix.",
16630  cimg_instance);
16631 
16632  switch (_width) {
16633  case 1 : return (Tdouble)((*this)(0,0));
16634  case 2 : return (Tdouble)((*this)(0,0))*(Tdouble)((*this)(1,1)) - (Tdouble)((*this)(0,1))*(Tdouble)((*this)(1,0));
16635  case 3 : {
16636  const Tdouble
16637  a = (Tdouble)_data[0], d = (Tdouble)_data[1], g = (Tdouble)_data[2],
16638  b = (Tdouble)_data[3], e = (Tdouble)_data[4], h = (Tdouble)_data[5],
16639  c = (Tdouble)_data[6], f = (Tdouble)_data[7], i = (Tdouble)_data[8];
16640  return i*a*e - a*h*f - i*b*d + b*g*f + c*d*h - c*g*e;
16641  }
16642  default : {
16643  CImg<Tfloat> lu(*this);
16644  CImg<uintT> indx;
16645  bool d;
16646  lu._LU(indx,d);
16647  Tdouble res = d?(Tdouble)1:(Tdouble)-1;
16648  cimg_forX(lu,i) res*=lu(i,i);
16649  return res;
16650  }
16651  }
16652  }
16653 
16655 
16658  template<typename t>
16659  Tdouble dot(const CImg<t>& img) const {
16660  if (is_empty())
16661  throw CImgInstanceException(_cimg_instance
16662  "dot(): Empty instance.",
16663  cimg_instance);
16664  if (!img)
16665  throw CImgArgumentException(_cimg_instance
16666  "dot(): Empty specified image.",
16667  cimg_instance);
16668 
16669  const unsigned int nb = cimg::min(size(),img.size());
16670  Tdouble res = 0;
16671  for (unsigned int off = 0; off<nb; ++off) res+=(Tdouble)_data[off]*(Tdouble)img[off];
16672  return res;
16673  }
16674 
16676 
16681  CImg<T> get_vector_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const {
16682  CImg<T> res;
16683  if (res._height!=_spectrum) res.assign(1,_spectrum);
16684  const unsigned long whd = (unsigned long)_width*_height*_depth;
16685  const T *ptrs = data(x,y,z);
16686  T *ptrd = res._data;
16687  cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
16688  return res;
16689  }
16690 
16692 
16698  CImg<T> get_matrix_at(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const {
16699  const int n = (int)std::sqrt((double)_spectrum);
16700  const T *ptrs = data(x,y,z,0);
16701  const unsigned long whd = (unsigned long)_width*_height*_depth;
16702  CImg<T> res(n,n);
16703  T *ptrd = res._data;
16704  cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
16705  return res;
16706  }
16707 
16709 
16714  CImg<T> get_tensor_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const {
16715  const T *ptrs = data(x,y,z,0);
16716  const unsigned long whd = (unsigned long)_width*_height*_depth;
16717  if (_spectrum==6) return tensor(*ptrs,*(ptrs+whd),*(ptrs+2*whd),*(ptrs+3*whd),*(ptrs+4*whd),*(ptrs+5*whd));
16718  if (_spectrum==3) return tensor(*ptrs,*(ptrs+whd),*(ptrs+2*whd));
16719  return tensor(*ptrs);
16720  }
16721 
16723 
16729  template<typename t>
16730  CImg<T>& set_vector_at(const CImg<t>& vec, const unsigned int x, const unsigned int y=0, const unsigned int z=0) {
16731  if (x<_width && y<_height && z<_depth) {
16732  const t *ptrs = vec._data;
16733  const unsigned long whd = (unsigned long)_width*_height*_depth;
16734  T *ptrd = data(x,y,z);
16735  for (unsigned int k = cimg::min((unsigned int)vec.size(),_spectrum); k; --k) {
16736  *ptrd = (T)*(ptrs++); ptrd+=whd;
16737  }
16738  }
16739  return *this;
16740  }
16741 
16743 
16749  template<typename t>
16750  CImg<T>& set_matrix_at(const CImg<t>& mat, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
16751  return set_vector_at(mat,x,y,z);
16752  }
16753 
16755 
16761  template<typename t>
16762  CImg<T>& set_tensor_at(const CImg<t>& ten, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
16763  T *ptrd = data(x,y,z,0);
16764  const unsigned long siz = (unsigned long)_width*_height*_depth;
16765  if (ten._height==2) {
16766  *ptrd = (T)ten[0]; ptrd+=siz;
16767  *ptrd = (T)ten[1]; ptrd+=siz;
16768  *ptrd = (T)ten[3];
16769  }
16770  else {
16771  *ptrd = (T)ten[0]; ptrd+=siz;
16772  *ptrd = (T)ten[1]; ptrd+=siz;
16773  *ptrd = (T)ten[2]; ptrd+=siz;
16774  *ptrd = (T)ten[4]; ptrd+=siz;
16775  *ptrd = (T)ten[5]; ptrd+=siz;
16776  *ptrd = (T)ten[8];
16777  }
16778  return *this;
16779  }
16780 
16782 
16786  return unroll('y');
16787  }
16788 
16791  return get_unroll('y');
16792  }
16793 
16795 
16798  const unsigned long siz = size();
16799  switch (siz) {
16800  case 1 : break;
16801  case 4 : _width = _height = 2; break;
16802  case 9 : _width = _height = 3; break;
16803  case 16 : _width = _height = 4; break;
16804  case 25 : _width = _height = 5; break;
16805  case 36 : _width = _height = 6; break;
16806  case 49 : _width = _height = 7; break;
16807  case 64 : _width = _height = 8; break;
16808  case 81 : _width = _height = 9; break;
16809  case 100 : _width = _height = 10; break;
16810  default : {
16811  unsigned long i = 11, i2 = i*i;
16812  while (i2<siz) { i2+=2*i + 1; ++i; }
16813  if (i2==siz) _width = _height = i;
16814  else throw CImgInstanceException(_cimg_instance
16815  "matrix(): Invalid instance size %u (should be a square integer).",
16816  cimg_instance,
16817  siz);
16818  }
16819  }
16820  return *this;
16821  }
16822 
16825  return (+*this).matrix();
16826  }
16827 
16829 
16832  return get_tensor().move_to(*this);
16833  }
16834 
16837  CImg<T> res;
16838  const unsigned long siz = size();
16839  switch (siz) {
16840  case 1 : break;
16841  case 3 :
16842  res.assign(2,2);
16843  res(0,0) = (*this)(0);
16844  res(1,0) = res(0,1) = (*this)(1);
16845  res(1,1) = (*this)(2);
16846  break;
16847  case 6 :
16848  res.assign(3,3);
16849  res(0,0) = (*this)(0);
16850  res(1,0) = res(0,1) = (*this)(1);
16851  res(2,0) = res(0,2) = (*this)(2);
16852  res(1,1) = (*this)(3);
16853  res(2,1) = res(1,2) = (*this)(4);
16854  res(2,2) = (*this)(5);
16855  break;
16856  default :
16857  throw CImgInstanceException(_cimg_instance
16858  "tensor(): Invalid instance size (does not define a 1x1, 2x2 or 3x3 tensor).",
16859  cimg_instance);
16860  }
16861  return res;
16862  }
16863 
16865 
16869  return get_diagonal().move_to(*this);
16870  }
16871 
16874  if (is_empty()) return *this;
16875  CImg<T> res(size(),size(),1,1,0);
16876  cimg_foroff(*this,off) res(off,off) = (*this)(off);
16877  return res;
16878  }
16879 
16881 
16886  return identity_matrix(cimg::max(_width,_height)).move_to(*this);
16887  }
16888 
16891  return identity_matrix(cimg::max(_width,_height));
16892  }
16893 
16895 
16899  CImg<T>& sequence(const T a0, const T a1) {
16900  if (is_empty()) return *this;
16901  const unsigned int siz = size() - 1;
16902  T* ptr = _data;
16903  if (siz) {
16904  const Tdouble delta = (Tdouble)a1 - (Tdouble)a0;
16905  cimg_foroff(*this,l) *(ptr++) = (T)(a0 + delta*l/siz);
16906  } else *ptr = a0;
16907  return *this;
16908  }
16909 
16911  CImg<T> get_sequence(const T a0, const T a1) const {
16912  return (+*this).sequence(a0,a1);
16913  }
16914 
16916 
16920  if (_width==1) { _width = _height; _height = 1; return *this; }
16921  if (_height==1) { _height = _width; _width = 1; return *this; }
16922  if (_width==_height) {
16923  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));
16924  return *this;
16925  }
16926  return get_transpose().move_to(*this);
16927  }
16928 
16931  return get_permute_axes("yxzc");
16932  }
16933 
16935 
16939  template<typename t>
16940  CImg<T>& cross(const CImg<t>& img) {
16941  if (_width!=1 || _height<3 || img._width!=1 || img._height<3)
16942  throw CImgInstanceException(_cimg_instance
16943  "cross(): Instance and/or specified image (%u,%u,%u,%u,%p) are not 3d vectors.",
16944  cimg_instance,
16945  img._width,img._height,img._depth,img._spectrum,img._data);
16946 
16947  const T x = (*this)[0], y = (*this)[1], z = (*this)[2];
16948  (*this)[0] = (T)(y*img[2] - z*img[1]);
16949  (*this)[1] = (T)(z*img[0] - x*img[2]);
16950  (*this)[2] = (T)(x*img[1] - y*img[0]);
16951  return *this;
16952  }
16953 
16955  template<typename t>
16956  CImg<_cimg_Tt> get_cross(const CImg<t>& img) const {
16957  return CImg<_cimg_Tt>(*this).cross(img);
16958  }
16959 
16961 
16966  CImg<T>& invert(const bool use_LU=true) {
16967  if (_width!=_height || _depth!=1 || _spectrum!=1)
16968  throw CImgInstanceException(_cimg_instance
16969  "invert(): Instance is not a square matrix.",
16970  cimg_instance);
16971 #ifdef cimg_use_lapack
16972  int INFO = (int)use_LU, N = _width, LWORK = 4*N, *const IPIV = new int[N];
16973  Tfloat
16974  *const lapA = new Tfloat[N*N],
16975  *const WORK = new Tfloat[LWORK];
16976  cimg_forXY(*this,k,l) lapA[k*N+l] = (Tfloat)((*this)(k,l));
16977  cimg::getrf(N,lapA,IPIV,INFO);
16978  if (INFO)
16979  cimg::warn(_cimg_instance
16980  "invert(): LAPACK function dgetrf_() returned error code %d.",
16981  cimg_instance,
16982  INFO);
16983  else {
16984  cimg::getri(N,lapA,IPIV,WORK,LWORK,INFO);
16985  if (INFO)
16986  cimg::warn(_cimg_instance
16987  "invert(): LAPACK function dgetri_() returned error code %d.",
16988  cimg_instance,
16989  INFO);
16990  }
16991  if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)(lapA[k*N+l]); else fill(0);
16992  delete[] IPIV; delete[] lapA; delete[] WORK;
16993 #else
16994  const double dete = _width>3?-1.0:det();
16995  if (dete!=0.0 && _width==2) {
16996  const double
16997  a = _data[0], c = _data[1],
16998  b = _data[2], d = _data[3];
16999  _data[0] = (T)(d/dete); _data[1] = (T)(-c/dete);
17000  _data[2] = (T)(-b/dete); _data[3] = (T)(a/dete);
17001  } else if (dete!=0.0 && _width==3) {
17002  const double
17003  a = _data[0], d = _data[1], g = _data[2],
17004  b = _data[3], e = _data[4], h = _data[5],
17005  c = _data[6], f = _data[7], i = _data[8];
17006  _data[0] = (T)((i*e-f*h)/dete), _data[1] = (T)((g*f-i*d)/dete), _data[2] = (T)((d*h-g*e)/dete);
17007  _data[3] = (T)((h*c-i*b)/dete), _data[4] = (T)((i*a-c*g)/dete), _data[5] = (T)((g*b-a*h)/dete);
17008  _data[6] = (T)((b*f-e*c)/dete), _data[7] = (T)((d*c-a*f)/dete), _data[8] = (T)((a*e-d*b)/dete);
17009  } else {
17010  if (use_LU) { // LU-based inverse computation
17011  CImg<Tfloat> A(*this), indx, col(1,_width);
17012  bool d;
17013  A._LU(indx,d);
17014  cimg_forX(*this,j) {
17015  col.fill(0);
17016  col(j) = 1;
17017  col._solve(A,indx);
17018  cimg_forX(*this,i) (*this)(j,i) = (T)col(i);
17019  }
17020  } else { // SVD-based inverse computation
17021  CImg<Tfloat> U(_width,_width), S(1,_width), V(_width,_width);
17022  SVD(U,S,V,false);
17023  U.transpose();
17024  cimg_forY(S,k) if (S[k]!=0) S[k]=1/S[k];
17025  S.diagonal();
17026  *this = V*S*U;
17027  }
17028  }
17029 #endif
17030  return *this;
17031  }
17032 
17034  CImg<Tfloat> get_invert(const bool use_LU=true) const {
17035  return CImg<Tfloat>(*this,false).invert(use_LU);
17036  }
17037 
17039 
17042  return get_pseudoinvert().move_to(*this);
17043  }
17044 
17047  CImg<Tfloat> U, S, V;
17048  SVD(U,S,V);
17049  const Tfloat tolerance = (sizeof(Tfloat)<=4?5.96e-8f:1.11e-16f)*cimg::max(_width,_height)*S.max();
17050  cimg_forX(V,x) {
17051  const Tfloat s = S(x), invs = s>tolerance?1/s:(Tfloat)0;
17052  cimg_forY(V,y) V(x,y)*=invs;
17053  }
17054  return V*U.transpose();
17055  }
17056 
17058 
17062  template<typename t>
17063  CImg<T>& solve(const CImg<t>& A) {
17064  if (_width!=1 || _depth!=1 || _spectrum!=1 || _height!=A._height || A._depth!=1 || A._spectrum!=1)
17065  throw CImgArgumentException(_cimg_instance
17066  "solve(): Instance and specified matrix (%u,%u,%u,%u,%p) have "
17067  "incompatible dimensions.",
17068  cimg_instance,
17069  A._width,A._height,A._depth,A._spectrum,A._data);
17070  typedef _cimg_Ttfloat Ttfloat;
17071  if (A._width==A._height) {
17072 #ifdef cimg_use_lapack
17073  char TRANS = 'N';
17074  int INFO, N = _height, LWORK = 4*N, *const IPIV = new int[N];
17075  Ttfloat
17076  *const lapA = new Ttfloat[N*N],
17077  *const lapB = new Ttfloat[N],
17078  *const WORK = new Ttfloat[LWORK];
17079  cimg_forXY(A,k,l) lapA[k*N+l] = (Ttfloat)(A(k,l));
17080  cimg_forY(*this,i) lapB[i] = (Ttfloat)((*this)(i));
17081  cimg::getrf(N,lapA,IPIV,INFO);
17082  if (INFO)
17083  cimg::warn(_cimg_instance
17084  "solve(): LAPACK library function dgetrf_() returned error code %d.",
17085  cimg_instance,
17086  INFO);
17087 
17088  if (!INFO) {
17089  cimg::getrs(TRANS,N,lapA,IPIV,lapB,INFO);
17090  if (INFO)
17091  cimg::warn(_cimg_instance
17092  "solve(): LAPACK library function dgetrs_() returned error code %d.",
17093  cimg_instance,
17094  INFO);
17095  }
17096  if (!INFO) cimg_forY(*this,i) (*this)(i) = (T)(lapB[i]); else fill(0);
17097  delete[] IPIV; delete[] lapA; delete[] lapB; delete[] WORK;
17098 #else
17099  CImg<Ttfloat> lu(A,false);
17100  CImg<Ttfloat> indx;
17101  bool d;
17102  lu._LU(indx,d);
17103  _solve(lu,indx);
17104 #endif
17105  } else { // Least-square solution for non-square systems.
17106 #ifdef cimg_use_lapack
17107  char TRANS = 'N';
17108  int INFO, N = A._width, M = A._height, LWORK = -1, LDA = M, LDB = M, NRHS = _width;
17109  Ttfloat WORK_QUERY;
17110  Ttfloat
17111  * const lapA = new Ttfloat[M*N],
17112  * const lapB = new Ttfloat[M*NRHS];
17113  cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, &WORK_QUERY, LWORK, INFO);
17114  LWORK = (int) WORK_QUERY;
17115  Ttfloat *const WORK = new Ttfloat[LWORK];
17116  cimg_forXY(A,k,l) lapA[k*M+l] = (Ttfloat)(A(k,l));
17117  cimg_forXY(*this,k,l) lapB[k*M+l] = (Ttfloat)((*this)(k,l));
17118  cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, WORK, LWORK, INFO);
17119  if (INFO != 0)
17120  cimg::warn(_cimg_instance
17121  "solve(): LAPACK library function sgels() returned error code %d.",
17122  cimg_instance,
17123  INFO);
17124  assign(NRHS, N);
17125  if (!INFO != 0)
17126  cimg_forXY(*this,k,l) (*this)(k,l) = (T)lapB[k*M+l];
17127  else
17128  assign(A.get_pseudoinvert()*(*this));
17129  delete[] lapA; delete[] lapB; delete[] WORK;
17130 #else
17131  assign(A.get_pseudoinvert()*(*this));
17132 #endif
17133  }
17134  return *this;
17135  }
17136 
17138  template<typename t>
17140  return CImg<_cimg_Ttfloat>(*this,false).solve(A);
17141  }
17142 
17143  template<typename t, typename ti>
17144  CImg<T>& _solve(const CImg<t>& A, const CImg<ti>& indx) {
17145  typedef _cimg_Ttfloat Ttfloat;
17146  const int N = size();
17147  int ii = -1;
17148  Ttfloat sum;
17149  for (int i = 0; i<N; ++i) {
17150  const int ip = (int)indx[i];
17151  Ttfloat sum = (*this)(ip);
17152  (*this)(ip) = (*this)(i);
17153  if (ii>=0) for (int j = ii; j<=i-1; ++j) sum-=A(j,i)*(*this)(j);
17154  else if (sum!=0) ii = i;
17155  (*this)(i) = (T)sum;
17156  }
17157  for (int i = N - 1; i>=0; --i) {
17158  sum = (*this)(i);
17159  for (int j = i + 1; j<N; ++j) sum-=A(j,i)*(*this)(j);
17160  (*this)(i) = (T)(sum/A(i,i));
17161  }
17162  return *this;
17163  }
17164 
17166 
17172  template<typename t>
17174  const unsigned int siz = (int)size();
17175  if (A._width!=3 || A._height!=siz)
17176  throw CImgArgumentException(_cimg_instance
17177  "solve_tridiagonal(): Instance and tridiagonal matrix "
17178  "(%u,%u,%u,%u,%p) have incompatible dimensions.",
17179  cimg_instance,
17180  A._width,A._height,A._depth,A._spectrum,A._data);
17181  typedef _cimg_Ttfloat Ttfloat;
17182  const Ttfloat epsilon = 1e-4f;
17183  CImg<Ttfloat> B = A.get_column(1), V(*this,false);
17184  for (int i = 1; i<(int)siz; ++i) {
17185  const Ttfloat m = A(0,i)/(B[i-1]?B[i-1]:epsilon);
17186  B[i] -= m*A(2,i-1);
17187  V[i] -= m*V[i-1];
17188  }
17189  (*this)[siz-1] = (T)(V[siz-1]/(B[siz-1]?B[siz-1]:epsilon));
17190  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));
17191  return *this;
17192  }
17193 
17195  template<typename t>
17197  return CImg<_cimg_Ttfloat>(*this,false).solve_tridiagonal(A);
17198  }
17199 
17201 
17205  template<typename t>
17206  const CImg<T>& eigen(CImg<t>& val, CImg<t> &vec) const {
17207  if (is_empty()) { val.assign(); vec.assign(); }
17208  else {
17209  if (_width!=_height || _depth>1 || _spectrum>1)
17210  throw CImgInstanceException(_cimg_instance
17211  "eigen(): Instance is not a square matrix.",
17212  cimg_instance);
17213 
17214  if (val.size()<(unsigned long)_width) val.assign(1,_width);
17215  if (vec.size()<(unsigned long)_width*_width) vec.assign(_width,_width);
17216  switch (_width) {
17217  case 1 : { val[0] = (t)(*this)[0]; vec[0] = (t)1; } break;
17218  case 2 : {
17219  const double a = (*this)[0], b = (*this)[1], c = (*this)[2], d = (*this)[3], e = a + d;
17220  double f = e*e - 4*(a*d - b*c);
17221  if (f<0)
17222  cimg::warn(_cimg_instance
17223  "eigen(): Complex eigenvalues found.",
17224  cimg_instance);
17225 
17226  f = std::sqrt(f);
17227  const double l1 = 0.5*(e-f), l2 = 0.5*(e+f);
17228  const double theta1 = std::atan2(l2-a,b), theta2 = std::atan2(l1-a,b);
17229  val[0] = (t)l2;
17230  val[1] = (t)l1;
17231  vec(0,0) = (t)std::cos(theta1);
17232  vec(0,1) = (t)std::sin(theta1);
17233  vec(1,0) = (t)std::cos(theta2);
17234  vec(1,1) = (t)std::sin(theta2);
17235  } break;
17236  default :
17237  throw CImgInstanceException(_cimg_instance
17238  "eigen(): Eigenvalues computation of general matrices is limited "
17239  "to 2x2 matrices.",
17240  cimg_instance);
17241  }
17242  }
17243  return *this;
17244  }
17245 
17247 
17251  CImgList<Tfloat> res(2);
17252  eigen(res[0],res[1]);
17253  return res;
17254  }
17255 
17257 
17261  template<typename t>
17262  const CImg<T>& symmetric_eigen(CImg<t>& val, CImg<t>& vec) const {
17263  if (is_empty()) { val.assign(); vec.assign(); }
17264  else {
17265 #ifdef cimg_use_lapack
17266  char JOB = 'V', UPLO = 'U';
17267  int N = _width, LWORK = 4*N, INFO;
17268  Tfloat
17269  *const lapA = new Tfloat[N*N],
17270  *const lapW = new Tfloat[N],
17271  *const WORK = new Tfloat[LWORK];
17272  cimg_forXY(*this,k,l) lapA[k*N+l] = (Tfloat)((*this)(k,l));
17273  cimg::syev(JOB,UPLO,N,lapA,lapW,WORK,LWORK,INFO);
17274  if (INFO)
17275  cimg::warn(_cimg_instance
17276  "symmetric_eigen(): LAPACK library function dsyev_() returned error code %d.",
17277  cimg_instance,
17278  INFO);
17279 
17280  val.assign(1,N);
17281  vec.assign(N,N);
17282  if (!INFO) {
17283  cimg_forY(val,i) val(i) = (T)lapW[N-1-i];
17284  cimg_forXY(vec,k,l) vec(k,l) = (T)(lapA[(N-1-k)*N+l]);
17285  } else { val.fill(0); vec.fill(0); }
17286  delete[] lapA; delete[] lapW; delete[] WORK;
17287 #else
17288  if (_width!=_height || _depth>1 || _spectrum>1)
17289  throw CImgInstanceException(_cimg_instance
17290  "eigen(): Instance is not a square matrix.",
17291  cimg_instance);
17292 
17293  val.assign(1,_width);
17294  if (vec._data) vec.assign(_width,_width);
17295  if (_width<3) {
17296  eigen(val,vec);
17297  if (_width==2) { vec[1] = -vec[2]; vec[3] = vec[0]; } // Force orthogonality for 2x2 matrices.
17298  return *this;
17299  }
17300  CImg<t> V(_width,_width);
17301  SVD(vec,val,V,false);
17302 
17303  bool is_ambiguous = false;
17304  float eig = 0;
17305  cimg_forY(val,p) { // check for ambiguous cases.
17306  if (val[p]>eig) eig = (float)val[p];
17307  t scal = 0;
17308  cimg_forY(vec,y) scal+=vec(p,y)*V(p,y);
17309  if (cimg::abs(scal)<0.9f) is_ambiguous = true;
17310  if (scal<0) val[p] = -val[p];
17311  }
17312  if (is_ambiguous) {
17313  ++(eig*=2);
17314  SVD(vec,val,V,false,40,eig);
17315  val-=eig;
17316  }
17317  CImg<intT> permutations; // sort eigenvalues in decreasing order
17318  CImg<t> tmp(_width);
17319  val.sort(permutations,false);
17320  cimg_forY(vec,k) {
17321  cimg_forY(permutations,y) tmp(y) = vec(permutations(y),k);
17322  std::memcpy(vec.data(0,k),tmp._data,sizeof(t)*_width);
17323  }
17324 #endif
17325  }
17326  return *this;
17327  }
17328 
17330 
17335  CImgList<Tfloat> res(2);
17336  symmetric_eigen(res[0],res[1]);
17337  return res;
17338  }
17339 
17341 
17345  template<typename t>
17346  CImg<T>& sort(CImg<t>& permutations, const bool is_increasing=true) {
17347  permutations.assign(_width,_height,_depth,_spectrum);
17348  if (is_empty()) return *this;
17349  cimg_foroff(permutations,off) permutations[off] = (t)off;
17350  return _quicksort(0,size()-1,permutations,is_increasing,true);
17351  }
17352 
17354  template<typename t>
17355  CImg<T> get_sort(CImg<t>& permutations, const bool is_increasing=true) const {
17356  return (+*this).sort(permutations,is_increasing);
17357  }
17358 
17360 
17369  CImg<T>& sort(const bool is_increasing=true, const char axis=0) {
17370  if (is_empty()) return *this;
17371  CImg<uintT> perm;
17372  switch (cimg::uncase(axis)) {
17373  case 0 :
17374  _quicksort(0,size()-1,perm,is_increasing,false);
17375  break;
17376  case 'x' : {
17377  perm.assign(_width);
17378  get_crop(0,0,0,0,_width-1,0,0,0).sort(perm,is_increasing);
17379  CImg<T> img(*this,false);
17380  cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(perm[x],y,z,c);
17381  } break;
17382  case 'y' : {
17383  perm.assign(_height);
17384  get_crop(0,0,0,0,0,_height-1,0,0).sort(perm,is_increasing);
17385  CImg<T> img(*this,false);
17386  cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,perm[y],z,c);
17387  } break;
17388  case 'z' : {
17389  perm.assign(_depth);
17390  get_crop(0,0,0,0,0,0,_depth-1,0).sort(perm,is_increasing);
17391  CImg<T> img(*this,false);
17392  cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,perm[z],c);
17393  } break;
17394  case 'c' : {
17395  perm.assign(_spectrum);
17396  get_crop(0,0,0,0,0,0,0,_spectrum-1).sort(perm,is_increasing);
17397  CImg<T> img(*this,false);
17398  cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,z,perm[c]);
17399  } break;
17400  default :
17401  throw CImgArgumentException(_cimg_instance
17402  "sort(): Invalid specified axis '%c' "
17403  "(should be { x | y | z | c }).",
17404  cimg_instance,axis);
17405  }
17406  return *this;
17407  }
17408 
17410  CImg<T> get_sort(const bool is_increasing=true, const char axis=0) const {
17411  return (+*this).sort(is_increasing,axis);
17412  }
17413 
17414  template<typename t>
17415  CImg<T>& _quicksort(const int indm, const int indM, CImg<t>& permutations,
17416  const bool is_increasing, const bool is_permutations) {
17417  if (indm<indM) {
17418  const int mid = (indm + indM)/2;
17419  if (is_increasing) {
17420  if ((*this)[indm]>(*this)[mid]) {
17421  cimg::swap((*this)[indm],(*this)[mid]);
17422  if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
17423  }
17424  if ((*this)[mid]>(*this)[indM]) {
17425  cimg::swap((*this)[indM],(*this)[mid]);
17426  if (is_permutations) cimg::swap(permutations[indM],permutations[mid]);
17427  }
17428  if ((*this)[indm]>(*this)[mid]) {
17429  cimg::swap((*this)[indm],(*this)[mid]);
17430  if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
17431  }
17432  } else {
17433  if ((*this)[indm]<(*this)[mid]) {
17434  cimg::swap((*this)[indm],(*this)[mid]);
17435  if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
17436  }
17437  if ((*this)[mid]<(*this)[indM]) {
17438  cimg::swap((*this)[indM],(*this)[mid]);
17439  if (is_permutations) cimg::swap(permutations[indM],permutations[mid]);
17440  }
17441  if ((*this)[indm]<(*this)[mid]) {
17442  cimg::swap((*this)[indm],(*this)[mid]);
17443  if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
17444  }
17445  }
17446  if (indM - indm>=3) {
17447  const T pivot = (*this)[mid];
17448  int i = indm, j = indM;
17449  if (is_increasing) {
17450  do {
17451  while ((*this)[i]<pivot) ++i;
17452  while ((*this)[j]>pivot) --j;
17453  if (i<=j) {
17454  if (is_permutations) cimg::swap(permutations[i],permutations[j]);
17455  cimg::swap((*this)[i++],(*this)[j--]);
17456  }
17457  } while (i<=j);
17458  } else {
17459  do {
17460  while ((*this)[i]>pivot) ++i;
17461  while ((*this)[j]<pivot) --j;
17462  if (i<=j) {
17463  if (is_permutations) cimg::swap(permutations[i],permutations[j]);
17464  cimg::swap((*this)[i++],(*this)[j--]);
17465  }
17466  } while (i<=j);
17467  }
17468  if (indm<j) _quicksort(indm,j,permutations,is_increasing,is_permutations);
17469  if (i<indM) _quicksort(i,indM,permutations,is_increasing,is_permutations);
17470  }
17471  }
17472  return *this;
17473  }
17474 
17476 
17493  template<typename t>
17494  const CImg<T>& SVD(CImg<t>& U, CImg<t>& S, CImg<t>& V, const bool sorting=true,
17495  const unsigned int max_iteration=40, const float lambda=0) const {
17496  if (is_empty()) { U.assign(); S.assign(); V.assign(); }
17497  else {
17498  U = *this;
17499  if (lambda!=0) {
17500  const unsigned int delta = cimg::min(U._width,U._height);
17501  for (unsigned int i = 0; i<delta; ++i) U(i,i) = (t)(U(i,i) + lambda);
17502  }
17503  if (S.size()<_width) S.assign(1,_width);
17504  if (V._width<_width || V._height<_height) V.assign(_width,_width);
17505  CImg<t> rv1(_width);
17506  t anorm = 0, c, f, g = 0, h, s, scale = 0;
17507  int l = 0, nm = 0;
17508 
17509  cimg_forX(U,i) {
17510  l = i+1; rv1[i] = scale*g; g = s = scale = 0;
17511  if (i<height()) {
17512  for (int k = i; k<height(); ++k) scale+= cimg::abs(U(i,k));
17513  if (scale) {
17514  for (int k = i; k<height(); ++k) { U(i,k)/=scale; s+= U(i,k)*U(i,k); }
17515  f = U(i,i); g = (t)((f>=0?-1:1)*std::sqrt(s)); h=f*g-s; U(i,i) = f-g;
17516  for (int j = l; j<width(); ++j) {
17517  s = 0;
17518  for (int k=i; k<height(); ++k) s+= U(i,k)*U(j,k);
17519  f = s/h;
17520  for (int k = i; k<height(); ++k) U(j,k)+= f*U(i,k);
17521  }
17522  for (int k = i; k<height(); ++k) U(i,k)*= scale;
17523  }
17524  }
17525  S[i]=scale*g;
17526 
17527  g = s = scale = 0;
17528  if (i<height() && i!=width()-1) {
17529  for (int k = l; k<width(); ++k) scale+=cimg::abs(U(k,i));
17530  if (scale) {
17531  for (int k = l; k<width(); ++k) { U(k,i)/= scale; s+= U(k,i)*U(k,i); }
17532  f = U(l,i); g = (t)((f>=0?-1:1)*std::sqrt(s)); h = f*g-s; U(l,i) = f-g;
17533  for (int k = l; k<width(); ++k) rv1[k]=U(k,i)/h;
17534  for (int j = l; j<height(); ++j) {
17535  s = 0;
17536  for (int k = l; k<width(); ++k) s+= U(k,j)*U(k,i);
17537  for (int k = l; k<width(); ++k) U(k,j)+= s*rv1[k];
17538  }
17539  for (int k = l; k<width(); ++k) U(k,i)*= scale;
17540  }
17541  }
17542  anorm = (t)cimg::max((float)anorm,(float)(cimg::abs(S[i])+cimg::abs(rv1[i])));
17543  }
17544 
17545  for (int i = width()-1; i>=0; --i) {
17546  if (i<width()-1) {
17547  if (g) {
17548  for (int j = l; j<width(); ++j) V(i,j) =(U(j,i)/U(l,i))/g;
17549  for (int j = l; j<width(); ++j) {
17550  s = 0;
17551  for (int k = l; k<width(); ++k) s+= U(k,i)*V(j,k);
17552  for (int k = l; k<width(); ++k) V(j,k)+= s*V(i,k);
17553  }
17554  }
17555  for (int j = l; j<width(); ++j) V(j,i) = V(i,j) = (t)0.0;
17556  }
17557  V(i,i) = (t)1.0; g = rv1[i]; l = i;
17558  }
17559 
17560  for (int i = cimg::min(width(),height())-1; i>=0; --i) {
17561  l = i+1; g = S[i];
17562  for (int j = l; j<width(); ++j) U(j,i) = 0;
17563  if (g) {
17564  g = 1/g;
17565  for (int j = l; j<width(); ++j) {
17566  s = 0; for (int k = l; k<height(); ++k) s+= U(i,k)*U(j,k);
17567  f = (s/U(i,i))*g;
17568  for (int k = i; k<height(); ++k) U(j,k)+= f*U(i,k);
17569  }
17570  for (int j = i; j<height(); ++j) U(i,j)*= g;
17571  } else for (int j = i; j<height(); ++j) U(i,j) = 0;
17572  ++U(i,i);
17573  }
17574 
17575  for (int k = width()-1; k>=0; --k) {
17576  for (unsigned int its = 0; its<max_iteration; ++its) {
17577  bool flag = true;
17578  for (l = k; l>=1; --l) {
17579  nm = l-1;
17580  if ((cimg::abs(rv1[l])+anorm)==anorm) { flag = false; break; }
17581  if ((cimg::abs(S[nm])+anorm)==anorm) break;
17582  }
17583  if (flag) {
17584  c = 0; s = 1;
17585  for (int i = l; i<=k; ++i) {
17586  f = s*rv1[i]; rv1[i] = c*rv1[i];
17587  if ((cimg::abs(f)+anorm)==anorm) break;
17588  g = S[i]; h = (t)cimg::_pythagore(f,g); S[i] = h; h = 1/h; c = g*h; s = -f*h;
17589  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; }
17590  }
17591  }
17592 
17593  const t z = S[k];
17594  if (l==k) { if (z<0) { S[k] = -z; cimg_forX(U,j) V(k,j) = -V(k,j); } break; }
17595  nm = k-1;
17596  t x = S[l], y = S[nm];
17597  g = rv1[nm]; h = rv1[k];
17598  f = ((y-z)*(y+z)+(g-h)*(g+h))/cimg::max((t)1e-25,2*h*y);
17599  g = (t)cimg::_pythagore(f,1.0);
17600  f = ((x-z)*(x+z)+h*((y/(f + (f>=0?g:-g)))-h))/cimg::max((t)1e-25,x);
17601  c = s = 1;
17602  for (int j = l; j<=nm; ++j) {
17603  const int i = j+1;
17604  g = rv1[i]; h = s*g; g = c*g;
17605  t y = S[i];
17606  t z = (t)cimg::_pythagore(f,h);
17607  rv1[j] = z; c = f/cimg::max((t)1e-25,z); s = h/cimg::max((t)1e-25,z);
17608  f = x*c+g*s; g = g*c-x*s; h = y*s; y*=c;
17609  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; }
17610  z = (t)cimg::_pythagore(f,h); S[j] = z;
17611  if (z) { z = 1/cimg::max((t)1e-25,z); c = f*z; s = h*z; }
17612  f = c*g+s*y; x = c*y-s*g;
17613  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; }
17614  }
17615  rv1[l] = 0; rv1[k]=f; S[k]=x;
17616  }
17617  }
17618 
17619  if (sorting) {
17620  CImg<intT> permutations;
17621  CImg<t> tmp(_width);
17622  S.sort(permutations,false);
17623  cimg_forY(U,k) {
17624  cimg_forY(permutations,y) tmp(y) = U(permutations(y),k);
17625  std::memcpy(U.data(0,k),tmp._data,sizeof(t)*_width);
17626  }
17627  cimg_forY(V,k) {
17628  cimg_forY(permutations,y) tmp(y) = V(permutations(y),k);
17629  std::memcpy(V.data(0,k),tmp._data,sizeof(t)*_width);
17630  }
17631  }
17632  }
17633  return *this;
17634  }
17635 
17637 
17641  CImgList<Tfloat> get_SVD(const bool sorting=true,
17642  const unsigned int max_iteration=40, const float lambda=0) const {
17643  CImgList<Tfloat> res(3);
17644  SVD(res[0],res[1],res[2],sorting,max_iteration,lambda);
17645  return res;
17646  }
17647 
17648  // [internal] Compute the LU decomposition of a permuted matrix.
17649  template<typename t>
17650  CImg<T>& _LU(CImg<t>& indx, bool& d) {
17651  const int N = width();
17652  int imax = 0;
17653  CImg<Tfloat> vv(N);
17654  indx.assign(N);
17655  d = true;
17656  cimg_forX(*this,i) {
17657  Tfloat vmax = 0;
17658  cimg_forX(*this,j) {
17659  const Tfloat tmp = cimg::abs((*this)(j,i));
17660  if (tmp>vmax) vmax = tmp;
17661  }
17662  if (vmax==0) { indx.fill(0); return fill(0); }
17663  vv[i] = 1/vmax;
17664  }
17665  cimg_forX(*this,j) {
17666  for (int i = 0; i<j; ++i) {
17667  Tfloat sum=(*this)(j,i);
17668  for (int k = 0; k<i; ++k) sum-=(*this)(k,i)*(*this)(j,k);
17669  (*this)(j,i) = (T)sum;
17670  }
17671  Tfloat vmax = 0;
17672  for (int i = j; i<width(); ++i) {
17673  Tfloat sum=(*this)(j,i);
17674  for (int k = 0; k<j; ++k) sum-=(*this)(k,i)*(*this)(j,k);
17675  (*this)(j,i) = (T)sum;
17676  const Tfloat tmp = vv[i]*cimg::abs(sum);
17677  if (tmp>=vmax) { vmax=tmp; imax=i; }
17678  }
17679  if (j!=imax) {
17680  cimg_forX(*this,k) cimg::swap((*this)(k,imax),(*this)(k,j));
17681  d =!d;
17682  vv[imax] = vv[j];
17683  }
17684  indx[j] = (t)imax;
17685  if ((*this)(j,j)==0) (*this)(j,j) = (T)1e-20;
17686  if (j<N) {
17687  const Tfloat tmp = 1/(Tfloat)(*this)(j,j);
17688  for (int i=j+1; i<N; ++i) (*this)(j,i) = (T)((*this)(j,i)*tmp);
17689  }
17690  }
17691  return *this;
17692  }
17693 
17695 
17705  template<typename tf, typename t>
17706  static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
17707  const unsigned int starting_node, const unsigned int ending_node,
17708  CImg<t>& previous_node) {
17709  if (starting_node>=nb_nodes)
17710  throw CImgArgumentException("CImg<%s>::dijkstra(): Specified indice of starting node %u is higher "
17711  "than number of nodes %u.",
17712  pixel_type(),starting_node,nb_nodes);
17713  CImg<T> dist(1,nb_nodes,1,1,cimg::type<T>::max());
17714  dist(starting_node) = 0;
17715  previous_node.assign(1,nb_nodes,1,1,(t)-1);
17716  previous_node(starting_node) = (t)starting_node;
17717  CImg<uintT> Q(nb_nodes);
17718  cimg_forX(Q,u) Q(u) = u;
17719  cimg::swap(Q(starting_node),Q(0));
17720  unsigned int sizeQ = nb_nodes;
17721  while (sizeQ) {
17722  // Update neighbors from minimal vertex
17723  const unsigned int umin = Q(0);
17724  if (umin==ending_node) sizeQ = 0;
17725  else {
17726  const T dmin = dist(umin);
17727  const T infty = cimg::type<T>::max();
17728  for (unsigned int q = 1; q<sizeQ; ++q) {
17729  const unsigned int v = Q(q);
17730  const T d = (T)distance(v,umin);
17731  if (d<infty) {
17732  const T alt = dmin + d;
17733  if (alt<dist(v)) {
17734  dist(v) = alt;
17735  previous_node(v) = (t)umin;
17736  const T distpos = dist(Q(q));
17737  for (unsigned int pos = q, par = 0; pos && distpos<dist(Q(par=(pos+1)/2-1)); pos=par)
17738  cimg::swap(Q(pos),Q(par));
17739  }
17740  }
17741  }
17742  // Remove minimal vertex from queue
17743  Q(0) = Q(--sizeQ);
17744  const T distpos = dist(Q(0));
17745  for (unsigned int pos = 0, left = 0, right = 0;
17746  ((right=2*(pos+1),(left=right-1))<sizeQ && distpos>dist(Q(left))) ||
17747  (right<sizeQ && distpos>dist(Q(right)));) {
17748  if (right<sizeQ) {
17749  if (dist(Q(left))<dist(Q(right))) { cimg::swap(Q(pos),Q(left)); pos = left; }
17750  else { cimg::swap(Q(pos),Q(right)); pos = right; }
17751  } else { cimg::swap(Q(pos),Q(left)); pos = left; }
17752  }
17753  }
17754  }
17755  return dist;
17756  }
17757 
17759  template<typename tf, typename t>
17760  static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
17761  const unsigned int starting_node, const unsigned int ending_node=~0U) {
17762  CImg<uintT> foo;
17763  return dijkstra(distance,nb_nodes,starting_node,ending_node,foo);
17764  }
17765 
17767 
17775  template<typename t>
17776  CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node,
17777  CImg<t>& previous_node) {
17778  return get_dijkstra(starting_node,ending_node,previous_node).move_to(*this);
17779  }
17780 
17782  template<typename t>
17783  CImg<T> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node,
17784  CImg<t>& previous_node) const {
17785  if (_width!=_height || _depth!=1 || _spectrum!=1)
17786  throw CImgInstanceException(_cimg_instance
17787  "dijkstra(): Instance is not a graph adjacency matrix.",
17788  cimg_instance);
17789 
17790  return dijkstra(*this,_width,starting_node,ending_node,previous_node);
17791  }
17792 
17794  CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) {
17795  return get_dijkstra(starting_node,ending_node).move_to(*this);
17796  }
17797 
17799  CImg<Tfloat> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) const {
17800  CImg<uintT> foo;
17801  return get_dijkstra(starting_node,ending_node,foo);
17802  }
17803 
17805 
17809  static CImg<T> string(const char *const str, const bool is_last_zero=true, const bool is_shared=false) {
17810  if (!str) return CImg<T>();
17811  return CImg<T>(str,(unsigned int)std::strlen(str)+(is_last_zero?1:0),1,1,1,is_shared);
17812  }
17813 
17815 
17818  static CImg<T> vector(const T& a0) {
17819  CImg<T> r(1,1);
17820  r[0] = a0;
17821  return r;
17822  }
17823 
17825 
17829  static CImg<T> vector(const T& a0, const T& a1) {
17830  CImg<T> r(1,2); T *ptr = r._data;
17831  *(ptr++) = a0; *(ptr++) = a1;
17832  return r;
17833  }
17834 
17836 
17841  static CImg<T> vector(const T& a0, const T& a1, const T& a2) {
17842  CImg<T> r(1,3); T *ptr = r._data;
17843  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2;
17844  return r;
17845  }
17846 
17848 
17854  static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3) {
17855  CImg<T> r(1,4); T *ptr = r._data;
17856  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
17857  return r;
17858  }
17859 
17861  static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
17862  CImg<T> r(1,5); T *ptr = r._data;
17863  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4;
17864  return r;
17865  }
17866 
17868  static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
17869  CImg<T> r(1,6); T *ptr = r._data;
17870  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5;
17871  return r;
17872  }
17873 
17875  static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
17876  const T& a4, const T& a5, const T& a6) {
17877  CImg<T> r(1,7); T *ptr = r._data;
17878  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
17879  *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6;
17880  return r;
17881  }
17882 
17884  static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
17885  const T& a4, const T& a5, const T& a6, const T& a7) {
17886  CImg<T> r(1,8); T *ptr = r._data;
17887  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
17888  *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
17889  return r;
17890  }
17891 
17893  static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
17894  const T& a4, const T& a5, const T& a6, const T& a7,
17895  const T& a8) {
17896  CImg<T> r(1,9); T *ptr = r._data;
17897  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
17898  *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
17899  *(ptr++) = a8;
17900  return r;
17901  }
17902 
17904  static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
17905  const T& a4, const T& a5, const T& a6, const T& a7,
17906  const T& a8, const T& a9) {
17907  CImg<T> r(1,10); T *ptr = r._data;
17908  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
17909  *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
17910  *(ptr++) = a8; *(ptr++) = a9;
17911  return r;
17912  }
17913 
17915  static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
17916  const T& a4, const T& a5, const T& a6, const T& a7,
17917  const T& a8, const T& a9, const T& a10) {
17918  CImg<T> r(1,11); T *ptr = r._data;
17919  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
17920  *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
17921  *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10;
17922  return r;
17923  }
17924 
17926  static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
17927  const T& a4, const T& a5, const T& a6, const T& a7,
17928  const T& a8, const T& a9, const T& a10, const T& a11) {
17929  CImg<T> r(1,12); T *ptr = r._data;
17930  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
17931  *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
17932  *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
17933  return r;
17934  }
17935 
17937  static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
17938  const T& a4, const T& a5, const T& a6, const T& a7,
17939  const T& a8, const T& a9, const T& a10, const T& a11,
17940  const T& a12) {
17941  CImg<T> r(1,13); T *ptr = r._data;
17942  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
17943  *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
17944  *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
17945  *(ptr++) = a12;
17946  return r;
17947  }
17948 
17950  static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
17951  const T& a4, const T& a5, const T& a6, const T& a7,
17952  const T& a8, const T& a9, const T& a10, const T& a11,
17953  const T& a12, const T& a13) {
17954  CImg<T> r(1,14); T *ptr = r._data;
17955  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
17956  *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
17957  *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
17958  *(ptr++) = a12; *(ptr++) = a13;
17959  return r;
17960  }
17961 
17963  static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
17964  const T& a4, const T& a5, const T& a6, const T& a7,
17965  const T& a8, const T& a9, const T& a10, const T& a11,
17966  const T& a12, const T& a13, const T& a14) {
17967  CImg<T> r(1,15); T *ptr = r._data;
17968  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
17969  *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
17970  *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
17971  *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14;
17972  return r;
17973  }
17974 
17976  static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
17977  const T& a4, const T& a5, const T& a6, const T& a7,
17978  const T& a8, const T& a9, const T& a10, const T& a11,
17979  const T& a12, const T& a13, const T& a14, const T& a15) {
17980  CImg<T> r(1,16); T *ptr = r._data;
17981  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
17982  *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
17983  *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
17984  *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15;
17985  return r;
17986  }
17987 
17989 
17993  static CImg<T> matrix(const T& a0) {
17994  return vector(a0);
17995  }
17996 
17998 
18004  static CImg<T> matrix(const T& a0, const T& a1,
18005  const T& a2, const T& a3) {
18006  CImg<T> r(2,2); T *ptr = r._data;
18007  *(ptr++) = a0; *(ptr++) = a1;
18008  *(ptr++) = a2; *(ptr++) = a3;
18009  return r;
18010  }
18011 
18013 
18024  static CImg<T> matrix(const T& a0, const T& a1, const T& a2,
18025  const T& a3, const T& a4, const T& a5,
18026  const T& a6, const T& a7, const T& a8) {
18027  CImg<T> r(3,3); T *ptr = r._data;
18028  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2;
18029  *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5;
18030  *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8;
18031  return r;
18032  }
18033 
18035  static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3,
18036  const T& a4, const T& a5, const T& a6, const T& a7,
18037  const T& a8, const T& a9, const T& a10, const T& a11,
18038  const T& a12, const T& a13, const T& a14, const T& a15) {
18039  CImg<T> r(4,4); T *ptr = r._data;
18040  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
18041  *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
18042  *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
18043  *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15;
18044  return r;
18045  }
18046 
18048  static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4,
18049  const T& a5, const T& a6, const T& a7, const T& a8, const T& a9,
18050  const T& a10, const T& a11, const T& a12, const T& a13, const T& a14,
18051  const T& a15, const T& a16, const T& a17, const T& a18, const T& a19,
18052  const T& a20, const T& a21, const T& a22, const T& a23, const T& a24) {
18053  CImg<T> r(5,5); T *ptr = r._data;
18054  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4;
18055  *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9;
18056  *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14;
18057  *(ptr++) = a15; *(ptr++) = a16; *(ptr++) = a17; *(ptr++) = a18; *(ptr++) = a19;
18058  *(ptr++) = a20; *(ptr++) = a21; *(ptr++) = a22; *(ptr++) = a23; *(ptr++) = a24;
18059  return r;
18060  }
18061 
18063 
18067  static CImg<T> tensor(const T& a0) {
18068  return matrix(a0);
18069  }
18070 
18072  static CImg<T> tensor(const T& a0, const T& a1, const T& a2) {
18073  return matrix(a0,a1,a1,a2);
18074  }
18075 
18077  static CImg<T> tensor(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
18078  return matrix(a0,a1,a2,a1,a3,a4,a2,a4,a5);
18079  }
18080 
18082  static CImg<T> diagonal(const T& a0) {
18083  return matrix(a0);
18084  }
18085 
18087  static CImg<T> diagonal(const T& a0, const T& a1) {
18088  return matrix(a0,0,0,a1);
18089  }
18090 
18092  static CImg<T> diagonal(const T& a0, const T& a1, const T& a2) {
18093  return matrix(a0,0,0,0,a1,0,0,0,a2);
18094  }
18095 
18097  static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3) {
18098  return matrix(a0,0,0,0,0,a1,0,0,0,0,a2,0,0,0,0,a3);
18099  }
18100 
18102  static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
18103  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);
18104  }
18105 
18107 
18110  static CImg<T> identity_matrix(const unsigned int N) {
18111  CImg<T> res(N,N,1,1,0);
18112  cimg_forX(res,x) res(x,x) = 1;
18113  return res;
18114  }
18115 
18117 
18122  static CImg<T> sequence(const unsigned int N, const T a0, const T a1) {
18123  if (N) return CImg<T>(1,N).sequence(a0,a1);
18124  return CImg<T>();
18125  }
18126 
18128 
18135  static CImg<T> rotation_matrix(const float x, const float y, const float z, const float w,
18136  const bool is_quaternion=false) {
18137  float X,Y,Z,W;
18138  if (!is_quaternion) {
18139  const float norm = (float)std::sqrt(x*x + y*y + z*z),
18140  nx = norm>0?x/norm:0,
18141  ny = norm>0?y/norm:0,
18142  nz = norm>0?z/norm:1,
18143  nw = norm>0?w:0,
18144  sina = (float)std::sin(nw/2),
18145  cosa = (float)std::cos(nw/2);
18146  X = nx*sina;
18147  Y = ny*sina;
18148  Z = nz*sina;
18149  W = cosa;
18150  } else {
18151  const float norm = (float)std::sqrt(x*x + y*y + z*z + w*w);
18152  if (norm>0) { X = x/norm; Y = y/norm; Z = z/norm; W = w/norm; }
18153  else { X = Y = Z = 0; W = 1; }
18154  }
18155  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;
18156  return CImg<T>::matrix((T)(1-2*(yy+zz)), (T)(2*(xy+zw)), (T)(2*(xz-yw)),
18157  (T)(2*(xy-zw)), (T)(1-2*(xx+zz)), (T)(2*(yz+xw)),
18158  (T)(2*(xz+yw)), (T)(2*(yz-xw)), (T)(1-2*(xx+yy)));
18159  }
18160 
18162  //-----------------------------------
18163  //
18165 
18166  //-----------------------------------
18167 
18169 
18172  CImg<T>& fill(const T val) {
18173  if (is_empty()) return *this;
18174  if (val && sizeof(T)!=1) cimg_for(*this,ptrd,T) *ptrd = val;
18175  else std::memset(_data,(int)val,sizeof(T)*size());
18176  return *this;
18177  }
18178 
18180  CImg<T> get_fill(const T val) const {
18181  return CImg<T>(_width,_height,_depth,_spectrum).fill(val);
18182  }
18183 
18185 
18189  CImg<T>& fill(const T val0, const T val1) {
18190  if (is_empty()) return *this;
18191  T *ptrd, *ptre = end()-1;
18192  for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; }
18193  if (ptrd!=ptre+1) *(ptrd++) = val0;
18194  return *this;
18195  }
18196 
18198  CImg<T> get_fill(const T val0, const T val1) const {
18199  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1);
18200  }
18201 
18203  CImg<T>& fill(const T val0, const T val1, const T val2) {
18204  if (is_empty()) return *this;
18205  T *ptrd, *ptre = end()-2;
18206  for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; }
18207  ptre+=2;
18208  switch (ptre - ptrd) {
18209  case 2 : *(--ptre) = val1;
18210  case 1 : *(--ptre) = val0;
18211  }
18212  return *this;
18213  }
18214 
18216  CImg<T> get_fill(const T val0, const T val1, const T val2) const {
18217  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2);
18218  }
18219 
18221  CImg<T>& fill(const T val0, const T val1, const T val2, const T val3) {
18222  if (is_empty()) return *this;
18223  T *ptrd, *ptre = end()-3;
18224  for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; }
18225  ptre+=3;
18226  switch (ptre - ptrd) {
18227  case 3 : *(--ptre) = val2;
18228  case 2 : *(--ptre) = val1;
18229  case 1 : *(--ptre) = val0;
18230  }
18231  return *this;
18232  }
18233 
18235  CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3) const {
18236  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3);
18237  }
18238 
18240  CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4) {
18241  if (is_empty()) return *this;
18242  T *ptrd, *ptre = end()-4;
18243  for (ptrd = _data; ptrd<ptre; ) {
18244  *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
18245  }
18246  ptre+=4;
18247  switch (ptre - ptrd) {
18248  case 4 : *(--ptre) = val3;
18249  case 3 : *(--ptre) = val2;
18250  case 2 : *(--ptre) = val1;
18251  case 1 : *(--ptre) = val0;
18252  }
18253  return *this;
18254  }
18255 
18257  CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4) const {
18258  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4);
18259  }
18260 
18262  CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5) {
18263  if (is_empty()) return *this;
18264  T *ptrd, *ptre = end()-5;
18265  for (ptrd = _data; ptrd<ptre; ) {
18266  *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
18267  }
18268  ptre+=5;
18269  switch (ptre - ptrd) {
18270  case 5 : *(--ptre) = val4;
18271  case 4 : *(--ptre) = val3;
18272  case 3 : *(--ptre) = val2;
18273  case 2 : *(--ptre) = val1;
18274  case 1 : *(--ptre) = val0;
18275  }
18276  return *this;
18277  }
18278 
18280  CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5) const {
18281  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5);
18282  }
18283 
18285  CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6) {
18286  if (is_empty()) return *this;
18287  T *ptrd, *ptre = end()-6;
18288  for (ptrd = _data; ptrd<ptre; ) {
18289  *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
18290  *(ptrd++) = val6;
18291  }
18292  ptre+=6;
18293  switch (ptre - ptrd) {
18294  case 6 : *(--ptre) = val5;
18295  case 5 : *(--ptre) = val4;
18296  case 4 : *(--ptre) = val3;
18297  case 3 : *(--ptre) = val2;
18298  case 2 : *(--ptre) = val1;
18299  case 1 : *(--ptre) = val0;
18300  }
18301  return *this;
18302  }
18303 
18305  CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5,
18306  const T val6) const {
18307  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6);
18308  }
18309 
18311  CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5,
18312  const T val6, const T val7) {
18313  if (is_empty()) return *this;
18314  T *ptrd, *ptre = end()-7;
18315  for (ptrd = _data; ptrd<ptre; ) {
18316  *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3;
18317  *(ptrd++) = val4; *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7;
18318  }
18319  ptre+=7;
18320  switch (ptre - ptrd) {
18321  case 7 : *(--ptre) = val6;
18322  case 6 : *(--ptre) = val5;
18323  case 5 : *(--ptre) = val4;
18324  case 4 : *(--ptre) = val3;
18325  case 3 : *(--ptre) = val2;
18326  case 2 : *(--ptre) = val1;
18327  case 1 : *(--ptre) = val0;
18328  }
18329  return *this;
18330  }
18331 
18333  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,
18334  const T val7) const {
18335  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7);
18336  }
18337 
18339  CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
18340  const T val7, const T val8) {
18341  if (is_empty()) return *this;
18342  T *ptrd, *ptre = end()-8;
18343  for (ptrd = _data; ptrd<ptre; ) {
18344  *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2;
18345  *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
18346  *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8;
18347  }
18348  ptre+=8;
18349  switch (ptre - ptrd) {
18350  case 8 : *(--ptre) = val7;
18351  case 7 : *(--ptre) = val6;
18352  case 6 : *(--ptre) = val5;
18353  case 5 : *(--ptre) = val4;
18354  case 4 : *(--ptre) = val3;
18355  case 3 : *(--ptre) = val2;
18356  case 2 : *(--ptre) = val1;
18357  case 1 : *(--ptre) = val0;
18358  }
18359  return *this;
18360  }
18361 
18363  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,
18364  const T val7, const T val8) const {
18365  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8);
18366  }
18367 
18369  CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
18370  const T val7, const T val8, const T val9) {
18371  if (is_empty()) return *this;
18372  T *ptrd, *ptre = end()-9;
18373  for (ptrd = _data; ptrd<ptre; ) {
18374  *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
18375  *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9;
18376  }
18377  ptre+=9;
18378  switch (ptre - ptrd) {
18379  case 9 : *(--ptre) = val8;
18380  case 8 : *(--ptre) = val7;
18381  case 7 : *(--ptre) = val6;
18382  case 6 : *(--ptre) = val5;
18383  case 5 : *(--ptre) = val4;
18384  case 4 : *(--ptre) = val3;
18385  case 3 : *(--ptre) = val2;
18386  case 2 : *(--ptre) = val1;
18387  case 1 : *(--ptre) = val0;
18388  }
18389  return *this;
18390  }
18391 
18393  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,
18394  const T val7, const T val8, const T val9) const {
18395  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9);
18396  }
18397 
18399  CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
18400  const T val7, const T val8, const T val9, const T val10) {
18401  if (is_empty()) return *this;
18402  T *ptrd, *ptre = end()-10;
18403  for (ptrd = _data; ptrd<ptre; ) {
18404  *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
18405  *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9;
18406  *(ptrd++) = val10;
18407  }
18408  ptre+=10;
18409  switch (ptre - ptrd) {
18410  case 10 : *(--ptre) = val9;
18411  case 9 : *(--ptre) = val8;
18412  case 8 : *(--ptre) = val7;
18413  case 7 : *(--ptre) = val6;
18414  case 6 : *(--ptre) = val5;
18415  case 5 : *(--ptre) = val4;
18416  case 4 : *(--ptre) = val3;
18417  case 3 : *(--ptre) = val2;
18418  case 2 : *(--ptre) = val1;
18419  case 1 : *(--ptre) = val0;
18420  }
18421  return *this;
18422  }
18423 
18425  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,
18426  const T val7, const T val8, const T val9, const T val10) const {
18427  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10);
18428  }
18429 
18431  CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
18432  const T val7, const T val8, const T val9, const T val10, const T val11) {
18433  if (is_empty()) return *this;
18434  T *ptrd, *ptre = end()-11;
18435  for (ptrd = _data; ptrd<ptre; ) {
18436  *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
18437  *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
18438  }
18439  ptre+=11;
18440  switch (ptre - ptrd) {
18441  case 11 : *(--ptre) = val10;
18442  case 10 : *(--ptre) = val9;
18443  case 9 : *(--ptre) = val8;
18444  case 8 : *(--ptre) = val7;
18445  case 7 : *(--ptre) = val6;
18446  case 6 : *(--ptre) = val5;
18447  case 5 : *(--ptre) = val4;
18448  case 4 : *(--ptre) = val3;
18449  case 3 : *(--ptre) = val2;
18450  case 2 : *(--ptre) = val1;
18451  case 1 : *(--ptre) = val0;
18452  }
18453  return *this;
18454  }
18455 
18457  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,
18458  const T val7, const T val8, const T val9, const T val10, const T val11) const {
18459  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
18460  val11);
18461  }
18462 
18464  CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
18465  const T val7, const T val8, const T val9, const T val10, const T val11, const T val12) {
18466  if (is_empty()) return *this;
18467  T *ptrd, *ptre = end()-12;
18468  for (ptrd = _data; ptrd<ptre; ) {
18469  *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
18470  *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
18471  *(ptrd++) = val12;
18472  }
18473  ptre+=12;
18474  switch (ptre - ptrd) {
18475  case 12 : *(--ptre) = val11;
18476  case 11 : *(--ptre) = val10;
18477  case 10 : *(--ptre) = val9;
18478  case 9 : *(--ptre) = val8;
18479  case 8 : *(--ptre) = val7;
18480  case 7 : *(--ptre) = val6;
18481  case 6 : *(--ptre) = val5;
18482  case 5 : *(--ptre) = val4;
18483  case 4 : *(--ptre) = val3;
18484  case 3 : *(--ptre) = val2;
18485  case 2 : *(--ptre) = val1;
18486  case 1 : *(--ptre) = val0;
18487  }
18488  return *this;
18489  }
18490 
18492  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,
18493  const T val7, const T val8, const T val9, const T val10, const T val11, const T val12) const {
18494  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
18495  val11,val12);
18496  }
18497 
18499  CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
18500  const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
18501  const T val13) {
18502  if (is_empty()) return *this;
18503  T *ptrd, *ptre = end()-13;
18504  for (ptrd = _data; ptrd<ptre; ) {
18505  *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
18506  *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
18507  *(ptrd++) = val12; *(ptrd++) = val13;
18508  }
18509  ptre+=13;
18510  switch (ptre - ptrd) {
18511  case 13 : *(--ptre) = val12;
18512  case 12 : *(--ptre) = val11;
18513  case 11 : *(--ptre) = val10;
18514  case 10 : *(--ptre) = val9;
18515  case 9 : *(--ptre) = val8;
18516  case 8 : *(--ptre) = val7;
18517  case 7 : *(--ptre) = val6;
18518  case 6 : *(--ptre) = val5;
18519  case 5 : *(--ptre) = val4;
18520  case 4 : *(--ptre) = val3;
18521  case 3 : *(--ptre) = val2;
18522  case 2 : *(--ptre) = val1;
18523  case 1 : *(--ptre) = val0;
18524  }
18525  return *this;
18526  }
18527 
18529  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,
18530  const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
18531  const T val13) const {
18532  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
18533  val11,val12,val13);
18534  }
18535 
18537  CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
18538  const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
18539  const T val13, const T val14) {
18540  if (is_empty()) return *this;
18541  T *ptrd, *ptre = end()-14;
18542  for (ptrd = _data; ptrd<ptre; ) {
18543  *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
18544  *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
18545  *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14;
18546  }
18547  ptre+=14;
18548  switch (ptre - ptrd) {
18549  case 14 : *(--ptre) = val13;
18550  case 13 : *(--ptre) = val12;
18551  case 12 : *(--ptre) = val11;
18552  case 11 : *(--ptre) = val10;
18553  case 10 : *(--ptre) = val9;
18554  case 9 : *(--ptre) = val8;
18555  case 8 : *(--ptre) = val7;
18556  case 7 : *(--ptre) = val6;
18557  case 6 : *(--ptre) = val5;
18558  case 5 : *(--ptre) = val4;
18559  case 4 : *(--ptre) = val3;
18560  case 3 : *(--ptre) = val2;
18561  case 2 : *(--ptre) = val1;
18562  case 1 : *(--ptre) = val0;
18563  }
18564  return *this;
18565  }
18566 
18568  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,
18569  const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
18570  const T val13, const T val14) const {
18571  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
18572  val11,val12,val13,val14);
18573  }
18574 
18576  CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
18577  const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
18578  const T val13, const T val14, const T val15) {
18579  if (is_empty()) return *this;
18580  T *ptrd, *ptre = end()-15;
18581  for (ptrd = _data; ptrd<ptre; ) {
18582  *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
18583  *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
18584  *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14; *(ptrd++) = val15;
18585  }
18586  ptre+=15;
18587  switch (ptre - ptrd) {
18588  case 15 : *(--ptre) = val14;
18589  case 14 : *(--ptre) = val13;
18590  case 13 : *(--ptre) = val12;
18591  case 12 : *(--ptre) = val11;
18592  case 11 : *(--ptre) = val10;
18593  case 10 : *(--ptre) = val9;
18594  case 9 : *(--ptre) = val8;
18595  case 8 : *(--ptre) = val7;
18596  case 7 : *(--ptre) = val6;
18597  case 6 : *(--ptre) = val5;
18598  case 5 : *(--ptre) = val4;
18599  case 4 : *(--ptre) = val3;
18600  case 3 : *(--ptre) = val2;
18601  case 2 : *(--ptre) = val1;
18602  case 1 : *(--ptre) = val0;
18603  }
18604  return *this;
18605  }
18606 
18608  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,
18609  const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
18610  const T val13, const T val14, const T val15) const {
18611  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
18612  val11,val12,val13,val14,val15);
18613  }
18614 
18616 
18620  CImg<T>& fill(const char *const expression, const bool repeat_flag) {
18621  if (is_empty() || !expression || !*expression) return *this;
18622  const unsigned int omode = cimg::exception_mode();
18623  cimg::exception_mode() = 0;
18624  try { // Try to fill values according to a formula.
18625  const CImg<T> _base = cimg::_is_self_expr(expression)?+*this:CImg<T>(), &base = _base?_base:*this;
18626  _cimg_math_parser mp(base,expression+(*expression=='>' || *expression=='<'?1:0),"fill");
18627  T *ptrd = *expression=='<'?end()-1:_data;
18628  if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) *(ptrd--) = (T)mp(x,y,z,c);
18629  else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) *(ptrd++) = (T)mp(x,y,z,c);
18630  else {
18631 #ifdef cimg_use_openmp
18632  if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)
18633 #pragma omp parallel
18634  {
18635  _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp;
18636 #pragma omp for collapse(3)
18637  cimg_forYZC(*this,y,z,c) {
18638  T *ptrd = data(0,y,z,c);
18639  cimg_forX(*this,x) *ptrd++ = (T)lmp(x,y,z,c);
18640  }
18641  }
18642  else
18643 #endif
18644  cimg_forXYZC(*this,x,y,z,c) *(ptrd++) = (T)mp(x,y,z,c);
18645  }
18646  } catch (CImgException& e) { // If failed, try to recognize a list of values.
18647  char item[16384] = { 0 }, sep = 0;
18648  const char *nexpression = expression;
18649  unsigned long nb = 0;
18650  const unsigned long siz = size();
18651  T *ptrd = _data;
18652  for (double val = 0; *nexpression && nb<siz; ++nb) {
18653  sep = 0;
18654  const int err = std::sscanf(nexpression,"%4095[ \n\t0-9.e+-]%c",item,&sep);
18655  if (err>0 && std::sscanf(item,"%lf",&val)==1) {
18656  nexpression+=std::strlen(item) + (err>1?1:0);
18657  *(ptrd++) = (T)val;
18658  } else break;
18659  }
18660  cimg::exception_mode() = omode;
18661  if (nb<siz && (sep || *nexpression))
18662  throw CImgArgumentException(e.what(),pixel_type(),expression);
18663  if (repeat_flag && nb && nb<siz)
18664  for (T *ptrs = _data, *const ptre = _data + siz; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs;
18665  }
18666  cimg::exception_mode() = omode;
18667  return *this;
18668  }
18669 
18671  CImg<T> get_fill(const char *const values, const bool repeat_values) const {
18672  return (+*this).fill(values,repeat_values);
18673  }
18674 
18676 
18681  template<typename t>
18682  CImg<T>& fill(const CImg<t>& values, const bool repeat_values=true) {
18683  if (is_empty() || !values) return *this;
18684  T *ptrd = _data, *ptre = ptrd + size();
18685  for (t *ptrs = values._data, *ptrs_end = ptrs + values.size(); ptrs<ptrs_end && ptrd<ptre; ++ptrs)
18686  *(ptrd++) = (T)*ptrs;
18687  if (repeat_values && ptrd<ptre) for (T *ptrs = _data; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs;
18688  return *this;
18689  }
18690 
18692  template<typename t>
18693  CImg<T> get_fill(const CImg<t>& values, const bool repeat_values=true) const {
18694  return repeat_values?CImg<T>(_width,_height,_depth,_spectrum).fill(values,repeat_values):
18695  (+*this).fill(values,repeat_values);
18696  }
18697 
18699 
18705  CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const int a0, ...) {
18706 #define _cimg_fill1(x,y,z,c,off,siz,t) { \
18707  va_list ap; va_start(ap,a0); T *ptrd = data(x,y,z,c); *ptrd = (T)a0; \
18708  for (unsigned long k = 1; k<siz; ++k) { ptrd+=off; *ptrd = (T)va_arg(ap,t); } \
18709  va_end(ap); }
18710  if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,int);
18711  return *this;
18712  }
18713 
18715  CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const double a0, ...) {
18716  if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,double);
18717  return *this;
18718  }
18719 
18721 
18727  CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const int a0, ...) {
18728  if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,int);
18729  return *this;
18730  }
18731 
18733  CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const double a0, ...) {
18734  if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,double);
18735  return *this;
18736  }
18737 
18739 
18745  CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const int a0, ...) {
18746  const unsigned long wh = (unsigned long)_width*_height;
18747  if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,int);
18748  return *this;
18749  }
18750 
18752  CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const double a0, ...) {
18753  const unsigned long wh = (unsigned long)_width*_height;
18754  if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,double);
18755  return *this;
18756  }
18757 
18759 
18765  CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const int a0, ...) {
18766  const unsigned long whd = (unsigned long)_width*_height*_depth;
18767  if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,int);
18768  return *this;
18769  }
18770 
18772  CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const double a0, ...) {
18773  const unsigned long whd = (unsigned long)_width*_height*_depth;
18774  if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,double);
18775  return *this;
18776  }
18777 
18779 
18784  CImg<T>& discard(const T value) {
18785  return get_discard(value).move_to(*this);
18786  }
18787 
18789  CImg<T> get_discard(const T value) const {
18790  CImg<T> res(1,size());
18791  T *pd = res._data;
18792  for (const T *ps = _data, *const pse = end(); ps<pse; ++ps)
18793  if (*ps!=value) *(pd++) = *ps;
18794  if (pd==res._data) return CImg<T>();
18795  return res.resize(1,pd-res._data,1,1,-1);
18796  }
18797 
18799 
18804  template<typename t>
18805  CImg<T>& discard(const CImg<t>& values) {
18806  return get_discard(values).move_to(*this);
18807  }
18808 
18810  template<typename t>
18811  CImg<T> get_discard(const CImg<t>& values) const {
18812  if (!values) return *this;
18813  if (values.size()==1) return get_discard(*values);
18814  CImg<T> res(1,size());
18815  T *pd = res._data;
18816  const t *const pve = values.end();
18817  for (const T *ps = _data, *const pse = end(); ps<pse; ) {
18818  const T *_ps = ps;
18819  const t *pv = values._data;
18820  while (_ps<pse && pv<pve) { if (*(_ps++)!=(T)*pv) break; ++pv; }
18821  if (pv!=pve) {
18822  const unsigned int l = _ps - ps;
18823  if (l==1) *(pd++) = *ps; else { std::memcpy(pd,ps,sizeof(T)*l); pd+=l; }
18824  }
18825  ps = _ps;
18826  }
18827  if (pd==res._data) return CImg<T>();
18828  return res.resize(1,pd-res._data,1,1,-1);
18829  }
18830 
18832 
18835  cimg::invert_endianness(_data,size());
18836  return *this;
18837  }
18838 
18841  return (+*this).invert_endianness();
18842  }
18843 
18845 
18850  CImg<T>& rand(const T val_min, const T val_max) {
18851  const float delta = (float)val_max - (float)val_min;
18852  cimg_for(*this,ptrd,T) *ptrd = (T)(val_min + cimg::rand()*delta);
18853  return *this;
18854  }
18855 
18857  CImg<T> get_rand(const T val_min, const T val_max) const {
18858  return (+*this).rand(val_min,val_max);
18859  }
18860 
18862 
18869  CImg<T>& round(const double y=1, const int rounding_type=0) {
18870  if (y>0)
18871 #ifdef cimg_use_openmp
18872 #pragma omp parallel for if (size()>=8192)
18873 #endif
18874  cimg_rof(*this,ptrd,T) *ptrd = cimg::round(*ptrd,y,rounding_type);
18875  return *this;
18876  }
18877 
18879  CImg<T> get_round(const double y=1, const unsigned int rounding_type=0) const {
18880  return (+*this).round(y,rounding_type);
18881  }
18882 
18884 
18901  CImg<T>& noise(const double sigma, const unsigned int noise_type=0) {
18902  if (!is_empty()) {
18903  const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
18904  Tfloat nsigma = (Tfloat)sigma, m = 0, M = 0;
18905  if (nsigma==0 && noise_type!=3) return *this;
18906  if (nsigma<0 || noise_type==2) m = (Tfloat)min_max(M);
18907  if (nsigma<0) nsigma = (Tfloat)(-nsigma*(M-m)/100.0);
18908  switch (noise_type) {
18909  case 0 : { // Gaussian noise
18910  cimg_rof(*this,ptrd,T) {
18911  Tfloat val = (Tfloat)(*ptrd + nsigma*cimg::grand());
18912  if (val>vmax) val = vmax;
18913  if (val<vmin) val = vmin;
18914  *ptrd = (T)val;
18915  }
18916  } break;
18917  case 1 : { // Uniform noise
18918  cimg_rof(*this,ptrd,T) {
18919  Tfloat val = (Tfloat)(*ptrd + nsigma*cimg::crand());
18920  if (val>vmax) val = vmax;
18921  if (val<vmin) val = vmin;
18922  *ptrd = (T)val;
18923  }
18924  } break;
18925  case 2 : { // Salt & Pepper noise
18926  if (nsigma<0) nsigma = -nsigma;
18927  if (M==m) { m = 0; M = (Tfloat)(cimg::type<T>::is_float()?1:cimg::type<T>::max()); }
18928  cimg_rof(*this,ptrd,T) if (cimg::rand()*100<nsigma) *ptrd = (T)(cimg::rand()<0.5?M:m);
18929  } break;
18930 
18931  case 3 : { // Poisson Noise
18932  cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::prand(*ptrd);
18933  } break;
18934 
18935  case 4 : { // Rice noise
18936  const Tfloat sqrt2 = (Tfloat)std::sqrt(2.0);
18937  cimg_rof(*this,ptrd,T) {
18938  const Tfloat
18939  val0 = (Tfloat)*ptrd/sqrt2,
18940  re = (Tfloat)(val0 + nsigma*cimg::grand()),
18941  im = (Tfloat)(val0 + nsigma*cimg::grand());
18942  Tfloat val = (Tfloat)std::sqrt(re*re + im*im);
18943  if (val>vmax) val = vmax;
18944  if (val<vmin) val = vmin;
18945  *ptrd = (T)val;
18946  }
18947  } break;
18948  default :
18949  throw CImgArgumentException(_cimg_instance
18950  "noise(): Invalid specified noise type %d "
18951  "(should be { 0=gaussian | 1=uniform | 2=salt&Pepper | 3=poisson }).",
18952  cimg_instance,
18953  noise_type);
18954  }
18955  }
18956  return *this;
18957  }
18958 
18960  CImg<T> get_noise(const double sigma, const unsigned int noise_type=0) const {
18961  return (+*this).noise(sigma,noise_type);
18962  }
18963 
18965 
18975  CImg<T>& normalize(const T min_value, const T max_value) {
18976  if (is_empty()) return *this;
18977  const T a = min_value<max_value?min_value:max_value, b = min_value<max_value?max_value:min_value;
18978  T m, M = max_min(m);
18979  const Tfloat fm = (Tfloat)m, fM = (Tfloat)M;
18980  if (m==M) return fill(min_value);
18981  if (m!=a || M!=b)
18982 #ifdef cimg_use_openmp
18983 #pragma omp parallel for if (size()>=65536)
18984 #endif
18985  cimg_rof(*this,ptrd,T) *ptrd = (T)((*ptrd-fm)/(fM-fm)*(b-a)+a);
18986  return *this;
18987  }
18988 
18990  CImg<Tfloat> get_normalize(const T min_value, const T max_value) const {
18991  return CImg<Tfloat>(*this,false).normalize((Tfloat)min_value,(Tfloat)max_value);
18992  }
18993 
18995 
19004  const unsigned long whd = (unsigned long)_width*_height*_depth;
19005 #ifdef cimg_use_openmp
19006 #pragma omp parallel for collapse(2) if (_width>=512 && _height*_depth>=16)
19007 #endif
19008  cimg_forYZ(*this,y,z) {
19009  T *ptrd = data(0,y,z,0);
19010  cimg_forX(*this,x) {
19011  const T *ptrs = ptrd;
19012  float n = 0;
19013  cimg_forC(*this,c) { n+=cimg::sqr((float)*ptrs); ptrs+=whd; }
19014  n = (float)std::sqrt(n);
19015  T *_ptrd = ptrd++;
19016  if (n>0) cimg_forC(*this,c) { *_ptrd = (T)(*_ptrd/n); _ptrd+=whd; }
19017  else cimg_forC(*this,c) { *_ptrd = (T)0; _ptrd+=whd; }
19018  }
19019  }
19020  return *this;
19021  }
19022 
19025  return CImg<Tfloat>(*this,false).normalize();
19026  }
19027 
19029 
19038  CImg<T>& norm(const int norm_type=2) {
19039  if (_spectrum==1) return abs();
19040  return get_norm(norm_type).move_to(*this);
19041  }
19042 
19044  CImg<Tfloat> get_norm(const int norm_type=2) const {
19045  if (is_empty()) return *this;
19046  if (_spectrum==1) return get_abs();
19047  const unsigned long whd = (unsigned long)_width*_height*_depth;
19048  CImg<Tfloat> res(_width,_height,_depth);
19049  switch (norm_type) {
19050  case -1 : { // Linf norm
19051 #ifdef cimg_use_openmp
19052 #pragma omp parallel for collapse(2) if (_width>=512 && _height*_depth>=16)
19053 #endif
19054  cimg_forYZ(*this,y,z) {
19055  const unsigned long off = offset(0,y,z);
19056  const T *ptrs = _data + off;
19057  Tfloat *ptrd = res._data + off;
19058  cimg_forX(*this,x) {
19059  Tfloat n = 0;
19060  const T *_ptrs = ptrs++;
19061  cimg_forC(*this,c) { const Tfloat val = (Tfloat)cimg::abs(*_ptrs); if (val>n) n = val; _ptrs+=whd; }
19062  *(ptrd++) = n;
19063  }
19064  }
19065  } break;
19066  case 1 : { // L1 norm
19067 #ifdef cimg_use_openmp
19068 #pragma omp parallel for collapse(2) if (_width>=512 && _height*_depth>=16)
19069 #endif
19070  cimg_forYZ(*this,y,z) {
19071  const unsigned long off = offset(0,y,z);
19072  const T *ptrs = _data + off;
19073  Tfloat *ptrd = res._data + off;
19074  cimg_forX(*this,x) {
19075  Tfloat n = 0;
19076  const T *_ptrs = ptrs++;
19077  cimg_forC(*this,c) { n+=cimg::abs(*_ptrs); _ptrs+=whd; }
19078  *(ptrd++) = n;
19079  }
19080  }
19081  } break;
19082  default : { // L2 norm
19083 #ifdef cimg_use_openmp
19084 #pragma omp parallel for collapse(2) if (_width>=512 && _height*_depth>=16)
19085 #endif
19086  cimg_forYZ(*this,y,z) {
19087  const unsigned long off = offset(0,y,z);
19088  const T *ptrs = _data + off;
19089  Tfloat *ptrd = res._data + off;
19090  cimg_forX(*this,x) {
19091  Tfloat n = 0;
19092  const T *_ptrs = ptrs++;
19093  cimg_forC(*this,c) { n+=cimg::sqr((Tfloat)*_ptrs); _ptrs+=whd; }
19094  *(ptrd++) = (Tfloat)std::sqrt((Tfloat)n);
19095  }
19096  }
19097  }
19098  }
19099  return res;
19100  }
19101 
19103 
19113  CImg<T>& cut(const T min_value, const T max_value) {
19114  if (is_empty()) return *this;
19115  const T a = min_value<max_value?min_value:max_value, b = min_value<max_value?max_value:min_value;
19116 #ifdef cimg_use_openmp
19117 #pragma omp parallel for if (size()>=32768)
19118 #endif
19119  cimg_rof(*this,ptrd,T) *ptrd = (*ptrd<a)?a:((*ptrd>b)?b:*ptrd);
19120  return *this;
19121  }
19122 
19124  CImg<T> get_cut(const T min_value, const T max_value) const {
19125  return (+*this).cut(min_value,max_value);
19126  }
19127 
19129 
19139  CImg<T>& quantize(const unsigned int nb_levels, const bool keep_range=true) {
19140  if (!nb_levels)
19141  throw CImgArgumentException(_cimg_instance
19142  "quantize(): Invalid quantization request with 0 values.",
19143  cimg_instance);
19144 
19145  if (is_empty()) return *this;
19146  Tfloat m, M = (Tfloat)max_min(m), range = M - m;
19147  if (range>0) {
19148  if (keep_range)
19149 #ifdef cimg_use_openmp
19150 #pragma omp parallel for if (size()>=32768)
19151 #endif
19152  cimg_rof(*this,ptrd,T) {
19153  const unsigned int val = (unsigned int)((*ptrd-m)*nb_levels/range);
19154  *ptrd = (T)(m + cimg::min(val,nb_levels-1)*range/nb_levels);
19155  } else
19156 #ifdef cimg_use_openmp
19157 #pragma omp parallel for if (size()>=32768)
19158 #endif
19159  cimg_rof(*this,ptrd,T) {
19160  const unsigned int val = (unsigned int)((*ptrd-m)*nb_levels/range);
19161  *ptrd = (T)cimg::min(val,nb_levels-1);
19162  }
19163  }
19164  return *this;
19165  }
19166 
19168  CImg<T> get_quantize(const unsigned int n, const bool keep_range=true) const {
19169  return (+*this).quantize(n,keep_range);
19170  }
19171 
19173 
19184  CImg<T>& threshold(const T value, const bool soft_threshold=false, const bool strict_threshold=false) {
19185  if (is_empty()) return *this;
19186  if (strict_threshold) {
19187  if (soft_threshold)
19188 #ifdef cimg_use_openmp
19189 #pragma omp parallel for if (size()>=32768)
19190 #endif
19191  cimg_rof(*this,ptrd,T) { const T v = *ptrd; *ptrd = v>value?(T)(v-value):v<-(float)value?(T)(v+value):(T)0; }
19192  else
19193 #ifdef cimg_use_openmp
19194 #pragma omp parallel for if (size()>=65536)
19195 #endif
19196  cimg_rof(*this,ptrd,T) *ptrd = *ptrd>value?(T)1:(T)0;
19197  } else {
19198  if (soft_threshold)
19199 #ifdef cimg_use_openmp
19200 #pragma omp parallel for if (size()>=32768)
19201 #endif
19202  cimg_rof(*this,ptrd,T) {
19203  const T v = *ptrd; *ptrd = v>=value?(T)(v-value):v<=-(float)value?(T)(v+value):(T)0;
19204  }
19205  else
19206 #ifdef cimg_use_openmp
19207 #pragma omp parallel for if (size()>=65536)
19208 #endif
19209  cimg_rof(*this,ptrd,T) *ptrd = *ptrd>=value?(T)1:(T)0;
19210  }
19211  return *this;
19212  }
19213 
19215  CImg<T> get_threshold(const T value, const bool soft_threshold=false, const bool strict_threshold=false) const {
19216  return (+*this).threshold(value,soft_threshold,strict_threshold);
19217  }
19218 
19220 
19237  CImg<T>& histogram(const unsigned int nb_levels, const T min_value, const T max_value) {
19238  return get_histogram(nb_levels,min_value,max_value).move_to(*this);
19239  }
19240 
19242  CImg<T>& histogram(const unsigned int nb_levels) {
19243  return get_histogram(nb_levels).move_to(*this);
19244  }
19245 
19247  CImg<ulongT> get_histogram(const unsigned int nb_levels, const T min_value, const T max_value) const {
19248  if (!nb_levels || is_empty()) return CImg<ulongT>();
19249  T vmin = min_value<max_value?min_value:max_value, vmax = min_value<max_value?max_value:min_value;
19250  CImg<ulongT> res(nb_levels,1,1,1,0);
19251  cimg_rof(*this,ptrs,T) {
19252  const T val = *ptrs;
19253  if (val>=vmin && val<=vmax) ++res[val==vmax?nb_levels-1:(unsigned int)((val-vmin)*nb_levels/(vmax-vmin))];
19254  }
19255  return res;
19256  }
19257 
19259  CImg<ulongT> get_histogram(const unsigned int nb_levels) const {
19260  if (!nb_levels || is_empty()) return CImg<ulongT>();
19261  T vmax = 0, vmin = min_max(vmax);
19262  return get_histogram(nb_levels,vmin,vmax);
19263  }
19264 
19266 
19279  CImg<T>& equalize(const unsigned int nb_levels, const T min_value, const T max_value) {
19280  if (!nb_levels || is_empty()) return *this;
19281  T vmin = min_value<max_value?min_value:max_value, vmax = min_value<max_value?max_value:min_value;
19282  CImg<ulongT> hist = get_histogram(nb_levels,vmin,vmax);
19283  unsigned long cumul = 0;
19284  cimg_forX(hist,pos) { cumul+=hist[pos]; hist[pos] = cumul; }
19285 #ifdef cimg_use_openmp
19286 #pragma omp parallel for if (size()>=1048576)
19287 #endif
19288  cimg_rof(*this,ptrd,T) {
19289  const int pos = (int)((*ptrd-vmin)*(nb_levels-1)/(vmax-vmin));
19290  if (pos>=0 && pos<(int)nb_levels) *ptrd = (T)(vmin + (vmax-vmin)*hist[pos]/size());
19291  }
19292  return *this;
19293  }
19294 
19296  CImg<T>& equalize(const unsigned int nb_levels) {
19297  if (!nb_levels || is_empty()) return *this;
19298  T vmax = 0, vmin = min_max(vmax);
19299  return equalize(nb_levels,vmin,vmax);
19300  }
19301 
19303  CImg<T> get_equalize(const unsigned int nblevels, const T val_min, const T val_max) const {
19304  return (+*this).equalize(nblevels,val_min,val_max);
19305  }
19306 
19308  CImg<T> get_equalize(const unsigned int nblevels) const {
19309  return (+*this).equalize(nblevels);
19310  }
19311 
19313 
19327  template<typename t>
19328  CImg<T>& index(const CImg<t>& colormap, const float dithering=1, const bool map_indexes=false) {
19329  return get_index(colormap,dithering,map_indexes).move_to(*this);
19330  }
19331 
19333  template<typename t>
19335  get_index(const CImg<t>& colormap, const float dithering=1, const bool map_indexes=true) const {
19336  if (colormap._spectrum!=_spectrum)
19337  throw CImgArgumentException(_cimg_instance
19338  "index(): Instance and specified colormap (%u,%u,%u,%u,%p) "
19339  "have incompatible dimensions.",
19340  cimg_instance,
19341  colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data);
19342 
19343  typedef typename CImg<t>::Tuint tuint;
19344  if (is_empty()) return CImg<tuint>();
19345  const unsigned long
19346  whd = (unsigned long)_width*_height*_depth,
19347  pwhd = (unsigned long)colormap._width*colormap._height*colormap._depth;
19348  CImg<tuint> res(_width,_height,_depth,map_indexes?_spectrum:1);
19349  tuint *ptrd = res._data;
19350  if (dithering>0) { // Dithered versions.
19351  const float ndithering = (dithering<0?0:dithering>1?1:dithering)/16;
19352  Tfloat valm = 0, valM = (Tfloat)max_min(valm);
19353  if (valm==valM && valm>=0 && valM<=255) { valm = 0; valM = 255; }
19354  CImg<Tfloat> cache = get_crop(-1,0,0,0,_width,1,0,_spectrum-1);
19355  Tfloat *cache_current = cache.data(1,0,0,0), *cache_next = cache.data(1,1,0,0);
19356  const unsigned long cwhd = (unsigned long)cache._width*cache._height*cache._depth;
19357  switch (_spectrum) {
19358  case 1 : { // Optimized for scalars.
19359  cimg_forYZ(*this,y,z) {
19360  if (y<height()-2) {
19361  Tfloat *ptrc0 = cache_next; const T *ptrs0 = data(0,y+1,z,0);
19362  cimg_forX(*this,x) *(ptrc0++) = (Tfloat)*(ptrs0++);
19363  }
19364  Tfloat *ptrs0 = cache_current, *ptrsn0 = cache_next;
19365  cimg_forX(*this,x) {
19366  const Tfloat _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0;
19367  Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
19368  for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) {
19369  const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0;
19370  if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
19371  }
19372  const Tfloat err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering;
19373  *ptrs0+=7*err0; *(ptrsn0-1)+=3*err0; *(ptrsn0++)+=5*err0; *ptrsn0+=err0;
19374  if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
19375  }
19376  cimg::swap(cache_current,cache_next);
19377  }
19378  } break;
19379  case 2 : { // Optimized for 2d vectors.
19380  tuint *ptrd1 = ptrd + whd;
19381  cimg_forYZ(*this,y,z) {
19382  if (y<height()-2) {
19383  Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd;
19384  const T *ptrs0 = data(0,y+1,z,0), *ptrs1 = ptrs0 + whd;
19385  cimg_forX(*this,x) { *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); }
19386  }
19387  Tfloat
19388  *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd,
19389  *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd;
19390  cimg_forX(*this,x) {
19391  const Tfloat
19392  _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0,
19393  _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1;
19394  Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
19395  for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
19396  const Tfloat
19397  pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1,
19398  dist = pval0*pval0 + pval1*pval1;
19399  if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
19400  }
19401  const t *const ptrmin1 = ptrmin0 + pwhd;
19402  const Tfloat
19403  err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering,
19404  err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)*ndithering;
19405  *ptrs0+=7*err0; *ptrs1+=7*err1;
19406  *(ptrsn0-1)+=3*err0; *(ptrsn1-1)+=3*err1;
19407  *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1;
19408  *ptrsn0+=err0; *ptrsn1+=err1;
19409  if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; }
19410  else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
19411  }
19412  cimg::swap(cache_current,cache_next);
19413  }
19414  } break;
19415  case 3 : { // Optimized for 3d vectors (colors).
19416  tuint *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd;
19417  cimg_forYZ(*this,y,z) {
19418  if (y<height()-2) {
19419  Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd, *ptrc2 = ptrc1 + cwhd;
19420  const T *ptrs0 = data(0,y+1,z,0), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd;
19421  cimg_forX(*this,x) {
19422  *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); *(ptrc2++) = (Tfloat)*(ptrs2++);
19423  }
19424  }
19425  Tfloat
19426  *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd, *ptrs2 = ptrs1 + cwhd,
19427  *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd, *ptrsn2 = ptrsn1 + cwhd;
19428  cimg_forX(*this,x) {
19429  const Tfloat
19430  _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0,
19431  _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1,
19432  _val2 = (Tfloat)*ptrs2, val2 = _val2<valm?valm:_val2>valM?valM:_val2;
19433  Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
19434  for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd,
19435  *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
19436  const Tfloat
19437  pval0 = (Tfloat)*(ptrp0++) - val0,
19438  pval1 = (Tfloat)*(ptrp1++) - val1,
19439  pval2 = (Tfloat)*(ptrp2++) - val2,
19440  dist = pval0*pval0 + pval1*pval1 + pval2*pval2;
19441  if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
19442  }
19443  const t *const ptrmin1 = ptrmin0 + pwhd, *const ptrmin2 = ptrmin1 + pwhd;
19444  const Tfloat
19445  err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering,
19446  err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)*ndithering,
19447  err2 = ((*(ptrs2++)=val2) - (Tfloat)*ptrmin2)*ndithering;
19448 
19449  *ptrs0+=7*err0; *ptrs1+=7*err1; *ptrs2+=7*err2;
19450  *(ptrsn0-1)+=3*err0; *(ptrsn1-1)+=3*err1; *(ptrsn2-1)+=3*err2;
19451  *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1; *(ptrsn2++)+=5*err2;
19452  *ptrsn0+=err0; *ptrsn1+=err1; *ptrsn2+=err2;
19453 
19454  if (map_indexes) {
19455  *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; *(ptrd2++) = (tuint)*ptrmin2;
19456  } else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
19457  }
19458  cimg::swap(cache_current,cache_next);
19459  }
19460  } break;
19461  default : // Generic version
19462  cimg_forYZ(*this,y,z) {
19463  if (y<height()-2) {
19464  Tfloat *ptrc = cache_next;
19465  cimg_forC(*this,c) {
19466  Tfloat *_ptrc = ptrc; const T *_ptrs = data(0,y+1,z,c);
19467  cimg_forX(*this,x) *(_ptrc++) = (Tfloat)*(_ptrs++);
19468  ptrc+=cwhd;
19469  }
19470  }
19471  Tfloat *ptrs = cache_current, *ptrsn = cache_next;
19472  cimg_forX(*this,x) {
19473  Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = colormap._data;
19474  for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) {
19475  Tfloat dist = 0; Tfloat *_ptrs = ptrs; const t *_ptrp = ptrp;
19476  cimg_forC(*this,c) {
19477  const Tfloat _val = *_ptrs, val = _val<valm?valm:_val>valM?valM:_val;
19478  dist+=cimg::sqr((*_ptrs=val) - (Tfloat)*_ptrp); _ptrs+=cwhd; _ptrp+=pwhd;
19479  }
19480  if (dist<distmin) { ptrmin = ptrp; distmin = dist; }
19481  }
19482  const t *_ptrmin = ptrmin; Tfloat *_ptrs = ptrs++, *_ptrsn = (ptrsn++)-1;
19483  cimg_forC(*this,c) {
19484  const Tfloat err = (*(_ptrs++) - (Tfloat)*_ptrmin)*ndithering;
19485  *_ptrs+=7*err; *(_ptrsn++)+=3*err; *(_ptrsn++)+=5*err; *_ptrsn+=err;
19486  _ptrmin+=pwhd; _ptrs+=cwhd-1; _ptrsn+=cwhd-2;
19487  }
19488  if (map_indexes) {
19489  tuint *_ptrd = ptrd++;
19490  cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; }
19491  }
19492  else *(ptrd++) = (tuint)(ptrmin - colormap._data);
19493  }
19494  cimg::swap(cache_current,cache_next);
19495  }
19496  }
19497  } else { // Non-dithered versions
19498  switch (_spectrum) {
19499  case 1 : { // Optimized for scalars.
19500 #ifdef cimg_use_openmp
19501 #pragma omp parallel for collapse(2) if (_width>=64 && _height*_depth>=16 && pwhd>=16)
19502 #endif
19503  cimg_forYZ(*this,y,z) {
19504  tuint *ptrd = res.data(0,y,z);
19505  for (const T *ptrs0 = data(0,y,z), *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) {
19506  const Tfloat val0 = (Tfloat)*(ptrs0++);
19507  Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
19508  for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) {
19509  const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0;
19510  if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
19511  }
19512  if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
19513  }
19514  }
19515  } break;
19516  case 2 : { // Optimized for 2d vectors.
19517 #ifdef cimg_use_openmp
19518 #pragma omp parallel for collapse(2) if (_width>=64 && _height*_depth>=16 && pwhd>=16)
19519 #endif
19520  cimg_forYZ(*this,y,z) {
19521  tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd;
19522  for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) {
19523  const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++);
19524  Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
19525  for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
19526  const Tfloat
19527  pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1,
19528  dist = pval0*pval0 + pval1*pval1;
19529  if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
19530  }
19531  if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*(ptrmin0 + pwhd); }
19532  else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
19533  }
19534  }
19535  } break;
19536  case 3 : { // Optimized for 3d vectors (colors).
19537 #ifdef cimg_use_openmp
19538 #pragma omp parallel for collapse(2) if (_width>=64 && _height*_depth>=16 && pwhd>=16)
19539 #endif
19540  cimg_forYZ(*this,y,z) {
19541  tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd;
19542  for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd,
19543  *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) {
19544  const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++), val2 = (Tfloat)*(ptrs2++);
19545  Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
19546  for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd,
19547  *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
19548  const Tfloat
19549  pval0 = (Tfloat)*(ptrp0++) - val0,
19550  pval1 = (Tfloat)*(ptrp1++) - val1,
19551  pval2 = (Tfloat)*(ptrp2++) - val2,
19552  dist = pval0*pval0 + pval1*pval1 + pval2*pval2;
19553  if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
19554  }
19555  if (map_indexes) {
19556  *(ptrd++) = (tuint)*ptrmin0;
19557  *(ptrd1++) = (tuint)*(ptrmin0 + pwhd);
19558  *(ptrd2++) = (tuint)*(ptrmin0 + 2*pwhd);
19559  } else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
19560  }
19561  }
19562  } break;
19563  default : // Generic version.
19564 #ifdef cimg_use_openmp
19565 #pragma omp parallel for collapse(2) if (_width>=64 && _height*_depth>=16 && pwhd>=16)
19566 #endif
19567  cimg_forYZ(*this,y,z) {
19568  tuint *ptrd = res.data(0,y,z);
19569  for (const T *ptrs = data(0,y,z), *ptrs_end = ptrs + _width; ptrs<ptrs_end; ++ptrs) {
19570  Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = colormap._data;
19571  for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) {
19572  Tfloat dist = 0; const T *_ptrs = ptrs; const t *_ptrp = ptrp;
19573  cimg_forC(*this,c) { dist+=cimg::sqr((Tfloat)*_ptrs - (Tfloat)*_ptrp); _ptrs+=whd; _ptrp+=pwhd; }
19574  if (dist<distmin) { ptrmin = ptrp; distmin = dist; }
19575  }
19576  if (map_indexes) {
19577  tuint *_ptrd = ptrd++;
19578  cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; }
19579  }
19580  else *(ptrd++) = (tuint)(ptrmin - colormap._data);
19581  }
19582  }
19583  }
19584  }
19585  return res;
19586  }
19587 
19589 
19602  template<typename t>
19603  CImg<T>& map(const CImg<t>& colormap, const unsigned int boundary_conditions=0) {
19604  return get_map(colormap,boundary_conditions).move_to(*this);
19605  }
19606 
19608  template<typename t>
19609  CImg<t> get_map(const CImg<t>& colormap, const unsigned int boundary_conditions=0) const {
19610  if (_spectrum!=1 && colormap._spectrum!=1)
19611  throw CImgArgumentException(_cimg_instance
19612  "map(): Instance and specified colormap (%u,%u,%u,%u,%p) "
19613  "have incompatible dimensions.",
19614  cimg_instance,
19615  colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data);
19616 
19617  const unsigned long
19618  whd = (unsigned long)_width*_height*_depth,
19619  pwhd = (unsigned long)colormap._width*colormap._height*colormap._depth;
19620  CImg<t> res(_width,_height,_depth,colormap._spectrum==1?_spectrum:colormap._spectrum);
19621  switch (colormap._spectrum) {
19622 
19623  case 1 : { // Optimized for scalars.
19624  const T *ptrs = _data;
19625  switch (boundary_conditions) {
19626  case 2 : // Periodic boundaries.
19627  cimg_for(res,ptrd,t) {
19628  const unsigned long ind = (unsigned long)*(ptrs++);
19629  *ptrd = colormap[ind%pwhd];
19630  } break;
19631  case 1 : // Neumann boundaries.
19632  cimg_for(res,ptrd,t) {
19633  const long ind = (long)*(ptrs++);
19634  *ptrd = colormap[ind<0?0:ind>=(long)pwhd?pwhd-1:ind];
19635  } break;
19636  default : // Dirichlet boundaries.
19637  cimg_for(res,ptrd,t) {
19638  const unsigned long ind = (unsigned long)*(ptrs++);
19639  *ptrd = ind<pwhd?colormap[ind]:(t)0;
19640  }
19641  }
19642  } break;
19643 
19644  case 2 : { // Optimized for 2d vectors.
19645  switch (boundary_conditions) {
19646  case 2 : { // Periodic boundaries.
19647  const t *const ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd;
19648  t *ptrd0 = res._data, *ptrd1 = ptrd0 + whd;
19649  for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
19650  const unsigned long _ind = (unsigned long)*(ptrs++), ind = _ind%pwhd;
19651  *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind];
19652  }
19653  } break;
19654  case 1 : { // Neumann boundaries.
19655  const t *const ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd;
19656  t *ptrd0 = res._data, *ptrd1 = ptrd0 + whd;
19657  for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
19658  const long _ind = (long)*(ptrs++), ind = _ind<0?0:_ind>=(long)pwhd?pwhd-1:_ind;
19659  *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind];
19660  }
19661  } break;
19662  default : { // Dirichlet boundaries.
19663  const t *const ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd;
19664  t *ptrd0 = res._data, *ptrd1 = ptrd0 + whd;
19665  for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
19666  const unsigned long ind = (unsigned long)*(ptrs++);
19667  const bool is_in = ind<pwhd;
19668  *(ptrd0++) = is_in?ptrp0[ind]:(t)0; *(ptrd1++) = is_in?ptrp1[ind]:(t)0;
19669  }
19670  }
19671  }
19672  } break;
19673 
19674  case 3 : { // Optimized for 3d vectors (colors).
19675  switch (boundary_conditions) {
19676  case 2 : { // Periodic boundaries.
19677  const t *const ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd;
19678  t *ptrd0 = res._data, *ptrd1 = ptrd0 + whd, *ptrd2 = ptrd1 + whd;
19679  for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
19680  const unsigned long _ind = (unsigned long)*(ptrs++), ind = _ind%pwhd;
19681  *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind]; *(ptrd2++) = ptrp2[ind];
19682  }
19683  } break;
19684  case 1 : { // Neumann boundaries.
19685  const t *const ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd;
19686  t *ptrd0 = res._data, *ptrd1 = ptrd0 + whd, *ptrd2 = ptrd1 + whd;
19687  for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
19688  const long _ind = (long)*(ptrs++), ind = _ind<0?0:_ind>=(long)pwhd?pwhd-1:_ind;
19689  *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind]; *(ptrd2++) = ptrp2[ind];
19690  }
19691  } break;
19692  default : { // Dirichlet boundaries.
19693  const t *const ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd;
19694  t *ptrd0 = res._data, *ptrd1 = ptrd0 + whd, *ptrd2 = ptrd1 + whd;
19695  for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
19696  const unsigned long ind = (unsigned long)*(ptrs++);
19697  const bool is_in = ind<pwhd;
19698  *(ptrd0++) = is_in?ptrp0[ind]:(t)0; *(ptrd1++) = is_in?ptrp1[ind]:(t)0; *(ptrd2++) = is_in?ptrp2[ind]:(t)0;
19699  }
19700  }
19701  }
19702  } break;
19703 
19704  default : { // Generic version.
19705  switch (boundary_conditions) {
19706  case 2 : { // Periodic boundaries.
19707  t *ptrd = res._data;
19708  for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
19709  const unsigned long _ind = (unsigned long)*(ptrs++), ind = _ind%pwhd;
19710  const t *ptrp = colormap._data + ind;
19711  t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = *ptrp; _ptrd+=whd; ptrp+=pwhd; }
19712  }
19713  } break;
19714  case 1 : { // Neumann boundaries.
19715  t *ptrd = res._data;
19716  for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
19717  const long _ind = (long)*(ptrs++), ind = _ind<0?0:_ind>=(long)pwhd?pwhd-1:_ind;
19718  const t *ptrp = colormap._data + ind;
19719  t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = *ptrp; _ptrd+=whd; ptrp+=pwhd; }
19720  }
19721  } break;
19722  default : { // Dirichlet boundaries.
19723  t *ptrd = res._data;
19724  for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
19725  const unsigned long ind = (unsigned long)*(ptrs++);
19726  const bool is_in = ind<pwhd;
19727  if (is_in) {
19728  const t *ptrp = colormap._data + ind;
19729  t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = *ptrp; _ptrd+=whd; ptrp+=pwhd; }
19730  } else {
19731  t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = (t)0; _ptrd+=whd; }
19732  }
19733  }
19734  }
19735  }
19736  }
19737  }
19738  return res;
19739  }
19740 
19742 
19752  CImg<T>& label(const bool is_high_connectivity=false, const Tfloat tolerance=0) {
19753  return get_label(is_high_connectivity,tolerance).move_to(*this);
19754  }
19755 
19757  CImg<unsigned long> get_label(const bool is_high_connectivity=false,
19758  const Tfloat tolerance=0) const {
19759  if (is_empty()) return CImg<unsigned long>();
19760 
19761  // Create neighborhood tables.
19762  int dx[13], dy[13], dz[13], nb = 0;
19763  dx[nb]=1; dy[nb] = 0; dz[nb++]=0;
19764  dx[nb]=0; dy[nb] = 1; dz[nb++]=0;
19765  if (is_high_connectivity) {
19766  dx[nb]=1; dy[nb] = 1; dz[nb++]=0;
19767  dx[nb]=1; dy[nb] = -1; dz[nb++]=0;
19768  }
19769  if (_depth>1) { // 3d version.
19770  dx[nb]=0; dy[nb] = 0; dz[nb++]=1;
19771  if (is_high_connectivity) {
19772  dx[nb]=1; dy[nb] = 1; dz[nb++]=-1;
19773  dx[nb]=1; dy[nb] = 0; dz[nb++]=-1;
19774  dx[nb]=1; dy[nb] = -1; dz[nb++]=-1;
19775  dx[nb]=0; dy[nb] = 1; dz[nb++]=-1;
19776 
19777  dx[nb]=0; dy[nb] = 1; dz[nb++]=1;
19778  dx[nb]=1; dy[nb] = -1; dz[nb++]=1;
19779  dx[nb]=1; dy[nb] = 0; dz[nb++]=1;
19780  dx[nb]=1; dy[nb] = 1; dz[nb++]=1;
19781  }
19782  }
19783  return _get_label(nb,dx,dy,dz,tolerance);
19784  }
19785 
19787 
19791  template<typename t>
19792  CImg<T>& label(const CImg<t>& connectivity_mask, const Tfloat tolerance=0) {
19793  return get_label(connectivity_mask,tolerance).move_to(*this);
19794  }
19795 
19797  template<typename t>
19798  CImg<unsigned long> get_label(const CImg<t>& connectivity_mask,
19799  const Tfloat tolerance=0) const {
19800  int nb = 0;
19801  cimg_for(connectivity_mask,ptr,t) if (*ptr) ++nb;
19802  CImg<intT> dx(nb,1,1,1,0), dy(nb,1,1,1,0), dz(nb,1,1,1,0);
19803  nb = 0;
19804  cimg_forXYZ(connectivity_mask,x,y,z) if ((x || y || z) &&
19805  connectivity_mask(x,y,z)) {
19806  dx[nb] = x; dy[nb] = y; dz[nb++] = z;
19807  }
19808  return _get_label(nb,dx,dy,dz,tolerance);
19809  }
19810 
19811  CImg<unsigned long> _get_label(const unsigned int nb, const int
19812  *const dx, const int *const dy, const int *const dz,
19813  const Tfloat tolerance) const {
19814  CImg<unsigned long> res(_width,_height,_depth,_spectrum);
19815  cimg_forC(*this,c) {
19817 
19818  // Init label numbers.
19819  unsigned long *ptr = _res.data();
19820  cimg_foroff(_res,p) *(ptr++) = p;
19821 
19822  // For each neighbour-direction, label.
19823  for (unsigned int n = 0; n<nb; ++n) {
19824  const int _dx = dx[n], _dy = dy[n], _dz = dz[n];
19825  if (_dx || _dy || _dz) {
19826  const int
19827  x0 = _dx<0?-_dx:0,
19828  x1 = _dx<0?_width:_width - _dx,
19829  y0 = _dy<0?-_dy:0,
19830  y1 = _dy<0?_height:_height - _dy,
19831  z0 = _dz<0?-_dz:0,
19832  z1 = _dz<0?_depth:_depth - _dz;
19833  const long wh = (long)_width*_height, offset = (long)_dz*wh + (long)_dy*_width + _dx;
19834  for (long z = z0, nz = z0 + _dz, pz = z0*wh; z<z1; ++z, ++nz, pz+=wh) {
19835  for (long y = y0, ny = y0 + _dy, py = y0*_width + pz; y<y1; ++y, ++ny, py+=_width) {
19836  for (long x = x0, nx = x0 + _dx, p = x0 + py; x<x1; ++x, ++nx, ++p) {
19837  if ((Tfloat)cimg::abs((*this)(x,y,z,c,wh)-(*this)(nx,ny,nz,c,wh))<=tolerance) {
19838  const long q = p + offset;
19839  unsigned long x, y;
19840  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); }
19841  if (x!=y) _res[x] = y;
19842  for (unsigned long _p = p; _p!=y; ) { const unsigned long h = _res[_p]; _res[_p] = y; _p = h; }
19843  for (unsigned long _q = q; _q!=y; ) { const unsigned long h = _res[_q]; _res[_q] = y; _q = h; }
19844  }
19845  }
19846  }
19847  }
19848  }
19849  }
19850 
19851  // Resolve equivalences.
19852  unsigned long counter = 0;
19853  ptr = _res.data();
19854  cimg_foroff(_res,p) { *ptr = *ptr==p?counter++:_res[*ptr]; ++ptr; }
19855  }
19856  return res;
19857  }
19858 
19859  // [internal] Replace possibly malicious characters for commands to be called by system() by their escaped version.
19860  CImg<T>& _system_strescape() {
19861 #define cimg_system_strescape(c,s) case c : if (p!=ptrs) CImg<T>(ptrs,(unsigned int)(p-ptrs),1,1,1,false).\
19862  move_to(list); \
19863  CImg<T>(s,(unsigned int)std::strlen(s),1,1,1,false).move_to(list); ptrs = p+1; break
19864  CImgList<T> list;
19865  const T *ptrs = _data;
19866  cimg_for(*this,p,T) switch ((int)*p) {
19867  cimg_system_strescape('\\',"\\\\");
19868  cimg_system_strescape('\"',"\\\"");
19869  cimg_system_strescape('!',"\"\\!\"");
19870  cimg_system_strescape('`',"\\`");
19871  cimg_system_strescape('$',"\\$");
19872  }
19873  if (ptrs<end()) CImg<T>(ptrs,(unsigned int)(end()-ptrs),1,1,1,false).move_to(list);
19874  return (list>'x').move_to(*this);
19875  }
19876 
19878  //---------------------------------
19879  //
19881 
19882  //---------------------------------
19883 
19885 
19889  static const CImg<Tuchar>& default_LUT256() {
19890  static CImg<Tuchar> colormap;
19891  cimg::mutex(8);
19892  if (!colormap) {
19893  colormap.assign(1,256,1,3);
19894  for (unsigned int index = 0, r = 16; r<256; r+=32)
19895  for (unsigned int g = 16; g<256; g+=32)
19896  for (unsigned int b = 32; b<256; b+=64) {
19897  colormap(0,index,0) = (Tuchar)r;
19898  colormap(0,index,1) = (Tuchar)g;
19899  colormap(0,index++,2) = (Tuchar)b;
19900  }
19901  }
19902  cimg::mutex(8,0);
19903  return colormap;
19904  }
19905 
19907 
19911  static const CImg<Tuchar>& HSV_LUT256() {
19912  static CImg<Tuchar> colormap;
19913  cimg::mutex(8);
19914  if (!colormap) {
19915  CImg<Tint> tmp(1,256,1,3,1);
19916  tmp.get_shared_channel(0).sequence(0,359);
19917  colormap = tmp.HSVtoRGB();
19918  }
19919  cimg::mutex(8,0);
19920  return colormap;
19921  }
19922 
19924 
19928  static const CImg<Tuchar>& lines_LUT256() {
19929  static const unsigned char pal[] = {
19930  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,
19931  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,
19932  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,
19933  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,
19934  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,
19935  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,
19936  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,
19937  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,
19938  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,
19939  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,
19940  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,
19941  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,
19942  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,
19943  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,
19944  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,
19945  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,
19946  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,
19947  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,
19948  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,
19949  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,
19950  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,
19951  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,
19952  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,
19953  23,168,19,50,240,244,185,0,1,144,10,168,31,82,1,13 };
19954  static const CImg<Tuchar> colormap(pal,1,256,1,3,false);
19955  return colormap;
19956  }
19957 
19959 
19963  static const CImg<Tuchar>& hot_LUT256() {
19964  static CImg<Tuchar> colormap;
19965  cimg::mutex(8);
19966  if (!colormap) {
19967  colormap.assign(1,4,1,3,0);
19968  colormap[1] = colormap[2] = colormap[3] = colormap[6] = colormap[7] = colormap[11] = 255;
19969  colormap.resize(1,256,1,3,3);
19970  }
19971  cimg::mutex(8,0);
19972  return colormap;
19973  }
19974 
19976 
19980  static const CImg<Tuchar>& cool_LUT256() {
19981  static CImg<Tuchar> colormap;
19982  cimg::mutex(8);
19983  if (!colormap) colormap.assign(1,2,1,3).fill(0,255,255,0,255,255).resize(1,256,1,3,3);
19984  cimg::mutex(8,0);
19985  return colormap;
19986  }
19987 
19989 
19993  static const CImg<Tuchar>& jet_LUT256() {
19994  static CImg<Tuchar> colormap;
19995  cimg::mutex(8);
19996  if (!colormap) {
19997  colormap.assign(1,4,1,3,0);
19998  colormap[2] = colormap[3] = colormap[5] = colormap[6] = colormap[8] = colormap[9] = 255;
19999  colormap.resize(1,256,1,3,3);
20000  }
20001  cimg::mutex(8,0);
20002  return colormap;
20003  }
20004 
20006 
20010  static const CImg<Tuchar>& flag_LUT256() {
20011  static CImg<Tuchar> colormap;
20012  cimg::mutex(8);
20013  if (!colormap) {
20014  colormap.assign(1,4,1,3,0);
20015  colormap[0] = colormap[1] = colormap[5] = colormap[9] = colormap[10] = 255;
20016  colormap.resize(1,256,1,3,0,2);
20017  }
20018  cimg::mutex(8,0);
20019  return colormap;
20020  }
20021 
20023 
20027  static const CImg<Tuchar>& cube_LUT256() {
20028  static CImg<Tuchar> colormap;
20029  cimg::mutex(8);
20030  if (!colormap) {
20031  colormap.assign(1,8,1,3,0);
20032  colormap[1] = colormap[3] = colormap[5] = colormap[7] =
20033  colormap[10] = colormap[11] = colormap[12] = colormap[13] =
20034  colormap[20] = colormap[21] = colormap[22] = colormap[23] = 255;
20035  colormap.resize(1,256,1,3,3);
20036  }
20037  cimg::mutex(8,0);
20038  return colormap;
20039  }
20040 
20043  cimg_for(*this,ptr,T) {
20044  const Tfloat
20045  sval = (Tfloat)*ptr,
20046  nsval = (sval<0?0:sval>255?255:sval)/255,
20047  val = (Tfloat)(nsval<=0.04045f?nsval/12.92f:std::pow((nsval+0.055f)/(1.055f),2.4f));
20048  *ptr = (T)(val*255);
20049  }
20050  return *this;
20051  }
20052 
20055  return CImg<Tfloat>(*this,false).sRGBtoRGB();
20056  }
20057 
20060  cimg_for(*this,ptr,T) {
20061  const Tfloat
20062  val = (Tfloat)*ptr,
20063  nval = (val<0?0:val>255?255:val)/255,
20064  sval = (Tfloat)(nval<=0.0031308f?nval*12.92f:1.055f*std::pow(nval,0.416667f)-0.055f);
20065  *ptr = (T)(sval*255);
20066  }
20067  return *this;
20068  }
20069 
20072  return CImg<Tfloat>(*this,false).RGBtosRGB();
20073  }
20074 
20077  if (_spectrum!=3)
20078  throw CImgInstanceException(_cimg_instance
20079  "RGBtoHSV(): Instance is not a RGB image.",
20080  cimg_instance);
20081 
20082  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
20083  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
20084  const Tfloat
20085  R = (Tfloat)*p1,
20086  G = (Tfloat)*p2,
20087  B = (Tfloat)*p3,
20088  nR = (R<0?0:(R>255?255:R))/255,
20089  nG = (G<0?0:(G>255?255:G))/255,
20090  nB = (B<0?0:(B>255?255:B))/255,
20091  m = cimg::min(nR,nG,nB),
20092  M = cimg::max(nR,nG,nB);
20093  Tfloat H = 0, S = 0;
20094  if (M!=m) {
20095  const Tfloat
20096  f = (nR==m)?(nG-nB):((nG==m)?(nB-nR):(nR-nG)),
20097  i = (Tfloat)((nR==m)?3:((nG==m)?5:1));
20098  H = (i-f/(M-m));
20099  if (H>=6) H-=6;
20100  H*=60;
20101  S = (M-m)/M;
20102  }
20103  *(p1++) = (T)H;
20104  *(p2++) = (T)S;
20105  *(p3++) = (T)M;
20106  }
20107  return *this;
20108  }
20109 
20112  return CImg<Tfloat>(*this,false).RGBtoHSV();
20113  }
20114 
20117  if (_spectrum!=3)
20118  throw CImgInstanceException(_cimg_instance
20119  "HSVtoRGB(): Instance is not a HSV image.",
20120  cimg_instance);
20121 
20122  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
20123  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
20124  Tfloat
20125  H = (Tfloat)*p1,
20126  S = (Tfloat)*p2,
20127  V = (Tfloat)*p3,
20128  R = 0, G = 0, B = 0;
20129  if (H==0 && S==0) R = G = B = V;
20130  else {
20131  H/=60;
20132  const int i = (int)std::floor(H);
20133  const Tfloat
20134  f = (i&1)?(H - i):(1 - H + i),
20135  m = V*(1 - S),
20136  n = V*(1 - S*f);
20137  switch (i) {
20138  case 6 :
20139  case 0 : R = V; G = n; B = m; break;
20140  case 1 : R = n; G = V; B = m; break;
20141  case 2 : R = m; G = V; B = n; break;
20142  case 3 : R = m; G = n; B = V; break;
20143  case 4 : R = n; G = m; B = V; break;
20144  case 5 : R = V; G = m; B = n; break;
20145  }
20146  }
20147  R*=255; G*=255; B*=255;
20148  *(p1++) = (T)(R<0?0:(R>255?255:R));
20149  *(p2++) = (T)(G<0?0:(G>255?255:G));
20150  *(p3++) = (T)(B<0?0:(B>255?255:B));
20151  }
20152  return *this;
20153  }
20154 
20157  return CImg<Tuchar>(*this,false).HSVtoRGB();
20158  }
20159 
20162  if (_spectrum!=3)
20163  throw CImgInstanceException(_cimg_instance
20164  "RGBtoHSL(): Instance is not a RGB image.",
20165  cimg_instance);
20166 
20167  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
20168  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
20169  const Tfloat
20170  R = (Tfloat)*p1,
20171  G = (Tfloat)*p2,
20172  B = (Tfloat)*p3,
20173  nR = (R<0?0:(R>255?255:R))/255,
20174  nG = (G<0?0:(G>255?255:G))/255,
20175  nB = (B<0?0:(B>255?255:B))/255,
20176  m = cimg::min(nR,nG,nB),
20177  M = cimg::max(nR,nG,nB),
20178  L = (m + M)/2;
20179  Tfloat H = 0, S = 0;
20180  if (M==m) H = S = 0;
20181  else {
20182  const Tfloat
20183  f = (nR==m)?(nG-nB):((nG==m)?(nB-nR):(nR-nG)),
20184  i = (nR==m)?3.0f:((nG==m)?5.0f:1.0f);
20185  H = (i-f/(M-m));
20186  if (H>=6) H-=6;
20187  H*=60;
20188  S = (2*L<=1)?((M-m)/(M+m)):((M-m)/(2-M-m));
20189  }
20190  *(p1++) = (T)H;
20191  *(p2++) = (T)S;
20192  *(p3++) = (T)L;
20193  }
20194  return *this;
20195  }
20196 
20199  return CImg< Tfloat>(*this,false).RGBtoHSL();
20200  }
20201 
20204  if (_spectrum!=3)
20205  throw CImgInstanceException(_cimg_instance
20206  "HSLtoRGB(): Instance is not a HSL image.",
20207  cimg_instance);
20208 
20209  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
20210  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
20211  const Tfloat
20212  H = (Tfloat)*p1,
20213  S = (Tfloat)*p2,
20214  L = (Tfloat)*p3,
20215  q = 2*L<1?L*(1+S):(L+S-L*S),
20216  p = 2*L-q,
20217  h = H/360,
20218  tr = h + 1.0f/3,
20219  tg = h,
20220  tb = h - 1.0f/3,
20221  ntr = tr<0?tr+1:(tr>1?tr-1:tr),
20222  ntg = tg<0?tg+1:(tg>1?tg-1:tg),
20223  ntb = tb<0?tb+1:(tb>1?tb-1:tb),
20224  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))),
20225  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))),
20226  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)));
20227  *(p1++) = (T)(R<0?0:(R>255?255:R));
20228  *(p2++) = (T)(G<0?0:(G>255?255:G));
20229  *(p3++) = (T)(B<0?0:(B>255?255:B));
20230  }
20231  return *this;
20232  }
20233 
20236  return CImg<Tuchar>(*this,false).HSLtoRGB();
20237  }
20238 
20241  if (_spectrum!=3)
20242  throw CImgInstanceException(_cimg_instance
20243  "RGBtoHSI(): Instance is not a RGB image.",
20244  cimg_instance);
20245 
20246  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
20247  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
20248  const Tfloat
20249  R = (Tfloat)*p1,
20250  G = (Tfloat)*p2,
20251  B = (Tfloat)*p3,
20252  nR = (R<0?0:(R>255?255:R))/255,
20253  nG = (G<0?0:(G>255?255:G))/255,
20254  nB = (B<0?0:(B>255?255:B))/255,
20255  m = cimg::min(nR,nG,nB),
20256  theta = (Tfloat)(std::acos(0.5f*((nR-nG)+(nR-nB))/std::sqrt(std::pow(nR-nG,2)+(nR-nB)*(nG-nB)))*180/cimg::PI),
20257  sum = nR + nG + nB;
20258  Tfloat H = 0, S = 0, I = 0;
20259  if (theta>0) H = (nB<=nG)?theta:360-theta;
20260  if (sum>0) S = 1 - 3/sum*m;
20261  I = sum/3;
20262  *(p1++) = (T)H;
20263  *(p2++) = (T)S;
20264  *(p3++) = (T)I;
20265  }
20266  return *this;
20267  }
20268 
20271  return CImg<Tfloat>(*this,false).RGBtoHSI();
20272  }
20273 
20276  if (_spectrum!=3)
20277  throw CImgInstanceException(_cimg_instance
20278  "HSItoRGB(): Instance is not a HSI image.",
20279  cimg_instance);
20280 
20281  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
20282  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
20283  Tfloat
20284  H = (Tfloat)*p1,
20285  S = (Tfloat)*p2,
20286  I = (Tfloat)*p3,
20287  a = I*(1-S),
20288  R = 0, G = 0, B = 0;
20289  if (H<120) {
20290  B = a;
20291  R = (Tfloat)(I*(1+S*std::cos(H*cimg::PI/180)/std::cos((60-H)*cimg::PI/180)));
20292  G = 3*I-(R+B);
20293  } else if (H<240) {
20294  H-=120;
20295  R = a;
20296  G = (Tfloat)(I*(1+S*std::cos(H*cimg::PI/180)/std::cos((60-H)*cimg::PI/180)));
20297  B = 3*I-(R+G);
20298  } else {
20299  H-=240;
20300  G = a;
20301  B = (Tfloat)(I*(1+S*std::cos(H*cimg::PI/180)/std::cos((60-H)*cimg::PI/180)));
20302  R = 3*I-(G+B);
20303  }
20304  R*=255; G*=255; B*=255;
20305  *(p1++) = (T)(R<0?0:(R>255?255:R));
20306  *(p2++) = (T)(G<0?0:(G>255?255:G));
20307  *(p3++) = (T)(B<0?0:(B>255?255:B));
20308  }
20309  return *this;
20310  }
20311 
20314  return CImg< Tuchar>(*this,false).HSItoRGB();
20315  }
20316 
20319  if (_spectrum!=3)
20320  throw CImgInstanceException(_cimg_instance
20321  "RGBtoYCbCr(): Instance is not a RGB image.",
20322  cimg_instance);
20323 
20324  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
20325  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
20326  const Tfloat
20327  R = (Tfloat)*p1,
20328  G = (Tfloat)*p2,
20329  B = (Tfloat)*p3,
20330  Y = (66*R + 129*G + 25*B + 128)/256 + 16,
20331  Cb = (-38*R - 74*G + 112*B + 128)/256 + 128,
20332  Cr = (112*R - 94*G - 18*B + 128)/256 + 128;
20333  *(p1++) = (T)(Y<0?0:(Y>255?255:Y));
20334  *(p2++) = (T)(Cb<0?0:(Cb>255?255:Cb));
20335  *(p3++) = (T)(Cr<0?0:(Cr>255?255:Cr));
20336  }
20337  return *this;
20338  }
20339 
20342  return CImg<Tuchar>(*this,false).RGBtoYCbCr();
20343  }
20344 
20347  if (_spectrum!=3)
20348  throw CImgInstanceException(_cimg_instance
20349  "YCbCrtoRGB(): Instance is not a YCbCr image.",
20350  cimg_instance);
20351 
20352  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
20353  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
20354  const Tfloat
20355  Y = (Tfloat)*p1 - 16,
20356  Cb = (Tfloat)*p2 - 128,
20357  Cr = (Tfloat)*p3 - 128,
20358  R = (298*Y + 409*Cr + 128)/256,
20359  G = (298*Y - 100*Cb - 208*Cr + 128)/256,
20360  B = (298*Y + 516*Cb + 128)/256;
20361  *(p1++) = (T)(R<0?0:(R>255?255:R));
20362  *(p2++) = (T)(G<0?0:(G>255?255:G));
20363  *(p3++) = (T)(B<0?0:(B>255?255:B));
20364  }
20365  return *this;
20366  }
20367 
20370  return CImg<Tuchar>(*this,false).YCbCrtoRGB();
20371  }
20372 
20375  if (_spectrum!=3)
20376  throw CImgInstanceException(_cimg_instance
20377  "RGBtoYUV(): Instance is not a RGB image.",
20378  cimg_instance);
20379 
20380  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
20381  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
20382  const Tfloat
20383  R = (Tfloat)*p1/255,
20384  G = (Tfloat)*p2/255,
20385  B = (Tfloat)*p3/255,
20386  Y = 0.299f*R + 0.587f*G + 0.114f*B;
20387  *(p1++) = (T)Y;
20388  *(p2++) = (T)(0.492f*(B-Y));
20389  *(p3++) = (T)(0.877*(R-Y));
20390  }
20391  return *this;
20392  }
20393 
20396  return CImg<Tfloat>(*this,false).RGBtoYUV();
20397  }
20398 
20401  if (_spectrum!=3)
20402  throw CImgInstanceException(_cimg_instance
20403  "YUVtoRGB(): Instance is not a YUV image.",
20404  cimg_instance);
20405 
20406  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
20407  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
20408  const Tfloat
20409  Y = (Tfloat)*p1,
20410  U = (Tfloat)*p2,
20411  V = (Tfloat)*p3,
20412  R = (Y + 1.140f*V)*255,
20413  G = (Y - 0.395f*U - 0.581f*V)*255,
20414  B = (Y + 2.032f*U)*255;
20415  *(p1++) = (T)(R<0?0:(R>255?255:R));
20416  *(p2++) = (T)(G<0?0:(G>255?255:G));
20417  *(p3++) = (T)(B<0?0:(B>255?255:B));
20418  }
20419  return *this;
20420  }
20421 
20424  return CImg< Tuchar>(*this,false).YUVtoRGB();
20425  }
20426 
20429  if (_spectrum!=3)
20430  throw CImgInstanceException(_cimg_instance
20431  "RGBtoCMY(): Instance is not a RGB image.",
20432  cimg_instance);
20433 
20434  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
20435  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
20436  const Tfloat
20437  R = (Tfloat)*p1,
20438  G = (Tfloat)*p2,
20439  B = (Tfloat)*p3,
20440  C = 255 - R,
20441  M = 255 - G,
20442  Y = 255 - B;
20443  *(p1++) = (T)(C<0?0:(C>255?255:C));
20444  *(p2++) = (T)(M<0?0:(M>255?255:M));
20445  *(p3++) = (T)(Y<0?0:(Y>255?255:Y));
20446  }
20447  return *this;
20448  }
20449 
20452  return CImg<Tfloat>(*this,false).RGBtoCMY();
20453  }
20454 
20457  if (_spectrum!=3)
20458  throw CImgInstanceException(_cimg_instance
20459  "CMYtoRGB(): Instance is not a CMY image.",
20460  cimg_instance);
20461 
20462  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
20463  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
20464  const Tfloat
20465  C = (Tfloat)*p1,
20466  M = (Tfloat)*p2,
20467  Y = (Tfloat)*p3,
20468  R = 255 - C,
20469  G = 255 - M,
20470  B = 255 - Y;
20471  *(p1++) = (T)(R<0?0:(R>255?255:R));
20472  *(p2++) = (T)(G<0?0:(G>255?255:G));
20473  *(p3++) = (T)(B<0?0:(B>255?255:B));
20474  }
20475  return *this;
20476  }
20477 
20480  return CImg<Tuchar>(*this,false).CMYtoRGB();
20481  }
20482 
20485  return get_CMYtoCMYK().move_to(*this);
20486  }
20487 
20490  if (_spectrum!=3)
20491  throw CImgInstanceException(_cimg_instance
20492  "CMYtoCMYK(): Instance is not a CMY image.",
20493  cimg_instance);
20494 
20495  CImg<Tfloat> res(_width,_height,_depth,4);
20496  const T *ps1 = data(0,0,0,0), *ps2 = data(0,0,0,1), *ps3 = data(0,0,0,2);
20497  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);
20498  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
20499  Tfloat
20500  C = (Tfloat)*(ps1++),
20501  M = (Tfloat)*(ps2++),
20502  Y = (Tfloat)*(ps3++),
20503  K = cimg::min(C,M,Y);
20504  if (K>=255) C = M = Y = 0;
20505  else { const Tfloat K1 = 255 - K; C = 255*(C - K)/K1; M = 255*(M - K)/K1; Y = 255*(Y - K)/K1; }
20506  *(pd1++) = (Tfloat)(C<0?0:(C>255?255:C));
20507  *(pd2++) = (Tfloat)(M<0?0:(M>255?255:M));
20508  *(pd3++) = (Tfloat)(Y<0?0:(Y>255?255:Y));
20509  *(pd4++) = (Tfloat)(K<0?0:(K>255?255:K));
20510  }
20511  return res;
20512  }
20513 
20516  return get_CMYKtoCMY().move_to(*this);
20517  }
20518 
20521  if (_spectrum!=4)
20522  throw CImgInstanceException(_cimg_instance
20523  "CMYKtoCMY(): Instance is not a CMYK image.",
20524  cimg_instance);
20525 
20526  CImg<Tfloat> res(_width,_height,_depth,3);
20527  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);
20528  Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2);
20529  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
20530  const Tfloat
20531  C = (Tfloat)*(ps1++),
20532  M = (Tfloat)*(ps2++),
20533  Y = (Tfloat)*(ps3++),
20534  K = (Tfloat)*(ps4++),
20535  K1 = 1 - K/255,
20536  nC = C*K1 + K,
20537  nM = M*K1 + K,
20538  nY = Y*K1 + K;
20539  *(pd1++) = (Tfloat)(nC<0?0:(nC>255?255:nC));
20540  *(pd2++) = (Tfloat)(nM<0?0:(nM>255?255:nM));
20541  *(pd3++) = (Tfloat)(nY<0?0:(nY>255?255:nY));
20542  }
20543  return res;
20544  }
20545 
20547 
20551  if (_spectrum!=3)
20552  throw CImgInstanceException(_cimg_instance
20553  "RGBtoXYZ(): Instance is not a RGB image.",
20554  cimg_instance);
20555 
20556  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
20557  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
20558  const Tfloat
20559  R = (Tfloat)*p1/255,
20560  G = (Tfloat)*p2/255,
20561  B = (Tfloat)*p3/255;
20562  *(p1++) = (T)(0.412453f*R + 0.357580f*G + 0.180423f*B);
20563  *(p2++) = (T)(0.212671f*R + 0.715160f*G + 0.072169f*B);
20564  *(p3++) = (T)(0.019334f*R + 0.119193f*G + 0.950227f*B);
20565  }
20566  return *this;
20567  }
20568 
20571  return CImg<Tfloat>(*this,false).RGBtoXYZ();
20572  }
20573 
20576  if (_spectrum!=3)
20577  throw CImgInstanceException(_cimg_instance
20578  "XYZtoRGB(): Instance is not a XYZ image.",
20579  cimg_instance);
20580 
20581  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
20582  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
20583  const Tfloat
20584  X = (Tfloat)*p1*255,
20585  Y = (Tfloat)*p2*255,
20586  Z = (Tfloat)*p3*255,
20587  R = 3.240479f*X - 1.537150f*Y - 0.498535f*Z,
20588  G = -0.969256f*X + 1.875992f*Y + 0.041556f*Z,
20589  B = 0.055648f*X - 0.204043f*Y + 1.057311f*Z;
20590  *(p1++) = (T)(R<0?0:(R>255?255:R));
20591  *(p2++) = (T)(G<0?0:(G>255?255:G));
20592  *(p3++) = (T)(B<0?0:(B>255?255:B));
20593  }
20594  return *this;
20595  }
20596 
20599  return CImg<Tuchar>(*this,false).XYZtoRGB();
20600  }
20601 
20604 #define _cimg_Labf(x) ((x)>=0.008856f?(std::pow(x,(Tfloat)1/3)):(7.787f*(x)+16.0f/116))
20605 
20606  if (_spectrum!=3)
20607  throw CImgInstanceException(_cimg_instance
20608  "XYZtoLab(): Instance is not a XYZ image.",
20609  cimg_instance);
20610 
20611  const Tfloat
20612  Xn = (Tfloat)(0.412453f + 0.357580f + 0.180423f),
20613  Yn = (Tfloat)(0.212671f + 0.715160f + 0.072169f),
20614  Zn = (Tfloat)(0.019334f + 0.119193f + 0.950227f);
20615  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
20616  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
20617  const Tfloat
20618  X = (Tfloat)*p1,
20619  Y = (Tfloat)*p2,
20620  Z = (Tfloat)*p3,
20621  XXn = X/Xn, YYn = Y/Yn, ZZn = Z/Zn,
20622  fX = (Tfloat)_cimg_Labf(XXn),
20623  fY = (Tfloat)_cimg_Labf(YYn),
20624  fZ = (Tfloat)_cimg_Labf(ZZn);
20625  *(p1++) = (T)cimg::max(0.0f,116*fY - 16);
20626  *(p2++) = (T)(500*(fX - fY));
20627  *(p3++) = (T)(200*(fY - fZ));
20628  }
20629  return *this;
20630  }
20631 
20634  return CImg<Tfloat>(*this,false).XYZtoLab();
20635  }
20636 
20639 #define _cimg_Labfi(x) ((x)>=0.206893f?((x)*(x)*(x)):(((x)-16.0f/116)/7.787f))
20640 
20641  if (_spectrum!=3)
20642  throw CImgInstanceException(_cimg_instance
20643  "LabtoXYZ(): Instance is not a Lab image.",
20644  cimg_instance);
20645 
20646  const Tfloat
20647  Xn = (Tfloat)(0.412453f + 0.357580f + 0.180423f),
20648  Yn = (Tfloat)(0.212671f + 0.715160f + 0.072169f),
20649  Zn = (Tfloat)(0.019334f + 0.119193f + 0.950227f);
20650  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
20651  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
20652  const Tfloat
20653  L = (Tfloat)*p1,
20654  a = (Tfloat)*p2,
20655  b = (Tfloat)*p3,
20656  cY = (L + 16)/116,
20657  Y = (Tfloat)(Yn*_cimg_Labfi(cY)),
20658  pY = (Tfloat)std::pow(Y/Yn,(Tfloat)1/3),
20659  cX = a/500 + pY,
20660  X = Xn*cX*cX*cX,
20661  cZ = pY - b/200,
20662  Z = Zn*cZ*cZ*cZ;
20663  *(p1++) = (T)(X);
20664  *(p2++) = (T)(Y);
20665  *(p3++) = (T)(Z);
20666  }
20667  return *this;
20668  }
20669 
20672  return CImg<Tfloat>(*this,false).LabtoXYZ();
20673  }
20674 
20677  if (_spectrum!=3)
20678  throw CImgInstanceException(_cimg_instance
20679  "XYZtoxyY(): Instance is not a XYZ image.",
20680  cimg_instance);
20681 
20682  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
20683  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
20684  const Tfloat
20685  X = (Tfloat)*p1,
20686  Y = (Tfloat)*p2,
20687  Z = (Tfloat)*p3,
20688  sum = (X+Y+Z),
20689  nsum = sum>0?sum:1;
20690  *(p1++) = (T)(X/nsum);
20691  *(p2++) = (T)(Y/nsum);
20692  *(p3++) = (T)Y;
20693  }
20694  return *this;
20695  }
20696 
20699  return CImg<Tfloat>(*this,false).XYZtoxyY();
20700  }
20701 
20704  if (_spectrum!=3)
20705  throw CImgInstanceException(_cimg_instance
20706  "xyYtoXYZ(): Instance is not a xyY image.",
20707  cimg_instance);
20708 
20709  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
20710  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
20711  const Tfloat
20712  px = (Tfloat)*p1,
20713  py = (Tfloat)*p2,
20714  Y = (Tfloat)*p3,
20715  ny = py>0?py:1;
20716  *(p1++) = (T)(px*Y/ny);
20717  *(p2++) = (T)Y;
20718  *(p3++) = (T)((1-px-py)*Y/ny);
20719  }
20720  return *this;
20721  }
20722 
20725  return CImg<Tfloat>(*this,false).xyYtoXYZ();
20726  }
20727 
20730  return RGBtoXYZ().XYZtoLab();
20731  }
20732 
20735  return CImg<Tfloat>(*this,false).RGBtoLab();
20736  }
20737 
20740  return LabtoXYZ().XYZtoRGB();
20741  }
20742 
20745  return CImg<Tuchar>(*this,false).LabtoRGB();
20746  }
20747 
20750  return RGBtoXYZ().XYZtoxyY();
20751  }
20752 
20755  return CImg<Tfloat>(*this,false).RGBtoxyY();
20756  }
20757 
20760  return xyYtoXYZ().XYZtoRGB();
20761  }
20762 
20765  return CImg<Tuchar>(*this,false).xyYtoRGB();
20766  }
20767 
20770  return RGBtoCMY().CMYtoCMYK();
20771  }
20772 
20775  return CImg<Tfloat>(*this,false).RGBtoCMYK();
20776  }
20777 
20780  return CMYKtoCMY().CMYtoRGB();
20781  }
20782 
20785  return CImg<Tuchar>(*this,false).CMYKtoRGB();
20786  }
20787 
20789 
20793  return get_RGBtoBayer().move_to(*this);
20794  }
20795 
20798  if (_spectrum!=3)
20799  throw CImgInstanceException(_cimg_instance
20800  "RGBtoBayer(): Instance is not a RGB image.",
20801  cimg_instance);
20802 
20803  CImg<T> res(_width,_height,_depth,1);
20804  const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
20805  T *ptrd = res._data;
20806  cimg_forXYZ(*this,x,y,z) {
20807  if (y%2) {
20808  if (x%2) *(ptrd++) = *ptr_b;
20809  else *(ptrd++) = *ptr_g;
20810  } else {
20811  if (x%2) *(ptrd++) = *ptr_g;
20812  else *(ptrd++) = *ptr_r;
20813  }
20814  ++ptr_r; ++ptr_g; ++ptr_b;
20815  }
20816  return res;
20817  }
20818 
20820  CImg<T>& BayertoRGB(const unsigned int interpolation_type=3) {
20821  return get_BayertoRGB(interpolation_type).move_to(*this);
20822  }
20823 
20825  CImg<Tuchar> get_BayertoRGB(const unsigned int interpolation_type=3) const {
20826  if (_spectrum!=1)
20827  throw CImgInstanceException(_cimg_instance
20828  "BayertoRGB(): Instance is not a Bayer image.",
20829  cimg_instance);
20830 
20831  CImg<Tuchar> res(_width,_height,_depth,3);
20832  CImg_3x3(I,T);
20833  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);
20834  switch (interpolation_type) {
20835  case 3 : { // Edge-directed
20836  CImg_3x3(R,T);
20837  CImg_3x3(G,T);
20838  CImg_3x3(B,T);
20839  cimg_forXYZ(*this,x,y,z) {
20840  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;
20841  cimg_get3x3(*this,x,y,z,0,I,T);
20842  if (y%2) {
20843  if (x%2) {
20844  const Tfloat
20845  alpha = cimg::sqr((Tfloat)Inc - Ipc),
20846  beta = cimg::sqr((Tfloat)Icn - Icp),
20847  cx = 1/(1+alpha), cy = 1/(1+beta);
20848  *ptr_g = (Tuchar)((cx*(Inc+Ipc) + cy*(Icn+Icp))/(2*(cx+cy)));
20849  } else *ptr_g = (Tuchar)Icc;
20850  } else {
20851  if (x%2) *ptr_g = (Tuchar)Icc;
20852  else {
20853  const Tfloat
20854  alpha = cimg::sqr((Tfloat)Inc - Ipc),
20855  beta = cimg::sqr((Tfloat)Icn - Icp),
20856  cx = 1/(1+alpha), cy = 1/(1+beta);
20857  *ptr_g = (Tuchar)((cx*(Inc+Ipc) + cy*(Icn+Icp))/(2*(cx+cy)));
20858  }
20859  }
20860  ++ptr_g;
20861  }
20862  cimg_forXYZ(*this,x,y,z) {
20863  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;
20864  cimg_get3x3(*this,x,y,z,0,I,T);
20865  cimg_get3x3(res,x,y,z,1,G,T);
20866  if (y%2) {
20867  if (x%2) *ptr_b = (Tuchar)Icc;
20868  else { *ptr_r = (Tuchar)((Icn+Icp)/2); *ptr_b = (Tuchar)((Inc+Ipc)/2); }
20869  } else {
20870  if (x%2) { *ptr_r = (Tuchar)((Inc+Ipc)/2); *ptr_b = (Tuchar)((Icn+Icp)/2); }
20871  else *ptr_r = (Tuchar)Icc;
20872  }
20873  ++ptr_r; ++ptr_b;
20874  }
20875  ptr_r = res.data(0,0,0,0);
20876  ptr_g = res.data(0,0,0,1);
20877  ptr_b = res.data(0,0,0,2);
20878  cimg_forXYZ(*this,x,y,z) {
20879  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;
20880  cimg_get3x3(res,x,y,z,0,R,T);
20881  cimg_get3x3(res,x,y,z,1,G,T);
20882  cimg_get3x3(res,x,y,z,2,B,T);
20883  if (y%2) {
20884  if (x%2) {
20885  const float
20886  alpha = (float)cimg::sqr(Rnc-Rpc),
20887  beta = (float)cimg::sqr(Rcn-Rcp),
20888  cx = 1/(1+alpha), cy = 1/(1+beta);
20889  *ptr_r = (Tuchar)((cx*(Rnc+Rpc) + cy*(Rcn+Rcp))/(2*(cx+cy)));
20890  }
20891  } else {
20892  if (!(x%2)) {
20893  const float
20894  alpha = (float)cimg::sqr(Bnc-Bpc),
20895  beta = (float)cimg::sqr(Bcn-Bcp),
20896  cx = 1/(1+alpha), cy = 1/(1+beta);
20897  *ptr_b = (Tuchar)((cx*(Bnc+Bpc) + cy*(Bcn+Bcp))/(2*(cx+cy)));
20898  }
20899  }
20900  ++ptr_r; ++ptr_g; ++ptr_b;
20901  }
20902  } break;
20903  case 2 : { // Linear interpolation
20904  cimg_forXYZ(*this,x,y,z) {
20905  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;
20906  cimg_get3x3(*this,x,y,z,0,I,T);
20907  if (y%2) {
20908  if (x%2) {
20909  *ptr_r = (Tuchar)((Ipp+Inn+Ipn+Inp)/4); *ptr_g = (Tuchar)((Inc+Ipc+Icn+Icp)/4); *ptr_b = (Tuchar)Icc;
20910  } else { *ptr_r = (Tuchar)((Icp+Icn)/2); *ptr_g = (Tuchar)Icc; *ptr_b = (Tuchar)((Inc+Ipc)/2); }
20911  } else {
20912  if (x%2) { *ptr_r = (Tuchar)((Ipc+Inc)/2); *ptr_g = (Tuchar)Icc; *ptr_b = (Tuchar)((Icn+Icp)/2); }
20913  else {
20914  *ptr_r = (Tuchar)Icc; *ptr_g = (Tuchar)((Inc+Ipc+Icn+Icp)/4); *ptr_b = (Tuchar)((Ipp+Inn+Ipn+Inp)/4);
20915  }
20916  }
20917  ++ptr_r; ++ptr_g; ++ptr_b;
20918  }
20919  } break;
20920  case 1 : { // Nearest neighbor interpolation
20921  cimg_forXYZ(*this,x,y,z) {
20922  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;
20923  cimg_get3x3(*this,x,y,z,0,I,T);
20924  if (y%2) {
20925  if (x%2) {
20926  *ptr_r = (Tuchar)cimg::min(Ipp,Inn,Ipn,Inp);
20927  *ptr_g = (Tuchar)cimg::min(Inc,Ipc,Icn,Icp);
20928  *ptr_b = (Tuchar)Icc;
20929  } else { *ptr_r = (Tuchar)cimg::min(Icn,Icp); *ptr_g = (Tuchar)Icc; *ptr_b = (Tuchar)cimg::min(Inc,Ipc); }
20930  } else {
20931  if (x%2) { *ptr_r = (Tuchar)cimg::min(Inc,Ipc); *ptr_g = (Tuchar)Icc; *ptr_b = (Tuchar)cimg::min(Icn,Icp); }
20932  else {
20933  *ptr_r = (Tuchar)Icc;
20934  *ptr_g = (Tuchar)cimg::min(Inc,Ipc,Icn,Icp);
20935  *ptr_b = (Tuchar)cimg::min(Ipp,Inn,Ipn,Inp);
20936  }
20937  }
20938  ++ptr_r; ++ptr_g; ++ptr_b;
20939  }
20940  } break;
20941  default : { // 0-filling interpolation
20942  const T *ptrs = _data;
20943  res.fill(0);
20944  cimg_forXYZ(*this,x,y,z) {
20945  const T val = *(ptrs++);
20946  if (y%2) { if (x%2) *ptr_b = val; else *ptr_g = val; } else { if (x%2) *ptr_g = val; else *ptr_r = val; }
20947  ++ptr_r; ++ptr_g; ++ptr_b;
20948  }
20949  }
20950  }
20951  return res;
20952  }
20953 
20955  //------------------------------------------
20956  //
20958 
20959  //------------------------------------------
20960 
20961  static float _cimg_lanczos(const float x) {
20962  if (x<=-2 || x>=2) return 0;
20963  const float a = (float)cimg::PI*x, b = 0.5f*a;
20964  return (float)(x?std::sin(a)*std::sin(b)/(a*b):1);
20965  }
20966 
20968 
20989  CImg<T>& resize(const int size_x, const int size_y=-100,
20990  const int size_z=-100, const int size_c=-100,
20991  const int interpolation_type=1, const unsigned int boundary_conditions=0,
20992  const float centering_x = 0, const float centering_y = 0,
20993  const float centering_z = 0, const float centering_c = 0) {
20994  if (!size_x || !size_y || !size_z || !size_c) return assign();
20995  const unsigned int
20996  _sx = (unsigned int)(size_x<0?-size_x*width()/100:size_x),
20997  _sy = (unsigned int)(size_y<0?-size_y*height()/100:size_y),
20998  _sz = (unsigned int)(size_z<0?-size_z*depth()/100:size_z),
20999  _sc = (unsigned int)(size_c<0?-size_c*spectrum()/100:size_c),
21000  sx = _sx?_sx:1, sy = _sy?_sy:1, sz = _sz?_sz:1, sc = _sc?_sc:1;
21001  if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return *this;
21002  if (is_empty()) return assign(sx,sy,sz,sc,(T)0);
21003  if (interpolation_type==-1 && sx*sy*sz*sc==size()) {
21004  _width = sx; _height = sy; _depth = sz; _spectrum = sc;
21005  return *this;
21006  }
21007  return get_resize(sx,sy,sz,sc,interpolation_type,boundary_conditions,
21008  centering_x,centering_y,centering_z,centering_c).move_to(*this);
21009  }
21010 
21012  CImg<T> get_resize(const int size_x, const int size_y = -100,
21013  const int size_z = -100, const int size_c = -100,
21014  const int interpolation_type=1, const unsigned int boundary_conditions=0,
21015  const float centering_x = 0, const float centering_y = 0,
21016  const float centering_z = 0, const float centering_c = 0) const {
21017  if (centering_x<0 || centering_x>1 || centering_y<0 || centering_y>1 ||
21018  centering_z<0 || centering_z>1 || centering_c<0 || centering_c>1)
21019  throw CImgArgumentException(_cimg_instance
21020  "resize(): Specified centering arguments (%g,%g,%g,%g) are outside range [0,1].",
21021  cimg_instance,
21022  centering_x,centering_y,centering_z,centering_c);
21023 
21024  if (!size_x || !size_y || !size_z || !size_c) return CImg<T>();
21025  const unsigned int
21026  _sx = (unsigned int)(size_x<0?-size_x*width()/100:size_x),
21027  _sy = (unsigned int)(size_y<0?-size_y*height()/100:size_y),
21028  _sz = (unsigned int)(size_z<0?-size_z*depth()/100:size_z),
21029  _sc = (unsigned int)(size_c<0?-size_c*spectrum()/100:size_c),
21030  sx = _sx?_sx:1, sy = _sy?_sy:1, sz = _sz?_sz:1, sc = _sc?_sc:1;
21031  if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return +*this;
21032  if (is_empty()) return CImg<T>(sx,sy,sz,sc,0);
21033 
21034  CImg<T> res;
21035  switch (interpolation_type) {
21036 
21037  // Raw resizing.
21038  //
21039  case -1 :
21040  std::memcpy(res.assign(sx,sy,sz,sc,0)._data,_data,sizeof(T)*cimg::min(size(),sx*sy*sz*sc));
21041  break;
21042 
21043  // No interpolation.
21044  //
21045  case 0 : {
21046  const int
21047  xc = (int)(centering_x*((int)sx - width())),
21048  yc = (int)(centering_y*((int)sy - height())),
21049  zc = (int)(centering_z*((int)sz - depth())),
21050  cc = (int)(centering_c*((int)sc - spectrum()));
21051 
21052  switch (boundary_conditions) {
21053  case 2 : { // Periodic borders.
21054  res.assign(sx,sy,sz,sc);
21055  const int
21056  x0 = ((int)xc%width()) - width(),
21057  y0 = ((int)yc%height()) - height(),
21058  z0 = ((int)zc%depth()) - depth(),
21059  c0 = ((int)cc%spectrum()) - spectrum();
21060 #ifdef cimg_use_openmp
21061 #pragma omp parallel for collapse(3) if (res.size()>=65536)
21062 #endif
21063  for (int c = c0; c<(int)sc; c+=spectrum())
21064  for (int z = z0; z<(int)sz; z+=depth())
21065  for (int y = y0; y<(int)sy; y+=height())
21066  for (int x = x0; x<(int)sx; x+=width())
21067  res.draw_image(x,y,z,c,*this);
21068  } break;
21069  case 1 : { // Neumann borders.
21070  res.assign(sx,sy,sz,sc).draw_image(xc,yc,zc,cc,*this);
21071  CImg<T> sprite;
21072  if (xc>0) { // X-backward
21073  res.get_crop(xc,yc,zc,cc,xc,yc+height()-1,zc+depth()-1,cc+spectrum()-1).move_to(sprite);
21074  for (int x = xc-1; x>=0; --x) res.draw_image(x,yc,zc,cc,sprite);
21075  }
21076  if (xc+width()<(int)sx) { // X-forward
21077  res.get_crop(xc+width()-1,yc,zc,cc,xc+width()-1,yc+height()-1,zc+depth()-1,cc+spectrum()-1).move_to(sprite);
21078  for (int x = xc+width(); x<(int)sx; ++x) res.draw_image(x,yc,zc,cc,sprite);
21079  }
21080  if (yc>0) { // Y-backward
21081  res.get_crop(0,yc,zc,cc,sx-1,yc,zc+depth()-1,cc+spectrum()-1).move_to(sprite);
21082  for (int y = yc-1; y>=0; --y) res.draw_image(0,y,zc,cc,sprite);
21083  }
21084  if (yc+height()<(int)sy) { // Y-forward
21085  res.get_crop(0,yc+height()-1,zc,cc,sx-1,yc+height()-1,zc+depth()-1,cc+spectrum()-1).move_to(sprite);
21086  for (int y = yc+height(); y<(int)sy; ++y) res.draw_image(0,y,zc,cc,sprite);
21087  }
21088  if (zc>0) { // Z-backward
21089  res.get_crop(0,0,zc,cc,sx-1,sy-1,zc,cc+spectrum()-1).move_to(sprite);
21090  for (int z = zc-1; z>=0; --z) res.draw_image(0,0,z,cc,sprite);
21091  }
21092  if (zc+depth()<(int)sz) { // Z-forward
21093  res.get_crop(0,0,zc+depth()-1,cc,sx-1,sy-1,zc+depth()-1,cc+spectrum()-1).move_to(sprite);
21094  for (int z = zc+depth(); z<(int)sz; ++z) res.draw_image(0,0,z,cc,sprite);
21095  }
21096  if (cc>0) { // C-backward
21097  res.get_crop(0,0,0,cc,sx-1,sy-1,sz-1,cc).move_to(sprite);
21098  for (int c = cc-1; c>=0; --c) res.draw_image(0,0,0,c,sprite);
21099  }
21100  if (cc+spectrum()<(int)sc) { // C-forward
21101  res.get_crop(0,0,0,cc+spectrum()-1,sx-1,sy-1,sz-1,cc+spectrum()-1).move_to(sprite);
21102  for (int c = cc+spectrum(); c<(int)sc; ++c) res.draw_image(0,0,0,c,sprite);
21103  }
21104  } break;
21105  default : // Dirichlet borders.
21106  res.assign(sx,sy,sz,sc,0).draw_image(xc,yc,zc,cc,*this);
21107  }
21108  break;
21109  } break;
21110 
21111  // Nearest neighbor interpolation.
21112  //
21113  case 1 : {
21114  res.assign(sx,sy,sz,sc);
21115  CImg<ulongT> off_x(sx), off_y(sy+1), off_z(sz+1), off_c(sc+1);
21116  const unsigned long
21117  wh = (unsigned long)_width*_height,
21118  whd = (unsigned long)_width*_height*_depth,
21119  sxy = (unsigned long)sx*sy,
21120  sxyz = (unsigned long)sx*sy*sz;
21121  if (sx==_width) off_x.fill(1);
21122  else {
21123  unsigned long *poff_x = off_x._data, curr = 0;
21124  cimg_forX(res,x) { const unsigned long old = curr; curr = ((x+1LU)*_width/sx); *(poff_x++) = curr - old; }
21125  }
21126  if (sy==_height) off_y.fill(_width);
21127  else {
21128  unsigned long *poff_y = off_y._data, curr = 0;
21129  cimg_forY(res,y) {
21130  const unsigned long old = curr;
21131  curr = ((y+1LU)*_height/sy);
21132  *(poff_y++) = _width*(curr - old);
21133  }
21134  *poff_y = 0;
21135  }
21136  if (sz==_depth) off_z.fill(wh);
21137  else {
21138  unsigned long *poff_z = off_z._data, curr = 0;
21139  cimg_forZ(res,z) {
21140  const unsigned long old = curr;
21141  curr = ((z+1LU)*_depth/sz);
21142  *(poff_z++) = wh*(curr - old);
21143  }
21144  *poff_z = 0;
21145  }
21146  if (sc==_spectrum) off_c.fill(whd);
21147  else {
21148  unsigned long *poff_c = off_c._data, curr = 0;
21149  cimg_forC(res,c) {
21150  const unsigned long old = curr;
21151  curr = ((c+1LU)*_spectrum/sc);
21152  *(poff_c++) = whd*(curr - old);
21153  }
21154  *poff_c = 0;
21155  }
21156 
21157  T *ptrd = res._data;
21158  const T* ptrc = _data;
21159  const unsigned long *poff_c = off_c._data;
21160  for (unsigned int c = 0; c<sc; ) {
21161  const T *ptrz = ptrc;
21162  const unsigned long *poff_z = off_z._data;
21163  for (unsigned int z = 0; z<sz; ) {
21164  const T *ptry = ptrz;
21165  const unsigned long *poff_y = off_y._data;
21166  for (unsigned int y = 0; y<sy; ) {
21167  const T *ptrx = ptry;
21168  const unsigned long *poff_x = off_x._data;
21169  cimg_forX(res,x) { *(ptrd++) = *ptrx; ptrx+=*(poff_x++); }
21170  ++y;
21171  unsigned long dy = *(poff_y++);
21172  for (;!dy && y<dy; std::memcpy(ptrd,ptrd - sx,sizeof(T)*sx), ++y, ptrd+=sx, dy = *(poff_y++)) {}
21173  ptry+=dy;
21174  }
21175  ++z;
21176  unsigned long dz = *(poff_z++);
21177  for (;!dz && z<dz; std::memcpy(ptrd,ptrd-sxy,sizeof(T)*sxy), ++z, ptrd+=sxy, dz = *(poff_z++)) {}
21178  ptrz+=dz;
21179  }
21180  ++c;
21181  unsigned long dc = *(poff_c++);
21182  for (;!dc && c<dc; std::memcpy(ptrd,ptrd-sxyz,sizeof(T)*sxyz), ++c, ptrd+=sxyz, dc = *(poff_c++)) {}
21183  ptrc+=dc;
21184  }
21185  } break;
21186 
21187  // Moving average.
21188  //
21189  case 2 : {
21190  bool instance_first = true;
21191  if (sx!=_width) {
21192  CImg<Tfloat> tmp(sx,_height,_depth,_spectrum,0);
21193  for (unsigned int a = _width*sx, b = _width, c = sx, s = 0, t = 0; a; ) {
21194  const unsigned int d = cimg::min(b,c);
21195  a-=d; b-=d; c-=d;
21196  cimg_forYZC(tmp,y,z,v) tmp(t,y,z,v)+=(Tfloat)(*this)(s,y,z,v)*d;
21197  if (!b) {
21198  cimg_forYZC(tmp,y,z,v) tmp(t,y,z,v)/=_width;
21199  ++t;
21200  b = _width;
21201  }
21202  if (!c) { ++s; c = sx; }
21203  }
21204  tmp.move_to(res);
21205  instance_first = false;
21206  }
21207  if (sy!=_height) {
21208  CImg<Tfloat> tmp(sx,sy,_depth,_spectrum,0);
21209  for (unsigned int a = _height*sy, b = _height, c = sy, s = 0, t = 0; a; ) {
21210  const unsigned int d = cimg::min(b,c);
21211  a-=d; b-=d; c-=d;
21212  if (instance_first)
21213  cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)(*this)(x,s,z,v)*d;
21214  else
21215  cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)res(x,s,z,v)*d;
21216  if (!b) {
21217  cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)/=_height;
21218  ++t;
21219  b = _height;
21220  }
21221  if (!c) { ++s; c = sy; }
21222  }
21223  tmp.move_to(res);
21224  instance_first = false;
21225  }
21226  if (sz!=_depth) {
21227  CImg<Tfloat> tmp(sx,sy,sz,_spectrum,0);
21228  for (unsigned int a = _depth*sz, b = _depth, c = sz, s = 0, t = 0; a; ) {
21229  const unsigned int d = cimg::min(b,c);
21230  a-=d; b-=d; c-=d;
21231  if (instance_first)
21232  cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)(*this)(x,y,s,v)*d;
21233  else
21234  cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)res(x,y,s,v)*d;
21235  if (!b) {
21236  cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)/=_depth;
21237  ++t;
21238  b = _depth;
21239  }
21240  if (!c) { ++s; c = sz; }
21241  }
21242  tmp.move_to(res);
21243  instance_first = false;
21244  }
21245  if (sc!=_spectrum) {
21246  CImg<Tfloat> tmp(sx,sy,sz,sc,0);
21247  for (unsigned int a = _spectrum*sc, b = _spectrum, c = sc, s = 0, t = 0; a; ) {
21248  const unsigned int d = cimg::min(b,c);
21249  a-=d; b-=d; c-=d;
21250  if (instance_first)
21251  cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(Tfloat)(*this)(x,y,z,s)*d;
21252  else
21253  cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(Tfloat)res(x,y,z,s)*d;
21254  if (!b) {
21255  cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)/=_spectrum;
21256  ++t;
21257  b = _spectrum;
21258  }
21259  if (!c) { ++s; c = sc; }
21260  }
21261  tmp.move_to(res);
21262  instance_first = false;
21263  }
21264  } break;
21265 
21266  // Linear interpolation.
21267  //
21268  case 3 : {
21269  CImg<uintT> off(cimg::max(sx,sy,sz,sc));
21270  CImg<floatT> foff(off._width);
21271  CImg<T> resx, resy, resz, resc;
21272 
21273  if (sx!=_width) {
21274  if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
21275  else {
21276  if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx);
21277  else {
21278  const float fx = (!boundary_conditions && sx>_width)?(sx>1?(_width-1.0f)/(sx-1):0):(float)_width/sx;
21279  resx.assign(sx,_height,_depth,_spectrum);
21280  float curr = 0, old = 0;
21281  unsigned int *poff = off._data;
21282  float *pfoff = foff._data;
21283  cimg_forX(resx,x) {
21284  *(pfoff++) = curr - (unsigned int)curr;
21285  old = curr;
21286  curr+=fx;
21287  *(poff++) = (unsigned int)curr - (unsigned int)old;
21288  }
21289 #ifdef cimg_use_openmp
21290 #pragma omp parallel for collapse(3) if (resx.size()>=65536)
21291 #endif
21292  cimg_forYZC(resx,y,z,c) {
21293  const T *ptrs = data(0,y,z,c), *const ptrsmax = ptrs + (_width-1);
21294  T *ptrd = resx.data(0,y,z,c);
21295  const unsigned int *poff = off._data;
21296  const float *pfoff = foff._data;
21297  cimg_forX(resx,x) {
21298  const float alpha = *(pfoff++);
21299  const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs+1):val1;
21300  *(ptrd++) = (T)((1-alpha)*val1 + alpha*val2);
21301  ptrs+=*(poff++);
21302  }
21303  }
21304  }
21305  }
21306  } else resx.assign(*this,true);
21307 
21308  if (sy!=_height) {
21309  if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
21310  else {
21311  if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy);
21312  else {
21313  const float fy = (!boundary_conditions && sy>_height)?(sy>1?(_height-1.0f)/(sy-1):0):(float)_height/sy;
21314  resy.assign(sx,sy,_depth,_spectrum);
21315  float curr = 0, old = 0;
21316  unsigned int *poff = off._data;
21317  float *pfoff = foff._data;
21318  cimg_forY(resy,y) {
21319  *(pfoff++) = curr - (unsigned int)curr;
21320  old = curr;
21321  curr+=fy;
21322  *(poff++) = sx*((unsigned int)curr-(unsigned int)old);
21323  }
21324 #ifdef cimg_use_openmp
21325 #pragma omp parallel for collapse(3) if (resy.size()>=65536)
21326 #endif
21327  cimg_forXZC(resy,x,z,c) {
21328  const T *ptrs = resx.data(x,0,z,c), *const ptrsmax = ptrs + (_height-1)*sx;
21329  T *ptrd = resy.data(x,0,z,c);
21330  const unsigned int *poff = off._data;
21331  const float *pfoff = foff._data;
21332  cimg_forY(resy,y) {
21333  const float alpha = *(pfoff++);
21334  const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs+sx):val1;
21335  *ptrd = (T)((1-alpha)*val1 + alpha*val2);
21336  ptrd+=sx;
21337  ptrs+=*(poff++);
21338  }
21339  }
21340  }
21341  }
21342  resx.assign();
21343  } else resy.assign(resx,true);
21344 
21345  if (sz!=_depth) {
21346  if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
21347  else {
21348  if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz);
21349  else {
21350  const float fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth-1.0f)/(sz-1):0):(float)_depth/sz;
21351  const unsigned int sxy = sx*sy;
21352  resz.assign(sx,sy,sz,_spectrum);
21353  float curr = 0, old = 0;
21354  unsigned int *poff = off._data;
21355  float *pfoff = foff._data;
21356  cimg_forZ(resz,z) {
21357  *(pfoff++) = curr - (unsigned int)curr;
21358  old = curr;
21359  curr+=fz;
21360  *(poff++) = sxy*((unsigned int)curr - (unsigned int)old);
21361  }
21362 #ifdef cimg_use_openmp
21363 #pragma omp parallel for collapse(3) if (resz.size()>=65536)
21364 #endif
21365  cimg_forXYC(resz,x,y,c) {
21366  const T *ptrs = resy.data(x,y,0,c), *const ptrsmax = ptrs + (_depth-1)*sxy;
21367  T *ptrd = resz.data(x,y,0,c);
21368  const unsigned int *poff = off._data;
21369  const float *pfoff = foff._data;
21370  cimg_forZ(resz,z) {
21371  const float alpha = *(pfoff++);
21372  const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs+sxy):val1;
21373  *ptrd = (T)((1-alpha)*val1 + alpha*val2);
21374  ptrd+=sxy;
21375  ptrs+=*(poff++);
21376  }
21377  }
21378  }
21379  }
21380  resy.assign();
21381  } else resz.assign(resy,true);
21382 
21383  if (sc!=_spectrum) {
21384  if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
21385  else {
21386  if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc);
21387  else {
21388  const float fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum-1.0f)/(sc-1):0):
21389  (float)_spectrum/sc;
21390  const unsigned int sxyz = sx*sy*sz;
21391  resc.assign(sx,sy,sz,sc);
21392  float curr = 0, old = 0;
21393  unsigned int *poff = off._data;
21394  float *pfoff = foff._data;
21395  cimg_forC(resc,c) {
21396  *(pfoff++) = curr - (unsigned int)curr;
21397  old = curr;
21398  curr+=fc;
21399  *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old);
21400  }
21401 #ifdef cimg_use_openmp
21402 #pragma omp parallel for collapse(3) if (resc.size()>=65536)
21403 #endif
21404  cimg_forXYZ(resc,x,y,z) {
21405  const T *ptrs = resz.data(x,y,z,0), *const ptrsmax = ptrs + (_spectrum-1)*sxyz;
21406  T *ptrd = resc.data(x,y,z,0);
21407  const unsigned int *poff = off._data;
21408  const float *pfoff = foff._data;
21409  cimg_forC(resc,c) {
21410  const float alpha = *(pfoff++);
21411  const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs+sxyz):val1;
21412  *ptrd = (T)((1-alpha)*val1 + alpha*val2);
21413  ptrd+=sxyz;
21414  ptrs+=*(poff++);
21415  }
21416  }
21417  }
21418  }
21419  resz.assign();
21420  } else resc.assign(resz,true);
21421  return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
21422  } break;
21423 
21424  // Grid interpolation.
21425  //
21426  case 4 : {
21427  CImg<T> resx, resy, resz, resc;
21428  if (sx!=_width) {
21429  if (sx<_width) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
21430  else {
21431  resx.assign(sx,_height,_depth,_spectrum,0);
21432  const int dx = sx*2, dy = width()*2;
21433  int err = (int)(dy + centering_x*(sx*dy/width() - dy)), xs = 0;
21434  cimg_forX(resx,x) if ((err-=dy)<=0) {
21435  cimg_forYZC(resx,y,z,c) resx(x,y,z,c) = (*this)(xs,y,z,c);
21436  ++xs;
21437  err+=dx;
21438  }
21439  }
21440  } else resx.assign(*this,true);
21441 
21442  if (sy!=_height) {
21443  if (sy<_height) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
21444  else {
21445  resy.assign(sx,sy,_depth,_spectrum,0);
21446  const int dx = sy*2, dy = height()*2;
21447  int err = (int)(dy + centering_y*(sy*dy/height() - dy)), ys = 0;
21448  cimg_forY(resy,y) if ((err-=dy)<=0) {
21449  cimg_forXZC(resy,x,z,c) resy(x,y,z,c) = resx(x,ys,z,c);
21450  ++ys;
21451  err+=dx;
21452  }
21453  }
21454  resx.assign();
21455  } else resy.assign(resx,true);
21456 
21457  if (sz!=_depth) {
21458  if (sz<_depth) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
21459  else {
21460  resz.assign(sx,sy,sz,_spectrum,0);
21461  const int dx = sz*2, dy = depth()*2;
21462  int err = (int)(dy + centering_z*(sz*dy/depth() - dy)), zs = 0;
21463  cimg_forZ(resz,z) if ((err-=dy)<=0) {
21464  cimg_forXYC(resz,x,y,c) resz(x,y,z,c) = resy(x,y,zs,c);
21465  ++zs;
21466  err+=dx;
21467  }
21468  }
21469  resy.assign();
21470  } else resz.assign(resy,true);
21471 
21472  if (sc!=_spectrum) {
21473  if (sc<_spectrum) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
21474  else {
21475  resc.assign(sx,sy,sz,sc,0);
21476  const int dx = sc*2, dy = spectrum()*2;
21477  int err = (int)(dy + centering_c*(sc*dy/spectrum() - dy)), cs = 0;
21478  cimg_forC(resc,c) if ((err-=dy)<=0) {
21479  cimg_forXYZ(resc,x,y,z) resc(x,y,z,c) = resz(x,y,z,cs);
21480  ++cs;
21481  err+=dx;
21482  }
21483  }
21484  resz.assign();
21485  } else resc.assign(resz,true);
21486 
21487  return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
21488  } break;
21489 
21490  // Cubic interpolation.
21491  //
21492  case 5 : {
21493  const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
21494  CImg<uintT> off(cimg::max(sx,sy,sz,sc));
21495  CImg<floatT> foff(off._width);
21496  CImg<T> resx, resy, resz, resc;
21497 
21498  if (sx!=_width) {
21499  if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
21500  else {
21501  if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx);
21502  else {
21503  const float fx = (!boundary_conditions && sx>_width)?(sx>1?(_width-1.0f)/(sx-1):0):(float)_width/sx;
21504  resx.assign(sx,_height,_depth,_spectrum);
21505  float curr = 0, old = 0;
21506  unsigned int *poff = off._data;
21507  float *pfoff = foff._data;
21508  cimg_forX(resx,x) {
21509  *(pfoff++) = curr - (unsigned int)curr;
21510  old = curr;
21511  curr+=fx;
21512  *(poff++) = (unsigned int)curr - (unsigned int)old;
21513  }
21514 #ifdef cimg_use_openmp
21515 #pragma omp parallel for collapse(3) if (resx.size()>=65536)
21516 #endif
21517  cimg_forYZC(resx,y,z,c) {
21518  const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_width-2);
21519  T *ptrd = resx.data(0,y,z,c);
21520  const unsigned int *poff = off._data;
21521  const float *pfoff = foff._data;
21522  cimg_forX(resx,x) {
21523  const float t = *(pfoff++);
21524  const Tfloat
21525  val1 = (Tfloat)*ptrs,
21526  val0 = ptrs>ptrs0?(Tfloat)*(ptrs-1):val1,
21527  val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+1):val1,
21528  val3 = ptrs<ptrsmax?(Tfloat)*(ptrs+2):val2,
21529  val = val1 + 0.5f*(t*(-val0+val2) + t*t*(2*val0-5*val1+4*val2-val3) +
21530  t*t*t*(-val0+3*val1-3*val2+val3));
21531  *(ptrd++) = (T)(val<vmin?vmin:val>vmax?vmax:val);
21532  ptrs+=*(poff++);
21533  }
21534  }
21535  }
21536  }
21537  } else resx.assign(*this,true);
21538 
21539  if (sy!=_height) {
21540  if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
21541  else {
21542  if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy);
21543  else {
21544  const float fy = (!boundary_conditions && sy>_height)?(sy>1?(_height-1.0f)/(sy-1):0):(float)_height/sy;
21545  resy.assign(sx,sy,_depth,_spectrum);
21546  float curr = 0, old = 0;
21547  unsigned int *poff = off._data;
21548  float *pfoff = foff._data;
21549  cimg_forY(resy,y) {
21550  *(pfoff++) = curr - (unsigned int)curr;
21551  old = curr;
21552  curr+=fy;
21553  *(poff++) = sx*((unsigned int)curr-(unsigned int)old);
21554  }
21555 #ifdef cimg_use_openmp
21556 #pragma omp parallel for collapse(3) if (resy.size()>=65536)
21557 #endif
21558  cimg_forXZC(resy,x,z,c) {
21559  const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_height-2)*sx;
21560  T *ptrd = resy.data(x,0,z,c);
21561  const unsigned int *poff = off._data;
21562  const float *pfoff = foff._data;
21563  cimg_forY(resy,y) {
21564  const float t = *(pfoff++);
21565  const Tfloat
21566  val1 = (Tfloat)*ptrs,
21567  val0 = ptrs>ptrs0?(Tfloat)*(ptrs-sx):val1,
21568  val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sx):val1,
21569  val3 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sx):val2,
21570  val = val1 + 0.5f*(t*(-val0+val2) + t*t*(2*val0-5*val1+4*val2-val3) +
21571  t*t*t*(-val0+3*val1-3*val2+val3));
21572  *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
21573  ptrd+=sx;
21574  ptrs+=*(poff++);
21575  }
21576  }
21577  }
21578  }
21579  resx.assign();
21580  } else resy.assign(resx,true);
21581 
21582  if (sz!=_depth) {
21583  if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
21584  else {
21585  if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz);
21586  else {
21587  const float fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth-1.0f)/(sz-1):0):(float)_depth/sz;
21588  const unsigned int sxy = sx*sy;
21589  resz.assign(sx,sy,sz,_spectrum);
21590  float curr = 0, old = 0;
21591  unsigned int *poff = off._data;
21592  float *pfoff = foff._data;
21593  cimg_forZ(resz,z) {
21594  *(pfoff++) = curr - (unsigned int)curr;
21595  old = curr;
21596  curr+=fz;
21597  *(poff++) = sxy*((unsigned int)curr - (unsigned int)old);
21598  }
21599 #ifdef cimg_use_openmp
21600 #pragma omp parallel for collapse(3) if (resz.size()>=65536)
21601 #endif
21602  cimg_forXYC(resz,x,y,c) {
21603  const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_depth-2)*sxy;
21604  T *ptrd = resz.data(x,y,0,c);
21605  const unsigned int *poff = off._data;
21606  const float *pfoff = foff._data;
21607  cimg_forZ(resz,z) {
21608  const float t = *(pfoff++);
21609  const Tfloat
21610  val1 = (Tfloat)*ptrs,
21611  val0 = ptrs>ptrs0?(Tfloat)*(ptrs-sxy):val1,
21612  val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxy):val1,
21613  val3 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sxy):val2,
21614  val = val1 + 0.5f*(t*(-val0+val2) + t*t*(2*val0-5*val1+4*val2-val3) +
21615  t*t*t*(-val0+3*val1-3*val2+val3));
21616  *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
21617  ptrd+=sxy;
21618  ptrs+=*(poff++);
21619  }
21620  }
21621  }
21622  }
21623  resy.assign();
21624  } else resz.assign(resy,true);
21625 
21626  if (sc!=_spectrum) {
21627  if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
21628  else {
21629  if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc);
21630  else {
21631  const float fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum-1.0f)/(sc-1):0):
21632  (float)_spectrum/sc;
21633  const unsigned int sxyz = sx*sy*sz;
21634  resc.assign(sx,sy,sz,sc);
21635  float curr = 0, old = 0;
21636  unsigned int *poff = off._data;
21637  float *pfoff = foff._data;
21638  cimg_forC(resc,c) {
21639  *(pfoff++) = curr - (unsigned int)curr;
21640  old = curr;
21641  curr+=fc;
21642  *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old);
21643  }
21644 #ifdef cimg_use_openmp
21645 #pragma omp parallel for collapse(3) if (resc.size()>=65536)
21646 #endif
21647  cimg_forXYZ(resc,x,y,z) {
21648  const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmax = ptrs + (_spectrum-2)*sxyz;
21649  T *ptrd = resc.data(x,y,z,0);
21650  const unsigned int *poff = off._data;
21651  const float *pfoff = foff._data;
21652  cimg_forC(resc,c) {
21653  const float t = *(pfoff++);
21654  const Tfloat
21655  val1 = (Tfloat)*ptrs,
21656  val0 = ptrs>ptrs0?(Tfloat)*(ptrs-sxyz):val1,
21657  val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxyz):val1,
21658  val3 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sxyz):val2,
21659  val = val1 + 0.5f*(t*(-val0+val2) + t*t*(2*val0-5*val1+4*val2-val3) +
21660  t*t*t*(-val0+3*val1-3*val2+val3));
21661  *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
21662  ptrd+=sxyz;
21663  ptrs+=*(poff++);
21664  }
21665  }
21666  }
21667  }
21668  resz.assign();
21669  } else resc.assign(resz,true);
21670 
21671  return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
21672  } break;
21673 
21674  // Lanczos interpolation.
21675  //
21676  case 6 : {
21677  const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
21678  CImg<uintT> off(cimg::max(sx,sy,sz,sc));
21679  CImg<floatT> foff(off._width);
21680  CImg<T> resx, resy, resz, resc;
21681 
21682  if (sx!=_width) {
21683  if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
21684  else {
21685  if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx);
21686  else {
21687  const float fx = (!boundary_conditions && sx>_width)?(sx>1?(_width-1.0f)/(sx-1):0):(float)_width/sx;
21688  resx.assign(sx,_height,_depth,_spectrum);
21689  float curr = 0, old = 0;
21690  unsigned int *poff = off._data;
21691  float *pfoff = foff._data;
21692  cimg_forX(resx,x) {
21693  *(pfoff++) = curr - (unsigned int)curr;
21694  old = curr;
21695  curr+=fx;
21696  *(poff++) = (unsigned int)curr - (unsigned int)old;
21697  }
21698 #ifdef cimg_use_openmp
21699 #pragma omp parallel for collapse(3) if (resx.size()>=65536)
21700 #endif
21701  cimg_forYZC(resx,y,z,c) {
21702  const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + 1,
21703  *const ptrsmax = ptrs0 + (_width-2);
21704  T *ptrd = resx.data(0,y,z,c);
21705  const unsigned int *poff = off._data;
21706  const float *pfoff = foff._data;
21707  cimg_forX(resx,x) {
21708  const float
21709  t = *(pfoff++),
21710  w0 = _cimg_lanczos(t+2),
21711  w1 = _cimg_lanczos(t+1),
21712  w2 = _cimg_lanczos(t),
21713  w3 = _cimg_lanczos(t-1),
21714  w4 = _cimg_lanczos(t-2);
21715  const Tfloat
21716  val2 = (Tfloat)*ptrs,
21717  val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-1):val2,
21718  val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2):val1,
21719  val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+1):val2,
21720  val4 = ptrs<ptrsmax?(Tfloat)*(ptrs+2):val3,
21721  val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
21722  *(ptrd++) = (T)(val<vmin?vmin:val>vmax?vmax:val);
21723  ptrs+=*(poff++);
21724  }
21725  }
21726  }
21727  }
21728  } else resx.assign(*this,true);
21729 
21730  if (sy!=_height) {
21731  if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
21732  else {
21733  if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy);
21734  else {
21735  const float fy = (!boundary_conditions && sy>_height)?(sy>1?(_height-1.0f)/(sy-1):0):(float)_height/sy;
21736  resy.assign(sx,sy,_depth,_spectrum);
21737  float curr = 0, old = 0;
21738  unsigned int *poff = off._data;
21739  float *pfoff = foff._data;
21740  cimg_forY(resy,y) {
21741  *(pfoff++) = curr - (unsigned int)curr;
21742  old = curr;
21743  curr+=fy;
21744  *(poff++) = sx*((unsigned int)curr-(unsigned int)old);
21745  }
21746 #ifdef cimg_use_openmp
21747 #pragma omp parallel for collapse(3) if (resy.size()>=65536)
21748 #endif
21749  cimg_forXZC(resy,x,z,c) {
21750  const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sx,
21751  *const ptrsmax = ptrs0 + (_height-2)*sx;
21752  T *ptrd = resy.data(x,0,z,c);
21753  const unsigned int *poff = off._data;
21754  const float *pfoff = foff._data;
21755  cimg_forY(resy,y) {
21756  const float
21757  t = *(pfoff++),
21758  w0 = _cimg_lanczos(t+2),
21759  w1 = _cimg_lanczos(t+1),
21760  w2 = _cimg_lanczos(t),
21761  w3 = _cimg_lanczos(t-1),
21762  w4 = _cimg_lanczos(t-2);
21763  const Tfloat
21764  val2 = (Tfloat)*ptrs,
21765  val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-sx):val2,
21766  val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2*sx):val1,
21767  val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sx):val2,
21768  val4 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sx):val3,
21769  val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
21770  *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
21771  ptrd+=sx;
21772  ptrs+=*(poff++);
21773  }
21774  }
21775  }
21776  }
21777  resx.assign();
21778  } else resy.assign(resx,true);
21779 
21780  if (sz!=_depth) {
21781  if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
21782  else {
21783  if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz);
21784  else {
21785  const float fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth-1.0f)/(sz-1):0):(float)_depth/sz;
21786  const unsigned int sxy = sx*sy;
21787  resz.assign(sx,sy,sz,_spectrum);
21788  float curr = 0, old = 0;
21789  unsigned int *poff = off._data;
21790  float *pfoff = foff._data;
21791  cimg_forZ(resz,z) {
21792  *(pfoff++) = curr - (unsigned int)curr;
21793  old = curr;
21794  curr+=fz;
21795  *(poff++) = sxy*((unsigned int)curr - (unsigned int)old);
21796  }
21797 #ifdef cimg_use_openmp
21798 #pragma omp parallel for collapse(3) if (resz.size()>=65536)
21799 #endif
21800  cimg_forXYC(resz,x,y,c) {
21801  const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxy,
21802  *const ptrsmax = ptrs0 + (_depth-2)*sxy;
21803  T *ptrd = resz.data(x,y,0,c);
21804  const unsigned int *poff = off._data;
21805  const float *pfoff = foff._data;
21806  cimg_forZ(resz,z) {
21807  const float
21808  t = *(pfoff++),
21809  w0 = _cimg_lanczos(t+2),
21810  w1 = _cimg_lanczos(t+1),
21811  w2 = _cimg_lanczos(t),
21812  w3 = _cimg_lanczos(t-1),
21813  w4 = _cimg_lanczos(t-2);
21814  const Tfloat
21815  val2 = (Tfloat)*ptrs,
21816  val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-sxy):val2,
21817  val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2*sxy):val1,
21818  val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxy):val2,
21819  val4 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sxy):val3,
21820  val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
21821  *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
21822  ptrd+=sxy;
21823  ptrs+=*(poff++);
21824  }
21825  }
21826  }
21827  }
21828  resy.assign();
21829  } else resz.assign(resy,true);
21830 
21831  if (sc!=_spectrum) {
21832  if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
21833  else {
21834  if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc);
21835  else {
21836  const float fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum-1.0f)/(sc-1):0):
21837  (float)_spectrum/sc;
21838  const unsigned int sxyz = sx*sy*sz;
21839  resc.assign(sx,sy,sz,sc);
21840  float curr = 0, old = 0;
21841  unsigned int *poff = off._data;
21842  float *pfoff = foff._data;
21843  cimg_forC(resc,c) {
21844  *(pfoff++) = curr - (unsigned int)curr;
21845  old = curr;
21846  curr+=fc;
21847  *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old);
21848  }
21849 #ifdef cimg_use_openmp
21850 #pragma omp parallel for collapse(3) if (resc.size()>=65536)
21851 #endif
21852  cimg_forXYZ(resc,x,y,z) {
21853  const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxyz,
21854  *const ptrsmax = ptrs + (_spectrum-2)*sxyz;
21855  T *ptrd = resc.data(x,y,z,0);
21856  const unsigned int *poff = off._data;
21857  const float *pfoff = foff._data;
21858  cimg_forC(resc,c) {
21859  const float
21860  t = *(pfoff++),
21861  w0 = _cimg_lanczos(t+2),
21862  w1 = _cimg_lanczos(t+1),
21863  w2 = _cimg_lanczos(t),
21864  w3 = _cimg_lanczos(t-1),
21865  w4 = _cimg_lanczos(t-2);
21866  const Tfloat
21867  val2 = (Tfloat)*ptrs,
21868  val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-sxyz):val2,
21869  val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2*sxyz):val1,
21870  val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxyz):val2,
21871  val4 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sxyz):val3,
21872  val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
21873  *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
21874  ptrd+=sxyz;
21875  ptrs+=*(poff++);
21876  }
21877  }
21878  }
21879  }
21880  resz.assign();
21881  } else resc.assign(resz,true);
21882 
21883  return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
21884  } break;
21885 
21886  // Unknow interpolation.
21887  //
21888  default :
21889  throw CImgArgumentException(_cimg_instance
21890  "resize(): Invalid specified interpolation %d "
21891  "(should be { -1=raw | 0=none | 1=nearest | 2=average | 3=linear | 4=grid | "
21892  "5=cubic | 6=lanczos }).",
21893  cimg_instance,
21894  interpolation_type);
21895  }
21896  return res;
21897  }
21898 
21900 
21909  template<typename t>
21910  CImg<T>& resize(const CImg<t>& src,
21911  const int interpolation_type=1, const unsigned int boundary_conditions=0,
21912  const float centering_x = 0, const float centering_y = 0,
21913  const float centering_z = 0, const float centering_c = 0) {
21914  return resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions,
21915  centering_x,centering_y,centering_z,centering_c);
21916  }
21917 
21919  template<typename t>
21921  const int interpolation_type=1, const unsigned int boundary_conditions=0,
21922  const float centering_x = 0, const float centering_y = 0,
21923  const float centering_z = 0, const float centering_c = 0) const {
21924  return get_resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions,
21925  centering_x,centering_y,centering_z,centering_c);
21926  }
21927 
21929 
21939  const int interpolation_type=1, const unsigned int boundary_conditions=0,
21940  const float centering_x = 0, const float centering_y = 0,
21941  const float centering_z = 0, const float centering_c = 0) {
21942  return resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions,
21943  centering_x,centering_y,centering_z,centering_c);
21944  }
21945 
21948  const int interpolation_type=1, const unsigned int boundary_conditions=0,
21949  const float centering_x = 0, const float centering_y = 0,
21950  const float centering_z = 0, const float centering_c = 0) const {
21951  return get_resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions,
21952  centering_x,centering_y,centering_z,centering_c);
21953  }
21954 
21957  return get_resize_halfXY().move_to(*this);
21958  }
21959 
21962  if (is_empty()) return *this;
21963  const Tfloat mask[9] = { 0.07842776544f, 0.1231940459f, 0.07842776544f,
21964  0.1231940459f, 0.1935127547f, 0.1231940459f,
21965  0.07842776544f, 0.1231940459f, 0.07842776544f };
21966  T I[9] = { 0 };
21967  CImg<T> res(_width/2,_height/2,_depth,_spectrum);
21968  T *ptrd = res._data;
21969  cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,T)
21970  if (x%2 && y%2) *(ptrd++) = (T)
21971  (I[0]*mask[0] + I[1]*mask[1] + I[2]*mask[2] +
21972  I[3]*mask[3] + I[4]*mask[4] + I[5]*mask[5] +
21973  I[6]*mask[6] + I[7]*mask[7] + I[8]*mask[8]);
21974  return res;
21975  }
21976 
21978 
21983  return get_resize_doubleXY().move_to(*this);
21984  }
21985 
21988 #define _cimg_gs2x_for3(bound,i) \
21989  for (int i = 0, _p1##i = 0, \
21990  _n1##i = 1>=(bound)?(int)(bound)-1:1; \
21991  _n1##i<(int)(bound) || i==--_n1##i; \
21992  _p1##i = i++, ++_n1##i, ptrd1+=(res)._width, ptrd2+=(res)._width)
21993 
21994 #define _cimg_gs2x_for3x3(img,x,y,z,c,I,T) \
21995  _cimg_gs2x_for3((img)._height,y) for (int x = 0, \
21996  _p1##x = 0, \
21997  _n1##x = (int)( \
21998  (I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
21999  (I[3] = I[4] = (T)(img)(0,y,z,c)), \
22000  (I[7] = (T)(img)(0,_n1##y,z,c)), \
22001  1>=(img)._width?(img).width()-1:1); \
22002  (_n1##x<(img).width() && ( \
22003  (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
22004  (I[5] = (T)(img)(_n1##x,y,z,c)), \
22005  (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
22006  x==--_n1##x; \
22007  I[1] = I[2], \
22008  I[3] = I[4], I[4] = I[5], \
22009  I[7] = I[8], \
22010  _p1##x = x++, ++_n1##x)
22011 
22012  if (is_empty()) return *this;
22013  CImg<T> res(_width<<1,_height<<1,_depth,_spectrum);
22014  CImg_3x3(I,T);
22015  cimg_forZC(*this,z,c) {
22016  T
22017  *ptrd1 = res.data(0,0,z,c),
22018  *ptrd2 = ptrd1 + res._width;
22019  _cimg_gs2x_for3x3(*this,x,y,z,c,I,T) {
22020  if (Icp!=Icn && Ipc!=Inc) {
22021  *(ptrd1++) = Ipc==Icp?Ipc:Icc;
22022  *(ptrd1++) = Icp==Inc?Inc:Icc;
22023  *(ptrd2++) = Ipc==Icn?Ipc:Icc;
22024  *(ptrd2++) = Icn==Inc?Inc:Icc;
22025  } else { *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc; }
22026  }
22027  }
22028  return res;
22029  }
22030 
22032 
22037  return get_resize_tripleXY().move_to(*this);
22038  }
22039 
22042 #define _cimg_gs3x_for3(bound,i) \
22043  for (int i = 0, _p1##i = 0, \
22044  _n1##i = 1>=(bound)?(int)(bound)-1:1; \
22045  _n1##i<(int)(bound) || i==--_n1##i; \
22046  _p1##i = i++, ++_n1##i, ptrd1+=2*(res)._width, ptrd2+=2*(res)._width, ptrd3+=2*(res)._width)
22047 
22048 #define _cimg_gs3x_for3x3(img,x,y,z,c,I,T) \
22049  _cimg_gs3x_for3((img)._height,y) for (int x = 0, \
22050  _p1##x = 0, \
22051  _n1##x = (int)( \
22052  (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
22053  (I[3] = I[4] = (T)(img)(0,y,z,c)), \
22054  (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)), \
22055  1>=(img)._width?(img).width()-1:1); \
22056  (_n1##x<(img).width() && ( \
22057  (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
22058  (I[5] = (T)(img)(_n1##x,y,z,c)), \
22059  (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
22060  x==--_n1##x; \
22061  I[0] = I[1], I[1] = I[2], \
22062  I[3] = I[4], I[4] = I[5], \
22063  I[6] = I[7], I[7] = I[8], \
22064  _p1##x = x++, ++_n1##x)
22065 
22066  if (is_empty()) return *this;
22067  CImg<T> res(3*_width,3*_height,_depth,_spectrum);
22068  CImg_3x3(I,T);
22069  cimg_forZC(*this,z,c) {
22070  T
22071  *ptrd1 = res.data(0,0,z,c),
22072  *ptrd2 = ptrd1 + res._width,
22073  *ptrd3 = ptrd2 + res._width;
22074  _cimg_gs3x_for3x3(*this,x,y,z,c,I,T) {
22075  if (Icp != Icn && Ipc != Inc) {
22076  *(ptrd1++) = Ipc==Icp?Ipc:Icc;
22077  *(ptrd1++) = (Ipc==Icp && Icc!=Inp) || (Icp==Inc && Icc!=Ipp)?Icp:Icc;
22078  *(ptrd1++) = Icp==Inc?Inc:Icc;
22079  *(ptrd2++) = (Ipc==Icp && Icc!=Ipn) || (Ipc==Icn && Icc!=Ipp)?Ipc:Icc;
22080  *(ptrd2++) = Icc;
22081  *(ptrd2++) = (Icp==Inc && Icc!=Inn) || (Icn==Inc && Icc!=Inp)?Inc:Icc;
22082  *(ptrd3++) = Ipc==Icn?Ipc:Icc;
22083  *(ptrd3++) = (Ipc==Icn && Icc!=Inn) || (Icn==Inc && Icc!=Ipn)?Icn:Icc;
22084  *(ptrd3++) = Icn==Inc?Inc:Icc;
22085  } else {
22086  *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd1++) = Icc;
22087  *(ptrd2++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc;
22088  *(ptrd3++) = Icc; *(ptrd3++) = Icc; *(ptrd3++) = Icc;
22089  }
22090  }
22091  }
22092  return res;
22093  }
22094 
22096 
22099  CImg<T>& mirror(const char axis) {
22100  if (is_empty()) return *this;
22101  T *pf, *pb, *buf = 0;
22102  switch (cimg::uncase(axis)) {
22103  case 'x' : {
22104  pf = _data; pb = data(_width-1);
22105  const unsigned int width2 = _width/2;
22106  for (unsigned int yzv = 0; yzv<_height*_depth*_spectrum; ++yzv) {
22107  for (unsigned int x = 0; x<width2; ++x) { const T val = *pf; *(pf++) = *pb; *(pb--) = val; }
22108  pf+=_width - width2;
22109  pb+=_width + width2;
22110  }
22111  } break;
22112  case 'y' : {
22113  buf = new T[_width];
22114  pf = _data; pb = data(0,_height-1);
22115  const unsigned int height2 = _height/2;
22116  for (unsigned int zv = 0; zv<_depth*_spectrum; ++zv) {
22117  for (unsigned int y = 0; y<height2; ++y) {
22118  std::memcpy(buf,pf,_width*sizeof(T));
22119  std::memcpy(pf,pb,_width*sizeof(T));
22120  std::memcpy(pb,buf,_width*sizeof(T));
22121  pf+=_width;
22122  pb-=_width;
22123  }
22124  pf+=(unsigned long)_width*(_height - height2);
22125  pb+=(unsigned long)_width*(_height + height2);
22126  }
22127  } break;
22128  case 'z' : {
22129  buf = new T[(unsigned long)_width*_height];
22130  pf = _data; pb = data(0,0,_depth-1);
22131  const unsigned int depth2 = _depth/2;
22132  cimg_forC(*this,c) {
22133  for (unsigned int z = 0; z<depth2; ++z) {
22134  std::memcpy(buf,pf,_width*_height*sizeof(T));
22135  std::memcpy(pf,pb,_width*_height*sizeof(T));
22136  std::memcpy(pb,buf,_width*_height*sizeof(T));
22137  pf+=(unsigned long)_width*_height;
22138  pb-=(unsigned long)_width*_height;
22139  }
22140  pf+=(unsigned long)_width*_height*(_depth - depth2);
22141  pb+=(unsigned long)_width*_height*(_depth + depth2);
22142  }
22143  } break;
22144  case 'c' : {
22145  buf = new T[(unsigned long)_width*_height*_depth];
22146  pf = _data; pb = data(0,0,0,_spectrum-1);
22147  const unsigned int _spectrum2 = _spectrum/2;
22148  for (unsigned int v = 0; v<_spectrum2; ++v) {
22149  std::memcpy(buf,pf,_width*_height*_depth*sizeof(T));
22150  std::memcpy(pf,pb,_width*_height*_depth*sizeof(T));
22151  std::memcpy(pb,buf,_width*_height*_depth*sizeof(T));
22152  pf+=(unsigned long)_width*_height*_depth;
22153  pb-=(unsigned long)_width*_height*_depth;
22154  }
22155  } break;
22156  default :
22157  throw CImgArgumentException(_cimg_instance
22158  "mirror(): Invalid specified axis '%c'.",
22159  cimg_instance,
22160  axis);
22161  }
22162  delete[] buf;
22163  return *this;
22164  }
22165 
22167  CImg<T> get_mirror(const char axis) const {
22168  return (+*this).mirror(axis);
22169  }
22170 
22172 
22176  CImg<T>& mirror(const char *const axes) {
22177  for (const char *s = axes; *s; s++) mirror(*s);
22178  return *this;
22179  }
22180 
22182  CImg<T> get_mirror(const char *const axes) const {
22183  return (+*this).mirror(axes);
22184  }
22185 
22187 
22199  CImg<T>& shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0,
22200  const int boundary_conditions=0) {
22201  if (is_empty()) return *this;
22202  if (delta_x) // Shift along X-axis
22203  switch (boundary_conditions) {
22204  case 0 :
22205  if (cimg::abs(delta_x)>=width()) return fill(0);
22206  if (delta_x<0) cimg_forYZC(*this,y,z,c) {
22207  std::memmove(data(0,y,z,c),data(-delta_x,y,z,c),(_width+delta_x)*sizeof(T));
22208  std::memset(data(_width+delta_x,y,z,c),0,-delta_x*sizeof(T));
22209  } else cimg_forYZC(*this,y,z,c) {
22210  std::memmove(data(delta_x,y,z,c),data(0,y,z,c),(_width-delta_x)*sizeof(T));
22211  std::memset(data(0,y,z,c),0,delta_x*sizeof(T));
22212  }
22213  break;
22214  case 1 :
22215  if (delta_x<0) {
22216  const int ndelta_x = (-delta_x>=width())?width()-1:-delta_x;
22217  if (!ndelta_x) return *this;
22218  cimg_forYZC(*this,y,z,c) {
22219  std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T));
22220  T *ptrd = data(_width-1,y,z,c);
22221  const T val = *ptrd;
22222  for (int l = 0; l<ndelta_x-1; ++l) *(--ptrd) = val;
22223  }
22224  } else {
22225  const int ndelta_x = (delta_x>=width())?width()-1:delta_x;
22226  if (!ndelta_x) return *this;
22227  cimg_forYZC(*this,y,z,c) {
22228  std::memmove(data(ndelta_x,y,z,c),data(0,y,z,c),(_width-ndelta_x)*sizeof(T));
22229  T *ptrd = data(0,y,z,c);
22230  const T val = *ptrd;
22231  for (int l = 0; l<ndelta_x-1; ++l) *(++ptrd) = val;
22232  }
22233  }
22234  break;
22235  default : {
22236  const int ml = cimg::mod(-delta_x,width()), ndelta_x = (ml<=width()/2)?ml:(ml-width());
22237  if (!ndelta_x) return *this;
22238  T *const buf = new T[cimg::abs(ndelta_x)];
22239  if (ndelta_x>0) cimg_forYZC(*this,y,z,c) {
22240  std::memcpy(buf,data(0,y,z,c),ndelta_x*sizeof(T));
22241  std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T));
22242  std::memcpy(data(_width-ndelta_x,y,z,c),buf,ndelta_x*sizeof(T));
22243  } else cimg_forYZC(*this,y,z,c) {
22244  std::memcpy(buf,data(_width+ndelta_x,y,z,c),-ndelta_x*sizeof(T));
22245  std::memmove(data(-ndelta_x,y,z,c),data(0,y,z,c),(_width+ndelta_x)*sizeof(T));
22246  std::memcpy(data(0,y,z,c),buf,-ndelta_x*sizeof(T));
22247  }
22248  delete[] buf;
22249  }
22250  }
22251 
22252  if (delta_y) // Shift along Y-axis
22253  switch (boundary_conditions) {
22254  case 0 :
22255  if (cimg::abs(delta_y)>=height()) return fill(0);
22256  if (delta_y<0) cimg_forZC(*this,z,c) {
22257  std::memmove(data(0,0,z,c),data(0,-delta_y,z,c),_width*(_height+delta_y)*sizeof(T));
22258  std::memset(data(0,_height+delta_y,z,c),0,-delta_y*_width*sizeof(T));
22259  } else cimg_forZC(*this,z,c) {
22260  std::memmove(data(0,delta_y,z,c),data(0,0,z,c),_width*(_height-delta_y)*sizeof(T));
22261  std::memset(data(0,0,z,c),0,delta_y*_width*sizeof(T));
22262  }
22263  break;
22264  case 1 :
22265  if (delta_y<0) {
22266  const int ndelta_y = (-delta_y>=height())?height()-1:-delta_y;
22267  if (!ndelta_y) return *this;
22268  cimg_forZC(*this,z,c) {
22269  std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T));
22270  T *ptrd = data(0,_height-ndelta_y,z,c), *ptrs = data(0,_height-1,z,c);
22271  for (int l = 0; l<ndelta_y-1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; }
22272  }
22273  } else {
22274  const int ndelta_y = (delta_y>=height())?height()-1:delta_y;
22275  if (!ndelta_y) return *this;
22276  cimg_forZC(*this,z,c) {
22277  std::memmove(data(0,ndelta_y,z,c),data(0,0,z,c),_width*(_height-ndelta_y)*sizeof(T));
22278  T *ptrd = data(0,1,z,c), *ptrs = data(0,0,z,c);
22279  for (int l = 0; l<ndelta_y-1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; }
22280  }
22281  }
22282  break;
22283  default : {
22284  const int ml = cimg::mod(-delta_y,height()), ndelta_y = (ml<=height()/2)?ml:(ml-height());
22285  if (!ndelta_y) return *this;
22286  T *const buf = new T[(unsigned long)_width*cimg::abs(ndelta_y)];
22287  if (ndelta_y>0) cimg_forZC(*this,z,c) {
22288  std::memcpy(buf,data(0,0,z,c),_width*ndelta_y*sizeof(T));
22289  std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T));
22290  std::memcpy(data(0,_height-ndelta_y,z,c),buf,_width*ndelta_y*sizeof(T));
22291  } else cimg_forZC(*this,z,c) {
22292  std::memcpy(buf,data(0,_height+ndelta_y,z,c),-ndelta_y*_width*sizeof(T));
22293  std::memmove(data(0,-ndelta_y,z,c),data(0,0,z,c),_width*(_height+ndelta_y)*sizeof(T));
22294  std::memcpy(data(0,0,z,c),buf,-ndelta_y*_width*sizeof(T));
22295  }
22296  delete[] buf;
22297  }
22298  }
22299 
22300  if (delta_z) // Shift along Z-axis
22301  switch (boundary_conditions) {
22302  case 0 :
22303  if (cimg::abs(delta_z)>=depth()) return fill(0);
22304  if (delta_z<0) cimg_forC(*this,c) {
22305  std::memmove(data(0,0,0,c),data(0,0,-delta_z,c),_width*_height*(_depth+delta_z)*sizeof(T));
22306  std::memset(data(0,0,_depth+delta_z,c),0,_width*_height*(-delta_z)*sizeof(T));
22307  } else cimg_forC(*this,c) {
22308  std::memmove(data(0,0,delta_z,c),data(0,0,0,c),_width*_height*(_depth-delta_z)*sizeof(T));
22309  std::memset(data(0,0,0,c),0,delta_z*_width*_height*sizeof(T));
22310  }
22311  break;
22312  case 1 :
22313  if (delta_z<0) {
22314  const int ndelta_z = (-delta_z>=depth())?depth()-1:-delta_z;
22315  if (!ndelta_z) return *this;
22316  cimg_forC(*this,c) {
22317  std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
22318  T *ptrd = data(0,0,_depth-ndelta_z,c), *ptrs = data(0,0,_depth-1,c);
22319  for (int l = 0; l<ndelta_z-1; ++l) {
22320  std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=(unsigned long)_width*_height;
22321  }
22322  }
22323  } else {
22324  const int ndelta_z = (delta_z>=depth())?depth()-1:delta_z;
22325  if (!ndelta_z) return *this;
22326  cimg_forC(*this,c) {
22327  std::memmove(data(0,0,ndelta_z,c),data(0,0,0,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
22328  T *ptrd = data(0,0,1,c), *ptrs = data(0,0,0,c);
22329  for (int l = 0; l<ndelta_z-1; ++l) {
22330  std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=(unsigned long)_width*_height;
22331  }
22332  }
22333  }
22334  break;
22335  default : {
22336  const int ml = cimg::mod(-delta_z,depth()), ndelta_z = (ml<=depth()/2)?ml:(ml-depth());
22337  if (!ndelta_z) return *this;
22338  T *const buf = new T[(unsigned long)_width*_height*cimg::abs(ndelta_z)];
22339  if (ndelta_z>0) cimg_forC(*this,c) {
22340  std::memcpy(buf,data(0,0,0,c),_width*_height*ndelta_z*sizeof(T));
22341  std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
22342  std::memcpy(data(0,0,_depth-ndelta_z,c),buf,_width*_height*ndelta_z*sizeof(T));
22343  } else cimg_forC(*this,c) {
22344  std::memcpy(buf,data(0,0,_depth+ndelta_z,c),-ndelta_z*_width*_height*sizeof(T));
22345  std::memmove(data(0,0,-ndelta_z,c),data(0,0,0,c),_width*_height*(_depth+ndelta_z)*sizeof(T));
22346  std::memcpy(data(0,0,0,c),buf,-ndelta_z*_width*_height*sizeof(T));
22347  }
22348  delete[] buf;
22349  }
22350  }
22351 
22352  if (delta_c) // Shift along C-axis
22353  switch (boundary_conditions) {
22354  case 0 :
22355  if (cimg::abs(delta_c)>=spectrum()) return fill(0);
22356  if (delta_c<0) {
22357  std::memmove(_data,data(0,0,0,-delta_c),_width*_height*_depth*(_spectrum+delta_c)*sizeof(T));
22358  std::memset(data(0,0,0,_spectrum+delta_c),0,_width*_height*_depth*(-delta_c)*sizeof(T));
22359  } else {
22360  std::memmove(data(0,0,0,delta_c),_data,_width*_height*_depth*(_spectrum-delta_c)*sizeof(T));
22361  std::memset(_data,0,delta_c*_width*_height*_depth*sizeof(T));
22362  }
22363  break;
22364  case 1 :
22365  if (delta_c<0) {
22366  const int ndelta_c = (-delta_c>=spectrum())?spectrum()-1:-delta_c;
22367  if (!ndelta_c) return *this;
22368  std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
22369  T *ptrd = data(0,0,0,_spectrum-ndelta_c), *ptrs = data(0,0,0,_spectrum-1);
22370  for (int l = 0; l<ndelta_c-1; ++l) {
22371  std::memcpy(ptrd,ptrs,_width*_height*_depth*sizeof(T)); ptrd+=(unsigned long)_width*_height*_depth;
22372  }
22373  } else {
22374  const int ndelta_c = (delta_c>=spectrum())?spectrum()-1:delta_c;
22375  if (!ndelta_c) return *this;
22376  std::memmove(data(0,0,0,ndelta_c),_data,_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
22377  T *ptrd = data(0,0,0,1);
22378  for (int l = 0; l<ndelta_c-1; ++l) {
22379  std::memcpy(ptrd,_data,_width*_height*_depth*sizeof(T)); ptrd+=(unsigned long)_width*_height*_depth;
22380  }
22381  }
22382  break;
22383  default : {
22384  const int ml = cimg::mod(-delta_c,spectrum()), ndelta_c = (ml<=spectrum()/2)?ml:(ml-spectrum());
22385  if (!ndelta_c) return *this;
22386  T *const buf = new T[(unsigned long)_width*_height*_depth*cimg::abs(ndelta_c)];
22387  if (ndelta_c>0) {
22388  std::memcpy(buf,_data,_width*_height*_depth*ndelta_c*sizeof(T));
22389  std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
22390  std::memcpy(data(0,0,0,_spectrum-ndelta_c),buf,_width*_height*_depth*ndelta_c*sizeof(T));
22391  } else {
22392  std::memcpy(buf,data(0,0,0,_spectrum+ndelta_c),-ndelta_c*_width*_height*_depth*sizeof(T));
22393  std::memmove(data(0,0,0,-ndelta_c),_data,_width*_height*_depth*(_spectrum+ndelta_c)*sizeof(T));
22394  std::memcpy(_data,buf,-ndelta_c*_width*_height*_depth*sizeof(T));
22395  }
22396  delete[] buf;
22397  }
22398  }
22399  return *this;
22400  }
22401 
22403  CImg<T> get_shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0,
22404  const int boundary_conditions=0) const {
22405  return (+*this).shift(delta_x,delta_y,delta_z,delta_c,boundary_conditions);
22406  }
22407 
22409 
22413  CImg<T>& permute_axes(const char *const order) {
22414  return get_permute_axes(order).move_to(*this);
22415  }
22416 
22418  CImg<T> get_permute_axes(const char *const order) const {
22419  const T foo = (T)0;
22420  return _get_permute_axes(order,foo);
22421  }
22422 
22423  template<typename t>
22424  CImg<t> _get_permute_axes(const char *const permut, const t&) const {
22425  if (is_empty() || !permut) return CImg<t>(*this,false);
22426  CImg<t> res;
22427  const T* ptrs = _data;
22428  if (!cimg::strncasecmp(permut,"xyzc",4)) return +*this;
22429  if (!cimg::strncasecmp(permut,"xycz",4)) {
22430  res.assign(_width,_height,_spectrum,_depth);
22431  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
22432  cimg_forXYZC(*this,x,y,z,c) res(x,y,c,z,wh,whd) = (t)*(ptrs++);
22433  }
22434  if (!cimg::strncasecmp(permut,"xzyc",4)) {
22435  res.assign(_width,_depth,_height,_spectrum);
22436  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
22437  cimg_forXYZC(*this,x,y,z,c) res(x,z,y,c,wh,whd) = (t)*(ptrs++);
22438  }
22439  if (!cimg::strncasecmp(permut,"xzcy",4)) {
22440  res.assign(_width,_depth,_spectrum,_height);
22441  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
22442  cimg_forXYZC(*this,x,y,z,c) res(x,z,c,y,wh,whd) = (t)*(ptrs++);
22443  }
22444  if (!cimg::strncasecmp(permut,"xcyz",4)) {
22445  res.assign(_width,_spectrum,_height,_depth);
22446  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
22447  cimg_forXYZC(*this,x,y,z,c) res(x,c,y,z,wh,whd) = (t)*(ptrs++);
22448  }
22449  if (!cimg::strncasecmp(permut,"xczy",4)) {
22450  res.assign(_width,_spectrum,_depth,_height);
22451  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
22452  cimg_forXYZC(*this,x,y,z,c) res(x,c,z,y,wh,whd) = (t)*(ptrs++);
22453  }
22454  if (!cimg::strncasecmp(permut,"yxzc",4)) {
22455  res.assign(_height,_width,_depth,_spectrum);
22456  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
22457  cimg_forXYZC(*this,x,y,z,c) res(y,x,z,c,wh,whd) = (t)*(ptrs++);
22458  }
22459  if (!cimg::strncasecmp(permut,"yxcz",4)) {
22460  res.assign(_height,_width,_spectrum,_depth);
22461  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
22462  cimg_forXYZC(*this,x,y,z,c) res(y,x,c,z,wh,whd) = (t)*(ptrs++);
22463  }
22464  if (!cimg::strncasecmp(permut,"yzxc",4)) {
22465  res.assign(_height,_depth,_width,_spectrum);
22466  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
22467  cimg_forXYZC(*this,x,y,z,c) res(y,z,x,c,wh,whd) = (t)*(ptrs++);
22468  }
22469  if (!cimg::strncasecmp(permut,"yzcx",4)) {
22470  res.assign(_height,_depth,_spectrum,_width);
22471  switch (_width) {
22472  case 1 : {
22473  t *ptr_r = res.data(0,0,0,0);
22474  for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
22475  *(ptr_r++) = (t)*(ptrs++);
22476  }
22477  } break;
22478  case 2 : {
22479  t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1);
22480  for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
22481  *(ptr_r++) = (t)*(ptrs++); *(ptr_g++) = (t)*(ptrs++);
22482  }
22483  } break;
22484  case 3 : { // Optimization for the classical conversion from interleaved RGB to planar RGB
22485  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);
22486  for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
22487  *(ptr_r++) = (t)*(ptrs++); *(ptr_g++) = (t)*(ptrs++); *(ptr_b++) = (t)*(ptrs++);
22488  }
22489  } break;
22490  case 4 : { // Optimization for the classical conversion from interleaved RGBA to planar RGBA
22491  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);
22492  for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
22493  *(ptr_r++) = (t)*(ptrs++); *(ptr_g++) = (t)*(ptrs++); *(ptr_b++) = (t)*(ptrs++); *(ptr_a++) = (t)*(ptrs++);
22494  }
22495  } break;
22496  default : {
22497  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
22498  cimg_forXYZC(*this,x,y,z,c) res(y,z,c,x,wh,whd) = *(ptrs++);
22499  return res;
22500  }
22501  }
22502  }
22503  if (!cimg::strncasecmp(permut,"ycxz",4)) {
22504  res.assign(_height,_spectrum,_width,_depth);
22505  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
22506  cimg_forXYZC(*this,x,y,z,c) res(y,c,x,z,wh,whd) = (t)*(ptrs++);
22507  }
22508  if (!cimg::strncasecmp(permut,"yczx",4)) {
22509  res.assign(_height,_spectrum,_depth,_width);
22510  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
22511  cimg_forXYZC(*this,x,y,z,c) res(y,c,z,x,wh,whd) = (t)*(ptrs++);
22512  }
22513  if (!cimg::strncasecmp(permut,"zxyc",4)) {
22514  res.assign(_depth,_width,_height,_spectrum);
22515  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
22516  cimg_forXYZC(*this,x,y,z,c) res(z,x,y,c,wh,whd) = (t)*(ptrs++);
22517  }
22518  if (!cimg::strncasecmp(permut,"zxcy",4)) {
22519  res.assign(_depth,_width,_spectrum,_height);
22520  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
22521  cimg_forXYZC(*this,x,y,z,c) res(z,x,c,y,wh,whd) = (t)*(ptrs++);
22522  }
22523  if (!cimg::strncasecmp(permut,"zyxc",4)) {
22524  res.assign(_depth,_height,_width,_spectrum);
22525  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
22526  cimg_forXYZC(*this,x,y,z,c) res(z,y,x,c,wh,whd) = (t)*(ptrs++);
22527  }
22528  if (!cimg::strncasecmp(permut,"zycx",4)) {
22529  res.assign(_depth,_height,_spectrum,_width);
22530  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
22531  cimg_forXYZC(*this,x,y,z,c) res(z,y,c,x,wh,whd) = (t)*(ptrs++);
22532  }
22533  if (!cimg::strncasecmp(permut,"zcxy",4)) {
22534  res.assign(_depth,_spectrum,_width,_height);
22535  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
22536  cimg_forXYZC(*this,x,y,z,c) res(z,c,x,y,wh,whd) = (t)*(ptrs++);
22537  }
22538  if (!cimg::strncasecmp(permut,"zcyx",4)) {
22539  res.assign(_depth,_spectrum,_height,_width);
22540  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
22541  cimg_forXYZC(*this,x,y,z,c) res(z,c,y,x,wh,whd) = (t)*(ptrs++);
22542  }
22543  if (!cimg::strncasecmp(permut,"cxyz",4)) {
22544  res.assign(_spectrum,_width,_height,_depth);
22545  switch (_spectrum) {
22546  case 1 : {
22547  const T *ptr_r = data(0,0,0,0);
22548  t *ptrd = res._data;
22549  for (unsigned long siz = (unsigned long)_width*_height*_depth; siz; --siz) *(ptrd++) = (t)*(ptr_r++);
22550  } break;
22551  case 2 : {
22552  const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1);
22553  t *ptrd = res._data;
22554  for (unsigned long siz = (unsigned long)_width*_height*_depth; siz; --siz) {
22555  *(ptrd++) = (t)*(ptr_r++); *(ptrd++) = (t)*(ptr_g++);
22556  }
22557  } break;
22558  case 3 : { // Optimization for the classical conversion from planar RGB to interleaved RGB
22559  const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
22560  t *ptrd = res._data;
22561  for (unsigned long siz = (unsigned long)_width*_height*_depth; siz; --siz) {
22562  *(ptrd++) = (t)*(ptr_r++); *(ptrd++) = (t)*(ptr_g++); *(ptrd++) = (t)*(ptr_b++);
22563  }
22564  } break;
22565  case 4 : { // Optimization for the classical conversion from planar RGBA to interleaved RGBA
22566  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);
22567  t *ptrd = res._data;
22568  for (unsigned long siz = (unsigned long)_width*_height*_depth; siz; --siz) {
22569  *(ptrd++) = (t)*(ptr_r++); *(ptrd++) = (t)*(ptr_g++); *(ptrd++) = (t)*(ptr_b++); *(ptrd++) = (t)*(ptr_a++);
22570  }
22571  } break;
22572  default : {
22573  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
22574  cimg_forXYZC(*this,x,y,z,c) res(c,x,y,z,wh,whd) = (t)*(ptrs++);
22575  }
22576  }
22577  }
22578  if (!cimg::strncasecmp(permut,"cxzy",4)) {
22579  res.assign(_spectrum,_width,_depth,_height);
22580  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
22581  cimg_forXYZC(*this,x,y,z,c) res(c,x,z,y,wh,whd) = (t)*(ptrs++);
22582  }
22583  if (!cimg::strncasecmp(permut,"cyxz",4)) {
22584  res.assign(_spectrum,_height,_width,_depth);
22585  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
22586  cimg_forXYZC(*this,x,y,z,c) res(c,y,x,z,wh,whd) = (t)*(ptrs++);
22587  }
22588  if (!cimg::strncasecmp(permut,"cyzx",4)) {
22589  res.assign(_spectrum,_height,_depth,_width);
22590  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
22591  cimg_forXYZC(*this,x,y,z,c) res(c,y,z,x,wh,whd) = (t)*(ptrs++);
22592  }
22593  if (!cimg::strncasecmp(permut,"czxy",4)) {
22594  res.assign(_spectrum,_depth,_width,_height);
22595  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
22596  cimg_forXYZC(*this,x,y,z,c) res(c,z,x,y,wh,whd) = (t)*(ptrs++);
22597  }
22598  if (!cimg::strncasecmp(permut,"czyx",4)) {
22599  res.assign(_spectrum,_depth,_height,_width);
22600  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
22601  cimg_forXYZC(*this,x,y,z,c) res(c,z,y,x,wh,whd) = (t)*(ptrs++);
22602  }
22603  if (!res)
22604  throw CImgArgumentException(_cimg_instance
22605  "permute_axes(): Invalid specified permutation '%s'.",
22606  cimg_instance,
22607  permut);
22608  return res;
22609  }
22610 
22612 
22615  CImg<T>& unroll(const char axis) {
22616  const unsigned int siz = size();
22617  if (siz) switch (axis) {
22618  case 'x' : _width = siz; _height = _depth = _spectrum = 1; break;
22619  case 'y' : _height = siz; _width = _depth = _spectrum = 1; break;
22620  case 'z' : _depth = siz; _width = _height = _spectrum = 1; break;
22621  default : _spectrum = siz; _width = _height = _depth = 1;
22622  }
22623  return *this;
22624  }
22625 
22627  CImg<T> get_unroll(const char axis) const {
22628  return (+*this).unroll(axis);
22629  }
22630 
22632 
22638  CImg<T>& rotate(const float angle, const unsigned int interpolation=1, const unsigned int boundary=0) {
22639  const float nangle = cimg::mod(angle,360.0f);
22640  if (nangle==0.0f) return *this;
22641  return get_rotate(angle,interpolation,boundary).move_to(*this);
22642  }
22643 
22645  CImg<T> get_rotate(const float angle, const unsigned int interpolation=1, const unsigned int boundary=0) const {
22646  if (is_empty()) return *this;
22647  CImg<T> res;
22648  const float nangle = cimg::mod(angle,360.0f);
22649  if (boundary!=1 && cimg::mod(nangle,90.0f)==0) { // Optimized version for orthogonal angles.
22650  const int wm1 = width() - 1, hm1 = height() - 1;
22651  const int iangle = (int)nangle/90;
22652  switch (iangle) {
22653  case 1 : { // 90 deg.
22654  res.assign(_height,_width,_depth,_spectrum);
22655  T *ptrd = res._data;
22656  cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(y,hm1-x,z,c);
22657  } break;
22658  case 2 : { // 180 deg.
22659  res.assign(_width,_height,_depth,_spectrum);
22660  T *ptrd = res._data;
22661  cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1-x,hm1-y,z,c);
22662  } break;
22663  case 3 : { // 270 deg.
22664  res.assign(_height,_width,_depth,_spectrum);
22665  T *ptrd = res._data;
22666  cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1-y,x,z,c);
22667  } break;
22668  default : // 0 deg.
22669  return *this;
22670  }
22671  } else { // Generic angle.
22672  const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
22673  const float
22674  rad = (float)(nangle*cimg::PI/180.0),
22675  ca = (float)std::cos(rad),
22676  sa = (float)std::sin(rad),
22677  ux = cimg::abs(_width*ca), uy = cimg::abs(_width*sa),
22678  vx = cimg::abs(_height*sa), vy = cimg::abs(_height*ca),
22679  w2 = 0.5f*_width, h2 = 0.5f*_height,
22680  dw2 = 0.5f*(ux+vx), dh2 = 0.5f*(uy+vy);
22681  res.assign((int)(ux+vx),(int)(uy+vy),_depth,_spectrum);
22682  switch (boundary) {
22683  case 0 : { // Dirichlet boundaries.
22684  switch (interpolation) {
22685  case 2 : { // Cubic interpolation.
22686 #ifdef cimg_use_openmp
22687 #pragma omp parallel for collapse(3) if (res.size()>=2048)
22688 #endif
22689  cimg_forXYZC(res,x,y,z,c) {
22690  const Tfloat val = cubic_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,c,0);
22691  res(x,y,z,c) = (T)(val<vmin?vmin:val>vmax?vmax:val);
22692  }
22693  } break;
22694  case 1 : { // Linear interpolation.
22695 #ifdef cimg_use_openmp
22696 #pragma omp parallel for collapse(3) if (res.size()>=2048)
22697 #endif
22698  cimg_forXYZC(res,x,y,z,c)
22699  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);
22700  } break;
22701  default : { // Nearest-neighbor interpolation.
22702 #ifdef cimg_use_openmp
22703 #pragma omp parallel for collapse(3) if (res.size()>=2048)
22704 #endif
22705  cimg_forXYZC(res,x,y,z,c)
22706  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);
22707  }
22708  }
22709  } break;
22710  case 1 : { // Neumann boundaries.
22711  switch (interpolation) {
22712  case 2 : { // Cubic interpolation.
22713 #ifdef cimg_use_openmp
22714 #pragma omp parallel for collapse(3) if (res.size()>=2048)
22715 #endif
22716  cimg_forXYZC(res,x,y,z,c) {
22717  const Tfloat val = _cubic_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,c);
22718  res(x,y,z,c) = (T)(val<vmin?vmin:val>vmax?vmax:val);
22719  }
22720  } break;
22721  case 1 : { // Linear interpolation.
22722 #ifdef cimg_use_openmp
22723 #pragma omp parallel for collapse(3) if (res.size()>=2048)
22724 #endif
22725  cimg_forXYZC(res,x,y,z,c)
22726  res(x,y,z,c) = (T)_linear_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,c);
22727  } break;
22728  default : { // Nearest-neighbor interpolation.
22729 #ifdef cimg_use_openmp
22730 #pragma omp parallel for collapse(3) if (res.size()>=2048)
22731 #endif
22732  cimg_forXYZC(res,x,y,z,c)
22733  res(x,y,z,c) = _atXY((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),(int)(h2 - (x-dw2)*sa + (y-dh2)*ca),z,c);
22734  }
22735  }
22736  } break;
22737  case 2 : { // Periodic boundaries.
22738  switch (interpolation) {
22739  case 2 : { // Cubic interpolation.
22740 #ifdef cimg_use_openmp
22741 #pragma omp parallel for collapse(3) if (res.size()>=2048)
22742 #endif
22743  cimg_forXYZC(res,x,y,z,c) {
22744  const Tfloat val = _cubic_atXY(cimg::mod(w2 + (x-dw2)*ca + (y-dh2)*sa,(float)width()),
22745  cimg::mod(h2 - (x-dw2)*sa + (y-dh2)*ca,(float)height()),z,c);
22746  res(x,y,z,c) = (T)(val<vmin?vmin:val>vmax?vmax:val);
22747  }
22748  } break;
22749  case 1 : { // Linear interpolation.
22750 #ifdef cimg_use_openmp
22751 #pragma omp parallel for collapse(3) if (res.size()>=2048)
22752 #endif
22753  cimg_forXYZC(res,x,y,z,c)
22754  res(x,y,z,c) = (T)_linear_atXY(cimg::mod(w2 + (x-dw2)*ca + (y-dh2)*sa,(float)width()),
22755  cimg::mod(h2 - (x-dw2)*sa + (y-dh2)*ca,(float)height()),z,c);
22756  } break;
22757  default : { // Nearest-neighbor interpolation.
22758 #ifdef cimg_use_openmp
22759 #pragma omp parallel for collapse(3) if (res.size()>=2048)
22760 #endif
22761  cimg_forXYZC(res,x,y,z,c)
22762  res(x,y,z,c) = (*this)(cimg::mod((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),width()),
22763  cimg::mod((int)(h2 - (x-dw2)*sa + (y-dh2)*ca),height()),z,c);
22764  }
22765  }
22766  } break;
22767  default :
22768  throw CImgArgumentException(_cimg_instance
22769  "rotate(): Invalid specified border conditions %d "
22770  "(should be { 0=dirichlet | 1=neumann | 2=periodic }).",
22771  cimg_instance,
22772  boundary);
22773  }
22774  }
22775  return res;
22776  }
22777 
22779 
22787  CImg<T>& rotate(const float angle, const float cx, const float cy, const float zoom,
22788  const unsigned int interpolation=1, const unsigned int boundary=3) {
22789  return get_rotate(angle,cx,cy,zoom,interpolation,boundary).move_to(*this);
22790  }
22791 
22793  CImg<T> get_rotate(const float angle, const float cx, const float cy, const float zoom,
22794  const unsigned int interpolation=1, const unsigned int boundary=3) const {
22795  if (interpolation>2)
22796  throw CImgArgumentException(_cimg_instance
22797  "rotate(): Invalid specified interpolation type %d "
22798  "(should be { 0=none | 1=linear | 2=cubic }).",
22799  cimg_instance,
22800  interpolation);
22801  if (is_empty()) return *this;
22802  CImg<T> res(_width,_height,_depth,_spectrum);
22803  const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
22804  const float
22805  rad = (float)((angle*cimg::PI)/180.0),
22806  ca = (float)std::cos(rad)/zoom,
22807  sa = (float)std::sin(rad)/zoom;
22808  switch (boundary) {
22809  case 0 : {
22810  switch (interpolation) {
22811  case 2 : {
22812 #ifdef cimg_use_openmp
22813 #pragma omp parallel for collapse(3) if (res.size()>=2048)
22814 #endif
22815  cimg_forXYZC(res,x,y,z,c) {
22816  const Tfloat val = cubic_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,c,0);
22817  res(x,y,z,c) = (T)(val<vmin?vmin:val>vmax?vmax:val);
22818  }
22819  } break;
22820  case 1 : {
22821 #ifdef cimg_use_openmp
22822 #pragma omp parallel for collapse(3) if (res.size()>=2048)
22823 #endif
22824  cimg_forXYZC(res,x,y,z,c)
22825  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);
22826  } break;
22827  default : {
22828 #ifdef cimg_use_openmp
22829 #pragma omp parallel for collapse(3) if (res.size()>=2048)
22830 #endif
22831  cimg_forXYZC(res,x,y,z,c)
22832  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);
22833  }
22834  }
22835  } break;
22836  case 1 : {
22837  switch (interpolation) {
22838  case 2 : {
22839 #ifdef cimg_use_openmp
22840 #pragma omp parallel for collapse(3) if (res.size()>=2048)
22841 #endif
22842  cimg_forXYZC(res,x,y,z,c) {
22843  const Tfloat val = _cubic_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,c);
22844  res(x,y,z,c) = (T)(val<vmin?vmin:val>vmax?vmax:val);
22845  }
22846  } break;
22847  case 1 : {
22848 #ifdef cimg_use_openmp
22849 #pragma omp parallel for collapse(3) if (res.size()>=2048)
22850 #endif
22851  cimg_forXYZC(res,x,y,z,c)
22852  res(x,y,z,c) = (T)_linear_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,c);
22853  } break;
22854  default : {
22855 #ifdef cimg_use_openmp
22856 #pragma omp parallel for collapse(3) if (res.size()>=2048)
22857 #endif
22858  cimg_forXYZC(res,x,y,z,c)
22859  res(x,y,z,c) = _atXY((int)(cx + (x-cx)*ca + (y-cy)*sa),(int)(cy - (x-cx)*sa + (y-cy)*ca),z,c);
22860  }
22861  }
22862  } break;
22863  case 2 : {
22864  switch (interpolation) {
22865  case 2 : {
22866 #ifdef cimg_use_openmp
22867 #pragma omp parallel for collapse(3) if (res.size()>=2048)
22868 #endif
22869  cimg_forXYZC(res,x,y,z,c) {
22870  const Tfloat val = _cubic_atXY(cimg::mod(cx + (x-cx)*ca + (y-cy)*sa,(float)width()),
22871  cimg::mod(cy - (x-cx)*sa + (y-cy)*ca,(float)height()),z,c);
22872  res(x,y,z,c) = (T)(val<vmin?vmin:val>vmax?vmax:val);
22873  }
22874  } break;
22875  case 1 : {
22876 #ifdef cimg_use_openmp
22877 #pragma omp parallel for collapse(3) if (res.size()>=2048)
22878 #endif
22879  cimg_forXYZC(res,x,y,z,c)
22880  res(x,y,z,c) = (T)_linear_atXY(cimg::mod(cx + (x-cx)*ca + (y-cy)*sa,(float)width()),
22881  cimg::mod(cy - (x-cx)*sa + (y-cy)*ca,(float)height()),z,c);
22882  } break;
22883  default : {
22884 #ifdef cimg_use_openmp
22885 #pragma omp parallel for collapse(3) if (res.size()>=2048)
22886 #endif
22887  cimg_forXYZC(res,x,y,z,c)
22888  res(x,y,z,c) = (*this)(cimg::mod((int)(cx + (x-cx)*ca + (y-cy)*sa),width()),
22889  cimg::mod((int)(cy - (x-cx)*sa + (y-cy)*ca),height()),z,c);
22890  }
22891  }
22892  } break;
22893  default :
22894  throw CImgArgumentException(_cimg_instance
22895  "rotate(): Invalid specified border conditions %d "
22896  "(should be { 0=dirichlet | 1=neumann | 2=periodic }).",
22897  cimg_instance,
22898  boundary);
22899  }
22900  return res;
22901  }
22902 
22904 
22910  template<typename t>
22911  CImg<T>& warp(const CImg<t>& warp, const bool is_relative=false,
22912  const unsigned int interpolation=1, const unsigned int boundary_conditions=0) {
22913  return get_warp(warp,is_relative,interpolation,boundary_conditions).move_to(*this);
22914  }
22915 
22917  template<typename t>
22918  CImg<T> get_warp(const CImg<t>& warp, const bool is_relative=false,
22919  const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const {
22920  if (is_empty() || !warp) return *this;
22921  if (is_relative && !is_sameXYZ(warp))
22922  throw CImgArgumentException(_cimg_instance
22923  "warp(): Instance and specified relative warping field (%u,%u,%u,%u,%p) "
22924  "have different XYZ dimensions.",
22925  cimg_instance,
22926  warp._width,warp._height,warp._depth,warp._spectrum,warp._data);
22927 
22928  CImg<T> res(warp._width,warp._height,warp._depth,_spectrum);
22929 
22930  if (warp._spectrum==1) { // 1d warping.
22931  if (is_relative) { // Relative warp.
22932  if (interpolation==2) { // Cubic interpolation.
22933  if (boundary_conditions==2) // Periodic boundaries.
22934 #ifdef cimg_use_openmp
22935 #pragma omp parallel for collapse(3) if (res.size()>=4096)
22936 #endif
22937  cimg_forYZC(res,y,z,c) {
22938  const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
22939  cimg_forX(res,x) *(ptrd++) = (T)_cubic_atX(cimg::mod(x - (float)*(ptrs0++),(float)_width),y,z,c);
22940  }
22941  else if (boundary_conditions==1) // Neumann boundaries.
22942 #ifdef cimg_use_openmp
22943 #pragma omp parallel for collapse(3) if (res.size()>=4096)
22944 #endif
22945  cimg_forYZC(res,y,z,c) {
22946  const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
22947  cimg_forX(res,x) *(ptrd++) = (T)_cubic_atX(x - (float)*(ptrs0++),y,z,c);
22948  }
22949  else // Dirichlet boundaries.
22950 #ifdef cimg_use_openmp
22951 #pragma omp parallel for collapse(3) if (res.size()>=4096)
22952 #endif
22953  cimg_forYZC(res,y,z,c) {
22954  const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
22955  cimg_forX(res,x) *(ptrd++) = (T)cubic_atX(x - (float)*(ptrs0++),y,z,c,0);
22956  }
22957  } else if (interpolation==1) { // Linear interpolation.
22958  if (boundary_conditions==2) // Periodic boundaries.
22959 #ifdef cimg_use_openmp
22960 #pragma omp parallel for collapse(3) if (res.size()>=1048576)
22961 #endif
22962  cimg_forYZC(res,y,z,c) {
22963  const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
22964  cimg_forX(res,x) *(ptrd++) = (T)_linear_atX(cimg::mod(x - (float)*(ptrs0++),(float)_width),y,z,c);
22965  }
22966  else if (boundary_conditions==1) // Neumann boundaries.
22967 #ifdef cimg_use_openmp
22968 #pragma omp parallel for collapse(3) if (res.size()>=1048576)
22969 #endif
22970  cimg_forYZC(res,y,z,c) {
22971  const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
22972  cimg_forX(res,x) *(ptrd++) = (T)_linear_atX(x - (float)*(ptrs0++),y,z,c);
22973  }
22974  else // Dirichlet boundaries.
22975 #ifdef cimg_use_openmp
22976 #pragma omp parallel for collapse(3) if (res.size()>=1048576)
22977 #endif
22978  cimg_forYZC(res,y,z,c) {
22979  const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
22980  cimg_forX(res,x) *(ptrd++) = (T)linear_atX(x - (float)*(ptrs0++),y,z,c,0);
22981  }
22982  } else { // Nearest-neighbor interpolation.
22983  if (boundary_conditions==2) // Periodic boundaries.
22984  cimg_forYZC(res,y,z,c) {
22985  const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
22986  cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)*(ptrs0++),(int)_width),y,z,c);
22987  }
22988  else if (boundary_conditions==1) // Neumann boundaries.
22989  cimg_forYZC(res,y,z,c) {
22990  const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
22991  cimg_forX(res,x) *(ptrd++) = _atX(x - (int)*(ptrs0++),y,z,c);
22992  }
22993  else // Dirichlet boundaries.
22994  cimg_forYZC(res,y,z,c) {
22995  const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
22996  cimg_forX(res,x) *(ptrd++) = atX(x - (int)*(ptrs0++),y,z,c,0);
22997  }
22998  }
22999  } else { // Absolute warp.
23000  if (interpolation==2) { // Cubic interpolation.
23001  if (boundary_conditions==2) // Periodic boundaries.
23002 #ifdef cimg_use_openmp
23003 #pragma omp parallel for collapse(3) if (res.size()>=4096)
23004 #endif
23005  cimg_forYZC(res,y,z,c) {
23006  const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
23007  cimg_forX(res,x) *(ptrd++) = (T)_cubic_atX(cimg::mod((float)*(ptrs0++),(float)_width),0,0,c);
23008  }
23009  else if (boundary_conditions==1) // Neumann boundaries.
23010 #ifdef cimg_use_openmp
23011 #pragma omp parallel for collapse(3) if (res.size()>=4096)
23012 #endif
23013  cimg_forYZC(res,y,z,c) {
23014  const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
23015  cimg_forX(res,x) *(ptrd++) = (T)_cubic_atX((float)*(ptrs0++),0,0,c);
23016  }
23017  else // Dirichlet boundaries.
23018 #ifdef cimg_use_openmp
23019 #pragma omp parallel for collapse(3) if (res.size()>=4096)
23020 #endif
23021  cimg_forYZC(res,y,z,c) {
23022  const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
23023  cimg_forX(res,x) *(ptrd++) = (T)cubic_atX((float)*(ptrs0++),0,0,c,0);
23024  }
23025  } else if (interpolation==1) { // Linear interpolation.
23026  if (boundary_conditions==2) // Periodic boundaries.
23027 #ifdef cimg_use_openmp
23028 #pragma omp parallel for collapse(3) if (res.size()>=1048576)
23029 #endif
23030  cimg_forYZC(res,y,z,c) {
23031  const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
23032  cimg_forX(res,x) *(ptrd++) = (T)_linear_atX(cimg::mod((float)*(ptrs0++),(float)_width),0,0,c);
23033  }
23034  else if (boundary_conditions==1) // Neumann boundaries.
23035 #ifdef cimg_use_openmp
23036 #pragma omp parallel for collapse(3) if (res.size()>=1048576)
23037 #endif
23038  cimg_forYZC(res,y,z,c) {
23039  const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
23040  cimg_forX(res,x) *(ptrd++) = (T)_linear_atX((float)*(ptrs0++),0,0,c);
23041  }
23042  else // Dirichlet boundaries.
23043 #ifdef cimg_use_openmp
23044 #pragma omp parallel for collapse(3) if (res.size()>=1048576)
23045 #endif
23046  cimg_forYZC(res,y,z,c) {
23047  const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
23048  cimg_forX(res,x) *(ptrd++) = (T)linear_atX((float)*(ptrs0++),0,0,c,0);
23049  }
23050  } else { // Nearest-neighbor interpolation.
23051  if (boundary_conditions==2) // Periodic boundaries.
23052  cimg_forYZC(res,y,z,c) {
23053  const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
23054  cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)*(ptrs0++),(int)_width),0,0,c);
23055  }
23056  else if (boundary_conditions==1) // Neumann boundaries.
23057  cimg_forYZC(res,y,z,c) {
23058  const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
23059  cimg_forX(res,x) *(ptrd++) = _atX((int)*(ptrs0++),0,0,c);
23060  }
23061  else // Dirichlet boundaries.
23062  cimg_forYZC(res,y,z,c) {
23063  const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
23064  cimg_forX(res,x) *(ptrd++) = atX((int)*(ptrs0++),0,0,c,0);
23065  }
23066  }
23067  }
23068 
23069  } else if (warp._spectrum==2) { // 2d warping.
23070  if (is_relative) { // Relative warp.
23071  if (interpolation==2) { // Cubic interpolation.
23072  if (boundary_conditions==2) // Periodic boundaries.
23073 #ifdef cimg_use_openmp
23074 #pragma omp parallel for collapse(3) if (res.size()>=4096)
23075 #endif
23076  cimg_forYZC(res,y,z,c) {
23077  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
23078  cimg_forX(res,x) *(ptrd++) = (T)_cubic_atXY(cimg::mod(x - (float)*(ptrs0++),(float)_width),
23079  cimg::mod(y - (float)*(ptrs1++),(float)_height),z,c);
23080  }
23081  else if (boundary_conditions==1) // Neumann boundaries.
23082 #ifdef cimg_use_openmp
23083 #pragma omp parallel for collapse(3) if (res.size()>=4096)
23084 #endif
23085  cimg_forYZC(res,y,z,c) {
23086  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
23087  cimg_forX(res,x) *(ptrd++) = (T)_cubic_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
23088  }
23089  else // Dirichlet boundaries.
23090 #ifdef cimg_use_openmp
23091 #pragma omp parallel for collapse(3) if (res.size()>=4096)
23092 #endif
23093  cimg_forYZC(res,y,z,c) {
23094  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
23095  cimg_forX(res,x) *(ptrd++) = (T)cubic_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,0);
23096  }
23097  } else if (interpolation==1) { // Linear interpolation.
23098  if (boundary_conditions==2) // Periodic boundaries.
23099 #ifdef cimg_use_openmp
23100 #pragma omp parallel for collapse(3) if (res.size()>=1048576)
23101 #endif
23102  cimg_forYZC(res,y,z,c) {
23103  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
23104  cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY(cimg::mod(x - (float)*(ptrs0++),(float)_width),
23105  cimg::mod(y - (float)*(ptrs1++),(float)_height),z,c);
23106  }
23107  else if (boundary_conditions==1) // Neumann boundaries.
23108 #ifdef cimg_use_openmp
23109 #pragma omp parallel for collapse(3) if (res.size()>=1048576)
23110 #endif
23111  cimg_forYZC(res,y,z,c) {
23112  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
23113  cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
23114  }
23115  else // Dirichlet boundaries.
23116 #ifdef cimg_use_openmp
23117 #pragma omp parallel for collapse(3) if (res.size()>=1048576)
23118 #endif
23119  cimg_forYZC(res,y,z,c) {
23120  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
23121  cimg_forX(res,x) *(ptrd++) = (T)linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,0);
23122  }
23123  } else { // Nearest-neighbor interpolation.
23124  if (boundary_conditions==2) // Periodic boundaries.
23125  cimg_forYZC(res,y,z,c) {
23126  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
23127  cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)*(ptrs0++),(int)_width),
23128  cimg::mod(y - (int)*(ptrs1++),(int)_height),z,c);
23129  }
23130  else if (boundary_conditions==1) // Neumann boundaries.
23131  cimg_forYZC(res,y,z,c) {
23132  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
23133  cimg_forX(res,x) *(ptrd++) = _atXY(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z,c);
23134  }
23135  else // Dirichlet boundaries.
23136  cimg_forYZC(res,y,z,c) {
23137  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
23138  cimg_forX(res,x) *(ptrd++) = atXY(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z,c,0);
23139  }
23140  }
23141  } else { // Absolute warp.
23142  if (interpolation==2) { // Cubic interpolation.
23143  if (boundary_conditions==2) // Periodic boundaries.
23144 #ifdef cimg_use_openmp
23145 #pragma omp parallel for collapse(3) if (res.size()>=4096)
23146 #endif
23147  cimg_forYZC(res,y,z,c) {
23148  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
23149  cimg_forX(res,x) *(ptrd++) = (T)_cubic_atXY(cimg::mod((float)*(ptrs0++),(float)_width),
23150  cimg::mod((float)*(ptrs1++),(float)_height),0,c);
23151  }
23152  else if (boundary_conditions==1) // Neumann boundaries.
23153 #ifdef cimg_use_openmp
23154 #pragma omp parallel for collapse(3) if (res.size()>=4096)
23155 #endif
23156  cimg_forYZC(res,y,z,c) {
23157  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
23158  cimg_forX(res,x) *(ptrd++) = (T)_cubic_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c);
23159  }
23160  else // Dirichlet boundaries.
23161 #ifdef cimg_use_openmp
23162 #pragma omp parallel for collapse(3) if (res.size()>=4096)
23163 #endif
23164  cimg_forYZC(res,y,z,c) {
23165  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
23166  cimg_forX(res,x) *(ptrd++) = (T)cubic_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c,0);
23167  }
23168  } else if (interpolation==1) { // Linear interpolation.
23169  if (boundary_conditions==2) // Periodic boundaries.
23170 #ifdef cimg_use_openmp
23171 #pragma omp parallel for collapse(3) if (res.size()>=1048576)
23172 #endif
23173  cimg_forYZC(res,y,z,c) {
23174  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
23175  cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY(cimg::mod((float)*(ptrs0++),(float)_width),
23176  cimg::mod((float)*(ptrs1++),(float)_height),0,c);
23177  }
23178  else if (boundary_conditions==1) // Neumann boundaries.
23179 #ifdef cimg_use_openmp
23180 #pragma omp parallel for collapse(3) if (res.size()>=1048576)
23181 #endif
23182  cimg_forYZC(res,y,z,c) {
23183  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
23184  cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c);
23185  }
23186  else // Dirichlet boundaries.
23187 #ifdef cimg_use_openmp
23188 #pragma omp parallel for collapse(3) if (res.size()>=1048576)
23189 #endif
23190  cimg_forYZC(res,y,z,c) {
23191  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
23192  cimg_forX(res,x) *(ptrd++) = (T)linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c,0);
23193  }
23194  } else { // Nearest-neighbor interpolation.
23195  if (boundary_conditions==2) // Periodic boundaries.
23196  cimg_forYZC(res,y,z,c) {
23197  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
23198  cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)*(ptrs0++),(int)_width),
23199  cimg::mod((int)*(ptrs1++),(int)_height),0,c);
23200  }
23201  else if (boundary_conditions==1) // Neumann boundaries.
23202  cimg_forYZC(res,y,z,c) {
23203  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
23204  cimg_forX(res,x) *(ptrd++) = _atXY((int)*(ptrs0++),(int)*(ptrs1++),0,c);
23205  }
23206  else // Dirichlet boundaries.
23207  cimg_forYZC(res,y,z,c) {
23208  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
23209  cimg_forX(res,x) *(ptrd++) = atXY((int)*(ptrs0++),(int)*(ptrs1++),0,c,0);
23210  }
23211  }
23212  }
23213 
23214  } else if (warp._spectrum==3) { // 3d warping.
23215  if (is_relative) { // Relative warp.
23216  if (interpolation==2) { // Cubic interpolation.
23217  if (boundary_conditions==2) // Periodic boundaries.
23218 #ifdef cimg_use_openmp
23219 #pragma omp parallel for collapse(3) if (res.size()>=4096)
23220 #endif
23221  cimg_forYZC(res,y,z,c) {
23222  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
23223  T *ptrd = res.data(0,y,z,c);
23224  cimg_forX(res,x) *(ptrd++) = (T)_cubic_atXYZ(cimg::mod(x - (float)*(ptrs0++),(float)_width),
23225  cimg::mod(y - (float)*(ptrs1++),(float)_height),
23226  cimg::mod(z - (float)*(ptrs2++),(float)_depth),c);
23227  }
23228  else if (boundary_conditions==1) // Neumann boundaries.
23229 #ifdef cimg_use_openmp
23230 #pragma omp parallel for collapse(3) if (res.size()>=4096)
23231 #endif
23232  cimg_forYZC(res,y,z,c) {
23233  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
23234  T *ptrd = res.data(0,y,z,c);
23235  cimg_forX(res,x)
23236  *(ptrd++) = (T)_cubic_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c);
23237  }
23238  else // Dirichlet boundaries.
23239 #ifdef cimg_use_openmp
23240 #pragma omp parallel for collapse(3) if (res.size()>=4096)
23241 #endif
23242  cimg_forYZC(res,y,z,c) {
23243  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
23244  T *ptrd = res.data(0,y,z,c);
23245  cimg_forX(res,x)
23246  *(ptrd++) = (T)cubic_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c,0);
23247  }
23248  } else if (interpolation==1) { // Linear interpolation.
23249  if (boundary_conditions==2) // Periodic boundaries.
23250 #ifdef cimg_use_openmp
23251 #pragma omp parallel for collapse(3) if (res.size()>=1048576)
23252 #endif
23253  cimg_forYZC(res,y,z,c) {
23254  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
23255  T *ptrd = res.data(0,y,z,c);
23256  cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ(cimg::mod(x - (float)*(ptrs0++),(float)_width),
23257  cimg::mod(y - (float)*(ptrs1++),(float)_height),
23258  cimg::mod(z - (float)*(ptrs2++),(float)_depth),c);
23259  }
23260  else if (boundary_conditions==1) // Neumann boundaries.
23261 #ifdef cimg_use_openmp
23262 #pragma omp parallel for collapse(3) if (res.size()>=1048576)
23263 #endif
23264  cimg_forYZC(res,y,z,c) {
23265  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
23266  T *ptrd = res.data(0,y,z,c);
23267  cimg_forX(res,x)
23268  *(ptrd++) = (T)_linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c);
23269  }
23270  else // Dirichlet boundaries.
23271 #ifdef cimg_use_openmp
23272 #pragma omp parallel for collapse(3) if (res.size()>=1048576)
23273 #endif
23274  cimg_forYZC(res,y,z,c) {
23275  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
23276  T *ptrd = res.data(0,y,z,c);
23277  cimg_forX(res,x)
23278  *(ptrd++) = (T)linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c,0);
23279  }
23280  } else { // Nearest neighbor interpolation.
23281  if (boundary_conditions==2) // Periodic boundaries.
23282  cimg_forYZC(res,y,z,c) {
23283  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
23284  T *ptrd = res.data(0,y,z,c);
23285  cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)*(ptrs0++),(int)_width),
23286  cimg::mod(y - (int)*(ptrs1++),(int)_height),
23287  cimg::mod(z - (int)*(ptrs2++),(int)_depth),c);
23288  }
23289  else if (boundary_conditions==1) // Neumann boundaries.
23290  cimg_forYZC(res,y,z,c) {
23291  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
23292  T *ptrd = res.data(0,y,z,c);
23293  cimg_forX(res,x) *(ptrd++) = _atXYZ(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z - (int)*(ptrs2++),c);
23294  }
23295  else // Dirichlet boundaries.
23296  cimg_forYZC(res,y,z,c) {
23297  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
23298  T *ptrd = res.data(0,y,z,c);
23299  cimg_forX(res,x) *(ptrd++) = atXYZ(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z - (int)*(ptrs2++),c,0);
23300  }
23301  }
23302  } else { // Absolute warp.
23303  if (interpolation==2) { // Cubic interpolation.
23304  if (boundary_conditions==2) // Periodic boundaries.
23305 #ifdef cimg_use_openmp
23306 #pragma omp parallel for collapse(3) if (res.size()>=4096)
23307 #endif
23308  cimg_forYZC(res,y,z,c) {
23309  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
23310  T *ptrd = res.data(0,y,z,c);
23311  cimg_forX(res,x) *(ptrd++) = (T)_cubic_atXYZ(cimg::mod((float)*(ptrs0++),(float)_width),
23312  cimg::mod((float)*(ptrs1++),(float)_height),
23313  cimg::mod((float)*(ptrs2++),(float)_depth),c);
23314  }
23315  else if (boundary_conditions==1) // Neumann boundaries.
23316 #ifdef cimg_use_openmp
23317 #pragma omp parallel for collapse(3) if (res.size()>=4096)
23318 #endif
23319  cimg_forYZC(res,y,z,c) {
23320  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
23321  T *ptrd = res.data(0,y,z,c);
23322  cimg_forX(res,x) *(ptrd++) = (T)_cubic_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
23323  }
23324  else // Dirichlet boundaries.
23325 #ifdef cimg_use_openmp
23326 #pragma omp parallel for collapse(3) if (res.size()>=4096)
23327 #endif
23328  cimg_forYZC(res,y,z,c) {
23329  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
23330  T *ptrd = res.data(0,y,z,c);
23331  cimg_forX(res,x) *(ptrd++) = (T)cubic_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c,0);
23332  }
23333  } else if (interpolation==1) { // Linear interpolation.
23334  if (boundary_conditions==2) // Periodic boundaries.
23335 #ifdef cimg_use_openmp
23336 #pragma omp parallel for collapse(3) if (res.size()>=1048576)
23337 #endif
23338  cimg_forYZC(res,y,z,c) {
23339  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
23340  T *ptrd = res.data(0,y,z,c);
23341  cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ(cimg::mod((float)*(ptrs0++),(float)_width),
23342  cimg::mod((float)*(ptrs1++),(float)_height),
23343  cimg::mod((float)*(ptrs2++),(float)_depth),c);
23344  }
23345  else if (boundary_conditions==1) // Neumann boundaries.
23346 #ifdef cimg_use_openmp
23347 #pragma omp parallel for collapse(3) if (res.size()>=1048576)
23348 #endif
23349  cimg_forYZC(res,y,z,c) {
23350  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
23351  T *ptrd = res.data(0,y,z,c);
23352  cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
23353  }
23354  else // Dirichlet boundaries.
23355 #ifdef cimg_use_openmp
23356 #pragma omp parallel for collapse(3) if (res.size()>=1048576)
23357 #endif
23358  cimg_forYZC(res,y,z,c) {
23359  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
23360  T *ptrd = res.data(0,y,z,c);
23361  cimg_forX(res,x) *(ptrd++) = (T)linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c,0);
23362  }
23363  } else { // Nearest-neighbor interpolation.
23364  if (boundary_conditions==2) // Periodic boundaries.
23365  cimg_forYZC(res,y,z,c) {
23366  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
23367  T *ptrd = res.data(0,y,z,c);
23368  cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)*(ptrs0++),(int)_width),
23369  cimg::mod((int)*(ptrs1++),(int)_height),
23370  cimg::mod((int)*(ptrs2++),(int)_depth),c);
23371  }
23372  else if (boundary_conditions==1) // Neumann boundaries.
23373  cimg_forYZC(res,y,z,c) {
23374  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
23375  T *ptrd = res.data(0,y,z,c);
23376  cimg_forX(res,x) *(ptrd++) = _atXYZ((int)*(ptrs0++),(int)*(ptrs1++),(int)*(ptrs2++),c);
23377  }
23378  else // Dirichlet boundaries.
23379  cimg_forYZC(res,y,z,c) {
23380  const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
23381  T *ptrd = res.data(0,y,z,c);
23382  cimg_forX(res,x) *(ptrd++) = atXYZ((int)*(ptrs0++),(int)*(ptrs1++),(int)*(ptrs2++),c,0);
23383  }
23384  }
23385  }
23386  }
23387  return res;
23388  }
23389 
23391 
23396  CImg<T> get_projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) const {
23397  if (is_empty() || _depth<2) return +*this;
23398  const unsigned int
23399  _x0 = (x0>=_width)?_width - 1:x0,
23400  _y0 = (y0>=_height)?_height - 1:y0,
23401  _z0 = (z0>=_depth)?_depth - 1:z0;
23402  const CImg<T>
23403  img_xy = get_crop(0,0,_z0,0,_width-1,_height-1,_z0,_spectrum-1),
23404  img_zy = get_crop(_x0,0,0,0,_x0,_height-1,_depth-1,_spectrum-1).permute_axes("xzyc").
23405  resize(_depth,_height,1,-100,-1),
23406  img_xz = get_crop(0,_y0,0,0,_width-1,_y0,_depth-1,_spectrum-1).resize(_width,_depth,1,-100,-1);
23407  return CImg<T>(_width + _depth,_height + _depth,1,_spectrum,cimg::min(img_xy.min(),img_zy.min(),img_xz.min())).
23408  draw_image(0,0,img_xy).draw_image(img_xy._width,0,img_zy).
23409  draw_image(0,img_xy._height,img_xz);
23410  }
23411 
23413  CImg<T>& projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) {
23414  if (_depth<2) return *this;
23415  return get_projections2d(x0,y0,z0).move_to(*this);
23416  }
23417 
23419 
23430  CImg<T>& crop(const int x0, const int y0, const int z0, const int c0,
23431  const int x1, const int y1, const int z1, const int c1,
23432  const bool boundary_conditions=false) {
23433  return get_crop(x0,y0,z0,c0,x1,y1,z1,c1,boundary_conditions).move_to(*this);
23434  }
23435 
23437  CImg<T> get_crop(const int x0, const int y0, const int z0, const int c0,
23438  const int x1, const int y1, const int z1, const int c1,
23439  const bool boundary_conditions=false) const {
23440  if (is_empty())
23441  throw CImgInstanceException(_cimg_instance
23442  "crop(): Empty instance.",
23443  cimg_instance);
23444  const int
23445  nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0,
23446  ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0,
23447  nz0 = z0<z1?z0:z1, nz1 = z0^z1^nz0,
23448  nc0 = c0<c1?c0:c1, nc1 = c0^c1^nc0;
23449  CImg<T> res(1U + nx1 - nx0,1U + ny1 - ny0,1U + nz1 - nz0,1U + nc1 - nc0);
23450  if (nx0<0 || nx1>=width() || ny0<0 || ny1>=height() || nz0<0 || nz1>=depth() || nc0<0 || nc1>=spectrum()) {
23451  if (boundary_conditions) cimg_forXYZC(res,x,y,z,c) res(x,y,z,c) = _atXYZC(nx0+x,ny0+y,nz0+z,nc0+c);
23452  else res.fill(0).draw_image(-nx0,-ny0,-nz0,-nc0,*this);
23453  } else res.draw_image(-nx0,-ny0,-nz0,-nc0,*this);
23454  return res;
23455  }
23456 
23458  CImg<T>& crop(const int x0, const int y0, const int z0,
23459  const int x1, const int y1, const int z1,
23460  const bool boundary_conditions=false) {
23461  return crop(x0,y0,z0,0,x1,y1,z1,_spectrum-1,boundary_conditions);
23462  }
23463 
23465  CImg<T> get_crop(const int x0, const int y0, const int z0,
23466  const int x1, const int y1, const int z1,
23467  const bool boundary_conditions=false) const {
23468  return get_crop(x0,y0,z0,0,x1,y1,z1,_spectrum-1,boundary_conditions);
23469  }
23470 
23472  CImg<T>& crop(const int x0, const int y0,
23473  const int x1, const int y1,
23474  const bool boundary_conditions=false) {
23475  return crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions);
23476  }
23477 
23479  CImg<T> get_crop(const int x0, const int y0,
23480  const int x1, const int y1,
23481  const bool boundary_conditions=false) const {
23482  return get_crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions);
23483  }
23484 
23486  CImg<T>& crop(const int x0, const int x1, const bool boundary_conditions=false) {
23487  return crop(x0,0,0,0,x1,_height-1,_depth-1,_spectrum-1,boundary_conditions);
23488  }
23489 
23491  CImg<T> get_crop(const int x0, const int x1, const bool boundary_conditions=false) const {
23492  return get_crop(x0,0,0,0,x1,_height-1,_depth-1,_spectrum-1,boundary_conditions);
23493  }
23494 
23496  CImg<T>& autocrop(const T value, const char *const axes="czyx") {
23497  if (is_empty()) return *this;
23498  for (const char *s = axes; *s; ++s) {
23499  const char axis = cimg::uncase(*s);
23500  const CImg<intT> coords = _autocrop(value,axis);
23501  if (coords[0]==-1 && coords[1]==-1) return assign(); // Image has only 'value' pixels.
23502  else switch (axis) {
23503  case 'x' : {
23504  const int x0 = coords[0], x1 = coords[1];
23505  if (x0>=0 && x1>=0) crop(x0,x1);
23506  } break;
23507  case 'y' : {
23508  const int y0 = coords[0], y1 = coords[1];
23509  if (y0>=0 && y1>=0) crop(0,y0,_width-1,y1);
23510  } break;
23511  case 'z' : {
23512  const int z0 = coords[0], z1 = coords[1];
23513  if (z0>=0 && z1>=0) crop(0,0,z0,_width-1,_height-1,z1);
23514  } break;
23515  default : {
23516  const int c0 = coords[0], c1 = coords[1];
23517  if (c0>=0 && c1>=0) crop(0,0,0,c0,_width-1,_height-1,_depth-1,c1);
23518  }
23519  }
23520  }
23521  return *this;
23522  }
23523 
23525  CImg<T> get_autocrop(const T value, const char *const axes="czyx") const {
23526  return (+*this).autocrop(value,axes);
23527  }
23528 
23530 
23534  CImg<T>& autocrop(const T *const color=0, const char *const axes="zyx") {
23535  if (is_empty()) return *this;
23536  if (!color) { // Guess color.
23537  const CImg<T> col1 = get_vector_at(0,0,0);
23538  const unsigned int w = _width, h = _height, d = _depth, s = _spectrum;
23539  autocrop(col1,axes);
23540  if (_width==w && _height==h && _depth==d && _spectrum==s) {
23541  const CImg<T> col2 = get_vector_at(w-1,h-1,d-1);
23542  autocrop(col2,axes);
23543  }
23544  return *this;
23545  }
23546  for (const char *s = axes; *s; ++s) {
23547  const char axis = cimg::uncase(*s);
23548  switch (axis) {
23549  case 'x' : {
23550  int x0 = width(), x1 = -1;
23551  cimg_forC(*this,c) {
23552  const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'x');
23553  const int nx0 = coords[0], nx1 = coords[1];
23554  if (nx0>=0 && nx1>=0) { x0 = cimg::min(x0,nx0); x1 = cimg::max(x1,nx1); }
23555  }
23556  if (x0==width() && x1==-1) return assign(); else crop(x0,x1);
23557  } break;
23558  case 'y' : {
23559  int y0 = height(), y1 = -1;
23560  cimg_forC(*this,c) {
23561  const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'y');
23562  const int ny0 = coords[0], ny1 = coords[1];
23563  if (ny0>=0 && ny1>=0) { y0 = cimg::min(y0,ny0); y1 = cimg::max(y1,ny1); }
23564  }
23565  if (y0==height() && y1==-1) return assign(); else crop(0,y0,_width-1,y1);
23566  } break;
23567  default : {
23568  int z0 = depth(), z1 = -1;
23569  cimg_forC(*this,c) {
23570  const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'z');
23571  const int nz0 = coords[0], nz1 = coords[1];
23572  if (nz0>=0 && nz1>=0) { z0 = cimg::min(z0,nz0); z1 = cimg::max(z1,nz1); }
23573  }
23574  if (z0==depth() && z1==-1) return assign(); else crop(0,0,z0,_width-1,_height-1,z1);
23575  }
23576  }
23577  }
23578  return *this;
23579  }
23580 
23582  CImg<T> get_autocrop(const T *const color=0, const char *const axes="zyx") const {
23583  return (+*this).autocrop(color,axes);
23584  }
23585 
23587  template<typename t> CImg<T>& autocrop(const CImg<t>& color, const char *const axes="zyx") {
23588  return get_autocrop(color,axes).move_to(*this);
23589  }
23590 
23592  template<typename t> CImg<T> get_autocrop(const CImg<t>& color, const char *const axes="zyx") const {
23593  return get_autocrop(color._data,axes);
23594  }
23595 
23596  CImg<intT> _autocrop(const T value, const char axis) const {
23597  CImg<intT> res;
23598  switch (cimg::uncase(axis)) {
23599  case 'x' : {
23600  int x0 = -1, x1 = -1;
23601  cimg_forX(*this,x) cimg_forYZC(*this,y,z,c)
23602  if ((*this)(x,y,z,c)!=value) { x0 = x; x = width(); y = height(); z = depth(); c = spectrum(); }
23603  if (x0>=0) {
23604  for (int x = width()-1; x>=0; --x) cimg_forYZC(*this,y,z,c)
23605  if ((*this)(x,y,z,c)!=value) { x1 = x; x = 0; y = height(); z = depth(); c = spectrum(); }
23606  }
23607  res = CImg<intT>::vector(x0,x1);
23608  } break;
23609  case 'y' : {
23610  int y0 = -1, y1 = -1;
23611  cimg_forY(*this,y) cimg_forXZC(*this,x,z,c)
23612  if ((*this)(x,y,z,c)!=value) { y0 = y; x = width(); y = height(); z = depth(); c = spectrum(); }
23613  if (y0>=0) {
23614  for (int y = height()-1; y>=0; --y) cimg_forXZC(*this,x,z,c)
23615  if ((*this)(x,y,z,c)!=value) { y1 = y; x = width(); y = 0; z = depth(); c = spectrum(); }
23616  }
23617  res = CImg<intT>::vector(y0,y1);
23618  } break;
23619  case 'z' : {
23620  int z0 = -1, z1 = -1;
23621  cimg_forZ(*this,z) cimg_forXYC(*this,x,y,c)
23622  if ((*this)(x,y,z,c)!=value) { z0 = z; x = width(); y = height(); z = depth(); c = spectrum(); }
23623  if (z0>=0) {
23624  for (int z = depth()-1; z>=0; --z) cimg_forXYC(*this,x,y,c)
23625  if ((*this)(x,y,z,c)!=value) { z1 = z; x = width(); y = height(); z = 0; c = spectrum(); }
23626  }
23627  res = CImg<intT>::vector(z0,z1);
23628  } break;
23629  default : {
23630  int c0 = -1, c1 = -1;
23631  cimg_forC(*this,c) cimg_forXYZ(*this,x,y,z)
23632  if ((*this)(x,y,z,c)!=value) { c0 = c; x = width(); y = height(); z = depth(); c = spectrum(); }
23633  if (c0>=0) {
23634  for (int c = spectrum()-1; c>=0; --c) cimg_forXYZ(*this,x,y,z)
23635  if ((*this)(x,y,z,c)!=value) { c1 = c; x = width(); y = height(); z = depth(); c = 0; }
23636  }
23637  res = CImg<intT>::vector(c0,c1);
23638  }
23639  }
23640  return res;
23641  }
23642 
23644 
23647  CImg<T> get_column(const int x0) const {
23648  return get_columns(x0,x0);
23649  }
23650 
23652  CImg<T>& column(const int x0) {
23653  return columns(x0,x0);
23654  }
23655 
23657 
23661  CImg<T>& columns(const int x0, const int x1) {
23662  return get_columns(x0,x1).move_to(*this);
23663  }
23664 
23666  CImg<T> get_columns(const int x0, const int x1) const {
23667  return get_crop(x0,0,0,0,x1,height()-1,depth()-1,spectrum()-1);
23668  }
23669 
23671  CImg<T> get_row(const int y0) const {
23672  return get_rows(y0,y0);
23673  }
23674 
23676 
23679  CImg<T>& row(const int y0) {
23680  return rows(y0,y0);
23681  }
23682 
23684 
23688  CImg<T> get_rows(const int y0, const int y1) const {
23689  return get_crop(0,y0,0,0,width()-1,y1,depth()-1,spectrum()-1);
23690  }
23691 
23693  CImg<T>& rows(const int y0, const int y1) {
23694  return get_rows(y0,y1).move_to(*this);
23695  }
23696 
23698 
23701  CImg<T> get_slice(const int z0) const {
23702  return get_slices(z0,z0);
23703  }
23704 
23706  CImg<T>& slice(const int z0) {
23707  return slices(z0,z0);
23708  }
23709 
23711 
23715  CImg<T> get_slices(const int z0, const int z1) const {
23716  return get_crop(0,0,z0,0,width()-1,height()-1,z1,spectrum()-1);
23717  }
23718 
23720  CImg<T>& slices(const int z0, const int z1) {
23721  return get_slices(z0,z1).move_to(*this);
23722  }
23723 
23725 
23728  CImg<T> get_channel(const int c0) const {
23729  return get_channels(c0,c0);
23730  }
23731 
23733  CImg<T>& channel(const int c0) {
23734  return channels(c0,c0);
23735  }
23736 
23738 
23742  CImg<T> get_channels(const int c0, const int c1) const {
23743  return get_crop(0,0,0,c0,width()-1,height()-1,depth()-1,c1);
23744  }
23745 
23747  CImg<T>& channels(const int c0, const int c1) {
23748  return get_channels(c0,c1).move_to(*this);
23749  }
23750 
23752  CImg<floatT> get_streamline(const float x, const float y, const float z,
23753  const float L=256, const float dl=0.1f,
23754  const unsigned int interpolation_type=2, const bool is_backward_tracking=false,
23755  const bool is_oriented_only=false) const {
23756  if (_spectrum!=2 && _spectrum!=3)
23757  throw CImgInstanceException(_cimg_instance
23758  "streamline(): Instance is not a 2d or 3d vector field.",
23759  cimg_instance);
23760  if (_spectrum==2) {
23761  if (is_oriented_only) {
23762  typename CImg<T>::_functor4d_streamline2d_oriented func(*this);
23763  return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,
23764  0,0,0,_width-1.0f,_height-1.0f,0.0f);
23765  } else {
23766  typename CImg<T>::_functor4d_streamline2d_directed func(*this);
23767  return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,
23768  0,0,0,_width-1.0f,_height-1.0f,0.0f);
23769  }
23770  }
23771  if (is_oriented_only) {
23772  typename CImg<T>::_functor4d_streamline3d_oriented func(*this);
23773  return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,
23774  0,0,0,_width-1.0f,_height-1.0f,_depth-1.0f);
23775  }
23776  typename CImg<T>::_functor4d_streamline3d_directed func(*this);
23777  return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,
23778  0,0,0,_width-1.0f,_height-1.0f,_depth-1.0f);
23779  }
23780 
23782 
23800  template<typename tfunc>
23801  static CImg<floatT> streamline(const tfunc& func,
23802  const float x, const float y, const float z,
23803  const float L=256, const float dl=0.1f,
23804  const unsigned int interpolation_type=2, const bool is_backward_tracking=false,
23805  const bool is_oriented_only=false,
23806  const float x0=0, const float y0=0, const float z0=0,
23807  const float x1=0, const float y1=0, const float z1=0) {
23808  if (dl<=0)
23809  throw CImgArgumentException("CImg<%s>::streamline(): Invalid specified integration length %g "
23810  "(should be >0).",
23811  pixel_type(),
23812  dl);
23813 
23814  const bool is_bounded = (x0!=x1 || y0!=y1 || z0!=z1);
23815  if (L<=0 || (is_bounded && (x<x0 || x>x1 || y<y0 || y>y1 || z<z0 || z>z1))) return CImg<floatT>();
23816  const unsigned int size_L = (unsigned int)cimg::round(L/dl+1);
23817  CImg<floatT> coordinates(size_L,3);
23818  const float dl2 = dl/2;
23819  float
23820  *ptr_x = coordinates.data(0,0),
23821  *ptr_y = coordinates.data(0,1),
23822  *ptr_z = coordinates.data(0,2),
23823  pu = (float)(dl*func(x,y,z,0)),
23824  pv = (float)(dl*func(x,y,z,1)),
23825  pw = (float)(dl*func(x,y,z,2)),
23826  X = x, Y = y, Z = z;
23827 
23828  switch (interpolation_type) {
23829  case 0 : { // Nearest integer interpolation.
23830  cimg_forX(coordinates,l) {
23831  *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
23832  const int
23833  xi = (int)(X>0?X+0.5f:X-0.5f),
23834  yi = (int)(Y>0?Y+0.5f:Y-0.5f),
23835  zi = (int)(Z>0?Z+0.5f:Z-0.5f);
23836  float
23837  u = (float)(dl*func((float)xi,(float)yi,(float)zi,0)),
23838  v = (float)(dl*func((float)xi,(float)yi,(float)zi,1)),
23839  w = (float)(dl*func((float)xi,(float)yi,(float)zi,2));
23840  if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
23841  if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
23842  if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
23843  }
23844  } break;
23845  case 1 : { // First-order interpolation.
23846  cimg_forX(coordinates,l) {
23847  *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
23848  float
23849  u = (float)(dl*func(X,Y,Z,0)),
23850  v = (float)(dl*func(X,Y,Z,1)),
23851  w = (float)(dl*func(X,Y,Z,2));
23852  if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
23853  if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
23854  if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
23855  }
23856  } break;
23857  case 2 : { // Second order interpolation.
23858  cimg_forX(coordinates,l) {
23859  *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
23860  float
23861  u0 = (float)(dl2*func(X,Y,Z,0)),
23862  v0 = (float)(dl2*func(X,Y,Z,1)),
23863  w0 = (float)(dl2*func(X,Y,Z,2));
23864  if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
23865  float
23866  u = (float)(dl*func(X+u0,Y+v0,Z+w0,0)),
23867  v = (float)(dl*func(X+u0,Y+v0,Z+w0,1)),
23868  w = (float)(dl*func(X+u0,Y+v0,Z+w0,2));
23869  if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
23870  if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
23871  if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
23872  }
23873  } break;
23874  default : { // Fourth order interpolation.
23875  cimg_forX(coordinates,x) {
23876  *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
23877  float
23878  u0 = (float)(dl2*func(X,Y,Z,0)),
23879  v0 = (float)(dl2*func(X,Y,Z,1)),
23880  w0 = (float)(dl2*func(X,Y,Z,2));
23881  if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
23882  float
23883  u1 = (float)(dl2*func(X+u0,Y+v0,Z+w0,0)),
23884  v1 = (float)(dl2*func(X+u0,Y+v0,Z+w0,1)),
23885  w1 = (float)(dl2*func(X+u0,Y+v0,Z+w0,2));
23886  if (is_oriented_only && u1*pu + v1*pv + w1*pw<0) { u1 = -u1; v1 = -v1; w1 = -w1; }
23887  float
23888  u2 = (float)(dl2*func(X+u1,Y+v1,Z+w1,0)),
23889  v2 = (float)(dl2*func(X+u1,Y+v1,Z+w1,1)),
23890  w2 = (float)(dl2*func(X+u1,Y+v1,Z+w1,2));
23891  if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u2 = -u2; v2 = -v2; w2 = -w2; }
23892  float
23893  u3 = (float)(dl2*func(X+u2,Y+v2,Z+w2,0)),
23894  v3 = (float)(dl2*func(X+u2,Y+v2,Z+w2,1)),
23895  w3 = (float)(dl2*func(X+u2,Y+v2,Z+w2,2));
23896  if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u3 = -u3; v3 = -v3; w3 = -w3; }
23897  const float
23898  u = (u0 + u3)/3 + (u1 + u2)/1.5f,
23899  v = (v0 + v3)/3 + (v1 + v2)/1.5f,
23900  w = (w0 + w3)/3 + (w1 + w2)/1.5f;
23901  if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
23902  if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
23903  }
23904  }
23905  }
23906  if (ptr_x!=coordinates.data(0,1)) coordinates.resize((int)(ptr_x-coordinates.data()),3,1,1,0);
23907  return coordinates;
23908  }
23909 
23911  static CImg<floatT> streamline(const char *const expression,
23912  const float x, const float y, const float z,
23913  const float L=256, const float dl=0.1f,
23914  const unsigned int interpolation_type=2, const bool is_backward_tracking=true,
23915  const bool is_oriented_only=false,
23916  const float x0=0, const float y0=0, const float z0=0,
23917  const float x1=0, const float y1=0, const float z1=0) {
23918  _functor4d_streamline_expr func(expression);
23919  return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,is_oriented_only,x0,y0,z0,x1,y1,z1);
23920  }
23921 
23923  const CImg<T>& ref;
23924  _functor4d_streamline2d_directed(const CImg<T>& pref):ref(pref) {}
23925  float operator()(const float x, const float y, const float z, const unsigned int c) const {
23926  return c<2?(float)ref._linear_atXY(x,y,(int)z,c):0;
23927  }
23928  };
23929 
23931  const CImg<T>& ref;
23932  _functor4d_streamline3d_directed(const CImg<T>& pref):ref(pref) {}
23933  float operator()(const float x, const float y, const float z, const unsigned int c) const {
23934  return (float)ref._linear_atXYZ(x,y,z,c);
23935  }
23936  };
23937 
23939  const CImg<T>& ref;
23940  CImg<floatT> *pI;
23941  _functor4d_streamline2d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,1,2); }
23942  ~_functor4d_streamline2d_oriented() { delete pI; }
23943  float operator()(const float x, const float y, const float z, const unsigned int c) const {
23944 #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); }
23945  int
23946  xi = (int)x - (x>=0?0:1), nxi = xi + 1,
23947  yi = (int)y - (y>=0?0:1), nyi = yi + 1,
23948  zi = (int)z;
23949  const float
23950  dx = x - xi,
23951  dy = y - yi;
23952  if (c==0) {
23953  CImg<floatT>& I = *pI;
23954  if (xi<0) xi = 0; if (nxi<0) nxi = 0;
23955  if (xi>=ref.width()) xi = ref.width()-1; if (nxi>=ref.width()) nxi = ref.width()-1;
23956  if (yi<0) yi = 0; if (nyi<0) nyi = 0;
23957  if (yi>=ref.height()) yi = ref.height()-1; if (nyi>=ref.height()) nyi = ref.height()-1;
23958  I(0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,1) = (float)ref(xi,yi,zi,1);
23959  I(1,0,0) = (float)ref(nxi,yi,zi,0); I(1,0,1) = (float)ref(nxi,yi,zi,1);
23960  I(1,1,0) = (float)ref(nxi,nyi,zi,0); I(1,1,1) = (float)ref(nxi,nyi,zi,1);
23961  I(0,1,0) = (float)ref(xi,nyi,zi,0); I(0,1,1) = (float)ref(xi,nyi,zi,1);
23962  _cimg_vecalign2d(1,0); _cimg_vecalign2d(1,1); _cimg_vecalign2d(0,1);
23963  }
23964  return c<2?(float)pI->_linear_atXY(dx,dy,0,c):0;
23965  }
23966  };
23967 
23969  const CImg<T>& ref;
23970  CImg<floatT> *pI;
23971  _functor4d_streamline3d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,2,3); }
23972  ~_functor4d_streamline3d_oriented() { delete pI; }
23973  float operator()(const float x, const float y, const float z, const unsigned int c) const {
23974 #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) { \
23975  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); }
23976  int
23977  xi = (int)x - (x>=0?0:1), nxi = xi + 1,
23978  yi = (int)y - (y>=0?0:1), nyi = yi + 1,
23979  zi = (int)z - (z>=0?0:1), nzi = zi + 1;
23980  const float
23981  dx = x - xi,
23982  dy = y - yi,
23983  dz = z - zi;
23984  if (c==0) {
23985  CImg<floatT>& I = *pI;
23986  if (xi<0) xi = 0; if (nxi<0) nxi = 0;
23987  if (xi>=ref.width()) xi = ref.width()-1; if (nxi>=ref.width()) nxi = ref.width()-1;
23988  if (yi<0) yi = 0; if (nyi<0) nyi = 0;
23989  if (yi>=ref.height()) yi = ref.height()-1; if (nyi>=ref.height()) nyi = ref.height()-1;
23990  if (zi<0) zi = 0; if (nzi<0) nzi = 0;
23991  if (zi>=ref.depth()) zi = ref.depth()-1; if (nzi>=ref.depth()) nzi = ref.depth()-1;
23992  I(0,0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,0,1) = (float)ref(xi,yi,zi,1);
23993  I(0,0,0,2) = (float)ref(xi,yi,zi,2); I(1,0,0,0) = (float)ref(nxi,yi,zi,0);
23994  I(1,0,0,1) = (float)ref(nxi,yi,zi,1); I(1,0,0,2) = (float)ref(nxi,yi,zi,2);
23995  I(1,1,0,0) = (float)ref(nxi,nyi,zi,0); I(1,1,0,1) = (float)ref(nxi,nyi,zi,1);
23996  I(1,1,0,2) = (float)ref(nxi,nyi,zi,2); I(0,1,0,0) = (float)ref(xi,nyi,zi,0);
23997  I(0,1,0,1) = (float)ref(xi,nyi,zi,1); I(0,1,0,2) = (float)ref(xi,nyi,zi,2);
23998  I(0,0,1,0) = (float)ref(xi,yi,nzi,0); I(0,0,1,1) = (float)ref(xi,yi,nzi,1);
23999  I(0,0,1,2) = (float)ref(xi,yi,nzi,2); I(1,0,1,0) = (float)ref(nxi,yi,nzi,0);
24000  I(1,0,1,1) = (float)ref(nxi,yi,nzi,1); I(1,0,1,2) = (float)ref(nxi,yi,nzi,2);
24001  I(1,1,1,0) = (float)ref(nxi,nyi,nzi,0); I(1,1,1,1) = (float)ref(nxi,nyi,nzi,1);
24002  I(1,1,1,2) = (float)ref(nxi,nyi,nzi,2); I(0,1,1,0) = (float)ref(xi,nyi,nzi,0);
24003  I(0,1,1,1) = (float)ref(xi,nyi,nzi,1); I(0,1,1,2) = (float)ref(xi,nyi,nzi,2);
24004  _cimg_vecalign3d(1,0,0); _cimg_vecalign3d(1,1,0); _cimg_vecalign3d(0,1,0);
24005  _cimg_vecalign3d(0,0,1); _cimg_vecalign3d(1,0,1); _cimg_vecalign3d(1,1,1); _cimg_vecalign3d(0,1,1);
24006  }
24007  return (float)pI->_linear_atXYZ(dx,dy,dz,c);
24008  }
24009  };
24010 
24012  _cimg_math_parser *mp;
24013  ~_functor4d_streamline_expr() { delete mp; }
24014  _functor4d_streamline_expr(const char *const expr):mp(0) {
24015  mp = new _cimg_math_parser(CImg<T>::empty(),expr,"streamline");
24016  }
24017  float operator()(const float x, const float y, const float z, const unsigned int c) const {
24018  return (float)(*mp)(x,y,z,c);
24019  }
24020  };
24021 
24023 
24030  CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
24031  const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) {
24032  const unsigned int beg = (unsigned int)offset(x0,y0,z0,c0), end = offset(x1,y0,z0,c0);
24033  if (beg>end || beg>=size() || end>=size())
24034  throw CImgArgumentException(_cimg_instance
24035  "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).",
24036  cimg_instance,
24037  x0,x1,y0,z0,c0);
24038 
24039  return CImg<T>(_data+beg,x1-x0+1,1,1,1,true);
24040  }
24041 
24043  const CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
24044  const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) const {
24045  const unsigned int beg = (unsigned int)offset(x0,y0,z0,c0), end = offset(x1,y0,z0,c0);
24046  if (beg>end || beg>=size() || end>=size())
24047  throw CImgArgumentException(_cimg_instance
24048  "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).",
24049  cimg_instance,
24050  x0,x1,y0,z0,c0);
24051 
24052  return CImg<T>(_data+beg,x1-x0+1,1,1,1,true);
24053  }
24054 
24056 
24062  CImg<T> get_shared_rows(const unsigned int y0, const unsigned int y1,
24063  const unsigned int z0=0, const unsigned int c0=0) {
24064  const unsigned int beg = offset(0,y0,z0,c0), end = offset(0,y1,z0,c0);
24065  if (beg>end || beg>=size() || end>=size())
24066  throw CImgArgumentException(_cimg_instance
24067  "get_shared_rows(): Invalid request of a shared-memory subset "
24068  "(0->%u,%u->%u,%u,%u).",
24069  cimg_instance,
24070  _width-1,y0,y1,z0,c0);
24071 
24072  return CImg<T>(_data+beg,_width,y1-y0+1,1,1,true);
24073  }
24074 
24076  const CImg<T> get_shared_rows(const unsigned int y0, const unsigned int y1,
24077  const unsigned int z0=0, const unsigned int c0=0) const {
24078  const unsigned int beg = offset(0,y0,z0,c0), end = offset(0,y1,z0,c0);
24079  if (beg>end || beg>=size() || end>=size())
24080  throw CImgArgumentException(_cimg_instance
24081  "get_shared_rows(): Invalid request of a shared-memory subset "
24082  "(0->%u,%u->%u,%u,%u).",
24083  cimg_instance,
24084  _width-1,y0,y1,z0,c0);
24085 
24086  return CImg<T>(_data+beg,_width,y1-y0+1,1,1,true);
24087  }
24088 
24090 
24095  CImg<T> get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) {
24096  return get_shared_rows(y0,y0,z0,c0);
24097  }
24098 
24100  const CImg<T> get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) const {
24101  return get_shared_rows(y0,y0,z0,c0);
24102  }
24103 
24105 
24110  CImg<T> get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) {
24111  const unsigned int beg = offset(0,0,z0,c0), end = offset(0,0,z1,c0);
24112  if (beg>end || beg>=size() || end>=size())
24113  throw CImgArgumentException(_cimg_instance
24114  "get_shared_slices(): Invalid request of a shared-memory subset "
24115  "(0->%u,0->%u,%u->%u,%u).",
24116  cimg_instance,
24117  _width-1,_height-1,z0,z1,c0);
24118 
24119  return CImg<T>(_data+beg,_width,_height,z1-z0+1,1,true);
24120  }
24121 
24123  const CImg<T> get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) const {
24124  const unsigned int beg = offset(0,0,z0,c0), end = offset(0,0,z1,c0);
24125  if (beg>end || beg>=size() || end>=size())
24126  throw CImgArgumentException(_cimg_instance
24127  "get_shared_slices(): Invalid request of a shared-memory subset "
24128  "(0->%u,0->%u,%u->%u,%u).",
24129  cimg_instance,
24130  _width-1,_height-1,z0,z1,c0);
24131 
24132  return CImg<T>(_data+beg,_width,_height,z1-z0+1,1,true);
24133  }
24134 
24136 
24140  CImg<T> get_shared_slice(const unsigned int z0, const unsigned int c0=0) {
24141  return get_shared_slices(z0,z0,c0);
24142  }
24143 
24145  const CImg<T> get_shared_slice(const unsigned int z0, const unsigned int c0=0) const {
24146  return get_shared_slices(z0,z0,c0);
24147  }
24148 
24150 
24154  CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) {
24155  const unsigned int beg = offset(0,0,0,c0), end = offset(0,0,0,c1);
24156  if (beg>end || beg>=size() || end>=size())
24157  throw CImgArgumentException(_cimg_instance
24158  "get_shared_channels(): Invalid request of a shared-memory subset "
24159  "(0->%u,0->%u,0->%u,%u->%u).",
24160  cimg_instance,
24161  _width-1,_height-1,_depth-1,c0,c1);
24162 
24163  return CImg<T>(_data+beg,_width,_height,_depth,c1-c0+1,true);
24164  }
24165 
24167  const CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) const {
24168  const unsigned int beg = offset(0,0,0,c0), end = offset(0,0,0,c1);
24169  if (beg>end || beg>=size() || end>=size())
24170  throw CImgArgumentException(_cimg_instance
24171  "get_shared_channels(): Invalid request of a shared-memory subset "
24172  "(0->%u,0->%u,0->%u,%u->%u).",
24173  cimg_instance,
24174  _width-1,_height-1,_depth-1,c0,c1);
24175 
24176  return CImg<T>(_data+beg,_width,_height,_depth,c1-c0+1,true);
24177  }
24178 
24180 
24183  CImg<T> get_shared_channel(const unsigned int c0) {
24184  return get_shared_channels(c0,c0);
24185  }
24186 
24188  const CImg<T> get_shared_channel(const unsigned int c0) const {
24189  return get_shared_channels(c0,c0);
24190  }
24191 
24194  return CImg<T>(_data,_width,_height,_depth,_spectrum,true);
24195  }
24196 
24198  const CImg<T> get_shared() const {
24199  return CImg<T>(_data,_width,_height,_depth,_spectrum,true);
24200  }
24201 
24203 
24211  CImgList<T> get_split(const char axis, const int nb=0) const {
24212  CImgList<T> res;
24213  if (is_empty()) return res;
24214  const char _axis = cimg::uncase(axis);
24215 
24216  if (nb<=0) { // Split by bloc size.
24217  const unsigned int dp = (unsigned int)(nb?-nb:1);
24218  switch (_axis) {
24219  case 'x': {
24220  if (_width>dp) {
24221  res.assign(_width/dp+(_width%dp?1:0),1,1);
24222  const unsigned int pe = _width - dp;
24223 #ifdef cimg_use_openmp
24224 #pragma omp parallel for if (res._width>=128 && _height*_depth*_spectrum>=128)
24225 #endif
24226  for (unsigned int p = 0; p<pe; p+=dp)
24227  get_crop(p,0,0,0,p+dp-1,_height-1,_depth-1,_spectrum-1).move_to(res[p/dp]);
24228  get_crop((res._width-1)*dp,0,0,0,_width-1,_height-1,_depth-1,_spectrum-1).move_to(res.back());
24229  } else res.assign(*this);
24230  } break;
24231  case 'y': {
24232  if (_height>dp) {
24233  res.assign(_height/dp+(_height%dp?1:0),1,1);
24234  const unsigned int pe = _height - dp;
24235 #ifdef cimg_use_openmp
24236 #pragma omp parallel for if (res._width>=128 && _width*_depth*_spectrum>=128)
24237 #endif
24238  for (unsigned int p = 0; p<pe; p+=dp)
24239  get_crop(0,p,0,0,_width-1,p+dp-1,_depth-1,_spectrum-1).move_to(res[p/dp]);
24240  get_crop(0,(res._width-1)*dp,0,0,_width-1,_height-1,_depth-1,_spectrum-1).move_to(res.back());
24241  } else res.assign(*this);
24242  } break;
24243  case 'z': {
24244  if (_depth>dp) {
24245  res.assign(_depth/dp+(_depth%dp?1:0),1,1);
24246  const unsigned int pe = _depth - dp;
24247 #ifdef cimg_use_openmp
24248 #pragma omp parallel for if (res._width>=128 && _width*_height*_spectrum>=128)
24249 #endif
24250  for (unsigned int p = 0; p<pe; p+=dp)
24251  get_crop(0,0,p,0,_width-1,_height-1,p+dp-1,_spectrum-1).move_to(res[p/dp]);
24252  get_crop(0,0,(res._width-1)*dp,0,_width-1,_height-1,_depth-1,_spectrum-1).move_to(res.back());
24253  } else res.assign(*this);
24254  } break;
24255  default : {
24256  if (_spectrum>dp) {
24257  res.assign(_spectrum/dp+(_spectrum%dp?1:0),1,1);
24258  const unsigned int pe = _spectrum - dp;
24259 #ifdef cimg_use_openmp
24260 #pragma omp parallel for if (res._width>=128 && _width*_height*_depth>=128)
24261 #endif
24262  for (unsigned int p = 0; p<pe; p+=dp)
24263  get_crop(0,0,0,p,_width-1,_height-1,_depth-1,p+dp-1).move_to(res[p/dp]);
24264  get_crop(0,0,0,(res._width-1)*dp,_width-1,_height-1,_depth-1,_spectrum-1).move_to(res.back());
24265  } else res.assign(*this);
24266  }
24267  }
24268  } else { // Split by number of (non-homogeneous) blocs.
24269  const unsigned int siz = _axis=='x'?_width:_axis=='y'?_height:_axis=='z'?_depth:_axis=='c'?_spectrum:0;
24270  if ((unsigned int)nb>siz)
24271  throw CImgArgumentException(_cimg_instance
24272  "get_split(): Instance cannot be split along %c-axis into %u blocs.",
24273  cimg_instance,
24274  axis,nb);
24275  if (nb==1) res.assign(*this);
24276  else {
24277  int err = (int)siz;
24278  unsigned int _p = 0;
24279  switch (_axis) {
24280  case 'x' : {
24281  cimg_forX(*this,p) if ((err-=nb)<=0) {
24282  get_crop(_p,0,0,0,p,_height-1,_depth-1,_spectrum-1).move_to(res);
24283  err+=(int)siz;
24284  _p=p+1;
24285  }
24286  } break;
24287  case 'y' : {
24288  cimg_forY(*this,p) if ((err-=nb)<=0) {
24289  get_crop(0,_p,0,0,_width-1,p,_depth-1,_spectrum-1).move_to(res);
24290  err+=(int)siz;
24291  _p=p+1;
24292  }
24293  } break;
24294  case 'z' : {
24295  cimg_forZ(*this,p) if ((err-=nb)<=0) {
24296  get_crop(0,0,_p,0,_width-1,_height-1,p,_spectrum-1).move_to(res);
24297  err+=(int)siz;
24298  _p=p+1;
24299  }
24300  } break;
24301  default : {
24302  cimg_forC(*this,p) if ((err-=nb)<=0) {
24303  get_crop(0,0,0,_p,_width-1,_height-1,_depth-1,p).move_to(res);
24304  err+=(int)siz;
24305  _p=p+1;
24306  }
24307  }
24308  }
24309  }
24310  }
24311  return res;
24312  }
24313 
24315 
24320  CImgList<T> get_split(const T value, const bool keep_values, const bool is_shared) const {
24321  CImgList<T> res;
24322  if (is_empty()) return res;
24323  for (const T *ps = _data, *_ps = ps, *const pe = end(); ps<pe; ) {
24324  while (_ps<pe && *_ps==value) ++_ps;
24325  unsigned int siz = _ps - ps;
24326  if (siz && keep_values) res.insert(CImg<T>(ps,1,siz,1,1,is_shared),~0U,is_shared);
24327  ps = _ps;
24328  while (_ps<pe && *_ps!=value) ++_ps;
24329  siz = _ps - ps;
24330  if (siz) res.insert(CImg<T>(ps,1,siz,1,1,is_shared),~0U,is_shared);
24331  ps = _ps;
24332  }
24333  return res;
24334  }
24335 
24337 
24342  template<typename t>
24343  CImgList<T> get_split(const CImg<t>& values, const bool keep_values, const bool is_shared) const {
24344  CImgList<T> res;
24345  if (is_empty()) return res;
24346  if (!values) return CImgList<T>(*this);
24347  if (values.size()==1) return get_split(*values,keep_values,is_shared);
24348  const t *pve = values.end();
24349  for (const T *ps = _data, *_ps = ps, *const pe = end(); ps<pe; ) {
24350 
24351  // Try to find match from current position.
24352  const t *pv = 0;
24353  do {
24354  pv = values._data;
24355  const T *__ps = _ps;
24356  while (__ps<pe && pv<pve && *__ps==(T)*pv) { ++__ps; ++pv; }
24357  if (pv==pve) _ps = __ps;
24358  } while (pv==pve);
24359  unsigned int siz = _ps - ps;
24360  if (siz && keep_values) res.insert(CImg<T>(ps,1,siz,1,1,is_shared),~0U,is_shared); // If match found.
24361  ps = _ps;
24362 
24363  // Try to find non-match from current position.
24364  do {
24365  pv = values._data;
24366  while (_ps<pe && *_ps!=(T)*pv) ++_ps;
24367  if (_ps<pe) {
24368  const T *__ps = _ps + 1;
24369  ++pv;
24370  while (__ps<pe && pv<pve && *__ps==(T)*pv) { ++__ps; ++pv; }
24371  if (pv!=pve) _ps = __ps;
24372  }
24373  } while (_ps<pe && pv!=pve);
24374 
24375  // Here, EOF of match found.
24376  siz = _ps - ps;
24377  if (siz) res.insert(CImg<T>(ps,1,siz,1,1,is_shared),~0U,is_shared);
24378  ps = _ps;
24379  }
24380  return res;
24381  }
24382 
24384  CImgList<T> get_split(const bool is_shared) const {
24385  CImgList<T> res;
24386  if (is_empty()) return res;
24387  T *p0 = _data, current = *p0;
24388  cimg_for(*this,p,T) if (*p!=current) {
24389  res.insert(CImg<T>(p0,1,p-p0,1,1,is_shared),~0U,is_shared); p0 = p; current = *p;
24390  }
24391  res.insert(CImg<T>(p0,1,end()-p0,1,1,is_shared),~0U,is_shared);
24392  return res;
24393  }
24394 
24396 
24401  template<typename t>
24402  CImg<T>& append(const CImg<t>& img, const char axis='x', const float align=0) {
24403  if (is_empty()) return assign(img,false);
24404  if (!img) return *this;
24405  return CImgList<T>(*this,true).insert(img).get_append(axis,align).move_to(*this);
24406  }
24407 
24409  CImg<T>& append(const CImg<T>& img, const char axis='x', const float align=0) {
24410  if (is_empty()) return assign(img,false);
24411  if (!img) return *this;
24412  return CImgList<T>(*this,img,true).get_append(axis,align).move_to(*this);
24413  }
24414 
24416  template<typename t>
24417  CImg<_cimg_Tt> get_append(const CImg<T>& img, const char axis='x', const float align=0) const {
24418  if (is_empty()) return +img;
24419  if (!img) return +*this;
24420  return CImgList<_cimg_Tt>(*this,true).insert(img).get_append(axis,align);
24421  }
24422 
24424  CImg<T> get_append(const CImg<T>& img, const char axis='x', const float align=0) const {
24425  if (is_empty()) return +img;
24426  if (!img) return +*this;
24427  return CImgList<T>(*this,img,true).get_append(axis,align);
24428  }
24429 
24431  //---------------------------------------
24432  //
24434 
24435  //---------------------------------------
24436 
24438 
24446  template<typename t>
24447  CImg<T>& correlate(const CImg<t>& mask, const unsigned int boundary_conditions=1, const bool is_normalized=false) {
24448  if (is_empty() || !mask) return *this;
24449  return get_correlate(mask,boundary_conditions,is_normalized).move_to(*this);
24450  }
24451 
24453  template<typename t>
24454  CImg<_cimg_Ttfloat> get_correlate(const CImg<t>& mask, const unsigned int boundary_conditions=1,
24455  const bool is_normalized=false) const {
24456  if (is_empty() || !mask) return *this;
24457  typedef _cimg_Ttfloat Ttfloat;
24458  CImg<Ttfloat> res(_width,_height,_depth,cimg::max(_spectrum,mask._spectrum));
24459  if (boundary_conditions && mask._width==mask._height &&
24460  ((mask._depth==1 && mask._width<=5) || (mask._depth==mask._width && mask._width<=3))) {
24461  // A special optimization is done for 2x2, 3x3, 4x4, 5x5, 2x2x2 and 3x3x3 mask (with boundary_conditions=1)
24462  Ttfloat *ptrd = res._data;
24463  switch (mask._depth) {
24464  case 3 : {
24465  T I[27] = { 0 };
24466  cimg_forC(res,c) {
24467  const CImg<T> _img = get_shared_channel(c%_spectrum);
24468  const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
24469  if (is_normalized) {
24470  const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
24471  cimg_for3x3x3(_img,x,y,z,0,I,T) {
24472  const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] +
24473  I[ 3]*I[ 3] + I[ 4]*I[ 4] + I[ 5]*I[ 5] +
24474  I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] +
24475  I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] +
24476  I[12]*I[12] + I[13]*I[13] + I[14]*I[14] +
24477  I[15]*I[15] + I[16]*I[16] + I[17]*I[17] +
24478  I[18]*I[18] + I[19]*I[19] + I[20]*I[20] +
24479  I[21]*I[21] + I[22]*I[22] + I[23]*I[23] +
24480  I[24]*I[24] + I[25]*I[25] + I[26]*I[26]);
24481  *(ptrd++) = (Ttfloat)(N?(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] +
24482  I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] +
24483  I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] +
24484  I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] +
24485  I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] +
24486  I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] +
24487  I[18]*_mask[18] + I[19]*_mask[19] + I[20]*_mask[20] +
24488  I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] +
24489  I[24]*_mask[24] + I[25]*_mask[25] + I[26]*_mask[26])/std::sqrt(N):0);
24490  }
24491  } else cimg_for3x3x3(_img,x,y,z,0,I,T)
24492  *(ptrd++) = (Ttfloat)(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] +
24493  I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] +
24494  I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] +
24495  I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] +
24496  I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] +
24497  I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] +
24498  I[18]*_mask[18] + I[19]*_mask[19] + I[20]*_mask[20] +
24499  I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] +
24500  I[24]*_mask[24] + I[25]*_mask[25] + I[26]*_mask[26]);
24501  }
24502  } break;
24503  case 2 : {
24504  T I[8] = { 0 };
24505  cimg_forC(res,c) {
24506  const CImg<T> _img = get_shared_channel(c%_spectrum);
24507  const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
24508  if (is_normalized) {
24509  const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
24510  cimg_for2x2x2(_img,x,y,z,0,I,T) {
24511  const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] +
24512  I[2]*I[2] + I[3]*I[3] +
24513  I[4]*I[4] + I[5]*I[5] +
24514  I[6]*I[6] + I[7]*I[7]);
24515  *(ptrd++) = (Ttfloat)(N?(I[0]*_mask[0] + I[1]*_mask[1] +
24516  I[2]*_mask[2] + I[3]*_mask[3] +
24517  I[4]*_mask[4] + I[5]*_mask[5] +
24518  I[6]*_mask[6] + I[7]*_mask[7])/std::sqrt(N):0);
24519  }
24520  } else cimg_for2x2x2(_img,x,y,z,0,I,T)
24521  *(ptrd++) = (Ttfloat)(I[0]*_mask[0] + I[1]*_mask[1] +
24522  I[2]*_mask[2] + I[3]*_mask[3] +
24523  I[4]*_mask[4] + I[5]*_mask[5] +
24524  I[6]*_mask[6] + I[7]*_mask[7]);
24525  }
24526  } break;
24527  default :
24528  case 1 :
24529  switch (mask._width) {
24530  case 6 : {
24531  T I[36] = { 0 };
24532  cimg_forC(res,c) {
24533  const CImg<T> _img = get_shared_channel(c%_spectrum);
24534  const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
24535  if (is_normalized) {
24536  const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
24537  cimg_forZ(_img,z) cimg_for6x6(_img,x,y,z,0,I,T) {
24538  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] +
24539  I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] +
24540  I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + I[15]*I[15] + I[16]*I[16] + I[17]*I[17] +
24541  I[18]*I[18] + I[19]*I[19] + I[20]*I[20] + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] +
24542  I[24]*I[24] + I[25]*I[25] + I[26]*I[26] + I[27]*I[27] + I[28]*I[28] + I[29]*I[29] +
24543  I[30]*I[30] + I[31]*I[31] + I[32]*I[32] + I[33]*I[33] + I[34]*I[34] + I[35]*I[35]);
24544  *(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] +
24545  I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] +
24546  I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] + I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] +
24547  I[18]*_mask[18] + I[19]*_mask[19] + I[20]*_mask[20] + I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] +
24548  I[24]*_mask[24] + I[25]*_mask[25] + I[26]*_mask[26] + I[27]*_mask[27] + I[28]*_mask[28] + I[29]*_mask[29] +
24549  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);
24550  }
24551  } else cimg_forZ(_img,z) cimg_for6x6(_img,x,y,z,0,I,T)
24552  *(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] +
24553  I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] +
24554  I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] + I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] +
24555  I[18]*_mask[18] + I[19]*_mask[19] + I[20]*_mask[20] + I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] +
24556  I[24]*_mask[24] + I[25]*_mask[25] + I[26]*_mask[26] + I[27]*_mask[27] + I[28]*_mask[28] + I[29]*_mask[29] +
24557  I[30]*_mask[30] + I[31]*_mask[31] + I[32]*_mask[32] + I[33]*_mask[33] + I[34]*_mask[34] + I[35]*_mask[35]);
24558  }
24559  } break;
24560  case 5 : {
24561  T I[25] = { 0 };
24562  cimg_forC(res,c) {
24563  const CImg<T> _img = get_shared_channel(c%_spectrum);
24564  const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
24565  if (is_normalized) {
24566  const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
24567  cimg_forZ(_img,z) cimg_for5x5(_img,x,y,z,0,I,T) {
24568  const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] +
24569  I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] +
24570  I[10]*I[10] + I[11]*I[11] + I[12]*I[12] + I[13]*I[13] + I[14]*I[14] +
24571  I[15]*I[15] + I[16]*I[16] + I[17]*I[17] + I[18]*I[18] + I[19]*I[19] +
24572  I[20]*I[20] + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] + I[24]*I[24]);
24573  *(ptrd++) = (Ttfloat)(N?(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] +
24574  I[ 5]*_mask[ 5] + I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] +
24575  I[10]*_mask[10] + I[11]*_mask[11] + I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] +
24576  I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] + I[18]*_mask[18] + I[19]*_mask[19] +
24577  I[20]*_mask[20] + I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] + I[24]*_mask[24])/std::sqrt(N):0);
24578  }
24579  } else cimg_forZ(_img,z) cimg_for5x5(_img,x,y,z,0,I,T)
24580  *(ptrd++) = (Ttfloat)(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] +
24581  I[ 5]*_mask[ 5] + I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] +
24582  I[10]*_mask[10] + I[11]*_mask[11] + I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] +
24583  I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] + I[18]*_mask[18] + I[19]*_mask[19] +
24584  I[20]*_mask[20] + I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] + I[24]*_mask[24]);
24585  }
24586  } break;
24587  case 4 : {
24588  T I[16] = { 0 };
24589  cimg_forC(res,c) {
24590  const CImg<T> _img = get_shared_channel(c%_spectrum);
24591  const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
24592  if (is_normalized) {
24593  const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
24594  cimg_forZ(_img,z) cimg_for4x4(_img,x,y,z,0,I,T) {
24595  const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] +
24596  I[ 4]*I[ 4] + I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] +
24597  I[ 8]*I[ 8] + I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] +
24598  I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + I[15]*I[15]);
24599  *(ptrd++) = (Ttfloat)(N?(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] +
24600  I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] + I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] +
24601  I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] +
24602  I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] + I[15]*_mask[15])/std::sqrt(N):0);
24603  }
24604  } else cimg_forZ(_img,z) cimg_for4x4(_img,x,y,z,0,I,T)
24605  *(ptrd++) = (Ttfloat)(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] +
24606  I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] + I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] +
24607  I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] +
24608  I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] + I[15]*_mask[15]);
24609  }
24610  } break;
24611  case 3 : {
24612  T I[9] = { 0 };
24613  cimg_forC(res,c) {
24614  const CImg<T> _img = get_shared_channel(c%_spectrum);
24615  const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
24616  if (is_normalized) {
24617  const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
24618  cimg_forZ(_img,z) cimg_for3x3(_img,x,y,z,0,I,T) {
24619  const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] + I[2]*I[2] +
24620  I[3]*I[3] + I[4]*I[4] + I[5]*I[5] +
24621  I[6]*I[6] + I[7]*I[7] + I[8]*I[8]);
24622  *(ptrd++) = (Ttfloat)(N?(I[0]*_mask[0] + I[1]*_mask[1] + I[2]*_mask[2] +
24623  I[3]*_mask[3] + I[4]*_mask[4] + I[5]*_mask[5] +
24624  I[6]*_mask[6] + I[7]*_mask[7] + I[8]*_mask[8])/std::sqrt(N):0);
24625  }
24626  } else cimg_forZ(_img,z) cimg_for3x3(_img,x,y,z,0,I,T)
24627  *(ptrd++) = (Ttfloat)(I[0]*_mask[0] + I[1]*_mask[1] + I[2]*_mask[2] +
24628  I[3]*_mask[3] + I[4]*_mask[4] + I[5]*_mask[5] +
24629  I[6]*_mask[6] + I[7]*_mask[7] + I[8]*_mask[8]);
24630  }
24631  } break;
24632  case 2 : {
24633  T I[4] = { 0 };
24634  cimg_forC(res,c) {
24635  const CImg<T> _img = get_shared_channel(c%_spectrum);
24636  const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
24637  if (is_normalized) {
24638  const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
24639  cimg_forZ(_img,z) cimg_for2x2(_img,x,y,z,0,I,T) {
24640  const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] +
24641  I[2]*I[2] + I[3]*I[3]);
24642  *(ptrd++) = (Ttfloat)(N?(I[0]*_mask[0] + I[1]*_mask[1] +
24643  I[2]*_mask[2] + I[3]*_mask[3])/std::sqrt(N):0);
24644  }
24645  } else cimg_forZ(_img,z) cimg_for2x2(_img,x,y,z,0,I,T)
24646  *(ptrd++) = (Ttfloat)(I[0]*_mask[0] + I[1]*_mask[1] +
24647  I[2]*_mask[2] + I[3]*_mask[3]);
24648  }
24649  } break;
24650  case 1 :
24651  if (is_normalized) res.fill(1);
24652  else cimg_forC(res,c) {
24653  const CImg<T> _img = get_shared_channel(c%_spectrum);
24654  const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
24655  res.get_shared_channel(c).assign(_img)*=_mask[0];
24656  }
24657  break;
24658  }
24659  }
24660  } else { // Generic version for other masks and borders conditions.
24661  const int
24662  mx2 = mask.width()/2, my2 = mask.height()/2, mz2 = mask.depth()/2,
24663  mx1 = mx2 - 1 + (mask.width()%2), my1 = my2 - 1 + (mask.height()%2), mz1 = mz2 - 1 + (mask.depth()%2),
24664  mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
24665 #ifdef cimg_use_openmp
24666 #pragma omp parallel for if (res._spectrum>=2)
24667 #endif
24668  cimg_forC(res,c) {
24669  const CImg<T> _img = get_shared_channel(c%_spectrum);
24670  const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
24671  if (is_normalized) { // Normalized correlation.
24672  const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
24673 #ifdef cimg_use_openmp
24674 #pragma omp parallel for collapse(3) if (_width*_height*_depth>=32768)
24675 #endif
24676  for (int z = mz1; z<mze; ++z)
24677  for (int y = my1; y<mye; ++y)
24678  for (int x = mx1; x<mxe; ++x) {
24679  Ttfloat val = 0, N = 0;
24680  for (int zm = -mz1; zm<=mz2; ++zm)
24681  for (int ym = -my1; ym<=my2; ++ym)
24682  for (int xm = -mx1; xm<=mx2; ++xm) {
24683  const Ttfloat _val = (Ttfloat)_img(x+xm,y+ym,z+zm);
24684  val+=_val*_mask(mx1+xm,my1+ym,mz1+zm);
24685  N+=_val*_val;
24686  }
24687  N*=M;
24688  res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0);
24689  }
24690  if (boundary_conditions)
24691 #ifdef cimg_use_openmp
24692 #pragma omp parallel for collapse(2) if (_width>=256 && _height*_depth>=128)
24693 #endif
24694  cimg_forYZ(res,y,z)
24695  for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
24696  Ttfloat val = 0, N = 0;
24697  for (int zm = -mz1; zm<=mz2; ++zm)
24698  for (int ym = -my1; ym<=my2; ++ym)
24699  for (int xm = -mx1; xm<=mx2; ++xm) {
24700  const Ttfloat _val = (Ttfloat)_img._atXYZ(x+xm,y+ym,z+zm);
24701  val+=_val*_mask(mx1+xm,my1+ym,mz1+zm);
24702  N+=_val*_val;
24703  }
24704  N*=M;
24705  res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0);
24706  }
24707  else
24708 #ifdef cimg_use_openmp
24709 #pragma omp parallel for collapse(2) if (_width>=256 && _height*_depth>=128)
24710 #endif
24711  cimg_forYZ(res,y,z)
24712  for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
24713  Ttfloat val = 0, N = 0;
24714  for (int zm = -mz1; zm<=mz2; ++zm)
24715  for (int ym = -my1; ym<=my2; ++ym)
24716  for (int xm = -mx1; xm<=mx2; ++xm) {
24717  const Ttfloat _val = (Ttfloat)_img.atXYZ(x+xm,y+ym,z+zm,0,0);
24718  val+=_val*_mask(mx1+xm,my1+ym,mz1+zm);
24719  N+=_val*_val;
24720  }
24721  N*=M;
24722  res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0);
24723  }
24724  } else { // Classical correlation.
24725 #ifdef cimg_use_openmp
24726 #pragma omp parallel for collapse(3) if (_width*_height*_depth>=32768)
24727 #endif
24728  for (int z = mz1; z<mze; ++z)
24729  for (int y = my1; y<mye; ++y)
24730  for (int x = mx1; x<mxe; ++x) {
24731  Ttfloat val = 0;
24732  for (int zm = -mz1; zm<=mz2; ++zm)
24733  for (int ym = -my1; ym<=my2; ++ym)
24734  for (int xm = -mx1; xm<=mx2; ++xm)
24735  val+=_img(x+xm,y+ym,z+zm)*_mask(mx1+xm,my1+ym,mz1+zm);
24736  res(x,y,z,c) = (Ttfloat)val;
24737  }
24738  if (boundary_conditions)
24739 #ifdef cimg_use_openmp
24740 #pragma omp parallel for collapse(2) if (_width>=256 && _height*_depth>=128)
24741 #endif
24742  cimg_forYZ(res,y,z)
24743  for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
24744  Ttfloat val = 0;
24745  for (int zm = -mz1; zm<=mz2; ++zm)
24746  for (int ym = -my1; ym<=my2; ++ym)
24747  for (int xm = -mx1; xm<=mx2; ++xm)
24748  val+=_img._atXYZ(x+xm,y+ym,z+zm)*_mask(mx1+xm,my1+ym,mz1+zm);
24749  res(x,y,z,c) = (Ttfloat)val;
24750  }
24751  else
24752 #ifdef cimg_use_openmp
24753 #pragma omp parallel for collapse(2) if (_width>=256 && _height*_depth>=128)
24754 #endif
24755  cimg_forYZ(res,y,z)
24756  for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
24757  Ttfloat val = 0;
24758  for (int zm = -mz1; zm<=mz2; ++zm)
24759  for (int ym = -my1; ym<=my2; ++ym)
24760  for (int xm = -mx1; xm<=mx2; ++xm)
24761  val+=_img.atXYZ(x+xm,y+ym,z+zm,0,0)*_mask(mx1+xm,my1+ym,mz1+zm);
24762  res(x,y,z,c) = (Ttfloat)val;
24763  }
24764  }
24765  }
24766  }
24767  return res;
24768  }
24769 
24771 
24779  template<typename t>
24780  CImg<T>& convolve(const CImg<t>& mask, const unsigned int boundary_conditions=1, const bool is_normalized=false) {
24781  if (is_empty() || !mask) return *this;
24782  return get_convolve(mask,boundary_conditions,is_normalized).move_to(*this);
24783  }
24784 
24786  template<typename t>
24787  CImg<_cimg_Ttfloat> get_convolve(const CImg<t>& mask, const unsigned int boundary_conditions=1,
24788  const bool is_normalized=false) const {
24789  if (is_empty() || !mask) return *this;
24790  return get_correlate(CImg<t>(mask._data,mask.size(),1,1,1,true).get_mirror('x').
24791  resize(mask,-1),boundary_conditions,is_normalized);
24792  }
24793 
24795 
24800  template<typename t>
24801  CImg<T>& erode(const CImg<t>& mask, const unsigned int boundary_conditions=1,
24802  const bool is_normalized=false) {
24803  if (is_empty() || !mask) return *this;
24804  return get_erode(mask,boundary_conditions,is_normalized).move_to(*this);
24805  }
24806 
24808  template<typename t>
24809  CImg<_cimg_Tt> get_erode(const CImg<t>& mask, const unsigned int boundary_conditions=1,
24810  const bool is_normalized=false) const {
24811  if (is_empty() || !mask) return *this;
24812  typedef _cimg_Tt Tt;
24813  CImg<Tt> res(_width,_height,_depth,cimg::max(_spectrum,mask._spectrum));
24814  const int
24815  mx2 = mask.width()/2, my2 = mask.height()/2, mz2 = mask.depth()/2,
24816  mx1 = mx2 - 1 + (mask.width()%2), my1 = my2 - 1 + (mask.height()%2), mz1 = mz2 - 1 + (mask.depth()%2),
24817  mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
24818 #ifdef cimg_use_openmp
24819 #pragma omp parallel for if (_spectrum>=2)
24820 #endif
24821  cimg_forC(*this,c) {
24822  const CImg<T> _img = get_shared_channel(c%_spectrum);
24823  const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
24824  if (is_normalized) { // Normalized erosion.
24825 #ifdef cimg_use_openmp
24826 #pragma omp parallel for collapse(3) if (_width*_height*_depth>=32768)
24827 #endif
24828  for (int z = mz1; z<mze; ++z)
24829  for (int y = my1; y<mye; ++y)
24830  for (int x = mx1; x<mxe; ++x) {
24831  Tt min_val = cimg::type<Tt>::max();
24832  for (int zm = -mz1; zm<=mz2; ++zm)
24833  for (int ym = -my1; ym<=my2; ++ym)
24834  for (int xm = -mx1; xm<=mx2; ++xm) {
24835  const t mval = _mask(mx1+xm,my1+ym,mz1+zm);
24836  const Tt cval = (Tt)(_img(x+xm,y+ym,z+zm) + mval);
24837  if (mval && cval<min_val) min_val = cval;
24838  }
24839  res(x,y,z,c) = min_val;
24840  }
24841  if (boundary_conditions)
24842 #ifdef cimg_use_openmp
24843 #pragma omp parallel for collapse(2) if (_width>=256 && _height*_depth>=128)
24844 #endif
24845  cimg_forYZ(res,y,z)
24846  for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
24847  Tt min_val = cimg::type<Tt>::max();
24848  for (int zm = -mz1; zm<=mz2; ++zm)
24849  for (int ym = -my1; ym<=my2; ++ym)
24850  for (int xm = -mx1; xm<=mx2; ++xm) {
24851  const t mval = _mask(mx1+xm,my1+ym,mz1+zm);
24852  const Tt cval = (Tt)(_img._atXYZ(x+xm,y+ym,z+zm) + mval);
24853  if (mval && cval<min_val) min_val = cval;
24854  }
24855  res(x,y,z,c) = min_val;
24856  }
24857  else
24858 #ifdef cimg_use_openmp
24859 #pragma omp parallel for collapse(2) if (_width>=256 && _height*_depth>=128)
24860 #endif
24861  cimg_forYZ(res,y,z)
24862  for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
24863  Tt min_val = cimg::type<Tt>::max();
24864  for (int zm = -mz1; zm<=mz2; ++zm)
24865  for (int ym = -my1; ym<=my2; ++ym)
24866  for (int xm = -mx1; xm<=mx2; ++xm) {
24867  const t mval = _mask(mx1+xm,my1+ym,mz1+zm);
24868  const Tt cval = (Tt)(_img.atXYZ(x+xm,y+ym,z+zm,0,0) + mval);
24869  if (mval && cval<min_val) min_val = cval;
24870  }
24871  res(x,y,z,c) = min_val;
24872  }
24873 
24874  } else { // Classical erosion.
24875 #ifdef cimg_use_openmp
24876 #pragma omp parallel for collapse(3) if (_width*_height*_depth>=32768)
24877 #endif
24878  for (int z = mz1; z<mze; ++z)
24879  for (int y = my1; y<mye; ++y)
24880  for (int x = mx1; x<mxe; ++x) {
24881  Tt min_val = cimg::type<Tt>::max();
24882  for (int zm = -mz1; zm<=mz2; ++zm)
24883  for (int ym = -my1; ym<=my2; ++ym)
24884  for (int xm = -mx1; xm<=mx2; ++xm) {
24885  const Tt cval = (Tt)_img(x+xm,y+ym,z+zm);
24886  if (_mask(mx1+xm,my1+ym,mz1+zm) && cval<min_val) min_val = cval;
24887  }
24888  res(x,y,z,c) = min_val;
24889  }
24890  if (boundary_conditions)
24891 #ifdef cimg_use_openmp
24892 #pragma omp parallel for collapse(2) if (_width>=256 && _height*_depth>=128)
24893 #endif
24894  cimg_forYZ(res,y,z)
24895  for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
24896  Tt min_val = cimg::type<Tt>::max();
24897  for (int zm = -mz1; zm<=mz2; ++zm)
24898  for (int ym = -my1; ym<=my2; ++ym)
24899  for (int xm = -mx1; xm<=mx2; ++xm) {
24900  const T cval = (Tt)_img._atXYZ(x+xm,y+ym,z+zm);
24901  if (_mask(mx1+xm,my1+ym,mz1+zm) && cval<min_val) min_val = cval;
24902  }
24903  res(x,y,z,c) = min_val;
24904  }
24905  else
24906 #ifdef cimg_use_openmp
24907 #pragma omp parallel for collapse(2) if (_width>=256 && _height*_depth>=128)
24908 #endif
24909  cimg_forYZ(res,y,z)
24910  for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
24911  Tt min_val = cimg::type<Tt>::max();
24912  for (int zm = -mz1; zm<=mz2; ++zm)
24913  for (int ym = -my1; ym<=my2; ++ym)
24914  for (int xm = -mx1; xm<=mx2; ++xm) {
24915  const T cval = (Tt)_img.atXYZ(x+xm,y+ym,z+zm,0,0);
24916  if (_mask(mx1+xm,my1+ym,mz1+zm) && cval<min_val) min_val = cval;
24917  }
24918  res(x,y,z,c) = min_val;
24919  }
24920  }
24921  }
24922  return res;
24923  }
24924 
24926 
24931  CImg<T>& erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
24932  if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this;
24933  if (sx>1 && _width>1) { // Along X-axis.
24934  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;
24935  CImg<T> buf(L);
24936 #ifdef cimg_use_opemp
24937 #pragma omp parallel for collapse(3) firstprivate(buf) if (size()>524288)
24938 #endif
24939  cimg_forYZC(*this,y,z,c) {
24940  T *const ptrdb = buf._data, *ptrd = buf._data, *const ptrde = buf._data + L - 1;
24941  const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
24942  T cur = *ptrs; ptrs+=off; bool is_first = true;
24943  for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
24944  const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }}
24945  *(ptrd++) = cur;
24946  if (ptrs>=ptrse) {
24947  T *pd = data(0,y,z,c); cur = cimg::min(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; }
24948  } else {
24949  for (int p = s1; p>0 && ptrd<=ptrde; --p) {
24950  const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
24951  *(ptrd++) = cur;
24952  }
24953  for (int p = L - s - 1; p>0; --p) {
24954  const T val = *ptrs; ptrs+=off;
24955  if (is_first) {
24956  const T *nptrs = ptrs - off; cur = val;
24957  for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
24958  nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
24959  } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
24960  *(ptrd++) = cur;
24961  }
24962  ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
24963  for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
24964  const T val = *ptrs; ptrs-=off; if (val<cur) cur = val;
24965  }
24966  *(ptrd--) = cur;
24967  for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
24968  const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur;
24969  }
24970  T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
24971  }
24972  }
24973  }
24974 
24975  if (sy>1 && _height>1) { // Along Y-axis.
24976  const int L = height(), off = width(), s = (int)sy, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1,
24977  s2 = _s2>L?L:_s2;
24978  CImg<T> buf(L);
24979 #ifdef cimg_use_opemp
24980 #pragma omp parallel for collapse(3) firstprivate(buf) if (size()>524288)
24981 #endif
24982  cimg_forXZC(*this,x,z,c) {
24983  T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
24984  const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
24985  T cur = *ptrs; ptrs+=off; bool is_first = true;
24986  for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
24987  const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
24988  }
24989  *(ptrd++) = cur;
24990  if (ptrs>=ptrse) {
24991  T *pd = data(x,0,z,c); cur = cimg::min(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; }
24992  } else {
24993  for (int p = s1; p>0 && ptrd<=ptrde; --p) {
24994  const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
24995  *(ptrd++) = cur;
24996  }
24997  for (int p = L - s - 1; p>0; --p) {
24998  const T val = *ptrs; ptrs+=off;
24999  if (is_first) {
25000  const T *nptrs = ptrs - off; cur = val;
25001  for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
25002  nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
25003  } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
25004  *(ptrd++) = cur;
25005  }
25006  ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
25007  for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
25008  const T val = *ptrs; ptrs-=off; if (val<cur) cur = val;
25009  }
25010  *(ptrd--) = cur;
25011  for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
25012  const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur;
25013  }
25014  T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
25015  }
25016  }
25017  }
25018 
25019  if (sz>1 && _depth>1) { // Along Z-axis.
25020  const int L = depth(), off = width()*height(), s = (int)sz, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1,
25021  s2 = _s2>L?L:_s2;
25022  CImg<T> buf(L);
25023 #ifdef cimg_use_opemp
25024 #pragma omp parallel for collapse(3) firstprivate(buf) if (size()>524288)
25025 #endif
25026  cimg_forXYC(*this,x,y,c) {
25027  T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
25028  const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
25029  T cur = *ptrs; ptrs+=off; bool is_first = true;
25030  for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
25031  const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
25032  }
25033  *(ptrd++) = cur;
25034  if (ptrs>=ptrse) {
25035  T *pd = data(x,y,0,c); cur = cimg::min(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; }
25036  } else {
25037  for (int p = s1; p>0 && ptrd<=ptrde; --p) {
25038  const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
25039  *(ptrd++) = cur;
25040  }
25041  for (int p = L - s - 1; p>0; --p) {
25042  const T val = *ptrs; ptrs+=off;
25043  if (is_first) {
25044  const T *nptrs = ptrs - off; cur = val;
25045  for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
25046  nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
25047  } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
25048  *(ptrd++) = cur;
25049  }
25050  ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
25051  for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
25052  const T val = *ptrs; ptrs-=off; if (val<cur) cur = val;
25053  }
25054  *(ptrd--) = cur;
25055  for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
25056  const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur;
25057  }
25058  T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
25059  }
25060  }
25061  }
25062  return *this;
25063  }
25064 
25066  CImg<T> get_erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
25067  return (+*this).erode(sx,sy,sz);
25068  }
25069 
25071 
25074  CImg<T>& erode(const unsigned int s) {
25075  return erode(s,s,s);
25076  }
25077 
25079  CImg<T> get_erode(const unsigned int s) const {
25080  return (+*this).erode(s);
25081  }
25082 
25084 
25089  template<typename t>
25090  CImg<T>& dilate(const CImg<t>& mask, const unsigned int boundary_conditions=1,
25091  const bool is_normalized=false) {
25092  if (is_empty() || !mask) return *this;
25093  return get_dilate(mask,boundary_conditions,is_normalized).move_to(*this);
25094  }
25095 
25097  template<typename t>
25098  CImg<_cimg_Tt> get_dilate(const CImg<t>& mask, const unsigned int boundary_conditions=1,
25099  const bool is_normalized=false) const {
25100  if (is_empty() || !mask) return *this;
25101  typedef _cimg_Tt Tt;
25102  CImg<Tt> res(_width,_height,_depth,_spectrum);
25103  const int
25104  mx2 = mask.width()/2, my2 = mask.height()/2, mz2 = mask.depth()/2,
25105  mx1 = mx2 - 1 + (mask.width()%2), my1 = my2 - 1 + (mask.height()%2), mz1 = mz2 - 1 + (mask.depth()%2),
25106  mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
25107 #ifdef cimg_use_openmp
25108 #pragma omp parallel for if (_spectrum>=2)
25109 #endif
25110  cimg_forC(*this,c) {
25111  const CImg<T> _img = get_shared_channel(c%_spectrum);
25112  const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
25113  if (is_normalized) { // Normalized dilation.
25114 #ifdef cimg_use_openmp
25115 #pragma omp parallel for collapse(3) if (_width*_height*_depth>=32768)
25116 #endif
25117  for (int z = mz1; z<mze; ++z)
25118  for (int y = my1; y<mye; ++y)
25119  for (int x = mx1; x<mxe; ++x) {
25120  Tt max_val = cimg::type<Tt>::min();
25121  for (int zm = -mz1; zm<=mz2; ++zm)
25122  for (int ym = -my1; ym<=my2; ++ym)
25123  for (int xm = -mx1; xm<=mx2; ++xm) {
25124  const t mval = _mask(mx1+xm,my1+ym,mz1+zm);
25125  const Tt cval = (Tt)(_img(x+xm,y+ym,z+zm) - mval);
25126  if (mval && cval>max_val) max_val = cval;
25127  }
25128  res(x,y,z,c) = max_val;
25129  }
25130  if (boundary_conditions)
25131 #ifdef cimg_use_openmp
25132 #pragma omp parallel for collapse(2) if (_width>=256 && _height*_depth>=128)
25133 #endif
25134  cimg_forYZ(res,y,z)
25135  for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
25136  Tt max_val = cimg::type<Tt>::min();
25137  for (int zm = -mz1; zm<=mz2; ++zm)
25138  for (int ym = -my1; ym<=my2; ++ym)
25139  for (int xm = -mx1; xm<=mx2; ++xm) {
25140  const t mval = _mask(mx1+xm,my1+ym,mz1+zm);
25141  const Tt cval = (Tt)(_img._atXYZ(x+xm,y+ym,z+zm) - mval);
25142  if (mval && cval>max_val) max_val = cval;
25143  }
25144  res(x,y,z,c) = max_val;
25145  }
25146  else
25147 #ifdef cimg_use_openmp
25148 #pragma omp parallel for collapse(2) if (_width>=256 && _height*_depth>=128)
25149 #endif
25150  cimg_forYZ(*this,y,z)
25151  for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
25152  Tt max_val = cimg::type<Tt>::min();
25153  for (int zm = -mz1; zm<=mz2; ++zm)
25154  for (int ym = -my1; ym<=my2; ++ym)
25155  for (int xm = -mx1; xm<=mx2; ++xm) {
25156  const t mval = _mask(mx1+xm,my1+ym,mz1+zm);
25157  const Tt cval = (Tt)(_img.atXYZ(x+xm,y+ym,z+zm,0,0) - mval);
25158  if (mval && cval>max_val) max_val = cval;
25159  }
25160  res(x,y,z,c) = max_val;
25161  }
25162  } else { // Classical dilation.
25163 #ifdef cimg_use_openmp
25164 #pragma omp parallel for collapse(3) if (_width>=256 && _height*_depth>=128)
25165 #endif
25166  for (int z = mz1; z<mze; ++z)
25167  for (int y = my1; y<mye; ++y)
25168  for (int x = mx1; x<mxe; ++x) {
25169  Tt max_val = cimg::type<Tt>::min();
25170  for (int zm = -mz1; zm<=mz2; ++zm)
25171  for (int ym = -my1; ym<=my2; ++ym)
25172  for (int xm = -mx1; xm<=mx2; ++xm) {
25173  const Tt cval = (Tt)_img(x+xm,y+ym,z+zm);
25174  if (_mask(mx1+xm,my1+ym,mz1+zm) && cval>max_val) max_val = cval;
25175  }
25176  res(x,y,z,c) = max_val;
25177  }
25178  if (boundary_conditions)
25179 #ifdef cimg_use_openmp
25180 #pragma omp parallel for collapse(2) if (_width>=256 && _height*_depth>=128)
25181 #endif
25182  cimg_forYZ(res,y,z)
25183  for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
25184  Tt max_val = cimg::type<Tt>::min();
25185  for (int zm = -mz1; zm<=mz2; ++zm)
25186  for (int ym = -my1; ym<=my2; ++ym)
25187  for (int xm = -mx1; xm<=mx2; ++xm) {
25188  const T cval = (Tt)_img._atXYZ(x+xm,y+ym,z+zm);
25189  if (_mask(mx1+xm,my1+ym,mz1+zm) && cval>max_val) max_val = cval;
25190  }
25191  res(x,y,z,c) = max_val;
25192  }
25193  else
25194 #ifdef cimg_use_openmp
25195 #pragma omp parallel for collapse(2) if (_width>=256 && _height*_depth>=128)
25196 #endif
25197  cimg_forYZ(res,y,z)
25198  for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
25199  Tt max_val = cimg::type<Tt>::min();
25200  for (int zm = -mz1; zm<=mz2; ++zm)
25201  for (int ym = -my1; ym<=my2; ++ym)
25202  for (int xm = -mx1; xm<=mx2; ++xm) {
25203  const T cval = (Tt)_img.atXYZ(x+xm,y+ym,z+zm,0,0);
25204  if (_mask(mx1+xm,my1+ym,mz1+zm) && cval>max_val) max_val = cval;
25205  }
25206  res(x,y,z,c) = max_val;
25207  }
25208  }
25209  }
25210  return res;
25211  }
25212 
25214 
25219  CImg<T>& dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
25220  if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this;
25221  if (sx>1 && _width>1) { // Along X-axis.
25222  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;
25223  CImg<T> buf(L);
25224 #ifdef cimg_use_opemp
25225 #pragma omp parallel for collapse(3) firstprivate(buf) if (size()>524288)
25226 #endif
25227  cimg_forYZC(*this,y,z,c) {
25228  T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
25229  const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
25230  T cur = *ptrs; ptrs+=off; bool is_first = true;
25231  for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
25232  const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
25233  }
25234  *(ptrd++) = cur;
25235  if (ptrs>=ptrse) {
25236  T *pd = data(0,y,z,c); cur = cimg::max(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; }
25237  } else {
25238  for (int p = s1; p>0 && ptrd<=ptrde; --p) {
25239  const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
25240  *(ptrd++) = cur;
25241  }
25242  for (int p = L - s - 1; p>0; --p) {
25243  const T val = *ptrs; ptrs+=off;
25244  if (is_first) {
25245  const T *nptrs = ptrs - off; cur = val;
25246  for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
25247  nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
25248  } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
25249  *(ptrd++) = cur;
25250  }
25251  ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
25252  for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
25253  const T val = *ptrs; ptrs-=off; if (val>cur) cur = val;
25254  }
25255  *(ptrd--) = cur;
25256  for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
25257  const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur;
25258  }
25259  T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
25260  }
25261  }
25262  }
25263 
25264  if (sy>1 && _height>1) { // Along Y-axis.
25265  const int L = height(), off = width(), s = (int)sy, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1,
25266  s2 = _s2>L?L:_s2;
25267  CImg<T> buf(L);
25268 #ifdef cimg_use_opemp
25269 #pragma omp parallel for collapse(3) firstprivate(buf) if (size()>524288)
25270 #endif
25271  cimg_forXZC(*this,x,z,c) {
25272  T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
25273  const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
25274  T cur = *ptrs; ptrs+=off; bool is_first = true;
25275  for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
25276  const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
25277  }
25278  *(ptrd++) = cur;
25279  if (ptrs>=ptrse) {
25280  T *pd = data(x,0,z,c); cur = cimg::max(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; }
25281  } else {
25282  for (int p = s1; p>0 && ptrd<=ptrde; --p) {
25283  const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
25284  *(ptrd++) = cur;
25285  }
25286  for (int p = L - s - 1; p>0; --p) {
25287  const T val = *ptrs; ptrs+=off;
25288  if (is_first) {
25289  const T *nptrs = ptrs - off; cur = val;
25290  for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
25291  nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
25292  } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
25293  *(ptrd++) = cur;
25294  }
25295  ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
25296  for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
25297  const T val = *ptrs; ptrs-=off; if (val>cur) cur = val;
25298  }
25299  *(ptrd--) = cur;
25300  for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
25301  const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur;
25302  }
25303  T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
25304  }
25305  }
25306  }
25307 
25308  if (sz>1 && _depth>1) { // Along Z-axis.
25309  const int L = depth(), off = width()*height(), s = (int)sz, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1,
25310  s2 = _s2>L?L:_s2;
25311  CImg<T> buf(L);
25312 #ifdef cimg_use_opemp
25313 #pragma omp parallel for collapse(3) firstprivate(buf) if (size()>524288)
25314 #endif
25315  cimg_forXYC(*this,x,y,c) {
25316  T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
25317  const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
25318  T cur = *ptrs; ptrs+=off; bool is_first = true;
25319  for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
25320  const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
25321  }
25322  *(ptrd++) = cur;
25323  if (ptrs>=ptrse) {
25324  T *pd = data(x,y,0,c); cur = cimg::max(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; }
25325  } else {
25326  for (int p = s1; p>0 && ptrd<=ptrde; --p) {
25327  const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
25328  *(ptrd++) = cur;
25329  }
25330  for (int p = L - s - 1; p>0; --p) {
25331  const T val = *ptrs; ptrs+=off;
25332  if (is_first) {
25333  const T *nptrs = ptrs - off; cur = val;
25334  for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
25335  nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
25336  } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
25337  *(ptrd++) = cur;
25338  }
25339  ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
25340  for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
25341  const T val = *ptrs; ptrs-=off; if (val>cur) cur = val;
25342  }
25343  *(ptrd--) = cur;
25344  for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
25345  const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur;
25346  }
25347  T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
25348  }
25349  }
25350  }
25351  return *this;
25352  }
25353 
25355  CImg<T> get_dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
25356  return (+*this).dilate(sx,sy,sz);
25357  }
25358 
25360 
25363  CImg<T>& dilate(const unsigned int s) {
25364  return dilate(s,s,s);
25365  }
25366 
25368  CImg<T> get_dilate(const unsigned int s) const {
25369  return (+*this).dilate(s);
25370  }
25371 
25373 
25379  template<typename t>
25380  CImg<T>& watershed(const CImg<t>& priority, const bool fill_lines=true) {
25381  if (is_empty()) return *this;
25382  if (!is_sameXYZ(priority))
25383  throw CImgArgumentException(_cimg_instance
25384  "watershed(): image instance and specified priority (%u,%u,%u,%u,%p) "
25385  "have different dimensions.",
25386  cimg_instance,
25387  priority._width,priority._height,priority._depth,priority._spectrum,priority._data);
25388  if (_spectrum!=1) {
25389  cimg_forC(*this,c)
25390  get_shared_channel(c).watershed(priority.get_shared_channel(c%priority._spectrum),fill_lines);
25391  return *this;
25392  }
25393 
25394  CImg<boolT> is_queued(_width,_height,_depth,1,0);
25396  unsigned int sizeQ = 0;
25397 
25398  // Find seed points and insert them in priority queue.
25399  const T *ptrs = _data;
25400  cimg_forXYZ(*this,x,y,z) if (*(ptrs++)) {
25401  if (x-1>=0 && !(*this)(x-1,y,z)) Q._priority_queue_insert(is_queued,sizeQ,priority(x-1,y,z),x-1,y,z);
25402  if (x+1<width() && !(*this)(x+1,y,z)) Q._priority_queue_insert(is_queued,sizeQ,priority(x+1,y,z),x+1,y,z);
25403  if (y-1>=0 && !(*this)(x,y-1,z)) Q._priority_queue_insert(is_queued,sizeQ,priority(x,y-1,z),x,y-1,z);
25404  if (y+1<height() && !(*this)(x,y+1,z)) Q._priority_queue_insert(is_queued,sizeQ,priority(x,y+1,z),x,y+1,z);
25405  if (z-1>=0 && !(*this)(x,y,z-1)) Q._priority_queue_insert(is_queued,sizeQ,priority(x,y,z-1),x,y,z-1);
25406  if (z+1<depth() && !(*this)(x,y,z+1)) Q._priority_queue_insert(is_queued,sizeQ,priority(x,y,z+1),x,y,z+1);
25407  }
25408 
25409  // Start watershed computation.
25410  while (sizeQ) {
25411 
25412  // Get and remove point with maximal priority from the queue.
25413  const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
25414  Q._priority_queue_remove(sizeQ);
25415 
25416  // Check labels of the neighbors.
25417  bool is_same_label = true;
25418  unsigned int label = 0;
25419  if (x-1>=0) {
25420  if ((*this)(x-1,y,z)) {
25421  if (!label) label = (unsigned int)(*this)(x-1,y,z);
25422  else if (label!=(*this)(x-1,y,z)) is_same_label = false;
25423  } else Q._priority_queue_insert(is_queued,sizeQ,priority(x-1,y,z),x-1,y,z);
25424  }
25425  if (x+1<width()) {
25426  if ((*this)(x+1,y,z)) {
25427  if (!label) label = (unsigned int)(*this)(x+1,y,z);
25428  else if (label!=(*this)(x+1,y,z)) is_same_label = false;
25429  } else Q._priority_queue_insert(is_queued,sizeQ,priority(x+1,y,z),x+1,y,z);
25430  }
25431  if (y-1>=0) {
25432  if ((*this)(x,y-1,z)) {
25433  if (!label) label = (unsigned int)(*this)(x,y-1,z);
25434  else if (label!=(*this)(x,y-1,z)) is_same_label = false;
25435  } else Q._priority_queue_insert(is_queued,sizeQ,priority(x,y-1,z),x,y-1,z);
25436  }
25437  if (y+1<height()) {
25438  if ((*this)(x,y+1,z)) {
25439  if (!label) label = (unsigned int)(*this)(x,y+1,z);
25440  else if (label!=(*this)(x,y+1,z)) is_same_label = false;
25441  } else Q._priority_queue_insert(is_queued,sizeQ,priority(x,y+1,z),x,y+1,z);
25442  }
25443  if (z-1>=0) {
25444  if ((*this)(x,y,z-1)) {
25445  if (!label) label = (unsigned int)(*this)(x,y,z-1);
25446  else if (label!=(*this)(x,y,z-1)) is_same_label = false;
25447  } else Q._priority_queue_insert(is_queued,sizeQ,priority(x,y,z-1),x,y,z-1);
25448  }
25449  if (z+1<depth()) {
25450  if ((*this)(x,y,z+1)) {
25451  if (!label) label = (unsigned int)(*this)(x,y,z+1);
25452  else if (label!=(*this)(x,y,z+1)) is_same_label = false;
25453  } else Q._priority_queue_insert(is_queued,sizeQ,priority(x,y,z+1),x,y,z+1);
25454  }
25455  if (is_same_label) (*this)(x,y,z) = (T)label;
25456  }
25457 
25458  // Fill lines.
25459  if (fill_lines) {
25460 
25461  // Sort all non-labeled pixels with labeled neighbors.
25462  is_queued = false;
25463  const T *ptrs = _data;
25464  cimg_forXYZ(*this,x,y,z) if (!*(ptrs++) &&
25465  ((x-1>=0 && (*this)(x-1,y,z)) || (x+1<width() && (*this)(x+1,y,z)) ||
25466  (y-1>=0 && (*this)(x,y-1,z)) || (y+1<height() && (*this)(x,y+1,z)) ||
25467  (z-1>=0 && (*this)(x,y,z-1)) || (z+1>depth() && (*this)(x,y,z+1))))
25468  Q._priority_queue_insert(is_queued,sizeQ,priority(x,y,z),x,y,z);
25469 
25470  // Start line filling process.
25471  while (sizeQ) {
25472  const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
25473  Q._priority_queue_remove(sizeQ);
25474  t pmax = cimg::type<t>::min();
25475  int xmax = 0, ymax = 0, zmax = 0;
25476  if (x-1>=0) {
25477  if ((*this)(x-1,y,z)) {
25478  if (priority(x-1,y,z)>pmax) { pmax = priority(x-1,y,z); xmax = x-1; ymax = y; zmax = z; }
25479  } else Q._priority_queue_insert(is_queued,sizeQ,priority(x-1,y,z),x-1,y,z);
25480  }
25481  if (x+1<width()) {
25482  if ((*this)(x+1,y,z)) {
25483  if (priority(x+1,y,z)>pmax) { pmax = priority(x+1,y,z); xmax = x+1; ymax = y; zmax = z; }
25484  } else Q._priority_queue_insert(is_queued,sizeQ,priority(x+1,y,z),x+1,y,z);
25485  }
25486  if (y-1>=0) {
25487  if ((*this)(x,y-1,z)) {
25488  if (priority(x,y-1,z)>pmax) { pmax = priority(x,y-1,z); xmax = x; ymax = y-1; zmax = z; }
25489  } else Q._priority_queue_insert(is_queued,sizeQ,priority(x,y-1,z),x,y-1,z);
25490  }
25491  if (y+1<height()) {
25492  if ((*this)(x,y+1,z)) {
25493  if (priority(x,y+1,z)>pmax) { pmax = priority(x,y+1,z); xmax = x; ymax = y+1; zmax = z; }
25494  } else Q._priority_queue_insert(is_queued,sizeQ,priority(x,y+1,z),x,y+1,z);
25495  }
25496  if (z-1>=0) {
25497  if ((*this)(x,y,z-1)) {
25498  if (priority(x,y,z-1)>pmax) { pmax = priority(x,y,z-1); xmax = x; ymax = y; zmax = z-1; }
25499  } else Q._priority_queue_insert(is_queued,sizeQ,priority(x,y,z-1),x,y,z-1);
25500  }
25501  if (z+1<depth()) {
25502  if ((*this)(x,y,z+1)) {
25503  if (priority(x,y,z+1)>pmax) { pmax = priority(x,y,z+1); xmax = x; ymax = y; zmax = z+1; }
25504  } else Q._priority_queue_insert(is_queued,sizeQ,priority(x,y,z+1),x,y,z+1);
25505  }
25506  (*this)(x,y,z) = (*this)(xmax,ymax,zmax);
25507  }
25508  }
25509  return *this;
25510  }
25511 
25513  template<typename t>
25514  CImg<T> get_watershed(const CImg<t>& priority, const bool fill_lines=true) const {
25515  return (+*this).watershed(priority,fill_lines);
25516  }
25517 
25518  // [internal] Insert/Remove items in priority queue, for watershed/distance transforms.
25519  template<typename t>
25520  bool _priority_queue_insert(CImg<boolT>& is_queued, unsigned int& siz, const t value,
25521  const unsigned int x, const unsigned int y, const unsigned int z) {
25522  if (is_queued(x,y,z)) return false;
25523  is_queued(x,y,z) = true;
25524  if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); }
25525  (*this)(siz-1,0) = (T)value; (*this)(siz-1,1) = (T)x; (*this)(siz-1,2) = (T)y; (*this)(siz-1,3) = (T)z;
25526  for (unsigned int pos = siz - 1, par = 0; pos && value>(*this)(par=(pos+1)/2-1,0); pos = par) {
25527  cimg::swap((*this)(pos,0),(*this)(par,0)); cimg::swap((*this)(pos,1),(*this)(par,1));
25528  cimg::swap((*this)(pos,2),(*this)(par,2)); cimg::swap((*this)(pos,3),(*this)(par,3));
25529  }
25530  return true;
25531  }
25532 
25533  CImg<T>& _priority_queue_remove(unsigned int& siz) {
25534  (*this)(0,0) = (*this)(--siz,0); (*this)(0,1) = (*this)(siz,1);
25535  (*this)(0,2) = (*this)(siz,2); (*this)(0,3) = (*this)(siz,3);
25536  const float value = (*this)(0,0);
25537  for (unsigned int pos = 0, left = 0, right = 0;
25538  ((right=2*(pos+1),(left=right-1))<siz && value<(*this)(left,0)) || (right<siz && value<(*this)(right,0));) {
25539  if (right<siz) {
25540  if ((*this)(left,0)>(*this)(right,0)) {
25541  cimg::swap((*this)(pos,0),(*this)(left,0)); cimg::swap((*this)(pos,1),(*this)(left,1));
25542  cimg::swap((*this)(pos,2),(*this)(left,2)); cimg::swap((*this)(pos,3),(*this)(left,3));
25543  pos = left;
25544  } else {
25545  cimg::swap((*this)(pos,0),(*this)(right,0)); cimg::swap((*this)(pos,1),(*this)(right,1));
25546  cimg::swap((*this)(pos,2),(*this)(right,2)); cimg::swap((*this)(pos,3),(*this)(right,3));
25547  pos = right;
25548  }
25549  } else {
25550  cimg::swap((*this)(pos,0),(*this)(left,0)); cimg::swap((*this)(pos,1),(*this)(left,1));
25551  cimg::swap((*this)(pos,2),(*this)(left,2)); cimg::swap((*this)(pos,3),(*this)(left,3));
25552  pos = left;
25553  }
25554  }
25555  return *this;
25556  }
25557 
25559 
25565  CImg<T>& deriche(const float sigma, const int order=0, const char axis='x', const bool boundary_conditions=true) {
25566 #define _cimg_deriche_apply \
25567  CImg<Tfloat> Y(N); \
25568  Tfloat *ptrY = Y._data, yb = 0, yp = 0; \
25569  T xp = (T)0; \
25570  if (boundary_conditions) { xp = *ptrX; yb = yp = (Tfloat)(coefp*xp); } \
25571  for (int m = 0; m<N; ++m) { \
25572  const T xc = *ptrX; ptrX+=off; \
25573  const Tfloat yc = *(ptrY++) = (Tfloat)(a0*xc + a1*xp - b1*yp - b2*yb); \
25574  xp = xc; yb = yp; yp = yc; \
25575  } \
25576  T xn = (T)0, xa = (T)0; \
25577  Tfloat yn = 0, ya = 0; \
25578  if (boundary_conditions) { xn = xa = *(ptrX-off); yn = ya = (Tfloat)coefn*xn; } \
25579  for (int n = N-1; n>=0; --n) { \
25580  const T xc = *(ptrX-=off); \
25581  const Tfloat yc = (Tfloat)(a2*xn + a3*xa - b1*yn - b2*ya); \
25582  xa = xn; xn = xc; ya = yn; yn = yc; \
25583  *ptrX = (T)(*(--ptrY)+yc); \
25584  }
25585  const char naxis = cimg::uncase(axis);
25586  const float nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100;
25587  if (is_empty() || (nsigma<0.1f && !order)) return *this;
25588  const float
25589  nnsigma = nsigma<0.1f?0.1f:nsigma,
25590  alpha = 1.695f/nnsigma,
25591  ema = (float)std::exp(-alpha),
25592  ema2 = (float)std::exp(-2*alpha),
25593  b1 = -2*ema,
25594  b2 = ema2;
25595  float a0 = 0, a1 = 0, a2 = 0, a3 = 0, coefp = 0, coefn = 0;
25596  switch (order) {
25597  case 0 : {
25598  const float k = (1-ema)*(1-ema)/(1+2*alpha*ema-ema2);
25599  a0 = k;
25600  a1 = k*(alpha-1)*ema;
25601  a2 = k*(alpha+1)*ema;
25602  a3 = -k*ema2;
25603  } break;
25604  case 1 : {
25605  const float k = -(1-ema)*(1-ema)*(1-ema)/(2*(ema+1)*ema);
25606  a0 = a3 = 0;
25607  a1 = k*ema;
25608  a2 = -a1;
25609  } break;
25610  case 2 : {
25611  const float
25612  ea = (float)std::exp(-alpha),
25613  k = -(ema2-1)/(2*alpha*ema),
25614  kn = (-2*(-1+3*ea-3*ea*ea+ea*ea*ea)/(3*ea+1+3*ea*ea+ea*ea*ea));
25615  a0 = kn;
25616  a1 = -kn*(1+k*alpha)*ema;
25617  a2 = kn*(1-k*alpha)*ema;
25618  a3 = -kn*ema2;
25619  } break;
25620  default :
25621  throw CImgArgumentException(_cimg_instance
25622  "deriche(): Invalid specified filter order %u "
25623  "(should be { 0=smoothing | 1=1st-derivative | 2=2nd-derivative }).",
25624  cimg_instance,
25625  order);
25626  }
25627  coefp = (a0+a1)/(1+b1+b2);
25628  coefn = (a2+a3)/(1+b1+b2);
25629  switch (naxis) {
25630  case 'x' : {
25631  const int N = _width;
25632  const unsigned long off = 1U;
25633 #ifdef cimg_use_openmp
25634 #pragma omp parallel for collapse(3) if (_width>=256 && _height*_depth*_spectrum>=16)
25635 #endif
25636  cimg_forYZC(*this,y,z,c) { T *ptrX = data(0,y,z,c); _cimg_deriche_apply; }
25637  } break;
25638  case 'y' : {
25639  const int N = _height;
25640  const unsigned long off = (unsigned long)_width;
25641 #ifdef cimg_use_openmp
25642 #pragma omp parallel for collapse(3) if (_width>=256 && _height*_depth*_spectrum>=16)
25643 #endif
25644  cimg_forXZC(*this,x,z,c) { T *ptrX = data(x,0,z,c); _cimg_deriche_apply; }
25645  } break;
25646  case 'z' : {
25647  const int N = _depth;
25648  const unsigned long off = (unsigned long)_width*_height;
25649 #ifdef cimg_use_openmp
25650 #pragma omp parallel for collapse(3) if (_width>=256 && _height*_depth*_spectrum>=16)
25651 #endif
25652  cimg_forXYC(*this,x,y,c) { T *ptrX = data(x,y,0,c); _cimg_deriche_apply; }
25653  } break;
25654  default : {
25655  const int N = _spectrum;
25656  const unsigned long off = (unsigned long)_width*_height*_depth;
25657 #ifdef cimg_use_openmp
25658 #pragma omp parallel for collapse(3) if (_width>=256 && _height*_depth*_spectrum>=16)
25659 #endif
25660  cimg_forXYZ(*this,x,y,z) { T *ptrX = data(x,y,z,0); _cimg_deriche_apply; }
25661  }
25662  }
25663  return *this;
25664  }
25665 
25667  CImg<Tfloat> get_deriche(const float sigma, const int order=0, const char axis='x',
25668  const bool boundary_conditions=true) const {
25669  return CImg<Tfloat>(*this,false).deriche(sigma,order,axis,boundary_conditions);
25670  }
25671 
25672  // [internal] Apply a recursive filter (used by CImg<T>::vanvliet()).
25683  template <int K>
25684  static void _cimg_recursive_apply(T *data, const Tfloat filter[], const int N, const unsigned long off,
25685  const int order, const bool boundary_conditions) {
25686  Tfloat val[K]; // res[n,n-1,n-2,n-3,..] or res[n,n+1,n+2,n+3,..]
25687  switch (order) {
25688  case 0 : {
25689  for (int pass = 0; pass<2; ++pass) {
25690  for (int k = 1; k<K; ++k) val[k] = (Tfloat)(boundary_conditions?*data:0);
25691  for (int n = 0; n<N; ++n) {
25692  val[0] = (Tfloat)(*data)*filter[0];
25693  for (int k = 1; k<K; ++k) val[0]+=val[k]*filter[k];
25694  *data = (T)val[0];
25695  if (!pass) data+=off; else data-=off;
25696  for (int k = K-1; k>0; --k) val[k] = val[k-1];
25697  }
25698  if (!pass) data-=off;
25699  }
25700  } break;
25701  case 1 : {
25702  Tfloat x[3]; // [front,center,back]
25703  for (int pass = 0; pass<2; ++pass) {
25704  for (int k = 0; k<3; ++k) x[k] = (Tfloat)(boundary_conditions?*data:0);
25705  for (int k = 0; k<K; ++k) val[k] = 0;
25706  for (int n = 0; n<N-1; ++n) {
25707  if (!pass) {
25708  x[0] = (Tfloat)*(data+off);
25709  val[0] = 0.5f * (x[0] - x[2])*filter[0];
25710  } else val[0] = (Tfloat)(*data)*filter[0];
25711  for (int k = 1; k<K; ++k) val[0]+=val[k]*filter[k];
25712  *data = (T)val[0];
25713  if (!pass) {
25714  data+=off;
25715  for (int k = 2; k>0; --k) x[k] = x[k-1];
25716  } else data-=off;
25717  for (int k = K-1; k>0; --k) val[k] = val[k-1];
25718  }
25719  *data = (T)0;
25720  }
25721  } break;
25722  case 2: {
25723  Tfloat x[3]; // [front,center,back]
25724  for (int pass = 0; pass<2; ++pass) {
25725  for (int k = 0; k<3; ++k) x[k] = (Tfloat)(boundary_conditions?*data:0);
25726  for (int k = 0; k<K; ++k) val[k] = 0;
25727  for (int n = 0; n<N-1; ++n) {
25728  if (!pass) { x[0] = (Tfloat)*(data+off); val[0] = (x[1] - x[2])*filter[0]; }
25729  else { x[0] = (Tfloat)*(data-off); val[0] = (x[2] - x[1])*filter[0]; }
25730  for (int k = 1; k<K; ++k) val[0]+=val[k]*filter[k];
25731  *data = (T)val[0];
25732  if (!pass) data+=off; else data-=off;
25733  for (int k = 2; k>0; --k) x[k] = x[k-1];
25734  for (int k = K-1; k>0; --k) val[k] = val[k-1];
25735  }
25736  *data = (T)0;
25737  }
25738  } break;
25739  case 3: {
25740  Tfloat x[3]; // [front,center,back]
25741  for (int pass = 0; pass<2; ++pass) {
25742  for (int k = 0; k<3; ++k) x[k] = (Tfloat)(boundary_conditions?*data:0);
25743  for (int k = 0; k<K; ++k) val[k] = 0;
25744  for (int n = 0; n<N-1; ++n) {
25745  if (!pass) { x[0] = (Tfloat)*(data+off); val[0] = (x[0] - 2*x[1] + x[2])*filter[0]; }
25746  else { x[0] = (Tfloat)*(data-off); val[0] = 0.5f*(x[2] - x[0])*filter[0]; }
25747  for (int k = 1; k<K; ++k) val[0]+=val[k]*filter[k];
25748  *data = (T)val[0];
25749  if (!pass) data+=off; else data-=off;
25750  for (int k = 2; k>0; --k) x[k] = x[k-1];
25751  for (int k = K-1; k>0; --k) val[k] = val[k-1];
25752  }
25753  *data = (T)0;
25754  }
25755  } break;
25756  }
25757  }
25758 
25760 
25771  CImg<T>& vanvliet(const float sigma, const int order, const char axis='x', const bool boundary_conditions=true) {
25772  if (is_empty()) return *this;
25773  const char naxis = cimg::uncase(axis);
25774  const float nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100;
25775  if (is_empty() || (nsigma<0.1f && !order)) return *this;
25776  const Tfloat
25777  nnsigma = nsigma<0.1f?0.1f:nsigma,
25778  q = (Tfloat)(nnsigma<2.5?3.97156-4.14554*std::sqrt(1-0.2689*nnsigma):0.98711*nnsigma-0.96330),
25779  b0 = 1.57825f + 2.44413f*q + 1.4281f*q*q + 0.422205f*q*q*q,
25780  b1 = (2.44413f*q + 2.85619f*q*q + 1.26661f*q*q*q),
25781  b2 = -(1.4281f*q*q + 1.26661f*q*q*q),
25782  b3 = 0.422205f*q*q*q,
25783  B = 1.f - (b1 + b2 + b3)/b0;
25784  Tfloat filter[4];
25785  filter[0] = B; filter[1] = b1/b0; filter[2] = b2/b0; filter[3] = b3/b0;
25786 
25787  switch (naxis) {
25788  case 'x' : {
25789 #ifdef cimg_use_openmp
25790 #pragma omp parallel for collapse(3) if (_width>=256 && _height*_depth*_spectrum>=16)
25791 #endif
25792  cimg_forYZC(*this,y,z,c)
25793  _cimg_recursive_apply<4>(data(0,y,z,c),filter,_width,1U,order,boundary_conditions);
25794  } break;
25795  case 'y' : {
25796 #ifdef cimg_use_openmp
25797 #pragma omp parallel for collapse(3) if (_width>=256 && _height*_depth*_spectrum>=16)
25798 #endif
25799  cimg_forXZC(*this,x,z,c)
25800  _cimg_recursive_apply<4>(data(x,0,z,c),filter,_height,(unsigned long)_width,order,boundary_conditions);
25801  } break;
25802  case 'z' : {
25803 #ifdef cimg_use_openmp
25804 #pragma omp parallel for collapse(3) if (_width>=256 && _height*_depth*_spectrum>=16)
25805 #endif
25806  cimg_forXYC(*this,x,y,c)
25807  _cimg_recursive_apply<4>(data(x,y,0,c),filter,_depth,(unsigned long)(_width*_height),
25808  order,boundary_conditions);
25809  } break;
25810  default : {
25811 #ifdef cimg_use_openmp
25812 #pragma omp parallel for collapse(3) if (_width>=256 && _height*_depth*_spectrum>=16)
25813 #endif
25814  cimg_forXYZ(*this,x,y,z)
25815  _cimg_recursive_apply<4>(data(x,y,z,0),filter,_spectrum,(unsigned long)(_width*_height*_depth),
25816  order,boundary_conditions);
25817  }
25818  }
25819  return *this;
25820  }
25821 
25823  CImg<Tfloat> get_vanvliet(const float sigma, const int order, const char axis='x',
25824  const bool boundary_conditions=true) const {
25825  return CImg<Tfloat>(*this,false).vanvliet(sigma,order,axis,boundary_conditions);
25826  }
25827 
25829 
25840  CImg<T>& blur(const float sigma_x, const float sigma_y, const float sigma_z,
25841  const bool boundary_conditions=true, const bool is_gaussian=false) {
25842  if (!is_empty()) {
25843  if (is_gaussian) {
25844  if (_width>1) vanvliet(sigma_x,0,'x',boundary_conditions);
25845  if (_height>1) vanvliet(sigma_y,0,'y',boundary_conditions);
25846  if (_depth>1) vanvliet(sigma_z,0,'z',boundary_conditions);
25847  } else {
25848  if (_width>1) deriche(sigma_x,0,'x',boundary_conditions);
25849  if (_height>1) deriche(sigma_y,0,'y',boundary_conditions);
25850  if (_depth>1) deriche(sigma_z,0,'z',boundary_conditions);
25851  }
25852  }
25853  return *this;
25854  }
25855 
25857  CImg<Tfloat> get_blur(const float sigma_x, const float sigma_y, const float sigma_z,
25858  const bool boundary_conditions=true, const bool is_gaussian=false) const {
25859  return CImg<Tfloat>(*this,false).blur(sigma_x,sigma_y,sigma_z,boundary_conditions,is_gaussian);
25860  }
25861 
25863 
25868  CImg<T>& blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=false) {
25869  const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100;
25870  return blur(nsigma,nsigma,nsigma,boundary_conditions,is_gaussian);
25871  }
25872 
25874  CImg<Tfloat> get_blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=false) const {
25875  return CImg<Tfloat>(*this,false).blur(sigma,boundary_conditions,is_gaussian);
25876  }
25877 
25879 
25889  template<typename t>
25891  const float amplitude=60, const float dl=0.8f, const float da=30,
25892  const float gauss_prec=2, const unsigned int interpolation_type=0,
25893  const bool is_fast_approx=1) {
25894 
25895  // Check arguments and init variables
25896  if (!is_sameXYZ(G) || (G._spectrum!=3 && G._spectrum!=6))
25897  throw CImgArgumentException(_cimg_instance
25898  "blur_anisotropic(): Invalid specified diffusion tensor field (%u,%u,%u,%u,%p).",
25899  cimg_instance,
25900  G._width,G._height,G._depth,G._spectrum,G._data);
25901 
25902  if (is_empty() || amplitude<=0 || dl<0) return *this;
25903  const bool is_3d = (G._spectrum==6);
25904  T val_min, val_max = max_min(val_min);
25905 
25906  if (da<=0) { // Iterated oriented Laplacians
25907  CImg<Tfloat> velocity(_width,_height,_depth,_spectrum);
25908  for (unsigned int iteration = 0; iteration<(unsigned int)amplitude; ++iteration) {
25909  Tfloat *ptrd = velocity._data, veloc_max = 0;
25910  if (is_3d) // 3d version
25911  cimg_forC(*this,c) {
25912  CImg_3x3x3(I,Tfloat);
25913  cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
25914  const Tfloat
25915  ixx = Incc + Ipcc - 2*Iccc,
25916  ixy = (Innc + Ippc - Inpc - Ipnc)/4,
25917  ixz = (Incn + Ipcp - Incp - Ipcn)/4,
25918  iyy = Icnc + Icpc - 2*Iccc,
25919  iyz = (Icnn + Icpp - Icnp - Icpn)/4,
25920  izz = Iccn + Iccp - 2*Iccc,
25921  veloc = (Tfloat)(G(x,y,z,0)*ixx + 2*G(x,y,z,1)*ixy + 2*G(x,y,z,2)*ixz +
25922  G(x,y,z,3)*iyy + 2*G(x,y,z,4)*iyz + G(x,y,z,5)*izz);
25923  *(ptrd++) = veloc;
25924  if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
25925  }
25926  }
25927  else // 2d version
25928  cimg_forZC(*this,z,c) {
25929  CImg_3x3(I,Tfloat);
25930  cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
25931  const Tfloat
25932  ixx = Inc + Ipc - 2*Icc,
25933  ixy = (Inn + Ipp - Inp - Ipn)/4,
25934  iyy = Icn + Icp - 2*Icc,
25935  veloc = (Tfloat)(G(x,y,0,0)*ixx + 2*G(x,y,0,1)*ixy + G(x,y,0,2)*iyy);
25936  *(ptrd++) = veloc;
25937  if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
25938  }
25939  }
25940  if (veloc_max>0) *this+=(velocity*=dl/veloc_max);
25941  }
25942  } else { // LIC-based smoothing.
25943  const unsigned long whd = (unsigned long)_width*_height*_depth;
25944  const float sqrt2amplitude = (float)std::sqrt(2*amplitude);
25945  const int dx1 = width() - 1, dy1 = height() - 1, dz1 = depth() - 1;
25946  CImg<Tfloat> res(_width,_height,_depth,_spectrum,0), W(_width,_height,_depth,is_3d?4:3), val(_spectrum,1,1,1,0);
25947  int N = 0;
25948  if (is_3d) { // 3d version
25949  for (float phi = (180%(int)da)/2.0f; phi<=180; phi+=da) {
25950  const float phir = (float)(phi*cimg::PI/180), datmp = (float)(da/std::cos(phir)),
25951  da2 = datmp<1?360.0f:datmp;
25952  for (float theta = 0; theta<360; (theta+=da2),++N) {
25953  const float
25954  thetar = (float)(theta*cimg::PI/180),
25955  vx = (float)(std::cos(thetar)*std::cos(phir)),
25956  vy = (float)(std::sin(thetar)*std::cos(phir)),
25957  vz = (float)std::sin(phir);
25958  const t
25959  *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2),
25960  *pd = G.data(0,0,0,3), *pe = G.data(0,0,0,4), *pf = G.data(0,0,0,5);
25961  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);
25962  cimg_forXYZ(G,xg,yg,zg) {
25963  const t a = *(pa++), b = *(pb++), c = *(pc++), d = *(pd++), e = *(pe++), f = *(pf++);
25964  const float
25965  u = (float)(a*vx + b*vy + c*vz),
25966  v = (float)(b*vx + d*vy + e*vz),
25967  w = (float)(c*vx + e*vy + f*vz),
25968  n = (float)std::sqrt(1e-5+u*u+v*v+w*w),
25969  dln = dl/n;
25970  *(pd0++) = (Tfloat)(u*dln);
25971  *(pd1++) = (Tfloat)(v*dln);
25972  *(pd2++) = (Tfloat)(w*dln);
25973  *(pd3++) = (Tfloat)n;
25974  }
25975 
25976 #ifdef cimg_use_openmp
25977 #pragma omp parallel for collapse(2) if (_width>=256 && _height*_depth>=2) firstprivate(val)
25978 #endif
25979  cimg_forXYZ(*this,x,y,z) {
25980  val.fill(0);
25981  const float
25982  n = (float)W(x,y,z,3),
25983  fsigma = (float)(n*sqrt2amplitude),
25984  fsigma2 = 2*fsigma*fsigma,
25985  length = gauss_prec*fsigma;
25986  float
25987  S = 0,
25988  X = (float)x,
25989  Y = (float)y,
25990  Z = (float)z;
25991  switch (interpolation_type) {
25992  case 0 : { // Nearest neighbor
25993  for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
25994  const int
25995  cx = (int)(X+0.5f),
25996  cy = (int)(Y+0.5f),
25997  cz = (int)(Z+0.5f);
25998  const float
25999  u = (float)W(cx,cy,cz,0),
26000  v = (float)W(cx,cy,cz,1),
26001  w = (float)W(cx,cy,cz,2);
26002  if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,cz,c); ++S; }
26003  else {
26004  const float coef = (float)std::exp(-l*l/fsigma2);
26005  cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,cz,c));
26006  S+=coef;
26007  }
26008  X+=u; Y+=v; Z+=w;
26009  }
26010  } break;
26011  case 1 : { // Linear interpolation
26012  for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
26013  const float
26014  u = (float)(W._linear_atXYZ(X,Y,Z,0)),
26015  v = (float)(W._linear_atXYZ(X,Y,Z,1)),
26016  w = (float)(W._linear_atXYZ(X,Y,Z,2));
26017  if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
26018  else {
26019  const float coef = (float)std::exp(-l*l/fsigma2);
26020  cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
26021  S+=coef;
26022  }
26023  X+=u; Y+=v; Z+=w;
26024  }
26025  } break;
26026  default : { // 2nd order Runge Kutta
26027  for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
26028  const float
26029  u0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,0)),
26030  v0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,1)),
26031  w0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,2)),
26032  u = (float)(W._linear_atXYZ(X+u0,Y+v0,Z+w0,0)),
26033  v = (float)(W._linear_atXYZ(X+u0,Y+v0,Z+w0,1)),
26034  w = (float)(W._linear_atXYZ(X+u0,Y+v0,Z+w0,2));
26035  if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
26036  else {
26037  const float coef = (float)std::exp(-l*l/fsigma2);
26038  cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
26039  S+=coef;
26040  }
26041  X+=u; Y+=v; Z+=w;
26042  }
26043  } break;
26044  }
26045  Tfloat *ptrd = res.data(x,y,z);
26046  if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; }
26047  else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,z,c)); ptrd+=whd; }
26048  }
26049  }
26050  }
26051  } else { // 2d LIC algorithm
26052  for (float theta = (360%(int)da)/2.0f; theta<360; (theta+=da),++N) {
26053  const float thetar = (float)(theta*cimg::PI/180),
26054  vx = (float)(std::cos(thetar)), vy = (float)(std::sin(thetar));
26055  const t *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2);
26056  Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(0,0,0,2);
26057  cimg_forXY(G,xg,yg) {
26058  const t a = *(pa++), b = *(pb++), c = *(pc++);
26059  const float
26060  u = (float)(a*vx + b*vy),
26061  v = (float)(b*vx + c*vy),
26062  n = (float)std::sqrt(1e-5+u*u+v*v),
26063  dln = dl/n;
26064  *(pd0++) = (Tfloat)(u*dln);
26065  *(pd1++) = (Tfloat)(v*dln);
26066  *(pd2++) = (Tfloat)n;
26067  }
26068 
26069 #ifdef cimg_use_openmp
26070 #pragma omp parallel for if (_width>=256 && _height>=2) firstprivate(val)
26071 #endif
26072  cimg_forXY(*this,x,y) {
26073  val.fill(0);
26074  const float
26075  n = (float)W(x,y,0,2),
26076  fsigma = (float)(n*sqrt2amplitude),
26077  fsigma2 = 2*fsigma*fsigma,
26078  length = gauss_prec*fsigma;
26079  float
26080  S = 0,
26081  X = (float)x,
26082  Y = (float)y;
26083  switch (interpolation_type) {
26084  case 0 : { // Nearest-neighbor
26085  for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
26086  const int
26087  cx = (int)(X+0.5f),
26088  cy = (int)(Y+0.5f);
26089  const float
26090  u = (float)W(cx,cy,0,0),
26091  v = (float)W(cx,cy,0,1);
26092  if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,0,c); ++S; }
26093  else {
26094  const float coef = (float)std::exp(-l*l/fsigma2);
26095  cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,0,c));
26096  S+=coef;
26097  }
26098  X+=u; Y+=v;
26099  }
26100  } break;
26101  case 1 : { // Linear interpolation
26102  for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
26103  const float
26104  u = (float)(W._linear_atXY(X,Y,0,0)),
26105  v = (float)(W._linear_atXY(X,Y,0,1));
26106  if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
26107  else {
26108  const float coef = (float)std::exp(-l*l/fsigma2);
26109  cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
26110  S+=coef;
26111  }
26112  X+=u; Y+=v;
26113  }
26114  } break;
26115  default : { // 2nd-order Runge-kutta interpolation
26116  for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
26117  const float
26118  u0 = (float)(0.5f*W._linear_atXY(X,Y,0,0)),
26119  v0 = (float)(0.5f*W._linear_atXY(X,Y,0,1)),
26120  u = (float)(W._linear_atXY(X+u0,Y+v0,0,0)),
26121  v = (float)(W._linear_atXY(X+u0,Y+v0,0,1));
26122  if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
26123  else {
26124  const float coef = (float)std::exp(-l*l/fsigma2);
26125  cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
26126  S+=coef;
26127  }
26128  X+=u; Y+=v;
26129  }
26130  }
26131  }
26132  Tfloat *ptrd = res.data(x,y);
26133  if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; }
26134  else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,0,c)); ptrd+=whd; }
26135  }
26136  }
26137  }
26138  const Tfloat *ptrs = res._data;
26139  cimg_for(*this,ptrd,T) {
26140  const Tfloat val = *(ptrs++)/N;
26141  *ptrd = val<val_min?val_min:(val>val_max?val_max:(T)val);
26142  }
26143  }
26144  return *this;
26145  }
26146 
26148  template<typename t>
26150  const float amplitude=60, const float dl=0.8f, const float da=30,
26151  const float gauss_prec=2, const unsigned int interpolation_type=0,
26152  const bool is_fast_approx=true) const {
26153  return (+*this).blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx);
26154  }
26155 
26157 
26170  CImg<T>& blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f,
26171  const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30,
26172  const float gauss_prec=2, const unsigned int interpolation_type=0,
26173  const bool is_fast_approx=true) {
26174  return blur_anisotropic(get_diffusion_tensors(sharpness,anisotropy,alpha,sigma,interpolation_type!=3),
26175  amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx);
26176  }
26177 
26179  CImg<T> get_blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f,
26180  const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f,
26181  const float da=30, const float gauss_prec=2, const unsigned int interpolation_type=0,
26182  const bool is_fast_approx=true) const {
26183  return (+*this).blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,interpolation_type,
26184  is_fast_approx);
26185  }
26186 
26188 
26202  template<typename t>
26204  const float sigma_x, const float sigma_y, const float sigma_z, const float sigma_r,
26205  const int bgrid_x, const int bgrid_y, const int bgrid_z, const int bgrid_r,
26206  const bool interpolation_type=true) {
26207  if (!is_sameXYZ(guide))
26208  throw CImgArgumentException(_cimg_instance
26209  "blur_bilateral(): Invalid size for specified guide image (%u,%u,%u,%u,%p).",
26210  cimg_instance,
26211  guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
26212  if (is_empty()) return *this;
26213  T m, M = guide.max_min(m);
26214  if (m==M) return *this;
26215  const float range = (float)(M - m);
26216  const unsigned int
26217  bx0 = bgrid_x>=0?bgrid_x:_width*-bgrid_x/100,
26218  by0 = bgrid_y>=0?bgrid_y:_height*-bgrid_y/100,
26219  bz0 = bgrid_z>=0?bgrid_z:_depth*-bgrid_z/100,
26220  br0 = bgrid_r>=0?bgrid_r:(int)(-range*bgrid_r/100),
26221  bx = bx0>0?bx0:1,
26222  by = by0>0?by0:1,
26223  bz = bz0>0?bz0:1,
26224  br = br0>0?br0:1;
26225  const float
26226  _sigma_x = sigma_x>=0?sigma_x:-sigma_x*_width/100,
26227  _sigma_y = sigma_y>=0?sigma_y:-sigma_y*_height/100,
26228  _sigma_z = sigma_z>=0?sigma_z:-sigma_z*_depth/100,
26229  _sigma_r = sigma_r>=0?sigma_r:-sigma_r*range/100,
26230  nsigma_x = _sigma_x*bx/_width,
26231  nsigma_y = _sigma_y*by/_height,
26232  nsigma_z = _sigma_z*bz/_depth,
26233  nsigma_r = _sigma_r*br/range;
26234  if (nsigma_x>0 || nsigma_y>0 || nsigma_z>0 || nsigma_r>0) {
26235  const bool is_3d = (_depth>1);
26236  if (is_3d) { // 3d version of the algorithm
26237  CImg<floatT> bgrid(bx,by,bz,br), bgridw(bx,by,bz,br);
26238  cimg_forC(*this,c) {
26239  const CImg<t> _guide = guide.get_shared_channel(c%guide._spectrum);
26240  bgrid.fill(0); bgridw.fill(0);
26241  cimg_forXYZ(*this,x,y,z) {
26242  const T val = (*this)(x,y,z,c);
26243  const float gval = (float)_guide(x,y,z);
26244  const int X = x*bx/_width, Y = y*by/_height, Z = z*bz/_depth,
26245  R = (int)cimg::min(br-1.0f,(gval-m)*br/range);
26246  bgrid(X,Y,Z,R) += (float)val;
26247  bgridw(X,Y,Z,R) += 1;
26248  }
26249  bgrid.blur(nsigma_x,nsigma_y,nsigma_z,true).deriche(nsigma_r,0,'c',false);
26250  bgridw.blur(nsigma_x,nsigma_y,nsigma_z,true).deriche(nsigma_r,0,'c',false);
26251  if (interpolation_type) cimg_forXYZ(*this,x,y,z) {
26252  const float gval = (float)_guide(x,y,z),
26253  X = (float)x*bx/_width, Y = (float)y*by/_height, Z = (float)z*bz/_depth,
26254  R = (float)cimg::min(br-1.0f,(gval-m)*br/range),
26255  bval0 = bgrid._linear_atXYZC(X,Y,Z,R), bval1 = bgridw._linear_atXYZC(X,Y,Z,R);
26256  (*this)(x,y,z,c) = (T)(bval0/bval1);
26257  } else cimg_forXYZ(*this,x,y,z) {
26258  const float gval = (float)_guide(x,y,z);
26259  const int X = x*bx/_width, Y = y*by/_height, Z = z*bz/_depth,
26260  R = (int)cimg::min(br-1.0f,(gval-m)*br/range);
26261  const float bval0 = bgrid(X,Y,Z,R), bval1 = bgridw(X,Y,Z,R);
26262  (*this)(x,y,z,c) = (T)(bval0/bval1);
26263  }
26264  }
26265  } else { // 2d version of the algorithm
26266  CImg<floatT> bgrid(bx,by,br,2);
26267  cimg_forC(*this,c) {
26268  const CImg<t> _guide = guide.get_shared_channel(c%guide._spectrum);
26269  bgrid.fill(0);
26270  cimg_forXY(*this,x,y) {
26271  const T val = (*this)(x,y,c);
26272  const float gval = (float)_guide(x,y);
26273  const int X = x*bx/_width, Y = y*by/_height, R = (int)cimg::min(br-1.0f,(gval-m)*br/range);
26274  bgrid(X,Y,R,0) += (float)val;
26275  bgrid(X,Y,R,1) += 1;
26276  }
26277  bgrid.blur(nsigma_x,nsigma_y,0,true).blur(0,0,nsigma_r,false);
26278  if (interpolation_type) cimg_forXY(*this,x,y) {
26279  const float gval = (float)_guide(x,y),
26280  X = (float)x*bx/_width, Y = (float)y*by/_height, R = (float)cimg::min(br-1.0f,(gval-m)*br/range),
26281  bval0 = bgrid._linear_atXYZ(X,Y,R,0), bval1 = bgrid._linear_atXYZ(X,Y,R,1);
26282  (*this)(x,y,c) = (T)(bval0/bval1);
26283  } else cimg_forXY(*this,x,y) {
26284  const float gval = (float)_guide(x,y);
26285  const int X = x*bx/_width, Y = y*by/_height, R = (int)cimg::min(br-1.0f,(gval-m)*br/range);
26286  const float bval0 = bgrid(X,Y,R,0), bval1 = bgrid(X,Y,R,1);
26287  (*this)(x,y,c) = (T)(bval0/bval1);
26288  }
26289  }
26290  }
26291  }
26292  return *this;
26293  }
26294 
26296  template<typename t>
26298  const float sigma_x, const float sigma_y, const float sigma_z, const float sigma_r,
26299  const int bgrid_x, const int bgrid_y, const int bgrid_z, const int bgrid_r,
26300  const bool interpolation_type=true) const {
26301  return (+*this).blur_bilateral(guide,sigma_x,sigma_y,sigma_z,sigma_r,bgrid_x,bgrid_y,bgrid_z,bgrid_r,
26302  interpolation_type);
26303  }
26304 
26306 
26314  template<typename t>
26316  const float sigma_s, const float sigma_r, const int bgrid_s=-33, const int bgrid_r=32,
26317  const bool interpolation_type=true) {
26318  const float nsigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100;
26319  return blur_bilateral(guide,nsigma_s,nsigma_s,nsigma_s,sigma_r,bgrid_s,bgrid_s,bgrid_s,bgrid_r,
26320  interpolation_type);
26321  }
26322 
26324  template<typename t>
26326  const float sigma_s, const float sigma_r, const int bgrid_s=-33, const int bgrid_r=32,
26327  const bool interpolation_type=true) const {
26328  return (+*this).blur_bilateral(guide,sigma_s,sigma_s,sigma_s,sigma_r,bgrid_s,bgrid_s,bgrid_s,bgrid_r,
26329  interpolation_type);
26330  }
26331 
26333 
26341  CImg<T>& blur_patch(const float sigma_s, const float sigma_p, const unsigned int patch_size=3,
26342  const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true) {
26343  if (is_empty() || !patch_size || !lookup_size) return *this;
26344  return get_blur_patch(sigma_s,sigma_p,patch_size,lookup_size,smoothness,is_fast_approx).move_to(*this);
26345  }
26346 
26348  CImg<T> get_blur_patch(const float sigma_s, const float sigma_p, const unsigned int patch_size=3,
26349  const unsigned int lookup_size=4, const float smoothness=0,
26350  const bool is_fast_approx=true) const {
26351 
26352 #define _cimg_blur_patch3d_fast(N) \
26353  cimg_for##N##XYZ(res,x,y,z) { \
26354  T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,x,y,z,c,pP,T); pP+=N3; } \
26355  const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, \
26356  x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \
26357  float sum_weights = 0; \
26358  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) { \
26359  T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,p,q,r,c,pQ,T); pQ+=N3; } \
26360  float distance2 = 0; \
26361  pQ = Q._data; cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(pQ++); distance2+=dI*dI; } \
26362  distance2/=Pnorm; \
26363  const float dx = (float)p - x, dy = (float)q - y, dz = (float)r - z, \
26364  alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = alldist>3?0.0f:1.0f; \
26365  sum_weights+=weight; \
26366  cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c); \
26367  } \
26368  if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights; \
26369  else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \
26370  }
26371 
26372 #define _cimg_blur_patch3d(N) \
26373  cimg_for##N##XYZ(res,x,y,z) { \
26374  T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,x,y,z,c,pP,T); pP+=N3; } \
26375  const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, \
26376  x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \
26377  float sum_weights = 0, weight_max = 0; \
26378  cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) { \
26379  T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,p,q,r,c,pQ,T); pQ+=N3; } \
26380  float distance2 = 0; \
26381  pQ = Q._data; cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(pQ++); distance2+=dI*dI; } \
26382  distance2/=Pnorm; \
26383  const float dx = (float)p - x, dy = (float)q - y, dz = (float)r - z, \
26384  alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = (float)std::exp(-alldist); \
26385  if (weight>weight_max) weight_max = weight; \
26386  sum_weights+=weight; \
26387  cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c); \
26388  } \
26389  sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=weight_max*(*this)(x,y,z,c); \
26390  if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights; \
26391  else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \
26392  }
26393 
26394 #define _cimg_blur_patch2d_fast(N) \
26395  cimg_for##N##XY(res,x,y) { \
26396  T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N(img,x,y,0,c,pP,T); pP+=N2; } \
26397  const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \
26398  float sum_weights = 0; \
26399  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) { \
26400  T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N(img,p,q,0,c,pQ,T); pQ+=N2; } \
26401  float distance2 = 0; \
26402  pQ = Q._data; cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(pQ++); distance2+=dI*dI; } \
26403  distance2/=Pnorm; \
26404  const float dx = (float)p - x, dy = (float)q - y, \
26405  alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = alldist>3?0.0f:1.0f; \
26406  sum_weights+=weight; \
26407  cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c); \
26408  } \
26409  if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights; \
26410  else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \
26411  }
26412 
26413 #define _cimg_blur_patch2d(N) \
26414  cimg_for##N##XY(res,x,y) { \
26415  T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N(img,x,y,0,c,pP,T); pP+=N2; } \
26416  const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \
26417  float sum_weights = 0, weight_max = 0; \
26418  cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) { \
26419  T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N(img,p,q,0,c,pQ,T); pQ+=N2; } \
26420  float distance2 = 0; \
26421  pQ = Q._data; cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(pQ++); distance2+=dI*dI; } \
26422  distance2/=Pnorm; \
26423  const float dx = (float)p - x, dy = (float)q - y, \
26424  alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = (float)std::exp(-alldist); \
26425  if (weight>weight_max) weight_max = weight; \
26426  sum_weights+=weight; \
26427  cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c); \
26428  } \
26429  sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=weight_max*(*this)(x,y,c); \
26430  if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights; \
26431  else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \
26432  }
26433 
26434  if (is_empty() || !patch_size || !lookup_size) return +*this;
26435  CImg<Tfloat> res(_width,_height,_depth,_spectrum,0);
26436  const CImg<T> _img = smoothness>0?get_blur(smoothness):CImg<Tfloat>(),&img = smoothness>0?_img:*this;
26437  CImg<T> P(patch_size*patch_size*_spectrum), Q(P);
26438  const float
26439  nsigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100,
26440  sigma_s2 = nsigma_s*nsigma_s, sigma_p2 = sigma_p*sigma_p, sigma_p3 = 3*sigma_p,
26441  Pnorm = P.size()*sigma_p2;
26442  const int rsize2 = (int)lookup_size/2, rsize1 = (int)lookup_size - rsize2 - 1;
26443  const unsigned int N2 = patch_size*patch_size, N3 = N2*patch_size;
26444  cimg::unused(N2,N3);
26445  if (_depth>1) switch (patch_size) { // 3d
26446  case 2 : if (is_fast_approx) _cimg_blur_patch3d_fast(2) else _cimg_blur_patch3d(2) break;
26447  case 3 : if (is_fast_approx) _cimg_blur_patch3d_fast(3) else _cimg_blur_patch3d(3) break;
26448  default : {
26449  const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1;
26450  if (is_fast_approx)
26451 #ifdef cimg_use_openmp
26452 #pragma omp parallel for collapse(2) if (res._width>=32 && res._height*res._depth>=4) private(P,Q)
26453 #endif
26454  cimg_forXYZ(res,x,y,z) { // Fast
26455  P = img.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true);
26456  const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1,
26457  x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2;
26458  float sum_weights = 0;
26459  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) {
26460  (Q = img.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P;
26461  const float
26462  dx = (float)x - p, dy = (float)y - q, dz = (float)z - r,
26463  distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2),
26464  weight = distance2>3?0.0f:1.0f;
26465  sum_weights+=weight;
26466  cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c);
26467  }
26468  if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights;
26469  else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c));
26470  } else
26471 #ifdef cimg_use_openmp
26472 #pragma omp parallel for collapse(2) if (res._width>=32 && res._height*res._depth>=4) firstprivate(P,Q)
26473 #endif
26474  cimg_forXYZ(res,x,y,z) { // Exact
26475  P = img.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true);
26476  const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1,
26477  x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2;
26478  float sum_weights = 0, weight_max = 0;
26479  cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) {
26480  (Q = img.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P;
26481  const float
26482  dx = (float)x - p, dy = (float)y - q, dz = (float)z - r,
26483  distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2),
26484  weight = (float)std::exp(-distance2);
26485  if (weight>weight_max) weight_max = weight;
26486  sum_weights+=weight;
26487  cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c);
26488  }
26489  sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=weight_max*(*this)(x,y,z,c);
26490  if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights;
26491  else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c));
26492  }
26493  }
26494  } else switch (patch_size) { // 2d
26495  case 2 : if (is_fast_approx) _cimg_blur_patch2d_fast(2) else _cimg_blur_patch2d(2) break;
26496  case 3 : if (is_fast_approx) _cimg_blur_patch2d_fast(3) else _cimg_blur_patch2d(3) break;
26497  case 4 : if (is_fast_approx) _cimg_blur_patch2d_fast(4) else _cimg_blur_patch2d(4) break;
26498  case 5 : if (is_fast_approx) _cimg_blur_patch2d_fast(5) else _cimg_blur_patch2d(5) break;
26499  case 6 : if (is_fast_approx) _cimg_blur_patch2d_fast(6) else _cimg_blur_patch2d(6) break;
26500  case 7 : if (is_fast_approx) _cimg_blur_patch2d_fast(7) else _cimg_blur_patch2d(7) break;
26501  case 8 : if (is_fast_approx) _cimg_blur_patch2d_fast(8) else _cimg_blur_patch2d(8) break;
26502  case 9 : if (is_fast_approx) _cimg_blur_patch2d_fast(9) else _cimg_blur_patch2d(9) break;
26503  default : { // Fast
26504  const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1;
26505  if (is_fast_approx)
26506 #ifdef cimg_use_openmp
26507 #pragma omp parallel for if (res._width>=32 && res._height>=4) firstprivate(P,Q)
26508 #endif
26509  cimg_forXY(res,x,y) { // 2d fast approximation.
26510  P = img.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true);
26511  const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2;
26512  float sum_weights = 0;
26513  cimg_for_inXY(res,x0,y0,x1,y1,p,q) if (cimg::abs(img(x,y,0)-img(p,q,0))<sigma_p3) {
26514  (Q = img.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P;
26515  const float
26516  dx = (float)x - p, dy = (float)y - q,
26517  distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2),
26518  weight = distance2>3?0.0f:1.0f;
26519  sum_weights+=weight;
26520  cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c);
26521  }
26522  if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights;
26523  else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c));
26524  } else
26525 #ifdef cimg_use_openmp
26526 #pragma omp parallel for if (res._width>=32 && res._height>=4) firstprivate(P,Q)
26527 #endif
26528  cimg_forXY(res,x,y) { // 2d exact algorithm.
26529  P = img.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true);
26530  const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2;
26531  float sum_weights = 0, weight_max = 0;
26532  cimg_for_inXY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) {
26533  (Q = img.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P;
26534  const float
26535  dx = (float)x - p, dy = (float)y - q,
26536  distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2),
26537  weight = (float)std::exp(-distance2);
26538  if (weight>weight_max) weight_max = weight;
26539  sum_weights+=weight;
26540  cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c);
26541  }
26542  sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=weight_max*(*this)(x,y,c);
26543  if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights;
26544  else cimg_forC(res,c) res(x,y,0,c) = (Tfloat)((*this)(x,y,c));
26545  }
26546  }
26547  }
26548  return res;
26549  }
26550 
26552 
26556  CImg<T>& blur_median(const unsigned int n, const float threshold=0) {
26557  if (!n) return *this;
26558  return get_blur_median(n,threshold).move_to(*this);
26559  }
26560 
26562  CImg<T> get_blur_median(const unsigned int n, const float threshold=0) const {
26563  if (is_empty() || n<=1) return +*this;
26564  CImg<T> res(_width,_height,_depth,_spectrum);
26565  T *ptrd = res._data;
26566  cimg::unused(ptrd);
26567  const int hl = n/2, hr = hl - 1 + n%2;
26568  if (res._depth!=1) { // 3d
26569  if (threshold>0)
26570 #if cimg_use_openmp
26571 #pragma omp parallel for collapse(3) if (_width>=16 && _height*_depth*_spectrum>=4)
26572 #endif
26573  cimg_forXYZC(*this,x,y,z,c) { // With threshold.
26574  const int
26575  x0 = x - hl, y0 = y - hl, z0 = z-hl, x1 = x + hr, y1 = y + hr, z1 = z+hr,
26576  nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0,
26577  nx1 = x1>=width()?width()-1:x1, ny1 = y1>=height()?height()-1:y1, nz1 = z1>=depth()?depth()-1:z1;
26578  const float val0 = (float)(*this)(x,y,z,c);
26579  CImg<T> values(n*n*n);
26580  unsigned int nb_values = 0;
26581  T *ptrd = values.data();
26582  cimg_for_inXYZ(*this,nx0,ny0,nz0,nx1,ny1,nz1,p,q,r)
26583  if (cimg::abs((float)(*this)(p,q,r,c)-val0)<=threshold) { *(ptrd++) = (*this)(p,q,r,c); ++nb_values; }
26584  res(x,y,z,c) = values.get_shared_points(0,nb_values-1).median();
26585  }
26586  else
26587 #if cimg_use_openmp
26588 #pragma omp parallel for collapse(3) if (_width>=16 && _height*_depth*_spectrum>=4)
26589 #endif
26590  cimg_forXYZC(*this,x,y,z,c) { // Without threshold.
26591  const int
26592  x0 = x - hl, y0 = y - hl, z0 = z-hl, x1 = x + hr, y1 = y + hr, z1 = z+hr,
26593  nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0,
26594  nx1 = x1>=width()?width()-1:x1, ny1 = y1>=height()?height()-1:y1, nz1 = z1>=depth()?depth()-1:z1;
26595  res(x,y,z,c) = get_crop(nx0,ny0,nz0,c,nx1,ny1,nz1,c).median();
26596  }
26597  } else {
26598 #define _cimg_median_sort(a,b) if ((a)>(b)) cimg::swap(a,b)
26599  if (res._height!=1) { // 2d
26600  if (threshold>0)
26601 #ifdef cimg_use_openmp
26602 #pragma omp parallel for collapse(2) if (_width>=16 && _height*_spectrum>=4)
26603 #endif
26604  cimg_forXYC(*this,x,y,c) { // With threshold.
26605  const int
26606  x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr,
26607  nx0 = x0<0?0:x0, ny0 = y0<0?0:y0,
26608  nx1 = x1>=width()?width()-1:x1, ny1 = y1>=height()?height()-1:y1;
26609  const float val0 = (float)(*this)(x,y,c);
26610  CImg<T> values(n*n);
26611  unsigned int nb_values = 0;
26612  T *ptrd = values.data();
26613  cimg_for_inXY(*this,nx0,ny0,nx1,ny1,p,q)
26614  if (cimg::abs((float)(*this)(p,q,c)-val0)<=threshold) { *(ptrd++) = (*this)(p,q,c); ++nb_values; }
26615  res(x,y,c) = values.get_shared_points(0,nb_values-1).median();
26616  }
26617  else switch (n) { // Without threshold.
26618  case 3 : {
26619 #if cimg_use_openmp
26620 #pragma omp parallel for if (_spectrum>=2)
26621 #endif
26622  cimg_forC(*this,c) {
26623  T I[9] = { 0 };
26624  CImg_3x3(J,T);
26625  cimg_for3x3(*this,x,y,0,c,I,T) {
26626  std::memcpy(J,I,9*sizeof(T));
26627  _cimg_median_sort(Jcp, Jnp); _cimg_median_sort(Jcc, Jnc); _cimg_median_sort(Jcn, Jnn);
26628  _cimg_median_sort(Jpp, Jcp); _cimg_median_sort(Jpc, Jcc); _cimg_median_sort(Jpn, Jcn);
26629  _cimg_median_sort(Jcp, Jnp); _cimg_median_sort(Jcc, Jnc); _cimg_median_sort(Jcn, Jnn);
26630  _cimg_median_sort(Jpp, Jpc); _cimg_median_sort(Jnc, Jnn); _cimg_median_sort(Jcc, Jcn);
26631  _cimg_median_sort(Jpc, Jpn); _cimg_median_sort(Jcp, Jcc); _cimg_median_sort(Jnp, Jnc);
26632  _cimg_median_sort(Jcc, Jcn); _cimg_median_sort(Jcc, Jnp); _cimg_median_sort(Jpn, Jcc);
26633  _cimg_median_sort(Jcc, Jnp);
26634  res(x,y,c) = Jcc;
26635  }
26636  }
26637  } break;
26638  case 5 : {
26639 #if cimg_use_openmp
26640 #pragma omp parallel for if (_spectrum>=2)
26641 #endif
26642  cimg_forC(*this,c) {
26643  T I[25] = { 0 };
26644  CImg_5x5(J,T);
26645  cimg_for5x5(*this,x,y,0,c,I,T) {
26646  std::memcpy(J,I,25*sizeof(T));
26647  _cimg_median_sort(Jbb,Jpb); _cimg_median_sort(Jnb,Jab); _cimg_median_sort(Jcb,Jab);
26648  _cimg_median_sort(Jcb,Jnb); _cimg_median_sort(Jpp,Jcp); _cimg_median_sort(Jbp,Jcp);
26649  _cimg_median_sort(Jbp,Jpp); _cimg_median_sort(Jap,Jbc); _cimg_median_sort(Jnp,Jbc);
26650  _cimg_median_sort(Jnp,Jap); _cimg_median_sort(Jcc,Jnc); _cimg_median_sort(Jpc,Jnc);
26651  _cimg_median_sort(Jpc,Jcc); _cimg_median_sort(Jbn,Jpn); _cimg_median_sort(Jac,Jpn);
26652  _cimg_median_sort(Jac,Jbn); _cimg_median_sort(Jnn,Jan); _cimg_median_sort(Jcn,Jan);
26653  _cimg_median_sort(Jcn,Jnn); _cimg_median_sort(Jpa,Jca); _cimg_median_sort(Jba,Jca);
26654  _cimg_median_sort(Jba,Jpa); _cimg_median_sort(Jna,Jaa); _cimg_median_sort(Jcb,Jbp);
26655  _cimg_median_sort(Jnb,Jpp); _cimg_median_sort(Jbb,Jpp); _cimg_median_sort(Jbb,Jnb);
26656  _cimg_median_sort(Jab,Jcp); _cimg_median_sort(Jpb,Jcp); _cimg_median_sort(Jpb,Jab);
26657  _cimg_median_sort(Jpc,Jac); _cimg_median_sort(Jnp,Jac); _cimg_median_sort(Jnp,Jpc);
26658  _cimg_median_sort(Jcc,Jbn); _cimg_median_sort(Jap,Jbn); _cimg_median_sort(Jap,Jcc);
26659  _cimg_median_sort(Jnc,Jpn); _cimg_median_sort(Jbc,Jpn); _cimg_median_sort(Jbc,Jnc);
26660  _cimg_median_sort(Jba,Jna); _cimg_median_sort(Jcn,Jna); _cimg_median_sort(Jcn,Jba);
26661  _cimg_median_sort(Jpa,Jaa); _cimg_median_sort(Jnn,Jaa); _cimg_median_sort(Jnn,Jpa);
26662  _cimg_median_sort(Jan,Jca); _cimg_median_sort(Jnp,Jcn); _cimg_median_sort(Jap,Jnn);
26663  _cimg_median_sort(Jbb,Jnn); _cimg_median_sort(Jbb,Jap); _cimg_median_sort(Jbc,Jan);
26664  _cimg_median_sort(Jpb,Jan); _cimg_median_sort(Jpb,Jbc); _cimg_median_sort(Jpc,Jba);
26665  _cimg_median_sort(Jcb,Jba); _cimg_median_sort(Jcb,Jpc); _cimg_median_sort(Jcc,Jpa);
26666  _cimg_median_sort(Jnb,Jpa); _cimg_median_sort(Jnb,Jcc); _cimg_median_sort(Jnc,Jca);
26667  _cimg_median_sort(Jab,Jca); _cimg_median_sort(Jab,Jnc); _cimg_median_sort(Jac,Jna);
26668  _cimg_median_sort(Jbp,Jna); _cimg_median_sort(Jbp,Jac); _cimg_median_sort(Jbn,Jaa);
26669  _cimg_median_sort(Jpp,Jaa); _cimg_median_sort(Jpp,Jbn); _cimg_median_sort(Jcp,Jpn);
26670  _cimg_median_sort(Jcp,Jan); _cimg_median_sort(Jnc,Jpa); _cimg_median_sort(Jbn,Jna);
26671  _cimg_median_sort(Jcp,Jnc); _cimg_median_sort(Jcp,Jbn); _cimg_median_sort(Jpb,Jap);
26672  _cimg_median_sort(Jnb,Jpc); _cimg_median_sort(Jbp,Jcn); _cimg_median_sort(Jpc,Jcn);
26673  _cimg_median_sort(Jap,Jcn); _cimg_median_sort(Jab,Jbc); _cimg_median_sort(Jpp,Jcc);
26674  _cimg_median_sort(Jcp,Jac); _cimg_median_sort(Jab,Jpp); _cimg_median_sort(Jab,Jcp);
26675  _cimg_median_sort(Jcc,Jac); _cimg_median_sort(Jbc,Jac); _cimg_median_sort(Jpp,Jcp);
26676  _cimg_median_sort(Jbc,Jcc); _cimg_median_sort(Jpp,Jbc); _cimg_median_sort(Jpp,Jcn);
26677  _cimg_median_sort(Jcc,Jcn); _cimg_median_sort(Jcp,Jcn); _cimg_median_sort(Jcp,Jbc);
26678  _cimg_median_sort(Jcc,Jnn); _cimg_median_sort(Jcp,Jcc); _cimg_median_sort(Jbc,Jnn);
26679  _cimg_median_sort(Jcc,Jba); _cimg_median_sort(Jbc,Jba); _cimg_median_sort(Jbc,Jcc);
26680  res(x,y,c) = Jcc;
26681  }
26682  }
26683  } break;
26684  default : {
26685 #ifdef cimg_use_openmp
26686 #pragma omp parallel for collapse(2) if (_width>=16 && _height*_spectrum>=4)
26687 #endif
26688  cimg_forXYC(*this,x,y,c) {
26689  const int
26690  x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr,
26691  nx0 = x0<0?0:x0, ny0 = y0<0?0:y0,
26692  nx1 = x1>=width()?width()-1:x1, ny1 = y1>=height()?height()-1:y1;
26693  res(x,y,c) = get_crop(nx0,ny0,0,c,nx1,ny1,0,c).median();
26694  }
26695  }
26696  }
26697  } else { // 1d
26698 
26699  if (threshold>0)
26700 #ifdef cimg_use_openmp
26701 #pragma omp parallel for if (_width>=16 && _spectrum>=2)
26702 #endif
26703  cimg_forXC(*this,x,c) { // With threshold.
26704  const int
26705  x0 = x - hl, x1 = x + hr,
26706  nx0 = x0<0?0:x0, nx1 = x1>=width()?width()-1:x1;
26707  const float val0 = (float)(*this)(x,c);
26708  CImg<T> values(n);
26709  unsigned int nb_values = 0;
26710  T *ptrd = values.data();
26711  cimg_for_inX(*this,nx0,nx1,p)
26712  if (cimg::abs((float)(*this)(p,c)-val0)<=threshold) { *(ptrd++) = (*this)(p,c); ++nb_values; }
26713  res(x,c) = values.get_shared_points(0,nb_values-1).median();
26714  }
26715  else switch (n) { // Without threshold.
26716  case 2 : {
26717 #ifdef cimg_use_openmp
26718 #pragma omp parallel for if (_spectrum>=2)
26719 #endif
26720  cimg_forC(*this,c) {
26721  T I[4] = { 0 };
26722  cimg_for2x2(*this,x,y,0,c,I,T) res(x,c) = (T)(0.5f*(I[0]+I[1]));
26723  }
26724  } break;
26725  case 3 : {
26726 #ifdef cimg_use_openmp
26727 #pragma omp parallel for if (_spectrum>=2)
26728 #endif
26729  cimg_forC(*this,c) {
26730  T I[9] = { 0 };
26731  cimg_for3x3(*this,x,y,0,c,I,T)
26732  res(x,c) = 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]));
26733  }
26734  } break;
26735  default : {
26736 #ifdef cimg_use_openmp
26737 #pragma omp parallel for if (_width>=16 && _spectrum>=2)
26738 #endif
26739  cimg_forXC(*this,x,c) {
26740  const int
26741  x0 = x - hl, x1 = x + hr,
26742  nx0 = x0<0?0:x0, nx1 = x1>=width()?width()-1:x1;
26743  res(x,c) = get_crop(nx0,0,0,c,nx1,0,0,c).median();
26744  }
26745  }
26746  }
26747  }
26748  }
26749  return res;
26750  }
26751 
26753 
26760  CImg<T>& sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1,
26761  const float alpha=0, const float sigma=0) {
26762  if (is_empty()) return *this;
26763  T val_min, val_max = max_min(val_min);
26764  const float nedge = edge/2;
26765  CImg<Tfloat> velocity(_width,_height,_depth,_spectrum), _veloc_max(_spectrum);
26766 
26767  if (_depth>1) { // 3d
26768  if (sharpen_type) { // Shock filters.
26769  CImg<Tfloat> G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors());
26770  if (sigma>0) G.blur(sigma);
26771 #ifdef cimg_use_openmp
26772 #pragma omp parallel for collapse(2) if (_width>=32 && _height*_depth>=16)
26773 #endif
26774  cimg_forYZ(G,y,z) {
26775  Tfloat *ptrG0 = G.data(0,y,z,0), *ptrG1 = G.data(0,y,z,1),
26776  *ptrG2 = G.data(0,y,z,2), *ptrG3 = G.data(0,y,z,3);
26777  CImg<Tfloat> val, vec;
26778  cimg_forX(G,x) {
26779  G.get_tensor_at(x,y,z).symmetric_eigen(val,vec);
26780  if (val[0]<0) val[0] = 0;
26781  if (val[1]<0) val[1] = 0;
26782  if (val[2]<0) val[2] = 0;
26783  *(ptrG0++) = vec(0,0);
26784  *(ptrG1++) = vec(0,1);
26785  *(ptrG2++) = vec(0,2);
26786  *(ptrG3++) = 1 - (Tfloat)std::pow(1+val[0]+val[1]+val[2],-(Tfloat)nedge);
26787  }
26788  }
26789 #ifdef cimg_use_openmp
26790 #pragma omp parallel for if (_width*_height*_depth>=512 && _spectrum>=2)
26791 #endif
26792  cimg_forC(*this,c) {
26793  Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
26794  CImg_3x3x3(I,Tfloat);
26795  cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
26796  const Tfloat
26797  u = G(x,y,z,0),
26798  v = G(x,y,z,1),
26799  w = G(x,y,z,2),
26800  amp = G(x,y,z,3),
26801  ixx = Incc + Ipcc - 2*Iccc,
26802  ixy = (Innc + Ippc - Inpc - Ipnc)/4,
26803  ixz = (Incn + Ipcp - Incp - Ipcn)/4,
26804  iyy = Icnc + Icpc - 2*Iccc,
26805  iyz = (Icnn + Icpp - Icnp - Icpn)/4,
26806  izz = Iccn + Iccp - 2*Iccc,
26807  ixf = Incc - Iccc,
26808  ixb = Iccc - Ipcc,
26809  iyf = Icnc - Iccc,
26810  iyb = Iccc - Icpc,
26811  izf = Iccn - Iccc,
26812  izb = Iccc - Iccp,
26813  itt = u*u*ixx + v*v*iyy + w*w*izz + 2*u*v*ixy + 2*u*w*ixz + 2*v*w*iyz,
26814  it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb) + w*cimg::minmod(izf,izb),
26815  veloc = -amp*cimg::sign(itt)*cimg::abs(it);
26816  *(ptrd++) = veloc;
26817  if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
26818  }
26819  _veloc_max[c] = veloc_max;
26820  }
26821  } else // Inverse diffusion.
26822  cimg_forC(*this,c) {
26823  Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
26824  CImg_3x3x3(I,Tfloat);
26825  cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
26826  const Tfloat veloc = -Ipcc - Incc - Icpc - Icnc - Iccp - Iccn + 6*Iccc;
26827  *(ptrd++) = veloc;
26828  if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
26829  }
26830  _veloc_max[c] = veloc_max;
26831  }
26832  } else { // 2d.
26833  if (sharpen_type) { // Shock filters.
26834  CImg<Tfloat> G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors());
26835  if (sigma>0) G.blur(sigma);
26836 #ifdef cimg_use_openmp
26837 #pragma omp parallel for if (_width>=32 && _height>=16)
26838 #endif
26839  cimg_forY(G,y) {
26840  CImg<Tfloat> val, vec;
26841  Tfloat *ptrG0 = G.data(0,y,0,0), *ptrG1 = G.data(0,y,0,1), *ptrG2 = G.data(0,y,0,2);
26842  cimg_forX(G,x) {
26843  G.get_tensor_at(x,y).symmetric_eigen(val,vec);
26844  if (val[0]<0) val[0] = 0;
26845  if (val[1]<0) val[1] = 0;
26846  *(ptrG0++) = vec(0,0);
26847  *(ptrG1++) = vec(0,1);
26848  *(ptrG2++) = 1 - (Tfloat)std::pow(1 + val[0] + val[1],-(Tfloat)nedge);
26849  }
26850  }
26851 #ifdef cimg_use_openmp
26852 #pragma omp parallel for if (_width*_height>=512 && _spectrum>=2)
26853 #endif
26854  cimg_forC(*this,c) {
26855  Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
26856  CImg_3x3(I,Tfloat);
26857  cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
26858  const Tfloat
26859  u = G(x,y,0),
26860  v = G(x,y,1),
26861  amp = G(x,y,2),
26862  ixx = Inc + Ipc - 2*Icc,
26863  ixy = (Inn + Ipp - Inp - Ipn)/4,
26864  iyy = Icn + Icp - 2*Icc,
26865  ixf = Inc - Icc,
26866  ixb = Icc - Ipc,
26867  iyf = Icn - Icc,
26868  iyb = Icc - Icp,
26869  itt = u*u*ixx + v*v*iyy + 2*u*v*ixy,
26870  it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb),
26871  veloc = -amp*cimg::sign(itt)*cimg::abs(it);
26872  *(ptrd++) = veloc;
26873  if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
26874  }
26875  _veloc_max[c] = veloc_max;
26876  }
26877  } else // Inverse diffusion.
26878  cimg_forC(*this,c) {
26879  Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
26880  CImg_3x3(I,Tfloat);
26881  cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
26882  const Tfloat veloc = -Ipc - Inc - Icp - Icn + 4*Icc;
26883  *(ptrd++) = veloc;
26884  if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
26885  }
26886  _veloc_max[c] = veloc_max;
26887  }
26888  }
26889  const Tfloat veloc_max = _veloc_max.max();
26890  if (veloc_max<=0) return *this;
26891  return ((velocity*=amplitude/veloc_max)+=*this).cut(val_min,val_max).move_to(*this);
26892  }
26893 
26895  CImg<T> get_sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1,
26896  const float alpha=0, const float sigma=0) const {
26897  return (+*this).sharpen(amplitude,sharpen_type,edge,alpha,sigma);
26898  }
26899 
26901 
26912  CImgList<Tfloat> get_gradient(const char *const axes=0, const int scheme=3) const {
26913  CImgList<Tfloat> grad(2,_width,_height,_depth,_spectrum);
26914  bool is_3d = false;
26915  if (axes) {
26916  for (unsigned int a = 0; axes[a]; ++a) {
26917  const char axis = cimg::uncase(axes[a]);
26918  switch (axis) {
26919  case 'x' : case 'y' : break;
26920  case 'z' : is_3d = true; break;
26921  default :
26922  throw CImgArgumentException(_cimg_instance
26923  "get_gradient(): Invalid specified axis '%c'.",
26924  cimg_instance,
26925  axis);
26926  }
26927  }
26928  } else is_3d = (_depth>1);
26929  if (is_3d) {
26930  CImg<Tfloat>(_width,_height,_depth,_spectrum).move_to(grad);
26931  switch (scheme) { // 3d.
26932  case -1 : { // Backward finite differences.
26933 #ifdef cimg_use_openmp
26934 #pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2)
26935 #endif
26936  cimg_forC(*this,c) {
26937  const unsigned long off = c*_width*_height*_depth;
26938  Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off, *ptrd2 = grad[2]._data + off;
26939  CImg_3x3x3(I,Tfloat);
26940  cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
26941  *(ptrd0++) = Iccc - Ipcc;
26942  *(ptrd1++) = Iccc - Icpc;
26943  *(ptrd2++) = Iccc - Iccp;
26944  }
26945  }
26946  } break;
26947  case 1 : { // Forward finite differences.
26948 #ifdef cimg_use_openmp
26949 #pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2)
26950 #endif
26951  cimg_forC(*this,c) {
26952  const unsigned long off = c*_width*_height*_depth;
26953  Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off, *ptrd2 = grad[2]._data + off;
26954  CImg_2x2x2(I,Tfloat);
26955  cimg_for2x2x2(*this,x,y,z,c,I,Tfloat) {
26956  *(ptrd0++) = Incc - Iccc;
26957  *(ptrd1++) = Icnc - Iccc;
26958  *(ptrd2++) = Iccn - Iccc;
26959  }
26960  }
26961  } break;
26962  case 4 : { // Deriche filter with low standard variation.
26963  grad[0] = get_deriche(0,1,'x');
26964  grad[1] = get_deriche(0,1,'y');
26965  grad[2] = get_deriche(0,1,'z');
26966  } break;
26967  case 5 : { // Van Vliet filter with low standard variation.
26968  grad[0] = get_vanvliet(0,1,'x');
26969  grad[1] = get_vanvliet(0,1,'y');
26970  grad[2] = get_vanvliet(0,1,'z');
26971  } break;
26972  default : { // Central finite differences.
26973 #ifdef cimg_use_openmp
26974 #pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2)
26975 #endif
26976  cimg_forC(*this,c) {
26977  const unsigned long off = c*_width*_height*_depth;
26978  Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off, *ptrd2 = grad[2]._data + off;
26979  CImg_3x3x3(I,Tfloat);
26980  cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
26981  *(ptrd0++) = (Incc - Ipcc)/2;
26982  *(ptrd1++) = (Icnc - Icpc)/2;
26983  *(ptrd2++) = (Iccn - Iccp)/2;
26984  }
26985  }
26986  }
26987  }
26988  } else switch (scheme) { // 2d.
26989  case -1 : { // Backward finite differences.
26990 #ifdef cimg_use_openmp
26991 #pragma omp parallel for collapse(2) if (_width*_height>=1048576 && _depth*_spectrum>=2)
26992 #endif
26993  cimg_forZC(*this,z,c) {
26994  const unsigned long off = c*_width*_height*_depth + z*_width*_height;
26995  Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off;
26996  CImg_3x3(I,Tfloat);
26997  cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
26998  *(ptrd0++) = Icc - Ipc;
26999  *(ptrd1++) = Icc - Icp;
27000  }
27001  }
27002  } break;
27003  case 1 : { // Forward finite differences.
27004 #ifdef cimg_use_openmp
27005 #pragma omp parallel for collapse(2) if (_width*_height>=1048576 && _depth*_spectrum>=2)
27006 #endif
27007  cimg_forZC(*this,z,c) {
27008  const unsigned long off = c*_width*_height*_depth + z*_width*_height;
27009  Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off;
27010  CImg_2x2(I,Tfloat);
27011  cimg_for2x2(*this,x,y,z,c,I,Tfloat) {
27012  *(ptrd0++) = Inc - Icc;
27013  *(ptrd1++) = Icn - Icc;
27014  }
27015  }
27016  } break;
27017  case 2 : { // Sobel scheme.
27018 #ifdef cimg_use_openmp
27019 #pragma omp parallel for collapse(2) if (_width*_height>=1048576 && _depth*_spectrum>=2)
27020 #endif
27021  cimg_forZC(*this,z,c) {
27022  const unsigned long off = c*_width*_height*_depth + z*_width*_height;
27023  Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off;
27024  CImg_3x3(I,Tfloat);
27025  cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
27026  *(ptrd0++) = -Ipp - 2*Ipc - Ipn + Inp + 2*Inc + Inn;
27027  *(ptrd1++) = -Ipp - 2*Icp - Inp + Ipn + 2*Icn + Inn;
27028  }
27029  }
27030  } break;
27031  case 3 : { // Rotation invariant mask.
27032 #ifdef cimg_use_openmp
27033 #pragma omp parallel for collapse(2) if (_width*_height>=1048576 && _depth*_spectrum>=2)
27034 #endif
27035  cimg_forZC(*this,z,c) {
27036  const unsigned long off = c*_width*_height*_depth + z*_width*_height;
27037  Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off;
27038  CImg_3x3(I,Tfloat);
27039  const Tfloat a = (Tfloat)(0.25f*(2-std::sqrt(2.0f))), b = (Tfloat)(0.5f*(std::sqrt(2.0f)-1));
27040  cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
27041  *(ptrd0++) = -a*Ipp - b*Ipc - a*Ipn + a*Inp + b*Inc + a*Inn;
27042  *(ptrd1++) = -a*Ipp - b*Icp - a*Inp + a*Ipn + b*Icn + a*Inn;
27043  }
27044  }
27045  } break;
27046  case 4 : { // Van Vliet filter with low standard variation
27047  grad[0] = get_deriche(0,1,'x');
27048  grad[1] = get_deriche(0,1,'y');
27049  } break;
27050  case 5 : { // Deriche filter with low standard variation
27051  grad[0] = get_vanvliet(0,1,'x');
27052  grad[1] = get_vanvliet(0,1,'y');
27053  } break;
27054  default : { // Central finite differences
27055 #ifdef cimg_use_openmp
27056 #pragma omp parallel for collapse(2) if (_width*_height>=1048576 && _depth*_spectrum>=2)
27057 #endif
27058  cimg_forZC(*this,z,c) {
27059  const unsigned long off = c*_width*_height*_depth + z*_width*_height;
27060  Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off;
27061  CImg_3x3(I,Tfloat);
27062  cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
27063  *(ptrd0++) = (Inc - Ipc)/2;
27064  *(ptrd1++) = (Icn - Icp)/2;
27065  }
27066  }
27067  }
27068  }
27069  if (!axes) return grad;
27070  CImgList<Tfloat> res;
27071  for (unsigned int l = 0; axes[l]; ++l) {
27072  const char axis = cimg::uncase(axes[l]);
27073  switch (axis) {
27074  case 'x' : res.insert(grad[0]); break;
27075  case 'y' : res.insert(grad[1]); break;
27076  case 'z' : res.insert(grad[2]); break;
27077  }
27078  }
27079  grad.assign();
27080  return res;
27081  }
27082 
27084 
27087  CImgList<Tfloat> get_hessian(const char *const axes=0) const {
27088  CImgList<Tfloat> res;
27089  const char *naxes = axes, *const def_axes2d = "xxxyyy", *const def_axes3d = "xxxyxzyyyzzz";
27090  if (!axes) naxes = _depth>1?def_axes3d:def_axes2d;
27091  const unsigned int lmax = std::strlen(naxes);
27092  if (lmax%2)
27093  throw CImgArgumentException(_cimg_instance
27094  "get_hessian(): Invalid specified axes '%s'.",
27095  cimg_instance,
27096  naxes);
27097 
27098  res.assign(lmax/2,_width,_height,_depth,_spectrum);
27099  if (!cimg::strcasecmp(naxes,def_axes3d)) { // 3d
27100 
27101 #ifdef cimg_use_openmp
27102 #pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2)
27103 #endif
27104  cimg_forC(*this,c) {
27105  const unsigned long off = c*_width*_height*_depth;
27106  Tfloat
27107  *ptrd0 = res[0]._data + off, *ptrd1 = res[1]._data + off, *ptrd2 = res[2]._data + off,
27108  *ptrd3 = res[3]._data + off, *ptrd4 = res[4]._data + off, *ptrd5 = res[5]._data + off;
27109  CImg_3x3x3(I,Tfloat);
27110  cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
27111  *(ptrd0++) = Ipcc + Incc - 2*Iccc; // Ixx
27112  *(ptrd1++) = (Ippc + Innc - Ipnc - Inpc)/4; // Ixy
27113  *(ptrd2++) = (Ipcp + Incn - Ipcn - Incp)/4; // Ixz
27114  *(ptrd3++) = Icpc + Icnc - 2*Iccc; // Iyy
27115  *(ptrd4++) = (Icpp + Icnn - Icpn - Icnp)/4; // Iyz
27116  *(ptrd5++) = Iccn + Iccp - 2*Iccc; // Izz
27117  }
27118  }
27119  } else if (!cimg::strcasecmp(naxes,def_axes2d)) { // 2d
27120 #ifdef cimg_use_openmp
27121 #pragma omp parallel for collapse(2) if (_width*_height>=1048576 && _depth*_spectrum>=2)
27122 #endif
27123  cimg_forZC(*this,z,c) {
27124  const unsigned long off = c*_width*_height*_depth + z*_width*_height;
27125  Tfloat *ptrd0 = res[0]._data + off, *ptrd1 = res[1]._data + off, *ptrd2 = res[2]._data + off;
27126  CImg_3x3(I,Tfloat);
27127  cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
27128  *(ptrd0++) = Ipc + Inc - 2*Icc; // Ixx
27129  *(ptrd1++) = (Ipp + Inn - Ipn - Inp)/4; // Ixy
27130  *(ptrd2++) = Icp + Icn - 2*Icc; // Iyy
27131  }
27132  }
27133  } else for (unsigned int l = 0; l<lmax; ) { // Version with custom axes.
27134  const unsigned int l2 = l/2;
27135  char axis1 = naxes[l++], axis2 = naxes[l++];
27136  if (axis1>axis2) cimg::swap(axis1,axis2);
27137  bool valid_axis = false;
27138  if (axis1=='x' && axis2=='x') { // Ixx
27139  valid_axis = true;
27140 #ifdef cimg_use_openmp
27141 #pragma omp parallel for collapse(2) if (_width*_height>=1048576 && _depth*_spectrum>=2)
27142 #endif
27143  cimg_forZC(*this,z,c) {
27144  Tfloat *ptrd = res[l2].data(0,0,z,c);
27145  CImg_3x3(I,Tfloat);
27146  cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Ipc + Inc - 2*Icc;
27147  }
27148  }
27149  else if (axis1=='x' && axis2=='y') { // Ixy
27150  valid_axis = true;
27151 #ifdef cimg_use_openmp
27152 #pragma omp parallel for collapse(2) if (_width*_height>=1048576 && _depth*_spectrum>=2)
27153 #endif
27154  cimg_forZC(*this,z,c) {
27155  Tfloat *ptrd = res[l2].data(0,0,z,c);
27156  CImg_3x3(I,Tfloat);
27157  cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Ipp + Inn - Ipn - Inp)/4;
27158  }
27159  }
27160  else if (axis1=='x' && axis2=='z') { // Ixz
27161  valid_axis = true;
27162 #ifdef cimg_use_openmp
27163 #pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2)
27164 #endif
27165  cimg_forC(*this,c) {
27166  Tfloat *ptrd = res[l2].data(0,0,0,c);
27167  CImg_3x3x3(I,Tfloat);
27168  cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Ipcp + Incn - Ipcn - Incp)/4;
27169  }
27170  }
27171  else if (axis1=='y' && axis2=='y') { // Iyy
27172  valid_axis = true;
27173 #ifdef cimg_use_openmp
27174 #pragma omp parallel for collapse(2) if (_width*_height>=1048576 && _depth*_spectrum>=2)
27175 #endif
27176  cimg_forZC(*this,z,c) {
27177  Tfloat *ptrd = res[l2].data(0,0,z,c);
27178  CImg_3x3(I,Tfloat);
27179  cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Icp + Icn - 2*Icc;
27180  }
27181  }
27182  else if (axis1=='y' && axis2=='z') { // Iyz
27183  valid_axis = true;
27184 #ifdef cimg_use_openmp
27185 #pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2)
27186 #endif
27187  cimg_forC(*this,c) {
27188  Tfloat *ptrd = res[l2].data(0,0,0,c);
27189  CImg_3x3x3(I,Tfloat);
27190  cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Icpp + Icnn - Icpn - Icnp)/4;
27191  }
27192  }
27193  else if (axis1=='z' && axis2=='z') { // Izz
27194  valid_axis = true;
27195 #ifdef cimg_use_openmp
27196 #pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2)
27197 #endif
27198  cimg_forC(*this,c) {
27199  Tfloat *ptrd = res[l2].data(0,0,0,c);
27200  CImg_3x3x3(I,Tfloat);
27201  cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Iccn + Iccp - 2*Iccc;
27202  }
27203  }
27204  else if (!valid_axis)
27205  throw CImgArgumentException(_cimg_instance
27206  "get_hessian(): Invalid specified axes '%s'.",
27207  cimg_instance,
27208  naxes);
27209  }
27210  return res;
27211  }
27212 
27215  return get_laplacian().move_to(*this);
27216  }
27217 
27220  if (is_empty()) return CImg<Tfloat>();
27221  CImg<Tfloat> res(_width,_height,_depth,_spectrum);
27222  if (_depth>1) { // 3d
27223 #ifdef cimg_use_openmp
27224 #pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2)
27225 #endif
27226  cimg_forC(*this,c) {
27227  Tfloat *ptrd = res.data(0,0,0,c);
27228  CImg_3x3x3(I,Tfloat);
27229  cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Incc + Ipcc + Icnc + Icpc + Iccn + Iccp - 6*Iccc;
27230  }
27231  } else if (_height>1) { // 2d
27232 #ifdef cimg_use_openmp
27233 #pragma omp parallel for if (_width*_height>=1048576 && _depth*_spectrum>=2)
27234 #endif
27235  cimg_forC(*this,c) {
27236  Tfloat *ptrd = res.data(0,0,0,c);
27237  CImg_3x3(I,Tfloat);
27238  cimg_for3x3(*this,x,y,0,c,I,Tfloat) *(ptrd++) = Inc + Ipc + Icn + Icp - 4*Icc;
27239  }
27240  } else { // 1d
27241 #ifdef cimg_use_openmp
27242 #pragma omp parallel for if (_width>=1048576 && _height*_depth*_spectrum>=2)
27243 #endif
27244  cimg_forC(*this,c) {
27245  Tfloat *ptrd = res.data(0,0,0,c);
27246  CImg_3x3(I,Tfloat);
27247  cimg_for3x3(*this,x,y,0,c,I,Tfloat) *(ptrd++) = Inc + Ipc - 2*Icc;
27248  }
27249  }
27250  return res;
27251  }
27252 
27254 
27257  CImg<T>& structure_tensors(const unsigned int scheme=2) {
27258  return get_structure_tensors(scheme).move_to(*this);
27259  }
27260 
27262  CImg<Tfloat> get_structure_tensors(const unsigned int scheme=2) const {
27263  if (is_empty()) return *this;
27264  CImg<Tfloat> res;
27265  if (_depth>1) { // 3d
27266  res.assign(_width,_height,_depth,6,0);
27267  switch (scheme) {
27268  case 0 : { // classical central finite differences
27269 #ifdef cimg_use_openmp
27270 #pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2)
27271 #endif
27272  cimg_forC(*this,c) {
27273  Tfloat
27274  *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
27275  *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
27276  CImg_3x3x3(I,Tfloat);
27277  cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
27278  const Tfloat
27279  ix = (Incc - Ipcc)/2,
27280  iy = (Icnc - Icpc)/2,
27281  iz = (Iccn - Iccp)/2;
27282  *(ptrd0++)+=ix*ix;
27283  *(ptrd1++)+=ix*iy;
27284  *(ptrd2++)+=ix*iz;
27285  *(ptrd3++)+=iy*iy;
27286  *(ptrd4++)+=iy*iz;
27287  *(ptrd5++)+=iz*iz;
27288  }
27289  }
27290  } break;
27291  case 1 : { // Forward/backward finite differences (version 1).
27292 #ifdef cimg_use_openmp
27293 #pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2)
27294 #endif
27295  cimg_forC(*this,c) {
27296  Tfloat
27297  *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
27298  *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
27299  CImg_3x3x3(I,Tfloat);
27300  cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
27301  const Tfloat
27302  ixf = Incc - Iccc, ixb = Iccc - Ipcc,
27303  iyf = Icnc - Iccc, iyb = Iccc - Icpc,
27304  izf = Iccn - Iccc, izb = Iccc - Iccp;
27305  *(ptrd0++)+=(ixf*ixf + 2*ixf*ixb + ixb*ixb)/4;
27306  *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
27307  *(ptrd2++)+=(ixf*izf + ixf*izb + ixb*izf + ixb*izb)/4;
27308  *(ptrd3++)+=(iyf*iyf + 2*iyf*iyb + iyb*iyb)/4;
27309  *(ptrd4++)+=(iyf*izf + iyf*izb + iyb*izf + iyb*izb)/4;
27310  *(ptrd5++)+=(izf*izf + 2*izf*izb + izb*izb)/4;
27311  }
27312  }
27313  } break;
27314  default : { // Forward/backward finite differences (version 2).
27315 #ifdef cimg_use_openmp
27316 #pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2)
27317 #endif
27318  cimg_forC(*this,c) {
27319  Tfloat
27320  *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
27321  *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
27322  CImg_3x3x3(I,Tfloat);
27323  cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
27324  const Tfloat
27325  ixf = Incc - Iccc, ixb = Iccc - Ipcc,
27326  iyf = Icnc - Iccc, iyb = Iccc - Icpc,
27327  izf = Iccn - Iccc, izb = Iccc - Iccp;
27328  *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2;
27329  *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
27330  *(ptrd2++)+=(ixf*izf + ixf*izb + ixb*izf + ixb*izb)/4;
27331  *(ptrd3++)+=(iyf*iyf + iyb*iyb)/2;
27332  *(ptrd4++)+=(iyf*izf + iyf*izb + iyb*izf + iyb*izb)/4;
27333  *(ptrd5++)+=(izf*izf + izb*izb)/2;
27334  }
27335  }
27336  } break;
27337  }
27338  } else { // 2d
27339  res.assign(_width,_height,_depth,3,0);
27340  switch (scheme) {
27341  case 0 : { // classical central finite differences
27342 #ifdef cimg_use_openmp
27343 #pragma omp parallel for if (_width*_height>=1048576 && _depth*_spectrum>=2)
27344 #endif
27345  cimg_forC(*this,c) {
27346  Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
27347  CImg_3x3(I,Tfloat);
27348  cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
27349  const Tfloat
27350  ix = (Inc - Ipc)/2,
27351  iy = (Icn - Icp)/2;
27352  *(ptrd0++)+=ix*ix;
27353  *(ptrd1++)+=ix*iy;
27354  *(ptrd2++)+=iy*iy;
27355  }
27356  }
27357  } break;
27358  case 1 : { // Forward/backward finite differences (version 1).
27359 #ifdef cimg_use_openmp
27360 #pragma omp parallel for if (_width*_height>=1048576 && _depth*_spectrum>=2)
27361 #endif
27362  cimg_forC(*this,c) {
27363  Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
27364  CImg_3x3(I,Tfloat);
27365  cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
27366  const Tfloat
27367  ixf = Inc - Icc, ixb = Icc - Ipc,
27368  iyf = Icn - Icc, iyb = Icc - Icp;
27369  *(ptrd0++)+=(ixf*ixf + 2*ixf*ixb + ixb*ixb)/4;
27370  *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
27371  *(ptrd2++)+=(iyf*iyf + 2*iyf*iyb + iyb*iyb)/4;
27372  }
27373  }
27374  } break;
27375  default : { // Forward/backward finite differences (version 2).
27376 #ifdef cimg_use_openmp
27377 #pragma omp parallel for if (_width*_height>=1048576 && _depth*_spectrum>=2)
27378 #endif
27379  cimg_forC(*this,c) {
27380  Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
27381  CImg_3x3(I,Tfloat);
27382  cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
27383  const Tfloat
27384  ixf = Inc - Icc, ixb = Icc - Ipc,
27385  iyf = Icn - Icc, iyb = Icc - Icp;
27386  *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2;
27387  *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
27388  *(ptrd2++)+=(iyf*iyf + iyb*iyb)/2;
27389  }
27390  }
27391  } break;
27392  }
27393  }
27394  return res;
27395  }
27396 
27398 
27405  CImg<T>& diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f,
27406  const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) {
27407  CImg<Tfloat> res;
27408  const float
27409  nsharpness = cimg::max(sharpness,1e-5f),
27410  power1 = (is_sqrt?0.5f:1)*nsharpness,
27411  power2 = power1/(1e-7f+1-anisotropy);
27412  blur(alpha).normalize(0,(T)255);
27413 
27414  if (_depth>1) { // 3d
27415  get_structure_tensors().move_to(res).blur(sigma);
27416 #ifdef cimg_use_openmp
27417 #pragma omp parallel for collapse(2) if(_width>=256 && _height*_depth>=256)
27418 #endif
27419  cimg_forYZ(*this,y,z) {
27420  Tfloat
27421  *ptrd0 = res.data(0,y,z,0), *ptrd1 = res.data(0,y,z,1), *ptrd2 = res.data(0,y,z,2),
27422  *ptrd3 = res.data(0,y,z,3), *ptrd4 = res.data(0,y,z,4), *ptrd5 = res.data(0,y,z,5);
27423  CImg<floatT> val(3), vec(3,3);
27424  cimg_forX(*this,x) {
27425  res.get_tensor_at(x,y,z).symmetric_eigen(val,vec);
27426  const float
27427  _l1 = val[2], _l2 = val[1], _l3 = val[0],
27428  l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0, l3 = _l3>0?_l3:0,
27429  ux = vec(0,0), uy = vec(0,1), uz = vec(0,2),
27430  vx = vec(1,0), vy = vec(1,1), vz = vec(1,2),
27431  wx = vec(2,0), wy = vec(2,1), wz = vec(2,2),
27432  n1 = (float)std::pow(1+l1+l2+l3,-power1),
27433  n2 = (float)std::pow(1+l1+l2+l3,-power2);
27434  *(ptrd0++) = n1*(ux*ux + vx*vx) + n2*wx*wx;
27435  *(ptrd1++) = n1*(ux*uy + vx*vy) + n2*wx*wy;
27436  *(ptrd2++) = n1*(ux*uz + vx*vz) + n2*wx*wz;
27437  *(ptrd3++) = n1*(uy*uy + vy*vy) + n2*wy*wy;
27438  *(ptrd4++) = n1*(uy*uz + vy*vz) + n2*wy*wz;
27439  *(ptrd5++) = n1*(uz*uz + vz*vz) + n2*wz*wz;
27440  }
27441  }
27442  } else { // for 2d images
27443  get_structure_tensors().move_to(res).blur(sigma);
27444 #ifdef cimg_use_openmp
27445 #pragma omp parallel for if(_width>=256 && _height>=256)
27446 #endif
27447  cimg_forY(*this,y) {
27448  Tfloat *ptrd0 = res.data(0,y,0,0), *ptrd1 = res.data(0,y,0,1), *ptrd2 = res.data(0,y,0,2);
27449  CImg<floatT> val(2), vec(2,2);
27450  cimg_forX(*this,x) {
27451  res.get_tensor_at(x,y).symmetric_eigen(val,vec);
27452  const float
27453  _l1 = val[1], _l2 = val[0],
27454  l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0,
27455  ux = vec(1,0), uy = vec(1,1),
27456  vx = vec(0,0), vy = vec(0,1),
27457  n1 = (float)std::pow(1+l1+l2,-power1),
27458  n2 = (float)std::pow(1+l1+l2,-power2);
27459  *(ptrd0++) = n1*ux*ux + n2*vx*vx;
27460  *(ptrd1++) = n1*ux*uy + n2*vx*vy;
27461  *(ptrd2++) = n1*uy*uy + n2*vy*vy;
27462  }
27463  }
27464  }
27465  return res.move_to(*this);
27466  }
27467 
27469  CImg<Tfloat> get_diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f,
27470  const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) const {
27471  return CImg<Tfloat>(*this,false).diffusion_tensors(sharpness,anisotropy,alpha,sigma,is_sqrt);
27472  }
27473 
27475 
27483  CImg<T>& displacement(const CImg<T>& source, const float smoothness=0.1f, const float precision=5.0f,
27484  const unsigned int nb_scales=0, const unsigned int iteration_max=10000,
27485  const bool is_backward=false) {
27486  return get_displacement(source,smoothness,precision,nb_scales,iteration_max,is_backward).move_to(*this);
27487  }
27488 
27491  const float smoothness=0.1f, const float precision=5.0f,
27492  const unsigned int nb_scales=0, const unsigned int iteration_max=10000,
27493  const bool is_backward=false) const {
27494  if (is_empty() || !source) return +*this;
27495  if (!is_sameXYZC(source))
27496  throw CImgArgumentException(_cimg_instance
27497  "displacement(): Instance and source image (%u,%u,%u,%u,%p) have "
27498  "different dimensions.",
27499  cimg_instance,
27500  source._width,source._height,source._depth,source._spectrum,source._data);
27501  if (precision<0)
27502  throw CImgArgumentException(_cimg_instance
27503  "displacement(): Invalid specified precision %g "
27504  "(should be >=0)",
27505  cimg_instance,
27506  precision);
27507  const unsigned int _nb_scales = nb_scales>0?nb_scales:
27508  (unsigned int)(2*std::log((double)(cimg::max(_width,_height))));
27509  const float _precision = (float)std::pow(10.0,-(double)precision);
27510  float sm, sM = source.max_min(sm), tm, tM = max_min(tm);
27511  const float sdelta = sm==sM?1:(sM - sm), tdelta = tm==tM?1:(tM - tm);
27512  const bool is_3d = source._depth>1;
27513  CImg<floatT> U;
27514 
27515  for (int scale = _nb_scales-1; scale>=0; --scale) {
27516  const float factor = (float)std::pow(1.5,(double)scale);
27517  const unsigned int
27518  _sw = (unsigned int)(_width/factor), sw = _sw?_sw:1,
27519  _sh = (unsigned int)(_height/factor), sh = _sh?_sh:1,
27520  _sd = (unsigned int)(_depth/factor), sd = _sd?_sd:1;
27521  if (sw<5 && sh<5 && (!is_3d || sd<5)) continue; // skip too small scales.
27522  const CImg<Tfloat>
27523  I1 = (source.get_resize(sw,sh,sd,-100,2)-=sm)/=sdelta,
27524  I2 = (get_resize(I1,2)-=tm)/=tdelta;
27525  if (U) (U*=1.5f).resize(I2._width,I2._height,I2._depth,-100,3);
27526  else U.assign(I2._width,I2._height,I2._depth,is_3d?3:2,0);
27527  float dt = 2, energy = cimg::type<float>::max();
27528  const CImgList<Tfloat> dI = is_backward?I1.get_gradient():I2.get_gradient();
27529 
27530  for (unsigned int iteration = 0; iteration<iteration_max; ++iteration) {
27531  float _energy = 0;
27532  if (is_3d) { // 3d version.
27533  if (smoothness>=0) cimg_for3XYZ(U,x,y,z) { // Isotropic regularization.
27534  const float
27535  X = is_backward?x - U(x,y,z,0):x + U(x,y,z,0),
27536  Y = is_backward?y - U(x,y,z,1):y + U(x,y,z,1),
27537  Z = is_backward?z - U(x,y,z,2):z + U(x,y,z,2);
27538  float delta_I = 0, _energy_regul = 0;
27539  if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1.linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c));
27540  else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,z,c) - I2.linear_atXYZ(X,Y,Z,c));
27541  cimg_forC(U,c) {
27542  const float
27543  Ux = 0.5f*(U(_n1x,y,z,c) - U(_p1x,y,z,c)),
27544  Uy = 0.5f*(U(x,_n1y,z,c) - U(x,_p1y,z,c)),
27545  Uz = 0.5f*(U(x,y,_n1z,c) - U(x,y,_p1z,c)),
27546  Uxx = U(_n1x,y,z,c) + U(_p1x,y,z,c),
27547  Uyy = U(x,_n1y,z,c) + U(x,_p1y,z,c),
27548  Uzz = U(x,y,_n1z,c) + U(x,y,_p1z,c);
27549  U(x,y,z,c) = (float)(U(x,y,z,c) + dt*(delta_I*dI[c].linear_atXYZ(X,Y,Z) +
27550  smoothness* ( Uxx + Uyy + Uzz)))/(1+6*smoothness*dt);
27551  _energy_regul+=Ux*Ux + Uy*Uy + Uz*Uz;
27552  }
27553  _energy+=delta_I*delta_I + smoothness*_energy_regul;
27554  } else {
27555  const float nsmoothness = -smoothness;
27556  cimg_for3XYZ(U,x,y,z) { // Anisotropic regularization.
27557  const float
27558  X = is_backward?x - U(x,y,z,0):x + U(x,y,z,0),
27559  Y = is_backward?y - U(x,y,z,1):y + U(x,y,z,1),
27560  Z = is_backward?z - U(x,y,z,2):z + U(x,y,z,2);
27561  float delta_I = 0, _energy_regul = 0;
27562  if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1.linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c));
27563  else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,z,c) - I2.linear_atXYZ(X,Y,Z,c));
27564  cimg_forC(U,c) {
27565  const float
27566  Ux = 0.5f*(U(_n1x,y,z,c) - U(_p1x,y,z,c)),
27567  Uy = 0.5f*(U(x,_n1y,z,c) - U(x,_p1y,z,c)),
27568  Uz = 0.5f*(U(x,y,_n1z,c) - U(x,y,_p1z,c)),
27569  N2 = Ux*Ux + Uy*Uy + Uz*Uz,
27570  N = std::sqrt(N2),
27571  N3 = 1e-5f + N2*N,
27572  coef_a = (1 - Ux*Ux/N2)/N,
27573  coef_b = -2*Ux*Uy/N3,
27574  coef_c = -2*Ux*Uz/N3,
27575  coef_d = (1 - Uy*Uy/N2)/N,
27576  coef_e = -2*Uy*Uz/N3,
27577  coef_f = (1 - Uz*Uz/N2)/N,
27578  Uxx = U(_n1x,y,z,c) + U(_p1x,y,z,c),
27579  Uyy = U(x,_n1y,z,c) + U(x,_p1y,z,c),
27580  Uzz = U(x,y,_n1z,c) + U(x,y,_p1z,c),
27581  Uxy = 0.25f*(U(_n1x,_n1y,z,c) + U(_p1x,_p1y,z,c) - U(_n1x,_p1y,z,c) - U(_n1x,_p1y,z,c)),
27582  Uxz = 0.25f*(U(_n1x,y,_n1z,c) + U(_p1x,y,_p1z,c) - U(_n1x,y,_p1z,c) - U(_n1x,y,_p1z,c)),
27583  Uyz = 0.25f*(U(x,_n1y,_n1z,c) + U(x,_p1y,_p1z,c) - U(x,_n1y,_p1z,c) - U(x,_n1y,_p1z,c));
27584  U(x,y,z,c) = (float)(U(x,y,z,c) + dt*(delta_I*dI[c].linear_atXYZ(X,Y,Z) +
27585  nsmoothness* ( coef_a*Uxx + coef_b*Uxy +
27586  coef_c*Uxz + coef_d*Uyy +
27587  coef_e*Uyz + coef_f*Uzz ))
27588  )/(1+2*(coef_a+coef_d+coef_f)*nsmoothness*dt);
27589  _energy_regul+=N;
27590  }
27591  _energy+=delta_I*delta_I + nsmoothness*_energy_regul;
27592  }
27593  }
27594  } else { // 2d version.
27595  if (smoothness>=0) cimg_for3XY(U,x,y) { // Isotropic regularization.
27596  const float
27597  X = is_backward?x - U(x,y,0):x + U(x,y,0),
27598  Y = is_backward?y - U(x,y,1):y + U(x,y,1);
27599  float delta_I = 0, _energy_regul = 0;
27600  if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1.linear_atXY(X,Y,c) - I2(x,y,c));
27601  else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,c) - I2.linear_atXY(X,Y,c));
27602  cimg_forC(U,c) {
27603  const float
27604  Ux = 0.5f*(U(_n1x,y,c) - U(_p1x,y,c)),
27605  Uy = 0.5f*(U(x,_n1y,c) - U(x,_p1y,c)),
27606  Uxx = U(_n1x,y,c) + U(_p1x,y,c),
27607  Uyy = U(x,_n1y,c) + U(x,_p1y,c);
27608  U(x,y,c) = (float)(U(x,y,c) + dt*(delta_I*dI[c].linear_atXY(X,Y) +
27609  smoothness*( Uxx + Uyy )))/(1+4*smoothness*dt);
27610  _energy_regul+=Ux*Ux + Uy*Uy;
27611  }
27612  _energy+=delta_I*delta_I + smoothness*_energy_regul;
27613  } else {
27614  const float nsmoothness = -smoothness;
27615  cimg_for3XY(U,x,y) { // Anisotropic regularization.
27616  const float
27617  X = is_backward?x - U(x,y,0):x + U(x,y,0),
27618  Y = is_backward?y - U(x,y,1):y + U(x,y,1);
27619  float delta_I = 0, _energy_regul = 0;
27620  if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1.linear_atXY(X,Y,c) - I2(x,y,c));
27621  else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,c) - I2.linear_atXY(X,Y,c));
27622  cimg_forC(U,c) {
27623  const float
27624  Ux = 0.5f*(U(_n1x,y,c) - U(_p1x,y,c)),
27625  Uy = 0.5f*(U(x,_n1y,c) - U(x,_p1y,c)),
27626  N2 = Ux*Ux + Uy*Uy,
27627  N = std::sqrt(N2),
27628  N3 = 1e-5f + N2*N,
27629  coef_a = Uy*Uy/N3,
27630  coef_b = -2*Ux*Uy/N3,
27631  coef_c = Ux*Ux/N3,
27632  Uxx = U(_n1x,y,c) + U(_p1x,y,c),
27633  Uyy = U(x,_n1y,c) + U(x,_p1y,c),
27634  Uxy = 0.25f*(U(_n1x,_n1y,c) + U(_p1x,_p1y,c) - U(_n1x,_p1y,c) - U(_n1x,_p1y,c));
27635  U(x,y,c) = (float)(U(x,y,c) + dt*(delta_I*dI[c].linear_atXY(X,Y) +
27636  nsmoothness*( coef_a*Uxx + coef_b*Uxy + coef_c*Uyy )))/
27637  (1+2*(coef_a+coef_c)*nsmoothness*dt);
27638  _energy_regul+=N;
27639  }
27640  _energy+=delta_I*delta_I + nsmoothness*_energy_regul;
27641  }
27642  }
27643  }
27644  const float d_energy = (_energy - energy)/(sw*sh*sd);
27645  if (d_energy<=0 && -d_energy<_precision) break;
27646  if (d_energy>0) dt*=0.5f;
27647  energy = _energy;
27648  }
27649  }
27650  return U;
27651  }
27652 
27654 
27665  CImg<T>& distance(const T value, const unsigned int metric=2) {
27666  if (is_empty()) return *this;
27667  bool is_value = false;
27668  cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,0:(T)999999999;
27669  if (!is_value) return fill(cimg::type<T>::max());
27670  switch (metric) {
27671  case 0 : return _distance_core(_distance_sep_cdt,_distance_dist_cdt); // Chebyshev.
27672  case 1 : return _distance_core(_distance_sep_mdt,_distance_dist_mdt); // Manhattan.
27673  case 3 : return _distance_core(_distance_sep_edt,_distance_dist_edt); // Squared Euclidean.
27674  default : return _distance_core(_distance_sep_edt,_distance_dist_edt).sqrt(); // Euclidean.
27675  }
27676  return *this;
27677  }
27678 
27680  CImg<Tfloat> get_distance(const T value, const unsigned int metric=2) const {
27681  return CImg<Tfloat>(*this,false).distance((Tfloat)value,metric);
27682  }
27683 
27684  static long _distance_sep_edt(const long i, const long u, const long *const g) {
27685  return (u*u-i*i+g[u]-g[i])/(2*(u-i));
27686  }
27687 
27688  static long _distance_dist_edt(const long x, const long i, const long *const g) {
27689  return (x-i)*(x-i) + g[i];
27690  }
27691 
27692  static long _distance_sep_mdt(const long i, const long u, const long *const g) {
27693  return (u-i<=g[u]-g[i]?999999999:(g[u]-g[i]+u+i)/2);
27694  }
27695 
27696  static long _distance_dist_mdt(const long x, const long i, const long *const g) {
27697  return (x<i?i-x:x-i) + g[i];
27698  }
27699 
27700  static long _distance_sep_cdt(const long i, const long u, const long *const g) {
27701  const long h = (i+u)/2;
27702  if (g[i]<=g[u]) { return h<i+g[u]?i+g[u]:h; }
27703  return h<u-g[i]?h:u-g[i];
27704  }
27705 
27706  static long _distance_dist_cdt(const long x, const long i, const long *const g) {
27707  const long d = x<i?i-x:x-i;
27708  return d<g[i]?g[i]:d;
27709  }
27710 
27711  static void _distance_scan(const unsigned int len,
27712  const long *const g,
27713  long (*const sep)(const long, const long, const long *const),
27714  long (*const f)(const long, const long, const long *const),
27715  long *const s,
27716  long *const t,
27717  long *const dt) {
27718  long q = s[0] = t[0] = 0;
27719  for (int u = 1; u<(int)len; ++u) { // Forward scan.
27720  while ((q>=0) && f(t[q],s[q],g)>f(t[q],u,g)) { --q; }
27721  if (q<0) { q = 0; s[0] = u; }
27722  else { const long w = 1 + sep(s[q], u, g); if (w<(long)len) { ++q; s[q] = u; t[q] = w; }}
27723  }
27724  for (int u = (int)len-1; u>=0; --u) { dt[u] = f(u,s[q],g); if (u==t[q]) --q; } // Backward scan.
27725  }
27726 
27727  CImg<T>& _distance_core(long (*const sep)(const long, const long, const long *const),
27728  long (*const f)(const long, const long, const long *const)) {
27729  const unsigned long wh = (unsigned long)_width*_height;
27730 #ifdef cimg_use_openmp
27731 #pragma omp parallel for if (_spectrum>=2)
27732 #endif
27733  cimg_forC(*this,c) {
27734  CImg<longT> g(_width), dt(_width), s(_width), t(_width);
27735  CImg<T> img = get_shared_channel(c);
27736 #ifdef cimg_use_openmp
27737 #pragma omp parallel for collapse(2) if (_width>=512 && _height*_depth>=16) firstprivate(g,dt,s,t)
27738 #endif
27739  cimg_forYZ(*this,y,z) { // Over X-direction.
27740  cimg_forX(*this,x) g[x] = (long)img(x,y,z,0,wh);
27741  _distance_scan(_width,g,sep,f,s,t,dt);
27742  cimg_forX(*this,x) img(x,y,z,0,wh) = (T)dt[x];
27743  }
27744  if (_height>1) {
27745  g.assign(_height); dt.assign(_height); s.assign(_height); t.assign(_height);
27746 #ifdef cimg_use_openmp
27747 #pragma omp parallel for collapse(2) if (_height>=512 && _width*_depth>=16) firstprivate(g,dt,s,t)
27748 #endif
27749  cimg_forXZ(*this,x,z) { // Over Y-direction.
27750  cimg_forY(*this,y) g[y] = (long)img(x,y,z,0,wh);
27751  _distance_scan(_height,g,sep,f,s,t,dt);
27752  cimg_forY(*this,y) img(x,y,z,0,wh) = (T)dt[y];
27753  }
27754  }
27755  if (_depth>1) {
27756  g.assign(_depth); dt.assign(_depth); s.assign(_depth); t.assign(_depth);
27757 #ifdef cimg_use_openmp
27758 #pragma omp parallel for collapse(2) if (_depth>=512 && _width*_height>=16) firstprivate(g,dt,s,t)
27759 #endif
27760  cimg_forXY(*this,x,y) { // Over Z-direction.
27761  cimg_forZ(*this,z) g[z] = (long)img(x,y,z,0,wh);
27762  _distance_scan(_depth,g,sep,f,s,t,dt);
27763  cimg_forZ(*this,z) img(x,y,z,0,wh) = (T)dt[z];
27764  }
27765  }
27766  }
27767  return *this;
27768  }
27769 
27771 
27776  template<typename t>
27777  CImg<T>& distance(const T value, const CImg<t>& metric_mask) {
27778  if (is_empty()) return *this;
27779  bool is_value = false;
27780  cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,0:(T)999999999;
27781  if (!is_value) return fill(cimg::type<T>::max());
27782  const unsigned long wh = (unsigned long)_width*_height;
27783 #ifdef cimg_use_openmp
27784 #pragma omp parallel for if (_spectrum>=2)
27785 #endif
27786  cimg_forC(*this,c) {
27787  CImg<T> img = get_shared_channel(c);
27788 #ifdef cimg_use_openmp
27789 #pragma omp parallel for collapse(3) if (_width*_height*_depth>=1024)
27790 #endif
27791  cimg_forXYZ(metric_mask,dx,dy,dz) {
27792  const t weight = metric_mask(dx,dy,dz);
27793  if (weight) {
27794  for (int z = dz, nz = 0; z<depth(); ++z,++nz) { // Forward scan.
27795  for (int y = dy , ny = 0; y<height(); ++y,++ny) {
27796  for (int x = dx, nx = 0; x<width(); ++x,++nx) {
27797  const T dd = img(nx,ny,nz,0,wh) + weight;
27798  if (dd<img(x,y,z,0,wh)) img(x,y,z,0,wh) = dd;
27799  }
27800  }
27801  }
27802  for (int z = depth() - 1 - dz, nz = depth() - 1; z>=0; --z,--nz) { // Backward scan.
27803  for (int y = height() - 1 - dy, ny = height() - 1; y>=0; --y,--ny) {
27804  for (int x = width() - 1 - dx, nx = width() - 1; x>=0; --x,--nx) {
27805  const T dd = img(nx,ny,nz,0,wh) + weight;
27806  if (dd<img(x,y,z,0,wh)) img(x,y,z,0,wh) = dd;
27807  }
27808  }
27809  }
27810  }
27811  }
27812  }
27813  return *this;
27814  }
27815 
27817  template<typename t>
27818  CImg<Tfloat> get_distance(const T value, const CImg<t>& metric_mask) const {
27819  return CImg<Tfloat>(*this,false).distance(value,metric_mask);
27820  }
27821 
27823 
27828  template<typename t, typename to>
27829  CImg<T>& distance_dijkstra(const T value, const CImg<t>& metric, const bool is_high_connectivity,
27830  CImg<to>& return_path) {
27831  return get_distance_dijkstra(value,metric,is_high_connectivity,return_path).move_to(*this);
27832  }
27833 
27835  template<typename t, typename to>
27837  get_distance_dijkstra(const T value, const CImg<t>& metric, const bool is_high_connectivity,
27838  CImg<to>& return_path) const {
27839  if (is_empty()) return return_path.assign();
27840  if (!is_sameXYZ(metric))
27841  throw CImgArgumentException(_cimg_instance
27842  "distance_dijkstra(): image instance and metric map (%u,%u,%u,%u) "
27843  "have incompatible dimensions.",
27844  cimg_instance,
27845  metric._width,metric._height,metric._depth,metric._spectrum);
27846  typedef typename cimg::superset<t,long>::type td; // Type used for computing cumulative distances.
27847  CImg<td> result(_width,_height,_depth,_spectrum), Q;
27848  CImg<boolT> is_queued(_width,_height,_depth,1);
27849  if (return_path) return_path.assign(_width,_height,_depth,_spectrum);
27850 
27851  cimg_forC(*this,c) {
27852  const CImg<T> img = get_shared_channel(c);
27853  const CImg<t> met = metric.get_shared_channel(c%metric._spectrum);
27854  CImg<td> res = result.get_shared_channel(c);
27855  CImg<to> path = return_path?return_path.get_shared_channel(c):CImg<to>();
27856  unsigned int sizeQ = 0;
27857 
27858  // Detect initial seeds.
27859  is_queued.fill(0);
27860  cimg_forXYZ(img,x,y,z) if (img(x,y,z)==value) {
27861  Q._priority_queue_insert(is_queued,sizeQ,0,x,y,z);
27862  res(x,y,z) = 0;
27863  if (path) path(x,y,z) = (to)0;
27864  }
27865 
27866  // Start distance propagation.
27867  while (sizeQ) {
27868 
27869  // Get and remove point with minimal potential from the queue.
27870  const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
27871  const td P = (td)-Q(0,0);
27872  Q._priority_queue_remove(sizeQ);
27873 
27874  // Update neighbors.
27875  td npot = 0;
27876  if (x-1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x-1,y,z)+P),x-1,y,z)) {
27877  res(x-1,y,z) = npot; if (path) path(x-1,y,z) = (to)2;
27878  }
27879  if (x+1<width() && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x+1,y,z)+P),x+1,y,z)) {
27880  res(x+1,y,z) = npot; if (path) path(x+1,y,z) = (to)1;
27881  }
27882  if (y-1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y-1,z)+P),x,y-1,z)) {
27883  res(x,y-1,z) = npot; if (path) path(x,y-1,z) = (to)8;
27884  }
27885  if (y+1<height() && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y+1,z)+P),x,y+1,z)) {
27886  res(x,y+1,z) = npot; if (path) path(x,y+1,z) = (to)4;
27887  }
27888  if (z-1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y,z-1)+P),x,y,z-1)) {
27889  res(x,y,z-1) = npot; if (path) path(x,y,z-1) = (to)32;
27890  }
27891  if (z+1<depth() && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y,z+1)+P),x,y,z+1)) {
27892  res(x,y,z+1) = npot; if (path) path(x,y,z+1) = (to)16;
27893  }
27894 
27895  if (is_high_connectivity) {
27896  const float sqrt2 = std::sqrt(2.0f), sqrt3 = std::sqrt(3.0f);
27897 
27898  // Diagonal neighbors on slice z.
27899  if (x-1>=0 && y-1>=0 &&
27900  Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x-1,y-1,z)+P)),x-1,y-1,z)) {
27901  res(x-1,y-1,z) = npot; if (path) path(x-1,y-1,z) = (to)10;
27902  }
27903  if (x+1<width() && y-1>=0 &&
27904  Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x+1,y-1,z)+P)),x+1,y-1,z)) {
27905  res(x+1,y-1,z) = npot; if (path) path(x+1,y-1,z) = (to)9;
27906  }
27907  if (x-1>=0 && y+1<height() &&
27908  Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x-1,y+1,z)+P)),x-1,y+1,z)) {
27909  res(x-1,y+1,z) = npot; if (path) path(x-1,y+1,z) = (to)6;
27910  }
27911  if (x+1<width() && y+1<height() &&
27912  Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x+1,y+1,z)+P)),x+1,y+1,z)) {
27913  res(x+1,y+1,z) = npot; if (path) path(x+1,y+1,z) = (to)5;
27914  }
27915 
27916  if (z-1>=0) { // Diagonal neighbors on slice z-1.
27917  if (x-1>=0 &&
27918  Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x-1,y,z-1)+P)),x-1,y,z-1)) {
27919  res(x-1,y,z-1) = npot; if (path) path(x-1,y,z-1) = (to)34;
27920  }
27921  if (x+1<width() &&
27922  Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x+1,y,z-1)+P)),x+1,y,z-1)) {
27923  res(x+1,y,z-1) = npot; if (path) path(x+1,y,z-1) = (to)33;
27924  }
27925  if (y-1>=0 &&
27926  Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y-1,z-1)+P)),x,y-1,z-1)) {
27927  res(x,y-1,z-1) = npot; if (path) path(x,y-1,z-1) = (to)40;
27928  }
27929  if (y+1<height() &&
27930  Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y+1,z-1)+P)),x,y+1,z-1)) {
27931  res(x,y+1,z-1) = npot; if (path) path(x,y+1,z-1) = (to)36;
27932  }
27933  if (x-1>=0 && y-1>=0 &&
27934  Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x-1,y-1,z-1)+P)),x-1,y-1,z-1)) {
27935  res(x-1,y-1,z-1) = npot; if (path) path(x-1,y-1,z-1) = (to)42;
27936  }
27937  if (x+1<width() && y-1>=0 &&
27938  Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x+1,y-1,z-1)+P)),x+1,y-1,z-1)) {
27939  res(x+1,y-1,z-1) = npot; if (path) path(x+1,y-1,z-1) = (to)41;
27940  }
27941  if (x-1>=0 && y+1<height() &&
27942  Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x-1,y+1,z-1)+P)),x-1,y+1,z-1)) {
27943  res(x-1,y+1,z-1) = npot; if (path) path(x-1,y+1,z-1) = (to)38;
27944  }
27945  if (x+1<width() && y+1<height() &&
27946  Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x+1,y+1,z-1)+P)),x+1,y+1,z-1)) {
27947  res(x+1,y+1,z-1) = npot; if (path) path(x+1,y+1,z-1) = (to)37;
27948  }
27949  }
27950 
27951  if (z+1<depth()) { // Diagonal neighbors on slice z+1.
27952  if (x-1>=0 &&
27953  Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x-1,y,z+1)+P)),x-1,y,z+1)) {
27954  res(x-1,y,z+1) = npot; if (path) path(x-1,y,z+1) = (to)18;
27955  }
27956  if (x+1<width() &&
27957  Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x+1,y,z+1)+P)),x+1,y,z+1)) {
27958  res(x+1,y,z+1) = npot; if (path) path(x+1,y,z+1) = (to)17;
27959  }
27960  if (y-1>=0 &&
27961  Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y-1,z+1)+P)),x,y-1,z+1)) {
27962  res(x,y-1,z+1) = npot; if (path) path(x,y-1,z+1) = (to)24;
27963  }
27964  if (y+1<height() &&
27965  Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y+1,z+1)+P)),x,y+1,z+1)) {
27966  res(x,y+1,z+1) = npot; if (path) path(x,y+1,z+1) = (to)20;
27967  }
27968  if (x-1>=0 && y-1>=0 &&
27969  Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x-1,y-1,z+1)+P)),x-1,y-1,z+1)) {
27970  res(x-1,y-1,z+1) = npot; if (path) path(x-1,y-1,z+1) = (to)26;
27971  }
27972  if (x+1<width() && y-1>=0 &&
27973  Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x+1,y-1,z+1)+P)),x+1,y-1,z+1)) {
27974  res(x+1,y-1,z+1) = npot; if (path) path(x+1,y-1,z+1) = (to)25;
27975  }
27976  if (x-1>=0 && y+1<height() &&
27977  Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x-1,y+1,z+1)+P)),x-1,y+1,z+1)) {
27978  res(x-1,y+1,z+1) = npot; if (path) path(x-1,y+1,z+1) = (to)22;
27979  }
27980  if (x+1<width() && y+1<height() &&
27981  Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x+1,y+1,z+1)+P)),x+1,y+1,z+1)) {
27982  res(x+1,y+1,z+1) = npot; if (path) path(x+1,y+1,z+1) = (to)21;
27983  }
27984  }
27985  }
27986  }
27987  }
27988  return result;
27989  }
27990 
27992  template<typename t>
27993  CImg<T>& distance_dijkstra(const T value, const CImg<t>& metric,
27994  const bool is_high_connectivity=false) {
27995  return get_distance_dijkstra(value,metric,is_high_connectivity).move_to(*this);
27996  }
27997 
27999  template<typename t>
28000  CImg<Tfloat> get_distance_dijkstra(const T value, const CImg<t>& metric,
28001  const bool is_high_connectivity=false) const {
28002  CImg<T> return_path;
28003  return get_distance_dijkstra(value,metric,is_high_connectivity,return_path);
28004  }
28005 
28007 
28011  template<typename t>
28012  CImg<T>& distance_eikonal(const T value, const CImg<t>& metric) {
28013  return get_distance_eikonal(value,metric).move_to(*this);
28014  }
28015 
28017  template<typename t>
28018  CImg<Tfloat> get_distance_eikonal(const T value, const CImg<t>& metric) const {
28019  if (is_empty()) return *this;
28020  if (!is_sameXYZ(metric))
28021  throw CImgArgumentException(_cimg_instance
28022  "distance_eikonal(): image instance and metric map (%u,%u,%u,%u) have "
28023  "incompatible dimensions.",
28024  cimg_instance,
28025  metric._width,metric._height,metric._depth,metric._spectrum);
28026  CImg<Tfloat> result(_width,_height,_depth,_spectrum,cimg::type<Tfloat>::max()), Q;
28027  CImg<charT> state(_width,_height,_depth); // -1=far away, 0=narrow, 1=frozen.
28028 
28029 #ifdef cimg_use_openmp
28030 #pragma omp parallel for if (_spectrum>=2) firstprivate(Q,state)
28031 #endif
28032  cimg_forC(*this,c) {
28033  const CImg<T> img = get_shared_channel(c);
28034  const CImg<t> met = metric.get_shared_channel(c%metric._spectrum);
28035  CImg<Tfloat> res = result.get_shared_channel(c);
28036  unsigned int sizeQ = 0;
28037  state.fill(-1);
28038 
28039  // Detect initial seeds.
28040  Tfloat *ptr1 = res._data; char *ptr2 = state._data;
28041  cimg_for(img,ptr0,T) { if (*ptr0==value) { *ptr1 = 0; *ptr2 = 1; } ++ptr1; ++ptr2; }
28042 
28043  // Initialize seeds neighbors.
28044  ptr2 = state._data;
28045  cimg_forXYZ(img,x,y,z) if (*(ptr2++)==1) {
28046  if (x-1>=0 && state(x-1,y,z)==-1) {
28047  const Tfloat dist = res(x-1,y,z) = __distance_eikonal(res,met(x-1,y,z),x-1,y,z);
28048  Q._eik_priority_queue_insert(state,sizeQ,-dist,x-1,y,z);
28049  }
28050  if (x+1<width() && state(x+1,y,z)==-1) {
28051  const Tfloat dist = res(x+1,y,z) = __distance_eikonal(res,met(x+1,y,z),x+1,y,z);
28052  Q._eik_priority_queue_insert(state,sizeQ,-dist,x+1,y,z);
28053  }
28054  if (y-1>=0 && state(x,y-1,z)==-1) {
28055  const Tfloat dist = res(x,y-1,z) = __distance_eikonal(res,met(x,y-1,z),x,y-1,z);
28056  Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y-1,z);
28057  }
28058  if (y+1<height() && state(x,y+1,z)==-1) {
28059  const Tfloat dist = res(x,y+1,z) = __distance_eikonal(res,met(x,y+1,z),x,y+1,z);
28060  Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y+1,z);
28061  }
28062  if (z-1>=0 && state(x,y,z-1)==-1) {
28063  const Tfloat dist = res(x,y,z-1) = __distance_eikonal(res,met(x,y,z-1),x,y,z-1);
28064  Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z-1);
28065  }
28066  if (z+1<depth() && state(x,y,z+1)==-1) {
28067  const Tfloat dist = res(x,y,z+1) = __distance_eikonal(res,met(x,y,z+1),x,y,z+1);
28068  Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z+1);
28069  }
28070  }
28071 
28072  // Propagate front.
28073  while (sizeQ) {
28074  int x = -1, y = -1, z = -1;
28075  while (sizeQ && x<0) {
28076  x = (int)Q(0,1); y = (int)Q(0,2); z = (int)Q(0,3);
28077  Q._priority_queue_remove(sizeQ);
28078  if (state(x,y,z)==1) x = -1; else state(x,y,z) = 1;
28079  }
28080  if (x>=0) {
28081  if (x-1>=0 && state(x-1,y,z)!=1) {
28082  const Tfloat dist = __distance_eikonal(res,met(x-1,y,z),x-1,y,z);
28083  if (dist<res(x-1,y,z)) { res(x-1,y,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x-1,y,z); }
28084  }
28085  if (x+1<width() && state(x+1,y,z)!=1) {
28086  const Tfloat dist = __distance_eikonal(res,met(x+1,y,z),x+1,y,z);
28087  if (dist<res(x+1,y,z)) { res(x+1,y,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x+1,y,z); }
28088  }
28089  if (y-1>=0 && state(x,y-1,z)!=1) {
28090  const Tfloat dist = __distance_eikonal(res,met(x,y-1,z),x,y-1,z);
28091  if (dist<res(x,y-1,z)) { res(x,y-1,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y-1,z); }
28092  }
28093  if (y+1<height() && state(x,y+1,z)!=1) {
28094  const Tfloat dist = __distance_eikonal(res,met(x,y+1,z),x,y+1,z);
28095  if (dist<res(x,y+1,z)) { res(x,y+1,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y+1,z); }
28096  }
28097  if (z-1>=0 && state(x,y,z-1)!=1) {
28098  const Tfloat dist = __distance_eikonal(res,met(x,y,z-1),x,y,z-1);
28099  if (dist<res(x,y,z-1)) { res(x,y,z-1) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z-1); }
28100  }
28101  if (z+1<depth() && state(x,y,z+1)!=1) {
28102  const Tfloat dist = __distance_eikonal(res,met(x,y,z+1),x,y,z+1);
28103  if (dist<res(x,y,z+1)) { res(x,y,z+1) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z+1); }
28104  }
28105  }
28106  }
28107  }
28108  return result;
28109  }
28110 
28111  // Locally solve eikonal equation.
28112  Tfloat __distance_eikonal(const CImg<Tfloat>& res, const Tfloat P,
28113  const int x=0, const int y=0, const int z=0) const {
28114  const T M = cimg::type<T>::max();
28115  T T1 = cimg::min(x-1>=0?res(x-1,y,z):M,x+1<width()?res(x+1,y,z):M);
28116  Tfloat root = 0;
28117  if (_depth>1) { // 3d.
28118  T
28119  T2 = cimg::min(y-1>=0?res(x,y-1,z):M,y+1<height()?res(x,y+1,z):M),
28120  T3 = cimg::min(z-1>=0?res(x,y,z-1):M,z+1<depth()?res(x,y,z+1):M);
28121  if (T1>T2) cimg::swap(T1,T2);
28122  if (T2>T3) cimg::swap(T2,T3);
28123  if (T1>T2) cimg::swap(T1,T2);
28124  if (P<=0) return (Tfloat)T1;
28125  if (T3<M && ___distance_eikonal(3,-2*(T1+T2+T3),T1*T1+T2*T2+T3*T3-P*P,root)) return cimg::max((Tfloat)T3,root);
28126  if (T2<M && ___distance_eikonal(2,-2*(T1+T2),T1*T1+T2*T2-P*P,root)) return cimg::max((Tfloat)T2,root);
28127  return P + T1;
28128  } else if (_height>1) { // 2d.
28129  T T2 = cimg::min(y-1>=0?res(x,y-1,z):M,y+1<height()?res(x,y+1,z):M);
28130  if (T1>T2) cimg::swap(T1,T2);
28131  if (P<=0) return (Tfloat)T1;
28132  if (T2<M && ___distance_eikonal(2,-2*(T1+T2),T1*T1+T2*T2-P*P,root)) return cimg::max((Tfloat)T2,root);
28133  return P + T1;
28134  } else { // 1d.
28135  if (P<=0) return (Tfloat)T1;
28136  return P + T1;
28137  }
28138  return 0;
28139  }
28140 
28141  // Find max root of a 2nd-order polynomial.
28142  static bool ___distance_eikonal(const Tfloat a, const Tfloat b, const Tfloat c, Tfloat &root) {
28143  const Tfloat delta = b*b - 4*a*c;
28144  if (delta<0) return false;
28145  root = 0.5f*(-b + std::sqrt(delta))/a;
28146  return true;
28147  }
28148 
28149  // Insert new point in heap.
28150  template<typename t>
28151  void _eik_priority_queue_insert(CImg<charT>& state, unsigned int& siz, const t value,
28152  const unsigned int x, const unsigned int y, const unsigned int z) {
28153  if (state(x,y,z)>0) return;
28154  state(x,y,z) = 0;
28155  if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); }
28156  (*this)(siz-1,0) = (T)value; (*this)(siz-1,1) = (T)x; (*this)(siz-1,2) = (T)y; (*this)(siz-1,3) = (T)z;
28157  for (unsigned int pos = siz - 1, par = 0; pos && value>(*this)(par=(pos+1)/2-1,0); pos = par) {
28158  cimg::swap((*this)(pos,0),(*this)(par,0)); cimg::swap((*this)(pos,1),(*this)(par,1));
28159  cimg::swap((*this)(pos,2),(*this)(par,2)); cimg::swap((*this)(pos,3),(*this)(par,3));
28160  }
28161  }
28162 
28164 
28169  CImg<T>& distance_eikonal(const unsigned int nb_iterations, const float band_size=0, const float time_step=0.5f) {
28170  if (is_empty()) return *this;
28171  CImg<Tfloat> velocity(*this);
28172  for (unsigned int iteration = 0; iteration<nb_iterations; ++iteration) {
28173  Tfloat *ptrd = velocity._data, veloc_max = 0;
28174  if (_depth>1) { // 3d
28175  CImg_3x3x3(I,Tfloat);
28176  cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) if (band_size<=0 || cimg::abs(Iccc)<band_size) {
28177  const Tfloat
28178  gx = (Incc - Ipcc)/2,
28179  gy = (Icnc - Icpc)/2,
28180  gz = (Iccn - Iccp)/2,
28181  sgn = -cimg::sign(Iccc),
28182  ix = gx*sgn>0?(Incc - Iccc):(Iccc - Ipcc),
28183  iy = gy*sgn>0?(Icnc - Iccc):(Iccc - Icpc),
28184  iz = gz*sgn>0?(Iccn - Iccc):(Iccc - Iccp),
28185  ng = (Tfloat)(1e-5f + std::sqrt(gx*gx + gy*gy + gz*gz)),
28186  ngx = gx/ng,
28187  ngy = gy/ng,
28188  ngz = gz/ng,
28189  veloc = sgn*(ngx*ix + ngy*iy + ngz*iz - 1);
28190  *(ptrd++) = veloc;
28191  if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
28192  } else *(ptrd++) = 0;
28193  } else { // 2d version
28194  CImg_3x3(I,Tfloat);
28195  cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) if (band_size<=0 || cimg::abs(Icc)<band_size) {
28196  const Tfloat
28197  gx = (Inc - Ipc)/2,
28198  gy = (Icn - Icp)/2,
28199  sgn = -cimg::sign(Icc),
28200  ix = gx*sgn>0?(Inc - Icc):(Icc - Ipc),
28201  iy = gy*sgn>0?(Icn - Icc):(Icc - Icp),
28202  ng = (Tfloat)(1e-5f + std::sqrt(gx*gx + gy*gy)),
28203  ngx = gx/ng,
28204  ngy = gy/ng,
28205  veloc = sgn*(ngx*ix + ngy*iy - 1);
28206  *(ptrd++) = veloc;
28207  if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
28208  } else *(ptrd++) = 0;
28209  }
28210  if (veloc_max>0) *this+=(velocity*=time_step/veloc_max);
28211  }
28212  return *this;
28213  }
28214 
28216  CImg<Tfloat> get_distance_eikonal(const unsigned int nb_iterations, const float band_size=0,
28217  const float time_step=0.5f) const {
28218  return CImg<Tfloat>(*this,false).distance_eikonal(nb_iterations,band_size,time_step);
28219  }
28220 
28222 
28227  CImg<T>& haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) {
28228  return get_haar(axis,invert,nb_scales).move_to(*this);
28229  }
28230 
28232  CImg<Tfloat> get_haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) const {
28233  if (is_empty() || !nb_scales) return +*this;
28234  CImg<Tfloat> res;
28235  const Tfloat sqrt2 = std::sqrt(2);
28236  if (nb_scales==1) {
28237  switch (cimg::uncase(axis)) { // Single scale transform
28238  case 'x' : {
28239  const unsigned int w = _width/2;
28240  if (w) {
28241  if ((w%2) && w!=1)
28242  throw CImgInstanceException(_cimg_instance
28243  "haar(): Sub-image width %u is not even.",
28244  cimg_instance,
28245  w);
28246 
28247  res.assign(_width,_height,_depth,_spectrum);
28248  if (invert) cimg_forYZC(*this,y,z,c) { // Inverse transform along X
28249  for (unsigned int x = 0, xw = w, x2 = 0; x<w; ++x, ++xw) {
28250  const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(xw,y,z,c);
28251  res(x2++,y,z,c) = (val0 - val1)/sqrt2;
28252  res(x2++,y,z,c) = (val0 + val1)/sqrt2;
28253  }
28254  } else cimg_forYZC(*this,y,z,c) { // Direct transform along X
28255  for (unsigned int x = 0, xw = w, x2 = 0; x<w; ++x, ++xw) {
28256  const Tfloat val0 = (Tfloat)(*this)(x2++,y,z,c), val1 = (Tfloat)(*this)(x2++,y,z,c);
28257  res(x,y,z,c) = (val0 + val1)/sqrt2;
28258  res(xw,y,z,c) = (val1 - val0)/sqrt2;
28259  }
28260  }
28261  } else return *this;
28262  } break;
28263  case 'y' : {
28264  const unsigned int h = _height/2;
28265  if (h) {
28266  if ((h%2) && h!=1)
28267  throw CImgInstanceException(_cimg_instance
28268  "haar(): Sub-image height %u is not even.",
28269  cimg_instance,
28270  h);
28271 
28272  res.assign(_width,_height,_depth,_spectrum);
28273  if (invert) cimg_forXZC(*this,x,z,c) { // Inverse transform along Y
28274  for (unsigned int y = 0, yh = h, y2 = 0; y<h; ++y, ++yh) {
28275  const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(x,yh,z,c);
28276  res(x,y2++,z,c) = (val0 - val1)/sqrt2;
28277  res(x,y2++,z,c) = (val0 + val1)/sqrt2;
28278  }
28279  } else cimg_forXZC(*this,x,z,c) {
28280  for (unsigned int y = 0, yh = h, y2 = 0; y<h; ++y, ++yh) { // Direct transform along Y
28281  const Tfloat val0 = (Tfloat)(*this)(x,y2++,z,c), val1 = (Tfloat)(*this)(x,y2++,z,c);
28282  res(x,y,z,c) = (val0 + val1)/sqrt2;
28283  res(x,yh,z,c) = (val1 - val0)/sqrt2;
28284  }
28285  }
28286  } else return *this;
28287  } break;
28288  case 'z' : {
28289  const unsigned int d = _depth/2;
28290  if (d) {
28291  if ((d%2) && d!=1)
28292  throw CImgInstanceException(_cimg_instance
28293  "haar(): Sub-image depth %u is not even.",
28294  cimg_instance,
28295  d);
28296 
28297  res.assign(_width,_height,_depth,_spectrum);
28298  if (invert) cimg_forXYC(*this,x,y,c) { // Inverse transform along Z
28299  for (unsigned int z = 0, zd = d, z2 = 0; z<d; ++z, ++zd) {
28300  const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(x,y,zd,c);
28301  res(x,y,z2++,c) = (val0 - val1)/sqrt2;
28302  res(x,y,z2++,c) = (val0 + val1)/sqrt2;
28303  }
28304  } else cimg_forXYC(*this,x,y,c) {
28305  for (unsigned int z = 0, zd = d, z2 = 0; z<d; ++z, ++zd) { // Direct transform along Z
28306  const Tfloat val0 = (Tfloat)(*this)(x,y,z2++,c), val1 = (Tfloat)(*this)(x,y,z2++,c);
28307  res(x,y,z,c) = (val0 + val1)/sqrt2;
28308  res(x,y,zd,c) = (val1 - val0)/sqrt2;
28309  }
28310  }
28311  } else return *this;
28312  } break;
28313  default :
28314  throw CImgArgumentException(_cimg_instance
28315  "haar(): Invalid specified axis '%c' "
28316  "(should be { x | y | z }).",
28317  cimg_instance,
28318  axis);
28319  }
28320  } else { // Multi-scale version
28321  if (invert) {
28322  res.assign(*this);
28323  switch (cimg::uncase(axis)) {
28324  case 'x' : {
28325  unsigned int w = _width;
28326  for (unsigned int s = 1; w && s<nb_scales; ++s) w/=2;
28327  for (w = w?w:1; w<=_width; w*=2) res.draw_image(res.get_crop(0,w-1).get_haar('x',true,1));
28328  } break;
28329  case 'y' : {
28330  unsigned int h = _width;
28331  for (unsigned int s = 1; h && s<nb_scales; ++s) h/=2;
28332  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));
28333  } break;
28334  case 'z' : {
28335  unsigned int d = _depth;
28336  for (unsigned int s = 1; d && s<nb_scales; ++s) d/=2;
28337  for (d = d?d:1; d<=_depth; d*=2)
28338  res.draw_image(res.get_crop(0,0,0,_width-1,_height-1,d-1).get_haar('z',true,1));
28339  } break;
28340  default :
28341  throw CImgArgumentException(_cimg_instance
28342  "haar(): Invalid specified axis '%c' "
28343  "(should be { x | y | z }).",
28344  cimg_instance,
28345  axis);
28346  }
28347  } else { // Direct transform
28348  res = get_haar(axis,false,1);
28349  switch (cimg::uncase(axis)) {
28350  case 'x' : {
28351  for (unsigned int s = 1, w = _width/2; w && s<nb_scales; ++s, w/=2)
28352  res.draw_image(res.get_crop(0,w-1).get_haar('x',false,1));
28353  } break;
28354  case 'y' : {
28355  for (unsigned int s = 1, h = _height/2; h && s<nb_scales; ++s, h/=2)
28356  res.draw_image(res.get_crop(0,0,_width-1,h-1).get_haar('y',false,1));
28357  } break;
28358  case 'z' : {
28359  for (unsigned int s = 1, d = _depth/2; d && s<nb_scales; ++s, d/=2)
28360  res.draw_image(res.get_crop(0,0,0,_width-1,_height-1,d-1).get_haar('z',false,1));
28361  } break;
28362  default :
28363  throw CImgArgumentException(_cimg_instance
28364  "haar(): Invalid specified axis '%c' "
28365  "(should be { x | y | z }).",
28366  cimg_instance,
28367  axis);
28368  }
28369  }
28370  }
28371  return res;
28372  }
28373 
28375 
28379  CImg<T>& haar(const bool invert=false, const unsigned int nb_scales=1) {
28380  return get_haar(invert,nb_scales).move_to(*this);
28381  }
28382 
28384  CImg<Tfloat> get_haar(const bool invert=false, const unsigned int nb_scales=1) const {
28385  CImg<Tfloat> res;
28386  if (nb_scales==1) { // Single scale transform
28387  if (_width>1) get_haar('x',invert,1).move_to(res);
28388  if (_height>1) { if (res) res.haar('y',invert,1); else get_haar('y',invert,1).move_to(res); }
28389  if (_depth>1) { if (res) res.haar('z',invert,1); else get_haar('z',invert,1).move_to(res); }
28390  if (res) return res;
28391  } else { // Multi-scale transform
28392  if (invert) { // Inverse transform
28393  res.assign(*this);
28394  if (_width>1) {
28395  if (_height>1) {
28396  if (_depth>1) {
28397  unsigned int w = _width, h = _height, d = _depth;
28398  for (unsigned int s = 1; w && h && d && s<nb_scales; ++s) { w/=2; h/=2; d/=2; }
28399  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)
28400  res.draw_image(res.get_crop(0,0,0,w-1,h-1,d-1).get_haar(true,1));
28401  } else {
28402  unsigned int w = _width, h = _height;
28403  for (unsigned int s = 1; w && h && s<nb_scales; ++s) { w/=2; h/=2; }
28404  for (w = w?w:1, h = h?h:1; w<=_width && h<=_height; w*=2, h*=2)
28405  res.draw_image(res.get_crop(0,0,0,w-1,h-1,0).get_haar(true,1));
28406  }
28407  } else {
28408  if (_depth>1) {
28409  unsigned int w = _width, d = _depth;
28410  for (unsigned int s = 1; w && d && s<nb_scales; ++s) { w/=2; d/=2; }
28411  for (w = w?w:1, d = d?d:1; w<=_width && d<=_depth; w*=2, d*=2)
28412  res.draw_image(res.get_crop(0,0,0,w-1,0,d-1).get_haar(true,1));
28413  } else {
28414  unsigned int w = _width;
28415  for (unsigned int s = 1; w && s<nb_scales; ++s) w/=2;
28416  for (w = w?w:1; w<=_width; w*=2)
28417  res.draw_image(res.get_crop(0,0,0,w-1,0,0).get_haar(true,1));
28418  }
28419  }
28420  } else {
28421  if (_height>1) {
28422  if (_depth>1) {
28423  unsigned int h = _height, d = _depth;
28424  for (unsigned int s = 1; h && d && s<nb_scales; ++s) { h/=2; d/=2; }
28425  for (h = h?h:1, d = d?d:1; h<=_height && d<=_depth; h*=2, d*=2)
28426  res.draw_image(res.get_crop(0,0,0,0,h-1,d-1).get_haar(true,1));
28427  } else {
28428  unsigned int h = _height;
28429  for (unsigned int s = 1; h && s<nb_scales; ++s) h/=2;
28430  for (h = h?h:1; h<=_height; h*=2)
28431  res.draw_image(res.get_crop(0,0,0,0,h-1,0).get_haar(true,1));
28432  }
28433  } else {
28434  if (_depth>1) {
28435  unsigned int d = _depth;
28436  for (unsigned int s = 1; d && s<nb_scales; ++s) d/=2;
28437  for (d = d?d:1; d<=_depth; d*=2)
28438  res.draw_image(res.get_crop(0,0,0,0,0,d-1).get_haar(true,1));
28439  } else return *this;
28440  }
28441  }
28442  } else { // Direct transform
28443  res = get_haar(false,1);
28444  if (_width>1) {
28445  if (_height>1) {
28446  if (_depth>1)
28447  for (unsigned int s = 1, w = _width/2, h = _height/2, d = _depth/2; w && h && d && s<nb_scales;
28448  ++s, w/=2, h/=2, d/=2)
28449  res.draw_image(res.get_crop(0,0,0,w-1,h-1,d-1).haar(false,1));
28450  else for (unsigned int s = 1, w = _width/2, h = _height/2; w && h && s<nb_scales; ++s, w/=2, h/=2)
28451  res.draw_image(res.get_crop(0,0,0,w-1,h-1,0).haar(false,1));
28452  } else {
28453  if (_depth>1) for (unsigned int s = 1, w = _width/2, d = _depth/2; w && d && s<nb_scales; ++s, w/=2, d/=2)
28454  res.draw_image(res.get_crop(0,0,0,w-1,0,d-1).haar(false,1));
28455  else for (unsigned int s = 1, w = _width/2; w && s<nb_scales; ++s, w/=2)
28456  res.draw_image(res.get_crop(0,0,0,w-1,0,0).haar(false,1));
28457  }
28458  } else {
28459  if (_height>1) {
28460  if (_depth>1)
28461  for (unsigned int s = 1, h = _height/2, d = _depth/2; h && d && s<nb_scales; ++s, h/=2, d/=2)
28462  res.draw_image(res.get_crop(0,0,0,0,h-1,d-1).haar(false,1));
28463  else for (unsigned int s = 1, h = _height/2; h && s<nb_scales; ++s, h/=2)
28464  res.draw_image(res.get_crop(0,0,0,0,h-1,0).haar(false,1));
28465  } else {
28466  if (_depth>1) for (unsigned int s = 1, d = _depth/2; d && s<nb_scales; ++s, d/=2)
28467  res.draw_image(res.get_crop(0,0,0,0,0,d-1).haar(false,1));
28468  else return *this;
28469  }
28470  }
28471  }
28472  return res;
28473  }
28474  return *this;
28475  }
28476 
28478 
28482  CImgList<Tfloat> get_FFT(const char axis, const bool is_invert=false) const {
28483  CImgList<Tfloat> res(*this,CImg<Tfloat>());
28484  CImg<Tfloat>::FFT(res[0],res[1],axis,is_invert);
28485  return res;
28486  }
28487 
28489  /*
28490  \param is_invert Tells if the forward (\c false) or inverse (\c true) FFT is computed.
28491  **/
28492  CImgList<Tfloat> get_FFT(const bool is_invert=false) const {
28493  CImgList<Tfloat> res(*this,CImg<Tfloat>());
28494  CImg<Tfloat>::FFT(res[0],res[1],is_invert);
28495  return res;
28496  }
28497 
28499 
28505  static void FFT(CImg<T>& real, CImg<T>& imag, const char axis, const bool is_invert=false) {
28506  if (!real)
28507  throw CImgInstanceException("CImg<%s>::FFT(): Specified real part is empty.",
28508  pixel_type());
28509 
28510  if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,0);
28511  if (!real.is_sameXYZC(imag))
28512  throw CImgInstanceException("CImg<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and "
28513  "imaginary part (%u,%u,%u,%u,%p) have different dimensions.",
28514  pixel_type(),
28515  real._width,real._height,real._depth,real._spectrum,real._data,
28516  imag._width,imag._height,imag._depth,imag._spectrum,imag._data);
28517 #ifdef cimg_use_fftw3
28518  cimg::mutex(12);
28519  fftw_complex *data_in;
28520  fftw_plan data_plan;
28521 
28522  switch (cimg::uncase(axis)) {
28523  case 'x' : { // Fourier along X, using FFTW library.
28524  data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width);
28525  if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) "
28526  "for computing FFT of image (%u,%u,%u,%u) along the X-axis.",
28527  pixel_type(),
28528  cimg::strbuffersize(sizeof(fftw_complex)*real._width),
28529  real._width,real._height,real._depth,real._spectrum);
28530 
28531  data_plan = fftw_plan_dft_1d(real._width,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
28532  cimg_forYZC(real,y,z,c) {
28533  T *ptrr = real.data(0,y,z,c), *ptri = imag.data(0,y,z,c);
28534  double *ptrd = (double*)data_in;
28535  cimg_forX(real,x) { *(ptrd++) = (double)*(ptrr++); *(ptrd++) = (double)*(ptri++); }
28536  fftw_execute(data_plan);
28537  const unsigned int fact = real._width;
28538  if (is_invert) cimg_forX(real,x) { *(--ptri) = (T)(*(--ptrd)/fact); *(--ptrr) = (T)(*(--ptrd)/fact); }
28539  else cimg_forX(real,x) { *(--ptri) = (T)*(--ptrd); *(--ptrr) = (T)*(--ptrd); }
28540  }
28541  } break;
28542  case 'y' : { // Fourier along Y, using FFTW library.
28543  data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * real._height);
28544  if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) "
28545  "for computing FFT of image (%u,%u,%u,%u) along the Y-axis.",
28546  pixel_type(),
28547  cimg::strbuffersize(sizeof(fftw_complex)*real._height),
28548  real._width,real._height,real._depth,real._spectrum);
28549 
28550  data_plan = fftw_plan_dft_1d(real._height,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
28551  const unsigned int off = real._width;
28552  cimg_forXZC(real,x,z,c) {
28553  T *ptrr = real.data(x,0,z,c), *ptri = imag.data(x,0,z,c);
28554  double *ptrd = (double*)data_in;
28555  cimg_forY(real,y) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; }
28556  fftw_execute(data_plan);
28557  const unsigned int fact = real._height;
28558  if (is_invert)
28559  cimg_forY(real,y) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); }
28560  else cimg_forY(real,y) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); }
28561  }
28562  } break;
28563  case 'z' : { // Fourier along Z, using FFTW library.
28564  data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * real._depth);
28565  if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) "
28566  "for computing FFT of image (%u,%u,%u,%u) along the Z-axis.",
28567  pixel_type(),
28568  cimg::strbuffersize(sizeof(fftw_complex)*real._depth),
28569  real._width,real._height,real._depth,real._spectrum);
28570 
28571  data_plan = fftw_plan_dft_1d(real._depth,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
28572  const unsigned long off = (unsigned long)real._width*real._height;
28573  cimg_forXYC(real,x,y,c) {
28574  T *ptrr = real.data(x,y,0,c), *ptri = imag.data(x,y,0,c);
28575  double *ptrd = (double*)data_in;
28576  cimg_forZ(real,z) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; }
28577  fftw_execute(data_plan);
28578  const unsigned int fact = real._depth;
28579  if (is_invert)
28580  cimg_forZ(real,z) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); }
28581  else cimg_forZ(real,z) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); }
28582  }
28583  } break;
28584  default : { // Fourier along C, using FFTW library.
28585  data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * real._spectrum);
28586  if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) "
28587  "for computing FFT of image (%u,%u,%u,%u) along the C-axis.",
28588  pixel_type(),
28589  cimg::strbuffersize(sizeof(fftw_complex)*real._spectrum),
28590  real._width,real._height,real._depth,real._spectrum);
28591 
28592  data_plan = fftw_plan_dft_1d(real._spectrum,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
28593  const unsigned long off = (unsigned long)real._width*real._height*real._depth;
28594  cimg_forXYZ(real,x,y,z) {
28595  T *ptrr = real.data(x,y,z,0), *ptri = imag.data(x,y,z,0);
28596  double *ptrd = (double*)data_in;
28597  cimg_forC(real,c) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; }
28598  fftw_execute(data_plan);
28599  const unsigned int fact = real._spectrum;
28600  if (is_invert)
28601  cimg_forC(real,c) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); }
28602  else cimg_forC(real,c) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); }
28603  }
28604  }
28605  }
28606  fftw_destroy_plan(data_plan);
28607  fftw_free(data_in);
28608  cimg::mutex(12,0);
28609 #else
28610  switch (cimg::uncase(axis)) {
28611  case 'x' : { // Fourier along X, using built-in functions.
28612  const unsigned int N = real._width, N2 = (N>>1);
28613  if (((N-1)&N) && N!=1)
28614  throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) "
28615  "have non 2^N dimension along the X-axis.",
28616  pixel_type(),
28617  real._width,real._height,real._depth,real._spectrum);
28618 
28619  for (unsigned int i = 0, j = 0; i<N2; ++i) {
28620  if (j>i) cimg_forYZC(real,y,z,c) {
28621  cimg::swap(real(i,y,z,c),real(j,y,z,c)); cimg::swap(imag(i,y,z,c),imag(j,y,z,c));
28622  if (j<N2) {
28623  const unsigned int ri = N-1-i, rj = N-1-j;
28624  cimg::swap(real(ri,y,z,c),real(rj,y,z,c)); cimg::swap(imag(ri,y,z,c),imag(rj,y,z,c));
28625  }
28626  }
28627  for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
28628  }
28629  for (unsigned int delta = 2; delta<=N; delta<<=1) {
28630  const unsigned int delta2 = (delta>>1);
28631  for (unsigned int i = 0; i<N; i+=delta) {
28632  float wr = 1, wi = 0;
28633  const float angle = (float)((is_invert?+1:-1)*2*cimg::PI/delta),
28634  ca = (float)std::cos(angle),
28635  sa = (float)std::sin(angle);
28636  for (unsigned int k = 0; k<delta2; ++k) {
28637  const unsigned int j = i + k, nj = j + delta2;
28638  cimg_forYZC(real,y,z,c) {
28639  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);
28640  const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
28641  nir = (T)(ir - tmpr);
28642  nii = (T)(ii - tmpi);
28643  ir+=(T)tmpr;
28644  ii+=(T)tmpi;
28645  }
28646  const float nwr = wr*ca-wi*sa;
28647  wi = wi*ca + wr*sa;
28648  wr = nwr;
28649  }
28650  }
28651  }
28652  if (is_invert) { real/=N; imag/=N; }
28653  } break;
28654  case 'y' : { // Fourier along Y, using built-in functions.
28655  const unsigned int N = real._height, N2 = (N>>1);
28656  if (((N-1)&N) && N!=1)
28657  throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) "
28658  "have non 2^N dimension along the Y-axis.",
28659  pixel_type(),
28660  real._width,real._height,real._depth,real._spectrum);
28661 
28662  for (unsigned int i = 0, j = 0; i<N2; ++i) {
28663  if (j>i) cimg_forXZC(real,x,z,c) {
28664  cimg::swap(real(x,i,z,c),real(x,j,z,c)); cimg::swap(imag(x,i,z,c),imag(x,j,z,c));
28665  if (j<N2) {
28666  const unsigned int ri = N - 1 - i, rj = N - 1 - j;
28667  cimg::swap(real(x,ri,z,c),real(x,rj,z,c)); cimg::swap(imag(x,ri,z,c),imag(x,rj,z,c));
28668  }
28669  }
28670  for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
28671  }
28672  for (unsigned int delta = 2; delta<=N; delta<<=1) {
28673  const unsigned int delta2 = (delta>>1);
28674  for (unsigned int i = 0; i<N; i+=delta) {
28675  float wr = 1, wi = 0;
28676  const float angle = (float)((is_invert?+1:-1)*2*cimg::PI/delta),
28677  ca = (float)std::cos(angle), sa = (float)std::sin(angle);
28678  for (unsigned int k = 0; k<delta2; ++k) {
28679  const unsigned int j = i + k, nj = j + delta2;
28680  cimg_forXZC(real,x,z,c) {
28681  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);
28682  const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
28683  nir = (T)(ir - tmpr);
28684  nii = (T)(ii - tmpi);
28685  ir+=(T)tmpr;
28686  ii+=(T)tmpi;
28687  }
28688  const float nwr = wr*ca-wi*sa;
28689  wi = wi*ca + wr*sa;
28690  wr = nwr;
28691  }
28692  }
28693  }
28694  if (is_invert) { real/=N; imag/=N; }
28695  } break;
28696  case 'z' : { // Fourier along Z, using built-in functions.
28697  const unsigned int N = real._depth, N2 = (N>>1);
28698  if (((N-1)&N) && N!=1)
28699  throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) "
28700  "have non 2^N dimension along the Z-axis.",
28701  pixel_type(),
28702  real._width,real._height,real._depth,real._spectrum);
28703 
28704  for (unsigned int i = 0, j = 0; i<N2; ++i) {
28705  if (j>i) cimg_forXYC(real,x,y,c) {
28706  cimg::swap(real(x,y,i,c),real(x,y,j,c)); cimg::swap(imag(x,y,i,c),imag(x,y,j,c));
28707  if (j<N2) {
28708  const unsigned int ri = N - 1 - i, rj = N - 1 - j;
28709  cimg::swap(real(x,y,ri,c),real(x,y,rj,c)); cimg::swap(imag(x,y,ri,c),imag(x,y,rj,c));
28710  }
28711  }
28712  for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
28713  }
28714  for (unsigned int delta = 2; delta<=N; delta<<=1) {
28715  const unsigned int delta2 = (delta>>1);
28716  for (unsigned int i = 0; i<N; i+=delta) {
28717  float wr = 1, wi = 0;
28718  const float angle = (float)((is_invert?+1:-1)*2*cimg::PI/delta),
28719  ca = (float)std::cos(angle), sa = (float)std::sin(angle);
28720  for (unsigned int k = 0; k<delta2; ++k) {
28721  const unsigned int j = i + k, nj = j + delta2;
28722  cimg_forXYC(real,x,y,c) {
28723  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);
28724  const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
28725  nir = (T)(ir - tmpr);
28726  nii = (T)(ii - tmpi);
28727  ir+=(T)tmpr;
28728  ii+=(T)tmpi;
28729  }
28730  const float nwr = wr*ca-wi*sa;
28731  wi = wi*ca + wr*sa;
28732  wr = nwr;
28733  }
28734  }
28735  }
28736  if (is_invert) { real/=N; imag/=N; }
28737  } break;
28738  default :
28739  throw CImgArgumentException("CImgList<%s>::FFT(): Invalid specified axis '%c' for real and imaginary parts "
28740  "(%u,%u,%u,%u) "
28741  "(should be { x | y | z }).",
28742  pixel_type(),axis,
28743  real._width,real._height,real._depth,real._spectrum);
28744  }
28745 #endif
28746  }
28747 
28749 
28756  static void FFT(CImg<T>& real, CImg<T>& imag, const bool is_invert=false, const unsigned int nb_threads=0) {
28757  if (!real)
28758  throw CImgInstanceException("CImgList<%s>::FFT(): Empty specified real part.",
28759  pixel_type());
28760 
28761  if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,0);
28762  if (!real.is_sameXYZC(imag))
28763  throw CImgInstanceException("CImgList<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and "
28764  "imaginary part (%u,%u,%u,%u,%p) have different dimensions.",
28765  pixel_type(),
28766  real._width,real._height,real._depth,real._spectrum,real._data,
28767  imag._width,imag._height,imag._depth,imag._spectrum,imag._data);
28768 
28769 #ifdef cimg_use_fftw3
28770  cimg::mutex(12);
28771 #ifndef cimg_use_fftw3_singlethread
28772  const unsigned int _nb_threads = nb_threads?nb_threads:cimg::nb_cpus();
28773  static int fftw_st = fftw_init_threads();
28774  cimg::unused(fftw_st);
28775  fftw_plan_with_nthreads(_nb_threads);
28776 #else
28777  cimg::unused(nb_threads);
28778 #endif
28779  fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width*real._height*real._depth);
28780  if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) "
28781  "for computing FFT of image (%u,%u,%u,%u).",
28782  pixel_type(),
28783  cimg::strbuffersize(sizeof(fftw_complex)*real._width*
28784  real._height*real._depth*real._spectrum),
28785  real._width,real._height,real._depth,real._spectrum);
28786 
28787  fftw_plan data_plan;
28788  const unsigned long w = (unsigned long)real._width, wh = w*real._height, whd = wh*real._depth;
28789  data_plan = fftw_plan_dft_3d(real._width,real._height,real._depth,data_in,data_in,
28790  is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
28791  cimg_forC(real,c) {
28792  T *ptrr = real.data(0,0,0,c), *ptri = imag.data(0,0,0,c);
28793  double *ptrd = (double*)data_in;
28794  for (unsigned int x = 0; x<real._width; ++x, ptrr-=wh-1, ptri-=wh-1)
28795  for (unsigned int y = 0; y<real._height; ++y, ptrr-=whd-w, ptri-=whd-w)
28796  for (unsigned int z = 0; z<real._depth; ++z, ptrr+=wh, ptri+=wh) {
28797  *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri;
28798  }
28799  fftw_execute(data_plan);
28800  ptrd = (double*)data_in;
28801  ptrr = real.data(0,0,0,c);
28802  ptri = imag.data(0,0,0,c);
28803  if (!is_invert) for (unsigned int x = 0; x<real._width; ++x, ptrr-=wh-1, ptri-=wh-1)
28804  for (unsigned int y = 0; y<real._height; ++y, ptrr-=whd-w, ptri-=whd-w)
28805  for (unsigned int z = 0; z<real._depth; ++z, ptrr+=wh, ptri+=wh) {
28806  *ptrr = (T)*(ptrd++); *ptri = (T)*(ptrd++);
28807  }
28808  else for (unsigned int x = 0; x<real._width; ++x, ptrr-=wh-1, ptri-=wh-1)
28809  for (unsigned int y = 0; y<real._height; ++y, ptrr-=whd-w, ptri-=whd-w)
28810  for (unsigned int z = 0; z<real._depth; ++z, ptrr+=wh, ptri+=wh) {
28811  *ptrr = (T)(*(ptrd++)/whd); *ptri = (T)(*(ptrd++)/whd);
28812  }
28813  }
28814  fftw_destroy_plan(data_plan);
28815  fftw_free(data_in);
28816 #ifndef cimg_use_fftw3_singlethread
28817  fftw_cleanup_threads();
28818 #endif
28819  cimg::mutex(12,0);
28820 #else
28821  cimg::unused(nb_threads);
28822  if (real._depth>1) FFT(real,imag,'z',is_invert);
28823  if (real._height>1) FFT(real,imag,'y',is_invert);
28824  if (real._width>1) FFT(real,imag,'x',is_invert);
28825 #endif
28826  }
28827 
28829  //-------------------------------------
28830  //
28832 
28833  //-------------------------------------
28834 
28836 
28841  CImg<T>& shift_object3d(const float tx, const float ty=0, const float tz=0) {
28842  if (_height!=3 || _depth>1 || _spectrum>1)
28843  throw CImgInstanceException(_cimg_instance
28844  "shift_object3d(): Instance is not a set of 3d vertices.",
28845  cimg_instance);
28846 
28847  get_shared_row(0)+=tx; get_shared_row(1)+=ty; get_shared_row(2)+=tz;
28848  return *this;
28849  }
28850 
28852  CImg<Tfloat> get_shift_object3d(const float tx, const float ty=0, const float tz=0) const {
28853  return CImg<Tfloat>(*this,false).shift_object3d(tx,ty,tz);
28854  }
28855 
28857 
28861  if (_height!=3 || _depth>1 || _spectrum>1)
28862  throw CImgInstanceException(_cimg_instance
28863  "shift_object3d(): Instance is not a set of 3d vertices.",
28864  cimg_instance);
28865 
28866  CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
28867  float
28868  xm, xM = (float)xcoords.max_min(xm),
28869  ym, yM = (float)ycoords.max_min(ym),
28870  zm, zM = (float)zcoords.max_min(zm);
28871  xcoords-=(xm + xM)/2; ycoords-=(ym + yM)/2; zcoords-=(zm + zM)/2;
28872  return *this;
28873  }
28874 
28877  return CImg<Tfloat>(*this,false).shift_object3d();
28878  }
28879 
28881 
28886  CImg<T>& resize_object3d(const float sx, const float sy=-100, const float sz=-100) {
28887  if (_height!=3 || _depth>1 || _spectrum>1)
28888  throw CImgInstanceException(_cimg_instance
28889  "resize_object3d(): Instance is not a set of 3d vertices.",
28890  cimg_instance);
28891 
28892  CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
28893  float
28894  xm, xM = (float)xcoords.max_min(xm),
28895  ym, yM = (float)ycoords.max_min(ym),
28896  zm, zM = (float)zcoords.max_min(zm);
28897  if (xm<xM) { if (sx>0) xcoords*=sx/(xM-xm); else xcoords*=-sx/100; }
28898  if (ym<yM) { if (sy>0) ycoords*=sy/(yM-ym); else ycoords*=-sy/100; }
28899  if (zm<zM) { if (sz>0) zcoords*=sz/(zM-zm); else zcoords*=-sz/100; }
28900  return *this;
28901  }
28902 
28904  CImg<Tfloat> get_resize_object3d(const float sx, const float sy=-100, const float sz=-100) const {
28905  return CImg<Tfloat>(*this,false).resize_object3d(sx,sy,sz);
28906  }
28907 
28910  if (_height!=3 || _depth>1 || _spectrum>1)
28911  throw CImgInstanceException(_cimg_instance
28912  "resize_object3d(): Instance is not a set of 3d vertices.",
28913  cimg_instance);
28914 
28915  CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
28916  float
28917  xm, xM = (float)xcoords.max_min(xm),
28918  ym, yM = (float)ycoords.max_min(ym),
28919  zm, zM = (float)zcoords.max_min(zm);
28920  const float dx = xM - xm, dy = yM - ym, dz = zM - zm, dmax = cimg::max(dx,dy,dz);
28921  if (dmax>0) { xcoords/=dmax; ycoords/=dmax; zcoords/=dmax; }
28922  return *this;
28923  }
28924 
28927  return CImg<Tfloat>(*this,false).resize_object3d();
28928  }
28929 
28931 
28936  template<typename tf, typename tp, typename tff>
28937  CImg<T>& append_object3d(CImgList<tf>& primitives, const CImg<tp>& obj_vertices,
28938  const CImgList<tff>& obj_primitives) {
28939  if (!obj_vertices || !obj_primitives) return *this;
28940  if (obj_vertices._height!=3 || obj_vertices._depth>1 || obj_vertices._spectrum>1)
28941  throw CImgInstanceException(_cimg_instance
28942  "append_object3d(): Specified vertice image (%u,%u,%u,%u,%p) is not a "
28943  "set of 3d vertices.",
28944  cimg_instance,
28945  obj_vertices._width,obj_vertices._height,
28946  obj_vertices._depth,obj_vertices._spectrum,obj_vertices._data);
28947 
28948  if (is_empty()) { primitives.assign(obj_primitives); return assign(obj_vertices); }
28949  if (_height!=3 || _depth>1 || _spectrum>1)
28950  throw CImgInstanceException(_cimg_instance
28951  "append_object3d(): Instance is not a set of 3d vertices.",
28952  cimg_instance);
28953 
28954  const unsigned int P = _width;
28955  append(obj_vertices,'x');
28956  const unsigned int N = primitives._width;
28957  primitives.insert(obj_primitives);
28958  for (unsigned int i = N; i<primitives._width; ++i) {
28959  CImg<tf> &p = primitives[i];
28960  switch (p.size()) {
28961  case 1 : p[0]+=P; break; // Point.
28962  case 5 : p[0]+=P; p[1]+=P; break; // Sphere.
28963  case 2 : case 6 : p[0]+=P; p[1]+=P; break; // Segment.
28964  case 3 : case 9 : p[0]+=P; p[1]+=P; p[2]+=P; break; // Triangle.
28965  case 4 : case 12 : p[0]+=P; p[1]+=P; p[2]+=P; p[3]+=P; break; // Rectangle.
28966  }
28967  }
28968  return *this;
28969  }
28970 
28972 
28978  template<typename tp, typename tc, typename tt, typename tx>
28980  const CImg<tt>& texture, const CImg<tx>& coords=CImg<tx>::empty()) const {
28981  if (is_empty()) return *this;
28982  if (_height!=3)
28983  throw CImgInstanceException(_cimg_instance
28984  "texturize_object3d(): image instance is not a set of 3d points.",
28985  cimg_instance);
28986  if (coords && (coords._width!=_width || coords._height!=2))
28987  throw CImgArgumentException(_cimg_instance
28988  "texturize_object3d(): Invalid specified texture coordinates (%u,%u,%u,%u,%p).",
28989  cimg_instance,
28990  coords._width,coords._height,coords._depth,coords._spectrum,coords._data);
28991  CImg<unsigned int> _coords;
28992  if (!coords) { // If no texture coordinates specified, do a default XY-projection.
28993  _coords.assign(_width,2);
28994  float
28995  xmin, xmax = (float)get_shared_row(0).max_min(xmin),
28996  ymin, ymax = (float)get_shared_row(1).max_min(ymin),
28997  dx = xmax>xmin?xmax-xmin:1,
28998  dy = ymax>ymin?ymax-ymin:1;
28999  cimg_forX(*this,p) {
29000  _coords(p,0) = (unsigned int)(((*this)(p,0)-xmin)*(texture._width-1)/dx);
29001  _coords(p,1) = (unsigned int)(((*this)(p,1)-ymin)*(texture._height-1)/dy);
29002  }
29003  } else _coords = coords;
29004 
29005  int texture_ind = -1;
29006  cimglist_for(primitives,l) {
29007  CImg<tp> &p = primitives[l];
29008  const unsigned int siz = p.size();
29009  switch (siz) {
29010  case 1 : { // Point.
29011  const unsigned int
29012  i0 = (unsigned int)p[0],
29013  x0 = (unsigned int)_coords(i0,0), y0 = (unsigned int)_coords(i0,1);
29014  texture.get_vector_at(x0,y0).move_to(colors[l]);
29015  } break;
29016  case 2 : case 6 : { // Line.
29017  const unsigned int
29018  i0 = (unsigned int)p[0], i1 = (unsigned int)p[1],
29019  x0 = (unsigned int)_coords(i0,0), y0 = (unsigned int)_coords(i0,1),
29020  x1 = (unsigned int)_coords(i1,0), y1 = (unsigned int)_coords(i1,1);
29021  if (texture_ind<0) colors[texture_ind=l] = texture; else colors[l].assign(colors[texture_ind],true);
29022  CImg<tp>::vector(i0,i1,x0,y0,x1,y1).move_to(p);
29023  } break;
29024  case 3 : case 9 : { // Triangle.
29025  const unsigned int
29026  i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2],
29027  x0 = (unsigned int)_coords(i0,0), y0 = (unsigned int)_coords(i0,1),
29028  x1 = (unsigned int)_coords(i1,0), y1 = (unsigned int)_coords(i1,1),
29029  x2 = (unsigned int)_coords(i2,0), y2 = (unsigned int)_coords(i2,1);
29030  if (texture_ind<0) colors[texture_ind=l] = texture; else colors[l].assign(colors[texture_ind],true);
29031  CImg<tp>::vector(i0,i1,i2,x0,y0,x1,y1,x2,y2).move_to(p);
29032  } break;
29033  case 4 : case 12 : { // Quadrangle.
29034  const unsigned int
29035  i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2], i3 = (unsigned int)p[3],
29036  x0 = (unsigned int)_coords(i0,0), y0 = (unsigned int)_coords(i0,1),
29037  x1 = (unsigned int)_coords(i1,0), y1 = (unsigned int)_coords(i1,1),
29038  x2 = (unsigned int)_coords(i2,0), y2 = (unsigned int)_coords(i2,1),
29039  x3 = (unsigned int)_coords(i3,0), y3 = (unsigned int)_coords(i3,1);
29040  if (texture_ind<0) colors[texture_ind=l] = texture; else colors[l].assign(colors[texture_ind],true);
29041  CImg<tp>::vector(i0,i1,i2,i3,x0,y0,x1,y1,x2,y2,x3,y3).move_to(p);
29042  } break;
29043  }
29044  }
29045  return *this;
29046  }
29047 
29049 
29065  template<typename tf, typename tc, typename te>
29066  CImg<floatT> get_elevation3d(CImgList<tf>& primitives, CImgList<tc>& colors, const CImg<te>& elevation) const {
29067  if (!is_sameXY(elevation) || elevation._depth>1 || elevation._spectrum>1)
29068  throw CImgArgumentException(_cimg_instance
29069  "get_elevation3d(): Instance and specified elevation (%u,%u,%u,%u,%p) "
29070  "have incompatible dimensions.",
29071  cimg_instance,
29072  elevation._width,elevation._height,elevation._depth,
29073  elevation._spectrum,elevation._data);
29074  if (is_empty()) return *this;
29075  float m, M = (float)max_min(m);
29076  if (M==m) ++M;
29077  colors.assign();
29078  const unsigned int size_x1 = _width - 1, size_y1 = _height - 1;
29079  for (unsigned int y = 0; y<size_y1; ++y)
29080  for (unsigned int x = 0; x<size_x1; ++x) {
29081  const unsigned char
29082  r = (unsigned char)(((*this)(x,y,0) - m)*255/(M-m)),
29083  g = _spectrum>1?(unsigned char)(((*this)(x,y,1) - m)*255/(M-m)):r,
29084  b = _spectrum>2?(unsigned char)(((*this)(x,y,2) - m)*255/(M-m)):(_spectrum>1?0:r);
29085  CImg<tc>::vector((tc)r,(tc)g,(tc)b).move_to(colors);
29086  }
29087  const typename CImg<te>::_functor2d_int func(elevation);
29088  return elevation3d(primitives,func,0,0,_width-1.0f,_height-1.0f,_width,_height);
29089  }
29090 
29092 
29100  template<typename tf, typename tc>
29102  const unsigned int x0, const unsigned int y0, const unsigned int z0,
29103  const bool normalize_colors=false) const {
29104  float m = 0, M = 0, delta = 1;
29105  if (normalize_colors) { m = (float)min_max(M); delta = 255/(m==M?1:M-m); }
29106  const unsigned int
29107  _x0 = (x0>=_width)?_width - 1:x0,
29108  _y0 = (y0>=_height)?_height - 1:y0,
29109  _z0 = (z0>=_depth)?_depth - 1:z0;
29110  CImg<tc> img_xy, img_xz, img_yz;
29111  if (normalize_colors) {
29112  ((get_crop(0,0,_z0,0,_width-1,_height-1,_z0,_spectrum-1)-=m)*=delta).move_to(img_xy);
29113  ((get_crop(0,_y0,0,0,_width-1,_y0,_depth-1,_spectrum-1)-=m)*=delta).resize(_width,_depth,1,-100,-1).
29114  move_to(img_xz);
29115  ((get_crop(_x0,0,0,0,_x0,_height-1,_depth-1,_spectrum-1)-=m)*=delta).resize(_height,_depth,1,-100,-1).
29116  move_to(img_yz);
29117  } else {
29118  get_crop(0,0,_z0,0,_width-1,_height-1,_z0,_spectrum-1).move_to(img_xy);
29119  get_crop(0,_y0,0,0,_width-1,_y0,_depth-1,_spectrum-1).resize(_width,_depth,1,-100,-1).move_to(img_xz);
29120  get_crop(_x0,0,0,0,_x0,_height-1,_depth-1,_spectrum-1).resize(_height,_depth,1,-100,-1).move_to(img_yz);
29121  }
29122  CImg<floatT> points(12,3,1,1,
29123  0,_width-1,_width-1,0, 0,_width-1,_width-1,0, _x0,_x0,_x0,_x0,
29124  0,0,_height-1,_height-1, _y0,_y0,_y0,_y0, 0,_height-1,_height-1,0,
29125  _z0,_z0,_z0,_z0, 0,0,_depth-1,_depth-1, 0,0,_depth-1,_depth-1);
29126  primitives.assign();
29127  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).
29128  move_to(primitives);
29129  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).
29130  move_to(primitives);
29131  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).
29132  move_to(primitives);
29133  colors.assign();
29134  img_xy.move_to(colors);
29135  img_xz.move_to(colors);
29136  img_yz.move_to(colors);
29137  return points;
29138  }
29139 
29141 
29157  template<typename tf>
29158  CImg<floatT> get_isoline3d(CImgList<tf>& primitives, const float isovalue,
29159  const int size_x=-100, const int size_y=-100) const {
29160  if (_spectrum>1)
29161  throw CImgInstanceException(_cimg_instance
29162  "get_isoline3d(): Instance is not a scalar image.",
29163  cimg_instance);
29164  if (_depth>1)
29165  throw CImgInstanceException(_cimg_instance
29166  "get_isoline3d(): Instance is not a 2d image.",
29167  cimg_instance);
29168  primitives.assign();
29169  if (is_empty()) return *this;
29170  CImg<floatT> vertices;
29171  if ((size_x==-100 && size_y==-100) || (size_x==width() && size_y==height())) {
29172  const _functor2d_int func(*this);
29173  vertices = isoline3d(primitives,func,isovalue,0,0,width()-1.0f,height()-1.0f,width(),height());
29174  } else {
29175  const _functor2d_float func(*this);
29176  vertices = isoline3d(primitives,func,isovalue,0,0,width()-1.0f,height()-1.0f,size_x,size_y);
29177  }
29178  return vertices;
29179  }
29180 
29182 
29199  template<typename tf>
29200  CImg<floatT> get_isosurface3d(CImgList<tf>& primitives, const float isovalue,
29201  const int size_x=-100, const int size_y=-100, const int size_z=-100) const {
29202  if (_spectrum>1)
29203  throw CImgInstanceException(_cimg_instance
29204  "get_isosurface3d(): Instance is not a scalar image.",
29205  cimg_instance);
29206  primitives.assign();
29207  if (is_empty()) return *this;
29208  CImg<floatT> vertices;
29209  if ((size_x==-100 && size_y==-100 && size_z==-100) || (size_x==width() && size_y==height() && size_z==depth())) {
29210  const _functor3d_int func(*this);
29211  vertices = isosurface3d(primitives,func,isovalue,0,0,0,width()-1.0f,height()-1.0f,depth()-1.0f,
29212  width(),height(),depth());
29213  } else {
29214  const _functor3d_float func(*this);
29215  vertices = isosurface3d(primitives,func,isovalue,0,0,0,width()-1.0f,height()-1.0f,depth()-1.0f,
29216  size_x,size_y,size_z);
29217  }
29218  return vertices;
29219  }
29220 
29222 
29232  template<typename tf, typename tfunc>
29233  static CImg<floatT> elevation3d(CImgList<tf>& primitives, const tfunc& func,
29234  const float x0, const float y0, const float x1, const float y1,
29235  const int size_x=256, const int size_y=256) {
29236  const float
29237  nx0 = x0<x1?x0:x1, ny0 = y0<y1?y0:y1,
29238  nx1 = x0<x1?x1:x0, ny1 = y0<y1?y1:y0;
29239  const unsigned int
29240  _nsize_x = (unsigned int)(size_x>=0?size_x:(nx1-nx0)*-size_x/100),
29241  nsize_x = _nsize_x?_nsize_x:1, nsize_x1 = nsize_x - 1,
29242  _nsize_y = (unsigned int)(size_y>=0?size_y:(ny1-ny0)*-size_y/100),
29243  nsize_y = _nsize_y?_nsize_y:1, nsize_y1 = nsize_y - 1;
29244  if (nsize_x<2 || nsize_y<2)
29245  throw CImgArgumentException("CImg<%s>::elevation3d(): Invalid specified size (%d,%d).",
29246  pixel_type(),
29247  nsize_x,nsize_y);
29248 
29249  CImg<floatT> vertices(nsize_x*nsize_y,3);
29250  floatT *ptr_x = vertices.data(0,0), *ptr_y = vertices.data(0,1), *ptr_z = vertices.data(0,2);
29251  for (unsigned int y = 0; y<nsize_y; ++y) {
29252  const float Y = ny0 + y*(ny1-ny0)/nsize_y1;
29253  for (unsigned int x = 0; x<nsize_x; ++x) {
29254  const float X = nx0 + x*(nx1-nx0)/nsize_x1;
29255  *(ptr_x++) = (float)x;
29256  *(ptr_y++) = (float)y;
29257  *(ptr_z++) = (float)func(X,Y);
29258  }
29259  }
29260  primitives.assign(nsize_x1*nsize_y1,1,4);
29261  for (unsigned int p = 0, y = 0; y<nsize_y1; ++y) {
29262  const unsigned int yw = y*nsize_x;
29263  for (unsigned int x = 0; x<nsize_x1; ++x) {
29264  const unsigned int xpyw = x + yw, xpyww = xpyw + nsize_x;
29265  primitives[p++].fill(xpyw,xpyww,xpyww+1,xpyw+1);
29266  }
29267  }
29268  return vertices;
29269  }
29270 
29272  template<typename tf>
29273  static CImg<floatT> elevation3d(CImgList<tf>& primitives, const char *const expression,
29274  const float x0, const float y0, const float x1, const float y1,
29275  const int size_x=256, const int size_y=256) {
29276  const _functor2d_expr func(expression);
29277  return elevation3d(primitives,func,x0,y0,x1,y1,size_x,size_y);
29278  }
29279 
29281 
29293  template<typename tf, typename tfunc>
29294  static CImg<floatT> isoline3d(CImgList<tf>& primitives, const tfunc& func, const float isovalue,
29295  const float x0, const float y0, const float x1, const float y1,
29296  const int size_x=256, const int size_y=256) {
29297  static const unsigned int edges[16] = { 0x0, 0x9, 0x3, 0xa, 0x6, 0xf, 0x5, 0xc, 0xc,
29298  0x5, 0xf, 0x6, 0xa, 0x3, 0x9, 0x0 };
29299  static const int segments[16][4] = { { -1,-1,-1,-1 }, { 0,3,-1,-1 }, { 0,1,-1,-1 }, { 1,3,-1,-1 },
29300  { 1,2,-1,-1 }, { 0,1,2,3 }, { 0,2,-1,-1 }, { 2,3,-1,-1 },
29301  { 2,3,-1,-1 }, { 0,2,-1,-1}, { 0,3,1,2 }, { 1,2,-1,-1 },
29302  { 1,3,-1,-1 }, { 0,1,-1,-1}, { 0,3,-1,-1}, { -1,-1,-1,-1 } };
29303  const unsigned int
29304  _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)),
29305  _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)),
29306  nx = _nx?_nx:1,
29307  ny = _ny?_ny:1,
29308  nxm1 = nx - 1,
29309  nym1 = ny - 1;
29310  primitives.assign();
29311  if (!nxm1 || !nym1) return CImg<floatT>();
29312  const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1;
29313  CImgList<floatT> vertices;
29314  CImg<intT> indices1(nx,1,1,2,-1), indices2(nx,1,1,2);
29315  CImg<floatT> values1(nx), values2(nx);
29316  float X = x0, Y = y0, nX = X + dx, nY = Y + dy;
29317 
29318  // Fill first line with values
29319  cimg_forX(values1,x) { values1(x) = (float)func(X,Y); X+=dx; }
29320 
29321  // Run the marching squares algorithm
29322  for (unsigned int yi = 0, nyi = 1; yi<nym1; ++yi, ++nyi, Y=nY, nY+=dy) {
29323  X = x0; nX = X + dx;
29324  indices2.fill(-1);
29325  for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X=nX, nX+=dx) {
29326 
29327  // Determine square configuration
29328  const float
29329  val0 = values1(xi),
29330  val1 = values1(nxi),
29331  val2 = values2(nxi) = (float)func(nX,nY),
29332  val3 = values2(xi) = (float)func(X,nY);
29333  const unsigned int
29334  configuration = (val0<isovalue?1:0) | (val1<isovalue?2:0) | (val2<isovalue?4:0) | (val3<isovalue?8:0),
29335  edge = edges[configuration];
29336 
29337  // Compute intersection vertices
29338  if (edge) {
29339  if ((edge&1) && indices1(xi,0)<0) {
29340  const float Xi = X + (isovalue-val0)*dx/(val1-val0);
29341  indices1(xi,0) = vertices._width;
29342  CImg<floatT>::vector(Xi,Y,0).move_to(vertices);
29343  }
29344  if ((edge&2) && indices1(nxi,1)<0) {
29345  const float Yi = Y + (isovalue-val1)*dy/(val2-val1);
29346  indices1(nxi,1) = vertices._width;
29347  CImg<floatT>::vector(nX,Yi,0).move_to(vertices);
29348  }
29349  if ((edge&4) && indices2(xi,0)<0) {
29350  const float Xi = X + (isovalue-val3)*dx/(val2-val3);
29351  indices2(xi,0) = vertices._width;
29352  CImg<floatT>::vector(Xi,nY,0).move_to(vertices);
29353  }
29354  if ((edge&8) && indices1(xi,1)<0) {
29355  const float Yi = Y + (isovalue-val0)*dy/(val3-val0);
29356  indices1(xi,1) = vertices._width;
29357  CImg<floatT>::vector(X,Yi,0).move_to(vertices);
29358  }
29359 
29360  // Create segments
29361  for (const int *segment = segments[configuration]; *segment!=-1; ) {
29362  const unsigned int p0 = *(segment++), p1 = *(segment++);
29363  const tf
29364  i0 = (tf)(_isoline3d_indice(p0,indices1,indices2,xi,nxi)),
29365  i1 = (tf)(_isoline3d_indice(p1,indices1,indices2,xi,nxi));
29366  CImg<tf>::vector(i0,i1).move_to(primitives);
29367  }
29368  }
29369  }
29370  values1.swap(values2);
29371  indices1.swap(indices2);
29372  }
29373  return vertices>'x';
29374  }
29375 
29377  template<typename tf>
29378  static CImg<floatT> isoline3d(CImgList<tf>& primitives, const char *const expression, const float isovalue,
29379  const float x0, const float y0, const float x1, const float y1,
29380  const int size_x=256, const int size_y=256) {
29381  const _functor2d_expr func(expression);
29382  return isoline3d(primitives,func,isovalue,x0,y0,x1,y1,size_x,size_y);
29383  }
29384 
29385  template<typename t>
29386  static int _isoline3d_indice(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& indices2,
29387  const unsigned int x, const unsigned int nx) {
29388  switch (edge) {
29389  case 0 : return (int)indices1(x,0);
29390  case 1 : return (int)indices1(nx,1);
29391  case 2 : return (int)indices2(x,0);
29392  case 3 : return (int)indices1(x,1);
29393  }
29394  return 0;
29395  }
29396 
29398 
29413  template<typename tf, typename tfunc>
29414  static CImg<floatT> isosurface3d(CImgList<tf>& primitives, const tfunc& func, const float isovalue,
29415  const float x0, const float y0, const float z0,
29416  const float x1, const float y1, const float z1,
29417  const int size_x=32, const int size_y=32, const int size_z=32) {
29418  static const unsigned int edges[256] = {
29419  0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
29420  0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
29421  0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
29422  0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
29423  0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
29424  0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
29425  0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
29426  0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
29427  0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
29428  0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
29429  0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
29430  0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460,
29431  0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0,
29432  0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230,
29433  0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190,
29434  0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000
29435  };
29436 
29437  static const int triangles[256][16] = {
29438  { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29439  { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29440  { 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29441  { 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29442  { 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29443  { 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29444  { 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29445  { 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1 },
29446  { 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29447  { 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29448  { 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29449  { 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1 },
29450  { 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29451  { 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1 },
29452  { 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1 },
29453  { 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29454  { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29455  { 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29456  { 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29457  { 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1 },
29458  { 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29459  { 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1 },
29460  { 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 },
29461  { 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1 },
29462  { 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29463  { 11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1 },
29464  { 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 },
29465  { 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1 },
29466  { 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1 },
29467  { 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1 },
29468  { 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1 },
29469  { 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1 },
29470  { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29471  { 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29472  { 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29473  { 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1 },
29474  { 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29475  { 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 },
29476  { 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1 },
29477  { 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1 },
29478  { 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29479  { 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 },
29480  { 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 },
29481  { 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1 },
29482  { 10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1 },
29483  { 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1 },
29484  { 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1 },
29485  { 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1 },
29486  { 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29487  { 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1 },
29488  { 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1 },
29489  { 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29490  { 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1 },
29491  { 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1 },
29492  { 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1 },
29493  { 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1 },
29494  { 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1 },
29495  { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1 },
29496  { 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1 },
29497  { 11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1 },
29498  { 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1 },
29499  { 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1 },
29500  { 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1 },
29501  { 11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29502  { 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29503  { 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29504  { 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29505  { 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 },
29506  { 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29507  { 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1 },
29508  { 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1 },
29509  { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1 },
29510  { 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29511  { 11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 },
29512  { 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 },
29513  { 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1 },
29514  { 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1 },
29515  { 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1 },
29516  { 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1 },
29517  { 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1 },
29518  { 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29519  { 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1 },
29520  { 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 },
29521  { 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1 },
29522  { 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1 },
29523  { 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1 },
29524  { 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1 },
29525  { 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1 },
29526  { 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 },
29527  { 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1 },
29528  { 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1 },
29529  { 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1 },
29530  { 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1 },
29531  { 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1 },
29532  { 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1 },
29533  { 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1 },
29534  { 10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29535  { 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1 },
29536  { 10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1 },
29537  { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1 },
29538  { 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1 },
29539  { 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1 },
29540  { 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29541  { 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1 },
29542  { 10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1 },
29543  { 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1 },
29544  { 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1 },
29545  { 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1 },
29546  { 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1 },
29547  { 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1 },
29548  { 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1 },
29549  { 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29550  { 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1 },
29551  { 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1 },
29552  { 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1 },
29553  { 10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1 },
29554  { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1 },
29555  { 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1 },
29556  { 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1 },
29557  { 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29558  { 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1 },
29559  { 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1 },
29560  { 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1 },
29561  { 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1 },
29562  { 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1 },
29563  { 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29564  { 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1 },
29565  { 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29566  { 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29567  { 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29568  { 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29569  { 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 },
29570  { 10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29571  { 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 },
29572  { 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 },
29573  { 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1 },
29574  { 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29575  { 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1 },
29576  { 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1 },
29577  { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1 },
29578  { 10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1 },
29579  { 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1 },
29580  { 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1 },
29581  { 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1 },
29582  { 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29583  { 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1 },
29584  { 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1 },
29585  { 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1 },
29586  { 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1 },
29587  { 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1 },
29588  { 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1 },
29589  { 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1 },
29590  { 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1 },
29591  { 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29592  { 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1 },
29593  { 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1 },
29594  { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1 },
29595  { 10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1 },
29596  { 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1 },
29597  { 10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29598  { 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29599  { 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 },
29600  { 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 },
29601  { 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1 },
29602  { 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 },
29603  { 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1 },
29604  { 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1 },
29605  { 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1 },
29606  { 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1 },
29607  { 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1 },
29608  { 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1 },
29609  { 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1 },
29610  { 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1 },
29611  { 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1 },
29612  { 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1 },
29613  { 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1 },
29614  { 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1 },
29615  { 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1 },
29616  { 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1 },
29617  { 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1 },
29618  { 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1 },
29619  { 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1 },
29620  { 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1 },
29621  { 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1 },
29622  { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1 },
29623  { 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1 },
29624  { 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1 },
29625  { 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29626  { 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1 },
29627  { 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1 },
29628  { 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29629  { 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29630  { 11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29631  { 11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1 },
29632  { 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1 },
29633  { 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1 },
29634  { 11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1 },
29635  { 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1 },
29636  { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1 },
29637  { 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1 },
29638  { 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1 },
29639  { 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1 },
29640  { 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1 },
29641  { 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1 },
29642  { 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29643  { 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1 },
29644  { 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1 },
29645  { 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29646  { 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1 },
29647  { 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1 },
29648  { 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1 },
29649  { 10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1 },
29650  { 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1 },
29651  { 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1 },
29652  { 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1 },
29653  { 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29654  { 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1 },
29655  { 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1 },
29656  { 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1 },
29657  { 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1 },
29658  { 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1 },
29659  { 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29660  { 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1 },
29661  { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29662  { 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1 },
29663  { 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1 },
29664  { 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1 },
29665  { 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1 },
29666  { 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1 },
29667  { 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1 },
29668  { 11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1 },
29669  { 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1 },
29670  { 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1 },
29671  { 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1 },
29672  { 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1 },
29673  { 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29674  { 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1 },
29675  { 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1 },
29676  { 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29677  { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29678  { 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29679  { 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1 },
29680  { 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1 },
29681  { 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29682  { 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1 },
29683  { 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1 },
29684  { 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29685  { 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29686  { 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1 },
29687  { 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29688  { 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1 },
29689  { 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29690  { 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29691  { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29692  { 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
29693  { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }
29694  };
29695 
29696  const unsigned int
29697  _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)),
29698  _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)),
29699  _nz = (unsigned int)(size_z>=0?size_z:cimg::round((z1-z0)*-size_z/100 + 1)),
29700  nx = _nx?_nx:1,
29701  ny = _ny?_ny:1,
29702  nz = _nz?_nz:1,
29703  nxm1 = nx - 1,
29704  nym1 = ny - 1,
29705  nzm1 = nz - 1;
29706  primitives.assign();
29707  if (!nxm1 || !nym1 || !nzm1) return CImg<floatT>();
29708  const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1, dz = (z1 - z0)/nzm1;
29709  CImgList<floatT> vertices;
29710  CImg<intT> indices1(nx,ny,1,3,-1), indices2(indices1);
29711  CImg<floatT> values1(nx,ny), values2(nx,ny);
29712  float X = 0, Y = 0, Z = 0, nX = 0, nY = 0, nZ = 0;
29713 
29714  // Fill the first plane with function values
29715  Y = y0;
29716  cimg_forY(values1,y) {
29717  X = x0;
29718  cimg_forX(values1,x) { values1(x,y) = (float)func(X,Y,z0); X+=dx; }
29719  Y+=dy;
29720  }
29721 
29722  // Run Marching Cubes algorithm
29723  Z = z0; nZ = Z + dz;
29724  for (unsigned int zi = 0; zi<nzm1; ++zi, Z = nZ, nZ+=dz) {
29725  Y = y0; nY = Y + dy;
29726  indices2.fill(-1);
29727  for (unsigned int yi = 0, nyi = 1; yi<nym1; ++yi, ++nyi, Y = nY, nY+=dy) {
29728  X = x0; nX = X + dx;
29729  for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X = nX, nX+=dx) {
29730 
29731  // Determine cube configuration
29732  const float
29733  val0 = values1(xi,yi),
29734  val1 = values1(nxi,yi),
29735  val2 = values1(nxi,nyi),
29736  val3 = values1(xi,nyi),
29737  val4 = values2(xi,yi) = (float)func(X,Y,nZ),
29738  val5 = values2(nxi,yi) = (float)func(nX,Y,nZ),
29739  val6 = values2(nxi,nyi) = (float)func(nX,nY,nZ),
29740  val7 = values2(xi,nyi) = (float)func(X,nY,nZ);
29741 
29742  const unsigned int configuration =
29743  (val0<isovalue?1:0) | (val1<isovalue?2:0) | (val2<isovalue?4:0) | (val3<isovalue?8:0) |
29744  (val4<isovalue?16:0) | (val5<isovalue?32:0) | (val6<isovalue?64:0) | (val7<isovalue?128:0),
29745  edge = edges[configuration];
29746 
29747  // Compute intersection vertices
29748  if (edge) {
29749  if ((edge&1) && indices1(xi,yi,0)<0) {
29750  const float Xi = X + (isovalue-val0)*dx/(val1-val0);
29751  indices1(xi,yi,0) = vertices._width;
29752  CImg<floatT>::vector(Xi,Y,Z).move_to(vertices);
29753  }
29754  if ((edge&2) && indices1(nxi,yi,1)<0) {
29755  const float Yi = Y + (isovalue-val1)*dy/(val2-val1);
29756  indices1(nxi,yi,1) = vertices._width;
29757  CImg<floatT>::vector(nX,Yi,Z).move_to(vertices);
29758  }
29759  if ((edge&4) && indices1(xi,nyi,0)<0) {
29760  const float Xi = X + (isovalue-val3)*dx/(val2-val3);
29761  indices1(xi,nyi,0) = vertices._width;
29762  CImg<floatT>::vector(Xi,nY,Z).move_to(vertices);
29763  }
29764  if ((edge&8) && indices1(xi,yi,1)<0) {
29765  const float Yi = Y + (isovalue-val0)*dy/(val3-val0);
29766  indices1(xi,yi,1) = vertices._width;
29767  CImg<floatT>::vector(X,Yi,Z).move_to(vertices);
29768  }
29769  if ((edge&16) && indices2(xi,yi,0)<0) {
29770  const float Xi = X + (isovalue-val4)*dx/(val5-val4);
29771  indices2(xi,yi,0) = vertices._width;
29772  CImg<floatT>::vector(Xi,Y,nZ).move_to(vertices);
29773  }
29774  if ((edge&32) && indices2(nxi,yi,1)<0) {
29775  const float Yi = Y + (isovalue-val5)*dy/(val6-val5);
29776  indices2(nxi,yi,1) = vertices._width;
29777  CImg<floatT>::vector(nX,Yi,nZ).move_to(vertices);
29778  }
29779  if ((edge&64) && indices2(xi,nyi,0)<0) {
29780  const float Xi = X + (isovalue-val7)*dx/(val6-val7);
29781  indices2(xi,nyi,0) = vertices._width;
29782  CImg<floatT>::vector(Xi,nY,nZ).move_to(vertices);
29783  }
29784  if ((edge&128) && indices2(xi,yi,1)<0) {
29785  const float Yi = Y + (isovalue-val4)*dy/(val7-val4);
29786  indices2(xi,yi,1) = vertices._width;
29787  CImg<floatT>::vector(X,Yi,nZ).move_to(vertices);
29788  }
29789  if ((edge&256) && indices1(xi,yi,2)<0) {
29790  const float Zi = Z+ (isovalue-val0)*dz/(val4-val0);
29791  indices1(xi,yi,2) = vertices._width;
29792  CImg<floatT>::vector(X,Y,Zi).move_to(vertices);
29793  }
29794  if ((edge&512) && indices1(nxi,yi,2)<0) {
29795  const float Zi = Z + (isovalue-val1)*dz/(val5-val1);
29796  indices1(nxi,yi,2) = vertices._width;
29797  CImg<floatT>::vector(nX,Y,Zi).move_to(vertices);
29798  }
29799  if ((edge&1024) && indices1(nxi,nyi,2)<0) {
29800  const float Zi = Z + (isovalue-val2)*dz/(val6-val2);
29801  indices1(nxi,nyi,2) = vertices._width;
29802  CImg<floatT>::vector(nX,nY,Zi).move_to(vertices);
29803  }
29804  if ((edge&2048) && indices1(xi,nyi,2)<0) {
29805  const float Zi = Z + (isovalue-val3)*dz/(val7-val3);
29806  indices1(xi,nyi,2) = vertices._width;
29807  CImg<floatT>::vector(X,nY,Zi).move_to(vertices);
29808  }
29809 
29810  // Create triangles
29811  for (const int *triangle = triangles[configuration]; *triangle!=-1; ) {
29812  const unsigned int p0 = *(triangle++), p1 = *(triangle++), p2 = *(triangle++);
29813  const tf
29814  i0 = (tf)(_isosurface3d_indice(p0,indices1,indices2,xi,yi,nxi,nyi)),
29815  i1 = (tf)(_isosurface3d_indice(p1,indices1,indices2,xi,yi,nxi,nyi)),
29816  i2 = (tf)(_isosurface3d_indice(p2,indices1,indices2,xi,yi,nxi,nyi));
29817  CImg<tf>::vector(i0,i2,i1).move_to(primitives);
29818  }
29819  }
29820  }
29821  }
29822  cimg::swap(values1,values2);
29823  cimg::swap(indices1,indices2);
29824  }
29825  return vertices>'x';
29826  }
29827 
29829  template<typename tf>
29830  static CImg<floatT> isosurface3d(CImgList<tf>& primitives, const char *const expression, const float isovalue,
29831  const float x0, const float y0, const float z0,
29832  const float x1, const float y1, const float z1,
29833  const int dx=32, const int dy=32, const int dz=32) {
29834  const _functor3d_expr func(expression);
29835  return isosurface3d(primitives,func,isovalue,x0,y0,z0,x1,y1,z1,dx,dy,dz);
29836  }
29837 
29838  template<typename t>
29839  static int _isosurface3d_indice(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& indices2,
29840  const unsigned int x, const unsigned int y,
29841  const unsigned int nx, const unsigned int ny) {
29842  switch (edge) {
29843  case 0 : return indices1(x,y,0);
29844  case 1 : return indices1(nx,y,1);
29845  case 2 : return indices1(x,ny,0);
29846  case 3 : return indices1(x,y,1);
29847  case 4 : return indices2(x,y,0);
29848  case 5 : return indices2(nx,y,1);
29849  case 6 : return indices2(x,ny,0);
29850  case 7 : return indices2(x,y,1);
29851  case 8 : return indices1(x,y,2);
29852  case 9 : return indices1(nx,y,2);
29853  case 10 : return indices1(nx,ny,2);
29854  case 11 : return indices1(x,ny,2);
29855  }
29856  return 0;
29857  }
29858 
29859  // Define functors for accessing image values (used in previous functions).
29861  const CImg<T>& ref;
29862  _functor2d_int(const CImg<T>& pref):ref(pref) {}
29863  float operator()(const float x, const float y) const {
29864  return (float)ref((int)x,(int)y);
29865  }
29866  };
29867 
29869  const CImg<T>& ref;
29870  _functor2d_float(const CImg<T>& pref):ref(pref) {}
29871  float operator()(const float x, const float y) const {
29872  return (float)ref._linear_atXY(x,y);
29873  }
29874  };
29875 
29877  _cimg_math_parser *mp;
29878  _functor2d_expr(const char *const expr):mp(0) { mp = new _cimg_math_parser(CImg<T>::empty(),expr,0); }
29879  ~_functor2d_expr() { delete mp; }
29880  float operator()(const float x, const float y) const {
29881  return (float)(*mp)(x,y,0,0);
29882  }
29883  };
29884 
29886  const CImg<T>& ref;
29887  _functor3d_int(const CImg<T>& pref):ref(pref) {}
29888  float operator()(const float x, const float y, const float z) const {
29889  return (float)ref((int)x,(int)y,(int)z);
29890  }
29891  };
29892 
29894  const CImg<T>& ref;
29895  _functor3d_float(const CImg<T>& pref):ref(pref) {}
29896  float operator()(const float x, const float y, const float z) const {
29897  return (float)ref._linear_atXYZ(x,y,z);
29898  }
29899  };
29900 
29902  _cimg_math_parser *mp;
29903  ~_functor3d_expr() { delete mp; }
29904  _functor3d_expr(const char *const expr):mp(0) { mp = new _cimg_math_parser(CImg<T>::empty(),expr,0); }
29905  float operator()(const float x, const float y, const float z) const {
29906  return (float)(*mp)(x,y,z,0);
29907  }
29908  };
29909 
29911  const CImg<T>& ref;
29912  _functor4d_int(const CImg<T>& pref):ref(pref) {}
29913  float operator()(const float x, const float y, const float z, const unsigned int c) const {
29914  return (float)ref((int)x,(int)y,(int)z,c);
29915  }
29916  };
29917 
29919 
29934  template<typename tf>
29935  static CImg<floatT> box3d(CImgList<tf>& primitives,
29936  const float size_x=200, const float size_y=100, const float size_z=100) {
29937  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);
29938  return CImg<floatT>(8,3,1,1,
29939  0.,size_x,size_x, 0., 0.,size_x,size_x, 0.,
29940  0., 0.,size_y,size_y, 0., 0.,size_y,size_y,
29941  0., 0., 0., 0.,size_z,size_z,size_z,size_z);
29942  }
29943 
29945 
29960  template<typename tf>
29961  static CImg<floatT> cone3d(CImgList<tf>& primitives,
29962  const float radius=50, const float size_z=100, const unsigned int subdivisions=24) {
29963  primitives.assign();
29964  if (!subdivisions) return CImg<floatT>();
29965  CImgList<floatT> vertices(2,1,3,1,1,
29966  0.,0.,size_z,
29967  0.,0.,0.);
29968  for (float delta = 360.0f/subdivisions, angle = 0; angle<360; angle+=delta) {
29969  const float a = (float)(angle*cimg::PI/180);
29970  CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0).move_to(vertices);
29971  }
29972  const unsigned int nbr = vertices._width - 2;
29973  for (unsigned int p = 0; p<nbr; ++p) {
29974  const unsigned int curr = 2 + p, next = 2 + ((p+1)%nbr);
29975  CImg<tf>::vector(1,next,curr).move_to(primitives);
29976  CImg<tf>::vector(0,curr,next).move_to(primitives);
29977  }
29978  return vertices>'x';
29979  }
29980 
29982 
29997  template<typename tf>
29999  const float radius=50, const float size_z=100, const unsigned int subdivisions=24) {
30000  primitives.assign();
30001  if (!subdivisions) return CImg<floatT>();
30002  CImgList<floatT> vertices(2,1,3,1,1,
30003  0.,0.,0.,
30004  0.,0.,size_z);
30005  for (float delta = 360.0f/subdivisions, angle = 0; angle<360; angle+=delta) {
30006  const float a = (float)(angle*cimg::PI/180);
30007  CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0.0f).move_to(vertices);
30008  CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),size_z).move_to(vertices);
30009  }
30010  const unsigned int nbr = (vertices._width - 2)/2;
30011  for (unsigned int p = 0; p<nbr; ++p) {
30012  const unsigned int curr = 2+2*p, next = 2+(2*((p+1)%nbr));
30013  CImg<tf>::vector(0,next,curr).move_to(primitives);
30014  CImg<tf>::vector(1,curr+1,next+1).move_to(primitives);
30015  CImg<tf>::vector(curr,next,next+1,curr+1).move_to(primitives);
30016  }
30017  return vertices>'x';
30018  }
30019 
30021 
30037  template<typename tf>
30038  static CImg<floatT> torus3d(CImgList<tf>& primitives,
30039  const float radius1=100, const float radius2=30,
30040  const unsigned int subdivisions1=24, const unsigned int subdivisions2=12) {
30041  primitives.assign();
30042  if (!subdivisions1 || !subdivisions2) return CImg<floatT>();
30043  CImgList<floatT> vertices;
30044  for (unsigned int v = 0; v<subdivisions1; ++v) {
30045  const float
30046  beta = (float)(v*2*cimg::PI/subdivisions1),
30047  xc = radius1*(float)std::cos(beta),
30048  yc = radius1*(float)std::sin(beta);
30049  for (unsigned int u = 0; u<subdivisions2; ++u) {
30050  const float
30051  alpha = (float)(u*2*cimg::PI/subdivisions2),
30052  x = xc + radius2*(float)(std::cos(alpha)*std::cos(beta)),
30053  y = yc + radius2*(float)(std::cos(alpha)*std::sin(beta)),
30054  z = radius2*(float)std::sin(alpha);
30055  CImg<floatT>::vector(x,y,z).move_to(vertices);
30056  }
30057  }
30058  for (unsigned int vv = 0; vv<subdivisions1; ++vv) {
30059  const unsigned int nv = (vv+1)%subdivisions1;
30060  for (unsigned int uu = 0; uu<subdivisions2; ++uu) {
30061  const unsigned int nu = (uu+1)%subdivisions2, svv = subdivisions2*vv, snv = subdivisions2*nv;
30062  CImg<tf>::vector(svv+nu,svv+uu,snv+uu,snv+nu).move_to(primitives);
30063  }
30064  }
30065  return vertices>'x';
30066  }
30067 
30069 
30085  template<typename tf>
30086  static CImg<floatT> plane3d(CImgList<tf>& primitives,
30087  const float size_x=100, const float size_y=100,
30088  const unsigned int subdivisions_x=10, const unsigned int subdivisions_y=10) {
30089  primitives.assign();
30090  if (!subdivisions_x || !subdivisions_y) return CImg<floatT>();
30091  CImgList<floatT> vertices;
30092  const unsigned int w = subdivisions_x + 1, h = subdivisions_y + 1;
30093  const float fx = (float)size_x/w, fy = (float)size_y/h;
30094  for (unsigned int y = 0; y<h; ++y) for (unsigned int x = 0; x<w; ++x)
30095  CImg<floatT>::vector(fx*x,fy*y,0).move_to(vertices);
30096  for (unsigned int y = 0; y<subdivisions_y; ++y) for (unsigned int x = 0; x<subdivisions_x; ++x) {
30097  const int off1 = x+y*w, off2 = x+1+y*w, off3 = x+1+(y+1)*w, off4 = x+(y+1)*w;
30098  CImg<tf>::vector(off1,off4,off3,off2).move_to(primitives);
30099  }
30100  return vertices>'x';
30101  }
30102 
30104 
30118  template<typename tf>
30119  static CImg<floatT> sphere3d(CImgList<tf>& primitives,
30120  const float radius=50, const unsigned int subdivisions=3) {
30121 
30122  // Create initial icosahedron
30123  primitives.assign();
30124  const double tmp = (1+std::sqrt(5.0f))/2, a = 1.0/std::sqrt(1+tmp*tmp), b = tmp*a;
30125  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,
30126  -a,0.0,-b, -a,0.0,b, 0.0,b,a, 0.0,-b,a, 0.0,-b,-a, 0.0,b,-a);
30127  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,
30128  8,0,11, 8,11,1, 9,10,3, 9,2,10, 8,4,0, 11,0,5, 4,9,3,
30129  5,3,10, 7,8,1, 6,1,11, 7,2,9, 6,10,2);
30130  // edge - length/2
30131  float he = (float)a;
30132 
30133  // Recurse subdivisions
30134  for (unsigned int i = 0; i<subdivisions; ++i) {
30135  const unsigned int L = primitives._width;
30136  he/=2;
30137  const float he2 = he*he;
30138  for (unsigned int l = 0; l<L; ++l) {
30139  const unsigned int
30140  p0 = (unsigned int)primitives(0,0), p1 = (unsigned int)primitives(0,1), p2 = (unsigned int)primitives(0,2);
30141  const float
30142  x0 = vertices(p0,0), y0 = vertices(p0,1), z0 = vertices(p0,2),
30143  x1 = vertices(p1,0), y1 = vertices(p1,1), z1 = vertices(p1,2),
30144  x2 = vertices(p2,0), y2 = vertices(p2,1), z2 = vertices(p2,2),
30145  tnx0 = (x0+x1)/2, tny0 = (y0+y1)/2, tnz0 = (z0+z1)/2, nn0 = (float)std::sqrt(tnx0*tnx0+tny0*tny0+tnz0*tnz0),
30146  tnx1 = (x0+x2)/2, tny1 = (y0+y2)/2, tnz1 = (z0+z2)/2, nn1 = (float)std::sqrt(tnx1*tnx1+tny1*tny1+tnz1*tnz1),
30147  tnx2 = (x1+x2)/2, tny2 = (y1+y2)/2, tnz2 = (z1+z2)/2, nn2 = (float)std::sqrt(tnx2*tnx2+tny2*tny2+tnz2*tnz2),
30148  nx0 = tnx0/nn0, ny0 = tny0/nn0, nz0 = tnz0/nn0,
30149  nx1 = tnx1/nn1, ny1 = tny1/nn1, nz1 = tnz1/nn1,
30150  nx2 = tnx2/nn2, ny2 = tny2/nn2, nz2 = tnz2/nn2;
30151  int i0 = -1, i1 = -1, i2 = -1;
30152  cimglist_for(vertices,p) {
30153  const float x = (float)vertices(p,0), y = (float)vertices(p,1), z = (float)vertices(p,2);
30154  if (cimg::sqr(x-nx0) + cimg::sqr(y-ny0) + cimg::sqr(z-nz0)<he2) i0 = p;
30155  if (cimg::sqr(x-nx1) + cimg::sqr(y-ny1) + cimg::sqr(z-nz1)<he2) i1 = p;
30156  if (cimg::sqr(x-nx2) + cimg::sqr(y-ny2) + cimg::sqr(z-nz2)<he2) i2 = p;
30157  }
30158  if (i0<0) { CImg<floatT>::vector(nx0,ny0,nz0).move_to(vertices); i0 = vertices._width - 1; }
30159  if (i1<0) { CImg<floatT>::vector(nx1,ny1,nz1).move_to(vertices); i1 = vertices._width - 1; }
30160  if (i2<0) { CImg<floatT>::vector(nx2,ny2,nz2).move_to(vertices); i2 = vertices._width - 1; }
30161  primitives.remove(0);
30162  CImg<tf>::vector(p0,i0,i1).move_to(primitives);
30163  CImg<tf>::vector((tf)i0,(tf)p1,(tf)i2).move_to(primitives);
30164  CImg<tf>::vector((tf)i1,(tf)i2,(tf)p2).move_to(primitives);
30165  CImg<tf>::vector((tf)i1,(tf)i0,(tf)i2).move_to(primitives);
30166  }
30167  }
30168  return (vertices>'x')*=radius;
30169  }
30170 
30172 
30187  template<typename tf, typename t>
30189  const CImg<t>& tensor, const unsigned int subdivisions=3) {
30190  primitives.assign();
30191  if (!subdivisions) return CImg<floatT>();
30192  CImg<floatT> S, V;
30193  tensor.symmetric_eigen(S,V);
30194  const float orient =
30195  (V(0,1)*V(1,2) - V(0,2)*V(1,1))*V(2,0) +
30196  (V(0,2)*V(1,0) - V(0,0)*V(1,2))*V(2,1) +
30197  (V(0,0)*V(1,1) - V(0,1)*V(1,0))*V(2,2);
30198  if (orient<0) { V(2,0) = -V(2,0); V(2,1) = -V(2,1); V(2,2) = -V(2,2); }
30199  const float l0 = S[0], l1 = S[1], l2 = S[2];
30200  CImg<floatT> vertices = sphere3d(primitives,1.0,subdivisions);
30201  vertices.get_shared_row(0)*=l0;
30202  vertices.get_shared_row(1)*=l1;
30203  vertices.get_shared_row(2)*=l2;
30204  return V*vertices;
30205  }
30206 
30208 
30214  template<typename tp, typename tc, typename to>
30216  const CImgList<tc>& colors,
30217  const to& opacities,
30218  const bool full_check=true) {
30219  return get_object3dtoCImg3d(primitives,colors,opacities,full_check).move_to(*this);
30220  }
30221 
30223  template<typename tp, typename tc>
30225  const CImgList<tc>& colors,
30226  const bool full_check=true) {
30227  return get_object3dtoCImg3d(primitives,colors,full_check).move_to(*this);
30228  }
30229 
30231  template<typename tp>
30233  const bool full_check=true) {
30234  return get_object3dtoCImg3d(primitives,full_check).move_to(*this);
30235  }
30236 
30238  CImg<T>& object3dtoCImg3d(const bool full_check=true) {
30239  return get_object3dtoCImg3d(full_check).move_to(*this);
30240  }
30241 
30243  template<typename tp, typename tc, typename to>
30245  const CImgList<tc>& colors,
30246  const to& opacities,
30247  const bool full_check=true) const {
30248  char error_message[1024] = { 0 };
30249  if (!is_object3d(primitives,colors,opacities,full_check,error_message))
30250  throw CImgInstanceException(_cimg_instance
30251  "object3dtoCImg3d(): Invalid specified 3d object (%u,%u) (%s).",
30252  cimg_instance,_width,primitives._width,error_message);
30253  CImg<floatT> res(1,_size_object3dtoCImg3d(primitives,colors,opacities));
30254  float *ptrd = res._data;
30255 
30256  // Put magick number.
30257  *(ptrd++) = 'C' + 0.5f; *(ptrd++) = 'I' + 0.5f; *(ptrd++) = 'm' + 0.5f;
30258  *(ptrd++) = 'g' + 0.5f; *(ptrd++) = '3' + 0.5f; *(ptrd++) = 'd' + 0.5f;
30259 
30260  // Put number of vertices and primitives.
30261  *(ptrd++) = cimg::uint2float(_width);
30262  *(ptrd++) = cimg::uint2float(primitives._width);
30263 
30264  // Put vertex data.
30265  if (is_empty() || !primitives) return res;
30266  const T *ptrx = data(0,0), *ptry = data(0,1), *ptrz = data(0,2);
30267  cimg_forX(*this,p) {
30268  *(ptrd++) = (float)*(ptrx++);
30269  *(ptrd++) = (float)*(ptry++);
30270  *(ptrd++) = (float)*(ptrz++);
30271  }
30272 
30273  // Put primitive data.
30274  cimglist_for(primitives,p) {
30275  *(ptrd++) = (float)primitives[p].size();
30276  const tp *ptrp = primitives[p]._data;
30277  cimg_foroff(primitives[p],i) *(ptrd++) = cimg::uint2float((unsigned int)*(ptrp++));
30278  }
30279 
30280  // Put color/texture data.
30281  const unsigned int csiz = cimg::min(colors._width,primitives._width);
30282  for (int c = 0; c<(int)csiz; ++c) {
30283  const CImg<tc>& color = colors[c];
30284  const tc *ptrc = color._data;
30285  if (color.size()==3) { *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*ptrc; }
30286  else {
30287  *(ptrd++) = -128.0f;
30288  int shared_ind = -1;
30289  if (color.is_shared()) for (int i = 0; i<c; ++i) if (ptrc==colors[i]._data) { shared_ind = i; break; }
30290  if (shared_ind<0) {
30291  *(ptrd++) = (float)color._width;
30292  *(ptrd++) = (float)color._height;
30293  *(ptrd++) = (float)color._spectrum;
30294  cimg_foroff(color,l) *(ptrd++) = (float)*(ptrc++);
30295  } else {
30296  *(ptrd++) = (float)shared_ind;
30297  *(ptrd++) = 0;
30298  *(ptrd++) = 0;
30299  }
30300  }
30301  }
30302  const int csiz2 = primitives._width - colors._width;
30303  for (int c = 0; c<csiz2; ++c) { *(ptrd++) = 200.0f; *(ptrd++) = 200.0f; *(ptrd++) = 200.0f; }
30304 
30305  // Put opacity data.
30306  ptrd = _object3dtoCImg3d(opacities,ptrd);
30307  const float *ptre = res.end();
30308  while (ptrd<ptre) *(ptrd++) = 1.0f;
30309  return res;
30310  }
30311 
30312  template<typename to>
30313  float* _object3dtoCImg3d(const CImgList<to>& opacities, float *ptrd) const {
30314  cimglist_for(opacities,o) {
30315  const CImg<to>& opacity = opacities[o];
30316  const to *ptro = opacity._data;
30317  if (opacity.size()==1) *(ptrd++) = (float)*ptro;
30318  else {
30319  *(ptrd++) = -128.0f;
30320  int shared_ind = -1;
30321  if (opacity.is_shared()) for (int i = 0; i<o; ++i) if (ptro==opacities[i]._data) { shared_ind = i; break; }
30322  if (shared_ind<0) {
30323  *(ptrd++) = (float)opacity._width;
30324  *(ptrd++) = (float)opacity._height;
30325  *(ptrd++) = (float)opacity._spectrum;
30326  cimg_foroff(opacity,l) *(ptrd++) = (float)*(ptro++);
30327  } else {
30328  *(ptrd++) = (float)shared_ind;
30329  *(ptrd++) = 0;
30330  *(ptrd++) = 0;
30331  }
30332  }
30333  }
30334  return ptrd;
30335  }
30336 
30337  template<typename to>
30338  float* _object3dtoCImg3d(const CImg<to>& opacities, float *ptrd) const {
30339  const to *ptro = opacities._data;
30340  cimg_foroff(opacities,o) *(ptrd++) = (float)*(ptro++);
30341  return ptrd;
30342  }
30343 
30344  template<typename tp, typename tc, typename to>
30345  unsigned int _size_object3dtoCImg3d(const CImgList<tp>& primitives,
30346  const CImgList<tc>& colors,
30347  const CImgList<to>& opacities) const {
30348  unsigned int siz = 8 + 3*width();
30349  cimglist_for(primitives,p) siz+=primitives[p].size() + 1;
30350  for (int c = cimg::min(primitives._width,colors._width)-1; c>=0; --c) {
30351  if (colors[c].is_shared()) siz+=4;
30352  else { const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4+csiz:3; }
30353  }
30354  if (colors._width<primitives._width) siz+=3*(primitives._width - colors._width);
30355  cimglist_for(opacities,o) {
30356  if (opacities[o].is_shared()) siz+=4;
30357  else { const unsigned int osiz = opacities[o].size(); siz+=(osiz!=1)?4+osiz:1; }
30358  }
30359  siz+=primitives._width - opacities._width;
30360  return siz;
30361  }
30362 
30363  template<typename tp, typename tc, typename to>
30364  unsigned int _size_object3dtoCImg3d(const CImgList<tp>& primitives,
30365  const CImgList<tc>& colors,
30366  const CImg<to>& opacities) const {
30367  unsigned int siz = 8 + 3*width();
30368  cimglist_for(primitives,p) siz+=primitives[p].size() + 1;
30369  for (int c = cimg::min(primitives._width,colors._width)-1; c>=0; --c) {
30370  const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4+csiz:3;
30371  }
30372  if (colors._width<primitives._width) siz+=3*(primitives._width - colors._width);
30373  siz+=primitives.size();
30374  cimg::unused(opacities);
30375  return siz;
30376  }
30377 
30379  template<typename tp, typename tc>
30381  const CImgList<tc>& colors,
30382  const bool full_check=true) const {
30383  CImgList<T> opacities;
30384  return get_object3dtoCImg3d(primitives,colors,opacities,full_check);
30385  }
30386 
30388  template<typename tp>
30390  const bool full_check=true) const {
30391  CImgList<T> colors, opacities;
30392  return get_object3dtoCImg3d(primitives,colors,opacities,full_check);
30393  }
30394 
30396  CImg<floatT> get_object3dtoCImg3d(const bool full_check=true) const {
30397  CImgList<T> opacities, colors;
30398  CImgList<uintT> primitives(width(),1,1,1,1);
30399  cimglist_for(primitives,p) primitives(p,0) = p;
30400  return get_object3dtoCImg3d(primitives,colors,opacities,full_check);
30401  }
30402 
30404 
30410  template<typename tp, typename tc, typename to>
30412  CImgList<tc>& colors,
30413  CImgList<to>& opacities,
30414  const bool full_check=true) {
30415  return get_CImg3dtoobject3d(primitives,colors,opacities,full_check).move_to(*this);
30416  }
30417 
30419  template<typename tp, typename tc, typename to>
30421  CImgList<tc>& colors,
30422  CImgList<to>& opacities,
30423  const bool full_check=true) const {
30424  char error_message[1024] = { 0 };
30425  if (!is_CImg3d(full_check,error_message))
30426  throw CImgInstanceException(_cimg_instance
30427  "CImg3dtoobject3d(): image instance is not a CImg3d (%s).",
30428  cimg_instance,error_message);
30429  const T *ptrs = _data + 6;
30430  const unsigned int
30431  nb_points = cimg::float2uint((float)*(ptrs++)),
30432  nb_primitives = cimg::float2uint((float)*(ptrs++));
30433  const CImg<T> points = CImg<T>(ptrs,3,nb_points,1,1,true).get_transpose();
30434  ptrs+=3*nb_points;
30435  primitives.assign(nb_primitives);
30436  cimglist_for(primitives,p) {
30437  const unsigned int nb_inds = (unsigned int)*(ptrs++);
30438  primitives[p].assign(1,nb_inds);
30439  tp *ptrp = primitives[p]._data;
30440  for (unsigned int i = 0; i<nb_inds; ++i) *(ptrp++) = (tp)cimg::float2uint((float)*(ptrs++));
30441  }
30442  colors.assign(nb_primitives);
30443  cimglist_for(colors,c) {
30444  if (*ptrs==(T)-128) {
30445  ++ptrs;
30446  const unsigned int w = (unsigned int)*(ptrs++), h = (unsigned int)*(ptrs++), s = (unsigned int)*(ptrs++);
30447  if (!h && !s) colors[c].assign(colors[w],true);
30448  else { colors[c].assign(ptrs,w,h,1,s,false); ptrs+=w*h*s; }
30449  } else { colors[c].assign(ptrs,1,1,1,3,false); ptrs+=3; }
30450  }
30451  opacities.assign(nb_primitives);
30452  cimglist_for(opacities,o) {
30453  if (*ptrs==(T)-128) {
30454  ++ptrs;
30455  const unsigned int w = (unsigned int)*(ptrs++), h = (unsigned int)*(ptrs++), s = (unsigned int)*(ptrs++);
30456  if (!h && !s) opacities[o].assign(opacities[w],true);
30457  else { opacities[o].assign(ptrs,w,h,1,s,false); ptrs+=w*h*s; }
30458  } else opacities[o].assign(1,1,1,1,*(ptrs++));
30459  }
30460  return points;
30461  }
30462 
30464  //---------------------------
30465  //
30467 
30468  //---------------------------
30469 
30470 #define cimg_init_scanline(color,opacity) \
30471  const float _sc_nopacity = cimg::abs((float)opacity), _sc_copacity = 1 - cimg::max((float)opacity,0); \
30472  const unsigned long _sc_whd = (unsigned long)_width*_height*_depth
30473 
30474 #define cimg_draw_scanline(x0,x1,y,color,opacity,brightness) \
30475  _draw_scanline(x0,x1,y,color,opacity,brightness,_sc_nopacity,_sc_copacity,_sc_whd)
30476 
30477  // [internal] The following _draw_scanline() routines are *non user-friendly functions*,
30478  // used only for internal purpose.
30479  // Pre-requisites: x0<x1, y-coordinate is valid, col is valid.
30480  template<typename tc>
30481  CImg<T>& _draw_scanline(const int x0, const int x1, const int y,
30482  const tc *const color, const float opacity,
30483  const float brightness,
30484  const float nopacity, const float copacity, const unsigned long whd) {
30485  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
30486  const int nx0 = x0>0?x0:0, nx1 = x1<width()?x1:width()-1, dx = nx1 - nx0;
30487  if (dx>=0) {
30488  const tc *col = color;
30489  const unsigned long off = whd - dx - 1;
30490  T *ptrd = data(nx0,y);
30491  if (opacity>=1) { // ** Opaque drawing **
30492  if (brightness==1) { // Brightness==1
30493  if (sizeof(T)!=1) cimg_forC(*this,c) {
30494  const T val = (T)*(col++);
30495  for (int x = dx; x>=0; --x) *(ptrd++) = val;
30496  ptrd+=off;
30497  } else cimg_forC(*this,c) {
30498  const T val = (T)*(col++);
30499  std::memset(ptrd,(int)val,dx+1);
30500  ptrd+=whd;
30501  }
30502  } else if (brightness<1) { // Brightness<1
30503  if (sizeof(T)!=1) cimg_forC(*this,c) {
30504  const T val = (T)(*(col++)*brightness);
30505  for (int x = dx; x>=0; --x) *(ptrd++) = val;
30506  ptrd+=off;
30507  } else cimg_forC(*this,c) {
30508  const T val = (T)(*(col++)*brightness);
30509  std::memset(ptrd,(int)val,dx+1);
30510  ptrd+=whd;
30511  }
30512  } else { // Brightness>1
30513  if (sizeof(T)!=1) cimg_forC(*this,c) {
30514  const T val = (T)((2-brightness)**(col++) + (brightness-1)*maxval);
30515  for (int x = dx; x>=0; --x) *(ptrd++) = val;
30516  ptrd+=off;
30517  } else cimg_forC(*this,c) {
30518  const T val = (T)((2-brightness)**(col++) + (brightness-1)*maxval);
30519  std::memset(ptrd,(int)val,dx+1);
30520  ptrd+=whd;
30521  }
30522  }
30523  } else { // ** Transparent drawing **
30524  if (brightness==1) { // Brightness==1
30525  cimg_forC(*this,c) {
30526  const T val = (T)*(col++);
30527  for (int x = dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ++ptrd; }
30528  ptrd+=off;
30529  }
30530  } else if (brightness<=1) { // Brightness<1
30531  cimg_forC(*this,c) {
30532  const T val = (T)(*(col++)*brightness);
30533  for (int x = dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ++ptrd; }
30534  ptrd+=off;
30535  }
30536  } else { // Brightness>1
30537  cimg_forC(*this,c) {
30538  const T val = (T)((2-brightness)**(col++) + (brightness-1)*maxval);
30539  for (int x = dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ++ptrd; }
30540  ptrd+=off;
30541  }
30542  }
30543  }
30544  }
30545  return *this;
30546  }
30547 
30549 
30564  template<typename tc>
30565  CImg<T>& draw_point(const int x0, const int y0, const int z0,
30566  const tc *const color, const float opacity=1) {
30567  if (is_empty()) return *this;
30568  if (!color)
30569  throw CImgArgumentException(_cimg_instance
30570  "draw_point(): Specified color is (null).",
30571  cimg_instance);
30572  if (x0>=0 && y0>=0 && z0>=0 && x0<width() && y0<height() && z0<depth()) {
30573  const unsigned long whd = (unsigned long)_width*_height*_depth;
30574  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
30575  T *ptrd = data(x0,y0,z0,0);
30576  const tc *col = color;
30577  if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; }
30578  else cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; }
30579  }
30580  return *this;
30581  }
30582 
30584  template<typename tc>
30585  CImg<T>& draw_point(const int x0, const int y0,
30586  const tc *const color, const float opacity=1) {
30587  return draw_point(x0,y0,0,color,opacity);
30588  }
30589 
30590  // Draw a points cloud.
30596  template<typename t, typename tc>
30597  CImg<T>& draw_point(const CImg<t>& points,
30598  const tc *const color, const float opacity=1) {
30599  if (is_empty() || !points) return *this;
30600  switch (points._height) {
30601  case 0 : case 1 :
30602  throw CImgArgumentException(_cimg_instance
30603  "draw_point(): Invalid specified point set (%u,%u,%u,%u,%p).",
30604  cimg_instance,
30605  points._width,points._height,points._depth,points._spectrum,points._data);
30606  case 2 : {
30607  cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),color,opacity);
30608  } break;
30609  default : {
30610  cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),(int)points(i,2),color,opacity);
30611  }
30612  }
30613  return *this;
30614  }
30615 
30617 
30636  template<typename tc>
30637  CImg<T>& draw_line(const int x0, const int y0,
30638  const int x1, const int y1,
30639  const tc *const color, const float opacity=1,
30640  const unsigned int pattern=~0U, const bool init_hatch=true) {
30641  if (is_empty()) return *this;
30642  if (!color)
30643  throw CImgArgumentException(_cimg_instance
30644  "draw_line(): Specified color is (null).",
30645  cimg_instance);
30646  static unsigned int hatch = ~0U - (~0U>>1);
30647  if (init_hatch) hatch = ~0U - (~0U>>1);
30648  const bool xdir = x0<x1, ydir = y0<y1;
30649  int
30650  nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
30651  &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
30652  &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
30653  &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
30654  &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
30655  if (xright<0 || xleft>=width()) return *this;
30656  if (xleft<0) { yleft-=(int)((float)xleft*((float)yright - yleft)/((float)xright - xleft)); xleft = 0; }
30657  if (xright>=width()) {
30658  yright-=(int)(((float)xright - width())*((float)yright - yleft)/((float)xright - xleft));
30659  xright = width() - 1;
30660  }
30661  if (ydown<0 || yup>=height()) return *this;
30662  if (yup<0) { xup-=(int)((float)yup*((float)xdown - xup)/((float)ydown - yup)); yup = 0; }
30663  if (ydown>=height()) {
30664  xdown-=(int)(((float)ydown - height())*((float)xdown - xup)/((float)ydown - yup));
30665  ydown = height() - 1;
30666  }
30667  T *ptrd0 = data(nx0,ny0);
30668  int dx = xright - xleft, dy = ydown - yup;
30669  const bool steep = dy>dx;
30670  if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
30671  const long
30672  offx = (nx0<nx1?1:-1)*(steep?width():1),
30673  offy = (ny0<ny1?1:-1)*(steep?1:width());
30674  const unsigned long wh = (unsigned long)_width*_height;
30675  if (opacity>=1) {
30676  if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
30677  if (pattern&hatch) {
30678  T *ptrd = ptrd0; const tc* col = color;
30679  cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
30680  }
30681  hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
30682  ptrd0+=offx;
30683  if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
30684  } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
30685  T *ptrd = ptrd0; const tc* col = color; cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
30686  ptrd0+=offx;
30687  if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
30688  }
30689  } else {
30690  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
30691  if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
30692  if (pattern&hatch) {
30693  T *ptrd = ptrd0; const tc* col = color;
30694  cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
30695  }
30696  hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
30697  ptrd0+=offx;
30698  if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
30699  } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
30700  T *ptrd = ptrd0; const tc* col = color;
30701  cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
30702  ptrd0+=offx;
30703  if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
30704  }
30705  }
30706  return *this;
30707  }
30708 
30710 
30723  template<typename tz,typename tc>
30725  const int x0, const int y0, const float z0,
30726  const int x1, const int y1, const float z1,
30727  const tc *const color, const float opacity=1,
30728  const unsigned int pattern=~0U, const bool init_hatch=true) {
30729  typedef typename cimg::superset<tz,float>::type tzfloat;
30730  if (is_empty() || z0<=0 || z1<=0) return *this;
30731  if (!color)
30732  throw CImgArgumentException(_cimg_instance
30733  "draw_line(): Specified color is (null).",
30734  cimg_instance);
30735  if (!is_sameXY(zbuffer))
30736  throw CImgArgumentException(_cimg_instance
30737  "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
30738  "different dimensions.",
30739  cimg_instance,
30740  zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
30741  static unsigned int hatch = ~0U - (~0U>>1);
30742  if (init_hatch) hatch = ~0U - (~0U>>1);
30743  const bool xdir = x0<x1, ydir = y0<y1;
30744  int
30745  nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
30746  &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
30747  &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
30748  &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
30749  &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
30750  tzfloat
30751  Z0 = 1/(tzfloat)z0, Z1 = 1/(tzfloat)z1, nz0 = Z0, nz1 = Z1, dz = Z1 - Z0,
30752  &zleft = xdir?nz0:nz1,
30753  &zright = xdir?nz1:nz0,
30754  &zup = ydir?nz0:nz1,
30755  &zdown = ydir?nz1:nz0;
30756  if (xright<0 || xleft>=width()) return *this;
30757  if (xleft<0) {
30758  const float D = (float)xright - xleft;
30759  yleft-=(int)((float)xleft*((float)yright - yleft)/D);
30760  zleft-=(tzfloat)xleft*(zright - zleft)/D;
30761  xleft = 0;
30762  }
30763  if (xright>=width()) {
30764  const float d = (float)xright - width(), D = (float)xright - xleft;
30765  yright-=(int)(d*((float)yright - yleft)/D);
30766  zright-=(tzfloat)d*(zright - zleft)/D;
30767  xright = width() - 1;
30768  }
30769  if (ydown<0 || yup>=height()) return *this;
30770  if (yup<0) {
30771  const float D = (float)ydown - yup;
30772  xup-=(int)((float)yup*((float)xdown - xup)/D);
30773  zup-=(tzfloat)yup*(zdown - zup)/D;
30774  yup = 0;
30775  }
30776  if (ydown>=height()) {
30777  const float d = (float)ydown - height(), D = (float)ydown - yup;
30778  xdown-=(int)(d*((float)xdown - xup)/D);
30779  zdown-=(tzfloat)d*(zdown - zup)/D;
30780  ydown = height() - 1;
30781  }
30782  T *ptrd0 = data(nx0,ny0);
30783  tz *ptrz = zbuffer.data(nx0,ny0);
30784  int dx = xright - xleft, dy = ydown - yup;
30785  const bool steep = dy>dx;
30786  if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
30787  const long
30788  offx = (nx0<nx1?1:-1)*(steep?width():1),
30789  offy = (ny0<ny1?1:-1)*(steep?1:width());
30790  const unsigned long wh = (unsigned long)_width*_height,
30791  ndx = dx>0?dx:1;
30792  if (opacity>=1) {
30793  if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
30794  const tzfloat z = Z0 + x*dz/ndx;
30795  if (z>=(tzfloat)*ptrz && pattern&hatch) {
30796  *ptrz = (tz)z;
30797  T *ptrd = ptrd0; const tc *col = color;
30798  cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
30799  }
30800  hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
30801  ptrd0+=offx; ptrz+=offx;
30802  if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
30803  } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
30804  const tzfloat z = Z0 + x*dz/ndx;
30805  if (z>=(tzfloat)*ptrz) {
30806  *ptrz = (tz)z;
30807  T *ptrd = ptrd0; const tc *col = color;
30808  cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
30809  }
30810  ptrd0+=offx; ptrz+=offx;
30811  if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
30812  }
30813  } else {
30814  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
30815  if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
30816  const tzfloat z = Z0 + x*dz/ndx;
30817  if (z>=(tzfloat)*ptrz && pattern&hatch) {
30818  *ptrz = (tz)z;
30819  T *ptrd = ptrd0; const tc *col = color;
30820  cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
30821  }
30822  hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
30823  ptrd0+=offx; ptrz+=offx;
30824  if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
30825  } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
30826  const tzfloat z = Z0 + x*dz/ndx;
30827  if (z>=(tzfloat)*ptrz) {
30828  *ptrz = (tz)z;
30829  T *ptrd = ptrd0; const tc *col = color;
30830  cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
30831  }
30832  ptrd0+=offx; ptrz+=offx;
30833  if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
30834  }
30835  }
30836  return *this;
30837  }
30838 
30840 
30852  template<typename tc>
30853  CImg<T>& draw_line(const int x0, const int y0, const int z0,
30854  const int x1, const int y1, const int z1,
30855  const tc *const color, const float opacity=1,
30856  const unsigned int pattern=~0U, const bool init_hatch=true) {
30857  if (is_empty()) return *this;
30858  if (!color)
30859  throw CImgArgumentException(_cimg_instance
30860  "draw_line(): Specified color is (null).",
30861  cimg_instance);
30862  static unsigned int hatch = ~0U - (~0U>>1);
30863  if (init_hatch) hatch = ~0U - (~0U>>1);
30864  int nx0 = x0, ny0 = y0, nz0 = z0, nx1 = x1, ny1 = y1, nz1 = z1;
30865  if (nx0>nx1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
30866  if (nx1<0 || nx0>=width()) return *this;
30867  if (nx0<0) {
30868  const float D = 1.0f + nx1 - nx0;
30869  ny0-=(int)((float)nx0*(1.0f + ny1 - ny0)/D);
30870  nz0-=(int)((float)nx0*(1.0f + nz1 - nz0)/D);
30871  nx0 = 0;
30872  }
30873  if (nx1>=width()) {
30874  const float d = (float)nx1 - width(), D = 1.0f + nx1 - nx0;
30875  ny1+=(int)(d*(1.0f + ny0 - ny1)/D);
30876  nz1+=(int)(d*(1.0f + nz0 - nz1)/D);
30877  nx1 = width() - 1;
30878  }
30879  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
30880  if (ny1<0 || ny0>=height()) return *this;
30881  if (ny0<0) {
30882  const float D = 1.0f + ny1 - ny0;
30883  nx0-=(int)((float)ny0*(1.0f + nx1 - nx0)/D);
30884  nz0-=(int)((float)ny0*(1.0f + nz1 - nz0)/D);
30885  ny0 = 0;
30886  }
30887  if (ny1>=height()) {
30888  const float d = (float)ny1 - height(), D = 1.0f + ny1 - ny0;
30889  nx1+=(int)(d*(1.0f + nx0 - nx1)/D);
30890  nz1+=(int)(d*(1.0f + nz0 - nz1)/D);
30891  ny1 = height() - 1;
30892  }
30893  if (nz0>nz1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
30894  if (nz1<0 || nz0>=depth()) return *this;
30895  if (nz0<0) {
30896  const float D = 1.0f + nz1 - nz0;
30897  nx0-=(int)((float)nz0*(1.0f + nx1 - nx0)/D);
30898  ny0-=(int)((float)nz0*(1.0f + ny1 - ny0)/D);
30899  nz0 = 0;
30900  }
30901  if (nz1>=depth()) {
30902  const float d = (float)nz1 - depth(), D = 1.0f + nz1 - nz0;
30903  nx1+=(int)(d*(1.0f + nx0 - nx1)/D);
30904  ny1+=(int)(d*(1.0f + ny0 - ny1)/D);
30905  nz1 = depth() - 1;
30906  }
30907  const unsigned int dmax = cimg::max(cimg::abs(nx1 - nx0),cimg::abs(ny1 - ny0),nz1 - nz0);
30908  const unsigned long whd = (unsigned long)_width*_height*_depth;
30909  const float px = (nx1 - nx0)/(float)dmax, py = (ny1 - ny0)/(float)dmax, pz = (nz1 - nz0)/(float)dmax;
30910  float x = (float)nx0, y = (float)ny0, z = (float)nz0;
30911  if (opacity>=1) for (unsigned int t = 0; t<=dmax; ++t) {
30912  if (!(~pattern) || (~pattern && pattern&hatch)) {
30913  T* ptrd = data((unsigned int)x,(unsigned int)y,(unsigned int)z);
30914  const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; }
30915  }
30916  x+=px; y+=py; z+=pz; if (pattern) { hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); }
30917  } else {
30918  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
30919  for (unsigned int t = 0; t<=dmax; ++t) {
30920  if (!(~pattern) || (~pattern && pattern&hatch)) {
30921  T* ptrd = data((unsigned int)x,(unsigned int)y,(unsigned int)z);
30922  const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; }
30923  }
30924  x+=px; y+=py; z+=pz; if (pattern) { hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); }
30925  }
30926  }
30927  return *this;
30928  }
30929 
30931 
30953  template<typename tc>
30954  CImg<T>& draw_line(const int x0, const int y0,
30955  const int x1, const int y1,
30956  const CImg<tc>& texture,
30957  const int tx0, const int ty0,
30958  const int tx1, const int ty1,
30959  const float opacity=1,
30960  const unsigned int pattern=~0U, const bool init_hatch=true) {
30961  if (is_empty()) return *this;
30962  if (texture._depth>1 || texture._spectrum<_spectrum)
30963  throw CImgArgumentException(_cimg_instance
30964  "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).",
30965  cimg_instance,
30966  texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
30967  if (is_overlapped(texture)) return draw_line(x0,y0,x1,y1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
30968  static unsigned int hatch = ~0U - (~0U>>1);
30969  if (init_hatch) hatch = ~0U - (~0U>>1);
30970  const bool xdir = x0<x1, ydir = y0<y1;
30971  int
30972  dtx = tx1-tx0, dty = ty1-ty0,
30973  nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
30974  tnx0 = tx0, tnx1 = tx1, tny0 = ty0, tny1 = ty1,
30975  &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1, &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
30976  &txleft = xdir?tnx0:tnx1, &tyleft = xdir?tny0:tny1, &txright = xdir?tnx1:tnx0, &tyright = xdir?tny1:tny0,
30977  &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1, &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0,
30978  &txup = ydir?tnx0:tnx1, &tyup = ydir?tny0:tny1, &txdown = ydir?tnx1:tnx0, &tydown = ydir?tny1:tny0;
30979  if (xright<0 || xleft>=width()) return *this;
30980  if (xleft<0) {
30981  const float D = (float)xright - xleft;
30982  yleft-=(int)((float)xleft*((float)yright - yleft)/D);
30983  txleft-=(int)((float)xleft*((float)txright - txleft)/D);
30984  tyleft-=(int)((float)xleft*((float)tyright - tyleft)/D);
30985  xleft = 0;
30986  }
30987  if (xright>=width()) {
30988  const float d = (float)xright - width(), D = (float)xright - xleft;
30989  yright-=(int)(d*((float)yright - yleft)/D);
30990  txright-=(int)(d*((float)txright - txleft)/D);
30991  tyright-=(int)(d*((float)tyright - tyleft)/D);
30992  xright = width() - 1;
30993  }
30994  if (ydown<0 || yup>=height()) return *this;
30995  if (yup<0) {
30996  const float D = (float)ydown - yup;
30997  xup-=(int)((float)yup*((float)xdown - xup)/D);
30998  txup-=(int)((float)yup*((float)txdown - txup)/D);
30999  tyup-=(int)((float)yup*((float)tydown - tyup)/D);
31000  yup = 0;
31001  }
31002  if (ydown>=height()) {
31003  const float d = (float)ydown - height(), D = (float)ydown - yup;
31004  xdown-=(int)(d*((float)xdown - xup)/D);
31005  txdown-=(int)(d*((float)txdown - txup)/D);
31006  tydown-=(int)(d*((float)tydown - tyup)/D);
31007  ydown = height() - 1;
31008  }
31009  T *ptrd0 = data(nx0,ny0);
31010  int dx = xright - xleft, dy = ydown - yup;
31011  const bool steep = dy>dx;
31012  if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
31013  const long
31014  offx = (nx0<nx1?1:-1)*(steep?width():1),
31015  offy = (ny0<ny1?1:-1)*(steep?1:width()),
31016  ndx = dx>0?dx:1;
31017  const unsigned long wh = (unsigned long)_width*_height;
31018 
31019  if (opacity>=1) {
31020  if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
31021  if (pattern&hatch) {
31022  T *ptrd = ptrd0;
31023  const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
31024  cimg_forC(*this,c) { *ptrd = (T)texture(tx,ty,0,c); ptrd+=wh; }
31025  }
31026  hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
31027  ptrd0+=offx;
31028  if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
31029  } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
31030  T *ptrd = ptrd0;
31031  const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
31032  cimg_forC(*this,c) { *ptrd = (T)texture(tx,ty,0,c); ptrd+=wh; }
31033  ptrd0+=offx;
31034  if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
31035  }
31036  } else {
31037  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
31038  if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
31039  T *ptrd = ptrd0;
31040  if (pattern&hatch) {
31041  const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
31042  cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture(tx,ty,0,c) + *ptrd*copacity); ptrd+=wh; }
31043  }
31044  hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
31045  ptrd0+=offx;
31046  if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
31047  } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
31048  T *ptrd = ptrd0;
31049  const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
31050  cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture(tx,ty,0,c) + *ptrd*copacity); ptrd+=wh; }
31051  ptrd0+=offx;
31052  if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
31053  }
31054  }
31055  return *this;
31056  }
31057 
31059 
31075  template<typename tc>
31076  CImg<T>& draw_line(const int x0, const int y0, const float z0,
31077  const int x1, const int y1, const float z1,
31078  const CImg<tc>& texture,
31079  const int tx0, const int ty0,
31080  const int tx1, const int ty1,
31081  const float opacity=1,
31082  const unsigned int pattern=~0U, const bool init_hatch=true) {
31083  if (is_empty() && z0<=0 && z1<=0) return *this;
31084  if (texture._depth>1 || texture._spectrum<_spectrum)
31085  throw CImgArgumentException(_cimg_instance
31086  "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).",
31087  cimg_instance,
31088  texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
31089  if (is_overlapped(texture))
31090  return draw_line(x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
31091  static unsigned int hatch = ~0U - (~0U>>1);
31092  if (init_hatch) hatch = ~0U - (~0U>>1);
31093  const bool xdir = x0<x1, ydir = y0<y1;
31094  int
31095  nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
31096  &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
31097  &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
31098  &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
31099  &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
31100  float
31101  Tx0 = tx0/z0, Tx1 = tx1/z1,
31102  Ty0 = ty0/z0, Ty1 = ty1/z1,
31103  Z0 = 1/z0, Z1 = 1/z1,
31104  dz = Z1 - Z0, dtx = Tx1 - Tx0, dty = Ty1 - Ty0,
31105  tnx0 = Tx0, tnx1 = Tx1, tny0 = Ty0, tny1 = Ty1, nz0 = Z0, nz1 = Z1,
31106  &zleft = xdir?nz0:nz1, &txleft = xdir?tnx0:tnx1, &tyleft = xdir?tny0:tny1,
31107  &zright = xdir?nz1:nz0, &txright = xdir?tnx1:tnx0, &tyright = xdir?tny1:tny0,
31108  &zup = ydir?nz0:nz1, &txup = ydir?tnx0:tnx1, &tyup = ydir?tny0:tny1,
31109  &zdown = ydir?nz1:nz0, &txdown = ydir?tnx1:tnx0, &tydown = ydir?tny1:tny0;
31110  if (xright<0 || xleft>=width()) return *this;
31111  if (xleft<0) {
31112  const float D = (float)xright - xleft;
31113  yleft-=(int)((float)xleft*((float)yright - yleft)/D);
31114  zleft-=(float)xleft*(zright - zleft)/D;
31115  txleft-=(float)xleft*(txright - txleft)/D;
31116  tyleft-=(float)xleft*(tyright - tyleft)/D;
31117  xleft = 0;
31118  }
31119  if (xright>=width()) {
31120  const float d = (float)xright - width(), D = (float)xright - xleft;
31121  yright-=(int)(d*((float)yright - yleft)/D);
31122  zright-=d*(zright - zleft)/D;
31123  txright-=d*(txright - txleft)/D;
31124  tyright-=d*(tyright - tyleft)/D;
31125  xright = width() - 1;
31126  }
31127  if (ydown<0 || yup>=height()) return *this;
31128  if (yup<0) {
31129  const float D = (float)ydown - yup;
31130  xup-=(int)((float)yup*((float)xdown - xup)/D);
31131  zup-=(float)yup*(zdown - zup)/D;
31132  txup-=(float)yup*(txdown - txup)/D;
31133  tyup-=(float)yup*(tydown - tyup)/D;
31134  yup = 0;
31135  }
31136  if (ydown>=height()) {
31137  const float d = (float)ydown - height(), D = (float)ydown - yup;
31138  xdown-=(int)(d*((float)xdown - xup)/D);
31139  zdown-=d*(zdown - zup)/D;
31140  txdown-=d*(txdown - txup)/D;
31141  tydown-=d*(tydown - tyup)/D;
31142  ydown = height() - 1;
31143  }
31144  T *ptrd0 = data(nx0,ny0);
31145  int dx = xright - xleft, dy = ydown - yup;
31146  const bool steep = dy>dx;
31147  if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
31148  const long
31149  offx = (nx0<nx1?1:-1)*(steep?width():1),
31150  offy = (ny0<ny1?1:-1)*(steep?1:width()),
31151  ndx = dx>0?dx:1;
31152  const unsigned long wh = (unsigned long)_width*_height;
31153 
31154  if (opacity>=1) {
31155  if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
31156  if (pattern&hatch) {
31157  const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
31158  T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); ptrd+=wh; }
31159  }
31160  hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
31161  ptrd0+=offx;
31162  if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
31163  } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
31164  const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
31165  T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); ptrd+=wh; }
31166  ptrd0+=offx;
31167  if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
31168  }
31169  } else {
31170  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
31171  if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
31172  if (pattern&hatch) {
31173  const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
31174  T *ptrd = ptrd0;
31175  cimg_forC(*this,c) {
31176  *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,c) + *ptrd*copacity); ptrd+=wh;
31177  }
31178  }
31179  hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
31180  ptrd0+=offx;
31181  if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
31182  } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
31183  const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
31184  T *ptrd = ptrd0;
31185  cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,c) + *ptrd*copacity); ptrd+=wh; }
31186  ptrd0+=offx;
31187  if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
31188  }
31189  }
31190  return *this;
31191  }
31192 
31194 
31211  template<typename tz, typename tc>
31213  const int x0, const int y0, const float z0,
31214  const int x1, const int y1, const float z1,
31215  const CImg<tc>& texture,
31216  const int tx0, const int ty0,
31217  const int tx1, const int ty1,
31218  const float opacity=1,
31219  const unsigned int pattern=~0U, const bool init_hatch=true) {
31220  typedef typename cimg::superset<tz,float>::type tzfloat;
31221  if (is_empty() || z0<=0 || z1<=0) return *this;
31222  if (!is_sameXY(zbuffer))
31223  throw CImgArgumentException(_cimg_instance
31224  "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
31225  "different dimensions.",
31226  cimg_instance,
31227  zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
31228  if (texture._depth>1 || texture._spectrum<_spectrum)
31229  throw CImgArgumentException(_cimg_instance
31230  "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).",
31231  cimg_instance,
31232  texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
31233  if (is_overlapped(texture))
31234  return draw_line(zbuffer,x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
31235  static unsigned int hatch = ~0U - (~0U>>1);
31236  if (init_hatch) hatch = ~0U - (~0U>>1);
31237  const bool xdir = x0<x1, ydir = y0<y1;
31238  int
31239  nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
31240  &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
31241  &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
31242  &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
31243  &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
31244  float
31245  Tx0 = tx0/z0, Tx1 = tx1/z1,
31246  Ty0 = ty0/z0, Ty1 = ty1/z1,
31247  dtx = Tx1 - Tx0, dty = Ty1 - Ty0,
31248  tnx0 = Tx0, tnx1 = Tx1, tny0 = Ty0, tny1 = Ty1,
31249  &txleft = xdir?tnx0:tnx1, &tyleft = xdir?tny0:tny1,
31250  &txright = xdir?tnx1:tnx0, &tyright = xdir?tny1:tny0,
31251  &txup = ydir?tnx0:tnx1, &tyup = ydir?tny0:tny1,
31252  &txdown = ydir?tnx1:tnx0, &tydown = ydir?tny1:tny0;
31253  tzfloat
31254  Z0 = 1/(tzfloat)z0, Z1 = 1/(tzfloat)z1,
31255  dz = Z1 - Z0, nz0 = Z0, nz1 = Z1,
31256  &zleft = xdir?nz0:nz1,
31257  &zright = xdir?nz1:nz0,
31258  &zup = ydir?nz0:nz1,
31259  &zdown = ydir?nz1:nz0;
31260  if (xright<0 || xleft>=width()) return *this;
31261  if (xleft<0) {
31262  const float D = (float)xright - xleft;
31263  yleft-=(int)((float)xleft*((float)yright - yleft)/D);
31264  zleft-=(float)xleft*(zright - zleft)/D;
31265  txleft-=(float)xleft*(txright - txleft)/D;
31266  tyleft-=(float)xleft*(tyright - tyleft)/D;
31267  xleft = 0;
31268  }
31269  if (xright>=width()) {
31270  const float d = (float)xright - width(), D = (float)xright - xleft;
31271  yright-=(int)(d*((float)yright - yleft)/D);
31272  zright-=d*(zright - zleft)/D;
31273  txright-=d*(txright - txleft)/D;
31274  tyright-=d*(tyright - tyleft)/D;
31275  xright = width()-1;
31276  }
31277  if (ydown<0 || yup>=height()) return *this;
31278  if (yup<0) {
31279  const float D = (float)ydown - yup;
31280  xup-=(int)((float)yup*((float)xdown - xup)/D);
31281  zup-=yup*(zdown - zup)/D;
31282  txup-=yup*(txdown - txup)/D;
31283  tyup-=yup*(tydown - tyup)/D;
31284  yup = 0;
31285  }
31286  if (ydown>=height()) {
31287  const float d = (float)ydown - height(), D = (float)ydown - yup;
31288  xdown-=(int)(d*((float)xdown - xup)/D);
31289  zdown-=d*(zdown - zup)/D;
31290  txdown-=d*(txdown - txup)/D;
31291  tydown-=d*(tydown - tyup)/D;
31292  ydown = height()-1;
31293  }
31294  T *ptrd0 = data(nx0,ny0);
31295  tz *ptrz = zbuffer.data(nx0,ny0);
31296  int dx = xright - xleft, dy = ydown - yup;
31297  const bool steep = dy>dx;
31298  if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
31299  const long
31300  offx = (nx0<nx1?1:-1)*(steep?width():1),
31301  offy = (ny0<ny1?1:-1)*(steep?1:width()),
31302  ndx = dx>0?dx:1;
31303  const unsigned long wh = (unsigned long)_width*_height;
31304 
31305  if (opacity>=1) {
31306  if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
31307  if (pattern&hatch) {
31308  const tzfloat z = Z0 + x*dz/ndx;
31309  if (z>=(tzfloat)*ptrz) {
31310  *ptrz = (tz)z;
31311  const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
31312  T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); ptrd+=wh; }
31313  }
31314  }
31315  hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
31316  ptrd0+=offx; ptrz+=offx;
31317  if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
31318  } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
31319  const tzfloat z = Z0 + x*dz/ndx;
31320  if (z>=(tzfloat)*ptrz) {
31321  *ptrz = (tz)z;
31322  const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
31323  T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); ptrd+=wh; }
31324  }
31325  ptrd0+=offx; ptrz+=offx;
31326  if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
31327  }
31328  } else {
31329  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
31330  if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
31331  if (pattern&hatch) {
31332  const tzfloat z = Z0 + x*dz/ndx;
31333  if (z>=(tzfloat)*ptrz) {
31334  *ptrz = (tz)z;
31335  const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
31336  T *ptrd = ptrd0;
31337  cimg_forC(*this,c) {
31338  *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,c) + *ptrd*copacity); ptrd+=wh;
31339  }
31340  }
31341  }
31342  hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
31343  ptrd0+=offx; ptrz+=offx;
31344  if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
31345  } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
31346  const tzfloat z = Z0 + x*dz/ndx;
31347  if (z>=(tzfloat)*ptrz) {
31348  *ptrz = (tz)z;
31349  const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
31350  T *ptrd = ptrd0;
31351  cimg_forC(*this,c) {
31352  *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,c) + *ptrd*copacity); ptrd+=wh;
31353  }
31354  }
31355  ptrd0+=offx; ptrz+=offx;
31356  if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
31357  }
31358  }
31359  return *this;
31360  }
31361 
31363 
31373  template<typename t, typename tc>
31374  CImg<T>& draw_line(const CImg<t>& points,
31375  const tc *const color, const float opacity=1,
31376  const unsigned int pattern=~0U, const bool init_hatch=true) {
31377  if (is_empty() || !points || points._width<2) return *this;
31378  bool ninit_hatch = init_hatch;
31379  switch (points._height) {
31380  case 0 : case 1 :
31381  throw CImgArgumentException(_cimg_instance
31382  "draw_line(): Invalid specified point set (%u,%u,%u,%u,%p).",
31383  cimg_instance,
31384  points._width,points._height,points._depth,points._spectrum,points._data);
31385 
31386  case 2 : {
31387  const int x0 = (int)points(0,0), y0 = (int)points(0,1);
31388  int ox = x0, oy = y0;
31389  for (unsigned int i = 1; i<points._width; ++i) {
31390  const int x = (int)points(i,0), y = (int)points(i,1);
31391  draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch);
31392  ninit_hatch = false;
31393  ox = x; oy = y;
31394  }
31395  } break;
31396  default : {
31397  const int x0 = (int)points(0,0), y0 = (int)points(0,1), z0 = (int)points(0,2);
31398  int ox = x0, oy = y0, oz = z0;
31399  for (unsigned int i = 1; i<points._width; ++i) {
31400  const int x = (int)points(i,0), y = (int)points(i,1), z = (int)points(i,2);
31401  draw_line(ox,oy,oz,x,y,z,color,opacity,pattern,ninit_hatch);
31402  ninit_hatch = false;
31403  ox = x; oy = y; oz = z;
31404  }
31405  }
31406  }
31407  return *this;
31408  }
31409 
31411 
31422  template<typename tc>
31423  CImg<T>& draw_arrow(const int x0, const int y0,
31424  const int x1, const int y1,
31425  const tc *const color, const float opacity=1,
31426  const float angle=30, const float length=-10,
31427  const unsigned int pattern=~0U) {
31428  if (is_empty()) return *this;
31429  const float u = (float)(x0 - x1), v = (float)(y0 - y1), sq = u*u + v*v,
31430  deg = (float)(angle*cimg::PI/180), ang = (sq>0)?(float)std::atan2(v,u):0.0f,
31431  l = (length>=0)?length:-length*(float)std::sqrt(sq)/100;
31432  if (sq>0) {
31433  const float
31434  cl = (float)std::cos(ang - deg), sl = (float)std::sin(ang - deg),
31435  cr = (float)std::cos(ang + deg), sr = (float)std::sin(ang + deg);
31436  const int
31437  xl = x1 + (int)(l*cl), yl = y1 + (int)(l*sl),
31438  xr = x1 + (int)(l*cr), yr = y1 + (int)(l*sr),
31439  xc = x1 + (int)((l+1)*(cl+cr))/2, yc = y1 + (int)((l+1)*(sl+sr))/2;
31440  draw_line(x0,y0,xc,yc,color,opacity,pattern).draw_triangle(x1,y1,xl,yl,xr,yr,color,opacity);
31441  } else draw_point(x0,y0,color,opacity);
31442  return *this;
31443  }
31444 
31446 
31479  template<typename tc>
31480  CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0,
31481  const int x1, const int y1, const float u1, const float v1,
31482  const tc *const color, const float opacity=1,
31483  const float precision=0.25, const unsigned int pattern=~0U,
31484  const bool init_hatch=true) {
31485  if (is_empty()) return *this;
31486  if (!color)
31487  throw CImgArgumentException(_cimg_instance
31488  "draw_spline(): Specified color is (null).",
31489  cimg_instance);
31490  if (x0==x1 && y0==y1) return draw_point(x0,y0,color,opacity);
31491  bool ninit_hatch = init_hatch;
31492  const float
31493  ax = u0 + u1 + 2*(x0 - x1),
31494  bx = 3*(x1 - x0) - 2*u0 - u1,
31495  ay = v0 + v1 + 2*(y0 - y1),
31496  by = 3*(y1 - y0) - 2*v0 - v1,
31497  _precision = 1/(std::sqrt(cimg::sqr((float)x0-x1)+cimg::sqr((float)y0-y1))*(precision>0?precision:1));
31498  int ox = x0, oy = y0;
31499  for (float t = 0; t<1; t+=_precision) {
31500  const float t2 = t*t, t3 = t2*t;
31501  const int
31502  nx = (int)(ax*t3 + bx*t2 + u0*t + x0),
31503  ny = (int)(ay*t3 + by*t2 + v0*t + y0);
31504  draw_line(ox,oy,nx,ny,color,opacity,pattern,ninit_hatch);
31505  ninit_hatch = false;
31506  ox = nx; oy = ny;
31507  }
31508  return draw_line(ox,oy,x1,y1,color,opacity,pattern,false);
31509  }
31510 
31512 
31516  template<typename tc>
31517  CImg<T>& draw_spline(const int x0, const int y0, const int z0, const float u0, const float v0, const float w0,
31518  const int x1, const int y1, const int z1, const float u1, const float v1, const float w1,
31519  const tc *const color, const float opacity=1,
31520  const float precision=4, const unsigned int pattern=~0U,
31521  const bool init_hatch=true) {
31522  if (is_empty()) return *this;
31523  if (!color)
31524  throw CImgArgumentException(_cimg_instance
31525  "draw_spline(): Specified color is (null).",
31526  cimg_instance);
31527  if (x0==x1 && y0==y1 && z0==z1) return draw_point(x0,y0,z0,color,opacity);
31528  bool ninit_hatch = init_hatch;
31529  const float
31530  ax = u0 + u1 + 2*(x0 - x1),
31531  bx = 3*(x1 - x0) - 2*u0 - u1,
31532  ay = v0 + v1 + 2*(y0 - y1),
31533  by = 3*(y1 - y0) - 2*v0 - v1,
31534  az = w0 + w1 + 2*(z0 - z1),
31535  bz = 3*(z1 - z0) - 2*w0 - w1,
31536  _precision = 1/(std::sqrt(cimg::sqr(x0-x1)+cimg::sqr(y0-y1))*(precision>0?precision:1));
31537  int ox = x0, oy = y0, oz = z0;
31538  for (float t = 0; t<1; t+=_precision) {
31539  const float t2 = t*t, t3 = t2*t;
31540  const int
31541  nx = (int)(ax*t3 + bx*t2 + u0*t + x0),
31542  ny = (int)(ay*t3 + by*t2 + v0*t + y0),
31543  nz = (int)(az*t3 + bz*t2 + w0*t + z0);
31544  draw_line(ox,oy,oz,nx,ny,nz,color,opacity,pattern,ninit_hatch);
31545  ninit_hatch = false;
31546  ox = nx; oy = ny; oz = nz;
31547  }
31548  return draw_line(ox,oy,oz,x1,y1,z1,color,opacity,pattern,false);
31549  }
31550 
31552 
31571  template<typename t>
31572  CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0,
31573  const int x1, const int y1, const float u1, const float v1,
31574  const CImg<t>& texture,
31575  const int tx0, const int ty0, const int tx1, const int ty1,
31576  const float opacity=1,
31577  const float precision=4, const unsigned int pattern=~0U,
31578  const bool init_hatch=true) {
31579  if (texture._depth>1 || texture._spectrum<_spectrum)
31580  throw CImgArgumentException(_cimg_instance
31581  "draw_spline(): Invalid specified texture (%u,%u,%u,%u,%p).",
31582  cimg_instance,
31583  texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
31584  if (is_empty()) return *this;
31585  if (is_overlapped(texture))
31586  return draw_spline(x0,y0,u0,v0,x1,y1,u1,v1,+texture,tx0,ty0,tx1,ty1,precision,opacity,pattern,init_hatch);
31587  if (x0==x1 && y0==y1) return draw_point(x0,y0,texture.get_vector_at(x0,y0),opacity);
31588  bool ninit_hatch = init_hatch;
31589  const float
31590  ax = u0 + u1 + 2*(x0 - x1),
31591  bx = 3*(x1 - x0) - 2*u0 - u1,
31592  ay = v0 + v1 + 2*(y0 - y1),
31593  by = 3*(y1 - y0) - 2*v0 - v1,
31594  _precision = 1/(std::sqrt(cimg::sqr(x0-x1)+cimg::sqr(y0-y1))*(precision>0?precision:1));
31595  int ox = x0, oy = y0, otx = tx0, oty = ty0;
31596  for (float t1 = 0; t1<1; t1+=_precision) {
31597  const float t2 = t1*t1, t3 = t2*t1;
31598  const int
31599  nx = (int)(ax*t3 + bx*t2 + u0*t1 + x0),
31600  ny = (int)(ay*t3 + by*t2 + v0*t1 + y0),
31601  ntx = tx0 + (int)((tx1-tx0)*t1),
31602  nty = ty0 + (int)((ty1-ty0)*t1);
31603  draw_line(ox,oy,nx,ny,texture,otx,oty,ntx,nty,opacity,pattern,ninit_hatch);
31604  ninit_hatch = false;
31605  ox = nx; oy = ny; otx = ntx; oty = nty;
31606  }
31607  return draw_line(ox,oy,x1,y1,texture,otx,oty,tx1,ty1,opacity,pattern,false);
31608  }
31609 
31611 
31621  template<typename tp, typename tt, typename tc>
31622  CImg<T>& draw_spline(const CImg<tp>& points, const CImg<tt>& tangents,
31623  const tc *const color, const float opacity=1,
31624  const bool is_closed_set=false, const float precision=4,
31625  const unsigned int pattern=~0U, const bool init_hatch=true) {
31626  if (is_empty() || !points || !tangents || points._width<2 || tangents._width<2) return *this;
31627  bool ninit_hatch = init_hatch;
31628  switch (points._height) {
31629  case 0 : case 1 :
31630  throw CImgArgumentException(_cimg_instance
31631  "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).",
31632  cimg_instance,
31633  points._width,points._height,points._depth,points._spectrum,points._data);
31634 
31635  case 2 : {
31636  const int x0 = (int)points(0,0), y0 = (int)points(0,1);
31637  const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1);
31638  int ox = x0, oy = y0;
31639  float ou = u0, ov = v0;
31640  for (unsigned int i = 1; i<points._width; ++i) {
31641  const int x = (int)points(i,0), y = (int)points(i,1);
31642  const float u = (float)tangents(i,0), v = (float)tangents(i,1);
31643  draw_spline(ox,oy,ou,ov,x,y,u,v,color,precision,opacity,pattern,ninit_hatch);
31644  ninit_hatch = false;
31645  ox = x; oy = y; ou = u; ov = v;
31646  }
31647  if (is_closed_set) draw_spline(ox,oy,ou,ov,x0,y0,u0,v0,color,precision,opacity,pattern,false);
31648  } break;
31649  default : {
31650  const int x0 = (int)points(0,0), y0 = (int)points(0,1), z0 = (int)points(0,2);
31651  const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1), w0 = (float)tangents(0,2);
31652  int ox = x0, oy = y0, oz = z0;
31653  float ou = u0, ov = v0, ow = w0;
31654  for (unsigned int i = 1; i<points._width; ++i) {
31655  const int x = (int)points(i,0), y = (int)points(i,1), z = (int)points(i,2);
31656  const float u = (float)tangents(i,0), v = (float)tangents(i,1), w = (float)tangents(i,2);
31657  draw_spline(ox,oy,oz,ou,ov,ow,x,y,z,u,v,w,color,opacity,pattern,ninit_hatch);
31658  ninit_hatch = false;
31659  ox = x; oy = y; oz = z; ou = u; ov = v; ow = w;
31660  }
31661  if (is_closed_set) draw_spline(ox,oy,oz,ou,ov,ow,x0,y0,z0,u0,v0,w0,color,precision,opacity,pattern,false);
31662  }
31663  }
31664  return *this;
31665  }
31666 
31668 
31671  template<typename tp, typename tc>
31672  CImg<T>& draw_spline(const CImg<tp>& points,
31673  const tc *const color, const float opacity=1,
31674  const bool is_closed_set=false, const float precision=4,
31675  const unsigned int pattern=~0U, const bool init_hatch=true) {
31676  if (is_empty() || !points || points._width<2) return *this;
31677  CImg<Tfloat> tangents;
31678  switch (points._height) {
31679  case 0 : case 1 :
31680  throw CImgArgumentException(_cimg_instance
31681  "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).",
31682  cimg_instance,
31683  points._width,points._height,points._depth,points._spectrum,points._data);
31684  case 2 : {
31685  tangents.assign(points._width,points._height);
31686  cimg_forX(points,p) {
31687  const unsigned int
31688  p0 = is_closed_set?(p+points._width-1)%points._width:(p?p-1:0),
31689  p1 = is_closed_set?(p+1)%points._width:(p+1<points._width?p+1:p);
31690  const float
31691  x = (float)points(p,0),
31692  y = (float)points(p,1),
31693  x0 = (float)points(p0,0),
31694  y0 = (float)points(p0,1),
31695  x1 = (float)points(p1,0),
31696  y1 = (float)points(p1,1),
31697  u0 = x - x0,
31698  v0 = y - y0,
31699  n0 = 1e-8f + (float)std::sqrt(u0*u0 + v0*v0),
31700  u1 = x1 - x,
31701  v1 = y1 - y,
31702  n1 = 1e-8f + (float)std::sqrt(u1*u1 + v1*v1),
31703  u = u0/n0 + u1/n1,
31704  v = v0/n0 + v1/n1,
31705  n = 1e-8f + (float)std::sqrt(u*u + v*v),
31706  fact = 0.5f*(n0 + n1);
31707  tangents(p,0) = (Tfloat)(fact*u/n);
31708  tangents(p,1) = (Tfloat)(fact*v/n);
31709  }
31710  } break;
31711  default : {
31712  tangents.assign(points._width,points._height);
31713  cimg_forX(points,p) {
31714  const unsigned int
31715  p0 = is_closed_set?(p+points._width-1)%points._width:(p?p-1:0),
31716  p1 = is_closed_set?(p+1)%points._width:(p+1<points._width?p+1:p);
31717  const float
31718  x = (float)points(p,0),
31719  y = (float)points(p,1),
31720  z = (float)points(p,2),
31721  x0 = (float)points(p0,0),
31722  y0 = (float)points(p0,1),
31723  z0 = (float)points(p0,2),
31724  x1 = (float)points(p1,0),
31725  y1 = (float)points(p1,1),
31726  z1 = (float)points(p1,2),
31727  u0 = x - x0,
31728  v0 = y - y0,
31729  w0 = z - z0,
31730  n0 = 1e-8f + (float)std::sqrt(u0*u0 + v0*v0 + w0*w0),
31731  u1 = x1 - x,
31732  v1 = y1 - y,
31733  w1 = z1 - z,
31734  n1 = 1e-8f + (float)std::sqrt(u1*u1 + v1*v1 + w1*w1),
31735  u = u0/n0 + u1/n1,
31736  v = v0/n0 + v1/n1,
31737  w = w0/n0 + w1/n1,
31738  n = 1e-8f + (float)std::sqrt(u*u + v*v + w*w),
31739  fact = 0.5f*(n0 + n1);
31740  tangents(p,0) = (Tfloat)(fact*u/n);
31741  tangents(p,1) = (Tfloat)(fact*v/n);
31742  tangents(p,2) = (Tfloat)(fact*w/n);
31743  }
31744  }
31745  }
31746  return draw_spline(points,tangents,color,opacity,is_closed_set,precision,pattern,init_hatch);
31747  }
31748 
31749  // Inner macro for drawing triangles.
31750 #define _cimg_for_triangle1(img,xl,xr,y,x0,y0,x1,y1,x2,y2) \
31751  for (int y = y0<0?0:y0, \
31752  xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \
31753  xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \
31754  _sxn=1, \
31755  _sxr=1, \
31756  _sxl=1, \
31757  _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \
31758  _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \
31759  _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \
31760  _dyn = y2-y1, \
31761  _dyr = y2-y0, \
31762  _dyl = y1-y0, \
31763  _counter = (_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
31764  _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
31765  _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
31766  cimg::min((int)(img)._height-y-1,y2-y)), \
31767  _errn = _dyn/2, \
31768  _errr = _dyr/2, \
31769  _errl = _dyl/2, \
31770  _rxn = _dyn?(x2-x1)/_dyn:0, \
31771  _rxr = _dyr?(x2-x0)/_dyr:0, \
31772  _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
31773  (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn); \
31774  _counter>=0; --_counter, ++y, \
31775  xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
31776  xl+=(y!=y1)?_rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0): \
31777  (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
31778 
31779 #define _cimg_for_triangle2(img,xl,cl,xr,cr,y,x0,y0,c0,x1,y1,c1,x2,y2,c2) \
31780  for (int y = y0<0?0:y0, \
31781  xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \
31782  cr = y0>=0?c0:(c0-y0*(c2-c0)/(y2-y0)), \
31783  xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \
31784  cl = y1>=0?(y0>=0?(y0==y1?c1:c0):(c0-y0*(c1-c0)/(y1-y0))):(c1-y1*(c2-c1)/(y2-y1)), \
31785  _sxn=1, _scn=1, \
31786  _sxr=1, _scr=1, \
31787  _sxl=1, _scl=1, \
31788  _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \
31789  _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \
31790  _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \
31791  _dcn = c2>c1?c2-c1:(_scn=-1,c1-c2), \
31792  _dcr = c2>c0?c2-c0:(_scr=-1,c0-c2), \
31793  _dcl = c1>c0?c1-c0:(_scl=-1,c0-c1), \
31794  _dyn = y2-y1, \
31795  _dyr = y2-y0, \
31796  _dyl = y1-y0, \
31797  _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
31798  _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
31799  _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
31800  _dcn-=_dyn?_dyn*(_dcn/_dyn):0, \
31801  _dcr-=_dyr?_dyr*(_dcr/_dyr):0, \
31802  _dcl-=_dyl?_dyl*(_dcl/_dyl):0, \
31803  cimg::min((int)(img)._height-y-1,y2-y)), \
31804  _errn = _dyn/2, _errcn = _errn, \
31805  _errr = _dyr/2, _errcr = _errr, \
31806  _errl = _dyl/2, _errcl = _errl, \
31807  _rxn = _dyn?(x2-x1)/_dyn:0, \
31808  _rcn = _dyn?(c2-c1)/_dyn:0, \
31809  _rxr = _dyr?(x2-x0)/_dyr:0, \
31810  _rcr = _dyr?(c2-c0)/_dyr:0, \
31811  _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
31812  (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
31813  _rcl = (y0!=y1 && y1>0)?(_dyl?(c1-c0)/_dyl:0): \
31814  (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcn ); \
31815  _counter>=0; --_counter, ++y, \
31816  xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
31817  cr+=_rcr+((_errcr-=_dcr)<0?_errcr+=_dyr,_scr:0), \
31818  xl+=(y!=y1)?(cl+=_rcl+((_errcl-=_dcl)<0?(_errcl+=_dyl,_scl):0), \
31819  _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
31820  (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcl=_rcn, cl=c1, \
31821  _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
31822 
31823 #define _cimg_for_triangle3(img,xl,txl,tyl,xr,txr,tyr,y,x0,y0,tx0,ty0,x1,y1,tx1,ty1,x2,y2,tx2,ty2) \
31824  for (int y = y0<0?0:y0, \
31825  xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \
31826  txr = y0>=0?tx0:(tx0-y0*(tx2-tx0)/(y2-y0)), \
31827  tyr = y0>=0?ty0:(ty0-y0*(ty2-ty0)/(y2-y0)), \
31828  xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \
31829  txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0-y0*(tx1-tx0)/(y1-y0))):(tx1-y1*(tx2-tx1)/(y2-y1)), \
31830  tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0-y0*(ty1-ty0)/(y1-y0))):(ty1-y1*(ty2-ty1)/(y2-y1)), \
31831  _sxn=1, _stxn=1, _styn=1, \
31832  _sxr=1, _stxr=1, _styr=1, \
31833  _sxl=1, _stxl=1, _styl=1, \
31834  _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \
31835  _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \
31836  _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \
31837  _dtxn = tx2>tx1?tx2-tx1:(_stxn=-1,tx1-tx2), \
31838  _dtxr = tx2>tx0?tx2-tx0:(_stxr=-1,tx0-tx2), \
31839  _dtxl = tx1>tx0?tx1-tx0:(_stxl=-1,tx0-tx1), \
31840  _dtyn = ty2>ty1?ty2-ty1:(_styn=-1,ty1-ty2), \
31841  _dtyr = ty2>ty0?ty2-ty0:(_styr=-1,ty0-ty2), \
31842  _dtyl = ty1>ty0?ty1-ty0:(_styl=-1,ty0-ty1), \
31843  _dyn = y2-y1, \
31844  _dyr = y2-y0, \
31845  _dyl = y1-y0, \
31846  _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
31847  _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
31848  _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
31849  _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \
31850  _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \
31851  _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \
31852  _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \
31853  _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \
31854  _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \
31855  cimg::min((int)(img)._height-y-1,y2-y)), \
31856  _errn = _dyn/2, _errtxn = _errn, _errtyn = _errn, \
31857  _errr = _dyr/2, _errtxr = _errr, _errtyr = _errr, \
31858  _errl = _dyl/2, _errtxl = _errl, _errtyl = _errl, \
31859  _rxn = _dyn?(x2-x1)/_dyn:0, \
31860  _rtxn = _dyn?(tx2-tx1)/_dyn:0, \
31861  _rtyn = _dyn?(ty2-ty1)/_dyn:0, \
31862  _rxr = _dyr?(x2-x0)/_dyr:0, \
31863  _rtxr = _dyr?(tx2-tx0)/_dyr:0, \
31864  _rtyr = _dyr?(ty2-ty0)/_dyr:0, \
31865  _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
31866  (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
31867  _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1-tx0)/_dyl:0): \
31868  (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \
31869  _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1-ty0)/_dyl:0): \
31870  (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ); \
31871  _counter>=0; --_counter, ++y, \
31872  xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
31873  txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \
31874  tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \
31875  xl+=(y!=y1)?(txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \
31876  tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \
31877  _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
31878  (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \
31879  _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1,\
31880  _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
31881 
31882 #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) \
31883  for (int y = y0<0?0:y0, \
31884  xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \
31885  cr = y0>=0?c0:(c0-y0*(c2-c0)/(y2-y0)), \
31886  txr = y0>=0?tx0:(tx0-y0*(tx2-tx0)/(y2-y0)), \
31887  tyr = y0>=0?ty0:(ty0-y0*(ty2-ty0)/(y2-y0)), \
31888  xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \
31889  cl = y1>=0?(y0>=0?(y0==y1?c1:c0):(c0-y0*(c1-c0)/(y1-y0))):(c1-y1*(c2-c1)/(y2-y1)), \
31890  txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0-y0*(tx1-tx0)/(y1-y0))):(tx1-y1*(tx2-tx1)/(y2-y1)), \
31891  tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0-y0*(ty1-ty0)/(y1-y0))):(ty1-y1*(ty2-ty1)/(y2-y1)), \
31892  _sxn=1, _scn=1, _stxn=1, _styn=1, \
31893  _sxr=1, _scr=1, _stxr=1, _styr=1, \
31894  _sxl=1, _scl=1, _stxl=1, _styl=1, \
31895  _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \
31896  _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \
31897  _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \
31898  _dcn = c2>c1?c2-c1:(_scn=-1,c1-c2), \
31899  _dcr = c2>c0?c2-c0:(_scr=-1,c0-c2), \
31900  _dcl = c1>c0?c1-c0:(_scl=-1,c0-c1), \
31901  _dtxn = tx2>tx1?tx2-tx1:(_stxn=-1,tx1-tx2), \
31902  _dtxr = tx2>tx0?tx2-tx0:(_stxr=-1,tx0-tx2), \
31903  _dtxl = tx1>tx0?tx1-tx0:(_stxl=-1,tx0-tx1), \
31904  _dtyn = ty2>ty1?ty2-ty1:(_styn=-1,ty1-ty2), \
31905  _dtyr = ty2>ty0?ty2-ty0:(_styr=-1,ty0-ty2), \
31906  _dtyl = ty1>ty0?ty1-ty0:(_styl=-1,ty0-ty1), \
31907  _dyn = y2-y1, \
31908  _dyr = y2-y0, \
31909  _dyl = y1-y0, \
31910  _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
31911  _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
31912  _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
31913  _dcn-=_dyn?_dyn*(_dcn/_dyn):0, \
31914  _dcr-=_dyr?_dyr*(_dcr/_dyr):0, \
31915  _dcl-=_dyl?_dyl*(_dcl/_dyl):0, \
31916  _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \
31917  _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \
31918  _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \
31919  _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \
31920  _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \
31921  _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \
31922  cimg::min((int)(img)._height-y-1,y2-y)), \
31923  _errn = _dyn/2, _errcn = _errn, _errtxn = _errn, _errtyn = _errn, \
31924  _errr = _dyr/2, _errcr = _errr, _errtxr = _errr, _errtyr = _errr, \
31925  _errl = _dyl/2, _errcl = _errl, _errtxl = _errl, _errtyl = _errl, \
31926  _rxn = _dyn?(x2-x1)/_dyn:0, \
31927  _rcn = _dyn?(c2-c1)/_dyn:0, \
31928  _rtxn = _dyn?(tx2-tx1)/_dyn:0, \
31929  _rtyn = _dyn?(ty2-ty1)/_dyn:0, \
31930  _rxr = _dyr?(x2-x0)/_dyr:0, \
31931  _rcr = _dyr?(c2-c0)/_dyr:0, \
31932  _rtxr = _dyr?(tx2-tx0)/_dyr:0, \
31933  _rtyr = _dyr?(ty2-ty0)/_dyr:0, \
31934  _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
31935  (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
31936  _rcl = (y0!=y1 && y1>0)?(_dyl?(c1-c0)/_dyl:0): \
31937  (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcn ), \
31938  _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1-tx0)/_dyl:0): \
31939  (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \
31940  _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1-ty0)/_dyl:0): \
31941  (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ); \
31942  _counter>=0; --_counter, ++y, \
31943  xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
31944  cr+=_rcr+((_errcr-=_dcr)<0?_errcr+=_dyr,_scr:0), \
31945  txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \
31946  tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \
31947  xl+=(y!=y1)?(cl+=_rcl+((_errcl-=_dcl)<0?(_errcl+=_dyl,_scl):0), \
31948  txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \
31949  tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \
31950  _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
31951  (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcl=_rcn, cl=c1, \
31952  _errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \
31953  _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1, \
31954  _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
31955 
31956 #define _cimg_for_triangle5(img,xl,txl,tyl,lxl,lyl,xr,txr,tyr,lxr,lyr,y,x0,y0,\
31957  tx0,ty0,lx0,ly0,x1,y1,tx1,ty1,lx1,ly1,x2,y2,tx2,ty2,lx2,ly2) \
31958  for (int y = y0<0?0:y0, \
31959  xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \
31960  txr = y0>=0?tx0:(tx0-y0*(tx2-tx0)/(y2-y0)), \
31961  tyr = y0>=0?ty0:(ty0-y0*(ty2-ty0)/(y2-y0)), \
31962  lxr = y0>=0?lx0:(lx0-y0*(lx2-lx0)/(y2-y0)), \
31963  lyr = y0>=0?ly0:(ly0-y0*(ly2-ly0)/(y2-y0)), \
31964  xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \
31965  txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0-y0*(tx1-tx0)/(y1-y0))):(tx1-y1*(tx2-tx1)/(y2-y1)), \
31966  tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0-y0*(ty1-ty0)/(y1-y0))):(ty1-y1*(ty2-ty1)/(y2-y1)), \
31967  lxl = y1>=0?(y0>=0?(y0==y1?lx1:lx0):(lx0-y0*(lx1-lx0)/(y1-y0))):(lx1-y1*(lx2-lx1)/(y2-y1)), \
31968  lyl = y1>=0?(y0>=0?(y0==y1?ly1:ly0):(ly0-y0*(ly1-ly0)/(y1-y0))):(ly1-y1*(ly2-ly1)/(y2-y1)), \
31969  _sxn=1, _stxn=1, _styn=1, _slxn=1, _slyn=1, \
31970  _sxr=1, _stxr=1, _styr=1, _slxr=1, _slyr=1, \
31971  _sxl=1, _stxl=1, _styl=1, _slxl=1, _slyl=1, \
31972  _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), _dyn = y2-y1, \
31973  _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), _dyr = y2-y0, \
31974  _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), _dyl = y1-y0, \
31975  _dtxn = tx2>tx1?tx2-tx1:(_stxn=-1,tx1-tx2), \
31976  _dtxr = tx2>tx0?tx2-tx0:(_stxr=-1,tx0-tx2), \
31977  _dtxl = tx1>tx0?tx1-tx0:(_stxl=-1,tx0-tx1), \
31978  _dtyn = ty2>ty1?ty2-ty1:(_styn=-1,ty1-ty2), \
31979  _dtyr = ty2>ty0?ty2-ty0:(_styr=-1,ty0-ty2), \
31980  _dtyl = ty1>ty0?ty1-ty0:(_styl=-1,ty0-ty1), \
31981  _dlxn = lx2>lx1?lx2-lx1:(_slxn=-1,lx1-lx2), \
31982  _dlxr = lx2>lx0?lx2-lx0:(_slxr=-1,lx0-lx2), \
31983  _dlxl = lx1>lx0?lx1-lx0:(_slxl=-1,lx0-lx1), \
31984  _dlyn = ly2>ly1?ly2-ly1:(_slyn=-1,ly1-ly2), \
31985  _dlyr = ly2>ly0?ly2-ly0:(_slyr=-1,ly0-ly2), \
31986  _dlyl = ly1>ly0?ly1-ly0:(_slyl=-1,ly0-ly1), \
31987  _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
31988  _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
31989  _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
31990  _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \
31991  _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \
31992  _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \
31993  _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \
31994  _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \
31995  _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \
31996  _dlxn-=_dyn?_dyn*(_dlxn/_dyn):0, \
31997  _dlxr-=_dyr?_dyr*(_dlxr/_dyr):0, \
31998  _dlxl-=_dyl?_dyl*(_dlxl/_dyl):0, \
31999  _dlyn-=_dyn?_dyn*(_dlyn/_dyn):0, \
32000  _dlyr-=_dyr?_dyr*(_dlyr/_dyr):0, \
32001  _dlyl-=_dyl?_dyl*(_dlyl/_dyl):0, \
32002  cimg::min((int)(img)._height-y-1,y2-y)), \
32003  _errn = _dyn/2, _errtxn = _errn, _errtyn = _errn, _errlxn = _errn, _errlyn = _errn, \
32004  _errr = _dyr/2, _errtxr = _errr, _errtyr = _errr, _errlxr = _errr, _errlyr = _errr, \
32005  _errl = _dyl/2, _errtxl = _errl, _errtyl = _errl, _errlxl = _errl, _errlyl = _errl, \
32006  _rxn = _dyn?(x2-x1)/_dyn:0, \
32007  _rtxn = _dyn?(tx2-tx1)/_dyn:0, \
32008  _rtyn = _dyn?(ty2-ty1)/_dyn:0, \
32009  _rlxn = _dyn?(lx2-lx1)/_dyn:0, \
32010  _rlyn = _dyn?(ly2-ly1)/_dyn:0, \
32011  _rxr = _dyr?(x2-x0)/_dyr:0, \
32012  _rtxr = _dyr?(tx2-tx0)/_dyr:0, \
32013  _rtyr = _dyr?(ty2-ty0)/_dyr:0, \
32014  _rlxr = _dyr?(lx2-lx0)/_dyr:0, \
32015  _rlyr = _dyr?(ly2-ly0)/_dyr:0, \
32016  _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
32017  (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
32018  _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1-tx0)/_dyl:0): \
32019  (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \
32020  _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1-ty0)/_dyl:0): \
32021  (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ), \
32022  _rlxl = (y0!=y1 && y1>0)?(_dyl?(lx1-lx0)/_dyl:0): \
32023  (_errlxl=_errlxn, _dlxl=_dlxn, _dyl=_dyn, _slxl=_slxn, _rlxn ), \
32024  _rlyl = (y0!=y1 && y1>0)?(_dyl?(ly1-ly0)/_dyl:0): \
32025  (_errlyl=_errlyn, _dlyl=_dlyn, _dyl=_dyn, _slyl=_slyn, _rlyn ); \
32026  _counter>=0; --_counter, ++y, \
32027  xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
32028  txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \
32029  tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \
32030  lxr+=_rlxr+((_errlxr-=_dlxr)<0?_errlxr+=_dyr,_slxr:0), \
32031  lyr+=_rlyr+((_errlyr-=_dlyr)<0?_errlyr+=_dyr,_slyr:0), \
32032  xl+=(y!=y1)?(txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \
32033  tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \
32034  lxl+=_rlxl+((_errlxl-=_dlxl)<0?(_errlxl+=_dyl,_slxl):0), \
32035  lyl+=_rlyl+((_errlyl-=_dlyl)<0?(_errlyl+=_dyl,_slyl):0), \
32036  _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
32037  (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \
32038  _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1, \
32039  _errlxl=_errlxn, _dlxl=_dlxn, _dyl=_dyn, _slxl=_slxn, _rlxl=_rlxn, lxl=lx1, \
32040  _errlyl=_errlyn, _dlyl=_dlyn, _dyl=_dyn, _slyl=_slyn, _rlyl=_rlyn, lyl=ly1, \
32041  _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
32042 
32043  // [internal] Draw a filled triangle.
32044  template<typename tc>
32045  CImg<T>& _draw_triangle(const int x0, const int y0,
32046  const int x1, const int y1,
32047  const int x2, const int y2,
32048  const tc *const color, const float opacity,
32049  const float brightness) {
32050  cimg_init_scanline(color,opacity);
32051  const float nbrightness = brightness<0?0:(brightness>2?2:brightness);
32052  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
32053  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1);
32054  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2);
32055  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2);
32056  if (ny0<height() && ny2>=0) {
32057  if ((nx1 - nx0)*(ny2 - ny0) - (nx2 - nx0)*(ny1 - ny0)<0)
32058  _cimg_for_triangle1(*this,xl,xr,y,nx0,ny0,nx1,ny1,nx2,ny2)
32059  cimg_draw_scanline(xl,xr,y,color,opacity,nbrightness);
32060  else
32061  _cimg_for_triangle1(*this,xl,xr,y,nx0,ny0,nx1,ny1,nx2,ny2)
32062  cimg_draw_scanline(xr,xl,y,color,opacity,nbrightness);
32063  }
32064  return *this;
32065  }
32066 
32068 
32078  template<typename tc>
32079  CImg<T>& draw_triangle(const int x0, const int y0,
32080  const int x1, const int y1,
32081  const int x2, const int y2,
32082  const tc *const color, const float opacity=1) {
32083  if (is_empty()) return *this;
32084  if (!color)
32085  throw CImgArgumentException(_cimg_instance
32086  "draw_triangle(): Specified color is (null).",
32087  cimg_instance);
32088  _draw_triangle(x0,y0,x1,y1,x2,y2,color,opacity,1);
32089  return *this;
32090  }
32091 
32093 
32104  template<typename tc>
32105  CImg<T>& draw_triangle(const int x0, const int y0,
32106  const int x1, const int y1,
32107  const int x2, const int y2,
32108  const tc *const color, const float opacity,
32109  const unsigned int pattern) {
32110  if (is_empty()) return *this;
32111  if (!color)
32112  throw CImgArgumentException(_cimg_instance
32113  "draw_triangle(): Specified color is (null).",
32114  cimg_instance);
32115  draw_line(x0,y0,x1,y1,color,opacity,pattern,true).
32116  draw_line(x1,y1,x2,y2,color,opacity,pattern,false).
32117  draw_line(x2,y2,x0,y0,color,opacity,pattern,false);
32118  return *this;
32119  }
32120 
32122 
32137  template<typename tz, typename tc>
32139  const int x0, const int y0, const float z0,
32140  const int x1, const int y1, const float z1,
32141  const int x2, const int y2, const float z2,
32142  const tc *const color, const float opacity=1,
32143  const float brightness=1) {
32144  typedef typename cimg::superset<tz,float>::type tzfloat;
32145  if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
32146  if (!color)
32147  throw CImgArgumentException(_cimg_instance
32148  "draw_triangle(): Specified color is (null).",
32149  cimg_instance);
32150  if (!is_sameXY(zbuffer))
32151  throw CImgArgumentException(_cimg_instance
32152  "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
32153  "different dimensions.",
32154  cimg_instance,
32155  zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
32156  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
32157  const float
32158  nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0),
32159  nbrightness = brightness<0?0:(brightness>2?2:brightness);
32160  const long whd = (long)_width*_height*_depth, offx = _spectrum*whd;
32161  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
32162  tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
32163  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
32164  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nz0,nz2);
32165  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nz1,nz2);
32166  if (ny0>=height() || ny2<0) return *this;
32167  tzfloat
32168  pzl = (nz1 - nz0)/(ny1 - ny0),
32169  pzr = (nz2 - nz0)/(ny2 - ny0),
32170  pzn = (nz2 - nz1)/(ny2 - ny1),
32171  zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
32172  zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
32173  _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) {
32174  if (y==ny1) { zl = nz1; pzl = pzn; }
32175  int xleft = xleft0, xright = xright0;
32176  tzfloat zleft = zl, zright = zr;
32177  if (xright<xleft) cimg::swap(xleft,xright,zleft,zright);
32178  const int dx = xright - xleft;
32179  const tzfloat pentez = (zright - zleft)/dx;
32180  if (xleft<0 && dx) zleft-=xleft*(zright - zleft)/dx;
32181  if (xleft<0) xleft = 0;
32182  if (xright>=width()-1) xright = width() - 1;
32183  T* ptrd = data(xleft,y,0,0);
32184  tz *ptrz = zbuffer.data(xleft,y);
32185  if (opacity>=1) {
32186  if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
32187  if (zleft>=(tzfloat)*ptrz) {
32188  *ptrz = (tz)zleft;
32189  const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; }
32190  ptrd-=offx;
32191  }
32192  zleft+=pentez;
32193  } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
32194  if (zleft>=(tzfloat)*ptrz) {
32195  *ptrz = (tz)zleft;
32196  const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(nbrightness*(*col++)); ptrd+=whd; }
32197  ptrd-=offx;
32198  }
32199  zleft+=pentez;
32200  } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
32201  if (zleft>=(tzfloat)*ptrz) {
32202  *ptrz = (tz)zleft;
32203  const tc *col = color;
32204  cimg_forC(*this,c) { *ptrd = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval); ptrd+=whd; }
32205  ptrd-=offx;
32206  }
32207  zleft+=pentez;
32208  }
32209  } else {
32210  if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
32211  if (zleft>=(tzfloat)*ptrz) {
32212  *ptrz = (tz)zleft;
32213  const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=whd; }
32214  ptrd-=offx;
32215  }
32216  zleft+=pentez;
32217  } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
32218  if (zleft>=(tzfloat)*ptrz) {
32219  *ptrz = (tz)zleft;
32220  const tc *col = color;
32221  cimg_forC(*this,c) { *ptrd = (T)(nopacity*nbrightness**(col++) + *ptrd*copacity); ptrd+=whd; }
32222  ptrd-=offx;
32223  }
32224  zleft+=pentez;
32225  } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
32226  if (zleft>=(tzfloat)*ptrz) {
32227  *ptrz = (tz)zleft;
32228  const tc *col = color;
32229  cimg_forC(*this,c) {
32230  const T val = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval);
32231  *ptrd = (T)(nopacity*val + *ptrd*copacity);
32232  ptrd+=whd;
32233  }
32234  ptrd-=offx;
32235  }
32236  zleft+=pentez;
32237  }
32238  }
32239  zr+=pzr; zl+=pzl;
32240  }
32241  return *this;
32242  }
32243 
32245 
32258  template<typename tc>
32259  CImg<T>& draw_triangle(const int x0, const int y0,
32260  const int x1, const int y1,
32261  const int x2, const int y2,
32262  const tc *const color,
32263  const float brightness0,
32264  const float brightness1,
32265  const float brightness2,
32266  const float opacity=1) {
32267  if (is_empty()) return *this;
32268  if (!color)
32269  throw CImgArgumentException(_cimg_instance
32270  "draw_triangle(): Specified color is (null).",
32271  cimg_instance);
32272  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
32273  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
32274  const long whd = (long)_width*_height*_depth, offx = _spectrum*whd-1;
32275  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
32276  nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
32277  nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
32278  nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
32279  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nc0,nc1);
32280  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nc0,nc2);
32281  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nc1,nc2);
32282  if (ny0>=height() || ny2<0) return *this;
32283  _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
32284  int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0;
32285  if (xright<xleft) cimg::swap(xleft,xright,cleft,cright);
32286  const int
32287  dx = xright - xleft,
32288  dc = cright>cleft?cright - cleft:cleft - cright,
32289  rc = dx?(cright - cleft)/dx:0,
32290  sc = cright>cleft?1:-1,
32291  ndc = dc-(dx?dx*(dc/dx):0);
32292  int errc = dx>>1;
32293  if (xleft<0 && dx) cleft-=xleft*(cright - cleft)/dx;
32294  if (xleft<0) xleft = 0;
32295  if (xright>=width()-1) xright = width() - 1;
32296  T* ptrd = data(xleft,y);
32297  if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
32298  const tc *col = color;
32299  cimg_forC(*this,c) {
32300  *ptrd = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256);
32301  ptrd+=whd;
32302  }
32303  ptrd-=offx;
32304  cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
32305  } else for (int x = xleft; x<=xright; ++x) {
32306  const tc *col = color;
32307  cimg_forC(*this,c) {
32308  const T val = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256);
32309  *ptrd = (T)(nopacity*val + *ptrd*copacity);
32310  ptrd+=whd;
32311  }
32312  ptrd-=offx;
32313  cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
32314  }
32315  }
32316  return *this;
32317  }
32318 
32320  template<typename tz, typename tc>
32322  const int x0, const int y0, const float z0,
32323  const int x1, const int y1, const float z1,
32324  const int x2, const int y2, const float z2,
32325  const tc *const color,
32326  const float brightness0,
32327  const float brightness1,
32328  const float brightness2,
32329  const float opacity=1) {
32330  typedef typename cimg::superset<tz,float>::type tzfloat;
32331  if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
32332  if (!color)
32333  throw CImgArgumentException(_cimg_instance
32334  "draw_triangle(): Specified color is (null).",
32335  cimg_instance);
32336  if (!is_sameXY(zbuffer))
32337  throw CImgArgumentException(_cimg_instance
32338  "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
32339  "different dimensions.",
32340  cimg_instance,
32341  zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
32342  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
32343  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
32344  const long whd = (long)_width*_height*_depth, offx = _spectrum*whd;
32345  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
32346  nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
32347  nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
32348  nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
32349  tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
32350  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1,nc0,nc1);
32351  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nz0,nz2,nc0,nc2);
32352  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nz1,nz2,nc1,nc2);
32353  if (ny0>=height() || ny2<0) return *this;
32354  tzfloat
32355  pzl = (nz1 - nz0)/(ny1 - ny0),
32356  pzr = (nz2 - nz0)/(ny2 - ny0),
32357  pzn = (nz2 - nz1)/(ny2 - ny1),
32358  zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
32359  zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
32360  _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
32361  if (y==ny1) { zl = nz1; pzl = pzn; }
32362  int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0;
32363  tzfloat zleft = zl, zright = zr;
32364  if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,cleft,cright);
32365  const int
32366  dx = xright - xleft,
32367  dc = cright>cleft?cright - cleft:cleft - cright,
32368  rc = dx?(cright-cleft)/dx:0,
32369  sc = cright>cleft?1:-1,
32370  ndc = dc-(dx?dx*(dc/dx):0);
32371  const tzfloat pentez = (zright - zleft)/dx;
32372  int errc = dx>>1;
32373  if (xleft<0 && dx) {
32374  cleft-=xleft*(cright - cleft)/dx;
32375  zleft-=xleft*(zright - zleft)/dx;
32376  }
32377  if (xleft<0) xleft = 0;
32378  if (xright>=width()-1) xright = width()-1;
32379  T *ptrd = data(xleft,y);
32380  tz *ptrz = zbuffer.data(xleft,y);
32381  if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
32382  if (zleft>=(tzfloat)*ptrz) {
32383  *ptrz = (tz)zleft;
32384  const tc *col = color;
32385  cimg_forC(*this,c) {
32386  *ptrd = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256);
32387  ptrd+=whd;
32388  }
32389  ptrd-=offx;
32390  }
32391  zleft+=pentez;
32392  cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
32393  } else for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
32394  if (zleft>=(tzfloat)*ptrz) {
32395  *ptrz = (tz)zleft;
32396  const tc *col = color;
32397  cimg_forC(*this,c) {
32398  const T val = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256);
32399  *ptrd = (T)(nopacity*val + *ptrd*copacity);
32400  ptrd+=whd;
32401  }
32402  ptrd-=offx;
32403  }
32404  zleft+=pentez;
32405  cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
32406  }
32407  zr+=pzr; zl+=pzl;
32408  }
32409  return *this;
32410  }
32411 
32413 
32425  template<typename tc1, typename tc2, typename tc3>
32426  CImg<T>& draw_triangle(const int x0, const int y0,
32427  const int x1, const int y1,
32428  const int x2, const int y2,
32429  const tc1 *const color1,
32430  const tc2 *const color2,
32431  const tc3 *const color3,
32432  const float opacity=1) {
32433  const unsigned char one = 1;
32434  cimg_forC(*this,c)
32435  get_shared_channel(c).draw_triangle(x0,y0,x1,y1,x2,y2,&one,color1[c],color2[c],color3[c],opacity);
32436  return *this;
32437  }
32438 
32440 
32457  template<typename tc>
32458  CImg<T>& draw_triangle(const int x0, const int y0,
32459  const int x1, const int y1,
32460  const int x2, const int y2,
32461  const CImg<tc>& texture,
32462  const int tx0, const int ty0,
32463  const int tx1, const int ty1,
32464  const int tx2, const int ty2,
32465  const float opacity=1,
32466  const float brightness=1) {
32467  if (is_empty()) return *this;
32468  if (texture._depth>1 || texture._spectrum<_spectrum)
32469  throw CImgArgumentException(_cimg_instance
32470  "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
32471  cimg_instance,
32472  texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
32473  if (is_overlapped(texture))
32474  return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
32475  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
32476  const float
32477  nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0),
32478  nbrightness = brightness<0?0:(brightness>2?2:brightness);
32479  const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth,
32480  offx = _spectrum*whd-1;
32481  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
32482  ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2;
32483  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1);
32484  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2);
32485  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2);
32486  if (ny0>=height() || ny2<0) return *this;
32487  _cimg_for_triangle3(*this,xleft0,txleft0,tyleft0,xright0,txright0,tyright0,y,
32488  nx0,ny0,ntx0,nty0,nx1,ny1,ntx1,nty1,nx2,ny2,ntx2,nty2) {
32489  int
32490  xleft = xleft0, xright = xright0,
32491  txleft = txleft0, txright = txright0,
32492  tyleft = tyleft0, tyright = tyright0;
32493  if (xright<xleft) cimg::swap(xleft,xright,txleft,txright,tyleft,tyright);
32494  const int
32495  dx = xright - xleft,
32496  dtx = txright>txleft?txright - txleft:txleft - txright,
32497  dty = tyright>tyleft?tyright - tyleft:tyleft - tyright,
32498  rtx = dx?(txright - txleft)/dx:0,
32499  rty = dx?(tyright - tyleft)/dx:0,
32500  stx = txright>txleft?1:-1,
32501  sty = tyright>tyleft?1:-1,
32502  ndtx = dtx - (dx?dx*(dtx/dx):0),
32503  ndty = dty - (dx?dx*(dty/dx):0);
32504  int errtx = dx>>1, errty = errtx;
32505  if (xleft<0 && dx) {
32506  txleft-=xleft*(txright - txleft)/dx;
32507  tyleft-=xleft*(tyright - tyleft)/dx;
32508  }
32509  if (xleft<0) xleft = 0;
32510  if (xright>=width()-1) xright = width()-1;
32511  T* ptrd = data(xleft,y,0,0);
32512  if (opacity>=1) {
32513  if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
32514  const tc *col = texture.data(txleft,tyleft);
32515  cimg_forC(*this,c) {
32516  *ptrd = (T)*col;
32517  ptrd+=whd; col+=twhd;
32518  }
32519  ptrd-=offx;
32520  txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
32521  tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
32522  } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) {
32523  const tc *col = texture.data(txleft,tyleft);
32524  cimg_forC(*this,c) {
32525  *ptrd = (T)(nbrightness**col);
32526  ptrd+=whd; col+=twhd;
32527  }
32528  ptrd-=offx;
32529  txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
32530  tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
32531  } else for (int x = xleft; x<=xright; ++x) {
32532  const tc *col = texture.data(txleft,tyleft);
32533  cimg_forC(*this,c) {
32534  *ptrd = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval);
32535  ptrd+=whd; col+=twhd;
32536  }
32537  ptrd-=offx;
32538  txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
32539  tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
32540  }
32541  } else {
32542  if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
32543  const tc *col = texture.data(txleft,tyleft);
32544  cimg_forC(*this,c) {
32545  *ptrd = (T)(nopacity**col + *ptrd*copacity);
32546  ptrd+=whd; col+=twhd;
32547  }
32548  ptrd-=offx;
32549  txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
32550  tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
32551  } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) {
32552  const tc *col = texture.data(txleft,tyleft);
32553  cimg_forC(*this,c) {
32554  *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity);
32555  ptrd+=whd; col+=twhd;
32556  }
32557  ptrd-=offx;
32558  txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
32559  tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
32560  } else for (int x = xleft; x<=xright; ++x) {
32561  const tc *col = texture.data(txleft,tyleft);
32562  cimg_forC(*this,c) {
32563  const T val = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval);
32564  *ptrd = (T)(nopacity*val + *ptrd*copacity);
32565  ptrd+=whd; col+=twhd;
32566  }
32567  ptrd-=offx;
32568  txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
32569  tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
32570  }
32571  }
32572  }
32573  return *this;
32574  }
32575 
32577  template<typename tc>
32578  CImg<T>& draw_triangle(const int x0, const int y0, const float z0,
32579  const int x1, const int y1, const float z1,
32580  const int x2, const int y2, const float z2,
32581  const CImg<tc>& texture,
32582  const int tx0, const int ty0,
32583  const int tx1, const int ty1,
32584  const int tx2, const int ty2,
32585  const float opacity=1,
32586  const float brightness=1) {
32587  if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
32588  if (texture._depth>1 || texture._spectrum<_spectrum)
32589  throw CImgArgumentException(_cimg_instance
32590  "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
32591  cimg_instance,
32592  texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
32593  if (is_overlapped(texture))
32594  return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
32595  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
32596  const float
32597  nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0),
32598  nbrightness = brightness<0?0:(brightness>2?2:brightness);
32599  const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth,
32600  offx = _spectrum*whd-1;
32601  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
32602  float
32603  ntx0 = tx0/z0, nty0 = ty0/z0,
32604  ntx1 = tx1/z1, nty1 = ty1/z1,
32605  ntx2 = tx2/z2, nty2 = ty2/z2,
32606  nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2;
32607  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1);
32608  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2);
32609  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2);
32610  if (ny0>=height() || ny2<0) return *this;
32611  float
32612  ptxl = (ntx1 - ntx0)/(ny1 - ny0),
32613  ptxr = (ntx2 - ntx0)/(ny2 - ny0),
32614  ptxn = (ntx2 - ntx1)/(ny2 - ny1),
32615  ptyl = (nty1 - nty0)/(ny1 - ny0),
32616  ptyr = (nty2 - nty0)/(ny2 - ny0),
32617  ptyn = (nty2 - nty1)/(ny2 - ny1),
32618  pzl = (nz1 - nz0)/(ny1 - ny0),
32619  pzr = (nz2 - nz0)/(ny2 - ny0),
32620  pzn = (nz2 - nz1)/(ny2 - ny1),
32621  zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
32622  txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
32623  tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
32624  zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))),
32625  txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):
32626  (ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
32627  tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):
32628  (ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
32629  _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) {
32630  if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
32631  int xleft = xleft0, xright = xright0;
32632  float
32633  zleft = zl, zright = zr,
32634  txleft = txl, txright = txr,
32635  tyleft = tyl, tyright = tyr;
32636  if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright);
32637  const int dx = xright - xleft;
32638  const float
32639  pentez = (zright - zleft)/dx,
32640  pentetx = (txright - txleft)/dx,
32641  pentety = (tyright - tyleft)/dx;
32642  if (xleft<0 && dx) {
32643  zleft-=xleft*(zright - zleft)/dx;
32644  txleft-=xleft*(txright - txleft)/dx;
32645  tyleft-=xleft*(tyright - tyleft)/dx;
32646  }
32647  if (xleft<0) xleft = 0;
32648  if (xright>=width()-1) xright = width()-1;
32649  T* ptrd = data(xleft,y,0,0);
32650  if (opacity>=1) {
32651  if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
32652  const float invz = 1/zleft;
32653  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
32654  cimg_forC(*this,c) {
32655  *ptrd = (T)*col;
32656  ptrd+=whd; col+=twhd;
32657  }
32658  ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
32659  } else if (nbrightness<1) for (int x=xleft; x<=xright; ++x) {
32660  const float invz = 1/zleft;
32661  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
32662  cimg_forC(*this,c) {
32663  *ptrd = (T)(nbrightness**col);
32664  ptrd+=whd; col+=twhd;
32665  }
32666  ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
32667  } else for (int x = xleft; x<=xright; ++x) {
32668  const float invz = 1/zleft;
32669  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
32670  cimg_forC(*this,c) {
32671  *ptrd = (T)((2-nbrightness)**col + (nbrightness-1)*maxval);
32672  ptrd+=whd; col+=twhd;
32673  }
32674  ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
32675  }
32676  } else {
32677  if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
32678  const float invz = 1/zleft;
32679  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
32680  cimg_forC(*this,c) {
32681  *ptrd = (T)(nopacity**col + *ptrd*copacity);
32682  ptrd+=whd; col+=twhd;
32683  }
32684  ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
32685  } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) {
32686  const float invz = 1/zleft;
32687  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
32688  cimg_forC(*this,c) {
32689  *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity);
32690  ptrd+=whd; col+=twhd;
32691  }
32692  ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
32693  } else for (int x = xleft; x<=xright; ++x) {
32694  const float invz = 1/zleft;
32695  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
32696  cimg_forC(*this,c) {
32697  const T val = (T)((2-nbrightness)**col + (nbrightness-1)*maxval);
32698  *ptrd = (T)(nopacity*val + *ptrd*copacity);
32699  ptrd+=whd; col+=twhd;
32700  }
32701  ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
32702  }
32703  }
32704  zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
32705  }
32706  return *this;
32707  }
32708 
32710  template<typename tz, typename tc>
32712  const int x0, const int y0, const float z0,
32713  const int x1, const int y1, const float z1,
32714  const int x2, const int y2, const float z2,
32715  const CImg<tc>& texture,
32716  const int tx0, const int ty0,
32717  const int tx1, const int ty1,
32718  const int tx2, const int ty2,
32719  const float opacity=1,
32720  const float brightness=1) {
32721  typedef typename cimg::superset<tz,float>::type tzfloat;
32722  if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
32723  if (!is_sameXY(zbuffer))
32724  throw CImgArgumentException(_cimg_instance
32725  "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
32726  "different dimensions.",
32727  cimg_instance,
32728  zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
32729 
32730  if (texture._depth>1 || texture._spectrum<_spectrum)
32731  throw CImgArgumentException(_cimg_instance
32732  "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
32733  cimg_instance,
32734  texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
32735  if (is_overlapped(texture))
32736  return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
32737  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
32738  const float
32739  nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0),
32740  nbrightness = brightness<0?0:(brightness>2?2:brightness);
32741  const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth,
32742  offx = _spectrum*whd;
32743  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
32744  float
32745  ntx0 = tx0/z0, nty0 = ty0/z0,
32746  ntx1 = tx1/z1, nty1 = ty1/z1,
32747  ntx2 = tx2/z2, nty2 = ty2/z2;
32748  tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
32749  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1);
32750  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2);
32751  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2);
32752  if (ny0>=height() || ny2<0) return *this;
32753  float
32754  ptxl = (ntx1 - ntx0)/(ny1 - ny0),
32755  ptxr = (ntx2 - ntx0)/(ny2 - ny0),
32756  ptxn = (ntx2 - ntx1)/(ny2 - ny1),
32757  ptyl = (nty1 - nty0)/(ny1 - ny0),
32758  ptyr = (nty2 - nty0)/(ny2 - ny0),
32759  ptyn = (nty2 - nty1)/(ny2 - ny1),
32760  txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
32761  tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
32762  txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):
32763  (ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
32764  tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):
32765  (ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
32766  tzfloat
32767  pzl = (nz1 - nz0)/(ny1 - ny0),
32768  pzr = (nz2 - nz0)/(ny2 - ny0),
32769  pzn = (nz2 - nz1)/(ny2 - ny1),
32770  zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
32771  zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
32772  _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) {
32773  if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
32774  int xleft = xleft0, xright = xright0;
32775  float txleft = txl, txright = txr, tyleft = tyl, tyright = tyr;
32776  tzfloat zleft = zl, zright = zr;
32777  if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright);
32778  const int dx = xright - xleft;
32779  const float pentetx = (txright - txleft)/dx, pentety = (tyright - tyleft)/dx;
32780  const tzfloat pentez = (zright - zleft)/dx;
32781  if (xleft<0 && dx) {
32782  zleft-=xleft*(zright - zleft)/dx;
32783  txleft-=xleft*(txright - txleft)/dx;
32784  tyleft-=xleft*(tyright - tyleft)/dx;
32785  }
32786  if (xleft<0) xleft = 0;
32787  if (xright>=width()-1) xright = width()-1;
32788  T *ptrd = data(xleft,y,0,0);
32789  tz *ptrz = zbuffer.data(xleft,y);
32790  if (opacity>=1) {
32791  if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
32792  if (zleft>=(tzfloat)*ptrz) {
32793  *ptrz = (tz)zleft;
32794  const tzfloat invz = 1/zleft;
32795  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
32796  cimg_forC(*this,c) {
32797  *ptrd = (T)*col;
32798  ptrd+=whd; col+=twhd;
32799  }
32800  ptrd-=offx;
32801  }
32802  zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
32803  } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
32804  if (zleft>=(tzfloat)*ptrz) {
32805  *ptrz = (tz)zleft;
32806  const tzfloat invz = 1/zleft;
32807  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
32808  cimg_forC(*this,c) {
32809  *ptrd = (T)(nbrightness**col);
32810  ptrd+=whd; col+=twhd;
32811  }
32812  ptrd-=offx;
32813  }
32814  zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
32815  } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
32816  if (zleft>=(tzfloat)*ptrz) {
32817  *ptrz = (tz)zleft;
32818  const tzfloat invz = 1/zleft;
32819  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
32820  cimg_forC(*this,c) {
32821  *ptrd = (T)((2-nbrightness)**col + (nbrightness-1)*maxval);
32822  ptrd+=whd; col+=twhd;
32823  }
32824  ptrd-=offx;
32825  }
32826  zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
32827  }
32828  } else {
32829  if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
32830  if (zleft>=(tzfloat)*ptrz) {
32831  *ptrz = (tz)zleft;
32832  const tzfloat invz = 1/zleft;
32833  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
32834  cimg_forC(*this,c) {
32835  *ptrd = (T)(nopacity**col + *ptrd*copacity);
32836  ptrd+=whd; col+=twhd;
32837  }
32838  ptrd-=offx;
32839  }
32840  zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
32841  } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
32842  if (zleft>=(tzfloat)*ptrz) {
32843  *ptrz = (tz)zleft;
32844  const tzfloat invz = 1/zleft;
32845  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
32846  cimg_forC(*this,c) {
32847  *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity);
32848  ptrd+=whd; col+=twhd;
32849  }
32850  ptrd-=offx;
32851  }
32852  zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
32853  } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
32854  if (zleft>=(tzfloat)*ptrz) {
32855  *ptrz = (tz)zleft;
32856  const tzfloat invz = 1/zleft;
32857  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
32858  cimg_forC(*this,c) {
32859  const T val = (T)((2-nbrightness)**col + (nbrightness-1)*maxval);
32860  *ptrd = (T)(nopacity*val + *ptrd*copacity);
32861  ptrd+=whd; col+=twhd;
32862  }
32863  ptrd-=offx;
32864  }
32865  zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
32866  }
32867  }
32868  zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
32869  }
32870  return *this;
32871  }
32872 
32874 
32891  template<typename tc, typename tl>
32892  CImg<T>& draw_triangle(const int x0, const int y0,
32893  const int x1, const int y1,
32894  const int x2, const int y2,
32895  const tc *const color,
32896  const CImg<tl>& light,
32897  const int lx0, const int ly0,
32898  const int lx1, const int ly1,
32899  const int lx2, const int ly2,
32900  const float opacity=1) {
32901  if (is_empty()) return *this;
32902  if (!color)
32903  throw CImgArgumentException(_cimg_instance
32904  "draw_triangle(): Specified color is (null).",
32905  cimg_instance);
32906  if (light._depth>1 || light._spectrum<_spectrum)
32907  throw CImgArgumentException(_cimg_instance
32908  "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
32909  cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
32910  if (is_overlapped(light)) return draw_triangle(x0,y0,x1,y1,x2,y2,color,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
32911  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
32912  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
32913  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
32914  nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
32915  const long whd = (long)_width*_height*_depth, offx = _spectrum*whd-1;
32916  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nlx0,nlx1,nly0,nly1);
32917  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nlx0,nlx2,nly0,nly2);
32918  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nlx1,nlx2,nly1,nly2);
32919  if (ny0>=height() || ny2<0) return *this;
32920  _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
32921  nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
32922  int
32923  xleft = xleft0, xright = xright0,
32924  lxleft = lxleft0, lxright = lxright0,
32925  lyleft = lyleft0, lyright = lyright0;
32926  if (xright<xleft) cimg::swap(xleft,xright,lxleft,lxright,lyleft,lyright);
32927  const int
32928  dx = xright - xleft,
32929  dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
32930  dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
32931  rlx = dx?(lxright - lxleft)/dx:0,
32932  rly = dx?(lyright - lyleft)/dx:0,
32933  slx = lxright>lxleft?1:-1,
32934  sly = lyright>lyleft?1:-1,
32935  ndlx = dlx - (dx?dx*(dlx/dx):0),
32936  ndly = dly - (dx?dx*(dly/dx):0);
32937  int errlx = dx>>1, errly = errlx;
32938  if (xleft<0 && dx) {
32939  lxleft-=xleft*(lxright - lxleft)/dx;
32940  lyleft-=xleft*(lyright - lyleft)/dx;
32941  }
32942  if (xleft<0) xleft = 0;
32943  if (xright>=width()-1) xright = width()-1;
32944  T* ptrd = data(xleft,y,0,0);
32945  if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
32946  const tc *col = color;
32947  cimg_forC(*this,c) {
32948  const tl l = light(lxleft,lyleft,c);
32949  *ptrd = (T)(l<1?l**(col++):((2-l)**(col++)+(l-1)*maxval));
32950  ptrd+=whd;
32951  }
32952  ptrd-=offx;
32953  lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
32954  lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
32955  } else for (int x = xleft; x<=xright; ++x) {
32956  const tc *col = color;
32957  cimg_forC(*this,c) {
32958  const tl l = light(lxleft,lyleft,c);
32959  const T val = (T)(l<1?l**(col++):((2-l)**(col++)+(l-1)*maxval));
32960  *ptrd = (T)(nopacity*val + *ptrd*copacity);
32961  ptrd+=whd;
32962  }
32963  ptrd-=offx;
32964  lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
32965  lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
32966  }
32967  }
32968  return *this;
32969  }
32970 
32972  template<typename tz, typename tc, typename tl>
32974  const int x0, const int y0, const float z0,
32975  const int x1, const int y1, const float z1,
32976  const int x2, const int y2, const float z2,
32977  const tc *const color,
32978  const CImg<tl>& light,
32979  const int lx0, const int ly0,
32980  const int lx1, const int ly1,
32981  const int lx2, const int ly2,
32982  const float opacity=1) {
32983  typedef typename cimg::superset<tz,float>::type tzfloat;
32984  if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
32985  if (!color)
32986  throw CImgArgumentException(_cimg_instance
32987  "draw_triangle(): Specified color is (null).",
32988  cimg_instance);
32989  if (light._depth>1 || light._spectrum<_spectrum)
32990  throw CImgArgumentException(_cimg_instance
32991  "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
32992  cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
32993  if (!is_sameXY(zbuffer))
32994  throw CImgArgumentException(_cimg_instance
32995  "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
32996  "different dimensions.",
32997  cimg_instance,
32998  zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
32999  if (is_overlapped(light)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,
33000  +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
33001  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
33002  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
33003  const long whd = (long)_width*_height*_depth, offx = _spectrum*whd;
33004  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
33005  nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
33006  tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
33007  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nlx0,nlx1,nly0,nly1,nz0,nz1);
33008  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nlx0,nlx2,nly0,nly2,nz0,nz2);
33009  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nlx1,nlx2,nly1,nly2,nz1,nz2);
33010  if (ny0>=height() || ny2<0) return *this;
33011  tzfloat
33012  pzl = (nz1 - nz0)/(ny1 - ny0),
33013  pzr = (nz2 - nz0)/(ny2 - ny0),
33014  pzn = (nz2 - nz1)/(ny2 - ny1),
33015  zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
33016  zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
33017  _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
33018  nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
33019  if (y==ny1) { zl = nz1; pzl = pzn; }
33020  int
33021  xleft = xleft0, xright = xright0,
33022  lxleft = lxleft0, lxright = lxright0,
33023  lyleft = lyleft0, lyright = lyright0;
33024  tzfloat zleft = zl, zright = zr;
33025  if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,lxleft,lxright,lyleft,lyright);
33026  const int
33027  dx = xright - xleft,
33028  dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
33029  dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
33030  rlx = dx?(lxright - lxleft)/dx:0,
33031  rly = dx?(lyright - lyleft)/dx:0,
33032  slx = lxright>lxleft?1:-1,
33033  sly = lyright>lyleft?1:-1,
33034  ndlx = dlx - (dx?dx*(dlx/dx):0),
33035  ndly = dly - (dx?dx*(dly/dx):0);
33036  const tzfloat pentez = (zright - zleft)/dx;
33037  int errlx = dx>>1, errly = errlx;
33038  if (xleft<0 && dx) {
33039  zleft-=xleft*(zright - zleft)/dx;
33040  lxleft-=xleft*(lxright - lxleft)/dx;
33041  lyleft-=xleft*(lyright - lyleft)/dx;
33042  }
33043  if (xleft<0) xleft = 0;
33044  if (xright>=width()-1) xright = width()-1;
33045  T *ptrd = data(xleft,y,0,0);
33046  tz *ptrz = zbuffer.data(xleft,y);
33047  if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
33048  if (zleft>=(tzfloat)*ptrz) {
33049  *ptrz = (tz)zleft;
33050  const tc *col = color;
33051  cimg_forC(*this,c) {
33052  const tl l = light(lxleft,lyleft,c);
33053  const tc cval = *(col++);
33054  *ptrd = (T)(l<1?l*cval:(2-l)*cval+(l-1)*maxval);
33055  ptrd+=whd;
33056  }
33057  ptrd-=offx;
33058  }
33059  zleft+=pentez;
33060  lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
33061  lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
33062  } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
33063  if (zleft>=(tzfloat)*ptrz) {
33064  *ptrz = (tz)zleft;
33065  const tc *col = color;
33066  cimg_forC(*this,c) {
33067  const tl l = light(lxleft,lyleft,c);
33068  const tc cval = *(col++);
33069  const T val = (T)(l<1?l*cval:(2-l)*cval+(l-1)*maxval);
33070  *ptrd = (T)(nopacity*val + *ptrd*copacity);
33071  ptrd+=whd;
33072  }
33073  ptrd-=offx;
33074  }
33075  zleft+=pentez;
33076  lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
33077  lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
33078  }
33079  zr+=pzr; zl+=pzl;
33080  }
33081  return *this;
33082  }
33083 
33085 
33104  template<typename tc>
33105  CImg<T>& draw_triangle(const int x0, const int y0,
33106  const int x1, const int y1,
33107  const int x2, const int y2,
33108  const CImg<tc>& texture,
33109  const int tx0, const int ty0,
33110  const int tx1, const int ty1,
33111  const int tx2, const int ty2,
33112  const float brightness0,
33113  const float brightness1,
33114  const float brightness2,
33115  const float opacity=1) {
33116  if (is_empty()) return *this;
33117  if (texture._depth>1 || texture._spectrum<_spectrum)
33118  throw CImgArgumentException(_cimg_instance
33119  "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
33120  cimg_instance,
33121  texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
33122  if (is_overlapped(texture))
33123  return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
33124  brightness0,brightness1,brightness2,opacity);
33125  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
33126  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
33127  const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth,
33128  offx = _spectrum*whd-1;
33129  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
33130  ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2,
33131  nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
33132  nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
33133  nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
33134  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nc0,nc1);
33135  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nc0,nc2);
33136  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nc1,nc2);
33137  if (ny0>=height() || ny2<0) return *this;
33138  _cimg_for_triangle4(*this,xleft0,cleft0,txleft0,tyleft0,xright0,cright0,txright0,tyright0,y,
33139  nx0,ny0,nc0,ntx0,nty0,nx1,ny1,nc1,ntx1,nty1,nx2,ny2,nc2,ntx2,nty2) {
33140  int
33141  xleft = xleft0, xright = xright0,
33142  cleft = cleft0, cright = cright0,
33143  txleft = txleft0, txright = txright0,
33144  tyleft = tyleft0, tyright = tyright0;
33145  if (xright<xleft) cimg::swap(xleft,xright,cleft,cright,txleft,txright,tyleft,tyright);
33146  const int
33147  dx = xright - xleft,
33148  dc = cright>cleft?cright - cleft:cleft - cright,
33149  dtx = txright>txleft?txright - txleft:txleft - txright,
33150  dty = tyright>tyleft?tyright - tyleft:tyleft - tyright,
33151  rc = dx?(cright - cleft)/dx:0,
33152  rtx = dx?(txright - txleft)/dx:0,
33153  rty = dx?(tyright - tyleft)/dx:0,
33154  sc = cright>cleft?1:-1,
33155  stx = txright>txleft?1:-1,
33156  sty = tyright>tyleft?1:-1,
33157  ndc = dc - (dx?dx*(dc/dx):0),
33158  ndtx = dtx - (dx?dx*(dtx/dx):0),
33159  ndty = dty - (dx?dx*(dty/dx):0);
33160  int errc = dx>>1, errtx = errc, errty = errc;
33161  if (xleft<0 && dx) {
33162  cleft-=xleft*(cright - cleft)/dx;
33163  txleft-=xleft*(txright - txleft)/dx;
33164  tyleft-=xleft*(tyright - tyleft)/dx;
33165  }
33166  if (xleft<0) xleft = 0;
33167  if (xright>=width()-1) xright = width()-1;
33168  T* ptrd = data(xleft,y,0,0);
33169  if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
33170  const tc *col = texture.data(txleft,tyleft);
33171  cimg_forC(*this,c) {
33172  *ptrd = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
33173  ptrd+=whd; col+=twhd;
33174  }
33175  ptrd-=offx;
33176  cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
33177  txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
33178  tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
33179  } else for (int x = xleft; x<=xright; ++x) {
33180  const tc *col = texture.data(txleft,tyleft);
33181  cimg_forC(*this,c) {
33182  const T val = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
33183  *ptrd = (T)(nopacity*val + *ptrd*copacity);
33184  ptrd+=whd; col+=twhd;
33185  }
33186  ptrd-=offx;
33187  cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
33188  txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
33189  tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
33190  }
33191  }
33192  return *this;
33193  }
33194 
33196  template<typename tc>
33197  CImg<T>& draw_triangle(const int x0, const int y0, const float z0,
33198  const int x1, const int y1, const float z1,
33199  const int x2, const int y2, const float z2,
33200  const CImg<tc>& texture,
33201  const int tx0, const int ty0,
33202  const int tx1, const int ty1,
33203  const int tx2, const int ty2,
33204  const float brightness0,
33205  const float brightness1,
33206  const float brightness2,
33207  const float opacity=1) {
33208  if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
33209  if (texture._depth>1 || texture._spectrum<_spectrum)
33210  throw CImgArgumentException(_cimg_instance
33211  "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
33212  cimg_instance,
33213  texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
33214  if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
33215  brightness0,brightness1,brightness2,opacity);
33216  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
33217  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
33218  const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth,
33219  offx = _spectrum*whd-1;
33220  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
33221  nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
33222  nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
33223  nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
33224  float
33225  ntx0 = tx0/z0, nty0 = ty0/z0,
33226  ntx1 = tx1/z1, nty1 = ty1/z1,
33227  ntx2 = tx2/z2, nty2 = ty2/z2,
33228  nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2;
33229  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1,nc0,nc1);
33230  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2,nc0,nc2);
33231  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2,nc1,nc2);
33232  if (ny0>=height() || ny2<0) return *this;
33233  float
33234  ptxl = (ntx1 - ntx0)/(ny1 - ny0),
33235  ptxr = (ntx2 - ntx0)/(ny2 - ny0),
33236  ptxn = (ntx2 - ntx1)/(ny2 - ny1),
33237  ptyl = (nty1 - nty0)/(ny1 - ny0),
33238  ptyr = (nty2 - nty0)/(ny2 - ny0),
33239  ptyn = (nty2 - nty1)/(ny2 - ny1),
33240  pzl = (nz1 - nz0)/(ny1 - ny0),
33241  pzr = (nz2 - nz0)/(ny2 - ny0),
33242  pzn = (nz2 - nz1)/(ny2 - ny1),
33243  zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
33244  txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
33245  tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
33246  zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))),
33247  txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):
33248  (ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
33249  tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):
33250  (ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
33251  _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
33252  if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
33253  int
33254  xleft = xleft0, xright = xright0,
33255  cleft = cleft0, cright = cright0;
33256  float
33257  zleft = zl, zright = zr,
33258  txleft = txl, txright = txr,
33259  tyleft = tyl, tyright = tyr;
33260  if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,cleft,cright);
33261  const int
33262  dx = xright - xleft,
33263  dc = cright>cleft?cright - cleft:cleft - cright,
33264  rc = dx?(cright - cleft)/dx:0,
33265  sc = cright>cleft?1:-1,
33266  ndc = dc - (dx?dx*(dc/dx):0);
33267  const float
33268  pentez = (zright - zleft)/dx,
33269  pentetx = (txright - txleft)/dx,
33270  pentety = (tyright - tyleft)/dx;
33271  int errc = dx>>1;
33272  if (xleft<0 && dx) {
33273  cleft-=xleft*(cright - cleft)/dx;
33274  zleft-=xleft*(zright - zleft)/dx;
33275  txleft-=xleft*(txright - txleft)/dx;
33276  tyleft-=xleft*(tyright - tyleft)/dx;
33277  }
33278  if (xleft<0) xleft = 0;
33279  if (xright>=width()-1) xright = width()-1;
33280  T* ptrd = data(xleft,y,0,0);
33281  if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
33282  const float invz = 1/zleft;
33283  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
33284  cimg_forC(*this,c) {
33285  *ptrd = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
33286  ptrd+=whd; col+=twhd;
33287  }
33288  ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
33289  cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
33290  } else for (int x = xleft; x<=xright; ++x) {
33291  const float invz = 1/zleft;
33292  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
33293  cimg_forC(*this,c) {
33294  const T val = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
33295  *ptrd = (T)(nopacity*val + *ptrd*copacity);
33296  ptrd+=whd; col+=twhd;
33297  }
33298  ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
33299  cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
33300  }
33301  zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
33302  }
33303  return *this;
33304  }
33305 
33307  template<typename tz, typename tc>
33309  const int x0, const int y0, const float z0,
33310  const int x1, const int y1, const float z1,
33311  const int x2, const int y2, const float z2,
33312  const CImg<tc>& texture,
33313  const int tx0, const int ty0,
33314  const int tx1, const int ty1,
33315  const int tx2, const int ty2,
33316  const float brightness0,
33317  const float brightness1,
33318  const float brightness2,
33319  const float opacity=1) {
33320  typedef typename cimg::superset<tz,float>::type tzfloat;
33321  if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
33322  if (!is_sameXY(zbuffer))
33323  throw CImgArgumentException(_cimg_instance
33324  "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
33325  "different dimensions.",
33326  cimg_instance,
33327  zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
33328  if (texture._depth>1 || texture._spectrum<_spectrum)
33329  throw CImgArgumentException(_cimg_instance
33330  "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
33331  cimg_instance,
33332  texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
33333  if (is_overlapped(texture))
33334  return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
33335  brightness0,brightness1,brightness2,opacity);
33336  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
33337  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
33338  const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth,
33339  offx = _spectrum*whd;
33340  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
33341  nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
33342  nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
33343  nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
33344  float
33345  ntx0 = tx0/z0, nty0 = ty0/z0,
33346  ntx1 = tx1/z1, nty1 = ty1/z1,
33347  ntx2 = tx2/z2, nty2 = ty2/z2;
33348  tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
33349  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1,nc0,nc1);
33350  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2,nc0,nc2);
33351  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2,nc1,nc2);
33352  if (ny0>=height() || ny2<0) return *this;
33353  float
33354  ptxl = (ntx1 - ntx0)/(ny1 - ny0),
33355  ptxr = (ntx2 - ntx0)/(ny2 - ny0),
33356  ptxn = (ntx2 - ntx1)/(ny2 - ny1),
33357  ptyl = (nty1 - nty0)/(ny1 - ny0),
33358  ptyr = (nty2 - nty0)/(ny2 - ny0),
33359  ptyn = (nty2 - nty1)/(ny2 - ny1),
33360  txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
33361  tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
33362  txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):
33363  (ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
33364  tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):
33365  (ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
33366  tzfloat
33367  pzl = (nz1 - nz0)/(ny1 - ny0),
33368  pzr = (nz2 - nz0)/(ny2 - ny0),
33369  pzn = (nz2 - nz1)/(ny2 - ny1),
33370  zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
33371  zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
33372  _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
33373  if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
33374  int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0;
33375  float txleft = txl, txright = txr, tyleft = tyl, tyright = tyr;
33376  tzfloat zleft = zl, zright = zr;
33377  if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,cleft,cright);
33378  const int
33379  dx = xright - xleft,
33380  dc = cright>cleft?cright - cleft:cleft - cright,
33381  rc = dx?(cright - cleft)/dx:0,
33382  sc = cright>cleft?1:-1,
33383  ndc = dc - (dx?dx*(dc/dx):0);
33384  float pentetx = (txright - txleft)/dx, pentety = (tyright - tyleft)/dx;
33385  const tzfloat pentez = (zright - zleft)/dx;
33386  int errc = dx>>1;
33387  if (xleft<0 && dx) {
33388  cleft-=xleft*(cright - cleft)/dx;
33389  zleft-=xleft*(zright - zleft)/dx;
33390  txleft-=xleft*(txright - txleft)/dx;
33391  tyleft-=xleft*(tyright - tyleft)/dx;
33392  }
33393  if (xleft<0) xleft = 0;
33394  if (xright>=width()-1) xright = width()-1;
33395  T* ptrd = data(xleft,y);
33396  tz *ptrz = zbuffer.data(xleft,y);
33397  if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
33398  if (zleft>=(tzfloat)*ptrz) {
33399  *ptrz = (tz)zleft;
33400  const tzfloat invz = 1/zleft;
33401  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
33402  cimg_forC(*this,c) {
33403  *ptrd = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
33404  ptrd+=whd; col+=twhd;
33405  }
33406  ptrd-=offx;
33407  }
33408  zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
33409  cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
33410  } else for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
33411  if (zleft>=(tzfloat)*ptrz) {
33412  *ptrz = (tz)zleft;
33413  const tzfloat invz = 1/zleft;
33414  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
33415  cimg_forC(*this,c) {
33416  const T val = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
33417  *ptrd = (T)(nopacity*val + *ptrd*copacity);
33418  ptrd+=whd; col+=twhd;
33419  }
33420  ptrd-=offx;
33421  }
33422  zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
33423  cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
33424  }
33425  zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
33426  }
33427  return *this;
33428  }
33429 
33431 
33454  template<typename tc, typename tl>
33455  CImg<T>& draw_triangle(const int x0, const int y0,
33456  const int x1, const int y1,
33457  const int x2, const int y2,
33458  const CImg<tc>& texture,
33459  const int tx0, const int ty0,
33460  const int tx1, const int ty1,
33461  const int tx2, const int ty2,
33462  const CImg<tl>& light,
33463  const int lx0, const int ly0,
33464  const int lx1, const int ly1,
33465  const int lx2, const int ly2,
33466  const float opacity=1) {
33467  if (is_empty()) return *this;
33468  if (texture._depth>1 || texture._spectrum<_spectrum)
33469  throw CImgArgumentException(_cimg_instance
33470  "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
33471  cimg_instance,
33472  texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
33473  if (light._depth>1 || light._spectrum<_spectrum)
33474  throw CImgArgumentException(_cimg_instance
33475  "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
33476  cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
33477  if (is_overlapped(texture))
33478  return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
33479  if (is_overlapped(light))
33480  return draw_triangle(x0,y0,x1,y1,x2,y2,texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
33481  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
33482  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
33483  const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth,
33484  offx = _spectrum*whd-1;
33485  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
33486  ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2,
33487  nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
33488  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1);
33489  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2);
33490  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2);
33491  if (ny0>=height() || ny2<0) return *this;
33492  _cimg_for_triangle5(*this,xleft0,lxleft0,lyleft0,txleft0,tyleft0,xright0,lxright0,lyright0,txright0,tyright0,y,
33493  nx0,ny0,nlx0,nly0,ntx0,nty0,nx1,ny1,nlx1,nly1,ntx1,nty1,nx2,ny2,nlx2,nly2,ntx2,nty2) {
33494  int
33495  xleft = xleft0, xright = xright0,
33496  lxleft = lxleft0, lxright = lxright0,
33497  lyleft = lyleft0, lyright = lyright0,
33498  txleft = txleft0, txright = txright0,
33499  tyleft = tyleft0, tyright = tyright0;
33500  if (xright<xleft) cimg::swap(xleft,xright,lxleft,lxright,lyleft,lyright,txleft,txright,tyleft,tyright);
33501  const int
33502  dx = xright - xleft,
33503  dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
33504  dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
33505  dtx = txright>txleft?txright - txleft:txleft - txright,
33506  dty = tyright>tyleft?tyright - tyleft:tyleft - tyright,
33507  rlx = dx?(lxright - lxleft)/dx:0,
33508  rly = dx?(lyright - lyleft)/dx:0,
33509  rtx = dx?(txright - txleft)/dx:0,
33510  rty = dx?(tyright - tyleft)/dx:0,
33511  slx = lxright>lxleft?1:-1,
33512  sly = lyright>lyleft?1:-1,
33513  stx = txright>txleft?1:-1,
33514  sty = tyright>tyleft?1:-1,
33515  ndlx = dlx - (dx?dx*(dlx/dx):0),
33516  ndly = dly - (dx?dx*(dly/dx):0),
33517  ndtx = dtx - (dx?dx*(dtx/dx):0),
33518  ndty = dty - (dx?dx*(dty/dx):0);
33519  int errlx = dx>>1, errly = errlx, errtx = errlx, errty = errlx;
33520  if (xleft<0 && dx) {
33521  lxleft-=xleft*(lxright - lxleft)/dx;
33522  lyleft-=xleft*(lyright - lyleft)/dx;
33523  txleft-=xleft*(txright - txleft)/dx;
33524  tyleft-=xleft*(tyright - tyleft)/dx;
33525  }
33526  if (xleft<0) xleft = 0;
33527  if (xright>=width()-1) xright = width()-1;
33528  T* ptrd = data(xleft,y,0,0);
33529  if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
33530  const tc *col = texture.data(txleft,tyleft);
33531  cimg_forC(*this,c) {
33532  const tl l = light(lxleft,lyleft,c);
33533  *ptrd = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
33534  ptrd+=whd; col+=twhd;
33535  }
33536  ptrd-=offx;
33537  lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
33538  lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
33539  txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
33540  tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
33541  } else for (int x = xleft; x<=xright; ++x) {
33542  const tc *col = texture.data(txleft,tyleft);
33543  cimg_forC(*this,c) {
33544  const tl l = light(lxleft,lyleft,c);
33545  const T val = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
33546  *ptrd = (T)(nopacity*val + *ptrd*copacity);
33547  ptrd+=whd; col+=twhd;
33548  }
33549  ptrd-=offx;
33550  lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
33551  lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
33552  txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
33553  tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
33554  }
33555  }
33556  return *this;
33557  }
33558 
33560  template<typename tc, typename tl>
33561  CImg<T>& draw_triangle(const int x0, const int y0, const float z0,
33562  const int x1, const int y1, const float z1,
33563  const int x2, const int y2, const float z2,
33564  const CImg<tc>& texture,
33565  const int tx0, const int ty0,
33566  const int tx1, const int ty1,
33567  const int tx2, const int ty2,
33568  const CImg<tl>& light,
33569  const int lx0, const int ly0,
33570  const int lx1, const int ly1,
33571  const int lx2, const int ly2,
33572  const float opacity=1) {
33573  if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
33574  if (texture._depth>1 || texture._spectrum<_spectrum)
33575  throw CImgArgumentException(_cimg_instance
33576  "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
33577  cimg_instance,
33578  texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
33579  if (light._depth>1 || light._spectrum<_spectrum)
33580  throw CImgArgumentException(_cimg_instance
33581  "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
33582  cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
33583  if (is_overlapped(texture))
33584  return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
33585  light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
33586  if (is_overlapped(light))
33587  return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,texture,tx0,ty0,tx1,ty1,tx2,ty2,
33588  +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
33589  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
33590  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
33591  const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth,
33592  offx = _spectrum*whd-1;
33593  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
33594  nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
33595  float
33596  ntx0 = tx0/z0, nty0 = ty0/z0,
33597  ntx1 = tx1/z1, nty1 = ty1/z1,
33598  ntx2 = tx2/z2, nty2 = ty2/z2,
33599  nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2;
33600  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1,nz0,nz1);
33601  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2,nz0,nz2);
33602  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2,nz1,nz2);
33603  if (ny0>=height() || ny2<0) return *this;
33604  float
33605  ptxl = (ntx1 - ntx0)/(ny1 - ny0),
33606  ptxr = (ntx2 - ntx0)/(ny2 - ny0),
33607  ptxn = (ntx2 - ntx1)/(ny2 - ny1),
33608  ptyl = (nty1 - nty0)/(ny1 - ny0),
33609  ptyr = (nty2 - nty0)/(ny2 - ny0),
33610  ptyn = (nty2 - nty1)/(ny2 - ny1),
33611  pzl = (nz1 - nz0)/(ny1 - ny0),
33612  pzr = (nz2 - nz0)/(ny2 - ny0),
33613  pzn = (nz2 - nz1)/(ny2 - ny1),
33614  zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
33615  txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
33616  tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
33617  zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))),
33618  txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):
33619  (ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
33620  tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):
33621  (ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
33622  _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
33623  nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
33624  if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
33625  int
33626  xleft = xleft0, xright = xright0,
33627  lxleft = lxleft0, lxright = lxright0,
33628  lyleft = lyleft0, lyright = lyright0;
33629  float
33630  zleft = zl, zright = zr,
33631  txleft = txl, txright = txr,
33632  tyleft = tyl, tyright = tyr;
33633  if (xright<xleft)
33634  cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,lxleft,lxright,lyleft,lyright);
33635  const int
33636  dx = xright - xleft,
33637  dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
33638  dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
33639  rlx = dx?(lxright - lxleft)/dx:0,
33640  rly = dx?(lyright - lyleft)/dx:0,
33641  slx = lxright>lxleft?1:-1,
33642  sly = lyright>lyleft?1:-1,
33643  ndlx = dlx - (dx?dx*(dlx/dx):0),
33644  ndly = dly - (dx?dx*(dly/dx):0);
33645  const float
33646  pentez = (zright - zleft)/dx,
33647  pentetx = (txright - txleft)/dx,
33648  pentety = (tyright - tyleft)/dx;
33649  int errlx = dx>>1, errly = errlx;
33650  if (xleft<0 && dx) {
33651  zleft-=xleft*(zright - zleft)/dx;
33652  lxleft-=xleft*(lxright - lxleft)/dx;
33653  lyleft-=xleft*(lyright - lyleft)/dx;
33654  txleft-=xleft*(txright - txleft)/dx;
33655  tyleft-=xleft*(tyright - tyleft)/dx;
33656  }
33657  if (xleft<0) xleft = 0;
33658  if (xright>=width()-1) xright = width()-1;
33659  T* ptrd = data(xleft,y,0,0);
33660  if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
33661  const float invz = 1/zleft;
33662  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
33663  cimg_forC(*this,c) {
33664  const tl l = light(lxleft,lyleft,c);
33665  *ptrd = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
33666  ptrd+=whd; col+=twhd;
33667  }
33668  ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
33669  lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
33670  lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
33671  } else for (int x = xleft; x<=xright; ++x) {
33672  const float invz = 1/zleft;
33673  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
33674  cimg_forC(*this,c) {
33675  const tl l = light(lxleft,lyleft,c);
33676  const T val = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
33677  *ptrd = (T)(nopacity*val + *ptrd*copacity);
33678  ptrd+=whd; col+=twhd;
33679  }
33680  ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
33681  lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
33682  lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
33683  }
33684  zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
33685  }
33686  return *this;
33687  }
33688 
33690  template<typename tz, typename tc, typename tl>
33692  const int x0, const int y0, const float z0,
33693  const int x1, const int y1, const float z1,
33694  const int x2, const int y2, const float z2,
33695  const CImg<tc>& texture,
33696  const int tx0, const int ty0,
33697  const int tx1, const int ty1,
33698  const int tx2, const int ty2,
33699  const CImg<tl>& light,
33700  const int lx0, const int ly0,
33701  const int lx1, const int ly1,
33702  const int lx2, const int ly2,
33703  const float opacity=1) {
33704  typedef typename cimg::superset<tz,float>::type tzfloat;
33705  if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
33706  if (!is_sameXY(zbuffer))
33707  throw CImgArgumentException(_cimg_instance
33708  "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
33709  "different dimensions.",
33710  cimg_instance,
33711  zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
33712  if (texture._depth>1 || texture._spectrum<_spectrum)
33713  throw CImgArgumentException(_cimg_instance
33714  "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
33715  cimg_instance,
33716  texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
33717  if (light._depth>1 || light._spectrum<_spectrum)
33718  throw CImgArgumentException(_cimg_instance
33719  "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
33720  cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
33721  if (is_overlapped(texture))
33722  return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,
33723  +texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
33724  if (is_overlapped(light))
33725  return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,
33726  texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
33727  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
33728  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
33729  const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth,
33730  offx = _spectrum*whd;
33731  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
33732  nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
33733  float
33734  ntx0 = tx0/z0, nty0 = ty0/z0,
33735  ntx1 = tx1/z1, nty1 = ty1/z1,
33736  ntx2 = tx2/z2, nty2 = ty2/z2;
33737  tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
33738  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1,nz0,nz1);
33739  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2,nz0,nz2);
33740  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2,nz1,nz2);
33741  if (ny0>=height() || ny2<0) return *this;
33742  float
33743  ptxl = (ntx1 - ntx0)/(ny1 - ny0),
33744  ptxr = (ntx2 - ntx0)/(ny2 - ny0),
33745  ptxn = (ntx2 - ntx1)/(ny2 - ny1),
33746  ptyl = (nty1 - nty0)/(ny1 - ny0),
33747  ptyr = (nty2 - nty0)/(ny2 - ny0),
33748  ptyn = (nty2 - nty1)/(ny2 - ny1),
33749  txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
33750  tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
33751  txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):
33752  (ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
33753  tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):
33754  (ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
33755  tzfloat
33756  pzl = (nz1 - nz0)/(ny1 - ny0),
33757  pzr = (nz2 - nz0)/(ny2 - ny0),
33758  pzn = (nz2 - nz1)/(ny2 - ny1),
33759  zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
33760  zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
33761  _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
33762  nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
33763  if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
33764  int
33765  xleft = xleft0, xright = xright0,
33766  lxleft = lxleft0, lxright = lxright0,
33767  lyleft = lyleft0, lyright = lyright0;
33768  float txleft = txl, txright = txr, tyleft = tyl, tyright = tyr;
33769  tzfloat zleft = zl, zright = zr;
33770  if (xright<xleft)
33771  cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,lxleft,lxright,lyleft,lyright);
33772  const int
33773  dx = xright - xleft,
33774  dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
33775  dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
33776  rlx = dx?(lxright - lxleft)/dx:0,
33777  rly = dx?(lyright - lyleft)/dx:0,
33778  slx = lxright>lxleft?1:-1,
33779  sly = lyright>lyleft?1:-1,
33780  ndlx = dlx - (dx?dx*(dlx/dx):0),
33781  ndly = dly - (dx?dx*(dly/dx):0);
33782  float pentetx = (txright - txleft)/dx, pentety = (tyright - tyleft)/dx;
33783  const tzfloat pentez = (zright - zleft)/dx;
33784  int errlx = dx>>1, errly = errlx;
33785  if (xleft<0 && dx) {
33786  zleft-=xleft*(zright - zleft)/dx;
33787  lxleft-=xleft*(lxright - lxleft)/dx;
33788  lyleft-=xleft*(lyright - lyleft)/dx;
33789  txleft-=xleft*(txright - txleft)/dx;
33790  tyleft-=xleft*(tyright - tyleft)/dx;
33791  }
33792  if (xleft<0) xleft = 0;
33793  if (xright>=width()-1) xright = width()-1;
33794  T* ptrd = data(xleft,y);
33795  tz *ptrz = zbuffer.data(xleft,y);
33796  if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
33797  if (zleft>=(tzfloat)*ptrz) {
33798  *ptrz = (tz)zleft;
33799  const tzfloat invz = 1/zleft;
33800  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
33801  cimg_forC(*this,c) {
33802  const tl l = light(lxleft,lyleft,c);
33803  *ptrd = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
33804  ptrd+=whd; col+=twhd;
33805  }
33806  ptrd-=offx;
33807  }
33808  zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
33809  lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
33810  lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
33811  } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
33812  if (zleft>=(tzfloat)*ptrz) {
33813  *ptrz = (tz)zleft;
33814  const tzfloat invz = 1/zleft;
33815  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
33816  cimg_forC(*this,c) {
33817  const tl l = light(lxleft,lyleft,c);
33818  const T val = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
33819  *ptrd = (T)(nopacity*val + *ptrd*copacity);
33820  ptrd+=whd; col+=twhd;
33821  }
33822  ptrd-=offx;
33823  }
33824  zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
33825  lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
33826  lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
33827  }
33828  zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
33829  }
33830  return *this;
33831  }
33832 
33834 
33846  CImg<T>& draw_rectangle(const int x0, const int y0, const int z0, const int c0,
33847  const int x1, const int y1, const int z1, const int c1,
33848  const T val, const float opacity=1) {
33849  if (is_empty()) return *this;
33850  const bool bx = (x0<x1), by = (y0<y1), bz = (z0<z1), bc = (c0<c1);
33851  const int
33852  nx0 = bx?x0:x1, nx1 = bx?x1:x0,
33853  ny0 = by?y0:y1, ny1 = by?y1:y0,
33854  nz0 = bz?z0:z1, nz1 = bz?z1:z0,
33855  nc0 = bc?c0:c1, nc1 = bc?c1:c0;
33856  const int
33857  lX = (1 + nx1 - nx0) + (nx1>=width()?width() - 1 - nx1:0) + (nx0<0?nx0:0),
33858  lY = (1 + ny1 - ny0) + (ny1>=height()?height() - 1 - ny1:0) + (ny0<0?ny0:0),
33859  lZ = (1 + nz1 - nz0) + (nz1>=depth()?depth() - 1 - nz1:0) + (nz0<0?nz0:0),
33860  lC = (1 + nc1 - nc0) + (nc1>=spectrum()?spectrum() - 1 - nc1:0) + (nc0<0?nc0:0);
33861  const unsigned long
33862  offX = (unsigned long)_width - lX,
33863  offY = (unsigned long)_width*(_height - lY),
33864  offZ = (unsigned long)_width*_height*(_depth - lZ);
33865  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
33866  T *ptrd = data(nx0<0?0:nx0,ny0<0?0:ny0,nz0<0?0:nz0,nc0<0?0:nc0);
33867  if (lX>0 && lY>0 && lZ>0 && lC>0)
33868  for (int v = 0; v<lC; ++v) {
33869  for (int z = 0; z<lZ; ++z) {
33870  for (int y = 0; y<lY; ++y) {
33871  if (opacity>=1) {
33872  if (sizeof(T)!=1) { for (int x = 0; x<lX; ++x) *(ptrd++) = val; ptrd+=offX; }
33873  else { std::memset(ptrd,(int)val,lX); ptrd+=_width; }
33874  } else { for (int x = 0; x<lX; ++x) { *ptrd = (T)(nopacity*val + *ptrd*copacity); ++ptrd; } ptrd+=offX; }
33875  }
33876  ptrd+=offY;
33877  }
33878  ptrd+=offZ;
33879  }
33880  return *this;
33881  }
33882 
33884 
33894  template<typename tc>
33895  CImg<T>& draw_rectangle(const int x0, const int y0, const int z0,
33896  const int x1, const int y1, const int z1,
33897  const tc *const color, const float opacity=1) {
33898  if (is_empty()) return *this;
33899  if (!color)
33900  throw CImgArgumentException(_cimg_instance
33901  "draw_rectangle(): Specified color is (null).",
33902  cimg_instance);
33903  cimg_forC(*this,c) draw_rectangle(x0,y0,z0,c,x1,y1,z1,c,(T)color[c],opacity);
33904  return *this;
33905  }
33906 
33908  template<typename tc>
33909  CImg<T>& draw_rectangle(const int x0, const int y0, const int z0,
33910  const int x1, const int y1, const int z1,
33911  const tc *const color, const float opacity,
33912  const unsigned int pattern) {
33913  return draw_line(x0,y0,z0,x1,y0,z0,color,opacity,pattern,true).
33914  draw_line(x1,y0,z0,x1,y1,z0,color,opacity,pattern,false).
33915  draw_line(x1,y1,z0,x0,y1,z0,color,opacity,pattern,false).
33916  draw_line(x0,y1,z0,x0,y0,z0,color,opacity,pattern,false).
33917  draw_line(x0,y0,z1,x1,y0,z1,color,opacity,pattern,true).
33918  draw_line(x1,y0,z1,x1,y1,z1,color,opacity,pattern,false).
33919  draw_line(x1,y1,z1,x0,y1,z1,color,opacity,pattern,false).
33920  draw_line(x0,y1,z1,x0,y0,z1,color,opacity,pattern,false).
33921  draw_line(x0,y0,z0,x0,y0,z1,color,opacity,pattern,true).
33922  draw_line(x1,y0,z0,x1,y0,z1,color,opacity,pattern,true).
33923  draw_line(x1,y1,z0,x1,y1,z1,color,opacity,pattern,true).
33924  draw_line(x0,y1,z0,x0,y1,z1,color,opacity,pattern,true);
33925  }
33926 
33928 
33936  template<typename tc>
33937  CImg<T>& draw_rectangle(const int x0, const int y0,
33938  const int x1, const int y1,
33939  const tc *const color, const float opacity=1) {
33940  return draw_rectangle(x0,y0,0,x1,y1,_depth-1,color,opacity);
33941  }
33942 
33944  template<typename tc>
33945  CImg<T>& draw_rectangle(const int x0, const int y0,
33946  const int x1, const int y1,
33947  const tc *const color, const float opacity,
33948  const unsigned int pattern) {
33949  if (is_empty()) return *this;
33950  if (y0==y1) return draw_line(x0,y0,x1,y0,color,opacity,pattern,true);
33951  if (x0==x1) return draw_line(x0,y0,x0,y1,color,opacity,pattern,true);
33952  const bool bx = (x0<x1), by = (y0<y1);
33953  const int
33954  nx0 = bx?x0:x1, nx1 = bx?x1:x0,
33955  ny0 = by?y0:y1, ny1 = by?y1:y0;
33956  if (ny1==ny0+1) return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true).
33957  draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false);
33958  return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true).
33959  draw_line(nx1,ny0+1,nx1,ny1-1,color,opacity,pattern,false).
33960  draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false).
33961  draw_line(nx0,ny1-1,nx0,ny0+1,color,opacity,pattern,false);
33962  }
33963 
33965 
33970  template<typename t, typename tc>
33971  CImg<T>& draw_polygon(const CImg<t>& points,
33972  const tc *const color, const float opacity=1) {
33973  if (is_empty() || !points) return *this;
33974  if (!color)
33975  throw CImgArgumentException(_cimg_instance
33976  "draw_polygon(): Specified color is (null).",
33977  cimg_instance);
33978 
33979  // Normalize 2d input coordinates (remove adjacent duplicates).
33980  CImg<intT> npoints(points._width,2);
33981  unsigned int nb_points = 1, p = 0;
33982  int cx = npoints(0,0) = (int)points(0,0), cy = npoints(0,1) = (int)points(0,1);
33983  const int cx0 = cx, cy0 = cy;
33984  for (p = 1; p<points._width; ++p) {
33985  const int nx = (int)points(p,0), ny = (int)points(p,1);
33986  if (nx!=cx || ny!=cy) { npoints(nb_points,0) = nx; npoints(nb_points++,1) = ny; cx = nx; cy = ny; }
33987  }
33988  --p;
33989  if ((int)points(p,0)==cx0 && (int)points(p,1)==cy0) --nb_points;
33990  if (nb_points<=1) return draw_point((int)npoints(0,0),(int)npoints(0,1),color,opacity);
33991  if (nb_points==2) return draw_line((int)npoints(0,0),(int)npoints(0,1),
33992  (int)npoints(1,0),(int)npoints(1,1),color,opacity);
33993  if (nb_points==3) return draw_triangle((int)npoints(0,0),(int)npoints(0,1),
33994  (int)npoints(1,0),(int)npoints(1,1),
33995  (int)npoints(2,0),(int)npoints(2,1),color,opacity);
33996 
33997  // Shift the coordinates so that the first and last vertices are not located on the same scanline.
33998  if (npoints(0,1)==npoints(nb_points-1,1)) {
33999  const intT y0 = npoints(0,1);
34000  unsigned int off = 1;
34001  while ((int)npoints(off,1)==y0 && off<nb_points) ++off;
34002  if (off<nb_points) {
34003  npoints.get_shared_points(0,nb_points-1,0).shift(-(int)off,0,0,0,2);
34004  npoints.get_shared_points(0,nb_points-1,1).shift(-(int)off,0,0,0,2);
34005  }
34006  }
34007 
34008  cimg_init_scanline(color,1);
34009 
34010  if (opacity!=1) { // For non-opaque polygons, do a little trick to avoid horizontal lines artefacts.
34011  npoints.resize(nb_points,2,1,1,0);
34012  CImg<intT> npoints_x = npoints.get_shared_row(0), npoints_y = npoints.get_shared_row(1);
34013  int xmax = 0, xmin = (int)npoints_x.min_max(xmax), ymax = 0, ymin = (int)npoints_y.min_max(ymax);
34014  if (xmax<0 || xmin>=width() || ymax<0 || ymin>=height()) return *this;
34015  if (ymin==ymax) return cimg_draw_scanline(xmin,xmax,ymin,color,opacity,1);
34016  const unsigned int
34017  nxmin = xmin<0?0:(unsigned int)xmin, nxmax = xmax>=width()?_width-1:(unsigned int)xmax,
34018  nymin = ymin<0?0:(unsigned int)ymin, nymax = ymax>=height()?_height-1:(unsigned int)ymax,
34019  dx = 1 + nxmax - nxmin,
34020  dy = 1 + nymax - nymin;
34021  npoints_x-=nxmin; npoints_y-=nymin;
34022  unsigned char one = 1;
34023  const CImg<unsigned char> mask = CImg<unsigned char>(dx,dy,1,1,0).draw_polygon(npoints,&one,1);
34024  CImg<T> _color(dx,dy,1,spectrum());
34025  cimg_forC(_color,c) _color.get_shared_channel(c).fill(color[c]);
34026  return draw_image(nxmin,nymin,0,0,_color,mask,opacity,1);
34027  }
34028 
34029  // Draw polygon segments.
34030  int
34031  xmax = 0, xmin = (int)npoints.get_shared_points(0,nb_points-1,0).min_max(xmax),
34032  ymax = 0, ymin = (int)npoints.get_shared_points(0,nb_points-1,1).min_max(ymax);
34033  if (xmax<0 || xmin>=width() || ymax<0 || ymin>=height()) return *this;
34034  if (ymin==ymax) return cimg_draw_scanline(xmin,xmax,ymin,color,1,1);
34035  const unsigned int
34036  nymin = ymin<0?0:(unsigned int)ymin,
34037  nymax = ymax>=height()?_height-1:(unsigned int)ymax,
34038  dy = 1 + nymax - nymin;
34039  CImg<intT> X(1+2*nb_points,dy,1,1,0), tmp;
34040  cx = (int)npoints(0,0), cy = (int)npoints(0,1);
34041  unsigned int cp = 0;
34042  for (unsigned int p = 0; p<nb_points; ++p) {
34043  const unsigned int np = (p!=nb_points-1)?p+1:0, ap = (np!=nb_points-1)?np+1:0;
34044  const int
34045  nx = (int)npoints(np,0), ny = (int)npoints(np,1), ay = (int)npoints(ap,1),
34046  y0 = cy - nymin, y1 = ny - nymin;
34047  if (y0!=y1) {
34048  const int countermin = ((ny<ay && cy<ny) || (ny>ay && cy>ny))?1:0;
34049  for (int x = cx, y = y0, _sx = 1, _sy = 1,
34050  _dx = nx>cx?nx-cx:((_sx=-1),cx-nx),
34051  _dy = y1>y0?y1-y0:((_sy=-1),y0-y1),
34052  _counter = ((_dx-=_dy?_dy*(_dx/_dy):0),_dy),
34053  _err = _dx>>1,
34054  _rx = _dy?(nx-cx)/_dy:0;
34055  _counter>=countermin;
34056  --_counter, y+=_sy, x+=_rx + ((_err-=_dx)<0?_err+=_dy,_sx:0))
34057  if (y>=0 && y<(int)dy) X(++X(0,y),y) = x;
34058  cp = np; cx = nx; cy = ny;
34059  } else {
34060  const int pp = (cp?cp-1:nb_points-1), py = (int)npoints(pp,1);
34061  if (y0>=0 && y0<(int)dy) {
34062  cimg_draw_scanline(cx<nx?cx:nx,cx<nx?nx:cx,y0+nymin,color,1,1);
34063  if ((cy>py && ay>cy) || (cy<py && ay<cy)) X(++X(0,y0),y0) = cx;
34064  }
34065  if (cy!=ay) { cp = np; cx = nx; cy = ny; }
34066  }
34067  }
34068 
34069  // Draw polygon scanlines.
34070  for (int y = 0; y<(int)dy; ++y) {
34071  tmp.assign(X.data(1,y),X(0,y),1,1,1,true).sort();
34072  for (int i = 1; i<=X(0,y); ) {
34073  const int xb = X(i++,y), xe = X(i++,y);
34074  cimg_draw_scanline(xb,xe,nymin+y,color,1,1);
34075  }
34076  }
34077  return *this;
34078  }
34079 
34081  template<typename t, typename tc>
34082  CImg<T>& draw_polygon(const CImg<t>& points,
34083  const tc *const color, const float opacity, const unsigned int pattern) {
34084  if (is_empty() || !points || points._width<3) return *this;
34085  bool ninit_hatch = true;
34086  switch (points._height) {
34087  case 0 : case 1 :
34088  throw CImgArgumentException(_cimg_instance
34089  "draw_polygon(): Invalid specified point set.",
34090  cimg_instance);
34091  case 2 : { // 2d version.
34092  CImg<intT> npoints(points._width,2);
34093  int x = npoints(0,0) = (int)points(0,0), y = npoints(0,1) = (int)points(0,1);
34094  unsigned int nb_points = 1;
34095  for (unsigned int p = 1; p<points._width; ++p) {
34096  const int nx = (int)points(p,0), ny = (int)points(p,1);
34097  if (nx!=x || ny!=y) { npoints(nb_points,0) = nx; npoints(nb_points++,1) = ny; x = nx; y = ny; }
34098  }
34099  const int x0 = (int)npoints(0,0), y0 = (int)npoints(0,1);
34100  int ox = x0, oy = y0;
34101  for (unsigned int i = 1; i<nb_points; ++i) {
34102  const int x = (int)npoints(i,0), y = (int)npoints(i,1);
34103  draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch);
34104  ninit_hatch = false;
34105  ox = x; oy = y;
34106  }
34107  draw_line(ox,oy,x0,y0,color,opacity,pattern,false);
34108  } break;
34109  default : { // 3d version.
34110  CImg<intT> npoints(points._width,3);
34111  int
34112  x = npoints(0,0) = (int)points(0,0),
34113  y = npoints(0,1) = (int)points(0,1),
34114  z = npoints(0,2) = (int)points(0,2);
34115  unsigned int nb_points = 1;
34116  for (unsigned int p = 1; p<points._width; ++p) {
34117  const int nx = (int)points(p,0), ny = (int)points(p,1), nz = (int)points(p,2);
34118  if (nx!=x || ny!=y || nz!=z) {
34119  npoints(nb_points,0) = nx; npoints(nb_points,1) = ny; npoints(nb_points++,2) = nz;
34120  x = nx; y = ny; z = nz;
34121  }
34122  }
34123  const int x0 = (int)npoints(0,0), y0 = (int)npoints(0,1), z0 = (int)npoints(0,2);
34124  int ox = x0, oy = y0, oz = z0;
34125  for (unsigned int i = 1; i<nb_points; ++i) {
34126  const int x = (int)npoints(i,0), y = (int)npoints(i,1), z = (int)npoints(i,2);
34127  draw_line(ox,oy,oz,x,y,z,color,opacity,pattern,ninit_hatch);
34128  ninit_hatch = false;
34129  ox = x; oy = y; oz = z;
34130  }
34131  draw_line(ox,oy,oz,x0,y0,z0,color,opacity,pattern,false);
34132  }
34133  }
34134  return *this;
34135  }
34136 
34138 
34147  template<typename tc>
34148  CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
34149  const tc *const color, const float opacity=1) {
34150  return _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,0U);
34151  }
34152 
34154 
34161  template<typename t, typename tc>
34162  CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
34163  const tc *const color, const float opacity=1) {
34164  CImgList<t> eig = tensor.get_symmetric_eigen();
34165  const CImg<t> &val = eig[0], &vec = eig[1];
34166  return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)),
34167  std::atan2(vec(0,1),vec(0,0))*180/cimg::PI,
34168  color,opacity);
34169  }
34170 
34172 
34182  template<typename tc>
34183  CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
34184  const tc *const color, const float opacity, const unsigned int pattern) {
34185  if (pattern) _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,pattern);
34186  return *this;
34187  }
34188 
34190 
34198  template<typename t, typename tc>
34199  CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
34200  const tc *const color, const float opacity,
34201  const unsigned int pattern) {
34202  CImgList<t> eig = tensor.get_symmetric_eigen();
34203  const CImg<t> &val = eig[0], &vec = eig[1];
34204  return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)),
34205  std::atan2(vec(0,1),vec(0,0))*180/cimg::PI,
34206  color,opacity,pattern);
34207  }
34208 
34209  template<typename tc>
34210  CImg<T>& _draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
34211  const tc *const color, const float opacity,
34212  const unsigned int pattern) {
34213  if (is_empty()) return *this;
34214  if (!color)
34215  throw CImgArgumentException(_cimg_instance
34216  "draw_ellipse(): Specified color is (null).",
34217  cimg_instance);
34218  if (r1<=0 || r2<=0) return draw_point(x0,y0,color,opacity);
34219  cimg_init_scanline(color,opacity);
34220  const float
34221  nr1 = cimg::abs(r1), nr2 = cimg::abs(r2),
34222  nangle = (float)(angle*cimg::PI/180),
34223  u = (float)std::cos(nangle),
34224  v = (float)std::sin(nangle),
34225  rmax = cimg::max(nr1,nr2),
34226  l1 = (float)std::pow(rmax/(nr1>0?nr1:1e-6),2),
34227  l2 = (float)std::pow(rmax/(nr2>0?nr2:1e-6),2),
34228  a = l1*u*u + l2*v*v,
34229  b = u*v*(l1-l2),
34230  c = l1*v*v + l2*u*u;
34231  const int
34232  yb = (int)std::sqrt(a*rmax*rmax/(a*c - b*b)),
34233  tymin = y0 - yb - 1,
34234  tymax = y0 + yb + 1,
34235  ymin = tymin<0?0:tymin,
34236  ymax = tymax>=height()?height()-1:tymax;
34237  int oxmin = 0, oxmax = 0;
34238  bool first_line = true;
34239  for (int y = ymin; y<=ymax; ++y) {
34240  const float
34241  Y = y - y0 + (y<y0?0.5f:-0.5f),
34242  delta = b*b*Y*Y - a*(c*Y*Y - rmax*rmax),
34243  sdelta = delta>0?(float)std::sqrt(delta)/a:0.0f,
34244  bY = b*Y/a,
34245  fxmin = x0 - 0.5f - bY - sdelta,
34246  fxmax = x0 + 0.5f - bY + sdelta;
34247  const int xmin = (int)fxmin, xmax = (int)fxmax;
34248  if (!pattern) cimg_draw_scanline(xmin,xmax,y,color,opacity,1);
34249  else {
34250  if (first_line) {
34251  if (y0-yb>=0) cimg_draw_scanline(xmin,xmax,y,color,opacity,1);
34252  else draw_point(xmin,y,color,opacity).draw_point(xmax,y,color,opacity);
34253  first_line = false;
34254  } else {
34255  if (xmin<oxmin) cimg_draw_scanline(xmin,oxmin-1,y,color,opacity,1);
34256  else cimg_draw_scanline(oxmin+(oxmin==xmin?0:1),xmin,y,color,opacity,1);
34257  if (xmax<oxmax) cimg_draw_scanline(xmax,oxmax-1,y,color,opacity,1);
34258  else cimg_draw_scanline(oxmax+(oxmax==xmax?0:1),xmax,y,color,opacity,1);
34259  if (y==tymax) cimg_draw_scanline(xmin+1,xmax-1,y,color,opacity,1);
34260  }
34261  }
34262  oxmin = xmin; oxmax = xmax;
34263  }
34264  return *this;
34265  }
34266 
34268 
34277  template<typename tc>
34278  CImg<T>& draw_circle(const int x0, const int y0, int radius,
34279  const tc *const color, const float opacity=1) {
34280  if (is_empty()) return *this;
34281  if (!color)
34282  throw CImgArgumentException(_cimg_instance
34283  "draw_circle(): Specified color is (null).",
34284  cimg_instance);
34285  cimg_init_scanline(color,opacity);
34286  if (radius<0 || x0-radius>=width() || y0+radius<0 || y0-radius>=height()) return *this;
34287  if (y0>=0 && y0<height()) cimg_draw_scanline(x0-radius,x0+radius,y0,color,opacity,1);
34288  for (int f = 1-radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x<y; ) {
34289  if (f>=0) {
34290  const int x1 = x0-x, x2 = x0+x, y1 = y0-y, y2 = y0+y;
34291  if (y1>=0 && y1<height()) cimg_draw_scanline(x1,x2,y1,color,opacity,1);
34292  if (y2>=0 && y2<height()) cimg_draw_scanline(x1,x2,y2,color,opacity,1);
34293  f+=(ddFy+=2); --y;
34294  }
34295  const bool no_diag = y!=(x++);
34296  ++(f+=(ddFx+=2));
34297  const int x1 = x0-y, x2 = x0+y, y1 = y0-x, y2 = y0+x;
34298  if (no_diag) {
34299  if (y1>=0 && y1<height()) cimg_draw_scanline(x1,x2,y1,color,opacity,1);
34300  if (y2>=0 && y2<height()) cimg_draw_scanline(x1,x2,y2,color,opacity,1);
34301  }
34302  }
34303  return *this;
34304  }
34305 
34307 
34315  template<typename tc>
34316  CImg<T>& draw_circle(const int x0, const int y0, int radius,
34317  const tc *const color, const float opacity,
34318  const unsigned int pattern) {
34319  cimg::unused(pattern);
34320  if (is_empty()) return *this;
34321  if (!color)
34322  throw CImgArgumentException(_cimg_instance
34323  "draw_circle(): Specified color is (null).",
34324  cimg_instance);
34325  if (radius<0 || x0-radius>=width() || y0+radius<0 || y0-radius>=height()) return *this;
34326  if (!radius) return draw_point(x0,y0,color,opacity);
34327  draw_point(x0-radius,y0,color,opacity).draw_point(x0+radius,y0,color,opacity).
34328  draw_point(x0,y0-radius,color,opacity).draw_point(x0,y0+radius,color,opacity);
34329  if (radius==1) return *this;
34330  for (int f = 1-radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x<y; ) {
34331  if (f>=0) { f+=(ddFy+=2); --y; }
34332  ++x; ++(f+=(ddFx+=2));
34333  if (x!=y+1) {
34334  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;
34335  draw_point(x1,y1,color,opacity).draw_point(x1,y2,color,opacity).
34336  draw_point(x2,y1,color,opacity).draw_point(x2,y2,color,opacity);
34337  if (x!=y)
34338  draw_point(x3,y3,color,opacity).draw_point(x4,y4,color,opacity).
34339  draw_point(x4,y3,color,opacity).draw_point(x3,y4,color,opacity);
34340  }
34341  }
34342  return *this;
34343  }
34344 
34346 
34354  template<typename t>
34355  CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
34356  const CImg<t>& sprite, const float opacity=1) {
34357  if (is_empty() || !sprite) return *this;
34358  if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity);
34359  if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1 && !is_shared())
34360  return assign(sprite,false);
34361  const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bc = (c0<0);
34362  const int
34363  lX = sprite.width() - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0) + (bx?x0:0),
34364  lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0),
34365  lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0),
34366  lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0);
34367  const t
34368  *ptrs = sprite._data -
34369  (bx?x0:0) -
34370  (by?y0*sprite.width():0) -
34371  (bz?z0*sprite.width()*sprite.height():0) -
34372  (bc?c0*sprite.width()*sprite.height()*sprite.depth():0);
34373  const unsigned long
34374  offX = (unsigned long)_width - lX,
34375  soffX = (unsigned long)sprite._width - lX,
34376  offY = (unsigned long)_width*(_height - lY),
34377  soffY = (unsigned long)sprite._width*(sprite._height - lY),
34378  offZ = (unsigned long)_width*_height*(_depth - lZ),
34379  soffZ = (unsigned long)sprite._width*sprite._height*(sprite._depth - lZ);
34380  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
34381  if (lX>0 && lY>0 && lZ>0 && lC>0) {
34382  T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0);
34383  for (int v = 0; v<lC; ++v) {
34384  for (int z = 0; z<lZ; ++z) {
34385  for (int y = 0; y<lY; ++y) {
34386  if (opacity>=1) for (int x = 0; x<lX; ++x) *(ptrd++) = (T)*(ptrs++);
34387  else for (int x = 0; x<lX; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; }
34388  ptrd+=offX; ptrs+=soffX;
34389  }
34390  ptrd+=offY; ptrs+=soffY;
34391  }
34392  ptrd+=offZ; ptrs+=soffZ;
34393  }
34394  }
34395  return *this;
34396  }
34397 
34399  CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
34400  const CImg<T>& sprite, const float opacity=1) {
34401  if (is_empty() || !sprite) return *this;
34402  if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity);
34403  if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1 && !is_shared())
34404  return assign(sprite,false);
34405  const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bc = (c0<0);
34406  const int
34407  lX = sprite.width() - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0) + (bx?x0:0),
34408  lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0),
34409  lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0),
34410  lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0);
34411  const T
34412  *ptrs = sprite._data -
34413  (bx?x0:0) -
34414  (by?y0*sprite.width():0) -
34415  (bz?z0*sprite.width()*sprite.height():0) -
34416  (bc?c0*sprite.width()*sprite.height()*sprite.depth():0);
34417  const unsigned long
34418  offX = (unsigned long)_width - lX,
34419  soffX = (unsigned long)sprite._width - lX,
34420  offY = (unsigned long)_width*(_height - lY),
34421  soffY = (unsigned long)sprite._width*(sprite._height - lY),
34422  offZ = (unsigned long)_width*_height*(_depth - lZ),
34423  soffZ = (unsigned long)sprite._width*sprite._height*(sprite._depth - lZ),
34424  slX = lX*sizeof(T);
34425  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
34426  if (lX>0 && lY>0 && lZ>0 && lC>0) {
34427  T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0);
34428  for (int v = 0; v<lC; ++v) {
34429  for (int z = 0; z<lZ; ++z) {
34430  if (opacity>=1)
34431  for (int y = 0; y<lY; ++y) { std::memcpy(ptrd,ptrs,slX); ptrd+=_width; ptrs+=sprite._width; }
34432  else for (int y = 0; y<lY; ++y) {
34433  for (int x = 0; x<lX; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; }
34434  ptrd+=offX; ptrs+=soffX;
34435  }
34436  ptrd+=offY; ptrs+=soffY;
34437  }
34438  ptrd+=offZ; ptrs+=soffZ;
34439  }
34440  }
34441  return *this;
34442  }
34443 
34445  template<typename t>
34446  CImg<T>& draw_image(const int x0, const int y0, const int z0,
34447  const CImg<t>& sprite, const float opacity=1) {
34448  return draw_image(x0,y0,z0,0,sprite,opacity);
34449  }
34450 
34452  template<typename t>
34453  CImg<T>& draw_image(const int x0, const int y0,
34454  const CImg<t>& sprite, const float opacity=1) {
34455  return draw_image(x0,y0,0,sprite,opacity);
34456  }
34457 
34459  template<typename t>
34460  CImg<T>& draw_image(const int x0,
34461  const CImg<t>& sprite, const float opacity=1) {
34462  return draw_image(x0,0,sprite,opacity);
34463  }
34464 
34466  template<typename t>
34467  CImg<T>& draw_image(const CImg<t>& sprite, const float opacity=1) {
34468  return draw_image(0,sprite,opacity);
34469  }
34470 
34472 
34485  template<typename ti, typename tm>
34486  CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
34487  const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
34488  const float mask_max_value=1) {
34489  if (is_empty() || !sprite || !mask) return *this;
34490  if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,mask,opacity,mask_max_value);
34491  if (is_overlapped(mask)) return draw_image(x0,y0,z0,c0,sprite,+mask,opacity,mask_max_value);
34492  if (mask._width!=sprite._width || mask._height!=sprite._height || mask._depth!=sprite._depth)
34493  throw CImgArgumentException(_cimg_instance
34494  "draw_image(): Sprite (%u,%u,%u,%u,%p) and mask (%u,%u,%u,%u,%p) have "
34495  "incompatible dimensions.",
34496  cimg_instance,
34497  sprite._width,sprite._height,sprite._depth,sprite._spectrum,sprite._data,
34498  mask._width,mask._height,mask._depth,mask._spectrum,mask._data);
34499 
34500  const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bc = (c0<0);
34501  const int
34502  lX = sprite.width() - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0) + (bx?x0:0),
34503  lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0),
34504  lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0),
34505  lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0);
34506  const int
34507  coff = -(bx?x0:0)-(by?y0*mask.width():0)-(bz?z0*mask.width()*mask.height():0)-
34508  (bc?c0*mask.width()*mask.height()*mask.depth():0),
34509  ssize = mask.width()*mask.height()*mask.depth()*mask.spectrum();
34510  const ti *ptrs = sprite._data + coff;
34511  const tm *ptrm = mask._data + coff;
34512  const unsigned long
34513  offX = (unsigned long)_width - lX,
34514  soffX = (unsigned long)sprite._width - lX,
34515  offY = (unsigned long)_width*(_height - lY),
34516  soffY = (unsigned long)sprite._width*(sprite._height - lY),
34517  offZ = (unsigned long)_width*_height*(_depth - lZ),
34518  soffZ = (unsigned long)sprite._width*sprite._height*(sprite._depth - lZ);
34519  if (lX>0 && lY>0 && lZ>0 && lC>0) {
34520  T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0);
34521  for (int c = 0; c<lC; ++c) {
34522  ptrm = mask._data + (ptrm - mask._data)%ssize;
34523  for (int z = 0; z<lZ; ++z) {
34524  for (int y = 0; y<lY; ++y) {
34525  for (int x = 0; x<lX; ++x) {
34526  const float mopacity = (float)(*(ptrm++)*opacity),
34527  nopacity = cimg::abs(mopacity), copacity = mask_max_value - cimg::max(mopacity,0);
34528  *ptrd = (T)((nopacity*(*(ptrs++)) + *ptrd*copacity)/mask_max_value);
34529  ++ptrd;
34530  }
34531  ptrd+=offX; ptrs+=soffX; ptrm+=soffX;
34532  }
34533  ptrd+=offY; ptrs+=soffY; ptrm+=soffY;
34534  }
34535  ptrd+=offZ; ptrs+=soffZ; ptrm+=soffZ;
34536  }
34537  }
34538  return *this;
34539  }
34540 
34542  template<typename ti, typename tm>
34543  CImg<T>& draw_image(const int x0, const int y0, const int z0,
34544  const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
34545  const float mask_max_value=1) {
34546  return draw_image(x0,y0,z0,0,sprite,mask,opacity,mask_max_value);
34547  }
34548 
34550  template<typename ti, typename tm>
34551  CImg<T>& draw_image(const int x0, const int y0,
34552  const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
34553  const float mask_max_value=1) {
34554  return draw_image(x0,y0,0,sprite,mask,opacity,mask_max_value);
34555  }
34556 
34558  template<typename ti, typename tm>
34559  CImg<T>& draw_image(const int x0,
34560  const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
34561  const float mask_max_value=1) {
34562  return draw_image(x0,0,sprite,mask,opacity,mask_max_value);
34563  }
34564 
34566  template<typename ti, typename tm>
34567  CImg<T>& draw_image(const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
34568  const float mask_max_value=1) {
34569  return draw_image(0,sprite,mask,opacity,mask_max_value);
34570  }
34571 
34573 
34582  template<typename tc1, typename tc2, typename t>
34583  CImg<T>& draw_text(const int x0, const int y0,
34584  const char *const text,
34585  const tc1 *const foreground_color, const tc2 *const background_color,
34586  const float opacity, const CImgList<t>& font, ...) {
34587  if (!font) return *this;
34588  char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font);
34589  cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap);
34590  return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font,false);
34591  }
34592 
34594 
34597  template<typename tc, typename t>
34598  CImg<T>& draw_text(const int x0, const int y0,
34599  const char *const text,
34600  const tc *const foreground_color, const int,
34601  const float opacity, const CImgList<t>& font, ...) {
34602  if (!font) return *this;
34603  char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font);
34604  cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap);
34605  return _draw_text(x0,y0,tmp,foreground_color,(tc*)0,opacity,font,false);
34606  }
34607 
34609 
34612  template<typename tc, typename t>
34613  CImg<T>& draw_text(const int x0, const int y0,
34614  const char *const text,
34615  const int, const tc *const background_color,
34616  const float opacity, const CImgList<t>& font, ...) {
34617  if (!font) return *this;
34618  char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font);
34619  cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap);
34620  return _draw_text(x0,y0,tmp,(tc*)0,background_color,opacity,font,false);
34621  }
34622 
34624 
34635  template<typename tc1, typename tc2>
34636  CImg<T>& draw_text(const int x0, const int y0,
34637  const char *const text,
34638  const tc1 *const foreground_color, const tc2 *const background_color,
34639  const float opacity=1, const unsigned int font_height=13, ...) {
34640  if (!font_height) return *this;
34641  char tmp[2048] = { 0 };
34642  std::va_list ap; va_start(ap,font_height); cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap);
34643  const CImgList<ucharT>& font = CImgList<ucharT>::font(font_height,true);
34644  _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font,true);
34645  return *this;
34646  }
34647 
34649  template<typename tc>
34650  CImg<T>& draw_text(const int x0, const int y0,
34651  const char *const text,
34652  const tc *const foreground_color, const int background_color=0,
34653  const float opacity=1, const unsigned int font_height=13, ...) {
34654  if (!font_height) return *this;
34655  cimg::unused(background_color);
34656  char tmp[2048] = { 0 };
34657  std::va_list ap; va_start(ap,font_height); cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap);
34658  return draw_text(x0,y0,"%s",foreground_color,(const tc*)0,opacity,font_height,tmp);
34659  }
34660 
34662  template<typename tc>
34663  CImg<T>& draw_text(const int x0, const int y0,
34664  const char *const text,
34665  const int, const tc *const background_color,
34666  const float opacity=1, const unsigned int font_height=13, ...) {
34667  if (!font_height) return *this;
34668  char tmp[2048] = { 0 };
34669  std::va_list ap; va_start(ap,font_height); cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap);
34670  return draw_text(x0,y0,"%s",(tc*)0,background_color,opacity,font_height,tmp);
34671  }
34672 
34673  template<typename tc1, typename tc2, typename t>
34674  CImg<T>& _draw_text(const int x0, const int y0,
34675  const char *const text,
34676  const tc1 *const foreground_color, const tc2 *const background_color,
34677  const float opacity, const CImgList<t>& font,
34678  const bool is_native_font) {
34679  if (!text) return *this;
34680  if (!font)
34681  throw CImgArgumentException(_cimg_instance
34682  "draw_text(): Empty specified font.",
34683  cimg_instance);
34684 
34685  const unsigned int text_length = (unsigned int)std::strlen(text);
34686  const bool _is_empty = is_empty();
34687  if (_is_empty) {
34688  // If needed, pre-compute necessary size of the image
34689  int x = 0, y = 0, w = 0;
34690  unsigned char c = 0;
34691  for (unsigned int i = 0; i<text_length; ++i) {
34692  c = text[i];
34693  switch (c) {
34694  case '\n' : y+=font[0]._height; if (x>w) w = x; x = 0; break;
34695  case '\t' : x+=4*font[' ']._width; break;
34696  default : if (c<font._width) x+=font[c]._width;
34697  }
34698  }
34699  if (x!=0 || c=='\n') {
34700  if (x>w) w=x;
34701  y+=font[0]._height;
34702  }
34703  assign(x0+w,y0+y,1,is_native_font?1:font[0]._spectrum,0);
34704  }
34705 
34706  int x = x0, y = y0;
34707  for (unsigned int i = 0; i<text_length; ++i) {
34708  const unsigned char c = text[i];
34709  switch (c) {
34710  case '\n' : y+=font[0]._height; x = x0; break;
34711  case '\t' : x+=4*font[' ']._width; break;
34712  default : if (c<font._width) {
34713  CImg<T> letter = font[c];
34714  if (letter) {
34715  if (is_native_font && _spectrum>letter._spectrum) letter.resize(-100,-100,1,_spectrum,0,2);
34716  const unsigned int cmin = cimg::min(_spectrum,letter._spectrum);
34717  if (foreground_color)
34718  for (unsigned int c = 0; c<cmin; ++c)
34719  if (foreground_color[c]!=1) letter.get_shared_channel(c)*=foreground_color[c];
34720  if (c+256<font.width()) { // Letter has mask.
34721  if (background_color)
34722  for (unsigned int c = 0; c<cmin; ++c)
34723  draw_rectangle(x,y,0,c,x+letter._width-1,y+letter._height-1,0,c,background_color[c],opacity);
34724  draw_image(x,y,letter,font[c+256],opacity,(T)255);
34725  } else draw_image(x,y,letter,opacity); // Letter has no mask.
34726  x+=letter._width;
34727  }
34728  }
34729  }
34730  }
34731  return *this;
34732  }
34733 
34735 
34745  template<typename t1, typename t2>
34747  const t2 *const color, const float opacity=1,
34748  const unsigned int sampling=25, const float factor=-20,
34749  const bool is_arrow=true, const unsigned int pattern=~0U) {
34750  return draw_quiver(flow,CImg<t2>(color,_spectrum,1,1,1,true),opacity,sampling,factor,is_arrow,pattern);
34751  }
34752 
34754 
34764  template<typename t1, typename t2>
34766  const CImg<t2>& color, const float opacity=1,
34767  const unsigned int sampling=25, const float factor=-20,
34768  const bool is_arrow=true, const unsigned int pattern=~0U) {
34769  if (is_empty()) return *this;
34770  if (!flow || flow._spectrum!=2)
34771  throw CImgArgumentException(_cimg_instance
34772  "draw_quiver(): Invalid dimensions of specified flow (%u,%u,%u,%u,%p).",
34773  cimg_instance,
34774  flow._width,flow._height,flow._depth,flow._spectrum,flow._data);
34775  if (sampling<=0)
34776  throw CImgArgumentException(_cimg_instance
34777  "draw_quiver(): Invalid sampling value %g "
34778  "(should be >0)",
34779  cimg_instance,
34780  sampling);
34781  const bool colorfield = (color._width==flow._width && color._height==flow._height &&
34782  color._depth==1 && color._spectrum==_spectrum);
34783  if (is_overlapped(flow)) return draw_quiver(+flow,color,opacity,sampling,factor,is_arrow,pattern);
34784  float vmax,fact;
34785  if (factor<=0) {
34786  float m, M = (float)flow.get_norm(2).max_min(m);
34787  vmax = (float)cimg::max(cimg::abs(m),cimg::abs(M));
34788  if (!vmax) vmax = 1;
34789  fact = -factor;
34790  } else { fact = factor; vmax = 1; }
34791 
34792  for (unsigned int y = sampling/2; y<_height; y+=sampling)
34793  for (unsigned int x = sampling/2; x<_width; x+=sampling) {
34794  const unsigned int X = x*flow._width/_width, Y = y*flow._height/_height;
34795  float u = (float)flow(X,Y,0,0)*fact/vmax, v = (float)flow(X,Y,0,1)*fact/vmax;
34796  if (is_arrow) {
34797  const int xx = x+(int)u, yy = y+(int)v;
34798  if (colorfield) draw_arrow(x,y,xx,yy,color.get_vector_at(X,Y)._data,opacity,45,sampling/5.0f,pattern);
34799  else draw_arrow(x,y,xx,yy,color._data,opacity,45,sampling/5.0f,pattern);
34800  } else {
34801  if (colorfield)
34802  draw_line((int)(x-0.5*u),(int)(y-0.5*v),(int)(x+0.5*u),(int)(y+0.5*v),
34803  color.get_vector_at(X,Y)._data,opacity,pattern);
34804  else draw_line((int)(x-0.5*u),(int)(y-0.5*v),(int)(x+0.5*u),(int)(y+0.5*v),
34805  color._data,opacity,pattern);
34806  }
34807  }
34808  return *this;
34809  }
34810 
34812 
34821  template<typename t, typename tc>
34822  CImg<T>& draw_axis(const CImg<t>& values_x, const int y,
34823  const tc *const color, const float opacity=1,
34824  const unsigned int pattern=~0U, const unsigned int font_height=13,
34825  const bool allow_zero=true) {
34826  if (is_empty()) return *this;
34827  const int yt = (y+3+font_height)<_height?(y+3):(y-2-font_height);
34828  const int siz = (int)values_x.size()-1;
34829  char txt[32] = { 0 };
34830  CImg<T> label;
34831  if (siz<=0) { // Degenerated case.
34832  draw_line(0,y,_width-1,y,color,opacity,pattern);
34833  if (!siz) {
34834  cimg_snprintf(txt,sizeof(txt),"%g",(double)*values_x);
34835  label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
34836  const int
34837  _xt = (width() - label.width())/2,
34838  xt = _xt<3?3:_xt+label.width()>=width()-2?width()-3-label.width():_xt;
34839  draw_point(width()/2,y-1,color,opacity).draw_point(width()/2,y+1,color,opacity);
34840  if (allow_zero || txt[0]!='0' || txt[1]!=0)
34841  draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
34842  }
34843  } else { // Regular case.
34844  if (values_x[0]<values_x[siz]) draw_arrow(0,y,_width-1,y,color,opacity,30,5,pattern);
34845  else draw_arrow(_width-1,y,0,y,color,opacity,30,5,pattern);
34846  cimg_foroff(values_x,x) {
34847  cimg_snprintf(txt,sizeof(txt),"%g",(double)values_x(x));
34848  label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
34849  const int
34850  xi = (int)(x*(_width-1)/siz),
34851  _xt = xi - label.width()/2,
34852  xt = _xt<3?3:_xt+label.width()>=width()-2?width()-3-label.width():_xt;
34853  draw_point(xi,y-1,color,opacity).draw_point(xi,y+1,color,opacity);
34854  if (allow_zero || txt[0]!='0' || txt[1]!=0)
34855  draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
34856  }
34857  }
34858  return *this;
34859  }
34860 
34862 
34871  template<typename t, typename tc>
34872  CImg<T>& draw_axis(const int x, const CImg<t>& values_y,
34873  const tc *const color, const float opacity=1,
34874  const unsigned int pattern=~0U, const unsigned int font_height=13,
34875  const bool allow_zero=true) {
34876  if (is_empty()) return *this;
34877  int siz = (int)values_y.size()-1;
34878  char txt[32] = { 0 };
34879  CImg<T> label;
34880  if (siz<=0) { // Degenerated case.
34881  draw_line(x,0,x,_height-1,color,opacity,pattern);
34882  if (!siz) {
34883  cimg_snprintf(txt,sizeof(txt),"%g",(double)*values_y);
34884  label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
34885  const int
34886  _yt = (height() - label.height())/2,
34887  yt = _yt<0?0:_yt+label.height()>=height()?height()-1-label.height():_yt,
34888  _xt = x - 2 - label.width(),
34889  xt = _xt>=0?_xt:x+3;
34890  draw_point(x-1,height()/2,color,opacity).draw_point(x+1,height()/2,color,opacity);
34891  if (allow_zero || txt[0]!='0' || txt[1]!=0)
34892  draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
34893  }
34894  } else { // Regular case.
34895  if (values_y[0]<values_y[siz]) draw_arrow(x,0,x,_height-1,color,opacity,30,5,pattern);
34896  else draw_arrow(x,_height-1,x,0,color,opacity,30,5,pattern);
34897  cimg_foroff(values_y,y) {
34898  cimg_snprintf(txt,sizeof(txt),"%g",(double)values_y(y));
34899  label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
34900  const int
34901  yi = (int)(y*(_height-1)/siz),
34902  _yt = yi - label.height()/2,
34903  yt = _yt<0?0:_yt+label.height()>=height()?height()-1-label.height():_yt,
34904  _xt = x - 2 - label.width(),
34905  xt = _xt>=0?_xt:x+3;
34906  draw_point(x-1,yi,color,opacity).draw_point(x+1,yi,color,opacity);
34907  if (allow_zero || txt[0]!='0' || txt[1]!=0)
34908  draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
34909  }
34910  }
34911  return *this;
34912  }
34913 
34915 
34925  template<typename tx, typename ty, typename tc>
34926  CImg<T>& draw_axes(const CImg<tx>& values_x, const CImg<ty>& values_y,
34927  const tc *const color, const float opacity=1,
34928  const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U,
34929  const unsigned int font_height=13, const bool allow_zero=true) {
34930  if (is_empty()) return *this;
34931  const CImg<tx> nvalues_x(values_x._data,values_x.size(),1,1,1,true);
34932  const int sizx = (int)values_x.size()-1, wm1 = width()-1;
34933  if (sizx>=0) {
34934  float ox = (float)*nvalues_x;
34935  for (unsigned int x = sizx?1:0; x<_width; ++x) {
34936  const float nx = (float)nvalues_x._linear_atX((float)x*sizx/wm1);
34937  if (nx*ox<=0) { draw_axis(nx==0?x:x-1,values_y,color,opacity,pattern_y,font_height,allow_zero); break; }
34938  ox = nx;
34939  }
34940  }
34941  const CImg<ty> nvalues_y(values_y._data,values_y.size(),1,1,1,true);
34942  const int sizy = (int)values_y.size()-1, hm1 = height()-1;
34943  if (sizy>0) {
34944  float oy = (float)nvalues_y[0];
34945  for (unsigned int y = sizy?1:0; y<_height; ++y) {
34946  const float ny = (float)nvalues_y._linear_atX((float)y*sizy/hm1);
34947  if (ny*oy<=0) { draw_axis(values_x,ny==0?y:y-1,color,opacity,pattern_x,font_height,allow_zero); break; }
34948  oy = ny;
34949  }
34950  }
34951  return *this;
34952  }
34953 
34955  template<typename tc>
34956  CImg<T>& draw_axes(const float x0, const float x1, const float y0, const float y1,
34957  const tc *const color, const float opacity=1,
34958  const int subdivisionx=-60, const int subdivisiony=-60,
34959  const float precisionx=0, const float precisiony=0,
34960  const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U,
34961  const unsigned int font_height=13) {
34962  if (is_empty()) return *this;
34963  const bool allow_zero = (x0*x1>0) || (y0*y1>0);
34964  const float
34965  dx = cimg::abs(x1-x0), dy = cimg::abs(y1-y0),
34966  px = dx<=0?1:precisionx==0?(float)std::pow(10.0,(int)std::log10(dx)-2.0):precisionx,
34967  py = dy<=0?1:precisiony==0?(float)std::pow(10.0,(int)std::log10(dy)-2.0):precisiony;
34968  if (x0!=x1 && y0!=y1)
34969  draw_axes(CImg<floatT>::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1).round(px),
34970  CImg<floatT>::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1).round(py),
34971  color,opacity,pattern_x,pattern_y,font_height,allow_zero);
34972  else if (x0==x1 && y0!=y1)
34973  draw_axis((int)x0,CImg<floatT>::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1).round(py),
34974  color,opacity,pattern_y,font_height);
34975  else if (x0!=x1 && y0==y1)
34976  draw_axis(CImg<floatT>::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1).round(px),(int)y0,
34977  color,opacity,pattern_x,font_height);
34978  return *this;
34979  }
34980 
34982 
34990  template<typename tx, typename ty, typename tc>
34991  CImg<T>& draw_grid(const CImg<tx>& values_x, const CImg<ty>& values_y,
34992  const tc *const color, const float opacity=1,
34993  const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) {
34994  if (is_empty()) return *this;
34995  if (values_x) cimg_foroff(values_x,x) {
34996  const int xi = (int)values_x[x];
34997  if (xi>=0 && xi<width()) draw_line(xi,0,xi,_height-1,color,opacity,pattern_x);
34998  }
34999  if (values_y) cimg_foroff(values_y,y) {
35000  const int yi = (int)values_y[y];
35001  if (yi>=0 && yi<height()) draw_line(0,yi,_width-1,yi,color,opacity,pattern_y);
35002  }
35003  return *this;
35004  }
35005 
35007  template<typename tc>
35008  CImg<T>& draw_grid(const float delta_x, const float delta_y,
35009  const float offsetx, const float offsety,
35010  const bool invertx, const bool inverty,
35011  const tc *const color, const float opacity=1,
35012  const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) {
35013  if (is_empty()) return *this;
35014  CImg<uintT> seqx, seqy;
35015  if (delta_x!=0) {
35016  const float dx = delta_x>0?delta_x:_width*-delta_x/100;
35017  const unsigned int nx = (unsigned int)(_width/dx);
35018  seqx = CImg<uintT>::sequence(1+nx,0,(unsigned int)(dx*nx));
35019  if (offsetx) cimg_foroff(seqx,x) seqx(x) = (unsigned int)cimg::mod(seqx(x)+offsetx,(float)_width);
35020  if (invertx) cimg_foroff(seqx,x) seqx(x) = _width - 1 - seqx(x);
35021  }
35022  if (delta_y!=0) {
35023  const float dy = delta_y>0?delta_y:_height*-delta_y/100;
35024  const unsigned int ny = (unsigned int)(_height/dy);
35025  seqy = CImg<uintT>::sequence(1+ny,0,(unsigned int)(dy*ny));
35026  if (offsety) cimg_foroff(seqy,y) seqy(y) = (unsigned int)cimg::mod(seqy(y)+offsety,(float)_height);
35027  if (inverty) cimg_foroff(seqy,y) seqy(y) = _height - 1 - seqy(y);
35028  }
35029  return draw_grid(seqx,seqy,color,opacity,pattern_x,pattern_y);
35030  }
35031 
35033 
35058  template<typename t, typename tc>
35060  const tc *const color, const float opacity=1,
35061  const unsigned int plot_type=1, const int vertex_type=1,
35062  const double ymin=0, const double ymax=0, const unsigned int pattern=~0U) {
35063  if (is_empty() || _height<=1) return *this;
35064  if (!color)
35065  throw CImgArgumentException(_cimg_instance
35066  "draw_graph(): Specified color is (null).",
35067  cimg_instance);
35068 
35069  // Create shaded colors for displaying bar plots.
35070  CImg<tc> color1, color2;
35071  if (plot_type==3) {
35072  color1.assign(_spectrum); color2.assign(_spectrum);
35073  cimg_forC(*this,c) {
35074  color1[c] = (tc)cimg::min((float)cimg::type<tc>::max(),color[c]*1.2f);
35075  color2[c] = (tc)(color[c]*0.4f);
35076  }
35077  }
35078 
35079  // Compute min/max and normalization factors.
35080  const unsigned long
35081  siz = data.size(),
35082  _siz1 = siz - (plot_type!=3?1:0),
35083  siz1 = _siz1?_siz1:1;
35084  const unsigned int
35085  _width1 = _width - (plot_type!=3?1:0),
35086  width1 = _width1?_width1:1;
35087  double m = ymin, M = ymax;
35088  if (ymin==ymax) m = (double)data.max_min(M);
35089  if (m==M) { --m; ++M; }
35090  const float ca = (float)(M-m)/(_height-1);
35091  bool init_hatch = true;
35092 
35093  // Draw graph edges
35094  switch (plot_type%4) {
35095  case 1 : { // Segments
35096  int oX = 0, oY = (int)((data[0]-m)/ca);
35097  const float fx = (float)_width1/siz1;
35098  if (siz==1) {
35099  const int Y = (int)((*data-m)/ca);
35100  draw_line(0,Y,width()-1,Y,color,opacity,pattern);
35101  } else for (unsigned long off = 1; off<siz; ++off) {
35102  const int
35103  X = (int)(off*fx),
35104  Y = (int)((data[off]-m)/ca);
35105  draw_line(oX,oY,X,Y,color,opacity,pattern,init_hatch);
35106  oX = X; oY = Y;
35107  init_hatch = false;
35108  }
35109  } break;
35110  case 2 : { // Spline
35111  const CImg<t> ndata(data._data,siz,1,1,1,true);
35112  int oY = (int)((data[0]-m)/ca);
35113  cimg_forX(*this,x) {
35114  const int Y = (int)((ndata._cubic_atX((float)x*siz1/width1)-m)/ca);
35115  if (x>0) draw_line(x,oY,x+1,Y,color,opacity,pattern,init_hatch);
35116  init_hatch = false;
35117  oY = Y;
35118  }
35119  } break;
35120  case 3 : { // Bars
35121  const int Y0 = (int)(-m/ca);
35122  const float fx = (float)_width/(siz-1);
35123  int oX = 0;
35124  cimg_foroff(data,off) {
35125  const int
35126  X = (int)((off+1)*fx),
35127  Y = (int)((data[off]-m)/ca);
35128  draw_rectangle(oX,Y0,X,Y,color,opacity).
35129  draw_line(oX,Y,oX,Y0,color2.data(),opacity).
35130  draw_line(oX,Y0,X,Y0,Y<=Y0?color2.data():color1.data(),opacity).
35131  draw_line(X,Y,X,Y0,color1.data(),opacity).
35132  draw_line(oX,Y,X,Y,Y<=Y0?color1.data():color2.data(),opacity);
35133  oX = X+1;
35134  }
35135  } break;
35136  default : break; // No edges
35137  }
35138 
35139  // Draw graph points
35140  const unsigned int wb2 = plot_type==3?_width1/(2*siz):0;
35141  const float fx = (float)_width1/siz1;
35142  switch (vertex_type%8) {
35143  case 1 : { // Point
35144  cimg_foroff(data,off) {
35145  const int
35146  X = (int)(off*fx) + wb2,
35147  Y = (int)((data[off]-m)/ca);
35148  draw_point(X,Y,color,opacity);
35149  }
35150  } break;
35151  case 2 : { // Straight Cross
35152  cimg_foroff(data,off) {
35153  const int
35154  X = (int)(off*fx) + wb2,
35155  Y = (int)((data[off]-m)/ca);
35156  draw_line(X-3,Y,X+3,Y,color,opacity).draw_line(X,Y-3,X,Y+3,color,opacity);
35157  }
35158  } break;
35159  case 3 : { // Diagonal Cross
35160  cimg_foroff(data,off) {
35161  const int
35162  X = (int)(off*fx) + wb2,
35163  Y = (int)((data[off]-m)/ca);
35164  draw_line(X-3,Y-3,X+3,Y+3,color,opacity).draw_line(X-3,Y+3,X+3,Y-3,color,opacity);
35165  }
35166  } break;
35167  case 4 : { // Filled Circle
35168  cimg_foroff(data,off) {
35169  const int
35170  X = (int)(off*fx) + wb2,
35171  Y = (int)((data[off]-m)/ca);
35172  draw_circle(X,Y,3,color,opacity);
35173  }
35174  } break;
35175  case 5 : { // Outlined circle
35176  cimg_foroff(data,off) {
35177  const int
35178  X = (int)(off*fx) + wb2,
35179  Y = (int)((data[off]-m)/ca);
35180  draw_circle(X,Y,3,color,opacity,0U);
35181  }
35182  } break;
35183  case 6 : { // Square
35184  cimg_foroff(data,off) {
35185  const int
35186  X = (int)(off*fx) + wb2,
35187  Y = (int)((data[off]-m)/ca);
35188  draw_rectangle(X-3,Y-3,X+3,Y+3,color,opacity,~0U);
35189  }
35190  } break;
35191  case 7 : { // Diamond
35192  cimg_foroff(data,off) {
35193  const int
35194  X = (int)(off*fx) + wb2,
35195  Y = (int)((data[off]-m)/ca);
35196  draw_line(X,Y-4,X+4,Y,color,opacity).
35197  draw_line(X+4,Y,X,Y+4,color,opacity).
35198  draw_line(X,Y+4,X-4,Y,color,opacity).
35199  draw_line(X-4,Y,X,Y-4,color,opacity);
35200  }
35201  } break;
35202  default : break; // No points
35203  }
35204  return *this;
35205  }
35206 
35208 
35219  template<typename tc, typename t>
35220  CImg<T>& draw_fill(const int x, const int y, const int z,
35221  const tc *const color, const float opacity,
35222  CImg<t>& region, const float sigma=0,
35223  const bool is_high_connexity=false) {
35224 
35225 #define _cimg_draw_fill_test(x,y,z,res) if (region(x,y,z)) res = false; else { \
35226  res = true; \
35227  const T *reference_col = reference_color._data + _spectrum, *ptrs = data(x,y,z) + siz; \
35228  for (unsigned int i = _spectrum; res && i; --i) { ptrs-=whd; res = (cimg::abs(*ptrs - *(--reference_col))<=sigma); } \
35229  region(x,y,z) = (t)(res?1:noregion); \
35230 }
35231 
35232 #define _cimg_draw_fill_set(x,y,z) { \
35233  const tc *col = color; \
35234  T *ptrd = data(x,y,z); \
35235  if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; } \
35236  else cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; } \
35237 }
35238 
35239 #define _cimg_draw_fill_insert(x,y,z) { \
35240  if (posr1>=remaining._height) remaining.resize(3,remaining._height<<1,1,1,0); \
35241  unsigned int *ptrr = remaining.data(0,posr1); \
35242  *(ptrr++) = x; *(ptrr++) = y; *(ptrr++) = z; ++posr1; \
35243 }
35244 
35245 #define _cimg_draw_fill_test_neighbor(x,y,z,cond) if (cond) { \
35246  const unsigned int tx = x, ty = y, tz = z; \
35247  _cimg_draw_fill_test(tx,ty,tz,res); if (res) _cimg_draw_fill_insert(tx,ty,tz); \
35248 }
35249 
35250  if (!color)
35251  throw CImgArgumentException(_cimg_instance
35252  "draw_fill(): Specified color is (null).",
35253  cimg_instance);
35254 
35255  region.assign(_width,_height,_depth,1,(t)0);
35256  if (x>=0 && x<width() && y>=0 && y<height() && z>=0 && z<depth()) {
35257  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
35258  const unsigned long whd = (unsigned long)_width*_height*_depth, siz = (unsigned long)_spectrum*whd;
35259  const unsigned int W1 = _width-1, H1 = _height-1, D1 = _depth-1;
35260  const bool is_3d = (_depth>1);
35261  const CImg<T> reference_color = get_vector_at(x,y,z);
35262  CImg<uintT> remaining(3,512,1,1,0);
35263  remaining(0,0) = x; remaining(1,0) = y; remaining(2,0) = z;
35264  unsigned int posr0 = 0, posr1 = 1;
35265  region(x,y,z) = (t)1;
35266  const t noregion = ((t)1==(t)2)?(t)0:(t)(-1);
35267  if (is_3d) do { // 3d version of the filling algorithm
35268  const unsigned int *pcurr = remaining.data(0,posr0++), xc = *(pcurr++), yc = *(pcurr++), zc = *(pcurr++);
35269  if (posr0>=512) { remaining.shift(0,-(int)posr0); posr1-=posr0; posr0 = 0; }
35270  bool cont, res;
35271  unsigned int nxc = xc;
35272  do { // X-backward
35273  _cimg_draw_fill_set(nxc,yc,zc);
35274  _cimg_draw_fill_test_neighbor(nxc,yc-1,zc,yc!=0);
35275  _cimg_draw_fill_test_neighbor(nxc,yc+1,zc,yc<H1);
35276  _cimg_draw_fill_test_neighbor(nxc,yc,zc-1,zc!=0);
35277  _cimg_draw_fill_test_neighbor(nxc,yc,zc+1,zc<D1);
35278  if (nxc) { --nxc; _cimg_draw_fill_test(nxc,yc,zc,cont); } else cont = false;
35279  } while (cont);
35280  nxc = xc;
35281  do { // X-forward
35282  if ((++nxc)<=W1) { _cimg_draw_fill_test(nxc,yc,zc,cont); } else cont = false;
35283  if (cont) {
35284  _cimg_draw_fill_set(nxc,yc,zc);
35285  _cimg_draw_fill_test_neighbor(nxc,yc-1,zc,yc!=0);
35286  _cimg_draw_fill_test_neighbor(nxc,yc+1,zc,yc<H1);
35287  _cimg_draw_fill_test_neighbor(nxc,yc,zc-1,zc!=0);
35288  _cimg_draw_fill_test_neighbor(nxc,yc,zc+1,zc<D1);
35289  }
35290  } while (cont);
35291  unsigned int nyc = yc;
35292  do { // Y-backward
35293  if (nyc) { --nyc; _cimg_draw_fill_test(xc,nyc,zc,cont); } else cont = false;
35294  if (cont) {
35295  _cimg_draw_fill_set(xc,nyc,zc);
35296  _cimg_draw_fill_test_neighbor(xc-1,nyc,zc,xc!=0);
35297  _cimg_draw_fill_test_neighbor(xc+1,nyc,zc,xc<W1);
35298  _cimg_draw_fill_test_neighbor(xc,nyc,zc-1,zc!=0);
35299  _cimg_draw_fill_test_neighbor(xc,nyc,zc+1,zc<D1);
35300  }
35301  } while (cont);
35302  nyc = yc;
35303  do { // Y-forward
35304  if ((++nyc)<=H1) { _cimg_draw_fill_test(xc,nyc,zc,cont); } else cont = false;
35305  if (cont) {
35306  _cimg_draw_fill_set(xc,nyc,zc);
35307  _cimg_draw_fill_test_neighbor(xc-1,nyc,zc,xc!=0);
35308  _cimg_draw_fill_test_neighbor(xc+1,nyc,zc,xc<W1);
35309  _cimg_draw_fill_test_neighbor(xc,nyc,zc-1,zc!=0);
35310  _cimg_draw_fill_test_neighbor(xc,nyc,zc+1,zc<D1);
35311  }
35312  } while (cont);
35313  unsigned int nzc = zc;
35314  do { // Z-backward
35315  if (nzc) { --nzc; _cimg_draw_fill_test(xc,yc,nzc,cont); } else cont = false;
35316  if (cont) {
35317  _cimg_draw_fill_set(xc,yc,nzc);
35318  _cimg_draw_fill_test_neighbor(xc-1,yc,nzc,xc!=0);
35319  _cimg_draw_fill_test_neighbor(xc+1,yc,nzc,xc<W1);
35320  _cimg_draw_fill_test_neighbor(xc,yc-1,nzc,yc!=0);
35321  _cimg_draw_fill_test_neighbor(xc,yc+1,nzc,yc<H1);
35322  }
35323  } while (cont);
35324  nzc = zc;
35325  do { // Z-forward
35326  if ((++nzc)<=D1) { _cimg_draw_fill_test(xc,yc,nzc,cont); } else cont = false;
35327  if (cont) {
35328  _cimg_draw_fill_set(xc,nyc,zc);
35329  _cimg_draw_fill_test_neighbor(xc-1,yc,nzc,xc!=0);
35330  _cimg_draw_fill_test_neighbor(xc+1,yc,nzc,xc<W1);
35331  _cimg_draw_fill_test_neighbor(xc,yc-1,nzc,yc!=0);
35332  _cimg_draw_fill_test_neighbor(xc,yc+1,nzc,yc<H1);
35333  }
35334  } while (cont);
35335  } while (posr1>posr0);
35336  else do { // 2d version of the filling algorithm
35337  const unsigned int *pcurr = remaining.data(0,posr0++), xc = *(pcurr++), yc = *(pcurr++);
35338  if (posr0>=512) { remaining.shift(0,-(int)posr0); posr1-=posr0; posr0 = 0; }
35339  bool cont, res;
35340  unsigned int nxc = xc;
35341  do { // X-backward
35342  _cimg_draw_fill_set(nxc,yc,0);
35343  _cimg_draw_fill_test_neighbor(nxc,yc-1,0,yc!=0);
35344  _cimg_draw_fill_test_neighbor(nxc,yc+1,0,yc<H1);
35345  if (is_high_connexity) {
35346  _cimg_draw_fill_test_neighbor(nxc-1,yc-1,0,(nxc!=0 && yc!=0));
35347  _cimg_draw_fill_test_neighbor(nxc+1,yc-1,0,(nxc<W1 && yc!=0));
35348  _cimg_draw_fill_test_neighbor(nxc-1,yc+1,0,(nxc!=0 && yc<H1));
35349  _cimg_draw_fill_test_neighbor(nxc+1,yc+1,0,(nxc<W1 && yc<H1));
35350  }
35351  if (nxc) { --nxc; _cimg_draw_fill_test(nxc,yc,0,cont); } else cont = false;
35352  } while (cont);
35353  nxc = xc;
35354  do { // X-forward
35355  if ((++nxc)<=W1) { _cimg_draw_fill_test(nxc,yc,0,cont); } else cont = false;
35356  if (cont) {
35357  _cimg_draw_fill_set(nxc,yc,0);
35358  _cimg_draw_fill_test_neighbor(nxc,yc-1,0,yc!=0);
35359  _cimg_draw_fill_test_neighbor(nxc,yc+1,0,yc<H1);
35360  if (is_high_connexity) {
35361  _cimg_draw_fill_test_neighbor(nxc-1,yc-1,0,(nxc!=0 && yc!=0));
35362  _cimg_draw_fill_test_neighbor(nxc+1,yc-1,0,(nxc<W1 && yc!=0));
35363  _cimg_draw_fill_test_neighbor(nxc-1,yc+1,0,(nxc!=0 && yc<H1));
35364  _cimg_draw_fill_test_neighbor(nxc+1,yc+1,0,(nxc<W1 && yc<H1));
35365  }
35366  }
35367  } while (cont);
35368  unsigned int nyc = yc;
35369  do { // Y-backward
35370  if (nyc) { --nyc; _cimg_draw_fill_test(xc,nyc,0,cont); } else cont = false;
35371  if (cont) {
35372  _cimg_draw_fill_set(xc,nyc,0);
35373  _cimg_draw_fill_test_neighbor(xc-1,nyc,0,xc!=0);
35374  _cimg_draw_fill_test_neighbor(xc+1,nyc,0,xc<W1);
35375  if (is_high_connexity) {
35376  _cimg_draw_fill_test_neighbor(xc-1,nyc-1,0,(xc!=0 && nyc!=0));
35377  _cimg_draw_fill_test_neighbor(xc+1,nyc-1,0,(xc<W1 && nyc!=0));
35378  _cimg_draw_fill_test_neighbor(xc-1,nyc+1,0,(xc!=0 && nyc<H1));
35379  _cimg_draw_fill_test_neighbor(xc+1,nyc+1,0,(xc<W1 && nyc<H1));
35380  }
35381  }
35382  } while (cont);
35383  nyc = yc;
35384  do { // Y-forward
35385  if ((++nyc)<=H1) { _cimg_draw_fill_test(xc,nyc,0,cont); } else cont = false;
35386  if (cont) {
35387  _cimg_draw_fill_set(xc,nyc,0);
35388  _cimg_draw_fill_test_neighbor(xc-1,nyc,0,xc!=0);
35389  _cimg_draw_fill_test_neighbor(xc+1,nyc,0,xc<W1);
35390  if (is_high_connexity) {
35391  _cimg_draw_fill_test_neighbor(xc-1,nyc-1,0,(xc!=0 && nyc!=0));
35392  _cimg_draw_fill_test_neighbor(xc+1,nyc-1,0,(xc<W1 && nyc!=0));
35393  _cimg_draw_fill_test_neighbor(xc-1,nyc+1,0,(xc!=0 && nyc<H1));
35394  _cimg_draw_fill_test_neighbor(xc+1,nyc+1,0,(xc<W1 && nyc<H1));
35395  }
35396  }
35397  } while (cont);
35398  } while (posr1>posr0);
35399  if (noregion) cimg_for(region,ptrd,t) if (*ptrd==noregion) *ptrd = (t)0;
35400  }
35401  return *this;
35402  }
35403 
35405  template<typename tc>
35406  CImg<T>& draw_fill(const int x, const int y, const int z,
35407  const tc *const color, const float opacity=1,
35408  const float sigma=0, const bool is_high_connexity=false) {
35409  CImg<boolT> tmp;
35410  return draw_fill(x,y,z,color,opacity,tmp,sigma,is_high_connexity);
35411  }
35412 
35414  template<typename tc>
35415  CImg<T>& draw_fill(const int x, const int y,
35416  const tc *const color, const float opacity=1,
35417  const float sigma=0, const bool is_high_connexity=false) {
35418  CImg<boolT> tmp;
35419  return draw_fill(x,y,0,color,opacity,tmp,sigma,is_high_connexity);
35420  }
35421 
35423 
35429  CImg<T>& draw_plasma(const float alpha=1, const float beta=0, const unsigned int scale=8) {
35430  if (is_empty()) return *this;
35431  const int w = width(), h = height();
35432  const Tfloat m = (Tfloat)cimg::type<T>::min(), M = (Tfloat)cimg::type<T>::max();
35433  cimg_forZC(*this,z,c) {
35434  CImg<T> ref = get_shared_slice(z,c);
35435  for (int delta = 1<<cimg::min(scale,31U); delta>1; delta>>=1) {
35436  const int delta2 = delta>>1;
35437  const float r = alpha*delta + beta;
35438 
35439  // Square step.
35440  for (int y0 = 0; y0<h; y0+=delta)
35441  for (int x0 = 0; x0<w; x0+=delta) {
35442  const int x1 = (x0 + delta)%w, y1 = (y0 + delta)%h, xc = (x0 + delta2)%w, yc = (y0 + delta2)%h;
35443  const Tfloat val = (Tfloat)(0.25f*(ref(x0,y0) + ref(x0,y1) + ref(x0,y1) + ref(x1,y1)) + r*cimg::crand());
35444  ref(xc,yc) = (T)(val<m?m:val>M?M:val);
35445  }
35446 
35447  // Diamond steps.
35448  for (int y = -delta2; y<h; y+=delta)
35449  for (int x0=0; x0<w; x0+=delta) {
35450  const int y0 = cimg::mod(y,h), x1 = (x0 + delta)%w, y1 = (y + delta)%h,
35451  xc = (x0 + delta2)%w, yc = (y + delta2)%h;
35452  const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) + r*cimg::crand());
35453  ref(xc,yc) = (T)(val<m?m:val>M?M:val);
35454  }
35455  for (int y0 = 0; y0<h; y0+=delta)
35456  for (int x = -delta2; x<w; x+=delta) {
35457  const int x0 = cimg::mod(x,w), x1 = (x + delta)%w, y1 = (y0 + delta)%h,
35458  xc = (x + delta2)%w, yc = (y0 + delta2)%h;
35459  const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) + r*cimg::crand());
35460  ref(xc,yc) = (T)(val<m?m:val>M?M:val);
35461  }
35462  for (int y = -delta2; y<h; y+=delta)
35463  for (int x = -delta2; x<w; x+=delta) {
35464  const int x0 = cimg::mod(x,w), y0 = cimg::mod(y,h), x1 = (x + delta)%w, y1 = (y + delta)%h,
35465  xc = (x + delta2)%w, yc = (y + delta2)%h;
35466  const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) + r*cimg::crand());
35467  ref(xc,yc) = (T)(val<m?m:val>M?M:val);
35468  }
35469  }
35470  }
35471  return *this;
35472  }
35473 
35475 
35493  template<typename tc>
35494  CImg<T>& draw_mandelbrot(const int x0, const int y0, const int x1, const int y1,
35495  const CImg<tc>& colormap, const float opacity=1,
35496  const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2,
35497  const unsigned int iteration_max=255,
35498  const bool is_normalized_iteration=false,
35499  const bool is_julia_set=false,
35500  const double param_r=0, const double param_i=0) {
35501  if (is_empty()) return *this;
35502  CImg<tc> palette;
35503  if (colormap) palette.assign(colormap._data,colormap.size()/colormap._spectrum,1,1,colormap._spectrum,true);
35504  if (palette && palette._spectrum!=_spectrum)
35505  throw CImgArgumentException(_cimg_instance
35506  "draw_mandelbrot(): Instance and specified colormap (%u,%u,%u,%u,%p) have "
35507  "incompatible dimensions.",
35508  cimg_instance,
35509  colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data);
35510 
35511  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0), ln2 = (float)std::log(2.0);
35512  const int
35513  _x0 = x0<0?0:x0>=width()?width()-1:x0,
35514  _y0 = y0<0?0:y0>=height()?height()-1:y0,
35515  _x1 = x1<0?1:x1>=width()?width()-1:x1,
35516  _y1 = y1<0?1:y1>=height()?height()-1:y1;
35517 #ifdef cimg_use_openmp
35518 #pragma omp parallel for collapse(2) if ((1+_x1-_x0)*(1+_y1-_y0)>=2048)
35519 #endif
35520  for (int q = _y0; q<=_y1; ++q)
35521  for (int p = _x0; p<=_x1; ++p) {
35522  unsigned int iteration = 0;
35523  const double x = z0r + p*(z1r-z0r)/_width, y = z0i + q*(z1i-z0i)/_height;
35524  double zr, zi, cr, ci;
35525  if (is_julia_set) { zr = x; zi = y; cr = param_r; ci = param_i; }
35526  else { zr = param_r; zi = param_i; cr = x; ci = y; }
35527  for (iteration=1; zr*zr + zi*zi<=4 && iteration<=iteration_max; ++iteration) {
35528  const double temp = zr*zr - zi*zi + cr;
35529  zi = 2*zr*zi + ci;
35530  zr = temp;
35531  }
35532  if (iteration>iteration_max) {
35533  if (palette) {
35534  if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette(0,c);
35535  else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(0,c)*nopacity + (*this)(p,q,0,c)*copacity);
35536  } else {
35537  if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)0;
35538  else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)((*this)(p,q,0,c)*copacity);
35539  }
35540  } else if (is_normalized_iteration) {
35541  const float
35542  normz = (float)cimg::abs(zr*zr+zi*zi),
35543  niteration = (float)(iteration + 1 - std::log(std::log(normz))/ln2);
35544  if (palette) {
35545  if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._linear_atX(niteration,c);
35546  else cimg_forC(*this,c)
35547  (*this)(p,q,0,c) = (T)(palette._linear_atX(niteration,c)*nopacity + (*this)(p,q,0,c)*copacity);
35548  } else {
35549  if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)niteration;
35550  else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(niteration*nopacity + (*this)(p,q,0,c)*copacity);
35551  }
35552  } else {
35553  if (palette) {
35554  if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._atX(iteration,c);
35555  else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(iteration,c)*nopacity + (*this)(p,q,0,c)*copacity);
35556  } else {
35557  if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)iteration;
35558  else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(iteration*nopacity + (*this)(p,q,0,c)*copacity);
35559  }
35560  }
35561  }
35562  return *this;
35563  }
35564 
35566  template<typename tc>
35567  CImg<T>& draw_mandelbrot(const CImg<tc>& colormap, const float opacity=1,
35568  const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2,
35569  const unsigned int iteration_max=255,
35570  const bool is_normalized_iteration=false,
35571  const bool is_julia_set=false,
35572  const double param_r=0, const double param_i=0) {
35573  return draw_mandelbrot(0,0,_width-1,_height-1,colormap,opacity,
35574  z0r,z0i,z1r,z1i,iteration_max,is_normalized_iteration,is_julia_set,param_r,param_i);
35575  }
35576 
35578 
35584  template<typename tc>
35585  CImg<T>& draw_gaussian(const float xc, const float sigma,
35586  const tc *const color, const float opacity=1) {
35587  if (is_empty()) return *this;
35588  if (!color)
35589  throw CImgArgumentException(_cimg_instance
35590  "draw_gaussian(): Specified color is (null).",
35591  cimg_instance);
35592  const float sigma2 = 2*sigma*sigma, nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
35593  const unsigned long whd = (unsigned long)_width*_height*_depth;
35594  const tc *col = color;
35595  cimg_forX(*this,x) {
35596  const float dx = (x - xc), val = (float)std::exp(-dx*dx/sigma2);
35597  T *ptrd = data(x,0,0,0);
35598  if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
35599  else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
35600  col-=_spectrum;
35601  }
35602  return *this;
35603  }
35604 
35606 
35613  template<typename t, typename tc>
35614  CImg<T>& draw_gaussian(const float xc, const float yc, const CImg<t>& tensor,
35615  const tc *const color, const float opacity=1) {
35616  if (is_empty()) return *this;
35617  if (tensor._width!=2 || tensor._height!=2 || tensor._depth!=1 || tensor._spectrum!=1)
35618  throw CImgArgumentException(_cimg_instance
35619  "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 2x2 matrix.",
35620  cimg_instance,
35621  tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data);
35622  if (!color)
35623  throw CImgArgumentException(_cimg_instance
35624  "draw_gaussian(): Specified color is (null).",
35625  cimg_instance);
35626  typedef typename CImg<t>::Tfloat tfloat;
35627  const CImg<tfloat> invT = tensor.get_invert(), invT2 = (invT*invT)/(-2.0);
35628  const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = invT2(1,1);
35629  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
35630  const unsigned long whd = (unsigned long)_width*_height*_depth;
35631  const tc *col = color;
35632  float dy = -yc;
35633  cimg_forY(*this,y) {
35634  float dx = -xc;
35635  cimg_forX(*this,x) {
35636  const float val = (float)std::exp(a*dx*dx + b*dx*dy + c*dy*dy);
35637  T *ptrd = data(x,y,0,0);
35638  if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
35639  else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
35640  col-=_spectrum;
35641  ++dx;
35642  }
35643  ++dy;
35644  }
35645  return *this;
35646  }
35647 
35649  template<typename tc>
35650  CImg<T>& draw_gaussian(const int xc, const int yc, const float r1, const float r2, const float ru, const float rv,
35651  const tc *const color, const float opacity=1) {
35652  const double
35653  a = r1*ru*ru + r2*rv*rv,
35654  b = (r1-r2)*ru*rv,
35655  c = r1*rv*rv + r2*ru*ru;
35656  const CImg<Tfloat> tensor(2,2,1,1, a,b,b,c);
35657  return draw_gaussian(xc,yc,tensor,color,opacity);
35658  }
35659 
35661  template<typename tc>
35662  CImg<T>& draw_gaussian(const float xc, const float yc, const float sigma,
35663  const tc *const color, const float opacity=1) {
35664  return draw_gaussian(xc,yc,CImg<floatT>::diagonal(sigma,sigma),color,opacity);
35665  }
35666 
35668  template<typename t, typename tc>
35669  CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const CImg<t>& tensor,
35670  const tc *const color, const float opacity=1) {
35671  if (is_empty()) return *this;
35672  typedef typename CImg<t>::Tfloat tfloat;
35673  if (tensor._width!=3 || tensor._height!=3 || tensor._depth!=1 || tensor._spectrum!=1)
35674  throw CImgArgumentException(_cimg_instance
35675  "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 3x3 matrix.",
35676  cimg_instance,
35677  tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data);
35678 
35679  const CImg<tfloat> invT = tensor.get_invert(), invT2 = (invT*invT)/(-2.0);
35680  const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = 2*invT2(2,0), d = invT2(1,1), e = 2*invT2(2,1), f = invT2(2,2);
35681  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
35682  const unsigned long whd = (unsigned long)_width*_height*_depth;
35683  const tc *col = color;
35684  cimg_forXYZ(*this,x,y,z) {
35685  const float
35686  dx = (x - xc), dy = (y - yc), dz = (z - zc),
35687  val = (float)std::exp(a*dx*dx + b*dx*dy + c*dx*dz + d*dy*dy + e*dy*dz + f*dz*dz);
35688  T *ptrd = data(x,y,z,0);
35689  if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
35690  else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
35691  col-=_spectrum;
35692  }
35693  return *this;
35694  }
35695 
35697  template<typename tc>
35698  CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const float sigma,
35699  const tc *const color, const float opacity=1) {
35700  return draw_gaussian(xc,yc,zc,CImg<floatT>::diagonal(sigma,sigma,sigma),color,opacity);
35701  }
35702 
35704 
35721  template<typename tp, typename tf, typename tc, typename to>
35722  CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
35723  const CImg<tp>& vertices, const CImgList<tf>& primitives,
35724  const CImgList<tc>& colors, const CImg<to>& opacities,
35725  const unsigned int render_type=4,
35726  const bool is_double_sided=false, const float focale=700,
35727  const float lightx=0, const float lighty=0, const float lightz=-5e8,
35728  const float specular_lightness=0.2f, const float specular_shininess=0.1f) {
35729  return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type,
35730  is_double_sided,focale,lightx,lighty,lightz,
35731  specular_lightness,specular_shininess,CImg<floatT>::empty());
35732  }
35733 
35735  template<typename tp, typename tf, typename tc, typename to, typename tz>
35736  CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
35737  const CImg<tp>& vertices, const CImgList<tf>& primitives,
35738  const CImgList<tc>& colors, const CImg<to>& opacities,
35739  const unsigned int render_type,
35740  const bool is_double_sided, const float focale,
35741  const float lightx, const float lighty, const float lightz,
35742  const float specular_lightness, const float specular_shininess,
35743  CImg<tz>& zbuffer) {
35744  return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
35745  render_type,is_double_sided,focale,lightx,lighty,lightz,
35746  specular_lightness,specular_shininess,1);
35747  }
35748 
35749 #ifdef cimg_use_board
35750  template<typename tp, typename tf, typename tc, typename to>
35751  CImg<T>& draw_object3d(LibBoard::Board& board,
35752  const float x0, const float y0, const float z0,
35753  const CImg<tp>& vertices, const CImgList<tf>& primitives,
35754  const CImgList<tc>& colors, const CImg<to>& opacities,
35755  const unsigned int render_type=4,
35756  const bool is_double_sided=false, const float focale=700,
35757  const float lightx=0, const float lighty=0, const float lightz=-5e8,
35758  const float specular_lightness=0.2f, const float specular_shininess=0.1f) {
35759  return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type,
35760  is_double_sided,focale,lightx,lighty,lightz,
35761  specular_lightness,specular_shininess,CImg<floatT>::empty());
35762  }
35763 
35764  template<typename tp, typename tf, typename tc, typename to, typename tz>
35765  CImg<T>& draw_object3d(LibBoard::Board& board,
35766  const float x0, const float y0, const float z0,
35767  const CImg<tp>& vertices, const CImgList<tf>& primitives,
35768  const CImgList<tc>& colors, const CImg<to>& opacities,
35769  const unsigned int render_type,
35770  const bool is_double_sided, const float focale,
35771  const float lightx, const float lighty, const float lightz,
35772  const float specular_lightness, const float specular_shininess,
35773  CImg<tz>& zbuffer) {
35774  return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
35775  render_type,is_double_sided,focale,lightx,lighty,lightz,
35776  specular_lightness,specular_shininess,1);
35777  }
35778 #endif
35779 
35781  template<typename tp, typename tf, typename tc, typename to>
35782  CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
35783  const CImg<tp>& vertices, const CImgList<tf>& primitives,
35784  const CImgList<tc>& colors, const CImgList<to>& opacities,
35785  const unsigned int render_type=4,
35786  const bool is_double_sided=false, const float focale=700,
35787  const float lightx=0, const float lighty=0, const float lightz=-5e8,
35788  const float specular_lightness=0.2f, const float specular_shininess=0.1f) {
35789  return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type,
35790  is_double_sided,focale,lightx,lighty,lightz,
35791  specular_lightness,specular_shininess,CImg<floatT>::empty());
35792  }
35793 
35795  template<typename tp, typename tf, typename tc, typename to, typename tz>
35796  CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
35797  const CImg<tp>& vertices, const CImgList<tf>& primitives,
35798  const CImgList<tc>& colors, const CImgList<to>& opacities,
35799  const unsigned int render_type,
35800  const bool is_double_sided, const float focale,
35801  const float lightx, const float lighty, const float lightz,
35802  const float specular_lightness, const float specular_shininess,
35803  CImg<tz>& zbuffer) {
35804  return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
35805  render_type,is_double_sided,focale,lightx,lighty,lightz,
35806  specular_lightness,specular_shininess,1);
35807  }
35808 
35809 #ifdef cimg_use_board
35810  template<typename tp, typename tf, typename tc, typename to>
35811  CImg<T>& draw_object3d(LibBoard::Board& board,
35812  const float x0, const float y0, const float z0,
35813  const CImg<tp>& vertices, const CImgList<tf>& primitives,
35814  const CImgList<tc>& colors, const CImgList<to>& opacities,
35815  const unsigned int render_type=4,
35816  const bool is_double_sided=false, const float focale=700,
35817  const float lightx=0, const float lighty=0, const float lightz=-5e8,
35818  const float specular_lightness=0.2f, const float specular_shininess=0.1f) {
35819  return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type,
35820  is_double_sided,focale,lightx,lighty,lightz,
35821  specular_lightness,specular_shininess,CImg<floatT>::empty());
35822  }
35823 
35824  template<typename tp, typename tf, typename tc, typename to, typename tz>
35825  CImg<T>& draw_object3d(LibBoard::Board& board,
35826  const float x0, const float y0, const float z0,
35827  const CImg<tp>& vertices, const CImgList<tf>& primitives,
35828  const CImgList<tc>& colors, const CImgList<to>& opacities,
35829  const unsigned int render_type,
35830  const bool is_double_sided, const float focale,
35831  const float lightx, const float lighty, const float lightz,
35832  const float specular_lightness, const float specular_shininess,
35833  CImg<tz>& zbuffer) {
35834  return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
35835  render_type,is_double_sided,focale,lightx,lighty,lightz,
35836  specular_lightness,specular_shininess,1);
35837  }
35838 #endif
35839 
35841  template<typename tp, typename tf, typename tc>
35842  CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
35843  const CImg<tp>& vertices, const CImgList<tf>& primitives,
35844  const CImgList<tc>& colors,
35845  const unsigned int render_type=4,
35846  const bool is_double_sided=false, const float focale=700,
35847  const float lightx=0, const float lighty=0, const float lightz=-5e8,
35848  const float specular_lightness=0.2f, const float specular_shininess=0.1f) {
35849  return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::empty(),
35850  render_type,is_double_sided,focale,lightx,lighty,lightz,
35851  specular_lightness,specular_shininess,CImg<floatT>::empty());
35852  }
35853 
35855  template<typename tp, typename tf, typename tc, typename tz>
35856  CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
35857  const CImg<tp>& vertices, const CImgList<tf>& primitives,
35858  const CImgList<tc>& colors,
35859  const unsigned int render_type,
35860  const bool is_double_sided, const float focale,
35861  const float lightx, const float lighty, const float lightz,
35862  const float specular_lightness, const float specular_shininess,
35863  CImg<tz>& zbuffer) {
35864  return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::empty(),
35865  render_type,is_double_sided,focale,lightx,lighty,lightz,
35866  specular_lightness,specular_shininess,zbuffer);
35867  }
35868 
35869 #ifdef cimg_use_board
35870  template<typename tp, typename tf, typename tc, typename to>
35871  CImg<T>& draw_object3d(LibBoard::Board& board,
35872  const float x0, const float y0, const float z0,
35873  const CImg<tp>& vertices, const CImgList<tf>& primitives,
35874  const CImgList<tc>& colors,
35875  const unsigned int render_type=4,
35876  const bool is_double_sided=false, const float focale=700,
35877  const float lightx=0, const float lighty=0, const float lightz=-5e8,
35878  const float specular_lightness=0.2f, const float specular_shininess=0.1f) {
35879  return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::empty(),
35880  render_type,is_double_sided,focale,lightx,lighty,lightz,
35881  specular_lightness,specular_shininess,CImg<floatT>::empty());
35882  }
35883 
35884  template<typename tp, typename tf, typename tc, typename to, typename tz>
35885  CImg<T>& draw_object3d(LibBoard::Board& board,
35886  const float x0, const float y0, const float z0,
35887  const CImg<tp>& vertices, const CImgList<tf>& primitives,
35888  const CImgList<tc>& colors,
35889  const unsigned int render_type,
35890  const bool is_double_sided, const float focale,
35891  const float lightx, const float lighty, const float lightz,
35892  const float specular_lightness, const float specular_shininess,
35893  CImg<tz>& zbuffer) {
35894  return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::empty(),
35895  render_type,is_double_sided,focale,lightx,lighty,lightz,
35896  specular_lightness,specular_shininess,zbuffer);
35897  }
35898 #endif
35899 
35900  template<typename t, typename to>
35901  static float __draw_object3d(const CImgList<t>& opacities, const unsigned int n_primitive, CImg<to>& opacity) {
35902  if (n_primitive>=opacities._width || opacities[n_primitive].is_empty()) { opacity.assign(); return 1; }
35903  if (opacities[n_primitive].size()==1) { opacity.assign(); return opacities(n_primitive,0); }
35904  opacity.assign(opacities[n_primitive],true);
35905  return 1.0f;
35906  }
35907 
35908  template<typename t, typename to>
35909  static float __draw_object3d(const CImg<t>& opacities, const unsigned int n_primitive, CImg<to>& opacity) {
35910  opacity.assign();
35911  return n_primitive>=opacities._width?1.0f:(float)opacities[n_primitive];
35912  }
35913 
35914  template<typename tz, typename tp, typename tf, typename tc, typename to>
35915  CImg<T>& _draw_object3d(void *const pboard, CImg<tz>& zbuffer,
35916  const float X, const float Y, const float Z,
35917  const CImg<tp>& vertices,
35918  const CImgList<tf>& primitives,
35919  const CImgList<tc>& colors,
35920  const to& opacities,
35921  const unsigned int render_type,
35922  const bool is_double_sided, const float focale,
35923  const float lightx, const float lighty, const float lightz,
35924  const float specular_lightness, const float specular_shininess,
35925  const float sprite_scale) {
35926  typedef typename cimg::superset2<tp,tz,float>::type tpfloat;
35927  if (is_empty() || !vertices || !primitives) return *this;
35928  char error_message[1024] = { 0 };
35929  if (!vertices.is_object3d(primitives,colors,opacities,false,error_message))
35930  throw CImgArgumentException(_cimg_instance
35931  "draw_object3d(): Invalid specified 3d object (%u,%u) (%s).",
35932  cimg_instance,vertices._width,primitives._width,error_message);
35933  if (render_type==5) cimg::mutex(10); // Static variable used in this case, breaks thread-safety.
35934 
35935 #ifndef cimg_use_board
35936  if (pboard) return *this;
35937 #endif
35938  const float
35939  nspec = 1 - (specular_lightness<0.0f?0.0f:(specular_lightness>1.0f?1.0f:specular_lightness)),
35940  nspec2 = 1 + (specular_shininess<0.0f?0.0f:specular_shininess),
35941  nsl1 = (nspec2 - 1)/cimg::sqr(nspec - 1),
35942  nsl2 = 1 - 2*nsl1*nspec,
35943  nsl3 = nspec2 - nsl1 - nsl2;
35944 
35945  // Create light texture for phong-like rendering.
35946  CImg<floatT> light_texture;
35947  if (render_type==5) {
35948  if (colors._width>primitives._width) {
35949  static CImg<floatT> default_light_texture;
35950  static const tc *lptr = 0;
35951  static tc ref_values[64] = { 0 };
35952  const CImg<tc>& img = colors.back();
35953  bool is_same_texture = (lptr==img._data);
35954  if (is_same_texture)
35955  for (unsigned int r = 0, j = 0; j<8; ++j)
35956  for (unsigned int i = 0; i<8; ++i)
35957  if (ref_values[r++]!=img(i*img._width/9,j*img._height/9,0,(i+j)%img._spectrum)) {
35958  is_same_texture = false; break;
35959  }
35960  if (!is_same_texture || default_light_texture._spectrum<_spectrum) {
35961  (default_light_texture.assign(img,false)/=255).resize(-100,-100,1,_spectrum);
35962  lptr = colors.back().data();
35963  for (unsigned int r = 0, j = 0; j<8; ++j)
35964  for (unsigned int i = 0; i<8; ++i)
35965  ref_values[r++] = img(i*img._width/9,j*img._height/9,0,(i+j)%img._spectrum);
35966  }
35967  light_texture.assign(default_light_texture,true);
35968  } else {
35969  static CImg<floatT> default_light_texture;
35970  static float olightx = 0, olighty = 0, olightz = 0, ospecular_shininess = 0;
35971  if (!default_light_texture ||
35972  lightx!=olightx || lighty!=olighty || lightz!=olightz ||
35973  specular_shininess!=ospecular_shininess || default_light_texture._spectrum<_spectrum) {
35974  default_light_texture.assign(512,512);
35975  const float
35976  dlx = lightx - X,
35977  dly = lighty - Y,
35978  dlz = lightz - Z,
35979  nl = (float)std::sqrt(dlx*dlx + dly*dly + dlz*dlz),
35980  nlx = (default_light_texture._width - 1)/2*(1 + dlx/nl),
35981  nly = (default_light_texture._height - 1)/2*(1 + dly/nl),
35982  white[] = { 1 };
35983  default_light_texture.draw_gaussian(nlx,nly,default_light_texture._width/3.0f,white);
35984  cimg_forXY(default_light_texture,x,y) {
35985  const float factor = default_light_texture(x,y);
35986  if (factor>nspec) default_light_texture(x,y) = cimg::min(2,nsl1*factor*factor + nsl2*factor + nsl3);
35987  }
35988  default_light_texture.resize(-100,-100,1,_spectrum);
35989  olightx = lightx; olighty = lighty; olightz = lightz; ospecular_shininess = specular_shininess;
35990  }
35991  light_texture.assign(default_light_texture,true);
35992  }
35993  }
35994 
35995  // Compute 3d to 2d projection.
35996  CImg<tpfloat> projections(vertices._width,2);
35997  tpfloat parallzmin = cimg::type<tpfloat>::max();
35998  const float absfocale = focale?cimg::abs(focale):0;
35999  if (absfocale) {
36000 #ifdef cimg_use_openmp
36001 #pragma omp parallel for if (projections.size()>4096)
36002 #endif
36003  cimg_forX(projections,l) { // Perspective projection
36004  const tpfloat
36005  x = (tpfloat)vertices(l,0),
36006  y = (tpfloat)vertices(l,1),
36007  z = (tpfloat)vertices(l,2);
36008  const tpfloat projectedz = z + Z + absfocale;
36009  projections(l,1) = Y + absfocale*y/projectedz;
36010  projections(l,0) = X + absfocale*x/projectedz;
36011  }
36012 
36013  } else {
36014 #ifdef cimg_use_openmp
36015 #pragma omp parallel for if (projections.size()>4096)
36016 #endif
36017  cimg_forX(projections,l) { // Parallel projection
36018  const tpfloat
36019  x = (tpfloat)vertices(l,0),
36020  y = (tpfloat)vertices(l,1),
36021  z = (tpfloat)vertices(l,2);
36022  if (z<parallzmin) parallzmin = z;
36023  projections(l,1) = Y + y;
36024  projections(l,0) = X + x;
36025  }
36026  }
36027  const float _focale = absfocale?absfocale:(1e5f-parallzmin);
36028 
36029  // Compute visible primitives.
36030  CImg<uintT> visibles(primitives._width,1,1,1,~0U);
36031  CImg<tpfloat> zrange(primitives._width);
36032  const tpfloat zmin = absfocale?(tpfloat)(1.5f - absfocale):cimg::type<tpfloat>::min();
36033 
36034 #ifdef cimg_use_openmp
36035 #pragma omp parallel for if (primitives.size()>4096)
36036 #endif
36037  cimglist_for(primitives,l) {
36038  const CImg<tf>& primitive = primitives[l];
36039  switch (primitive.size()) {
36040  case 1 : { // Point
36041  const unsigned int i0 = (unsigned int)primitive(0);
36042  const tpfloat z0 = Z + vertices(i0,2);
36043  if (z0>zmin) {
36044  visibles(l) = (unsigned int)l;
36045  zrange(l) = z0;
36046  }
36047  } break;
36048  case 5 : { // Sphere
36049  const unsigned int
36050  i0 = (unsigned int)primitive(0),
36051  i1 = (unsigned int)primitive(1);
36052  const tpfloat
36053  Xc = 0.5f*((float)vertices(i0,0) + (float)vertices(i1,0)),
36054  Yc = 0.5f*((float)vertices(i0,1) + (float)vertices(i1,1)),
36055  Zc = 0.5f*((float)vertices(i0,2) + (float)vertices(i1,2)),
36056  _zc = Z + Zc,
36057  zc = _zc + _focale,
36058  xc = X + Xc*(absfocale?absfocale/zc:1),
36059  yc = Y + Yc*(absfocale?absfocale/zc:1),
36060  radius = 0.5f*std::sqrt(cimg::sqr(vertices(i1,0) - vertices(i0,0)) +
36061  cimg::sqr(vertices(i1,1) - vertices(i0,1)) +
36062  cimg::sqr(vertices(i1,2) - vertices(i0,2)))*(absfocale?absfocale/zc:1),
36063  xm = xc - radius,
36064  ym = yc - radius,
36065  xM = xc + radius,
36066  yM = yc + radius;
36067  if (xM>=0 && xm<_width && yM>=0 && ym<_height && _zc>zmin) {
36068  visibles(l) = (unsigned int)l;
36069  zrange(l) = _zc;
36070  }
36071  } break;
36072  case 2 : // Segment
36073  case 6 : {
36074  const unsigned int
36075  i0 = (unsigned int)primitive(0),
36076  i1 = (unsigned int)primitive(1);
36077  const tpfloat
36078  x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
36079  x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2);
36080  tpfloat xm, xM, ym, yM;
36081  if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
36082  if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
36083  if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin) {
36084  visibles(l) = (unsigned int)l;
36085  zrange(l) = (z0 + z1)/2;
36086  }
36087  } break;
36088  case 3 : // Triangle
36089  case 9 : {
36090  const unsigned int
36091  i0 = (unsigned int)primitive(0),
36092  i1 = (unsigned int)primitive(1),
36093  i2 = (unsigned int)primitive(2);
36094  const tpfloat
36095  x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
36096  x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2),
36097  x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2);
36098  tpfloat xm, xM, ym, yM;
36099  if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
36100  if (x2<xm) xm = x2;
36101  if (x2>xM) xM = x2;
36102  if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
36103  if (y2<ym) ym = y2;
36104  if (y2>yM) yM = y2;
36105  if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin) {
36106  const tpfloat d = (x1-x0)*(y2-y0) - (x2-x0)*(y1-y0);
36107  if (is_double_sided || d<0) {
36108  visibles(l) = (unsigned int)l;
36109  zrange(l) = (z0 + z1 + z2)/3;
36110  }
36111  }
36112  } break;
36113  case 4 : // Rectangle
36114  case 12 : {
36115  const unsigned int
36116  i0 = (unsigned int)primitive(0),
36117  i1 = (unsigned int)primitive(1),
36118  i2 = (unsigned int)primitive(2),
36119  i3 = (unsigned int)primitive(3);
36120  const tpfloat
36121  x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
36122  x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2),
36123  x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2),
36124  x3 = projections(i3,0), y3 = projections(i3,1), z3 = Z + vertices(i3,2);
36125  tpfloat xm, xM, ym, yM;
36126  if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
36127  if (x2<xm) xm = x2;
36128  if (x2>xM) xM = x2;
36129  if (x3<xm) xm = x3;
36130  if (x3>xM) xM = x3;
36131  if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
36132  if (y2<ym) ym = y2;
36133  if (y2>yM) yM = y2;
36134  if (y3<ym) ym = y3;
36135  if (y3>yM) yM = y3;
36136  if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin) {
36137  const float d = (x1 - x0)*(y2 - y0) - (x2 - x0)*(y1 - y0);
36138  if (is_double_sided || d<0) {
36139  visibles(l) = (unsigned int)l;
36140  zrange(l) = (z0 + z1 + z2 + z3)/4;
36141  }
36142  }
36143  } break;
36144  default :
36145  throw CImgArgumentException(_cimg_instance
36146  "draw_object3d(): Invalid primitive[%u] with size %u "
36147  "(should have size 1,2,3,4,5,6,9 or 12).",
36148  cimg_instance,
36149  l,primitive.size());
36150  }
36151  }
36152 
36153  // Sort only visibles primitives.
36154  unsigned int *p_visibles = visibles._data;
36155  float *p_zrange = zrange._data;
36156  const float *ptrz = p_zrange;
36157  cimg_for(visibles,ptr,unsigned int) {
36158  if (*ptr!=~0U) { *(p_visibles++) = *ptr; *(p_zrange++) = *ptrz; }
36159  ++ptrz;
36160  }
36161  const unsigned int nb_visibles = p_zrange - zrange._data;
36162  if (!nb_visibles) return *this;
36163  CImg<uintT> permutations;
36164  CImg<tpfloat>(zrange._data,nb_visibles,1,1,1,true).sort(permutations,false);
36165 
36166  // Compute light properties
36167  CImg<floatT> lightprops;
36168  switch (render_type) {
36169  case 3 : { // Flat Shading
36170  lightprops.assign(nb_visibles);
36171 #ifdef cimg_use_openmp
36172 #pragma omp parallel for if (nb_visibles>4096)
36173 #endif
36174  cimg_forX(lightprops,l) {
36175  const CImg<tf>& primitive = primitives(visibles(permutations(l)));
36176  const unsigned int psize = primitive.size();
36177  if (psize==3 || psize==4 || psize==9 || psize==12) {
36178  const unsigned int
36179  i0 = (unsigned int)primitive(0),
36180  i1 = (unsigned int)primitive(1),
36181  i2 = (unsigned int)primitive(2);
36182  const tpfloat
36183  x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2),
36184  x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2),
36185  x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2),
36186  dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0,
36187  dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0,
36188  nx = dy1*dz2 - dz1*dy2,
36189  ny = dz1*dx2 - dx1*dz2,
36190  nz = dx1*dy2 - dy1*dx2,
36191  norm = (tpfloat)std::sqrt(1e-5f + nx*nx + ny*ny + nz*nz),
36192  lx = X + (x0 + x1 + x2)/3 - lightx,
36193  ly = Y + (y0 + y1 + y2)/3 - lighty,
36194  lz = Z + (z0 + z1 + z2)/3 - lightz,
36195  nl = (tpfloat)std::sqrt(1e-5f + lx*lx + ly*ly + lz*lz),
36196  factor = cimg::max(cimg::abs(-lx*nx-ly*ny-lz*nz)/(norm*nl),0);
36197  lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3);
36198  } else lightprops[l] = 1;
36199  }
36200  } break;
36201 
36202  case 4 : // Gouraud Shading
36203  case 5 : { // Phong-Shading
36204  CImg<tpfloat> vertices_normals(vertices._width,3,1,1,0);
36205 #ifdef cimg_use_openmp
36206 #pragma omp parallel for if (nb_visibles>4096)
36207 #endif
36208  for (unsigned int l = 0; l<nb_visibles; ++l) {
36209  const CImg<tf>& primitive = primitives[visibles(l)];
36210  const unsigned int psize = primitive.size();
36211  const bool
36212  triangle_flag = (psize==3) || (psize==9),
36213  rectangle_flag = (psize==4) || (psize==12);
36214  if (triangle_flag || rectangle_flag) {
36215  const unsigned int
36216  i0 = (unsigned int)primitive(0),
36217  i1 = (unsigned int)primitive(1),
36218  i2 = (unsigned int)primitive(2),
36219  i3 = rectangle_flag?(unsigned int)primitive(3):0;
36220  const tpfloat
36221  x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2),
36222  x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2),
36223  x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2),
36224  dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0,
36225  dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0,
36226  nnx = dy1*dz2 - dz1*dy2,
36227  nny = dz1*dx2 - dx1*dz2,
36228  nnz = dx1*dy2 - dy1*dx2,
36229  norm = (tpfloat)(1e-5f + std::sqrt(nnx*nnx + nny*nny + nnz*nnz)),
36230  nx = nnx/norm,
36231  ny = nny/norm,
36232  nz = nnz/norm;
36233  vertices_normals(i0,0)+=nx; vertices_normals(i0,1)+=ny; vertices_normals(i0,2)+=nz;
36234  vertices_normals(i1,0)+=nx; vertices_normals(i1,1)+=ny; vertices_normals(i1,2)+=nz;
36235  vertices_normals(i2,0)+=nx; vertices_normals(i2,1)+=ny; vertices_normals(i2,2)+=nz;
36236  if (rectangle_flag) { vertices_normals(i3,0)+=nx; vertices_normals(i3,1)+=ny; vertices_normals(i3,2)+=nz; }
36237  }
36238  }
36239 
36240  if (is_double_sided) cimg_forX(vertices_normals,p) if (vertices_normals(p,2)>0) {
36241  vertices_normals(p,0) = -vertices_normals(p,0);
36242  vertices_normals(p,1) = -vertices_normals(p,1);
36243  vertices_normals(p,2) = -vertices_normals(p,2);
36244  }
36245 
36246  if (render_type==4) {
36247  lightprops.assign(vertices._width);
36248 #ifdef cimg_use_openmp
36249 #pragma omp parallel for if (nb_visibles>4096)
36250 #endif
36251  cimg_forX(lightprops,l) {
36252  const tpfloat
36253  nx = vertices_normals(l,0),
36254  ny = vertices_normals(l,1),
36255  nz = vertices_normals(l,2),
36256  norm = (tpfloat)std::sqrt(1e-5f + nx*nx + ny*ny + nz*nz),
36257  lx = X + vertices(l,0) - lightx,
36258  ly = Y + vertices(l,1) - lighty,
36259  lz = Z + vertices(l,2) - lightz,
36260  nl = (tpfloat)std::sqrt(1e-5f + lx*lx + ly*ly + lz*lz),
36261  factor = cimg::max((-lx*nx-ly*ny-lz*nz)/(norm*nl),0);
36262  lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3);
36263  }
36264  } else {
36265  const unsigned int
36266  lw2 = light_texture._width/2 - 1,
36267  lh2 = light_texture._height/2 - 1;
36268  lightprops.assign(vertices._width,2);
36269 #ifdef cimg_use_openmp
36270 #pragma omp parallel for if (nb_visibles>4096)
36271 #endif
36272  cimg_forX(lightprops,l) {
36273  const tpfloat
36274  nx = vertices_normals(l,0),
36275  ny = vertices_normals(l,1),
36276  nz = vertices_normals(l,2),
36277  norm = (tpfloat)std::sqrt(1e-5f + nx*nx + ny*ny + nz*nz),
36278  nnx = nx/norm,
36279  nny = ny/norm;
36280  lightprops(l,0) = lw2*(1 + nnx);
36281  lightprops(l,1) = lh2*(1 + nny);
36282  }
36283  }
36284  } break;
36285  }
36286 
36287  // Draw visible primitives
36288  const CImg<tc> default_color(1,_spectrum,1,1,(tc)200);
36289  typedef typename to::value_type _to;
36290  CImg<_to> _opacity;
36291 
36292  for (unsigned int l = 0; l<nb_visibles; ++l) {
36293  const unsigned int n_primitive = visibles(permutations(l));
36294  const CImg<tf>& primitive = primitives[n_primitive];
36295  const CImg<tc>
36296  &__color = n_primitive<colors._width?colors[n_primitive]:CImg<tc>(),
36297  _color = (__color && __color.size()!=_spectrum && __color._spectrum<_spectrum)?
36298  __color.get_resize(-100,-100,-100,_spectrum,0):CImg<tc>(),
36299  &color = _color?_color:(__color?__color:default_color);
36300  const tc *const pcolor = color._data;
36301  const float opacity = __draw_object3d(opacities,n_primitive,_opacity);
36302 
36303 #ifdef cimg_use_board
36304  LibBoard::Board &board = *(LibBoard::Board*)pboard;
36305 #endif
36306 
36307  switch (primitive.size()) {
36308  case 1 : { // Colored point or sprite
36309  const unsigned int n0 = (unsigned int)primitive[0];
36310  const int x0 = (int)projections(n0,0), y0 = (int)projections(n0,1);
36311 
36312  if (_opacity.is_empty()) { // Scalar opacity.
36313 
36314  if (color.size()==_spectrum) { // Colored point.
36315  draw_point(x0,y0,pcolor,opacity);
36316 #ifdef cimg_use_board
36317  if (pboard) {
36318  board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
36319  board.fillCircle((float)x0,height()-(float)y0,0);
36320  }
36321 #endif
36322  } else { // Sprite.
36323  const tpfloat z = Z + vertices(n0,2);
36324  const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1);
36325  const unsigned int
36326  _sw = (unsigned int)(color._width*factor),
36327  _sh = (unsigned int)(color._height*factor),
36328  sw = _sw?_sw:1, sh = _sh?_sh:1;
36329  const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2;
36330  if (sw<=3*_width/2 && sh<=3*_height/2 &&
36331  (nx0+(int)sw/2>=0 || nx0-(int)sw/2<width() || ny0+(int)sh/2>=0 || ny0-(int)sh/2<height())) {
36332  const CImg<tc>
36333  _sprite = (sw!=color._width || sh!=color._height)?
36334  color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<tc>(),
36335  &sprite = _sprite?_sprite:color;
36336  draw_image(nx0,ny0,sprite,opacity);
36337 #ifdef cimg_use_board
36338  if (pboard) {
36339  board.setPenColorRGBi(128,128,128);
36340  board.setFillColor(LibBoard::Color::None);
36341  board.drawRectangle((float)nx0,height()-(float)ny0,sw,sh);
36342  }
36343 #endif
36344  }
36345  }
36346  } else { // Opacity mask.
36347  const tpfloat z = Z + vertices(n0,2);
36348  const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1);
36349  const unsigned int
36350  _sw = (unsigned int)(cimg::max(color._width,_opacity._width)*factor),
36351  _sh = (unsigned int)(cimg::max(color._height,_opacity._height)*factor),
36352  sw = _sw?_sw:1, sh = _sh?_sh:1;
36353  const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2;
36354  if (sw<=3*_width/2 && sh<=3*_height/2 &&
36355  (nx0+(int)sw/2>=0 || nx0-(int)sw/2<width() || ny0+(int)sh/2>=0 || ny0-(int)sh/2<height())) {
36356  const CImg<tc>
36357  _sprite = (sw!=color._width || sh!=color._height)?
36358  color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<tc>(),
36359  &sprite = _sprite?_sprite:color;
36360  const CImg<_to>
36361  _nopacity = (sw!=_opacity._width || sh!=_opacity._height)?
36362  _opacity.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<_to>(),
36363  &nopacity = _nopacity?_nopacity:_opacity;
36364  draw_image(nx0,ny0,sprite,nopacity);
36365 #ifdef cimg_use_board
36366  if (pboard) {
36367  board.setPenColorRGBi(128,128,128);
36368  board.setFillColor(LibBoard::Color::None);
36369  board.drawRectangle((float)nx0,height()-(float)ny0,sw,sh);
36370  }
36371 #endif
36372  }
36373  }
36374  } break;
36375  case 2 : { // Colored line
36376  const unsigned int
36377  n0 = (unsigned int)primitive[0],
36378  n1 = (unsigned int)primitive[1];
36379  const int
36380  x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
36381  x1 = (int)projections(n1,0), y1 = (int)projections(n1,1);
36382  const float
36383  z0 = vertices(n0,2) + Z + _focale,
36384  z1 = vertices(n1,2) + Z + _focale;
36385  if (render_type) {
36386  if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity);
36387  else draw_line(x0,y0,x1,y1,pcolor,opacity);
36388 #ifdef cimg_use_board
36389  if (pboard) {
36390  board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
36391  board.drawLine((float)x0,height()-(float)y0,x1,height()-(float)y1);
36392  }
36393 #endif
36394  } else {
36395  draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity);
36396 #ifdef cimg_use_board
36397  if (pboard) {
36398  board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
36399  board.drawCircle((float)x0,height()-(float)y0,0);
36400  board.drawCircle((float)x1,height()-(float)y1,0);
36401  }
36402 #endif
36403  }
36404  } break;
36405  case 5 : { // Colored sphere
36406  const unsigned int
36407  n0 = (unsigned int)primitive[0],
36408  n1 = (unsigned int)primitive[1],
36409  is_wireframe = (unsigned int)primitive[2];
36410  const float
36411  Xc = 0.5f*((float)vertices(n0,0) + (float)vertices(n1,0)),
36412  Yc = 0.5f*((float)vertices(n0,1) + (float)vertices(n1,1)),
36413  Zc = 0.5f*((float)vertices(n0,2) + (float)vertices(n1,2)),
36414  zc = Z + Zc + _focale,
36415  xc = X + Xc*(absfocale?absfocale/zc:1),
36416  yc = Y + Yc*(absfocale?absfocale/zc:1),
36417  radius = 0.5f*std::sqrt(cimg::sqr(vertices(n1,0) - vertices(n0,0)) +
36418  cimg::sqr(vertices(n1,1) - vertices(n0,1)) +
36419  cimg::sqr(vertices(n1,2) - vertices(n0,2)))*(absfocale?absfocale/zc:1);
36420  switch (render_type) {
36421  case 0 :
36422  draw_point((int)xc,(int)yc,pcolor,opacity);
36423 #ifdef cimg_use_board
36424  if (pboard) {
36425  board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
36426  board.fillCircle(xc,height()-yc,0);
36427  }
36428 #endif
36429  break;
36430  case 1 :
36431  draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U);
36432 #ifdef cimg_use_board
36433  if (pboard) {
36434  board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
36435  board.setFillColor(LibBoard::Color::None);
36436  board.drawCircle(xc,height()-yc,radius);
36437  }
36438 #endif
36439  break;
36440  default :
36441  if (is_wireframe) draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U);
36442  else draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity);
36443 #ifdef cimg_use_board
36444  if (pboard) {
36445  board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
36446  if (!is_wireframe) board.fillCircle(xc,height()-yc,radius);
36447  else {
36448  board.setFillColor(LibBoard::Color::None);
36449  board.drawCircle(xc,height()-yc,radius);
36450  }
36451  }
36452 #endif
36453  break;
36454  }
36455  } break;
36456  case 6 : { // Textured line
36457  if (!__color)
36458  throw CImgArgumentException(_cimg_instance
36459  "draw_object3d(): Undefined texture for line primitive [%u].",
36460  cimg_instance,n_primitive);
36461  const unsigned int
36462  n0 = (unsigned int)primitive[0],
36463  n1 = (unsigned int)primitive[1],
36464  tx0 = (unsigned int)primitive[2],
36465  ty0 = (unsigned int)primitive[3],
36466  tx1 = (unsigned int)primitive[4],
36467  ty1 = (unsigned int)primitive[5];
36468  const int
36469  x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
36470  x1 = (int)projections(n1,0), y1 = (int)projections(n1,1);
36471  const float
36472  z0 = vertices(n0,2) + Z + _focale,
36473  z1 = vertices(n1,2) + Z + _focale;
36474  if (render_type) {
36475  if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity);
36476  else draw_line(x0,y0,x1,y1,color,tx0,ty0,tx1,ty1,opacity);
36477 #ifdef cimg_use_board
36478  if (pboard) {
36479  board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
36480  board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1);
36481  }
36482 #endif
36483  } else {
36484  draw_point(x0,y0,color.get_vector_at(tx0,ty0)._data,opacity).
36485  draw_point(x1,y1,color.get_vector_at(tx1,ty1)._data,opacity);
36486 #ifdef cimg_use_board
36487  if (pboard) {
36488  board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
36489  board.drawCircle((float)x0,height()-(float)y0,0);
36490  board.drawCircle((float)x1,height()-(float)y1,0);
36491  }
36492 #endif
36493  }
36494  } break;
36495  case 3 : { // Colored triangle
36496  const unsigned int
36497  n0 = (unsigned int)primitive[0],
36498  n1 = (unsigned int)primitive[1],
36499  n2 = (unsigned int)primitive[2];
36500  const int
36501  x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
36502  x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
36503  x2 = (int)projections(n2,0), y2 = (int)projections(n2,1);
36504  const float
36505  z0 = vertices(n0,2) + Z + _focale,
36506  z1 = vertices(n1,2) + Z + _focale,
36507  z2 = vertices(n2,2) + Z + _focale;
36508  switch (render_type) {
36509  case 0 :
36510  draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity).draw_point(x2,y2,pcolor,opacity);
36511 #ifdef cimg_use_board
36512  if (pboard) {
36513  board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
36514  board.drawCircle((float)x0,height()-(float)y0,0);
36515  board.drawCircle((float)x1,height()-(float)y1,0);
36516  board.drawCircle((float)x2,height()-(float)y2,0);
36517  }
36518 #endif
36519  break;
36520  case 1 :
36521  if (zbuffer)
36522  draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x0,y0,z0,x2,y2,z2,pcolor,opacity).
36523  draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity);
36524  else
36525  draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x0,y0,x2,y2,pcolor,opacity).
36526  draw_line(x1,y1,x2,y2,pcolor,opacity);
36527 #ifdef cimg_use_board
36528  if (pboard) {
36529  board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
36530  board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1);
36531  board.drawLine((float)x0,height()-(float)y0,(float)x2,height()-(float)y2);
36532  board.drawLine((float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
36533  }
36534 #endif
36535  break;
36536  case 2 :
36537  if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity);
36538  else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity);
36539 #ifdef cimg_use_board
36540  if (pboard) {
36541  board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
36542  board.fillTriangle((float)x0,height()-(float)y0,
36543  (float)x1,height()-(float)y1,
36544  (float)x2,height()-(float)y2);
36545  }
36546 #endif
36547  break;
36548  case 3 :
36549  if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l));
36550  else _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,lightprops(l));
36551 #ifdef cimg_use_board
36552  if (pboard) {
36553  const float lp = cimg::min(lightprops(l),1);
36554  board.setPenColorRGBi((unsigned char)(color[0]*lp),
36555  (unsigned char)(color[1]*lp),
36556  (unsigned char)(color[2]*lp),
36557  (unsigned char)(opacity*255));
36558  board.fillTriangle((float)x0,height()-(float)y0,
36559  (float)x1,height()-(float)y1,
36560  (float)x2,height()-(float)y2);
36561  }
36562 #endif
36563  break;
36564  case 4 :
36565  if (zbuffer)
36566  draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,
36567  lightprops(n0),lightprops(n1),lightprops(n2),opacity);
36568  else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprops(n0),lightprops(n1),lightprops(n2),opacity);
36569 #ifdef cimg_use_board
36570  if (pboard) {
36571  board.setPenColorRGBi((unsigned char)(color[0]),
36572  (unsigned char)(color[1]),
36573  (unsigned char)(color[2]),
36574  (unsigned char)(opacity*255));
36575  board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprops(n0),
36576  (float)x1,height()-(float)y1,lightprops(n1),
36577  (float)x2,height()-(float)y2,lightprops(n2));
36578  }
36579 #endif
36580  break;
36581  case 5 : {
36582  const unsigned int
36583  lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1),
36584  lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1),
36585  lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1);
36586  if (zbuffer)
36587  draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
36588  else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
36589 #ifdef cimg_use_board
36590  if (pboard) {
36591  const float
36592  l0 = light_texture((int)(light_texture.width()/2*(1+lightprops(n0,0))),
36593  (int)(light_texture.height()/2*(1+lightprops(n0,1)))),
36594  l1 = light_texture((int)(light_texture.width()/2*(1+lightprops(n1,0))),
36595  (int)(light_texture.height()/2*(1+lightprops(n1,1)))),
36596  l2 = light_texture((int)(light_texture.width()/2*(1+lightprops(n2,0))),
36597  (int)(light_texture.height()/2*(1+lightprops(n2,1))));
36598  board.setPenColorRGBi((unsigned char)(color[0]),
36599  (unsigned char)(color[1]),
36600  (unsigned char)(color[2]),
36601  (unsigned char)(opacity*255));
36602  board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,
36603  (float)x1,height()-(float)y1,l1,
36604  (float)x2,height()-(float)y2,l2);
36605  }
36606 #endif
36607  } break;
36608  }
36609  } break;
36610  case 4 : { // Colored rectangle
36611  const unsigned int
36612  n0 = (unsigned int)primitive[0],
36613  n1 = (unsigned int)primitive[1],
36614  n2 = (unsigned int)primitive[2],
36615  n3 = (unsigned int)primitive[3];
36616  const int
36617  x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
36618  x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
36619  x2 = (int)projections(n2,0), y2 = (int)projections(n2,1),
36620  x3 = (int)projections(n3,0), y3 = (int)projections(n3,1);
36621  const float
36622  z0 = vertices(n0,2) + Z + _focale,
36623  z1 = vertices(n1,2) + Z + _focale,
36624  z2 = vertices(n2,2) + Z + _focale,
36625  z3 = vertices(n3,2) + Z + _focale;
36626 
36627  switch (render_type) {
36628  case 0 :
36629  draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity).
36630  draw_point(x2,y2,pcolor,opacity).draw_point(x3,y3,pcolor,opacity);
36631 #ifdef cimg_use_board
36632  if (pboard) {
36633  board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
36634  board.drawCircle((float)x0,height()-(float)y0,0);
36635  board.drawCircle((float)x1,height()-(float)y1,0);
36636  board.drawCircle((float)x2,height()-(float)y2,0);
36637  board.drawCircle((float)x3,height()-(float)y3,0);
36638  }
36639 #endif
36640  break;
36641  case 1 :
36642  if (zbuffer)
36643  draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity).
36644  draw_line(zbuffer,x2,y2,z2,x3,y3,z3,pcolor,opacity).draw_line(zbuffer,x3,y3,z3,x0,y0,z0,pcolor,opacity);
36645  else
36646  draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x1,y1,x2,y2,pcolor,opacity).
36647  draw_line(x2,y2,x3,y3,pcolor,opacity).draw_line(x3,y3,x0,y0,pcolor,opacity);
36648 #ifdef cimg_use_board
36649  if (pboard) {
36650  board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
36651  board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1);
36652  board.drawLine((float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
36653  board.drawLine((float)x2,height()-(float)y2,(float)x3,height()-(float)y3);
36654  board.drawLine((float)x3,height()-(float)y3,(float)x0,height()-(float)y0);
36655  }
36656 #endif
36657  break;
36658  case 2 :
36659  if (zbuffer)
36660  draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity).
36661  draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity);
36662  else
36663  draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity).draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity);
36664 #ifdef cimg_use_board
36665  if (pboard) {
36666  board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
36667  board.fillTriangle((float)x0,height()-(float)y0,
36668  (float)x1,height()-(float)y1,
36669  (float)x2,height()-(float)y2);
36670  board.fillTriangle((float)x0,height()-(float)y0,
36671  (float)x2,height()-(float)y2,
36672  (float)x3,height()-(float)y3);
36673  }
36674 #endif
36675  break;
36676  case 3 :
36677  if (zbuffer)
36678  draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l)).
36679  draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity,lightprops(l));
36680  else
36681  _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,lightprops(l)).
36682  _draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity,lightprops(l));
36683 #ifdef cimg_use_board
36684  if (pboard) {
36685  const float lp = cimg::min(lightprops(l),1);
36686  board.setPenColorRGBi((unsigned char)(color[0]*lp),
36687  (unsigned char)(color[1]*lp),
36688  (unsigned char)(color[2]*lp),(unsigned char)(opacity*255));
36689  board.fillTriangle((float)x0,height()-(float)y0,
36690  (float)x1,height()-(float)y1,
36691  (float)x2,height()-(float)y2);
36692  board.fillTriangle((float)x0,height()-(float)y0,
36693  (float)x2,height()-(float)y2,
36694  (float)x3,height()-(float)y3);
36695  }
36696 #endif
36697  break;
36698  case 4 : {
36699  const float
36700  lightprop0 = lightprops(n0), lightprop1 = lightprops(n1),
36701  lightprop2 = lightprops(n2), lightprop3 = lightprops(n3);
36702  if (zbuffer)
36703  draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,lightprop0,lightprop1,lightprop2,opacity).
36704  draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,lightprop0,lightprop2,lightprop3,opacity);
36705  else
36706  draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprop0,lightprop1,lightprop2,opacity).
36707  draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,lightprop0,lightprop2,lightprop3,opacity);
36708 #ifdef cimg_use_board
36709  if (pboard) {
36710  board.setPenColorRGBi((unsigned char)(color[0]),
36711  (unsigned char)(color[1]),
36712  (unsigned char)(color[2]),
36713  (unsigned char)(opacity*255));
36714  board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprop0,
36715  (float)x1,height()-(float)y1,lightprop1,
36716  (float)x2,height()-(float)y2,lightprop2);
36717  board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprop0,
36718  (float)x2,height()-(float)y2,lightprop2,
36719  (float)x3,height()-(float)y3,lightprop3);
36720  }
36721 #endif
36722  } break;
36723  case 5 : {
36724  const unsigned int
36725  lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1),
36726  lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1),
36727  lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1),
36728  lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1);
36729  if (zbuffer)
36730  draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity).
36731  draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity);
36732  else
36733  draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity).
36734  draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity);
36735 #ifdef cimg_use_board
36736  if (pboard) {
36737  const float
36738  l0 = light_texture((int)(light_texture.width()/2*(1+lx0)), (int)(light_texture.height()/2*(1+ly0))),
36739  l1 = light_texture((int)(light_texture.width()/2*(1+lx1)), (int)(light_texture.height()/2*(1+ly1))),
36740  l2 = light_texture((int)(light_texture.width()/2*(1+lx2)), (int)(light_texture.height()/2*(1+ly2))),
36741  l3 = light_texture((int)(light_texture.width()/2*(1+lx3)), (int)(light_texture.height()/2*(1+ly3)));
36742  board.setPenColorRGBi((unsigned char)(color[0]),
36743  (unsigned char)(color[1]),
36744  (unsigned char)(color[2]),
36745  (unsigned char)(opacity*255));
36746  board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,
36747  (float)x1,height()-(float)y1,l1,
36748  (float)x2,height()-(float)y2,l2);
36749  board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,
36750  (float)x2,height()-(float)y2,l2,
36751  (float)x3,height()-(float)y3,l3);
36752  }
36753 #endif
36754  } break;
36755  }
36756  } break;
36757  case 9 : { // Textured triangle
36758  if (!__color)
36759  throw CImgArgumentException(_cimg_instance
36760  "draw_object3d(): Undefined texture for triangle primitive [%u].",
36761  cimg_instance,n_primitive);
36762  const unsigned int
36763  n0 = (unsigned int)primitive[0],
36764  n1 = (unsigned int)primitive[1],
36765  n2 = (unsigned int)primitive[2],
36766  tx0 = (unsigned int)primitive[3],
36767  ty0 = (unsigned int)primitive[4],
36768  tx1 = (unsigned int)primitive[5],
36769  ty1 = (unsigned int)primitive[6],
36770  tx2 = (unsigned int)primitive[7],
36771  ty2 = (unsigned int)primitive[8];
36772  const int
36773  x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
36774  x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
36775  x2 = (int)projections(n2,0), y2 = (int)projections(n2,1);
36776  const float
36777  z0 = vertices(n0,2) + Z + _focale,
36778  z1 = vertices(n1,2) + Z + _focale,
36779  z2 = vertices(n2,2) + Z + _focale;
36780  switch (render_type) {
36781  case 0 :
36782  draw_point(x0,y0,color.get_vector_at(tx0,ty0)._data,opacity).
36783  draw_point(x1,y1,color.get_vector_at(tx1,ty1)._data,opacity).
36784  draw_point(x2,y2,color.get_vector_at(tx2,ty2)._data,opacity);
36785 #ifdef cimg_use_board
36786  if (pboard) {
36787  board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
36788  board.drawCircle((float)x0,height()-(float)y0,0);
36789  board.drawCircle((float)x1,height()-(float)y1,0);
36790  board.drawCircle((float)x2,height()-(float)y2,0);
36791  }
36792 #endif
36793  break;
36794  case 1 :
36795  if (zbuffer)
36796  draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
36797  draw_line(zbuffer,x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity).
36798  draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity);
36799  else
36800  draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
36801  draw_line(x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity).
36802  draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity);
36803 #ifdef cimg_use_board
36804  if (pboard) {
36805  board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
36806  board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1);
36807  board.drawLine((float)x0,height()-(float)y0,(float)x2,height()-(float)y2);
36808  board.drawLine((float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
36809  }
36810 #endif
36811  break;
36812  case 2 :
36813  if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity);
36814  else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity);
36815 #ifdef cimg_use_board
36816  if (pboard) {
36817  board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
36818  board.fillTriangle((float)x0,height()-(float)y0,
36819  (float)x1,height()-(float)y1,
36820  (float)x2,height()-(float)y2);
36821  }
36822 #endif
36823  break;
36824  case 3 :
36825  if (zbuffer)
36826  draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l));
36827  else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l));
36828 #ifdef cimg_use_board
36829  if (pboard) {
36830  const float lp = cimg::min(lightprops(l),1);
36831  board.setPenColorRGBi((unsigned char)(128*lp),
36832  (unsigned char)(128*lp),
36833  (unsigned char)(128*lp),
36834  (unsigned char)(opacity*255));
36835  board.fillTriangle((float)x0,height()-(float)y0,
36836  (float)x1,height()-(float)y1,
36837  (float)x2,height()-(float)y2);
36838  }
36839 #endif
36840  break;
36841  case 4 :
36842  if (zbuffer)
36843  draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
36844  lightprops(n0),lightprops(n1),lightprops(n2),opacity);
36845  else
36846  draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
36847  lightprops(n0),lightprops(n1),lightprops(n2),opacity);
36848 #ifdef cimg_use_board
36849  if (pboard) {
36850  board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
36851  board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprops(n0),
36852  (float)x1,height()-(float)y1,lightprops(n1),
36853  (float)x2,height()-(float)y2,lightprops(n2));
36854  }
36855 #endif
36856  break;
36857  case 5 :
36858  if (zbuffer)
36859  draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,
36860  (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1),
36861  (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1),
36862  (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1),
36863  opacity);
36864  else
36865  draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,
36866  (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1),
36867  (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1),
36868  (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1),
36869  opacity);
36870 #ifdef cimg_use_board
36871  if (pboard) {
36872  const float
36873  l0 = light_texture((int)(light_texture.width()/2*(1+lightprops(n0,0))),
36874  (int)(light_texture.height()/2*(1+lightprops(n0,1)))),
36875  l1 = light_texture((int)(light_texture.width()/2*(1+lightprops(n1,0))),
36876  (int)(light_texture.height()/2*(1+lightprops(n1,1)))),
36877  l2 = light_texture((int)(light_texture.width()/2*(1+lightprops(n2,0))),
36878  (int)(light_texture.height()/2*(1+lightprops(n2,1))));
36879  board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
36880  board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,
36881  (float)x1,height()-(float)y1,l1,
36882  (float)x2,height()-(float)y2,l2);
36883  }
36884 #endif
36885  break;
36886  }
36887  } break;
36888  case 12 : { // Textured quadrangle
36889  if (!__color)
36890  throw CImgArgumentException(_cimg_instance
36891  "draw_object3d(): Undefined texture for quadrangle primitive [%u].",
36892  cimg_instance,n_primitive);
36893  const unsigned int
36894  n0 = (unsigned int)primitive[0],
36895  n1 = (unsigned int)primitive[1],
36896  n2 = (unsigned int)primitive[2],
36897  n3 = (unsigned int)primitive[3],
36898  tx0 = (unsigned int)primitive[4],
36899  ty0 = (unsigned int)primitive[5],
36900  tx1 = (unsigned int)primitive[6],
36901  ty1 = (unsigned int)primitive[7],
36902  tx2 = (unsigned int)primitive[8],
36903  ty2 = (unsigned int)primitive[9],
36904  tx3 = (unsigned int)primitive[10],
36905  ty3 = (unsigned int)primitive[11];
36906  const int
36907  x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
36908  x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
36909  x2 = (int)projections(n2,0), y2 = (int)projections(n2,1),
36910  x3 = (int)projections(n3,0), y3 = (int)projections(n3,1);
36911  const float
36912  z0 = vertices(n0,2) + Z + _focale,
36913  z1 = vertices(n1,2) + Z + _focale,
36914  z2 = vertices(n2,2) + Z + _focale,
36915  z3 = vertices(n3,2) + Z + _focale;
36916 
36917  switch (render_type) {
36918  case 0 :
36919  draw_point(x0,y0,color.get_vector_at(tx0,ty0)._data,opacity).
36920  draw_point(x1,y1,color.get_vector_at(tx1,ty1)._data,opacity).
36921  draw_point(x2,y2,color.get_vector_at(tx2,ty2)._data,opacity).
36922  draw_point(x3,y3,color.get_vector_at(tx3,ty3)._data,opacity);
36923 #ifdef cimg_use_board
36924  if (pboard) {
36925  board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
36926  board.drawCircle((float)x0,height()-(float)y0,0);
36927  board.drawCircle((float)x1,height()-(float)y1,0);
36928  board.drawCircle((float)x2,height()-(float)y2,0);
36929  board.drawCircle((float)x3,height()-(float)y3,0);
36930  }
36931 #endif
36932  break;
36933  case 1 :
36934  if (zbuffer)
36935  draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
36936  draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity).
36937  draw_line(zbuffer,x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity).
36938  draw_line(zbuffer,x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity);
36939  else
36940  draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
36941  draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity).
36942  draw_line(x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity).
36943  draw_line(x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity);
36944 #ifdef cimg_use_board
36945  if (pboard) {
36946  board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
36947  board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1);
36948  board.drawLine((float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
36949  board.drawLine((float)x2,height()-(float)y2,(float)x3,height()-(float)y3);
36950  board.drawLine((float)x3,height()-(float)y3,(float)x0,height()-(float)y0);
36951  }
36952 #endif
36953  break;
36954  case 2 :
36955  if (zbuffer)
36956  draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity).
36957  draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity);
36958  else
36959  draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity).
36960  draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity);
36961 #ifdef cimg_use_board
36962  if (pboard) {
36963  board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
36964  board.fillTriangle((float)x0,height()-(float)y0,
36965  (float)x1,height()-(float)y1,
36966  (float)x2,height()-(float)y2);
36967  board.fillTriangle((float)x0,height()-(float)y0,
36968  (float)x2,height()-(float)y2,
36969  (float)x3,height()-(float)y3);
36970  }
36971 #endif
36972  break;
36973  case 3 :
36974  if (zbuffer)
36975  draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)).
36976  draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,lightprops(l));
36977  else
36978  draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)).
36979  draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,lightprops(l));
36980 #ifdef cimg_use_board
36981  if (pboard) {
36982  const float lp = cimg::min(lightprops(l),1);
36983  board.setPenColorRGBi((unsigned char)(128*lp),
36984  (unsigned char)(128*lp),
36985  (unsigned char)(128*lp),
36986  (unsigned char)(opacity*255));
36987  board.fillTriangle((float)x0,height()-(float)y0,
36988  (float)x1,height()-(float)y1,
36989  (float)x2,height()-(float)y2);
36990  board.fillTriangle((float)x0,height()-(float)y0,
36991  (float)x2,height()-(float)y2,
36992  (float)x3,height()-(float)y3);
36993  }
36994 #endif
36995  break;
36996  case 4 : {
36997  const float
36998  lightprop0 = lightprops(n0), lightprop1 = lightprops(n1),
36999  lightprop2 = lightprops(n2), lightprop3 = lightprops(n3);
37000  if (zbuffer)
37001  draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
37002  lightprop0,lightprop1,lightprop2,opacity).
37003  draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
37004  lightprop0,lightprop2,lightprop3,opacity);
37005  else
37006  draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
37007  lightprop0,lightprop1,lightprop2,opacity).
37008  draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
37009  lightprop0,lightprop2,lightprop3,opacity);
37010 #ifdef cimg_use_board
37011  if (pboard) {
37012  board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
37013  board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprop0,
37014  (float)x1,height()-(float)y1,lightprop1,
37015  (float)x2,height()-(float)y2,lightprop2);
37016  board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprop0,
37017  (float)x2,height()-(float)y2,lightprop2,
37018  (float)x3,height()-(float)y3,lightprop3);
37019  }
37020 #endif
37021  } break;
37022  case 5 : {
37023  const unsigned int
37024  lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1),
37025  lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1),
37026  lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1),
37027  lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1);
37028  if (zbuffer)
37029  draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
37030  light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity).
37031  draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
37032  light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity);
37033  else
37034  draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
37035  light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity).
37036  draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
37037  light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity);
37038 #ifdef cimg_use_board
37039  if (pboard) {
37040  const float
37041  l0 = light_texture((int)(light_texture.width()/2*(1+lx0)), (int)(light_texture.height()/2*(1+ly0))),
37042  l1 = light_texture((int)(light_texture.width()/2*(1+lx1)), (int)(light_texture.height()/2*(1+ly1))),
37043  l2 = light_texture((int)(light_texture.width()/2*(1+lx2)), (int)(light_texture.height()/2*(1+ly2))),
37044  l3 = light_texture((int)(light_texture.width()/2*(1+lx3)), (int)(light_texture.height()/2*(1+ly3)));
37045  board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
37046  board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,
37047  (float)x1,height()-(float)y1,l1,
37048  (float)x2,height()-(float)y2,l2);
37049  board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,
37050  (float)x2,height()-(float)y2,l2,
37051  (float)x3,height()-(float)y3,l3);
37052  }
37053 #endif
37054  } break;
37055  }
37056  } break;
37057  }
37058  }
37059 
37060  if (render_type==5) cimg::mutex(10,0);
37061  return *this;
37062  }
37063 
37065  //---------------------------
37066  //
37068 
37069  //---------------------------
37070 
37072 
37078  const unsigned int feature_type=2, unsigned int *const XYZ=0) {
37079  return get_select(disp,feature_type,XYZ).move_to(*this);
37080  }
37081 
37083  CImg<T>& select(const char *const title,
37084  const unsigned int feature_type=2, unsigned int *const XYZ=0) {
37085  return get_select(title,feature_type,XYZ).move_to(*this);
37086  }
37087 
37090  const unsigned int feature_type=2, unsigned int *const XYZ=0) const {
37091  return _get_select(disp,0,feature_type,XYZ,0,0,0,true,false);
37092  }
37093 
37095  CImg<intT> get_select(const char *const title,
37096  const unsigned int feature_type=2, unsigned int *const XYZ=0) const {
37097  CImgDisplay disp;
37098  return _get_select(disp,title,feature_type,XYZ,0,0,0,true,false);
37099  }
37100 
37101  CImg<intT> _get_select(CImgDisplay &disp, const char *const title,
37102  const unsigned int feature_type, unsigned int *const XYZ,
37103  const int origX, const int origY, const int origZ,
37104  const bool reset_view3d,
37105  const bool force_display_z_coord) const {
37106  if (is_empty()) return CImg<intT>(1,feature_type==0?3:6,1,1,-1);
37107  if (!disp) {
37108  disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1);
37109  if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum);
37110  } else if (title) disp.set_title("%s",title);
37111 
37112  const unsigned int old_normalization = disp.normalization();
37113  bool old_is_resized = disp.is_resized();
37114  disp._normalization = 0;
37115  disp.show().set_key(0).set_wheel().show_mouse();
37116 
37117  unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 };
37118 
37119  int area = 0, starting_area = 0, clicked_area = 0, phase = 0,
37120  X0 = (int)((XYZ?XYZ[0]:(_width-1)/2)%_width),
37121  Y0 = (int)((XYZ?XYZ[1]:(_height-1)/2)%_height),
37122  Z0 = (int)((XYZ?XYZ[2]:(_depth-1)/2)%_depth),
37123  X1 =-1, Y1 = -1, Z1 = -1,
37124  X3d = -1, Y3d = -1,
37125  oX3d = X3d, oY3d = -1,
37126  omx = -1, omy = -1;
37127  float X = -1, Y = -1, Z = -1;
37128  unsigned int old_button = 0, key = 0;
37129 
37130  bool shape_selected = false, text_down = false, visible_cursor = true;
37131  static CImg<floatT> pose3d;
37132  static bool is_view3d = false, is_axes = true;
37133  if (reset_view3d) { pose3d.assign(); is_view3d = false; }
37134  CImg<floatT> points3d, opacities3d, sel_opacities3d;
37135  CImgList<uintT> primitives3d, sel_primitives3d;
37136  CImgList<ucharT> colors3d, sel_colors3d;
37137  CImg<ucharT> visu, visu0, view3d;
37138  char text[1024] = { 0 };
37139 
37140  while (!key && !disp.is_closed() && !shape_selected) {
37141 
37142  // Handle mouse motion and selection
37143  int
37144  mx = disp.mouse_x(),
37145  my = disp.mouse_y();
37146 
37147  const float
37148  mX = mx<0?-1.0f:(float)mx*(width()+(depth()>1?depth():0))/disp.width(),
37149  mY = my<0?-1.0f:(float)my*(height()+(depth()>1?depth():0))/disp.height();
37150 
37151  area = 0;
37152  if (mX>=0 && mY>=0 && mX<width() && mY<height()) { area = 1; X = mX; Y = mY; Z = (float)(phase?Z1:Z0); }
37153  if (mX>=0 && mX<width() && mY>=height()) { area = 2; X = mX; Z = mY - _height; Y = (float)(phase?Y1:Y0); }
37154  if (mY>=0 && mX>=width() && mY<height()) { area = 3; Y = mY; Z = mX - _width; X = (float)(phase?X1:X0); }
37155  if (mX>=width() && mY>=height()) area = 4;
37156  if (disp.button()) { if (!clicked_area) clicked_area = area; } else clicked_area = 0;
37157 
37158  switch (key = disp.key()) {
37159 #if cimg_OS!=2
37160  case cimg::keyCTRLRIGHT :
37161 #endif
37162  case 0 : case cimg::keyCTRLLEFT : key = 0; break;
37163  case cimg::keyPAGEUP :
37164  if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(1); key = 0; } break;
37165  case cimg::keyPAGEDOWN :
37166  if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(-1); key = 0; } break;
37167  case cimg::keyA : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
37168  is_axes = !is_axes; disp.set_key(key,false); key = 0; visu0.assign();
37169  } break;
37170  case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
37171  disp.set_fullscreen(false).
37172  resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
37173  CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
37174  _is_resized = true;
37175  disp.set_key(key,false); key = 0; visu0.assign();
37176  } break;
37177  case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
37178  disp.set_fullscreen(false).
37179  resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
37180  disp.set_key(key,false); key = 0; visu0.assign();
37181  } break;
37182  case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
37183  disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true;
37184  disp.set_key(key,false); key = 0; visu0.assign();
37185  } break;
37186  case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
37187  disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
37188  disp.set_key(key,false); key = 0; visu0.assign();
37189  } break;
37190  case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
37191  is_view3d = !is_view3d; disp.set_key(key,false); key = 0; visu0.assign();
37192  } break;
37193  case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
37194  static unsigned int snap_number = 0;
37195  char filename[32] = { 0 };
37196  std::FILE *file;
37197  do {
37198  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++);
37199  if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
37200  } while (file);
37201  if (visu0) {
37202  (+visu0).draw_text(0,0," Saving snapshot... ",foreground_color,background_color,0.7f,13).display(disp);
37203  visu0.save(filename);
37204  (+visu0).draw_text(0,0," Snapshot '%s' saved. ",foreground_color,background_color,0.7f,13,filename).
37205  display(disp);
37206  }
37207  disp.set_key(key,false); key = 0;
37208  } break;
37209  case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
37210  static unsigned int snap_number = 0;
37211  char filename[32] = { 0 };
37212  std::FILE *file;
37213  do {
37214 #ifdef cimg_use_zlib
37215  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++);
37216 #else
37217  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++);
37218 #endif
37219  if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
37220  } while (file);
37221  (+visu0).draw_text(0,0," Saving instance... ",foreground_color,background_color,0.7f,13).display(disp);
37222  save(filename);
37223  (+visu0).draw_text(0,0," Instance '%s' saved. ",foreground_color,background_color,0.7f,13,filename).
37224  display(disp);
37225  disp.set_key(key,false); key = 0;
37226  } break;
37227  }
37228 
37229  switch (area) {
37230 
37231  case 0 : // When mouse is out of image range.
37232  mx = my = -1; X = Y = Z = -1;
37233  break;
37234 
37235  case 1 : case 2 : case 3 : // When mouse is over the XY,XZ or YZ projections.
37236  if (disp.button()&1 && phase<2 && clicked_area==area) { // When selection has been started (1st step).
37237  if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign();
37238  X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z;
37239  }
37240  if (!(disp.button()&1) && phase>=2 && clicked_area!=area) { // When selection is at 2nd step (for volumes).
37241  switch (starting_area) {
37242  case 1 : if (Z1!=(int)Z) visu0.assign(); Z1 = (int)Z; break;
37243  case 2 : if (Y1!=(int)Y) visu0.assign(); Y1 = (int)Y; break;
37244  case 3 : if (X1!=(int)X) visu0.assign(); X1 = (int)X; break;
37245  }
37246  }
37247  if (disp.button()&2 && clicked_area==area) { // When moving through the image/volume.
37248  if (phase) {
37249  if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign();
37250  X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z;
37251  } else {
37252  if (_depth>1 && (X0!=(int)X || Y0!=(int)Y || Z0!=(int)Z)) visu0.assign();
37253  X0 = (int)X; Y0 = (int)Y; Z0 = (int)Z;
37254  }
37255  }
37256  if (disp.button()&4) { // Reset positions.
37257  X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = area = clicked_area = starting_area = 0;
37258  visu0.assign();
37259  }
37260  if (disp.wheel()) { // When moving through the slices of the volume (with mouse wheel).
37261  if (_depth>1 && !disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT() &&
37262  !disp.is_keySHIFTLEFT() && !disp.is_keySHIFTRIGHT() &&
37263  !disp.is_keyALT() && !disp.is_keyALTGR()) {
37264  switch (area) {
37265  case 1 :
37266  if (phase) Z = (float)(Z1+=disp.wheel()); else Z = (float)(Z0+=disp.wheel());
37267  visu0.assign(); break;
37268  case 2 :
37269  if (phase) Y = (float)(Y1+=disp.wheel()); else Y = (float)(Y0+=disp.wheel());
37270  visu0.assign(); break;
37271  case 3 :
37272  if (phase) X = (float)(X1+=disp.wheel()); else X = (float)(X0+=disp.wheel());
37273  visu0.assign(); break;
37274  }
37275  disp.set_wheel();
37276  } else key = ~0U;
37277  }
37278  if ((disp.button()&1)!=old_button) { // When left button has just been pressed or released.
37279  switch (phase) {
37280  case 0 :
37281  if (area==clicked_area) {
37282  X0 = X1 = (int)X; Y0 = Y1 = (int)Y; Z0 = Z1 = (int)Z; starting_area = area; ++phase;
37283  } break;
37284  case 1 :
37285  if (area==starting_area) {
37286  X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; ++phase;
37287  } else if (!(disp.button()&1)) { X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = 0; visu0.assign(); }
37288  break;
37289  case 2 : ++phase; break;
37290  }
37291  old_button = disp.button()&1;
37292  }
37293  break;
37294 
37295  case 4 : // When mouse is over the 3d view.
37296  if (is_view3d && points3d) {
37297  X3d = mx - _width*disp.width()/(_width+(_depth>1?_depth:0));
37298  Y3d = my - _height*disp.height()/(_height+(_depth>1?_depth:0));
37299  if (oX3d<0) { oX3d = X3d; oY3d = Y3d; }
37300  // Left + right buttons: reset.
37301  if ((disp.button()&3)==3) { pose3d.assign(); view3d.assign(); oX3d = oY3d = X3d = Y3d = -1; }
37302  else if (disp.button()&1 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Left button: rotate.
37303  const float
37304  R = 0.45f*cimg::min(view3d._width,view3d._height),
37305  R2 = R*R,
37306  u0 = (float)(oX3d-view3d.width()/2),
37307  v0 = (float)(oY3d-view3d.height()/2),
37308  u1 = (float)(X3d-view3d.width()/2),
37309  v1 = (float)(Y3d-view3d.height()/2),
37310  n0 = (float)std::sqrt(u0*u0+v0*v0),
37311  n1 = (float)std::sqrt(u1*u1+v1*v1),
37312  nu0 = n0>R?(u0*R/n0):u0,
37313  nv0 = n0>R?(v0*R/n0):v0,
37314  nw0 = (float)std::sqrt(cimg::max(0,R2-nu0*nu0-nv0*nv0)),
37315  nu1 = n1>R?(u1*R/n1):u1,
37316  nv1 = n1>R?(v1*R/n1):v1,
37317  nw1 = (float)std::sqrt(cimg::max(0,R2-nu1*nu1-nv1*nv1)),
37318  u = nv0*nw1 - nw0*nv1,
37319  v = nw0*nu1 - nu0*nw1,
37320  w = nv0*nu1 - nu0*nv1,
37321  n = (float)std::sqrt(u*u+v*v+w*w),
37322  alpha = (float)std::asin(n/R2);
37323  pose3d.draw_image(CImg<floatT>::rotation_matrix(u,v,w,alpha)*pose3d.get_crop(0,0,2,2));
37324  view3d.assign();
37325  } else if (disp.button()&2 && pose3d && oY3d!=Y3d) { // Right button: zoom.
37326  pose3d(3,2)-=(oY3d - Y3d)*1.5f; view3d.assign();
37327  }
37328  if (disp.wheel()) { // Wheel: zoom
37329  pose3d(3,2)-=disp.wheel()*15; view3d.assign(); disp.set_wheel();
37330  }
37331  if (disp.button()&4 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Middle button: shift.
37332  pose3d(3,0)-=oX3d - X3d; pose3d(3,1)-=oY3d - Y3d; view3d.assign();
37333  }
37334  oX3d = X3d; oY3d = Y3d;
37335  }
37336  mx = my = -1; X = Y = Z = -1;
37337  break;
37338  }
37339 
37340  if (phase) {
37341  if (!feature_type) shape_selected = phase?true:false;
37342  else {
37343  if (_depth>1) shape_selected = (phase==3)?true:false;
37344  else shape_selected = (phase==2)?true:false;
37345  }
37346  }
37347 
37348  if (X0<0) X0 = 0; if (X0>=width()) X0 = width() - 1;
37349  if (Y0<0) Y0 = 0; if (Y0>=height()) Y0 = height() - 1;
37350  if (Z0<0) Z0 = 0; if (Z0>=depth()) Z0 = depth() - 1;
37351  if (X1<1) X1 = 0; if (X1>=width()) X1 = width() - 1;
37352  if (Y1<0) Y1 = 0; if (Y1>=height()) Y1 = height() - 1;
37353  if (Z1<0) Z1 = 0; if (Z1>=depth()) Z1 = depth() - 1;
37354 
37355  // Draw visualization image on the display
37356  if (mx!=omx || my!=omy || !visu0 || (_depth>1 && !view3d)) {
37357 
37358  if (!visu0) { // Create image of projected planes.
37359  __get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0).resize(disp);
37360  view3d.assign();
37361  points3d.assign();
37362  }
37363 
37364  if (is_view3d && _depth>1 && !view3d) { // Create 3d view for volumetric images.
37365  const unsigned int
37366  _x3d = (unsigned int)cimg::round((float)_width*visu0._width/(_width+_depth),1,1),
37367  _y3d = (unsigned int)cimg::round((float)_height*visu0._height/(_height+_depth),1,1),
37368  x3d = _x3d>=visu0._width?visu0._width-1:_x3d,
37369  y3d = _y3d>=visu0._height?visu0._height-1:_y3d;
37370  CImg<ucharT>(1,2,1,1,64,128).resize(visu0._width-x3d,visu0._height-y3d,1,visu0._spectrum,3).move_to(view3d);
37371  if (!points3d) {
37372  get_projections3d(primitives3d,colors3d,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0,true).move_to(points3d);
37373  points3d.append(CImg<floatT>(8,3,1,1,
37374  0,_width-1,_width-1,0,0,_width-1,_width-1,0,
37375  0,0,_height-1,_height-1,0,0,_height-1,_height-1,
37376  0,0,0,0,_depth-1,_depth-1,_depth-1,_depth-1),'x');
37377  CImg<uintT>::vector(12,13).move_to(primitives3d); CImg<uintT>::vector(13,14).move_to(primitives3d);
37378  CImg<uintT>::vector(14,15).move_to(primitives3d); CImg<uintT>::vector(15,12).move_to(primitives3d);
37379  CImg<uintT>::vector(16,17).move_to(primitives3d); CImg<uintT>::vector(17,18).move_to(primitives3d);
37380  CImg<uintT>::vector(18,19).move_to(primitives3d); CImg<uintT>::vector(19,16).move_to(primitives3d);
37381  CImg<uintT>::vector(12,16).move_to(primitives3d); CImg<uintT>::vector(13,17).move_to(primitives3d);
37382  CImg<uintT>::vector(14,18).move_to(primitives3d); CImg<uintT>::vector(15,19).move_to(primitives3d);
37383  colors3d.insert(12,CImg<ucharT>::vector(255,255,255));
37384  opacities3d.assign(primitives3d.width(),1,1,1,0.5f);
37385  if (!phase) {
37386  opacities3d[0] = opacities3d[1] = opacities3d[2] = 0.8f;
37387  sel_primitives3d.assign();
37388  sel_colors3d.assign();
37389  sel_opacities3d.assign();
37390  } else {
37391  if (feature_type==2) {
37392  points3d.append(CImg<floatT>(8,3,1,1,
37393  X0,X1,X1,X0,X0,X1,X1,X0,
37394  Y0,Y0,Y1,Y1,Y0,Y0,Y1,Y1,
37395  Z0,Z0,Z0,Z0,Z1,Z1,Z1,Z1),'x');
37396  sel_primitives3d.assign();
37397  CImg<uintT>::vector(20,21).move_to(sel_primitives3d);
37398  CImg<uintT>::vector(21,22).move_to(sel_primitives3d);
37399  CImg<uintT>::vector(22,23).move_to(sel_primitives3d);
37400  CImg<uintT>::vector(23,20).move_to(sel_primitives3d);
37401  CImg<uintT>::vector(24,25).move_to(sel_primitives3d);
37402  CImg<uintT>::vector(25,26).move_to(sel_primitives3d);
37403  CImg<uintT>::vector(26,27).move_to(sel_primitives3d);
37404  CImg<uintT>::vector(27,24).move_to(sel_primitives3d);
37405  CImg<uintT>::vector(20,24).move_to(sel_primitives3d);
37406  CImg<uintT>::vector(21,25).move_to(sel_primitives3d);
37407  CImg<uintT>::vector(22,26).move_to(sel_primitives3d);
37408  CImg<uintT>::vector(23,27).move_to(sel_primitives3d);
37409  } else {
37410  points3d.append(CImg<floatT>(2,3,1,1,
37411  X0,X1,
37412  Y0,Y1,
37413  Z0,Z1),'x');
37414  sel_primitives3d.assign(CImg<uintT>::vector(20,21));
37415  }
37416  sel_colors3d.assign(sel_primitives3d._width,CImg<ucharT>::vector(255,255,255));
37417  sel_opacities3d.assign(sel_primitives3d._width,1,1,1,0.8f);
37418  }
37419  points3d.shift_object3d(-0.5f*(_width-1),-0.5f*(_height-1),-0.5f*(_depth-1)).resize_object3d();
37420  points3d*=0.75f*cimg::min(view3d._width,view3d._height);
37421  }
37422 
37423  if (!pose3d) CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose3d);
37424  CImg<floatT> zbuffer3d(view3d._width,view3d._height,1,1,0);
37425  const CImg<floatT> rotated_points3d = pose3d.get_crop(0,0,2,2)*points3d;
37426  if (sel_primitives3d)
37427  view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width,
37428  pose3d(3,1) + 0.5f*view3d._height,
37429  pose3d(3,2),
37430  rotated_points3d,sel_primitives3d,sel_colors3d,sel_opacities3d,
37431  2,true,500,0,0,0,0,0,zbuffer3d);
37432  view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width,
37433  pose3d(3,1) + 0.5f*view3d._height,
37434  pose3d(3,2),
37435  rotated_points3d,primitives3d,colors3d,opacities3d,
37436  2,true,500,0,0,0,0,0,zbuffer3d);
37437  visu0.draw_image(x3d,y3d,view3d);
37438  }
37439  visu = visu0;
37440 
37441  if (X<0 || Y<0 || Z<0) { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }}
37442  else {
37443  if (is_axes) { if (visible_cursor) { disp.hide_mouse(); visible_cursor = false; }}
37444  else { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }}
37445  const int d = (_depth>1)?_depth:0;
37446  int
37447  w = disp.width(), W = width() + d,
37448  h = disp.height(), H = height() + d,
37449  _xp = (int)X*w/W, xp = _xp + (_xp*W/w!=(int)X?1:0),
37450  _yp = (int)Y*h/H, yp = _yp + (_yp*H/h!=(int)Y?1:0),
37451  _xn = (int)(X+1)*w/W-1, xn = _xn + ((_xn+1)*W/w!=(int)X+1?1:0),
37452  _yn = (int)(Y+1)*h/H-1, yn = _yn + ((_yn+1)*H/h!=(int)Y+1?1:0),
37453  _zxp = ((int)Z+width())*w/W, zxp = _zxp + (_zxp*W/w!=(int)Z+width()?1:0),
37454  _zyp = ((int)Z+height())*h/H, zyp = _zyp + (_zyp*H/h!=(int)Z+height()?1:0),
37455  _zxn = ((int)Z+width()+1)*w/W-1, zxn = _zxn + ((_zxn+1)*W/w!=(int)Z+width()+1?1:0),
37456  _zyn = ((int)Z+height()+1)*h/H-1, zyn = _zyn + ((_zyn+1)*H/h!=(int)Z+height()+1?1:0),
37457  _xM = width()*w/W-1, xM = _xM + ((_xM+1)*W/w!=width()?1:0),
37458  _yM = height()*h/H-1, yM = _yM + ((_yM+1)*H/h!=height()?1:0),
37459  xc = (xp + xn)/2,
37460  yc = (yp + yn)/2,
37461  zxc = (zxp + zxn)/2,
37462  zyc = (zyp + zyn)/2,
37463  xf = (int)(X*w/W),
37464  yf = (int)(Y*h/H),
37465  zxf = (int)((Z+width())*w/W),
37466  zyf = (int)((Z+height())*h/H);
37467 
37468  if (is_axes) { // Draw axes.
37469  visu.draw_line(0,yf,visu.width()-1,yf,foreground_color,0.7f,0xFF00FF00).
37470  draw_line(0,yf,visu.width()-1,yf,background_color,0.7f,0x00FF00FF).
37471  draw_line(xf,0,xf,visu.height()-1,foreground_color,0.7f,0xFF00FF00).
37472  draw_line(xf,0,xf,visu.height()-1,background_color,0.7f,0x00FF00FF);
37473  if (_depth>1)
37474  visu.draw_line(zxf,0,zxf,yM,foreground_color,0.7f,0xFF00FF00).
37475  draw_line(zxf,0,zxf,yM,background_color,0.7f,0x00FF00FF).
37476  draw_line(0,zyf,xM,zyf,foreground_color,0.7f,0xFF00FF00).
37477  draw_line(0,zyf,xM,zyf,background_color,0.7f,0x00FF00FF);
37478  }
37479 
37480  // Draw box cursor.
37481  if (xn-xp>=4 && yn-yp>=4) visu.draw_rectangle(xp,yp,xn,yn,foreground_color,0.2f).
37482  draw_rectangle(xp,yp,xn,yn,foreground_color,1,0xAAAAAAAA).
37483  draw_rectangle(xp,yp,xn,yn,background_color,1,0x55555555);
37484  if (_depth>1) {
37485  if (yn-yp>=4 && zxn-zxp>=4) visu.draw_rectangle(zxp,yp,zxn,yn,background_color,0.2f).
37486  draw_rectangle(zxp,yp,zxn,yn,foreground_color,1,0xAAAAAAAA).
37487  draw_rectangle(zxp,yp,zxn,yn,background_color,1,0x55555555);
37488  if (xn-xp>=4 && zyn-zyp>=4) visu.draw_rectangle(xp,zyp,xn,zyn,background_color,0.2f).
37489  draw_rectangle(xp,zyp,xn,zyn,foreground_color,1,0xAAAAAAAA).
37490  draw_rectangle(xp,zyp,xn,zyn,background_color,1,0x55555555);
37491  }
37492 
37493  // Draw selection.
37494  if (phase) {
37495  const int
37496  _xp0 = X0*w/W, xp0 = _xp0 + (_xp0*W/w!=X0?1:0),
37497  _yp0 = Y0*h/H, yp0 = _yp0 + (_yp0*H/h!=Y0?1:0),
37498  _xn0 = (X0+1)*w/W-1, xn0 = _xn0 + ((_xn0+1)*W/w!=X0+1?1:0),
37499  _yn0 = (Y0+1)*h/H-1, yn0 = _yn0 + ((_yn0+1)*H/h!=Y0+1?1:0),
37500  _zxp0 = (Z0+width())*w/W, zxp0 = _zxp0 + (_zxp0*W/w!=Z0+width()?1:0),
37501  _zyp0 = (Z0+height())*h/H, zyp0 = _zyp0 + (_zyp0*H/h!=Z0+height()?1:0),
37502  _zxn0 = (Z0+width()+1)*w/W-1, zxn0 = _zxn0 + ((_zxn0+1)*W/w!=Z0+width()+1?1:0),
37503  _zyn0 = (Z0+height()+1)*h/H-1, zyn0 = _zyn0 + ((_zyn0+1)*H/h!=Z0+height()+1?1:0),
37504  xc0 = (xp0 + xn0)/2,
37505  yc0 = (yp0 + yn0)/2,
37506  zxc0 = (zxp0 + zxn0)/2,
37507  zyc0 = (zyp0 + zyn0)/2;
37508 
37509  switch (feature_type) {
37510  case 1 : {
37511  visu.draw_arrow(xc0,yc0,xc,yc,background_color,0.9f,30,5,0x55555555).
37512  draw_arrow(xc0,yc0,xc,yc,foreground_color,0.9f,30,5,0xAAAAAAAA);
37513  if (d) {
37514  visu.draw_arrow(zxc0,yc0,zxc,yc,background_color,0.9f,30,5,0x55555555).
37515  draw_arrow(zxc0,yc0,zxc,yc,foreground_color,0.9f,30,5,0xAAAAAAAA).
37516  draw_arrow(xc0,zyc0,xc,zyc,background_color,0.9f,30,5,0x55555555).
37517  draw_arrow(xc0,zyc0,xc,zyc,foreground_color,0.9f,30,5,0xAAAAAAAA);
37518  }
37519  } break;
37520  case 2 : {
37521  visu.draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,background_color,0.2f).
37522  draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,foreground_color,0.9f,0xAAAAAAAA).
37523  draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,background_color,0.9f,0x55555555);
37524  if (d) {
37525  visu.draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,background_color,0.2f).
37526  draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,
37527  foreground_color,0.9f,0xAAAAAAAA).
37528  draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,
37529  background_color,0.9f,0x55555555).
37530  draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0,
37531  background_color,0.2f).
37532  draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0,
37533  foreground_color,0.9f,0xAAAAAAAA).
37534  draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0,
37535  background_color,0.9f,0x55555555);
37536  }
37537  } break;
37538  case 3 : {
37539  visu.draw_ellipse(xc0,yc0,(float)cimg::abs(xc-xc0),(float)cimg::abs(yc-yc0),0,background_color,0.2f).
37540  draw_ellipse(xc0,yc0,(float)cimg::abs(xc-xc0),(float)cimg::abs(yc-yc0),0,foreground_color,0.9f,~0U).
37541  draw_point(xc0,yc0,foreground_color,0.9f);
37542  if (d) {
37543  visu.draw_ellipse(zxc0,yc0,(float)cimg::abs(zxc-zxc0),(float)cimg::abs(yc-yc0),0,
37544  background_color,0.2f).
37545  draw_ellipse(zxc0,yc0,(float)cimg::abs(zxc-zxc0),(float)cimg::abs(yc-yc0),0,
37546  foreground_color,0.9f,~0U).
37547  draw_point(zxc0,yc0,foreground_color,0.9f).
37548  draw_ellipse(xc0,zyc0,(float)cimg::abs(xc-xc0),(float)cimg::abs(zyc-zyc0),0,
37549  background_color,0.2f).
37550  draw_ellipse(xc0,zyc0,(float)cimg::abs(xc-xc0),(float)cimg::abs(zyc-zyc0),0,
37551  foreground_color,0.9f,~0U).
37552  draw_point(xc0,zyc0,foreground_color,0.9f);
37553  }
37554  } break;
37555  }
37556  }
37557 
37558  // Draw text info.
37559  if (my>=0 && my<13) text_down = true; else if (my>=visu.height()-13) text_down = false;
37560  if (!feature_type || !phase) {
37561  if (X>=0 && Y>=0 && Z>=0 && X<width() && Y<height() && Z<depth()) {
37562  if (_depth>1 || force_display_z_coord)
37563  cimg_snprintf(text,sizeof(text)," Point (%d,%d,%d) = [ ",origX+(int)X,origY+(int)Y,origZ+(int)Z);
37564  else cimg_snprintf(text,sizeof(text)," Point (%d,%d) = [ ",origX+(int)X,origY+(int)Y);
37565  char *ctext = text + std::strlen(text), *const ltext = text + 512;
37566  for (unsigned int c = 0; c<_spectrum && ctext<ltext; ++c) {
37567  cimg_snprintf(ctext,sizeof(text)/2,cimg::type<T>::format(),
37568  cimg::type<T>::format((*this)((int)X,(int)Y,(int)Z,c)));
37569  ctext = text + std::strlen(text);
37570  *(ctext++) = ' '; *ctext = 0;
37571  }
37572  std::strcpy(text + std::strlen(text),"] ");
37573  }
37574  } else switch (feature_type) {
37575  case 1 : {
37576  const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1),
37577  norm = std::sqrt(dX*dX+dY*dY+dZ*dZ);
37578  if (_depth>1 || force_display_z_coord)
37579  cimg_snprintf(text,sizeof(text)," Vect (%d,%d,%d)-(%d,%d,%d), Norm = %g ",
37580  origX+X0,origY+Y0,origZ+Z0,origX+X1,origY+Y1,origZ+Z1,norm);
37581  else cimg_snprintf(text,sizeof(text)," Vect (%d,%d)-(%d,%d), Norm = %g ",
37582  origX+X0,origY+Y0,origX+X1,origY+Y1,norm);
37583  } break;
37584  case 2 :
37585  if (_depth>1 || force_display_z_coord)
37586  cimg_snprintf(text,sizeof(text)," Box (%d,%d,%d)-(%d,%d,%d), Size = (%d,%d,%d) ",
37587  origX+(X0<X1?X0:X1),origY+(Y0<Y1?Y0:Y1),origZ+(Z0<Z1?Z0:Z1),
37588  origX+(X0<X1?X1:X0),origY+(Y0<Y1?Y1:Y0),origZ+(Z0<Z1?Z1:Z0),
37589  1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1),1+cimg::abs(Z0-Z1));
37590  else cimg_snprintf(text,sizeof(text)," Box (%d,%d)-(%d,%d), Size = (%d,%d) ",
37591  origX+(X0<X1?X0:X1),origY+(Y0<Y1?Y0:Y1),origX+(X0<X1?X1:X0),origY+(Y0<Y1?Y1:Y0),
37592  1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1));
37593  break;
37594  default :
37595  if (_depth>1 || force_display_z_coord)
37596  cimg_snprintf(text,sizeof(text)," Ellipse (%d,%d,%d)-(%d,%d,%d), Radii = (%d,%d,%d) ",
37597  origX+X0,origY+Y0,origZ+Z0,origX+X1,origY+Y1,origZ+Z1,
37598  1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1),1+cimg::abs(Z0-Z1));
37599  else cimg_snprintf(text,sizeof(text)," Ellipse (%d,%d)-(%d,%d), Radii = (%d,%d) ",
37600  origX+X0,origY+Y0,origX+X1,origY+Y1,1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1));
37601  }
37602  if (phase || (mx>=0 && my>=0))
37603  visu.draw_text(0,text_down?visu.height()-13:0,text,foreground_color,background_color,0.7f,13);
37604  }
37605 
37606  disp.display(visu).wait();
37607  } else if (!shape_selected) disp.wait();
37608  if (disp.is_resized()) { disp.resize(false)._is_resized = false; old_is_resized = true; visu0.assign(); }
37609  omx = mx; omy = my;
37610  }
37611 
37612  // Return result.
37613  CImg<intT> res(1,feature_type==0?3:6,1,1,-1);
37614  if (XYZ) { XYZ[0] = (unsigned int)X0; XYZ[1] = (unsigned int)Y0; XYZ[2] = (unsigned int)Z0; }
37615  if (shape_selected) {
37616  if (feature_type==2) {
37617  if (X0>X1) cimg::swap(X0,X1);
37618  if (Y0>Y1) cimg::swap(Y0,Y1);
37619  if (Z0>Z1) cimg::swap(Z0,Z1);
37620  }
37621  if (X1<0 || Y1<0 || Z1<0) X0 = Y0 = Z0 = X1 = Y1 = Z1 = -1;
37622  switch (feature_type) {
37623  case 1 : case 2 : res[0] = X0; res[1] = Y0; res[2] = Z0; res[3] = X1; res[4] = Y1; res[5] = Z1; break;
37624  case 3 :
37625  res[3] = cimg::abs(X1-X0); res[4] = cimg::abs(Y1-Y0); res[5] = cimg::abs(Z1-Z0); // keep no break here!
37626  default : res[0] = X0; res[1] = Y0; res[2] = Z0;
37627  }
37628  }
37629  disp.set_button();
37630  if (!visible_cursor) disp.show_mouse();
37631  disp._normalization = old_normalization;
37632  disp._is_resized = old_is_resized;
37633  if (key!=~0U) disp.set_key(key);
37634  return res;
37635  }
37636 
37637  // Return a visualizable uchar8 image for display routines.
37638  CImg<ucharT> __get_select(const CImgDisplay& disp, const int normalization,
37639  const int x, const int y, const int z) const {
37640  if (is_empty()) return CImg<ucharT>(1,1,1,1,0);
37641  const CImg<T> crop = get_shared_channels(0,cimg::min(2,spectrum()-1));
37642  CImg<Tuchar> img2d;
37643  if (_depth>1) crop.get_projections2d(x,y,z).move_to(img2d);
37644  else CImg<Tuchar>(crop,false).move_to(img2d);
37645 
37646  if (cimg::type<T>::is_float()) { // Check for inf and nan values.
37647  bool is_inf = false, is_nan = false;
37648  cimg_for(img2d,ptr,Tuchar)
37649  if (cimg::type<T>::is_inf(*ptr)) { is_inf = true; break; }
37650  else if (cimg::type<T>::is_nan(*ptr)) { is_nan = true; break; }
37651  if (is_inf || is_nan) {
37652  T m0 = cimg::type<T>::max(), M0 = cimg::type<T>::min();
37653  if (!normalization) { m0 = 0; M0 = 255; }
37654  else if (normalization==2) { m0 = (T)disp._min; M0 = (T)disp._max; }
37655  else
37656  cimg_for(img2d,ptr,Tuchar)
37657  if (!cimg::type<T>::is_inf(*ptr) && !cimg::type<T>::is_nan(*ptr)) {
37658  if (*ptr<m0) m0 = *ptr; if (*ptr>M0) M0 = *ptr;
37659  }
37660  const T
37661  val_minf = (normalization==1 || normalization==3)?m0-(M0-m0)*20-1:m0,
37662  val_pinf = (normalization==1 || normalization==3)?M0+(M0-m0)*20+1:M0;
37663  if (is_nan)
37664  cimg_for(img2d,ptr,Tuchar)
37665  if (cimg::type<T>::is_nan(*ptr)) *ptr = val_minf; // Replace nan values.
37666  if (is_inf)
37667  cimg_for(img2d,ptr,Tuchar)
37668  if (cimg::type<T>::is_inf(*ptr)) *ptr = (float)*ptr<0?val_minf:val_pinf; // Replace +-inf values.
37669  }
37670  }
37671 
37672  switch (normalization) {
37673  case 1 : img2d.normalize(0,255); break;
37674  case 2 : {
37675  const float m = disp._min, M = disp._max;
37676  (img2d-=m)*=255.0f/(M-m>0?M-m:1);
37677  } break;
37678  case 3 :
37679  if (cimg::type<T>::is_float()) img2d.normalize(0,255);
37680  else {
37681  const float m = (float)cimg::type<T>::min(), M = (float)cimg::type<T>::max();
37682  (img2d-=m)*=255.0f/(M-m>0?M-m:1);
37683  } break;
37684  }
37685 
37686  if (img2d.spectrum()==2) img2d.channels(0,2);
37687  return img2d;
37688  }
37689 
37692  const unsigned int plot_type=1, const unsigned int vertex_type=1,
37693  const char *const labelx=0, const double xmin=0, const double xmax=0,
37694  const char *const labely=0, const double ymin=0, const double ymax=0) const {
37695  if (is_empty())
37696  throw CImgInstanceException(_cimg_instance
37697  "select_graph(): Empty instance.",
37698  cimg_instance);
37699  if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,1),0,0).
37700  set_title("CImg<%s>",pixel_type());
37701  const unsigned long siz = (unsigned long)_width*_height*_depth;
37702  const unsigned int old_normalization = disp.normalization();
37703  disp.show().set_button().set_wheel()._normalization = 0;
37704 
37705  double nymin = ymin, nymax = ymax, nxmin = xmin, nxmax = xmax;
37706  if (nymin==nymax) { nymin = (Tfloat)min_max(nymax); const double dy = nymax - nymin; nymin-=dy/20; nymax+=dy/20; }
37707  if (nymin==nymax) { --nymin; ++nymax; }
37708  if (nxmin==nxmax && nxmin==0) { nxmin = 0; nxmax = siz - 1.0; }
37709 
37710  const unsigned char black[] = { 0, 0, 0 }, white[] = { 255, 255, 255 }, gray[] = { 220, 220, 220 };
37711  const unsigned char gray2[] = { 110, 110, 110 }, ngray[] = { 35, 35, 35 };
37712  static unsigned int odimv = 0;
37713  static CImg<ucharT> colormap;
37714  if (odimv!=_spectrum) {
37715  odimv = _spectrum;
37716  colormap = CImg<ucharT>(3,_spectrum,1,1,120).noise(70,1);
37717  if (_spectrum==1) { colormap[0] = colormap[1] = 120; colormap[2] = 200; }
37718  else {
37719  colormap(0,0) = 220; colormap(1,0) = 10; colormap(2,0) = 10;
37720  if (_spectrum>1) { colormap(0,1) = 10; colormap(1,1) = 220; colormap(2,1) = 10; }
37721  if (_spectrum>2) { colormap(0,2) = 10; colormap(1,2) = 10; colormap(2,2) = 220; }
37722  }
37723  }
37724 
37725  CImg<ucharT> visu0, visu, graph, text, axes;
37726  int x0 = -1, x1 = -1, y0 = -1, y1 = -1, omouse_x = -2, omouse_y = -2;
37727  const unsigned int one = plot_type==3?0:1;
37728  unsigned int okey = 0, obutton = 0;
37729  char message[1024] = { 0 };
37730  CImg_3x3(I,unsigned char);
37731 
37732  for (bool selected = false; !selected && !disp.is_closed() && !okey && !disp.wheel(); ) {
37733  const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y();
37734  const unsigned int key = disp.key(), button = disp.button();
37735 
37736  // Generate graph representation.
37737  if (!visu0) {
37738  visu0.assign(disp.width(),disp.height(),1,3,220);
37739  const int gdimx = disp.width() - 32, gdimy = disp.height() - 32;
37740  if (gdimx>0 && gdimy>0) {
37741  graph.assign(gdimx,gdimy,1,3,255);
37742  if (siz<32) {
37743  if (siz>1) graph.draw_grid(gdimx/(float)(siz - one),gdimy/(float)(siz - one),0,0,
37744  false,true,black,0.2f,0x33333333,0x33333333);
37745  } else graph.draw_grid(-10,-10,0,0,false,true,black,0.2f,0x33333333,0x33333333);
37746  cimg_forC(*this,c)
37747  graph.draw_graph(get_shared_channel(c),&colormap(0,c),(plot_type!=3 || _spectrum==1)?1:0.6f,
37748  plot_type,vertex_type,nymax,nymin);
37749 
37750  axes.assign(gdimx,gdimy,1,1,0);
37751  const float
37752  dx = (float)cimg::abs(nxmax-nxmin), dy = (float)cimg::abs(nymax-nymin),
37753  px = (float)std::pow(10.0,(int)std::log10(dx?dx:1)-2.0),
37754  py = (float)std::pow(10.0,(int)std::log10(dy?dy:1)-2.0);
37755  const CImg<Tdouble>
37756  seqx = dx<=0?CImg<Tdouble>::vector(nxmin):
37757  CImg<Tdouble>::sequence(1 + gdimx/60,nxmin,one?nxmax:nxmin+(nxmax-nxmin)*(siz+1)/siz).round(px),
37758  seqy = CImg<Tdouble>::sequence(1 + gdimy/60,nymax,nymin).round(py);
37759 
37760  const bool allow_zero = (nxmin*nxmax>0) || (nymin*nymax>0);
37761  axes.draw_axes(seqx,seqy,white,1,~0U,~0U,13,allow_zero);
37762  if (nymin>0) axes.draw_axis(seqx,gdimy-1,gray,1,~0U,13,allow_zero);
37763  if (nymax<0) axes.draw_axis(seqx,0,gray,1,~0U,13,allow_zero);
37764  if (nxmin>0) axes.draw_axis(0,seqy,gray,1,~0U,13,allow_zero);
37765  if (nxmax<0) axes.draw_axis(gdimx-1,seqy,gray,1,~0U,13,allow_zero);
37766 
37767  cimg_for3x3(axes,x,y,0,0,I,unsigned char)
37768  if (Icc) {
37769  if (Icc==255) cimg_forC(graph,c) graph(x,y,c) = 0;
37770  else cimg_forC(graph,c) graph(x,y,c) = (unsigned char)(2*graph(x,y,c)/3);
37771  }
37772  else if (Ipc || Inc || Icp || Icn || Ipp || Inn || Ipn || Inp)
37773  cimg_forC(graph,c) graph(x,y,c) = (graph(x,y,c)+511)/3;
37774 
37775  visu0.draw_image(16,16,graph);
37776  visu0.draw_line(15,15,16+gdimx,15,gray2).draw_line(16+gdimx,15,16+gdimx,16+gdimy,gray2).
37777  draw_line(16+gdimx,16+gdimy,15,16+gdimy,white).draw_line(15,16+gdimy,15,15,white);
37778  } else graph.assign();
37779  text.assign().draw_text(0,0,labelx?labelx:"X-axis",white,ngray,1,13).resize(-100,-100,1,3);
37780  visu0.draw_image((visu0.width()-text.width())/2,visu0.height()-14,~text);
37781  text.assign().draw_text(0,0,labely?labely:"Y-axis",white,ngray,1,13).rotate(-90).resize(-100,-100,1,3);
37782  visu0.draw_image(1,(visu0.height()-text.height())/2,~text);
37783  visu.assign();
37784  }
37785 
37786  // Generate and display current view.
37787  if (!visu) {
37788  visu.assign(visu0);
37789  if (graph && x0>=0 && x1>=0) {
37790  const int
37791  nx0 = x0<=x1?x0:x1,
37792  nx1 = x0<=x1?x1:x0,
37793  ny0 = y0<=y1?y0:y1,
37794  ny1 = y0<=y1?y1:y0,
37795  sx0 = 16 + nx0*(visu.width()-32)/cimg::max(1U,siz-one),
37796  sx1 = 15 + (nx1+1)*(visu.width()-32)/cimg::max(1U,siz-one),
37797  sy0 = 16 + ny0,
37798  sy1 = 16 + ny1;
37799  if (y0>=0 && y1>=0)
37800  visu.draw_rectangle(sx0,sy0,sx1,sy1,gray,0.5f).draw_rectangle(sx0,sy0,sx1,sy1,black,0.5f,0xCCCCCCCCU);
37801  else visu.draw_rectangle(sx0,0,sx1,visu.height()-17,gray,0.5f).
37802  draw_line(sx0,16,sx0,visu.height()-17,black,0.5f,0xCCCCCCCCU).
37803  draw_line(sx1,16,sx1,visu.height()-17,black,0.5f,0xCCCCCCCCU);
37804  }
37805  if (mouse_x>=16 && mouse_y>=16 && mouse_x<visu.width()-16 && mouse_y<visu.height()-16) {
37806  if (graph) visu.draw_line(mouse_x,16,mouse_x,visu.height()-17,black,0.5f,0x55555555U);
37807  const unsigned int x = (unsigned int)cimg::round((mouse_x-16.0f)*(siz-one)/(disp.width()-32),1,one?0:-1);
37808  const double cx = nxmin + x*(nxmax-nxmin)/cimg::max(1U,siz-1);
37809  if (_spectrum>=7)
37810  cimg_snprintf(message,sizeof(message),"Value[%u:%g] = ( %g %g %g ... %g %g %g )",x,cx,
37811  (double)(*this)(x,0,0,0),(double)(*this)(x,0,0,1),(double)(*this)(x,0,0,2),
37812  (double)(*this)(x,0,0,_spectrum-4),(double)(*this)(x,0,0,_spectrum-3),
37813  (double)(*this)(x,0,0,_spectrum-1));
37814  else {
37815  cimg_snprintf(message,sizeof(message),"Value[%u:%g] = ( ",x,cx);
37816  cimg_forC(*this,c) std::sprintf(message + std::strlen(message),"%g ",(double)(*this)(x,0,0,c));
37817  std::sprintf(message + std::strlen(message),")");
37818  }
37819  if (x0>=0 && x1>=0) {
37820  const unsigned int
37821  nx0 = x0<=x1?x0:x1,
37822  nx1 = x0<=x1?x1:x0,
37823  ny0 = y0<=y1?y0:y1,
37824  ny1 = y0<=y1?y1:y0;
37825  const double
37826  cx0 = nxmin + nx0*(nxmax-nxmin)/cimg::max(1U,siz-1),
37827  cx1 = nxmin + (nx1+one)*(nxmax-nxmin)/cimg::max(1U,siz-1),
37828  cy0 = nymax - ny0*(nymax-nymin)/(visu._height-32),
37829  cy1 = nymax - ny1*(nymax-nymin)/(visu._height-32);
37830  if (y0>=0 && y1>=0)
37831  std::sprintf(message + std::strlen(message)," - Range ( %u:%g, %g ) - ( %u:%g, %g )",
37832  x0,cx0,cy0,x1+one,cx1,cy1);
37833  else
37834  std::sprintf(message + std::strlen(message)," - Range [ %u:%g - %u:%g ]",
37835  x0,cx0,x1+one,cx1);
37836  }
37837  text.assign().draw_text(0,0,message,white,ngray,1,13).resize(-100,-100,1,3);
37838  visu.draw_image((visu.width()-text.width())/2,1,~text);
37839  }
37840  visu.display(disp);
37841  }
37842 
37843  // Test keys.
37844  switch (okey = key) {
37845 #if cimg_OS!=2
37847 #endif
37848  case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : okey = 0; break;
37849  case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
37850  disp.set_fullscreen(false).
37851  resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
37852  CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
37853  _is_resized = true;
37854  disp.set_key(key,false); okey = 0;
37855  } break;
37856  case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
37857  disp.set_fullscreen(false).
37858  resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
37859  disp.set_key(key,false); okey = 0;
37860  } break;
37861  case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
37862  disp.set_fullscreen(false).
37863  resize(cimg_fitscreen(CImgDisplay::screen_width()/2,
37864  CImgDisplay::screen_height()/2,1),false)._is_resized = true;
37865  disp.set_key(key,false); okey = 0;
37866  } break;
37867  case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
37868  disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
37869  disp.set_key(key,false); okey = 0;
37870  } break;
37871  case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
37872  static unsigned int snap_number = 0;
37873  if (visu || visu0) {
37874  CImg<ucharT> &screen = visu?visu:visu0;
37875  char filename[32] = { 0 };
37876  std::FILE *file;
37877  do {
37878  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++);
37879  if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
37880  } while (file);
37881  (+screen).draw_text(0,0," Saving snapshot... ",black,gray,1,13).display(disp);
37882  screen.save(filename);
37883  (+screen).draw_text(0,0," Snapshot '%s' saved. ",black,gray,1,13,filename).display(disp);
37884  }
37885  disp.set_key(key,false); okey = 0;
37886  } break;
37887  case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
37888  static unsigned int snap_number = 0;
37889  if (visu || visu0) {
37890  CImg<ucharT> &screen = visu?visu:visu0;
37891  char filename[32] = { 0 };
37892  std::FILE *file;
37893  do {
37894 #ifdef cimg_use_zlib
37895  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++);
37896 #else
37897  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++);
37898 #endif
37899  if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
37900  } while (file);
37901  (+screen).draw_text(0,0," Saving instance... ",black,gray,1,13).display(disp);
37902  save(filename);
37903  (+screen).draw_text(0,0," Instance '%s' saved. ",black,gray,1,13,filename).display(disp);
37904  }
37905  disp.set_key(key,false); okey = 0;
37906  } break;
37907  }
37908 
37909  // Handle mouse motion and mouse buttons
37910  if (obutton!=button || omouse_x!=mouse_x || omouse_y!=mouse_y) {
37911  visu.assign();
37912  if (disp.mouse_x()>=0 && disp.mouse_y()>=0) {
37913  const int
37914  mx = (mouse_x -16)*(int)(siz-one)/(disp.width()-32),
37915  cx = mx<0?0:(mx>=(int)(siz-one)?(int)(siz-1-one):mx),
37916  my = mouse_y - 16,
37917  cy = my<=0?0:(my>=(disp.height()-32)?(disp.height()-32):my);
37918  if (button&1) {
37919  if (!obutton) { x0 = cx; y0 = -1; } else { x1 = cx; y1 = -1; }
37920  }
37921  else if (button&2) {
37922  if (!obutton) { x0 = cx; y0 = cy; } else { x1 = cx; y1 = cy; }
37923  }
37924  else if (obutton) { x1 = x1>=0?cx:-1; y1 = y1>=0?cy:-1; selected = true; }
37925  } else if (!button && obutton) selected = true;
37926  obutton = button; omouse_x = mouse_x; omouse_y = mouse_y;
37927  }
37928  if (disp.is_resized()) { disp.resize(false); visu0.assign(); }
37929  if (visu && visu0) disp.wait();
37930  }
37931 
37932  disp._normalization = old_normalization;
37933  if (x1>=0 && x1<x0) cimg::swap(x0,x1);
37934  if (y1<y0) cimg::swap(y0,y1);
37935  disp.set_key(okey);
37936  return CImg<intT>(4,1,1,1,x0,y0,x1>=0?x1+(int)one:-1,y1);
37937  }
37938 
37940 
37945  CImg<T>& load(const char *const filename) {
37946  if (!filename)
37947  throw CImgArgumentException(_cimg_instance
37948  "load(): Specified filename is (null).",
37949  cimg_instance);
37950 
37951  if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) {
37952  char filename_local[1024] = { 0 };
37953  load(cimg::load_network_external(filename,filename_local));
37954  std::remove(filename_local);
37955  return *this;
37956  }
37957 
37958  const char *const ext = cimg::split_filename(filename);
37959  const unsigned int omode = cimg::exception_mode();
37960  cimg::exception_mode() = 0;
37961  try {
37962 #ifdef cimg_load_plugin
37963  cimg_load_plugin(filename);
37964 #endif
37965 #ifdef cimg_load_plugin1
37966  cimg_load_plugin1(filename);
37967 #endif
37968 #ifdef cimg_load_plugin2
37969  cimg_load_plugin2(filename);
37970 #endif
37971 #ifdef cimg_load_plugin3
37972  cimg_load_plugin3(filename);
37973 #endif
37974 #ifdef cimg_load_plugin4
37975  cimg_load_plugin4(filename);
37976 #endif
37977 #ifdef cimg_load_plugin5
37978  cimg_load_plugin5(filename);
37979 #endif
37980 #ifdef cimg_load_plugin6
37981  cimg_load_plugin6(filename);
37982 #endif
37983 #ifdef cimg_load_plugin7
37984  cimg_load_plugin7(filename);
37985 #endif
37986 #ifdef cimg_load_plugin8
37987  cimg_load_plugin8(filename);
37988 #endif
37989  // Ascii formats
37990  if (!cimg::strcasecmp(ext,"asc")) load_ascii(filename);
37991  else if (!cimg::strcasecmp(ext,"dlm") ||
37992  !cimg::strcasecmp(ext,"txt")) load_dlm(filename);
37993 
37994  // 2d binary formats
37995  else if (!cimg::strcasecmp(ext,"bmp")) load_bmp(filename);
37996  else if (!cimg::strcasecmp(ext,"jpg") ||
37997  !cimg::strcasecmp(ext,"jpeg") ||
37998  !cimg::strcasecmp(ext,"jpe") ||
37999  !cimg::strcasecmp(ext,"jfif") ||
38000  !cimg::strcasecmp(ext,"jif")) load_jpeg(filename);
38001  else if (!cimg::strcasecmp(ext,"png")) load_png(filename);
38002  else if (!cimg::strcasecmp(ext,"ppm") ||
38003  !cimg::strcasecmp(ext,"pgm") ||
38004  !cimg::strcasecmp(ext,"pnm") ||
38005  !cimg::strcasecmp(ext,"pbm") ||
38006  !cimg::strcasecmp(ext,"pnk")) load_pnm(filename);
38007  else if (!cimg::strcasecmp(ext,"pfm")) load_pfm(filename);
38008  else if (!cimg::strcasecmp(ext,"tif") ||
38009  !cimg::strcasecmp(ext,"tiff")) load_tiff(filename);
38010  else if (!cimg::strcasecmp(ext,"exr")) load_exr(filename);
38011  else if (!cimg::strcasecmp(ext,"cr2") ||
38012  !cimg::strcasecmp(ext,"crw") ||
38013  !cimg::strcasecmp(ext,"dcr") ||
38014  !cimg::strcasecmp(ext,"mrw") ||
38015  !cimg::strcasecmp(ext,"nef") ||
38016  !cimg::strcasecmp(ext,"orf") ||
38017  !cimg::strcasecmp(ext,"pix") ||
38018  !cimg::strcasecmp(ext,"ptx") ||
38019  !cimg::strcasecmp(ext,"raf") ||
38020  !cimg::strcasecmp(ext,"srf")) load_dcraw_external(filename);
38021  else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename);
38022 
38023  // 3d binary formats
38024  else if (!cimg::strcasecmp(ext,"dcm") ||
38025  !cimg::strcasecmp(ext,"dicom")) load_medcon_external(filename);
38026  else if (!cimg::strcasecmp(ext,"hdr") ||
38027  !cimg::strcasecmp(ext,"nii")) load_analyze(filename);
38028  else if (!cimg::strcasecmp(ext,"par") ||
38029  !cimg::strcasecmp(ext,"rec")) load_parrec(filename);
38030  else if (!cimg::strcasecmp(ext,"mnc")) load_minc2(filename);
38031  else if (!cimg::strcasecmp(ext,"inr")) load_inr(filename);
38032  else if (!cimg::strcasecmp(ext,"pan")) load_pandore(filename);
38033  else if (!cimg::strcasecmp(ext,"cimg") ||
38034  !cimg::strcasecmp(ext,"cimgz") ||
38035  !*ext) return load_cimg(filename);
38036 
38037  // Archive files
38038  else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename);
38039 
38040  // Image sequences
38041  else if (!cimg::strcasecmp(ext,"avi") ||
38042  !cimg::strcasecmp(ext,"mov") ||
38043  !cimg::strcasecmp(ext,"asf") ||
38044  !cimg::strcasecmp(ext,"divx") ||
38045  !cimg::strcasecmp(ext,"flv") ||
38046  !cimg::strcasecmp(ext,"mpg") ||
38047  !cimg::strcasecmp(ext,"m1v") ||
38048  !cimg::strcasecmp(ext,"m2v") ||
38049  !cimg::strcasecmp(ext,"m4v") ||
38050  !cimg::strcasecmp(ext,"mjp") ||
38051  !cimg::strcasecmp(ext,"mp4") ||
38052  !cimg::strcasecmp(ext,"mkv") ||
38053  !cimg::strcasecmp(ext,"mpe") ||
38054  !cimg::strcasecmp(ext,"movie") ||
38055  !cimg::strcasecmp(ext,"ogm") ||
38056  !cimg::strcasecmp(ext,"ogg") ||
38057  !cimg::strcasecmp(ext,"ogv") ||
38058  !cimg::strcasecmp(ext,"qt") ||
38059  !cimg::strcasecmp(ext,"rm") ||
38060  !cimg::strcasecmp(ext,"vob") ||
38061  !cimg::strcasecmp(ext,"wmv") ||
38062  !cimg::strcasecmp(ext,"xvid") ||
38063  !cimg::strcasecmp(ext,"mpeg")) load_ffmpeg(filename);
38064  else throw CImgIOException("CImg<%s>::load()",
38065  pixel_type());
38066  } catch (CImgIOException&) {
38067  std::FILE *file = 0;
38068  try {
38069  file = cimg::fopen(filename,"rb");
38070  } catch (CImgIOException&) {
38071  cimg::exception_mode() = omode;
38072  throw CImgIOException(_cimg_instance
38073  "load(): Failed to open file '%s'.",
38074  cimg_instance,
38075  filename);
38076  }
38077 
38078  try {
38079  const char *const f_type = cimg::file_type(file,filename);
38080  std::fclose(file);
38081  if (!cimg::strcasecmp(f_type,"pnm")) load_pnm(filename);
38082  else if (!cimg::strcasecmp(f_type,"pfm")) load_pfm(filename);
38083  else if (!cimg::strcasecmp(f_type,"bmp")) load_bmp(filename);
38084  else if (!cimg::strcasecmp(f_type,"jpg")) load_jpeg(filename);
38085  else if (!cimg::strcasecmp(f_type,"pan")) load_pandore(filename);
38086  else if (!cimg::strcasecmp(f_type,"png")) load_png(filename);
38087  else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename);
38088  else if (!cimg::strcasecmp(f_type,"inr")) load_inr(filename);
38089  else if (!cimg::strcasecmp(f_type,"dcm")) load_medcon_external(filename);
38090  else throw CImgIOException("CImg<%s>::load()",
38091  pixel_type());
38092  } catch (CImgIOException&) {
38093  try {
38094  load_other(filename);
38095  } catch (CImgIOException&) {
38096  cimg::exception_mode() = omode;
38097  throw CImgIOException(_cimg_instance
38098  "load(): Failed to recognize format of file '%s'.",
38099  cimg_instance,
38100  filename);
38101  }
38102  }
38103  }
38104  cimg::exception_mode() = omode;
38105  return *this;
38106  }
38107 
38109  static CImg<T> get_load(const char *const filename) {
38110  return CImg<T>().load(filename);
38111  }
38112 
38114 
38117  CImg<T>& load_ascii(const char *const filename) {
38118  return _load_ascii(0,filename);
38119  }
38120 
38122  static CImg<T> get_load_ascii(const char *const filename) {
38123  return CImg<T>().load_ascii(filename);
38124  }
38125 
38127  CImg<T>& load_ascii(std::FILE *const file) {
38128  return _load_ascii(file,0);
38129  }
38130 
38132  static CImg<T> get_load_ascii(std::FILE *const file) {
38133  return CImg<T>().load_ascii(file);
38134  }
38135 
38136  CImg<T>& _load_ascii(std::FILE *const file, const char *const filename) {
38137  if (!file && !filename)
38138  throw CImgArgumentException(_cimg_instance
38139  "load_ascii(): Specified filename is (null).",
38140  cimg_instance);
38141 
38142  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
38143  char line[256] = { 0 };
38144  int err = std::fscanf(nfile,"%255[^\n]",line);
38145  unsigned int dx = 0, dy = 1, dz = 1, dc = 1;
38146  std::sscanf(line,"%u%*c%u%*c%u%*c%u",&dx,&dy,&dz,&dc);
38147  err = std::fscanf(nfile,"%*[^0-9.eE+-]");
38148  if (!dx || !dy || !dz || !dc) {
38149  if (!file) cimg::fclose(nfile);
38150  throw CImgIOException(_cimg_instance
38151  "load_ascii(): Invalid ascii header in file '%s', image dimensions are set "
38152  "to (%u,%u,%u,%u).",
38153  cimg_instance,
38154  filename?filename:"(FILE*)",dx,dy,dz,dc);
38155  }
38156  assign(dx,dy,dz,dc);
38157  const unsigned long siz = size();
38158  unsigned long off = 0;
38159  double val;
38160  T *ptr = _data;
38161  for (err = 1, off = 0; off<siz && err==1; ++off) {
38162  err = std::fscanf(nfile,"%lf%*[^0-9.eE+-]",&val);
38163  *(ptr++) = (T)val;
38164  }
38165  if (err!=1)
38166  cimg::warn(_cimg_instance
38167  "load_ascii(): Only %lu/%lu values read from file '%s'.",
38168  cimg_instance,
38169  off-1,siz,filename?filename:"(FILE*)");
38170 
38171  if (!file) cimg::fclose(nfile);
38172  return *this;
38173  }
38174 
38176 
38179  CImg<T>& load_dlm(const char *const filename) {
38180  return _load_dlm(0,filename);
38181  }
38182 
38184  static CImg<T> get_load_dlm(const char *const filename) {
38185  return CImg<T>().load_dlm(filename);
38186  }
38187 
38189  CImg<T>& load_dlm(std::FILE *const file) {
38190  return _load_dlm(file,0);
38191  }
38192 
38194  static CImg<T> get_load_dlm(std::FILE *const file) {
38195  return CImg<T>().load_dlm(file);
38196  }
38197 
38198  CImg<T>& _load_dlm(std::FILE *const file, const char *const filename) {
38199  if (!file && !filename)
38200  throw CImgArgumentException(_cimg_instance
38201  "load_dlm(): Specified filename is (null).",
38202  cimg_instance);
38203 
38204  std::FILE *const nfile = file?file:cimg::fopen(filename,"r");
38205  char delimiter[256] = { 0 }, tmp[256] = { 0 };
38206  unsigned int cdx = 0, dx = 0, dy = 0;
38207  int err = 0;
38208  double val;
38209  assign(256,256);
38210  while ((err = std::fscanf(nfile,"%lf%255[^0-9.+-]",&val,delimiter))>0) {
38211  if (err>0) (*this)(cdx++,dy) = (T)val;
38212  if (cdx>=_width) resize(3*_width/2,_height,1,1,0);
38213  char c = 0;
38214  if (!std::sscanf(delimiter,"%255[^\n]%c",tmp,&c) || c=='\n') {
38215  dx = cimg::max(cdx,dx);
38216  if (++dy>=_height) resize(_width,3*_height/2,1,1,0);
38217  cdx = 0;
38218  }
38219  }
38220  if (cdx && err==1) { dx = cdx; ++dy; }
38221  if (!dx || !dy) {
38222  if (!file) cimg::fclose(nfile);
38223  throw CImgIOException(_cimg_instance
38224  "load_dlm(): Invalid DLM file '%s'.",
38225  cimg_instance,
38226  filename?filename:"(FILE*)");
38227  }
38228  resize(dx,dy,1,1,0);
38229  if (!file) cimg::fclose(nfile);
38230  return *this;
38231  }
38232 
38234 
38237  CImg<T>& load_bmp(const char *const filename) {
38238  return _load_bmp(0,filename);
38239  }
38240 
38242  static CImg<T> get_load_bmp(const char *const filename) {
38243  return CImg<T>().load_bmp(filename);
38244  }
38245 
38247  CImg<T>& load_bmp(std::FILE *const file) {
38248  return _load_bmp(file,0);
38249  }
38250 
38252  static CImg<T> get_load_bmp(std::FILE *const file) {
38253  return CImg<T>().load_bmp(file);
38254  }
38255 
38256  CImg<T>& _load_bmp(std::FILE *const file, const char *const filename) {
38257  if (!file && !filename)
38258  throw CImgArgumentException(_cimg_instance
38259  "load_bmp(): Specified filename is (null).",
38260  cimg_instance);
38261 
38262  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
38263  unsigned char header[64] = { 0 };
38264  cimg::fread(header,54,nfile);
38265  if (*header!='B' || header[1]!='M') {
38266  if (!file) cimg::fclose(nfile);
38267  throw CImgIOException(_cimg_instance
38268  "load_bmp(): Invalid BMP file '%s'.",
38269  cimg_instance,
38270  filename?filename:"(FILE*)");
38271  }
38272 
38273  // Read header and pixel buffer
38274  int
38275  file_size = header[0x02] + (header[0x03]<<8) + (header[0x04]<<16) + (header[0x05]<<24),
38276  offset = header[0x0A] + (header[0x0B]<<8) + (header[0x0C]<<16) + (header[0x0D]<<24),
38277  header_size = header[0x0E] + (header[0x0F]<<8) + (header[0x10]<<16) + (header[0x11]<<24),
38278  dx = header[0x12] + (header[0x13]<<8) + (header[0x14]<<16) + (header[0x15]<<24),
38279  dy = header[0x16] + (header[0x17]<<8) + (header[0x18]<<16) + (header[0x19]<<24),
38280  compression = header[0x1E] + (header[0x1F]<<8) + (header[0x20]<<16) + (header[0x21]<<24),
38281  nb_colors = header[0x2E] + (header[0x2F]<<8) + (header[0x30]<<16) + (header[0x31]<<24),
38282  bpp = header[0x1C] + (header[0x1D]<<8);
38283 
38284  if (!file_size || file_size==offset) {
38285  std::fseek(nfile,0,SEEK_END);
38286  file_size = (int)std::ftell(nfile);
38287  std::fseek(nfile,54,SEEK_SET);
38288  }
38289  if (header_size>40) std::fseek(nfile, header_size - 40, SEEK_CUR);
38290 
38291  const int
38292  cimg_iobuffer = 12*1024*1024,
38293  dx_bytes = (bpp==1)?(dx/8+(dx%8?1:0)):((bpp==4)?(dx/2+(dx%2?1:0)):(dx*bpp/8)),
38294  align_bytes = (4-dx_bytes%4)%4,
38295  buf_size = cimg::min(cimg::abs(dy)*(dx_bytes + align_bytes),file_size - offset);
38296 
38297  CImg<intT> colormap;
38298  if (bpp<16) { if (!nb_colors) nb_colors = 1<<bpp; } else nb_colors = 0;
38299  if (nb_colors) { colormap.assign(nb_colors); cimg::fread(colormap._data,nb_colors,nfile); }
38300  const int xoffset = offset - 14 - header_size - 4*nb_colors;
38301  if (xoffset>0) std::fseek(nfile,xoffset,SEEK_CUR);
38302 
38303  CImg<ucharT> buffer;
38304  if (buf_size<cimg_iobuffer) { buffer.assign(buf_size); cimg::fread(buffer._data,buf_size,nfile); }
38305  else buffer.assign(dx_bytes + align_bytes);
38306  unsigned char *ptrs = buffer;
38307 
38308  // Decompress buffer (if necessary)
38309  if (compression) {
38310  if (file)
38311  throw CImgIOException(_cimg_instance
38312  "load_bmp(): Unable to load compressed data from '(*FILE)' inputs.",
38313  cimg_instance);
38314  else {
38315  if (!file) cimg::fclose(nfile);
38316  return load_other(filename);
38317  }
38318  }
38319 
38320  // Read pixel data
38321  assign(dx,cimg::abs(dy),1,3);
38322  switch (bpp) {
38323  case 1 : { // Monochrome
38324  for (int y = height()-1; y>=0; --y) {
38325  if (buf_size>=cimg_iobuffer) {
38326  cimg::fread(ptrs=buffer._data,dx_bytes,nfile);
38327  std::fseek(nfile,align_bytes,SEEK_CUR);
38328  }
38329  unsigned char mask = 0x80, val = 0;
38330  cimg_forX(*this,x) {
38331  if (mask==0x80) val = *(ptrs++);
38332  const unsigned char *col = (unsigned char*)(colormap._data + (val&mask?1:0));
38333  (*this)(x,y,2) = (T)*(col++);
38334  (*this)(x,y,1) = (T)*(col++);
38335  (*this)(x,y,0) = (T)*(col++);
38336  mask = cimg::ror(mask);
38337  }
38338  ptrs+=align_bytes;
38339  }
38340  } break;
38341  case 4 : { // 16 colors
38342  for (int y = height()-1; y>=0; --y) {
38343  if (buf_size>=cimg_iobuffer) {
38344  cimg::fread(ptrs=buffer._data,dx_bytes,nfile);
38345  std::fseek(nfile,align_bytes,SEEK_CUR);
38346  }
38347  unsigned char mask = 0xF0, val = 0;
38348  cimg_forX(*this,x) {
38349  if (mask==0xF0) val = *(ptrs++);
38350  const unsigned char color = (unsigned char)((mask<16)?(val&mask):((val&mask)>>4));
38351  const unsigned char *col = (unsigned char*)(colormap._data + color);
38352  (*this)(x,y,2) = (T)*(col++);
38353  (*this)(x,y,1) = (T)*(col++);
38354  (*this)(x,y,0) = (T)*(col++);
38355  mask = cimg::ror(mask,4);
38356  }
38357  ptrs+=align_bytes;
38358  }
38359  } break;
38360  case 8 : { // 256 colors
38361  for (int y = height()-1; y>=0; --y) {
38362  if (buf_size>=cimg_iobuffer) {
38363  cimg::fread(ptrs=buffer._data,dx_bytes,nfile);
38364  std::fseek(nfile,align_bytes,SEEK_CUR);
38365  }
38366  cimg_forX(*this,x) {
38367  const unsigned char *col = (unsigned char*)(colormap._data + *(ptrs++));
38368  (*this)(x,y,2) = (T)*(col++);
38369  (*this)(x,y,1) = (T)*(col++);
38370  (*this)(x,y,0) = (T)*(col++);
38371  }
38372  ptrs+=align_bytes;
38373  }
38374  } break;
38375  case 16 : { // 16 bits colors
38376  for (int y = height()-1; y>=0; --y) {
38377  if (buf_size>=cimg_iobuffer) {
38378  cimg::fread(ptrs=buffer._data,dx_bytes,nfile);
38379  std::fseek(nfile,align_bytes,SEEK_CUR);
38380  }
38381  cimg_forX(*this,x) {
38382  const unsigned char c1 = *(ptrs++), c2 = *(ptrs++);
38383  const unsigned short col = (unsigned short)(c1|(c2<<8));
38384  (*this)(x,y,2) = (T)(col&0x1F);
38385  (*this)(x,y,1) = (T)((col>>5)&0x1F);
38386  (*this)(x,y,0) = (T)((col>>10)&0x1F);
38387  }
38388  ptrs+=align_bytes;
38389  }
38390  } break;
38391  case 24 : { // 24 bits colors
38392  for (int y = height()-1; y>=0; --y) {
38393  if (buf_size>=cimg_iobuffer) {
38394  cimg::fread(ptrs=buffer._data,dx_bytes,nfile);
38395  std::fseek(nfile,align_bytes,SEEK_CUR);
38396  }
38397  cimg_forX(*this,x) {
38398  (*this)(x,y,2) = (T)*(ptrs++);
38399  (*this)(x,y,1) = (T)*(ptrs++);
38400  (*this)(x,y,0) = (T)*(ptrs++);
38401  }
38402  ptrs+=align_bytes;
38403  }
38404  } break;
38405  case 32 : { // 32 bits colors
38406  for (int y = height()-1; y>=0; --y) {
38407  if (buf_size>=cimg_iobuffer) {
38408  cimg::fread(ptrs=buffer._data,dx_bytes,nfile);
38409  std::fseek(nfile,align_bytes,SEEK_CUR);
38410  }
38411  cimg_forX(*this,x) {
38412  (*this)(x,y,2) = (T)*(ptrs++);
38413  (*this)(x,y,1) = (T)*(ptrs++);
38414  (*this)(x,y,0) = (T)*(ptrs++);
38415  ++ptrs;
38416  }
38417  ptrs+=align_bytes;
38418  }
38419  } break;
38420  }
38421  if (dy<0) mirror('y');
38422  if (!file) cimg::fclose(nfile);
38423  return *this;
38424  }
38425 
38427 
38430  CImg<T>& load_jpeg(const char *const filename) {
38431  return _load_jpeg(0,filename);
38432  }
38433 
38435  static CImg<T> get_load_jpeg(const char *const filename) {
38436  return CImg<T>().load_jpeg(filename);
38437  }
38438 
38440  CImg<T>& load_jpeg(std::FILE *const file) {
38441  return _load_jpeg(file,0);
38442  }
38443 
38445  static CImg<T> get_load_jpeg(std::FILE *const file) {
38446  return CImg<T>().load_jpeg(file);
38447  }
38448 
38449  // Custom error handler for libjpeg.
38450 #ifdef cimg_use_jpeg
38451  struct _cimg_error_mgr {
38452  struct jpeg_error_mgr original;
38453  jmp_buf setjmp_buffer;
38454  char message[JMSG_LENGTH_MAX];
38455  };
38456 
38457  typedef struct _cimg_error_mgr *_cimg_error_ptr;
38458 
38459  METHODDEF(void) _cimg_jpeg_error_exit(j_common_ptr cinfo) {
38460  _cimg_error_ptr c_err = (_cimg_error_ptr) cinfo->err; // Return control to the setjmp point
38461  (*cinfo->err->format_message)(cinfo,c_err->message);
38462  jpeg_destroy(cinfo); // Clean memory and temp files.
38463  longjmp(c_err->setjmp_buffer,1);
38464  }
38465 #endif
38466 
38467  CImg<T>& _load_jpeg(std::FILE *const file, const char *const filename) {
38468  if (!file && !filename)
38469  throw CImgArgumentException(_cimg_instance
38470  "load_jpeg(): Specified filename is (null).",
38471  cimg_instance);
38472 
38473 #ifndef cimg_use_jpeg
38474  if (file)
38475  throw CImgIOException(_cimg_instance
38476  "load_jpeg(): Unable to load data from '(FILE*)' unless libjpeg is enabled.",
38477  cimg_instance);
38478  else return load_other(filename);
38479 #else
38480 
38481  struct jpeg_decompress_struct cinfo;
38482  struct _cimg_error_mgr jerr;
38483  cinfo.err = jpeg_std_error(&jerr.original);
38484  jerr.original.error_exit = _cimg_jpeg_error_exit;
38485 
38486  if (setjmp(jerr.setjmp_buffer)) { // JPEG error
38487  throw CImgIOException(_cimg_instance
38488  "load_jpeg(): Error message returned by libjpeg: %s.",
38489  cimg_instance,jerr.message);
38490  }
38491 
38492  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
38493  jpeg_create_decompress(&cinfo);
38494  jpeg_stdio_src(&cinfo,nfile);
38495  jpeg_read_header(&cinfo,TRUE);
38496  jpeg_start_decompress(&cinfo);
38497 
38498  if (cinfo.output_components!=1 && cinfo.output_components!=3 && cinfo.output_components!=4) {
38499  if (!file) {
38500  cimg::fclose(nfile);
38501  return load_other(filename);
38502  } else
38503  throw CImgIOException(_cimg_instance
38504  "load_jpeg(): Failed to load JPEG data from file '%s'.",
38505  cimg_instance,filename?filename:"(FILE*)");
38506  }
38507  CImg<ucharT> buffer(cinfo.output_width*cinfo.output_components);
38508  JSAMPROW row_pointer[1];
38509  assign(cinfo.output_width,cinfo.output_height,1,cinfo.output_components);
38510  T *ptr_r = _data, *ptr_g = _data + 1UL*_width*_height, *ptr_b = _data + 2UL*_width*_height,
38511  *ptr_a = _data + 3UL*_width*_height;
38512  while (cinfo.output_scanline<cinfo.output_height) {
38513  *row_pointer = buffer._data;
38514  if (jpeg_read_scanlines(&cinfo,row_pointer,1)!=1) {
38515  cimg::warn(_cimg_instance
38516  "load_jpeg(): Incomplete data in file '%s'.",
38517  cimg_instance,filename?filename:"(FILE*)");
38518  break;
38519  }
38520  const unsigned char *ptrs = buffer._data;
38521  switch (_spectrum) {
38522  case 1 : {
38523  cimg_forX(*this,x) *(ptr_r++) = (T)*(ptrs++);
38524  } break;
38525  case 3 : {
38526  cimg_forX(*this,x) {
38527  *(ptr_r++) = (T)*(ptrs++);
38528  *(ptr_g++) = (T)*(ptrs++);
38529  *(ptr_b++) = (T)*(ptrs++);
38530  }
38531  } break;
38532  case 4 : {
38533  cimg_forX(*this,x) {
38534  *(ptr_r++) = (T)*(ptrs++);
38535  *(ptr_g++) = (T)*(ptrs++);
38536  *(ptr_b++) = (T)*(ptrs++);
38537  *(ptr_a++) = (T)*(ptrs++);
38538  }
38539  } break;
38540  }
38541  }
38542  jpeg_finish_decompress(&cinfo);
38543  jpeg_destroy_decompress(&cinfo);
38544  if (!file) cimg::fclose(nfile);
38545  return *this;
38546 #endif
38547  }
38548 
38550 
38553  // Added April/may 2006 by Christoph Hormann <chris_hormann@gmx.de>
38554  // This is experimental code, not much tested, use with care.
38555  CImg<T>& load_magick(const char *const filename) {
38556  if (!filename)
38557  throw CImgArgumentException(_cimg_instance
38558  "load_magick(): Specified filename is (null).",
38559  cimg_instance);
38560 #ifdef cimg_use_magick
38561  Magick::Image image(filename);
38562  const unsigned int W = image.size().width(), H = image.size().height();
38563  switch (image.type()) {
38564  case Magick::PaletteMatteType :
38565  case Magick::TrueColorMatteType :
38566  case Magick::ColorSeparationType : {
38567  assign(W,H,1,4);
38568  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);
38569  Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
38570  for (unsigned long off = (unsigned long)W*H; off; --off) {
38571  *(ptr_r++) = (T)(pixels->red);
38572  *(ptr_g++) = (T)(pixels->green);
38573  *(ptr_b++) = (T)(pixels->blue);
38574  *(ptr_a++) = (T)(pixels->opacity);
38575  ++pixels;
38576  }
38577  } break;
38578  case Magick::PaletteType :
38579  case Magick::TrueColorType : {
38580  assign(W,H,1,3);
38581  T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
38582  Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
38583  for (unsigned long off = (unsigned long)W*H; off; --off) {
38584  *(ptr_r++) = (T)(pixels->red);
38585  *(ptr_g++) = (T)(pixels->green);
38586  *(ptr_b++) = (T)(pixels->blue);
38587  ++pixels;
38588  }
38589  } break;
38590  case Magick::GrayscaleMatteType : {
38591  assign(W,H,1,2);
38592  T *ptr_r = data(0,0,0,0), *ptr_a = data(0,0,0,1);
38593  Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
38594  for (unsigned long off = (unsigned long)W*H; off; --off) {
38595  *(ptr_r++) = (T)(pixels->red);
38596  *(ptr_a++) = (T)(pixels->opacity);
38597  ++pixels;
38598  }
38599  } break;
38600  default : {
38601  assign(W,H,1,1);
38602  T *ptr_r = data(0,0,0,0);
38603  Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
38604  for (unsigned long off = (unsigned long)W*H; off; --off) {
38605  *(ptr_r++) = (T)(pixels->red);
38606  ++pixels;
38607  }
38608  }
38609  }
38610  return *this;
38611 #else
38612  throw CImgIOException(_cimg_instance
38613  "load_magick(): Unable to load file '%s' unless libMagick++ is enabled.",
38614  cimg_instance,
38615  filename);
38616 #endif
38617  }
38618 
38620  static CImg<T> get_load_magick(const char *const filename) {
38621  return CImg<T>().load_magick(filename);
38622  }
38623 
38625 
38628  CImg<T>& load_png(const char *const filename) {
38629  return _load_png(0,filename);
38630  }
38631 
38633  static CImg<T> get_load_png(const char *const filename) {
38634  return CImg<T>().load_png(filename);
38635  }
38636 
38638  CImg<T>& load_png(std::FILE *const file) {
38639  return _load_png(file,0);
38640  }
38641 
38643  static CImg<T> get_load_png(std::FILE *const file) {
38644  return CImg<T>().load_png(file);
38645  }
38646 
38647  // (Note: Most of this function has been written by Eric Fausett)
38648  CImg<T>& _load_png(std::FILE *const file, const char *const filename) {
38649  if (!file && !filename)
38650  throw CImgArgumentException(_cimg_instance
38651  "load_png(): Specified filename is (null).",
38652  cimg_instance);
38653 
38654 #ifndef cimg_use_png
38655  if (file)
38656  throw CImgIOException(_cimg_instance
38657  "load_png(): Unable to load data from '(FILE*)' unless libpng is enabled.",
38658  cimg_instance);
38659 
38660  else return load_other(filename);
38661 #else
38662  // Open file and check for PNG validity
38663  const char *volatile nfilename = filename; // two 'volatile' here to remove a g++ warning due to 'setjmp'.
38664  std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"rb");
38665 
38666  unsigned char pngCheck[8] = { 0 };
38667  cimg::fread(pngCheck,8,(std::FILE*)nfile);
38668  if (png_sig_cmp(pngCheck,0,8)) {
38669  if (!file) cimg::fclose(nfile);
38670  throw CImgIOException(_cimg_instance
38671  "load_png(): Invalid PNG file '%s'.",
38672  cimg_instance,
38673  nfilename?nfilename:"(FILE*)");
38674  }
38675 
38676  // Setup PNG structures for read
38677  png_voidp user_error_ptr = 0;
38678  png_error_ptr user_error_fn = 0, user_warning_fn = 0;
38679  png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,user_error_ptr,user_error_fn,user_warning_fn);
38680  if (!png_ptr) {
38681  if (!file) cimg::fclose(nfile);
38682  throw CImgIOException(_cimg_instance
38683  "load_png(): Failed to initialize 'png_ptr' structure for file '%s'.",
38684  cimg_instance,
38685  nfilename?nfilename:"(FILE*)");
38686  }
38687  png_infop info_ptr = png_create_info_struct(png_ptr);
38688  if (!info_ptr) {
38689  if (!file) cimg::fclose(nfile);
38690  png_destroy_read_struct(&png_ptr,(png_infopp)0,(png_infopp)0);
38691  throw CImgIOException(_cimg_instance
38692  "load_png(): Failed to initialize 'info_ptr' structure for file '%s'.",
38693  cimg_instance,
38694  nfilename?nfilename:"(FILE*)");
38695  }
38696  png_infop end_info = png_create_info_struct(png_ptr);
38697  if (!end_info) {
38698  if (!file) cimg::fclose(nfile);
38699  png_destroy_read_struct(&png_ptr,&info_ptr,(png_infopp)0);
38700  throw CImgIOException(_cimg_instance
38701  "load_png(): Failed to initialize 'end_info' structure for file '%s'.",
38702  cimg_instance,
38703  nfilename?nfilename:"(FILE*)");
38704  }
38705 
38706  // Error handling callback for png file reading
38707  if (setjmp(png_jmpbuf(png_ptr))) {
38708  if (!file) cimg::fclose((std::FILE*)nfile);
38709  png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0);
38710  throw CImgIOException(_cimg_instance
38711  "load_png(): Encountered unknown fatal error in libpng for file '%s'.",
38712  cimg_instance,
38713  nfilename?nfilename:"(FILE*)");
38714  }
38715  png_init_io(png_ptr, nfile);
38716  png_set_sig_bytes(png_ptr, 8);
38717 
38718  // Get PNG Header Info up to data block
38719  png_read_info(png_ptr,info_ptr);
38720  png_uint_32 W, H;
38721  int bit_depth, color_type, interlace_type;
38722  bool is_gray = false;
38723  png_get_IHDR(png_ptr,info_ptr,&W,&H,&bit_depth,&color_type,&interlace_type,(int*)0,(int*)0);
38724 
38725  // Transforms to unify image data
38726  if (color_type==PNG_COLOR_TYPE_PALETTE) {
38727  png_set_palette_to_rgb(png_ptr);
38728  color_type = PNG_COLOR_TYPE_RGB;
38729  bit_depth = 8;
38730  }
38731  if (color_type==PNG_COLOR_TYPE_GRAY && bit_depth<8) {
38732  png_set_expand_gray_1_2_4_to_8(png_ptr);
38733  is_gray = true;
38734  bit_depth = 8;
38735  }
38736  if (png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS)) {
38737  png_set_tRNS_to_alpha(png_ptr);
38738  color_type |= PNG_COLOR_MASK_ALPHA;
38739  }
38740  if (color_type==PNG_COLOR_TYPE_GRAY || color_type==PNG_COLOR_TYPE_GRAY_ALPHA) {
38741  png_set_gray_to_rgb(png_ptr);
38742  color_type |= PNG_COLOR_MASK_COLOR;
38743  is_gray = true;
38744  }
38745  if (color_type==PNG_COLOR_TYPE_RGB)
38746  png_set_filler(png_ptr,0xffffU,PNG_FILLER_AFTER);
38747 
38748  png_read_update_info(png_ptr,info_ptr);
38749  if (bit_depth!=8 && bit_depth!=16) {
38750  if (!file) cimg::fclose(nfile);
38751  png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0);
38752  throw CImgIOException(_cimg_instance
38753  "load_png(): Invalid bit depth %u in file '%s'.",
38754  cimg_instance,
38755  bit_depth,nfilename?nfilename:"(FILE*)");
38756  }
38757  const int byte_depth = bit_depth>>3;
38758 
38759  // Allocate Memory for Image Read
38760  png_bytep *const imgData = new png_bytep[H];
38761  for (unsigned int row = 0; row<H; ++row) imgData[row] = new png_byte[byte_depth*4*W];
38762  png_read_image(png_ptr,imgData);
38763  png_read_end(png_ptr,end_info);
38764 
38765  // Read pixel data
38766  if (color_type!=PNG_COLOR_TYPE_RGB && color_type!=PNG_COLOR_TYPE_RGB_ALPHA) {
38767  if (!file) cimg::fclose(nfile);
38768  png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0);
38769  throw CImgIOException(_cimg_instance
38770  "load_png(): Invalid color coding type %u in file '%s'.",
38771  cimg_instance,
38772  color_type,nfilename?nfilename:"(FILE*)");
38773  }
38774  const bool is_alpha = (color_type==PNG_COLOR_TYPE_RGBA);
38775  assign(W,H,1,(is_gray?1:3) + (is_alpha?1:0));
38776  T
38777  *ptr_r = data(0,0,0,0),
38778  *ptr_g = is_gray?0:data(0,0,0,1),
38779  *ptr_b = is_gray?0:data(0,0,0,2),
38780  *ptr_a = !is_alpha?0:data(0,0,0,is_gray?1:3);
38781  switch (bit_depth) {
38782  case 8 : {
38783  cimg_forY(*this,y) {
38784  const unsigned char *ptrs = (unsigned char*)imgData[y];
38785  cimg_forX(*this,x) {
38786  *(ptr_r++) = (T)*(ptrs++);
38787  if (ptr_g) *(ptr_g++) = (T)*(ptrs++); else ++ptrs;
38788  if (ptr_b) *(ptr_b++) = (T)*(ptrs++); else ++ptrs;
38789  if (ptr_a) *(ptr_a++) = (T)*(ptrs++); else ++ptrs;
38790  }
38791  }
38792  } break;
38793  case 16 : {
38794  cimg_forY(*this,y) {
38795  const unsigned short *ptrs = (unsigned short*)(imgData[y]);
38796  if (!cimg::endianness()) cimg::invert_endianness(ptrs,4*_width);
38797  cimg_forX(*this,x) {
38798  *(ptr_r++) = (T)*(ptrs++);
38799  if (ptr_g) *(ptr_g++) = (T)*(ptrs++); else ++ptrs;
38800  if (ptr_b) *(ptr_b++) = (T)*(ptrs++); else ++ptrs;
38801  if (ptr_a) *(ptr_a++) = (T)*(ptrs++); else ++ptrs;
38802  }
38803  }
38804  } break;
38805  }
38806  png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
38807 
38808  // Deallocate Image Read Memory
38809  cimg_forY(*this,n) delete[] imgData[n];
38810  delete[] imgData;
38811  if (!file) cimg::fclose(nfile);
38812  return *this;
38813 #endif
38814  }
38815 
38817 
38820  CImg<T>& load_pnm(const char *const filename) {
38821  return _load_pnm(0,filename);
38822  }
38823 
38825  static CImg<T> get_load_pnm(const char *const filename) {
38826  return CImg<T>().load_pnm(filename);
38827  }
38828 
38830  CImg<T>& load_pnm(std::FILE *const file) {
38831  return _load_pnm(file,0);
38832  }
38833 
38835  static CImg<T> get_load_pnm(std::FILE *const file) {
38836  return CImg<T>().load_pnm(file);
38837  }
38838 
38839  CImg<T>& _load_pnm(std::FILE *const file, const char *const filename) {
38840  if (!file && !filename)
38841  throw CImgArgumentException(_cimg_instance
38842  "load_pnm(): Specified filename is (null).",
38843  cimg_instance);
38844 
38845  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
38846  unsigned int ppm_type, W, H, D = 1, colormax = 255;
38847  CImg<charT> item(16384,1,1,1,0);
38848  int err, rval, gval, bval;
38849  const long cimg_iobuffer = 12*1024*1024;
38850  while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
38851  if (std::sscanf(item," P%u",&ppm_type)!=1) {
38852  if (!file) cimg::fclose(nfile);
38853  throw CImgIOException(_cimg_instance
38854  "load_pnm(): PNM header not found in file '%s'.",
38855  cimg_instance,
38856  filename?filename:"(FILE*)");
38857  }
38858  while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
38859  if ((err=std::sscanf(item," %u %u %u %u",&W,&H,&D,&colormax))<2) {
38860  if (!file) cimg::fclose(nfile);
38861  throw CImgIOException(_cimg_instance
38862  "load_pnm(): WIDTH and HEIGHT fields undefined in file '%s'.",
38863  cimg_instance,
38864  filename?filename:"(FILE*)");
38865  }
38866  if (ppm_type!=1 && ppm_type!=4) {
38867  if (err==2 || (err==3 && (ppm_type==5 || ppm_type==7 || ppm_type==8 || ppm_type==9))) {
38868  while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
38869  if (std::sscanf(item,"%u",&colormax)!=1)
38870  cimg::warn(_cimg_instance
38871  "load_pnm(): COLORMAX field is undefined in file '%s'.",
38872  cimg_instance,
38873  filename?filename:"(FILE*)");
38874  } else { colormax = D; D = 1; }
38875  }
38876  std::fgetc(nfile);
38877 
38878  switch (ppm_type) {
38879  case 1 : { // 2d b&w ascii.
38880  assign(W,H,1,1);
38881  T* ptrd = _data;
38882  cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)(rval?0:255); else break; }
38883  } break;
38884  case 2 : { // 2d grey ascii.
38885  assign(W,H,1,1);
38886  T* ptrd = _data;
38887  cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)rval; else break; }
38888  } break;
38889  case 3 : { // 2d color ascii.
38890  assign(W,H,1,3);
38891  T *ptrd = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
38892  cimg_forXY(*this,x,y) {
38893  if (std::fscanf(nfile,"%d %d %d",&rval,&gval,&bval)==3) {
38894  *(ptrd++) = (T)rval; *(ptr_g++) = (T)gval; *(ptr_b++) = (T)bval;
38895  } else break;
38896  }
38897  } break;
38898  case 4 : { // 2d b&w binary (support 3D PINK extension).
38899  CImg<ucharT> raw;
38900  assign(W,H,D,1);
38901  T *ptrd = data(0,0,0,0);
38902  unsigned int w = 0, h = 0, d = 0;
38903  for (long to_read = (long)((W/8 + (W%8?1:0))*H*D); to_read>0; ) {
38904  raw.assign(cimg::min(to_read,cimg_iobuffer));
38905  cimg::fread(raw._data,raw._width,nfile);
38906  to_read-=raw._width;
38907  const unsigned char *ptrs = raw._data;
38908  unsigned char mask = 0, val = 0;
38909  for (unsigned long off = (unsigned long)raw._width; off || mask; mask>>=1) {
38910  if (!mask) { if (off--) val = *(ptrs++); mask = 128; }
38911  *(ptrd++) = (T)((val&mask)?0:255);
38912  if (++w==W) { w = 0; mask = 0; if (++h==H) { h = 0; if (++d==D) break; }}
38913  }
38914  }
38915  } break;
38916  case 5 : case 7 : { // 2d/3d grey binary (support 3D PINK extension).
38917  if (colormax<256) { // 8 bits.
38918  CImg<ucharT> raw;
38919  assign(W,H,D,1);
38920  T *ptrd = data(0,0,0,0);
38921  for (long to_read = (long)size(); to_read>0; ) {
38922  raw.assign(cimg::min(to_read,cimg_iobuffer));
38923  cimg::fread(raw._data,raw._width,nfile);
38924  to_read-=raw._width;
38925  const unsigned char *ptrs = raw._data;
38926  for (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
38927  }
38928  } else { // 16 bits.
38929  CImg<ushortT> raw;
38930  assign(W,H,D,1);
38931  T *ptrd = data(0,0,0,0);
38932  for (long to_read = (long)size(); to_read>0; ) {
38933  raw.assign(cimg::min(to_read,cimg_iobuffer/2));
38934  cimg::fread(raw._data,raw._width,nfile);
38935  if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width);
38936  to_read-=raw._width;
38937  const unsigned short *ptrs = raw._data;
38938  for (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
38939  }
38940  }
38941  } break;
38942  case 6 : { // 2d color binary.
38943  if (colormax<256) { // 8 bits.
38944  CImg<ucharT> raw;
38945  assign(W,H,1,3);
38946  T
38947  *ptr_r = data(0,0,0,0),
38948  *ptr_g = data(0,0,0,1),
38949  *ptr_b = data(0,0,0,2);
38950  for (long to_read = (long)size(); to_read>0; ) {
38951  raw.assign(cimg::min(to_read,cimg_iobuffer));
38952  cimg::fread(raw._data,raw._width,nfile);
38953  to_read-=raw._width;
38954  const unsigned char *ptrs = raw._data;
38955  for (unsigned long off = (unsigned long)raw._width/3; off; --off) {
38956  *(ptr_r++) = (T)*(ptrs++);
38957  *(ptr_g++) = (T)*(ptrs++);
38958  *(ptr_b++) = (T)*(ptrs++);
38959  }
38960  }
38961  } else { // 16 bits.
38962  CImg<ushortT> raw;
38963  assign(W,H,1,3);
38964  T
38965  *ptr_r = data(0,0,0,0),
38966  *ptr_g = data(0,0,0,1),
38967  *ptr_b = data(0,0,0,2);
38968  for (long to_read = (int)size(); to_read>0; ) {
38969  raw.assign(cimg::min(to_read,cimg_iobuffer/2));
38970  cimg::fread(raw._data,raw._width,nfile);
38971  if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width);
38972  to_read-=raw._width;
38973  const unsigned short *ptrs = raw._data;
38974  for (unsigned long off = (unsigned long)raw._width/3; off; --off) {
38975  *(ptr_r++) = (T)*(ptrs++);
38976  *(ptr_g++) = (T)*(ptrs++);
38977  *(ptr_b++) = (T)*(ptrs++);
38978  }
38979  }
38980  }
38981  } break;
38982  case 8 : { // 2d/3d grey binary with int32 integers (PINK extension).
38983  CImg<intT> raw;
38984  assign(W,H,D,1);
38985  T *ptrd = data(0,0,0,0);
38986  for (long to_read = (long)size(); to_read>0; ) {
38987  raw.assign(cimg::min(to_read,cimg_iobuffer));
38988  cimg::fread(raw._data,raw._width,nfile);
38989  to_read-=raw._width;
38990  const int *ptrs = raw._data;
38991  for (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
38992  }
38993  } break;
38994  case 9 : { // 2d/3d grey binary with float values (PINK extension).
38995  CImg<floatT> raw;
38996  assign(W,H,D,1);
38997  T *ptrd = data(0,0,0,0);
38998  for (long to_read = (long)size(); to_read>0; ) {
38999  raw.assign(cimg::min(to_read,cimg_iobuffer));
39000  cimg::fread(raw._data,raw._width,nfile);
39001  to_read-=raw._width;
39002  const float *ptrs = raw._data;
39003  for (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
39004  }
39005  } break;
39006  default :
39007  assign();
39008  if (!file) cimg::fclose(nfile);
39009  throw CImgIOException(_cimg_instance
39010  "load_pnm(): PNM type 'P%d' found, but type is not supported.",
39011  cimg_instance,
39012  filename?filename:"(FILE*)",ppm_type);
39013  }
39014  if (!file) cimg::fclose(nfile);
39015  return *this;
39016  }
39017 
39019 
39022  CImg<T>& load_pfm(const char *const filename) {
39023  return _load_pfm(0,filename);
39024  }
39025 
39027  static CImg<T> get_load_pfm(const char *const filename) {
39028  return CImg<T>().load_pfm(filename);
39029  }
39030 
39032  CImg<T>& load_pfm(std::FILE *const file) {
39033  return _load_pfm(file,0);
39034  }
39035 
39037  static CImg<T> get_load_pfm(std::FILE *const file) {
39038  return CImg<T>().load_pfm(file);
39039  }
39040 
39041  CImg<T>& _load_pfm(std::FILE *const file, const char *const filename) {
39042  if (!file && !filename)
39043  throw CImgArgumentException(_cimg_instance
39044  "load_pfm(): Specified filename is (null).",
39045  cimg_instance);
39046 
39047  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
39048  char pfm_type;
39049  CImg<charT> item(16384,1,1,1,0);
39050  int W = 0, H = 0, err = 0;
39051  double scale = 0;
39052  while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
39053  if (std::sscanf(item," P%c",&pfm_type)!=1) {
39054  if (!file) cimg::fclose(nfile);
39055  throw CImgIOException(_cimg_instance
39056  "load_pfm(): PFM header not found in file '%s'.",
39057  cimg_instance,
39058  filename?filename:"(FILE*)");
39059  }
39060  while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
39061  if ((err=std::sscanf(item," %d %d",&W,&H))<2) {
39062  if (!file) cimg::fclose(nfile);
39063  throw CImgIOException(_cimg_instance
39064  "load_pfm(): WIDTH and HEIGHT fields are undefined in file '%s'.",
39065  cimg_instance,
39066  filename?filename:"(FILE*)");
39067  }
39068  if (err==2) {
39069  while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
39070  if (std::sscanf(item,"%lf",&scale)!=1)
39071  cimg::warn(_cimg_instance
39072  "load_pfm(): SCALE field is undefined in file '%s'.",
39073  cimg_instance,
39074  filename?filename:"(FILE*)");
39075  }
39076  std::fgetc(nfile);
39077  const bool is_color = (pfm_type=='F'), is_inverted = (scale>0)!=cimg::endianness();
39078  if (is_color) {
39079  assign(W,H,1,3,0);
39080  CImg<floatT> buf(3*W);
39081  T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
39082  cimg_forY(*this,y) {
39083  cimg::fread(buf._data,3*W,nfile);
39084  if (is_inverted) cimg::invert_endianness(buf._data,3*W);
39085  const float *ptrs = buf._data;
39086  cimg_forX(*this,x) {
39087  *(ptr_r++) = (T)*(ptrs++);
39088  *(ptr_g++) = (T)*(ptrs++);
39089  *(ptr_b++) = (T)*(ptrs++);
39090  }
39091  }
39092  } else {
39093  assign(W,H,1,1,0);
39094  CImg<floatT> buf(W);
39095  T *ptrd = data(0,0,0,0);
39096  cimg_forY(*this,y) {
39097  cimg::fread(buf._data,W,nfile);
39098  if (is_inverted) cimg::invert_endianness(buf._data,W);
39099  const float *ptrs = buf._data;
39100  cimg_forX(*this,x) *(ptrd++) = (T)*(ptrs++);
39101  }
39102  }
39103  if (!file) cimg::fclose(nfile);
39104  return mirror('y'); // Most of the .pfm files are flipped along the y-axis.
39105  }
39106 
39108 
39113  CImg<T>& load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
39114  return _load_rgb(0,filename,dimw,dimh);
39115  }
39116 
39118  static CImg<T> get_load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
39119  return CImg<T>().load_rgb(filename,dimw,dimh);
39120  }
39121 
39123  CImg<T>& load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
39124  return _load_rgb(file,0,dimw,dimh);
39125  }
39126 
39128  static CImg<T> get_load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
39129  return CImg<T>().load_rgb(file,dimw,dimh);
39130  }
39131 
39132  CImg<T>& _load_rgb(std::FILE *const file, const char *const filename,
39133  const unsigned int dimw, const unsigned int dimh) {
39134  if (!file && !filename)
39135  throw CImgArgumentException(_cimg_instance
39136  "load_rgb(): Specified filename is (null).",
39137  cimg_instance);
39138 
39139  if (!dimw || !dimh) return assign();
39140  const long cimg_iobuffer = 12*1024*1024;
39141  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
39142  CImg<ucharT> raw;
39143  assign(dimw,dimh,1,3);
39144  T
39145  *ptr_r = data(0,0,0,0),
39146  *ptr_g = data(0,0,0,1),
39147  *ptr_b = data(0,0,0,2);
39148  for (long to_read = (long)size(); to_read>0; ) {
39149  raw.assign(cimg::min(to_read,cimg_iobuffer));
39150  cimg::fread(raw._data,raw._width,nfile);
39151  to_read-=raw._width;
39152  const unsigned char *ptrs = raw._data;
39153  for (unsigned long off = raw._width/3UL; off; --off) {
39154  *(ptr_r++) = (T)*(ptrs++);
39155  *(ptr_g++) = (T)*(ptrs++);
39156  *(ptr_b++) = (T)*(ptrs++);
39157  }
39158  }
39159  if (!file) cimg::fclose(nfile);
39160  return *this;
39161  }
39162 
39164 
39169  CImg<T>& load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
39170  return _load_rgba(0,filename,dimw,dimh);
39171  }
39172 
39174  static CImg<T> get_load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
39175  return CImg<T>().load_rgba(filename,dimw,dimh);
39176  }
39177 
39179  CImg<T>& load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
39180  return _load_rgba(file,0,dimw,dimh);
39181  }
39182 
39184  static CImg<T> get_load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
39185  return CImg<T>().load_rgba(file,dimw,dimh);
39186  }
39187 
39188  CImg<T>& _load_rgba(std::FILE *const file, const char *const filename,
39189  const unsigned int dimw, const unsigned int dimh) {
39190  if (!file && !filename)
39191  throw CImgArgumentException(_cimg_instance
39192  "load_rgba(): Specified filename is (null).",
39193  cimg_instance);
39194 
39195  if (!dimw || !dimh) return assign();
39196  const long cimg_iobuffer = 12*1024*1024;
39197  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
39198  CImg<ucharT> raw;
39199  assign(dimw,dimh,1,4);
39200  T
39201  *ptr_r = data(0,0,0,0),
39202  *ptr_g = data(0,0,0,1),
39203  *ptr_b = data(0,0,0,2),
39204  *ptr_a = data(0,0,0,3);
39205  for (long to_read = (long)size(); to_read>0; ) {
39206  raw.assign(cimg::min(to_read,cimg_iobuffer));
39207  cimg::fread(raw._data,raw._width,nfile);
39208  to_read-=raw._width;
39209  const unsigned char *ptrs = raw._data;
39210  for (unsigned long off = raw._width/4UL; off; --off) {
39211  *(ptr_r++) = (T)*(ptrs++);
39212  *(ptr_g++) = (T)*(ptrs++);
39213  *(ptr_b++) = (T)*(ptrs++);
39214  *(ptr_a++) = (T)*(ptrs++);
39215  }
39216  }
39217  if (!file) cimg::fclose(nfile);
39218  return *this;
39219  }
39220 
39222 
39236  CImg<T>& load_tiff(const char *const filename,
39237  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
39238  const unsigned int step_frame=1) {
39239  if (!filename)
39240  throw CImgArgumentException(_cimg_instance
39241  "load_tiff(): Specified filename is (null).",
39242  cimg_instance);
39243 
39244  const unsigned int
39245  nfirst_frame = first_frame<last_frame?first_frame:last_frame,
39246  nstep_frame = step_frame?step_frame:1;
39247  unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
39248 
39249 #ifndef cimg_use_tiff
39250  if (nfirst_frame || nlast_frame!=~0U || nstep_frame>1)
39251  throw CImgArgumentException(_cimg_instance
39252  "load_tiff(): Unable to read sub-images from file '%s' unless libtiff is enabled.",
39253  cimg_instance,
39254  filename);
39255  return load_other(filename);
39256 #else
39257  TIFF *tif = TIFFOpen(filename,"r");
39258  if (tif) {
39259  unsigned int nb_images = 0;
39260  do ++nb_images; while (TIFFReadDirectory(tif));
39261  if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images))
39262  cimg::warn(_cimg_instance
39263  "load_tiff(): File '%s' contains %u image(s) while specified frame range is [%u,%u] (step %u).",
39264  cimg_instance,
39265  filename,nb_images,nfirst_frame,nlast_frame,nstep_frame);
39266 
39267  if (nfirst_frame>=nb_images) return assign();
39268  if (nlast_frame>=nb_images) nlast_frame = nb_images-1;
39269  TIFFSetDirectory(tif,0);
39270  CImg<T> frame;
39271  for (unsigned int l = nfirst_frame; l<=nlast_frame; l+=nstep_frame) {
39272  frame._load_tiff(tif,l);
39273  if (l==nfirst_frame)
39274  assign(frame._width,frame._height,1+(nlast_frame-nfirst_frame)/nstep_frame,frame._spectrum);
39275  if (frame._width>_width || frame._height>_height || frame._spectrum>_spectrum)
39276  resize(cimg::max(frame._width,_width),
39277  cimg::max(frame._height,_height),-100,
39278  cimg::max(frame._spectrum,_spectrum),0);
39279  draw_image(0,0,(l-nfirst_frame)/nstep_frame,frame);
39280  }
39281  TIFFClose(tif);
39282  } else throw CImgIOException(_cimg_instance
39283  "load_tiff(): Failed to open file '%s'.",
39284  cimg_instance,
39285  filename);
39286  return *this;
39287 #endif
39288  }
39289 
39291  static CImg<T> get_load_tiff(const char *const filename,
39292  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
39293  const unsigned int step_frame=1) {
39294  return CImg<T>().load_tiff(filename,first_frame,last_frame,step_frame);
39295  }
39296 
39297  // (Original contribution by Jerome Boulanger).
39298 #ifdef cimg_use_tiff
39299  template<typename t>
39300  void _load_tiff_tiled_contig(TIFF *const tif, const uint16 samplesperpixel,
39301  const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) {
39302  t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif));
39303  if (buf) {
39304  for (unsigned int row = 0; row<ny; row+=th)
39305  for (unsigned int col = 0; col<nx; col+=tw) {
39306  if (TIFFReadTile(tif,buf,col,row,0,0)<0) {
39307  _TIFFfree(buf); TIFFClose(tif);
39308  throw CImgIOException(_cimg_instance
39309  "load_tiff(): Invalid tile in file '%s'.",
39310  cimg_instance,
39311  TIFFFileName(tif));
39312  }
39313  const t *ptr = buf;
39314  for (unsigned int rr = row; rr<cimg::min((unsigned int)(row+th),(unsigned int)ny); ++rr)
39315  for (unsigned int cc = col; cc<cimg::min((unsigned int)(col+tw),(unsigned int)nx); ++cc)
39316  for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
39317  (*this)(cc,rr,vv) = (T)(ptr[(rr-row)*th*samplesperpixel + (cc-col)*samplesperpixel + vv]);
39318  }
39319  _TIFFfree(buf);
39320  }
39321  }
39322 
39323  template<typename t>
39324  void _load_tiff_tiled_separate(TIFF *const tif, const uint16 samplesperpixel,
39325  const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) {
39326  t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif));
39327  if (buf) {
39328  for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
39329  for (unsigned int row = 0; row<ny; row+=th)
39330  for (unsigned int col = 0; col<nx; col+=tw) {
39331  if (TIFFReadTile(tif,buf,col,row,0,vv)<0) {
39332  _TIFFfree(buf); TIFFClose(tif);
39333  throw CImgIOException(_cimg_instance
39334  "load_tiff(): Invalid tile in file '%s'.",
39335  cimg_instance,
39336  TIFFFileName(tif));
39337  }
39338  const t *ptr = buf;
39339  for (unsigned int rr = row; rr<cimg::min((unsigned int)(row+th),(unsigned int)ny); ++rr)
39340  for (unsigned int cc = col; cc<cimg::min((unsigned int)(col+tw),(unsigned int)nx); ++cc)
39341  (*this)(cc,rr,vv) = (T)*(ptr++);
39342  }
39343  _TIFFfree(buf);
39344  }
39345  }
39346 
39347  template<typename t>
39348  void _load_tiff_contig(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) {
39349  t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
39350  if (buf) {
39351  uint32 row, rowsperstrip = (uint32)-1;
39352  TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
39353  for (row = 0; row<ny; row+= rowsperstrip) {
39354  uint32 nrow = (row+rowsperstrip>ny?ny-row:rowsperstrip);
39355  tstrip_t strip = TIFFComputeStrip(tif, row, 0);
39356  if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) {
39357  _TIFFfree(buf); TIFFClose(tif);
39358  throw CImgIOException(_cimg_instance
39359  "load_tiff(): Invalid strip in file '%s'.",
39360  cimg_instance,
39361  TIFFFileName(tif));
39362  }
39363  const t *ptr = buf;
39364  for (unsigned int rr = 0; rr<nrow; ++rr)
39365  for (unsigned int cc = 0; cc<nx; ++cc)
39366  for (unsigned int vv = 0; vv<samplesperpixel; ++vv) (*this)(cc,row+rr,vv) = (T)*(ptr++);
39367  }
39368  _TIFFfree(buf);
39369  }
39370  }
39371 
39372  template<typename t>
39373  void _load_tiff_separate(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) {
39374  t *buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
39375  if (buf) {
39376  uint32 row, rowsperstrip = (uint32)-1;
39377  TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
39378  for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
39379  for (row = 0; row<ny; row+= rowsperstrip) {
39380  uint32 nrow = (row+rowsperstrip>ny?ny-row:rowsperstrip);
39381  tstrip_t strip = TIFFComputeStrip(tif, row, vv);
39382  if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) {
39383  _TIFFfree(buf); TIFFClose(tif);
39384  throw CImgIOException(_cimg_instance
39385  "load_tiff(): Invalid strip in file '%s'.",
39386  cimg_instance,
39387  TIFFFileName(tif));
39388  }
39389  const t *ptr = buf;
39390  for (unsigned int rr = 0;rr<nrow; ++rr)
39391  for (unsigned int cc = 0; cc<nx; ++cc)
39392  (*this)(cc,row+rr,vv) = (T)*(ptr++);
39393  }
39394  _TIFFfree(buf);
39395  }
39396  }
39397 
39398  CImg<T>& _load_tiff(TIFF *const tif, const unsigned int directory) {
39399  if (!TIFFSetDirectory(tif,directory)) return assign();
39400  uint16 samplesperpixel = 1, bitspersample = 8, photo = 0;
39401  uint16 sampleformat = 1;
39402  uint32 nx = 1, ny = 1;
39403  const char *const filename = TIFFFileName(tif);
39404  const bool is_spp = (bool)TIFFGetField(tif,TIFFTAG_SAMPLESPERPIXEL,&samplesperpixel);
39405  TIFFGetField(tif,TIFFTAG_IMAGEWIDTH,&nx);
39406  TIFFGetField(tif,TIFFTAG_IMAGELENGTH,&ny);
39407  TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sampleformat);
39408  TIFFGetFieldDefaulted(tif,TIFFTAG_BITSPERSAMPLE,&bitspersample);
39409  TIFFGetField(tif,TIFFTAG_PHOTOMETRIC,&photo);
39410 
39411  const unsigned int spectrum = is_spp?samplesperpixel:photo==3?3:1;
39412  assign(nx,ny,1,spectrum);
39413 
39414  if (photo>=3 && sampleformat==1 && bitspersample==8 && (samplesperpixel==3 || samplesperpixel==4)) {
39415  // Special case for unsigned color images.
39416  uint32 *const raster = (uint32*)_TIFFmalloc(nx*ny*sizeof(uint32));
39417  if (!raster) {
39418  _TIFFfree(raster); TIFFClose(tif);
39419  throw CImgException(_cimg_instance
39420  "load_tiff(): Failed to allocate memory (%s) for file '%s'.",
39421  cimg_instance,
39422  cimg::strbuffersize(nx*ny*sizeof(uint32)),filename);
39423  }
39424  TIFFReadRGBAImage(tif,nx,ny,raster,0);
39425  switch (spectrum) {
39426  case 1 : {
39427  cimg_forXY(*this,x,y) (*this)(x,y) = (T)(float)((raster[nx*(ny-1-y)+x] + 128)/257);
39428  } break;
39429  case 3 : {
39430  cimg_forXY(*this,x,y) {
39431  (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny-1-y)+x]);
39432  (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny-1-y)+x]);
39433  (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny-1-y)+x]);
39434  }
39435  } break;
39436  case 4 : {
39437  cimg_forXY(*this,x,y) {
39438  (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny-1-y)+x]);
39439  (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny-1-y)+x]);
39440  (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny-1-y)+x]);
39441  (*this)(x,y,3) = (T)(float)TIFFGetA(raster[nx*(ny-1-y)+x]);
39442  }
39443  } break;
39444  }
39445  _TIFFfree(raster);
39446  } else { // Other cases.
39447  uint16 config;
39448  TIFFGetField(tif,TIFFTAG_PLANARCONFIG,&config);
39449  if (TIFFIsTiled(tif)) {
39450  uint32 tw = 1, th = 1;
39451  TIFFGetField(tif,TIFFTAG_TILEWIDTH,&tw);
39452  TIFFGetField(tif,TIFFTAG_TILELENGTH,&th);
39453  if (config==PLANARCONFIG_CONTIG) switch (bitspersample) {
39454  case 8 : {
39455  if (sampleformat==SAMPLEFORMAT_UINT)
39456  _load_tiff_tiled_contig<unsigned char>(tif,samplesperpixel,nx,ny,tw,th);
39457  else _load_tiff_tiled_contig<signed char>(tif,samplesperpixel,nx,ny,tw,th);
39458  } break;
39459  case 16 :
39460  if (sampleformat==SAMPLEFORMAT_UINT)
39461  _load_tiff_tiled_contig<unsigned short>(tif,samplesperpixel,nx,ny,tw,th);
39462  else _load_tiff_tiled_contig<short>(tif,samplesperpixel,nx,ny,tw,th);
39463  break;
39464  case 32 :
39465  if (sampleformat==SAMPLEFORMAT_UINT)
39466  _load_tiff_tiled_contig<unsigned int>(tif,samplesperpixel,nx,ny,tw,th);
39467  else if (sampleformat==SAMPLEFORMAT_INT)
39468  _load_tiff_tiled_contig<int>(tif,samplesperpixel,nx,ny,tw,th);
39469  else _load_tiff_tiled_contig<float>(tif,samplesperpixel,nx,ny,tw,th);
39470  break;
39471  } else switch (bitspersample) {
39472  case 8 :
39473  if (sampleformat==SAMPLEFORMAT_UINT)
39474  _load_tiff_tiled_separate<unsigned char>(tif,samplesperpixel,nx,ny,tw,th);
39475  else _load_tiff_tiled_separate<signed char>(tif,samplesperpixel,nx,ny,tw,th);
39476  break;
39477  case 16 :
39478  if (sampleformat==SAMPLEFORMAT_UINT)
39479  _load_tiff_tiled_separate<unsigned short>(tif,samplesperpixel,nx,ny,tw,th);
39480  else _load_tiff_tiled_separate<short>(tif,samplesperpixel,nx,ny,tw,th);
39481  break;
39482  case 32 :
39483  if (sampleformat==SAMPLEFORMAT_UINT)
39484  _load_tiff_tiled_separate<unsigned int>(tif,samplesperpixel,nx,ny,tw,th);
39485  else if (sampleformat==SAMPLEFORMAT_INT)
39486  _load_tiff_tiled_separate<int>(tif,samplesperpixel,nx,ny,tw,th);
39487  else _load_tiff_tiled_separate<float>(tif,samplesperpixel,nx,ny,tw,th);
39488  break;
39489  }
39490  } else {
39491  if (config==PLANARCONFIG_CONTIG) switch (bitspersample) {
39492  case 8 :
39493  if (sampleformat==SAMPLEFORMAT_UINT)
39494  _load_tiff_contig<unsigned char>(tif,samplesperpixel,nx,ny);
39495  else _load_tiff_contig<signed char>(tif,samplesperpixel,nx,ny);
39496  break;
39497  case 16 :
39498  if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned short>(tif,samplesperpixel,nx,ny);
39499  else _load_tiff_contig<short>(tif,samplesperpixel,nx,ny);
39500  break;
39501  case 32 :
39502  if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned int>(tif,samplesperpixel,nx,ny);
39503  else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig<int>(tif,samplesperpixel,nx,ny);
39504  else _load_tiff_contig<float>(tif,samplesperpixel,nx,ny);
39505  break;
39506  } else switch (bitspersample) {
39507  case 8 :
39508  if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned char>(tif,samplesperpixel,nx,ny);
39509  else _load_tiff_separate<signed char>(tif,samplesperpixel,nx,ny);
39510  break;
39511  case 16 :
39512  if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned short>(tif,samplesperpixel,nx,ny);
39513  else _load_tiff_separate<short>(tif,samplesperpixel,nx,ny);
39514  break;
39515  case 32 :
39516  if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned int>(tif,samplesperpixel,nx,ny);
39517  else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate<int>(tif,samplesperpixel,nx,ny);
39518  else _load_tiff_separate<float>(tif,samplesperpixel,nx,ny);
39519  break;
39520  }
39521  }
39522  }
39523  return *this;
39524  }
39525 #endif
39526 
39528 
39531  // (Original code by Haz-Edine Assemlal).
39532  CImg<T>& load_minc2(const char *const filename) {
39533  if (!filename)
39534  throw CImgArgumentException(_cimg_instance
39535  "load_minc2(): Specified filename is (null).",
39536  cimg_instance);
39537 #ifndef cimg_use_minc2
39538  return load_other(filename);
39539 #else
39540  minc::minc_1_reader rdr;
39541  rdr.open(filename);
39542  assign(rdr.ndim(1)?rdr.ndim(1):1,
39543  rdr.ndim(2)?rdr.ndim(2):1,
39544  rdr.ndim(3)?rdr.ndim(3):1,
39545  rdr.ndim(4)?rdr.ndim(4):1);
39546  if(typeid(T)==typeid(unsigned char))
39547  rdr.setup_read_byte();
39548  else if(typeid(T)==typeid(int))
39549  rdr.setup_read_int();
39550  else if(typeid(T)==typeid(double))
39551  rdr.setup_read_double();
39552  else
39553  rdr.setup_read_float();
39554  minc::load_standard_volume(rdr, this->_data);
39555  return *this;
39556 #endif
39557  }
39558 
39560  static CImg<T> get_load_minc2(const char *const filename) {
39561  return CImg<T>().load_analyze(filename);
39562  }
39563 
39565 
39569  CImg<T>& load_analyze(const char *const filename, float *const voxel_size=0) {
39570  return _load_analyze(0,filename,voxel_size);
39571  }
39572 
39574  static CImg<T> get_load_analyze(const char *const filename, float *const voxel_size=0) {
39575  return CImg<T>().load_analyze(filename,voxel_size);
39576  }
39577 
39579  CImg<T>& load_analyze(std::FILE *const file, float *const voxel_size=0) {
39580  return _load_analyze(file,0,voxel_size);
39581  }
39582 
39584  static CImg<T> get_load_analyze(std::FILE *const file, float *const voxel_size=0) {
39585  return CImg<T>().load_analyze(file,voxel_size);
39586  }
39587 
39588  CImg<T>& _load_analyze(std::FILE *const file, const char *const filename, float *const voxel_size=0) {
39589  if (!file && !filename)
39590  throw CImgArgumentException(_cimg_instance
39591  "load_analyze(): Specified filename is (null).",
39592  cimg_instance);
39593 
39594  std::FILE *nfile_header = 0, *nfile = 0;
39595  if (!file) {
39596  char body[1024] = { 0 };
39597  const char *const ext = cimg::split_filename(filename,body);
39598  if (!cimg::strcasecmp(ext,"hdr")) { // File is an Analyze header file.
39599  nfile_header = cimg::fopen(filename,"rb");
39600  std::sprintf(body + std::strlen(body),".img");
39601  nfile = cimg::fopen(body,"rb");
39602  } else if (!cimg::strcasecmp(ext,"img")) { // File is an Analyze data file.
39603  nfile = cimg::fopen(filename,"rb");
39604  std::sprintf(body + std::strlen(body),".hdr");
39605  nfile_header = cimg::fopen(body,"rb");
39606  } else nfile_header = nfile = cimg::fopen(filename,"rb"); // File is a Niftii file.
39607  } else nfile_header = nfile = file; // File is a Niftii file.
39608  if (!nfile || !nfile_header)
39609  throw CImgIOException(_cimg_instance
39610  "load_analyze(): Invalid Analyze7.5 or NIFTI header in file '%s'.",
39611  cimg_instance,
39612  filename?filename:"(FILE*)");
39613 
39614  // Read header.
39615  bool endian = false;
39616  unsigned int header_size;
39617  cimg::fread(&header_size,1,nfile_header);
39618  if (!header_size)
39619  throw CImgIOException(_cimg_instance
39620  "load_analyze(): Invalid zero-sized header in file '%s'.",
39621  cimg_instance,
39622  filename?filename:"(FILE*)");
39623 
39624  if (header_size>=4096) { endian = true; cimg::invert_endianness(header_size); }
39625  unsigned char *const header = new unsigned char[header_size];
39626  cimg::fread(header+4,header_size-4,nfile_header);
39627  if (!file && nfile_header!=nfile) cimg::fclose(nfile_header);
39628  if (endian) {
39629  cimg::invert_endianness((short*)(header+40),5);
39630  cimg::invert_endianness((short*)(header+70),1);
39631  cimg::invert_endianness((short*)(header+72),1);
39632  cimg::invert_endianness((float*)(header+76),4);
39633  cimg::invert_endianness((float*)(header+112),1);
39634  }
39635  unsigned short *dim = (unsigned short*)(header+40), dimx = 1, dimy = 1, dimz = 1, dimv = 1;
39636  if (!dim[0])
39637  cimg::warn(_cimg_instance
39638  "load_analyze(): File '%s' defines an image with zero dimensions.",
39639  cimg_instance,
39640  filename?filename:"(FILE*)");
39641 
39642  if (dim[0]>4)
39643  cimg::warn(_cimg_instance
39644  "load_analyze(): File '%s' defines an image with %u dimensions, reading only the 4 first.",
39645  cimg_instance,
39646  filename?filename:"(FILE*)",dim[0]);
39647 
39648  if (dim[0]>=1) dimx = dim[1];
39649  if (dim[0]>=2) dimy = dim[2];
39650  if (dim[0]>=3) dimz = dim[3];
39651  if (dim[0]>=4) dimv = dim[4];
39652  float scalefactor = *(float*)(header+112); if (scalefactor==0) scalefactor=1;
39653  const unsigned short datatype = *(short*)(header+70);
39654  if (voxel_size) {
39655  const float *vsize = (float*)(header+76);
39656  voxel_size[0] = vsize[1]; voxel_size[1] = vsize[2]; voxel_size[2] = vsize[3];
39657  }
39658  delete[] header;
39659 
39660  // Read pixel data.
39661  assign(dimx,dimy,dimz,dimv);
39662  switch (datatype) {
39663  case 2 : {
39664  unsigned char *const buffer = new unsigned char[dimx*dimy*dimz*dimv];
39665  cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile);
39666  cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
39667  delete[] buffer;
39668  } break;
39669  case 4 : {
39670  short *const buffer = new short[dimx*dimy*dimz*dimv];
39671  cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile);
39672  if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv);
39673  cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
39674  delete[] buffer;
39675  } break;
39676  case 8 : {
39677  int *const buffer = new int[dimx*dimy*dimz*dimv];
39678  cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile);
39679  if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv);
39680  cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
39681  delete[] buffer;
39682  } break;
39683  case 16 : {
39684  float *const buffer = new float[dimx*dimy*dimz*dimv];
39685  cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile);
39686  if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv);
39687  cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
39688  delete[] buffer;
39689  } break;
39690  case 64 : {
39691  double *const buffer = new double[dimx*dimy*dimz*dimv];
39692  cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile);
39693  if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv);
39694  cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
39695  delete[] buffer;
39696  } break;
39697  default :
39698  if (!file) cimg::fclose(nfile);
39699  throw CImgIOException(_cimg_instance
39700  "load_analyze(): Unable to load datatype %d in file '%s'",
39701  cimg_instance,
39702  datatype,filename?filename:"(FILE*)");
39703  }
39704  if (!file) cimg::fclose(nfile);
39705  return *this;
39706  }
39707 
39709 
39714  CImg<T>& load_cimg(const char *const filename, const char axis='z', const float align=0) {
39715  CImgList<T> list;
39716  list.load_cimg(filename);
39717  if (list._width==1) return list[0].move_to(*this);
39718  return assign(list.get_append(axis,align));
39719  }
39720 
39722  static CImg<T> get_load_cimg(const char *const filename, const char axis='z', const float align=0) {
39723  return CImg<T>().load_cimg(filename,axis,align);
39724  }
39725 
39727  CImg<T>& load_cimg(std::FILE *const file, const char axis='z', const float align=0) {
39728  CImgList<T> list;
39729  list.load_cimg(file);
39730  if (list._width==1) return list[0].move_to(*this);
39731  return assign(list.get_append(axis,align));
39732  }
39733 
39735  static CImg<T> get_load_cimg(std::FILE *const file, const char axis='z', const float align=0) {
39736  return CImg<T>().load_cimg(file,axis,align);
39737  }
39738 
39740 
39755  CImg<T>& load_cimg(const char *const filename,
39756  const unsigned int n0, const unsigned int n1,
39757  const unsigned int x0, const unsigned int y0,
39758  const unsigned int z0, const unsigned int c0,
39759  const unsigned int x1, const unsigned int y1,
39760  const unsigned int z1, const unsigned int c1,
39761  const char axis='z', const float align=0) {
39762  CImgList<T> list;
39763  list.load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
39764  if (list._width==1) return list[0].move_to(*this);
39765  return assign(list.get_append(axis,align));
39766  }
39767 
39769  static CImg<T> get_load_cimg(const char *const filename,
39770  const unsigned int n0, const unsigned int n1,
39771  const unsigned int x0, const unsigned int y0,
39772  const unsigned int z0, const unsigned int c0,
39773  const unsigned int x1, const unsigned int y1,
39774  const unsigned int z1, const unsigned int c1,
39775  const char axis='z', const float align=0) {
39776  return CImg<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align);
39777  }
39778 
39780  CImg<T>& load_cimg(std::FILE *const file,
39781  const unsigned int n0, const unsigned int n1,
39782  const unsigned int x0, const unsigned int y0,
39783  const unsigned int z0, const unsigned int c0,
39784  const unsigned int x1, const unsigned int y1,
39785  const unsigned int z1, const unsigned int c1,
39786  const char axis='z', const float align=0) {
39787  CImgList<T> list;
39788  list.load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
39789  if (list._width==1) return list[0].move_to(*this);
39790  return assign(list.get_append(axis,align));
39791  }
39792 
39794  static CImg<T> get_load_cimg(std::FILE *const file,
39795  const unsigned int n0, const unsigned int n1,
39796  const unsigned int x0, const unsigned int y0,
39797  const unsigned int z0, const unsigned int c0,
39798  const unsigned int x1, const unsigned int y1,
39799  const unsigned int z1, const unsigned int c1,
39800  const char axis='z', const float align=0) {
39801  return CImg<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align);
39802  }
39803 
39805 
39809  CImg<T>& load_inr(const char *const filename, float *const voxel_size=0) {
39810  return _load_inr(0,filename,voxel_size);
39811  }
39812 
39814  static CImg<T> get_load_inr(const char *const filename, float *const voxel_size=0) {
39815  return CImg<T>().load_inr(filename,voxel_size);
39816  }
39817 
39819  CImg<T>& load_inr(std::FILE *const file, float *const voxel_size=0) {
39820  return _load_inr(file,0,voxel_size);
39821  }
39822 
39824  static CImg<T> get_load_inr(std::FILE *const file, float *voxel_size=0) {
39825  return CImg<T>().load_inr(file,voxel_size);
39826  }
39827 
39828  static void _load_inr_header(std::FILE *file, int out[8], float *const voxel_size) {
39829  char item[1024] = { 0 }, tmp1[64] = { 0 }, tmp2[64] = { 0 };
39830  out[0] = std::fscanf(file,"%63s",item);
39831  out[0] = out[1] = out[2] = out[3] = out[5] = 1; out[4] = out[6] = out[7] = -1;
39832  if(cimg::strncasecmp(item,"#INRIMAGE-4#{",13)!=0)
39833  throw CImgIOException("CImg<%s>::load_inr(): INRIMAGE-4 header not found.",
39834  pixel_type());
39835 
39836  while (std::fscanf(file," %63[^\n]%*c",item)!=EOF && std::strncmp(item,"##}",3)) {
39837  std::sscanf(item," XDIM%*[^0-9]%d",out);
39838  std::sscanf(item," YDIM%*[^0-9]%d",out+1);
39839  std::sscanf(item," ZDIM%*[^0-9]%d",out+2);
39840  std::sscanf(item," VDIM%*[^0-9]%d",out+3);
39841  std::sscanf(item," PIXSIZE%*[^0-9]%d",out+6);
39842  if (voxel_size) {
39843  std::sscanf(item," VX%*[^0-9.+-]%f",voxel_size);
39844  std::sscanf(item," VY%*[^0-9.+-]%f",voxel_size+1);
39845  std::sscanf(item," VZ%*[^0-9.+-]%f",voxel_size+2);
39846  }
39847  if (std::sscanf(item," CPU%*[ =]%s",tmp1)) out[7]=cimg::strncasecmp(tmp1,"sun",3)?0:1;
39848  switch (std::sscanf(item," TYPE%*[ =]%s %s",tmp1,tmp2)) {
39849  case 0 : break;
39850  case 2 : out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0; std::strncpy(tmp1,tmp2,sizeof(tmp1)-1);
39851  case 1 :
39852  if (!cimg::strncasecmp(tmp1,"int",3) || !cimg::strncasecmp(tmp1,"fixed",5)) out[4] = 0;
39853  if (!cimg::strncasecmp(tmp1,"float",5) || !cimg::strncasecmp(tmp1,"double",6)) out[4] = 1;
39854  if (!cimg::strncasecmp(tmp1,"packed",6)) out[4] = 2;
39855  if (out[4]>=0) break;
39856  default :
39857  throw CImgIOException("CImg<%s>::load_inr(): Invalid pixel type '%s' defined in header.",
39858  pixel_type(),
39859  tmp2);
39860  }
39861  }
39862  if(out[0]<0 || out[1]<0 || out[2]<0 || out[3]<0)
39863  throw CImgIOException("CImg<%s>::load_inr(): Invalid dimensions (%d,%d,%d,%d) defined in header.",
39864  pixel_type(),
39865  out[0],out[1],out[2],out[3]);
39866  if(out[4]<0 || out[5]<0)
39867  throw CImgIOException("CImg<%s>::load_inr(): Incomplete pixel type defined in header.",
39868  pixel_type());
39869  if(out[6]<0)
39870  throw CImgIOException("CImg<%s>::load_inr(): Incomplete PIXSIZE field defined in header.",
39871  pixel_type());
39872  if(out[7]<0)
39873  throw CImgIOException("CImg<%s>::load_inr(): Big/Little Endian coding type undefined in header.",
39874  pixel_type());
39875  }
39876 
39877  CImg<T>& _load_inr(std::FILE *const file, const char *const filename, float *const voxel_size) {
39878 #define _cimg_load_inr_case(Tf,sign,pixsize,Ts) \
39879  if (!loaded && fopt[6]==pixsize && fopt[4]==Tf && fopt[5]==sign) { \
39880  Ts *xval, *const val = new Ts[fopt[0]*fopt[3]]; \
39881  cimg_forYZ(*this,y,z) { \
39882  cimg::fread(val,fopt[0]*fopt[3],nfile); \
39883  if (fopt[7]!=endian) cimg::invert_endianness(val,fopt[0]*fopt[3]); \
39884  xval = val; cimg_forX(*this,x) cimg_forC(*this,c) (*this)(x,y,z,c) = (T)*(xval++); \
39885  } \
39886  delete[] val; \
39887  loaded = true; \
39888  }
39889 
39890  if (!file && !filename)
39891  throw CImgArgumentException(_cimg_instance
39892  "load_inr(): Specified filename is (null).",
39893  cimg_instance);
39894 
39895  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
39896  int fopt[8], endian=cimg::endianness()?1:0;
39897  bool loaded = false;
39898  if (voxel_size) voxel_size[0] = voxel_size[1] = voxel_size[2] = 1;
39899  _load_inr_header(nfile,fopt,voxel_size);
39900  assign(fopt[0],fopt[1],fopt[2],fopt[3]);
39901  _cimg_load_inr_case(0,0,8,unsigned char);
39902  _cimg_load_inr_case(0,1,8,char);
39903  _cimg_load_inr_case(0,0,16,unsigned short);
39904  _cimg_load_inr_case(0,1,16,short);
39905  _cimg_load_inr_case(0,0,32,unsigned int);
39906  _cimg_load_inr_case(0,1,32,int);
39907  _cimg_load_inr_case(1,0,32,float);
39908  _cimg_load_inr_case(1,1,32,float);
39909  _cimg_load_inr_case(1,0,64,double);
39910  _cimg_load_inr_case(1,1,64,double);
39911  if (!loaded) {
39912  if (!file) cimg::fclose(nfile);
39913  throw CImgIOException(_cimg_instance
39914  "load_inr(): Unknown pixel type defined in file '%s'.",
39915  cimg_instance,
39916  filename?filename:"(FILE*)");
39917  }
39918  if (!file) cimg::fclose(nfile);
39919  return *this;
39920  }
39921 
39923 
39926  CImg<T>& load_exr(const char *const filename) {
39927  if (!filename)
39928  throw CImgArgumentException(_cimg_instance
39929  "load_exr(): Specified filename is (null).",
39930  cimg_instance);
39931 
39932 #ifndef cimg_use_openexr
39933  return load_other(filename);
39934 #else
39935  Imf::RgbaInputFile file(filename);
39936  Imath::Box2i dw = file.dataWindow();
39937  const int
39938  inwidth = dw.max.x - dw.min.x + 1,
39939  inheight = dw.max.y - dw.min.y + 1;
39940  Imf::Array2D<Imf::Rgba> pixels;
39941  pixels.resizeErase(inheight,inwidth);
39942  file.setFrameBuffer(&pixels[0][0] - dw.min.x - dw.min.y*inwidth, 1, inwidth);
39943  file.readPixels(dw.min.y, dw.max.y);
39944  assign(inwidth,inheight,1,4);
39945  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);
39946  cimg_forXY(*this,x,y) {
39947  *(ptr_r++) = (T)pixels[y][x].r;
39948  *(ptr_g++) = (T)pixels[y][x].g;
39949  *(ptr_b++) = (T)pixels[y][x].b;
39950  *(ptr_a++) = (T)pixels[y][x].a;
39951  }
39952  return *this;
39953 #endif
39954  }
39955 
39957  static CImg<T> get_load_exr(const char *const filename) {
39958  return CImg<T>().load_exr(filename);
39959  }
39960 
39962 
39965  CImg<T>& load_pandore(const char *const filename) {
39966  return _load_pandore(0,filename);
39967  }
39968 
39970  static CImg<T> get_load_pandore(const char *const filename) {
39971  return CImg<T>().load_pandore(filename);
39972  }
39973 
39975  CImg<T>& load_pandore(std::FILE *const file) {
39976  return _load_pandore(file,0);
39977  }
39978 
39980  static CImg<T> get_load_pandore(std::FILE *const file) {
39981  return CImg<T>().load_pandore(file);
39982  }
39983 
39984  CImg<T>& _load_pandore(std::FILE *const file, const char *const filename) {
39985 #define __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,ndim,stype) \
39986  cimg::fread(dims,nbdim,nfile); \
39987  if (endian) cimg::invert_endianness(dims,nbdim); \
39988  assign(nwidth,nheight,ndepth,ndim); \
39989  const unsigned int siz = size(); \
39990  stype *buffer = new stype[siz]; \
39991  cimg::fread(buffer,siz,nfile); \
39992  if (endian) cimg::invert_endianness(buffer,siz); \
39993  T *ptrd = _data; \
39994  cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); \
39995  buffer-=siz; \
39996  delete[] buffer
39997 
39998 #define _cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1,stype2,stype3,ltype) { \
39999  if (sizeof(stype1)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1); } \
40000  else if (sizeof(stype2)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype2); } \
40001  else if (sizeof(stype3)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype3); } \
40002  else throw CImgIOException(_cimg_instance \
40003  "load_pandore(): Unknown pixel datatype in file '%s'.", \
40004  cimg_instance, \
40005  filename?filename:"(FILE*)"); }
40006 
40007  if (!file && !filename)
40008  throw CImgArgumentException(_cimg_instance
40009  "load_pandore(): Specified filename is (null).",
40010  cimg_instance);
40011 
40012  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
40013  char header[32] = { 0 };
40014  cimg::fread(header,12,nfile);
40015  if (cimg::strncasecmp("PANDORE",header,7)) {
40016  if (!file) cimg::fclose(nfile);
40017  throw CImgIOException(_cimg_instance
40018  "load_pandore(): PANDORE header not found in file '%s'.",
40019  cimg_instance,
40020  filename?filename:"(FILE*)");
40021  }
40022  unsigned int imageid, dims[8] = { 0 };
40023  cimg::fread(&imageid,1,nfile);
40024  const bool endian = (imageid>255);
40025  if (endian) cimg::invert_endianness(imageid);
40026  cimg::fread(header,20,nfile);
40027 
40028  switch (imageid) {
40029  case 2: _cimg_load_pandore_case(2,dims[1],1,1,1,unsigned char,unsigned char,unsigned char,1); break;
40030  case 3: _cimg_load_pandore_case(2,dims[1],1,1,1,long,int,short,4); break;
40031  case 4: _cimg_load_pandore_case(2,dims[1],1,1,1,double,float,float,4); break;
40032  case 5: _cimg_load_pandore_case(3,dims[2],dims[1],1,1,unsigned char,unsigned char,unsigned char,1); break;
40033  case 6: _cimg_load_pandore_case(3,dims[2],dims[1],1,1,long,int,short,4); break;
40034  case 7: _cimg_load_pandore_case(3,dims[2],dims[1],1,1,double,float,float,4); break;
40035  case 8: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,unsigned char,unsigned char,unsigned char,1); break;
40036  case 9: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,long,int,short,4); break;
40037  case 10: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,double,float,float,4); break;
40038  case 11 : { // Region 1d
40039  cimg::fread(dims,3,nfile);
40040  if (endian) cimg::invert_endianness(dims,3);
40041  assign(dims[1],1,1,1);
40042  const unsigned siz = size();
40043  if (dims[2]<256) {
40044  unsigned char *buffer = new unsigned char[siz];
40045  cimg::fread(buffer,siz,nfile);
40046  T *ptrd = _data;
40047  cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
40048  buffer-=siz;
40049  delete[] buffer;
40050  } else {
40051  if (dims[2]<65536) {
40052  unsigned short *buffer = new unsigned short[siz];
40053  cimg::fread(buffer,siz,nfile);
40054  if (endian) cimg::invert_endianness(buffer,siz);
40055  T *ptrd = _data;
40056  cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
40057  buffer-=siz;
40058  delete[] buffer;
40059  } else {
40060  unsigned int *buffer = new unsigned int[siz];
40061  cimg::fread(buffer,siz,nfile);
40062  if (endian) cimg::invert_endianness(buffer,siz);
40063  T *ptrd = _data;
40064  cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
40065  buffer-=siz;
40066  delete[] buffer;
40067  }
40068  }
40069  }
40070  break;
40071  case 12 : { // Region 2d
40072  cimg::fread(dims,4,nfile);
40073  if (endian) cimg::invert_endianness(dims,4);
40074  assign(dims[2],dims[1],1,1);
40075  const unsigned int siz = size();
40076  if (dims[3]<256) {
40077  unsigned char *buffer = new unsigned char[siz];
40078  cimg::fread(buffer,siz,nfile);
40079  T *ptrd = _data;
40080  cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
40081  buffer-=siz;
40082  delete[] buffer;
40083  } else {
40084  if (dims[3]<65536) {
40085  unsigned short *buffer = new unsigned short[siz];
40086  cimg::fread(buffer,siz,nfile);
40087  if (endian) cimg::invert_endianness(buffer,siz);
40088  T *ptrd = _data;
40089  cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
40090  buffer-=siz;
40091  delete[] buffer;
40092  } else {
40093  unsigned long *buffer = new unsigned long[siz];
40094  cimg::fread(buffer,siz,nfile);
40095  if (endian) cimg::invert_endianness(buffer,siz);
40096  T *ptrd = _data;
40097  cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
40098  buffer-=siz;
40099  delete[] buffer;
40100  }
40101  }
40102  }
40103  break;
40104  case 13 : { // Region 3d
40105  cimg::fread(dims,5,nfile);
40106  if (endian) cimg::invert_endianness(dims,5);
40107  assign(dims[3],dims[2],dims[1],1);
40108  const unsigned int siz = size();
40109  if (dims[4]<256) {
40110  unsigned char *buffer = new unsigned char[siz];
40111  cimg::fread(buffer,siz,nfile);
40112  T *ptrd = _data;
40113  cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
40114  buffer-=siz;
40115  delete[] buffer;
40116  } else {
40117  if (dims[4]<65536) {
40118  unsigned short *buffer = new unsigned short[siz];
40119  cimg::fread(buffer,siz,nfile);
40120  if (endian) cimg::invert_endianness(buffer,siz);
40121  T *ptrd = _data;
40122  cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
40123  buffer-=siz;
40124  delete[] buffer;
40125  } else {
40126  unsigned int *buffer = new unsigned int[siz];
40127  cimg::fread(buffer,siz,nfile);
40128  if (endian) cimg::invert_endianness(buffer,siz);
40129  T *ptrd = _data;
40130  cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
40131  buffer-=siz;
40132  delete[] buffer;
40133  }
40134  }
40135  }
40136  break;
40137  case 16: _cimg_load_pandore_case(4,dims[2],dims[1],1,3,unsigned char,unsigned char,unsigned char,1); break;
40138  case 17: _cimg_load_pandore_case(4,dims[2],dims[1],1,3,long,int,short,4); break;
40139  case 18: _cimg_load_pandore_case(4,dims[2],dims[1],1,3,double,float,float,4); break;
40140  case 19: _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,unsigned char,unsigned char,unsigned char,1); break;
40141  case 20: _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,long,int,short,4); break;
40142  case 21: _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,double,float,float,4); break;
40143  case 22: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned char,unsigned char,unsigned char,1); break;
40144  case 23: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],long,int,short,4);
40145  case 24: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned long,unsigned int,unsigned short,4); break;
40146  case 25: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],double,float,float,4); break;
40147  case 26: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned char,unsigned char,unsigned char,1); break;
40148  case 27: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],long,int,short,4); break;
40149  case 28: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned long,unsigned int,unsigned short,4); break;
40150  case 29: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],double,float,float,4); break;
40151  case 30: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned char,unsigned char,unsigned char,1);
40152  break;
40153  case 31: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],long,int,short,4); break;
40154  case 32: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned long,unsigned int,unsigned short,4);
40155  break;
40156  case 33: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],double,float,float,4); break;
40157  case 34 : { // Points 1d
40158  int ptbuf[4] = { 0 };
40159  cimg::fread(ptbuf,1,nfile);
40160  if (endian) cimg::invert_endianness(ptbuf,1);
40161  assign(1); (*this)(0) = (T)ptbuf[0];
40162  } break;
40163  case 35 : { // Points 2d
40164  int ptbuf[4] = { 0 };
40165  cimg::fread(ptbuf,2,nfile);
40166  if (endian) cimg::invert_endianness(ptbuf,2);
40167  assign(2); (*this)(0) = (T)ptbuf[1]; (*this)(1) = (T)ptbuf[0];
40168  } break;
40169  case 36 : { // Points 3d
40170  int ptbuf[4] = { 0 };
40171  cimg::fread(ptbuf,3,nfile);
40172  if (endian) cimg::invert_endianness(ptbuf,3);
40173  assign(3); (*this)(0) = (T)ptbuf[2]; (*this)(1) = (T)ptbuf[1]; (*this)(2) = (T)ptbuf[0];
40174  } break;
40175  default :
40176  if (!file) cimg::fclose(nfile);
40177  throw CImgIOException(_cimg_instance
40178  "load_pandore(): Unable to load data with ID_type %u in file '%s'.",
40179  cimg_instance,
40180  imageid,filename?filename:"(FILE*)");
40181  }
40182  if (!file) cimg::fclose(nfile);
40183  return *this;
40184  }
40185 
40187 
40192  CImg<T>& load_parrec(const char *const filename, const char axis='c', const float align=0) {
40193  CImgList<T> list;
40194  list.load_parrec(filename);
40195  if (list._width==1) return list[0].move_to(*this);
40196  return assign(list.get_append(axis,align));
40197  }
40198 
40200  static CImg<T> get_load_parrec(const char *const filename, const char axis='c', const float align=0) {
40201  return CImg<T>().load_parrec(filename,axis,align);
40202  }
40203 
40205 
40215  CImg<T>& load_raw(const char *const filename,
40216  const unsigned int size_x=0, const unsigned int size_y=1,
40217  const unsigned int size_z=1, const unsigned int size_c=1,
40218  const bool is_multiplexed=false, const bool invert_endianness=false,
40219  const unsigned long offset=0) {
40220  return _load_raw(0,filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
40221  }
40222 
40224  static CImg<T> get_load_raw(const char *const filename,
40225  const unsigned int size_x=0, const unsigned int size_y=1,
40226  const unsigned int size_z=1, const unsigned int size_c=1,
40227  const bool is_multiplexed=false, const bool invert_endianness=false,
40228  const unsigned long offset=0) {
40229  return CImg<T>().load_raw(filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
40230  }
40231 
40233  CImg<T>& load_raw(std::FILE *const file,
40234  const unsigned int size_x=0, const unsigned int size_y=1,
40235  const unsigned int size_z=1, const unsigned int size_c=1,
40236  const bool is_multiplexed=false, const bool invert_endianness=false,
40237  const unsigned long offset=0) {
40238  return _load_raw(file,0,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
40239  }
40240 
40242  static CImg<T> get_load_raw(std::FILE *const file,
40243  const unsigned int size_x=0, const unsigned int size_y=1,
40244  const unsigned int size_z=1, const unsigned int size_c=1,
40245  const bool is_multiplexed=false, const bool invert_endianness=false,
40246  const unsigned long offset=0) {
40247  return CImg<T>().load_raw(file,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
40248  }
40249 
40250  CImg<T>& _load_raw(std::FILE *const file, const char *const filename,
40251  const unsigned int size_x, const unsigned int size_y,
40252  const unsigned int size_z, const unsigned int size_c,
40253  const bool is_multiplexed, const bool invert_endianness,
40254  const unsigned long offset) {
40255  if (!file && !filename)
40256  throw CImgArgumentException(_cimg_instance
40257  "load_raw(): Specified filename is (null).",
40258  cimg_instance);
40259  unsigned int siz = size_x*size_y*size_z*size_c,
40260  _size_x = size_x, _size_y = size_y, _size_z = size_z, _size_c = size_c;
40261  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
40262  if (!siz) { // Retrieve file size.
40263  const long fpos = std::ftell(nfile);
40264  if (fpos<0) throw CImgArgumentException(_cimg_instance
40265  "load_raw(): Cannot determine size of input file '%s'.",
40266  cimg_instance,filename?filename:"(FILE*)");
40267  std::fseek(nfile,0,SEEK_END);
40268  siz = _size_y = (unsigned int)std::ftell(nfile)/sizeof(T);
40269  _size_x = _size_z = _size_c = 1;
40270  std::fseek(nfile,fpos,SEEK_SET);
40271  }
40272  std::fseek(nfile,(long)offset,SEEK_SET);
40273  assign(_size_x,_size_y,_size_z,_size_c,0);
40274  if (!is_multiplexed || size_c==1) {
40275  cimg::fread(_data,siz,nfile);
40276  if (invert_endianness) cimg::invert_endianness(_data,siz);
40277  } else {
40278  CImg<T> buf(1,1,1,_size_c);
40279  cimg_forXYZ(*this,x,y,z) {
40280  cimg::fread(buf._data,_size_c,nfile);
40281  if (invert_endianness) cimg::invert_endianness(buf._data,_size_c);
40282  set_vector_at(buf,x,y,z);
40283  }
40284  }
40285  if (!file) cimg::fclose(nfile);
40286  return *this;
40287  }
40288 
40290 
40300  CImg<T>& load_ffmpeg(const char *const filename,
40301  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
40302  const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false,
40303  const char axis='z', const float align=0) {
40304  return get_load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format,resume,axis,align).move_to(*this);
40305  }
40306 
40308  static CImg<T> get_load_ffmpeg(const char *const filename,
40309  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
40310  const unsigned int step_frame=1, const bool pixel_format=true,
40311  const bool resume=false,
40312  const char axis='z', const float align=0) {
40313  return CImgList<T>().load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format,resume).
40314  get_append(axis,align);
40315  }
40316 
40318 
40328  CImg<T>& load_yuv(const char *const filename,
40329  const unsigned int size_x, const unsigned int size_y=1,
40330  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
40331  const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
40332  return get_load_yuv(filename,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this);
40333  }
40334 
40336  static CImg<T> get_load_yuv(const char *const filename,
40337  const unsigned int size_x, const unsigned int size_y=1,
40338  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
40339  const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
40340  return CImgList<T>().load_yuv(filename,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb).get_append(axis);
40341  }
40342 
40344  CImg<T>& load_yuv(std::FILE *const file,
40345  const unsigned int size_x, const unsigned int size_y=1,
40346  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
40347  const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
40348  return get_load_yuv(file,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this);
40349  }
40350 
40352  static CImg<T> get_load_yuv(std::FILE *const file,
40353  const unsigned int size_x, const unsigned int size_y=1,
40354  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
40355  const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
40356  return CImgList<T>().load_yuv(file,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb).get_append(axis);
40357  }
40358 
40360 
40365  template<typename tf, typename tc>
40366  CImg<T>& load_off(CImgList<tf>& primitives, CImgList<tc>& colors, const char *const filename) {
40367  return _load_off(primitives,colors,0,filename);
40368  }
40369 
40371  template<typename tf, typename tc>
40372  static CImg<T> get_load_off(CImgList<tf>& primitives, CImgList<tc>& colors, const char *const filename) {
40373  return CImg<T>().load_off(primitives,colors,filename);
40374  }
40375 
40377  template<typename tf, typename tc>
40378  CImg<T>& load_off(CImgList<tf>& primitives, CImgList<tc>& colors, std::FILE *const file) {
40379  return _load_off(primitives,colors,file,0);
40380  }
40381 
40383  template<typename tf, typename tc>
40384  static CImg<T> get_load_off(CImgList<tf>& primitives, CImgList<tc>& colors, std::FILE *const file) {
40385  return CImg<T>().load_off(primitives,colors,file);
40386  }
40387 
40388  template<typename tf, typename tc>
40389  CImg<T>& _load_off(CImgList<tf>& primitives, CImgList<tc>& colors,
40390  std::FILE *const file, const char *const filename) {
40391  if (!file && !filename)
40392  throw CImgArgumentException(_cimg_instance
40393  "load_off(): Specified filename is (null).",
40394  cimg_instance);
40395 
40396  std::FILE *const nfile = file?file:cimg::fopen(filename,"r");
40397  unsigned int nb_points = 0, nb_primitives = 0, nb_read = 0;
40398  char line[256] = { 0 };
40399  int err;
40400 
40401  // Skip comments, and read magic string OFF
40402  do { err = std::fscanf(nfile,"%255[^\n] ",line); } while (!err || (err==1 && *line=='#'));
40403  if (cimg::strncasecmp(line,"OFF",3) && cimg::strncasecmp(line,"COFF",4)) {
40404  if (!file) cimg::fclose(nfile);
40405  throw CImgIOException(_cimg_instance
40406  "load_off(): OFF header not found in file '%s'.",
40407  cimg_instance,
40408  filename?filename:"(FILE*)");
40409  }
40410  do { err = std::fscanf(nfile,"%255[^\n] ",line); } while (!err || (err==1 && *line=='#'));
40411  if ((err = std::sscanf(line,"%u%u%*[^\n] ",&nb_points,&nb_primitives))!=2) {
40412  if (!file) cimg::fclose(nfile);
40413  throw CImgIOException(_cimg_instance
40414  "load_off(): Invalid number of vertices or primitives specified in file '%s'.",
40415  cimg_instance,
40416  filename?filename:"(FILE*)");
40417  }
40418 
40419  // Read points data
40420  assign(nb_points,3);
40421  float X = 0, Y = 0, Z = 0;
40422  cimg_forX(*this,l) {
40423  do { err = std::fscanf(nfile,"%255[^\n] ",line); } while (!err || (err==1 && *line=='#'));
40424  if ((err = std::sscanf(line,"%f%f%f%*[^\n] ",&X,&Y,&Z))!=3) {
40425  if (!file) cimg::fclose(nfile);
40426  throw CImgIOException(_cimg_instance
40427  "load_off(): Failed to read vertex %u/%u in file '%s'.",
40428  cimg_instance,
40429  l+1,nb_points,filename?filename:"(FILE*)");
40430  }
40431  (*this)(l,0) = (T)X; (*this)(l,1) = (T)Y; (*this)(l,2) = (T)Z;
40432  }
40433 
40434  // Read primitive data
40435  primitives.assign();
40436  colors.assign();
40437  bool stop_flag = false;
40438  while (!stop_flag) {
40439  float c0 = 0.7f, c1 = 0.7f, c2 = 0.7f;
40440  unsigned int prim = 0, i0 = 0, i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, i6 = 0, i7 = 0;
40441  *line = 0;
40442  if ((err = std::fscanf(nfile,"%u",&prim))!=1) stop_flag = true;
40443  else {
40444  ++nb_read;
40445  switch (prim) {
40446  case 1 : {
40447  if ((err = std::fscanf(nfile,"%u%255[^\n] ",&i0,line))<2) {
40448  cimg::warn(_cimg_instance
40449  "load_off(): Failed to read primitive %u/%u from file '%s'.",
40450  cimg_instance,
40451  nb_read,nb_primitives,filename?filename:"(FILE*)");
40452 
40453  err = std::fscanf(nfile,"%*[^\n] ");
40454  } else {
40455  err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
40456  CImg<tf>::vector(i0).move_to(primitives);
40457  CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
40458  }
40459  } break;
40460  case 2 : {
40461  if ((err = std::fscanf(nfile,"%u%u%255[^\n] ",&i0,&i1,line))<2) {
40462  cimg::warn(_cimg_instance
40463  "load_off(): Failed to read primitive %u/%u from file '%s'.",
40464  cimg_instance,
40465  nb_read,nb_primitives,filename?filename:"(FILE*)");
40466 
40467  err = std::fscanf(nfile,"%*[^\n] ");
40468  } else {
40469  err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
40470  CImg<tf>::vector(i0,i1).move_to(primitives);
40471  CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
40472  }
40473  } break;
40474  case 3 : {
40475  if ((err = std::fscanf(nfile,"%u%u%u%255[^\n] ",&i0,&i1,&i2,line))<3) {
40476  cimg::warn(_cimg_instance
40477  "load_off(): Failed to read primitive %u/%u from file '%s'.",
40478  cimg_instance,
40479  nb_read,nb_primitives,filename?filename:"(FILE*)");
40480 
40481  err = std::fscanf(nfile,"%*[^\n] ");
40482  } else {
40483  err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
40484  CImg<tf>::vector(i0,i2,i1).move_to(primitives);
40485  CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
40486  }
40487  } break;
40488  case 4 : {
40489  if ((err = std::fscanf(nfile,"%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,line))<4) {
40490  cimg::warn(_cimg_instance
40491  "load_off(): Failed to read primitive %u/%u from file '%s'.",
40492  cimg_instance,
40493  nb_read,nb_primitives,filename?filename:"(FILE*)");
40494 
40495  err = std::fscanf(nfile,"%*[^\n] ");
40496  } else {
40497  err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
40498  CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
40499  CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
40500  }
40501  } break;
40502  case 5 : {
40503  if ((err = std::fscanf(nfile,"%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,line))<5) {
40504  cimg::warn(_cimg_instance
40505  "load_off(): Failed to read primitive %u/%u from file '%s'.",
40506  cimg_instance,
40507  nb_read,nb_primitives,filename?filename:"(FILE*)");
40508 
40509  err = std::fscanf(nfile,"%*[^\n] ");
40510  } else {
40511  err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
40512  CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
40513  CImg<tf>::vector(i0,i4,i3).move_to(primitives);
40514  colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
40515  ++nb_primitives;
40516  }
40517  } break;
40518  case 6 : {
40519  if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,line))<6) {
40520  cimg::warn(_cimg_instance
40521  "load_off(): Failed to read primitive %u/%u from file '%s'.",
40522  cimg_instance,
40523  nb_read,nb_primitives,filename?filename:"(FILE*)");
40524 
40525  err = std::fscanf(nfile,"%*[^\n] ");
40526  } else {
40527  err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
40528  CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
40529  CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives);
40530  colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
40531  ++nb_primitives;
40532  }
40533  } break;
40534  case 7 : {
40535  if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,line))<7) {
40536  cimg::warn(_cimg_instance
40537  "load_off(): Failed to read primitive %u/%u from file '%s'.",
40538  cimg_instance,
40539  nb_read,nb_primitives,filename?filename:"(FILE*)");
40540 
40541  err = std::fscanf(nfile,"%*[^\n] ");
40542  } else {
40543  err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
40544  CImg<tf>::vector(i0,i4,i3,i1).move_to(primitives);
40545  CImg<tf>::vector(i0,i6,i5,i4).move_to(primitives);
40546  CImg<tf>::vector(i3,i2,i1).move_to(primitives);
40547  colors.insert(3,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
40548  ++(++nb_primitives);
40549  }
40550  } break;
40551  case 8 : {
40552  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) {
40553  cimg::warn(_cimg_instance
40554  "load_off(): Failed to read primitive %u/%u from file '%s'.",
40555  cimg_instance,
40556  nb_read,nb_primitives,filename?filename:"(FILE*)");
40557 
40558  err = std::fscanf(nfile,"%*[^\n] ");
40559  } else {
40560  err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
40561  CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
40562  CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives);
40563  CImg<tf>::vector(i0,i7,i6,i5).move_to(primitives);
40564  colors.insert(3,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
40565  ++(++nb_primitives);
40566  }
40567  } break;
40568  default :
40569  cimg::warn(_cimg_instance
40570  "load_off(): Failed to read primitive %u/%u (%u vertices) from file '%s'.",
40571  cimg_instance,
40572  nb_read,nb_primitives,prim,filename?filename:"(FILE*)");
40573 
40574  err = std::fscanf(nfile,"%*[^\n] ");
40575  }
40576  }
40577  }
40578  if (!file) cimg::fclose(nfile);
40579  if (primitives._width!=nb_primitives)
40580  cimg::warn(_cimg_instance
40581  "load_off(): Only %u/%u primitives read from file '%s'.",
40582  cimg_instance,
40583  primitives._width,nb_primitives,filename?filename:"(FILE*)");
40584  return *this;
40585  }
40586 
40588 
40593  CImg<T>& load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) {
40594  return get_load_ffmpeg_external(filename,axis,align).move_to(*this);
40595  }
40596 
40598  static CImg<T> get_load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) {
40599  return CImgList<T>().load_ffmpeg_external(filename).get_append(axis,align);
40600  }
40601 
40603 
40609  CImg<T>& load_gif_external(const char *const filename,
40610  const char axis='z', const float align=0) {
40611  return get_load_gif_external(filename,axis,align).move_to(*this);
40612  }
40613 
40615  static CImg<T> get_load_gif_external(const char *const filename,
40616  const char axis='z', const float align=0) {
40617  return CImgList<T>().load_gif_external(filename).get_append(axis,align);
40618  }
40619 
40621 
40624  CImg<T>& load_graphicsmagick_external(const char *const filename) {
40625  if (!filename)
40626  throw CImgArgumentException(_cimg_instance
40627  "load_graphicsmagick_external(): Specified filename is (null).",
40628  cimg_instance);
40629  std::fclose(cimg::fopen(filename,"rb")); // Check if file exists.
40630  char command[1024] = { 0 }, filetmp[512] = { 0 };
40631  std::FILE *file = 0;
40632  const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
40633 #if cimg_OS==1
40634  cimg_snprintf(command,sizeof(command),"%s convert \"%s\" pnm:-",
40635  cimg::graphicsmagick_path(),s_filename.data());
40636  file = popen(command,"r");
40637  if (file) {
40638  const unsigned int omode = cimg::exception_mode();
40639  cimg::exception_mode() = 0;
40640  try { load_pnm(file); } catch (...) {
40641  pclose(file);
40642  cimg::exception_mode() = omode;
40643  throw CImgIOException(_cimg_instance
40644  "load_graphicsmagick_external(): Failed to load file '%s' with external command 'gm'.",
40645  cimg_instance,
40646  filename);
40647  }
40648  pclose(file);
40649  return *this;
40650  }
40651 #endif
40652  do {
40653  cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.pnm",
40654  cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
40655  if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
40656  } while (file);
40657  cimg_snprintf(command,sizeof(command),"%s convert \"%s\" \"%s\"",
40658  cimg::graphicsmagick_path(),s_filename.data(),
40659  CImg<charT>::string(filetmp)._system_strescape().data());
40661  if (!(file = std::fopen(filetmp,"rb"))) {
40662  cimg::fclose(cimg::fopen(filename,"r"));
40663  throw CImgIOException(_cimg_instance
40664  "load_graphicsmagick_external(): Failed to load file '%s' with external command 'gm'.",
40665  cimg_instance,
40666  filename);
40667 
40668  } else cimg::fclose(file);
40669  load_pnm(filetmp);
40670  std::remove(filetmp);
40671  return *this;
40672  }
40673 
40675  static CImg<T> get_load_graphicsmagick_external(const char *const filename) {
40676  return CImg<T>().load_graphicsmagick_external(filename);
40677  }
40678 
40680 
40683  CImg<T>& load_gzip_external(const char *const filename) {
40684  if (!filename)
40685  throw CImgIOException(_cimg_instance
40686  "load_gzip_external(): Specified filename is (null).",
40687  cimg_instance);
40688  std::fclose(cimg::fopen(filename,"rb")); // Check if file exists.
40689  char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
40690  const char
40691  *const ext = cimg::split_filename(filename,body),
40692  *const ext2 = cimg::split_filename(body,0);
40693 
40694  std::FILE *file = 0;
40695  do {
40696  if (!cimg::strcasecmp(ext,"gz")) {
40697  if (*ext2) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",
40698  cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
40699  else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",
40700  cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
40701  } else {
40702  if (*ext) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",
40703  cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
40704  else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",
40705  cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
40706  }
40707  if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
40708  } while (file);
40709  cimg_snprintf(command,sizeof(command),"%s -c \"%s\" > \"%s\"",
40711  CImg<charT>::string(filename)._system_strescape().data(),
40712  CImg<charT>::string(filetmp)._system_strescape().data());
40713  cimg::system(command);
40714  if (!(file = std::fopen(filetmp,"rb"))) {
40715  cimg::fclose(cimg::fopen(filename,"r"));
40716  throw CImgIOException(_cimg_instance
40717  "load_gzip_external(): Failed to load file '%s' with external command 'gunzip'.",
40718  cimg_instance,
40719  filename);
40720 
40721  } else cimg::fclose(file);
40722  load(filetmp);
40723  std::remove(filetmp);
40724  return *this;
40725  }
40726 
40728  static CImg<T> get_load_gzip_external(const char *const filename) {
40729  return CImg<T>().load_gzip_external(filename);
40730  }
40731 
40733 
40736  CImg<T>& load_imagemagick_external(const char *const filename) {
40737  if (!filename)
40738  throw CImgArgumentException(_cimg_instance
40739  "load_imagemagick_external(): Specified filename is (null).",
40740  cimg_instance);
40741  std::fclose(cimg::fopen(filename,"rb")); // Check if file exists.
40742  char command[1024] = { 0 }, filetmp[512] = { 0 };
40743  std::FILE *file = 0;
40744  const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
40745 #if cimg_OS==1
40746  cimg_snprintf(command,sizeof(command),"%s%s \"%s\" pnm:-",
40748  !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"",
40749  s_filename.data());
40750  file = popen(command,"r");
40751  if (file) {
40752  const unsigned int omode = cimg::exception_mode();
40753  cimg::exception_mode() = 0;
40754  try { load_pnm(file); } catch (...) {
40755  pclose(file);
40756  cimg::exception_mode() = omode;
40757  throw CImgIOException(_cimg_instance
40758  "load_imagemagick_external(): Failed to load file '%s' with "
40759  "external command 'convert'.",
40760  cimg_instance,
40761  filename);
40762  }
40763  pclose(file);
40764  return *this;
40765  }
40766 #endif
40767  do {
40768  cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.pnm",
40769  cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
40770  if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
40771  } while (file);
40772  cimg_snprintf(command,sizeof(command),"%s%s \"%s\" \"%s\"",
40774  !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"",
40775  s_filename.data(),CImg<charT>::string(filetmp)._system_strescape().data());
40777  if (!(file = std::fopen(filetmp,"rb"))) {
40778  cimg::fclose(cimg::fopen(filename,"r"));
40779  throw CImgIOException(_cimg_instance
40780  "load_imagemagick_external(): Failed to load file '%s' with external command 'convert'.",
40781  cimg_instance,
40782  filename);
40783 
40784  } else cimg::fclose(file);
40785  load_pnm(filetmp);
40786  std::remove(filetmp);
40787  return *this;
40788  }
40789 
40791  static CImg<T> get_load_imagemagick_external(const char *const filename) {
40792  return CImg<T>().load_imagemagick_external(filename);
40793  }
40794 
40796 
40799  CImg<T>& load_medcon_external(const char *const filename) {
40800  if (!filename)
40801  throw CImgArgumentException(_cimg_instance
40802  "load_medcon_external(): Specified filename is (null).",
40803  cimg_instance);
40804  std::fclose(cimg::fopen(filename,"rb")); // Check if file exists.
40805  char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
40806  cimg::fclose(cimg::fopen(filename,"r"));
40807  std::FILE *file = 0;
40808  do {
40809  cimg_snprintf(filetmp,sizeof(filetmp),"%s.hdr",cimg::filenamerand());
40810  if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
40811  } while (file);
40812  cimg_snprintf(command,sizeof(command),"%s -w -c anlz -o \"%s\" -f \"%s\"",
40814  CImg<charT>::string(filetmp)._system_strescape().data(),
40815  CImg<charT>::string(filename)._system_strescape().data());
40816  cimg::system(command);
40817  cimg::split_filename(filetmp,body);
40818 
40819  cimg_snprintf(command,sizeof(command),"%s.hdr",body);
40820  file = std::fopen(command,"rb");
40821  if (!file) {
40822  cimg_snprintf(command,sizeof(command),"m000-%s.hdr",body);
40823  file = std::fopen(command,"rb");
40824  if (!file) {
40825  throw CImgIOException(_cimg_instance
40826  "load_medcon_external(): Failed to load file '%s' with external command 'medcon'.",
40827  cimg_instance,
40828  filename);
40829  }
40830  }
40831  cimg::fclose(file);
40832  load_analyze(command);
40833  std::remove(command);
40834  cimg::split_filename(command,body);
40835  cimg_snprintf(command,sizeof(command),"%s.img",body);
40836  std::remove(command);
40837  return *this;
40838  }
40839 
40841  static CImg<T> get_load_medcon_external(const char *const filename) {
40842  return CImg<T>().load_medcon_external(filename);
40843  }
40844 
40846 
40849  CImg<T>& load_dcraw_external(const char *const filename) {
40850  if (!filename)
40851  throw CImgArgumentException(_cimg_instance
40852  "load_dcraw_external(): Specified filename is (null).",
40853  cimg_instance);
40854  std::fclose(cimg::fopen(filename,"rb")); // Check if file exists.
40855  char command[1024] = { 0 }, filetmp[512] = { 0 };
40856  std::FILE *file = 0;
40857  const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
40858 #if cimg_OS==1
40859  cimg_snprintf(command,sizeof(command),"%s -w -4 -c \"%s\"",
40860  cimg::dcraw_path(),s_filename.data());
40861  file = popen(command,"r");
40862  if (file) {
40863  const unsigned int omode = cimg::exception_mode();
40864  cimg::exception_mode() = 0;
40865  try { load_pnm(file); } catch (...) {
40866  pclose(file);
40867  cimg::exception_mode() = omode;
40868  throw CImgIOException(_cimg_instance
40869  "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.",
40870  cimg_instance,
40871  filename);
40872  }
40873  pclose(file);
40874  return *this;
40875  }
40876 #endif
40877  do {
40878  cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.ppm",
40879  cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
40880  if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
40881  } while (file);
40882  cimg_snprintf(command,sizeof(command),"%s -w -4 -c \"%s\" > \"%s\"",
40883  cimg::dcraw_path(),s_filename.data(),CImg<charT>::string(filetmp)._system_strescape().data());
40884  cimg::system(command,cimg::dcraw_path());
40885  if (!(file = std::fopen(filetmp,"rb"))) {
40886  cimg::fclose(cimg::fopen(filename,"r"));
40887  throw CImgIOException(_cimg_instance
40888  "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.",
40889  cimg_instance,
40890  filename);
40891 
40892  } else cimg::fclose(file);
40893  load_pnm(filetmp);
40894  std::remove(filetmp);
40895  return *this;
40896  }
40897 
40899  static CImg<T> get_load_dcraw_external(const char *const filename) {
40900  return CImg<T>().load_dcraw_external(filename);
40901  }
40902 
40904 
40909  CImg<T>& load_camera(const unsigned int camera_index=0, const unsigned int skip_frames=0,
40910  const bool release_camera=true, const unsigned int capture_width=0,
40911  const unsigned int capture_height=0) {
40912 #ifdef cimg_use_opencv
40913  if (camera_index>255)
40914  throw CImgArgumentException(_cimg_instance
40915  "load_camera(): Invalid request for camera #%u "
40916  "(no more than 256 cameras can be managed).",
40917  cimg_instance,
40918  camera_index);
40919  static CvCapture *capture[256] = { 0 };
40920  if (release_camera) {
40921  if (capture[camera_index]) cvReleaseCapture(&(capture[camera_index]));
40922  capture[camera_index] = 0;
40923  return *this;
40924  }
40925  if (!capture[camera_index]) {
40926  capture[camera_index] = cvCreateCameraCapture(camera_index);
40927  if (!capture[camera_index]) {
40928  throw CImgIOException(_cimg_instance
40929  "load_camera(): Failed to initialize camera #%u.",
40930  cimg_instance,
40931  camera_index);
40932  }
40933  }
40934  if (capture_width) cvSetCaptureProperty(capture[camera_index],CV_CAP_PROP_FRAME_WIDTH,capture_width);
40935  if (capture_height) cvSetCaptureProperty(capture[camera_index],CV_CAP_PROP_FRAME_HEIGHT,capture_height);
40936  const IplImage *img = 0;
40937  for (unsigned int i = 0; i<skip_frames; ++i) img = cvQueryFrame(capture[camera_index]);
40938  img = cvQueryFrame(capture[camera_index]);
40939  if (img) {
40940  const int step = (int)(img->widthStep - 3*img->width);
40941  assign(img->width,img->height,1,3);
40942  const unsigned char* ptrs = (unsigned char*)img->imageData;
40943  T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
40944  if (step>0) cimg_forY(*this,y) {
40945  cimg_forX(*this,x) { *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++); }
40946  ptrs+=step;
40947  } else for (unsigned long siz = (unsigned long)img->width*img->height; siz; --siz) {
40948  *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++);
40949  }
40950  }
40951  return *this;
40952 #else
40953  cimg::unused(camera_index,skip_frames,release_camera,capture_width,capture_height);
40954  throw CImgIOException(_cimg_instance
40955  "load_camera(): This function requires the OpenCV library to run "
40956  "(macro 'cimg_use_opencv' must be defined).",
40957  cimg_instance);
40958 #endif
40959  }
40960 
40962  static CImg<T> get_load_camera(const unsigned int camera_index=0, const unsigned int skip_frames=0,
40963  const bool release_camera=true,
40964  const unsigned int capture_width=0, const unsigned int capture_height=0) {
40965  return CImg<T>().load_camera(camera_index,skip_frames,release_camera,capture_width,capture_height);
40966  }
40967 
40969 
40972  CImg<T>& load_other(const char *const filename) {
40973  if (!filename)
40974  throw CImgArgumentException(_cimg_instance
40975  "load_other(): Specified filename is (null).",
40976  cimg_instance);
40977 
40978  const unsigned int omode = cimg::exception_mode();
40979  cimg::exception_mode() = 0;
40980  try { load_magick(filename); }
40981  catch (CImgException&) {
40982  try { load_imagemagick_external(filename); }
40983  catch (CImgException&) {
40984  try { load_graphicsmagick_external(filename); }
40985  catch (CImgException&) {
40986  try { load_cimg(filename); }
40987  catch (CImgException&) {
40988  try {
40989  std::fclose(cimg::fopen(filename,"rb"));
40990  } catch (CImgException&) {
40991  cimg::exception_mode() = omode;
40992  throw CImgIOException(_cimg_instance
40993  "load_other(): Failed to open file '%s'.",
40994  cimg_instance,
40995  filename);
40996  }
40997  cimg::exception_mode() = omode;
40998  throw CImgIOException(_cimg_instance
40999  "load_other(): Failed to recognize format of file '%s'.",
41000  cimg_instance,
41001  filename);
41002  }
41003  }
41004  }
41005  }
41006  cimg::exception_mode() = omode;
41007  return *this;
41008  }
41009 
41011  static CImg<T> get_load_other(const char *const filename) {
41012  return CImg<T>().load_other(filename);
41013  }
41014 
41016  //---------------------------
41017  //
41019 
41020  //---------------------------
41021 
41023 
41027  const CImg<T>& print(const char *const title=0, const bool display_stats=true) const {
41028  int xm = 0, ym = 0, zm = 0, vm = 0, xM = 0, yM = 0, zM = 0, vM = 0;
41029  CImg<doubleT> st;
41030  if (!is_empty() && display_stats) {
41031  st = get_stats();
41032  xm = (int)st[4]; ym = (int)st[5], zm = (int)st[6], vm = (int)st[7];
41033  xM = (int)st[8]; yM = (int)st[9], zM = (int)st[10], vM = (int)st[11];
41034  }
41035  const unsigned long siz = size(), msiz = siz*sizeof(T), siz1 = siz-1,
41036  mdisp = msiz<8*1024?0:(msiz<8*1024*1024?1:2), width1 = _width-1;
41037 
41038  char _title[64] = { 0 };
41039  if (!title) cimg_snprintf(_title,sizeof(_title),"CImg<%s>",pixel_type());
41040 
41041  std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = (%u,%u,%u,%u) [%lu %s], %sdata%s = (%s*)%p",
41042  cimg::t_magenta,cimg::t_bold,title?title:_title,cimg::t_normal,
41043  cimg::t_bold,cimg::t_normal,(void*)this,
41044  cimg::t_bold,cimg::t_normal,_width,_height,_depth,_spectrum,
41045  mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)),
41046  mdisp==0?"b":(mdisp==1?"Kio":"Mio"),
41047  cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin());
41048  if (_data) std::fprintf(cimg::output(),"..%p (%s) = [ ",(void*)((char*)end()-1),_is_shared?"shared":"non-shared");
41049  else std::fprintf(cimg::output()," (%s) = [ ",_is_shared?"shared":"non-shared");
41050 
41051  if (!is_empty()) cimg_foroff(*this,off) {
41052  std::fprintf(cimg::output(),cimg::type<T>::format(),cimg::type<T>::format(_data[off]));
41053  if (off!=siz1) std::fprintf(cimg::output(),"%s",off%_width==width1?" ; ":" ");
41054  if (off==7 && siz>16) { off = siz1-8; std::fprintf(cimg::output(),"... "); }
41055  }
41056  if (!is_empty() && display_stats)
41057  std::fprintf(cimg::output(),
41058  " ], %smin%s = %g, %smax%s = %g, %smean%s = %g, %sstd%s = %g, %scoords_min%s = (%u,%u,%u,%u), "
41059  "%scoords_max%s = (%u,%u,%u,%u).\n",
41060  cimg::t_bold,cimg::t_normal,st[0],
41061  cimg::t_bold,cimg::t_normal,st[1],
41062  cimg::t_bold,cimg::t_normal,st[2],
41063  cimg::t_bold,cimg::t_normal,std::sqrt(st[3]),
41064  cimg::t_bold,cimg::t_normal,xm,ym,zm,vm,
41065  cimg::t_bold,cimg::t_normal,xM,yM,zM,vM);
41066  else std::fprintf(cimg::output(),"%s].\n",is_empty()?"":" ");
41067  std::fflush(cimg::output());
41068  return *this;
41069  }
41070 
41072 
41075  const CImg<T>& display(CImgDisplay& disp) const {
41076  disp.display(*this);
41077  return *this;
41078  }
41079 
41081 
41085  const CImg<T>& display(CImgDisplay &disp, const bool display_info, unsigned int *const XYZ=0) const {
41086  return _display(disp,0,display_info,XYZ,false);
41087  }
41088 
41090 
41094  const CImg<T>& display(const char *const title=0, const bool display_info=true, unsigned int *const XYZ=0) const {
41095  CImgDisplay disp;
41096  return _display(disp,title,display_info,XYZ,false);
41097  }
41098 
41099  const CImg<T>& _display(CImgDisplay &disp, const char *const title,
41100  const bool display_info, unsigned int *const XYZ,
41101  const bool exit_on_simpleclick) const {
41102  unsigned int oldw = 0, oldh = 0, _XYZ[3] = { 0 }, key = 0;
41103  int x0 = 0, y0 = 0, z0 = 0, x1 = width()-1, y1 = height()-1, z1 = depth()-1;
41104 
41105  if (!disp) {
41106  disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1);
41107  if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum);
41108  else disp.set_title("%s",title);
41109  } else if (title) disp.set_title("%s",title);
41110  disp.show().flush();
41111 
41112  const CImg<char> dtitle = CImg<char>::string(disp.title());
41113  if (display_info) print(dtitle);
41114 
41115  CImg<T> zoom;
41116  for (bool reset_view = true, resize_disp = false, is_first_select = true; !key && !disp.is_closed(); ) {
41117  if (reset_view) {
41118  if (XYZ) { _XYZ[0] = XYZ[0]; _XYZ[1] = XYZ[1]; _XYZ[2] = XYZ[2]; }
41119  else { _XYZ[0] = (x0 + x1)/2; _XYZ[1] = (y0 + y1)/2; _XYZ[2] = (z0 + z1)/2; }
41120  x0 = 0; y0 = 0; z0 = 0; x1 = width()-1; y1 = height()-1; z1 = depth()-1;
41121  oldw = disp.width(); oldh = disp.height();
41122  reset_view = false;
41123  }
41124  if (!x0 && !y0 && !z0 && x1==width()-1 && y1==height()-1 && z1==depth()-1) {
41125  if (is_empty()) zoom.assign(1,1,1,1,0); else zoom.assign();
41126  } else zoom = get_crop(x0,y0,z0,x1,y1,z1);
41127 
41128  const unsigned int
41129  dx = 1 + x1 - x0, dy = 1 + y1 - y0, dz = 1 + z1 - z0,
41130  tw = dx + (dz>1?dz:0), th = dy + (dz>1?dz:0);
41131  if (!is_empty() && !disp.is_fullscreen() && resize_disp) {
41132  const unsigned int
41133  ttw = tw*disp.width()/oldw, tth = th*disp.height()/oldh,
41134  dM = cimg::max(ttw,tth), diM = (unsigned int)cimg::max(disp.width(),disp.height()),
41135  imgw = cimg::max(16U,ttw*diM/dM), imgh = cimg::max(16U,tth*diM/dM);
41136  disp.set_fullscreen(false).resize(cimg_fitscreen(imgw,imgh,1),false);
41137  resize_disp = false;
41138  }
41139  oldw = tw; oldh = th;
41140 
41141  bool
41142  go_up = false, go_down = false, go_left = false, go_right = false,
41143  go_inc = false, go_dec = false, go_in = false, go_out = false,
41144  go_in_center = false;
41145  const CImg<T>& visu = zoom?zoom:*this;
41146 
41147  disp.set_title("%s",dtitle._data);
41148  if (_width>1 && visu._width==1) disp.set_title("%s | x=%u",disp._title,x0);
41149  if (_height>1 && visu._height==1) disp.set_title("%s | y=%u",disp._title,y0);
41150  if (_depth>1 && visu._depth==1) disp.set_title("%s | z=%u",disp._title,z0);
41151 
41152  if (!is_first_select) { _XYZ[0] = (x1-x0)/2; _XYZ[1] = (y1-y0)/2; _XYZ[2] = (z1-z0)/2; }
41153  const CImg<intT> selection = visu._get_select(disp,0,2,_XYZ,x0,y0,z0,is_first_select,_depth>1);
41154  is_first_select = false;
41155 
41156  if (disp.wheel()) {
41157  if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
41158  go_out = !(go_in = disp.wheel()>0); go_in_center = false;
41159  } else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) { go_left = !(go_right = disp.wheel()>0); }
41160  else if (disp.is_keyALT() || disp.is_keyALTGR() || _depth==1) { go_down = !(go_up = disp.wheel()>0); }
41161  disp.set_wheel();
41162  }
41163 
41164  const int
41165  sx0 = selection(0), sy0 = selection(1), sz0 = selection(2),
41166  sx1 = selection(3), sy1 = selection(4), sz1 = selection(5);
41167  if (sx0>=0 && sy0>=0 && sz0>=0 && sx1>=0 && sy1>=0 && sz1>=0) {
41168  x1 = x0 + sx1; y1 = y0 + sy1; z1 = z0 + sz1;
41169  x0+=sx0; y0+=sy0; z0+=sz0;
41170  if (sx0==sx1 && sy0==sy1 && sz0==sz1) {
41171  if (exit_on_simpleclick && (!zoom || is_empty())) break; else reset_view = true;
41172  }
41173  resize_disp = true;
41174  } else switch (key = disp.key()) {
41175 #if cimg_OS!=2
41177 #endif
41178  case 0 : case cimg::keyCTRLLEFT : case cimg::keyPAD5 : case cimg::keySHIFTLEFT :
41179 #if cimg_OS!=2
41180  case cimg::keyALTGR :
41181 #endif
41182  case cimg::keyALT : key = 0; break;
41183  case cimg::keyP : if (visu._depth>1 && (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT())) {
41184  // Special mode: play stack of frames
41185  const unsigned int
41186  w1 = visu._width*disp.width()/(visu._width+(visu._depth>1?visu._depth:0)),
41187  h1 = visu._height*disp.height()/(visu._height+(visu._depth>1?visu._depth:0));
41188  float frame_timing = 5;
41189  bool is_stopped = false;
41190  disp.set_key(key,false).set_wheel().resize(cimg_fitscreen(w1,h1,1),false); key = 0;
41191  for (unsigned int timer = 0; !key && !disp.is_closed() && !disp.button(); ) {
41192  if (disp.is_resized()) disp.resize(false);
41193  if (!timer) {
41194  visu.get_slice((int)_XYZ[2]).display(disp.set_title("%s | z=%d",dtitle.data(),_XYZ[2]));
41195  (++_XYZ[2])%=visu._depth;
41196  }
41197  if (!is_stopped) { if (++timer>(unsigned int)frame_timing) timer = 0; } else timer = ~0U;
41198  if (disp.wheel()) { frame_timing-=disp.wheel()/3.0f; disp.set_wheel(); }
41199  switch (key = disp.key()) {
41200 #if cimg_OS!=2
41201  case cimg::keyCTRLRIGHT :
41202 #endif
41203  case cimg::keyCTRLLEFT : key = 0; break;
41204  case cimg::keyPAGEUP : frame_timing-=0.3f; key = 0; break;
41205  case cimg::keyPAGEDOWN : frame_timing+=0.3f; key = 0; break;
41206  case cimg::keySPACE : is_stopped = !is_stopped; disp.set_key(key,false); key = 0; break;
41207  case cimg::keyARROWLEFT : case cimg::keyARROWUP : is_stopped = true; timer = 0; key = 0; break;
41208  case cimg::keyARROWRIGHT : case cimg::keyARROWDOWN : is_stopped = true;
41209  (_XYZ[2]+=visu._depth-2)%=visu._depth; timer = 0; key = 0; break;
41210  case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
41211  disp.set_fullscreen(false).
41212  resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
41213  CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false);
41214  disp.set_key(key,false); key = 0;
41215  } break;
41216  case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
41217  disp.set_fullscreen(false).
41218  resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false).set_key(key,false); key = 0;
41219  } break;
41220  case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
41221  disp.set_fullscreen(false).
41222  resize(cimg_fitscreen(_width,_height,_depth),false).set_key(key,false); key = 0;
41223  } break;
41224  case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
41225  disp.resize(disp.screen_width(),disp.screen_height(),false).
41226  toggle_fullscreen().set_key(key,false); key = 0;
41227  } break;
41228  }
41229  frame_timing = frame_timing<1?1:(frame_timing>39?39:frame_timing);
41230  disp.wait(20);
41231  }
41232  const unsigned int
41233  w2 = (visu._width + (visu._depth>1?visu._depth:0))*disp.width()/visu._width,
41234  h2 = (visu._height + (visu._depth>1?visu._depth:0))*disp.height()/visu._height;
41235  disp.resize(cimg_fitscreen(w2,h2,1),false).set_title(dtitle.data()).set_key().set_button().set_wheel();
41236  key = 0;
41237  } break;
41238  case cimg::keyHOME : reset_view = resize_disp = true; key = 0; break;
41239  case cimg::keyPADADD : go_in = true; go_in_center = true; key = 0; break;
41240  case cimg::keyPADSUB : go_out = true; key = 0; break;
41241  case cimg::keyARROWLEFT : case cimg::keyPAD4: go_left = true; key = 0; break;
41242  case cimg::keyARROWRIGHT : case cimg::keyPAD6: go_right = true; key = 0; break;
41243  case cimg::keyARROWUP : case cimg::keyPAD8: go_up = true; key = 0; break;
41244  case cimg::keyARROWDOWN : case cimg::keyPAD2: go_down = true; key = 0; break;
41245  case cimg::keyPAD7 : go_up = go_left = true; key = 0; break;
41246  case cimg::keyPAD9 : go_up = go_right = true; key = 0; break;
41247  case cimg::keyPAD1 : go_down = go_left = true; key = 0; break;
41248  case cimg::keyPAD3 : go_down = go_right = true; key = 0; break;
41249  case cimg::keyPAGEUP : go_inc = true; key = 0; break;
41250  case cimg::keyPAGEDOWN : go_dec = true; key = 0; break;
41251  }
41252  if (go_in) {
41253  const int
41254  mx = go_in_center?disp.width()/2:disp.mouse_x(),
41255  my = go_in_center?disp.height()/2:disp.mouse_y(),
41256  mX = mx*(_width+(_depth>1?_depth:0))/disp.width(),
41257  mY = my*(_height+(_depth>1?_depth:0))/disp.height();
41258  int X = _XYZ[0], Y = _XYZ[1], Z = _XYZ[2];
41259  if (mX<width() && mY<height()) {
41260  X = x0 + mX*(1+x1-x0)/_width; Y = y0 + mY*(1+y1-y0)/_height; Z = _XYZ[2];
41261  }
41262  if (mX<width() && mY>=height()) {
41263  X = x0 + mX*(1+x1-x0)/_width; Z = z0 + (mY-_height)*(1+z1-z0)/_depth; Y = _XYZ[1];
41264  }
41265  if (mX>=width() && mY<height()) {
41266  Y = y0 + mY*(1+y1-y0)/_height; Z = z0 + (mX-_width)*(1+z1-z0)/_depth; X = _XYZ[0];
41267  }
41268  if (x1-x0>4) { x0 = X - 3*(X-x0)/4; x1 = X + 3*(x1-X)/4; }
41269  if (y1-y0>4) { y0 = Y - 3*(Y-y0)/4; y1 = Y + 3*(y1-Y)/4; }
41270  if (z1-z0>4) { z0 = Z - 3*(Z-z0)/4; z1 = Z + 3*(z1-Z)/4; }
41271  }
41272  if (go_out) {
41273  const int
41274  delta_x = (x1-x0)/8, delta_y = (y1-y0)/8, delta_z = (z1-z0)/8,
41275  ndelta_x = delta_x?delta_x:(_width>1?1:0),
41276  ndelta_y = delta_y?delta_y:(_height>1?1:0),
41277  ndelta_z = delta_z?delta_z:(_depth>1?1:0);
41278  x0-=ndelta_x; y0-=ndelta_y; z0-=ndelta_z;
41279  x1+=ndelta_x; y1+=ndelta_y; z1+=ndelta_z;
41280  if (x0<0) { x1-=x0; x0 = 0; if (x1>=width()) x1 = width() - 1; }
41281  if (y0<0) { y1-=y0; y0 = 0; if (y1>=height()) y1 = height() - 1; }
41282  if (z0<0) { z1-=z0; z0 = 0; if (z1>=depth()) z1 = depth() - 1; }
41283  if (x1>=width()) { x0-=(x1-width()+1); x1 = width()-1; if (x0<0) x0 = 0; }
41284  if (y1>=height()) { y0-=(y1-height()+1); y1 = height()-1; if (y0<0) y0 = 0; }
41285  if (z1>=depth()) { z0-=(z1-depth()+1); z1 = depth()-1; if (z0<0) z0 = 0; }
41286  }
41287  if (go_left) {
41288  const int delta = (x1-x0)/4, ndelta = delta?delta:(_width>1?1:0);
41289  if (x0-ndelta>=0) { x0-=ndelta; x1-=ndelta; }
41290  else { x1-=x0; x0 = 0; }
41291  }
41292  if (go_right) {
41293  const int delta = (x1-x0)/4, ndelta = delta?delta:(_width>1?1:0);
41294  if (x1+ndelta<width()) { x0+=ndelta; x1+=ndelta; }
41295  else { x0+=(width()-1-x1); x1 = width()-1; }
41296  }
41297  if (go_up) {
41298  const int delta = (y1-y0)/4, ndelta = delta?delta:(_height>1?1:0);
41299  if (y0-ndelta>=0) { y0-=ndelta; y1-=ndelta; }
41300  else { y1-=y0; y0 = 0; }
41301  }
41302  if (go_down) {
41303  const int delta = (y1-y0)/4, ndelta = delta?delta:(_height>1?1:0);
41304  if (y1+ndelta<height()) { y0+=ndelta; y1+=ndelta; }
41305  else { y0+=(height()-1-y1); y1 = height()-1; }
41306  }
41307  if (go_inc) {
41308  const int delta = (z1-z0)/4, ndelta = delta?delta:(_depth>1?1:0);
41309  if (z0-ndelta>=0) { z0-=ndelta; z1-=ndelta; }
41310  else { z1-=z0; z0 = 0; }
41311  }
41312  if (go_dec) {
41313  const int delta = (z1-z0)/4, ndelta = delta?delta:(_depth>1?1:0);
41314  if (z1+ndelta<depth()) { z0+=ndelta; z1+=ndelta; }
41315  else { z0+=(depth()-1-z1); z1 = depth()-1; }
41316  }
41317  disp.wait(100);
41318  }
41319  disp.set_key(key);
41320  if (XYZ) { XYZ[0] = _XYZ[0]; XYZ[1] = _XYZ[1]; XYZ[2] = _XYZ[2]; }
41321  return *this;
41322  }
41323 
41325 
41344  template<typename tp, typename tf, typename tc, typename to>
41346  const CImg<tp>& vertices,
41347  const CImgList<tf>& primitives,
41348  const CImgList<tc>& colors,
41349  const to& opacities,
41350  const bool centering=true,
41351  const int render_static=4, const int render_motion=1,
41352  const bool is_double_sided=true, const float focale=700,
41353  const float light_x=0, const float light_y=0, const float light_z=-5e8f,
41354  const float specular_lightness=0.2f, const float specular_shininess=0.1f,
41355  const bool display_axes=true, float *const pose_matrix=0) const {
41356  return _display_object3d(disp,0,vertices,primitives,colors,opacities,centering,render_static,
41357  render_motion,is_double_sided,focale,
41358  light_x,light_y,light_z,specular_lightness,specular_shininess,
41359  display_axes,pose_matrix);
41360  }
41361 
41363  template<typename tp, typename tf, typename tc, typename to>
41364  const CImg<T>& display_object3d(const char *const title,
41365  const CImg<tp>& vertices,
41366  const CImgList<tf>& primitives,
41367  const CImgList<tc>& colors,
41368  const to& opacities,
41369  const bool centering=true,
41370  const int render_static=4, const int render_motion=1,
41371  const bool is_double_sided=true, const float focale=700,
41372  const float light_x=0, const float light_y=0, const float light_z=-5e8f,
41373  const float specular_lightness=0.2f, const float specular_shininess=0.1f,
41374  const bool display_axes=true, float *const pose_matrix=0) const {
41375  CImgDisplay disp;
41376  return _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,render_static,
41377  render_motion,is_double_sided,focale,
41378  light_x,light_y,light_z,specular_lightness,specular_shininess,
41379  display_axes,pose_matrix);
41380  }
41381 
41383  template<typename tp, typename tf, typename tc>
41385  const CImg<tp>& vertices,
41386  const CImgList<tf>& primitives,
41387  const CImgList<tc>& colors,
41388  const bool centering=true,
41389  const int render_static=4, const int render_motion=1,
41390  const bool is_double_sided=true, const float focale=700,
41391  const float light_x=0, const float light_y=0, const float light_z=-5e8f,
41392  const float specular_lightness=0.2f, const float specular_shininess=0.1f,
41393  const bool display_axes=true, float *const pose_matrix=0) const {
41394  return display_object3d(disp,vertices,primitives,colors,CImgList<floatT>(),centering,
41395  render_static,render_motion,is_double_sided,focale,
41396  light_x,light_y,light_z,specular_lightness,specular_shininess,
41397  display_axes,pose_matrix);
41398  }
41399 
41401  template<typename tp, typename tf, typename tc>
41402  const CImg<T>& display_object3d(const char *const title,
41403  const CImg<tp>& vertices,
41404  const CImgList<tf>& primitives,
41405  const CImgList<tc>& colors,
41406  const bool centering=true,
41407  const int render_static=4, const int render_motion=1,
41408  const bool is_double_sided=true, const float focale=700,
41409  const float light_x=0, const float light_y=0, const float light_z=-5e8f,
41410  const float specular_lightness=0.2f, const float specular_shininess=0.1f,
41411  const bool display_axes=true, float *const pose_matrix=0) const {
41412  return display_object3d(title,vertices,primitives,colors,CImgList<floatT>(),centering,
41413  render_static,render_motion,is_double_sided,focale,
41414  light_x,light_y,light_z,specular_lightness,specular_shininess,
41415  display_axes,pose_matrix);
41416  }
41417 
41419  template<typename tp, typename tf>
41421  const CImg<tp>& vertices,
41422  const CImgList<tf>& primitives,
41423  const bool centering=true,
41424  const int render_static=4, const int render_motion=1,
41425  const bool is_double_sided=true, const float focale=700,
41426  const float light_x=0, const float light_y=0, const float light_z=-5e8f,
41427  const float specular_lightness=0.2f, const float specular_shininess=0.1f,
41428  const bool display_axes=true, float *const pose_matrix=0) const {
41429  return display_object3d(disp,vertices,primitives,CImgList<T>(),centering,
41430  render_static,render_motion,is_double_sided,focale,
41431  light_x,light_y,light_z,specular_lightness,specular_shininess,
41432  display_axes,pose_matrix);
41433  }
41434 
41435 
41437  template<typename tp, typename tf>
41438  const CImg<T>& display_object3d(const char *const title,
41439  const CImg<tp>& vertices,
41440  const CImgList<tf>& primitives,
41441  const bool centering=true,
41442  const int render_static=4, const int render_motion=1,
41443  const bool is_double_sided=true, const float focale=700,
41444  const float light_x=0, const float light_y=0, const float light_z=-5e8f,
41445  const float specular_lightness=0.2f, const float specular_shininess=0.1f,
41446  const bool display_axes=true, float *const pose_matrix=0) const {
41447  return display_object3d(title,vertices,primitives,CImgList<T>(),centering,
41448  render_static,render_motion,is_double_sided,focale,
41449  light_x,light_y,light_z,specular_lightness,specular_shininess,
41450  display_axes,pose_matrix);
41451  }
41452 
41454  template<typename tp>
41456  const CImg<tp>& vertices,
41457  const bool centering=true,
41458  const int render_static=4, const int render_motion=1,
41459  const bool is_double_sided=true, const float focale=700,
41460  const float light_x=0, const float light_y=0, const float light_z=-5e8f,
41461  const float specular_lightness=0.2f, const float specular_shininess=0.1f,
41462  const bool display_axes=true, float *const pose_matrix=0) const {
41463  return display_object3d(disp,vertices,CImgList<uintT>(),centering,
41464  render_static,render_motion,is_double_sided,focale,
41465  light_x,light_y,light_z,specular_lightness,specular_shininess,
41466  display_axes,pose_matrix);
41467  }
41468 
41470  template<typename tp>
41471  const CImg<T>& display_object3d(const char *const title,
41472  const CImg<tp>& vertices,
41473  const bool centering=true,
41474  const int render_static=4, const int render_motion=1,
41475  const bool is_double_sided=true, const float focale=700,
41476  const float light_x=0, const float light_y=0, const float light_z=-5e8f,
41477  const float specular_lightness=0.2f, const float specular_shininess=0.1f,
41478  const bool display_axes=true, float *const pose_matrix=0) const {
41479  return display_object3d(title,vertices,CImgList<uintT>(),centering,
41480  render_static,render_motion,is_double_sided,focale,
41481  light_x,light_y,light_z,specular_lightness,specular_shininess,
41482  display_axes,pose_matrix);
41483  }
41484 
41485  template<typename tp, typename tf, typename tc, typename to>
41486  const CImg<T>& _display_object3d(CImgDisplay& disp, const char *const title,
41487  const CImg<tp>& vertices,
41488  const CImgList<tf>& primitives,
41489  const CImgList<tc>& colors,
41490  const to& opacities,
41491  const bool centering,
41492  const int render_static, const int render_motion,
41493  const bool is_double_sided, const float focale,
41494  const float light_x, const float light_y, const float light_z,
41495  const float specular_lightness, const float specular_shininess,
41496  const bool display_axes, float *const pose_matrix) const {
41497  typedef typename cimg::superset<tp,float>::type tpfloat;
41498 
41499  // Check input arguments
41500  if (is_empty()) {
41501  if (disp) return CImg<T>(disp.width(),disp.height(),1,(colors && colors[0].size()==1)?1:3,0).
41502  _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,
41503  render_static,render_motion,is_double_sided,focale,
41504  light_x,light_y,light_z,specular_lightness,specular_shininess,
41505  display_axes,pose_matrix);
41506  else return CImg<T>(1,2,1,1,64,128).resize(cimg_fitscreen(CImgDisplay::screen_width()/2,
41508  1,(colors && colors[0].size()==1)?1:3,3).
41509  _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,
41510  render_static,render_motion,is_double_sided,focale,
41511  light_x,light_y,light_z,specular_lightness,specular_shininess,
41512  display_axes,pose_matrix);
41513  } else { if (disp) disp.resize(*this,false); }
41514  char error_message[1024] = { 0 };
41515  if (!vertices.is_object3d(primitives,colors,opacities,true,error_message))
41516  throw CImgArgumentException(_cimg_instance
41517  "display_object3d(): Invalid specified 3d object (%u,%u) (%s).",
41518  cimg_instance,vertices._width,primitives._width,error_message);
41519  if (vertices._width && !primitives) {
41520  CImgList<tf> nprimitives(vertices._width,1,1,1,1);
41521  cimglist_for(nprimitives,l) nprimitives(l,0) = l;
41522  return _display_object3d(disp,title,vertices,nprimitives,colors,opacities,centering,
41523  render_static,render_motion,is_double_sided,focale,
41524  light_x,light_y,light_z,specular_lightness,specular_shininess,
41525  display_axes,pose_matrix);
41526  }
41527  if (!disp) {
41528  disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,3);
41529  if (!title) disp.set_title("CImg<%s> (%u vertices, %u primitives)",
41530  pixel_type(),vertices._width,primitives._width);
41531  } else if (title) disp.set_title("%s",title);
41532 
41533  // Init 3d objects and compute object statistics
41534  CImg<floatT>
41535  pose,
41536  rotated_vertices(vertices._width,3),
41537  bbox_vertices, rotated_bbox_vertices,
41538  axes_vertices, rotated_axes_vertices,
41539  bbox_opacities, axes_opacities;
41540  CImgList<uintT> bbox_primitives, axes_primitives;
41541  CImgList<tf> reverse_primitives;
41542  CImgList<T> bbox_colors, bbox_colors2, axes_colors;
41543  unsigned int ns_width = 0, ns_height = 0;
41544  int _is_double_sided = (int)is_double_sided;
41545  bool ndisplay_axes = display_axes;
41546  const CImg<T>
41547  background_color(1,1,1,_spectrum,0),
41548  foreground_color(1,1,1,_spectrum,255);
41549  float
41550  Xoff = 0, Yoff = 0, Zoff = 0, sprite_scale = 1,
41551  xm = 0, xM = vertices?vertices.get_shared_row(0).max_min(xm):0,
41552  ym = 0, yM = vertices?vertices.get_shared_row(1).max_min(ym):0,
41553  zm = 0, zM = vertices?vertices.get_shared_row(2).max_min(zm):0;
41554  const float delta = cimg::max(xM-xm,yM-ym,zM-zm);
41555 
41556  rotated_bbox_vertices = bbox_vertices.assign(8,3,1,1,
41557  xm,xM,xM,xm,xm,xM,xM,xm,
41558  ym,ym,yM,yM,ym,ym,yM,yM,
41559  zm,zm,zm,zm,zM,zM,zM,zM);
41560  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);
41561  bbox_colors.assign(6,_spectrum,1,1,1,background_color[0]);
41562  bbox_colors2.assign(6,_spectrum,1,1,1,foreground_color[0]);
41563  bbox_opacities.assign(bbox_colors._width,1,1,1,0.3f);
41564 
41565  rotated_axes_vertices = axes_vertices.assign(7,3,1,1,
41566  0,20,0,0,22,-6,-6,
41567  0,0,20,0,-6,22,-6,
41568  0,0,0,20,0,0,22);
41569  axes_opacities.assign(3,1,1,1,1);
41570  axes_colors.assign(3,_spectrum,1,1,1,foreground_color[0]);
41571  axes_primitives.assign(3,1,2,1,1, 0,1, 0,2, 0,3);
41572 
41573  // Begin user interaction loop
41574  CImg<T> visu0(*this), visu;
41575  CImg<tpfloat> zbuffer(visu0.width(),visu0.height(),1,1,0);
41576  bool init_pose = true, clicked = false, redraw = true;
41577  unsigned int key = 0;
41578  int
41579  x0 = 0, y0 = 0, x1 = 0, y1 = 0,
41580  nrender_static = render_static,
41581  nrender_motion = render_motion;
41582  disp.show().flush();
41583 
41584  while (!disp.is_closed() && !key) {
41585 
41586  // Init object pose
41587  if (init_pose) {
41588  const float
41589  ratio = delta>0?(2.0f*cimg::min(disp.width(),disp.height())/(3.0f*delta)):1,
41590  dx = (xM + xm)/2, dy = (yM + ym)/2, dz = (zM + zm)/2;
41591  if (centering)
41592  CImg<floatT>(4,3,1,1, ratio,0.,0.,-ratio*dx, 0.,ratio,0.,-ratio*dy, 0.,0.,ratio,-ratio*dz).move_to(pose);
41593  else CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose);
41594  if (pose_matrix) {
41595  CImg<floatT> pose0(pose_matrix,4,3,1,1,false);
41596  pose0.resize(4,4,1,1,0); pose.resize(4,4,1,1,0);
41597  pose0(3,3) = pose(3,3) = 1;
41598  (pose0*pose).get_crop(0,0,3,2).move_to(pose);
41599  Xoff = pose_matrix[12]; Yoff = pose_matrix[13]; Zoff = pose_matrix[14]; sprite_scale = pose_matrix[15];
41600  } else { Xoff = Yoff = Zoff = 0; sprite_scale = 1; }
41601  init_pose = false;
41602  redraw = true;
41603  }
41604 
41605  // Rotate and draw 3d object
41606  if (redraw) {
41607  const float
41608  r00 = pose(0,0), r10 = pose(1,0), r20 = pose(2,0), r30 = pose(3,0),
41609  r01 = pose(0,1), r11 = pose(1,1), r21 = pose(2,1), r31 = pose(3,1),
41610  r02 = pose(0,2), r12 = pose(1,2), r22 = pose(2,2), r32 = pose(3,2);
41611  if ((clicked && nrender_motion>=0) || (!clicked && nrender_static>=0))
41612  cimg_forX(vertices,l) {
41613  const float x = (float)vertices(l,0), y = (float)vertices(l,1), z = (float)vertices(l,2);
41614  rotated_vertices(l,0) = r00*x + r10*y + r20*z + r30;
41615  rotated_vertices(l,1) = r01*x + r11*y + r21*z + r31;
41616  rotated_vertices(l,2) = r02*x + r12*y + r22*z + r32;
41617  }
41618  else cimg_forX(bbox_vertices,l) {
41619  const float x = bbox_vertices(l,0), y = bbox_vertices(l,1), z = bbox_vertices(l,2);
41620  rotated_bbox_vertices(l,0) = r00*x + r10*y + r20*z + r30;
41621  rotated_bbox_vertices(l,1) = r01*x + r11*y + r21*z + r31;
41622  rotated_bbox_vertices(l,2) = r02*x + r12*y + r22*z + r32;
41623  }
41624 
41625  // Draw objects
41626 #ifdef cimg_use_openmp
41627  const bool render_with_zbuffer = true;
41628 #else
41629  const bool render_with_zbuffer = !clicked && nrender_static>0;
41630 #endif
41631  visu = visu0;
41632  if ((clicked && nrender_motion<0) || (!clicked && nrender_static<0))
41633  visu.draw_object3d(Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
41634  rotated_bbox_vertices,bbox_primitives,bbox_colors,bbox_opacities,2,false,focale).
41635  draw_object3d(Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
41636  rotated_bbox_vertices,bbox_primitives,bbox_colors2,1,false,focale);
41637  else visu._draw_object3d((void*)0,render_with_zbuffer?zbuffer.fill(0):CImg<tpfloat>::empty(),
41638  Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
41639  rotated_vertices,reverse_primitives?reverse_primitives:primitives,
41640  colors,opacities,clicked?nrender_motion:nrender_static,_is_double_sided==1,focale,
41641  width()/2.0f+light_x,height()/2.0f+light_y,light_z+Zoff,
41642  specular_lightness,specular_shininess,sprite_scale);
41643  // Draw axes
41644  if (ndisplay_axes) {
41645  const float
41646  n = (float)std::sqrt(1e-8 + r00*r00 + r01*r01 + r02*r02),
41647  _r00 = r00/n, _r10 = r10/n, _r20 = r20/n,
41648  _r01 = r01/n, _r11 = r11/n, _r21 = r21/n,
41649  _r02 = r01/n, _r12 = r12/n, _r22 = r22/n,
41650  Xaxes = 25, Yaxes = visu._height - 38.0f;
41651  cimg_forX(axes_vertices,l) {
41652  const float
41653  x = axes_vertices(l,0),
41654  y = axes_vertices(l,1),
41655  z = axes_vertices(l,2);
41656  rotated_axes_vertices(l,0) = _r00*x + _r10*y + _r20*z;
41657  rotated_axes_vertices(l,1) = _r01*x + _r11*y + _r21*z;
41658  rotated_axes_vertices(l,2) = _r02*x + _r12*y + _r22*z;
41659  }
41660  axes_opacities(0,0) = (rotated_axes_vertices(1,2)>0)?0.5f:1.0f;
41661  axes_opacities(1,0) = (rotated_axes_vertices(2,2)>0)?0.5f:1.0f;
41662  axes_opacities(2,0) = (rotated_axes_vertices(3,2)>0)?0.5f:1.0f;
41663  visu.draw_object3d(Xaxes,Yaxes,0,rotated_axes_vertices,axes_primitives,
41664  axes_colors,axes_opacities,1,false,focale).
41665  draw_text((int)(Xaxes+rotated_axes_vertices(4,0)),
41666  (int)(Yaxes+rotated_axes_vertices(4,1)),
41667  "X",axes_colors[0]._data,0,axes_opacities(0,0),13).
41668  draw_text((int)(Xaxes+rotated_axes_vertices(5,0)),
41669  (int)(Yaxes+rotated_axes_vertices(5,1)),
41670  "Y",axes_colors[1]._data,0,axes_opacities(1,0),13).
41671  draw_text((int)(Xaxes+rotated_axes_vertices(6,0)),
41672  (int)(Yaxes+rotated_axes_vertices(6,1)),
41673  "Z",axes_colors[2]._data,0,axes_opacities(2,0),13);
41674  }
41675  visu.display(disp);
41676  if (!clicked || nrender_motion==nrender_static) redraw = false;
41677  }
41678 
41679  // Handle user interaction
41680  disp.wait();
41681  if ((disp.button() || disp.wheel()) && disp.mouse_x()>=0 && disp.mouse_y()>=0) {
41682  redraw = true;
41683  if (!clicked) { x0 = x1 = disp.mouse_x(); y0 = y1 = disp.mouse_y(); if (!disp.wheel()) clicked = true; }
41684  else { x1 = disp.mouse_x(); y1 = disp.mouse_y(); }
41685  if (disp.button()&1) {
41686  const float
41687  R = 0.45f*cimg::min(disp.width(),disp.height()),
41688  R2 = R*R,
41689  u0 = (float)(x0-disp.width()/2),
41690  v0 = (float)(y0-disp.height()/2),
41691  u1 = (float)(x1-disp.width()/2),
41692  v1 = (float)(y1-disp.height()/2),
41693  n0 = (float)std::sqrt(u0*u0+v0*v0),
41694  n1 = (float)std::sqrt(u1*u1+v1*v1),
41695  nu0 = n0>R?(u0*R/n0):u0,
41696  nv0 = n0>R?(v0*R/n0):v0,
41697  nw0 = (float)std::sqrt(cimg::max(0,R2-nu0*nu0-nv0*nv0)),
41698  nu1 = n1>R?(u1*R/n1):u1,
41699  nv1 = n1>R?(v1*R/n1):v1,
41700  nw1 = (float)std::sqrt(cimg::max(0,R2-nu1*nu1-nv1*nv1)),
41701  u = nv0*nw1-nw0*nv1,
41702  v = nw0*nu1-nu0*nw1,
41703  w = nv0*nu1-nu0*nv1,
41704  n = (float)std::sqrt(u*u+v*v+w*w),
41705  alpha = (float)std::asin(n/R2);
41706  (CImg<floatT>::rotation_matrix(u,v,w,alpha)*pose).move_to(pose);
41707  x0 = x1; y0 = y1;
41708  }
41709  if (disp.button()&2) {
41710  if (focale>0) Zoff-=(y0-y1)*focale/400;
41711  else { const float s = std::exp((y0-y1)/400.0f); pose*=s; sprite_scale*=s; }
41712  x0 = x1; y0 = y1;
41713  }
41714  if (disp.wheel()) {
41715  if (focale>0) Zoff-=disp.wheel()*focale/20;
41716  else { const float s = std::exp(disp.wheel()/20.0f); pose*=s; sprite_scale*=s; }
41717  disp.set_wheel();
41718  }
41719  if (disp.button()&4) { Xoff+=(x1-x0); Yoff+=(y1-y0); x0 = x1; y0 = y1; }
41720  if ((disp.button()&1) && (disp.button()&2)) {
41721  init_pose = true; disp.set_button(); x0 = x1; y0 = y1;
41722  pose = CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0);
41723  }
41724  } else if (clicked) { x0 = x1; y0 = y1; clicked = false; redraw = true; }
41725 
41726  switch (key = disp.key()) {
41727 #if cimg_OS!=2
41728  case cimg::keyCTRLRIGHT :
41729 #endif
41730  case 0 : case cimg::keyCTRLLEFT : key = 0; break;
41731  case cimg::keyD: if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
41732  disp.set_fullscreen(false).
41733  resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
41734  CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
41735  _is_resized = true;
41736  disp.set_key(key,false); key = 0;
41737  } break;
41738  case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
41739  disp.set_fullscreen(false).
41740  resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
41741  disp.set_key(key,false); key = 0;
41742  } break;
41743  case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
41744  disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true;
41745  disp.set_key(key,false); key = 0;
41746  } break;
41747  case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
41748  if (!ns_width || !ns_height ||
41749  ns_width>(unsigned int)disp.screen_width() || ns_height>(unsigned int)disp.screen_height()) {
41750  ns_width = disp.screen_width()*3U/4;
41751  ns_height = disp.screen_height()*3U/4;
41752  }
41753  if (disp.is_fullscreen()) disp.resize(ns_width,ns_height,false);
41754  else {
41755  ns_width = (unsigned int)disp.width(); ns_height = disp.height();
41756  disp.resize(disp.screen_width(),disp.screen_height(),false);
41757  }
41758  disp.toggle_fullscreen()._is_resized = true;
41759  disp.set_key(key,false); key = 0;
41760  } break;
41761  case cimg::keyT : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
41762  // Switch single/double-sided primitives.
41763  if (--_is_double_sided==-2) _is_double_sided = 1;
41764  if (_is_double_sided>=0) reverse_primitives.assign();
41765  else primitives.get_reverse_object3d().move_to(reverse_primitives);
41766  disp.set_key(key,false); key = 0; redraw = true;
41767  } break;
41768  case cimg::keyZ : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Enable/disable Z-buffer
41769  if (zbuffer) zbuffer.assign();
41770  else zbuffer.assign(visu0.width(),visu0.height(),1,1,0);
41771  disp.set_key(key,false); key = 0; redraw = true;
41772  } break;
41773  case cimg::keyA : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Show/hide 3d axes.
41774  ndisplay_axes = !ndisplay_axes;
41775  disp.set_key(key,false); key = 0; redraw = true;
41776  } break;
41777  case cimg::keyF1 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to points.
41778  nrender_motion = (nrender_static==0 && nrender_motion!=0)?0:-1; nrender_static = 0;
41779  disp.set_key(key,false); key = 0; redraw = true;
41780  } break;
41781  case cimg::keyF2 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to lines.
41782  nrender_motion = (nrender_static==1 && nrender_motion!=1)?1:-1; nrender_static = 1;
41783  disp.set_key(key,false); key = 0; redraw = true;
41784  } break;
41785  case cimg::keyF3 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat.
41786  nrender_motion = (nrender_static==2 && nrender_motion!=2)?2:-1; nrender_static = 2;
41787  disp.set_key(key,false); key = 0; redraw = true;
41788  } break;
41789  case cimg::keyF4 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat-shaded.
41790  nrender_motion = (nrender_static==3 && nrender_motion!=3)?3:-1; nrender_static = 3;
41791  disp.set_key(key,false); key = 0; redraw = true;
41792  } break;
41793  case cimg::keyF5 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
41794  // Set rendering mode to gouraud-shaded.
41795  nrender_motion = (nrender_static==4 && nrender_motion!=4)?4:-1; nrender_static = 4;
41796  disp.set_key(key,false); key = 0; redraw = true;
41797  } break;
41798  case cimg::keyF6 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to phong-shaded.
41799  nrender_motion = (nrender_static==5 && nrender_motion!=5)?5:-1; nrender_static = 5;
41800  disp.set_key(key,false); key = 0; redraw = true;
41801  } break;
41802  case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save snapshot
41803  static unsigned int snap_number = 0;
41804  char filename[32] = { 0 };
41805  std::FILE *file;
41806  do {
41807  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++);
41808  if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
41809  } while (file);
41810  (+visu).draw_text(0,0," Saving snapshot... ",
41811  foreground_color._data,background_color._data,0.7f,13).display(disp);
41812  visu.save(filename);
41813  (+visu).draw_text(0,0," Snapshot '%s' saved. ",
41814  foreground_color._data,background_color._data,0.7f,13,filename).display(disp);
41815  disp.set_key(key,false); key = 0;
41816  } break;
41817  case cimg::keyG : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .off file
41818  static unsigned int snap_number = 0;
41819  char filename[32] = { 0 };
41820  std::FILE *file;
41821  do {
41822  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.off",snap_number++);
41823  if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
41824  } while (file);
41825  (+visu).draw_text(0,0," Saving object... ",
41826  foreground_color._data,background_color._data,0.7f,13).display(disp);
41827  vertices.save_off(reverse_primitives?reverse_primitives:primitives,colors,filename);
41828  (+visu).draw_text(0,0," Object '%s' saved. ",
41829  foreground_color._data,background_color._data,0.7f,13,filename).display(disp);
41830  disp.set_key(key,false); key = 0;
41831  } break;
41832  case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .cimg file
41833  static unsigned int snap_number = 0;
41834  char filename[32] = { 0 };
41835  std::FILE *file;
41836  do {
41837 #ifdef cimg_use_zlib
41838  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++);
41839 #else
41840  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++);
41841 #endif
41842  if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
41843  } while (file);
41844  (+visu).draw_text(0,0," Saving object... ",
41845  foreground_color._data,background_color._data,0.7f,13).display(disp);
41846  vertices.get_object3dtoCImg3d(reverse_primitives?reverse_primitives:primitives,colors,opacities).
41847  save(filename);
41848  (+visu).draw_text(0,0," Object '%s' saved. ",
41849  foreground_color._data,background_color._data,0.7f,13,filename).display(disp);
41850  disp.set_key(key,false); key = 0;
41851  } break;
41852 #ifdef cimg_use_board
41853  case cimg::keyP : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .EPS file
41854  static unsigned int snap_number = 0;
41855  char filename[32] = { 0 };
41856  std::FILE *file;
41857  do {
41858  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.eps",snap_number++);
41859  if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
41860  } while (file);
41861  (+visu).draw_text(0,0," Saving EPS snapshot... ",
41862  foreground_color._data,background_color._data,0.7f,13).display(disp);
41863  LibBoard::Board board;
41864  (+visu)._draw_object3d(&board,zbuffer.fill(0),
41865  Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
41866  rotated_vertices,reverse_primitives?reverse_primitives:primitives,
41867  colors,opacities,clicked?nrender_motion:nrender_static,
41868  _is_double_sided==1,focale,
41869  visu.width()/2.0f+light_x,visu.height()/2.0f+light_y,light_z+Zoff,
41870  specular_lightness,specular_shininess,
41871  sprite_scale);
41872  board.saveEPS(filename);
41873  (+visu).draw_text(0,0," Object '%s' saved. ",
41874  foreground_color._data,background_color._data,0.7f,13,filename).display(disp);
41875  disp.set_key(key,false); key = 0;
41876  } break;
41877  case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .SVG file
41878  static unsigned int snap_number = 0;
41879  char filename[32] = { 0 };
41880  std::FILE *file;
41881  do {
41882  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.svg",snap_number++);
41883  if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
41884  } while (file);
41885  (+visu).draw_text(0,0," Saving SVG snapshot... ",
41886  foreground_color._data,background_color._data,0.7f,13).display(disp);
41887  LibBoard::Board board;
41888  (+visu)._draw_object3d(&board,zbuffer.fill(0),
41889  Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
41890  rotated_vertices,reverse_primitives?reverse_primitives:primitives,
41891  colors,opacities,clicked?nrender_motion:nrender_static,
41892  _is_double_sided==1,focale,
41893  visu.width()/2.0f+light_x,visu.height()/2.0f+light_y,light_z+Zoff,
41894  specular_lightness,specular_shininess,
41895  sprite_scale);
41896  board.saveSVG(filename);
41897  (+visu).draw_text(0,0," Object '%s' saved. ",
41898  foreground_color._data,background_color._data,0.7f,13,filename).display(disp);
41899  disp.set_key(key,false); key = 0;
41900  } break;
41901 #endif
41902  }
41903  if (disp.is_resized()) {
41904  disp.resize(false); visu0 = get_resize(disp,1);
41905  if (zbuffer) zbuffer.assign(disp.width(),disp.height());
41906  redraw = true;
41907  }
41908  }
41909  if (pose_matrix) {
41910  std::memcpy(pose_matrix,pose._data,12*sizeof(float));
41911  pose_matrix[12] = Xoff; pose_matrix[13] = Yoff; pose_matrix[14] = Zoff; pose_matrix[15] = sprite_scale;
41912  }
41913  disp.set_button().set_key(key);
41914  return *this;
41915  }
41916 
41918 
41930  const unsigned int plot_type=1, const unsigned int vertex_type=1,
41931  const char *const labelx=0, const double xmin=0, const double xmax=0,
41932  const char *const labely=0, const double ymin=0, const double ymax=0) const {
41933  return _display_graph(disp,0,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax);
41934  }
41935 
41937  const CImg<T>& display_graph(const char *const title=0,
41938  const unsigned int plot_type=1, const unsigned int vertex_type=1,
41939  const char *const labelx=0, const double xmin=0, const double xmax=0,
41940  const char *const labely=0, const double ymin=0, const double ymax=0) const {
41941  CImgDisplay disp;
41942  return _display_graph(disp,title,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax);
41943  }
41944 
41945  const CImg<T>& _display_graph(CImgDisplay &disp, const char *const title=0,
41946  const unsigned int plot_type=1, const unsigned int vertex_type=1,
41947  const char *const labelx=0, const double xmin=0, const double xmax=0,
41948  const char *const labely=0, const double ymin=0, const double ymax=0) const {
41949  if (is_empty())
41950  throw CImgInstanceException(_cimg_instance
41951  "display_graph(): Empty instance.",
41952  cimg_instance);
41953  if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,1),0,0).
41954  set_title(title?"%s":"CImg<%s>",title?title:pixel_type());
41955  const unsigned long siz = (unsigned long)_width*_height*_depth, siz1 = cimg::max(1U,siz-1);
41956  const unsigned int old_normalization = disp.normalization();
41957  disp.show().flush()._normalization = 0;
41958 
41959  double y0 = ymin, y1 = ymax, nxmin = xmin, nxmax = xmax;
41960  if (nxmin==nxmax) { nxmin = 0; nxmax = siz1; }
41961  int x0 = 0, x1 = width()*height()*depth() - 1, key = 0;
41962 
41963  for (bool reset_view = true; !key && !disp.is_closed(); ) {
41964  if (reset_view) { x0 = 0; x1 = width()*height()*depth()-1; y0 = ymin; y1 = ymax; reset_view = false; }
41965  CImg<T> zoom(x1-x0+1,1,1,spectrum());
41966  cimg_forC(*this,c) zoom.get_shared_channel(c) = CImg<T>(data(x0,0,0,c),x1-x0+1,1,1,1,true);
41967  if (y0==y1) { y0 = zoom.min_max(y1); const double dy = y1 - y0; y0-=dy/20; y1+=dy/20; }
41968  if (y0==y1) { --y0; ++y1; }
41969 
41970  const CImg<intT> selection = zoom.get_select_graph(disp,plot_type,vertex_type,
41971  labelx,
41972  nxmin + x0*(nxmax-nxmin)/siz1,
41973  nxmin + x1*(nxmax-nxmin)/siz1,
41974  labely,y0,y1);
41975  const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y();
41976  if (selection[0]>=0) {
41977  if (selection[2]<0) reset_view = true;
41978  else {
41979  x1 = x0 + selection[2]; x0+=selection[0];
41980  if (selection[1]>=0 && selection[3]>=0) {
41981  y0 = y1 - selection[3]*(y1-y0)/(disp.height()-32);
41982  y1-=selection[1]*(y1-y0)/(disp.height()-32);
41983  }
41984  }
41985  } else {
41986  bool go_in = false, go_out = false, go_left = false, go_right = false, go_up = false, go_down = false;
41987  switch (key = disp.key()) {
41988  case cimg::keyHOME : reset_view = true; key = 0; disp.set_key(); break;
41989  case cimg::keyPADADD : go_in = true; go_out = false; key = 0; disp.set_key(); break;
41990  case cimg::keyPADSUB : go_out = true; go_in = false; key = 0; disp.set_key(); break;
41991  case cimg::keyARROWLEFT : case cimg::keyPAD4 : go_left = true; go_right = false; key = 0; disp.set_key();
41992  break;
41993  case cimg::keyARROWRIGHT : case cimg::keyPAD6 : go_right = true; go_left = false; key = 0; disp.set_key();
41994  break;
41995  case cimg::keyARROWUP : case cimg::keyPAD8 : go_up = true; go_down = false; key = 0; disp.set_key(); break;
41996  case cimg::keyARROWDOWN : case cimg::keyPAD2 : go_down = true; go_up = false; key = 0; disp.set_key(); break;
41997  case cimg::keyPAD7 : go_left = true; go_up = true; key = 0; disp.set_key(); break;
41998  case cimg::keyPAD9 : go_right = true; go_up = true; key = 0; disp.set_key(); break;
41999  case cimg::keyPAD1 : go_left = true; go_down = true; key = 0; disp.set_key(); break;
42000  case cimg::keyPAD3 : go_right = true; go_down = true; key = 0; disp.set_key(); break;
42001  }
42002  if (disp.wheel()) {
42003  if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) go_out = !(go_in = disp.wheel()>0);
42004  else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) go_left = !(go_right = disp.wheel()>0);
42005  else go_up = !(go_down = disp.wheel()<0);
42006  key = 0;
42007  }
42008 
42009  if (go_in) {
42010  const int
42011  xsiz = x1 - x0,
42012  mx = (mouse_x-16)*xsiz/(disp.width()-32),
42013  cx = x0 + (mx<0?0:(mx>=xsiz?xsiz:mx));
42014  if (x1-x0>4) {
42015  x0 = cx - 7*(cx-x0)/8; x1 = cx + 7*(x1-cx)/8;
42016  if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
42017  const double
42018  ysiz = y1 - y0,
42019  my = (mouse_y-16)*ysiz/(disp.height()-32),
42020  cy = y1 - (my<0?0:(my>=ysiz?ysiz:my));
42021  y0 = cy - 7*(cy-y0)/8; y1 = cy + 7*(y1-cy)/8;
42022  } else y0 = y1 = 0;
42023  }
42024  }
42025  if (go_out) {
42026  if (x0>0 || x1<(int)siz1) {
42027  const int delta_x = (x1-x0)/8, ndelta_x = delta_x?delta_x:(siz>1?1:0);
42028  const double ndelta_y = (y1-y0)/8;
42029  x0-=ndelta_x; x1+=ndelta_x;
42030  y0-=ndelta_y; y1+=ndelta_y;
42031  if (x0<0) { x1-=x0; x0 = 0; if (x1>=(int)siz) x1 = (int)siz1; }
42032  if (x1>=(int)siz) { x0-=(x1-siz1); x1 = (int)siz1; if (x0<0) x0 = 0; }
42033  }
42034  }
42035  if (go_left) {
42036  const int delta = (x1-x0)/5, ndelta = delta?delta:1;
42037  if (x0-ndelta>=0) { x0-=ndelta; x1-=ndelta; }
42038  else { x1-=x0; x0 = 0; }
42039  go_left = false;
42040  }
42041  if (go_right) {
42042  const int delta = (x1-x0)/5, ndelta = delta?delta:1;
42043  if (x1+ndelta<(int)siz) { x0+=ndelta; x1+=ndelta; }
42044  else { x0+=(siz1-x1); x1 = siz1; }
42045  go_right = false;
42046  }
42047  if (go_up) {
42048  const double delta = (y1-y0)/10, ndelta = delta?delta:1;
42049  y0+=ndelta; y1+=ndelta;
42050  go_up = false;
42051  }
42052  if (go_down) {
42053  const double delta = (y1-y0)/10, ndelta = delta?delta:1;
42054  y0-=ndelta; y1-=ndelta;
42055  go_down = false;
42056  }
42057  }
42058  }
42059  disp._normalization = old_normalization;
42060  return *this;
42061  }
42062 
42064 
42073  const CImg<T>& save(const char *const filename, const int number=-1, const unsigned int digits=6) const {
42074  if (!filename)
42075  throw CImgArgumentException(_cimg_instance
42076  "save(): Specified filename is (null).",
42077  cimg_instance);
42078  // Do not test for empty instances, since .cimg format is able to manage empty instances.
42079  const bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.');
42080  const char *const ext = cimg::split_filename(filename);
42081  char nfilename[1024] = { 0 };
42082  const char *const fn = is_stdout?filename:(number>=0)?cimg::number_filename(filename,number,digits,nfilename):
42083  filename;
42084 
42085 #ifdef cimg_save_plugin
42086  cimg_save_plugin(fn);
42087 #endif
42088 #ifdef cimg_save_plugin1
42089  cimg_save_plugin1(fn);
42090 #endif
42091 #ifdef cimg_save_plugin2
42092  cimg_save_plugin2(fn);
42093 #endif
42094 #ifdef cimg_save_plugin3
42095  cimg_save_plugin3(fn);
42096 #endif
42097 #ifdef cimg_save_plugin4
42098  cimg_save_plugin4(fn);
42099 #endif
42100 #ifdef cimg_save_plugin5
42101  cimg_save_plugin5(fn);
42102 #endif
42103 #ifdef cimg_save_plugin6
42104  cimg_save_plugin6(fn);
42105 #endif
42106 #ifdef cimg_save_plugin7
42107  cimg_save_plugin7(fn);
42108 #endif
42109 #ifdef cimg_save_plugin8
42110  cimg_save_plugin8(fn);
42111 #endif
42112  // Ascii formats
42113  if (!cimg::strcasecmp(ext,"asc")) return save_ascii(fn);
42114  else if (!cimg::strcasecmp(ext,"dlm") ||
42115  !cimg::strcasecmp(ext,"txt")) return save_dlm(fn);
42116  else if (!cimg::strcasecmp(ext,"cpp") ||
42117  !cimg::strcasecmp(ext,"hpp") ||
42118  !cimg::strcasecmp(ext,"h") ||
42119  !cimg::strcasecmp(ext,"c")) return save_cpp(fn);
42120 
42121  // 2d binary formats
42122  else if (!cimg::strcasecmp(ext,"bmp")) return save_bmp(fn);
42123  else if (!cimg::strcasecmp(ext,"jpg") ||
42124  !cimg::strcasecmp(ext,"jpeg") ||
42125  !cimg::strcasecmp(ext,"jpe") ||
42126  !cimg::strcasecmp(ext,"jfif") ||
42127  !cimg::strcasecmp(ext,"jif")) return save_jpeg(fn);
42128  else if (!cimg::strcasecmp(ext,"rgb")) return save_rgb(fn);
42129  else if (!cimg::strcasecmp(ext,"rgba")) return save_rgba(fn);
42130  else if (!cimg::strcasecmp(ext,"png")) return save_png(fn);
42131  else if (!cimg::strcasecmp(ext,"pgm") ||
42132  !cimg::strcasecmp(ext,"ppm") ||
42133  !cimg::strcasecmp(ext,"pnm")) return save_pnm(fn);
42134  else if (!cimg::strcasecmp(ext,"pnk")) return save_pnk(fn);
42135  else if (!cimg::strcasecmp(ext,"pfm")) return save_pfm(fn);
42136  else if (!cimg::strcasecmp(ext,"exr")) return save_exr(fn);
42137  else if (!cimg::strcasecmp(ext,"tif") ||
42138  !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn);
42139 
42140  // 3d binary formats
42141  else if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true);
42142  else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false);
42143  else if (!cimg::strcasecmp(ext,"dcm")) return save_medcon_external(fn);
42144  else if (!cimg::strcasecmp(ext,"hdr") ||
42145  !cimg::strcasecmp(ext,"nii")) return save_analyze(fn);
42146  else if (!cimg::strcasecmp(ext,"inr")) return save_inr(fn);
42147  else if (!cimg::strcasecmp(ext,"mnc")) return save_minc2(fn);
42148  else if (!cimg::strcasecmp(ext,"pan")) return save_pandore(fn);
42149  else if (!cimg::strcasecmp(ext,"raw")) return save_raw(fn);
42150 
42151  // Archive files
42152  else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn);
42153 
42154  // Image sequences
42155  else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,true);
42156  else if (!cimg::strcasecmp(ext,"avi") ||
42157  !cimg::strcasecmp(ext,"mov") ||
42158  !cimg::strcasecmp(ext,"asf") ||
42159  !cimg::strcasecmp(ext,"divx") ||
42160  !cimg::strcasecmp(ext,"flv") ||
42161  !cimg::strcasecmp(ext,"mpg") ||
42162  !cimg::strcasecmp(ext,"m1v") ||
42163  !cimg::strcasecmp(ext,"m2v") ||
42164  !cimg::strcasecmp(ext,"m4v") ||
42165  !cimg::strcasecmp(ext,"mjp") ||
42166  !cimg::strcasecmp(ext,"mp4") ||
42167  !cimg::strcasecmp(ext,"mkv") ||
42168  !cimg::strcasecmp(ext,"mpe") ||
42169  !cimg::strcasecmp(ext,"movie") ||
42170  !cimg::strcasecmp(ext,"ogm") ||
42171  !cimg::strcasecmp(ext,"ogg") ||
42172  !cimg::strcasecmp(ext,"ogv") ||
42173  !cimg::strcasecmp(ext,"qt") ||
42174  !cimg::strcasecmp(ext,"rm") ||
42175  !cimg::strcasecmp(ext,"vob") ||
42176  !cimg::strcasecmp(ext,"wmv") ||
42177  !cimg::strcasecmp(ext,"xvid") ||
42178  !cimg::strcasecmp(ext,"mpeg")) return save_ffmpeg(fn);
42179  return save_other(fn);
42180  }
42181 
42183 
42186  const CImg<T>& save_ascii(const char *const filename) const {
42187  return _save_ascii(0,filename);
42188  }
42189 
42191  const CImg<T>& save_ascii(std::FILE *const file) const {
42192  return _save_ascii(file,0);
42193  }
42194 
42195  const CImg<T>& _save_ascii(std::FILE *const file, const char *const filename) const {
42196  if (!file && !filename)
42197  throw CImgArgumentException(_cimg_instance
42198  "save_ascii(): Specified filename is (null).",
42199  cimg_instance);
42200  std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
42201  std::fprintf(nfile,"%u %u %u %u\n",_width,_height,_depth,_spectrum);
42202  const T* ptrs = _data;
42203  cimg_forYZC(*this,y,z,c) {
42204  cimg_forX(*this,x) std::fprintf(nfile,"%.16g ",(double)*(ptrs++));
42205  std::fputc('\n',nfile);
42206  }
42207  if (!file) cimg::fclose(nfile);
42208  return *this;
42209  }
42210 
42212 
42215  const CImg<T>& save_cpp(const char *const filename) const {
42216  return _save_cpp(0,filename);
42217  }
42218 
42220  const CImg<T>& save_cpp(std::FILE *const file) const {
42221  return _save_cpp(file,0);
42222  }
42223 
42224  const CImg<T>& _save_cpp(std::FILE *const file, const char *const filename) const {
42225  if (!file && !filename)
42226  throw CImgArgumentException(_cimg_instance
42227  "save_cpp(): Specified filename is (null).",
42228  cimg_instance);
42229  std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
42230  char varname[1024] = { 0 };
42231  if (filename) std::sscanf(cimg::basename(filename),"%1023[a-zA-Z0-9_]",varname);
42232  if (!*varname) cimg_snprintf(varname,sizeof(varname),"unnamed");
42233  std::fprintf(nfile,
42234  "/* Define image '%s' of size %ux%ux%ux%u and type '%s' */\n"
42235  "%s data_%s[] = { %s\n ",
42236  varname,_width,_height,_depth,_spectrum,pixel_type(),pixel_type(),varname,
42237  is_empty()?"};":"");
42238  if (!is_empty()) for (unsigned long off = 0, siz = size()-1; off<=siz; ++off) {
42239  std::fprintf(nfile,cimg::type<T>::format(),cimg::type<T>::format((*this)[off]));
42240  if (off==siz) std::fprintf(nfile," };\n");
42241  else if (!((off+1)%16)) std::fprintf(nfile,",\n ");
42242  else std::fprintf(nfile,", ");
42243  }
42244  if (!file) cimg::fclose(nfile);
42245  return *this;
42246  }
42247 
42249 
42252  const CImg<T>& save_dlm(const char *const filename) const {
42253  return _save_dlm(0,filename);
42254  }
42255 
42257  const CImg<T>& save_dlm(std::FILE *const file) const {
42258  return _save_dlm(file,0);
42259  }
42260 
42261  const CImg<T>& _save_dlm(std::FILE *const file, const char *const filename) const {
42262  if (!file && !filename)
42263  throw CImgArgumentException(_cimg_instance
42264  "save_dlm(): Specified filename is (null).",
42265  cimg_instance);
42266  if (is_empty()) { cimg::fempty(file,filename); return *this; }
42267  if (_depth>1)
42268  cimg::warn(_cimg_instance
42269  "save_dlm(): Instance is volumetric, values along Z will be unrolled in file '%s'.",
42270  cimg_instance,
42271  filename?filename:"(FILE*)");
42272  if (_spectrum>1)
42273  cimg::warn(_cimg_instance
42274  "save_dlm(): Instance is multispectral, values along C will be unrolled in file '%s'.",
42275  cimg_instance,
42276  filename?filename:"(FILE*)");
42277 
42278  std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
42279  const T* ptrs = _data;
42280  cimg_forYZC(*this,y,z,c) {
42281  cimg_forX(*this,x) std::fprintf(nfile,"%.16g%s",(double)*(ptrs++),(x==width()-1)?"":",");
42282  std::fputc('\n',nfile);
42283  }
42284  if (!file) cimg::fclose(nfile);
42285  return *this;
42286  }
42287 
42289 
42292  const CImg<T>& save_bmp(const char *const filename) const {
42293  return _save_bmp(0,filename);
42294  }
42295 
42297  const CImg<T>& save_bmp(std::FILE *const file) const {
42298  return _save_bmp(file,0);
42299  }
42300 
42301  const CImg<T>& _save_bmp(std::FILE *const file, const char *const filename) const {
42302  if (!file && !filename)
42303  throw CImgArgumentException(_cimg_instance
42304  "save_bmp(): Specified filename is (null).",
42305  cimg_instance);
42306  if (is_empty()) { cimg::fempty(file,filename); return *this; }
42307  if (_depth>1)
42308  cimg::warn(_cimg_instance
42309  "save_bmp(): Instance is volumetric, only the first slice will be saved in file '%s'.",
42310  cimg_instance,
42311  filename?filename:"(FILE*)");
42312  if (_spectrum>3)
42313  cimg::warn(_cimg_instance
42314  "save_bmp(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
42315  cimg_instance,
42316  filename?filename:"(FILE*)");
42317 
42318  std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
42319  unsigned char header[54] = { 0 }, align_buf[4] = { 0 };
42320  const unsigned int
42321  align = (4 - (3*_width)%4)%4,
42322  buf_size = (3*_width + align)*height(),
42323  file_size = 54 + buf_size;
42324  header[0] = 'B'; header[1] = 'M';
42325  header[0x02] = file_size&0xFF;
42326  header[0x03] = (file_size>>8)&0xFF;
42327  header[0x04] = (file_size>>16)&0xFF;
42328  header[0x05] = (file_size>>24)&0xFF;
42329  header[0x0A] = 0x36;
42330  header[0x0E] = 0x28;
42331  header[0x12] = _width&0xFF;
42332  header[0x13] = (_width>>8)&0xFF;
42333  header[0x14] = (_width>>16)&0xFF;
42334  header[0x15] = (_width>>24)&0xFF;
42335  header[0x16] = _height&0xFF;
42336  header[0x17] = (_height>>8)&0xFF;
42337  header[0x18] = (_height>>16)&0xFF;
42338  header[0x19] = (_height>>24)&0xFF;
42339  header[0x1A] = 1;
42340  header[0x1B] = 0;
42341  header[0x1C] = 24;
42342  header[0x1D] = 0;
42343  header[0x22] = buf_size&0xFF;
42344  header[0x23] = (buf_size>>8)&0xFF;
42345  header[0x24] = (buf_size>>16)&0xFF;
42346  header[0x25] = (buf_size>>24)&0xFF;
42347  header[0x27] = 0x1;
42348  header[0x2B] = 0x1;
42349  cimg::fwrite(header,54,nfile);
42350 
42351  const T
42352  *ptr_r = data(0,_height-1,0,0),
42353  *ptr_g = (_spectrum>=2)?data(0,_height-1,0,1):0,
42354  *ptr_b = (_spectrum>=3)?data(0,_height-1,0,2):0;
42355 
42356  switch (_spectrum) {
42357  case 1 : {
42358  cimg_forY(*this,y) {
42359  cimg_forX(*this,x) {
42360  const unsigned char val = (unsigned char)*(ptr_r++);
42361  std::fputc(val,nfile); std::fputc(val,nfile); std::fputc(val,nfile);
42362  }
42363  cimg::fwrite(align_buf,align,nfile);
42364  ptr_r-=2*_width;
42365  }
42366  } break;
42367  case 2 : {
42368  cimg_forY(*this,y) {
42369  cimg_forX(*this,x) {
42370  std::fputc(0,nfile);
42371  std::fputc((unsigned char)(*(ptr_g++)),nfile);
42372  std::fputc((unsigned char)(*(ptr_r++)),nfile);
42373  }
42374  cimg::fwrite(align_buf,align,nfile);
42375  ptr_r-=2*_width; ptr_g-=2*_width;
42376  }
42377  } break;
42378  default : {
42379  cimg_forY(*this,y) {
42380  cimg_forX(*this,x) {
42381  std::fputc((unsigned char)(*(ptr_b++)),nfile);
42382  std::fputc((unsigned char)(*(ptr_g++)),nfile);
42383  std::fputc((unsigned char)(*(ptr_r++)),nfile);
42384  }
42385  cimg::fwrite(align_buf,align,nfile);
42386  ptr_r-=2*_width; ptr_g-=2*_width; ptr_b-=2*_width;
42387  }
42388  }
42389  }
42390  if (!file) cimg::fclose(nfile);
42391  return *this;
42392  }
42393 
42395 
42399  const CImg<T>& save_jpeg(const char *const filename, const unsigned int quality=100) const {
42400  return _save_jpeg(0,filename,quality);
42401  }
42402 
42404  const CImg<T>& save_jpeg(std::FILE *const file, const unsigned int quality=100) const {
42405  return _save_jpeg(file,0,quality);
42406  }
42407 
42408  const CImg<T>& _save_jpeg(std::FILE *const file, const char *const filename, const unsigned int quality) const {
42409  if (!file && !filename)
42410  throw CImgArgumentException(_cimg_instance
42411  "save_jpeg(): Specified filename is (null).",
42412  cimg_instance);
42413  if (is_empty()) { cimg::fempty(file,filename); return *this; }
42414  if (_depth>1)
42415  cimg::warn(_cimg_instance
42416  "save_jpeg(): Instance is volumetric, only the first slice will be saved in file '%s'.",
42417  cimg_instance,
42418  filename?filename:"(FILE*)");
42419 
42420 #ifndef cimg_use_jpeg
42421  if (!file) return save_other(filename,quality);
42422  else throw CImgIOException(_cimg_instance
42423  "save_jpeg(): Unable to save data in '(*FILE)' unless libjpeg is enabled.",
42424  cimg_instance);
42425 #else
42426  unsigned int dimbuf = 0;
42427  J_COLOR_SPACE colortype = JCS_RGB;
42428 
42429  switch(_spectrum) {
42430  case 1 : dimbuf = 1; colortype = JCS_GRAYSCALE; break;
42431  case 2 : dimbuf = 3; colortype = JCS_RGB; break;
42432  case 3 : dimbuf = 3; colortype = JCS_RGB; break;
42433  default : dimbuf = 4; colortype = JCS_CMYK; break;
42434  }
42435 
42436  // Call libjpeg functions
42437  struct jpeg_compress_struct cinfo;
42438  struct jpeg_error_mgr jerr;
42439  cinfo.err = jpeg_std_error(&jerr);
42440  jpeg_create_compress(&cinfo);
42441  std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
42442  jpeg_stdio_dest(&cinfo,nfile);
42443  cinfo.image_width = _width;
42444  cinfo.image_height = _height;
42445  cinfo.input_components = dimbuf;
42446  cinfo.in_color_space = colortype;
42447  jpeg_set_defaults(&cinfo);
42448  jpeg_set_quality(&cinfo,quality<100?quality:100,TRUE);
42449  jpeg_start_compress(&cinfo,TRUE);
42450 
42451  JSAMPROW row_pointer[1];
42452  CImg<ucharT> buffer((unsigned long)_width*dimbuf);
42453 
42454  while (cinfo.next_scanline<cinfo.image_height) {
42455  unsigned char *ptrd = buffer._data;
42456 
42457  // Fill pixel buffer
42458  switch (_spectrum) {
42459  case 1 : { // Greyscale images
42460  const T *ptr_g = data(0, cinfo.next_scanline);
42461  for(unsigned int b = 0; b < cinfo.image_width; b++)
42462  *(ptrd++) = (unsigned char)*(ptr_g++);
42463  } break;
42464  case 2 : { // RG images
42465  const T *ptr_r = data(0,cinfo.next_scanline,0,0),
42466  *ptr_g = data(0,cinfo.next_scanline,0,1);
42467  for(unsigned int b = 0; b < cinfo.image_width; ++b) {
42468  *(ptrd++) = (unsigned char)*(ptr_r++);
42469  *(ptrd++) = (unsigned char)*(ptr_g++);
42470  *(ptrd++) = 0;
42471  }
42472  } break;
42473  case 3 : { // RGB images
42474  const T *ptr_r = data(0,cinfo.next_scanline,0,0),
42475  *ptr_g = data(0,cinfo.next_scanline,0,1),
42476  *ptr_b = data(0,cinfo.next_scanline,0,2);
42477  for(unsigned int b = 0; b < cinfo.image_width; ++b) {
42478  *(ptrd++) = (unsigned char)*(ptr_r++);
42479  *(ptrd++) = (unsigned char)*(ptr_g++);
42480  *(ptrd++) = (unsigned char)*(ptr_b++);
42481  }
42482  } break;
42483  default : { // CMYK images
42484  const T *ptr_r = data(0,cinfo.next_scanline,0,0),
42485  *ptr_g = data(0,cinfo.next_scanline,0,1),
42486  *ptr_b = data(0,cinfo.next_scanline,0,2),
42487  *ptr_a = data(0,cinfo.next_scanline,0,3);
42488  for(unsigned int b = 0; b < cinfo.image_width; ++b) {
42489  *(ptrd++) = (unsigned char)*(ptr_r++);
42490  *(ptrd++) = (unsigned char)*(ptr_g++);
42491  *(ptrd++) = (unsigned char)*(ptr_b++);
42492  *(ptrd++) = (unsigned char)*(ptr_a++);
42493  }
42494  }
42495  }
42496  *row_pointer = buffer._data;
42497  jpeg_write_scanlines(&cinfo,row_pointer,1);
42498  }
42499  jpeg_finish_compress(&cinfo);
42500  if (!file) cimg::fclose(nfile);
42501  jpeg_destroy_compress(&cinfo);
42502  return *this;
42503 #endif
42504  }
42505 
42507 
42511  const CImg<T>& save_magick(const char *const filename, const unsigned int bytes_per_pixel=0) const {
42512  if (!filename)
42513  throw CImgArgumentException(_cimg_instance
42514  "save_magick(): Specified filename is (null).",
42515  cimg_instance);
42516  if (is_empty()) { cimg::fempty(0,filename); return *this; }
42517 
42518 #ifdef cimg_use_magick
42519  double stmin, stmax = (double)max_min(stmin);
42520  if (_depth>1)
42521  cimg::warn(_cimg_instance
42522  "save_magick(): Instance is volumetric, only the first slice will be saved in file '%s'.",
42523  cimg_instance,
42524  filename);
42525 
42526  if (_spectrum>3)
42527  cimg::warn(_cimg_instance
42528  "save_magick(): Instance is multispectral, only the three first channels will be "
42529  "saved in file '%s'.",
42530  cimg_instance,
42531  filename);
42532 
42533  if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
42534  cimg::warn(_cimg_instance
42535  "save_magick(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
42536  cimg_instance,
42537  filename,stmin,stmax);
42538 
42539  Magick::Image image(Magick::Geometry(_width,_height),"black");
42540  image.type(Magick::TrueColorType);
42541  image.depth(bytes_per_pixel?(8*bytes_per_pixel):(stmax>=256?16:8));
42542  const T
42543  *ptr_r = data(0,0,0,0),
42544  *ptr_g = _spectrum>1?data(0,0,0,1):0,
42545  *ptr_b = _spectrum>2?data(0,0,0,2):0;
42546  Magick::PixelPacket *pixels = image.getPixels(0,0,_width,_height);
42547  switch (_spectrum) {
42548  case 1 : // Scalar images
42549  for (unsigned long off = (unsigned long)_width*_height; off; --off) {
42550  pixels->red = pixels->green = pixels->blue = (Magick::Quantum)*(ptr_r++);
42551  ++pixels;
42552  }
42553  break;
42554  case 2 : // RG images
42555  for (unsigned long off = (unsigned long)_width*_height; off; --off) {
42556  pixels->red = (Magick::Quantum)*(ptr_r++);
42557  pixels->green = (Magick::Quantum)*(ptr_g++);
42558  pixels->blue = 0; ++pixels;
42559  }
42560  break;
42561  default : // RGB images
42562  for (unsigned long off = (unsigned long)_width*_height; off; --off) {
42563  pixels->red = (Magick::Quantum)*(ptr_r++);
42564  pixels->green = (Magick::Quantum)*(ptr_g++);
42565  pixels->blue = (Magick::Quantum)*(ptr_b++);
42566  ++pixels;
42567  }
42568  }
42569  image.syncPixels();
42570  image.write(filename);
42571  return *this;
42572 #else
42573  cimg::unused(bytes_per_pixel);
42574  throw CImgIOException(_cimg_instance
42575  "save_magick(): Unable to save file '%s' unless libMagick++ is enabled.",
42576  cimg_instance,
42577  filename);
42578 #endif
42579  }
42580 
42582 
42586  const CImg<T>& save_png(const char *const filename, const unsigned int bytes_per_pixel=0) const {
42587  return _save_png(0,filename,bytes_per_pixel);
42588  }
42589 
42591  const CImg<T>& save_png(std::FILE *const file, const unsigned int bytes_per_pixel=0) const {
42592  return _save_png(file,0,bytes_per_pixel);
42593  }
42594 
42595  const CImg<T>& _save_png(std::FILE *const file, const char *const filename,
42596  const unsigned int bytes_per_pixel=0) const {
42597  if (!file && !filename)
42598  throw CImgArgumentException(_cimg_instance
42599  "save_png(): Specified filename is (null).",
42600  cimg_instance);
42601  if (is_empty()) { cimg::fempty(file,filename); return *this; }
42602 
42603 #ifndef cimg_use_png
42604  cimg::unused(bytes_per_pixel);
42605  if (!file) return save_other(filename);
42606  else throw CImgIOException(_cimg_instance
42607  "save_png(): Unable to save data in '(*FILE)' unless libpng is enabled.",
42608  cimg_instance);
42609 #else
42610  const char *volatile nfilename = filename; // two 'volatile' here to remove a g++ warning due to 'setjmp'.
42611  std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"wb");
42612  volatile double stmin, stmax = (double)max_min(stmin);
42613 
42614  if (_depth>1)
42615  cimg::warn(_cimg_instance
42616  "save_png(): Instance is volumetric, only the first slice will be saved in file '%s'.",
42617  cimg_instance,
42618  filename);
42619 
42620  if (_spectrum>4)
42621  cimg::warn(_cimg_instance
42622  "save_png(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
42623  cimg_instance,
42624  filename);
42625 
42626  if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
42627  cimg::warn(_cimg_instance
42628  "save_png(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
42629  cimg_instance,
42630  filename,stmin,stmax);
42631 
42632  // Setup PNG structures for write
42633  png_voidp user_error_ptr = 0;
42634  png_error_ptr user_error_fn = 0, user_warning_fn = 0;
42635  png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,user_error_ptr, user_error_fn,
42636  user_warning_fn);
42637  if(!png_ptr){
42638  if (!file) cimg::fclose(nfile);
42639  throw CImgIOException(_cimg_instance
42640  "save_png(): Failed to initialize 'png_ptr' structure when saving file '%s'.",
42641  cimg_instance,
42642  nfilename?nfilename:"(FILE*)");
42643  }
42644  png_infop info_ptr = png_create_info_struct(png_ptr);
42645  if (!info_ptr) {
42646  png_destroy_write_struct(&png_ptr,(png_infopp)0);
42647  if (!file) cimg::fclose(nfile);
42648  throw CImgIOException(_cimg_instance
42649  "save_png(): Failed to initialize 'info_ptr' structure when saving file '%s'.",
42650  cimg_instance,
42651  nfilename?nfilename:"(FILE*)");
42652  }
42653  if (setjmp(png_jmpbuf(png_ptr))) {
42654  png_destroy_write_struct(&png_ptr, &info_ptr);
42655  if (!file) cimg::fclose(nfile);
42656  throw CImgIOException(_cimg_instance
42657  "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.",
42658  cimg_instance,
42659  nfilename?nfilename:"(FILE*)");
42660  }
42661  png_init_io(png_ptr, nfile);
42662 
42663  const int bit_depth = bytes_per_pixel?(bytes_per_pixel*8):(stmax>=256?16:8);
42664 
42665  int color_type;
42666  switch (spectrum()) {
42667  case 1 : color_type = PNG_COLOR_TYPE_GRAY; break;
42668  case 2 : color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break;
42669  case 3 : color_type = PNG_COLOR_TYPE_RGB; break;
42670  default : color_type = PNG_COLOR_TYPE_RGB_ALPHA;
42671  }
42672  const int interlace_type = PNG_INTERLACE_NONE;
42673  const int compression_type = PNG_COMPRESSION_TYPE_DEFAULT;
42674  const int filter_method = PNG_FILTER_TYPE_DEFAULT;
42675  png_set_IHDR(png_ptr,info_ptr,_width,_height,bit_depth,color_type,interlace_type,compression_type,filter_method);
42676  png_write_info(png_ptr,info_ptr);
42677  const int byte_depth = bit_depth>>3;
42678  const int numChan = spectrum()>4?4:spectrum();
42679  const int pixel_bit_depth_flag = numChan * (bit_depth-1);
42680 
42681  // Allocate Memory for Image Save and Fill pixel data
42682  png_bytep *const imgData = new png_byte*[_height];
42683  for (unsigned int row = 0; row<_height; ++row) imgData[row] = new png_byte[byte_depth*numChan*_width];
42684  const T *pC0 = data(0,0,0,0);
42685  switch (pixel_bit_depth_flag) {
42686  case 7 : { // Gray 8-bit
42687  cimg_forY(*this,y) {
42688  unsigned char *ptrd = imgData[y];
42689  cimg_forX(*this,x) *(ptrd++) = (unsigned char)*(pC0++);
42690  }
42691  } break;
42692  case 14 : { // Gray w/ Alpha 8-bit
42693  const T *pC1 = data(0,0,0,1);
42694  cimg_forY(*this,y) {
42695  unsigned char *ptrd = imgData[y];
42696  cimg_forX(*this,x) {
42697  *(ptrd++) = (unsigned char)*(pC0++);
42698  *(ptrd++) = (unsigned char)*(pC1++);
42699  }
42700  }
42701  } break;
42702  case 21 : { // RGB 8-bit
42703  const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2);
42704  cimg_forY(*this,y) {
42705  unsigned char *ptrd = imgData[y];
42706  cimg_forX(*this,x) {
42707  *(ptrd++) = (unsigned char)*(pC0++);
42708  *(ptrd++) = (unsigned char)*(pC1++);
42709  *(ptrd++) = (unsigned char)*(pC2++);
42710  }
42711  }
42712  } break;
42713  case 28 : { // RGB x/ Alpha 8-bit
42714  const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3);
42715  cimg_forY(*this,y){
42716  unsigned char *ptrd = imgData[y];
42717  cimg_forX(*this,x){
42718  *(ptrd++) = (unsigned char)*(pC0++);
42719  *(ptrd++) = (unsigned char)*(pC1++);
42720  *(ptrd++) = (unsigned char)*(pC2++);
42721  *(ptrd++) = (unsigned char)*(pC3++);
42722  }
42723  }
42724  } break;
42725  case 15 : { // Gray 16-bit
42726  cimg_forY(*this,y){
42727  unsigned short *ptrd = (unsigned short*)(imgData[y]);
42728  cimg_forX(*this,x) *(ptrd++) = (unsigned short)*(pC0++);
42729  if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],_width);
42730  }
42731  } break;
42732  case 30 : { // Gray w/ Alpha 16-bit
42733  const T *pC1 = data(0,0,0,1);
42734  cimg_forY(*this,y){
42735  unsigned short *ptrd = (unsigned short*)(imgData[y]);
42736  cimg_forX(*this,x) {
42737  *(ptrd++) = (unsigned short)*(pC0++);
42738  *(ptrd++) = (unsigned short)*(pC1++);
42739  }
42740  if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],2*_width);
42741  }
42742  } break;
42743  case 45 : { // RGB 16-bit
42744  const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2);
42745  cimg_forY(*this,y) {
42746  unsigned short *ptrd = (unsigned short*)(imgData[y]);
42747  cimg_forX(*this,x) {
42748  *(ptrd++) = (unsigned short)*(pC0++);
42749  *(ptrd++) = (unsigned short)*(pC1++);
42750  *(ptrd++) = (unsigned short)*(pC2++);
42751  }
42752  if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],3*_width);
42753  }
42754  } break;
42755  case 60 : { // RGB w/ Alpha 16-bit
42756  const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3);
42757  cimg_forY(*this,y) {
42758  unsigned short *ptrd = (unsigned short*)(imgData[y]);
42759  cimg_forX(*this,x) {
42760  *(ptrd++) = (unsigned short)*(pC0++);
42761  *(ptrd++) = (unsigned short)*(pC1++);
42762  *(ptrd++) = (unsigned short)*(pC2++);
42763  *(ptrd++) = (unsigned short)*(pC3++);
42764  }
42765  if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],4*_width);
42766  }
42767  } break;
42768  default :
42769  if (!file) cimg::fclose(nfile);
42770  throw CImgIOException(_cimg_instance
42771  "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.",
42772  cimg_instance,
42773  nfilename?nfilename:"(FILE*)");
42774  }
42775  png_write_image(png_ptr,imgData);
42776  png_write_end(png_ptr,info_ptr);
42777  png_destroy_write_struct(&png_ptr, &info_ptr);
42778 
42779  // Deallocate Image Write Memory
42780  cimg_forY(*this,n) delete[] imgData[n];
42781  delete[] imgData;
42782 
42783  if (!file) cimg::fclose(nfile);
42784  return *this;
42785 #endif
42786  }
42787 
42789 
42793  const CImg<T>& save_pnm(const char *const filename, const unsigned int bytes_per_pixel=0) const {
42794  return _save_pnm(0,filename,bytes_per_pixel);
42795  }
42796 
42798  const CImg<T>& save_pnm(std::FILE *const file, const unsigned int bytes_per_pixel=0) const {
42799  return _save_pnm(file,0,bytes_per_pixel);
42800  }
42801 
42802  const CImg<T>& _save_pnm(std::FILE *const file, const char *const filename,
42803  const unsigned int bytes_per_pixel=0) const {
42804  if (!file && !filename)
42805  throw CImgArgumentException(_cimg_instance
42806  "save_pnm(): Specified filename is (null).",
42807  cimg_instance);
42808  if (is_empty()) { cimg::fempty(file,filename); return *this; }
42809 
42810  double stmin, stmax = (double)max_min(stmin);
42811  if (_depth>1)
42812  cimg::warn(_cimg_instance
42813  "save_pnm(): Instance is volumetric, only the first slice will be saved in file '%s'.",
42814  cimg_instance,
42815  filename?filename:"(FILE*)");
42816  if (_spectrum>3)
42817  cimg::warn(_cimg_instance
42818  "save_pnm(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
42819  cimg_instance,
42820  filename?filename:"(FILE*)");
42821  if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
42822  cimg::warn(_cimg_instance
42823  "save_pnm(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
42824  cimg_instance,
42825  stmin,stmax,filename?filename:"(FILE*)");
42826 
42827  std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
42828  const T
42829  *ptr_r = data(0,0,0,0),
42830  *ptr_g = (_spectrum>=2)?data(0,0,0,1):0,
42831  *ptr_b = (_spectrum>=3)?data(0,0,0,2):0;
42832  const unsigned long buf_size = cimg::min(1024*1024UL,_width*_height*(_spectrum==1?1UL:3UL));
42833 
42834  std::fprintf(nfile,"P%c\n%u %u\n%u\n",
42835  (_spectrum==1?'5':'6'),_width,_height,stmax<256?255:(stmax<4096?4095:65535));
42836 
42837  switch (_spectrum) {
42838  case 1 : { // Scalar image
42839  if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PGM 8 bits
42840  CImg<ucharT> buf(buf_size);
42841  for (long to_write = (long)_width*_height; to_write>0; ) {
42842  const unsigned long N = cimg::min((unsigned long)to_write,buf_size);
42843  unsigned char *ptrd = buf._data;
42844  for (unsigned long i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr_r++);
42845  cimg::fwrite(buf._data,N,nfile);
42846  to_write-=N;
42847  }
42848  } else { // Binary PGM 16 bits
42849  CImg<ushortT> buf(buf_size);
42850  for (long to_write = (long)_width*_height; to_write>0; ) {
42851  const unsigned long N = cimg::min((unsigned long)to_write,buf_size);
42852  unsigned short *ptrd = buf._data;
42853  for (unsigned long i = N; i>0; --i) *(ptrd++) = (unsigned short)*(ptr_r++);
42854  if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
42855  cimg::fwrite(buf._data,N,nfile);
42856  to_write-=N;
42857  }
42858  }
42859  } break;
42860  case 2 : { // RG image
42861  if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits
42862  CImg<ucharT> buf(buf_size);
42863  for (long to_write = (long)_width*_height; to_write>0; ) {
42864  const unsigned long N = cimg::min((unsigned long)to_write,buf_size/3);
42865  unsigned char *ptrd = buf._data;
42866  for (unsigned long i = N; i>0; --i) {
42867  *(ptrd++) = (unsigned char)*(ptr_r++);
42868  *(ptrd++) = (unsigned char)*(ptr_g++);
42869  *(ptrd++) = 0;
42870  }
42871  cimg::fwrite(buf._data,3*N,nfile);
42872  to_write-=N;
42873  }
42874  } else { // Binary PPM 16 bits
42875  CImg<ushortT> buf(buf_size);
42876  for (long to_write = (long)_width*_height; to_write>0; ) {
42877  const unsigned long N = cimg::min((unsigned long)to_write,buf_size/3);
42878  unsigned short *ptrd = buf._data;
42879  for (unsigned long i = N; i>0; --i) {
42880  *(ptrd++) = (unsigned short)*(ptr_r++);
42881  *(ptrd++) = (unsigned short)*(ptr_g++);
42882  *(ptrd++) = 0;
42883  }
42884  if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
42885  cimg::fwrite(buf._data,3*N,nfile);
42886  to_write-=N;
42887  }
42888  }
42889  } break;
42890  default : { // RGB image
42891  if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits
42892  CImg<ucharT> buf(buf_size);
42893  for (long to_write = (long)_width*_height; to_write>0; ) {
42894  const unsigned long N = cimg::min((unsigned long)to_write,buf_size/3);
42895  unsigned char *ptrd = buf._data;
42896  for (unsigned long i = N; i>0; --i) {
42897  *(ptrd++) = (unsigned char)*(ptr_r++);
42898  *(ptrd++) = (unsigned char)*(ptr_g++);
42899  *(ptrd++) = (unsigned char)*(ptr_b++);
42900  }
42901  cimg::fwrite(buf._data,3*N,nfile);
42902  to_write-=N;
42903  }
42904  } else { // Binary PPM 16 bits
42905  CImg<ushortT> buf(buf_size);
42906  for (long to_write = (long)_width*_height; to_write>0; ) {
42907  const unsigned long N = cimg::min((unsigned long)to_write,buf_size/3);
42908  unsigned short *ptrd = buf._data;
42909  for (unsigned long i = N; i>0; --i) {
42910  *(ptrd++) = (unsigned short)*(ptr_r++);
42911  *(ptrd++) = (unsigned short)*(ptr_g++);
42912  *(ptrd++) = (unsigned short)*(ptr_b++);
42913  }
42914  if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
42915  cimg::fwrite(buf._data,3*N,nfile);
42916  to_write-=N;
42917  }
42918  }
42919  }
42920  }
42921  if (!file) cimg::fclose(nfile);
42922  return *this;
42923  }
42924 
42926 
42929  const CImg<T>& save_pnk(const char *const filename) const {
42930  return _save_pnk(0,filename);
42931  }
42932 
42934  const CImg<T>& save_pnk(std::FILE *const file) const {
42935  return _save_pnk(file,0);
42936  }
42937 
42938  const CImg<T>& _save_pnk(std::FILE *const file, const char *const filename) const {
42939  if (!file && !filename)
42940  throw CImgArgumentException(_cimg_instance
42941  "save_pnk(): Specified filename is (null).",
42942  cimg_instance);
42943  if (is_empty()) { cimg::fempty(file,filename); return *this; }
42944  if (_spectrum>1)
42945  cimg::warn(_cimg_instance
42946  "save_pnk(): Instance is multispectral, only the first channel will be saved in file '%s'.",
42947  cimg_instance,
42948  filename?filename:"(FILE*)");
42949 
42950  const unsigned long buf_size = cimg::min(1024*1024LU,_width*_height*_depth);
42951  std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
42952  const T *ptr = data(0,0,0,0);
42953 
42954  if (!cimg::type<T>::is_float() && sizeof(T)==1 && _depth<2) // Can be saved as regular PNM file.
42955  _save_pnm(file,filename,0);
42956  else if (!cimg::type<T>::is_float() && sizeof(T)==1) { // Save as extended P5 file: Binary byte-valued 3d.
42957  std::fprintf(nfile,"P5\n%u %u %u\n255\n",_width,_height,_depth);
42958  CImg<ucharT> buf(buf_size);
42959  for (long to_write = (long)_width*_height*_depth; to_write>0; ) {
42960  const unsigned long N = cimg::min((unsigned long)to_write,buf_size);
42961  unsigned char *ptrd = buf._data;
42962  for (unsigned long i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr++);
42963  cimg::fwrite(buf._data,N,nfile);
42964  to_write-=N;
42965  }
42966  } else if (!cimg::type<T>::is_float()) { // Save as P8: Binary int32-valued 3d.
42967  if (_depth>1) std::fprintf(nfile,"P8\n%u %u %u\n%d\n",_width,_height,_depth,(int)max());
42968  else std::fprintf(nfile,"P8\n%u %u\n%d\n",_width,_height,(int)max());
42969  CImg<intT> buf(buf_size);
42970  for (long to_write = (long)_width*_height*_depth; to_write>0; ) {
42971  const unsigned long N = cimg::min((unsigned long)to_write,buf_size);
42972  int *ptrd = buf._data;
42973  for (unsigned long i = N; i>0; --i) *(ptrd++) = (int)*(ptr++);
42974  cimg::fwrite(buf._data,N,nfile);
42975  to_write-=N;
42976  }
42977  } else { // Save as P9: Binary float-valued 3d.
42978  if (_depth>1) std::fprintf(nfile,"P9\n%u %u %u\n%g\n",_width,_height,_depth,(double)max());
42979  else std::fprintf(nfile,"P9\n%u %u\n%g\n",_width,_height,(double)max());
42980  CImg<floatT> buf(buf_size);
42981  for (long to_write = (long)_width*_height*_depth; to_write>0; ) {
42982  const unsigned long N = cimg::min((unsigned long)to_write,buf_size);
42983  float *ptrd = buf._data;
42984  for (unsigned long i = N; i>0; --i) *(ptrd++) = (float)*(ptr++);
42985  cimg::fwrite(buf._data,N,nfile);
42986  to_write-=N;
42987  }
42988  }
42989 
42990  if (!file) cimg::fclose(nfile);
42991  return *this;
42992  }
42993 
42995 
42998  const CImg<T>& save_pfm(const char *const filename) const {
42999  return get_mirror('y')._save_pfm(0,filename);
43000  }
43001 
43003  const CImg<T>& save_pfm(std::FILE *const file) const {
43004  return get_mirror('y')._save_pfm(file,0);
43005  }
43006 
43007  const CImg<T>& _save_pfm(std::FILE *const file, const char *const filename) const {
43008  if (!file && !filename)
43009  throw CImgArgumentException(_cimg_instance
43010  "save_pfm(): Specified filename is (null).",
43011  cimg_instance);
43012  if (is_empty()) { cimg::fempty(file,filename); return *this; }
43013  if (_depth>1)
43014  cimg::warn(_cimg_instance
43015  "save_pfm(): Instance is volumetric, only the first slice will be saved in file '%s'.",
43016  cimg_instance,
43017  filename?filename:"(FILE*)");
43018  if (_spectrum>3)
43019  cimg::warn(_cimg_instance
43020  "save_pfm(): image instance is multispectral, only the three first channels will be saved "
43021  "in file '%s'.",
43022  cimg_instance,
43023  filename?filename:"(FILE*)");
43024 
43025  std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
43026  const T
43027  *ptr_r = data(0,0,0,0),
43028  *ptr_g = (_spectrum>=2)?data(0,0,0,1):0,
43029  *ptr_b = (_spectrum>=3)?data(0,0,0,2):0;
43030  const unsigned int buf_size = cimg::min(1024*1024U,_width*_height*(_spectrum==1?1:3));
43031 
43032  std::fprintf(nfile,"P%c\n%u %u\n1.0\n",
43033  (_spectrum==1?'f':'F'),_width,_height);
43034 
43035  switch (_spectrum) {
43036  case 1 : { // Scalar image
43037  CImg<floatT> buf(buf_size);
43038  for (long to_write = (long)_width*_height; to_write>0; ) {
43039  const unsigned long N = cimg::min((unsigned long)to_write,buf_size);
43040  float *ptrd = buf._data;
43041  for (unsigned long i = N; i>0; --i) *(ptrd++) = (float)*(ptr_r++);
43042  if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
43043  cimg::fwrite(buf._data,N,nfile);
43044  to_write-=N;
43045  }
43046  } break;
43047  case 2 : { // RG image
43048  CImg<floatT> buf(buf_size);
43049  for (long to_write = (long)_width*_height; to_write>0; ) {
43050  const unsigned int N = cimg::min((unsigned int)to_write,buf_size/3);
43051  float *ptrd = buf._data;
43052  for (unsigned long i = N; i>0; --i) {
43053  *(ptrd++) = (float)*(ptr_r++);
43054  *(ptrd++) = (float)*(ptr_g++);
43055  *(ptrd++) = 0;
43056  }
43057  if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
43058  cimg::fwrite(buf._data,3*N,nfile);
43059  to_write-=N;
43060  }
43061  } break;
43062  default : { // RGB image
43063  CImg<floatT> buf(buf_size);
43064  for (long to_write = (long)_width*_height; to_write>0; ) {
43065  const unsigned int N = cimg::min((unsigned int)to_write,buf_size/3);
43066  float *ptrd = buf._data;
43067  for (unsigned long i = N; i>0; --i) {
43068  *(ptrd++) = (float)*(ptr_r++);
43069  *(ptrd++) = (float)*(ptr_g++);
43070  *(ptrd++) = (float)*(ptr_b++);
43071  }
43072  if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
43073  cimg::fwrite(buf._data,3*N,nfile);
43074  to_write-=N;
43075  }
43076  }
43077  }
43078  if (!file) cimg::fclose(nfile);
43079  return *this;
43080  }
43081 
43083 
43086  const CImg<T>& save_rgb(const char *const filename) const {
43087  return _save_rgb(0,filename);
43088  }
43089 
43091  const CImg<T>& save_rgb(std::FILE *const file) const {
43092  return _save_rgb(file,0);
43093  }
43094 
43095  const CImg<T>& _save_rgb(std::FILE *const file, const char *const filename) const {
43096  if (!file && !filename)
43097  throw CImgArgumentException(_cimg_instance
43098  "save_rgb(): Specified filename is (null).",
43099  cimg_instance);
43100  if (is_empty()) { cimg::fempty(file,filename); return *this; }
43101  if (_spectrum!=3)
43102  cimg::warn(_cimg_instance
43103  "save_rgb(): image instance has not exactly 3 channels, for file '%s'.",
43104  cimg_instance,
43105  filename?filename:"(FILE*)");
43106 
43107  std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
43108  const unsigned long wh = (unsigned long)_width*_height;
43109  unsigned char *const buffer = new unsigned char[3*wh], *nbuffer = buffer;
43110  const T
43111  *ptr1 = data(0,0,0,0),
43112  *ptr2 = _spectrum>1?data(0,0,0,1):0,
43113  *ptr3 = _spectrum>2?data(0,0,0,2):0;
43114  switch (_spectrum) {
43115  case 1 : { // Scalar image
43116  for (unsigned long k = 0; k<wh; ++k) {
43117  const unsigned char val = (unsigned char)*(ptr1++);
43118  *(nbuffer++) = val;
43119  *(nbuffer++) = val;
43120  *(nbuffer++) = val;
43121  }
43122  } break;
43123  case 2 : { // RG image
43124  for (unsigned long k = 0; k<wh; ++k) {
43125  *(nbuffer++) = (unsigned char)(*(ptr1++));
43126  *(nbuffer++) = (unsigned char)(*(ptr2++));
43127  *(nbuffer++) = 0;
43128  }
43129  } break;
43130  default : { // RGB image
43131  for (unsigned long k = 0; k<wh; ++k) {
43132  *(nbuffer++) = (unsigned char)(*(ptr1++));
43133  *(nbuffer++) = (unsigned char)(*(ptr2++));
43134  *(nbuffer++) = (unsigned char)(*(ptr3++));
43135  }
43136  }
43137  }
43138  cimg::fwrite(buffer,3*wh,nfile);
43139  if (!file) cimg::fclose(nfile);
43140  delete[] buffer;
43141  return *this;
43142  }
43143 
43145 
43148  const CImg<T>& save_rgba(const char *const filename) const {
43149  return _save_rgba(0,filename);
43150  }
43151 
43153  const CImg<T>& save_rgba(std::FILE *const file) const {
43154  return _save_rgba(file,0);
43155  }
43156 
43157  const CImg<T>& _save_rgba(std::FILE *const file, const char *const filename) const {
43158  if (!file && !filename)
43159  throw CImgArgumentException(_cimg_instance
43160  "save_rgba(): Specified filename is (null).",
43161  cimg_instance);
43162  if (is_empty()) { cimg::fempty(file,filename); return *this; }
43163  if (_spectrum!=4)
43164  cimg::warn(_cimg_instance
43165  "save_rgba(): image instance has not exactly 4 channels, for file '%s'.",
43166  cimg_instance,
43167  filename?filename:"(FILE*)");
43168 
43169  std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
43170  const unsigned long wh = (unsigned long)_width*_height;
43171  unsigned char *const buffer = new unsigned char[4*wh], *nbuffer = buffer;
43172  const T
43173  *ptr1 = data(0,0,0,0),
43174  *ptr2 = _spectrum>1?data(0,0,0,1):0,
43175  *ptr3 = _spectrum>2?data(0,0,0,2):0,
43176  *ptr4 = _spectrum>3?data(0,0,0,3):0;
43177  switch (_spectrum) {
43178  case 1 : { // Scalar images
43179  for (unsigned long k = 0; k<wh; ++k) {
43180  const unsigned char val = (unsigned char)*(ptr1++);
43181  *(nbuffer++) = val;
43182  *(nbuffer++) = val;
43183  *(nbuffer++) = val;
43184  *(nbuffer++) = 255;
43185  }
43186  } break;
43187  case 2 : { // RG images
43188  for (unsigned long k = 0; k<wh; ++k) {
43189  *(nbuffer++) = (unsigned char)(*(ptr1++));
43190  *(nbuffer++) = (unsigned char)(*(ptr2++));
43191  *(nbuffer++) = 0;
43192  *(nbuffer++) = 255;
43193  }
43194  } break;
43195  case 3 : { // RGB images
43196  for (unsigned long k = 0; k<wh; ++k) {
43197  *(nbuffer++) = (unsigned char)(*(ptr1++));
43198  *(nbuffer++) = (unsigned char)(*(ptr2++));
43199  *(nbuffer++) = (unsigned char)(*(ptr3++));
43200  *(nbuffer++) = 255;
43201  }
43202  } break;
43203  default : { // RGBA images
43204  for (unsigned long k = 0; k<wh; ++k) {
43205  *(nbuffer++) = (unsigned char)(*(ptr1++));
43206  *(nbuffer++) = (unsigned char)(*(ptr2++));
43207  *(nbuffer++) = (unsigned char)(*(ptr3++));
43208  *(nbuffer++) = (unsigned char)(*(ptr4++));
43209  }
43210  }
43211  }
43212  cimg::fwrite(buffer,4*wh,nfile);
43213  if (!file) cimg::fclose(nfile);
43214  delete[] buffer;
43215  return *this;
43216  }
43217 
43219 
43232  const CImg<T>& save_tiff(const char *const filename, const unsigned int compression_type=0) const {
43233  if (!filename)
43234  throw CImgArgumentException(_cimg_instance
43235  "save_tiff(): Specified filename is (null).",
43236  cimg_instance);
43237  if (is_empty()) { cimg::fempty(0,filename); return *this; }
43238 
43239 #ifdef cimg_use_tiff
43240  TIFF *tif = TIFFOpen(filename,"w");
43241  if (tif) {
43242  cimg_forZ(*this,z) get_slice(z)._save_tiff(tif,z,compression_type);
43243  TIFFClose(tif);
43244  } else throw CImgIOException(_cimg_instance
43245  "save_tiff(): Failed to open file '%s' for writing.",
43246  cimg_instance,
43247  filename);
43248  return *this;
43249 #else
43250  cimg::unused(compression_type);
43251  return save_other(filename);
43252 #endif
43253  }
43254 
43255 #ifdef cimg_use_tiff
43256 
43257 #define _cimg_save_tiff(types,typed,compression_type) if (!std::strcmp(types,pixel_type())) { \
43258  const typed foo = (typed)0; return _save_tiff(tif,directory,foo,compression_type); }
43259 
43260  // [internal] Save a plane into a tiff file
43261  template<typename t>
43262  const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory, const t& pixel_t,
43263  const unsigned int compression_type) const {
43264  if (is_empty() || !tif || pixel_t) return *this;
43265  const char *const filename = TIFFFileName(tif);
43266  uint32 rowsperstrip = (uint32)-1;
43267  uint16 spp = _spectrum, bpp = sizeof(t)*8, photometric;
43268  if (spp==3 || spp==4) photometric = PHOTOMETRIC_RGB;
43269  else photometric = PHOTOMETRIC_MINISBLACK;
43270  TIFFSetDirectory(tif,directory);
43271  TIFFSetField(tif,TIFFTAG_IMAGEWIDTH,_width);
43272  TIFFSetField(tif,TIFFTAG_IMAGELENGTH,_height);
43273  TIFFSetField(tif,TIFFTAG_ORIENTATION,ORIENTATION_TOPLEFT);
43274  TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,spp);
43275  if (cimg::type<t>::is_float()) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,3);
43276  else if (cimg::type<t>::min()==0) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,1);
43277  else TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,2);
43278  TIFFSetField(tif,TIFFTAG_BITSPERSAMPLE,bpp);
43279  TIFFSetField(tif,TIFFTAG_PLANARCONFIG,PLANARCONFIG_CONTIG);
43280  TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,photometric);
43281  TIFFSetField(tif,TIFFTAG_COMPRESSION,compression_type?(compression_type-1):COMPRESSION_NONE);
43282  rowsperstrip = TIFFDefaultStripSize(tif,rowsperstrip);
43283  TIFFSetField(tif,TIFFTAG_ROWSPERSTRIP,rowsperstrip);
43284  TIFFSetField(tif,TIFFTAG_FILLORDER,FILLORDER_MSB2LSB);
43285  TIFFSetField(tif,TIFFTAG_SOFTWARE,"CImg");
43286  t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
43287  if (buf) {
43288  for (unsigned int row = 0; row<_height; row+=rowsperstrip) {
43289  uint32 nrow = (row + rowsperstrip>_height?_height-row:rowsperstrip);
43290  tstrip_t strip = TIFFComputeStrip(tif,row,0);
43291  tsize_t i = 0;
43292  for (unsigned int rr = 0; rr<nrow; ++rr)
43293  for (unsigned int cc = 0; cc<_width; ++cc)
43294  for (unsigned int vv = 0; vv<spp; ++vv)
43295  buf[i++] = (t)(*this)(cc,row + rr,0,vv);
43296  if (TIFFWriteEncodedStrip(tif,strip,buf,i*sizeof(t))<0)
43297  throw CImgIOException(_cimg_instance
43298  "save_tiff(): Invalid strip writing when saving file '%s'.",
43299  cimg_instance,
43300  filename?filename:"(FILE*)");
43301  }
43302  _TIFFfree(buf);
43303  }
43304  TIFFWriteDirectory(tif);
43305  return (*this);
43306  }
43307 
43308  const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int compression_type) const {
43309  _cimg_save_tiff("bool",unsigned char,compression_type);
43310  _cimg_save_tiff("char",char,compression_type);
43311  _cimg_save_tiff("unsigned char",unsigned char,compression_type);
43312  _cimg_save_tiff("short",short,compression_type);
43313  _cimg_save_tiff("unsigned short",unsigned short,compression_type);
43314  _cimg_save_tiff("int",int,compression_type);
43315  _cimg_save_tiff("unsigned int",unsigned int,compression_type);
43316  _cimg_save_tiff("long",int,compression_type);
43317  _cimg_save_tiff("unsigned long",unsigned int,compression_type);
43318  _cimg_save_tiff("float",float,compression_type);
43319  _cimg_save_tiff("double",float,compression_type);
43320  const char *const filename = TIFFFileName(tif);
43321  throw CImgInstanceException(_cimg_instance
43322  "save_tiff(): Unsupported pixel type '%s' for file '%s'.",
43323  cimg_instance,
43324  pixel_type(),filename?filename:"(FILE*)");
43325  return *this;
43326  }
43327 #endif
43328 
43330 
43334  const CImg<T>& save_minc2(const char *const filename,
43335  const char *const imitate_file=0) const {
43336  if (!filename)
43337  throw CImgArgumentException(_cimg_instance
43338  "save_minc2(): Specified filename is (null).",
43339  cimg_instance);
43340  if (is_empty()) { cimg::fempty(0,filename); return *this; }
43341 
43342 #ifndef cimg_use_minc2
43343  cimg::unused(imitate_file);
43344  return save_other(filename);
43345 #else
43346  minc::minc_1_writer wtr;
43347  if (imitate_file)
43348  wtr.open(filename, imitate_file);
43349  else {
43350  minc::minc_info di;
43351  if(width()) di.push_back(minc::dim_info(width(), width()*0.5, -1, minc::dim_info::DIM_X));
43352  if(height()) di.push_back(minc::dim_info(height(), height()*0.5, -1, minc::dim_info::DIM_Y));
43353  if(depth()) di.push_back(minc::dim_info(depth(), depth()*0.5, -1, minc::dim_info::DIM_Z));
43354  if(spectrum()) di.push_back(minc::dim_info(spectrum(), spectrum()*0.5, -1, minc::dim_info::DIM_TIME));
43355  wtr.open(filename, di, 1, NC_FLOAT, 0);
43356  }
43357  if(typeid(T)==typeid(unsigned char))
43358  wtr.setup_write_byte();
43359  else if(typeid(T)==typeid(int))
43360  wtr.setup_write_int();
43361  else if(typeid(T)==typeid(double))
43362  wtr.setup_write_double();
43363  else
43364  wtr.setup_write_float();
43365  minc::save_standard_volume(wtr, this->_data);
43366  return *this;
43367 #endif
43368  }
43369 
43371 
43375  const CImg<T>& save_analyze(const char *const filename, const float *const voxel_size=0) const {
43376  if (!filename)
43377  throw CImgArgumentException(_cimg_instance
43378  "save_analyze(): Specified filename is (null).",
43379  cimg_instance);
43380  if (is_empty()) { cimg::fempty(0,filename); return *this; }
43381 
43382  std::FILE *file;
43383  char header[348] = { 0 }, hname[1024] = { 0 }, iname[1024] = { 0 };
43384  const char *const ext = cimg::split_filename(filename);
43385  short datatype=-1;
43386  std::memset(header,0,348);
43387  if (!*ext) {
43388  cimg_snprintf(hname,sizeof(hname),"%s.hdr",filename);
43389  cimg_snprintf(iname,sizeof(iname),"%s.img",filename);
43390  }
43391  if (!cimg::strncasecmp(ext,"hdr",3)) {
43392  std::strcpy(hname,filename);
43393  std::strncpy(iname,filename,sizeof(iname)-1);
43394  std::sprintf(iname + std::strlen(iname)-3,"img");
43395  }
43396  if (!cimg::strncasecmp(ext,"img",3)) {
43397  std::strcpy(hname,filename);
43398  std::strncpy(iname,filename,sizeof(iname)-1);
43399  std::sprintf(hname + std::strlen(iname)-3,"hdr");
43400  }
43401  if (!cimg::strncasecmp(ext,"nii",3)) {
43402  std::strncpy(hname,filename,sizeof(hname)-1); *iname = 0;
43403  }
43404  int *const iheader = (int*)header;
43405  *iheader = 348;
43406  std::strcpy(header + 4,"CImg");
43407  std::strcpy(header + 14," ");
43408  ((short*)(header + 36))[0] = 4096;
43409  ((char*)(header + 38))[0] = 114;
43410  ((short*)(header + 40))[0] = 4;
43411  ((short*)(header + 40))[1] = _width;
43412  ((short*)(header + 40))[2] = _height;
43413  ((short*)(header + 40))[3] = _depth;
43414  ((short*)(header + 40))[4] = _spectrum;
43415  if (!cimg::strcasecmp(pixel_type(),"bool")) datatype = 2;
43416  if (!cimg::strcasecmp(pixel_type(),"unsigned char")) datatype = 2;
43417  if (!cimg::strcasecmp(pixel_type(),"char")) datatype = 2;
43418  if (!cimg::strcasecmp(pixel_type(),"unsigned short")) datatype = 4;
43419  if (!cimg::strcasecmp(pixel_type(),"short")) datatype = 4;
43420  if (!cimg::strcasecmp(pixel_type(),"unsigned int")) datatype = 8;
43421  if (!cimg::strcasecmp(pixel_type(),"int")) datatype = 8;
43422  if (!cimg::strcasecmp(pixel_type(),"unsigned long")) datatype = 8;
43423  if (!cimg::strcasecmp(pixel_type(),"long")) datatype = 8;
43424  if (!cimg::strcasecmp(pixel_type(),"float")) datatype = 16;
43425  if (!cimg::strcasecmp(pixel_type(),"double")) datatype = 64;
43426  if (datatype<0)
43427  throw CImgIOException(_cimg_instance
43428  "save_analyze(): Unsupported pixel type '%s' for file '%s'.",
43429  cimg_instance,
43430  pixel_type(),filename);
43431 
43432  ((short*)(header+70))[0] = datatype;
43433  ((short*)(header+72))[0] = sizeof(T);
43434  ((float*)(header+112))[0] = 1;
43435  ((float*)(header+76))[0] = 0;
43436  if (voxel_size) {
43437  ((float*)(header+76))[1] = voxel_size[0];
43438  ((float*)(header+76))[2] = voxel_size[1];
43439  ((float*)(header+76))[3] = voxel_size[2];
43440  } else ((float*)(header+76))[1] = ((float*)(header+76))[2] = ((float*)(header+76))[3] = 1;
43441  file = cimg::fopen(hname,"wb");
43442  cimg::fwrite(header,348,file);
43443  if (*iname) { cimg::fclose(file); file = cimg::fopen(iname,"wb"); }
43444  cimg::fwrite(_data,size(),file);
43445  cimg::fclose(file);
43446  return *this;
43447  }
43448 
43450 
43454  const CImg<T>& save_cimg(const char *const filename, const bool is_compressed=false) const {
43455  CImgList<T>(*this,true).save_cimg(filename,is_compressed);
43456  return *this;
43457  }
43458 
43460  const CImg<T>& save_cimg(std::FILE *const file, const bool is_compressed=false) const {
43461  CImgList<T>(*this,true).save_cimg(file,is_compressed);
43462  return *this;
43463  }
43464 
43466 
43474  const CImg<T>& save_cimg(const char *const filename,
43475  const unsigned int n0,
43476  const unsigned int x0, const unsigned int y0,
43477  const unsigned int z0, const unsigned int c0) const {
43478  CImgList<T>(*this,true).save_cimg(filename,n0,x0,y0,z0,c0);
43479  return *this;
43480  }
43481 
43483  const CImg<T>& save_cimg(std::FILE *const file,
43484  const unsigned int n0,
43485  const unsigned int x0, const unsigned int y0,
43486  const unsigned int z0, const unsigned int c0) const {
43487  CImgList<T>(*this,true).save_cimg(file,n0,x0,y0,z0,c0);
43488  return *this;
43489  }
43490 
43492 
43502  static void save_empty_cimg(const char *const filename,
43503  const unsigned int dx, const unsigned int dy=1,
43504  const unsigned int dz=1, const unsigned int dc=1) {
43505  return CImgList<T>::save_empty_cimg(filename,1,dx,dy,dz,dc);
43506  }
43507 
43509 
43513  static void save_empty_cimg(std::FILE *const file,
43514  const unsigned int dx, const unsigned int dy=1,
43515  const unsigned int dz=1, const unsigned int dc=1) {
43516  return CImgList<T>::save_empty_cimg(file,1,dx,dy,dz,dc);
43517  }
43518 
43520 
43524  const CImg<T>& save_inr(const char *const filename, const float *const voxel_size=0) const {
43525  return _save_inr(0,filename,voxel_size);
43526  }
43527 
43529  const CImg<T>& save_inr(std::FILE *const file, const float *const voxel_size=0) const {
43530  return _save_inr(file,0,voxel_size);
43531  }
43532 
43533  const CImg<T>& _save_inr(std::FILE *const file, const char *const filename, const float *const voxel_size) const {
43534  if (!file && !filename)
43535  throw CImgArgumentException(_cimg_instance
43536  "save_inr(): Specified filename is (null).",
43537  cimg_instance);
43538  if (is_empty()) { cimg::fempty(file,filename); return *this; }
43539 
43540  int inrpixsize=-1;
43541  const char *inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0";
43542  if (!cimg::strcasecmp(pixel_type(),"unsigned char")) {
43543  inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1;
43544  }
43545  if (!cimg::strcasecmp(pixel_type(),"char")) {
43546  inrtype = "fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1;
43547  }
43548  if (!cimg::strcasecmp(pixel_type(),"unsigned short")) {
43549  inrtype = "unsigned fixed\nPIXSIZE=16 bits\nSCALE=2**0";inrpixsize = 2;
43550  }
43551  if (!cimg::strcasecmp(pixel_type(),"short")) {
43552  inrtype = "fixed\nPIXSIZE=16 bits\nSCALE=2**0"; inrpixsize = 2;
43553  }
43554  if (!cimg::strcasecmp(pixel_type(),"unsigned int")) {
43555  inrtype = "unsigned fixed\nPIXSIZE=32 bits\nSCALE=2**0";inrpixsize = 4;
43556  }
43557  if (!cimg::strcasecmp(pixel_type(),"int")) {
43558  inrtype = "fixed\nPIXSIZE=32 bits\nSCALE=2**0"; inrpixsize = 4;
43559  }
43560  if (!cimg::strcasecmp(pixel_type(),"float")) {
43561  inrtype = "float\nPIXSIZE=32 bits"; inrpixsize = 4;
43562  }
43563  if (!cimg::strcasecmp(pixel_type(),"double")) {
43564  inrtype = "float\nPIXSIZE=64 bits"; inrpixsize = 8;
43565  }
43566  if (inrpixsize<=0)
43567  throw CImgIOException(_cimg_instance
43568  "save_inr(): Unsupported pixel type '%s' for file '%s'",
43569  cimg_instance,
43570  pixel_type(),filename?filename:"(FILE*)");
43571 
43572  std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
43573  char header[257] = { 0 };
43574  int err = cimg_snprintf(header,sizeof(header),"#INRIMAGE-4#{\nXDIM=%u\nYDIM=%u\nZDIM=%u\nVDIM=%u\n",
43575  _width,_height,_depth,_spectrum);
43576  if (voxel_size) err+=std::sprintf(header + err,"VX=%g\nVY=%g\nVZ=%g\n",voxel_size[0],voxel_size[1],voxel_size[2]);
43577  err+=std::sprintf(header + err,"TYPE=%s\nCPU=%s\n",inrtype,cimg::endianness()?"sun":"decm");
43578  std::memset(header + err,'\n',252 - err);
43579  std::memcpy(header + 252,"##}\n",4);
43580  cimg::fwrite(header,256,nfile);
43581  cimg_forXYZ(*this,x,y,z) cimg_forC(*this,c) cimg::fwrite(&((*this)(x,y,z,c)),1,nfile);
43582  if (!file) cimg::fclose(nfile);
43583  return *this;
43584  }
43585 
43587 
43591  const CImg<T>& save_exr(const char *const filename) const {
43592  if (!filename)
43593  throw CImgArgumentException(_cimg_instance
43594  "save_exr(): Specified filename is (null).",
43595  cimg_instance);
43596  if (is_empty()) { cimg::fempty(0,filename); return *this; }
43597  if (_depth>1)
43598  cimg::warn(_cimg_instance
43599  "save_exr(): Instance is volumetric, only the first slice will be saved in file '%s'.",
43600  cimg_instance,
43601  filename);
43602 
43603 #ifndef cimg_use_openexr
43604  return save_other(filename);
43605 #else
43606  Imf::Rgba *const ptrd0 = new Imf::Rgba[(unsigned long)_width*_height], *ptrd = ptrd0, rgba;
43607  switch (_spectrum) {
43608  case 1 : { // Grayscale image.
43609  for (const T *ptr_r = data(), *const ptr_e = ptr_r + (unsigned long)_width*_height; ptr_r<ptr_e;) {
43610  rgba.r = rgba.g = rgba.b = (half)(*(ptr_r++));
43611  rgba.a = (half)1;
43612  *(ptrd++) = rgba;
43613  }
43614  } break;
43615  case 2 : { // RG image.
43616  for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1),
43617  *const ptr_e = ptr_r + (unsigned long)_width*_height; ptr_r<ptr_e; ) {
43618  rgba.r = (half)(*(ptr_r++));
43619  rgba.g = (half)(*(ptr_g++));
43620  rgba.b = (half)0;
43621  rgba.a = (half)1;
43622  *(ptrd++) = rgba;
43623  }
43624  } break;
43625  case 3 : { // RGB image.
43626  for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2),
43627  *const ptr_e = ptr_r + (unsigned long)_width*_height; ptr_r<ptr_e;) {
43628  rgba.r = (half)(*(ptr_r++));
43629  rgba.g = (half)(*(ptr_g++));
43630  rgba.b = (half)(*(ptr_b++));
43631  rgba.a = (half)1;
43632  *(ptrd++) = rgba;
43633  }
43634  } break;
43635  default : { // RGBA image.
43636  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),
43637  *const ptr_e = ptr_r + (unsigned long)_width*_height; ptr_r<ptr_e;) {
43638  rgba.r = (half)(*(ptr_r++));
43639  rgba.g = (half)(*(ptr_g++));
43640  rgba.b = (half)(*(ptr_b++));
43641  rgba.a = (half)(*(ptr_a++));
43642  *(ptrd++) = rgba;
43643  }
43644  } break;
43645  }
43646  Imf::RgbaOutputFile outFile(filename,_width,_height,
43647  _spectrum==1?Imf::WRITE_Y:_spectrum==2?Imf::WRITE_YA:_spectrum==3?
43648  Imf::WRITE_RGB:Imf::WRITE_RGBA);
43649  outFile.setFrameBuffer(ptrd0,1,_width);
43650  outFile.writePixels(_height);
43651  delete[] ptrd0;
43652  return *this;
43653 #endif
43654  }
43655 
43657 
43663  const CImg<T>& save_pandore(const char *const filename, const unsigned int colorspace=0) const {
43664  return _save_pandore(0,filename,colorspace);
43665  }
43666 
43668 
43672  const CImg<T>& save_pandore(std::FILE *const file, const unsigned int colorspace=0) const {
43673  return _save_pandore(file,0,colorspace);
43674  }
43675 
43676  unsigned int _save_pandore_header_length(unsigned int id, unsigned int *dims, const unsigned int colorspace) const {
43677  unsigned int nbdims = 0;
43678  if (id==2 || id==3 || id==4) {
43679  dims[0] = 1; dims[1] = _width; nbdims = 2;
43680  }
43681  if (id==5 || id==6 || id==7) {
43682  dims[0] = 1; dims[1] = _height; dims[2] = _width; nbdims=3;
43683  }
43684  if (id==8 || id==9 || id==10) {
43685  dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4;
43686  }
43687  if (id==16 || id==17 || id==18) {
43688  dims[0] = 3; dims[1] = _height; dims[2] = _width; dims[3] = colorspace; nbdims = 4;
43689  }
43690  if (id==19 || id==20 || id==21) {
43691  dims[0] = 3; dims[1] = _depth; dims[2] = _height; dims[3] = _width; dims[4] = colorspace; nbdims = 5;
43692  }
43693  if (id==22 || id==23 || id==25) {
43694  dims[0] = _spectrum; dims[1] = _width; nbdims = 2;
43695  }
43696  if (id==26 || id==27 || id==29) {
43697  dims[0] = _spectrum; dims[1] = _height; dims[2] = _width; nbdims=3;
43698  }
43699  if (id==30 || id==31 || id==33) {
43700  dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4;
43701  }
43702  return nbdims;
43703  }
43704 
43705  const CImg<T>& _save_pandore(std::FILE *const file, const char *const filename,
43706  const unsigned int colorspace) const {
43707 
43708 #define __cimg_save_pandore_case(dtype) \
43709  dtype *buffer = new dtype[size()]; \
43710  const T *ptrs = _data; \
43711  cimg_foroff(*this,off) *(buffer++) = (dtype)(*(ptrs++)); \
43712  buffer-=size(); \
43713  cimg::fwrite(buffer,size(),nfile); \
43714  delete[] buffer
43715 
43716 #define _cimg_save_pandore_case(sy,sz,sv,stype,id) \
43717  if (!saved && (sy?(sy==_height):true) && (sz?(sz==_depth):true) && \
43718  (sv?(sv==_spectrum):true) && !std::strcmp(stype,pixel_type())) { \
43719  unsigned int *iheader = (unsigned int*)(header+12); \
43720  nbdims = _save_pandore_header_length((*iheader=id),dims,colorspace); \
43721  cimg::fwrite(header,36,nfile); \
43722  if (sizeof(unsigned long)==4) { unsigned long ndims[5] = { 0 }; \
43723  for (int d = 0; d<5; ++d) ndims[d] = (unsigned long)dims[d]; cimg::fwrite(ndims,nbdims,nfile); } \
43724  else if (sizeof(unsigned int)==4) { unsigned int ndims[5] = { 0 }; \
43725  for (int d = 0; d<5; ++d) ndims[d] = (unsigned int)dims[d]; cimg::fwrite(ndims,nbdims,nfile); } \
43726  else if (sizeof(unsigned short)==4) { unsigned short ndims[5] = { 0 }; \
43727  for (int d = 0; d<5; ++d) ndims[d] = (unsigned short)dims[d]; cimg::fwrite(ndims,nbdims,nfile); } \
43728  else throw CImgIOException(_cimg_instance \
43729  "save_pandore(): Unsupported datatype for file '%s'.",\
43730  cimg_instance, \
43731  filename?filename:"(FILE*)"); \
43732  if (id==2 || id==5 || id==8 || id==16 || id==19 || id==22 || id==26 || id==30) { \
43733  __cimg_save_pandore_case(unsigned char); \
43734  } else if (id==3 || id==6 || id==9 || id==17 || id==20 || id==23 || id==27 || id==31) { \
43735  if (sizeof(unsigned long)==4) { __cimg_save_pandore_case(unsigned long); } \
43736  else if (sizeof(unsigned int)==4) { __cimg_save_pandore_case(unsigned int); } \
43737  else if (sizeof(unsigned short)==4) { __cimg_save_pandore_case(unsigned short); } \
43738  else throw CImgIOException(_cimg_instance \
43739  "save_pandore(): Unsupported datatype for file '%s'.",\
43740  cimg_instance, \
43741  filename?filename:"(FILE*)"); \
43742  } else if (id==4 || id==7 || id==10 || id==18 || id==21 || id==25 || id==29 || id==33) { \
43743  if (sizeof(double)==4) { __cimg_save_pandore_case(double); } \
43744  else if (sizeof(float)==4) { __cimg_save_pandore_case(float); } \
43745  else throw CImgIOException(_cimg_instance \
43746  "save_pandore(): Unsupported datatype for file '%s'.",\
43747  cimg_instance, \
43748  filename?filename:"(FILE*)"); \
43749  } \
43750  saved = true; \
43751  }
43752 
43753  if (!file && !filename)
43754  throw CImgArgumentException(_cimg_instance
43755  "save_pandore(): Specified filename is (null).",
43756  cimg_instance);
43757  if (is_empty()) { cimg::fempty(file,filename); return *this; }
43758 
43759  std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
43760  unsigned char header[36] = { 'P','A','N','D','O','R','E','0','4',0,0,0,
43761  0,0,0,0,'C','I','m','g',0,0,0,0,0,'N','o',' ','d','a','t','e',0,0,0,0 };
43762  unsigned int nbdims, dims[5] = { 0 };
43763  bool saved = false;
43764  _cimg_save_pandore_case(1,1,1,"unsigned char",2);
43765  _cimg_save_pandore_case(1,1,1,"char",3);
43766  _cimg_save_pandore_case(1,1,1,"short",3);
43767  _cimg_save_pandore_case(1,1,1,"unsigned short",3);
43768  _cimg_save_pandore_case(1,1,1,"unsigned int",3);
43769  _cimg_save_pandore_case(1,1,1,"int",3);
43770  _cimg_save_pandore_case(1,1,1,"unsigned long",4);
43771  _cimg_save_pandore_case(1,1,1,"long",3);
43772  _cimg_save_pandore_case(1,1,1,"float",4);
43773  _cimg_save_pandore_case(1,1,1,"double",4);
43774 
43775  _cimg_save_pandore_case(0,1,1,"unsigned char",5);
43776  _cimg_save_pandore_case(0,1,1,"char",6);
43777  _cimg_save_pandore_case(0,1,1,"short",6);
43778  _cimg_save_pandore_case(0,1,1,"unsigned short",6);
43779  _cimg_save_pandore_case(0,1,1,"unsigned int",6);
43780  _cimg_save_pandore_case(0,1,1,"int",6);
43781  _cimg_save_pandore_case(0,1,1,"unsigned long",7);
43782  _cimg_save_pandore_case(0,1,1,"long",6);
43783  _cimg_save_pandore_case(0,1,1,"float",7);
43784  _cimg_save_pandore_case(0,1,1,"double",7);
43785 
43786  _cimg_save_pandore_case(0,0,1,"unsigned char",8);
43787  _cimg_save_pandore_case(0,0,1,"char",9);
43788  _cimg_save_pandore_case(0,0,1,"short",9);
43789  _cimg_save_pandore_case(0,0,1,"unsigned short",9);
43790  _cimg_save_pandore_case(0,0,1,"unsigned int",9);
43791  _cimg_save_pandore_case(0,0,1,"int",9);
43792  _cimg_save_pandore_case(0,0,1,"unsigned long",10);
43793  _cimg_save_pandore_case(0,0,1,"long",9);
43794  _cimg_save_pandore_case(0,0,1,"float",10);
43795  _cimg_save_pandore_case(0,0,1,"double",10);
43796 
43797  _cimg_save_pandore_case(0,1,3,"unsigned char",16);
43798  _cimg_save_pandore_case(0,1,3,"char",17);
43799  _cimg_save_pandore_case(0,1,3,"short",17);
43800  _cimg_save_pandore_case(0,1,3,"unsigned short",17);
43801  _cimg_save_pandore_case(0,1,3,"unsigned int",17);
43802  _cimg_save_pandore_case(0,1,3,"int",17);
43803  _cimg_save_pandore_case(0,1,3,"unsigned long",18);
43804  _cimg_save_pandore_case(0,1,3,"long",17);
43805  _cimg_save_pandore_case(0,1,3,"float",18);
43806  _cimg_save_pandore_case(0,1,3,"double",18);
43807 
43808  _cimg_save_pandore_case(0,0,3,"unsigned char",19);
43809  _cimg_save_pandore_case(0,0,3,"char",20);
43810  _cimg_save_pandore_case(0,0,3,"short",20);
43811  _cimg_save_pandore_case(0,0,3,"unsigned short",20);
43812  _cimg_save_pandore_case(0,0,3,"unsigned int",20);
43813  _cimg_save_pandore_case(0,0,3,"int",20);
43814  _cimg_save_pandore_case(0,0,3,"unsigned long",21);
43815  _cimg_save_pandore_case(0,0,3,"long",20);
43816  _cimg_save_pandore_case(0,0,3,"float",21);
43817  _cimg_save_pandore_case(0,0,3,"double",21);
43818 
43819  _cimg_save_pandore_case(1,1,0,"unsigned char",22);
43820  _cimg_save_pandore_case(1,1,0,"char",23);
43821  _cimg_save_pandore_case(1,1,0,"short",23);
43822  _cimg_save_pandore_case(1,1,0,"unsigned short",23);
43823  _cimg_save_pandore_case(1,1,0,"unsigned int",23);
43824  _cimg_save_pandore_case(1,1,0,"int",23);
43825  _cimg_save_pandore_case(1,1,0,"unsigned long",25);
43826  _cimg_save_pandore_case(1,1,0,"long",23);
43827  _cimg_save_pandore_case(1,1,0,"float",25);
43828  _cimg_save_pandore_case(1,1,0,"double",25);
43829 
43830  _cimg_save_pandore_case(0,1,0,"unsigned char",26);
43831  _cimg_save_pandore_case(0,1,0,"char",27);
43832  _cimg_save_pandore_case(0,1,0,"short",27);
43833  _cimg_save_pandore_case(0,1,0,"unsigned short",27);
43834  _cimg_save_pandore_case(0,1,0,"unsigned int",27);
43835  _cimg_save_pandore_case(0,1,0,"int",27);
43836  _cimg_save_pandore_case(0,1,0,"unsigned long",29);
43837  _cimg_save_pandore_case(0,1,0,"long",27);
43838  _cimg_save_pandore_case(0,1,0,"float",29);
43839  _cimg_save_pandore_case(0,1,0,"double",29);
43840 
43841  _cimg_save_pandore_case(0,0,0,"unsigned char",30);
43842  _cimg_save_pandore_case(0,0,0,"char",31);
43843  _cimg_save_pandore_case(0,0,0,"short",31);
43844  _cimg_save_pandore_case(0,0,0,"unsigned short",31);
43845  _cimg_save_pandore_case(0,0,0,"unsigned int",31);
43846  _cimg_save_pandore_case(0,0,0,"int",31);
43847  _cimg_save_pandore_case(0,0,0,"unsigned long",33);
43848  _cimg_save_pandore_case(0,0,0,"long",31);
43849  _cimg_save_pandore_case(0,0,0,"float",33);
43850  _cimg_save_pandore_case(0,0,0,"double",33);
43851 
43852  if (!file) cimg::fclose(nfile);
43853  return *this;
43854  }
43855 
43857 
43863  const CImg<T>& save_raw(const char *const filename, const bool is_multiplexed=false) const {
43864  return _save_raw(0,filename,is_multiplexed);
43865  }
43866 
43868 
43872  const CImg<T>& save_raw(std::FILE *const file, const bool is_multiplexed=false) const {
43873  return _save_raw(file,0,is_multiplexed);
43874  }
43875 
43876  const CImg<T>& _save_raw(std::FILE *const file, const char *const filename, const bool is_multiplexed) const {
43877  if (!file && !filename)
43878  throw CImgArgumentException(_cimg_instance
43879  "save_raw(): Specified filename is (null).",
43880  cimg_instance);
43881  if (is_empty()) { cimg::fempty(file,filename); return *this; }
43882 
43883  std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
43884  if (!is_multiplexed) cimg::fwrite(_data,size(),nfile);
43885  else {
43886  CImg<T> buf(_spectrum);
43887  cimg_forXYZ(*this,x,y,z) {
43888  cimg_forC(*this,c) buf[c] = (*this)(x,y,z,c);
43889  cimg::fwrite(buf._data,_spectrum,nfile);
43890  }
43891  }
43892  if (!file) cimg::fclose(nfile);
43893  return *this;
43894  }
43895 
43897 
43908  const CImg<T>& save_ffmpeg(const char *const filename, const unsigned int fps=25,
43909  const unsigned int bitrate=2048) const {
43910  if (!filename)
43911  throw CImgArgumentException(_cimg_instance
43912  "save_ffmpeg(): Specified filename is (null).",
43913  cimg_instance);
43914  if (!fps)
43915  throw CImgArgumentException(_cimg_instance
43916  "save_ffmpeg(): Invalid specified framerate 0, for file '%s'.",
43917  cimg_instance,
43918  filename);
43919  if (is_empty()) { cimg::fempty(0,filename); return *this; }
43920 
43921 #ifndef cimg_use_ffmpeg
43922  return save_ffmpeg_external(filename,0,fps,bitrate);
43923 #else
43924  CImgList<T> list;
43925  get_split('z').move_to(list);
43926  list.save_ffmpeg(filename,fps,bitrate);
43927  return *this;
43928 #endif
43929  }
43930 
43932 
43937  const CImg<T>& save_yuv(const char *const filename, const bool is_rgb=true) const {
43938  get_split('z').save_yuv(filename,is_rgb);
43939  return *this;
43940  }
43941 
43943 
43947  const CImg<T>& save_yuv(std::FILE *const file, const bool is_rgb=true) const {
43948  get_split('z').save_yuv(file,is_rgb);
43949  return *this;
43950  }
43951 
43953 
43963  template<typename tf, typename tc>
43964  const CImg<T>& save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
43965  const char *const filename) const {
43966  return _save_off(primitives,colors,0,filename);
43967  }
43968 
43970 
43974  template<typename tf, typename tc>
43975  const CImg<T>& save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
43976  std::FILE *const file) const {
43977  return _save_off(primitives,colors,file,0);
43978  }
43979 
43980  template<typename tf, typename tc>
43981  const CImg<T>& _save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
43982  std::FILE *const file, const char *const filename) const {
43983  if (!file && !filename)
43984  throw CImgArgumentException(_cimg_instance
43985  "save_off(): Specified filename is (null).",
43986  cimg_instance);
43987  if (is_empty())
43988  throw CImgInstanceException(_cimg_instance
43989  "save_off(): Empty instance, for file '%s'.",
43990  cimg_instance,
43991  filename?filename:"(FILE*)");
43992 
43993  CImgList<T> opacities;
43994  char error_message[1024] = { 0 };
43995  if (!is_object3d(primitives,colors,opacities,true,error_message))
43996  throw CImgInstanceException(_cimg_instance
43997  "save_off(): Invalid specified 3d object, for file '%s' (%s).",
43998  cimg_instance,
43999  filename?filename:"(FILE*)",error_message);
44000 
44001  const CImg<tc> default_color(1,3,1,1,200);
44002  std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
44003  unsigned int supported_primitives = 0;
44004  cimglist_for(primitives,l) if (primitives[l].size()!=5) ++supported_primitives;
44005  std::fprintf(nfile,"OFF\n%u %u %u\n",_width,supported_primitives,3*primitives._width);
44006  cimg_forX(*this,i) std::fprintf(nfile,"%f %f %f\n",
44007  (float)((*this)(i,0)),(float)((*this)(i,1)),(float)((*this)(i,2)));
44008  cimglist_for(primitives,l) {
44009  const CImg<tc>& color = l<colors.width()?colors[l]:default_color;
44010  const unsigned int psiz = primitives[l].size(), csiz = color.size();
44011  const float r = color[0]/255.0f, g = (csiz>1?color[1]:r)/255.0f, b = (csiz>2?color[2]:g)/255.0f;
44012  switch (psiz) {
44013  case 1 : std::fprintf(nfile,"1 %u %f %f %f\n",
44014  (unsigned int)primitives(l,0),r,g,b); break;
44015  case 2 : std::fprintf(nfile,"2 %u %u %f %f %f\n",
44016  (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break;
44017  case 3 : std::fprintf(nfile,"3 %u %u %u %f %f %f\n",
44018  (unsigned int)primitives(l,0),(unsigned int)primitives(l,2),
44019  (unsigned int)primitives(l,1),r,g,b); break;
44020  case 4 : std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n",
44021  (unsigned int)primitives(l,0),(unsigned int)primitives(l,3),
44022  (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),r,g,b); break;
44023  case 5 : std::fprintf(nfile,"2 %u %u %f %f %f\n",
44024  (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break;
44025  case 6 : {
44026  const unsigned int xt = (unsigned int)primitives(l,2), yt = (unsigned int)primitives(l,3);
44027  const float
44028  rt = color.atXY(xt,yt,0)/255.0f,
44029  gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f,
44030  bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f;
44031  std::fprintf(nfile,"2 %u %u %f %f %f\n",
44032  (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),rt,gt,bt);
44033  } break;
44034  case 9 : {
44035  const unsigned int xt = (unsigned int)primitives(l,3), yt = (unsigned int)primitives(l,4);
44036  const float
44037  rt = color.atXY(xt,yt,0)/255.0f,
44038  gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f,
44039  bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f;
44040  std::fprintf(nfile,"3 %u %u %u %f %f %f\n",
44041  (unsigned int)primitives(l,0),(unsigned int)primitives(l,2),
44042  (unsigned int)primitives(l,1),rt,gt,bt);
44043  } break;
44044  case 12 : {
44045  const unsigned int xt = (unsigned int)primitives(l,4), yt = (unsigned int)primitives(l,5);
44046  const float
44047  rt = color.atXY(xt,yt,0)/255.0f,
44048  gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f,
44049  bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f;
44050  std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n",
44051  (unsigned int)primitives(l,0),(unsigned int)primitives(l,3),
44052  (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),rt,gt,bt);
44053  } break;
44054  }
44055  }
44056  if (!file) cimg::fclose(nfile);
44057  return *this;
44058  }
44059 
44061 
44072  const CImg<T>& save_ffmpeg_external(const char *const filename, const char *const codec=0,
44073  const unsigned int fps=25, const unsigned int bitrate=2048) const {
44074  if (!filename)
44075  throw CImgArgumentException(_cimg_instance
44076  "save_ffmpeg_external(): Specified filename is (null).",
44077  cimg_instance);
44078  if (is_empty()) { cimg::fempty(0,filename); return *this; }
44079 
44080  CImgList<T> list;
44081  get_split('z').move_to(list);
44082  list.save_ffmpeg_external(filename,codec,fps,bitrate);
44083  return *this;
44084  }
44085 
44087 
44093  const CImg<T>& save_gzip_external(const char *const filename) const {
44094  if (!filename)
44095  throw CImgArgumentException(_cimg_instance
44096  "save_gzip_external(): Specified filename is (null).",
44097  cimg_instance);
44098  if (is_empty()) { cimg::fempty(0,filename); return *this; }
44099 
44100  char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
44101  const char
44102  *ext = cimg::split_filename(filename,body),
44103  *ext2 = cimg::split_filename(body,0);
44104  std::FILE *file;
44105  do {
44106  if (!cimg::strcasecmp(ext,"gz")) {
44107  if (*ext2) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",
44108  cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
44109  else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.cimg",
44110  cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
44111  } else {
44112  if (*ext) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",
44113  cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
44114  else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.cimg",
44115  cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
44116  }
44117  if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
44118  } while (file);
44119  save(filetmp);
44120  cimg_snprintf(command,sizeof(command),"%s -c \"%s\" > \"%s\"",
44121  cimg::gzip_path(),
44122  CImg<charT>::string(filetmp)._system_strescape().data(),
44123  CImg<charT>::string(filename)._system_strescape().data());
44124  cimg::system(command);
44125  file = std::fopen(filename,"rb");
44126  if (!file)
44127  throw CImgIOException(_cimg_instance
44128  "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.",
44129  cimg_instance,
44130  filename);
44131 
44132  else cimg::fclose(file);
44133  std::remove(filetmp);
44134  return *this;
44135  }
44136 
44138 
44145  const CImg<T>& save_graphicsmagick_external(const char *const filename, const unsigned int quality=100) const {
44146  if (!filename)
44147  throw CImgArgumentException(_cimg_instance
44148  "save_graphicsmagick_external(): Specified filename is (null).",
44149  cimg_instance);
44150  if (is_empty()) { cimg::fempty(0,filename); return *this; }
44151 
44152 #ifdef cimg_use_png
44153 #define _cimg_sge_ext1 "png"
44154 #define _cimg_sge_ext2 "png"
44155 #else
44156 #define _cimg_sge_ext1 "pgm"
44157 #define _cimg_sge_ext2 "ppm"
44158 #endif
44159  char command[1024] = { 0 }, filetmp[512] = { 0 };
44160  std::FILE *file;
44161  do {
44162  cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",
44163  cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),
44164  _spectrum==1?_cimg_sge_ext1:_cimg_sge_ext2);
44165  if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
44166  } while (file);
44167 #ifdef cimg_use_png
44168  save_png(filetmp);
44169 #else
44170  save_pnm(filetmp);
44171 #endif
44172  cimg_snprintf(command,sizeof(command),"%s convert -quality %u \"%s\" \"%s\"",
44173  cimg::graphicsmagick_path(),quality,
44174  CImg<charT>::string(filetmp)._system_strescape().data(),
44175  CImg<charT>::string(filename)._system_strescape().data());
44176  cimg::system(command);
44177  file = std::fopen(filename,"rb");
44178  if (!file)
44179  throw CImgIOException(_cimg_instance
44180  "save_graphicsmagick_external(): Failed to save file '%s' with external command 'gm'.",
44181  cimg_instance,
44182  filename);
44183 
44184  if (file) cimg::fclose(file);
44185  std::remove(filetmp);
44186  return *this;
44187  }
44188 
44190 
44197  const CImg<T>& save_imagemagick_external(const char *const filename, const unsigned int quality=100) const {
44198  if (!filename)
44199  throw CImgArgumentException(_cimg_instance
44200  "save_imagemagick_external(): Specified filename is (null).",
44201  cimg_instance);
44202  if (is_empty()) { cimg::fempty(0,filename); return *this; }
44203 
44204 #ifdef cimg_use_png
44205 #define _cimg_sie_ext1 "png"
44206 #define _cimg_sie_ext2 "png"
44207 #else
44208 #define _cimg_sie_ext1 "pgm"
44209 #define _cimg_sie_ext2 "ppm"
44210 #endif
44211  char command[1024] = { 0 }, filetmp[512] = { 0 };
44212  std::FILE *file;
44213  do {
44214  cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),
44215  cimg_file_separator,cimg::filenamerand(),_spectrum==1?_cimg_sie_ext1:_cimg_sie_ext2);
44216  if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
44217  } while (file);
44218 #ifdef cimg_use_png
44219  save_png(filetmp);
44220 #else
44221  save_pnm(filetmp);
44222 #endif
44223  cimg_snprintf(command,sizeof(command),"%s -quality %u \"%s\" \"%s\"",
44224  cimg::imagemagick_path(),quality,
44225  CImg<charT>::string(filetmp)._system_strescape().data(),
44226  CImg<charT>::string(filename)._system_strescape().data());
44227  cimg::system(command);
44228  file = std::fopen(filename,"rb");
44229  if (!file)
44230  throw CImgIOException(_cimg_instance
44231  "save_imagemagick_external(): Failed to save file '%s' with external command 'convert'.",
44232  cimg_instance,
44233  filename);
44234 
44235  if (file) cimg::fclose(file);
44236  std::remove(filetmp);
44237  return *this;
44238  }
44239 
44241 
44247  const CImg<T>& save_medcon_external(const char *const filename) const {
44248  if (!filename)
44249  throw CImgArgumentException(_cimg_instance
44250  "save_medcon_external(): Specified filename is (null).",
44251  cimg_instance);
44252  if (is_empty()) { cimg::fempty(0,filename); return *this; }
44253 
44254  char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
44255  std::FILE *file;
44256  do {
44257  cimg_snprintf(filetmp,sizeof(filetmp),"%s.hdr",cimg::filenamerand());
44258  if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
44259  } while (file);
44260  save_analyze(filetmp);
44261  cimg_snprintf(command,sizeof(command),"%s -w -c dicom -o \"%s\" -f \"%s\"",
44263  CImg<charT>::string(filename)._system_strescape().data(),
44264  CImg<charT>::string(filetmp)._system_strescape().data());
44265  cimg::system(command);
44266  std::remove(filetmp);
44267  cimg::split_filename(filetmp,body);
44268  cimg_snprintf(filetmp,sizeof(filetmp),"%s.img",body);
44269  std::remove(filetmp);
44270 
44271  file = std::fopen(filename,"rb");
44272  if (!file) {
44273  cimg_snprintf(command,sizeof(command),"m000-%s",filename);
44274  file = std::fopen(command,"rb");
44275  if (!file) {
44276  cimg::fclose(cimg::fopen(filename,"r"));
44277  throw CImgIOException(_cimg_instance
44278  "save_medcon_external(): Failed to save file '%s' with external command 'medcon'.",
44279  cimg_instance,
44280  filename);
44281  }
44282  }
44283  cimg::fclose(file);
44284  std::rename(command,filename);
44285  return *this;
44286  }
44287 
44288  // Save image for non natively supported formats.
44301  const CImg<T>& save_other(const char *const filename, const unsigned int quality=100) const {
44302  if (!filename)
44303  throw CImgArgumentException(_cimg_instance
44304  "save_other(): Specified filename is (null).",
44305  cimg_instance);
44306  if (is_empty()) { cimg::fempty(0,filename); return *this; }
44307 
44308  const unsigned int omode = cimg::exception_mode();
44309  bool is_saved = true;
44310  cimg::exception_mode() = 0;
44311  try { save_magick(filename); }
44312  catch (CImgException&) {
44313  try { save_imagemagick_external(filename,quality); }
44314  catch (CImgException&) {
44315  try { save_graphicsmagick_external(filename,quality); }
44316  catch (CImgException&) {
44317  is_saved = false;
44318  }
44319  }
44320  }
44321  cimg::exception_mode() = omode;
44322  if (!is_saved)
44323  throw CImgIOException(_cimg_instance
44324  "save_other(): Failed to save file '%s'. Format is not natively supported, "
44325  "and no external commands succeeded.",
44326  cimg_instance,
44327  filename);
44328  return *this;
44329  }
44330 
44331  // [internal] Return a 40x38 color logo of a 'danger' item.
44332  static CImg<T> _logo40x38() {
44333  CImg<T> res(40,38,1,3);
44334  const unsigned char *ptrs = cimg::logo40x38;
44335  T *ptr1 = res.data(0,0,0,0), *ptr2 = res.data(0,0,0,1), *ptr3 = res.data(0,0,0,2);
44336  for (unsigned long off = 0; off<(unsigned long)res._width*res._height;) {
44337  const unsigned char n = *(ptrs++), r = *(ptrs++), g = *(ptrs++), b = *(ptrs++);
44338  for (unsigned int l = 0; l<n; ++off, ++l) { *(ptr1++) = (T)r; *(ptr2++) = (T)g; *(ptr3++) = (T)b; }
44339  }
44340  return res;
44341  }
44342 
44344  };
44345 
44346  /*
44347  #-----------------------------------------
44348  #
44349  #
44350  #
44351  # Definition of the CImgList<T> structure
44352  #
44353  #
44354  #
44355  #------------------------------------------
44356  */
44358  template<typename T>
44359  struct CImgList {
44360  unsigned int _width, _allocated_width;
44361  CImg<T> *_data;
44362 
44364 
44377  typedef CImg<T>* iterator;
44378 
44380 
44385  typedef const CImg<T>* const_iterator;
44386 
44388 
44396  typedef T value_type;
44397 
44398  // Define common T-dependant types.
44399  typedef typename cimg::superset<T,bool>::type Tbool;
44400  typedef typename cimg::superset<T,unsigned char>::type Tuchar;
44401  typedef typename cimg::superset<T,char>::type Tchar;
44402  typedef typename cimg::superset<T,unsigned short>::type Tushort;
44403  typedef typename cimg::superset<T,short>::type Tshort;
44404  typedef typename cimg::superset<T,unsigned int>::type Tuint;
44405  typedef typename cimg::superset<T,int>::type Tint;
44406  typedef typename cimg::superset<T,unsigned long>::type Tulong;
44407  typedef typename cimg::superset<T,long>::type Tlong;
44408  typedef typename cimg::superset<T,float>::type Tfloat;
44409  typedef typename cimg::superset<T,double>::type Tdouble;
44410  typedef typename cimg::last<T,bool>::type boolT;
44411  typedef typename cimg::last<T,unsigned char>::type ucharT;
44412  typedef typename cimg::last<T,char>::type charT;
44413  typedef typename cimg::last<T,unsigned short>::type ushortT;
44414  typedef typename cimg::last<T,short>::type shortT;
44415  typedef typename cimg::last<T,unsigned int>::type uintT;
44416  typedef typename cimg::last<T,int>::type intT;
44417  typedef typename cimg::last<T,unsigned long>::type ulongT;
44418  typedef typename cimg::last<T,long>::type longT;
44419  typedef typename cimg::last<T,float>::type floatT;
44420  typedef typename cimg::last<T,double>::type doubleT;
44421 
44423  //---------------------------
44424  //
44426 
44427  //---------------------------
44428 #ifdef cimglist_plugin
44429 #include cimglist_plugin
44430 #endif
44431 #ifdef cimglist_plugin1
44432 #include cimglist_plugin1
44433 #endif
44434 #ifdef cimglist_plugin2
44435 #include cimglist_plugin2
44436 #endif
44437 #ifdef cimglist_plugin3
44438 #include cimglist_plugin3
44439 #endif
44440 #ifdef cimglist_plugin4
44441 #include cimglist_plugin4
44442 #endif
44443 #ifdef cimglist_plugin5
44444 #include cimglist_plugin5
44445 #endif
44446 #ifdef cimglist_plugin6
44447 #include cimglist_plugin6
44448 #endif
44449 #ifdef cimglist_plugin7
44450 #include cimglist_plugin7
44451 #endif
44452 #ifdef cimglist_plugin8
44453 #include cimglist_plugin8
44454 #endif
44455 
44457  //--------------------------------------------------------
44458  //
44460 
44461  //--------------------------------------------------------
44462 
44464 
44471  delete[] _data;
44472  }
44473 
44475 
44484  _width(0),_allocated_width(0),_data(0) {}
44485 
44487 
44493  explicit CImgList(const unsigned int n):_width(n) {
44494  if (n) _data = new CImg<T>[_allocated_width = cimg::max(16UL,cimg::nearest_pow2(n))];
44495  else { _allocated_width = 0; _data = 0; }
44496  }
44497 
44499 
44507  CImgList(const unsigned int n, const unsigned int width, const unsigned int height=1,
44508  const unsigned int depth=1, const unsigned int spectrum=1):
44509  _width(0),_allocated_width(0),_data(0) {
44510  assign(n);
44511  cimglist_apply(*this,assign)(width,height,depth,spectrum);
44512  }
44513 
44515 
44523  CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
44524  const unsigned int depth, const unsigned int spectrum, const T val):
44525  _width(0),_allocated_width(0),_data(0) {
44526  assign(n);
44527  cimglist_apply(*this,assign)(width,height,depth,spectrum,val);
44528  }
44529 
44531 
44542  CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
44543  const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...):
44544  _width(0),_allocated_width(0),_data(0) {
44545 #define _CImgList_stdarg(t) { \
44546  assign(n,width,height,depth,spectrum); \
44547  const unsigned long siz = (unsigned long)width*height*depth*spectrum, nsiz = siz*n; \
44548  T *ptrd = _data->_data; \
44549  va_list ap; \
44550  va_start(ap,val1); \
44551  for (unsigned long l = 0, s = 0, i = 0; i<nsiz; ++i) { \
44552  *(ptrd++) = (T)(i==0?val0:(i==1?val1:va_arg(ap,t))); \
44553  if ((++s)==siz) { ptrd = _data[++l]._data; s = 0; } \
44554  } \
44555  va_end(ap); \
44556  }
44557  _CImgList_stdarg(int);
44558  }
44559 
44561 
44572  CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
44573  const unsigned int depth, const unsigned int spectrum, const double val0, const double val1, ...):
44574  _width(0),_allocated_width(0),_data(0) {
44575  _CImgList_stdarg(double);
44576  }
44577 
44579 
44584  template<typename t>
44585  CImgList(const unsigned int n, const CImg<t>& img, const bool is_shared=false):
44586  _width(0),_allocated_width(0),_data(0) {
44587  assign(n);
44588  cimglist_apply(*this,assign)(img,is_shared);
44589  }
44590 
44592 
44596  template<typename t>
44597  explicit CImgList(const CImg<t>& img, const bool is_shared=false):
44598  _width(0),_allocated_width(0),_data(0) {
44599  assign(1);
44600  _data[0].assign(img,is_shared);
44601  }
44602 
44604 
44609  template<typename t1, typename t2>
44610  CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const bool is_shared=false):
44611  _width(0),_allocated_width(0),_data(0) {
44612  assign(2);
44613  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared);
44614  }
44615 
44617 
44623  template<typename t1, typename t2, typename t3>
44624  CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool is_shared=false):
44625  _width(0),_allocated_width(0),_data(0) {
44626  assign(3);
44627  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
44628  }
44629 
44631 
44638  template<typename t1, typename t2, typename t3, typename t4>
44639  CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
44640  const bool is_shared=false):
44641  _width(0),_allocated_width(0),_data(0) {
44642  assign(4);
44643  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
44644  _data[3].assign(img4,is_shared);
44645  }
44646 
44648 
44656  template<typename t1, typename t2, typename t3, typename t4, typename t5>
44657  CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
44658  const CImg<t5>& img5, const bool is_shared=false):
44659  _width(0),_allocated_width(0),_data(0) {
44660  assign(5);
44661  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
44662  _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared);
44663  }
44664 
44666 
44675  template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
44676  CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
44677  const CImg<t5>& img5, const CImg<t6>& img6, const bool is_shared=false):
44678  _width(0),_allocated_width(0),_data(0) {
44679  assign(6);
44680  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
44681  _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
44682  }
44683 
44685 
44695  template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
44696  CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
44697  const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool is_shared=false):
44698  _width(0),_allocated_width(0),_data(0) {
44699  assign(7);
44700  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
44701  _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
44702  _data[6].assign(img7,is_shared);
44703  }
44704 
44706 
44717  template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
44718  CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
44719  const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8,
44720  const bool is_shared=false):
44721  _width(0),_allocated_width(0),_data(0) {
44722  assign(8);
44723  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
44724  _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
44725  _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared);
44726  }
44727 
44729 
44733  template<typename t>
44734  CImgList(const CImgList<t>& list):_width(0),_allocated_width(0),_data(0) {
44735  assign(list._width);
44736  cimglist_for(*this,l) _data[l].assign(list[l],false);
44737  }
44738 
44740  CImgList(const CImgList<T>& list):_width(0),_allocated_width(0),_data(0) {
44741  assign(list._width);
44742  cimglist_for(*this,l) _data[l].assign(list[l],list[l]._is_shared);
44743  }
44744 
44746 
44750  template<typename t>
44751  CImgList(const CImgList<t>& list, const bool is_shared):_width(0),_allocated_width(0),_data(0) {
44752  assign(list._width);
44753  cimglist_for(*this,l) _data[l].assign(list[l],is_shared);
44754  }
44755 
44757 
44760  explicit CImgList(const char *const filename):_width(0),_allocated_width(0),_data(0) {
44761  assign(filename);
44762  }
44763 
44765 
44769  explicit CImgList(const CImgDisplay& disp):_width(0),_allocated_width(0),_data(0) {
44770  assign(disp);
44771  }
44772 
44774 
44778  CImgList<T> res(_width);
44779  cimglist_for(*this,l) res[l].assign(_data[l],true);
44780  return res;
44781  }
44782 
44784  const CImgList<T> get_shared() const {
44785  CImgList<T> res(_width);
44786  cimglist_for(*this,l) res[l].assign(_data[l],true);
44787  return res;
44788  }
44789 
44791 
44795  delete[] _data;
44796  _width = _allocated_width = 0;
44797  _data = 0;
44798  return *this;
44799  }
44800 
44802 
44807  return assign();
44808  }
44809 
44811 
44814  CImgList<T>& assign(const unsigned int n) {
44815  if (!n) return assign();
44816  if (_allocated_width<n || _allocated_width>(n<<2)) {
44817  delete[] _data;
44818  _data = new CImg<T>[_allocated_width=cimg::max(16UL,cimg::nearest_pow2(n))];
44819  }
44820  _width = n;
44821  return *this;
44822  }
44823 
44825 
44828  CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height=1,
44829  const unsigned int depth=1, const unsigned int spectrum=1) {
44830  assign(n);
44831  cimglist_apply(*this,assign)(width,height,depth,spectrum);
44832  return *this;
44833  }
44834 
44836 
44839  CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
44840  const unsigned int depth, const unsigned int spectrum, const T val) {
44841  assign(n);
44842  cimglist_apply(*this,assign)(width,height,depth,spectrum,val);
44843  return *this;
44844  }
44845 
44847 
44850  CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
44851  const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...) {
44852  _CImgList_stdarg(int);
44853  return *this;
44854  }
44855 
44857 
44860  CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
44861  const unsigned int depth, const unsigned int spectrum,
44862  const double val0, const double val1, ...) {
44863  _CImgList_stdarg(double);
44864  return *this;
44865  }
44866 
44868 
44871  template<typename t>
44872  CImgList<T>& assign(const unsigned int n, const CImg<t>& img, const bool is_shared=false) {
44873  assign(n);
44874  cimglist_apply(*this,assign)(img,is_shared);
44875  return *this;
44876  }
44877 
44879 
44882  template<typename t>
44883  CImgList<T>& assign(const CImg<t>& img, const bool is_shared=false) {
44884  assign(1);
44885  _data[0].assign(img,is_shared);
44886  return *this;
44887  }
44888 
44890 
44893  template<typename t1, typename t2>
44894  CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const bool is_shared=false) {
44895  assign(2);
44896  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared);
44897  return *this;
44898  }
44899 
44901 
44904  template<typename t1, typename t2, typename t3>
44905  CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool is_shared=false) {
44906  assign(3);
44907  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
44908  return *this;
44909  }
44910 
44912 
44915  template<typename t1, typename t2, typename t3, typename t4>
44916  CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
44917  const bool is_shared=false) {
44918  assign(4);
44919  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
44920  _data[3].assign(img4,is_shared);
44921  return *this;
44922  }
44923 
44925 
44928  template<typename t1, typename t2, typename t3, typename t4, typename t5>
44929  CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
44930  const CImg<t5>& img5, const bool is_shared=false) {
44931  assign(5);
44932  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
44933  _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared);
44934  return *this;
44935  }
44936 
44938 
44941  template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
44942  CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
44943  const CImg<t5>& img5, const CImg<t6>& img6, const bool is_shared=false) {
44944  assign(6);
44945  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
44946  _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
44947  return *this;
44948  }
44949 
44951 
44954  template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
44955  CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
44956  const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool is_shared=false) {
44957  assign(7);
44958  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
44959  _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
44960  _data[6].assign(img7,is_shared);
44961  return *this;
44962  }
44963 
44965 
44968  template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
44969  CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
44970  const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8,
44971  const bool is_shared=false) {
44972  assign(8);
44973  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
44974  _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
44975  _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared);
44976  return *this;
44977  }
44978 
44980 
44983  template<typename t>
44984  CImgList<T>& assign(const CImgList<t>& list, const bool is_shared=false) {
44986  assign(list._width);
44987  cimglist_for(*this,l) _data[l].assign(list[l],false);
44988  return *this;
44989  }
44990 
44992  CImgList<T>& assign(const CImgList<T>& list, const bool is_shared=false) {
44993  if (this==&list) return *this;
44994  CImgList<T> res(list._width);
44995  cimglist_for(res,l) res[l].assign(list[l],is_shared);
44996  return res.move_to(*this);
44997  }
44998 
45000 
45003  CImgList<T>& assign(const char *const filename) {
45004  return load(filename);
45005  }
45006 
45008 
45012  return assign(CImg<T>(disp));
45013  }
45014 
45016 
45020  template<typename t>
45022  list.assign(_width);
45023  bool is_one_shared_element = false;
45024  cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared;
45025  if (is_one_shared_element) cimglist_for(*this,l) list[l].assign(_data[l]);
45026  else cimglist_for(*this,l) _data[l].move_to(list[l]);
45027  assign();
45028  return list;
45029  }
45030 
45032 
45038  template<typename t>
45039  CImgList<t>& move_to(CImgList<t>& list, const unsigned int pos) {
45040  if (is_empty()) return list;
45041  const unsigned int npos = pos>list._width?list._width:pos;
45042  list.insert(_width,npos);
45043  bool is_one_shared_element = false;
45044  cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared;
45045  if (is_one_shared_element) cimglist_for(*this,l) list[npos+l].assign(_data[l]);
45046  else cimglist_for(*this,l) _data[l].move_to(list[npos+l]);
45047  assign();
45048  return list;
45049  }
45050 
45052 
45057  cimg::swap(_width,list._width);
45058  cimg::swap(_allocated_width,list._allocated_width);
45059  cimg::swap(_data,list._data);
45060  return list;
45061  }
45062 
45064 
45070  static CImgList<T>& empty() {
45071  static CImgList<T> _empty;
45072  return _empty.assign();
45073  }
45074 
45076  //------------------------------------------
45077  //
45079 
45080  //------------------------------------------
45081 
45083 
45086  CImg<T>& operator()(const unsigned int pos) {
45087 #if cimg_verbosity>=3
45088  if (pos>=_width) {
45089  cimg::warn(_cimglist_instance
45090  "operator(): Invalid image request, at position [%u].",
45091  cimglist_instance,
45092  pos);
45093  return *_data;
45094  }
45095 #endif
45096  return _data[pos];
45097  }
45098 
45100 
45103  const CImg<T>& operator()(const unsigned int pos) const {
45104  return const_cast<CImgList<T>*>(this)->operator()(pos);
45105  }
45106 
45108 
45116  T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0,
45117  const unsigned int z=0, const unsigned int c=0) {
45118  return (*this)[pos](x,y,z,c);
45119  }
45120 
45122  const T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0,
45123  const unsigned int z=0, const unsigned int c=0) const {
45124  return (*this)[pos](x,y,z,c);
45125  }
45126 
45128 
45131  operator CImg<T>*() {
45132  return _data;
45133  }
45134 
45136  operator const CImg<T>*() const {
45137  return _data;
45138  }
45139 
45141 
45145  template<typename t>
45147  return assign(img);
45148  }
45149 
45151 
45155  template<typename t>
45157  return assign(list);
45158  }
45159 
45162  return assign(list);
45163  }
45164 
45166 
45169  CImgList<T>& operator=(const char *const filename) {
45170  return assign(filename);
45171  }
45172 
45174 
45178  return assign(disp);
45179  }
45180 
45182 
45187  return CImgList<T>(*this,false);
45188  }
45189 
45191 
45198  template<typename t>
45200  return insert(img);
45201  }
45202 
45204  template<typename t>
45205  CImgList<T> operator,(const CImg<t>& img) const {
45206  return (+*this).insert(img);
45207  }
45208 
45210 
45213  template<typename t>
45215  return insert(list);
45216  }
45217 
45219  template<typename t>
45220  CImgList<T>& operator,(const CImgList<t>& list) const {
45221  return (+*this).insert(list);
45222  }
45223 
45225 
45229  CImg<T> operator>(const char axis) const {
45230  return get_append(axis,0);
45231  }
45232 
45234 
45238  CImgList<T> operator<(const char axis) const {
45239  return get_split(axis);
45240  }
45241 
45243  //-------------------------------------
45244  //
45246 
45247  //-------------------------------------
45248 
45250 
45257  static const char* pixel_type() {
45258  return cimg::type<T>::string();
45259  }
45260 
45262 
45265  int width() const {
45266  return (int)_width;
45267  }
45268 
45270 
45273  unsigned int size() const {
45274  return _width;
45275  }
45276 
45278 
45282  return _data;
45283  }
45284 
45286  const CImg<T> *data() const {
45287  return _data;
45288  }
45289 
45291 
45295 #if cimg_verbosity>=3
45296  CImg<T> *data(const unsigned int pos) {
45297  if (pos>=size())
45298  cimg::warn(_cimglist_instance
45299  "data(): Invalid pointer request, at position [%u].",
45300  cimglist_instance,
45301  pos);
45302  return _data + pos;
45303  }
45304 
45305  const CImg<T> *data(const unsigned int l) const {
45306  return const_cast<CImgList<T>*>(this)->data(l);
45307  }
45308 #else
45309  CImg<T> *data(const unsigned int l) {
45310  return _data + l;
45311  }
45312 
45314  const CImg<T> *data(const unsigned int l) const {
45315  return _data + l;
45316  }
45317 #endif
45318 
45320 
45322  iterator begin() {
45323  return _data;
45324  }
45325 
45327  const_iterator begin() const {
45328  return _data;
45329  }
45330 
45332 
45334  iterator end() {
45335  return _data + _width;
45336  }
45337 
45339  const_iterator end() const {
45340  return _data + _width;
45341  }
45342 
45344 
45347  return *_data;
45348  }
45349 
45351  const CImg<T>& front() const {
45352  return *_data;
45353  }
45354 
45356 
45358  const CImg<T>& back() const {
45359  return *(_data + _width - 1);
45360  }
45361 
45364  return *(_data + _width - 1);
45365  }
45366 
45368 
45371  CImg<T>& at(const int pos) {
45372  if (is_empty())
45373  throw CImgInstanceException(_cimglist_instance
45374  "at(): Empty instance.",
45375  cimglist_instance);
45376 
45377  return _data[pos<0?0:pos>=(int)_width?(int)_width-1:pos];
45378  }
45379 
45381 
45390  T& atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T out_value) {
45391  return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atXYZC(x,y,z,c,out_value);
45392  }
45393 
45395  T atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T out_value) const {
45396  return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atXYZC(x,y,z,c,out_value);
45397  }
45398 
45400 
45408  T& atNXYZC(const int pos, const int x, const int y, const int z, const int c) {
45409  if (is_empty())
45410  throw CImgInstanceException(_cimglist_instance
45411  "atNXYZC(): Empty instance.",
45412  cimglist_instance);
45413 
45414  return _atNXYZC(pos,x,y,z,c);
45415  }
45416 
45418  T atNXYZC(const int pos, const int x, const int y, const int z, const int c) const {
45419  if (is_empty())
45420  throw CImgInstanceException(_cimglist_instance
45421  "atNXYZC(): Empty instance.",
45422  cimglist_instance);
45423 
45424  return _atNXYZC(pos,x,y,z,c);
45425  }
45426 
45427  T& _atNXYZC(const int pos, const int x, const int y, const int z, const int c) {
45428  return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZC(x,y,z,c);
45429  }
45430 
45431  T _atNXYZC(const int pos, const int x, const int y, const int z, const int c) const {
45432  return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZC(x,y,z,c);
45433  }
45434 
45436 
45445  T& atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T out_value) {
45446  return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atXYZ(x,y,z,c,out_value);
45447  }
45448 
45450  T atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T out_value) const {
45451  return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atXYZ(x,y,z,c,out_value);
45452  }
45453 
45455 
45463  T& atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) {
45464  if (is_empty())
45465  throw CImgInstanceException(_cimglist_instance
45466  "atNXYZ(): Empty instance.",
45467  cimglist_instance);
45468 
45469  return _atNXYZ(pos,x,y,z,c);
45470  }
45471 
45473  T atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const {
45474  if (is_empty())
45475  throw CImgInstanceException(_cimglist_instance
45476  "atNXYZ(): Empty instance.",
45477  cimglist_instance);
45478 
45479  return _atNXYZ(pos,x,y,z,c);
45480  }
45481 
45482  T& _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) {
45483  return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZ(x,y,z,c);
45484  }
45485 
45486  T _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const {
45487  return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZ(x,y,z,c);
45488  }
45489 
45491 
45500  T& atNXY(const int pos, const int x, const int y, const int z, const int c, const T out_value) {
45501  return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atXY(x,y,z,c,out_value);
45502  }
45503 
45505  T atNXY(const int pos, const int x, const int y, const int z, const int c, const T out_value) const {
45506  return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atXY(x,y,z,c,out_value);
45507  }
45508 
45510 
45518  T& atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) {
45519  if (is_empty())
45520  throw CImgInstanceException(_cimglist_instance
45521  "atNXY(): Empty instance.",
45522  cimglist_instance);
45523 
45524  return _atNXY(pos,x,y,z,c);
45525  }
45526 
45528  T atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const {
45529  if (is_empty())
45530  throw CImgInstanceException(_cimglist_instance
45531  "atNXY(): Empty instance.",
45532  cimglist_instance);
45533 
45534  return _atNXY(pos,x,y,z,c);
45535  }
45536 
45537  T& _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) {
45538  return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXY(x,y,z,c);
45539  }
45540 
45541  T _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const {
45542  return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXY(x,y,z,c);
45543  }
45544 
45546 
45555  T& atNX(const int pos, const int x, const int y, const int z, const int c, const T out_value) {
45556  return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atX(x,y,z,c,out_value);
45557  }
45558 
45560  T atNX(const int pos, const int x, const int y, const int z, const int c, const T out_value) const {
45561  return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atX(x,y,z,c,out_value);
45562  }
45563 
45565 
45573  T& atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) {
45574  if (is_empty())
45575  throw CImgInstanceException(_cimglist_instance
45576  "atNX(): Empty instance.",
45577  cimglist_instance);
45578 
45579  return _atNX(pos,x,y,z,c);
45580  }
45581 
45583  T atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const {
45584  if (is_empty())
45585  throw CImgInstanceException(_cimglist_instance
45586  "atNX(): Empty instance.",
45587  cimglist_instance);
45588 
45589  return _atNX(pos,x,y,z,c);
45590  }
45591 
45592  T& _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) {
45593  return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atX(x,y,z,c);
45594  }
45595 
45596  T _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const {
45597  return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atX(x,y,z,c);
45598  }
45599 
45601 
45610  T& atN(const int pos, const int x, const int y, const int z, const int c, const T out_value) {
45611  return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):(*this)(pos,x,y,z,c);
45612  }
45613 
45615  T atN(const int pos, const int x, const int y, const int z, const int c, const T out_value) const {
45616  return (pos<0 || pos>=(int)_width)?out_value:(*this)(pos,x,y,z,c);
45617  }
45618 
45620 
45628  T& atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) {
45629  if (is_empty())
45630  throw CImgInstanceException(_cimglist_instance
45631  "atN(): Empty instance.",
45632  cimglist_instance);
45633  return _atN(pos,x,y,z,c);
45634  }
45635 
45637  T atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const {
45638  if (is_empty())
45639  throw CImgInstanceException(_cimglist_instance
45640  "atN(): Empty instance.",
45641  cimglist_instance);
45642  return _atN(pos,x,y,z,c);
45643  }
45644 
45645  T& _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) {
45646  return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)](x,y,z,c);
45647  }
45648 
45649  T _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const {
45650  return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)](x,y,z,c);
45651  }
45652 
45654 
45659  CImg<charT> value_string(const char separator=',', const unsigned int max_size=0) const {
45660  if (is_empty()) return CImg<ucharT>(1,1,1,1,0);
45661  CImgList<charT> items;
45662  for (unsigned int l = 0; l<_width-1; ++l) {
45663  CImg<charT> item = _data[l].value_string(separator,0);
45664  item.back() = separator;
45665  item.move_to(items);
45666  }
45667  _data[_width-1].value_string(separator,0).move_to(items);
45668  CImg<charT> res; (items>'x').move_to(res);
45669  if (max_size) { res.crop(0,max_size); res(max_size) = 0; }
45670  return res;
45671  }
45672 
45674  //-------------------------------------
45675  //
45677 
45678  //-------------------------------------
45679 
45681 
45683  bool is_empty() const {
45684  return (!_data || !_width);
45685  }
45686 
45688 
45691  bool is_sameN(const unsigned int size_n) const {
45692  return _width==size_n;
45693  }
45694 
45696 
45699  template<typename t>
45700  bool is_sameN(const CImgList<t>& list) const {
45701  return is_sameN(list._width);
45702  }
45703 
45704  // Define useful functions to check list dimensions.
45705  // (cannot be documented because macro-generated).
45706 #define _cimglist_def_is_same1(axis) \
45707  bool is_same##axis(const unsigned int val) const { \
45708  bool res = true; \
45709  for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(val); return res; \
45710  } \
45711  bool is_sameN##axis(const unsigned int n, const unsigned int val) const { \
45712  return is_sameN(n) && is_same##axis(val); \
45713  } \
45714 
45715 #define _cimglist_def_is_same2(axis1,axis2) \
45716  bool is_same##axis1##axis2(const unsigned int val1, const unsigned int val2) const { \
45717  bool res = true; \
45718  for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2(val1,val2); return res; \
45719  } \
45720  bool is_sameN##axis1##axis2(const unsigned int n, const unsigned int val1, const unsigned int val2) const { \
45721  return is_sameN(n) && is_same##axis1##axis2(val1,val2); \
45722  } \
45723 
45724 #define _cimglist_def_is_same3(axis1,axis2,axis3) \
45725  bool is_same##axis1##axis2##axis3(const unsigned int val1, const unsigned int val2, \
45726  const unsigned int val3) const { \
45727  bool res = true; \
45728  for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2##axis3(val1,val2,val3); \
45729  return res; \
45730  } \
45731  bool is_sameN##axis1##axis2##axis3(const unsigned int n, const unsigned int val1, \
45732  const unsigned int val2, const unsigned int val3) const { \
45733  return is_sameN(n) && is_same##axis1##axis2##axis3(val1,val2,val3); \
45734  } \
45735 
45736 #define _cimglist_def_is_same(axis) \
45737  template<typename t> bool is_same##axis(const CImg<t>& img) const { \
45738  bool res = true; for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(img); return res; \
45739  } \
45740  template<typename t> bool is_same##axis(const CImgList<t>& list) const { \
45741  const unsigned int lmin = cimg::min(_width,list._width); \
45742  bool res = true; for (unsigned int l = 0; l<lmin && res; ++l) res = _data[l].is_same##axis(list[l]); return res; \
45743  } \
45744  template<typename t> bool is_sameN##axis(const unsigned int n, const CImg<t>& img) const { \
45745  return (is_sameN(n) && is_same##axis(img)); \
45746  } \
45747  template<typename t> bool is_sameN##axis(const CImgList<t>& list) const { \
45748  return (is_sameN(list) && is_same##axis(list)); \
45749  }
45750 
45751  _cimglist_def_is_same(XY)
45752  _cimglist_def_is_same(XZ)
45753  _cimglist_def_is_same(XC)
45754  _cimglist_def_is_same(YZ)
45755  _cimglist_def_is_same(YC)
45756  _cimglist_def_is_same(XYZ)
45757  _cimglist_def_is_same(XYC)
45758  _cimglist_def_is_same(YZC)
45759  _cimglist_def_is_same(XYZC)
45760  _cimglist_def_is_same1(X)
45761  _cimglist_def_is_same1(Y)
45762  _cimglist_def_is_same1(Z)
45763  _cimglist_def_is_same1(C)
45764  _cimglist_def_is_same2(X,Y)
45765  _cimglist_def_is_same2(X,Z)
45766  _cimglist_def_is_same2(X,C)
45767  _cimglist_def_is_same2(Y,Z)
45768  _cimglist_def_is_same2(Y,C)
45769  _cimglist_def_is_same2(Z,C)
45770  _cimglist_def_is_same3(X,Y,Z)
45771  _cimglist_def_is_same3(X,Y,C)
45772  _cimglist_def_is_same3(X,Z,C)
45773  _cimglist_def_is_same3(Y,Z,C)
45774 
45776 
45782  bool is_sameXYZC(const unsigned int dx, const unsigned int dy,
45783  const unsigned int dz, const unsigned int dc) const {
45784  bool res = true;
45785  for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_sameXYZC(dx,dy,dz,dc);
45786  return res;
45787  }
45788 
45790 
45797  bool is_sameNXYZC(const unsigned int n,
45798  const unsigned int dx, const unsigned int dy,
45799  const unsigned int dz, const unsigned int dc) const {
45800  return is_sameN(n) && is_sameXYZC(dx,dy,dz,dc);
45801  }
45802 
45804 
45811  bool containsNXYZC(const int n, const int x=0, const int y=0, const int z=0, const int c=0) const {
45812  if (is_empty()) return false;
45813  return n>=0 && n<(int)_width && x>=0 && x<_data[n].width() && y>=0 && y<_data[n].height() &&
45814  z>=0 && z<_data[n].depth() && c>=0 && c<_data[n].spectrum();
45815  }
45816 
45818 
45821  bool containsN(const int n) const {
45822  if (is_empty()) return false;
45823  return n>=0 && n<(int)_width;
45824  }
45825 
45827 
45836  template<typename t>
45837  bool contains(const T& pixel, t& n, t& x, t&y, t& z, t& c) const {
45838  if (is_empty()) return false;
45839  cimglist_for(*this,l) if (_data[l].contains(pixel,x,y,z,c)) { n = (t)l; return true; }
45840  return false;
45841  }
45842 
45844 
45852  template<typename t>
45853  bool contains(const T& pixel, t& n, t& x, t&y, t& z) const {
45854  t c;
45855  return contains(pixel,n,x,y,z,c);
45856  }
45857 
45859 
45866  template<typename t>
45867  bool contains(const T& pixel, t& n, t& x, t&y) const {
45868  t z, c;
45869  return contains(pixel,n,x,y,z,c);
45870  }
45871 
45873 
45879  template<typename t>
45880  bool contains(const T& pixel, t& n, t& x) const {
45881  t y, z, c;
45882  return contains(pixel,n,x,y,z,c);
45883  }
45884 
45886 
45891  template<typename t>
45892  bool contains(const T& pixel, t& n) const {
45893  t x, y, z, c;
45894  return contains(pixel,n,x,y,z,c);
45895  }
45896 
45898 
45901  bool contains(const T& pixel) const {
45902  unsigned int n, x, y, z, c;
45903  return contains(pixel,n,x,y,z,c);
45904  }
45905 
45907 
45912  template<typename t>
45913  bool contains(const CImg<T>& img, t& n) const {
45914  if (is_empty()) return false;
45915  const CImg<T> *const ptr = &img;
45916  cimglist_for(*this,i) if (_data+i==ptr) { n = (t)i; return true; }
45917  return false;
45918  }
45919 
45921 
45924  bool contains(const CImg<T>& img) const {
45925  unsigned int n;
45926  return contains(img,n);
45927  }
45928 
45930  //-------------------------------------
45931  //
45933 
45934  //-------------------------------------
45935 
45937 
45939  T& min() {
45940  if (is_empty())
45941  throw CImgInstanceException(_cimglist_instance
45942  "min(): Empty instance.",
45943  cimglist_instance);
45944  T *ptr_min = _data->_data;
45945  T min_value = *ptr_min;
45946  cimglist_for(*this,l) {
45947  const CImg<T>& img = _data[l];
45948  cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
45949  }
45950  return *ptr_min;
45951  }
45952 
45954  const T& min() const {
45955  if (is_empty())
45956  throw CImgInstanceException(_cimglist_instance
45957  "min(): Empty instance.",
45958  cimglist_instance);
45959  const T *ptr_min = _data->_data;
45960  T min_value = *ptr_min;
45961  cimglist_for(*this,l) {
45962  const CImg<T>& img = _data[l];
45963  cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
45964  }
45965  return *ptr_min;
45966  }
45967 
45969 
45971  T& max() {
45972  if (is_empty())
45973  throw CImgInstanceException(_cimglist_instance
45974  "max(): Empty instance.",
45975  cimglist_instance);
45976  T *ptr_max = _data->_data;
45977  T max_value = *ptr_max;
45978  cimglist_for(*this,l) {
45979  const CImg<T>& img = _data[l];
45980  cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
45981  }
45982  return *ptr_max;
45983  }
45984 
45986  const T& max() const {
45987  if (is_empty())
45988  throw CImgInstanceException(_cimglist_instance
45989  "max(): Empty instance.",
45990  cimglist_instance);
45991  const T *ptr_max = _data->_data;
45992  T max_value = *ptr_max;
45993  cimglist_for(*this,l) {
45994  const CImg<T>& img = _data[l];
45995  cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
45996  }
45997  return *ptr_max;
45998  }
45999 
46001 
46004  template<typename t>
46005  T& min_max(t& max_val) {
46006  if (is_empty())
46007  throw CImgInstanceException(_cimglist_instance
46008  "min_max(): Empty instance.",
46009  cimglist_instance);
46010  T *ptr_min = _data->_data;
46011  T min_value = *ptr_min, max_value = min_value;
46012  cimglist_for(*this,l) {
46013  const CImg<T>& img = _data[l];
46014  cimg_for(img,ptrs,T) {
46015  const T val = *ptrs;
46016  if (val<min_value) { min_value = val; ptr_min = ptrs; }
46017  if (val>max_value) max_value = val;
46018  }
46019  }
46020  max_val = (t)max_value;
46021  return *ptr_min;
46022  }
46023 
46025 
46028  template<typename t>
46029  const T& min_max(t& max_val) const {
46030  if (is_empty())
46031  throw CImgInstanceException(_cimglist_instance
46032  "min_max(): Empty instance.",
46033  cimglist_instance);
46034  const T *ptr_min = _data->_data;
46035  T min_value = *ptr_min, max_value = min_value;
46036  cimglist_for(*this,l) {
46037  const CImg<T>& img = _data[l];
46038  cimg_for(img,ptrs,T) {
46039  const T val = *ptrs;
46040  if (val<min_value) { min_value = val; ptr_min = ptrs; }
46041  if (val>max_value) max_value = val;
46042  }
46043  }
46044  max_val = (t)max_value;
46045  return *ptr_min;
46046  }
46047 
46049 
46052  template<typename t>
46053  T& max_min(t& min_val) {
46054  if (is_empty())
46055  throw CImgInstanceException(_cimglist_instance
46056  "max_min(): Empty instance.",
46057  cimglist_instance);
46058  T *ptr_max = _data->_data;
46059  T min_value = *ptr_max, max_value = min_value;
46060  cimglist_for(*this,l) {
46061  const CImg<T>& img = _data[l];
46062  cimg_for(img,ptrs,T) {
46063  const T val = *ptrs;
46064  if (val>max_value) { max_value = val; ptr_max = ptrs; }
46065  if (val<min_value) min_value = val;
46066  }
46067  }
46068  min_val = (t)min_value;
46069  return *ptr_max;
46070  }
46071 
46073  template<typename t>
46074  const T& max_min(t& min_val) const {
46075  if (is_empty())
46076  throw CImgInstanceException(_cimglist_instance
46077  "max_min(): Empty instance.",
46078  cimglist_instance);
46079  const T *ptr_max = _data->_data;
46080  T min_value = *ptr_max, max_value = min_value;
46081  cimglist_for(*this,l) {
46082  const CImg<T>& img = _data[l];
46083  cimg_for(img,ptrs,T) {
46084  const T val = *ptrs;
46085  if (val>max_value) { max_value = val; ptr_max = ptrs; }
46086  if (val<min_value) min_value = val;
46087  }
46088  }
46089  min_val = (t)min_value;
46090  return *ptr_max;
46091  }
46092 
46094  //---------------------------
46095  //
46097 
46098  //---------------------------
46099 
46101 
46106  template<typename t>
46107  CImgList<T>& insert(const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) {
46108  const unsigned int npos = pos==~0U?_width:pos;
46109  if (npos>_width)
46110  throw CImgArgumentException(_cimglist_instance
46111  "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) "
46112  "at position %u.",
46113  cimglist_instance,
46114  img._width,img._height,img._depth,img._spectrum,img._data,npos);
46115  if (is_shared)
46116  throw CImgArgumentException(_cimglist_instance
46117  "insert(): Invalid insertion request of specified shared image "
46118  "CImg<%s>(%u,%u,%u,%u,%p) at position %u (pixel types are different).",
46119  cimglist_instance,
46120  img.pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data,npos);
46121 
46122  CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1):
46123  (_allocated_width=16)]:0;
46124  if (!_data) { // Insert new element into empty list.
46125  _data = new_data;
46126  *_data = img;
46127  } else {
46128  if (new_data) { // Insert with re-allocation.
46129  if (npos) std::memcpy(new_data,_data,sizeof(CImg<T>)*npos);
46130  if (npos!=_width-1) std::memcpy(new_data+npos+1,_data+npos,sizeof(CImg<T>)*(_width-1-npos));
46131  std::memset(_data,0,sizeof(CImg<T>)*(_width-1));
46132  delete[] _data;
46133  _data = new_data;
46134  } else if (npos!=_width-1)
46135  std::memmove(_data+npos+1,_data+npos,sizeof(CImg<T>)*(_width-1-npos)); // Insert without re-allocation.
46136  _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0; _data[npos]._data = 0;
46137  _data[npos] = img;
46138  }
46139  return *this;
46140  }
46141 
46143  CImgList<T>& insert(const CImg<T>& img, const unsigned int pos=~0U, const bool is_shared=false) {
46144  const unsigned int npos = pos==~0U?_width:pos;
46145  if (npos>_width)
46146  throw CImgArgumentException(_cimglist_instance
46147  "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) "
46148  "at position %u.",
46149  cimglist_instance,
46150  img._width,img._height,img._depth,img._spectrum,img._data,npos);
46151  CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1):
46152  (_allocated_width=16)]:0;
46153  if (!_data) { // Insert new element into empty list.
46154  _data = new_data;
46155  if (is_shared && img) {
46156  _data->_width = img._width;
46157  _data->_height = img._height;
46158  _data->_depth = img._depth;
46159  _data->_spectrum = img._spectrum;
46160  _data->_is_shared = true;
46161  _data->_data = img._data;
46162  } else *_data = img;
46163  }
46164  else {
46165  if (new_data) { // Insert with re-allocation.
46166  if (npos) std::memcpy(new_data,_data,sizeof(CImg<T>)*npos);
46167  if (npos!=_width-1) std::memcpy(new_data+npos+1,_data+npos,sizeof(CImg<T>)*(_width-1-npos));
46168  if (is_shared && img) {
46169  new_data[npos]._width = img._width;
46170  new_data[npos]._height = img._height;
46171  new_data[npos]._depth = img._depth;
46172  new_data[npos]._spectrum = img._spectrum;
46173  new_data[npos]._is_shared = true;
46174  new_data[npos]._data = img._data;
46175  } else {
46176  new_data[npos]._width = new_data[npos]._height = new_data[npos]._depth = new_data[npos]._spectrum = 0;
46177  new_data[npos]._data = 0;
46178  new_data[npos] = img;
46179  }
46180  std::memset(_data,0,sizeof(CImg<T>)*(_width-1));
46181  delete[] _data;
46182  _data = new_data;
46183  } else { // Insert without re-allocation.
46184  if (npos!=_width-1) std::memmove(_data+npos+1,_data+npos,sizeof(CImg<T>)*(_width-1-npos));
46185  if (is_shared && img) {
46186  _data[npos]._width = img._width;
46187  _data[npos]._height = img._height;
46188  _data[npos]._depth = img._depth;
46189  _data[npos]._spectrum = img._spectrum;
46190  _data[npos]._is_shared = true;
46191  _data[npos]._data = img._data;
46192  } else {
46193  _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0;
46194  _data[npos]._data = 0;
46195  _data[npos] = img;
46196  }
46197  }
46198  }
46199  return *this;
46200  }
46201 
46203  template<typename t>
46204  CImgList<T> get_insert(const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) const {
46205  return (+*this).insert(img,pos,is_shared);
46206  }
46207 
46209 
46213  CImgList<T>& insert(const unsigned int n, const unsigned int pos=~0U) {
46214  CImg<T> empty;
46215  if (!n) return *this;
46216  const unsigned int npos = pos==~0U?_width:pos;
46217  for (unsigned int i = 0; i<n; ++i) insert(empty,npos+i);
46218  return *this;
46219  }
46220 
46222  CImgList<T> get_insert(const unsigned int n, const unsigned int pos=~0U) const {
46223  return (+*this).insert(n,pos);
46224  }
46225 
46227 
46233  template<typename t>
46234  CImgList<T>& insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U,
46235  const bool is_shared=false) {
46236  if (!n) return *this;
46237  const unsigned int npos = pos==~0U?_width:pos;
46238  insert(img,npos,is_shared);
46239  for (unsigned int i = 1; i<n; ++i) insert(_data[npos],npos+i,is_shared);
46240  return *this;
46241  }
46242 
46244  template<typename t>
46245  CImgList<T> get_insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U,
46246  const bool is_shared=false) const {
46247  return (+*this).insert(n,img,pos,is_shared);
46248  }
46249 
46251 
46256  template<typename t>
46257  CImgList<T>& insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) {
46258  const unsigned int npos = pos==~0U?_width:pos;
46259  if ((void*)this!=(void*)&list) cimglist_for(list,l) insert(list[l],npos+l,is_shared);
46260  else insert(CImgList<T>(list),npos,is_shared);
46261  return *this;
46262  }
46263 
46265  template<typename t>
46266  CImgList<T> get_insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) const {
46267  return (+*this).insert(list,pos,is_shared);
46268  }
46269 
46271 
46277  template<typename t>
46278  CImgList<T>& insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U,
46279  const bool is_shared=false) {
46280  if (!n) return *this;
46281  const unsigned int npos = pos==~0U?_width:pos;
46282  for (unsigned int i = 0; i<n; ++i) insert(list,npos,is_shared);
46283  return *this;
46284  }
46285 
46287  template<typename t>
46288  CImgList<T> get_insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U,
46289  const bool is_shared=false) const {
46290  return (+*this).insert(n,list,pos,is_shared);
46291  }
46292 
46294 
46298  CImgList<T>& remove(const unsigned int pos1, const unsigned int pos2) {
46299  const unsigned int
46300  npos1 = pos1<pos2?pos1:pos2,
46301  tpos2 = pos1<pos2?pos2:pos1,
46302  npos2 = tpos2<_width?tpos2:_width-1;
46303  if (npos1>=_width)
46304  throw CImgArgumentException(_cimglist_instance
46305  "remove(): Invalid remove request at positions %u->%u.",
46306  cimglist_instance,
46307  npos1,tpos2);
46308  else {
46309  if (tpos2>=_width)
46310  throw CImgArgumentException(_cimglist_instance
46311  "remove(): Invalid remove request at positions %u->%u.",
46312  cimglist_instance,
46313  npos1,tpos2);
46314 
46315  for (unsigned int k = npos1; k<=npos2; ++k) _data[k].assign();
46316  const unsigned int nb = 1 + npos2 - npos1;
46317  if (!(_width-=nb)) return assign();
46318  if (_width>(_allocated_width>>2) || _allocated_width<=16) { // Removing items without reallocation.
46319  if (npos1!=_width) std::memmove(_data+npos1,_data+npos2+1,sizeof(CImg<T>)*(_width - npos1));
46320  std::memset(_data + _width,0,sizeof(CImg<T>)*nb);
46321  } else { // Removing items with reallocation.
46322  _allocated_width>>=2;
46323  while (_allocated_width>16 && _width<(_allocated_width>>1)) _allocated_width>>=1;
46324  CImg<T> *const new_data = new CImg<T>[_allocated_width];
46325  if (npos1) std::memcpy(new_data,_data,sizeof(CImg<T>)*npos1);
46326  if (npos1!=_width) std::memcpy(new_data+npos1,_data+npos2+1,sizeof(CImg<T>)*(_width-npos1));
46327  if (_width!=_allocated_width) std::memset(new_data+_width,0,sizeof(_allocated_width - _width));
46328  std::memset(_data,0,sizeof(CImg<T>)*(_width+nb));
46329  delete[] _data;
46330  _data = new_data;
46331  }
46332  }
46333  return *this;
46334  }
46335 
46337  CImgList<T> get_remove(const unsigned int pos1, const unsigned int pos2) const {
46338  return (+*this).remove(pos1,pos2);
46339  }
46340 
46342 
46345  CImgList<T>& remove(const unsigned int pos) {
46346  return remove(pos,pos);
46347  }
46348 
46350  CImgList<T> get_remove(const unsigned int pos) const {
46351  return (+*this).remove(pos);
46352  }
46353 
46355 
46357  CImgList<T>& remove() {
46358  return remove(_width-1);
46359  }
46360 
46363  return (+*this).remove();
46364  }
46365 
46368  for (unsigned int l = 0; l<_width/2; ++l) (*this)[l].swap((*this)[_width-1-l]);
46369  return *this;
46370  }
46371 
46374  return (+*this).reverse();
46375  }
46376 
46378 
46382  CImgList<T>& images(const unsigned int pos0, const unsigned int pos1) {
46383  return get_images(pos0,pos1).move_to(*this);
46384  }
46385 
46387  CImgList<T> get_images(const unsigned int pos0, const unsigned int pos1) const {
46388  if (pos0>pos1 || pos1>=_width)
46389  throw CImgArgumentException(_cimglist_instance
46390  "images(): Specified sub-list indices (%u->%u) are out of bounds.",
46391  cimglist_instance,
46392  pos0,pos1);
46393  CImgList<T> res(pos1-pos0+1);
46394  cimglist_for(res,l) res[l].assign(_data[pos0+l]);
46395  return res;
46396  }
46397 
46399 
46403  CImgList<T> get_shared_images(const unsigned int pos0, const unsigned int pos1) {
46404  if (pos0>pos1 || pos1>=_width)
46405  throw CImgArgumentException(_cimglist_instance
46406  "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.",
46407  cimglist_instance,
46408  pos0,pos1);
46409  CImgList<T> res(pos1-pos0+1);
46410  cimglist_for(res,l) res[l].assign(_data[pos0+l],_data[pos0+l]?true:false);
46411  return res;
46412  }
46413 
46415  const CImgList<T> get_shared_images(const unsigned int pos0, const unsigned int pos1) const {
46416  if (pos0>pos1 || pos1>=_width)
46417  throw CImgArgumentException(_cimglist_instance
46418  "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.",
46419  cimglist_instance,
46420  pos0,pos1);
46421  CImgList<T> res(pos1-pos0+1);
46422  cimglist_for(res,l) res[l].assign(_data[pos0+l],_data[pos0+l]?true:false);
46423  return res;
46424  }
46425 
46427 
46431  CImg<T> get_append(const char axis, const float align=0) const {
46432  if (is_empty()) return CImg<T>();
46433  if (_width==1) return +((*this)[0]);
46434  unsigned int dx = 0, dy = 0, dz = 0, dc = 0, pos = 0;
46435  CImg<T> res;
46436  switch (cimg::uncase(axis)) {
46437  case 'x' : { // Along the X-axis.
46438  cimglist_for(*this,l) {
46439  const CImg<T>& img = (*this)[l];
46440  if (img) {
46441  dx+=img._width;
46442  dy = cimg::max(dy,img._height);
46443  dz = cimg::max(dz,img._depth);
46444  dc = cimg::max(dc,img._spectrum);
46445  }
46446  }
46447  res.assign(dx,dy,dz,dc,0);
46448  if (res) cimglist_for(*this,l) {
46449  const CImg<T>& img = (*this)[l];
46450  if (img) res.draw_image(pos,
46451  (int)(align*(dy-img._height)),
46452  (int)(align*(dz-img._depth)),
46453  (int)(align*(dc-img._spectrum)),
46454  img);
46455  pos+=img._width;
46456  }
46457  } break;
46458  case 'y' : { // Along the Y-axis.
46459  cimglist_for(*this,l) {
46460  const CImg<T>& img = (*this)[l];
46461  if (img) {
46462  dx = cimg::max(dx,img._width);
46463  dy+=img._height;
46464  dz = cimg::max(dz,img._depth);
46465  dc = cimg::max(dc,img._spectrum);
46466  }
46467  }
46468  res.assign(dx,dy,dz,dc,0);
46469  if (res) cimglist_for(*this,l) {
46470  const CImg<T>& img = (*this)[l];
46471  if (img) res.draw_image((int)(align*(dx-img._width)),
46472  pos,
46473  (int)(align*(dz-img._depth)),
46474  (int)(align*(dc-img._spectrum)),
46475  img);
46476  pos+=img._height;
46477  }
46478  } break;
46479  case 'z' : { // Along the Z-axis.
46480  cimglist_for(*this,l) {
46481  const CImg<T>& img = (*this)[l];
46482  if (img) {
46483  dx = cimg::max(dx,img._width);
46484  dy = cimg::max(dy,img._height);
46485  dz+=img._depth;
46486  dc = cimg::max(dc,img._spectrum);
46487  }
46488  }
46489  res.assign(dx,dy,dz,dc,0);
46490  if (res) cimglist_for(*this,l) {
46491  const CImg<T>& img = (*this)[l];
46492  if (img) res.draw_image((int)(align*(dx-img._width)),
46493  (int)(align*(dy-img._height)),
46494  pos,
46495  (int)(align*(dc-img._spectrum)),
46496  img);
46497  pos+=img._depth;
46498  }
46499  } break;
46500  default : { // Along the C-axis.
46501  cimglist_for(*this,l) {
46502  const CImg<T>& img = (*this)[l];
46503  if (img) {
46504  dx = cimg::max(dx,img._width);
46505  dy = cimg::max(dy,img._height);
46506  dz = cimg::max(dz,img._depth);
46507  dc+=img._spectrum;
46508  }
46509  }
46510  res.assign(dx,dy,dz,dc,0);
46511  if (res) cimglist_for(*this,l) {
46512  const CImg<T>& img = (*this)[l];
46513  if (img) res.draw_image((int)(align*(dx-img._width)),
46514  (int)(align*(dy-img._height)),
46515  (int)(align*(dz-img._depth)),
46516  pos,
46517  img);
46518  pos+=img._spectrum;
46519  }
46520  }
46521  }
46522  return res;
46523  }
46524 
46526 
46530  CImgList<T>& split(const char axis, const int nb=0) {
46531  return get_split(axis,nb).move_to(*this);
46532  }
46533 
46535  CImgList<T> get_split(const char axis, const int nb=0) const {
46536  CImgList<T> res;
46537  cimglist_for(*this,l) _data[l].get_split(axis,nb).move_to(res,~0U);
46538  return res;
46539  }
46540 
46542 
46545  template<typename t>
46547  return insert(img);
46548  }
46549 
46551 
46554  template<typename t>
46556  return insert(img,0);
46557  }
46558 
46560 
46563  template<typename t>
46565  return insert(list);
46566  }
46567 
46569 
46572  template<typename t>
46574  return insert(list,0);
46575  }
46576 
46578 
46581  return remove(_width-1);
46582  }
46583 
46585 
46588  return remove(0);
46589  }
46590 
46592 
46595  CImgList<T>& erase(const iterator iter) {
46596  return remove(iter-_data);
46597  }
46598 
46600  //----------------------------------
46601  //
46603 
46604  //----------------------------------
46605 
46607 
46614  CImg<intT> get_select(CImgDisplay &disp, const bool feature_type=true,
46615  const char axis='x', const float align=0) const {
46616  return _get_select(disp,0,feature_type,axis,align,0,false,false,false);
46617  }
46618 
46620 
46627  CImg<intT> get_select(const char *const title, const bool feature_type=true,
46628  const char axis='x', const float align=0) const {
46629  CImgDisplay disp;
46630  return _get_select(disp,title,feature_type,axis,align,0,false,false,false);
46631  }
46632 
46633  CImg<intT> _get_select(CImgDisplay &disp, const char *const title, const bool feature_type,
46634  const char axis, const float align,
46635  const unsigned int orig, const bool resize_disp,
46636  const bool exit_on_rightbutton, const bool exit_on_wheel) const {
46637  if (is_empty())
46638  throw CImgInstanceException(_cimglist_instance
46639  "select(): Empty instance.",
46640  cimglist_instance);
46641 
46642  // Create image correspondence table and get list dimensions for visualization.
46643  CImgList<uintT> _indices;
46644  unsigned int max_width = 0, max_height = 0, sum_width = 0, sum_height = 0;
46645  cimglist_for(*this,l) {
46646  const CImg<T>& img = _data[l];
46647  const unsigned int
46648  w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
46649  h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
46650  if (w>max_width) max_width = w;
46651  if (h>max_height) max_height = h;
46652  sum_width+=w; sum_height+=h;
46653  if (axis=='x') CImg<uintT>(w,1,1,1,(unsigned int)l).move_to(_indices);
46654  else CImg<uintT>(h,1,1,1,(unsigned int)l).move_to(_indices);
46655  }
46656  const CImg<uintT> indices0 = _indices>'x';
46657 
46658  // Create display window.
46659  if (!disp) {
46660  if (axis=='x') disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:0,1);
46661  else disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:0,1);
46662  if (!title) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width);
46663  } else if (title) disp.set_title("%s",title);
46664  if (resize_disp) {
46665  if (axis=='x') disp.resize(cimg_fitscreen(sum_width,max_height,1),false);
46666  else disp.resize(cimg_fitscreen(max_width,sum_height,1),false);
46667  }
46668 
46669  const unsigned int old_normalization = disp.normalization();
46670  bool old_is_resized = disp.is_resized();
46671  disp._normalization = 0;
46672  disp.show().set_key(0);
46673  const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 };
46674 
46675  // Enter event loop.
46676  CImg<ucharT> visu0, visu;
46677  CImg<uintT> indices;
46678  CImg<intT> positions(_width,4,1,1,-1);
46679  int oindice0 = -1, oindice1 = -1, indice0 = -1, indice1 = -1;
46680  bool is_clicked = false, is_selected = false, text_down = false, update_display = true;
46681  unsigned int key = 0;
46682  while (!is_selected && !disp.is_closed() && !key) {
46683 
46684  // Create background image.
46685  if (!visu0) {
46686  visu0.assign(disp._width,disp._height,1,3,0); visu.assign();
46687  (indices0.get_resize(axis=='x'?visu0._width:visu0._height,1)).move_to(indices);
46688  unsigned int ind = 0;
46689  if (axis=='x') for (unsigned int x = 0; x<visu0._width; ) {
46690  const unsigned int x0 = x;
46691  ind = indices[x];
46692  while (x<indices._width && indices[x++]==ind) {}
46693  const CImg<T>
46694  onexone(1,1,1,1,0),
46695  &src = _data[ind]?_data[ind]:onexone;
46696  CImg<ucharT> res;
46697  src.__get_select(disp,old_normalization,(src._width-1)/2,(src._height-1)/2,(src._depth-1)/2).move_to(res);
46698  const unsigned int h = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,true);
46699  res.resize(x - x0,cimg::max(32U,h*disp._height/max_height),1,res._spectrum==1?3:-100);
46700  positions(ind,0) = positions(ind,2) = (int)x0;
46701  positions(ind,1) = positions(ind,3) = (int)(align*(visu0.height()-res.height()));
46702  positions(ind,2)+=res._width;
46703  positions(ind,3)+=res._height - 1;
46704  visu0.draw_image(positions(ind,0),positions(ind,1),res);
46705  } else for (unsigned int y = 0; y<visu0._height; ) {
46706  const unsigned int y0 = y;
46707  ind = indices[y];
46708  while (y<visu0._height && indices[++y]==ind) {}
46709  const CImg<T>
46710  &src = _data[ind],
46711  _img2d = src._depth>1?src.get_projections2d((src._width-1)/2,(src._height-1)/2,(src._depth-1)/2):
46712  CImg<T>(),
46713  &img2d = _img2d?_img2d:src;
46714  CImg<ucharT> res = old_normalization==1 ||
46715  (old_normalization==3 && cimg::type<T>::string()!=cimg::type<unsigned char>::string())?
46716  CImg<ucharT>(img2d.get_normalize(0,255)):
46717  CImg<ucharT>(img2d);
46718  if (res._spectrum>3) res.channels(0,2);
46719  const unsigned int w = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,false);
46720  res.resize(cimg::max(32U,w*disp._width/max_width),y - y0,1,res._spectrum==1?3:-100);
46721  positions(ind,0) = positions(ind,2) = (int)(align*(visu0.width()-res.width()));
46722  positions(ind,1) = positions(ind,3) = (int)y0;
46723  positions(ind,2)+=res._width - 1;
46724  positions(ind,3)+=res._height;
46725  visu0.draw_image(positions(ind,0),positions(ind,1),res);
46726  }
46727  if (axis=='x') --positions(ind,2); else --positions(ind,3);
46728  update_display = true;
46729  }
46730 
46731  if (!visu || oindice0!=indice0 || oindice1!=indice1) {
46732  if (indice0>=0 && indice1>=0) {
46733  visu.assign(visu0,false);
46734  const int indm = cimg::min(indice0,indice1), indM = cimg::max(indice0,indice1);
46735  for (int ind = indm; ind<=indM; ++ind) if (positions(ind,0)>=0) {
46736  visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),
46737  background_color,0.2f);
46738  if ((axis=='x' && positions(ind,2) - positions(ind,0)>=8) ||
46739  (axis!='x' && positions(ind,3) - positions(ind,1)>=8))
46740  visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),
46741  foreground_color,0.9f,0xAAAAAAAA);
46742  }
46743  const int yt = (int)text_down?visu.height()-13:0;
46744  if (is_clicked) visu.draw_text(0,yt," Images #%u - #%u, Size = %u",
46745  foreground_color,background_color,0.7f,13,
46746  orig + indm,orig + indM,indM - indm + 1);
46747  else visu.draw_text(0,yt," Image #%u (%u,%u,%u,%u)",foreground_color,background_color,0.7f,13,
46748  orig + indice0,
46749  _data[orig+indice0]._width,
46750  _data[orig+indice0]._height,
46751  _data[orig+indice0]._depth,
46752  _data[orig+indice0]._spectrum);
46753  update_display = true;
46754  } else visu.assign();
46755  }
46756  if (!visu) { visu.assign(visu0,true); update_display = true; }
46757  if (update_display) { visu.display(disp); update_display = false; }
46758  disp.wait();
46759 
46760  // Manage user events.
46761  const int xm = disp.mouse_x(), ym = disp.mouse_y();
46762  int indice = -1;
46763 
46764  if (xm>=0) {
46765  indice = (int)indices(axis=='x'?xm:ym);
46766  if (disp.button()&1) {
46767  if (!is_clicked) { is_clicked = true; oindice0 = indice0; indice0 = indice; }
46768  oindice1 = indice1; indice1 = indice;
46769  if (!feature_type) is_selected = true;
46770  } else {
46771  if (!is_clicked) { oindice0 = oindice1 = indice0; indice0 = indice1 = indice; }
46772  else is_selected = true;
46773  }
46774  } else {
46775  if (is_clicked) {
46776  if (!(disp.button()&1)) { is_clicked = is_selected = false; indice0 = indice1 = -1; }
46777  else indice1 = -1;
46778  } else indice0 = indice1 = -1;
46779  }
46780 
46781  if (disp.button()&4) { is_clicked = is_selected = false; indice0 = indice1 = -1; }
46782  if (disp.button()&2 && exit_on_rightbutton) { is_selected = true; indice1 = indice0 = -1; }
46783  if (disp.wheel() && exit_on_wheel) is_selected = true;
46784 
46785  switch (key = disp.key()) {
46786 #if cimg_OS!=2
46787  case cimg::keyCTRLRIGHT :
46788 #endif
46789  case 0 : case cimg::keyCTRLLEFT : key = 0; break;
46790  case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
46791  disp.set_fullscreen(false).
46792  resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
46793  CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
46794  _is_resized = true;
46795  disp.set_key(key,false); key = 0; visu0.assign();
46796  } break;
46797  case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
46798  disp.set_fullscreen(false).
46799  resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
46800  disp.set_key(key,false); key = 0; visu0.assign();
46801  } break;
46802  case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
46803  disp.set_fullscreen(false).
46804  resize(cimg_fitscreen(axis=='x'?sum_width:max_width,axis=='x'?max_height:sum_height,1),false).
46805  _is_resized = true;
46806  disp.set_key(key,false); key = 0; visu0.assign();
46807  } break;
46808  case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
46809  disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
46810  disp.set_key(key,false); key = 0; visu0.assign();
46811  } break;
46812  case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
46813  static unsigned int snap_number = 0;
46814  char filename[32] = { 0 };
46815  std::FILE *file;
46816  do {
46817  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++);
46818  if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
46819  } while (file);
46820  if (visu0) {
46821  (+visu0).draw_text(0,0," Saving snapshot... ",
46822  foreground_color,background_color,0.7f,13).display(disp);
46823  visu0.save(filename);
46824  (+visu0).draw_text(0,0," Snapshot '%s' saved. ",
46825  foreground_color,background_color,0.7f,13,filename).display(disp);
46826  }
46827  disp.set_key(key,false).wait(); key = 0;
46828  } break;
46829  case cimg::keyO :
46830  if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
46831  static unsigned int snap_number = 0;
46832  char filename[32] = { 0 };
46833  std::FILE *file;
46834  do {
46835 #ifdef cimg_use_zlib
46836  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++);
46837 #else
46838  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++);
46839 #endif
46840  if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
46841  } while (file);
46842  (+visu0).draw_text(0,0," Saving instance... ",
46843  foreground_color,background_color,0.7f,13).display(disp);
46844  save(filename);
46845  (+visu0).draw_text(0,0," Instance '%s' saved. ",
46846  foreground_color,background_color,0.7f,13,filename).display(disp);
46847  disp.set_key(key,false).wait(); key = 0;
46848  } break;
46849  }
46850  if (disp.is_resized()) { disp.resize(false); visu0.assign(); }
46851  if (ym>=0 && ym<13) { if (!text_down) { visu.assign(); text_down = true; }}
46852  else if (ym>=visu.height()-13) { if(text_down) { visu.assign(); text_down = false; }}
46853  }
46854  CImg<intT> res(1,2,1,1,-1);
46855  if (is_selected) {
46856  if (feature_type) res.fill(cimg::min(indice0,indice1),cimg::max(indice0,indice1));
46857  else res.fill(indice0);
46858  }
46859  if (!(disp.button()&2)) disp.set_button();
46860  disp._normalization = old_normalization;
46861  disp._is_resized = old_is_resized;
46862  disp.set_key(key);
46863  return res;
46864  }
46865 
46867 
46870  CImgList<T>& load(const char *const filename) {
46871  if (!filename)
46872  throw CImgArgumentException(_cimglist_instance
46873  "load(): Specified filename is (null).",
46874  cimglist_instance);
46875 
46876  if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) {
46877  char filename_local[1024] = { 0 };
46878  load(cimg::load_network_external(filename,filename_local));
46879  std::remove(filename_local);
46880  return *this;
46881  }
46882 
46883  const char *const ext = cimg::split_filename(filename);
46884  const unsigned int omode = cimg::exception_mode();
46885  cimg::exception_mode() = 0;
46886  try {
46887 #ifdef cimglist_load_plugin
46888  cimglist_load_plugin(filename);
46889 #endif
46890 #ifdef cimglist_load_plugin1
46891  cimglist_load_plugin1(filename);
46892 #endif
46893 #ifdef cimglist_load_plugin2
46894  cimglist_load_plugin2(filename);
46895 #endif
46896 #ifdef cimglist_load_plugin3
46897  cimglist_load_plugin3(filename);
46898 #endif
46899 #ifdef cimglist_load_plugin4
46900  cimglist_load_plugin4(filename);
46901 #endif
46902 #ifdef cimglist_load_plugin5
46903  cimglist_load_plugin5(filename);
46904 #endif
46905 #ifdef cimglist_load_plugin6
46906  cimglist_load_plugin6(filename);
46907 #endif
46908 #ifdef cimglist_load_plugin7
46909  cimglist_load_plugin7(filename);
46910 #endif
46911 #ifdef cimglist_load_plugin8
46912  cimglist_load_plugin8(filename);
46913 #endif
46914  if (!cimg::strcasecmp(ext,"tif") ||
46915  !cimg::strcasecmp(ext,"tiff")) load_tiff(filename);
46916  else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename);
46917  else if (!cimg::strcasecmp(ext,"cimg") ||
46918  !cimg::strcasecmp(ext,"cimgz") ||
46919  !*ext) load_cimg(filename);
46920  else if (!cimg::strcasecmp(ext,"rec") ||
46921  !cimg::strcasecmp(ext,"par")) load_parrec(filename);
46922  else if (!cimg::strcasecmp(ext,"avi") ||
46923  !cimg::strcasecmp(ext,"mov") ||
46924  !cimg::strcasecmp(ext,"asf") ||
46925  !cimg::strcasecmp(ext,"divx") ||
46926  !cimg::strcasecmp(ext,"flv") ||
46927  !cimg::strcasecmp(ext,"mpg") ||
46928  !cimg::strcasecmp(ext,"m1v") ||
46929  !cimg::strcasecmp(ext,"m2v") ||
46930  !cimg::strcasecmp(ext,"m4v") ||
46931  !cimg::strcasecmp(ext,"mjp") ||
46932  !cimg::strcasecmp(ext,"mp4") ||
46933  !cimg::strcasecmp(ext,"mkv") ||
46934  !cimg::strcasecmp(ext,"mpe") ||
46935  !cimg::strcasecmp(ext,"movie") ||
46936  !cimg::strcasecmp(ext,"ogm") ||
46937  !cimg::strcasecmp(ext,"ogg") ||
46938  !cimg::strcasecmp(ext,"ogv") ||
46939  !cimg::strcasecmp(ext,"qt") ||
46940  !cimg::strcasecmp(ext,"rm") ||
46941  !cimg::strcasecmp(ext,"vob") ||
46942  !cimg::strcasecmp(ext,"wmv") ||
46943  !cimg::strcasecmp(ext,"xvid") ||
46944  !cimg::strcasecmp(ext,"mpeg")) load_ffmpeg(filename);
46945  else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename);
46946  else throw CImgIOException("CImgList<%s>::load()",
46947  pixel_type());
46948  } catch (CImgIOException&) {
46949  try {
46950  cimg::fclose(cimg::fopen(filename,"rb"));
46951  } catch (CImgIOException&) {
46952  cimg::exception_mode() = omode;
46953  throw CImgIOException(_cimglist_instance
46954  "load(): Failed to open file '%s'.",
46955  cimglist_instance,
46956  filename);
46957  }
46958  assign(1);
46959  try {
46960  _data->load(filename);
46961  } catch (CImgIOException&) {
46962  cimg::exception_mode() = omode;
46963  throw CImgIOException(_cimglist_instance
46964  "load(): Failed to recognize format of file '%s'.",
46965  cimglist_instance,
46966  filename);
46967  }
46968  }
46969  cimg::exception_mode() = omode;
46970  return *this;
46971  }
46972 
46974  static CImgList<T> get_load(const char *const filename) {
46975  return CImgList<T>().load(filename);
46976  }
46977 
46979 
46982  CImgList<T>& load_cimg(const char *const filename) {
46983  return _load_cimg(0,filename);
46984  }
46985 
46987  static CImgList<T> get_load_cimg(const char *const filename) {
46988  return CImgList<T>().load_cimg(filename);
46989  }
46990 
46992 
46995  CImgList<T>& load_cimg(std::FILE *const file) {
46996  return _load_cimg(file,0);
46997  }
46998 
47000  static CImgList<T> get_load_cimg(std::FILE *const file) {
47001  return CImgList<T>().load_cimg(file);
47002  }
47003 
47004  CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename) {
47005 #ifdef cimg_use_zlib
47006 #define _cimgz_load_cimg_case(Tss) { \
47007  Bytef *const cbuf = new Bytef[csiz]; \
47008  cimg::fread(cbuf,csiz,nfile); \
47009  raw.assign(W,H,D,C); \
47010  unsigned long destlen = (unsigned long)raw.size()*sizeof(Tss); \
47011  uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \
47012  delete[] cbuf; \
47013  const Tss *ptrs = raw._data; \
47014  for (unsigned long off = raw.size(); off; --off) *(ptrd++) = (T)*(ptrs++); \
47015 }
47016 #else
47017 #define _cimgz_load_cimg_case(Tss) \
47018  throw CImgIOException(_cimglist_instance \
47019  "load_cimg(): Unable to load compressed data from file '%s' unless zlib is enabled.", \
47020  cimglist_instance, \
47021  filename?filename:"(FILE*)");
47022 #endif
47023 
47024 #define _cimg_load_cimg_case(Ts,Tss) \
47025  if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
47026  for (unsigned int l = 0; l<N; ++l) { \
47027  j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0) tmp[j++] = (char)i; tmp[j] = 0; \
47028  W = H = D = C = 0; csiz = 0; \
47029  if ((err = std::sscanf(tmp,"%u %u %u %u #%u",&W,&H,&D,&C,&csiz))<4) \
47030  throw CImgIOException(_cimglist_instance \
47031  "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'", \
47032  cimglist_instance, \
47033  W,H,D,C,l,filename?filename:("(FILE*)")); \
47034  if (W*H*D*C>0) { \
47035  CImg<Tss> raw; \
47036  CImg<T> &img = _data[l]; \
47037  img.assign(W,H,D,C); \
47038  T *ptrd = img._data; \
47039  if (err==5) _cimgz_load_cimg_case(Tss) \
47040  else for (long to_read = (long)img.size(); to_read>0; ) { \
47041  raw.assign(cimg::min(to_read,cimg_iobuffer)); \
47042  cimg::fread(raw._data,raw._width,nfile); \
47043  if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); \
47044  to_read-=raw._width; \
47045  const Tss *ptrs = raw._data; \
47046  for (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
47047  } \
47048  } \
47049  } \
47050  loaded = true; \
47051  }
47052 
47053  if (!filename && !file)
47054  throw CImgArgumentException(_cimglist_instance
47055  "load_cimg(): Specified filename is (null).",
47056  cimglist_instance);
47057 
47058  const int cimg_iobuffer = 12*1024*1024;
47059  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
47060  bool loaded = false, endian = cimg::endianness();
47061  char tmp[256] = { 0 }, str_pixeltype[256] = { 0 }, str_endian[256] = { 0 };
47062  unsigned int j, err, N = 0, W, H, D, C, csiz;
47063  int i;
47064  do {
47065  j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
47066  } while (*tmp=='#' && i!=EOF);
47067  err = std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype,str_endian);
47068  if (err<2) {
47069  if (!file) cimg::fclose(nfile);
47070  throw CImgIOException(_cimglist_instance
47071  "load_cimg(): CImg header not found in file '%s'.",
47072  cimglist_instance,
47073  filename?filename:"(FILE*)");
47074  }
47075  if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
47076  else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
47077  assign(N);
47078  _cimg_load_cimg_case("bool",bool);
47079  _cimg_load_cimg_case("unsigned_char",unsigned char);
47080  _cimg_load_cimg_case("uchar",unsigned char);
47081  _cimg_load_cimg_case("char",char);
47082  _cimg_load_cimg_case("unsigned_short",unsigned short);
47083  _cimg_load_cimg_case("ushort",unsigned short);
47084  _cimg_load_cimg_case("short",short);
47085  _cimg_load_cimg_case("unsigned_int",unsigned int);
47086  _cimg_load_cimg_case("uint",unsigned int);
47087  _cimg_load_cimg_case("int",int);
47088  _cimg_load_cimg_case("unsigned_long",unsigned long);
47089  _cimg_load_cimg_case("ulong",unsigned long);
47090  _cimg_load_cimg_case("long",long);
47091  _cimg_load_cimg_case("float",float);
47092  _cimg_load_cimg_case("double",double);
47093  if (!loaded) {
47094  if (!file) cimg::fclose(nfile);
47095  throw CImgIOException(_cimglist_instance
47096  "load_cimg(): Unsupported pixel type '%s' for file '%s'.",
47097  cimglist_instance,
47098  str_pixeltype,filename?filename:"(FILE*)");
47099  }
47100  if (!file) cimg::fclose(nfile);
47101  return *this;
47102  }
47103 
47105 
47118  CImgList<T>& load_cimg(const char *const filename,
47119  const unsigned int n0, const unsigned int n1,
47120  const unsigned int x0, const unsigned int y0,
47121  const unsigned int z0, const unsigned int c0,
47122  const unsigned int x1, const unsigned int y1,
47123  const unsigned int z1, const unsigned int c1) {
47124  return _load_cimg(0,filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
47125  }
47126 
47128  static CImgList<T> get_load_cimg(const char *const filename,
47129  const unsigned int n0, const unsigned int n1,
47130  const unsigned int x0, const unsigned int y0,
47131  const unsigned int z0, const unsigned int c0,
47132  const unsigned int x1, const unsigned int y1,
47133  const unsigned int z1, const unsigned int c1) {
47134  return CImgList<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
47135  }
47136 
47138  CImgList<T>& load_cimg(std::FILE *const file,
47139  const unsigned int n0, const unsigned int n1,
47140  const unsigned int x0, const unsigned int y0,
47141  const unsigned int z0, const unsigned int c0,
47142  const unsigned int x1, const unsigned int y1,
47143  const unsigned int z1, const unsigned int c1) {
47144  return _load_cimg(file,0,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
47145  }
47146 
47148  static CImgList<T> get_load_cimg(std::FILE *const file,
47149  const unsigned int n0, const unsigned int n1,
47150  const unsigned int x0, const unsigned int y0,
47151  const unsigned int z0, const unsigned int c0,
47152  const unsigned int x1, const unsigned int y1,
47153  const unsigned int z1, const unsigned int c1) {
47154  return CImgList<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
47155  }
47156 
47157  CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename,
47158  const unsigned int n0, const unsigned int n1,
47159  const unsigned int x0, const unsigned int y0,
47160  const unsigned int z0, const unsigned int c0,
47161  const unsigned int x1, const unsigned int y1,
47162  const unsigned int z1, const unsigned int c1) {
47163 #define _cimg_load_cimg_case2(Ts,Tss) \
47164  if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
47165  for (unsigned int l = 0; l<=nn1; ++l) { \
47166  j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0) tmp[j++] = (char)i; tmp[j] = 0; \
47167  W = H = D = C = 0; \
47168  if (std::sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
47169  throw CImgIOException(_cimglist_instance \
47170  "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'", \
47171  cimglist_instance, \
47172  W,H,D,C,l,filename?filename:"(FILE*)"); \
47173  if (W*H*D*C>0) { \
47174  if (l<nn0 || nx0>=W || ny0>=H || nz0>=D || nc0>=C) std::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
47175  else { \
47176  const unsigned int \
47177  _nx1 = nx1==~0U?W-1:nx1, \
47178  _ny1 = ny1==~0U?H-1:ny1, \
47179  _nz1 = nz1==~0U?D-1:nz1, \
47180  _nc1 = nc1==~0U?C-1:nc1; \
47181  if (_nx1>=W || _ny1>=H || _nz1>=D || _nc1>=C) \
47182  throw CImgArgumentException(_cimglist_instance \
47183  "load_cimg(): Invalid specified coordinates " \
47184  "[%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) " \
47185  "because image [%u] in file '%s' has size (%u,%u,%u,%u).", \
47186  cimglist_instance, \
47187  n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,l,filename?filename:"(FILE*)",W,H,D,C); \
47188  CImg<Tss> raw(1 + _nx1 - nx0); \
47189  CImg<T> &img = _data[l - nn0]; \
47190  img.assign(1 + _nx1 - nx0,1 + _ny1 - ny0,1 + _nz1 - nz0,1 + _nc1 - nc0); \
47191  T *ptrd = img._data; \
47192  const unsigned int skipvb = nc0*W*H*D*sizeof(Tss); \
47193  if (skipvb) std::fseek(nfile,skipvb,SEEK_CUR); \
47194  for (unsigned int c = 1 + _nc1 - nc0; c; --c) { \
47195  const unsigned int skipzb = nz0*W*H*sizeof(Tss); \
47196  if (skipzb) std::fseek(nfile,skipzb,SEEK_CUR); \
47197  for (unsigned int z = 1 + _nz1 - nz0; z; --z) { \
47198  const unsigned int skipyb = ny0*W*sizeof(Tss); \
47199  if (skipyb) std::fseek(nfile,skipyb,SEEK_CUR); \
47200  for (unsigned int y = 1 + _ny1 - ny0; y; --y) { \
47201  const unsigned int skipxb = nx0*sizeof(Tss); \
47202  if (skipxb) std::fseek(nfile,skipxb,SEEK_CUR); \
47203  cimg::fread(raw._data,raw._width,nfile); \
47204  if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); \
47205  const Tss *ptrs = raw._data; \
47206  for (unsigned int off = raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
47207  const unsigned int skipxe = (W-1-_nx1)*sizeof(Tss); \
47208  if (skipxe) std::fseek(nfile,skipxe,SEEK_CUR); \
47209  } \
47210  const unsigned int skipye = (H-1-_ny1)*W*sizeof(Tss); \
47211  if (skipye) std::fseek(nfile,skipye,SEEK_CUR); \
47212  } \
47213  const unsigned int skipze = (D-1-_nz1)*W*H*sizeof(Tss); \
47214  if (skipze) std::fseek(nfile,skipze,SEEK_CUR); \
47215  } \
47216  const unsigned int skipve = (C-1-_nc1)*W*H*D*sizeof(Tss); \
47217  if (skipve) std::fseek(nfile,skipve,SEEK_CUR); \
47218  } \
47219  } \
47220  } \
47221  loaded = true; \
47222  }
47223 
47224  if (!filename && !file)
47225  throw CImgArgumentException(_cimglist_instance
47226  "load_cimg(): Specified filename is (null).",
47227  cimglist_instance);
47228  unsigned int
47229  nn0 = cimg::min(n0,n1), nn1 = cimg::max(n0,n1),
47230  nx0 = cimg::min(x0,x1), nx1 = cimg::max(x0,x1),
47231  ny0 = cimg::min(y0,y1), ny1 = cimg::max(y0,y1),
47232  nz0 = cimg::min(z0,z1), nz1 = cimg::max(z0,z1),
47233  nc0 = cimg::min(c0,c1), nc1 = cimg::max(c0,c1);
47234 
47235  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
47236  bool loaded = false, endian = cimg::endianness();
47237  char tmp[256] = { 0 }, str_pixeltype[256] = { 0 }, str_endian[256] = { 0 };
47238  unsigned int j, err, N, W, H, D, C;
47239  int i;
47240  j = 0; while((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
47241  err = std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype,str_endian);
47242  if (err<2) {
47243  if (!file) cimg::fclose(nfile);
47244  throw CImgIOException(_cimglist_instance
47245  "load_cimg(): CImg header not found in file '%s'.",
47246  cimglist_instance,
47247  filename?filename:"(FILE*)");
47248  }
47249  if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
47250  else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
47251  nn1 = n1==~0U?N-1:n1;
47252  if (nn1>=N)
47253  throw CImgArgumentException(_cimglist_instance
47254  "load_cimg(): Invalid specified coordinates [%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) "
47255  "because file '%s' contains only %u images.",
47256  cimglist_instance,
47257  n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,filename?filename:"(FILE*)",N);
47258  assign(1+nn1-n0);
47259  _cimg_load_cimg_case2("bool",bool);
47260  _cimg_load_cimg_case2("unsigned_char",unsigned char);
47261  _cimg_load_cimg_case2("uchar",unsigned char);
47262  _cimg_load_cimg_case2("char",char);
47263  _cimg_load_cimg_case2("unsigned_short",unsigned short);
47264  _cimg_load_cimg_case2("ushort",unsigned short);
47265  _cimg_load_cimg_case2("short",short);
47266  _cimg_load_cimg_case2("unsigned_int",unsigned int);
47267  _cimg_load_cimg_case2("uint",unsigned int);
47268  _cimg_load_cimg_case2("int",int);
47269  _cimg_load_cimg_case2("unsigned_long",unsigned long);
47270  _cimg_load_cimg_case2("ulong",unsigned long);
47271  _cimg_load_cimg_case2("long",long);
47272  _cimg_load_cimg_case2("float",float);
47273  _cimg_load_cimg_case2("double",double);
47274  if (!loaded) {
47275  if (!file) cimg::fclose(nfile);
47276  throw CImgIOException(_cimglist_instance
47277  "load_cimg(): Unsupported pixel type '%s' for file '%s'.",
47278  cimglist_instance,
47279  str_pixeltype,filename?filename:"(FILE*)");
47280  }
47281  if (!file) cimg::fclose(nfile);
47282  return *this;
47283  }
47284 
47286 
47289  CImgList<T>& load_parrec(const char *const filename) {
47290  if (!filename)
47291  throw CImgArgumentException(_cimglist_instance
47292  "load_parrec(): Specified filename is (null).",
47293  cimglist_instance);
47294 
47295  char body[1024] = { 0 }, filenamepar[1024] = { 0 }, filenamerec[1024] = { 0 };
47296  const char *const ext = cimg::split_filename(filename,body);
47297  if (!std::strcmp(ext,"par")) {
47298  std::strncpy(filenamepar,filename,sizeof(filenamepar)-1);
47299  cimg_snprintf(filenamerec,sizeof(filenamerec),"%s.rec",body);
47300  }
47301  if (!std::strcmp(ext,"PAR")) {
47302  std::strncpy(filenamepar,filename,sizeof(filenamepar)-1);
47303  cimg_snprintf(filenamerec,sizeof(filenamerec),"%s.REC",body);
47304  }
47305  if (!std::strcmp(ext,"rec")) {
47306  std::strncpy(filenamerec,filename,sizeof(filenamerec)-1);
47307  cimg_snprintf(filenamepar,sizeof(filenamepar),"%s.par",body);
47308  }
47309  if (!std::strcmp(ext,"REC")) {
47310  std::strncpy(filenamerec,filename,sizeof(filenamerec)-1);
47311  cimg_snprintf(filenamepar,sizeof(filenamepar),"%s.PAR",body);
47312  }
47313  std::FILE *file = cimg::fopen(filenamepar,"r");
47314 
47315  // Parse header file
47316  CImgList<floatT> st_slices;
47317  CImgList<uintT> st_global;
47318  int err;
47319  char line[256] = { 0 };
47320  do { err=std::fscanf(file,"%255[^\n]%*c",line); } while (err!=EOF && (*line=='#' || *line=='.'));
47321  do {
47322  unsigned int sn,size_x,size_y,pixsize;
47323  float rs,ri,ss;
47324  err = std::fscanf(file,"%u%*u%*u%*u%*u%*u%*u%u%*u%u%u%g%g%g%*[^\n]",&sn,&pixsize,&size_x,&size_y,&ri,&rs,&ss);
47325  if (err==7) {
47326  CImg<floatT>::vector((float)sn,(float)pixsize,(float)size_x,(float)size_y,ri,rs,ss,0).move_to(st_slices);
47327  unsigned int i; for (i = 0; i<st_global._width && sn<=st_global[i][2]; ++i) {}
47328  if (i==st_global._width) CImg<uintT>::vector(size_x,size_y,sn).move_to(st_global);
47329  else {
47330  CImg<uintT> &vec = st_global[i];
47331  if (size_x>vec[0]) vec[0] = size_x;
47332  if (size_y>vec[1]) vec[1] = size_y;
47333  vec[2] = sn;
47334  }
47335  st_slices[st_slices._width-1][7] = (float)i;
47336  }
47337  } while (err==7);
47338 
47339  // Read data
47340  std::FILE *file2 = cimg::fopen(filenamerec,"rb");
47341  cimglist_for(st_global,l) {
47342  const CImg<uintT>& vec = st_global[l];
47343  CImg<T>(vec[0],vec[1],vec[2]).move_to(*this);
47344  }
47345 
47346  cimglist_for(st_slices,l) {
47347  const CImg<floatT>& vec = st_slices[l];
47348  const unsigned int
47349  sn = (unsigned int)vec[0] - 1,
47350  pixsize = (unsigned int)vec[1],
47351  size_x = (unsigned int)vec[2],
47352  size_y = (unsigned int)vec[3],
47353  imn = (unsigned int)vec[7];
47354  const float ri = vec[4], rs = vec[5], ss = vec[6];
47355  switch (pixsize) {
47356  case 8 : {
47357  CImg<ucharT> buf(size_x,size_y);
47358  cimg::fread(buf._data,size_x*size_y,file2);
47359  if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
47360  CImg<T>& img = (*this)[imn];
47361  cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
47362  } break;
47363  case 16 : {
47364  CImg<ushortT> buf(size_x,size_y);
47365  cimg::fread(buf._data,size_x*size_y,file2);
47366  if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
47367  CImg<T>& img = (*this)[imn];
47368  cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
47369  } break;
47370  case 32 : {
47371  CImg<uintT> buf(size_x,size_y);
47372  cimg::fread(buf._data,size_x*size_y,file2);
47373  if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
47374  CImg<T>& img = (*this)[imn];
47375  cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
47376  } break;
47377  default :
47378  cimg::fclose(file);
47379  cimg::fclose(file2);
47380  throw CImgIOException(_cimglist_instance
47381  "load_parrec(): Unsupported %d-bits pixel type for file '%s'.",
47382  cimglist_instance,
47383  pixsize,filename);
47384  }
47385  }
47386  cimg::fclose(file);
47387  cimg::fclose(file2);
47388  if (!_width)
47389  throw CImgIOException(_cimglist_instance
47390  "load_parrec(): Failed to recognize valid PAR-REC data in file '%s'.",
47391  cimglist_instance,
47392  filename);
47393  return *this;
47394  }
47395 
47397  static CImgList<T> get_load_parrec(const char *const filename) {
47398  return CImgList<T>().load_parrec(filename);
47399  }
47400 
47402 
47411  CImgList<T>& load_yuv(const char *const filename,
47412  const unsigned int size_x, const unsigned int size_y,
47413  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
47414  const unsigned int step_frame=1, const bool yuv2rgb=true) {
47415  return _load_yuv(0,filename,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb);
47416  }
47417 
47419  static CImgList<T> get_load_yuv(const char *const filename,
47420  const unsigned int size_x, const unsigned int size_y=1,
47421  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
47422  const unsigned int step_frame=1, const bool yuv2rgb=true) {
47423  return CImgList<T>().load_yuv(filename,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb);
47424  }
47425 
47427  CImgList<T>& load_yuv(std::FILE *const file,
47428  const unsigned int size_x, const unsigned int size_y,
47429  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
47430  const unsigned int step_frame=1, const bool yuv2rgb=true) {
47431  return _load_yuv(file,0,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb);
47432  }
47433 
47435  static CImgList<T> get_load_yuv(std::FILE *const file,
47436  const unsigned int size_x, const unsigned int size_y=1,
47437  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
47438  const unsigned int step_frame=1, const bool yuv2rgb=true) {
47439  return CImgList<T>().load_yuv(file,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb);
47440  }
47441 
47442  CImgList<T>& _load_yuv(std::FILE *const file, const char *const filename,
47443  const unsigned int size_x, const unsigned int size_y,
47444  const unsigned int first_frame, const unsigned int last_frame,
47445  const unsigned int step_frame, const bool yuv2rgb) {
47446  if (!filename && !file)
47447  throw CImgArgumentException(_cimglist_instance
47448  "load_yuv(): Specified filename is (null).",
47449  cimglist_instance);
47450  if (size_x%2 || size_y%2)
47451  throw CImgArgumentException(_cimglist_instance
47452  "load_yuv(): Invalid odd XY dimensions %ux%u in file '%s'.",
47453  cimglist_instance,
47454  size_x,size_y,filename?filename:"(FILE*)");
47455  if (!size_x || !size_y)
47456  throw CImgArgumentException(_cimglist_instance
47457  "load_yuv(): Invalid sequence size (%u,%u) in file '%s'.",
47458  cimglist_instance,
47459  size_x,size_y,filename?filename:"(FILE*)");
47460 
47461  const unsigned int
47462  nfirst_frame = first_frame<last_frame?first_frame:last_frame,
47463  nlast_frame = first_frame<last_frame?last_frame:first_frame,
47464  nstep_frame = step_frame?step_frame:1;
47465 
47466  CImg<ucharT> tmp(size_x,size_y,1,3), UV(size_x/2,size_y/2,1,2);
47467  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
47468  bool stop_flag = false;
47469  int err;
47470  if (nfirst_frame) {
47471  err = std::fseek(nfile,nfirst_frame*(size_x*size_y + size_x*size_y/2),SEEK_CUR);
47472  if (err) {
47473  if (!file) cimg::fclose(nfile);
47474  throw CImgIOException(_cimglist_instance
47475  "load_yuv(): File '%s' doesn't contain frame number %u.",
47476  cimglist_instance,
47477  filename?filename:"(FILE*)",nfirst_frame);
47478  }
47479  }
47480  unsigned int frame;
47481  for (frame = nfirst_frame; !stop_flag && frame<=nlast_frame; frame+=nstep_frame) {
47482  tmp.fill(0);
47483  // *TRY* to read the luminance part, do not replace by cimg::fread!
47484  err = (int)std::fread((void*)(tmp._data),1,(unsigned long)tmp._width*tmp._height,nfile);
47485  if (err!=(int)(tmp._width*tmp._height)) {
47486  stop_flag = true;
47487  if (err>0)
47488  cimg::warn(_cimglist_instance
47489  "load_yuv(): File '%s' contains incomplete data or given image dimensions "
47490  "(%u,%u) are incorrect.",
47491  cimglist_instance,
47492  filename?filename:"(FILE*)",size_x,size_y);
47493  } else {
47494  UV.fill(0);
47495  // *TRY* to read the luminance part, do not replace by cimg::fread!
47496  err = (int)std::fread((void*)(UV._data),1,(size_t)(UV.size()),nfile);
47497  if (err!=(int)(UV.size())) {
47498  stop_flag = true;
47499  if (err>0)
47500  cimg::warn(_cimglist_instance
47501  "load_yuv(): File '%s' contains incomplete data or given image dimensions (%u,%u) "
47502  "are incorrect.",
47503  cimglist_instance,
47504  filename?filename:"(FILE*)",size_x,size_y);
47505  } else {
47506  cimg_forXY(UV,x,y) {
47507  const int x2 = x*2, y2 = y*2;
47508  tmp(x2,y2,1) = tmp(x2+1,y2,1) = tmp(x2,y2+1,1) = tmp(x2+1,y2+1,1) = UV(x,y,0);
47509  tmp(x2,y2,2) = tmp(x2+1,y2,2) = tmp(x2,y2+1,2) = tmp(x2+1,y2+1,2) = UV(x,y,1);
47510  }
47511  if (yuv2rgb) tmp.YCbCrtoRGB();
47512  insert(tmp);
47513  if (nstep_frame>1) std::fseek(nfile,(nstep_frame-1)*(size_x*size_y + size_x*size_y/2),SEEK_CUR);
47514  }
47515  }
47516  }
47517  if (stop_flag && nlast_frame!=~0U && frame!=nlast_frame)
47518  cimg::warn(_cimglist_instance
47519  "load_yuv(): Frame %d not reached since only %u frames were found in file '%s'.",
47520  cimglist_instance,
47521  nlast_frame,frame-1,filename?filename:"(FILE*)");
47522 
47523  if (!file) cimg::fclose(nfile);
47524  return *this;
47525  }
47526 
47528 
47536  // This piece of code has been firstly created by David Starweather (starkdg(at)users(dot)sourceforge(dot)net)
47537  // I modified it afterwards for direct inclusion in the library core.
47538  CImgList<T>& load_ffmpeg(const char *const filename,
47539  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
47540  const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false) {
47541  if (!filename)
47542  throw CImgArgumentException(_cimglist_instance
47543  "load_ffmpeg(): Specified filename is (null).",
47544  cimglist_instance);
47545 
47546  const unsigned int
47547  nfirst_frame = first_frame<last_frame?first_frame:last_frame,
47548  nlast_frame = first_frame<last_frame?last_frame:first_frame,
47549  nstep_frame = step_frame?step_frame:1;
47550  assign();
47551 
47552 #ifndef cimg_use_ffmpeg
47553  if ((nfirst_frame || nlast_frame!=~0U || nstep_frame>1) || (resume && (pixel_format || !pixel_format)))
47554  throw CImgArgumentException(_cimglist_instance
47555  "load_ffmpeg(): Unable to load sub-frames from file '%s' unless libffmpeg "
47556  "is enabled.",
47557  cimglist_instance,
47558  filename);
47559 
47560  return load_ffmpeg_external(filename);
47561 #else
47562  const PixelFormat ffmpeg_pixfmt = pixel_format?PIX_FMT_RGB24:PIX_FMT_GRAY8;
47563  avcodec_register_all();
47564  av_register_all();
47565  static AVFormatContext *format_ctx = 0;
47566  static AVCodecContext *codec_ctx = 0;
47567  static AVCodec *codec = 0;
47568  static AVFrame *avframe = avcodec_alloc_frame(), *converted_frame = avcodec_alloc_frame();
47569  static int vstream = 0;
47570 
47571  if (resume) {
47572  if (!format_ctx || !codec_ctx || !codec || !avframe || !converted_frame)
47573  throw CImgArgumentException(_cimglist_instance
47574  "load_ffmpeg(): Failed to resume loading of file '%s', "
47575  "due to unallocated FFMPEG structures.",
47576  cimglist_instance,
47577  filename);
47578  } else {
47579  // Open video file, find main video stream and codec.
47580  if (format_ctx) avformat_close_input(&format_ctx);
47581  if (avformat_open_input(&format_ctx,filename,0,0)!=0)
47582  throw CImgIOException(_cimglist_instance
47583  "load_ffmpeg(): Failed to open file '%s'.",
47584  cimglist_instance,
47585  filename);
47586 
47587  if (!avframe || !converted_frame || avformat_find_stream_info(format_ctx,NULL)<0) {
47588  avformat_close_input(&format_ctx); format_ctx = 0;
47589  return load_ffmpeg_external(filename);
47590  }
47591 #if cimg_verbosity>=3
47592  dump_format(format_ctx,0,0,0);
47593 #endif
47594 
47595  // Special command: Return informations on main video stream.
47596  // as a vector 1x4 containing: (nb_frames,width,height,fps).
47597  if (!first_frame && !last_frame && !step_frame) {
47598  for (vstream = 0; vstream<(int)(format_ctx->nb_streams); ++vstream)
47599  if (format_ctx->streams[vstream]->codec->codec_type==AVMEDIA_TYPE_VIDEO) break;
47600  if (vstream==(int)format_ctx->nb_streams) assign();
47601  else {
47602  CImgList<doubleT> timestamps;
47603  int nb_frames;
47604  AVPacket packet;
47605  // Count frames and store timestamps.
47606  for (nb_frames = 0; av_read_frame(format_ctx,&packet)>=0; av_free_packet(&packet))
47607  if (packet.stream_index==vstream) {
47608  CImg<doubleT>::vector((double)packet.pts).move_to(timestamps);
47609  ++nb_frames;
47610  }
47611  // Get frame with, height and fps.
47612  const int
47613  framew = format_ctx->streams[vstream]->codec->width,
47614  frameh = format_ctx->streams[vstream]->codec->height;
47615  const float
47616  num = (float)(format_ctx->streams[vstream]->r_frame_rate).num,
47617  den = (float)(format_ctx->streams[vstream]->r_frame_rate).den,
47618  fps = num/den;
47619  // Return infos as a list.
47620  assign(2);
47621  (*this)[0].assign(1,4).fill((T)nb_frames,(T)framew,(T)frameh,(T)fps);
47622  (*this)[1] = (timestamps>'y');
47623  }
47624  avformat_close_input(&format_ctx); format_ctx = 0;
47625  return *this;
47626  }
47627 
47628  for (vstream = 0; vstream<(int)(format_ctx->nb_streams) &&
47629  format_ctx->streams[vstream]->codec->codec_type!=AVMEDIA_TYPE_VIDEO; ) ++vstream;
47630  if (vstream==(int)format_ctx->nb_streams) {
47631  avformat_close_input(&format_ctx); format_ctx = 0;
47632  return load_ffmpeg_external(filename);
47633  }
47634  codec_ctx = format_ctx->streams[vstream]->codec;
47635  codec = avcodec_find_decoder(codec_ctx->codec_id);
47636  if (!codec) {
47637  return load_ffmpeg_external(filename);
47638  }
47639  if (avcodec_open2(codec_ctx,codec,NULL)<0) { // Open codec
47640  return load_ffmpeg_external(filename);
47641  }
47642  }
47643 
47644  // Read video frames
47645  const unsigned int numBytes = avpicture_get_size(ffmpeg_pixfmt,codec_ctx->width,codec_ctx->height);
47646  uint8_t *const buffer = new uint8_t[numBytes];
47647  avpicture_fill((AVPicture *)converted_frame,buffer,ffmpeg_pixfmt,codec_ctx->width,codec_ctx->height);
47648  const T foo = (T)0;
47649  AVPacket packet;
47650  for (unsigned int frame = 0, next_frame = nfirst_frame;
47651  frame<=nlast_frame && av_read_frame(format_ctx,&packet)>=0; ) {
47652  if (packet.stream_index==(int)vstream) {
47653  int decoded = 0;
47654 #if defined(AV_VERSION_INT)
47655 #if LIBAVCODEC_VERSION_INT<AV_VERSION_INT(52,26,0)
47656  avcodec_decode_video(codec_ctx,avframe,&decoded,packet.data, packet.size);
47657 #else
47658  avcodec_decode_video2(codec_ctx,avframe,&decoded,&packet);
47659 #endif
47660 #else
47661  avcodec_decode_video(codec_ctx,avframe,&decoded,packet.data, packet.size);
47662 #endif
47663  if (decoded) {
47664  if (frame==next_frame) {
47665  SwsContext *c = sws_getContext(codec_ctx->width,codec_ctx->height,codec_ctx->pix_fmt,codec_ctx->width,
47666  codec_ctx->height,ffmpeg_pixfmt,1,0,0,0);
47667  sws_scale(c,avframe->data,avframe->linesize,0,codec_ctx->height,
47668  converted_frame->data,converted_frame->linesize);
47669  if (ffmpeg_pixfmt==PIX_FMT_RGB24) {
47670  CImg<ucharT> next_image(*converted_frame->data,3,codec_ctx->width,codec_ctx->height,1,true);
47671  next_image._get_permute_axes("yzcx",foo).move_to(*this);
47672  } else {
47673  CImg<ucharT> next_image(*converted_frame->data,1,codec_ctx->width,codec_ctx->height,1,true);
47674  next_image._get_permute_axes("yzcx",foo).move_to(*this);
47675  }
47676  next_frame+=nstep_frame;
47677  }
47678  ++frame;
47679  }
47680  av_free_packet(&packet);
47681  if (next_frame>nlast_frame) break;
47682  }
47683  }
47684  delete[] buffer;
47685  return *this;
47686 #endif
47687  }
47688 
47690  static CImgList<T> get_load_ffmpeg(const char *const filename,
47691  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
47692  const unsigned int step_frame=1, const bool pixel_format=true) {
47693  return CImgList<T>().load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format);
47694  }
47695 
47697 
47700  CImgList<T>& load_ffmpeg_external(const char *const filename) {
47701  if (!filename)
47702  throw CImgArgumentException(_cimglist_instance
47703  "load_ffmpeg_external(): Specified filename is (null).",
47704  cimglist_instance);
47705  std::fclose(cimg::fopen(filename,"rb")); // Check if file exists.
47706  char command[1024] = { 0 }, filetmp[512] = { 0 }, filetmp2[512] = { 0 };
47707  std::FILE *file = 0;
47708  do {
47709  cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
47710  cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_000001.ppm",filetmp);
47711  if ((file=std::fopen(filetmp2,"rb"))!=0) cimg::fclose(file);
47712  } while (file);
47713  cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_%%6d.ppm",filetmp);
47714 #if cimg_OS!=2
47715  cimg_snprintf(command,sizeof(command),"%s -i \"%s\" \"%s\" >/dev/null 2>&1",
47717  CImg<charT>::string(filename)._system_strescape().data(),
47718  CImg<charT>::string(filetmp2)._system_strescape().data());
47719 #else
47720  cimg_snprintf(command,sizeof(command),"\"%s -i \"%s\" \"%s\"\" >NUL 2>&1",
47722  CImg<charT>::string(filename)._system_strescape().data(),
47723  CImg<charT>::string(filetmp2)._system_strescape().data());
47724 #endif
47725  cimg::system(command,0);
47726  const unsigned int omode = cimg::exception_mode();
47727  cimg::exception_mode() = 0;
47728  assign();
47729  unsigned int i = 1;
47730  for (bool stop_flag = false; !stop_flag; ++i) {
47731  cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_%.6u.ppm",filetmp,i);
47732  CImg<T> img;
47733  try { img.load_pnm(filetmp2); }
47734  catch (CImgException&) { stop_flag = true; }
47735  if (img) { img.move_to(*this); std::remove(filetmp2); }
47736  }
47737  cimg::exception_mode() = omode;
47738  if (is_empty())
47739  throw CImgIOException(_cimglist_instance
47740  "load_ffmpeg_external(): Failed to open file '%s' with external command 'ffmpeg'.",
47741  cimglist_instance,
47742  filename);
47743  return *this;
47744  }
47745 
47747  static CImgList<T> get_load_ffmpeg_external(const char *const filename) {
47748  return CImgList<T>().load_ffmpeg_external(filename);
47749  }
47750 
47752 
47756  CImgList<T>& load_gif_external(const char *const filename) {
47757  if (!filename)
47758  throw CImgArgumentException(_cimglist_instance
47759  "load_gif_external(): Specified filename is (null).",
47760  cimglist_instance);
47761  std::fclose(cimg::fopen(filename,"rb")); // Check if file exists.
47762  if (!_load_gif_external(filename,false))
47763  if (!_load_gif_external(filename,true))
47764  try { assign(CImg<T>().load_other(filename)); } catch (CImgException&) { assign(); }
47765  if (is_empty())
47766  throw CImgIOException(_cimglist_instance
47767  "load_gif_external(): Failed to open file '%s'.",
47768  cimglist_instance,filename);
47769  return *this;
47770  }
47771 
47772  CImgList<T>& _load_gif_external(const char *const filename, const bool use_graphicsmagick=false) {
47773  char command[1024] = { 0 }, filetmp[512] = { 0 }, filetmp2[512] = { 0 };
47774  std::FILE *file = 0;
47775  do {
47776  cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
47777  if (use_graphicsmagick) cimg_snprintf(filetmp2,sizeof(filetmp2),"%s.png.0",filetmp);
47778  else cimg_snprintf(filetmp2,sizeof(filetmp2),"%s-0.png",filetmp);
47779  if ((file=std::fopen(filetmp2,"rb"))!=0) cimg::fclose(file);
47780  } while (file);
47781 #if cimg_OS!=2
47782  if (use_graphicsmagick) cimg_snprintf(command,sizeof(command),"%s convert \"%s\" \"%s.png\" >/dev/null 2>&1",
47784  CImg<charT>::string(filename)._system_strescape().data(),
47785  CImg<charT>::string(filetmp)._system_strescape().data());
47786  else cimg_snprintf(command,sizeof(command),"%s \"%s\" \"%s.png\" >/dev/null 2>&1",
47788  CImg<charT>::string(filename)._system_strescape().data(),
47789  CImg<charT>::string(filetmp)._system_strescape().data());
47790 #else
47791  if (use_graphicsmagick) cimg_snprintf(command,sizeof(command),"\"%s convert \"%s\" \"%s.png\"\" >NUL 2>&1",
47793  CImg<charT>::string(filename)._system_strescape().data(),
47794  CImg<charT>::string(filetmp)._system_strescape().data());
47795  else cimg_snprintf(command,sizeof(command),"\"%s \"%s\" \"%s.png\"\" >NUL 2>&1",
47797  CImg<charT>::string(filename)._system_strescape().data(),
47798  CImg<charT>::string(filetmp)._system_strescape().data());
47799 #endif
47800  cimg::system(command,0);
47801  const unsigned int omode = cimg::exception_mode();
47802  cimg::exception_mode() = 0;
47803  assign();
47804 
47805  // Try to read a single frame gif.
47806  cimg_snprintf(filetmp2,sizeof(filetmp2),"%s.png",filetmp);
47807  CImg<T> img;
47808  try { img.load_png(filetmp2); }
47809  catch (CImgException&) { }
47810  if (img) { img.move_to(*this); std::remove(filetmp2); }
47811  else { // Try to read animated gif.
47812  unsigned int i = 0;
47813  for (bool stop_flag = false; !stop_flag; ++i) {
47814  if (use_graphicsmagick) cimg_snprintf(filetmp2,sizeof(filetmp2),"%s.png.%u",filetmp,i);
47815  else cimg_snprintf(filetmp2,sizeof(filetmp2),"%s-%u.png",filetmp,i);
47816  CImg<T> img;
47817  try { img.load_png(filetmp2); }
47818  catch (CImgException&) { stop_flag = true; }
47819  if (img) { img.move_to(*this); std::remove(filetmp2); }
47820  }
47821  }
47822  cimg::exception_mode() = omode;
47823  return *this;
47824  }
47825 
47827  static CImgList<T> get_load_gif_external(const char *const filename) {
47828  return CImgList<T>().load_gif_external(filename);
47829  }
47830 
47832 
47835  CImgList<T>& load_gzip_external(const char *const filename) {
47836  if (!filename)
47837  throw CImgIOException(_cimglist_instance
47838  "load_gzip_external(): Specified filename is (null).",
47839  cimglist_instance);
47840  std::fclose(cimg::fopen(filename,"rb")); // Check if file exists.
47841  char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
47842  const char
47843  *ext = cimg::split_filename(filename,body),
47844  *ext2 = cimg::split_filename(body,0);
47845  std::FILE *file = 0;
47846  do {
47847  if (!cimg::strcasecmp(ext,"gz")) {
47848  if (*ext2) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",
47849  cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
47850  else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",
47851  cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
47852  } else {
47853  if (*ext) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",
47854  cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
47855  else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",
47856  cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
47857  }
47858  if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
47859  } while (file);
47860  cimg_snprintf(command,sizeof(command),"%s -c \"%s\" > \"%s\"",
47862  CImg<charT>::string(filename)._system_strescape().data(),
47863  CImg<charT>::string(filetmp)._system_strescape().data());
47864  cimg::system(command);
47865  if (!(file = std::fopen(filetmp,"rb"))) {
47866  cimg::fclose(cimg::fopen(filename,"r"));
47867  throw CImgIOException(_cimglist_instance
47868  "load_gzip_external(): Failed to open file '%s'.",
47869  cimglist_instance,
47870  filename);
47871 
47872  } else cimg::fclose(file);
47873  load(filetmp);
47874  std::remove(filetmp);
47875  return *this;
47876  }
47877 
47879  static CImgList<T> get_load_gzip_external(const char *const filename) {
47880  return CImgList<T>().load_gzip_external(filename);
47881  }
47882 
47884 
47890  template<typename tf, typename tc>
47891  CImgList<T>& load_off(const char *const filename,
47892  CImgList<tf>& primitives, CImgList<tc>& colors) {
47893  return get_load_off(filename,primitives,colors).move_to(*this);
47894  }
47895 
47897  template<typename tf, typename tc>
47898  static CImgList<T> get_load_off(const char *const filename,
47899  CImgList<tf>& primitives, CImgList<tc>& colors) {
47900  return CImg<T>().load_off(filename,primitives,colors)<'x';
47901  }
47902 
47904 
47910  CImgList<T>& load_tiff(const char *const filename,
47911  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
47912  const unsigned int step_frame=1) {
47913  const unsigned int
47914  nfirst_frame = first_frame<last_frame?first_frame:last_frame,
47915  nstep_frame = step_frame?step_frame:1;
47916  unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
47917 #ifndef cimg_use_tiff
47918  if (nfirst_frame || nlast_frame!=~0U || nstep_frame!=1)
47919  throw CImgArgumentException(_cimglist_instance
47920  "load_tiff(): Unable to load sub-images from file '%s' unless libtiff is enabled.",
47921  cimglist_instance,
47922  filename);
47923 
47924  return assign(CImg<T>::get_load_tiff(filename));
47925 #else
47926  TIFF *tif = TIFFOpen(filename,"r");
47927  if (tif) {
47928  unsigned int nb_images = 0;
47929  do ++nb_images; while (TIFFReadDirectory(tif));
47930  if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images))
47931  cimg::warn(_cimglist_instance
47932  "load_tiff(): Invalid specified frame range is [%u,%u] (step %u) since "
47933  "file '%s' contains %u image(s).",
47934  cimglist_instance,
47935  nfirst_frame,nlast_frame,nstep_frame,filename,nb_images);
47936 
47937  if (nfirst_frame>=nb_images) return assign();
47938  if (nlast_frame>=nb_images) nlast_frame = nb_images-1;
47939  assign(1+(nlast_frame-nfirst_frame)/nstep_frame);
47940  TIFFSetDirectory(tif,0);
47941 #if cimg_verbosity>=3
47942  TIFFSetWarningHandler(0);
47943  TIFFSetErrorHandler(0);
47944 #endif
47945  cimglist_for(*this,l) _data[l]._load_tiff(tif,nfirst_frame + l*nstep_frame);
47946  TIFFClose(tif);
47947  } else throw CImgIOException(_cimglist_instance
47948  "load_tiff(): Failed to open file '%s'.",
47949  cimglist_instance,
47950  filename);
47951  return *this;
47952 #endif
47953  }
47954 
47956  static CImgList<T> get_load_tiff(const char *const filename,
47957  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
47958  const unsigned int step_frame=1) {
47959  return CImgList<T>().load_tiff(filename,first_frame,last_frame,step_frame);
47960  }
47961 
47963  //----------------------------------
47964  //
47966 
47967  //----------------------------------
47968 
47970 
47974  const CImgList<T>& print(const char *const title=0, const bool display_stats=true) const {
47975  unsigned int msiz = 0;
47976  cimglist_for(*this,l) msiz+=_data[l].size();
47977  msiz*=sizeof(T);
47978  const unsigned int mdisp = msiz<8*1024?0:(msiz<8*1024*1024?1:2);
47979  char _title[64] = { 0 };
47980  if (!title) cimg_snprintf(_title,sizeof(_title),"CImgList<%s>",pixel_type());
47981  std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = %u/%u [%u %s], %sdata%s = (CImg<%s>*)%p",
47982  cimg::t_magenta,cimg::t_bold,title?title:_title,cimg::t_normal,
47983  cimg::t_bold,cimg::t_normal,(void*)this,
47984  cimg::t_bold,cimg::t_normal,_width,_allocated_width,
47985  mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)),
47986  mdisp==0?"b":(mdisp==1?"Kio":"Mio"),
47987  cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin());
47988  if (_data) std::fprintf(cimg::output(),"..%p.\n",(void*)((char*)end()-1));
47989  else std::fprintf(cimg::output(),".\n");
47990 
47991  char tmp[16] = { 0 };
47992  cimglist_for(*this,ll) {
47993  cimg_snprintf(tmp,sizeof(tmp),"[%d]",ll);
47994  std::fprintf(cimg::output()," ");
47995  _data[ll].print(tmp,display_stats);
47996  if (ll==3 && _width>8) { ll = _width-5; std::fprintf(cimg::output()," ...\n"); }
47997  }
47998  std::fflush(cimg::output());
47999  return *this;
48000  }
48001 
48003 
48012  const CImgList<T>& display(CImgDisplay &disp, const char axis='x', const float align=0) const {
48013  disp.display(*this,axis,align);
48014  return *this;
48015  }
48016 
48018 
48028  const CImgList<T>& display(CImgDisplay &disp, const bool display_info,
48029  const char axis='x', const float align=0,
48030  unsigned int *const XYZ=0) const {
48031  bool is_exit = false;
48032  return _display(disp,0,display_info,axis,align,XYZ,0,true,is_exit);
48033  }
48034 
48036 
48042  const CImgList<T>& display(const char *const title=0, const bool display_info=true,
48043  const char axis='x', const float align=0,
48044  unsigned int *const XYZ=0) const {
48045  CImgDisplay disp;
48046  bool is_exit = false;
48047  return _display(disp,title,display_info,axis,align,XYZ,0,true,is_exit);
48048  }
48049 
48050  const CImgList<T>& _display(CImgDisplay &disp, const char *const title, const bool display_info,
48051  const char axis, const float align, unsigned int *const XYZ,
48052  const unsigned int orig, const bool is_first_call, bool &is_exit) const {
48053  if (is_empty())
48054  throw CImgInstanceException(_cimglist_instance
48055  "display(): Empty instance.",
48056  cimglist_instance);
48057  if (!disp) {
48058  if (axis=='x') {
48059  unsigned int sum_width = 0, max_height = 0;
48060  cimglist_for(*this,l) {
48061  const CImg<T> &img = _data[l];
48062  const unsigned int
48063  w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
48064  h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
48065  sum_width+=w;
48066  if (h>max_height) max_height = h;
48067  }
48068  disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:0,1);
48069  } else {
48070  unsigned int max_width = 0, sum_height = 0;
48071  cimglist_for(*this,l) {
48072  const CImg<T> &img = _data[l];
48073  const unsigned int
48074  w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
48075  h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
48076  if (w>max_width) max_width = w;
48077  sum_height+=h;
48078  }
48079  disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:0,1);
48080  }
48081  if (!title) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width);
48082  } else if (title) disp.set_title("%s",title);
48083  const CImg<char> dtitle = CImg<char>::string(disp.title());
48084  if (display_info) print(disp.title());
48085  disp.show().flush();
48086 
48087  if (_width==1) {
48088  const unsigned int dw = disp._width, dh = disp._height;
48089  if (!is_first_call)
48090  disp.resize(cimg_fitscreen(_data[0]._width,_data[0]._height,_data[0]._depth),false).
48091  set_title("%s (%ux%ux%ux%u)",
48092  dtitle.data(),_data[0]._width,_data[0]._height,_data[0]._depth,_data[0]._spectrum);
48093  _data[0]._display(disp,0,false,XYZ,!is_first_call);
48094  if (disp.key()) is_exit = true;
48095  disp.resize(cimg_fitscreen(dw,dh,1),false).set_title("%s",dtitle.data());
48096  } else {
48097  bool disp_resize = !is_first_call;
48098  while (!disp.is_closed() && !is_exit) {
48099  const CImg<intT> s = _get_select(disp,0,true,axis,align,orig,disp_resize,!is_first_call,true);
48100  disp_resize = true;
48101  if (s[0]<0) { // No selections done.
48102  if (disp.button()&2) { disp.flush(); break; }
48103  is_exit = true;
48104  } else if (disp.wheel()) { // Zoom in/out.
48105  const int wheel = disp.wheel();
48106  disp.set_wheel();
48107  if (!is_first_call && wheel<0) break;
48108  if (wheel>0 && _width>=4) {
48109  const unsigned int
48110  delta = cimg::max(1U,(unsigned int)cimg::round(0.3*_width)),
48111  ind0 = (unsigned int)cimg::max(0,s[0] - (int)delta),
48112  ind1 = (unsigned int)cimg::min(width() - 1,s[0] + (int)delta);
48113  if ((ind0!=0 || ind1!=_width-1) && ind1 - ind0>=3)
48114  get_shared_images(ind0,ind1)._display(disp,0,false,axis,align,XYZ,orig + ind0,false,is_exit);
48115  }
48116  } else if (s[0]!=0 || s[1]!=width()-1)
48117  get_shared_images(s[0],s[1])._display(disp,0,false,axis,align,XYZ,orig+s[0],false,is_exit);
48118  }
48119  }
48120  return *this;
48121  }
48122 
48124 
48129  const CImgList<T>& save(const char *const filename, const int number=-1, const unsigned int digits=6) const {
48130  if (!filename)
48131  throw CImgArgumentException(_cimglist_instance
48132  "save(): Specified filename is (null).",
48133  cimglist_instance);
48134  // Do not test for empty instances, since .cimg format is able to manage empty instances.
48135  const bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.');
48136  const char *const ext = cimg::split_filename(filename);
48137  char nfilename[1024] = { 0 };
48138  const char *const fn = is_stdout?filename:number>=0?cimg::number_filename(filename,number,digits,nfilename):
48139  filename;
48140 
48141 #ifdef cimglist_save_plugin
48142  cimglist_save_plugin(fn);
48143 #endif
48144 #ifdef cimglist_save_plugin1
48145  cimglist_save_plugin1(fn);
48146 #endif
48147 #ifdef cimglist_save_plugin2
48148  cimglist_save_plugin2(fn);
48149 #endif
48150 #ifdef cimglist_save_plugin3
48151  cimglist_save_plugin3(fn);
48152 #endif
48153 #ifdef cimglist_save_plugin4
48154  cimglist_save_plugin4(fn);
48155 #endif
48156 #ifdef cimglist_save_plugin5
48157  cimglist_save_plugin5(fn);
48158 #endif
48159 #ifdef cimglist_save_plugin6
48160  cimglist_save_plugin6(fn);
48161 #endif
48162 #ifdef cimglist_save_plugin7
48163  cimglist_save_plugin7(fn);
48164 #endif
48165 #ifdef cimglist_save_plugin8
48166  cimglist_save_plugin8(fn);
48167 #endif
48168  if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true);
48169  else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false);
48170  else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,true);
48171  else if (!cimg::strcasecmp(ext,"avi") ||
48172  !cimg::strcasecmp(ext,"mov") ||
48173  !cimg::strcasecmp(ext,"asf") ||
48174  !cimg::strcasecmp(ext,"divx") ||
48175  !cimg::strcasecmp(ext,"flv") ||
48176  !cimg::strcasecmp(ext,"mpg") ||
48177  !cimg::strcasecmp(ext,"m1v") ||
48178  !cimg::strcasecmp(ext,"m2v") ||
48179  !cimg::strcasecmp(ext,"m4v") ||
48180  !cimg::strcasecmp(ext,"mjp") ||
48181  !cimg::strcasecmp(ext,"mp4") ||
48182  !cimg::strcasecmp(ext,"mkv") ||
48183  !cimg::strcasecmp(ext,"mpe") ||
48184  !cimg::strcasecmp(ext,"movie") ||
48185  !cimg::strcasecmp(ext,"ogm") ||
48186  !cimg::strcasecmp(ext,"ogg") ||
48187  !cimg::strcasecmp(ext,"ogv") ||
48188  !cimg::strcasecmp(ext,"qt") ||
48189  !cimg::strcasecmp(ext,"rm") ||
48190  !cimg::strcasecmp(ext,"vob") ||
48191  !cimg::strcasecmp(ext,"wmv") ||
48192  !cimg::strcasecmp(ext,"xvid") ||
48193  !cimg::strcasecmp(ext,"mpeg")) return save_ffmpeg(fn);
48194 #ifdef cimg_use_tiff
48195  else if (!cimg::strcasecmp(ext,"tif") ||
48196  !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn);
48197 #endif
48198  else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn);
48199  else {
48200  if (_width==1) _data[0].save(fn,-1);
48201  else cimglist_for(*this,l) { _data[l].save(fn,is_stdout?-1:l); if (is_stdout) std::fputc(EOF,stdout); }
48202  }
48203  return *this;
48204  }
48205 
48207 
48211  static bool is_saveable(const char *const filename) {
48212  const char *const ext = cimg::split_filename(filename);
48213  if (!cimg::strcasecmp(ext,"cimgz") ||
48214 #ifdef cimg_use_tiff
48215  !cimg::strcasecmp(ext,"tif") ||
48216  !cimg::strcasecmp(ext,"tiff") ||
48217 #endif
48218  !cimg::strcasecmp(ext,"yuv") ||
48219  !cimg::strcasecmp(ext,"avi") ||
48220  !cimg::strcasecmp(ext,"mov") ||
48221  !cimg::strcasecmp(ext,"asf") ||
48222  !cimg::strcasecmp(ext,"divx") ||
48223  !cimg::strcasecmp(ext,"flv") ||
48224  !cimg::strcasecmp(ext,"mpg") ||
48225  !cimg::strcasecmp(ext,"m1v") ||
48226  !cimg::strcasecmp(ext,"m2v") ||
48227  !cimg::strcasecmp(ext,"m4v") ||
48228  !cimg::strcasecmp(ext,"mjp") ||
48229  !cimg::strcasecmp(ext,"mp4") ||
48230  !cimg::strcasecmp(ext,"mkv") ||
48231  !cimg::strcasecmp(ext,"mpe") ||
48232  !cimg::strcasecmp(ext,"movie") ||
48233  !cimg::strcasecmp(ext,"ogm") ||
48234  !cimg::strcasecmp(ext,"ogg") ||
48235  !cimg::strcasecmp(ext,"ogv") ||
48236  !cimg::strcasecmp(ext,"qt") ||
48237  !cimg::strcasecmp(ext,"rm") ||
48238  !cimg::strcasecmp(ext,"vob") ||
48239  !cimg::strcasecmp(ext,"wmv") ||
48240  !cimg::strcasecmp(ext,"xvid") ||
48241  !cimg::strcasecmp(ext,"mpeg")) return true;
48242  return false;
48243  }
48244 
48246 
48251  const CImgList<T>& save_gif_external(const char *const filename, const unsigned int fps=25,
48252  const unsigned int nb_loops=0) {
48253  char command[1024] = { 0 }, filetmp[512] = { 0 }, filetmp2[512] = { 0 };
48254  CImgList<charT> filenames;
48255  std::FILE *file = 0;
48256 
48257 #ifdef cimg_use_png
48258 #define _cimg_save_gif_ext "png"
48259 #else
48260 #define _cimg_save_gif_ext "ppm"
48261 #endif
48262 
48263  do {
48264  cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
48265  cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_000001." _cimg_save_gif_ext,filetmp);
48266  if ((file=std::fopen(filetmp2,"rb"))!=0) cimg::fclose(file);
48267  } while (file);
48268  cimglist_for(*this,l) {
48269  cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_%.6u." _cimg_save_gif_ext,filetmp,l+1);
48270  CImg<charT>::string(filetmp2).move_to(filenames);
48271  if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save(filetmp2);
48272  else _data[l].save(filetmp2);
48273  }
48274 
48275 #if cimg_OS!=2
48276  cimg_snprintf(command,sizeof(command),"%s -delay 1x%u -loop %u",
48277  cimg::imagemagick_path(),fps,nb_loops);
48278  CImg<ucharT>::string(command).move_to(filenames,0);
48279  cimg_snprintf(command,sizeof(command),"\"%s\" >/dev/null 2>&1",
48280  CImg<charT>::string(filename)._system_strescape().data());
48281  CImg<ucharT>::string(command).move_to(filenames);
48282 #else
48283  cimg_snprintf(command,sizeof(command),"\"%s -delay 1x%u -loop %u",
48284  cimg::imagemagick_path(),fps,nb_loops);
48285  CImg<ucharT>::string(command).move_to(filenames,0);
48286  cimg_snprintf(command,sizeof(command),"\"%s\"\" >NUL 2>&1",
48287  CImg<charT>::string(filename)._system_strescape().data());
48288  CImg<ucharT>::string(command).move_to(filenames);
48289 #endif
48290  CImg<charT> _command = filenames>'x';
48291  cimg_for(_command,p,char) if (!*p) *p = ' ';
48292  _command.back() = 0;
48293 
48294  cimg::system(_command);
48295  file = std::fopen(filename,"rb");
48296  if (!file)
48297  throw CImgIOException(_cimglist_instance
48298  "save_gif_external(): Failed to save file '%s' with external command 'convert'.",
48299  cimglist_instance,
48300  filename);
48301  else cimg::fclose(file);
48302  cimglist_for_in(*this,1,filenames._width-1,l) std::remove(filenames[l]);
48303  return *this;
48304  }
48305 
48307 
48312  // This piece of code has been originally written by David. G. Starkweather.
48313  const CImgList<T>& save_ffmpeg(const char *const filename, const unsigned int fps=25,
48314  const unsigned int bitrate=2048) const {
48315  if (!filename)
48316  throw CImgArgumentException(_cimglist_instance
48317  "save_ffmpeg(): Specified filename is (null).",
48318  cimglist_instance);
48319  if (!fps)
48320  throw CImgArgumentException(_cimglist_instance
48321  "save_ffmpeg(): Invalid specified framerate 0, for file '%s'.",
48322  cimglist_instance,
48323  filename);
48324  if (is_empty()) { cimg::fempty(0,filename); return *this; }
48325 
48326  cimglist_for(*this,l) if (!_data[l].is_sameXYZ(_data[0]))
48327  throw CImgInstanceException(_cimglist_instance
48328  "save_ffmpeg(): Invalid instance dimensions, for file '%s'.",
48329  cimglist_instance,
48330  filename);
48331 
48332 #ifndef cimg_use_ffmpeg
48333  return save_ffmpeg_external(filename,0,fps,bitrate);
48334 #else
48335  avcodec_register_all();
48336  av_register_all();
48337  const int
48338  frame_dimx = _data[0].width(),
48339  frame_dimy = _data[0].height(),
48340  frame_dimv = _data[0].spectrum();
48341  if (frame_dimv!=1 && frame_dimv!=3)
48342  throw CImgInstanceException(_cimglist_instance
48343  "save_ffmpeg(): Image[0] (%u,%u,%u,%u,%p) has not 1 or 3 channels, for file '%s'.",
48344  cimglist_instance,
48345  _data[0]._width,_data[0]._height,_data[0]._depth,_data[0]._spectrum,_data,filename);
48346 
48347  PixelFormat dest_pxl_fmt = PIX_FMT_YUV420P;
48348  PixelFormat src_pxl_fmt = (frame_dimv==3)?PIX_FMT_RGB24:PIX_FMT_GRAY8;
48349 
48350  int sws_flags = SWS_FAST_BILINEAR; // Interpolation method (keeping same size images for now).
48351  AVOutputFormat *fmt = 0;
48352 #if defined(AV_VERSION_INT)
48353 #if LIBAVFORMAT_VERSION_INT<AV_VERSION_INT(52,45,0)
48354  fmt = guess_format(0,filename,0);
48355  if (!fmt) fmt = guess_format("mpeg",0,0); // Default format "mpeg".
48356 #else
48357  fmt = av_guess_format(0,filename,0);
48358  if (!fmt) fmt = av_guess_format("mpeg",0,0); // Default format "mpeg".
48359 #endif
48360 #else
48361  fmt = guess_format(0,filename,0);
48362  if (!fmt) fmt = guess_format("mpeg",0,0); // Default format "mpeg".
48363 #endif
48364 
48365  if (!fmt)
48366  throw CImgArgumentException(_cimglist_instance
48367  "save_ffmpeg(): Unable to determine codec for file '%s'.",
48368  cimglist_instance,
48369  filename);
48370 
48371  AVFormatContext *oc = 0;
48372 #if defined(AV_VERSION_INT)
48373 #if LIBAVFORMAT_VERSION_INT<AV_VERSION_INT(52,36,0)
48374  oc = av_alloc_format_context();
48375 #else
48376  oc = avformat_alloc_context();
48377 #endif
48378 #else
48379  oc = av_alloc_format_context();
48380 #endif
48381  if (!oc) // Failed to allocate format context.
48382  throw CImgIOException(_cimglist_instance
48383  "save_ffmpeg(): Failed to allocate FFMPEG structure for format context, for file '%s'.",
48384  cimglist_instance,
48385  filename);
48386 
48387  AVCodec *codec = 0;
48388  AVFrame *picture = 0;
48389  AVFrame *tmp_pict = 0;
48390  oc->oformat = fmt;
48391  std::sprintf(oc->filename,"%s",filename);
48392 
48393  // Add video stream.
48394  AVStream *video_str = 0;
48395  if (fmt->video_codec!=CODEC_ID_NONE) {
48396  video_str = avformat_new_stream(oc,NULL);
48397  if (!video_str) { // Failed to allocate stream.
48398  av_free(oc);
48399  throw CImgIOException(_cimglist_instance
48400  "save_ffmpeg(): Failed to allocate FFMPEG structure for video stream, for file '%s'.",
48401  cimglist_instance,
48402  filename);
48403  }
48404  } else { // No codec identified.
48405  av_free(oc);
48406  throw CImgIOException(_cimglist_instance
48407  "save_ffmpeg(): Failed to identify proper codec, for file '%s'.",
48408  cimglist_instance,
48409  filename);
48410  }
48411 
48412  AVCodecContext *c = video_str->codec;
48413  c->codec_id = fmt->video_codec;
48414  c->codec_type = AVMEDIA_TYPE_VIDEO;
48415  c->bit_rate = 1024*bitrate;
48416  c->width = frame_dimx;
48417  c->height = frame_dimy;
48418  c->time_base.num = 1;
48419  c->time_base.den = fps;
48420  c->gop_size = 12;
48421  c->pix_fmt = dest_pxl_fmt;
48422  if (c->codec_id==CODEC_ID_MPEG2VIDEO) c->max_b_frames = 2;
48423  if (c->codec_id==CODEC_ID_MPEG1VIDEO) c->mb_decision = 2;
48424 
48425  // Open codecs and alloc buffers.
48426  codec = avcodec_find_encoder(c->codec_id);
48427  if (!codec) { // Failed to find codec.
48428  av_free(oc);
48429  throw CImgIOException(_cimglist_instance
48430  "save_ffmpeg(): No valid codec found for file '%s'.",
48431  cimglist_instance,
48432  filename);
48433  }
48434  if (avcodec_open2(c,codec,NULL)<0) // Failed to open codec.
48435  throw CImgIOException(_cimglist_instance
48436  "save_ffmpeg(): Failed to open codec for file '%s'.",
48437  cimglist_instance,
48438  filename);
48439 
48440  tmp_pict = avcodec_alloc_frame();
48441  if (!tmp_pict) { // Failed to allocate memory for tmp_pict frame.
48442  avcodec_close(video_str->codec);
48443  av_free(oc);
48444  throw CImgIOException(_cimglist_instance
48445  "save_ffmpeg(): Failed to allocate memory for file '%s'.",
48446  cimglist_instance,
48447  filename);
48448  }
48449  tmp_pict->linesize[0] = (src_pxl_fmt==PIX_FMT_RGB24)?3*frame_dimx:frame_dimx;
48450  // tmp_pict->type = FF_BUFFER_TYPE_USER;
48451  int tmp_size = avpicture_get_size(src_pxl_fmt,frame_dimx,frame_dimy);
48452  uint8_t *tmp_buffer = (uint8_t*)av_malloc(tmp_size);
48453  if (!tmp_buffer) { // Failed to allocate memory for tmp buffer.
48454  av_free(tmp_pict);
48455  avcodec_close(video_str->codec);
48456  av_free(oc);
48457  throw CImgIOException(_cimglist_instance
48458  "save_ffmpeg(): Failed to allocate memory for file '%s'.",
48459  cimglist_instance,
48460  filename);
48461  }
48462 
48463  // Associate buffer with tmp_pict.
48464  avpicture_fill((AVPicture*)tmp_pict,tmp_buffer,src_pxl_fmt,frame_dimx,frame_dimy);
48465  picture = avcodec_alloc_frame();
48466  if (!picture) { // Failed to allocate picture frame.
48467  av_free(tmp_pict->data[0]);
48468  av_free(tmp_pict);
48469  avcodec_close(video_str->codec);
48470  av_free(oc);
48471  throw CImgIOException(_cimglist_instance
48472  "save_ffmpeg(): Failed to allocate memory for file '%s'.",
48473  cimglist_instance,
48474  filename);
48475  }
48476 
48477  int size = avpicture_get_size(c->pix_fmt,frame_dimx,frame_dimy);
48478  uint8_t *buffer = (uint8_t*)av_malloc(size);
48479  if (!buffer) { // Failed to allocate picture frame buffer.
48480  av_free(picture);
48481  av_free(tmp_pict->data[0]);
48482  av_free(tmp_pict);
48483  avcodec_close(video_str->codec);
48484  av_free(oc);
48485  throw CImgIOException(_cimglist_instance
48486  "save_ffmpeg(): Failed to allocate memory for file '%s'.",
48487  cimglist_instance,
48488  filename);
48489  }
48490 
48491  // Associate the buffer with picture.
48492  avpicture_fill((AVPicture*)picture,buffer,c->pix_fmt,frame_dimx,frame_dimy);
48493 
48494  // Open file.
48495  if (!(fmt->flags&AVFMT_NOFILE)) {
48496  if (avio_open(&oc->pb,filename,AVIO_FLAG_WRITE)<0)
48497  throw CImgIOException(_cimglist_instance
48498  "save_ffmpeg(): Failed to open file '%s'.",
48499  cimglist_instance,
48500  filename);
48501  }
48502 
48503  if (avformat_write_header(oc,NULL)<0)
48504  throw CImgIOException(_cimglist_instance
48505  "save_ffmpeg(): Failed to write header in file '%s'.",
48506  cimglist_instance,
48507  filename);
48508 
48509  SwsContext *img_convert_context = 0;
48510  img_convert_context = sws_getContext(frame_dimx,frame_dimy,src_pxl_fmt,
48511  c->width,c->height,c->pix_fmt,sws_flags,0,0,0);
48512  if (!img_convert_context) { // Failed to get swscale context.
48513  // if (!(fmt->flags & AVFMT_NOFILE)) url_fclose(&oc->pb);
48514  av_free(picture->data);
48515  av_free(picture);
48516  av_free(tmp_pict->data[0]);
48517  av_free(tmp_pict);
48518  avcodec_close(video_str->codec);
48519  av_free(oc);
48520  throw CImgIOException(_cimglist_instance
48521  "save_ffmpeg(): Failed to get conversion context for file '%s'.",
48522  cimglist_instance,
48523  filename);
48524  }
48525  int ret = 0, out_size;
48526  uint8_t *video_outbuf = 0;
48527  int video_outbuf_size = 1000000;
48528  video_outbuf = (uint8_t*)av_malloc(video_outbuf_size);
48529  if (!video_outbuf) {
48530  // if (!(fmt->flags & AVFMT_NOFILE)) url_fclose(&oc->pb);
48531  av_free(picture->data);
48532  av_free(picture);
48533  av_free(tmp_pict->data[0]);
48534  av_free(tmp_pict);
48535  avcodec_close(video_str->codec);
48536  av_free(oc);
48537  throw CImgIOException(_cimglist_instance
48538  "save_ffmpeg(): Failed to allocate memory, for file '%s'.",
48539  cimglist_instance,
48540  filename);
48541  }
48542 
48543  // Loop through each desired image in list.
48544  cimglist_for(*this,i) {
48545  CImg<uint8_t> currentIm = _data[i], red, green, blue, gray;
48546  if (src_pxl_fmt==PIX_FMT_RGB24) {
48547  red = currentIm.get_shared_channel(0);
48548  green = currentIm.get_shared_channel(1);
48549  blue = currentIm.get_shared_channel(2);
48550  cimg_forXY(currentIm,X,Y) { // Assign pizel values to data buffer in interlaced RGBRGB ... format.
48551  tmp_pict->data[0][Y*tmp_pict->linesize[0] + 3*X] = red(X,Y);
48552  tmp_pict->data[0][Y*tmp_pict->linesize[0] + 3*X + 1] = green(X,Y);
48553  tmp_pict->data[0][Y*tmp_pict->linesize[0] + 3*X + 2] = blue(X,Y);
48554  }
48555  } else {
48556  gray = currentIm.get_shared_channel(0);
48557  cimg_forXY(currentIm,X,Y) tmp_pict->data[0][Y*tmp_pict->linesize[0] + X] = gray(X,Y);
48558  }
48559  if (!video_str) break;
48560  if (sws_scale(img_convert_context,tmp_pict->data,tmp_pict->linesize,0,
48561  c->height,picture->data,picture->linesize)<0) break;
48562 
48563  AVPacket pkt;
48564  int got_packet;
48565  av_init_packet(&pkt);
48566  out_size = avcodec_encode_video2(c,&pkt,picture,&got_packet);
48567  if (got_packet) {
48568  pkt.pts = av_rescale_q(c->coded_frame->pts,c->time_base,video_str->time_base);
48569  if (c->coded_frame->key_frame) pkt.flags|=AV_PKT_FLAG_KEY;
48570  pkt.stream_index = video_str->index;
48571  pkt.data = video_outbuf;
48572  pkt.size = out_size;
48573  ret = av_write_frame(oc,&pkt);
48574  } else if (out_size<0) break;
48575  if (ret) break; // Error occured in writing frame.
48576  }
48577 
48578  // Close codec.
48579  if (video_str) {
48580  avcodec_close(video_str->codec);
48581  av_free(picture->data[0]);
48582  av_free(picture);
48583  av_free(tmp_pict->data[0]);
48584  av_free(tmp_pict);
48585  }
48586  if (av_write_trailer(oc)<0)
48587  throw CImgIOException(_cimglist_instance
48588  "save_ffmpeg(): Failed to write trailer for file '%s'.",
48589  cimglist_instance,
48590  filename);
48591 
48592  av_freep(&oc->streams[0]->codec);
48593  av_freep(&oc->streams[0]);
48594  if (!(fmt->flags&AVFMT_NOFILE)) {
48595  /*if (url_fclose(oc->pb)<0)
48596  throw CImgIOException(_cimglist_instance
48597  "save_ffmpeg(): File '%s', failed to close file.",
48598  cimglist_instance,
48599  filename);
48600  */
48601  }
48602  av_free(oc);
48603  av_free(video_outbuf);
48604  return *this;
48605 #endif
48606  }
48607 
48608  const CImgList<T>& _save_yuv(std::FILE *const file, const char *const filename, const bool is_rgb) const {
48609  if (!file && !filename)
48610  throw CImgArgumentException(_cimglist_instance
48611  "save_yuv(): Specified filename is (null).",
48612  cimglist_instance);
48613  if (is_empty()) { cimg::fempty(file,filename); return *this; }
48614  if ((*this)[0].width()%2 || (*this)[0].height()%2)
48615  throw CImgInstanceException(_cimglist_instance
48616  "save_yuv(): Invalid odd instance dimensions (%u,%u) for file '%s'.",
48617  cimglist_instance,
48618  (*this)[0].width(),(*this)[0].height(),
48619  filename?filename:"(FILE*)");
48620 
48621  std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
48622  cimglist_for(*this,l) {
48623  CImg<ucharT> YCbCr((*this)[l]);
48624  if (is_rgb) YCbCr.RGBtoYCbCr();
48625  cimg::fwrite(YCbCr._data,(unsigned long)YCbCr._width*YCbCr._height,nfile);
48626  cimg::fwrite(YCbCr.get_resize(YCbCr._width/2, YCbCr._height/2,1,3,3).data(0,0,0,1),
48627  (unsigned long)YCbCr._width*YCbCr._height/2,nfile);
48628  }
48629  if (!file) cimg::fclose(nfile);
48630  return *this;
48631  }
48632 
48634 
48638  const CImgList<T>& save_yuv(const char *const filename=0, const bool is_rgb=true) const {
48639  return _save_yuv(0,filename,is_rgb);
48640  }
48641 
48643 
48647  const CImgList<T>& save_yuv(std::FILE *const file, const bool is_rgb=true) const {
48648  return _save_yuv(file,0,is_rgb);
48649  }
48650 
48651  const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename, const bool is_compressed) const {
48652  if (!file && !filename)
48653  throw CImgArgumentException(_cimglist_instance
48654  "save_cimg(): Specified filename is (null).",
48655  cimglist_instance);
48656 #ifndef cimg_use_zlib
48657  if (is_compressed)
48658  cimg::warn(_cimglist_instance
48659  "save_cimg(): Unable to save compressed data in file '%s' unless zlib is enabled, "
48660  "saving them uncompressed.",
48661  cimglist_instance,
48662  filename?filename:"(FILE*)");
48663 #endif
48664  std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
48665  const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little";
48666  if (std::strstr(ptype,"unsigned")==ptype) std::fprintf(nfile,"%u unsigned_%s %s_endian\n",_width,ptype+9,etype);
48667  else std::fprintf(nfile,"%u %s %s_endian\n",_width,ptype,etype);
48668  cimglist_for(*this,l) {
48669  const CImg<T>& img = _data[l];
48670  std::fprintf(nfile,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum);
48671  if (img._data) {
48672  CImg<T> tmp;
48673  if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); }
48674  const CImg<T>& ref = cimg::endianness()?tmp:img;
48675  bool failed_to_compress = true;
48676  if (is_compressed) {
48677 #ifdef cimg_use_zlib
48678  const unsigned long siz = sizeof(T)*ref.size();
48679  unsigned long csiz = siz + siz/100 + 16;
48680  Bytef *const cbuf = new Bytef[csiz];
48681  if (compress(cbuf,&csiz,(Bytef*)ref._data,siz))
48682  cimg::warn(_cimglist_instance
48683  "save_cimg(): Failed to save compressed data for file '%s', saving them uncompressed.",
48684  cimglist_instance,
48685  filename?filename:"(FILE*)");
48686  else {
48687  std::fprintf(nfile," #%lu\n",csiz);
48688  cimg::fwrite(cbuf,csiz,nfile);
48689  delete[] cbuf;
48690  failed_to_compress = false;
48691  }
48692 #endif
48693  }
48694  if (failed_to_compress) { // Write in a non-compressed way.
48695  std::fputc('\n',nfile);
48696  cimg::fwrite(ref._data,ref.size(),nfile);
48697  }
48698  } else std::fputc('\n',nfile);
48699  }
48700  if (!file) cimg::fclose(nfile);
48701  return *this;
48702  }
48703 
48705 
48709  const CImgList<T>& save_cimg(const char *const filename, const bool is_compressed=false) const {
48710  return _save_cimg(0,filename,is_compressed);
48711  }
48712 
48714 
48718  const CImgList<T>& save_cimg(std::FILE *file, const bool is_compressed=false) const {
48719  return _save_cimg(file,0,is_compressed);
48720  }
48721 
48722  const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename,
48723  const unsigned int n0,
48724  const unsigned int x0, const unsigned int y0,
48725  const unsigned int z0, const unsigned int c0) const {
48726 #define _cimg_save_cimg_case(Ts,Tss) \
48727  if (!saved && !cimg::strcasecmp(Ts,str_pixeltype)) { \
48728  for (unsigned int l = 0; l<lmax; ++l) { \
48729  j = 0; while((i=std::fgetc(nfile))!='\n') tmp[j++]=(char)i; tmp[j] = 0; \
48730  W = H = D = C = 0; \
48731  if (std::sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
48732  throw CImgIOException(_cimglist_instance \
48733  "save_cimg(): Invalid size (%u,%u,%u,%u) of image[%u], for file '%s'.", \
48734  cimglist_instance, \
48735  W,H,D,C,l,filename?filename:"(FILE*)"); \
48736  if (W*H*D*C>0) { \
48737  if (l<n0 || x0>=W || y0>=H || z0>=D || c0>=D) std::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
48738  else { \
48739  const CImg<T>& img = (*this)[l - n0]; \
48740  const T *ptrs = img._data; \
48741  const unsigned int \
48742  x1 = x0 + img._width - 1, \
48743  y1 = y0 + img._height - 1, \
48744  z1 = z0 + img._depth - 1, \
48745  c1 = c0 + img._spectrum - 1, \
48746  nx1 = x1>=W?W-1:x1, \
48747  ny1 = y1>=H?H-1:y1, \
48748  nz1 = z1>=D?D-1:z1, \
48749  nc1 = c1>=C?C-1:c1; \
48750  CImg<Tss> raw(1+nx1-x0); \
48751  const unsigned int skipvb = c0*W*H*D*sizeof(Tss); \
48752  if (skipvb) std::fseek(nfile,skipvb,SEEK_CUR); \
48753  for (unsigned int v = 1 + nc1 - c0; v; --v) { \
48754  const unsigned int skipzb = z0*W*H*sizeof(Tss); \
48755  if (skipzb) std::fseek(nfile,skipzb,SEEK_CUR); \
48756  for (unsigned int z = 1 + nz1 - z0; z; --z) { \
48757  const unsigned int skipyb = y0*W*sizeof(Tss); \
48758  if (skipyb) std::fseek(nfile,skipyb,SEEK_CUR); \
48759  for (unsigned int y = 1 + ny1 - y0; y; --y) { \
48760  const unsigned int skipxb = x0*sizeof(Tss); \
48761  if (skipxb) std::fseek(nfile,skipxb,SEEK_CUR); \
48762  raw.assign(ptrs, raw._width); \
48763  ptrs+=img._width; \
48764  if (endian) cimg::invert_endianness(raw._data,raw._width); \
48765  cimg::fwrite(raw._data,raw._width,nfile); \
48766  const unsigned int skipxe = (W - 1 - nx1)*sizeof(Tss); \
48767  if (skipxe) std::fseek(nfile,skipxe,SEEK_CUR); \
48768  } \
48769  const unsigned int skipye = (H - 1 - ny1)*W*sizeof(Tss); \
48770  if (skipye) std::fseek(nfile,skipye,SEEK_CUR); \
48771  } \
48772  const unsigned int skipze = (D - 1 - nz1)*W*H*sizeof(Tss); \
48773  if (skipze) std::fseek(nfile,skipze,SEEK_CUR); \
48774  } \
48775  const unsigned int skipve = (C - 1 - nc1)*W*H*D*sizeof(Tss); \
48776  if (skipve) std::fseek(nfile,skipve,SEEK_CUR); \
48777  } \
48778  } \
48779  } \
48780  saved = true; \
48781  }
48782 
48783  if (!file && !filename)
48784  throw CImgArgumentException(_cimglist_instance
48785  "save_cimg(): Specified filename is (null).",
48786  cimglist_instance);
48787  if (is_empty())
48788  throw CImgInstanceException(_cimglist_instance
48789  "save_cimg(): Empty instance, for file '%s'.",
48790  cimglist_instance,
48791  filename?filename:"(FILE*)");
48792 
48793  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb+");
48794  bool saved = false, endian = cimg::endianness();
48795  char tmp[256] = { 0 }, str_pixeltype[256] = { 0 }, str_endian[256] = { 0 };
48796  unsigned int j, err, N, W, H, D, C;
48797  int i;
48798  j = 0; while((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
48799  err = std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype,str_endian);
48800  if (err<2) {
48801  if (!file) cimg::fclose(nfile);
48802  throw CImgIOException(_cimglist_instance
48803  "save_cimg(): CImg header not found in file '%s'.",
48804  cimglist_instance,
48805  filename?filename:"(FILE*)");
48806  }
48807  if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
48808  else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
48809  const unsigned int lmax = cimg::min(N,n0+_width);
48810  _cimg_save_cimg_case("bool",bool);
48811  _cimg_save_cimg_case("unsigned_char",unsigned char);
48812  _cimg_save_cimg_case("uchar",unsigned char);
48813  _cimg_save_cimg_case("char",char);
48814  _cimg_save_cimg_case("unsigned_short",unsigned short);
48815  _cimg_save_cimg_case("ushort",unsigned short);
48816  _cimg_save_cimg_case("short",short);
48817  _cimg_save_cimg_case("unsigned_int",unsigned int);
48818  _cimg_save_cimg_case("uint",unsigned int);
48819  _cimg_save_cimg_case("int",int);
48820  _cimg_save_cimg_case("unsigned_long",unsigned long);
48821  _cimg_save_cimg_case("ulong",unsigned long);
48822  _cimg_save_cimg_case("long",long);
48823  _cimg_save_cimg_case("float",float);
48824  _cimg_save_cimg_case("double",double);
48825  if (!saved) {
48826  if (!file) cimg::fclose(nfile);
48827  throw CImgIOException(_cimglist_instance
48828  "save_cimg(): Unsupported data type '%s' for file '%s'.",
48829  cimglist_instance,
48830  filename?filename:"(FILE*)",str_pixeltype);
48831  }
48832  if (!file) cimg::fclose(nfile);
48833  return *this;
48834  }
48835 
48837 
48845  const CImgList<T>& save_cimg(const char *const filename,
48846  const unsigned int n0,
48847  const unsigned int x0, const unsigned int y0,
48848  const unsigned int z0, const unsigned int c0) const {
48849  return _save_cimg(0,filename,n0,x0,y0,z0,c0);
48850  }
48851 
48853 
48861  const CImgList<T>& save_cimg(std::FILE *const file,
48862  const unsigned int n0,
48863  const unsigned int x0, const unsigned int y0,
48864  const unsigned int z0, const unsigned int c0) const {
48865  return _save_cimg(file,0,n0,x0,y0,z0,c0);
48866  }
48867 
48868  static void _save_empty_cimg(std::FILE *const file, const char *const filename,
48869  const unsigned int nb,
48870  const unsigned int dx, const unsigned int dy,
48871  const unsigned int dz, const unsigned int dc) {
48872  std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
48873  const unsigned long siz = (unsigned long)dx*dy*dz*dc*sizeof(T);
48874  std::fprintf(nfile,"%u %s\n",nb,pixel_type());
48875  for (unsigned int i=nb; i; --i) {
48876  std::fprintf(nfile,"%u %u %u %u\n",dx,dy,dz,dc);
48877  for (unsigned long off=siz; off; --off) std::fputc(0,nfile);
48878  }
48879  if (!file) cimg::fclose(nfile);
48880  }
48881 
48883 
48891  static void save_empty_cimg(const char *const filename,
48892  const unsigned int nb,
48893  const unsigned int dx, const unsigned int dy=1,
48894  const unsigned int dz=1, const unsigned int dc=1) {
48895  return _save_empty_cimg(0,filename,nb,dx,dy,dz,dc);
48896  }
48897 
48899 
48907  static void save_empty_cimg(std::FILE *const file,
48908  const unsigned int nb,
48909  const unsigned int dx, const unsigned int dy=1,
48910  const unsigned int dz=1, const unsigned int dc=1) {
48911  return _save_empty_cimg(file,0,nb,dx,dy,dz,dc);
48912  }
48913 
48915 
48919  const CImgList<T>& save_tiff(const char *const filename, const unsigned int compression_type=0) const {
48920  if (!filename)
48921  throw CImgArgumentException(_cimglist_instance
48922  "save_tiff(): Specified filename is (null).",
48923  cimglist_instance);
48924  if (is_empty()) { cimg::fempty(0,filename); return *this; }
48925 
48926 #ifndef cimg_use_tiff
48927  if (_width==1) _data[0].save_tiff(filename,compression_type);
48928  else cimglist_for(*this,l) {
48929  char nfilename[1024] = { 0 };
48930  cimg::number_filename(filename,l,6,nfilename);
48931  _data[l].save_tiff(nfilename,compression_type);
48932  }
48933 #else
48934  TIFF *tif = TIFFOpen(filename,"w");
48935  if (tif) {
48936  for (unsigned int dir = 0, l = 0; l<_width; ++l) {
48937  const CImg<T>& img = (*this)[l];
48938  if (img) {
48939  if (img._depth==1) img._save_tiff(tif,dir++,compression_type);
48940  else cimg_forZ(img,z) img.get_slice(z)._save_tiff(tif,dir++,compression_type);
48941  }
48942  }
48943  TIFFClose(tif);
48944  } else
48945  throw CImgIOException(_cimglist_instance
48946  "save_tiff(): Failed to open stream for file '%s'.",
48947  cimglist_instance,
48948  filename);
48949 #endif
48950  return *this;
48951  }
48952 
48953 
48955 
48958  const CImgList<T>& save_gzip_external(const char *const filename) const {
48959  if (!filename)
48960  throw CImgIOException(_cimglist_instance
48961  "save_gzip_external(): Specified filename is (null).",
48962  cimglist_instance);
48963 
48964  char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
48965  const char
48966  *ext = cimg::split_filename(filename,body),
48967  *ext2 = cimg::split_filename(body,0);
48968  std::FILE *file;
48969  do {
48970  if (!cimg::strcasecmp(ext,"gz")) {
48971  if (*ext2) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",
48972  cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
48973  else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.cimg",
48974  cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
48975  } else {
48976  if (*ext) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",
48977  cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
48978  else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.cimg",
48979  cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
48980  }
48981  if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
48982  } while (file);
48983 
48984  if (is_saveable(body)) {
48985  save(filetmp);
48986  cimg_snprintf(command,sizeof(command),"%s -c \"%s\" > \"%s\"",
48987  cimg::gzip_path(),
48988  CImg<charT>::string(filetmp)._system_strescape().data(),
48989  CImg<charT>::string(filename)._system_strescape().data());
48990  cimg::system(command);
48991  file = std::fopen(filename,"rb");
48992  if (!file)
48993  throw CImgIOException(_cimglist_instance
48994  "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.",
48995  cimglist_instance,
48996  filename);
48997  else cimg::fclose(file);
48998  std::remove(filetmp);
48999  } else {
49000  char nfilename[1024] = { 0 };
49001  cimglist_for(*this,l) {
49002  cimg::number_filename(body,l,6,nfilename);
49003  if (*ext) std::sprintf(nfilename + std::strlen(nfilename),".%s",ext);
49004  _data[l].save_gzip_external(nfilename);
49005  }
49006  }
49007  return *this;
49008  }
49009 
49011 
49017  const CImgList<T>& save_ffmpeg_external(const char *const filename, const char *const codec=0,
49018  const unsigned int fps=25, const unsigned int bitrate=2048) const {
49019  if (!filename)
49020  throw CImgArgumentException(_cimglist_instance
49021  "save_ffmpeg_external(): Specified filename is (null).",
49022  cimglist_instance);
49023  if (is_empty()) { cimg::fempty(0,filename); return *this; }
49024 
49025  const char
49026  *const ext = cimg::split_filename(filename),
49027  *const _codec = codec?codec:!cimg::strcasecmp(ext,"flv")?"flv":"mpeg2video";
49028 
49029  char command[1024] = { 0 }, filetmp[512] = { 0 }, filetmp2[512] = { 0 };
49030  CImgList<charT> filenames;
49031  std::FILE *file = 0;
49032  cimglist_for(*this,l) if (!_data[l].is_sameXYZ(_data[0]))
49033  throw CImgInstanceException(_cimglist_instance
49034  "save_ffmpeg_external(): Invalid instance dimensions for file '%s'.",
49035  cimglist_instance,
49036  filename);
49037  do {
49038  cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
49039  cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_000001.ppm",filetmp);
49040  if ((file=std::fopen(filetmp2,"rb"))!=0) cimg::fclose(file);
49041  } while (file);
49042  cimglist_for(*this,l) {
49043  cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_%.6u.ppm",filetmp,l+1);
49044  CImg<charT>::string(filetmp2).move_to(filenames);
49045  if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save_pnm(filetmp2);
49046  else _data[l].save_pnm(filetmp2);
49047  }
49048 #if cimg_OS!=2
49049  cimg_snprintf(command,sizeof(command),"%s -i \"%s_%%6d.ppm\" -vcodec %s -b %uk -r %u -y \"%s\" >/dev/null 2>&1",
49051  CImg<charT>::string(filetmp)._system_strescape().data(),
49052  _codec,bitrate,fps,
49053  CImg<charT>::string(filename)._system_strescape().data());
49054 #else
49055  cimg_snprintf(command,sizeof(command),"\"%s -i \"%s_%%6d.ppm\" -vcodec %s -b %uk -r %u -y \"%s\"\" >NUL 2>&1",
49057  CImg<charT>::string(filetmp)._system_strescape().data(),
49058  _codec,bitrate,fps,
49059  CImg<charT>::string(filename)._system_strescape().data());
49060 #endif
49061  cimg::system(command);
49062  file = std::fopen(filename,"rb");
49063  if (!file)
49064  throw CImgIOException(_cimglist_instance
49065  "save_ffmpeg_external(): Failed to save file '%s' with external command 'ffmpeg'.",
49066  cimglist_instance,
49067  filename);
49068  else cimg::fclose(file);
49069  cimglist_for(*this,l) std::remove(filenames[l]);
49070  return *this;
49071  }
49072 
49074  //----------------------------------
49075  //
49077 
49078  //----------------------------------
49079 
49081 
49084  return get_crop_font().move_to(*this);
49085  }
49086 
49088 
49091  CImgList<T> res;
49092  cimglist_for(*this,l) {
49093  const CImg<T>& letter = (*this)[l];
49094  int xmin = letter._width, xmax = 0;
49095  cimg_forXY(letter,x,y) if (letter(x,y)) { if (x<xmin) xmin = x; if (x>xmax) xmax = x; }
49096  if (xmin>xmax) CImg<T>(letter._width,letter._height,1,letter._spectrum,0).move_to(res);
49097  else letter.get_crop(xmin,0,xmax,letter._height-1).move_to(res);
49098  }
49099  res[' '].resize(res['f']._width,-100,-100,-100,0);
49100  if (' '+256<res.size()) res[' '+256].resize(res['f']._width,-100,-100,-100,0);
49101  return res;
49102  }
49103 
49105 
49109  static const CImgList<ucharT>& font(const unsigned int font_height, const bool is_variable_width=true) {
49110  if (!font_height) return CImgList<ucharT>::empty();
49111  cimg::mutex(11);
49112 
49113  // Decompress nearest base font data if needed.
49114  const char *data_fonts[] = { cimg::data_font12x13, cimg::data_font20x23, cimg::data_font47x53, 0 };
49115  const unsigned int data_widths[] = { 12,20,47,90 }, data_heights[] = { 13,23,53,103 },
49116  data_Ms[] = { 86,79,57,47 };
49117  const unsigned int data_ind = font_height<=13?0:font_height<=23?1:font_height<=53?2:3;
49118  static CImg<ucharT> base_fonts[4];
49119  CImg<ucharT> &base_font = base_fonts[data_ind];
49120  if (!base_font) {
49121  const unsigned int w = data_widths[data_ind], h = data_heights[data_ind], M = data_Ms[data_ind];
49122  base_font.assign(256*w,h);
49123  const char *data_font = data_fonts[data_ind];
49124  unsigned char *ptrd = base_font;
49125  const unsigned char *const ptrde = base_font.end();
49126 
49127  // Special case needed for 90x103 to avoid MS compiler limit with big strings.
49128  CImg<char> data90x103;
49129  if (!data_font) {
49130  ((CImg<char>(cimg::_data_font90x103[0],
49131  (unsigned int)std::strlen(cimg::_data_font90x103[0]),1,1,1,true),
49132  CImg<char>(cimg::_data_font90x103[1],
49133  (unsigned int)std::strlen(cimg::_data_font90x103[1])+1,1,1,1,true))>'x').
49134  move_to(data90x103);
49135  data_font = data90x103.data();
49136  }
49137 
49138  // Uncompress font data (decode RLE).
49139  for (const char *ptrs = data_font; *ptrs; ++ptrs) {
49140  const int c = *ptrs-M-32, v = c>=0?255:0, n = c>=0?c:-c;
49141  if (ptrd+n<=ptrde) { std::memset(ptrd,v,n); ptrd+=n; }
49142  else { std::memset(ptrd,v,ptrde-ptrd); break; }
49143  }
49144  }
49145 
49146  // Find optimal font cache location to return.
49147  static CImgList<ucharT> fonts[16];
49148  static bool is_variable_widths[16] = { 0 };
49149  unsigned int ind = ~0U;
49150  for (int i = 0; i<16; ++i)
49151  if (!fonts[i] || (is_variable_widths[i]==is_variable_width && font_height==fonts[i][0]._height)) {
49152  ind = i; break; // Found empty slot or cached font.
49153  }
49154  if (ind==~0U) { // No empty slots nor existing font in cache.
49155  std::memmove(fonts,fonts+1,15*sizeof(CImgList<ucharT>));
49156  std::memmove(is_variable_widths,is_variable_widths+1,15*sizeof(bool));
49157  std::memset(fonts+(ind=15),0,sizeof(CImgList<ucharT>)); // Free a slot in cache for new font.
49158  }
49159  CImgList<ucharT> &font = fonts[ind];
49160 
49161  // Render requested font.
49162  if (!font) {
49163  const unsigned int padding_x = font_height<33?1:font_height<53?2:font_height<103?3:4;
49164  is_variable_widths[ind] = is_variable_width;
49165  font = base_font.get_split('x',256);
49166  if (font_height!=font[0]._height)
49167  cimglist_for(font,l)
49168  font[l].resize(cimg::max(1U,font[l]._width*font_height/font[l]._height),font_height,-100,-100,
49169  font[0]._height>font_height?2:5);
49170  if (is_variable_width) font.crop_font();
49171  cimglist_for(font,l) font[l].resize(font[l]._width + padding_x,-100,1,1,0,0,0.5);
49172  font.insert(256,0);
49173  cimglist_for_in(font,0,255,l) font[l].assign(font[l+256]._width,font[l+256]._height,1,3,1);
49174  }
49175  cimg::mutex(11,0);
49176  return font;
49177  }
49178 
49180 
49184  CImgList<T>& FFT(const char axis, const bool invert=false) {
49185  if (is_empty()) return *this;
49186  if (_width==1) insert(1);
49187  if (_width>2)
49188  cimg::warn(_cimglist_instance
49189  "FFT(): Instance has more than 2 images",
49190  cimglist_instance);
49191 
49192  CImg<T>::FFT(_data[0],_data[1],axis,invert);
49193  return *this;
49194  }
49195 
49197  CImgList<Tfloat> get_FFT(const char axis, const bool invert=false) const {
49198  return CImgList<Tfloat>(*this,false).FFT(axis,invert);
49199  }
49200 
49202 
49205  CImgList<T>& FFT(const bool invert=false) {
49206  if (is_empty()) return *this;
49207  if (_width==1) insert(1);
49208  if (_width>2)
49209  cimg::warn(_cimglist_instance
49210  "FFT(): Instance has more than 2 images",
49211  cimglist_instance);
49212 
49213  CImg<T>::FFT(_data[0],_data[1],invert);
49214  return *this;
49215  }
49216 
49218  CImgList<Tfloat> get_FFT(const bool invert=false) const {
49219  return CImgList<Tfloat>(*this,false).FFT(invert);
49220  }
49221 
49223 
49226  cimglist_for(*this,l) {
49227  CImg<T>& p = _data[l];
49228  switch (p.size()) {
49229  case 2: case 3: cimg::swap(p[0],p[1]); break;
49230  case 6: cimg::swap(p[0],p[1],p[2],p[4],p[3],p[5]); break;
49231  case 9: cimg::swap(p[0],p[1],p[3],p[5],p[4],p[6]); break;
49232  case 4: cimg::swap(p[0],p[1],p[2],p[3]); break;
49233  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;
49234  }
49235  }
49236  return *this;
49237  }
49238 
49241  return (+*this).reverse_object3d();
49242  }
49243 
49245  }; // struct CImgList<T> { ...
49246 
49247  /*
49248  #---------------------------------------------
49249  #
49250  # Completion of previously declared functions
49251  #
49252  #----------------------------------------------
49253  */
49254 
49255 namespace cimg {
49256 
49257  // Implement a tic/toc mechanism to display elapsed time of algorithms.
49258  inline unsigned long tictoc(const bool is_tic) {
49259  cimg::mutex(2);
49260  static CImg<unsigned long> times(64);
49261  static unsigned int pos = 0;
49262  const unsigned long t1 = cimg::time();
49263  if (is_tic) { // Tic.
49264  times[pos++] = t1;
49265  if (pos>=times._width)
49266  throw CImgArgumentException("cimg::tic(): Too much calls to 'cimg::tic()' without calls to 'cimg::toc()'.");
49267  cimg::mutex(2,0);
49268  return t1;
49269  }
49270  // Toc.
49271  if (!pos)
49272  throw CImgArgumentException("cimg::toc(): No previous call to 'cimg::tic()' has been made.");
49273  const unsigned long
49274  t0 = times[--pos],
49275  dt = t1>=t0?(t1-t0):cimg::type<unsigned long>::max();
49276  const unsigned int
49277  edays = (unsigned int)(dt/86400000.0),
49278  ehours = (unsigned int)((dt - edays*86400000.0)/3600000.0),
49279  emin = (unsigned int)((dt - edays*86400000.0 - ehours*3600000.0)/60000.0),
49280  esec = (unsigned int)((dt - edays*86400000.0 - ehours*3600000.0 - emin*60000.0)/1000.0),
49281  ems = (unsigned int)(dt - edays*86400000.0 - ehours*3600000.0 - emin*60000.0 - esec*1000.0);
49282  if (!edays && !ehours && !emin && !esec)
49283  std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u ms%s\n",
49284  cimg::t_red,1+2*pos,"",ems,cimg::t_normal);
49285  else {
49286  if (!edays && !ehours && !emin)
49287  std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u sec %u ms%s\n",
49288  cimg::t_red,1+2*pos,"",esec,ems,cimg::t_normal);
49289  else {
49290  if (!edays && !ehours)
49291  std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u min %u sec %u ms%s\n",
49292  cimg::t_red,1+2*pos,"",emin,esec,ems,cimg::t_normal);
49293  else{
49294  if (!edays)
49295  std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u hours %u min %u sec %u ms%s\n",
49296  cimg::t_red,1+2*pos,"",ehours,emin,esec,ems,cimg::t_normal);
49297  else{
49298  std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u days %u hours %u min %u sec %u ms%s\n",
49299  cimg::t_red,1+2*pos,"",edays,ehours,emin,esec,ems,cimg::t_normal);
49300  }
49301  }
49302  }
49303  }
49304  cimg::mutex(2,0);
49305  return dt;
49306  }
49307 
49309 
49327  template<typename t>
49328  inline int dialog(const char *const title, const char *const msg,
49329  const char *const button1_label, const char *const button2_label,
49330  const char *const button3_label, const char *const button4_label,
49331  const char *const button5_label, const char *const button6_label,
49332  const CImg<t>& logo, const bool is_centered = false) {
49333 #if cimg_display==0
49334  cimg::unused(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label,
49335  logo._data,is_centered);
49336  throw CImgIOException("cimg::dialog(): No display available.");
49337 #else
49338  const unsigned char
49339  black[] = { 0,0,0 }, white[] = { 255,255,255 }, gray[] = { 200,200,200 }, gray2[] = { 150,150,150 };
49340 
49341  // Create buttons and canvas graphics
49342  CImgList<unsigned char> buttons, cbuttons, sbuttons;
49343  if (button1_label) { CImg<unsigned char>().draw_text(0,0,button1_label,black,gray,1,13).move_to(buttons);
49344  if (button2_label) { CImg<unsigned char>().draw_text(0,0,button2_label,black,gray,1,13).move_to(buttons);
49345  if (button3_label) { CImg<unsigned char>().draw_text(0,0,button3_label,black,gray,1,13).move_to(buttons);
49346  if (button4_label) { CImg<unsigned char>().draw_text(0,0,button4_label,black,gray,1,13).move_to(buttons);
49347  if (button5_label) { CImg<unsigned char>().draw_text(0,0,button5_label,black,gray,1,13).move_to(buttons);
49348  if (button6_label) { CImg<unsigned char>().draw_text(0,0,button6_label,black,gray,1,13).move_to(buttons);
49349  }}}}}}
49350  if (!buttons._width)
49351  throw CImgArgumentException("cimg::dialog(): No buttons have been defined.");
49352  cimglist_for(buttons,l) buttons[l].resize(-100,-100,1,3);
49353 
49354  unsigned int bw = 0, bh = 0;
49355  cimglist_for(buttons,l) { bw = cimg::max(bw,buttons[l]._width); bh = cimg::max(bh,buttons[l]._height); }
49356  bw+=8; bh+=8;
49357  if (bw<64) bw = 64;
49358  if (bw>128) bw = 128;
49359  if (bh<24) bh = 24;
49360  if (bh>48) bh = 48;
49361 
49362  CImg<unsigned char> button(bw,bh,1,3);
49363  button.draw_rectangle(0,0,bw-1,bh-1,gray);
49364  button.draw_line(0,0,bw-1,0,white).draw_line(0,bh-1,0,0,white);
49365  button.draw_line(bw-1,0,bw-1,bh-1,black).draw_line(bw-1,bh-1,0,bh-1,black);
49366  button.draw_line(1,bh-2,bw-2,bh-2,gray2).draw_line(bw-2,bh-2,bw-2,1,gray2);
49367  CImg<unsigned char> sbutton(bw,bh,1,3);
49368  sbutton.draw_rectangle(0,0,bw-1,bh-1,gray);
49369  sbutton.draw_line(0,0,bw-1,0,black).draw_line(bw-1,0,bw-1,bh-1,black);
49370  sbutton.draw_line(bw-1,bh-1,0,bh-1,black).draw_line(0,bh-1,0,0,black);
49371  sbutton.draw_line(1,1,bw-2,1,white).draw_line(1,bh-2,1,1,white);
49372  sbutton.draw_line(bw-2,1,bw-2,bh-2,black).draw_line(bw-2,bh-2,1,bh-2,black);
49373  sbutton.draw_line(2,bh-3,bw-3,bh-3,gray2).draw_line(bw-3,bh-3,bw-3,2,gray2);
49374  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);
49375  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);
49376  CImg<unsigned char> cbutton(bw,bh,1,3);
49377  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);
49378  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);
49379  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);
49380 
49381  cimglist_for(buttons,ll) {
49382  CImg<unsigned char>(cbutton).draw_image(1+(bw-buttons[ll].width())/2,1+(bh-buttons[ll].height())/2,buttons[ll]).
49383  move_to(cbuttons);
49384  CImg<unsigned char>(sbutton).draw_image((bw-buttons[ll].width())/2,(bh-buttons[ll].height())/2,buttons[ll]).
49385  move_to(sbuttons);
49386  CImg<unsigned char>(button).draw_image((bw-buttons[ll].width())/2,(bh-buttons[ll].height())/2,buttons[ll]).
49387  move_to(buttons[ll]);
49388  }
49389 
49390  CImg<unsigned char> canvas;
49391  if (msg)
49392  ((CImg<unsigned char>().draw_text(0,0,"%s",gray,0,1,13,msg)*=-1)+=200).resize(-100,-100,1,3).move_to(canvas);
49393 
49394  const unsigned int
49395  bwall = (buttons._width-1)*(12+bw) + bw,
49396  w = cimg::max(196U,36+logo._width+canvas._width,24+bwall),
49397  h = cimg::max(96U,36+canvas._height+bh,36+logo._height+bh),
49398  lx = 12 + (canvas._data?0:((w-24-logo._width)/2)),
49399  ly = (h-12-bh-logo._height)/2,
49400  tx = lx+logo._width+12,
49401  ty = (h-12-bh-canvas._height)/2,
49402  bx = (w-bwall)/2,
49403  by = h-12-bh;
49404 
49405  if (canvas._data)
49406  canvas = CImg<unsigned char>(w,h,1,3).
49407  draw_rectangle(0,0,w-1,h-1,gray).
49408  draw_line(0,0,w-1,0,white).draw_line(0,h-1,0,0,white).
49409  draw_line(w-1,0,w-1,h-1,black).draw_line(w-1,h-1,0,h-1,black).
49410  draw_image(tx,ty,canvas);
49411  else
49412  canvas = CImg<unsigned char>(w,h,1,3).
49413  draw_rectangle(0,0,w-1,h-1,gray).
49414  draw_line(0,0,w-1,0,white).draw_line(0,h-1,0,0,white).
49415  draw_line(w-1,0,w-1,h-1,black).draw_line(w-1,h-1,0,h-1,black);
49416  if (logo._data) canvas.draw_image(lx,ly,logo);
49417 
49418  unsigned int xbuttons[6] = { 0 };
49419  cimglist_for(buttons,lll) { xbuttons[lll] = bx+(bw+12)*lll; canvas.draw_image(xbuttons[lll],by,buttons[lll]); }
49420 
49421  // Open window and enter events loop
49422  CImgDisplay disp(canvas,title?title:" ",0,false,is_centered?true:false);
49423  if (is_centered) disp.move((CImgDisplay::screen_width() - disp.width())/2,
49424  (CImgDisplay::screen_height() - disp.height())/2);
49425  bool stop_flag = false, refresh = false;
49426  int oselected = -1, oclicked = -1, selected = -1, clicked = -1;
49427  while (!disp.is_closed() && !stop_flag) {
49428  if (refresh) {
49429  if (clicked>=0)
49430  CImg<unsigned char>(canvas).draw_image(xbuttons[clicked],by,cbuttons[clicked]).display(disp);
49431  else {
49432  if (selected>=0)
49433  CImg<unsigned char>(canvas).draw_image(xbuttons[selected],by,sbuttons[selected]).display(disp);
49434  else canvas.display(disp);
49435  }
49436  refresh = false;
49437  }
49438  disp.wait(15);
49439  if (disp.is_resized()) disp.resize(disp,false);
49440 
49441  if (disp.button()&1) {
49442  oclicked = clicked;
49443  clicked = -1;
49444  cimglist_for(buttons,l)
49445  if (disp.mouse_y()>=(int)by && disp.mouse_y()<(int)(by+bh) &&
49446  disp.mouse_x()>=(int)xbuttons[l] && disp.mouse_x()<(int)(xbuttons[l]+bw)) {
49447  clicked = selected = l;
49448  refresh = true;
49449  }
49450  if (clicked!=oclicked) refresh = true;
49451  } else if (clicked>=0) stop_flag = true;
49452 
49453  if (disp.key()) {
49454  oselected = selected;
49455  switch (disp.key()) {
49456  case cimg::keyESC : selected=-1; stop_flag = true; break;
49457  case cimg::keyENTER : if (selected<0) selected = 0; stop_flag = true; break;
49458  case cimg::keyTAB :
49459  case cimg::keyARROWRIGHT :
49460  case cimg::keyARROWDOWN : selected = (selected+1)%buttons._width; break;
49461  case cimg::keyARROWLEFT :
49462  case cimg::keyARROWUP : selected = (selected+buttons._width-1)%buttons._width; break;
49463  }
49464  disp.set_key();
49465  if (selected!=oselected) refresh = true;
49466  }
49467  }
49468  if (!disp) selected = -1;
49469  return selected;
49470 #endif
49471  }
49472 
49474  inline int dialog(const char *const title, const char *const msg,
49475  const char *const button1_label, const char *const button2_label, const char *const button3_label,
49476  const char *const button4_label, const char *const button5_label, const char *const button6_label,
49477  const bool is_centered) {
49478  return dialog(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label,
49479  CImg<unsigned char>::_logo40x38(),is_centered);
49480  }
49481 
49483 
49498  inline double eval(const char *const expression, const double x, const double y, const double z, const double c) {
49499  static const CImg<float> empty;
49500  return empty.eval(expression,x,y,z,c);
49501  }
49502 
49503  template<typename t>
49504  inline CImg<typename cimg::superset<double,t>::type> eval(const char *const expression, const CImg<t>& xyzc) {
49505  static const CImg<float> empty;
49506  return empty.eval(expression,xyzc);
49507  }
49508 
49509  // End of cimg:: namespace
49510 }
49511 
49512  // End of cimg_library:: namespace
49513 }
49514 
49516 namespace cil = cimg_library_suffixed;
49517 
49518 #ifdef _cimg_redefine_False
49519 #define False 0
49520 #endif
49521 #ifdef _cimg_redefine_True
49522 #define True 1
49523 #endif
49524 #ifdef _cimg_redefine_None
49525 #define None 0
49526 #endif
49527 #ifdef _cimg_redefine_min
49528 #define min(a,b) (((a)<(b))?(a):(b))
49529 #endif
49530 #ifdef _cimg_redefine_max
49531 #define max(a,b) (((a)>(b))?(a):(b))
49532 #endif
49533 #ifdef _cimg_redefine_PI
49534 #define PI 3.141592653589793238462643383
49535 #endif
49536 
49537 #endif
49538 // Local Variables:
49539 // mode: c++
49540 // End:
CImg< T > & front()
Return reference to the first image of the list.
Definition: CImg.h:45346
CImg< _cimg_Tt > get_erode(const CImg< t > &mask, const unsigned int boundary_conditions=1, const bool is_normalized=false) const
Erode image by a structuring element .
Definition: CImg.h:24809
const CImgList< T > & save(const char *const filename, const int number=-1, const unsigned int digits=6) const
Save list into a file.
Definition: CImg.h:48129
static void wait(CImgDisplay &disp1, CImgDisplay &disp2, CImgDisplay &disp3, CImgDisplay &disp4, CImgDisplay &disp5)
Wait for any event occuring either on the display disp1, disp2, disp3, disp4 or disp5.
Definition: CImg.h:7341
CImgDisplay & assign()
Destructor - Empty constructor .
Definition: CImg.h:6225
CImg< T > get_rol(const CImg< t > &img) const
Compute the bitwise left rotation of each pixel value .
Definition: CImg.h:15812
const unsigned int keyCTRLLEFT
Keycode for the CTRLLEFT key (architecture-dependent).
Definition: CImg.h:2936
const CImg< T > & save_rgb(const char *const filename) const
Save image as a RGB file.
Definition: CImg.h:43086
CImg< T > get_resize(const CImgDisplay &disp, const int interpolation_type=1, const unsigned int boundary_conditions=0, const float centering_x=0, const float centering_y=0, const float centering_z=0, const float centering_c=0) const
Resize image to dimensions of a display window .
Definition: CImg.h:21947
CImg< T > & resize_tripleXY()
Resize image to triple-size, using the Scale3X algorithm.
Definition: CImg.h:22036
CImg< T > get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0)
Return a shared-memory image referencing one row of the image instance.
Definition: CImg.h:24095
CImg< T > & load_camera(const unsigned int camera_index=0, const unsigned int skip_frames=0, const bool release_camera=true, const unsigned int capture_width=0, const unsigned int capture_height=0)
Load image from a camera stream, using OpenCV.
Definition: CImg.h:40909
CImgList< T > get_split(const T value, const bool keep_values, const bool is_shared) const
Split image into a list of one-column vectors, according to a specified splitting value...
Definition: CImg.h:24320
CImgList< T > & reverse_object3d()
Reverse primitives orientations of a 3d object.
Definition: CImg.h:49225
CImg< T > & draw_triangle(CImg< tz > &zbuffer, const int x0, const int y0, const float z0, const int x1, const int y1, const float z1, const int x2, const int y2, const float z2, const tc *const color, const CImg< tl > &light, const int lx0, const int ly0, const int lx1, const int ly1, const int lx2, const int ly2, const float opacity=1)
Draw a Phong-shaded 2d triangle, with z-buffering.
Definition: CImg.h:32973
CImg< T > & draw_object3d(const float x0, const float y0, const float z0, const CImg< tp > &vertices, const CImgList< tf > &primitives, const CImgList< tc > &colors, const unsigned int render_type, const bool is_double_sided, const float focale, const float lightx, const float lighty, const float lightz, const float specular_lightness, const float specular_shininess, CImg< tz > &zbuffer)
Draw a 3d object .
Definition: CImg.h:35856
const CImg< T > & display_object3d(CImgDisplay &disp, const CImg< tp > &vertices, const CImgList< tf > &primitives, const CImgList< tc > &colors, const bool centering=true, const int render_static=4, const int render_motion=1, const bool is_double_sided=true, const float focale=700, const float light_x=0, const float light_y=0, const float light_z=-5e8f, const float specular_lightness=0.2f, const float specular_shininess=0.1f, const bool display_axes=true, float *const pose_matrix=0) const
Display object 3d in an interactive window .
Definition: CImg.h:41384
CImg< T > operator^(const CImg< t > &img) const
Bitwise XOR operator.
Definition: CImg.h:11590
const CImg< T > & save_off(const CImgList< tf > &primitives, const CImgList< tc > &colors, std::FILE *const file) const
Save 3d object as an Object File Format (.off) file .
Definition: CImg.h:43975
CImg< T > & atan2(const CImg< t > &img)
Compute the arctangent2 of each pixel value.
Definition: CImg.h:15486
CImg< T > operator++(int)
In-place increment operator (postfix).
Definition: CImg.h:10780
const_iterator end() const
Return a CImg::iterator pointing next to the last pixel value .
Definition: CImg.h:12198
const unsigned int keyF5
Keycode for the F5 key (architecture-dependent).
Definition: CImg.h:2878
CImg< T > get_resize(const int size_x, const int size_y=-100, const int size_z=-100, const int size_c=-100, const int interpolation_type=1, const unsigned int boundary_conditions=0, const float centering_x=0, const float centering_y=0, const float centering_z=0, const float centering_c=0) const
Resize image to new dimensions .
Definition: CImg.h:21012
CImg< T > & solve(const CImg< t > &A)
Solve a system of linear equations.
Definition: CImg.h:17063
CImgList< T > & push_front(const CImg< t > &img)
Insert image at the front of the list.
Definition: CImg.h:46555
const unsigned int keyZ
Keycode for the Z key (architecture-dependent).
Definition: CImg.h:2927
const CImgList< T > & save_yuv(std::FILE *const file, const bool is_rgb=true) const
Save image sequence into a YUV file.
Definition: CImg.h:48647
CImg< T > get_column(const int x0) const
Return specified image column.
Definition: CImg.h:23647
CImg< T > & operator=(const CImgDisplay &disp)
Copy the content of a display window to the current image instance.
Definition: CImg.h:10640
CImg< T > get_rand(const T val_min, const T val_max) const
Fill image with random values in specified range .
Definition: CImg.h:18857
CImg< T > & LabtoRGB()
Convert pixel values from Lab to RGB color spaces.
Definition: CImg.h:20739
CImg< T > & draw_triangle(CImg< tz > &zbuffer, const int x0, const int y0, const float z0, const int x1, const int y1, const float z1, const int x2, const int y2, const float z2, const CImg< tc > &texture, const int tx0, const int ty0, const int tx1, const int ty1, const int tx2, const int ty2, const float opacity=1, const float brightness=1)
Draw a textured 2d triangle, with perspective correction and z-buffering.
Definition: CImg.h:32711
CImg< T > & draw_triangle(const int x0, const int y0, const float z0, const int x1, const int y1, const float z1, const int x2, const int y2, const float z2, const CImg< tc > &texture, const int tx0, const int ty0, const int tx1, const int ty1, const int tx2, const int ty2, const CImg< tl > &light, const int lx0, const int ly0, const int lx1, const int ly1, const int lx2, const int ly2, const float opacity=1)
Draw a textured Phong-shaded 2d triangle, with perspective correction.
Definition: CImg.h:33561
CImg< Tfloat > get_displacement(const CImg< T > &source, const float smoothness=0.1f, const float precision=5.0f, const unsigned int nb_scales=0, const unsigned int iteration_max=10000, const bool is_backward=false) const
Estimate displacement field between two images .
Definition: CImg.h:27490
CImg< Tfloat > get_tan() const
Compute the tangent of each pixel value .
Definition: CImg.h:15332
CImg< T > & tanh()
Compute the hyperbolic tangent of each pixel value.
Definition: CImg.h:15388
CImg< T > & operator/=(const t value)
In-place division operator.
Definition: CImg.h:11094
T & atXYZ(const int x, const int y, const int z, const int c=0)
Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates.
Definition: CImg.h:12432
const CImg< T > & save_rgba(const char *const filename) const
Save image as a RGBA file.
Definition: CImg.h:43148
CImgList< T > get_insert(const CImg< t > &img, const unsigned int pos=~0U, const bool is_shared=false) const
Insert a copy of the image img into the current image list, at position pos .
Definition: CImg.h:46204
CImg< T > & draw_triangle(const int x0, const int y0, const int x1, const int y1, const int x2, const int y2, const CImg< tc > &texture, const int tx0, const int ty0, const int tx1, const int ty1, const int tx2, const int ty2, const float opacity=1, const float brightness=1)
Draw a textured 2d triangle.
Definition: CImg.h:32458
CImg< T > & operator-=(const t value)
In-place substraction operator.
Definition: CImg.h:10833
Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T out_value) const
Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X and Y-coord...
Definition: CImg.h:12918
CImg< T > & move_to(CImg< T > &img)
Transfer content of an image instance into another one .
Definition: CImg.h:10330
const CImgList< T > get_shared_images(const unsigned int pos0, const unsigned int pos1) const
Return a shared sublist .
Definition: CImg.h:46415
CImg< T > get_row(const int y0) const
Return specified image row.
Definition: CImg.h:23671
CImg< T > & RGBtoYUV()
Convert pixel values from RGB to YUV color spaces.
Definition: CImg.h:20374
CImg< T > get_vector_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const
Get vector-valued pixel located at specified position.
Definition: CImg.h:16681
CImg< T > & load_raw(const char *const filename, const unsigned int size_x=0, const unsigned int size_y=1, const unsigned int size_z=1, const unsigned int size_c=1, const bool is_multiplexed=false, const bool invert_endianness=false, const unsigned long offset=0)
Load image from a raw binary file.
Definition: CImg.h:40215
T & atXY(const int x, const int y, const int z, const int c, const T out_value)
Access to a pixel value, using Dirichlet boundary conditions for the X and Y-coordinates.
Definition: CImg.h:12369
CImgList< T > & assign(const unsigned int n, const unsigned int width, const unsigned int height, const unsigned int depth, const unsigned int spectrum, const int val0, const int val1,...)
Construct list with images of specified size, and initialize pixel values from a sequence of integers...
Definition: CImg.h:44850
bool is_sameX(const CImg< t > &img) const
Test if image width is equal to specified value.
Definition: CImg.h:13385
static void save_empty_cimg(std::FILE *const file, const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dc=1)
Save blank image as a .cimg file .
Definition: CImg.h:43513
CImgList< T > & pop_front()
Remove first image.
Definition: CImg.h:46587
CImg< T > & operator>>=(const CImg< t > &img)
In-place bitwise right shift operator.
Definition: CImg.h:11751
Tfloat cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const
Return pixel value, using cubic interpolation and Neumann boundary conditions for the X and Y-coordin...
Definition: CImg.h:12958
const CImg< T > & save_imagemagick_external(const char *const filename, const unsigned int quality=100) const
Save image using ImageMagick's external binary.
Definition: CImg.h:44197
CImg< T > get_blur_bilateral(const CImg< t > &guide, const float sigma_s, const float sigma_r, const int bgrid_s=-33, const int bgrid_r=32, const bool interpolation_type=true) const
Blur image using the bilateral filter .
Definition: CImg.h:26325
Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T out_value, const Tfloat min_value, const Tfloat max_value) const
Return damped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coor...
Definition: CImg.h:12850
CImg< T > & BayertoRGB(const unsigned int interpolation_type=3)
Convert Bayer-coded scalar image to a RGB color image.
Definition: CImg.h:20820
static CImg< T > matrix(const T &a0, const T &a1, const T &a2, const T &a3, const T &a4, const T &a5, const T &a6, const T &a7, const T &a8, const T &a9, const T &a10, const T &a11, const T &a12, const T &a13, const T &a14, const T &a15, const T &a16, const T &a17, const T &a18, const T &a19, const T &a20, const T &a21, const T &a22, const T &a23, const T &a24)
Return a 5x5 matrix containing specified coefficients.
Definition: CImg.h:18048
static void wait(CImgDisplay &disp1, CImgDisplay &disp2, CImgDisplay &disp3, CImgDisplay &disp4, CImgDisplay &disp5, CImgDisplay &disp6, CImgDisplay &disp7, CImgDisplay &disp8, CImgDisplay &disp9)
Wait for any event occuring either on the display disp1, disp2, disp3, disp4, ... disp9...
Definition: CImg.h:7383
CImg< T > & operator+=(const CImg< t > &img)
In-place addition operator.
Definition: CImg.h:10745
const char * basename(const char *const str)
Return the basename of a filename.
Definition: CImg.h:4600
CImgList< T > & insert(const CImg< t > &img, const unsigned int pos=~0U, const bool is_shared=false)
Insert a copy of the image img into the current image list, at position pos.
Definition: CImg.h:46107
CImg< T > & fill(const T val0, const T val1, const T val2, const T val3)
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18221
CImgList< T > get_reverse() const
Reverse list order .
Definition: CImg.h:46373
CImg< _cimg_Tt > get_min(const CImg< t > &img) const
Pointwise min operator between two images .
Definition: CImg.h:15951
static void wait(CImgDisplay &disp1, CImgDisplay &disp2, CImgDisplay &disp3, CImgDisplay &disp4)
Wait for any event occuring either on the display disp1, disp2, disp3 or disp4.
Definition: CImg.h:7334
CImg< T > operator+() const
Return a non-shared copy of the image instance.
Definition: CImg.h:10795
CImg< T > get_permute_axes(const char *const order) const
Permute axes order .
Definition: CImg.h:22418
const CImg< T > & display_object3d(const char *const title, const CImg< tp > &vertices, const CImgList< tf > &primitives, const CImgList< tc > &colors, const to &opacities, const bool centering=true, const int render_static=4, const int render_motion=1, const bool is_double_sided=true, const float focale=700, const float light_x=0, const float light_y=0, const float light_z=-5e8f, const float specular_lightness=0.2f, const float specular_shininess=0.1f, const bool display_axes=true, float *const pose_matrix=0) const
Display object 3d in an interactive window .
Definition: CImg.h:41364
cimg::superset< t1, t2 >::type min(const t1 &a, const t2 &b)
Return the minimum between two values.
Definition: CImg.h:4309
CImg< T > & draw_object3d(const float x0, const float y0, const float z0, const CImg< tp > &vertices, const CImgList< tf > &primitives, const CImgList< tc > &colors, const CImgList< to > &opacities, const unsigned int render_type, const bool is_double_sided, const float focale, const float lightx, const float lighty, const float lightz, const float specular_lightness, const float specular_shininess, CImg< tz > &zbuffer)
Draw a 3d object .
Definition: CImg.h:35796
CImg< T > & sin()
Compute the sine of each pixel value.
Definition: CImg.h:15277
CImg< T > & object3dtoCImg3d(const bool full_check=true)
Convert 3d object into a CImg3d representation .
Definition: CImg.h:30238
const unsigned int keyDELETE
Keycode for the DELETE key (architecture-dependent).
Definition: CImg.h:2912
bool contains(const T &pixel, t &n, t &x, t &y, t &z) const
Test if one of the image list contains the specified referenced value.
Definition: CImg.h:45853
CImg< T > & XYZtoLab()
Convert pixel values from XYZ_709 to Lab color spaces.
Definition: CImg.h:20603
CImg< T > operator^(const t value) const
Bitwise XOR operator.
Definition: CImg.h:11571
const T & operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const
Return a reference to one pixel value of one image of the list .
Definition: CImg.h:45122
static CImgList< T > get_load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1, const bool pixel_format=true)
Load an image from a video file, using ffmpeg libraries .
Definition: CImg.h:47690
CImg< T > & div(const CImg< t > &img)
In-place pointwise division.
Definition: CImg.h:15547
CImg< t > & move_to(CImg< t > &img)
Transfer content of an image instance into another one.
Definition: CImg.h:10323
CImgDisplay & set_mouse(const int pos_x, const int pos_y)
Move mouse pointer to a specified location.
Definition: CImg.h:7122
cimg::superset< t1, t2 >::type max(const t1 &a, const t2 &b)
Return the maximum between two values.
Definition: CImg.h:4330
CImgList< T > & assign(const CImgList< T > &list, const bool is_shared=false)
Construct list as a copy of an existing list and force shared state of elements .
Definition: CImg.h:44992
CImg< T > get_invert_endianness() const
Invert endianness of all pixel values .
Definition: CImg.h:18840
CImg< T > & mirror(const char axis)
Mirror image content along specified axis.
Definition: CImg.h:22099
CImg< T > & draw_gaussian(const float xc, const float sigma, const tc *const color, const float opacity=1)
Draw a 1d gaussian function.
Definition: CImg.h:35585
CImg< T > & RGBtoxyY()
Convert pixel values from RGB to xyY color spaces.
Definition: CImg.h:20749
CImgList< T > & load_cimg(const char *const filename, const unsigned int n0, const unsigned int n1, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0, const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1)
Load a sublist list from a (non compressed) .cimg file.
Definition: CImg.h:47118
CImg< T > & draw_grid(const float delta_x, const float delta_y, const float offsetx, const float offsety, const bool invertx, const bool inverty, const tc *const color, const float opacity=1, const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U)
Draw 2d grid .
Definition: CImg.h:35008
CImg< T > & min(const char *const expression)
Pointwise min operator between an image and an expression.
Definition: CImg.h:15961
static CImg< T > get_load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0)
Load image sequence using FFMPEG's external tool 'ffmpeg' .
Definition: CImg.h:40598
CImg< T > & fillC(const unsigned int x, const unsigned int y, const unsigned int z, const int a0,...)
Fill pixel values along the C-axis at a specified pixel position.
Definition: CImg.h:18765
CImg< T > & assign(const t *const values, const unsigned int size_x, const unsigned int size_y=1, const unsigned int size_z=1, const unsigned int size_c=1)
Construct image with specified size and initialize pixel values from a memory buffer ...
Definition: CImg.h:10144
CImg< intT > get_select(const char *const title, const unsigned int feature_type=2, unsigned int *const XYZ=0) const
Simple interface to select a shape from an image .
Definition: CImg.h:37095
static CImg< floatT > cylinder3d(CImgList< tf > &primitives, const float radius=50, const float size_z=100, const unsigned int subdivisions=24)
Generate a 3d cylinder.
Definition: CImg.h:29998
CImg< T > get_blur_bilateral(const CImg< t > &guide, const float sigma_x, const float sigma_y, const float sigma_z, const float sigma_r, const int bgrid_x, const int bgrid_y, const int bgrid_z, const int bgrid_r, const bool interpolation_type=true) const
Blur image, with the joint bilateral filter .
Definition: CImg.h:26297
static unsigned int keycode(const char *const keycode)
Return keycode corresponding to the specified string.
Definition: CImg.h:6833
CImg< intT > get_select(const char *const title, const bool feature_type=true, const char axis='x', const float align=0) const
Display a simple interactive interface to select images or sublists.
Definition: CImg.h:46627
CImg< T > & load_jpeg(std::FILE *const file)
Load image from a JPEG file .
Definition: CImg.h:38440
CImg< T > & operator&=(const t value)
In-place bitwise AND operator.
Definition: CImg.h:11290
const CImgList< T > & display(CImgDisplay &disp, const char axis='x', const float align=0) const
Display the current CImgList instance in an existing CImgDisplay window (by reference).
Definition: CImg.h:48012
CImg< T > & stats(const unsigned int variance_method=1)
Compute statistics vector from the pixel values .
Definition: CImg.h:16571
CImg< T > & operator<<=(const t value)
In-place bitwise left shift operator.
Definition: CImg.h:11599
CImg< T > & draw_graph(const CImg< t > &data, const tc *const color, const float opacity=1, const unsigned int plot_type=1, const int vertex_type=1, const double ymin=0, const double ymax=0, const unsigned int pattern=~0U)
Draw 1d graph.
Definition: CImg.h:35059
CImg< T > & XYZtoRGB()
Convert pixel values from XYZ_709 to RGB color spaces.
Definition: CImg.h:20575
unsigned int size() const
Return the size of the list, i.e. the number of images contained in it.
Definition: CImg.h:45273
CImg< Tfloat > get_RGBtoHSV() const
Convert pixel values from RGB to HSV color spaces .
Definition: CImg.h:20111
T mod(const T &x, const T &m)
Return the modulo of a value.
Definition: CImg.h:4375
CImg< T > & fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, const T val7, const T val8, const T val9, const T val10, const T val11, const T val12, const T val13)
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18499
static void wait(CImgDisplay &disp1, CImgDisplay &disp2, CImgDisplay &disp3, CImgDisplay &disp4, CImgDisplay &disp5, CImgDisplay &disp6, CImgDisplay &disp7, CImgDisplay &disp8, CImgDisplay &disp9, CImgDisplay &disp10)
Wait for any event occuring either on the display disp1, disp2, disp3, disp4, ... disp10...
Definition: CImg.h:7394
const CImg< T > & save_rgba(std::FILE *const file) const
Save image as a RGBA file .
Definition: CImg.h:43153
int mouse_y() const
Return Y-coordinate of the mouse pointer.
Definition: CImg.h:6727
CImg< T > & tan()
Compute the tangent of each pixel value.
Definition: CImg.h:15322
static const CImgList< ucharT > & font(const unsigned int font_height, const bool is_variable_width=true)
Return a CImg pre-defined font with desired size.
Definition: CImg.h:49109
const CImg< T > & display_graph(CImgDisplay &disp, const unsigned int plot_type=1, const unsigned int vertex_type=1, const char *const labelx=0, const double xmin=0, const double xmax=0, const char *const labely=0, const double ymin=0, const double ymax=0) const
Display 1d graph in an interactive window.
Definition: CImg.h:41929
static CImg< T > get_load_magick(const char *const filename)
Load image from a file, using Magick++ library .
Definition: CImg.h:38620
CImgDisplay & operator=(const CImgDisplay &disp)
Construct a display as a copy of another one .
Definition: CImg.h:6336
const char * title() const
Return title of the associated window as a C-string.
Definition: CImg.h:6673
CImgList< Tfloat > get_eigen() const
Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix.
Definition: CImg.h:17250
CImg< T > & load_bmp(std::FILE *const file)
Load image from a BMP file .
Definition: CImg.h:38247
CImgList(const char *const filename)
Construct list by reading the content of a file.
Definition: CImg.h:44760
CImg< charT > value_string(const char separator=',', const unsigned int max_size=0) const
Return a C-string containing the values of all images in the instance list.
Definition: CImg.h:45659
CImg< T > & correlate(const CImg< t > &mask, const unsigned int boundary_conditions=1, const bool is_normalized=false)
Correlate image by a mask.
Definition: CImg.h:24447
T * data()
Return a pointer to the first pixel value.
Definition: CImg.h:12094
CImg< T > & LabtoXYZ()
Convert pixel values from Lab to XYZ_709 color spaces.
Definition: CImg.h:20638
const CImg< T > & symmetric_eigen(CImg< t > &val, CImg< t > &vec) const
Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix...
Definition: CImg.h:17262
const T & min() const
Return a reference to the minimum pixel value .
Definition: CImg.h:16118
const CImg< T > & save_dlm(const char *const filename) const
Save image as a DLM file.
Definition: CImg.h:42252
CImg< T > & object3dtoCImg3d(const CImgList< tp > &primitives, const bool full_check=true)
Convert 3d object into a CImg3d representation .
Definition: CImg.h:30232
CImgList(const CImgList< t > &list)
Construct list copy.
Definition: CImg.h:44734
bool is_sameXZ(const CImg< t > &img) const
Test if image width and depth are the same as that of another image.
Definition: CImg.h:13470
const char * wget_path(const char *const user_path=0, const bool reinit_path=false)
Get/set path to the wget binary.
Definition: CImg.h:5195
CImg< Tfloat > get_RGBtoYUV() const
Convert pixel values from RGB to YUV color spaces .
Definition: CImg.h:20395
CImg< T > get_channel(const int c0) const
Return specified image channel.
Definition: CImg.h:23728
static CImg< floatT > plane3d(CImgList< tf > &primitives, const float size_x=100, const float size_y=100, const unsigned int subdivisions_x=10, const unsigned int subdivisions_y=10)
Generate a 3d XY-plane.
Definition: CImg.h:30086
static CImgList< T > get_load_gzip_external(const char *const filename)
Load a gzipped list, using external tool 'gunzip' .
Definition: CImg.h:47879
CImg< T > & load_pandore(const char *const filename)
Load image from a PANDORE-5 file.
Definition: CImg.h:39965
static CImg< floatT > cone3d(CImgList< tf > &primitives, const float radius=50, const float size_z=100, const unsigned int subdivisions=24)
Generate a 3d cone.
Definition: CImg.h:29961
CImg< T > & draw_image(const int x0, const int y0, const int z0, const CImg< ti > &sprite, const CImg< tm > &mask, const float opacity=1, const float mask_max_value=1)
Draw a masked image .
Definition: CImg.h:34543
unsigned long nearest_pow2(const T x)
Return the nearest power of 2 higher than given value.
Definition: CImg.h:4357
CImgList< Tfloat > get_FFT(const char axis, const bool is_invert=false) const
Compute 1d Fast Fourier Transform, along a specified axis.
Definition: CImg.h:28482
static const CImg< Tuchar > & default_LUT256()
Return colormap "default", containing 256 colors entries in RGB.
Definition: CImg.h:19889
CImg< T > & load_dcraw_external(const char *const filename)
Load image from a RAW Color Camera file, using external tool 'dcraw'.
Definition: CImg.h:40849
CImg< T > & operator&=(const CImg< t > &img)
In-place bitwise AND operator.
Definition: CImg.h:11344
CImg< T > & blur(const float sigma_x, const float sigma_y, const float sigma_z, const bool boundary_conditions=true, const bool is_gaussian=false)
Blur image.
Definition: CImg.h:25840
CImg< T > & RGBtoHSL()
Convert pixel values from RGB to HSL color spaces.
Definition: CImg.h:20161
CImgList< T > get_split(const bool is_shared) const
Split the image into a list of one-column vectors each having same values.
Definition: CImg.h:24384
CImg< T > & draw_axis(const int x, const CImg< t > &values_y, const tc *const color, const float opacity=1, const unsigned int pattern=~0U, const unsigned int font_height=13, const bool allow_zero=true)
Draw a labeled vertical axis.
Definition: CImg.h:34872
static CImg< T > sequence(const unsigned int N, const T a0, const T a1)
Return a N-numbered sequence vector from a0 to a1.
Definition: CImg.h:18122
CImg< T > & draw_arrow(const int x0, const int y0, const int x1, const int y1, const tc *const color, const float opacity=1, const float angle=30, const float length=-10, const unsigned int pattern=~0U)
Draw a 2d arrow.
Definition: CImg.h:31423
CImg< T > & draw_rectangle(const int x0, const int y0, const int z0, const int c0, const int x1, const int y1, const int z1, const int c1, const T val, const float opacity=1)
Draw a filled 4d rectangle.
Definition: CImg.h:33846
CImg< T > & draw_spline(const int x0, const int y0, const float u0, const float v0, const int x1, const int y1, const float u1, const float v1, const CImg< t > &texture, const int tx0, const int ty0, const int tx1, const int ty1, const float opacity=1, const float precision=4, const unsigned int pattern=~0U, const bool init_hatch=true)
Draw a textured 2d spline.
Definition: CImg.h:31572
CImg< T > operator&(const CImg< t > &img) const
Bitwise AND operator.
Definition: CImg.h:11382
CImg< T > get_noise(const double sigma, const unsigned int noise_type=0) const
Add random noise to pixel values .
Definition: CImg.h:18960
const CImg< T > & save_analyze(const char *const filename, const float *const voxel_size=0) const
Save image as an ANALYZE7.5 or NIFTI file.
Definition: CImg.h:43375
static CImg< T > get_load_analyze(std::FILE *const file, float *const voxel_size=0)
Load image from an ANALYZE7.5/NIFTI file .
Definition: CImg.h:39584
const CImg< T > & save_cimg(std::FILE *const file, const bool is_compressed=false) const
Save image as a .cimg file .
Definition: CImg.h:43460
T & atXY(const int x, const int y, const int z=0, const int c=0)
Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates.
Definition: CImg.h:12385
static CImg< floatT > isoline3d(CImgList< tf > &primitives, const char *const expression, const float isovalue, const float x0, const float y0, const float x1, const float y1, const int size_x=256, const int size_y=256)
Compute isolines of a function, as a 3d object .
Definition: CImg.h:29378
static CImg< floatT > isoline3d(CImgList< tf > &primitives, const tfunc &func, const float isovalue, const float x0, const float y0, const float x1, const float y1, const int size_x=256, const int size_y=256)
Compute 0-isolines of a function, as a 3d object.
Definition: CImg.h:29294
CImg< T > get_identity_matrix() const
Replace the image by an identity matrix .
Definition: CImg.h:16890
const CImg< T > & save_raw(const char *const filename, const bool is_multiplexed=false) const
Save image as a raw data file.
Definition: CImg.h:43863
const unsigned int keyHOME
Keycode for the HOME key (architecture-dependent).
Definition: CImg.h:2899
CImg< T > & draw_image(const int x0, const int y0, const int z0, const int c0, const CImg< ti > &sprite, const CImg< tm > &mask, const float opacity=1, const float mask_max_value=1)
Draw a masked image.
Definition: CImg.h:34486
CImg< T > & min(const T val)
Pointwise min operator between instance image and a value.
Definition: CImg.h:15915
static CImg< T > get_load_png(const char *const filename)
Load image from a PNG file .
Definition: CImg.h:38633
CImg< T > & operator|=(const CImg< t > &img)
In-place bitwise OR operator.
Definition: CImg.h:11445
CImg< T > & operator^=(const char *const expression)
In-place bitwise XOR operator.
Definition: CImg.h:11509
static CImg< T > get_load_pfm(std::FILE *const file)
Load image from a PFM file .
Definition: CImg.h:39037
CImg< T > & max(const char *const expression)
Pointwise max operator between an image and an expression.
Definition: CImg.h:16058
CImg< T > & draw_line(CImg< tz > &zbuffer, const int x0, const int y0, const float z0, const int x1, const int y1, const float z1, const tc *const color, const float opacity=1, const unsigned int pattern=~0U, const bool init_hatch=true)
Draw a 2d line, with z-buffering.
Definition: CImg.h:30724
CImg< T > & RGBtosRGB()
Convert pixel values from RGB to sRGB color spaces.
Definition: CImg.h:20059
int depth() const
Return the number of image slices.
Definition: CImg.h:12040
const CImg< T > & save_graphicsmagick_external(const char *const filename, const unsigned int quality=100) const
Save image using GraphicsMagick's external binary.
Definition: CImg.h:44145
int fwrite(const T *ptr, const unsigned long nmemb, std::FILE *stream)
Write data to file.
Definition: CImg.h:5377
static void wait(CImgDisplay &disp1, CImgDisplay &disp2)
Wait for any event occuring either on the display disp1 or disp2.
Definition: CImg.h:7320
bool operator!=(const char *const expression) const
Test if all pixel values of an image are different from a specified expression.
Definition: CImg.h:11891
Tfloat linear_atXY(const float fx, const float fy, const int z, const int c, const T out_value) const
Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X and Y-coor...
Definition: CImg.h:12580
CImg< T > & sort(const bool is_increasing=true, const char axis=0)
Sort pixel values.
Definition: CImg.h:17369
CImg< T > & distance_eikonal(const unsigned int nb_iterations, const float band_size=0, const float time_step=0.5f)
Compute distance function to 0-valued isophotes, using the Eikonal PDE.
Definition: CImg.h:28169
CImg< T > & draw_rectangle(const int x0, const int y0, const int z0, const int x1, const int y1, const int z1, const tc *const color, const float opacity=1)
Draw a filled 3d rectangle.
Definition: CImg.h:33895
int window_y() const
Return Y-coordinate of the associated window.
Definition: CImg.h:6707
CImgList< T > get_remove() const
Remove last image .
Definition: CImg.h:46362
CImg< T > & fillX(const unsigned int y, const unsigned int z, const unsigned int c, const int a0,...)
Fill pixel values along the X-axis at a specified pixel position.
Definition: CImg.h:18705
CImg< T > & draw_line(CImg< tz > &zbuffer, const int x0, const int y0, const float z0, const int x1, const int y1, const float z1, const CImg< tc > &texture, const int tx0, const int ty0, const int tx1, const int ty1, const float opacity=1, const unsigned int pattern=~0U, const bool init_hatch=true)
Draw a textured 2d line, with perspective correction and z-buffering.
Definition: CImg.h:31212
const unsigned int keyPAD5
Keycode for the PAD5 key (architecture-dependent).
Definition: CImg.h:2952
CImgDisplay & set_wheel()
Flush all mouse wheel events.
Definition: CImg.h:7166
CImgList(const CImg< t1 > &img1, const CImg< t2 > &img2, const CImg< t3 > &img3, const bool is_shared=false)
Construct list from three images.
Definition: CImg.h:44624
CImg< T > get_resize_halfXY() const
Resize image to half-size along XY axes, using an optimized filter .
Definition: CImg.h:21961
static CImg< T > diagonal(const T &a0)
Return a 1x1 diagonal matrix containing specified coefficients.
Definition: CImg.h:18082
bool is_sameXYZ(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z) const
Test if image width, height and depth are equal to specified values.
Definition: CImg.h:13546
CImg(const CImg< T > &img)
Construct image copy .
Definition: CImg.h:9904
static CImg< T > get_load_minc2(const char *const filename)
Load image from a MINC2 file .
Definition: CImg.h:39560
CImg< Tfloat > get_cos() const
Compute the cosine of each pixel value .
Definition: CImg.h:15265
const CImg< T > & save_ffmpeg(const char *const filename, const unsigned int fps=25, const unsigned int bitrate=2048) const
Save image as a video file, using the FFmpeg library.
Definition: CImg.h:43908
CImgList< Tfloat > get_SVD(const bool sorting=true, const unsigned int max_iteration=40, const float lambda=0) const
Compute the SVD of the instance image, viewed as a general matrix.
Definition: CImg.h:17641
Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T out_value, const Tfloat min_value, const Tfloat max_value) const
Return damped pixel value, using cubic interpolation and Dirichlet boundary conditions for the XYZ-co...
Definition: CImg.h:13080
CImg< T > & operator|=(const char *const expression)
In-place bitwise OR operator.
Definition: CImg.h:11404
T & atXYZC(const int x, const int y, const int z, const int c, const T out_value)
Access to a pixel value, using Dirichlet boundary conditions.
Definition: CImg.h:12464
const_iterator begin() const
Return iterator to the first image of the list .
Definition: CImg.h:45327
const unsigned int keyPAD9
Keycode for the PAD9 key (architecture-dependent).
Definition: CImg.h:2956
static CImg< T > get_load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1)
Load image from a RGBA file .
Definition: CImg.h:39174
Tdouble det() const
Compute the determinant of the image, viewed as a matrix.
Definition: CImg.h:16626
CImg< Tfloat > get_RGBtoLab() const
Convert pixel values from RGB to Lab color spaces .
Definition: CImg.h:20734
static CImg< T > diagonal(const T &a0, const T &a1)
Return a 2x2 diagonal matrix containing specified coefficients.
Definition: CImg.h:18087
CImg< T > & load_graphicsmagick_external(const char *const filename)
Load image using GraphicsMagick's external tool 'gm'.
Definition: CImg.h:40624
static const char * pixel_type()
Return the type of image pixel values as a C string.
Definition: CImg.h:45257
const CImgList< T > & display(const char *const title=0, const bool display_info=true, const char axis='x', const float align=0, unsigned int *const XYZ=0) const
Display the current CImgList instance in a new display window.
Definition: CImg.h:48042
CImg< T > get_fill(const T val0, const T val1, const T val2) const
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18216
T atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const
Access to pixel value with Neumann boundary conditions for the 2 first coordinates (pos...
Definition: CImg.h:45583
CImg(const CImg< t > &img)
Construct image copy.
Definition: CImg.h:9887
CImg< floatT > get_isosurface3d(CImgList< tf > &primitives, const float isovalue, const int size_x=-100, const int size_y=-100, const int size_z=-100) const
Generate an isosurface of the image instance as a 3d object.
Definition: CImg.h:29200
bool is_key() const
Return true if any key is being pressed on the associated window, false otherwise.
Definition: CImg.h:6404
CImg< T > & identity_matrix()
Replace the image by an identity matrix.
Definition: CImg.h:16885
CImgList< T > get_remove(const unsigned int pos) const
Remove image at index pos from the image list .
Definition: CImg.h:46350
CImg< T > & assign(const CImg< t > &img, const char *const dimensions, const T value)
Construct image with dimensions borrowed from another image and initialize pixel values ...
Definition: CImg.h:10283
CImg< T > & matrix()
Resize image to become a scalar square matrix.
Definition: CImg.h:16797
CImg< T > get_fill(const T val) const
Fill all pixel values with specified value .
Definition: CImg.h:18180
CImgList< T > & assign(const unsigned int n, const unsigned int width, const unsigned int height=1, const unsigned int depth=1, const unsigned int spectrum=1)
Construct list containing images of specified size .
Definition: CImg.h:44828
const CImg< T > & print(const char *const title=0, const bool display_stats=true) const
Display informations about the image data.
Definition: CImg.h:41027
CImg< T > & RGBtoYCbCr()
Convert pixel values from RGB to YCbCr color spaces.
Definition: CImg.h:20318
CImg< T > & load_ascii(const char *const filename)
Load image from an ascii file.
Definition: CImg.h:38117
CImgList(const unsigned int n)
Construct list containing empty images.
Definition: CImg.h:44493
static CImg< T > get_load_imagemagick_external(const char *const filename)
Load image using ImageMagick's external tool 'convert' .
Definition: CImg.h:40791
CImgList< _cimg_Tt > operator,(const CImgList< t > &list) const
Construct an image list from image instance and an input image list.
Definition: CImg.h:11954
CImg< T > & RGBtoXYZ()
Convert pixel values from RGB to XYZ_709 color spaces.
Definition: CImg.h:20550
CImg< T > & assign(const T *const values, const unsigned int size_x, const unsigned int size_y=1, const unsigned int size_z=1, const unsigned int size_c=1)
Construct image with specified size and initialize pixel values from a memory buffer ...
Definition: CImg.h:10154
const unsigned int keySPACE
Keycode for the SPACE key (architecture-dependent).
Definition: CImg.h:2939
CImg< T > & resize(const CImgDisplay &disp, const int interpolation_type=1, const unsigned int boundary_conditions=0, const float centering_x=0, const float centering_y=0, const float centering_z=0, const float centering_c=0)
Resize image to dimensions of a display window.
Definition: CImg.h:21938
CImg< Tfloat > operator%(const char *const expression) const
Modulo operator.
Definition: CImg.h:11271
CImg< T > get_blur_anisotropic(const CImg< t > &G, const float amplitude=60, const float dl=0.8f, const float da=30, const float gauss_prec=2, const unsigned int interpolation_type=0, const bool is_fast_approx=true) const
Blur image anisotropically, directed by a field of diffusion tensors .
Definition: CImg.h:26149
static const CImg< Tuchar > & hot_LUT256()
Return colormap "hot", containing 256 colors entries in RGB.
Definition: CImg.h:19963
CImg< T > get_diagonal() const
Resize image to become a diagonal matrix .
Definition: CImg.h:16873
const CImg< T > & save(const char *const filename, const int number=-1, const unsigned int digits=6) const
Save image as a file.
Definition: CImg.h:42073
CImg< _cimg_Tt > get_div(const CImg< t > &img) const
In-place pointwise division .
Definition: CImg.h:15562
CImg< T > & back()
Return a reference to the last image of the list .
Definition: CImg.h:45363
CImgDisplay & flush()
Flush all display events.
Definition: CImg.h:7289
CImg< T > operator^(const char *const expression) const
Bitwise XOR operator.
Definition: CImg.h:11580
static CImg< T > get_load_tiff(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1)
Load image from a TIFF file .
Definition: CImg.h:39291
const CImg< T > & save_off(const CImgList< tf > &primitives, const CImgList< tc > &colors, const char *const filename) const
Save 3d object as an Object File Format (.off) file.
Definition: CImg.h:43964
Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const Tfloat min_value, const Tfloat max_value) const
Return damped pixel value, using cubic interpolation and Neumann boundary conditions for the X...
Definition: CImg.h:12996
CImg< T > get_rotate(const float angle, const float cx, const float cy, const float zoom, const unsigned int interpolation=1, const unsigned int boundary=3) const
Rotate image with arbitrary angle, around a center point .
Definition: CImg.h:22793
CImg< T > get_slices(const int z0, const int z1) const
Return specified range of image slices.
Definition: CImg.h:23715
CImg< T > & erode(const CImg< t > &mask, const unsigned int boundary_conditions=1, const bool is_normalized=false)
Erode image by a structuring element.
Definition: CImg.h:24801
const unsigned int keyPAD3
Keycode for the PAD3 key (architecture-dependent).
Definition: CImg.h:2950
CImg< T > & xyYtoXYZ()
Convert pixel values from xyY pixels to XYZ_709 color spaces.
Definition: CImg.h:20703
CImg< T > & fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, const T val7, const T val8)
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18339
CImgList< T > get_crop_font() const
Crop font along the X-axis .
Definition: CImg.h:49090
CImg< _cimg_Tt > get_max(const CImg< t > &img) const
Pointwise max operator between two images .
Definition: CImg.h:16048
static CImgList< T > get_load_cimg(const char *const filename, const unsigned int n0, const unsigned int n1, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0, const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1)
Load a sublist list from a (non compressed) .cimg file .
Definition: CImg.h:47128
CImg< T > & draw_rectangle(const int x0, const int y0, const int z0, const int x1, const int y1, const int z1, const tc *const color, const float opacity, const unsigned int pattern)
Draw an outlined 3d rectangle .
Definition: CImg.h:33909
CImg< T > & RGBtoCMYK()
Convert pixel values from RGB to CMYK color spaces.
Definition: CImg.h:20769
const_iterator begin() const
Return a CImg::iterator pointing to the first value of the pixel buffer .
Definition: CImg.h:12173
CImg< T > & load_medcon_external(const char *const filename)
Load image from a DICOM file, using XMedcon's external tool 'medcon'.
Definition: CImg.h:40799
CImg< T > & HSVtoRGB()
Convert pixel values from HSV to RGB color spaces.
Definition: CImg.h:20116
const CImg< T > & save_cpp(std::FILE *const file) const
Save image as a .cpp source file .
Definition: CImg.h:42220
int system(const char *const command, const char *const module_name=0)
Definition: CImg.h:3907
static const CImg< Tuchar > & cube_LUT256()
Return colormap "cube", containing 256 colors entries in RGB.
Definition: CImg.h:20027
CImg< T > & draw_line(const int x0, const int y0, const int x1, const int y1, const CImg< tc > &texture, const int tx0, const int ty0, const int tx1, const int ty1, const float opacity=1, const unsigned int pattern=~0U, const bool init_hatch=true)
Draw a textured 2d line.
Definition: CImg.h:30954
CImg< Tfloat > get_cosh() const
Compute the hyperbolic cosine of each pixel value .
Definition: CImg.h:15354
CImg< T > & draw_polygon(const CImg< t > &points, const tc *const color, const float opacity, const unsigned int pattern)
Draw a outlined 2d polygon .
Definition: CImg.h:34082
CImg< T > get_erode(const unsigned int s) const
Erode the image by a square structuring element of specified size .
Definition: CImg.h:25079
unsigned long toc()
End tic/toc timer and displays elapsed time from last call to tic().
Definition: CImg.h:4097
CImg< T > & operator%=(const t value)
In-place modulo operator.
Definition: CImg.h:11191
CImgDisplay & show()
Show (closed) associated window on the screen.
Definition: CImg.h:6928
CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, const char *const values, const bool repeat_values)
Construct image with specified size and initialize pixel values from a value string.
Definition: CImg.h:9748
CImgList< T > & operator,(const CImg< t > &img)
Return a copy of the list instance, where image img has been inserted at the end. ...
Definition: CImg.h:45199
CImgList< T > & erase(const iterator iter)
Remove image pointed by iterator.
Definition: CImg.h:46595
CImg< T > & crop(const int x0, const int x1, const bool boundary_conditions=false)
Crop image region .
Definition: CImg.h:23486
static CImg< T > get_load_graphicsmagick_external(const char *const filename)
Load image using GraphicsMagick's external tool 'gm' .
Definition: CImg.h:40675
CImg< Tfloat > get_exp() const
Compute the exponential of each pixel value .
Definition: CImg.h:15130
CImg< T > & min(const CImg< t > &img)
Pointwise min operator between two images.
Definition: CImg.h:15936
CImg< T > & fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, const T val7, const T val8, const T val9)
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18369
CImg< Tfloat > get_distance_eikonal(const T value, const CImg< t > &metric) const
Compute distance map to one source point, according to a custom metric (use fast marching algorithm)...
Definition: CImg.h:28018
CImg< T > & draw_image(const int x0, const int y0, const int z0, const int c0, const CImg< t > &sprite, const float opacity=1)
Draw an image.
Definition: CImg.h:34355
CImgList< T > & push_back(const CImgList< t > &list)
Insert list at the end of the current list.
Definition: CImg.h:46564
CImgList< T > & assign(const CImg< t1 > &img1, const CImg< t2 > &img2, const CImg< t3 > &img3, const CImg< t4 > &img4, const CImg< t5 > &img5, const bool is_shared=false)
Construct list from five images .
Definition: CImg.h:44929
static CImg< T > get_load_dcraw_external(const char *const filename)
Load image from a RAW Color Camera file, using external tool 'dcraw' .
Definition: CImg.h:40899
CImgList(const CImg< t1 > &img1, const CImg< t2 > &img2, const CImg< t3 > &img3, const CImg< t4 > &img4, const CImg< t5 > &img5, const CImg< t6 > &img6, const CImg< t7 > &img7, const bool is_shared=false)
Construct list from seven images.
Definition: CImg.h:44696
CImg< T > get_sort(const bool is_increasing=true, const char axis=0) const
Sort pixel values .
Definition: CImg.h:17410
CImg< T > get_mirror(const char axis) const
Mirror image content along specified axis .
Definition: CImg.h:22167
CImg< T > & draw_triangle(const int x0, const int y0, const int x1, const int y1, const int x2, const int y2, const tc *const color, const CImg< tl > &light, const int lx0, const int ly0, const int lx1, const int ly1, const int lx2, const int ly2, const float opacity=1)
Draw a Phong-shaded 2d triangle.
Definition: CImg.h:32892
CImg< T > & distance_eikonal(const T value, const CImg< t > &metric)
Compute distance map to one source point, according to a custom metric (use fast marching algorithm)...
Definition: CImg.h:28012
CImg< T > get_discard(const T value) const
Discard specified value in the image buffer .
Definition: CImg.h:18789
CImgList< T > & assign(const CImg< t1 > &img1, const CImg< t2 > &img2, const CImg< t3 > &img3, const CImg< t4 > &img4, const bool is_shared=false)
Construct list from four images .
Definition: CImg.h:44916
T & operator()(const unsigned int x)
Access to a pixel value.
Definition: CImg.h:10485
CImg< _cimg_Ttfloat > get_convolve(const CImg< t > &mask, const unsigned int boundary_conditions=1, const bool is_normalized=false) const
Convolve image by a mask .
Definition: CImg.h:24787
CImg< T > & load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1)
Load image from a RGB file .
Definition: CImg.h:39123
CImg< T > & draw_quiver(const CImg< t1 > &flow, const t2 *const color, const float opacity=1, const unsigned int sampling=25, const float factor=-20, const bool is_arrow=true, const unsigned int pattern=~0U)
Draw a 2d vector field.
Definition: CImg.h:34746
static CImg< T > get_load_ascii(std::FILE *const file)
Loadimage from an ascii file .
Definition: CImg.h:38132
static CImg< floatT > torus3d(CImgList< tf > &primitives, const float radius1=100, const float radius2=30, const unsigned int subdivisions1=24, const unsigned int subdivisions2=12)
Generate a 3d torus.
Definition: CImg.h:30038
const T & max_min(t &min_val) const
Return a reference to the maximum pixel value as well as the minimum pixel value .
Definition: CImg.h:16217
CImgDisplay & move(const int pos_x, const int pos_y)
Move associated window to a new location.
Definition: CImg.h:6951
const CImg< T > & display_graph(const char *const title=0, const unsigned int plot_type=1, const unsigned int vertex_type=1, const char *const labelx=0, const double xmin=0, const double xmax=0, const char *const labely=0, const double ymin=0, const double ymax=0) const
Display 1d graph in an interactive window .
Definition: CImg.h:41937
static CImg< T > vector(const T &a0, const T &a1, const T &a2, const T &a3, const T &a4, const T &a5, const T &a6, const T &a7, const T &a8, const T &a9, const T &a10)
Return a 1x11 image containing specified values.
Definition: CImg.h:17915
const CImg< T > & save_png(const char *const filename, const unsigned int bytes_per_pixel=0) const
Save image as a PNG file.
Definition: CImg.h:42586
CImgList(const unsigned int n, const CImg< t > &img, const bool is_shared=false)
Construct list containing copies of an input image.
Definition: CImg.h:44585
CImgDisplay & set_key()
Flush all key events.
Definition: CImg.h:7199
T & at(const int offset)
Access to a pixel value at a specified offset, using Neumann boundary conditions. ...
Definition: CImg.h:12269
bool contains(const T &pixel, t &x, t &y) const
Test if pixel value is inside image bounds and get its X and Y-coordinates.
Definition: CImg.h:13707
const CImg< T > get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) const
Return a shared-memory image referencing one row of the image instance .
Definition: CImg.h:24100
CImg< T > get_rows(const int y0, const int y1) const
Return specified range of image rows.
Definition: CImg.h:23688
const unsigned int keyD
Keycode for the D key (architecture-dependent).
Definition: CImg.h:2918
CImg< T > & rows(const int y0, const int y1)
Return specified range of image rows .
Definition: CImg.h:23693
CImg< T > & draw_line(const int x0, const int y0, const float z0, const int x1, const int y1, const float z1, const CImg< tc > &texture, const int tx0, const int ty0, const int tx1, const int ty1, const float opacity=1, const unsigned int pattern=~0U, const bool init_hatch=true)
Draw a textured 2d line, with perspective correction.
Definition: CImg.h:31076
bool is_sameYZ(const unsigned int size_y, const unsigned int size_z) const
Test if image height and depth are equal to specified values.
Definition: CImg.h:13495
CImg< doubleT > eval(const char *const expression, const CImg< t > &xyzc) const
Evaluate math formula on a set of variables.
Definition: CImg.h:16509
CImgList< T > & insert(const CImg< T > &img, const unsigned int pos=~0U, const bool is_shared=false)
Insert a copy of the image img into the current image list, at position pos .
Definition: CImg.h:46143
bool is_resized() const
Return true if associated window has been resized on the screen, false otherwise. ...
Definition: CImg.h:6375
const CImg< T > & back() const
Return a reference to the last image of the list.
Definition: CImg.h:45358
CImg< Tuchar > get_CMYKtoRGB() const
Convert pixel values from CMYK to RGB color spaces .
Definition: CImg.h:20784
CImgList< T > & load_off(const char *const filename, CImgList< tf > &primitives, CImgList< tc > &colors)
Load a 3d object from a .OFF file.
Definition: CImg.h:47891
CImg< Tfloat > get_structure_tensors(const unsigned int scheme=2) const
Compute the structure tensor field of an image .
Definition: CImg.h:27262
static CImg< T > get_load_raw(const char *const filename, const unsigned int size_x=0, const unsigned int size_y=1, const unsigned int size_z=1, const unsigned int size_c=1, const bool is_multiplexed=false, const bool invert_endianness=false, const unsigned long offset=0)
Load image from a raw binary file .
Definition: CImg.h:40224
std::FILE * output(std::FILE *file=0)
Get/set default output stream for the library messages.
Definition: CImg.h:3838
CImg< Tfloat > get_resize_object3d(const float sx, const float sy=-100, const float sz=-100) const
Resize 3d object .
Definition: CImg.h:28904
CImg< T > get_shared_channels(const unsigned int c0, const unsigned int c1)
Return a shared-memory image referencing a range of channels of the image instance.
Definition: CImg.h:24154
T atXYZC(const int x, const int y, const int z, const int c) const
Access to a pixel value, using Neumann boundary conditions .
Definition: CImg.h:12496
CImg< Tfloat > get_resize_object3d() const
Resize 3d object to unit size .
Definition: CImg.h:28926
CImg< T > & draw_grid(const CImg< tx > &values_x, const CImg< ty > &values_y, const tc *const color, const float opacity=1, const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U)
Draw 2d grid.
Definition: CImg.h:34991
CImgList< T > & load_cimg(const char *const filename)
Load a list from a .cimg file.
Definition: CImg.h:46982
CImg< T > & pow(const double p)
Raise each pixel value to a specified power.
Definition: CImg.h:15582
CImg< T > & load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0)
Load image sequence using FFMPEG's external tool 'ffmpeg'.
Definition: CImg.h:40593
CImg< _cimg_Tt > operator+(const CImg< t > &img) const
Addition operator.
Definition: CImg.h:10824
CImg< T > & draw_triangle(const int x0, const int y0, const float z0, const int x1, const int y1, const float z1, const int x2, const int y2, const float z2, const CImg< tc > &texture, const int tx0, const int ty0, const int tx1, const int ty1, const int tx2, const int ty2, const float opacity=1, const float brightness=1)
Draw a 2d textured triangle, with perspective correction.
Definition: CImg.h:32578
CImg< T > & operator|=(const t value)
In-place bitwise OR operator.
Definition: CImg.h:11391
Tfloat linear_atXYZC(const float fx, const float fy, const float fz, const float fc, const T out_value) const
Return pixel value, using linear interpolation and Dirichlet boundary conditions for all X...
Definition: CImg.h:12712
CImgDisplay & display(const CImgList< T > &list, const char axis='x', const float align=0)
Display list of images on associated window.
Definition: CImg.h:6910
CImg< T > get_rol(const char *const expression) const
Compute the bitwise left rotation of each pixel value .
Definition: CImg.h:15788
CImg< T > & operator*=(const char *const expression)
In-place multiplication operator.
Definition: CImg.h:10986
CImg< T > & clear()
Construct empty image .
Definition: CImg.h:10302
Contains all classes and functions of the library.
Definition: CImg.h:2060
Tdouble MSE(const CImg< t > &img) const
Compute the MSE (Mean-Squared Error) between two images.
Definition: CImg.h:16461
CImg< Tuchar > get_LabtoRGB() const
Convert pixel values from Lab to RGB color spaces .
Definition: CImg.h:20744
CImg< T > & channels(const int c0, const int c1)
Return specified range of image channels .
Definition: CImg.h:23747
const unsigned int key2
Keycode for the 2 key (architecture-dependent).
Definition: CImg.h:2888
CImgList< T > & load_ffmpeg_external(const char *const filename)
Load an image from a video file using the external tool 'ffmpeg'.
Definition: CImg.h:47700
CImg< T > get_crop(const int x0, const int x1, const bool boundary_conditions=false) const
Crop image region .
Definition: CImg.h:23491
CImg< T > & sinh()
Compute the hyperbolic sine of each pixel value.
Definition: CImg.h:15366
CImg< _cimg_Ttfloat > get_solve_tridiagonal(const CImg< t > &A) const
Solve a tridiagonal system of linear equations .
Definition: CImg.h:17196
CImg< T > & operator^=(const t value)
In-place bitwise XOR operator.
Definition: CImg.h:11494
CImg< T > & RGBtoBayer()
Convert RGB color image to a Bayer-coded scalar image.
Definition: CImg.h:20792
T & atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T out_value)
Access pixel value with Dirichlet boundary conditions for the 3 first coordinates (pos...
Definition: CImg.h:45445
CImg< Tfloat > get_pseudoinvert() const
Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix ...
Definition: CImg.h:17046
CImgList< T > & push_back(const CImg< t > &img)
Insert image at the end of the list.
Definition: CImg.h:46546
const CImg< T > * const_iterator
Simple const iterator type, to loop through each image of a const list instance.
Definition: CImg.h:44385
T atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const
Access to pixel value with Neumann boundary conditions for the 4 first coordinates (pos...
Definition: CImg.h:45473
CImgList< T > & assign(const CImg< t1 > &img1, const CImg< t2 > &img2, const CImg< t3 > &img3, const bool is_shared=false)
Construct list from three images .
Definition: CImg.h:44905
CImgDisplay & resize(const CImg< T > &img, const bool force_redraw=true)
Resize display to the size of an input image.
Definition: CImg.h:6995
CImg< T > & append_object3d(CImgList< tf > &primitives, const CImg< tp > &obj_vertices, const CImgList< tff > &obj_primitives)
Merge two 3d objects together.
Definition: CImg.h:28937
Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const
Return pixel value, using cubic interpolation and Neumann boundary conditions for the X...
Definition: CImg.h:13094
const unsigned int keyTAB
Keycode for the TAB key (architecture-dependent).
Definition: CImg.h:2901
CImgList(const unsigned int n, const unsigned int width, const unsigned int height=1, const unsigned int depth=1, const unsigned int spectrum=1)
Construct list containing images of specified size.
Definition: CImg.h:44507
const unsigned int keyPAGEUP
Keycode for the PAGEUP key (architecture-dependent).
Definition: CImg.h:2900
CImg< T > & load(const char *const filename)
Load image from a file.
Definition: CImg.h:37945
CImg< ulongT > get_histogram(const unsigned int nb_levels) const
Compute the histogram of pixel values .
Definition: CImg.h:19259
CImg< T > & log10()
Compute the base-10 logarithm of each pixel value.
Definition: CImg.h:15186
CImg< T > & load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1)
Load image from a RGBA file .
Definition: CImg.h:39179
CImg< T > get_watershed(const CImg< t > &priority, const bool fill_lines=true) const
Compute watershed transform .
Definition: CImg.h:25514
T & max()
Return a reference to the maximum pixel value.
Definition: CImg.h:16132
CImg< T > get_rotate(const float angle, const unsigned int interpolation=1, const unsigned int boundary=0) const
Rotate image with arbitrary angle .
Definition: CImg.h:22645
static int screen_width()
Return width of the screen (current resolution along the X-axis).
Definition: CImg.h:6608
const CImgList< T > & save_ffmpeg(const char *const filename, const unsigned int fps=25, const unsigned int bitrate=2048) const
Save image sequence, using FFMPEG library.
Definition: CImg.h:48313
CImg< T > & draw_text(const int x0, const int y0, const char *const text, const tc *const foreground_color, const int, const float opacity, const CImgList< t > &font,...)
Draw a text string .
Definition: CImg.h:34598
const unsigned int keyINSERT
Keycode for the INSERT key (architecture-dependent).
Definition: CImg.h:2898
CImg< typename cimg::superset< t, long >::type > get_distance_dijkstra(const T value, const CImg< t > &metric, const bool is_high_connectivity, CImg< to > &return_path) const
Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm)...
Definition: CImg.h:27837
CImg< T > & operator/=(const char *const expression)
In-place division operator.
Definition: CImg.h:11107
CImg< T > & at(const int pos)
Return pos-th image of the list.
Definition: CImg.h:45371
CImg< T > & invert(const bool use_LU=true)
Invert the instance image, viewed as a matrix.
Definition: CImg.h:16966
CImgList< T > get_split(const char axis, const int nb=0) const
Split image into a list along specified axis.
Definition: CImg.h:24211
const CImg< T > & display(CImgDisplay &disp, const bool display_info, unsigned int *const XYZ=0) const
Display image into a CImgDisplay window, in an interactive way.
Definition: CImg.h:41085
static CImgDisplay & empty()
Return a reference to an empty display.
Definition: CImg.h:6281
CImg< T > get_discard(const CImg< t > &values) const
Discard specified sequence of values in the image buffer .
Definition: CImg.h:18811
CImg< T > & fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, const T val7)
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18311
static const CImg< Tuchar > & cool_LUT256()
Return colormap "cool", containing 256 colors entries in RGB.
Definition: CImg.h:19980
T * iterator
Simple iterator type, to loop through each pixel value of an image instance.
Definition: CImg.h:9341
CImg< T > & draw_rectangle(const int x0, const int y0, const int x1, const int y1, const tc *const color, const float opacity=1)
Draw a filled 2d rectangle.
Definition: CImg.h:33937
CImg< Tuchar > get_HSLtoRGB() const
Convert pixel values from HSL to RGB color spaces .
Definition: CImg.h:20235
CImg< T > & solve_tridiagonal(const CImg< t > &A)
Solve a tridiagonal system of linear equations.
Definition: CImg.h:17173
static void wait(CImgDisplay &disp1, CImgDisplay &disp2, CImgDisplay &disp3, CImgDisplay &disp4, CImgDisplay &disp5, CImgDisplay &disp6, CImgDisplay &disp7)
Wait for any event occuring either on the display disp1, disp2, disp3, disp4, ... disp7...
Definition: CImg.h:7361
CImg< T > & noise(const double sigma, const unsigned int noise_type=0)
Add random noise to pixel values.
Definition: CImg.h:18901
const CImg< T > * data() const
Return pointer to the first image of the list .
Definition: CImg.h:45286
CImg< T > & sequence(const T a0, const T a1)
Fill image with a linear sequence of values.
Definition: CImg.h:16899
T ror(const T a, const unsigned int n=1)
Bitwise-rotate value on the right.
Definition: CImg.h:4254
Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T out_value) const
Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X...
Definition: CImg.h:13013
const T & min() const
Return a reference to the minimum pixel value of the instance list .
Definition: CImg.h:45954
CImg< T > & threshold(const T value, const bool soft_threshold=false, const bool strict_threshold=false)
Threshold pixel values.
Definition: CImg.h:19184
T & max_min(t &min_val)
Return a reference to the maximum pixel value as well as the minimum pixel value. ...
Definition: CImg.h:16199
CImg< Tfloat > get_acos() const
Compute the arccosine of each pixel value .
Definition: CImg.h:15420
CImg< T > & fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, const T val7, const T val8, const T val9, const T val10)
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18399
static CImg< T > tensor(const T &a0, const T &a1, const T &a2, const T &a3, const T &a4, const T &a5)
Return a 3x3 symmetric matrix containing specified coefficients.
Definition: CImg.h:18077
static CImgList< T > get_load_parrec(const char *const filename)
Load a list from a PAR/REC (Philips) file .
Definition: CImg.h:47397
CImg< T > & draw_triangle(CImg< tz > &zbuffer, const int x0, const int y0, const float z0, const int x1, const int y1, const float z1, const int x2, const int y2, const float z2, const CImg< tc > &texture, const int tx0, const int ty0, const int tx1, const int ty1, const int tx2, const int ty2, const float brightness0, const float brightness1, const float brightness2, const float opacity=1)
Draw a textured Gouraud-shaded 2d triangle, with perspective correction and z-buffering ...
Definition: CImg.h:33308
const unsigned int keyPAD1
Keycode for the PAD1 key (architecture-dependent).
Definition: CImg.h:2948
CImg< T > & CMYKtoRGB()
Convert pixel values from CMYK to RGB color spaces.
Definition: CImg.h:20779
static void wait(CImgDisplay &disp1, CImgDisplay &disp2, CImgDisplay &disp3)
Wait for any event occuring either on the display disp1, disp2 or disp3.
Definition: CImg.h:7327
bool is_sameXY(const CImgDisplay &disp) const
Test if image width and height are the same as that of an existing display window.
Definition: CImg.h:13453
CImg< T > & draw_image(const CImg< t > &sprite, const float opacity=1)
Draw an image .
Definition: CImg.h:34467
const CImg< T > & save_tiff(const char *const filename, const unsigned int compression_type=0) const
Save image as a TIFF file.
Definition: CImg.h:43232
CImg< T > & assign(const CImg< t > &img, const char *const dimensions)
Construct image with dimensions borrowed from another image .
Definition: CImg.h:10250
bool operator==(const t value) const
Test if all pixels of an image have the same value.
Definition: CImg.h:11811
const CImg< T > & display_object3d(CImgDisplay &disp, const CImg< tp > &vertices, const bool centering=true, const int render_static=4, const int render_motion=1, const bool is_double_sided=true, const float focale=700, const float light_x=0, const float light_y=0, const float light_z=-5e8f, const float specular_lightness=0.2f, const float specular_shininess=0.1f, const bool display_axes=true, float *const pose_matrix=0) const
Display object 3d in an interactive window .
Definition: CImg.h:41455
const CImg< T > & save_jpeg(const char *const filename, const unsigned int quality=100) const
Save image as a JPEG file.
Definition: CImg.h:42399
bool containsXYZC(const int x, const int y=0, const int z=0, const int c=0) const
Test if specified coordinates are inside image bounds.
Definition: CImg.h:13645
CImgList< Tfloat > get_FFT(const bool is_invert=false) const
Compute n-d Fast Fourier Transform.
Definition: CImg.h:28492
CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, const T value)
Construct image with specified size and initialize pixel values.
Definition: CImg.h:9522
CImg< T > & operator^=(const CImg< t > &img)
In-place bitwise XOR operator.
Definition: CImg.h:11552
CImg< T > & equalize(const unsigned int nb_levels, const T min_value, const T max_value)
Equalize histogram of pixel values.
Definition: CImg.h:19279
const unsigned int key6
Keycode for the 6 key (architecture-dependent).
Definition: CImg.h:2892
const unsigned int keyL
Keycode for the L key (architecture-dependent).
Definition: CImg.h:2924
T rol(const T a, const unsigned int n=1)
Bitwise-rotate value on the left.
Definition: CImg.h:4240
CImg< T > & draw_image(const int x0, const int y0, const int z0, const CImg< t > &sprite, const float opacity=1)
Draw an image .
Definition: CImg.h:34446
const char * gzip_path(const char *const user_path=0, const bool reinit_path=false)
Get/set path to the gzip binary.
Definition: CImg.h:5081
const CImg< T > & display_object3d(const char *const title, const CImg< tp > &vertices, const bool centering=true, const int render_static=4, const int render_motion=1, const bool is_double_sided=true, const float focale=700, const float light_x=0, const float light_y=0, const float light_z=-5e8f, const float specular_lightness=0.2f, const float specular_shininess=0.1f, const bool display_axes=true, float *const pose_matrix=0) const
Display object 3d in an interactive window .
Definition: CImg.h:41471
const unsigned int keyW
Keycode for the W key (architecture-dependent).
Definition: CImg.h:2903
CImg< T > & autocrop(const CImg< t > &color, const char *const axes="zyx")
Autocrop image region, regarding the specified background color .
Definition: CImg.h:23587
static CImg< T > get_load_pandore(const char *const filename)
Load image from a PANDORE-5 file .
Definition: CImg.h:39970
bool is_fullscreen() const
Return true if current display is in fullscreen mode, false otherwise.
Definition: CImg.h:6396
CImg< T > & load_raw(std::FILE *const file, const unsigned int size_x=0, const unsigned int size_y=1, const unsigned int size_z=1, const unsigned int size_c=1, const bool is_multiplexed=false, const bool invert_endianness=false, const unsigned long offset=0)
Load image from a raw binary file .
Definition: CImg.h:40233
CImgList< T > & operator=(const CImg< t > &img)
Construct list from one image .
Definition: CImg.h:45146
CImgList< T > & pop_back()
Remove last image.
Definition: CImg.h:46580
iterator end()
Return iterator to one position after the last image of the list.
Definition: CImg.h:45334
CImg()
Construct empty image.
Definition: CImg.h:9465
Instances of CImgException are thrown when errors are encountered in a function call.
Definition: CImg.h:2219
static CImgList< T > & empty()
Return a reference to an empty list.
Definition: CImg.h:45070
CImg< T > & permute_axes(const char *const order)
Permute axes order.
Definition: CImg.h:22413
static CImg< T > vector(const T &a0, const T &a1, const T &a2, const T &a3, const T &a4, const T &a5)
Return a 1x6 image containing specified values.
Definition: CImg.h:17868
CImgList(const CImgDisplay &disp)
Construct list from the content of a display window.
Definition: CImg.h:44769
volatile bool & is_key(const char *const keycode)
Return true if key specified by given keycode is being pressed on the associated window, false otherwise.
Definition: CImg.h:6490
const double PI
Value of the mathematical constant PI.
Definition: CImg.h:2963
CImg< T > * data()
Return pointer to the first image of the list.
Definition: CImg.h:45281
const T & max() const
Return a reference to the maximum pixel value of the instance list .
Definition: CImg.h:45986
bool is_sameX(const unsigned int size_x) const
Test if image width is equal to specified value.
Definition: CImg.h:13379
CImg< T > & distance_dijkstra(const T value, const CImg< t > &metric, const bool is_high_connectivity, CImg< to > &return_path)
Compute distance to a specified value, according to a custom metric (use dijkstra algorithm)...
Definition: CImg.h:27829
CImg< T > get_equalize(const unsigned int nblevels) const
Equalize histogram of pixel values .
Definition: CImg.h:19308
const CImg< T > & texturize_object3d(CImgList< tp > &primitives, CImgList< tc > &colors, const CImg< tt > &texture, const CImg< tx > &coords=CImg< tx >::empty()) const
Texturize primitives of a 3d object.
Definition: CImg.h:28979
std::FILE * fopen(const char *const path, const char *const mode)
Open a file.
Definition: CImg.h:4637
CImg< T > & draw_image(const int x0, const CImg< ti > &sprite, const CImg< tm > &mask, const float opacity=1, const float mask_max_value=1)
Draw a image .
Definition: CImg.h:34559
const unsigned int keyO
Keycode for the O key (architecture-dependent).
Definition: CImg.h:2910
CImg< floatT > get_projections3d(CImgList< tf > &primitives, CImgList< tc > &colors, const unsigned int x0, const unsigned int y0, const unsigned int z0, const bool normalize_colors=false) const
Generate the 3d projection planes of the image instance.
Definition: CImg.h:29101
const char * curl_path(const char *const user_path=0, const bool reinit_path=false)
Get/set path to the curl binary.
Definition: CImg.h:5233
static CImgList< T > get_load_off(const char *const filename, CImgList< tf > &primitives, CImgList< tc > &colors)
Load a 3d object from a .OFF file .
Definition: CImg.h:47898
CImg< T > & load_analyze(std::FILE *const file, float *const voxel_size=0)
Load image from an ANALYZE7.5/NIFTI file .
Definition: CImg.h:39579
const CImgList< T > & save_tiff(const char *const filename, const unsigned int compression_type=0) const
Save list as a TIFF file.
Definition: CImg.h:48919
CImgList< t > & move_to(CImgList< t > &list, const unsigned int pos=~0U)
Transfer content of an image instance into a new image in an image list.
Definition: CImg.h:10356
const unsigned int keyY
Keycode for the Y key (architecture-dependent).
Definition: CImg.h:2907
T & operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0)
Return a reference to one pixel value of one image of the list.
Definition: CImg.h:45116
Tfloat linear_atXYZ(const float fx, const float fy, const float fz, const int c, const T out_value) const
Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X...
Definition: CImg.h:12634
static CImg< T > get_load_dlm(std::FILE *const file)
Load image from a DLM file .
Definition: CImg.h:38194
static CImg< T > vector(const T &a0, const T &a1, const T &a2, const T &a3, const T &a4, const T &a5, const T &a6, const T &a7)
Return a 1x8 image containing specified values.
Definition: CImg.h:17884
CImg< T > & crop(const int x0, const int y0, const int z0, const int x1, const int y1, const int z1, const bool boundary_conditions=false)
Crop image region .
Definition: CImg.h:23458
static CImg< T > get_load_parrec(const char *const filename, const char axis='c', const float align=0)
Load image from a PAR-REC (Philips) file .
Definition: CImg.h:40200
CImg< T > & load_yuv(std::FILE *const file, const unsigned int size_x, const unsigned int size_y=1, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z')
Load image sequence from a YUV file .
Definition: CImg.h:40344
CImgList(const CImg< t1 > &img1, const CImg< t2 > &img2, const CImg< t3 > &img3, const CImg< t4 > &img4, const CImg< t5 > &img5, const CImg< t6 > &img6, const bool is_shared=false)
Construct list from six images.
Definition: CImg.h:44676
CImg< floatT > get_isoline3d(CImgList< tf > &primitives, const float isovalue, const int size_x=-100, const int size_y=-100) const
Generate a isoline of the image instance as a 3d object.
Definition: CImg.h:29158
Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const Tfloat min_value, const Tfloat max_value) const
Return damped pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordi...
Definition: CImg.h:12901
CImg< T > operator|(const CImg< t > &img) const
Bitwise OR operator.
Definition: CImg.h:11483
CImgList< T > get_insert(const unsigned int n, const CImg< t > &img, const unsigned int pos=~0U, const bool is_shared=false) const
Insert n copies of the image img into the current image list, at position pos .
Definition: CImg.h:46245
T atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const
Return pixel value with Neumann boundary conditions for the first coordinate (pos) ...
Definition: CImg.h:45637
static CImg< T > tensor(const T &a0)
Return a 1x1 symmetric matrix containing specified coefficients.
Definition: CImg.h:18067
CImg< T > & load_minc2(const char *const filename)
Load image from a MINC2 file.
Definition: CImg.h:39532
CImg< T > & load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1)
Load image from a RGB file.
Definition: CImg.h:39113
CImg< T > & draw_gaussian(const int xc, const int yc, const float r1, const float r2, const float ru, const float rv, const tc *const color, const float opacity=1)
Draw a 2d gaussian function .
Definition: CImg.h:35650
CImg< T > operator>>(const char *const expression) const
Bitwise right shift operator.
Definition: CImg.h:11779
int fread(T *const ptr, const unsigned long nmemb, std::FILE *stream)
Read data from file.
Definition: CImg.h:5349
static CImg< T > get_load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false, const char axis='z', const float align=0)
Load image sequence using FFMPEG av's libraries .
Definition: CImg.h:40308
CImg< T > & row(const int y0)
Return specified image row .
Definition: CImg.h:23679
CImg< Tfloat > operator-(const char *const expression) const
Substraction operator.
Definition: CImg.h:10954
const CImgList< T > & save_ffmpeg_external(const char *const filename, const char *const codec=0, const unsigned int fps=25, const unsigned int bitrate=2048) const
Save image sequence, using the external tool 'ffmpeg'.
Definition: CImg.h:49017
int wheel() const
Return current state of the mouse wheel.
Definition: CImg.h:6786
bool is_sameZC(const CImg< t > &img) const
Test if image depth and spectrum are the same as that of another image.
Definition: CImg.h:13538
CImgList(const CImgList< t > &list, const bool is_shared)
Construct list copy, and force the shared state of the list elements.
Definition: CImg.h:44751
static CImg< T > get_load_dlm(const char *const filename)
Load image from a DLM file .
Definition: CImg.h:38184
CImg< T > & quantize(const unsigned int nb_levels, const bool keep_range=true)
Uniformly quantize pixel values.
Definition: CImg.h:19139
CImg< T > & draw_image(const int x0, const int y0, const CImg< ti > &sprite, const CImg< tm > &mask, const float opacity=1, const float mask_max_value=1)
Draw a image .
Definition: CImg.h:34551
unsigned int prand(const double z)
Return a random variable following a Poisson distribution of parameter z.
Definition: CImg.h:4229
CImg< T > & draw_triangle(CImg< tz > &zbuffer, const int x0, const int y0, const float z0, const int x1, const int y1, const float z1, const int x2, const int y2, const float z2, const tc *const color, const float opacity=1, const float brightness=1)
Draw a filled 2d triangle, with z-buffering.
Definition: CImg.h:32138
const unsigned int keyPAD6
Keycode for the PAD6 key (architecture-dependent).
Definition: CImg.h:2953
CImg< T > get_fill(const char *const values, const bool repeat_values) const
Fill sequentially pixel values according to a given expression .
Definition: CImg.h:18671
CImg< T > & load_pfm(const char *const filename)
Load image from a PFM file.
Definition: CImg.h:39022
CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, const int value0, const int value1,...)
Construct image with specified size and initialize pixel values from a sequence of integers...
Definition: CImg.h:9568
CImg< T > operator>>(const CImg< t > &img) const
Bitwise right shift operator.
Definition: CImg.h:11790
Tdouble mean() const
Return the average pixel value.
Definition: CImg.h:16299
CImg< T > get_matrix() const
Resize image to become a scalar square matrix .
Definition: CImg.h:16824
CImg< Tfloat > get_sin() const
Compute the sine of each pixel value .
Definition: CImg.h:15287
CImg< Tfloat > get_sinh() const
Compute the hyperbolic sine of each pixel value .
Definition: CImg.h:15376
const CImg< T > & save_other(const char *const filename, const unsigned int quality=100) const
Definition: CImg.h:44301
bool is_sameXZC(const CImg< t > &img) const
Test if image width, depth and spectrum are the same as that of another image.
Definition: CImg.h:13589
static CImg< T > tensor(const T &a0, const T &a1, const T &a2)
Return a 2x2 symmetric matrix tensor containing specified coefficients.
Definition: CImg.h:18072
const unsigned int key1
Keycode for the 1 key (architecture-dependent).
Definition: CImg.h:2887
CImg< typename CImg< t >::Tuint > get_index(const CImg< t > &colormap, const float dithering=1, const bool map_indexes=true) const
Index multi-valued pixels regarding to a specified colormap .
Definition: CImg.h:19335
CImg< T > get_resize_doubleXY() const
Resize image to double-size, using the Scale2X algorithm .
Definition: CImg.h:21987
CImgList< T > & insert(const CImgList< t > &list, const unsigned int pos=~0U, const bool is_shared=false)
Insert a copy of the image list list into the current image list, starting from position pos...
Definition: CImg.h:46257
CImgList< T > & clear()
Destructor .
Definition: CImg.h:44806
const CImg< T > & display_object3d(const char *const title, const CImg< tp > &vertices, const CImgList< tf > &primitives, const bool centering=true, const int render_static=4, const int render_motion=1, const bool is_double_sided=true, const float focale=700, const float light_x=0, const float light_y=0, const float light_z=-5e8f, const float specular_lightness=0.2f, const float specular_shininess=0.1f, const bool display_axes=true, float *const pose_matrix=0) const
Display object 3d in an interactive window .
Definition: CImg.h:41438
CImg< T > & operator=(const CImg< t > &img)
Copy an image into the current image instance.
Definition: CImg.h:10627
CImg< T > & map(const CImg< t > &colormap, const unsigned int boundary_conditions=0)
Map predefined colormap on the scalar (indexed) image instance.
Definition: CImg.h:19603
bool is_overlapped(const CImg< t > &img) const
Test if pixel buffers of instance and input images overlap.
Definition: CImg.h:13758
static CImg< T > get_load_yuv(const char *const filename, const unsigned int size_x, const unsigned int size_y=1, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z')
Load image sequence from a YUV file .
Definition: CImg.h:40336
CImgList< T > & assign(const CImg< t > &img, const bool is_shared=false)
Construct list from one image .
Definition: CImg.h:44883
CImg< T > & select(CImgDisplay &disp, const unsigned int feature_type=2, unsigned int *const XYZ=0)
Launch simple interface to select a shape from an image.
Definition: CImg.h:37077
CImg< T > & structure_tensors(const unsigned int scheme=2)
Compute the structure tensor field of an image.
Definition: CImg.h:27257
CImg< T > & object3dtoCImg3d(const CImgList< tp > &primitives, const CImgList< tc > &colors, const to &opacities, const bool full_check=true)
Convert 3d object into a CImg3d representation.
Definition: CImg.h:30215
CImgList< T > & swap(CImgList< T > &list)
Swap all fields between two list instances.
Definition: CImg.h:45056
T & atXYZC(const int x, const int y, const int z, const int c)
Access to a pixel value, using Neumann boundary conditions.
Definition: CImg.h:12482
CImg< T > get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5) const
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18280
static void wait_all()
Wait for any window event occuring in any opened CImgDisplay.
Definition: CImg.h:7409
CImgDisplay(const CImg< T > &img, const char *const title=0, const unsigned int normalization=3, const bool is_fullscreen=false, const bool is_closed=false)
Construct a display from an image.
Definition: CImg.h:6163
static CImg< T > get_load_cimg(const char *const filename, const char axis='z', const float align=0)
Load image from a .cimg[z] file .
Definition: CImg.h:39722
CImgDisplay & set_button()
Simulate a mouse button release event.
Definition: CImg.h:7132
CImg< T > & columns(const int x0, const int x1)
Return specified range of image columns.
Definition: CImg.h:23661
CImg< T > & vanvliet(const float sigma, const int order, const char axis='x', const bool boundary_conditions=true)
Van Vliet recursive Gaussian filter.
Definition: CImg.h:25771
CImg< T > & label(const bool is_high_connectivity=false, const Tfloat tolerance=0)
Label connected components.
Definition: CImg.h:19752
CImgDisplay & hide_mouse()
Hide mouse pointer.
Definition: CImg.h:7113
CImg< T > operator>(const char axis) const
Return image corresponding to the appending of all images of the instance list along specified axis...
Definition: CImg.h:45229
CImg< T > & operator/=(const CImg< t > &img)
In-place division operator.
Definition: CImg.h:11153
int width() const
Return the size of the list, i.e. the number of images contained in it.
Definition: CImg.h:45265
const unsigned int keyF7
Keycode for the F7 key (architecture-dependent).
Definition: CImg.h:2880
const CImg< T > & display(CImgDisplay &disp) const
Display image into a CImgDisplay window.
Definition: CImg.h:41075
static const CImg< Tuchar > & jet_LUT256()
Return colormap "jet", containing 256 colors entries in RGB.
Definition: CImg.h:19993
const CImg< T > & save_minc2(const char *const filename, const char *const imitate_file=0) const
Save image as a MINC2 file.
Definition: CImg.h:43334
CImgList< t > & move_to(CImgList< t > &list, const unsigned int pos)
Transfer the content of the list instance at a specified position in another list.
Definition: CImg.h:45039
CImg< T > get_autocrop(const T value, const char *const axes="czyx") const
Autocrop image region, regarding the specified background value .
Definition: CImg.h:23525
Tdouble variance_noise(const unsigned int variance_method=2) const
Return estimated variance of the noise.
Definition: CImg.h:16391
CImg< T > & draw_ellipse(const int x0, const int y0, const CImg< t > &tensor, const tc *const color, const float opacity=1)
Draw a filled 2d ellipse .
Definition: CImg.h:34162
const unsigned int key3
Keycode for the 3 key (architecture-dependent).
Definition: CImg.h:2889
CImgList< T > & images(const unsigned int pos0, const unsigned int pos1)
Return a sublist.
Definition: CImg.h:46382
CImg< unsigned long > get_label(const bool is_high_connectivity=false, const Tfloat tolerance=0) const
Label connected components .
Definition: CImg.h:19757
const unsigned int keyV
Keycode for the V key (architecture-dependent).
Definition: CImg.h:2930
static CImg< T > vector(const T &a0, const T &a1, const T &a2, const T &a3, const T &a4, const T &a5, const T &a6, const T &a7, const T &a8, const T &a9, const T &a10, const T &a11, const T &a12, const T &a13)
Return a 1x14 image containing specified values.
Definition: CImg.h:17950
CImg< T > get_transpose() const
Transpose the image, viewed as a matrix .
Definition: CImg.h:16930
CImg< Tfloat > operator+(const char *const expression) const
Addition operator.
Definition: CImg.h:10814
CImg< T > & load_inr(const char *const filename, float *const voxel_size=0)
Load image from an INRIMAGE-4 file.
Definition: CImg.h:39809
CImgList< T > & reverse()
Reverse list order.
Definition: CImg.h:46367
CImg< T > & fillC(const unsigned int x, const unsigned int y, const unsigned int z, const double a0,...)
Fill pixel values along the C-axis at a specified pixel position .
Definition: CImg.h:18772
const T & max() const
Return a reference to the maximum pixel value .
Definition: CImg.h:16144
CImg< T > get_warp(const CImg< t > &warp, const bool is_relative=false, const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const
Warp image content by a warping field .
Definition: CImg.h:22918
Tdouble sum() const
Return the sum of all the pixel values.
Definition: CImg.h:16286
CImg< T > & draw_spline(const int x0, const int y0, const int z0, const float u0, const float v0, const float w0, const int x1, const int y1, const int z1, const float u1, const float v1, const float w1, const tc *const color, const float opacity=1, const float precision=4, const unsigned int pattern=~0U, const bool init_hatch=true)
Draw a 3d spline .
Definition: CImg.h:31517
int mouse_x() const
Return X-coordinate of the mouse pointer.
Definition: CImg.h:6717
static CImg< T > vector(const T &a0, const T &a1, const T &a2, const T &a3)
Return a 1x4 image containing specified values.
Definition: CImg.h:17854
CImg< T > get_append(const char axis, const float align=0) const
Return a single image which is the appending of all images of the current CImgList instance...
Definition: CImg.h:46431
T & back()
Return a reference to the last pixel value.
Definition: CImg.h:12224
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 T val7, const T val8, const T val9, const T val10, const T val11, const T val12) const
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18492
CImg< Tfloat > get_asin() const
Compute the arcsine of each pixel value .
Definition: CImg.h:15442
const char * graphicsmagick_path(const char *const user_path=0, const bool reinit_path=false)
Get/set path to the GraphicsMagick's gm binary.
Definition: CImg.h:4881
T atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const
Access to pixel value with Neumann boundary conditions for the 3 first coordinates (pos...
Definition: CImg.h:45528
CImg< T > & load_other(const char *const filename)
Load image using various non-native ways.
Definition: CImg.h:40972
CImg< T > & operator*=(const t value)
In-place multiplication operator.
Definition: CImg.h:10973
const unsigned int keyCTRLRIGHT
Keycode for the CTRLRIGHT key (architecture-dependent).
Definition: CImg.h:2943
CImg< floatT > get_object3dtoCImg3d(const CImgList< tp > &primitives, const CImgList< tc > &colors, const to &opacities, const bool full_check=true) const
Convert 3d object into a CImg3d representation .
Definition: CImg.h:30244
static CImgList< T > get_load_gif_external(const char *const filename)
Load gif file, using ImageMagick or GraphicsMagick's external tools .
Definition: CImg.h:47827
CImg< _cimg_Tt > operator-(const t value) const
Substraction operator.
Definition: CImg.h:10945
CImg< T > & ror(const CImg< t > &img)
Compute the bitwise right rotation of each pixel value.
Definition: CImg.h:15890
CImg< T > & autocrop(const T value, const char *const axes="czyx")
Autocrop image region, regarding the specified background value.
Definition: CImg.h:23496
void strunescape(char *const str)
Replace escape sequences in C-strings by their binary ascii values.
Definition: CImg.h:4533
CImg< Tuchar > get_CMYtoRGB() const
Convert pixel values from CMY to RGB color spaces .
Definition: CImg.h:20479
T value_type
Pixel value type.
Definition: CImg.h:9367
CImgList< Tfloat > get_hessian(const char *const axes=0) const
Return image hessian.
Definition: CImg.h:27087
T & min()
Return a reference to the minimum pixel value of the instance list.
Definition: CImg.h:45939
const CImg< T > get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) const
Return a shared memory image referencing a range of slices of the image instance .
Definition: CImg.h:24123
const CImg< T > & front() const
Return reference to the first image of the list .
Definition: CImg.h:45351
CImg< Tfloat > get_laplacian() const
Compute image laplacian .
Definition: CImg.h:27219
CImgList(const CImg< t1 > &img1, const CImg< t2 > &img2, const CImg< t3 > &img3, const CImg< t4 > &img4, const bool is_shared=false)
Construct list from four images.
Definition: CImg.h:44639
bool is_shared() const
Test shared state of the pixel buffer.
Definition: CImg.h:13347
bool is_inf() const
Test if image instance contains a 'inf' value.
Definition: CImg.h:13364
double log2(const double x)
Return base-2 logarithm of a value.
Definition: CImg.h:4419
T minmod(const T a, const T b)
Return the min-mod of two values.
Definition: CImg.h:4414
static CImg< floatT > isosurface3d(CImgList< tf > &primitives, const tfunc &func, const float isovalue, const float x0, const float y0, const float z0, const float x1, const float y1, const float z1, const int size_x=32, const int size_y=32, const int size_z=32)
Compute isosurface of a function, as a 3d object.
Definition: CImg.h:29414
bool is_key_sequence(const unsigned int *const keycodes_sequence, const unsigned int length, const bool remove_sequence=false)
Return true if specified key sequence has been typed on the associated window, false otherwise...
Definition: CImg.h:6539
CImg< T > & draw_object3d(const float x0, const float y0, const float z0, const CImg< tp > &vertices, const CImgList< tf > &primitives, const CImgList< tc > &colors, const CImgList< to > &opacities, const unsigned int render_type=4, const bool is_double_sided=false, const float focale=700, const float lightx=0, const float lighty=0, const float lightz=-5e8, const float specular_lightness=0.2f, const float specular_shininess=0.1f)
Draw a 3d object .
Definition: CImg.h:35782
iterator begin()
Return iterator to the first image of the list.
Definition: CImg.h:45322
T & atNXY(const int pos, const int x, const int y, const int z, const int c, const T out_value)
Access to pixel value with Dirichlet boundary conditions for the 3 first coordinates (pos...
Definition: CImg.h:45500
CImg< T > & load_inr(std::FILE *const file, float *const voxel_size=0)
Load image from an INRIMAGE-4 file .
Definition: CImg.h:39819
CImg< T > & draw_fill(const int x, const int y, const int z, const tc *const color, const float opacity, CImg< t > &region, const float sigma=0, const bool is_high_connexity=false)
Draw filled 3d region with the flood fill algorithm.
Definition: CImg.h:35220
CImgDisplay & assign(const CImg< T > &img, const char *const title=0, const unsigned int normalization=3, const bool is_fullscreen=false, const bool is_closed=false)
Construct a display from an image .
Definition: CImg.h:6244
static CImgList< T > get_load_tiff(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1)
Load a multi-page TIFF file .
Definition: CImg.h:47956
CImg< T > & fill(const T val)
Fill all pixel values with specified value.
Definition: CImg.h:18172
const CImgList< T > & save_yuv(const char *const filename=0, const bool is_rgb=true) const
Save list as a YUV image sequence file.
Definition: CImg.h:48638
CImg< ulongT > get_histogram(const unsigned int nb_levels, const T min_value, const T max_value) const
Compute the histogram of pixel values .
Definition: CImg.h:19247
Tfloat linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const
Return pixel value, using linear interpolation and Neumann boundary conditions for the X...
Definition: CImg.h:12666
bool is_sameYC(const unsigned int size_y, const unsigned int size_c) const
Test if image height and spectrum are equal to specified values.
Definition: CImg.h:13512
CImg< T > & dilate(const CImg< t > &mask, const unsigned int boundary_conditions=1, const bool is_normalized=false)
Dilate image by a structuring element.
Definition: CImg.h:25090
const CImgList< T > & save_cimg(const char *const filename, const bool is_compressed=false) const
Save list into a .cimg file.
Definition: CImg.h:48709
CImg< T > & draw_rectangle(const int x0, const int y0, const int x1, const int y1, const tc *const color, const float opacity, const unsigned int pattern)
Draw a outlined 2d rectangle .
Definition: CImg.h:33945
CImg< T > & load_jpeg(const char *const filename)
Load image from a JPEG file.
Definition: CImg.h:38430
bool is_sameY(const unsigned int size_y) const
Test if image height is equal to specified value.
Definition: CImg.h:13395
void info()
Print informations about environement variables.
Definition: CImg.h:5576
CImgList< T > & assign(const unsigned int n, const unsigned int width, const unsigned int height, const unsigned int depth, const unsigned int spectrum, const T val)
Construct list containing images of specified size, and initialize pixel values . ...
Definition: CImg.h:44839
CImg< T > get_sequence(const T a0, const T a1) const
Fill image with a linear sequence of values .
Definition: CImg.h:16911
Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T out_value) const
Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate...
Definition: CImg.h:12834
const unsigned int keyK
Keycode for the K key (architecture-dependent).
Definition: CImg.h:2923
static CImg< T > get_load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1)
Load image from a RGB file .
Definition: CImg.h:39118
static CImg< T > get_load_bmp(std::FILE *const file)
Load image from a BMP file .
Definition: CImg.h:38252
CImg< T > get_ror(const unsigned int n=1) const
Compute the bitwise right rotation of each pixel value .
Definition: CImg.h:15830
CImg(const char *const filename)
Construct image from reading an image file.
Definition: CImg.h:9863
CImgDisplay & assign(const CImgList< T > &list, const char *const title=0, const unsigned int normalization=3, const bool is_fullscreen=false, const bool is_closed=false)
Construct a display from an image list .
Definition: CImg.h:6255
CImg< T > & draw_spline(const CImg< tp > &points, const CImg< tt > &tangents, const tc *const color, const float opacity=1, const bool is_closed_set=false, const float precision=4, const unsigned int pattern=~0U, const bool init_hatch=true)
Draw a set of consecutive splines.
Definition: CImg.h:31622
CImgList< T > get_remove(const unsigned int pos1, const unsigned int pos2) const
Remove all images between from indexes .
Definition: CImg.h:46337
T sqr(const T val)
Return square of a value.
Definition: CImg.h:4298
static CImg< T > vector(const T &a0, const T &a1, const T &a2, const T &a3, const T &a4)
Return a 1x5 image containing specified values.
Definition: CImg.h:17861
CImg< T > & draw_circle(const int x0, const int y0, int radius, const tc *const color, const float opacity, const unsigned int pattern)
Draw an outlined 2d circle.
Definition: CImg.h:34316
bool is_sameXZC(const unsigned int size_x, const unsigned int size_z, const unsigned int size_c) const
Test if image width, depth and spectrum are equal to specified values.
Definition: CImg.h:13580
static void save_empty_cimg(std::FILE *const file, const unsigned int nb, const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dc=1)
Save empty .cimg file with specified dimensions.
Definition: CImg.h:48907
CImg(const CImg< t > &img, const char *const dimensions, const T value)
Construct image with dimensions borrowed from another image and initialize pixel values.
Definition: CImg.h:10023
CImgList< T > & insert(const unsigned int n, const CImgList< t > &list, const unsigned int pos=~0U, const bool is_shared=false)
Insert n copies of the list list at position pos of the current list.
Definition: CImg.h:46278
const CImgList< T > & display(CImgDisplay &disp, const bool display_info, const char axis='x', const float align=0, unsigned int *const XYZ=0) const
Display the current CImgList instance in a new display window.
Definition: CImg.h:48028
CImg< T > & normalize()
Normalize multi-valued pixels of the image instance, with respect to their L2-norm.
Definition: CImg.h:19003
CImg< T > & xyYtoRGB()
Convert pixel values from xyY to RGB color spaces.
Definition: CImg.h:20759
static CImg< T > vector(const T &a0, const T &a1, const T &a2, const T &a3, const T &a4, const T &a5, const T &a6, const T &a7, const T &a8, const T &a9, const T &a10, const T &a11, const T &a12)
Return a 1x13 image containing specified values.
Definition: CImg.h:17937
CImg< T > & equalize(const unsigned int nb_levels)
Equalize histogram of pixel values .
Definition: CImg.h:19296
CImg< T > & draw_spline(const CImg< tp > &points, const tc *const color, const float opacity=1, const bool is_closed_set=false, const float precision=4, const unsigned int pattern=~0U, const bool init_hatch=true)
Draw a set of consecutive splines .
Definition: CImg.h:31672
CImg< _cimg_Tt > operator%(const CImg< t > &img) const
Modulo operator.
Definition: CImg.h:11281
CImg< T > & rol(const unsigned int n=1)
Compute the bitwise left rotation of each pixel value.
Definition: CImg.h:15727
const CImg< T > & SVD(CImg< t > &U, CImg< t > &S, CImg< t > &V, const bool sorting=true, const unsigned int max_iteration=40, const float lambda=0) const
Compute the SVD of the instance image, viewed as a general matrix.
Definition: CImg.h:17494
const CImg< T > & eigen(CImg< t > &val, CImg< t > &vec) const
Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix.
Definition: CImg.h:17206
CImg< Tuchar > get_CMYtoCMYK() const
Convert pixel values from CMY to CMYK color spaces .
Definition: CImg.h:20489
CImg< T > operator>>(const t value) const
Bitwise right shift operator.
Definition: CImg.h:11770
CImg< Tfloat > get_sinc() const
Compute the sinc of each pixel value .
Definition: CImg.h:15310
CImg< Tfloat > get_pow(const char *const expression) const
Raise each pixel value to a power, specified from an expression .
Definition: CImg.h:15695
CImg< _cimg_Tt > operator/(const CImg< t > &img) const
Division operator.
Definition: CImg.h:11182
CImg< T > get_erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const
Erode image by a rectangular structuring element of specified size .
Definition: CImg.h:25066
CImg< T > & fillY(const unsigned int x, const unsigned int z, const unsigned int c, const int a0,...)
Fill pixel values along the Y-axis at a specified pixel position.
Definition: CImg.h:18727
CImg< Tfloat > operator/(const char *const expression) const
Division operator.
Definition: CImg.h:11172
CImg< T > & discard(const CImg< t > &values)
Discard specified sequence of values in the image buffer.
Definition: CImg.h:18805
CImg< T > & norm(const int norm_type=2)
Compute L2-norm of each multi-valued pixel of the image instance.
Definition: CImg.h:19038
CImgList< T > & operator=(const char *const filename)
Construct list by reading the content of a file .
Definition: CImg.h:45169
int width() const
Return the number of image columns.
Definition: CImg.h:12008
CImgList< T > & push_front(const CImgList< t > &list)
Insert list at the front of the current list.
Definition: CImg.h:46573
CImgList< T > & assign(const unsigned int n, const unsigned int width, const unsigned int height, const unsigned int depth, const unsigned int spectrum, const double val0, const double val1,...)
Construct list with images of specified size, and initialize pixel values from a sequence of doubles ...
Definition: CImg.h:44860
CImg< T > get_autocrop(const T *const color=0, const char *const axes="zyx") const
Autocrop image region, regarding the specified background color .
Definition: CImg.h:23582
CImg< T > & draw_triangle(const int x0, const int y0, const int x1, const int y1, const int x2, const int y2, const tc1 *const color1, const tc2 *const color2, const tc3 *const color3, const float opacity=1)
Draw a color-interpolated 2d triangle.
Definition: CImg.h:32426
CImg< T > & cosh()
Compute the hyperbolic cosine of each pixel value.
Definition: CImg.h:15344
const CImg< T > get_shared_channels(const unsigned int c0, const unsigned int c1) const
Return a shared-memory image referencing a range of channels of the image instance ...
Definition: CImg.h:24167
bool is_sameYZC(const CImg< t > &img) const
Test if image height, depth and spectrum are the same as that of another image.
Definition: CImg.h:13606
CImg(const unsigned int size_x, const unsigned int size_y=1, const unsigned int size_z=1, const unsigned int size_c=1)
Construct image with specified size.
Definition: CImg.h:9490
static CImg< T > get_load_pnm(std::FILE *const file)
Load image from a PNM file .
Definition: CImg.h:38835
const CImgList< T > & save_cimg(const char *const filename, const unsigned int n0, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0) const
Insert the image instance into into an existing .cimg file, at specified coordinates.
Definition: CImg.h:48845
CImgList< T > & assign(const CImg< t1 > &img1, const CImg< t2 > &img2, const bool is_shared=false)
Construct list from two images .
Definition: CImg.h:44894
static void FFT(CImg< T > &real, CImg< T > &imag, const char axis, const bool is_invert=false)
Compute 1d Fast Fourier Transform, along a specified axis.
Definition: CImg.h:28505
const T * data() const
Return a pointer to the first pixel value .
Definition: CImg.h:12099
const unsigned int keyQ
Keycode for the Q key (architecture-dependent).
Definition: CImg.h:2902
static CImg< T > get_load_bmp(const char *const filename)
Load image from a BMP file .
Definition: CImg.h:38242
CImgList< T > & assign(const CImg< t1 > &img1, const CImg< t2 > &img2, const CImg< t3 > &img3, const CImg< t4 > &img4, const CImg< t5 > &img5, const CImg< t6 > &img6, const CImg< t7 > &img7, const CImg< t8 > &img8, const bool is_shared=false)
Construct list from eight images .
Definition: CImg.h:44969
CImg< T > operator~() const
Bitwise inversion operator.
Definition: CImg.h:11798
CImg< T > & asin()
Compute the arcsine of each pixel value.
Definition: CImg.h:15432
char * number_filename(const char *const filename, const int number, const unsigned int digits, char *const str)
Generate a numbered version of a filename.
Definition: CImg.h:5279
CImg< T > & ror(const char *const expression)
Compute the bitwise right rotation of each pixel value.
Definition: CImg.h:15838
CImg< T > & blur_bilateral(const CImg< t > &guide, const float sigma_s, const float sigma_r, const int bgrid_s=-33, const int bgrid_r=32, const bool interpolation_type=true)
Blur image using the joint bilateral filter.
Definition: CImg.h:26315
CImgList()
Default constructor.
Definition: CImg.h:44483
CImg< T > & RGBtoLab()
Convert pixel values from RGB to Lab color spaces.
Definition: CImg.h:20729
CImg< T > & operator>>=(const t value)
In-place bitwise right shift operator.
Definition: CImg.h:11699
const unsigned int keyF2
Keycode for the F2 key (architecture-dependent).
Definition: CImg.h:2875
const unsigned int keyF10
Keycode for the F10 key (architecture-dependent).
Definition: CImg.h:2883
char * load_network_external(const char *const filename, char *const filename_local)
Load file from network as a local temporary file.
Definition: CImg.h:5416
CImg< floatT > get_object3dtoCImg3d(const bool full_check=true) const
Convert 3d object into a CImg3d representation .
Definition: CImg.h:30396
CImg< T > & draw_polygon(const CImg< t > &points, const tc *const color, const float opacity=1)
Draw a filled 2d polygon.
Definition: CImg.h:33971
CImg< T > & assign(const CImg< t > &img)
Construct image copy .
Definition: CImg.h:10232
const unsigned int key8
Keycode for the 8 key (architecture-dependent).
Definition: CImg.h:2894
CImgList< T > get_shared_images(const unsigned int pos0, const unsigned int pos1)
Return a shared sublist.
Definition: CImg.h:46403
T atNX(const int pos, const int x, const int y, const int z, const int c, const T out_value) const
Access to pixel value with Dirichlet boundary conditions for the 2 first coordinates (pos...
Definition: CImg.h:45560
int xln(const int x)
Return 1 + log_10(x) of a value x.
Definition: CImg.h:4303
CImg< T > & log2()
Compute the base-2 logarithm of each pixel value.
Definition: CImg.h:15164
bool is_sameXY(const unsigned int size_x, const unsigned int size_y) const
Test if image width and height are equal to specified values.
Definition: CImg.h:13436
CImg< Tfloat > get_dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) const
Return minimal path in a graph, using the Dijkstra algorithm .
Definition: CImg.h:17799
unsigned int wait(const unsigned int milliseconds)
Wait for a given number of milliseconds since the last call to wait().
Definition: CImg.h:4135
CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, const double value0, const double value1,...)
Construct image with specified size and initialize pixel values from a sequence of doubles...
Definition: CImg.h:9711
const char * temporary_path(const char *const user_path=0, const bool reinit_path=false)
Get/set path to store temporary files.
Definition: CImg.h:4679
CImg< T > & draw_spline(const int x0, const int y0, const float u0, const float v0, const int x1, const int y1, const float u1, const float v1, const tc *const color, const float opacity=1, const float precision=0.25, const unsigned int pattern=~0U, const bool init_hatch=true)
Draw a 2d spline.
Definition: CImg.h:31480
const unsigned int keyPAUSE
Keycode for the PAUSE key (architecture-dependent).
Definition: CImg.h:2886
CImg< Tfloat > get_RGBtosRGB() const
Convert pixel values from RGB to sRGB color spaces .
Definition: CImg.h:20071
CImg< T > get_blur_patch(const float sigma_s, const float sigma_p, const unsigned int patch_size=3, const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true) const
Blur image using patch-based space .
Definition: CImg.h:26348
CImg< T > & log()
Compute the logarithm of each pixel value.
Definition: CImg.h:15142
bool is_object3d(const CImgList< tp > &primitives, const CImgList< tc > &colors, const to &opacities, const bool full_check=true, char *const error_message=0) const
Test if the set {*this,primitives,colors,opacities} defines a valid 3d object.
Definition: CImg.h:13778
static const CImg< Tuchar > & HSV_LUT256()
Return colormap "HSV", containing 256 colors entries in RGB.
Definition: CImg.h:19911
static CImg< T > diagonal(const T &a0, const T &a1, const T &a2, const T &a3)
Return a 4x4 diagonal matrix containing specified coefficients.
Definition: CImg.h:18097
CImg< T > & histogram(const unsigned int nb_levels, const T min_value, const T max_value)
Compute the histogram of pixel values.
Definition: CImg.h:19237
T atXY(const int x, const int y, const int z, const int c, const T out_value) const
Access to a pixel value, using Dirichlet boundary conditions for the X and Y coordinates ...
Definition: CImg.h:12374
CImgList< T > & remove(const unsigned int pos1, const unsigned int pos2)
Remove all images between from indexes.
Definition: CImg.h:46298
CImg< T > & draw_image(const int x0, const CImg< t > &sprite, const float opacity=1)
Draw an image .
Definition: CImg.h:34460
const unsigned int keyPAD7
Keycode for the PAD7 key (architecture-dependent).
Definition: CImg.h:2954
const unsigned int keyAPPLEFT
Keycode for the APPLEFT key (architecture-dependent).
Definition: CImg.h:2937
CImg< Tfloat > get_normalize(const T min_value, const T max_value) const
Linearly normalize pixel values .
Definition: CImg.h:18990
CImg< T > get_fill(const CImg< t > &values, const bool repeat_values=true) const
Fill sequentially pixel values according to the values found in another image .
Definition: CImg.h:18693
CImg(const CImgDisplay &disp)
Construct image from a display window.
Definition: CImg.h:10038
CImg< T > & ror(const unsigned int n=1)
Compute the bitwise right rotation of each pixel value.
Definition: CImg.h:15820
CImg< T > & draw_text(const int x0, const int y0, const char *const text, const tc1 *const foreground_color, const tc2 *const background_color, const float opacity=1, const unsigned int font_height=13,...)
Draw a text string .
Definition: CImg.h:34636
CImgList< T > get_split(const char axis, const int nb=0) const
Return a list where each image has been split along the specified axis .
Definition: CImg.h:46535
CImg< T > & draw_circle(const int x0, const int y0, int radius, const tc *const color, const float opacity=1)
Draw a filled 2d circle.
Definition: CImg.h:34278
const unsigned int keyPAD8
Keycode for the PAD8 key (architecture-dependent).
Definition: CImg.h:2955
static CImg< floatT > streamline(const char *const expression, const float x, const float y, const float z, const float L=256, const float dl=0.1f, const unsigned int interpolation_type=2, const bool is_backward_tracking=true, const bool is_oriented_only=false, const float x0=0, const float y0=0, const float z0=0, const float x1=0, const float y1=0, const float z1=0)
Return stream line of a 3d vector field .
Definition: CImg.h:23911
CImg< Tfloat > get_RGBtoHSI() const
Convert pixel values from RGB to HSI color spaces .
Definition: CImg.h:20270
CImg< T > & operator>>=(const char *const expression)
In-place bitwise right shift operator.
Definition: CImg.h:11712
bool is_sameYZC(const unsigned int size_y, const unsigned int size_z, const unsigned int size_c) const
Test if image height, depth and spectrum are equal to specified values.
Definition: CImg.h:13597
const unsigned int keyF8
Keycode for the F8 key (architecture-dependent).
Definition: CImg.h:2881
const char * split_filename(const char *const filename, char *const body=0)
Split filename into two C-strings body and extension.
Definition: CImg.h:5266
static CImg< T > get_load_off(CImgList< tf > &primitives, CImgList< tc > &colors, std::FILE *const file)
Load 3d object from a .OFF file .
Definition: CImg.h:40384
const unsigned int keyR
Keycode for the R key (architecture-dependent).
Definition: CImg.h:2905
CImg< Tfloat > get_atan() const
Compute the arctangent of each pixel value .
Definition: CImg.h:15464
void warn(const char *const format,...)
Display a warning message on the default output stream.
Definition: CImg.h:3883
int window_width() const
Return width of the associated window.
Definition: CImg.h:6682
CImg< Tfloat > get_deriche(const float sigma, const int order=0, const char axis='x', const bool boundary_conditions=true) const
Apply recursive Deriche filter .
Definition: CImg.h:25667
CImg< T > & haar(const char axis, const bool invert=false, const unsigned int nb_scales=1)
Compute Haar multiscale wavelet transform.
Definition: CImg.h:28227
unsigned long time()
Return the value of a system timer, with a millisecond precision.
Definition: CImg.h:4068
CImg< T > & HSLtoRGB()
Convert pixel values from HSL to RGB color spaces.
Definition: CImg.h:20203
CImg< T > operator<<(const t value) const
Bitwise left shift operator.
Definition: CImg.h:11670
CImg< T > & load_dlm(std::FILE *const file)
Load image from a DLM file .
Definition: CImg.h:38189
CImg< T > & crop(const int x0, const int y0, const int x1, const int y1, const bool boundary_conditions=false)
Crop image region .
Definition: CImg.h:23472
CImg< T > & assign(const char *const filename)
Construct image from reading an image file .
Definition: CImg.h:10223
CImg< T > get_shared_slice(const unsigned int z0, const unsigned int c0=0)
Return a shared-memory image referencing one slice of the image instance.
Definition: CImg.h:24140
CImg< T > get_columns(const int x0, const int x1) const
Return specified range of image columns .
Definition: CImg.h:23666
CImg< T > & fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const double a0,...)
Fill pixel values along the Z-axis at a specified pixel position .
Definition: CImg.h:18752
CImg< T > & load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1)
Load image from a RGBA file.
Definition: CImg.h:39169
CImgList< T > & load(const char *const filename)
Load a list from a file.
Definition: CImg.h:46870
CImg< T > * data(const unsigned int l)
Return pointer to the pos-th image of the list.
Definition: CImg.h:45309
CImgList(const unsigned int n, const unsigned int width, const unsigned int height, const unsigned int depth, const unsigned int spectrum, const double val0, const double val1,...)
Construct list containing images of specified size, and initialize pixel values from a sequence of do...
Definition: CImg.h:44572
CImgList< T > & operator,(const CImgList< t > &list) const
Return a copy of the list instance, where all elements of input list have been inserted at the end ...
Definition: CImg.h:45220
CImg< T > & draw_quiver(const CImg< t1 > &flow, const CImg< t2 > &color, const float opacity=1, const unsigned int sampling=25, const float factor=-20, const bool is_arrow=true, const unsigned int pattern=~0U)
Draw a 2d vector field, using a field of colors.
Definition: CImg.h:34765
CImg< T > & draw_triangle(const int x0, const int y0, const int x1, const int y1, const int x2, const int y2, const tc *const color, const float opacity, const unsigned int pattern)
Draw a outlined 2d triangle.
Definition: CImg.h:32105
CImg< T > & operator=(const char *const expression)
Assign pixels values from a specified expression.
Definition: CImg.h:10609
CImg< T > & unroll(const char axis)
Unroll pixel values along specified axis.
Definition: CImg.h:22615
CImg< T > get_unroll(const char axis) const
Unroll pixel values along specified axis .
Definition: CImg.h:22627
CImg< Tuchar > get_xyYtoRGB() const
Convert pixel values from xyY to RGB color spaces .
Definition: CImg.h:20764
CImgList< T > & load_tiff(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1)
Load images from a TIFF file.
Definition: CImg.h:47910
CImg< T > & load_png(const char *const filename)
Load image from a PNG file.
Definition: CImg.h:38628
const CImg< T > & save_pfm(std::FILE *const file) const
Save image as a PFM file .
Definition: CImg.h:43003
unsigned int & exception_mode(const unsigned int mode)
Set current exception mode.
Definition: CImg.h:2131
const CImg< T > & save_inr(std::FILE *const file, const float *const voxel_size=0) const
Save image as an INRIMAGE-4 file .
Definition: CImg.h:43529
bool is_sameXY(const CImg< t > &img) const
Test if image width and height are the same as that of another image.
Definition: CImg.h:13445
static CImg< T > string(const char *const str, const bool is_last_zero=true, const bool is_shared=false)
Return an image containing the ascii codes of the specified string.
Definition: CImg.h:17809
CImg< T > & load_analyze(const char *const filename, float *const voxel_size=0)
Load image from an ANALYZE7.5/NIFTI file.
Definition: CImg.h:39569
CImg< Tfloat > get_max(const char *const expression) const
Pointwise max operator between an image and an expression .
Definition: CImg.h:16099
T * data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0)
Return a pointer to a located pixel value.
Definition: CImg.h:12132
const unsigned int keyESC
Keycode for the ESC key (architecture-dependent).
Definition: CImg.h:2873
CImg< T > & load_imagemagick_external(const char *const filename)
Load image using ImageMagick's external tool 'convert'.
Definition: CImg.h:40736
CImgList< T > & assign(const unsigned int n, const CImg< t > &img, const bool is_shared=false)
Construct list containing copies of an input image .
Definition: CImg.h:44872
static CImg< T > get_load_jpeg(const char *const filename)
Load image from a JPEG file .
Definition: CImg.h:38435
const unsigned int keyB
Keycode for the B key (architecture-dependent).
Definition: CImg.h:2931
T & atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T out_value)
Access to pixel value with Dirichlet boundary conditions.
Definition: CImg.h:45390
T & atNXYZ(const int pos, const int x, const int y, const int z, const int c=0)
Access to pixel value with Neumann boundary conditions for the 4 first coordinates (pos...
Definition: CImg.h:45463
T & atNXY(const int pos, const int x, const int y, const int z=0, const int c=0)
Access to pixel value with Neumann boundary conditions for the 3 first coordinates (pos...
Definition: CImg.h:45518
const T & min_max(t &max_val) const
Return a reference to the minimum pixel value as well as the maximum pixel value .
Definition: CImg.h:16178
const CImg< T > & display_object3d(CImgDisplay &disp, const CImg< tp > &vertices, const CImgList< tf > &primitives, const bool centering=true, const int render_static=4, const int render_motion=1, const bool is_double_sided=true, const float focale=700, const float light_x=0, const float light_y=0, const float light_z=-5e8f, const float specular_lightness=0.2f, const float specular_shininess=0.1f, const bool display_axes=true, float *const pose_matrix=0) const
Display object 3d in an interactive window .
Definition: CImg.h:41420
CImg< T > & operator+=(const char *const expression)
In-place addition operator.
Definition: CImg.h:10690
float frames_per_second()
Return the current refresh rate, in frames per second.
Definition: CImg.h:6868
CImg< Tuchar > get_YUVtoRGB() const
Convert pixel values from YUV to RGB color spaces .
Definition: CImg.h:20423
CImgList< T > & FFT(const bool invert=false)
Compute a n-d Fast Fourier Transform.
Definition: CImg.h:49205
CImg< Tfloat > get_log10() const
Compute the base-10 logarithm of each pixel value .
Definition: CImg.h:15196
CImg< T > & load_pnm(std::FILE *const file)
Load image from a PNM file .
Definition: CImg.h:38830
CImg< T > & draw_object3d(const float x0, const float y0, const float z0, const CImg< tp > &vertices, const CImgList< tf > &primitives, const CImgList< tc > &colors, const CImg< to > &opacities, const unsigned int render_type, const bool is_double_sided, const float focale, const float lightx, const float lighty, const float lightz, const float specular_lightness, const float specular_shininess, CImg< tz > &zbuffer)
Draw a 3d object .
Definition: CImg.h:35736
unsigned int button() const
Return current state of the mouse buttons.
Definition: CImg.h:6757
long offset(const int x, const int y=0, const int z=0, const int c=0) const
Return the offset to a located pixel value, with respect to the beginning of the pixel buffer...
Definition: CImg.h:12158
bool contains(const T &pixel) const
Test if pixel value is inside image bounds.
Definition: CImg.h:13733
CImgList< T > get_insert(const unsigned int n, const CImgList< t > &list, const unsigned int pos=~0U, const bool is_shared=false) const
Insert n copies of the list list at position pos of the current list .
Definition: CImg.h:46288
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 T val7, const T val8, const T val9, const T val10, const T val11, const T val12, const T val13) const
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18529
CImg< T > get_max(const T val) const
Pointwise max operator between instance image and a value .
Definition: CImg.h:16022
CImg< T > & draw_triangle(CImg< tz > &zbuffer, const int x0, const int y0, const float z0, const int x1, const int y1, const float z1, const int x2, const int y2, const float z2, const CImg< tc > &texture, const int tx0, const int ty0, const int tx1, const int ty1, const int tx2, const int ty2, const CImg< tl > &light, const int lx0, const int ly0, const int lx1, const int ly1, const int lx2, const int ly2, const float opacity=1)
Draw a textured Phong-shaded 2d triangle, with perspective correction and z-buffering.
Definition: CImg.h:33691
CImgList< T > & assign(const CImgList< t > &list, const bool is_shared=false)
Construct list as a copy of an existing list and force the shared state of the list elements ...
Definition: CImg.h:44984
CImgDisplay(const CImgList< T > &list, const char *const title=0, const unsigned int normalization=3, const bool is_fullscreen=false, const bool is_closed=false)
Construct a display from an image list.
Definition: CImg.h:6186
CImg< T > & CMYKtoCMY()
Convert pixel values from CMYK to CMY color spaces.
Definition: CImg.h:20515
CImg< T > & pow(const CImg< t > &img)
Raise each pixel value to a power, pointwisely specified from another image.
Definition: CImg.h:15704
CImg< T > & blur_bilateral(const CImg< t > &guide, const float sigma_x, const float sigma_y, const float sigma_z, const float sigma_r, const int bgrid_x, const int bgrid_y, const int bgrid_z, const int bgrid_r, const bool interpolation_type=true)
Blur image, with the joint bilateral filter.
Definition: CImg.h:26203
const unsigned int keyPAD4
Keycode for the PAD4 key (architecture-dependent).
Definition: CImg.h:2951
int window_height() const
Return height of the associated window.
Definition: CImg.h:6691
CImg< T > operator&(const t value) const
Bitwise AND operator.
Definition: CImg.h:11363
CImgDisplay & close()
Close (visible) associated window and make it disappear from the screen.
Definition: CImg.h:6940
static CImg< T > get_load_camera(const unsigned int camera_index=0, const unsigned int skip_frames=0, const bool release_camera=true, const unsigned int capture_width=0, const unsigned int capture_height=0)
Load image from a camera stream, using OpenCV .
Definition: CImg.h:40962
CImg< T > & draw_text(const int x0, const int y0, const char *const text, const tc1 *const foreground_color, const tc2 *const background_color, const float opacity, const CImgList< t > &font,...)
Draw a text string.
Definition: CImg.h:34583
CImg< T > * iterator
Simple iterator type, to loop through each image of a list.
Definition: CImg.h:44377
CImg< T > & blur_patch(const float sigma_s, const float sigma_p, const unsigned int patch_size=3, const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true)
Blur image using patch-based space.
Definition: CImg.h:26341
CImg< T > get_ror(const char *const expression) const
Compute the bitwise right rotation of each pixel value .
Definition: CImg.h:15881
CImg< unsigned long > get_label(const CImg< t > &connectivity_mask, const Tfloat tolerance=0) const
Label connected components .
Definition: CImg.h:19798
CImg< intT > get_select(CImgDisplay &disp, const bool feature_type=true, const char axis='x', const float align=0) const
Display a simple interactive interface to select images or sublists.
Definition: CImg.h:46614
static CImg< T > get_load_inr(std::FILE *const file, float *voxel_size=0)
Load image from an INRIMAGE-4 file .
Definition: CImg.h:39824
const CImg< T > & save_medcon_external(const char *const filename) const
Save image as a Dicom file.
Definition: CImg.h:44247
CImg< T > get_mirror(const char *const axes) const
Mirror image content along specified axes .
Definition: CImg.h:22182
CImg< T > & YCbCrtoRGB()
Convert pixel values from RGB to YCbCr color spaces.
Definition: CImg.h:20346
Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T out_value, const Tfloat min_value, const Tfloat max_value) const
Return damped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X...
Definition: CImg.h:12944
const unsigned int keyEND
Keycode for the END key (architecture-dependent).
Definition: CImg.h:2913
const unsigned int keyE
Keycode for the E key (architecture-dependent).
Definition: CImg.h:2904
CImg< T > & cos()
Compute the cosine of each pixel value.
Definition: CImg.h:15255
T abs(const T a)
Return absolute value of a value.
Definition: CImg.h:4268
const CImgDisplay & snapshot(CImg< T > &img) const
Take a snapshot of the associated window content.
Definition: CImg.h:7441
const unsigned int keyARROWRIGHT
Keycode for the ARROWRIGHT key (architecture-dependent).
Definition: CImg.h:2946
bool strpare(char *const str, const char delimiter=' ', const bool is_symmetric=false, const bool is_iterative=false)
Remove delimiters on the start and/or end of a C-string.
Definition: CImg.h:4513
const T * const_iterator
Simple const iterator type, to loop through each pixel value of a const image instance.
Definition: CImg.h:9357
const CImg< T > & save_pnk(std::FILE *const file) const
Save image as a PNK file .
Definition: CImg.h:42934
CImg< T > get_vector() const
Unroll pixel values along axis y .
Definition: CImg.h:16790
CImg< Tfloat > get_pow(const double p) const
Raise each pixel value to a specified power .
Definition: CImg.h:15645
bool is_sameXZ(const unsigned int size_x, const unsigned int size_z) const
Test if image width and depth are equal to specified values.
Definition: CImg.h:13461
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)
Construct image with specified size and initialize pixel values .
Definition: CImg.h:10100
bool containsN(const int n) const
Test if list contains image with specified indice.
Definition: CImg.h:45821
static CImg< T > get_load_gzip_external(const char *const filename)
Load gzipped image file, using external tool 'gunzip' .
Definition: CImg.h:40728
CImg< intT > get_select(CImgDisplay &disp, const unsigned int feature_type=2, unsigned int *const XYZ=0) const
Simple interface to select a shape from an image .
Definition: CImg.h:37089
CImg< T > & column(const int x0)
Return specified image column .
Definition: CImg.h:23652
const CImg< T > get_shared_points(const unsigned int x0, const unsigned int x1, const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) const
Return a shared-memory image referencing a range of pixels of the image instance .
Definition: CImg.h:24043
const T & back() const
Return a reference to the last pixel value .
Definition: CImg.h:12229
CImgDisplay & display(const CImg< T > &img)
Display image on associated window.
Definition: CImg.h:6895
const char * what() const
Return a C-string containing the error message associated to the thrown exception.
Definition: CImg.h:2233
const char * ffmpeg_path(const char *const user_path=0, const bool reinit_path=false)
Get/set path to the FFMPEG's ffmpeg binary.
Definition: CImg.h:5043
CImg< T > get_dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const
Dilate image by a rectangular structuring element of specified size .
Definition: CImg.h:25355
CImg< T > get_crop(const int x0, const int y0, const int z0, const int c0, const int x1, const int y1, const int z1, const int c1, const bool boundary_conditions=false) const
Crop image region .
Definition: CImg.h:23437
unsigned int normalization() const
Return normalization type of the display.
Definition: CImg.h:6664
static CImg< T > get_load_pfm(const char *const filename)
Load image from a PFM file .
Definition: CImg.h:39027
CImg< Tuchar > get_XYZtoRGB() const
Convert pixel values from XYZ_709 to RGB color spaces .
Definition: CImg.h:20598
static CImg< T > get_load_cimg(std::FILE *const file, const unsigned int n0, const unsigned int n1, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0, const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1, const char axis='z', const float align=0)
Load sub-images of a .cimg file .
Definition: CImg.h:39794
CImgList< T > get_images(const unsigned int pos0, const unsigned int pos1) const
Return a sublist .
Definition: CImg.h:46387
T atX(const int x, const int y=0, const int z=0, const int c=0) const
Access to a pixel value, using Neumann boundary conditions for the X-coordinate . ...
Definition: CImg.h:12353
CImg< T > & shift_object3d(const float tx, const float ty=0, const float tz=0)
Shift 3d object's vertices.
Definition: CImg.h:28841
CImg< T > & draw_text(const int x0, const int y0, const char *const text, const int, const tc *const background_color, const float opacity=1, const unsigned int font_height=13,...)
Draw a text string .
Definition: CImg.h:34663
T atXYZC(const int x, const int y, const int z, const int c, const T out_value) const
Access to a pixel value, using Dirichlet boundary conditions .
Definition: CImg.h:12470
CImg< Tfloat > get_vanvliet(const float sigma, const int order, const char axis='x', const bool boundary_conditions=true) const
Blur image using Van Vliet recursive Gaussian filter. .
Definition: CImg.h:25823
void swap(T &a, T &b)
Exchange values of variables a and b.
Definition: CImg.h:3951
static CImg< T > vector(const T &a0)
Return a 1x1 image containing specified value.
Definition: CImg.h:17818
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
Sharpen image .
Definition: CImg.h:26895
T atXYZ(const int x, const int y, const int z, const int c, const T out_value) const
Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates ...
Definition: CImg.h:12421
double rand()
Return a random variable between [0,1] with respect to an uniform distribution.
Definition: CImg.h:4201
const unsigned int keyPADADD
Keycode for the PADADD key (architecture-dependent).
Definition: CImg.h:2957
CImgDisplay & set_button(const unsigned int button, const bool is_pressed=true)
Simulate a mouse button press or release event.
Definition: CImg.h:7148
bool is_sameZC(const unsigned int size_z, const unsigned int size_c) const
Test if image depth and spectrum are equal to specified values.
Definition: CImg.h:13529
CImgList< Tfloat > get_gradient(const char *const axes=0, const int scheme=3) const
Return image gradient.
Definition: CImg.h:26912
CImg< T > & draw_image(const CImg< ti > &sprite, const CImg< tm > &mask, const float opacity=1, const float mask_max_value=1)
Draw an image.
Definition: CImg.h:34567
bool is_empty() const
Return true if display is empty, false otherwise.
Definition: CImg.h:6358
const unsigned int keyALT
Keycode for the ALT key (architecture-dependent).
Definition: CImg.h:2938
bool is_sameYZ(const CImg< t > &img) const
Test if image height and depth are the same as that of another image.
Definition: CImg.h:13504
const char * file_type(std::FILE *const file, const char *const filename)
Try to guess format from an image file.
Definition: CImg.h:5296
CImg< T > & vector()
Unroll pixel values along axis y.
Definition: CImg.h:16785
static CImg< T > get_load_other(const char *const filename)
Load image using various non-native ways .
Definition: CImg.h:41011
const unsigned int keyF12
Keycode for the F12 key (architecture-dependent).
Definition: CImg.h:2885
CImg< T > & RGBtoHSV()
Convert pixel values from RGB to HSV color spaces.
Definition: CImg.h:20076
Tdouble variance(const unsigned int variance_method=1) const
Return the variance of the pixel values.
Definition: CImg.h:16320
static CImg< floatT > streamline(const tfunc &func, const float x, const float y, const float z, const float L=256, const float dl=0.1f, const unsigned int interpolation_type=2, const bool is_backward_tracking=false, const bool is_oriented_only=false, const float x0=0, const float y0=0, const float z0=0, const float x1=0, const float y1=0, const float z1=0)
Return stream line of a 3d vector field.
Definition: CImg.h:23801
T value_type
Pixel value type.
Definition: CImg.h:44396
bool is_sameC(const unsigned int size_c) const
Test if image spectrum is equal to specified value.
Definition: CImg.h:13422
CImg< T > & set_linear_atXY(const T &value, const float fx, const float fy=0, const int z=0, const int c=0, const bool is_added=false)
Set pixel value, using linear interpolation for the X and Y-coordinates.
Definition: CImg.h:13201
const CImg< T > & save_pandore(std::FILE *const file, const unsigned int colorspace=0) const
Save image as a Pandore-5 file .
Definition: CImg.h:43672
CImg< T > get_resize(const CImg< t > &src, const int interpolation_type=1, const unsigned int boundary_conditions=0, const float centering_x=0, const float centering_y=0, const float centering_z=0, const float centering_c=0) const
Resize image to dimensions of another image .
Definition: CImg.h:21920
CImg< T > get_channels(const int c0, const int c1) const
Return specified range of image channels.
Definition: CImg.h:23742
CImg< _cimg_Tt > operator*(const t value) const
Multiplication operator.
Definition: CImg.h:11048
CImg< T > & projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0)
Construct a 2d representation of a 3d image, with XY,XZ and YZ views .
Definition: CImg.h:23413
T & atX(const int x, const int y=0, const int z=0, const int c=0)
Access to a pixel value, using Neumann boundary conditions for the X-coordinate.
Definition: CImg.h:12340
CImg< T > & rand(const T val_min, const T val_max)
Fill image with random values in specified range.
Definition: CImg.h:18850
CImg< T > & draw_image(const int x0, const int y0, const CImg< t > &sprite, const float opacity=1)
Draw an image .
Definition: CImg.h:34453
CImg< T > & draw_object3d(const float x0, const float y0, const float z0, const CImg< tp > &vertices, const CImgList< tf > &primitives, const CImgList< tc > &colors, const CImg< to > &opacities, const unsigned int render_type=4, const bool is_double_sided=false, const float focale=700, const float lightx=0, const float lighty=0, const float lightz=-5e8, const float specular_lightness=0.2f, const float specular_shininess=0.1f)
Draw a 3d object.
Definition: CImg.h:35722
const CImg< T > * data(const unsigned int l) const
Return pointer to the pos-th image of the list .
Definition: CImg.h:45314
CImg(const CImg< t > &img, const char *const dimensions)
Construct image with dimensions borrowed from another image.
Definition: CImg.h:10007
static CImg< T > rotation_matrix(const float x, const float y, const float z, const float w, const bool is_quaternion=false)
Return a 3x3 rotation matrix along the (x,y,z)-axis with an angle w.
Definition: CImg.h:18135
const unsigned int keyARROWUP
Keycode for the ARROWUP key (architecture-dependent).
Definition: CImg.h:2935
CImg< T > & fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, const T val7, const T val8, const T val9, const T val10, const T val11, const T val12, const T val13, const T val14)
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18537
CImgDisplay & operator=(const CImgList< t > &list)
Display list of images on associated window.
Definition: CImg.h:6328
const CImg< T > get_shared_channel(const unsigned int c0) const
Return a shared-memory image referencing one channel of the image instance .
Definition: CImg.h:24188
CImg< Tuchar > get_RGBtoCMY() const
Convert pixel values from RGB to CMY color spaces .
Definition: CImg.h:20451
CImg< T > & CImg3dtoobject3d(CImgList< tp > &primitives, CImgList< tc > &colors, CImgList< to > &opacities, const bool full_check=true)
Convert CImg3d representation into a 3d object.
Definition: CImg.h:30411
T & atX(const int x, const int y, const int z, const int c, const T out_value)
Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate.
Definition: CImg.h:12313
CImgList< T > & assign(const CImg< t1 > &img1, const CImg< t2 > &img2, const CImg< t3 > &img3, const CImg< t4 > &img4, const CImg< t5 > &img5, const CImg< t6 > &img6, const bool is_shared=false)
Construct list from six images .
Definition: CImg.h:44942
Tdouble PSNR(const CImg< t > &img, const Tdouble max_value=255) const
Compute the PSNR (Peak Signal-to-Noise Ratio) between two images.
Definition: CImg.h:16484
bool is_CImg3d(const bool full_check=true, char *const error_message=0) const
Test if image instance represents a valid serialization of a 3d object.
Definition: CImg.h:13932
static CImg< T > get_load_cimg(const char *const filename, const unsigned int n0, const unsigned int n1, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0, const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1, const char axis='z', const float align=0)
Load sub-images of a .cimg file .
Definition: CImg.h:39769
CImg< T > & draw_point(const int x0, const int y0, const int z0, const tc *const color, const float opacity=1)
Draw a 3d point.
Definition: CImg.h:30565
const CImg< T > & save_ascii(const char *const filename) const
Save image as an ascii file.
Definition: CImg.h:42186
T sign(const T x)
Return the sign of a value.
Definition: CImg.h:4351
const unsigned int keyP
Keycode for the P key (architecture-dependent).
Definition: CImg.h:2911
T atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T out_value) const
Access to pixel value with Dirichlet boundary conditions .
Definition: CImg.h:45395
CImg< T > & fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6)
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18285
CImg< T > & sqrt()
Compute the square root of each pixel value.
Definition: CImg.h:15099
CImg< Tfloat > get_distance_eikonal(const unsigned int nb_iterations, const float band_size=0, const float time_step=0.5f) const
Compute distance function to 0-valued isophotes, using the Eikonal PDE .
Definition: CImg.h:28216
T & max_min(t &min_val)
Return a reference to the minimum pixel value of the instance list and return the minimum value as we...
Definition: CImg.h:46053
const CImg< T > & operator()(const unsigned int pos) const
Return a reference to one image of the list.
Definition: CImg.h:45103
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 T val7) const
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18333
CImg< Tfloat > get_sRGBtoRGB() const
Convert pixel values from sRGB to RGB color spaces .
Definition: CImg.h:20054
static const char * pixel_type()
Return the type of image pixel values as a C string.
Definition: CImg.h:11992
static CImg< T > matrix(const T &a0, const T &a1, const T &a2, const T &a3, const T &a4, const T &a5, const T &a6, const T &a7, const T &a8, const T &a9, const T &a10, const T &a11, const T &a12, const T &a13, const T &a14, const T &a15)
Return a 4x4 matrix containing specified coefficients.
Definition: CImg.h:18035
static CImg< T > get_load_inr(const char *const filename, float *const voxel_size=0)
Load image from an INRIMAGE-4 file .
Definition: CImg.h:39814
CImg< T > & operator+=(const t value)
In-place addition operator.
Definition: CImg.h:10673
static CImg< T > get_load_off(CImgList< tf > &primitives, CImgList< tc > &colors, const char *const filename)
Load 3d object from a .OFF file .
Definition: CImg.h:40372
const CImg< T > & save_raw(std::FILE *const file, const bool is_multiplexed=false) const
Save image as a raw data file .
Definition: CImg.h:43872
iterator begin()
Return a CImg::iterator pointing to the first pixel value.
Definition: CImg.h:12168
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 T val7, const T val8, const T val9, const T val10) const
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18425
CImg< T > & assign()
Construct empty image .
Definition: CImg.h:10058
CImgDisplay & wait(const unsigned int milliseconds)
Wait for a given number of milliseconds since the last call to wait().
Definition: CImg.h:7308
CImg< T > & resize_halfXY()
Resize image to half-size along XY axes, using an optimized filter.
Definition: CImg.h:21956
CImg< T > & dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1)
Dilate image by a rectangular structuring element of specified size.
Definition: CImg.h:25219
CImg< T > & watershed(const CImg< t > &priority, const bool fill_lines=true)
Compute watershed transform.
Definition: CImg.h:25380
CImg< T > & sqr()
Compute the square value of each pixel value.
Definition: CImg.h:15072
Class representing an image (up to 4 dimensions wide), each pixel being of type T.
Definition: CImg.h:2063
Tfloat linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const
Return pixel value, using linear interpolation and Neumann boundary conditions for the X and Y-coordi...
Definition: CImg.h:12601
CImgList< T > & load_gzip_external(const char *const filename)
Load a gzipped list, using external tool 'gunzip'.
Definition: CImg.h:47835
int strncasecmp(const char *const str1, const char *const str2, const int l)
Compare the first l characters of two C-strings, ignoring the case.
Definition: CImg.h:4481
const unsigned int keyA
Keycode for the A key (architecture-dependent).
Definition: CImg.h:2916
T & min_max(t &max_val)
Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as w...
Definition: CImg.h:46005
double sinc(const double x)
Return the sinc of a given value.
Definition: CImg.h:4364
CImgList< T > & assign(const CImgDisplay &disp)
Construct list from the content of a display window .
Definition: CImg.h:45011
CImg< T > get_crop(const int x0, const int y0, const int z0, const int x1, const int y1, const int z1, const bool boundary_conditions=false) const
Crop image region .
Definition: CImg.h:23465
CImgDisplay & resize(const int width, const int height, const bool force_redraw=true)
Resize display to the specified size.
Definition: CImg.h:6979
static CImg< floatT > isosurface3d(CImgList< tf > &primitives, const char *const expression, const float isovalue, const float x0, const float y0, const float z0, const float x1, const float y1, const float z1, const int dx=32, const int dy=32, const int dz=32)
Compute isosurface of a function, as a 3d object .
Definition: CImg.h:29830
CImg< T > & autocrop(const T *const color=0, const char *const axes="zyx")
Autocrop image region, regarding the specified background color.
Definition: CImg.h:23534
Tdouble trace() const
Compute the trace of the image, viewed as a matrix.
Definition: CImg.h:16613
CImg< Tfloat > get_XYZtoLab() const
Convert pixel values from XYZ_709 to Lab color spaces .
Definition: CImg.h:20633
const unsigned int keyS
Keycode for the S key (architecture-dependent).
Definition: CImg.h:2917
const CImg< T > & save_magick(const char *const filename, const unsigned int bytes_per_pixel=0) const
Save image, using built-in ImageMagick++ library.
Definition: CImg.h:42511
CImg< charT > value_string(const char separator=',', const unsigned int max_size=0) const
Return a C-string containing a list of all values of the image instance.
Definition: CImg.h:13311
CImg< T > operator-() const
Replace each pixel by its opposite value.
Definition: CImg.h:10935
CImg< T > & load_pnm(const char *const filename)
Load image from a PNM file.
Definition: CImg.h:38820
CImg< Tfloat > get_abs() const
Compute the absolute value of each pixel value .
Definition: CImg.h:15217
const CImg< T > get_shared() const
Return a shared-memory version of the image instance .
Definition: CImg.h:24198
Tfloat linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const
Return pixel value, using linear interpolation and Neumann boundary conditions for the X-coordinate...
Definition: CImg.h:12552
const CImgList< T > get_shared() const
Return a list with elements being shared copies of images in the list instance .
Definition: CImg.h:44784
CImg< T > & assign(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, const double value0, const double value1,...)
Construct image with specified size and initialize pixel values from a sequence of doubles ...
Definition: CImg.h:10121
CImg< T > & resize_object3d(const float sx, const float sy=-100, const float sz=-100)
Resize 3d object.
Definition: CImg.h:28886
static void save_empty_cimg(const char *const filename, const unsigned int nb, const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dc=1)
Save empty (non-compressed) .cimg file with specified dimensions.
Definition: CImg.h:48891
CImgList< T > get_insert(const CImgList< t > &list, const unsigned int pos=~0U, const bool is_shared=false) const
Insert a copy of the image list list into the current image list, starting from position pos ...
Definition: CImg.h:46266
bool contains(const T &pixel, t &n, t &x) const
Test if one of the image list contains the specified referenced value.
Definition: CImg.h:45880
unsigned int key(const unsigned int pos=0) const
Return one entry from the pressed keys history.
Definition: CImg.h:6803
CImg< T > get_tensor() const
Resize image to become a symmetric tensor .
Definition: CImg.h:16836
CImg< T > & draw_point(const CImg< t > &points, const tc *const color, const float opacity=1)
Definition: CImg.h:30597
CImg< T > & draw_triangle(const int x0, const int y0, const int x1, const int y1, const int x2, const int y2, const CImg< tc > &texture, const int tx0, const int ty0, const int tx1, const int ty1, const int tx2, const int ty2, const float brightness0, const float brightness1, const float brightness2, const float opacity=1)
Draw a textured Gouraud-shaded 2d triangle.
Definition: CImg.h:33105
CImg< _cimg_Tt > operator/(const t value) const
Division operator.
Definition: CImg.h:11163
CImg< intT > get_select_graph(CImgDisplay &disp, const unsigned int plot_type=1, const unsigned int vertex_type=1, const char *const labelx=0, const double xmin=0, const double xmax=0, const char *const labely=0, const double ymin=0, const double ymax=0) const
Select sub-graph in a graph.
Definition: CImg.h:37691
CImg< Tfloat > get_RGBtoxyY() const
Convert pixel values from RGB to xyY color spaces .
Definition: CImg.h:20754
static CImgList< T > get_load(const char *const filename)
Load a list from a file .
Definition: CImg.h:46974
CImg< Tfloat > get_RGBtoCMYK() const
Convert pixel values from RGB to CMYK color spaces .
Definition: CImg.h:20774
CImg< T > & XYZtoxyY()
Convert pixel values from XYZ_709 to xyY color spaces.
Definition: CImg.h:20676
const CImg< T > & display_object3d(CImgDisplay &disp, const CImg< tp > &vertices, const CImgList< tf > &primitives, const CImgList< tc > &colors, const to &opacities, const bool centering=true, const int render_static=4, const int render_motion=1, const bool is_double_sided=true, const float focale=700, const float light_x=0, const float light_y=0, const float light_z=-5e8f, const float specular_lightness=0.2f, const float specular_shininess=0.1f, const bool display_axes=true, float *const pose_matrix=0) const
Display object 3d in an interactive window.
Definition: CImg.h:41345
static void _cimg_recursive_apply(T *data, const Tfloat filter[], const int N, const unsigned long off, const int order, const bool boundary_conditions)
Definition: CImg.h:25684
CImg< T > & blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=false)
Blur image isotropically.
Definition: CImg.h:25868
CImg< Tfloat > get_LabtoXYZ() const
Convert pixel values from Lab to XYZ_709 color spaces .
Definition: CImg.h:20671
CImg< T > get_equalize(const unsigned int nblevels, const T val_min, const T val_max) const
Equalize histogram of pixel values .
Definition: CImg.h:19303
bool is_sameXC(const CImg< t > &img) const
Test if image width and spectrum are the same as that of another image.
Definition: CImg.h:13487
CImg< Tfloat > get_HSItoRGB() const
Convert pixel values from HSI to RGB color spaces .
Definition: CImg.h:20313
CImg< T > & object3dtoCImg3d(const CImgList< tp > &primitives, const CImgList< tc > &colors, const bool full_check=true)
Convert 3d object into a CImg3d representation .
Definition: CImg.h:30224
CImg< T > & assign(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, const int value0, const int value1,...)
Construct image with specified size and initialize pixel values from a sequence of integers ...
Definition: CImg.h:10109
CImg< T > & operator()(const unsigned int pos)
Return a reference to one image element of the list.
Definition: CImg.h:45086
CImgList(const CImg< t1 > &img1, const CImg< t2 > &img2, const CImg< t3 > &img3, const CImg< t4 > &img4, const CImg< t5 > &img5, const bool is_shared=false)
Construct list from five images.
Definition: CImg.h:44657
CImgDisplay & set_title(const char *const format,...)
Set title of the associated window.
Definition: CImg.h:7065
CImg< T > & dilate(const unsigned int s)
Dilate image by a square structuring element of specified size.
Definition: CImg.h:25363
CImg< floatT > get_object3dtoCImg3d(const CImgList< tp > &primitives, const CImgList< tc > &colors, const bool full_check=true) const
Convert 3d object into a CImg3d representation .
Definition: CImg.h:30380
CImg< T > & set_tensor_at(const CImg< t > &ten, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0)
Set tensor-valued pixel at specified position.
Definition: CImg.h:16762
bool contains(const T &pixel, t &x) const
Test if pixel value is inside image bounds and get its X-coordinate.
Definition: CImg.h:13722
bool operator==(const CImg< t > &img) const
Test if two images have the same size and values.
Definition: CImg.h:11866
double atof(const char *const str)
Read value in a C-string.
Definition: CImg.h:4468
const char * option(const char *const name, const int argc, const char *const *const argv, const char *const defaut, const char *const usage, const bool reset_static)
Return options specified on the command line.
Definition: CImg.h:5468
CImgList< T > get_insert(const unsigned int n, const unsigned int pos=~0U) const
Insert n empty images img into the current image list, at position pos .
Definition: CImg.h:46222
const CImg< T > & save_jpeg(std::FILE *const file, const unsigned int quality=100) const
Save image as a JPEG file .
Definition: CImg.h:42404
CImg< T > get_autocrop(const CImg< t > &color, const char *const axes="zyx") const
Autocrop image region, regarding the specified background color .
Definition: CImg.h:23592
const unsigned int keyMENU
Keycode for the MENU key (architecture-dependent).
Definition: CImg.h:2942
CImg< Tfloat > get_atan2(const CImg< t > &img) const
Compute the arctangent2 of each pixel value .
Definition: CImg.h:15501
bool contains(const CImg< T > &img, t &n) const
Test if the list contains the image 'img'.
Definition: CImg.h:45913
CImg< T > get_fill(const T val0, const T val1, const T val2, const T val3) const
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18235
CImgList(const unsigned int n, const unsigned int width, const unsigned int height, const unsigned int depth, const unsigned int spectrum, const T val)
Construct list containing images of specified size, and initialize pixel values.
Definition: CImg.h:44523
CImg< T > & fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5)
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18262
CImg< T > get_fill(const T val0, const T val1, const T val2, const T val3, const T val4) const
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18257
const CImg< T > & save_ffmpeg_external(const char *const filename, const char *const codec=0, const unsigned int fps=25, const unsigned int bitrate=2048) const
Save volumetric image as a video, using ffmpeg external binary.
Definition: CImg.h:44072
CImg< T > get_RGBtoBayer() const
Convert RGB color image to a Bayer-coded scalar image .
Definition: CImg.h:20797
CImg< T > & label(const CImg< t > &connectivity_mask, const Tfloat tolerance=0)
Label connected components .
Definition: CImg.h:19792
Tdouble variance_mean(const unsigned int variance_method, t &mean) const
Return the variance as well as the average of the pixel values.
Definition: CImg.h:16331
const unsigned int keyPADSUB
Keycode for the PADSUB key (architecture-dependent).
Definition: CImg.h:2958
const CImg< T > & save_pnm(const char *const filename, const unsigned int bytes_per_pixel=0) const
Save image as a PNM file.
Definition: CImg.h:42793
static bool is_saveable(const char *const filename)
Tell if an image list can be saved as one single file.
Definition: CImg.h:48211
bool is_empty() const
Return true if list is empty.
Definition: CImg.h:45683
bool is_sameXYC(const CImg< t > &img) const
Test if image width, height and spectrum are the same as that of another image.
Definition: CImg.h:13572
bool is_sameYC(const CImg< t > &img) const
Test if image height and spectrum are the same as that of another image.
Definition: CImg.h:13521
CImg< T > & laplacian()
Compute image laplacian.
Definition: CImg.h:27214
static CImg< T > get_load_pandore(std::FILE *const file)
Load image from a PANDORE-5 file .
Definition: CImg.h:39980
CImg< T > & RGBtoHSI()
Convert pixel values from RGB to HSI color spaces.
Definition: CImg.h:20240
double grand()
Return a random variable following a gaussian distribution and a standard deviation of 1...
Definition: CImg.h:4216
CImg< T > get_dilate(const unsigned int s) const
Dilate image by a square structuring element of specified size .
Definition: CImg.h:25368
CImg< T > & sinc()
Compute the sinc of each pixel value.
Definition: CImg.h:15300
CImg< T > get_shared_points(const unsigned int x0, const unsigned int x1, const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0)
Return a shared-memory image referencing a range of pixels of the image instance. ...
Definition: CImg.h:24030
CImg< T > & slice(const int z0)
Return specified image slice .
Definition: CImg.h:23706
void sleep(const unsigned int milliseconds)
Sleep for a given numbers of milliseconds.
Definition: CImg.h:4107
CImg< T > & draw_axis(const CImg< t > &values_x, const int y, const tc *const color, const float opacity=1, const unsigned int pattern=~0U, const unsigned int font_height=13, const bool allow_zero=true)
Draw a labeled horizontal axis.
Definition: CImg.h:34822
CImg< T > & fillY(const unsigned int x, const unsigned int z, const unsigned int c, const double a0,...)
Fill pixel values along the Y-axis at a specified pixel position .
Definition: CImg.h:18733
CImg< floatT > get_elevation3d(CImgList< tf > &primitives, CImgList< tc > &colors, const CImg< te > &elevation) const
Generate a 3d elevation of the image instance.
Definition: CImg.h:29066
CImg(const CImg< t > &img, const bool is_shared)
Advanced copy constructor.
Definition: CImg.h:9941
CImg< T > & erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1)
Erode image by a rectangular structuring element of specified size.
Definition: CImg.h:24931
bool is_sameY(const CImgDisplay &disp) const
Test if image height is equal to specified value.
Definition: CImg.h:13406
CImg< Tfloat > get_norm(const int norm_type=2) const
Compute L2-norm of each multi-valued pixel of the image instance .
Definition: CImg.h:19044
const unsigned int key0
Keycode for the 0 key (architecture-dependent).
Definition: CImg.h:2896
Tdouble magnitude(const int magnitude_type=2) const
Compute norm of the image, viewed as a matrix.
Definition: CImg.h:16589
int strcasecmp(const char *const str1, const char *const str2)
Compare two C-strings, ignoring the case.
Definition: CImg.h:4496
CImg< T > & load_yuv(const char *const filename, const unsigned int size_x, const unsigned int size_y=1, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z')
Load image sequence from a YUV file.
Definition: CImg.h:40328
bool is_sameXYZC(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c) const
Test if image width, height, depth and spectrum are equal to specified values.
Definition: CImg.h:13615
int height() const
Return the number of image rows.
Definition: CImg.h:12023
const_iterator end() const
Return iterator to one position after the last image of the list .
Definition: CImg.h:45339
CImg< T > & diagonal()
Resize image to become a diagonal matrix.
Definition: CImg.h:16868
CImg< T > & max(const T val)
Pointwise max operator between instance image and a value.
Definition: CImg.h:16012
static CImg< T > vector(const T &a0, const T &a1, const T &a2, const T &a3, const T &a4, const T &a5, const T &a6, const T &a7, const T &a8, const T &a9, const T &a10, const T &a11, const T &a12, const T &a13, const T &a14)
Return a 1x15 image containing specified values.
Definition: CImg.h:17963
const CImg< T > & save_png(std::FILE *const file, const unsigned int bytes_per_pixel=0) const
Save image as a PNG file .
Definition: CImg.h:42591
const CImg< T > & display(const char *const title=0, const bool display_info=true, unsigned int *const XYZ=0) const
Display image into an interactive window.
Definition: CImg.h:41094
CImg< Tfloat > get_distance(const T value, const unsigned int metric=2) const
Compute distance to a specified value .
Definition: CImg.h:27680
const unsigned int keyARROWLEFT
Keycode for the ARROWLEFT key (architecture-dependent).
Definition: CImg.h:2944
CImg< T > & histogram(const unsigned int nb_levels)
Compute the histogram of pixel values .
Definition: CImg.h:19242
CImg< T > & channel(const int c0)
Return specified image channel .
Definition: CImg.h:23733
CImg< T > & operator-=(const CImg< t > &img)
In-place substraction operator.
Definition: CImg.h:10885
const CImg< T > & save_yuv(std::FILE *const file, const bool is_rgb=true) const
Save image as a .yuv video file .
Definition: CImg.h:43947
CImg< T > & assign(const t *const values, const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, const bool is_shared)
Construct image with specified size and initialize pixel values from a memory buffer ...
Definition: CImg.h:10182
CImg< floatT > get_object3dtoCImg3d(const CImgList< tp > &primitives, const bool full_check=true) const
Convert 3d object into a CImg3d representation .
Definition: CImg.h:30389
const unsigned int keyPAD0
Keycode for the PAD0 key (architecture-dependent).
Definition: CImg.h:2947
CImg(const t *const values, const unsigned int size_x, const unsigned int size_y=1, const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false)
Construct image with specified size and initialize pixel values from a memory buffer.
Definition: CImg.h:9796
static int screen_height()
Return height of the screen (current resolution along the Y-axis).
Definition: CImg.h:6616
static void wait(CImgDisplay &disp1, CImgDisplay &disp2, CImgDisplay &disp3, CImgDisplay &disp4, CImgDisplay &disp5, CImgDisplay &disp6, CImgDisplay &disp7, CImgDisplay &disp8)
Wait for any event occuring either on the display disp1, disp2, disp3, disp4, ... disp8...
Definition: CImg.h:7372
CImg< T > & load_ascii(std::FILE *const file)
Load image from an ascii file .
Definition: CImg.h:38127
bool is_sameZ(const unsigned int size_z) const
Test if image depth is equal to specified value.
Definition: CImg.h:13411
const T & max_min(t &min_val) const
Return a reference to the minimum pixel value of the instance list and return the minimum value as we...
Definition: CImg.h:46074
int dialog(const char *const title, const char *const msg, const char *const button1_label="OK", const char *const button2_label=0, const char *const button3_label=0, const char *const button4_label=0, const char *const button5_label=0, const char *const button6_label=0, const bool centering=false)
Display a simple dialog box, and wait for the user's response .
Definition: CImg.h:49474
CImg< T > get_ror(const CImg< t > &img) const
Compute the bitwise right rotation of each pixel value .
Definition: CImg.h:15905
bool contains(const T &pixel, t &x, t &y, t &z, t &c) const
Test if pixel value is inside image bounds and get its X,Y,Z and C-coordinates.
Definition: CImg.h:13671
CImg< _cimg_Tt > get_cross(const CImg< t > &img) const
Compute the cross product between two 1x3 images, viewed as 3d vectors .
Definition: CImg.h:16956
CImg< T > & sort(CImg< t > &permutations, const bool is_increasing=true)
Sort pixel values and get sorting permutations.
Definition: CImg.h:17346
CImg< T > & draw_image(const int x0, const int y0, const int z0, const int c0, const CImg< T > &sprite, const float opacity=1)
Draw an image .
Definition: CImg.h:34399
Represent a list of images CImg.
Definition: CImg.h:2064
const unsigned int keyF4
Keycode for the F4 key (architecture-dependent).
Definition: CImg.h:2877
const unsigned int keyPADMUL
Keycode for the PADMUL key (architecture-dependent).
Definition: CImg.h:2959
static CImg< T > get_load_pnm(const char *const filename)
Load image from a PNM file .
Definition: CImg.h:38825
CImgDisplay & resize(const CImgDisplay &disp, const bool force_redraw=true)
Resize display to the size of another CImgDisplay instance.
Definition: CImg.h:7008
T & max()
Return a reference to the maximum pixel value of the instance list.
Definition: CImg.h:45971
const CImg< T > & save_rgb(std::FILE *const file) const
Save image as a RGB file .
Definition: CImg.h:43091
T & at(const int offset, const T out_value)
Access to a pixel value at a specified offset, using Dirichlet boundary conditions.
Definition: CImg.h:12246
CImg< T > & resize_doubleXY()
Resize image to double-size, using the Scale2X algorithm.
Definition: CImg.h:21982
CImg< T > get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0)
Return a shared memory image referencing a range of slices of the image instance. ...
Definition: CImg.h:24110
CImg< T > & operator++()
In-place increment operator (prefix).
Definition: CImg.h:10764
T kth_smallest(const unsigned int k) const
Return the kth smallest pixel value.
Definition: CImg.h:16237
CImg< T > & assign(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, const char *const values, const bool repeat_values)
Construct image with specified size and initialize pixel values from a value string ...
Definition: CImg.h:10133
CImgList< T > & insert(const unsigned int n, const CImg< t > &img, const unsigned int pos=~0U, const bool is_shared=false)
Insert n copies of the image img into the current image list, at position pos.
Definition: CImg.h:46234
CImg< T > & operator=(const CImg< T > &img)
Copy an image into the current image instance .
Definition: CImg.h:10632
CImg< T > & draw_line(const int x0, const int y0, const int z0, const int x1, const int y1, const int z1, const tc *const color, const float opacity=1, const unsigned int pattern=~0U, const bool init_hatch=true)
Draw a 3d line.
Definition: CImg.h:30853
CImg< T > & pow(const char *const expression)
Raise each pixel value to a power, specified from an expression.
Definition: CImg.h:15653
static CImgList< T > get_load_yuv(std::FILE *const file, const unsigned int size_x, const unsigned int size_y=1, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1, const bool yuv2rgb=true)
Load a list from an image sequence YUV file .
Definition: CImg.h:47435
double crand()
Return a random variable between [-1,1] with respect to an uniform distribution.
Definition: CImg.h:4209
CImg< Tfloat > get_CMYKtoCMY() const
Convert pixel values from CMYK to CMY color spaces .
Definition: CImg.h:20520
static CImgList< T > get_load_cimg(const char *const filename)
Load a list from a .cimg file .
Definition: CImg.h:46987
CImg< Tdouble > get_stats(const unsigned int variance_method=1) const
Compute statistics vector from the pixel values.
Definition: CImg.h:16539
CImg< _cimg_Tt > operator-(const CImg< t > &img) const
Substraction operator.
Definition: CImg.h:10964
CImg< T > & rotate(const float angle, const float cx, const float cy, const float zoom, const unsigned int interpolation=1, const unsigned int boundary=3)
Rotate image with arbitrary angle, around a center point.
Definition: CImg.h:22787
static CImg< T > vector(const T &a0, const T &a1, const T &a2, const T &a3, const T &a4, const T &a5, const T &a6, const T &a7, const T &a8)
Return a 1x9 image containing specified values.
Definition: CImg.h:17893
bool contains(const T &pixel, t &n, t &x, t &y, t &z, t &c) const
Test if one image of the list contains the specified referenced value.
Definition: CImg.h:45837
bool is_sameXYZC(const CImg< t > &img) const
Test if image width, height, depth and spectrum are the same as that of another image.
Definition: CImg.h:13625
static CImg< T > matrix(const T &a0)
Return a 1x1 matrix containing specified coefficients.
Definition: CImg.h:17993
CImg< T > & select(const char *const title, const unsigned int feature_type=2, unsigned int *const XYZ=0)
Simple interface to select a shape from an image .
Definition: CImg.h:37083
bool endianness()
Return the endianness of the current architecture.
Definition: CImg.h:4001
CImgDisplay & assign(const CImgDisplay &disp)
Construct a display as a copy of another one .
Definition: CImg.h:6265
CImg< T > & operator--()
In-place decrement operator (prefix).
Definition: CImg.h:10902
static CImg< T > vector(const T &a0, const T &a1, const T &a2, const T &a3, const T &a4, const T &a5, const T &a6, const T &a7, const T &a8, const T &a9, const T &a10, const T &a11, const T &a12, const T &a13, const T &a14, const T &a15)
Return a 1x16 image containing specified values.
Definition: CImg.h:17976
CImg< T > & draw_mandelbrot(const int x0, const int y0, const int x1, const int y1, const CImg< tc > &colormap, const float opacity=1, const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2, const unsigned int iteration_max=255, const bool is_normalized_iteration=false, const bool is_julia_set=false, const double param_r=0, const double param_i=0)
Draw a quadratic Mandelbrot or Julia 2d fractal.
Definition: CImg.h:35494
static CImg< T > get_load_analyze(const char *const filename, float *const voxel_size=0)
Load image from an ANALYZE7.5/NIFTI file .
Definition: CImg.h:39574
CImg< T > & operator%=(const CImg< t > &img)
In-place modulo operator.
Definition: CImg.h:11243
T & min_max(t &max_val)
Return a reference to the minimum pixel value as well as the maximum pixel value. ...
Definition: CImg.h:16160
bool containsNXYZC(const int n, const int x=0, const int y=0, const int z=0, const int c=0) const
Test if list contains one particular pixel location.
Definition: CImg.h:45811
CImg< T > & draw_mandelbrot(const CImg< tc > &colormap, const float opacity=1, const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2, const unsigned int iteration_max=255, const bool is_normalized_iteration=false, const bool is_julia_set=false, const double param_r=0, const double param_i=0)
Draw a quadratic Mandelbrot or Julia 2d fractal .
Definition: CImg.h:35567
CImg< T > & load_cimg(std::FILE *const file, const char axis='z', const float align=0)
Load image from a .cimg[z] file .
Definition: CImg.h:39727
CImg< Tfloat > operator*(const char *const expression) const
Multiplication operator.
Definition: CImg.h:11057
const char * imagemagick_path(const char *const user_path=0, const bool reinit_path=false)
Get/set path to the Program Files/ directory (Windows only).
Definition: CImg.h:4770
CImg< Tfloat > get_XYZtoxyY() const
Convert pixel values from XYZ_709 to xyY color spaces .
Definition: CImg.h:20698
bool is_sameZ(const CImg< t > &img) const
Test if image depth is equal to specified value.
Definition: CImg.h:13417
CImg< T > & load_png(std::FILE *const file)
Load image from a PNG file .
Definition: CImg.h:38638
CImg< T > & fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, const T val7, const T val8, const T val9, const T val10, const T val11, const T val12)
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18464
CImgList< T > operator+() const
Return a non-shared copy of a list.
Definition: CImg.h:45186
CImg< T > & blur_anisotropic(const CImg< t > &G, const float amplitude=60, const float dl=0.8f, const float da=30, const float gauss_prec=2, const unsigned int interpolation_type=0, const bool is_fast_approx=1)
Blur image anisotropically, directed by a field of diffusion tensors.
Definition: CImg.h:25890
T & atNXYZC(const int pos, const int x, const int y, const int z, const int c)
Access to pixel value with Neumann boundary conditions.
Definition: CImg.h:45408
CImg< _cimg_Ttfloat > get_correlate(const CImg< t > &mask, const unsigned int boundary_conditions=1, const bool is_normalized=false) const
Correlate image by a mask .
Definition: CImg.h:24454
unsigned long size() const
Return the total number of pixel values.
Definition: CImg.h:12079
CImg< T > & draw_axes(const float x0, const float x1, const float y0, const float y1, const tc *const color, const float opacity=1, const int subdivisionx=-60, const int subdivisiony=-60, const float precisionx=0, const float precisiony=0, const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U, const unsigned int font_height=13)
Draw labeled horizontal and vertical axes .
Definition: CImg.h:34956
CImg< T > & draw_ellipse(const int x0, const int y0, const CImg< t > &tensor, const tc *const color, const float opacity, const unsigned int pattern)
Draw an outlined 2d ellipse .
Definition: CImg.h:34199
CImg< T > & load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false, const char axis='z', const float align=0)
Load image sequence using FFMPEG av's libraries.
Definition: CImg.h:40300
static CImg< T > get_load_raw(std::FILE *const file, const unsigned int size_x=0, const unsigned int size_y=1, const unsigned int size_z=1, const unsigned int size_c=1, const bool is_multiplexed=false, const bool invert_endianness=false, const unsigned long offset=0)
Load image from a raw binary file .
Definition: CImg.h:40242
CImg< Tfloat > get_tanh() const
Compute the hyperbolic tangent of each pixel value .
Definition: CImg.h:15398
CImgList< T > & load_yuv(const char *const filename, const unsigned int size_x, const unsigned int size_y, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1, const bool yuv2rgb=true)
Load a list from a YUV image sequence file.
Definition: CImg.h:47411
Allow to create windows, display images on them and manage user events (keyboard, mouse and windows e...
Definition: CImg.h:6036
CImg< _cimg_Ttfloat > get_solve(const CImg< t > &A) const
Solve a system of linear equations .
Definition: CImg.h:17139
T atX(const int x, const int y, const int z, const int c, const T out_value) const
Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate ...
Definition: CImg.h:12318
CImg< T > & shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0, const int boundary_conditions=0)
Shift image content.
Definition: CImg.h:22199
CImg< T > get_rol(const unsigned int n=1) const
Compute the bitwise left rotation of each pixel value .
Definition: CImg.h:15737
CImgList< T > & crop_font()
Crop font along the X-axis.
Definition: CImg.h:49083
CImg< T > & distance_dijkstra(const T value, const CImg< t > &metric, const bool is_high_connectivity=false)
Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm)...
Definition: CImg.h:27993
Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const Tfloat min_value, const Tfloat max_value) const
Return damped pixel value, using cubic interpolation and Neumann boundary conditions for the XYZ-coor...
Definition: CImg.h:13174
static CImg< T > matrix(const T &a0, const T &a1, const T &a2, const T &a3, const T &a4, const T &a5, const T &a6, const T &a7, const T &a8)
Return a 3x3 matrix containing specified coefficients.
Definition: CImg.h:18024
CImgList< T > & load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false)
Load an image from a video file, using ffmpeg libraries.
Definition: CImg.h:47538
CImg< T > get_quantize(const unsigned int n, const bool keep_range=true) const
Uniformly quantize pixel values .
Definition: CImg.h:19168
CImg< T > get_append(const CImg< T > &img, const char axis='x', const float align=0) const
Append two images along specified axis .
Definition: CImg.h:24424
CImg< T > & draw_triangle(const int x0, const int y0, const float z0, const int x1, const int y1, const float z1, const int x2, const int y2, const float z2, const CImg< tc > &texture, const int tx0, const int ty0, const int tx1, const int ty1, const int tx2, const int ty2, const float brightness0, const float brightness1, const float brightness2, const float opacity=1)
Draw a textured Gouraud-shaded 2d triangle, with perspective correction .
Definition: CImg.h:33197
int fclose(std::FILE *file)
Close a file.
Definition: CImg.h:4664
void fempty(std::FILE *const file, const char *const filename)
Create an empty file.
Definition: CImg.h:5401
CImg< Tfloat > get_sign() const
Compute the sign of each pixel value .
Definition: CImg.h:15243
CImg< Tfloat > get_blur(const float sigma_x, const float sigma_y, const float sigma_z, const bool boundary_conditions=true, const bool is_gaussian=false) const
Blur image .
Definition: CImg.h:25857
T & atN(const int pos, const int x, const int y, const int z, const int c, const T out_value)
Access to pixel value with Dirichlet boundary conditions for the first coordinate (pos)...
Definition: CImg.h:45610
const CImg< T > & save_bmp(const char *const filename) const
Save image as a BMP file.
Definition: CImg.h:42292
CImgList< T > & split(const char axis, const int nb=0)
Return a list where each image has been split along the specified axis.
Definition: CImg.h:46530
CImg(const CImg< T > &img, const bool is_shared)
Advanced copy constructor .
Definition: CImg.h:9966
CImg< Tfloat > get_pow(const CImg< t > &img) const
Raise each pixel value to a power, pointwisely specified from another image .
Definition: CImg.h:15719
CImg< _cimg_Tt > operator+(const t value) const
Addition operator.
Definition: CImg.h:10805
int spectrum() const
Return the number of image channels.
Definition: CImg.h:12059
CImg< T > & YUVtoRGB()
Convert pixel values from YUV to RGB color spaces.
Definition: CImg.h:20400
CImgList< T > & operator,(const CImgList< t > &list)
Return a copy of the list instance, where all elements of input list list have been inserted at the e...
Definition: CImg.h:45214
CImg< T > & mirror(const char *const axes)
Mirror image content along specified axes.
Definition: CImg.h:22176
CImg< Tuchar > get_HSVtoRGB() const
Convert pixel values from HSV to RGB color spaces .
Definition: CImg.h:20156
bool operator==(const char *const expression) const
Test if all pixel values of an image follow a specified expression.
Definition: CImg.h:11824
CImg< T > get_projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) const
Generate a 2d representation of a 3d image, with XY,XZ and YZ views.
Definition: CImg.h:23396
CImgDisplay & toggle_fullscreen(const bool force_redraw=true)
Toggle fullscreen mode.
Definition: CImg.h:7095
CImg< T > & operator*=(const CImg< t > &img)
In-place multiplication operator.
Definition: CImg.h:11038
const unsigned int keyCAPSLOCK
Keycode for the CAPSLOCK key (architecture-dependent).
Definition: CImg.h:2915
static void wait(CImgDisplay &disp1)
Wait for any event occuring on the display disp1.
Definition: CImg.h:7314
CImg< T > & load_parrec(const char *const filename, const char axis='c', const float align=0)
Load image from a PAR-REC (Philips) file.
Definition: CImg.h:40192
Tfloat linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const
Return pixel value, using linear interpolation and Neumann boundary conditions for all X...
Definition: CImg.h:12759
CImgDisplay & operator=(const CImg< t > &img)
Display image on associated window.
Definition: CImg.h:6319
static CImgList< T > get_load_cimg(std::FILE *const file, const unsigned int n0, const unsigned int n1, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0, const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1)
Load a sub-image list from a (non compressed) .cimg file .
Definition: CImg.h:47148
const CImg< T > & display_object3d(const char *const title, const CImg< tp > &vertices, const CImgList< tf > &primitives, const CImgList< tc > &colors, const bool centering=true, const int render_static=4, const int render_motion=1, const bool is_double_sided=true, const float focale=700, const float light_x=0, const float light_y=0, const float light_z=-5e8f, const float specular_lightness=0.2f, const float specular_shininess=0.1f, const bool display_axes=true, float *const pose_matrix=0) const
Display object 3d in an interactive window .
Definition: CImg.h:41402
CImg< T > & load_tiff(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1)
Load image from a TIFF file.
Definition: CImg.h:39236
static CImg< T > matrix(const T &a0, const T &a1, const T &a2, const T &a3)
Return a 2x2 matrix containing specified coefficients.
Definition: CImg.h:18004
CImg< T > & draw_triangle(const int x0, const int y0, const int x1, const int y1, const int x2, const int y2, const tc *const color, const float brightness0, const float brightness1, const float brightness2, const float opacity=1)
Draw a Gouraud-shaded 2d triangle.
Definition: CImg.h:32259
const unsigned int keyENTER
Keycode for the ENTER key (architecture-dependent).
Definition: CImg.h:2925
const unsigned int keyF6
Keycode for the F6 key (architecture-dependent).
Definition: CImg.h:2879
CImg< T > & draw_gaussian(const float xc, const float yc, const float sigma, const tc *const color, const float opacity=1)
Draw a 2d gaussian function .
Definition: CImg.h:35662
static CImg< T > get_load_cimg(std::FILE *const file, const char axis='z', const float align=0)
Load image from a .cimg[z] file .
Definition: CImg.h:39735
T & atNX(const int pos, const int x, const int y, const int z, const int c, const T out_value)
Access to pixel value with Dirichlet boundary conditions for the 2 first coordinates (pos...
Definition: CImg.h:45555
CImgDisplay & resize(const bool force_redraw=true)
Resize display to the size of the associated window.
Definition: CImg.h:6965
CImg< T > get_round(const double y=1, const unsigned int rounding_type=0) const
Round pixel values .
Definition: CImg.h:18879
CImgDisplay & assign(const unsigned int width, const unsigned int height, const char *const title=0, const unsigned int normalization=3, const bool is_fullscreen=false, const bool is_closed=false)
Construct a display with specified dimensions .
Definition: CImg.h:6232
CImg< T > & normalize(const T min_value, const T max_value)
Linearly normalize pixel values.
Definition: CImg.h:18975
_cimg_iskey_def(ESC)
Return true if the ESC key is being pressed on the associated window, false otherwise.
CImg< T > & draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle, const tc *const color, const float opacity=1)
Draw a filled 2d ellipse.
Definition: CImg.h:34148
const CImgList< T > & save_cimg(std::FILE *const file, const unsigned int n0, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0) const
Insert the image instance into into an existing .cimg file, at specified coordinates.
Definition: CImg.h:48861
CImg< T > & draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle, const tc *const color, const float opacity, const unsigned int pattern)
Draw an outlined 2d ellipse.
Definition: CImg.h:34183
CImg< T > & invert_endianness()
Invert endianness of all pixel values.
Definition: CImg.h:18834
static const CImg< Tuchar > & flag_LUT256()
Return colormap "flag", containing 256 colors entries in RGB.
Definition: CImg.h:20010
T & atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0)
Return pixel value with Neumann boundary conditions for the first coordinate (pos).
Definition: CImg.h:45628
bool is_sameXYC(const unsigned int size_x, const unsigned int size_y, const unsigned int size_c) const
Test if image width, height and spectrum are equal to specified values.
Definition: CImg.h:13563
CImg< T > & fill(const T val0, const T val1, const T val2)
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18203
const CImg< T > & save_bmp(std::FILE *const file) const
Save image as a BMP file .
Definition: CImg.h:42297
CImg< T > get_slice(const int z0) const
Return specified image slice.
Definition: CImg.h:23701
Tfloat cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const
Return pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate...
Definition: CImg.h:12873
CImg< T > & load_bmp(const char *const filename)
Load image from a BMP file.
Definition: CImg.h:38237
static CImg< floatT > sphere3d(CImgList< tf > &primitives, const float radius=50, const unsigned int subdivisions=3)
Generate a 3d sphere.
Definition: CImg.h:30119
CImg< Tuchar > get_RGBtoYCbCr() const
Convert pixel values from RGB to YCbCr color spaces .
Definition: CImg.h:20341
const CImg< T > & save_yuv(const char *const filename, const bool is_rgb=true) const
Save image as a .yuv video file.
Definition: CImg.h:43937
CImg< T > & load_cimg(const char *const filename, const char axis='z', const float align=0)
Load image from a .cimg[z] file.
Definition: CImg.h:39714
CImg< T > & fillX(const unsigned int y, const unsigned int z, const unsigned int c, const double a0,...)
Fill pixel values along the X-axis at a specified pixel position .
Definition: CImg.h:18715
CImgDisplay & set_fullscreen(const bool is_fullscreen, const bool force_redraw=true)
Enable or disable fullscreen mode.
Definition: CImg.h:7083
CImg< T > & deriche(const float sigma, const int order=0, const char axis='x', const bool boundary_conditions=true)
Apply recursive Deriche filter.
Definition: CImg.h:25565
CImgList< T > & assign(const unsigned int n)
Construct list containing empty images .
Definition: CImg.h:44814
const CImgList< T > & print(const char *const title=0, const bool display_stats=true) const
Print informations about the list on the standard output.
Definition: CImg.h:47974
CImgList< Tfloat > get_symmetric_eigen() const
Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix...
Definition: CImg.h:17334
bool is_closed() const
Return true if display is closed (i.e. not visible on the screen), false otherwise.
Definition: CImg.h:6368
CImgList< T > get_reverse_object3d() const
Reverse primitives orientations of a 3d object .
Definition: CImg.h:49240
CImgDisplay(const CImgDisplay &disp)
Construct a display as a copy of an existing one.
Definition: CImg.h:6204
CImg< T > & draw_text(const int x0, const int y0, const char *const text, const int, const tc *const background_color, const float opacity, const CImgList< t > &font,...)
Draw a text string .
Definition: CImg.h:34613
CImg< T > & operator-=(const char *const expression)
In-place substraction operator.
Definition: CImg.h:10846
CImgList(const unsigned int n, const unsigned int width, const unsigned int height, const unsigned int depth, const unsigned int spectrum, const int val0, const int val1,...)
Construct list containing images of specified size, and initialize pixel values from a sequence of in...
Definition: CImg.h:44542
static CImg< T > identity_matrix(const unsigned int N)
Return a NxN identity matrix.
Definition: CImg.h:18110
static CImg< T > vector(const T &a0, const T &a1)
Return a 1x2 image containing specified values.
Definition: CImg.h:17829
CImg< T > & rotate(const float angle, const unsigned int interpolation=1, const unsigned int boundary=0)
Rotate image with arbitrary angle.
Definition: CImg.h:22638
static CImg< T > get_load(const char *const filename)
Load image from a file .
Definition: CImg.h:38109
CImg< T > & convolve(const CImg< t > &mask, const unsigned int boundary_conditions=1, const bool is_normalized=false)
Convolve image by a mask.
Definition: CImg.h:24780
const CImg< T > & save_pandore(const char *const filename, const unsigned int colorspace=0) const
Save image as a Pandore-5 file.
Definition: CImg.h:43663
const unsigned int key9
Keycode for the 9 key (architecture-dependent).
Definition: CImg.h:2895
CImg< T > get_matrix_at(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const
Get (square) matrix-valued pixel located at specified position.
Definition: CImg.h:16698
CImgDisplay & render(const CImg< T > &img)
Render image into internal display buffer.
Definition: CImg.h:7422
static CImg< floatT > elevation3d(CImgList< tf > &primitives, const tfunc &func, const float x0, const float y0, const float x1, const float y1, const int size_x=256, const int size_y=256)
Compute 3d elevation of a function as a 3d object.
Definition: CImg.h:29233
CImg< Tfloat > get_RGBtoXYZ() const
Convert pixel values from RGB to XYZ_709 color spaces .
Definition: CImg.h:20570
CImg< T > & operator%=(const char *const expression)
In-place modulo operator.
Definition: CImg.h:11204
CImg< Tfloat > get_shift_object3d() const
Shift 3d object's vertices, so that it becomes centered .
Definition: CImg.h:28876
const CImg< T > & save_cimg(std::FILE *const file, const unsigned int n0, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0) const
Save image as a sub-image into an existing .cimg file .
Definition: CImg.h:43483
CImg< Tfloat > get_shift_object3d(const float tx, const float ty=0, const float tz=0) const
Shift 3d object's vertices .
Definition: CImg.h:28852
CImg< T > resize_object3d()
Resize 3d object to unit size.
Definition: CImg.h:28909
static CImg< T > get_load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1)
Load image from a RGBA file .
Definition: CImg.h:39184
T round(const T x, const double y=1, const int rounding_type=0)
Return rounded value.
Definition: CImg.h:4432
T atNXYZC(const int pos, const int x, const int y, const int z, const int c) const
Access to pixel value with Neumann boundary conditions .
Definition: CImg.h:45418
CImg< T > & acos()
Compute the arccosine of each pixel value.
Definition: CImg.h:15410
CImgList< T > & operator=(const CImgList< T > &list)
Construct list from another list .
Definition: CImg.h:45161
CImg< T > & fill(const char *const expression, const bool repeat_flag)
Fill sequentially pixel values according to a given expression.
Definition: CImg.h:18620
static CImg< T > get_load_ascii(const char *const filename)
Load image from an ascii file .
Definition: CImg.h:38122
CImgDisplay & wait()
Wait for any user event occuring on the current display.
Definition: CImg.h:7298
const unsigned int keyF1
Keycode for the F1 key (architecture-dependent).
Definition: CImg.h:2874
const unsigned int keyPADDIV
Keycode for the PADDDIV key (architecture-dependent).
Definition: CImg.h:2960
CImg< T > & discard(const T value)
Discard specified value in the image buffer.
Definition: CImg.h:18784
CImg< T > & resize(const CImg< t > &src, const int interpolation_type=1, const unsigned int boundary_conditions=0, const float centering_x=0, const float centering_y=0, const float centering_z=0, const float centering_c=0)
Resize image to dimensions of another image.
Definition: CImg.h:21910
static const CImg< Tuchar > & lines_LUT256()
Return colormap "lines", containing 256 colors entries in RGB.
Definition: CImg.h:19928
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 T val7, const T val8, const T val9) const
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18393
CImg< T > & load_cimg(std::FILE *const file, const unsigned int n0, const unsigned int n1, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0, const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1, const char axis='z', const float align=0)
Load sub-images of a .cimg file .
Definition: CImg.h:39780
const CImgList< T > & save_cimg(std::FILE *file, const bool is_compressed=false) const
Save list into a .cimg file.
Definition: CImg.h:48718
bool contains(const CImg< T > &img) const
Test if the list contains the image img.
Definition: CImg.h:45924
CImg< T > & draw_point(const int x0, const int y0, const tc *const color, const float opacity=1)
Draw a 2d point .
Definition: CImg.h:30585
CImgDisplay & set_key(const unsigned int keycode, const bool is_pressed=true)
Simulate a keyboard press/release event.
Definition: CImg.h:7230
const unsigned int keyC
Keycode for the C key (architecture-dependent).
Definition: CImg.h:2929
int width() const
Return display width.
Definition: CImg.h:6628
static CImgList< T > get_load_ffmpeg_external(const char *const filename)
Load an image from a video file using the external tool 'ffmpeg' .
Definition: CImg.h:47747
const unsigned int keyJ
Keycode for the J key (architecture-dependent).
Definition: CImg.h:2922
static CImg< floatT > box3d(CImgList< tf > &primitives, const float size_x=200, const float size_y=100, const float size_z=100)
Generate a 3d box object.
Definition: CImg.h:29935
bool is_sameNXYZC(const unsigned int n, const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dc) const
Test if list dimensions match specified arguments.
Definition: CImg.h:45797
CImg< T > operator|(const t value) const
Bitwise OR operator.
Definition: CImg.h:11464
CImg< T > & operator=(const T value)
Assign a value to all image pixels.
Definition: CImg.h:10585
~CImg()
Destroy image.
Definition: CImg.h:9444
CImg< Tfloat > get_xyYtoXYZ() const
Convert pixel values from xyY pixels to XYZ_709 color spaces .
Definition: CImg.h:20724
CImg< T > & draw_line(const CImg< t > &points, const tc *const color, const float opacity=1, const unsigned int pattern=~0U, const bool init_hatch=true)
Draw a set of consecutive lines.
Definition: CImg.h:31374
const CImg< T > & save_ascii(std::FILE *const file) const
Save image as an ascii file .
Definition: CImg.h:42191
CImg< t > get_map(const CImg< t > &colormap, const unsigned int boundary_conditions=0) const
Map predefined colormap on the scalar (indexed) image instance .
Definition: CImg.h:19609
CImg< T > get_tensor_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const
Get tensor-valued pixel located at specified position.
Definition: CImg.h:16714
static CImgList< T > get_load_cimg(std::FILE *const file)
Load a list from a .cimg file .
Definition: CImg.h:47000
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 T val7, const T val8) const
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18363
T at(const int offset) const
Access to a pixel value at a specified offset, using Neumann boundary conditions .
Definition: CImg.h:12283
CImg< T > & pseudoinvert()
Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix.
Definition: CImg.h:17041
CImg< T > & set_linear_atXYZ(const T &value, const float fx, const float fy=0, const float fz=0, const int c=0, const bool is_added=false)
Set pixel value, using linear interpolation for the X,Y and Z-coordinates.
Definition: CImg.h:13239
const unsigned int keyI
Keycode for the I key (architecture-dependent).
Definition: CImg.h:2909
T atXY(const int x, const int y, const int z=0, const int c=0) const
Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates ...
Definition: CImg.h:12398
static CImg< T > vector(const T &a0, const T &a1, const T &a2, const T &a3, const T &a4, const T &a5, const T &a6, const T &a7, const T &a8, const T &a9)
Return a 1x10 image containing specified values.
Definition: CImg.h:17904
CImgList< T > operator<(const char axis) const
Split image along specified axis.
Definition: CImg.h:11973
static CImg< T > get_load_exr(const char *const filename)
Load image from a EXR file .
Definition: CImg.h:39957
static void wait(CImgDisplay &disp1, CImgDisplay &disp2, CImgDisplay &disp3, CImgDisplay &disp4, CImgDisplay &disp5, CImgDisplay &disp6)
Wait for any event occuring either on the display disp1, disp2, disp3, disp4, ... disp6...
Definition: CImg.h:7350
const unsigned int keyH
Keycode for the H key (architecture-dependent).
Definition: CImg.h:2921
CImg< T > & blur_median(const unsigned int n, const float threshold=0)
Blur image with the median filter.
Definition: CImg.h:26556
CImg< T > & warp(const CImg< t > &warp, const bool is_relative=false, const unsigned int interpolation=1, const unsigned int boundary_conditions=0)
Warp image content by a warping field.
Definition: CImg.h:22911
static void save_empty_cimg(const char *const filename, const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dc=1)
Save blank image as a .cimg file.
Definition: CImg.h:43502
CImg< T > & fill(const CImg< t > &values, const bool repeat_values=true)
Fill sequentially pixel values according to the values found in another image.
Definition: CImg.h:18682
CImg< T > operator--(int)
In-place decrement operator (postfix).
Definition: CImg.h:10915
const char * medcon_path(const char *const user_path=0, const bool reinit_path=false)
Get/set path to the XMedcon's medcon binary.
Definition: CImg.h:4992
CImgList< T > & load_gif_external(const char *const filename)
Load gif file, using ImageMagick or GraphicsMagick's external tools.
Definition: CImg.h:47756
int height() const
Return display height.
Definition: CImg.h:6637
T atXYZ(const int x, const int y, const int z, const int c=0) const
Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates ...
Definition: CImg.h:12446
CImg< T > & dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U)
Return minimal path in a graph, using the Dijkstra algorithm.
Definition: CImg.h:17794
static CImg< T > diagonal(const T &a0, const T &a1, const T &a2)
Return a 3x3 diagonal matrix containing specified coefficients.
Definition: CImg.h:18092
CImg< T > & crop(const int x0, const int y0, const int z0, const int c0, const int x1, const int y1, const int z1, const int c1, const bool boundary_conditions=false)
Crop image region.
Definition: CImg.h:23430
const CImg< T > & save_pfm(const char *const filename) const
Save image as a PFM file.
Definition: CImg.h:42998
bool is_sameN(const CImgList< t > &list) const
Test if number of image elements is equal between two images lists.
Definition: CImg.h:45700
CImg< Tfloat > get_sqrt() const
Compute the square root of each pixel value .
Definition: CImg.h:15109
CImg< T > & mul(const CImg< t > &img)
In-place pointwise multiplication.
Definition: CImg.h:15523
CImg< T > & shift_object3d()
Shift 3d object's vertices, so that it becomes centered.
Definition: CImg.h:28860
static CImgList< T > get_load_yuv(const char *const filename, const unsigned int size_x, const unsigned int size_y=1, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1, const bool yuv2rgb=true)
Load a list from a YUV image sequence file .
Definition: CImg.h:47419
static CImg< floatT > ellipsoid3d(CImgList< tf > &primitives, const CImg< t > &tensor, const unsigned int subdivisions=3)
Generate a 3d ellipsoid.
Definition: CImg.h:30188
CImg< T > & fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const int a0,...)
Fill pixel values along the Z-axis at a specified pixel position.
Definition: CImg.h:18745
CImg< T > get_shared_channel(const unsigned int c0)
Return a shared-memory image referencing one channel of the image instance.
Definition: CImg.h:24183
CImg< T > & append(const CImg< t > &img, const char axis='x', const float align=0)
Append two images along specified axis.
Definition: CImg.h:24402
CImg< T > get_CImg3dtoobject3d(CImgList< tp > &primitives, CImgList< tc > &colors, CImgList< to > &opacities, const bool full_check=true) const
Convert CImg3d representation into a 3d object .
Definition: CImg.h:30420
CImg< T > & cross(const CImg< t > &img)
Compute the cross product between two 1x3 images, viewed as 3d vectors.
Definition: CImg.h:16940
CImg< T > & tensor()
Resize image to become a symmetric tensor.
Definition: CImg.h:16831
CImg< T > & blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f, const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30, const float gauss_prec=2, const unsigned int interpolation_type=0, const bool is_fast_approx=true)
Blur image anisotropically, in an edge-preserving way.
Definition: CImg.h:26170
CImg< T > & diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f, const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false)
Compute field of diffusion tensors for edge-preserving smoothing.
Definition: CImg.h:27405
T & atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0)
Access to pixel value with Neumann boundary conditions for the 2 first coordinates (pos...
Definition: CImg.h:45573
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 T val7, const T val8, const T val9, const T val10, const T val11, const T val12, const T val13, const T val14) const
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18568
CImg< T > & load_dlm(const char *const filename)
Load image from a DLM file.
Definition: CImg.h:38179
CImg< T > get_shared()
Return a shared-memory version of the image instance.
Definition: CImg.h:24193
CImg< T > & load_gif_external(const char *const filename, const char axis='z', const float align=0)
Load gif file, using Imagemagick or GraphicsMagicks's external tools.
Definition: CImg.h:40609
CImgList< _cimg_Tt > operator,(const CImg< t > &img) const
Construct an image list from two images.
Definition: CImg.h:11939
unsigned int released_key(const unsigned int pos=0) const
Return one entry from the released keys history.
Definition: CImg.h:6820
bool contains(const T &pixel) const
Test if one of the image list contains the specified referenced value.
Definition: CImg.h:45901
CImg< T > & rol(const CImg< t > &img)
Compute the bitwise left rotation of each pixel value.
Definition: CImg.h:15797
const unsigned int key4
Keycode for the 4 key (architecture-dependent).
Definition: CImg.h:2890
CImg< Tfloat > get_distance_dijkstra(const T value, const CImg< t > &metric, const bool is_high_connectivity=false) const
Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm)...
Definition: CImg.h:28000
CImg< T > & fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, const T val7, const T val8, const T val9, const T val10, const T val11, const T val12, const T val13, const T val14, const T val15)
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18576
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 T val7, const T val8, const T val9, const T val10, const T val11, const T val12, const T val13, const T val14, const T val15) const
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18608
static void FFT(CImg< T > &real, CImg< T > &imag, const bool is_invert=false, const unsigned int nb_threads=0)
Compute n-d Fast Fourier Transform.
Definition: CImg.h:28756
const CImg< T > & save_inr(const char *const filename, const float *const voxel_size=0) const
Save image as an INRIMAGE-4 file.
Definition: CImg.h:43524
const unsigned int keyN
Keycode for the N key (architecture-dependent).
Definition: CImg.h:2932
static CImg< T > get_load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1)
Load image from a RGB file .
Definition: CImg.h:39128
Tfloat linear_atX(const float fx, const int y, const int z, const int c, const T out_value) const
Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X-coordinate...
Definition: CImg.h:12525
static CImg< T > get_load_yuv(std::FILE *const file, const unsigned int size_x, const unsigned int size_y=1, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z')
Load image sequence from a YUV file .
Definition: CImg.h:40352
CImg< T > & exp()
Compute the exponential of each pixel value.
Definition: CImg.h:15120
CImg< Tfloat > get_RGBtoHSL() const
Convert pixel values from RGB to HSL color spaces .
Definition: CImg.h:20198
const CImg< T > & save_pnk(const char *const filename) const
Save image as a PNK file.
Definition: CImg.h:42929
CImg< _cimg_Tt > get_mul(const CImg< t > &img) const
In-place pointwise multiplication .
Definition: CImg.h:15538
const unsigned int keyPAGEDOWN
Keycode for the PAGEDOWN key (architecture-dependent).
Definition: CImg.h:2914
unsigned long tic()
Start tic/toc timer for time measurement between code instructions.
Definition: CImg.h:4089
static CImg< T > vector(const T &a0, const T &a1, const T &a2, const T &a3, const T &a4, const T &a5, const T &a6, const T &a7, const T &a8, const T &a9, const T &a10, const T &a11)
Return a 1x12 image containing specified values.
Definition: CImg.h:17926
double eval(const char *const expression, const double x=0, const double y=0, const double z=0, const double c=0)
Evaluate math expression.
Definition: CImg.h:49498
CImg< T > & assign(const T *const values, const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, const bool is_shared)
Construct image with specified size and initialize pixel values from a memory buffer ...
Definition: CImg.h:10194
CImg< Tuchar > get_YCbCrtoRGB() const
Convert pixel values from RGB to YCbCr color spaces .
Definition: CImg.h:20369
CImg< T > & displacement(const CImg< T > &source, const float smoothness=0.1f, const float precision=5.0f, const unsigned int nb_scales=0, const unsigned int iteration_max=10000, const bool is_backward=false)
Estimate displacement field between two images.
Definition: CImg.h:27483
const unsigned int keyBACKSPACE
Keycode for the BACKSPACE key (architecture-dependent).
Definition: CImg.h:2897
CImg< T > & append(const CImg< T > &img, const char axis='x', const float align=0)
Append two images along specified axis .
Definition: CImg.h:24409
T & front()
Return a reference to the first pixel value.
Definition: CImg.h:12208
const unsigned int keyX
Keycode for the X key (architecture-dependent).
Definition: CImg.h:2928
CImg< T > & assign(const CImg< t > &img, const bool is_shared)
In-place version of the advanced copy constructor.
Definition: CImg.h:10241
CImg< T > & sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1, const float alpha=0, const float sigma=0)
Sharpen image.
Definition: CImg.h:26760
CImgList< Tfloat > get_FFT(const bool invert=false) const
Compute a n-d Fast Fourier Transform .
Definition: CImg.h:49218
bool contains(const T &pixel, t &n) const
Test if one of the image list contains the specified referenced value.
Definition: CImg.h:45892
CImg< T > get_blur_median(const unsigned int n, const float threshold=0) const
Blur image with the median filter .
Definition: CImg.h:26562
bool is_nan() const
Test if image instance contains a 'nan' value.
Definition: CImg.h:13373
CImgList(const CImgList< T > &list)
Construct list copy .
Definition: CImg.h:44740
CImgList< T > & load_yuv(std::FILE *const file, const unsigned int size_x, const unsigned int size_y, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1, const bool yuv2rgb=true)
Load a list from an image sequence YUV file .
Definition: CImg.h:47427
const CImg< T > & save_gzip_external(const char *const filename) const
Save image using gzip external binary.
Definition: CImg.h:44093
bool is_sameXC(const unsigned int size_x, const unsigned int size_c) const
Test if image width and spectrum are equal to specified values.
Definition: CImg.h:13478
const CImg< T > & save_cimg(const char *const filename, const bool is_compressed=false) const
Save image as a .cimg file.
Definition: CImg.h:43454
CImg< T > & fill(const T val0, const T val1)
Fill sequentially all pixel values with specified values.
Definition: CImg.h:18189
CImg< T > & sRGBtoRGB()
Convert pixel values from sRGB to RGB color spaces.
Definition: CImg.h:20042
static CImg< T > get_load_png(std::FILE *const file)
Load image from a PNG file .
Definition: CImg.h:38643
const unsigned int keySHIFTRIGHT
Keycode for the SHIFTRIGHT key (architecture-dependent).
Definition: CImg.h:2934
CImg< T > & RGBtoCMY()
Convert pixel values from RGB to CMY color spaces.
Definition: CImg.h:20428
CImg< T > operator|(const char *const expression) const
Bitwise OR operator.
Definition: CImg.h:11473
CImg< T > & haar(const bool invert=false, const unsigned int nb_scales=1)
Compute Haar multiscale wavelet transform .
Definition: CImg.h:28379
CImg< Tfloat > get_normalize() const
Normalize multi-valued pixels of the image instance, with respect to their L2-norm ...
Definition: CImg.h:19024
const CImg< T > & save_pnm(std::FILE *const file, const unsigned int bytes_per_pixel=0) const
Save image as a PNM file .
Definition: CImg.h:42798
CImg< Tfloat > get_distance(const T value, const CImg< t > &metric_mask) const
Compute chamfer distance to a specified value, with a custom metric .
Definition: CImg.h:27818
CImgList< T > & load_parrec(const char *const filename)
Load a list from a PAR/REC (Philips) file.
Definition: CImg.h:47289
CImg< T > & erode(const unsigned int s)
Erode the image by a square structuring element of specified size.
Definition: CImg.h:25074
const CImg< T > get_shared_slice(const unsigned int z0, const unsigned int c0=0) const
Return a shared-memory image referencing one slice of the image instance .
Definition: CImg.h:24145
iterator end()
Return a CImg::iterator pointing next to the last pixel value.
Definition: CImg.h:12193
CImgDisplay()
Construct an empty display.
Definition: CImg.h:6119
bool contains(const T &pixel, t &x, t &y, t &z) const
Test if pixel value is inside image bounds and get its X,Y and Z-coordinates.
Definition: CImg.h:13690
CImg< T > & fill(const T val0, const T val1, const T val2, const T val3, const T val4)
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18240
CImg< T > & rol(const char *const expression)
Compute the bitwise left rotation of each pixel value.
Definition: CImg.h:15745
CImg< T > & cut(const T min_value, const T max_value)
Cut pixel values in specified range.
Definition: CImg.h:19113
const unsigned int keyF3
Keycode for the F3 key (architecture-dependent).
Definition: CImg.h:2876
bool is_event() const
Return true if any event has occured on the associated window, false otherwise.
Definition: CImg.h:6389
CImg< T > & draw_axes(const CImg< tx > &values_x, const CImg< ty > &values_y, const tc *const color, const float opacity=1, const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U, const unsigned int font_height=13, const bool allow_zero=true)
Draw labeled horizontal and vertical axes.
Definition: CImg.h:34926
bool is_key(const unsigned int keycode) const
Return true if key specified by given keycode is being pressed on the associated window, false otherwise.
Definition: CImg.h:6446
const unsigned int key7
Keycode for the 7 key (architecture-dependent).
Definition: CImg.h:2893
CImg< T > & HSItoRGB()
Convert pixel values from HSI to RGB color spaces.
Definition: CImg.h:20275
CImg< T > & draw_gaussian(const float xc, const float yc, const float zc, const CImg< t > &tensor, const tc *const color, const float opacity=1)
Draw a 3d gaussian function .
Definition: CImg.h:35669
T & temporary(const T &)
Return a reference to a temporary variable of type T.
Definition: CImg.h:3944
CImgList(const CImg< t1 > &img1, const CImg< t2 > &img2, const bool is_shared=false)
Construct list from two images.
Definition: CImg.h:44610
CImg< _cimg_Tt > get_append(const CImg< T > &img, const char axis='x', const float align=0) const
Append two images along specified axis .
Definition: CImg.h:24417
CImgList< T > & operator=(const CImgList< t > &list)
Construct list from another list.
Definition: CImg.h:45156
CImg(const T *const values, const unsigned int size_x, const unsigned int size_y=1, const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false)
Construct image with specified size and initialize pixel values from a memory buffer ...
Definition: CImg.h:9823
CImg< Tfloat > get_log2() const
Compute the base-10 logarithm of each pixel value .
Definition: CImg.h:15174
CImg< floatT > get_streamline(const float x, const float y, const float z, const float L=256, const float dl=0.1f, const unsigned int interpolation_type=2, const bool is_backward_tracking=false, const bool is_oriented_only=false) const
Return stream line of a 2d or 3d vector field.
Definition: CImg.h:23752
CImg< T > & load_cimg(const char *const filename, const unsigned int n0, const unsigned int n1, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0, const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1, const char axis='z', const float align=0)
Load sub-images of a .cimg file.
Definition: CImg.h:39755
int window_x() const
Return X-coordinate of the associated window.
Definition: CImg.h:6699
CImg< T > & load_off(CImgList< tf > &primitives, CImgList< tc > &colors, std::FILE *const file)
Load 3d object from a .OFF file .
Definition: CImg.h:40378
Tdouble dot(const CImg< t > &img) const
Compute the dot product between instance and argument, viewed as matrices.
Definition: CImg.h:16659
CImgList< T > & load_cimg(std::FILE *const file)
Load a list from a .cimg file.
Definition: CImg.h:46995
CImgList< T > get_split(const CImg< t > &values, const bool keep_values, const bool is_shared) const
Split image into a list of one-column vectors, according to a specified splitting value sequence...
Definition: CImg.h:24343
CImg< T > & load_pfm(std::FILE *const file)
Load image from a PFM file .
Definition: CImg.h:39032
CImg< T > & draw_line(const int x0, const int y0, const int x1, const int y1, const tc *const color, const float opacity=1, const unsigned int pattern=~0U, const bool init_hatch=true)
Draw a 2d line.
Definition: CImg.h:30637
CImg< T > & draw_triangle(const int x0, const int y0, const int x1, const int y1, const int x2, const int y2, const CImg< tc > &texture, const int tx0, const int ty0, const int tx1, const int ty1, const int tx2, const int ty2, const CImg< tl > &light, const int lx0, const int ly0, const int lx1, const int ly1, const int lx2, const int ly2, const float opacity=1)
Draw a textured Phong-shaded 2d triangle.
Definition: CImg.h:33455
bool is_sameXYZ(const CImg< t > &img) const
Test if image width, height and depth are the same as that of another image.
Definition: CImg.h:13555
CImgList(const CImg< t1 > &img1, const CImg< t2 > &img2, const CImg< t3 > &img3, const CImg< t4 > &img4, const CImg< t5 > &img5, const CImg< t6 > &img6, const CImg< t7 > &img7, const CImg< t8 > &img8, const bool is_shared=false)
Construct list from eight images.
Definition: CImg.h:44718
void unused(const T &,...)
Avoid warning messages due to unused parameters. Do nothing actually.
Definition: CImg.h:2106
const unsigned int keyM
Keycode for the M key (architecture-dependent).
Definition: CImg.h:2933
CImg< T > & resize(const int size_x, const int size_y=-100, const int size_z=-100, const int size_c=-100, const int interpolation_type=1, const unsigned int boundary_conditions=0, const float centering_x=0, const float centering_y=0, const float centering_z=0, const float centering_c=0)
Resize image to new dimensions.
Definition: CImg.h:20989
CImg< Tuchar > get_BayertoRGB(const unsigned int interpolation_type=3) const
Convert Bayer-coded scalar image to a RGB color image .
Definition: CImg.h:20825
CImg< T > get_crop(const int x0, const int y0, const int x1, const int y1, const bool boundary_conditions=false) const
Crop image region .
Definition: CImg.h:23479
const T & min_max(t &max_val) const
Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as w...
Definition: CImg.h:46029
const unsigned int keyPAD2
Keycode for the PAD2 key (architecture-dependent).
Definition: CImg.h:2949
CImg< Tfloat > get_log() const
Compute the logarithm of each pixel value .
Definition: CImg.h:15152
T median() const
Return the median pixel value.
Definition: CImg.h:16273
CImg< Tfloat > get_haar(const bool invert=false, const unsigned int nb_scales=1) const
Compute Haar multiscale wavelet transform .
Definition: CImg.h:28384
CImgList< T > & operator=(const CImgDisplay &disp)
Construct list from the content of a display window .
Definition: CImg.h:45177
CImg< T > & set_vector_at(const CImg< t > &vec, const unsigned int x, const unsigned int y=0, const unsigned int z=0)
Set vector-valued pixel at specified position.
Definition: CImg.h:16730
CImg< T > & round(const double y=1, const int rounding_type=0)
Round pixel values.
Definition: CImg.h:18869
const unsigned int keyU
Keycode for the U key (architecture-dependent).
Definition: CImg.h:2908
void invert_endianness(T *const buffer, const unsigned long size)
Reverse endianness of all elements in a memory buffer.
Definition: CImg.h:4012
static CImg< T > get_load_jpeg(std::FILE *const file)
Load image from a JPEG file .
Definition: CImg.h:38445
CImgList< T > & assign()
Destructor .
Definition: CImg.h:44794
CImgList< T > operator,(const CImg< t > &img) const
Return a copy of the list instance, where image img has been inserted at the end .
Definition: CImg.h:45205
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)
Construct image with specified size .
Definition: CImg.h:10068
CImg< T > & draw_object3d(const float x0, const float y0, const float z0, const CImg< tp > &vertices, const CImgList< tf > &primitives, const CImgList< tc > &colors, const unsigned int render_type=4, const bool is_double_sided=false, const float focale=700, const float lightx=0, const float lighty=0, const float lightz=-5e8, const float specular_lightness=0.2f, const float specular_shininess=0.1f)
Draw a 3d object .
Definition: CImg.h:35842
T atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T out_value) const
Access pixel value with Dirichlet boundary conditions for the 3 first coordinates (pos...
Definition: CImg.h:45450
CImgList< t > & move_to(CImgList< t > &list)
Transfer the content of the list instance to another list.
Definition: CImg.h:45021
CImgDisplay & show_mouse()
Show mouse pointer.
Definition: CImg.h:7104
CImgList< T > & FFT(const char axis, const bool invert=false)
Compute a 1d Fast Fourier Transform, along specified axis.
Definition: CImg.h:49184
const CImg< T > & save_dlm(std::FILE *const file) const
Save image as a DLM file .
Definition: CImg.h:42257
CImg< T > & transpose()
Transpose the image, viewed as a matrix.
Definition: CImg.h:16919
const unsigned int key5
Keycode for the 5 key (architecture-dependent).
Definition: CImg.h:2891
CImg< T > & max(const CImg< t > &img)
Pointwise max operator between two images.
Definition: CImg.h:16033
CImg< T > & draw_plasma(const float alpha=1, const float beta=0, const unsigned int scale=8)
Draw a random plasma texture.
Definition: CImg.h:35429
bool is_empty() const
Test if image instance is empty.
Definition: CImg.h:13356
CImg< T > & load_magick(const char *const filename)
Load image from a file, using Magick++ library.
Definition: CImg.h:38555
const unsigned int keyF
Keycode for the F key (architecture-dependent).
Definition: CImg.h:2919
bool operator!=(const t value) const
Test if pixels of an image are all different from a value.
Definition: CImg.h:11882
CImg< T > operator&(const char *const expression) const
Bitwise AND operator.
Definition: CImg.h:11372
CImg< Tfloat > get_sqr() const
Compute the square value of each pixel value .
Definition: CImg.h:15082
CImg< T > & load_pandore(std::FILE *const file)
Load image from a PANDORE-5 file .
Definition: CImg.h:39975
CImg< T > & draw_text(const int x0, const int y0, const char *const text, const tc *const foreground_color, const int background_color=0, const float opacity=1, const unsigned int font_height=13,...)
Draw a text string .
Definition: CImg.h:34650
const unsigned int keyF9
Keycode for the F9 key (architecture-dependent).
Definition: CImg.h:2882
CImg< T > & draw_gaussian(const float xc, const float yc, const CImg< t > &tensor, const tc *const color, const float opacity=1)
Draw a 2d gaussian function.
Definition: CImg.h:35614
CImgList(const CImg< t > &img, const bool is_shared=false)
Construct list from one image.
Definition: CImg.h:44597
CImgList< T > & insert(const unsigned int n, const unsigned int pos=~0U)
Insert n empty images img into the current image list, at position pos.
Definition: CImg.h:46213
CImg< T > get_shared_rows(const unsigned int y0, const unsigned int y1, const unsigned int z0=0, const unsigned int c0=0)
Return a shared-memory image referencing a range of rows of the image instance.
Definition: CImg.h:24062
CImg< T > & atan()
Compute the arctangent of each pixel value.
Definition: CImg.h:15454
CImg< T > & draw_triangle(CImg< tz > &zbuffer, const int x0, const int y0, const float z0, const int x1, const int y1, const float z1, const int x2, const int y2, const float z2, const tc *const color, const float brightness0, const float brightness1, const float brightness2, const float opacity=1)
Draw a Gouraud-shaded 2d triangle, with z-buffering .
Definition: CImg.h:32321
static CImg< T > vector(const T &a0, const T &a1, const T &a2, const T &a3, const T &a4, const T &a5, const T &a6)
Return a 1x7 image containing specified values.
Definition: CImg.h:17875
double eval(const char *const expression, const double x=0, const double y=0, const double z=0, const double c=0) const
Evaluate math formula.
Definition: CImg.h:16497
CImg< T > get_cut(const T min_value, const T max_value) const
Cut pixel values in specified range .
Definition: CImg.h:19124
const CImg< T > get_shared_rows(const unsigned int y0, const unsigned int y1, const unsigned int z0=0, const unsigned int c0=0) const
Return a shared-memory image referencing a range of rows of the image instance .
Definition: CImg.h:24076
CImg< T > get_sort(CImg< t > &permutations, const bool is_increasing=true) const
Sort pixel values and get sorting permutations .
Definition: CImg.h:17355
CImg< T > get_blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f, const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30, const float gauss_prec=2, const unsigned int interpolation_type=0, const bool is_fast_approx=true) const
Blur image anisotropically, in an edge-preserving way .
Definition: CImg.h:26179
T at(const int offset, const T out_value) const
Access to a pixel value at a specified offset, using Dirichlet boundary conditions ...
Definition: CImg.h:12251
CImg< _cimg_Tt > operator%(const t value) const
Modulo operator.
Definition: CImg.h:11262
CImg< T > & abs()
Compute the absolute value of each pixel value.
Definition: CImg.h:15207
CImgDisplay & set_wheel(const int amplitude)
Simulate a wheel event.
Definition: CImg.h:7182
static CImg< T > dijkstra(const tf &distance, const unsigned int nb_nodes, const unsigned int starting_node, const unsigned int ending_node, CImg< t > &previous_node)
Compute minimal path in a graph, using the Dijkstra algorithm.
Definition: CImg.h:17706
CImg< T > & operator&=(const char *const expression)
In-place bitwise AND operator.
Definition: CImg.h:11303
static CImg< T > get_load_gif_external(const char *const filename, const char axis='z', const float align=0)
Load gif file, using ImageMagick or GraphicsMagick's external tool 'convert' .
Definition: CImg.h:40615
CImg< Tfloat > get_min(const char *const expression) const
Pointwise min operator between an image and an expression .
Definition: CImg.h:16002
bool is_sameC(const CImg< t > &img) const
Test if image spectrum is equal to specified value.
Definition: CImg.h:13428
CImg< Tfloat > get_blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=false) const
Blur image isotropically .
Definition: CImg.h:25874
CImg< T > & set_matrix_at(const CImg< t > &mat, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0)
Set (square) matrix-valued pixel at specified position.
Definition: CImg.h:16750
const CImgList< T > & save_gzip_external(const char *const filename) const
Save list as a gzipped file, using external tool 'gzip'.
Definition: CImg.h:48958
const char * dcraw_path(const char *const user_path=0, const bool reinit_path=false)
Get/set path to the dcraw binary.
Definition: CImg.h:5157
CImg< T > & distance(const T value, const unsigned int metric=2)
Compute Euclidean distance function to a specified value.
Definition: CImg.h:27665
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
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18305
CImg< T > & fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, const T val7, const T val8, const T val9, const T val10, const T val11)
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18431
CImgList< Tfloat > get_FFT(const char axis, const bool invert=false) const
Compute a 1-D Fast Fourier Transform, along specified axis .
Definition: CImg.h:49197
T & min()
Return a reference to the minimum pixel value.
Definition: CImg.h:16106
static CImg< T > get_load_medcon_external(const char *const filename)
Load image from a DICOM file, using XMedcon's external tool 'medcon' .
Definition: CImg.h:40841
static CImg< T > vector(const T &a0, const T &a1, const T &a2)
Return a 1x3 image containing specified values.
Definition: CImg.h:17841
const T & front() const
Return a reference to the first pixel value .
Definition: CImg.h:12213
CImgList< T > & assign(const char *const filename)
Construct list by reading the content of a file .
Definition: CImg.h:45003
CImg< T > get_threshold(const T value, const bool soft_threshold=false, const bool strict_threshold=false) const
Threshold pixel values .
Definition: CImg.h:19215
const unsigned int keyT
Keycode for the T key (architecture-dependent).
Definition: CImg.h:2906
const unsigned int keyG
Keycode for the G key (architecture-dependent).
Definition: CImg.h:2920
const CImg< T > & save_cpp(const char *const filename) const
Save image as a .cpp source file.
Definition: CImg.h:42215
CImg< T > & index(const CImg< t > &colormap, const float dithering=1, const bool map_indexes=false)
Index multi-valued pixels regarding to a specified colormap.
Definition: CImg.h:19328
CImgDisplay & set_normalization(const unsigned int normalization)
Set normalization type.
Definition: CImg.h:7044
CImgList< T > & assign(const CImg< t1 > &img1, const CImg< t2 > &img2, const CImg< t3 > &img3, const CImg< t4 > &img4, const CImg< t5 > &img5, const CImg< t6 > &img6, const CImg< t7 > &img7, const bool is_shared=false)
Construct list from seven images .
Definition: CImg.h:44955
CImg< T > get_shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0, const int boundary_conditions=0) const
Shift image content .
Definition: CImg.h:22403
CImg< _cimg_Tt > get_dilate(const CImg< t > &mask, const unsigned int boundary_conditions=1, const bool is_normalized=false) const
Dilate image by a structuring element .
Definition: CImg.h:25098
CImg< T > get_dijkstra(const unsigned int starting_node, const unsigned int ending_node, CImg< t > &previous_node) const
Return minimal path in a graph, using the Dijkstra algorithm .
Definition: CImg.h:17783
const unsigned int keyAPPRIGHT
Keycode for the APPRIGHT key (architecture-dependent).
Definition: CImg.h:2941
const unsigned int keySHIFTLEFT
Keycode for the SHIFTLEFT key (architecture-dependent).
Definition: CImg.h:2926
CImg< _cimg_Tt > operator*(const CImg< t > &img) const
Multiplication operator.
Definition: CImg.h:11067
bool contains(const T &pixel, t &n, t &x, t &y) const
Test if one of the image list contains the specified referenced value.
Definition: CImg.h:45867
static CImg< T > diagonal(const T &a0, const T &a1, const T &a2, const T &a3, const T &a4)
Return a 5x5 diagonal matrix containing specified coefficients.
Definition: CImg.h:18102
CImg< T > & load_off(CImgList< tf > &primitives, CImgList< tc > &colors, const char *const filename)
Load 3d object from a .OFF file.
Definition: CImg.h:40366
CImg< T > & load_gzip_external(const char *const filename)
Load gzipped image file, using external tool 'gunzip'.
Definition: CImg.h:40683
const CImg< T > & save_exr(const char *const filename) const
Save image as an OpenEXR file.
Definition: CImg.h:43591
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 T val7, const T val8, const T val9, const T val10, const T val11) const
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18457
CImg< Tfloat > get_haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) const
Compute Haar multiscale wavelet transform .
Definition: CImg.h:28232
CImg< T > & CMYtoRGB()
Convert pixel values from CMY to RGB color spaces.
Definition: CImg.h:20456
CImg< T > get_min(const T val) const
Pointwise min operator between instance image and a value .
Definition: CImg.h:15925
CImg< T > & assign(const CImgDisplay &disp)
Construct image from a display window .
Definition: CImg.h:10291
CImg< T > & dijkstra(const unsigned int starting_node, const unsigned int ending_node, CImg< t > &previous_node)
Return minimal path in a graph, using the Dijkstra algorithm.
Definition: CImg.h:17776
char uncase(const char x)
Convert ascii character to lower case.
Definition: CImg.h:4452
CImg< T > & sign()
Compute the sign of each pixel value.
Definition: CImg.h:15233
const CImg< T > & save_cimg(const char *const filename, const unsigned int n0, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0) const
Save image as a sub-image into an existing .cimg file.
Definition: CImg.h:43474
CImg< Tfloat > get_diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f, const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) const
Compute field of diffusion tensors for edge-preserving smoothing .
Definition: CImg.h:27469
CImg< T > & CMYtoCMYK()
Convert pixel values from CMY to CMYK color spaces.
Definition: CImg.h:20484
static CImg< T > & empty()
Return a reference to an empty image.
Definition: CImg.h:10394
bool operator!=(const CImg< t > &img) const
Test if two images have different sizes or values.
Definition: CImg.h:11904
bool is_moved() const
Return true if associated window has been moved on the screen, false otherwise.
Definition: CImg.h:6382
CImg< T > get_resize_tripleXY() const
Resize image to triple-size, using the Scale3X algorithm .
Definition: CImg.h:22041
bool is_sameY(const CImg< t > &img) const
Test if image height is equal to specified value.
Definition: CImg.h:13401
CImg< T > & slices(const int z0, const int z1)
Return specified range of image slices .
Definition: CImg.h:23720
CImg< T > & draw_fill(const int x, const int y, const tc *const color, const float opacity=1, const float sigma=0, const bool is_high_connexity=false)
Draw filled 2d region with the flood fill algorithm .
Definition: CImg.h:35415
CImgDisplay(const unsigned int width, const unsigned int height, const char *const title=0, const unsigned int normalization=3, const bool is_fullscreen=false, const bool is_closed=false)
Construct a display with specified dimensions.
Definition: CImg.h:6140
static CImg< T > dijkstra(const tf &distance, const unsigned int nb_nodes, const unsigned int starting_node, const unsigned int ending_node=~0U)
Return minimal path in a graph, using the Dijkstra algorithm.
Definition: CImg.h:17760
CImgList< T > get_shared()
Return a list with elements being shared copies of images in the list instance.
Definition: CImg.h:44777
CImg< T > & draw_fill(const int x, const int y, const int z, const tc *const color, const float opacity=1, const float sigma=0, const bool is_high_connexity=false)
Draw filled 3d region with the flood fill algorithm .
Definition: CImg.h:35406
T atN(const int pos, const int x, const int y, const int z, const int c, const T out_value) const
Access to pixel value with Dirichlet boundary conditions for the first coordinate (pos) ...
Definition: CImg.h:45615
CImg< Tfloat > get_invert(const bool use_LU=true) const
Invert the instance image, viewed as a matrix .
Definition: CImg.h:17034
const unsigned int keyF11
Keycode for the F11 key (architecture-dependent).
Definition: CImg.h:2884
CImg< T > get_fill(const T val0, const T val1) const
Fill sequentially all pixel values with specified values .
Definition: CImg.h:18198
CImg< T > & draw_triangle(const int x0, const int y0, const int x1, const int y1, const int x2, const int y2, const tc *const color, const float opacity=1)
Draw a filled 2d triangle.
Definition: CImg.h:32079
static CImg< floatT > elevation3d(CImgList< tf > &primitives, const char *const expression, const float x0, const float y0, const float x1, const float y1, const int size_x=256, const int size_y=256)
Compute 3d elevation of a function, as a 3d object .
Definition: CImg.h:29273
const CImgList< T > & save_gif_external(const char *const filename, const unsigned int fps=25, const unsigned int nb_loops=0)
Save image sequence as a GIF animated file.
Definition: CImg.h:48251
bool is_sameN(const unsigned int size_n) const
Test if number of image elements is equal to specified value.
Definition: CImg.h:45691
const unsigned int keyARROWDOWN
Keycode for the ARROWDOWN key (architecture-dependent).
Definition: CImg.h:2945
CImg< T > & draw_gaussian(const float xc, const float yc, const float zc, const float sigma, const tc *const color, const float opacity=1)
Draw a 3d gaussian function .
Definition: CImg.h:35698
T & atXYZ(const int x, const int y, const int z, const int c, const T out_value)
Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates.
Definition: CImg.h:12415
CImgList< T > & load_cimg(std::FILE *const file, const unsigned int n0, const unsigned int n1, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0, const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1)
Load a sub-image list from a (non compressed) .cimg file .
Definition: CImg.h:47138
const unsigned int keyALTGR
Keycode for the ALTGR key (architecture-dependent).
Definition: CImg.h:2940
bool is_sameX(const CImgDisplay &disp) const
Test if image width is equal to specified value.
Definition: CImg.h:13390
CImgDisplay & paint()
Paint internal display buffer on associated window.
Definition: CImg.h:7432
T atNXY(const int pos, const int x, const int y, const int z, const int c, const T out_value) const
Access to pixel value with Dirichlet boundary conditions for the 3 first coordinates (pos...
Definition: CImg.h:45505
CImg< T > & swap(CImg< T > &img)
Swap fields of two image instances.
Definition: CImg.h:10375
CImg< T > & load_exr(const char *const filename)
Load image from a EXR file.
Definition: CImg.h:39926
const char * gunzip_path(const char *const user_path=0, const bool reinit_path=false)
Get/set path to the gzip binary.
Definition: CImg.h:5119