/* # # File : CImg.h # ( C++ header file ) # # Description : The C++ Template Image Processing Library. # This file is the main part of the CImg Library project. # ( http://cimg.sourceforge.net ) # # Project manager : David Tschumperle. # ( http://www.greyc.ensicaen.fr/~dtschump/ ) # # The complete contributor list can be seen in the 'README.txt' file. # # Licenses : This file is "dual-licensed", you have to choose one # of the two licenses below to apply on this file. # # CeCILL-C # The CeCILL-C license is close to the GNU LGPL. # ( http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html ) # # or CeCILL v2.0 # The CeCILL license is compatible with the GNU GPL. # ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html ) # # This software is governed either by the CeCILL or the CeCILL-C license # under French law and abiding by the rules of distribution of free software. # You can use, modify and or redistribute the software under the terms of # the CeCILL or CeCILL-C licenses as circulated by CEA, CNRS and INRIA # at the following URL : "http://www.cecill.info". # # As a counterpart to the access to the source code and rights to copy, # modify and redistribute granted by the license, users are provided only # with a limited warranty and the software's author, the holder of the # economic rights, and the successive licensors have only limited # liability. # # In this respect, the user's attention is drawn to the risks associated # with loading, using, modifying and/or developing or reproducing the # software by the user in light of its specific status of free software, # that may mean that it is complicated to manipulate, and that also # therefore means that it is reserved for developers and experienced # professionals having in-depth computer knowledge. Users are therefore # encouraged to load and test the software's suitability as regards their # requirements in conditions enabling the security of their systems and/or # data to be ensured and, more generally, to use and operate it in the # same conditions as regards security. # # The fact that you are presently reading this means that you have had # knowledge of the CeCILL and CeCILL-C licenses and that you accept its terms. # */ // Define version number of the current file. // #ifndef cimg_version #define cimg_version 130 /*----------------------------------------------------------- # # Test/auto-set CImg configuration variables # and include required headers. # # If you find that default configuration variables are # not adapted, you can override their values before including # the header file "CImg.h" (using the #define directive). # ------------------------------------------------------------*/ // Include required standard C++ headers. // #include #include #include #include #include #include // Operating system configuration. // // Define 'cimg_OS' to : 0 for an unknown OS (will try to minize library dependancies). // 1 for a Unix-like OS (Linux, Solaris, BSD, MacOSX, Irix, ...). // 2 for Microsoft Windows. // #ifndef cimg_OS #if defined(unix) || defined(__unix) || defined(__unix__) \ || defined(linux) || defined(__linux) || defined(__linux__) \ || defined(sun) || defined(__sun) \ || defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) \ || defined(__FreeBSD__) || defined __DragonFly__ \ || defined(sgi) || defined(__sgi) \ || defined(__MACOSX__) || defined(__APPLE__) \ || defined(__CYGWIN__) #define cimg_OS 1 #elif defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) \ || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) #define cimg_OS 2 #else #define cimg_OS 0 #endif #elif !(cimg_OS==0 || cimg_OS==1 || cimg_OS==2) #error CImg Library : Configuration variable 'cimg_OS' is badly defined. #error (valid values are '0=unknown OS', '1=Unix-like OS', '2=Microsoft Windows'). #endif // Compiler configuration. // // Try to detect Microsoft VC++ compilers. // (lot of workarounds are needed afterwards to // make CImg working, particularly with VC++ 6.0). // #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4311) #pragma warning(disable:4312) #pragma warning(disable:4800) #pragma warning(disable:4804) #pragma warning(disable:4996) #define _CRT_SECURE_NO_DEPRECATE 1 #define _CRT_NONSTDC_NO_DEPRECATE 1 #if _MSC_VER<1300 #define cimg_use_visualcpp6 #define cimg_std #define _WIN32_WINNT 0x0500 #endif #endif // Include OS-specific headers. // #if cimg_OS==1 #include #include #elif cimg_OS==2 #include #ifndef _WIN32_IE #define _WIN32_IE 0x0400 #endif #include #endif // Define defaut pipe for output messages // // Define 'cimg_stdout' to : stdout to print CImg messages on the standard output. // stderr to print CImg messages on the standart error output (default behavior). // #ifndef cimg_std #define cimg_std std #endif #ifndef cimg_stdout #define cimg_stdout stderr #endif // Output messages configuration. // // Define 'cimg_debug' to : 0 to hide debug messages (quiet mode, but exceptions are still thrown). // 1 to display debug messages on the console. // 2 to display debug messages with dialog windows (default behavior). // 3 to do as 1 + add extra warnings (may slow down the code !). // 4 to do as 2 + add extra warnings (may slow down the code !). // // Define 'cimg_strict_warnings' to replace warning messages by exception throwns. // // Define 'cimg_use_vt100' to allow output of color messages (require VT100-compatible terminal). // #ifndef cimg_debug #define cimg_debug 2 #elif !(cimg_debug==0 || cimg_debug==1 || cimg_debug==2 || cimg_debug==3 || cimg_debug==4) #error CImg Library : Configuration variable 'cimg_debug' is badly defined. #error (valid values are '0=quiet', '1=console', '2=dialog', '3=console+warnings', '4=dialog+warnings'). #endif // Display framework configuration. // // Define 'cimg_display' to : 0 to disable display capabilities. // 1 to use X-Window framework (X11). // 2 to use Microsoft GDI32 framework. // 3 to use Apple Carbon framework. // #ifndef cimg_display #if cimg_OS==0 #define cimg_display 0 #elif cimg_OS==1 #if defined(__MACOSX__) || defined(__APPLE__) #define cimg_display 1 #else #define cimg_display 1 #endif #elif cimg_OS==2 #define cimg_display 2 #endif #elif !(cimg_display==0 || cimg_display==1 || cimg_display==2 || cimg_display==3) #error CImg Library : Configuration variable 'cimg_display' is badly defined. #error (valid values are '0=disable', '1=X-Window (X11)', '2=Microsoft GDI32', '3=Apple Carbon'). #endif // Include display-specific headers. // #if cimg_display==1 #include #include #include #include #ifdef cimg_use_xshm #include #include #include #endif #ifdef cimg_use_xrandr #include #endif #elif cimg_display==3 #include #include #endif // OpenMP configuration. // (http://www.openmp.org) // // Define 'cimg_use_openmp' to enable OpenMP support. // // OpenMP directives can be used in few CImg functions to get // advantages of multi-core CPUs. Using OpenMP is not mandatory. // #ifdef cimg_use_openmp #include "omp.h" #endif // LibPNG configuration. // (http://www.libpng.org) // // Define 'cimg_use_png' to enable LibPNG support. // // LibPNG can be used in functions 'CImg::{load,save}_png()' // to get a builtin support of PNG files. Using LibPNG is not mandatory. // #ifdef cimg_use_png extern "C" { #include "png.h" } #endif // LibJPEG configuration. // (http://en.wikipedia.org/wiki/Libjpeg) // // Define 'cimg_use_jpeg' to enable LibJPEG support. // // LibJPEG can be used in functions 'CImg::{load,save}_jpeg()' // to get a builtin support of JPEG files. Using LibJPEG is not mandatory. // #ifdef cimg_use_jpeg extern "C" { #include "jpeglib.h" } #endif // LibTIFF configuration. // (http://www.libtiff.org) // // Define 'cimg_use_tiff' to enable LibTIFF support. // // LibTIFF can be used in functions 'CImg[List]::{load,save}_tiff()' // to get a builtin support of TIFF files. Using LibTIFF is not mandatory. // #ifdef cimg_use_tiff extern "C" { #include "tiffio.h" } #endif // FFMPEG Avcodec and Avformat libraries configuration. // (http://www.ffmpeg.org) // // Define 'cimg_use_ffmpeg' to enable FFMPEG lib support. // // Avcodec and Avformat libraries can be used in functions // 'CImg[List]::load_ffmpeg()' to get a builtin // support of various image sequences files. // Using FFMPEG libraries is not mandatory. // #ifdef cimg_use_ffmpeg extern "C" { #include "avformat.h" #include "avcodec.h" #include "swscale.h" } #endif // Zlib configuration // (http://www.zlib.net) // // Define 'cimg_use_zlib' to enable Zlib support. // // Zlib can be used in functions 'CImg[List]::{load,save}_cimg()' // to allow compressed data in '.cimg' files. Using Zlib is not mandatory. // #ifdef cimg_use_zlib extern "C" { #include "zlib.h" } #endif // Magick++ configuration. // (http://www.imagemagick.org/Magick++) // // Define 'cimg_use_magick' to enable Magick++ support. // // Magick++ library can be used in functions 'CImg::{load,save}()' // to get a builtin support of various image formats (PNG,JPEG,TIFF,...). // Using Magick++ is not mandatory. // #ifdef cimg_use_magick #include "Magick++.h" #endif // FFTW3 configuration. // (http://www.fftw.org) // // Define 'cimg_use_fftw3' to enable libFFTW3 support. // // FFTW3 library can be used in functions 'CImg[List]::FFT()' to // efficiently compile the Fast Fourier Transform of image data. // #ifdef cimg_use_fftw3 extern "C" { #include "fftw3.h" } #endif // Board configuration. // (http://libboard.sourceforge.net/) // // Define 'cimg_use_board' to enable Board support. // // Board library can be used in functions 'CImg::draw_object3d()' // to draw objects 3D in vector-graphics canvas that can be saved // as .PS or .SVG files afterwards. // #ifdef cimg_use_board #include "Board.h" #endif // Lapack configuration. // (http://www.netlib.org/lapack) // // Define 'cimg_use_lapack' to enable LAPACK support. // // Lapack can be used in various CImg functions dealing with // matrix computation and algorithms (eigenvalues, inverse, ...). // Using Lapack is not mandatory. // #ifdef cimg_use_lapack extern "C" { extern void sgetrf_(int*, int*, float*, int*, int*, int*); extern void sgetri_(int*, float*, int*, int*, float*, int*, int*); extern void sgetrs_(char*, int*, int*, float*, int*, int*, float*, int*, int*); extern void sgesvd_(char*, char*, int*, int*, float*, int*, float*, float*, int*, float*, int*, float*, int*, int*); extern void ssyev_(char*, char*, int*, float*, int*, float*, float*, int*, int*); extern void dgetrf_(int*, int*, double*, int*, int*, int*); extern void dgetri_(int*, double*, int*, int*, double*, int*, int*); extern void dgetrs_(char*, int*, int*, double*, int*, int*, double*, int*, int*); extern void dgesvd_(char*, char*, int*, int*, double*, int*, double*, double*, int*, double*, int*, double*, int*, int*); extern void dsyev_(char*, char*, int*, double*, int*, double*, double*, int*, int*); } #endif // Check if min/max macros are defined. // // CImg does not compile if macros 'min' or 'max' are defined, // because min() and max() functions are also defined in the cimg:: namespace. // so it '#undef' these macros if necessary, and restore them to reasonable // values at the end of the file. // #ifdef min #undef min #define _cimg_redefine_min #endif #ifdef max #undef max #define _cimg_redefine_max #endif // Set the current working directory for native MacOSX bundled applications. // // By default, MacOS bundled applications set the cwd at the root directory '/', // the code below allows to set it to the current exec directory instead when // a CImg-based program is executed. // #if cimg_OS==1 && cimg_display==3 static struct _cimg_macosx_setcwd { _cimg_macosx_setcwd() { FSRef location; ProcessSerialNumber psn; char filePath[512]; if (GetCurrentProcess(&psn)!=noErr) return; if (GetProcessBundleLocation(&psn,&location)!=noErr) return; FSRefMakePath(&location,(UInt8*)filePath,sizeof(filePath)-1); int p = cimg_std::strlen(filePath); while (filePath[p] != '/') --p; filePath[p] = 0; chdir(filePath); } } cimg_macosx_setcwd; #endif /*------------------------------------------------------------------------------ # # Define user-friendly macros. # # User macros are prefixed by 'cimg_' and can be used in your own code. # They are particularly useful for option parsing, and image loops creation. # ------------------------------------------------------------------------------*/ // Define the program usage, and retrieve command line arguments. // #define cimg_usage(usage) cimg_library::cimg::option((char*)0,argc,argv,(char*)0,usage) #define cimg_help(str) cimg_library::cimg::option((char*)0,argc,argv,str,(char*)0) #define cimg_option(name,defaut,usage) cimg_library::cimg::option(name,argc,argv,defaut,usage) #define cimg_argument(pos) cimg_library::cimg::argument(pos,argc,argv) #define cimg_argument1(pos,s0) cimg_library::cimg::argument(pos,argc,argv,1,s0) #define cimg_argument2(pos,s0,s1) cimg_library::cimg::argument(pos,argc,argv,2,s0,s1) #define cimg_argument3(pos,s0,s1,s2) cimg_library::cimg::argument(pos,argc,argv,3,s0,s1,s2) #define cimg_argument4(pos,s0,s1,s2,s3) cimg_library::cimg::argument(pos,argc,argv,4,s0,s1,s2,s3) #define cimg_argument5(pos,s0,s1,s2,s3,s4) cimg_library::cimg::argument(pos,argc,argv,5,s0,s1,s2,s3,s4) #define cimg_argument6(pos,s0,s1,s2,s3,s4,s5) cimg_library::cimg::argument(pos,argc,argv,6,s0,s1,s2,s3,s4,s5) #define cimg_argument7(pos,s0,s1,s2,s3,s4,s5,s6) cimg_library::cimg::argument(pos,argc,argv,7,s0,s1,s2,s3,s4,s5,s6) #define cimg_argument8(pos,s0,s1,s2,s3,s4,s5,s6,s7) cimg_library::cimg::argument(pos,argc,argv,8,s0,s1,s2,s3,s4,s5,s6,s7) #define cimg_argument9(pos,s0,s1,s2,s3,s4,s5,s6,s7,s8) cimg_library::cimg::argument(pos,argc,argv,9,s0,s1,s2,s3,s4,s5,s6,s7,s8) // Define and manipulate local neighborhoods. // #define CImg_2x2(I,T) T I[4]; \ T& I##cc = I[0]; T& I##nc = I[1]; \ T& I##cn = I[2]; T& I##nn = I[3]; \ I##cc = I##nc = \ I##cn = I##nn = 0 #define CImg_3x3(I,T) T I[9]; \ T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; \ T& I##pc = I[3]; T& I##cc = I[4]; T& I##nc = I[5]; \ T& I##pn = I[6]; T& I##cn = I[7]; T& I##nn = I[8]; \ I##pp = I##cp = I##np = \ I##pc = I##cc = I##nc = \ I##pn = I##cn = I##nn = 0 #define CImg_4x4(I,T) T I[16]; \ T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; T& I##ap = I[3]; \ T& I##pc = I[4]; T& I##cc = I[5]; T& I##nc = I[6]; T& I##ac = I[7]; \ T& I##pn = I[8]; T& I##cn = I[9]; T& I##nn = I[10]; T& I##an = I[11]; \ T& I##pa = I[12]; T& I##ca = I[13]; T& I##na = I[14]; T& I##aa = I[15]; \ I##pp = I##cp = I##np = I##ap = \ I##pc = I##cc = I##nc = I##ac = \ I##pn = I##cn = I##nn = I##an = \ I##pa = I##ca = I##na = I##aa = 0 #define CImg_5x5(I,T) T I[25]; \ 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]; \ 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]; \ 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]; \ 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]; \ 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]; \ I##bb = I##pb = I##cb = I##nb = I##ab = \ I##bp = I##pp = I##cp = I##np = I##ap = \ I##bc = I##pc = I##cc = I##nc = I##ac = \ I##bn = I##pn = I##cn = I##nn = I##an = \ I##ba = I##pa = I##ca = I##na = I##aa = 0 #define CImg_2x2x2(I,T) T I[8]; \ T& I##ccc = I[0]; T& I##ncc = I[1]; \ T& I##cnc = I[2]; T& I##nnc = I[3]; \ T& I##ccn = I[4]; T& I##ncn = I[5]; \ T& I##cnn = I[6]; T& I##nnn = I[7]; \ I##ccc = I##ncc = \ I##cnc = I##nnc = \ I##ccn = I##ncn = \ I##cnn = I##nnn = 0 #define CImg_3x3x3(I,T) T I[27]; \ T& I##ppp = I[0]; T& I##cpp = I[1]; T& I##npp = I[2]; \ T& I##pcp = I[3]; T& I##ccp = I[4]; T& I##ncp = I[5]; \ T& I##pnp = I[6]; T& I##cnp = I[7]; T& I##nnp = I[8]; \ T& I##ppc = I[9]; T& I##cpc = I[10]; T& I##npc = I[11]; \ T& I##pcc = I[12]; T& I##ccc = I[13]; T& I##ncc = I[14]; \ T& I##pnc = I[15]; T& I##cnc = I[16]; T& I##nnc = I[17]; \ T& I##ppn = I[18]; T& I##cpn = I[19]; T& I##npn = I[20]; \ T& I##pcn = I[21]; T& I##ccn = I[22]; T& I##ncn = I[23]; \ T& I##pnn = I[24]; T& I##cnn = I[25]; T& I##nnn = I[26]; \ I##ppp = I##cpp = I##npp = \ I##pcp = I##ccp = I##ncp = \ I##pnp = I##cnp = I##nnp = \ I##ppc = I##cpc = I##npc = \ I##pcc = I##ccc = I##ncc = \ I##pnc = I##cnc = I##nnc = \ I##ppn = I##cpn = I##npn = \ I##pcn = I##ccn = I##ncn = \ I##pnn = I##cnn = I##nnn = 0 #define cimg_get2x2(img,x,y,z,v,I) \ I[0] = (img)(x,y,z,v), I[1] = (img)(_n1##x,y,z,v), I[2] = (img)(x,_n1##y,z,v), I[3] = (img)(_n1##x,_n1##y,z,v) #define cimg_get3x3(img,x,y,z,v,I) \ I[0] = (img)(_p1##x,_p1##y,z,v), I[1] = (img)(x,_p1##y,z,v), I[2] = (img)(_n1##x,_p1##y,z,v), I[3] = (img)(_p1##x,y,z,v), \ I[4] = (img)(x,y,z,v), I[5] = (img)(_n1##x,y,z,v), I[6] = (img)(_p1##x,_n1##y,z,v), I[7] = (img)(x,_n1##y,z,v), \ I[8] = (img)(_n1##x,_n1##y,z,v) #define cimg_get4x4(img,x,y,z,v,I) \ I[0] = (img)(_p1##x,_p1##y,z,v), I[1] = (img)(x,_p1##y,z,v), I[2] = (img)(_n1##x,_p1##y,z,v), I[3] = (img)(_n2##x,_p1##y,z,v), \ I[4] = (img)(_p1##x,y,z,v), I[5] = (img)(x,y,z,v), I[6] = (img)(_n1##x,y,z,v), I[7] = (img)(_n2##x,y,z,v), \ I[8] = (img)(_p1##x,_n1##y,z,v), I[9] = (img)(x,_n1##y,z,v), I[10] = (img)(_n1##x,_n1##y,z,v), I[11] = (img)(_n2##x,_n1##y,z,v), \ I[12] = (img)(_p1##x,_n2##y,z,v), I[13] = (img)(x,_n2##y,z,v), I[14] = (img)(_n1##x,_n2##y,z,v), I[15] = (img)(_n2##x,_n2##y,z,v) #define cimg_get5x5(img,x,y,z,v,I) \ I[0] = (img)(_p2##x,_p2##y,z,v), I[1] = (img)(_p1##x,_p2##y,z,v), I[2] = (img)(x,_p2##y,z,v), I[3] = (img)(_n1##x,_p2##y,z,v), \ I[4] = (img)(_n2##x,_p2##y,z,v), I[5] = (img)(_p2##x,_p1##y,z,v), I[6] = (img)(_p1##x,_p1##y,z,v), I[7] = (img)(x,_p1##y,z,v), \ I[8] = (img)(_n1##x,_p1##y,z,v), I[9] = (img)(_n2##x,_p1##y,z,v), I[10] = (img)(_p2##x,y,z,v), I[11] = (img)(_p1##x,y,z,v), \ I[12] = (img)(x,y,z,v), I[13] = (img)(_n1##x,y,z,v), I[14] = (img)(_n2##x,y,z,v), I[15] = (img)(_p2##x,_n1##y,z,v), \ I[16] = (img)(_p1##x,_n1##y,z,v), I[17] = (img)(x,_n1##y,z,v), I[18] = (img)(_n1##x,_n1##y,z,v), I[19] = (img)(_n2##x,_n1##y,z,v), \ I[20] = (img)(_p2##x,_n2##y,z,v), I[21] = (img)(_p1##x,_n2##y,z,v), I[22] = (img)(x,_n2##y,z,v), I[23] = (img)(_n1##x,_n2##y,z,v), \ I[24] = (img)(_n2##x,_n2##y,z,v) #define cimg_get6x6(img,x,y,z,v,I) \ I[0] = (img)(_p2##x,_p2##y,z,v), I[1] = (img)(_p1##x,_p2##y,z,v), I[2] = (img)(x,_p2##y,z,v), I[3] = (img)(_n1##x,_p2##y,z,v), \ I[4] = (img)(_n2##x,_p2##y,z,v), I[5] = (img)(_n3##x,_p2##y,z,v), I[6] = (img)(_p2##x,_p1##y,z,v), I[7] = (img)(_p1##x,_p1##y,z,v), \ I[8] = (img)(x,_p1##y,z,v), I[9] = (img)(_n1##x,_p1##y,z,v), I[10] = (img)(_n2##x,_p1##y,z,v), I[11] = (img)(_n3##x,_p1##y,z,v), \ I[12] = (img)(_p2##x,y,z,v), I[13] = (img)(_p1##x,y,z,v), I[14] = (img)(x,y,z,v), I[15] = (img)(_n1##x,y,z,v), \ I[16] = (img)(_n2##x,y,z,v), I[17] = (img)(_n3##x,y,z,v), I[18] = (img)(_p2##x,_n1##y,z,v), I[19] = (img)(_p1##x,_n1##y,z,v), \ I[20] = (img)(x,_n1##y,z,v), I[21] = (img)(_n1##x,_n1##y,z,v), I[22] = (img)(_n2##x,_n1##y,z,v), I[23] = (img)(_n3##x,_n1##y,z,v), \ I[24] = (img)(_p2##x,_n2##y,z,v), I[25] = (img)(_p1##x,_n2##y,z,v), I[26] = (img)(x,_n2##y,z,v), I[27] = (img)(_n1##x,_n2##y,z,v), \ I[28] = (img)(_n2##x,_n2##y,z,v), I[29] = (img)(_n3##x,_n2##y,z,v), I[30] = (img)(_p2##x,_n3##y,z,v), I[31] = (img)(_p1##x,_n3##y,z,v), \ I[32] = (img)(x,_n3##y,z,v), I[33] = (img)(_n1##x,_n3##y,z,v), I[34] = (img)(_n2##x,_n3##y,z,v), I[35] = (img)(_n3##x,_n3##y,z,v) #define cimg_get7x7(img,x,y,z,v,I) \ I[0] = (img)(_p3##x,_p3##y,z,v), I[1] = (img)(_p2##x,_p3##y,z,v), I[2] = (img)(_p1##x,_p3##y,z,v), I[3] = (img)(x,_p3##y,z,v), \ I[4] = (img)(_n1##x,_p3##y,z,v), I[5] = (img)(_n2##x,_p3##y,z,v), I[6] = (img)(_n3##x,_p3##y,z,v), I[7] = (img)(_p3##x,_p2##y,z,v), \ I[8] = (img)(_p2##x,_p2##y,z,v), I[9] = (img)(_p1##x,_p2##y,z,v), I[10] = (img)(x,_p2##y,z,v), I[11] = (img)(_n1##x,_p2##y,z,v), \ I[12] = (img)(_n2##x,_p2##y,z,v), I[13] = (img)(_n3##x,_p2##y,z,v), I[14] = (img)(_p3##x,_p1##y,z,v), I[15] = (img)(_p2##x,_p1##y,z,v), \ I[16] = (img)(_p1##x,_p1##y,z,v), I[17] = (img)(x,_p1##y,z,v), I[18] = (img)(_n1##x,_p1##y,z,v), I[19] = (img)(_n2##x,_p1##y,z,v), \ I[20] = (img)(_n3##x,_p1##y,z,v), I[21] = (img)(_p3##x,y,z,v), I[22] = (img)(_p2##x,y,z,v), I[23] = (img)(_p1##x,y,z,v), \ I[24] = (img)(x,y,z,v), I[25] = (img)(_n1##x,y,z,v), I[26] = (img)(_n2##x,y,z,v), I[27] = (img)(_n3##x,y,z,v), \ I[28] = (img)(_p3##x,_n1##y,z,v), I[29] = (img)(_p2##x,_n1##y,z,v), I[30] = (img)(_p1##x,_n1##y,z,v), I[31] = (img)(x,_n1##y,z,v), \ I[32] = (img)(_n1##x,_n1##y,z,v), I[33] = (img)(_n2##x,_n1##y,z,v), I[34] = (img)(_n3##x,_n1##y,z,v), I[35] = (img)(_p3##x,_n2##y,z,v), \ I[36] = (img)(_p2##x,_n2##y,z,v), I[37] = (img)(_p1##x,_n2##y,z,v), I[38] = (img)(x,_n2##y,z,v), I[39] = (img)(_n1##x,_n2##y,z,v), \ I[40] = (img)(_n2##x,_n2##y,z,v), I[41] = (img)(_n3##x,_n2##y,z,v), I[42] = (img)(_p3##x,_n3##y,z,v), I[43] = (img)(_p2##x,_n3##y,z,v), \ I[44] = (img)(_p1##x,_n3##y,z,v), I[45] = (img)(x,_n3##y,z,v), I[46] = (img)(_n1##x,_n3##y,z,v), I[47] = (img)(_n2##x,_n3##y,z,v), \ I[48] = (img)(_n3##x,_n3##y,z,v) #define cimg_get8x8(img,x,y,z,v,I) \ I[0] = (img)(_p3##x,_p3##y,z,v), I[1] = (img)(_p2##x,_p3##y,z,v), I[2] = (img)(_p1##x,_p3##y,z,v), I[3] = (img)(x,_p3##y,z,v), \ I[4] = (img)(_n1##x,_p3##y,z,v), I[5] = (img)(_n2##x,_p3##y,z,v), I[6] = (img)(_n3##x,_p3##y,z,v), I[7] = (img)(_n4##x,_p3##y,z,v), \ I[8] = (img)(_p3##x,_p2##y,z,v), I[9] = (img)(_p2##x,_p2##y,z,v), I[10] = (img)(_p1##x,_p2##y,z,v), I[11] = (img)(x,_p2##y,z,v), \ I[12] = (img)(_n1##x,_p2##y,z,v), I[13] = (img)(_n2##x,_p2##y,z,v), I[14] = (img)(_n3##x,_p2##y,z,v), I[15] = (img)(_n4##x,_p2##y,z,v), \ I[16] = (img)(_p3##x,_p1##y,z,v), I[17] = (img)(_p2##x,_p1##y,z,v), I[18] = (img)(_p1##x,_p1##y,z,v), I[19] = (img)(x,_p1##y,z,v), \ I[20] = (img)(_n1##x,_p1##y,z,v), I[21] = (img)(_n2##x,_p1##y,z,v), I[22] = (img)(_n3##x,_p1##y,z,v), I[23] = (img)(_n4##x,_p1##y,z,v), \ I[24] = (img)(_p3##x,y,z,v), I[25] = (img)(_p2##x,y,z,v), I[26] = (img)(_p1##x,y,z,v), I[27] = (img)(x,y,z,v), \ I[28] = (img)(_n1##x,y,z,v), I[29] = (img)(_n2##x,y,z,v), I[30] = (img)(_n3##x,y,z,v), I[31] = (img)(_n4##x,y,z,v), \ I[32] = (img)(_p3##x,_n1##y,z,v), I[33] = (img)(_p2##x,_n1##y,z,v), I[34] = (img)(_p1##x,_n1##y,z,v), I[35] = (img)(x,_n1##y,z,v), \ I[36] = (img)(_n1##x,_n1##y,z,v), I[37] = (img)(_n2##x,_n1##y,z,v), I[38] = (img)(_n3##x,_n1##y,z,v), I[39] = (img)(_n4##x,_n1##y,z,v), \ I[40] = (img)(_p3##x,_n2##y,z,v), I[41] = (img)(_p2##x,_n2##y,z,v), I[42] = (img)(_p1##x,_n2##y,z,v), I[43] = (img)(x,_n2##y,z,v), \ I[44] = (img)(_n1##x,_n2##y,z,v), I[45] = (img)(_n2##x,_n2##y,z,v), I[46] = (img)(_n3##x,_n2##y,z,v), I[47] = (img)(_n4##x,_n2##y,z,v), \ I[48] = (img)(_p3##x,_n3##y,z,v), I[49] = (img)(_p2##x,_n3##y,z,v), I[50] = (img)(_p1##x,_n3##y,z,v), I[51] = (img)(x,_n3##y,z,v), \ I[52] = (img)(_n1##x,_n3##y,z,v), I[53] = (img)(_n2##x,_n3##y,z,v), I[54] = (img)(_n3##x,_n3##y,z,v), I[55] = (img)(_n4##x,_n3##y,z,v), \ I[56] = (img)(_p3##x,_n4##y,z,v), I[57] = (img)(_p2##x,_n4##y,z,v), I[58] = (img)(_p1##x,_n4##y,z,v), I[59] = (img)(x,_n4##y,z,v), \ I[60] = (img)(_n1##x,_n4##y,z,v), I[61] = (img)(_n2##x,_n4##y,z,v), I[62] = (img)(_n3##x,_n4##y,z,v), I[63] = (img)(_n4##x,_n4##y,z,v); #define cimg_get9x9(img,x,y,z,v,I) \ I[0] = (img)(_p4##x,_p4##y,z,v), I[1] = (img)(_p3##x,_p4##y,z,v), I[2] = (img)(_p2##x,_p4##y,z,v), I[3] = (img)(_p1##x,_p4##y,z,v), \ I[4] = (img)(x,_p4##y,z,v), I[5] = (img)(_n1##x,_p4##y,z,v), I[6] = (img)(_n2##x,_p4##y,z,v), I[7] = (img)(_n3##x,_p4##y,z,v), \ I[8] = (img)(_n4##x,_p4##y,z,v), I[9] = (img)(_p4##x,_p3##y,z,v), I[10] = (img)(_p3##x,_p3##y,z,v), I[11] = (img)(_p2##x,_p3##y,z,v), \ I[12] = (img)(_p1##x,_p3##y,z,v), I[13] = (img)(x,_p3##y,z,v), I[14] = (img)(_n1##x,_p3##y,z,v), I[15] = (img)(_n2##x,_p3##y,z,v), \ I[16] = (img)(_n3##x,_p3##y,z,v), I[17] = (img)(_n4##x,_p3##y,z,v), I[18] = (img)(_p4##x,_p2##y,z,v), I[19] = (img)(_p3##x,_p2##y,z,v), \ I[20] = (img)(_p2##x,_p2##y,z,v), I[21] = (img)(_p1##x,_p2##y,z,v), I[22] = (img)(x,_p2##y,z,v), I[23] = (img)(_n1##x,_p2##y,z,v), \ I[24] = (img)(_n2##x,_p2##y,z,v), I[25] = (img)(_n3##x,_p2##y,z,v), I[26] = (img)(_n4##x,_p2##y,z,v), I[27] = (img)(_p4##x,_p1##y,z,v), \ I[28] = (img)(_p3##x,_p1##y,z,v), I[29] = (img)(_p2##x,_p1##y,z,v), I[30] = (img)(_p1##x,_p1##y,z,v), I[31] = (img)(x,_p1##y,z,v), \ I[32] = (img)(_n1##x,_p1##y,z,v), I[33] = (img)(_n2##x,_p1##y,z,v), I[34] = (img)(_n3##x,_p1##y,z,v), I[35] = (img)(_n4##x,_p1##y,z,v), \ I[36] = (img)(_p4##x,y,z,v), I[37] = (img)(_p3##x,y,z,v), I[38] = (img)(_p2##x,y,z,v), I[39] = (img)(_p1##x,y,z,v), \ I[40] = (img)(x,y,z,v), I[41] = (img)(_n1##x,y,z,v), I[42] = (img)(_n2##x,y,z,v), I[43] = (img)(_n3##x,y,z,v), \ I[44] = (img)(_n4##x,y,z,v), I[45] = (img)(_p4##x,_n1##y,z,v), I[46] = (img)(_p3##x,_n1##y,z,v), I[47] = (img)(_p2##x,_n1##y,z,v), \ I[48] = (img)(_p1##x,_n1##y,z,v), I[49] = (img)(x,_n1##y,z,v), I[50] = (img)(_n1##x,_n1##y,z,v), I[51] = (img)(_n2##x,_n1##y,z,v), \ I[52] = (img)(_n3##x,_n1##y,z,v), I[53] = (img)(_n4##x,_n1##y,z,v), I[54] = (img)(_p4##x,_n2##y,z,v), I[55] = (img)(_p3##x,_n2##y,z,v), \ I[56] = (img)(_p2##x,_n2##y,z,v), I[57] = (img)(_p1##x,_n2##y,z,v), I[58] = (img)(x,_n2##y,z,v), I[59] = (img)(_n1##x,_n2##y,z,v), \ I[60] = (img)(_n2##x,_n2##y,z,v), I[61] = (img)(_n3##x,_n2##y,z,v), I[62] = (img)(_n4##x,_n2##y,z,v), I[63] = (img)(_p4##x,_n3##y,z,v), \ I[64] = (img)(_p3##x,_n3##y,z,v), I[65] = (img)(_p2##x,_n3##y,z,v), I[66] = (img)(_p1##x,_n3##y,z,v), I[67] = (img)(x,_n3##y,z,v), \ I[68] = (img)(_n1##x,_n3##y,z,v), I[69] = (img)(_n2##x,_n3##y,z,v), I[70] = (img)(_n3##x,_n3##y,z,v), I[71] = (img)(_n4##x,_n3##y,z,v), \ I[72] = (img)(_p4##x,_n4##y,z,v), I[73] = (img)(_p3##x,_n4##y,z,v), I[74] = (img)(_p2##x,_n4##y,z,v), I[75] = (img)(_p1##x,_n4##y,z,v), \ I[76] = (img)(x,_n4##y,z,v), I[77] = (img)(_n1##x,_n4##y,z,v), I[78] = (img)(_n2##x,_n4##y,z,v), I[79] = (img)(_n3##x,_n4##y,z,v), \ I[80] = (img)(_n4##x,_n4##y,z,v) #define cimg_get2x2x2(img,x,y,z,v,I) \ I[0] = (img)(x,y,z,v), I[1] = (img)(_n1##x,y,z,v), I[2] = (img)(x,_n1##y,z,v), I[3] = (img)(_n1##x,_n1##y,z,v), \ I[4] = (img)(x,y,_n1##z,v), I[5] = (img)(_n1##x,y,_n1##z,v), I[6] = (img)(x,_n1##y,_n1##z,v), I[7] = (img)(_n1##x,_n1##y,_n1##z,v) #define cimg_get3x3x3(img,x,y,z,v,I) \ I[0] = (img)(_p1##x,_p1##y,_p1##z,v), I[1] = (img)(x,_p1##y,_p1##z,v), I[2] = (img)(_n1##x,_p1##y,_p1##z,v), \ I[3] = (img)(_p1##x,y,_p1##z,v), I[4] = (img)(x,y,_p1##z,v), I[5] = (img)(_n1##x,y,_p1##z,v), \ I[6] = (img)(_p1##x,_n1##y,_p1##z,v), I[7] = (img)(x,_n1##y,_p1##z,v), I[8] = (img)(_n1##x,_n1##y,_p1##z,v), \ I[9] = (img)(_p1##x,_p1##y,z,v), I[10] = (img)(x,_p1##y,z,v), I[11] = (img)(_n1##x,_p1##y,z,v), \ I[12] = (img)(_p1##x,y,z,v), I[13] = (img)(x,y,z,v), I[14] = (img)(_n1##x,y,z,v), \ I[15] = (img)(_p1##x,_n1##y,z,v), I[16] = (img)(x,_n1##y,z,v), I[17] = (img)(_n1##x,_n1##y,z,v), \ I[18] = (img)(_p1##x,_p1##y,_n1##z,v), I[19] = (img)(x,_p1##y,_n1##z,v), I[20] = (img)(_n1##x,_p1##y,_n1##z,v), \ I[21] = (img)(_p1##x,y,_n1##z,v), I[22] = (img)(x,y,_n1##z,v), I[23] = (img)(_n1##x,y,_n1##z,v), \ I[24] = (img)(_p1##x,_n1##y,_n1##z,v), I[25] = (img)(x,_n1##y,_n1##z,v), I[26] = (img)(_n1##x,_n1##y,_n1##z,v) // Define various image loops. // // These macros generally avoid the use of iterators, but you are not forced to used them ! // #define cimg_for(img,ptr,T_ptr) for (T_ptr *ptr = (img).data + (img).size(); (ptr--)>(img).data; ) #define cimg_foroff(img,off) for (unsigned int off = 0, _max##off = (unsigned int)(img).size(); off<_max##off; ++off) #define cimglist_for(list,l) for (unsigned int l=0; l<(list).size; ++l) #define cimglist_apply(list,fn) cimglist_for(list,__##fn) (list)[__##fn].fn #define cimg_for1(bound,i) for (int i = 0; i<(int)(bound); ++i) #define cimg_forX(img,x) cimg_for1((img).width,x) #define cimg_forY(img,y) cimg_for1((img).height,y) #define cimg_forZ(img,z) cimg_for1((img).depth,z) #define cimg_forV(img,v) cimg_for1((img).dim,v) #define cimg_forXY(img,x,y) cimg_forY(img,y) cimg_forX(img,x) #define cimg_forXZ(img,x,z) cimg_forZ(img,z) cimg_forX(img,x) #define cimg_forYZ(img,y,z) cimg_forZ(img,z) cimg_forY(img,y) #define cimg_forXV(img,x,v) cimg_forV(img,v) cimg_forX(img,x) #define cimg_forYV(img,y,v) cimg_forV(img,v) cimg_forY(img,y) #define cimg_forZV(img,z,v) cimg_forV(img,v) cimg_forZ(img,z) #define cimg_forXYZ(img,x,y,z) cimg_forZ(img,z) cimg_forXY(img,x,y) #define cimg_forXYV(img,x,y,v) cimg_forV(img,v) cimg_forXY(img,x,y) #define cimg_forXZV(img,x,z,v) cimg_forV(img,v) cimg_forXZ(img,x,z) #define cimg_forYZV(img,y,z,v) cimg_forV(img,v) cimg_forYZ(img,y,z) #define cimg_forXYZV(img,x,y,z,v) cimg_forV(img,v) cimg_forXYZ(img,x,y,z) #define cimg_for_in1(bound,i0,i1,i) \ for (int i = (int)(i0)<0?0:(int)(i0), _max##i = (int)(i1)<(int)(bound)?(int)(i1):(int)(bound)-1; i<=_max##i; ++i) #define cimg_for_inX(img,x0,x1,x) cimg_for_in1((img).width,x0,x1,x) #define cimg_for_inY(img,y0,y1,y) cimg_for_in1((img).height,y0,y1,y) #define cimg_for_inZ(img,z0,z1,z) cimg_for_in1((img).depth,z0,z1,z) #define cimg_for_inV(img,v0,v1,v) cimg_for_in1((img).dim,v0,v1,v) #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) #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) #define cimg_for_inXV(img,x0,v0,x1,v1,x,v) cimg_for_inV(img,v0,v1,v) cimg_for_inX(img,x0,x1,x) #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) #define cimg_for_inYV(img,y0,v0,y1,v1,y,v) cimg_for_inV(img,v0,v1,v) cimg_for_inY(img,y0,y1,y) #define cimg_for_inZV(img,z0,v0,z1,v1,z,v) cimg_for_inV(img,v0,v1,v) cimg_for_inZ(img,z0,z1,z) #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) #define cimg_for_inXYV(img,x0,y0,v0,x1,y1,v1,x,y,v) cimg_for_inV(img,v0,v1,v) cimg_for_inXY(img,x0,y0,x1,y1,x,y) #define cimg_for_inXZV(img,x0,z0,v0,x1,z1,v1,x,z,v) cimg_for_inV(img,v0,v1,v) cimg_for_inXZ(img,x0,z0,x1,z1,x,z) #define cimg_for_inYZV(img,y0,z0,v0,y1,z1,v1,y,z,v) cimg_for_inV(img,v0,v1,v) cimg_for_inYZ(img,y0,z0,y1,z1,y,z) #define cimg_for_inXYZV(img,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) cimg_for_inV(img,v0,v1,v) cimg_for_inXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) #define cimg_for_insideX(img,x,n) cimg_for_inX(img,n,(img).width-1-(n),x) #define cimg_for_insideY(img,y,n) cimg_for_inY(img,n,(img).height-1-(n),y) #define cimg_for_insideZ(img,z,n) cimg_for_inZ(img,n,(img).depth-1-(n),z) #define cimg_for_insideV(img,v,n) cimg_for_inV(img,n,(img).dim-1-(n),v) #define cimg_for_insideXY(img,x,y,n) cimg_for_inXY(img,n,n,(img).width-1-(n),(img).height-1-(n),x,y) #define cimg_for_insideXYZ(img,x,y,z,n) cimg_for_inXYZ(img,n,n,n,(img).width-1-(n),(img).height-1-(n),(img).depth-1-(n),x,y,z) #define cimg_for_insideXYZV(img,x,y,z,v,n) cimg_for_inXYZ(img,n,n,n,(img).width-1-(n),(img).height-1-(n),(img).depth-1-(n),x,y,z) #define cimg_for_out1(boundi,i0,i1,i) \ for (int i = (int)(i0)>0?0:(int)(i1)+1; i<(int)(boundi); ++i, i = i==(int)(i0)?(int)(i1)+1:i) #define cimg_for_out2(boundi,boundj,i0,j0,i1,j1,i,j) \ for (int j = 0; j<(int)(boundj); ++j) \ for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j?0:(int)(i0)>0?0:(int)(i1)+1; i<(int)(boundi); \ ++i, i = _n1j?i:(i==(int)(i0)?(int)(i1)+1:i)) #define cimg_for_out3(boundi,boundj,boundk,i0,j0,k0,i1,j1,k1,i,j,k) \ for (int k = 0; k<(int)(boundk); ++k) \ for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \ for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k?0:(int)(i0)>0?0:(int)(i1)+1; i<(int)(boundi); \ ++i, i = _n1j || _n1k?i:(i==(int)(i0)?(int)(i1)+1:i)) #define cimg_for_out4(boundi,boundj,boundk,boundl,i0,j0,k0,l0,i1,j1,k1,l1,i,j,k,l) \ for (int l = 0; l<(int)(boundl); ++l) \ for (int _n1l = (int)(l<(int)(l0) || l>(int)(l1)), k = 0; k<(int)(boundk); ++k) \ for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \ for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k || _n1l?0:(int)(i0)>0?0:(int)(i1)+1; i<(int)(boundi); \ ++i, i = _n1j || _n1k || _n1l?i:(i==(int)(i0)?(int)(i1)+1:i)) #define cimg_for_outX(img,x0,x1,x) cimg_for_out1((img).width,x0,x1,x) #define cimg_for_outY(img,y0,y1,y) cimg_for_out1((img).height,y0,y1,y) #define cimg_for_outZ(img,z0,z1,z) cimg_for_out1((img).depth,z0,z1,z) #define cimg_for_outV(img,v0,v1,v) cimg_for_out1((img).dim,v0,v1,v) #define cimg_for_outXY(img,x0,y0,x1,y1,x,y) cimg_for_out2((img).width,(img).height,x0,y0,x1,y1,x,y) #define cimg_for_outXZ(img,x0,z0,x1,z1,x,z) cimg_for_out2((img).width,(img).depth,x0,z0,x1,z1,x,z) #define cimg_for_outXV(img,x0,v0,x1,v1,x,v) cimg_for_out2((img).width,(img).dim,x0,v0,x1,v1,x,v) #define cimg_for_outYZ(img,y0,z0,y1,z1,y,z) cimg_for_out2((img).height,(img).depth,y0,z0,y1,z1,y,z) #define cimg_for_outYV(img,y0,v0,y1,v1,y,v) cimg_for_out2((img).height,(img).dim,y0,v0,y1,v1,y,v) #define cimg_for_outZV(img,z0,v0,z1,v1,z,v) cimg_for_out2((img).depth,(img).dim,z0,v0,z1,v1,z,v) #define cimg_for_outXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_out3((img).width,(img).height,(img).depth,x0,y0,z0,x1,y1,z1,x,y,z) #define cimg_for_outXYV(img,x0,y0,v0,x1,y1,v1,x,y,v) cimg_for_out3((img).width,(img).height,(img).dim,x0,y0,v0,x1,y1,v1,x,y,v) #define cimg_for_outXZV(img,x0,z0,v0,x1,z1,v1,x,z,v) cimg_for_out3((img).width,(img).depth,(img).dim,x0,z0,v0,x1,z1,v1,x,z,v) #define cimg_for_outYZV(img,y0,z0,v0,y1,z1,v1,y,z,v) cimg_for_out3((img).height,(img).depth,(img).dim,y0,z0,v0,y1,z1,v1,y,z,v) #define cimg_for_outXYZV(img,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) \ cimg_for_out4((img).width,(img).height,(img).depth,(img).dim,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) #define cimg_for_borderX(img,x,n) cimg_for_outX(img,n,(img).width-1-(n),x) #define cimg_for_borderY(img,y,n) cimg_for_outY(img,n,(img).height-1-(n),y) #define cimg_for_borderZ(img,z,n) cimg_for_outZ(img,n,(img).depth-1-(n),z) #define cimg_for_borderV(img,v,n) cimg_for_outV(img,n,(img).dim-1-(n),v) #define cimg_for_borderXY(img,x,y,n) cimg_for_outXY(img,n,n,(img).width-1-(n),(img).height-1-(n),x,y) #define cimg_for_borderXYZ(img,x,y,z,n) cimg_for_outXYZ(img,n,n,n,(img).width-1-(n),(img).height-1-(n),(img).depth-1-(n),x,y,z) #define cimg_for_borderXYZV(img,x,y,z,v,n) \ cimg_for_outXYZV(img,n,n,n,n,(img).width-1-(n),(img).height-1-(n),(img).depth-1-(n),(img).dim-1-(n),x,y,z,v) #define cimg_for_spiralXY(img,x,y) \ for (int x = 0, y = 0, _n1##x = 1, _n1##y = (int)((img).width*(img).height); _n1##y; \ --_n1##y, _n1##x += (_n1##x>>2)-((!(_n1##x&3)?--y:((_n1##x&3)==1?(img).width-1-++x:((_n1##x&3)==2?(img).height-1-++y:--x))))?0:1) #define cimg_for_lineXY(x,y,x0,y0,x1,y1) \ for (int x = (int)(x0), y = (int)(y0), _sx = 1, _sy = 1, _steep = 0, \ _dx=(x1)>(x0)?(int)(x1)-(int)(x0):(_sx=-1,(int)(x0)-(int)(x1)), \ _dy=(y1)>(y0)?(int)(y1)-(int)(y0):(_sy=-1,(int)(y0)-(int)(y1)), \ _counter = _dx, \ _err = _dx>_dy?(_dy>>1):((_steep=1),(_counter=_dy),(_dx>>1)); \ _counter>=0; \ --_counter, x+=_steep? \ (y+=_sy,(_err-=_dx)<0?_err+=_dy,_sx:0): \ (y+=(_err-=_dy)<0?_err+=_dx,_sy:0,_sx)) #define cimg_for2(bound,i) \ for (int i = 0, _n1##i = 1>=(bound)?(int)(bound)-1:1; \ _n1##i<(int)(bound) || i==--_n1##i; \ ++i, ++_n1##i) #define cimg_for2X(img,x) cimg_for2((img).width,x) #define cimg_for2Y(img,y) cimg_for2((img).height,y) #define cimg_for2Z(img,z) cimg_for2((img).depth,z) #define cimg_for2V(img,v) cimg_for2((img).dim,v) #define cimg_for2XY(img,x,y) cimg_for2Y(img,y) cimg_for2X(img,x) #define cimg_for2XZ(img,x,z) cimg_for2Z(img,z) cimg_for2X(img,x) #define cimg_for2XV(img,x,v) cimg_for2V(img,v) cimg_for2X(img,x) #define cimg_for2YZ(img,y,z) cimg_for2Z(img,z) cimg_for2Y(img,y) #define cimg_for2YV(img,y,v) cimg_for2V(img,v) cimg_for2Y(img,y) #define cimg_for2ZV(img,z,v) cimg_for2V(img,v) cimg_for2Z(img,z) #define cimg_for2XYZ(img,x,y,z) cimg_for2Z(img,z) cimg_for2XY(img,x,y) #define cimg_for2XZV(img,x,z,v) cimg_for2V(img,v) cimg_for2XZ(img,x,z) #define cimg_for2YZV(img,y,z,v) cimg_for2V(img,v) cimg_for2YZ(img,y,z) #define cimg_for2XYZV(img,x,y,z,v) cimg_for2V(img,v) cimg_for2XYZ(img,x,y,z) #define cimg_for_in2(bound,i0,i1,i) \ for (int i = (int)(i0)<0?0:(int)(i0), \ _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1; \ i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \ ++i, ++_n1##i) #define cimg_for_in2X(img,x0,x1,x) cimg_for_in2((img).width,x0,x1,x) #define cimg_for_in2Y(img,y0,y1,y) cimg_for_in2((img).height,y0,y1,y) #define cimg_for_in2Z(img,z0,z1,z) cimg_for_in2((img).depth,z0,z1,z) #define cimg_for_in2V(img,v0,v1,v) cimg_for_in2((img).dim,v0,v1,v) #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) #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) #define cimg_for_in2XV(img,x0,v0,x1,v1,x,v) cimg_for_in2V(img,v0,v1,v) cimg_for_in2X(img,x0,x1,x) #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) #define cimg_for_in2YV(img,y0,v0,y1,v1,y,v) cimg_for_in2V(img,v0,v1,v) cimg_for_in2Y(img,y0,y1,y) #define cimg_for_in2ZV(img,z0,v0,z1,v1,z,v) cimg_for_in2V(img,v0,v1,v) cimg_for_in2Z(img,z0,z1,z) #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) #define cimg_for_in2XZV(img,x0,z0,v0,x1,y1,v1,x,z,v) cimg_for_in2V(img,v0,v1,v) cimg_for_in2XZ(img,x0,y0,x1,y1,x,z) #define cimg_for_in2YZV(img,y0,z0,v0,y1,z1,v1,y,z,v) cimg_for_in2V(img,v0,v1,v) cimg_for_in2YZ(img,y0,z0,y1,z1,y,z) #define cimg_for_in2XYZV(img,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) cimg_for_in2V(img,v0,v1,v) cimg_for_in2XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) #define cimg_for3(bound,i) \ for (int i = 0, _p1##i = 0, \ _n1##i = 1>=(bound)?(int)(bound)-1:1; \ _n1##i<(int)(bound) || i==--_n1##i; \ _p1##i = i++, ++_n1##i) #define cimg_for3X(img,x) cimg_for3((img).width,x) #define cimg_for3Y(img,y) cimg_for3((img).height,y) #define cimg_for3Z(img,z) cimg_for3((img).depth,z) #define cimg_for3V(img,v) cimg_for3((img).dim,v) #define cimg_for3XY(img,x,y) cimg_for3Y(img,y) cimg_for3X(img,x) #define cimg_for3XZ(img,x,z) cimg_for3Z(img,z) cimg_for3X(img,x) #define cimg_for3XV(img,x,v) cimg_for3V(img,v) cimg_for3X(img,x) #define cimg_for3YZ(img,y,z) cimg_for3Z(img,z) cimg_for3Y(img,y) #define cimg_for3YV(img,y,v) cimg_for3V(img,v) cimg_for3Y(img,y) #define cimg_for3ZV(img,z,v) cimg_for3V(img,v) cimg_for3Z(img,z) #define cimg_for3XYZ(img,x,y,z) cimg_for3Z(img,z) cimg_for3XY(img,x,y) #define cimg_for3XZV(img,x,z,v) cimg_for3V(img,v) cimg_for3XZ(img,x,z) #define cimg_for3YZV(img,y,z,v) cimg_for3V(img,v) cimg_for3YZ(img,y,z) #define cimg_for3XYZV(img,x,y,z,v) cimg_for3V(img,v) cimg_for3XYZ(img,x,y,z) #define cimg_for_in3(bound,i0,i1,i) \ for (int i = (int)(i0)<0?0:(int)(i0), \ _p1##i = i-1<0?0:i-1, \ _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1; \ i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \ _p1##i = i++, ++_n1##i) #define cimg_for_in3X(img,x0,x1,x) cimg_for_in3((img).width,x0,x1,x) #define cimg_for_in3Y(img,y0,y1,y) cimg_for_in3((img).height,y0,y1,y) #define cimg_for_in3Z(img,z0,z1,z) cimg_for_in3((img).depth,z0,z1,z) #define cimg_for_in3V(img,v0,v1,v) cimg_for_in3((img).dim,v0,v1,v) #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) #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) #define cimg_for_in3XV(img,x0,v0,x1,v1,x,v) cimg_for_in3V(img,v0,v1,v) cimg_for_in3X(img,x0,x1,x) #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) #define cimg_for_in3YV(img,y0,v0,y1,v1,y,v) cimg_for_in3V(img,v0,v1,v) cimg_for_in3Y(img,y0,y1,y) #define cimg_for_in3ZV(img,z0,v0,z1,v1,z,v) cimg_for_in3V(img,v0,v1,v) cimg_for_in3Z(img,z0,z1,z) #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) #define cimg_for_in3XZV(img,x0,z0,v0,x1,y1,v1,x,z,v) cimg_for_in3V(img,v0,v1,v) cimg_for_in3XZ(img,x0,y0,x1,y1,x,z) #define cimg_for_in3YZV(img,y0,z0,v0,y1,z1,v1,y,z,v) cimg_for_in3V(img,v0,v1,v) cimg_for_in3YZ(img,y0,z0,y1,z1,y,z) #define cimg_for_in3XYZV(img,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) cimg_for_in3V(img,v0,v1,v) cimg_for_in3XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) #define cimg_for4(bound,i) \ for (int i = 0, _p1##i = 0, _n1##i = 1>=(bound)?(int)(bound)-1:1, \ _n2##i = 2>=(bound)?(int)(bound)-1:2; \ _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \ _p1##i = i++, ++_n1##i, ++_n2##i) #define cimg_for4X(img,x) cimg_for4((img).width,x) #define cimg_for4Y(img,y) cimg_for4((img).height,y) #define cimg_for4Z(img,z) cimg_for4((img).depth,z) #define cimg_for4V(img,v) cimg_for4((img).dim,v) #define cimg_for4XY(img,x,y) cimg_for4Y(img,y) cimg_for4X(img,x) #define cimg_for4XZ(img,x,z) cimg_for4Z(img,z) cimg_for4X(img,x) #define cimg_for4XV(img,x,v) cimg_for4V(img,v) cimg_for4X(img,x) #define cimg_for4YZ(img,y,z) cimg_for4Z(img,z) cimg_for4Y(img,y) #define cimg_for4YV(img,y,v) cimg_for4V(img,v) cimg_for4Y(img,y) #define cimg_for4ZV(img,z,v) cimg_for4V(img,v) cimg_for4Z(img,z) #define cimg_for4XYZ(img,x,y,z) cimg_for4Z(img,z) cimg_for4XY(img,x,y) #define cimg_for4XZV(img,x,z,v) cimg_for4V(img,v) cimg_for4XZ(img,x,z) #define cimg_for4YZV(img,y,z,v) cimg_for4V(img,v) cimg_for4YZ(img,y,z) #define cimg_for4XYZV(img,x,y,z,v) cimg_for4V(img,v) cimg_for4XYZ(img,x,y,z) #define cimg_for_in4(bound,i0,i1,i) \ for (int i = (int)(i0)<0?0:(int)(i0), \ _p1##i = i-1<0?0:i-1, \ _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \ _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2; \ i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \ _p1##i = i++, ++_n1##i, ++_n2##i) #define cimg_for_in4X(img,x0,x1,x) cimg_for_in4((img).width,x0,x1,x) #define cimg_for_in4Y(img,y0,y1,y) cimg_for_in4((img).height,y0,y1,y) #define cimg_for_in4Z(img,z0,z1,z) cimg_for_in4((img).depth,z0,z1,z) #define cimg_for_in4V(img,v0,v1,v) cimg_for_in4((img).dim,v0,v1,v) #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) #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) #define cimg_for_in4XV(img,x0,v0,x1,v1,x,v) cimg_for_in4V(img,v0,v1,v) cimg_for_in4X(img,x0,x1,x) #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) #define cimg_for_in4YV(img,y0,v0,y1,v1,y,v) cimg_for_in4V(img,v0,v1,v) cimg_for_in4Y(img,y0,y1,y) #define cimg_for_in4ZV(img,z0,v0,z1,v1,z,v) cimg_for_in4V(img,v0,v1,v) cimg_for_in4Z(img,z0,z1,z) #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) #define cimg_for_in4XZV(img,x0,z0,v0,x1,y1,v1,x,z,v) cimg_for_in4V(img,v0,v1,v) cimg_for_in4XZ(img,x0,y0,x1,y1,x,z) #define cimg_for_in4YZV(img,y0,z0,v0,y1,z1,v1,y,z,v) cimg_for_in4V(img,v0,v1,v) cimg_for_in4YZ(img,y0,z0,y1,z1,y,z) #define cimg_for_in4XYZV(img,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) cimg_for_in4V(img,v0,v1,v) cimg_for_in4XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) #define cimg_for5(bound,i) \ for (int i = 0, _p2##i = 0, _p1##i = 0, \ _n1##i = 1>=(bound)?(int)(bound)-1:1, \ _n2##i = 2>=(bound)?(int)(bound)-1:2; \ _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \ _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i) #define cimg_for5X(img,x) cimg_for5((img).width,x) #define cimg_for5Y(img,y) cimg_for5((img).height,y) #define cimg_for5Z(img,z) cimg_for5((img).depth,z) #define cimg_for5V(img,v) cimg_for5((img).dim,v) #define cimg_for5XY(img,x,y) cimg_for5Y(img,y) cimg_for5X(img,x) #define cimg_for5XZ(img,x,z) cimg_for5Z(img,z) cimg_for5X(img,x) #define cimg_for5XV(img,x,v) cimg_for5V(img,v) cimg_for5X(img,x) #define cimg_for5YZ(img,y,z) cimg_for5Z(img,z) cimg_for5Y(img,y) #define cimg_for5YV(img,y,v) cimg_for5V(img,v) cimg_for5Y(img,y) #define cimg_for5ZV(img,z,v) cimg_for5V(img,v) cimg_for5Z(img,z) #define cimg_for5XYZ(img,x,y,z) cimg_for5Z(img,z) cimg_for5XY(img,x,y) #define cimg_for5XZV(img,x,z,v) cimg_for5V(img,v) cimg_for5XZ(img,x,z) #define cimg_for5YZV(img,y,z,v) cimg_for5V(img,v) cimg_for5YZ(img,y,z) #define cimg_for5XYZV(img,x,y,z,v) cimg_for5V(img,v) cimg_for5XYZ(img,x,y,z) #define cimg_for_in5(bound,i0,i1,i) \ for (int i = (int)(i0)<0?0:(int)(i0), \ _p2##i = i-2<0?0:i-2, \ _p1##i = i-1<0?0:i-1, \ _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \ _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2; \ i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \ _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i) #define cimg_for_in5X(img,x0,x1,x) cimg_for_in5((img).width,x0,x1,x) #define cimg_for_in5Y(img,y0,y1,y) cimg_for_in5((img).height,y0,y1,y) #define cimg_for_in5Z(img,z0,z1,z) cimg_for_in5((img).depth,z0,z1,z) #define cimg_for_in5V(img,v0,v1,v) cimg_for_in5((img).dim,v0,v1,v) #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) #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) #define cimg_for_in5XV(img,x0,v0,x1,v1,x,v) cimg_for_in5V(img,v0,v1,v) cimg_for_in5X(img,x0,x1,x) #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) #define cimg_for_in5YV(img,y0,v0,y1,v1,y,v) cimg_for_in5V(img,v0,v1,v) cimg_for_in5Y(img,y0,y1,y) #define cimg_for_in5ZV(img,z0,v0,z1,v1,z,v) cimg_for_in5V(img,v0,v1,v) cimg_for_in5Z(img,z0,z1,z) #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) #define cimg_for_in5XZV(img,x0,z0,v0,x1,y1,v1,x,z,v) cimg_for_in5V(img,v0,v1,v) cimg_for_in5XZ(img,x0,y0,x1,y1,x,z) #define cimg_for_in5YZV(img,y0,z0,v0,y1,z1,v1,y,z,v) cimg_for_in5V(img,v0,v1,v) cimg_for_in5YZ(img,y0,z0,y1,z1,y,z) #define cimg_for_in5XYZV(img,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) cimg_for_in5V(img,v0,v1,v) cimg_for_in5XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) #define cimg_for6(bound,i) \ for (int i = 0, _p2##i = 0, _p1##i = 0, \ _n1##i = 1>=(bound)?(int)(bound)-1:1, \ _n2##i = 2>=(bound)?(int)(bound)-1:2, \ _n3##i = 3>=(bound)?(int)(bound)-1:3; \ _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \ _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) #define cimg_for6X(img,x) cimg_for6((img).width,x) #define cimg_for6Y(img,y) cimg_for6((img).height,y) #define cimg_for6Z(img,z) cimg_for6((img).depth,z) #define cimg_for6V(img,v) cimg_for6((img).dim,v) #define cimg_for6XY(img,x,y) cimg_for6Y(img,y) cimg_for6X(img,x) #define cimg_for6XZ(img,x,z) cimg_for6Z(img,z) cimg_for6X(img,x) #define cimg_for6XV(img,x,v) cimg_for6V(img,v) cimg_for6X(img,x) #define cimg_for6YZ(img,y,z) cimg_for6Z(img,z) cimg_for6Y(img,y) #define cimg_for6YV(img,y,v) cimg_for6V(img,v) cimg_for6Y(img,y) #define cimg_for6ZV(img,z,v) cimg_for6V(img,v) cimg_for6Z(img,z) #define cimg_for6XYZ(img,x,y,z) cimg_for6Z(img,z) cimg_for6XY(img,x,y) #define cimg_for6XZV(img,x,z,v) cimg_for6V(img,v) cimg_for6XZ(img,x,z) #define cimg_for6YZV(img,y,z,v) cimg_for6V(img,v) cimg_for6YZ(img,y,z) #define cimg_for6XYZV(img,x,y,z,v) cimg_for6V(img,v) cimg_for6XYZ(img,x,y,z) #define cimg_for_in6(bound,i0,i1,i) \ for (int i = (int)(i0)<0?0:(int)(i0), \ _p2##i = i-2<0?0:i-2, \ _p1##i = i-1<0?0:i-1, \ _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \ _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \ _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3; \ i<=(int)(i1) && (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \ _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) #define cimg_for_in6X(img,x0,x1,x) cimg_for_in6((img).width,x0,x1,x) #define cimg_for_in6Y(img,y0,y1,y) cimg_for_in6((img).height,y0,y1,y) #define cimg_for_in6Z(img,z0,z1,z) cimg_for_in6((img).depth,z0,z1,z) #define cimg_for_in6V(img,v0,v1,v) cimg_for_in6((img).dim,v0,v1,v) #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) #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) #define cimg_for_in6XV(img,x0,v0,x1,v1,x,v) cimg_for_in6V(img,v0,v1,v) cimg_for_in6X(img,x0,x1,x) #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) #define cimg_for_in6YV(img,y0,v0,y1,v1,y,v) cimg_for_in6V(img,v0,v1,v) cimg_for_in6Y(img,y0,y1,y) #define cimg_for_in6ZV(img,z0,v0,z1,v1,z,v) cimg_for_in6V(img,v0,v1,v) cimg_for_in6Z(img,z0,z1,z) #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) #define cimg_for_in6XZV(img,x0,z0,v0,x1,y1,v1,x,z,v) cimg_for_in6V(img,v0,v1,v) cimg_for_in6XZ(img,x0,y0,x1,y1,x,z) #define cimg_for_in6YZV(img,y0,z0,v0,y1,z1,v1,y,z,v) cimg_for_in6V(img,v0,v1,v) cimg_for_in6YZ(img,y0,z0,y1,z1,y,z) #define cimg_for_in6XYZV(img,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) cimg_for_in6V(img,v0,v1,v) cimg_for_in6XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) #define cimg_for7(bound,i) \ for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \ _n1##i = 1>=(bound)?(int)(bound)-1:1, \ _n2##i = 2>=(bound)?(int)(bound)-1:2, \ _n3##i = 3>=(bound)?(int)(bound)-1:3; \ _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \ _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) #define cimg_for7X(img,x) cimg_for7((img).width,x) #define cimg_for7Y(img,y) cimg_for7((img).height,y) #define cimg_for7Z(img,z) cimg_for7((img).depth,z) #define cimg_for7V(img,v) cimg_for7((img).dim,v) #define cimg_for7XY(img,x,y) cimg_for7Y(img,y) cimg_for7X(img,x) #define cimg_for7XZ(img,x,z) cimg_for7Z(img,z) cimg_for7X(img,x) #define cimg_for7XV(img,x,v) cimg_for7V(img,v) cimg_for7X(img,x) #define cimg_for7YZ(img,y,z) cimg_for7Z(img,z) cimg_for7Y(img,y) #define cimg_for7YV(img,y,v) cimg_for7V(img,v) cimg_for7Y(img,y) #define cimg_for7ZV(img,z,v) cimg_for7V(img,v) cimg_for7Z(img,z) #define cimg_for7XYZ(img,x,y,z) cimg_for7Z(img,z) cimg_for7XY(img,x,y) #define cimg_for7XZV(img,x,z,v) cimg_for7V(img,v) cimg_for7XZ(img,x,z) #define cimg_for7YZV(img,y,z,v) cimg_for7V(img,v) cimg_for7YZ(img,y,z) #define cimg_for7XYZV(img,x,y,z,v) cimg_for7V(img,v) cimg_for7XYZ(img,x,y,z) #define cimg_for_in7(bound,i0,i1,i) \ for (int i = (int)(i0)<0?0:(int)(i0), \ _p3##i = i-3<0?0:i-3, \ _p2##i = i-2<0?0:i-2, \ _p1##i = i-1<0?0:i-1, \ _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \ _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \ _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3; \ i<=(int)(i1) && (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \ _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) #define cimg_for_in7X(img,x0,x1,x) cimg_for_in7((img).width,x0,x1,x) #define cimg_for_in7Y(img,y0,y1,y) cimg_for_in7((img).height,y0,y1,y) #define cimg_for_in7Z(img,z0,z1,z) cimg_for_in7((img).depth,z0,z1,z) #define cimg_for_in7V(img,v0,v1,v) cimg_for_in7((img).dim,v0,v1,v) #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) #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) #define cimg_for_in7XV(img,x0,v0,x1,v1,x,v) cimg_for_in7V(img,v0,v1,v) cimg_for_in7X(img,x0,x1,x) #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) #define cimg_for_in7YV(img,y0,v0,y1,v1,y,v) cimg_for_in7V(img,v0,v1,v) cimg_for_in7Y(img,y0,y1,y) #define cimg_for_in7ZV(img,z0,v0,z1,v1,z,v) cimg_for_in7V(img,v0,v1,v) cimg_for_in7Z(img,z0,z1,z) #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) #define cimg_for_in7XZV(img,x0,z0,v0,x1,y1,v1,x,z,v) cimg_for_in7V(img,v0,v1,v) cimg_for_in7XZ(img,x0,y0,x1,y1,x,z) #define cimg_for_in7YZV(img,y0,z0,v0,y1,z1,v1,y,z,v) cimg_for_in7V(img,v0,v1,v) cimg_for_in7YZ(img,y0,z0,y1,z1,y,z) #define cimg_for_in7XYZV(img,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) cimg_for_in7V(img,v0,v1,v) cimg_for_in7XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) #define cimg_for8(bound,i) \ for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \ _n1##i = 1>=(bound)?(int)(bound)-1:1, \ _n2##i = 2>=(bound)?(int)(bound)-1:2, \ _n3##i = 3>=(bound)?(int)(bound)-1:3, \ _n4##i = 4>=(bound)?(int)(bound)-1:4; \ _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \ i==(_n4##i = _n3##i = _n2##i = --_n1##i); \ _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i) #define cimg_for8X(img,x) cimg_for8((img).width,x) #define cimg_for8Y(img,y) cimg_for8((img).height,y) #define cimg_for8Z(img,z) cimg_for8((img).depth,z) #define cimg_for8V(img,v) cimg_for8((img).dim,v) #define cimg_for8XY(img,x,y) cimg_for8Y(img,y) cimg_for8X(img,x) #define cimg_for8XZ(img,x,z) cimg_for8Z(img,z) cimg_for8X(img,x) #define cimg_for8XV(img,x,v) cimg_for8V(img,v) cimg_for8X(img,x) #define cimg_for8YZ(img,y,z) cimg_for8Z(img,z) cimg_for8Y(img,y) #define cimg_for8YV(img,y,v) cimg_for8V(img,v) cimg_for8Y(img,y) #define cimg_for8ZV(img,z,v) cimg_for8V(img,v) cimg_for8Z(img,z) #define cimg_for8XYZ(img,x,y,z) cimg_for8Z(img,z) cimg_for8XY(img,x,y) #define cimg_for8XZV(img,x,z,v) cimg_for8V(img,v) cimg_for8XZ(img,x,z) #define cimg_for8YZV(img,y,z,v) cimg_for8V(img,v) cimg_for8YZ(img,y,z) #define cimg_for8XYZV(img,x,y,z,v) cimg_for8V(img,v) cimg_for8XYZ(img,x,y,z) #define cimg_for_in8(bound,i0,i1,i) \ for (int i = (int)(i0)<0?0:(int)(i0), \ _p3##i = i-3<0?0:i-3, \ _p2##i = i-2<0?0:i-2, \ _p1##i = i-1<0?0:i-1, \ _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \ _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \ _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3, \ _n4##i = i+4>=(int)(bound)?(int)(bound)-1:i+4; \ i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \ i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \ _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i) #define cimg_for_in8X(img,x0,x1,x) cimg_for_in8((img).width,x0,x1,x) #define cimg_for_in8Y(img,y0,y1,y) cimg_for_in8((img).height,y0,y1,y) #define cimg_for_in8Z(img,z0,z1,z) cimg_for_in8((img).depth,z0,z1,z) #define cimg_for_in8V(img,v0,v1,v) cimg_for_in8((img).dim,v0,v1,v) #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) #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) #define cimg_for_in8XV(img,x0,v0,x1,v1,x,v) cimg_for_in8V(img,v0,v1,v) cimg_for_in8X(img,x0,x1,x) #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) #define cimg_for_in8YV(img,y0,v0,y1,v1,y,v) cimg_for_in8V(img,v0,v1,v) cimg_for_in8Y(img,y0,y1,y) #define cimg_for_in8ZV(img,z0,v0,z1,v1,z,v) cimg_for_in8V(img,v0,v1,v) cimg_for_in8Z(img,z0,z1,z) #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) #define cimg_for_in8XZV(img,x0,z0,v0,x1,y1,v1,x,z,v) cimg_for_in8V(img,v0,v1,v) cimg_for_in8XZ(img,x0,y0,x1,y1,x,z) #define cimg_for_in8YZV(img,y0,z0,v0,y1,z1,v1,y,z,v) cimg_for_in8V(img,v0,v1,v) cimg_for_in8YZ(img,y0,z0,y1,z1,y,z) #define cimg_for_in8XYZV(img,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) cimg_for_in8V(img,v0,v1,v) cimg_for_in8XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) #define cimg_for9(bound,i) \ for (int i = 0, _p4##i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \ _n1##i = 1>=(int)(bound)?(int)(bound)-1:1, \ _n2##i = 2>=(int)(bound)?(int)(bound)-1:2, \ _n3##i = 3>=(int)(bound)?(int)(bound)-1:3, \ _n4##i = 4>=(int)(bound)?(int)(bound)-1:4; \ _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \ i==(_n4##i = _n3##i = _n2##i = --_n1##i); \ _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i) #define cimg_for9X(img,x) cimg_for9((img).width,x) #define cimg_for9Y(img,y) cimg_for9((img).height,y) #define cimg_for9Z(img,z) cimg_for9((img).depth,z) #define cimg_for9V(img,v) cimg_for9((img).dim,v) #define cimg_for9XY(img,x,y) cimg_for9Y(img,y) cimg_for9X(img,x) #define cimg_for9XZ(img,x,z) cimg_for9Z(img,z) cimg_for9X(img,x) #define cimg_for9XV(img,x,v) cimg_for9V(img,v) cimg_for9X(img,x) #define cimg_for9YZ(img,y,z) cimg_for9Z(img,z) cimg_for9Y(img,y) #define cimg_for9YV(img,y,v) cimg_for9V(img,v) cimg_for9Y(img,y) #define cimg_for9ZV(img,z,v) cimg_for9V(img,v) cimg_for9Z(img,z) #define cimg_for9XYZ(img,x,y,z) cimg_for9Z(img,z) cimg_for9XY(img,x,y) #define cimg_for9XZV(img,x,z,v) cimg_for9V(img,v) cimg_for9XZ(img,x,z) #define cimg_for9YZV(img,y,z,v) cimg_for9V(img,v) cimg_for9YZ(img,y,z) #define cimg_for9XYZV(img,x,y,z,v) cimg_for9V(img,v) cimg_for9XYZ(img,x,y,z) #define cimg_for_in9(bound,i0,i1,i) \ for (int i = (int)(i0)<0?0:(int)(i0), \ _p4##i = i-4<0?0:i-4, \ _p3##i = i-3<0?0:i-3, \ _p2##i = i-2<0?0:i-2, \ _p1##i = i-1<0?0:i-1, \ _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \ _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \ _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3, \ _n4##i = i+4>=(int)(bound)?(int)(bound)-1:i+4; \ i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \ i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \ _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i) #define cimg_for_in9X(img,x0,x1,x) cimg_for_in9((img).width,x0,x1,x) #define cimg_for_in9Y(img,y0,y1,y) cimg_for_in9((img).height,y0,y1,y) #define cimg_for_in9Z(img,z0,z1,z) cimg_for_in9((img).depth,z0,z1,z) #define cimg_for_in9V(img,v0,v1,v) cimg_for_in9((img).dim,v0,v1,v) #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) #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) #define cimg_for_in9XV(img,x0,v0,x1,v1,x,v) cimg_for_in9V(img,v0,v1,v) cimg_for_in9X(img,x0,x1,x) #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) #define cimg_for_in9YV(img,y0,v0,y1,v1,y,v) cimg_for_in9V(img,v0,v1,v) cimg_for_in9Y(img,y0,y1,y) #define cimg_for_in9ZV(img,z0,v0,z1,v1,z,v) cimg_for_in9V(img,v0,v1,v) cimg_for_in9Z(img,z0,z1,z) #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) #define cimg_for_in9XZV(img,x0,z0,v0,x1,y1,v1,x,z,v) cimg_for_in9V(img,v0,v1,v) cimg_for_in9XZ(img,x0,y0,x1,y1,x,z) #define cimg_for_in9YZV(img,y0,z0,v0,y1,z1,v1,y,z,v) cimg_for_in9V(img,v0,v1,v) cimg_for_in9YZ(img,y0,z0,y1,z1,y,z) #define cimg_for_in9XYZV(img,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) cimg_for_in9V(img,v0,v1,v) cimg_for_in9XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) #define cimg_for2x2(img,x,y,z,v,I) \ cimg_for2((img).height,y) for (int x = 0, \ _n1##x = (int)( \ (I[0] = (img)(0,y,z,v)), \ (I[2] = (img)(0,_n1##y,z,v)), \ 1>=(img).width?(int)((img).width)-1:1); \ (_n1##x<(int)((img).width) && ( \ (I[1] = (img)(_n1##x,y,z,v)), \ (I[3] = (img)(_n1##x,_n1##y,z,v)),1)) || \ x==--_n1##x; \ I[0] = I[1], \ I[2] = I[3], \ ++x, ++_n1##x) #define cimg_for_in2x2(img,x0,y0,x1,y1,x,y,z,v,I) \ cimg_for_in2((img).height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ _n1##x = (int)( \ (I[0] = (img)(x,y,z,v)), \ (I[2] = (img)(x,_n1##y,z,v)), \ x+1>=(int)(img).width?(int)((img).width)-1:x+1); \ x<=(int)(x1) && ((_n1##x<(int)((img).width) && ( \ (I[1] = (img)(_n1##x,y,z,v)), \ (I[3] = (img)(_n1##x,_n1##y,z,v)),1)) || \ x==--_n1##x); \ I[0] = I[1], \ I[2] = I[3], \ ++x, ++_n1##x) #define cimg_for3x3(img,x,y,z,v,I) \ cimg_for3((img).height,y) for (int x = 0, \ _p1##x = 0, \ _n1##x = (int)( \ (I[0] = I[1] = (img)(0,_p1##y,z,v)), \ (I[3] = I[4] = (img)(0,y,z,v)), \ (I[6] = I[7] = (img)(0,_n1##y,z,v)), \ 1>=(img).width?(int)((img).width)-1:1); \ (_n1##x<(int)((img).width) && ( \ (I[2] = (img)(_n1##x,_p1##y,z,v)), \ (I[5] = (img)(_n1##x,y,z,v)), \ (I[8] = (img)(_n1##x,_n1##y,z,v)),1)) || \ x==--_n1##x; \ I[0] = I[1], I[1] = I[2], \ I[3] = I[4], I[4] = I[5], \ I[6] = I[7], I[7] = I[8], \ _p1##x = x++, ++_n1##x) #define cimg_for_in3x3(img,x0,y0,x1,y1,x,y,z,v,I) \ cimg_for_in3((img).height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ _p1##x = x-1<0?0:x-1, \ _n1##x = (int)( \ (I[0] = (img)(_p1##x,_p1##y,z,v)), \ (I[3] = (img)(_p1##x,y,z,v)), \ (I[6] = (img)(_p1##x,_n1##y,z,v)), \ (I[1] = (img)(x,_p1##y,z,v)), \ (I[4] = (img)(x,y,z,v)), \ (I[7] = (img)(x,_n1##y,z,v)), \ x+1>=(int)(img).width?(int)((img).width)-1:x+1); \ x<=(int)(x1) && ((_n1##x<(int)((img).width) && ( \ (I[2] = (img)(_n1##x,_p1##y,z,v)), \ (I[5] = (img)(_n1##x,y,z,v)), \ (I[8] = (img)(_n1##x,_n1##y,z,v)),1)) || \ x==--_n1##x); \ I[0] = I[1], I[1] = I[2], \ I[3] = I[4], I[4] = I[5], \ I[6] = I[7], I[7] = I[8], \ _p1##x = x++, ++_n1##x) #define cimg_for4x4(img,x,y,z,v,I) \ cimg_for4((img).height,y) for (int x = 0, \ _p1##x = 0, \ _n1##x = 1>=(img).width?(int)((img).width)-1:1, \ _n2##x = (int)( \ (I[0] = I[1] = (img)(0,_p1##y,z,v)), \ (I[4] = I[5] = (img)(0,y,z,v)), \ (I[8] = I[9] = (img)(0,_n1##y,z,v)), \ (I[12] = I[13] = (img)(0,_n2##y,z,v)), \ (I[2] = (img)(_n1##x,_p1##y,z,v)), \ (I[6] = (img)(_n1##x,y,z,v)), \ (I[10] = (img)(_n1##x,_n1##y,z,v)), \ (I[14] = (img)(_n1##x,_n2##y,z,v)), \ 2>=(img).width?(int)((img).width)-1:2); \ (_n2##x<(int)((img).width) && ( \ (I[3] = (img)(_n2##x,_p1##y,z,v)), \ (I[7] = (img)(_n2##x,y,z,v)), \ (I[11] = (img)(_n2##x,_n1##y,z,v)), \ (I[15] = (img)(_n2##x,_n2##y,z,v)),1)) || \ _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \ I[0] = I[1], I[1] = I[2], I[2] = I[3], \ I[4] = I[5], I[5] = I[6], I[6] = I[7], \ I[8] = I[9], I[9] = I[10], I[10] = I[11], \ I[12] = I[13], I[13] = I[14], I[14] = I[15], \ _p1##x = x++, ++_n1##x, ++_n2##x) #define cimg_for_in4x4(img,x0,y0,x1,y1,x,y,z,v,I) \ cimg_for_in4((img).height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ _p1##x = x-1<0?0:x-1, \ _n1##x = x+1>=(int)(img).width?(int)((img).width)-1:x+1, \ _n2##x = (int)( \ (I[0] = (img)(_p1##x,_p1##y,z,v)), \ (I[4] = (img)(_p1##x,y,z,v)), \ (I[8] = (img)(_p1##x,_n1##y,z,v)), \ (I[12] = (img)(_p1##x,_n2##y,z,v)), \ (I[1] = (img)(x,_p1##y,z,v)), \ (I[5] = (img)(x,y,z,v)), \ (I[9] = (img)(x,_n1##y,z,v)), \ (I[13] = (img)(x,_n2##y,z,v)), \ (I[2] = (img)(_n1##x,_p1##y,z,v)), \ (I[6] = (img)(_n1##x,y,z,v)), \ (I[10] = (img)(_n1##x,_n1##y,z,v)), \ (I[14] = (img)(_n1##x,_n2##y,z,v)), \ x+2>=(int)(img).width?(int)((img).width)-1:x+2); \ x<=(int)(x1) && ((_n2##x<(int)((img).width) && ( \ (I[3] = (img)(_n2##x,_p1##y,z,v)), \ (I[7] = (img)(_n2##x,y,z,v)), \ (I[11] = (img)(_n2##x,_n1##y,z,v)), \ (I[15] = (img)(_n2##x,_n2##y,z,v)),1)) || \ _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \ I[0] = I[1], I[1] = I[2], I[2] = I[3], \ I[4] = I[5], I[5] = I[6], I[6] = I[7], \ I[8] = I[9], I[9] = I[10], I[10] = I[11], \ I[12] = I[13], I[13] = I[14], I[14] = I[15], \ _p1##x = x++, ++_n1##x, ++_n2##x) #define cimg_for5x5(img,x,y,z,v,I) \ cimg_for5((img).height,y) for (int x = 0, \ _p2##x = 0, _p1##x = 0, \ _n1##x = 1>=(img).width?(int)((img).width)-1:1, \ _n2##x = (int)( \ (I[0] = I[1] = I[2] = (img)(0,_p2##y,z,v)), \ (I[5] = I[6] = I[7] = (img)(0,_p1##y,z,v)), \ (I[10] = I[11] = I[12] = (img)(0,y,z,v)), \ (I[15] = I[16] = I[17] = (img)(0,_n1##y,z,v)), \ (I[20] = I[21] = I[22] = (img)(0,_n2##y,z,v)), \ (I[3] = (img)(_n1##x,_p2##y,z,v)), \ (I[8] = (img)(_n1##x,_p1##y,z,v)), \ (I[13] = (img)(_n1##x,y,z,v)), \ (I[18] = (img)(_n1##x,_n1##y,z,v)), \ (I[23] = (img)(_n1##x,_n2##y,z,v)), \ 2>=(img).width?(int)((img).width)-1:2); \ (_n2##x<(int)((img).width) && ( \ (I[4] = (img)(_n2##x,_p2##y,z,v)), \ (I[9] = (img)(_n2##x,_p1##y,z,v)), \ (I[14] = (img)(_n2##x,y,z,v)), \ (I[19] = (img)(_n2##x,_n1##y,z,v)), \ (I[24] = (img)(_n2##x,_n2##y,z,v)),1)) || \ _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \ I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \ I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \ I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \ I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \ I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \ _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x) #define cimg_for_in5x5(img,x0,y0,x1,y1,x,y,z,v,I) \ cimg_for_in5((img).height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ _p2##x = x-2<0?0:x-2, \ _p1##x = x-1<0?0:x-1, \ _n1##x = x+1>=(int)(img).width?(int)((img).width)-1:x+1, \ _n2##x = (int)( \ (I[0] = (img)(_p2##x,_p2##y,z,v)), \ (I[5] = (img)(_p2##x,_p1##y,z,v)), \ (I[10] = (img)(_p2##x,y,z,v)), \ (I[15] = (img)(_p2##x,_n1##y,z,v)), \ (I[20] = (img)(_p2##x,_n2##y,z,v)), \ (I[1] = (img)(_p1##x,_p2##y,z,v)), \ (I[6] = (img)(_p1##x,_p1##y,z,v)), \ (I[11] = (img)(_p1##x,y,z,v)), \ (I[16] = (img)(_p1##x,_n1##y,z,v)), \ (I[21] = (img)(_p1##x,_n2##y,z,v)), \ (I[2] = (img)(x,_p2##y,z,v)), \ (I[7] = (img)(x,_p1##y,z,v)), \ (I[12] = (img)(x,y,z,v)), \ (I[17] = (img)(x,_n1##y,z,v)), \ (I[22] = (img)(x,_n2##y,z,v)), \ (I[3] = (img)(_n1##x,_p2##y,z,v)), \ (I[8] = (img)(_n1##x,_p1##y,z,v)), \ (I[13] = (img)(_n1##x,y,z,v)), \ (I[18] = (img)(_n1##x,_n1##y,z,v)), \ (I[23] = (img)(_n1##x,_n2##y,z,v)), \ x+2>=(int)(img).width?(int)((img).width)-1:x+2); \ x<=(int)(x1) && ((_n2##x<(int)((img).width) && ( \ (I[4] = (img)(_n2##x,_p2##y,z,v)), \ (I[9] = (img)(_n2##x,_p1##y,z,v)), \ (I[14] = (img)(_n2##x,y,z,v)), \ (I[19] = (img)(_n2##x,_n1##y,z,v)), \ (I[24] = (img)(_n2##x,_n2##y,z,v)),1)) || \ _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \ I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \ I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \ I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \ I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \ I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \ _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x) #define cimg_for6x6(img,x,y,z,v,I) \ cimg_for6((img).height,y) for (int x = 0, \ _p2##x = 0, _p1##x = 0, \ _n1##x = 1>=(img).width?(int)((img).width)-1:1, \ _n2##x = 2>=(img).width?(int)((img).width)-1:2, \ _n3##x = (int)( \ (I[0] = I[1] = I[2] = (img)(0,_p2##y,z,v)), \ (I[6] = I[7] = I[8] = (img)(0,_p1##y,z,v)), \ (I[12] = I[13] = I[14] = (img)(0,y,z,v)), \ (I[18] = I[19] = I[20] = (img)(0,_n1##y,z,v)), \ (I[24] = I[25] = I[26] = (img)(0,_n2##y,z,v)), \ (I[30] = I[31] = I[32] = (img)(0,_n3##y,z,v)), \ (I[3] = (img)(_n1##x,_p2##y,z,v)), \ (I[9] = (img)(_n1##x,_p1##y,z,v)), \ (I[15] = (img)(_n1##x,y,z,v)), \ (I[21] = (img)(_n1##x,_n1##y,z,v)), \ (I[27] = (img)(_n1##x,_n2##y,z,v)), \ (I[33] = (img)(_n1##x,_n3##y,z,v)), \ (I[4] = (img)(_n2##x,_p2##y,z,v)), \ (I[10] = (img)(_n2##x,_p1##y,z,v)), \ (I[16] = (img)(_n2##x,y,z,v)), \ (I[22] = (img)(_n2##x,_n1##y,z,v)), \ (I[28] = (img)(_n2##x,_n2##y,z,v)), \ (I[34] = (img)(_n2##x,_n3##y,z,v)), \ 3>=(img).width?(int)((img).width)-1:3); \ (_n3##x<(int)((img).width) && ( \ (I[5] = (img)(_n3##x,_p2##y,z,v)), \ (I[11] = (img)(_n3##x,_p1##y,z,v)), \ (I[17] = (img)(_n3##x,y,z,v)), \ (I[23] = (img)(_n3##x,_n1##y,z,v)), \ (I[29] = (img)(_n3##x,_n2##y,z,v)), \ (I[35] = (img)(_n3##x,_n3##y,z,v)),1)) || \ _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x); \ I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \ I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \ I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], 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[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \ I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \ _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) #define cimg_for_in6x6(img,x0,y0,x1,y1,x,y,z,v,I) \ cimg_for_in6((img).height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)x0, \ _p2##x = x-2<0?0:x-2, \ _p1##x = x-1<0?0:x-1, \ _n1##x = x+1>=(int)(img).width?(int)((img).width)-1:x+1, \ _n2##x = x+2>=(int)(img).width?(int)((img).width)-1:x+2, \ _n3##x = (int)( \ (I[0] = (img)(_p2##x,_p2##y,z,v)), \ (I[6] = (img)(_p2##x,_p1##y,z,v)), \ (I[12] = (img)(_p2##x,y,z,v)), \ (I[18] = (img)(_p2##x,_n1##y,z,v)), \ (I[24] = (img)(_p2##x,_n2##y,z,v)), \ (I[30] = (img)(_p2##x,_n3##y,z,v)), \ (I[1] = (img)(_p1##x,_p2##y,z,v)), \ (I[7] = (img)(_p1##x,_p1##y,z,v)), \ (I[13] = (img)(_p1##x,y,z,v)), \ (I[19] = (img)(_p1##x,_n1##y,z,v)), \ (I[25] = (img)(_p1##x,_n2##y,z,v)), \ (I[31] = (img)(_p1##x,_n3##y,z,v)), \ (I[2] = (img)(x,_p2##y,z,v)), \ (I[8] = (img)(x,_p1##y,z,v)), \ (I[14] = (img)(x,y,z,v)), \ (I[20] = (img)(x,_n1##y,z,v)), \ (I[26] = (img)(x,_n2##y,z,v)), \ (I[32] = (img)(x,_n3##y,z,v)), \ (I[3] = (img)(_n1##x,_p2##y,z,v)), \ (I[9] = (img)(_n1##x,_p1##y,z,v)), \ (I[15] = (img)(_n1##x,y,z,v)), \ (I[21] = (img)(_n1##x,_n1##y,z,v)), \ (I[27] = (img)(_n1##x,_n2##y,z,v)), \ (I[33] = (img)(_n1##x,_n3##y,z,v)), \ (I[4] = (img)(_n2##x,_p2##y,z,v)), \ (I[10] = (img)(_n2##x,_p1##y,z,v)), \ (I[16] = (img)(_n2##x,y,z,v)), \ (I[22] = (img)(_n2##x,_n1##y,z,v)), \ (I[28] = (img)(_n2##x,_n2##y,z,v)), \ (I[34] = (img)(_n2##x,_n3##y,z,v)), \ x+3>=(int)(img).width?(int)((img).width)-1:x+3); \ x<=(int)(x1) && ((_n3##x<(int)((img).width) && ( \ (I[5] = (img)(_n3##x,_p2##y,z,v)), \ (I[11] = (img)(_n3##x,_p1##y,z,v)), \ (I[17] = (img)(_n3##x,y,z,v)), \ (I[23] = (img)(_n3##x,_n1##y,z,v)), \ (I[29] = (img)(_n3##x,_n2##y,z,v)), \ (I[35] = (img)(_n3##x,_n3##y,z,v)),1)) || \ _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x)); \ I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \ I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \ I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], 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[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \ I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \ _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) #define cimg_for7x7(img,x,y,z,v,I) \ cimg_for7((img).height,y) for (int x = 0, \ _p3##x = 0, _p2##x = 0, _p1##x = 0, \ _n1##x = 1>=(img).width?(int)((img).width)-1:1, \ _n2##x = 2>=(img).width?(int)((img).width)-1:2, \ _n3##x = (int)( \ (I[0] = I[1] = I[2] = I[3] = (img)(0,_p3##y,z,v)), \ (I[7] = I[8] = I[9] = I[10] = (img)(0,_p2##y,z,v)), \ (I[14] = I[15] = I[16] = I[17] = (img)(0,_p1##y,z,v)), \ (I[21] = I[22] = I[23] = I[24] = (img)(0,y,z,v)), \ (I[28] = I[29] = I[30] = I[31] = (img)(0,_n1##y,z,v)), \ (I[35] = I[36] = I[37] = I[38] = (img)(0,_n2##y,z,v)), \ (I[42] = I[43] = I[44] = I[45] = (img)(0,_n3##y,z,v)), \ (I[4] = (img)(_n1##x,_p3##y,z,v)), \ (I[11] = (img)(_n1##x,_p2##y,z,v)), \ (I[18] = (img)(_n1##x,_p1##y,z,v)), \ (I[25] = (img)(_n1##x,y,z,v)), \ (I[32] = (img)(_n1##x,_n1##y,z,v)), \ (I[39] = (img)(_n1##x,_n2##y,z,v)), \ (I[46] = (img)(_n1##x,_n3##y,z,v)), \ (I[5] = (img)(_n2##x,_p3##y,z,v)), \ (I[12] = (img)(_n2##x,_p2##y,z,v)), \ (I[19] = (img)(_n2##x,_p1##y,z,v)), \ (I[26] = (img)(_n2##x,y,z,v)), \ (I[33] = (img)(_n2##x,_n1##y,z,v)), \ (I[40] = (img)(_n2##x,_n2##y,z,v)), \ (I[47] = (img)(_n2##x,_n3##y,z,v)), \ 3>=(img).width?(int)((img).width)-1:3); \ (_n3##x<(int)((img).width) && ( \ (I[6] = (img)(_n3##x,_p3##y,z,v)), \ (I[13] = (img)(_n3##x,_p2##y,z,v)), \ (I[20] = (img)(_n3##x,_p1##y,z,v)), \ (I[27] = (img)(_n3##x,y,z,v)), \ (I[34] = (img)(_n3##x,_n1##y,z,v)), \ (I[41] = (img)(_n3##x,_n2##y,z,v)), \ (I[48] = (img)(_n3##x,_n3##y,z,v)),1)) || \ _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x); \ 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[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \ I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \ I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \ I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \ I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \ I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \ _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) #define cimg_for_in7x7(img,x0,y0,x1,y1,x,y,z,v,I) \ cimg_for_in7((img).height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ _p3##x = x-3<0?0:x-3, \ _p2##x = x-2<0?0:x-2, \ _p1##x = x-1<0?0:x-1, \ _n1##x = x+1>=(int)(img).width?(int)((img).width)-1:x+1, \ _n2##x = x+2>=(int)(img).width?(int)((img).width)-1:x+2, \ _n3##x = (int)( \ (I[0] = (img)(_p3##x,_p3##y,z,v)), \ (I[7] = (img)(_p3##x,_p2##y,z,v)), \ (I[14] = (img)(_p3##x,_p1##y,z,v)), \ (I[21] = (img)(_p3##x,y,z,v)), \ (I[28] = (img)(_p3##x,_n1##y,z,v)), \ (I[35] = (img)(_p3##x,_n2##y,z,v)), \ (I[42] = (img)(_p3##x,_n3##y,z,v)), \ (I[1] = (img)(_p2##x,_p3##y,z,v)), \ (I[8] = (img)(_p2##x,_p2##y,z,v)), \ (I[15] = (img)(_p2##x,_p1##y,z,v)), \ (I[22] = (img)(_p2##x,y,z,v)), \ (I[29] = (img)(_p2##x,_n1##y,z,v)), \ (I[36] = (img)(_p2##x,_n2##y,z,v)), \ (I[43] = (img)(_p2##x,_n3##y,z,v)), \ (I[2] = (img)(_p1##x,_p3##y,z,v)), \ (I[9] = (img)(_p1##x,_p2##y,z,v)), \ (I[16] = (img)(_p1##x,_p1##y,z,v)), \ (I[23] = (img)(_p1##x,y,z,v)), \ (I[30] = (img)(_p1##x,_n1##y,z,v)), \ (I[37] = (img)(_p1##x,_n2##y,z,v)), \ (I[44] = (img)(_p1##x,_n3##y,z,v)), \ (I[3] = (img)(x,_p3##y,z,v)), \ (I[10] = (img)(x,_p2##y,z,v)), \ (I[17] = (img)(x,_p1##y,z,v)), \ (I[24] = (img)(x,y,z,v)), \ (I[31] = (img)(x,_n1##y,z,v)), \ (I[38] = (img)(x,_n2##y,z,v)), \ (I[45] = (img)(x,_n3##y,z,v)), \ (I[4] = (img)(_n1##x,_p3##y,z,v)), \ (I[11] = (img)(_n1##x,_p2##y,z,v)), \ (I[18] = (img)(_n1##x,_p1##y,z,v)), \ (I[25] = (img)(_n1##x,y,z,v)), \ (I[32] = (img)(_n1##x,_n1##y,z,v)), \ (I[39] = (img)(_n1##x,_n2##y,z,v)), \ (I[46] = (img)(_n1##x,_n3##y,z,v)), \ (I[5] = (img)(_n2##x,_p3##y,z,v)), \ (I[12] = (img)(_n2##x,_p2##y,z,v)), \ (I[19] = (img)(_n2##x,_p1##y,z,v)), \ (I[26] = (img)(_n2##x,y,z,v)), \ (I[33] = (img)(_n2##x,_n1##y,z,v)), \ (I[40] = (img)(_n2##x,_n2##y,z,v)), \ (I[47] = (img)(_n2##x,_n3##y,z,v)), \ x+3>=(int)(img).width?(int)((img).width)-1:x+3); \ x<=(int)(x1) && ((_n3##x<(int)((img).width) && ( \ (I[6] = (img)(_n3##x,_p3##y,z,v)), \ (I[13] = (img)(_n3##x,_p2##y,z,v)), \ (I[20] = (img)(_n3##x,_p1##y,z,v)), \ (I[27] = (img)(_n3##x,y,z,v)), \ (I[34] = (img)(_n3##x,_n1##y,z,v)), \ (I[41] = (img)(_n3##x,_n2##y,z,v)), \ (I[48] = (img)(_n3##x,_n3##y,z,v)),1)) || \ _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x)); \ 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[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \ I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \ I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \ I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \ I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \ I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \ _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) #define cimg_for8x8(img,x,y,z,v,I) \ cimg_for8((img).height,y) for (int x = 0, \ _p3##x = 0, _p2##x = 0, _p1##x = 0, \ _n1##x = 1>=((img).width)?(int)((img).width)-1:1, \ _n2##x = 2>=((img).width)?(int)((img).width)-1:2, \ _n3##x = 3>=((img).width)?(int)((img).width)-1:3, \ _n4##x = (int)( \ (I[0] = I[1] = I[2] = I[3] = (img)(0,_p3##y,z,v)), \ (I[8] = I[9] = I[10] = I[11] = (img)(0,_p2##y,z,v)), \ (I[16] = I[17] = I[18] = I[19] = (img)(0,_p1##y,z,v)), \ (I[24] = I[25] = I[26] = I[27] = (img)(0,y,z,v)), \ (I[32] = I[33] = I[34] = I[35] = (img)(0,_n1##y,z,v)), \ (I[40] = I[41] = I[42] = I[43] = (img)(0,_n2##y,z,v)), \ (I[48] = I[49] = I[50] = I[51] = (img)(0,_n3##y,z,v)), \ (I[56] = I[57] = I[58] = I[59] = (img)(0,_n4##y,z,v)), \ (I[4] = (img)(_n1##x,_p3##y,z,v)), \ (I[12] = (img)(_n1##x,_p2##y,z,v)), \ (I[20] = (img)(_n1##x,_p1##y,z,v)), \ (I[28] = (img)(_n1##x,y,z,v)), \ (I[36] = (img)(_n1##x,_n1##y,z,v)), \ (I[44] = (img)(_n1##x,_n2##y,z,v)), \ (I[52] = (img)(_n1##x,_n3##y,z,v)), \ (I[60] = (img)(_n1##x,_n4##y,z,v)), \ (I[5] = (img)(_n2##x,_p3##y,z,v)), \ (I[13] = (img)(_n2##x,_p2##y,z,v)), \ (I[21] = (img)(_n2##x,_p1##y,z,v)), \ (I[29] = (img)(_n2##x,y,z,v)), \ (I[37] = (img)(_n2##x,_n1##y,z,v)), \ (I[45] = (img)(_n2##x,_n2##y,z,v)), \ (I[53] = (img)(_n2##x,_n3##y,z,v)), \ (I[61] = (img)(_n2##x,_n4##y,z,v)), \ (I[6] = (img)(_n3##x,_p3##y,z,v)), \ (I[14] = (img)(_n3##x,_p2##y,z,v)), \ (I[22] = (img)(_n3##x,_p1##y,z,v)), \ (I[30] = (img)(_n3##x,y,z,v)), \ (I[38] = (img)(_n3##x,_n1##y,z,v)), \ (I[46] = (img)(_n3##x,_n2##y,z,v)), \ (I[54] = (img)(_n3##x,_n3##y,z,v)), \ (I[62] = (img)(_n3##x,_n4##y,z,v)), \ 4>=((img).width)?(int)((img).width)-1:4); \ (_n4##x<(int)((img).width) && ( \ (I[7] = (img)(_n4##x,_p3##y,z,v)), \ (I[15] = (img)(_n4##x,_p2##y,z,v)), \ (I[23] = (img)(_n4##x,_p1##y,z,v)), \ (I[31] = (img)(_n4##x,y,z,v)), \ (I[39] = (img)(_n4##x,_n1##y,z,v)), \ (I[47] = (img)(_n4##x,_n2##y,z,v)), \ (I[55] = (img)(_n4##x,_n3##y,z,v)), \ (I[63] = (img)(_n4##x,_n4##y,z,v)),1)) || \ _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \ 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[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], \ 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], \ 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], \ 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], \ 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], \ 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], \ 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], \ _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x) #define cimg_for_in8x8(img,x0,y0,x1,y1,x,y,z,v,I) \ cimg_for_in8((img).height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ _p3##x = x-3<0?0:x-3, \ _p2##x = x-2<0?0:x-2, \ _p1##x = x-1<0?0:x-1, \ _n1##x = x+1>=(int)((img).width)?(int)((img).width)-1:x+1, \ _n2##x = x+2>=(int)((img).width)?(int)((img).width)-1:x+2, \ _n3##x = x+3>=(int)((img).width)?(int)((img).width)-1:x+3, \ _n4##x = (int)( \ (I[0] = (img)(_p3##x,_p3##y,z,v)), \ (I[8] = (img)(_p3##x,_p2##y,z,v)), \ (I[16] = (img)(_p3##x,_p1##y,z,v)), \ (I[24] = (img)(_p3##x,y,z,v)), \ (I[32] = (img)(_p3##x,_n1##y,z,v)), \ (I[40] = (img)(_p3##x,_n2##y,z,v)), \ (I[48] = (img)(_p3##x,_n3##y,z,v)), \ (I[56] = (img)(_p3##x,_n4##y,z,v)), \ (I[1] = (img)(_p2##x,_p3##y,z,v)), \ (I[9] = (img)(_p2##x,_p2##y,z,v)), \ (I[17] = (img)(_p2##x,_p1##y,z,v)), \ (I[25] = (img)(_p2##x,y,z,v)), \ (I[33] = (img)(_p2##x,_n1##y,z,v)), \ (I[41] = (img)(_p2##x,_n2##y,z,v)), \ (I[49] = (img)(_p2##x,_n3##y,z,v)), \ (I[57] = (img)(_p2##x,_n4##y,z,v)), \ (I[2] = (img)(_p1##x,_p3##y,z,v)), \ (I[10] = (img)(_p1##x,_p2##y,z,v)), \ (I[18] = (img)(_p1##x,_p1##y,z,v)), \ (I[26] = (img)(_p1##x,y,z,v)), \ (I[34] = (img)(_p1##x,_n1##y,z,v)), \ (I[42] = (img)(_p1##x,_n2##y,z,v)), \ (I[50] = (img)(_p1##x,_n3##y,z,v)), \ (I[58] = (img)(_p1##x,_n4##y,z,v)), \ (I[3] = (img)(x,_p3##y,z,v)), \ (I[11] = (img)(x,_p2##y,z,v)), \ (I[19] = (img)(x,_p1##y,z,v)), \ (I[27] = (img)(x,y,z,v)), \ (I[35] = (img)(x,_n1##y,z,v)), \ (I[43] = (img)(x,_n2##y,z,v)), \ (I[51] = (img)(x,_n3##y,z,v)), \ (I[59] = (img)(x,_n4##y,z,v)), \ (I[4] = (img)(_n1##x,_p3##y,z,v)), \ (I[12] = (img)(_n1##x,_p2##y,z,v)), \ (I[20] = (img)(_n1##x,_p1##y,z,v)), \ (I[28] = (img)(_n1##x,y,z,v)), \ (I[36] = (img)(_n1##x,_n1##y,z,v)), \ (I[44] = (img)(_n1##x,_n2##y,z,v)), \ (I[52] = (img)(_n1##x,_n3##y,z,v)), \ (I[60] = (img)(_n1##x,_n4##y,z,v)), \ (I[5] = (img)(_n2##x,_p3##y,z,v)), \ (I[13] = (img)(_n2##x,_p2##y,z,v)), \ (I[21] = (img)(_n2##x,_p1##y,z,v)), \ (I[29] = (img)(_n2##x,y,z,v)), \ (I[37] = (img)(_n2##x,_n1##y,z,v)), \ (I[45] = (img)(_n2##x,_n2##y,z,v)), \ (I[53] = (img)(_n2##x,_n3##y,z,v)), \ (I[61] = (img)(_n2##x,_n4##y,z,v)), \ (I[6] = (img)(_n3##x,_p3##y,z,v)), \ (I[14] = (img)(_n3##x,_p2##y,z,v)), \ (I[22] = (img)(_n3##x,_p1##y,z,v)), \ (I[30] = (img)(_n3##x,y,z,v)), \ (I[38] = (img)(_n3##x,_n1##y,z,v)), \ (I[46] = (img)(_n3##x,_n2##y,z,v)), \ (I[54] = (img)(_n3##x,_n3##y,z,v)), \ (I[62] = (img)(_n3##x,_n4##y,z,v)), \ x+4>=(int)((img).width)?(int)((img).width)-1:x+4); \ x<=(int)(x1) && ((_n4##x<(int)((img).width) && ( \ (I[7] = (img)(_n4##x,_p3##y,z,v)), \ (I[15] = (img)(_n4##x,_p2##y,z,v)), \ (I[23] = (img)(_n4##x,_p1##y,z,v)), \ (I[31] = (img)(_n4##x,y,z,v)), \ (I[39] = (img)(_n4##x,_n1##y,z,v)), \ (I[47] = (img)(_n4##x,_n2##y,z,v)), \ (I[55] = (img)(_n4##x,_n3##y,z,v)), \ (I[63] = (img)(_n4##x,_n4##y,z,v)),1)) || \ _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \ 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[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], \ 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], \ 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], \ 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], \ 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], \ 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], \ 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], \ _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x) #define cimg_for9x9(img,x,y,z,v,I) \ cimg_for9((img).height,y) for (int x = 0, \ _p4##x = 0, _p3##x = 0, _p2##x = 0, _p1##x = 0, \ _n1##x = 1>=((img).width)?(int)((img).width)-1:1, \ _n2##x = 2>=((img).width)?(int)((img).width)-1:2, \ _n3##x = 3>=((img).width)?(int)((img).width)-1:3, \ _n4##x = (int)( \ (I[0] = I[1] = I[2] = I[3] = I[4] = (img)(0,_p4##y,z,v)), \ (I[9] = I[10] = I[11] = I[12] = I[13] = (img)(0,_p3##y,z,v)), \ (I[18] = I[19] = I[20] = I[21] = I[22] = (img)(0,_p2##y,z,v)), \ (I[27] = I[28] = I[29] = I[30] = I[31] = (img)(0,_p1##y,z,v)), \ (I[36] = I[37] = I[38] = I[39] = I[40] = (img)(0,y,z,v)), \ (I[45] = I[46] = I[47] = I[48] = I[49] = (img)(0,_n1##y,z,v)), \ (I[54] = I[55] = I[56] = I[57] = I[58] = (img)(0,_n2##y,z,v)), \ (I[63] = I[64] = I[65] = I[66] = I[67] = (img)(0,_n3##y,z,v)), \ (I[72] = I[73] = I[74] = I[75] = I[76] = (img)(0,_n4##y,z,v)), \ (I[5] = (img)(_n1##x,_p4##y,z,v)), \ (I[14] = (img)(_n1##x,_p3##y,z,v)), \ (I[23] = (img)(_n1##x,_p2##y,z,v)), \ (I[32] = (img)(_n1##x,_p1##y,z,v)), \ (I[41] = (img)(_n1##x,y,z,v)), \ (I[50] = (img)(_n1##x,_n1##y,z,v)), \ (I[59] = (img)(_n1##x,_n2##y,z,v)), \ (I[68] = (img)(_n1##x,_n3##y,z,v)), \ (I[77] = (img)(_n1##x,_n4##y,z,v)), \ (I[6] = (img)(_n2##x,_p4##y,z,v)), \ (I[15] = (img)(_n2##x,_p3##y,z,v)), \ (I[24] = (img)(_n2##x,_p2##y,z,v)), \ (I[33] = (img)(_n2##x,_p1##y,z,v)), \ (I[42] = (img)(_n2##x,y,z,v)), \ (I[51] = (img)(_n2##x,_n1##y,z,v)), \ (I[60] = (img)(_n2##x,_n2##y,z,v)), \ (I[69] = (img)(_n2##x,_n3##y,z,v)), \ (I[78] = (img)(_n2##x,_n4##y,z,v)), \ (I[7] = (img)(_n3##x,_p4##y,z,v)), \ (I[16] = (img)(_n3##x,_p3##y,z,v)), \ (I[25] = (img)(_n3##x,_p2##y,z,v)), \ (I[34] = (img)(_n3##x,_p1##y,z,v)), \ (I[43] = (img)(_n3##x,y,z,v)), \ (I[52] = (img)(_n3##x,_n1##y,z,v)), \ (I[61] = (img)(_n3##x,_n2##y,z,v)), \ (I[70] = (img)(_n3##x,_n3##y,z,v)), \ (I[79] = (img)(_n3##x,_n4##y,z,v)), \ 4>=((img).width)?(int)((img).width)-1:4); \ (_n4##x<(int)((img).width) && ( \ (I[8] = (img)(_n4##x,_p4##y,z,v)), \ (I[17] = (img)(_n4##x,_p3##y,z,v)), \ (I[26] = (img)(_n4##x,_p2##y,z,v)), \ (I[35] = (img)(_n4##x,_p1##y,z,v)), \ (I[44] = (img)(_n4##x,y,z,v)), \ (I[53] = (img)(_n4##x,_n1##y,z,v)), \ (I[62] = (img)(_n4##x,_n2##y,z,v)), \ (I[71] = (img)(_n4##x,_n3##y,z,v)), \ (I[80] = (img)(_n4##x,_n4##y,z,v)),1)) || \ _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \ 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], \ I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \ I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], \ I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \ I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], \ I[45] = I[46], I[46] = I[47], I[47] = I[48], I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], \ I[54] = I[55], I[55] = I[56], I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], \ I[63] = I[64], I[64] = I[65], I[65] = I[66], I[66] = I[67], I[67] = I[68], I[68] = I[69], I[69] = I[70], I[70] = I[71], \ I[72] = I[73], I[73] = I[74], I[74] = I[75], I[75] = I[76], I[76] = I[77], I[77] = I[78], I[78] = I[79], I[79] = I[80], \ _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x) #define cimg_for_in9x9(img,x0,y0,x1,y1,x,y,z,v,I) \ cimg_for_in9((img).height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ _p4##x = x-4<0?0:x-4, \ _p3##x = x-3<0?0:x-3, \ _p2##x = x-2<0?0:x-2, \ _p1##x = x-1<0?0:x-1, \ _n1##x = x+1>=(int)((img).width)?(int)((img).width)-1:x+1, \ _n2##x = x+2>=(int)((img).width)?(int)((img).width)-1:x+2, \ _n3##x = x+3>=(int)((img).width)?(int)((img).width)-1:x+3, \ _n4##x = (int)( \ (I[0] = (img)(_p4##x,_p4##y,z,v)), \ (I[9] = (img)(_p4##x,_p3##y,z,v)), \ (I[18] = (img)(_p4##x,_p2##y,z,v)), \ (I[27] = (img)(_p4##x,_p1##y,z,v)), \ (I[36] = (img)(_p4##x,y,z,v)), \ (I[45] = (img)(_p4##x,_n1##y,z,v)), \ (I[54] = (img)(_p4##x,_n2##y,z,v)), \ (I[63] = (img)(_p4##x,_n3##y,z,v)), \ (I[72] = (img)(_p4##x,_n4##y,z,v)), \ (I[1] = (img)(_p3##x,_p4##y,z,v)), \ (I[10] = (img)(_p3##x,_p3##y,z,v)), \ (I[19] = (img)(_p3##x,_p2##y,z,v)), \ (I[28] = (img)(_p3##x,_p1##y,z,v)), \ (I[37] = (img)(_p3##x,y,z,v)), \ (I[46] = (img)(_p3##x,_n1##y,z,v)), \ (I[55] = (img)(_p3##x,_n2##y,z,v)), \ (I[64] = (img)(_p3##x,_n3##y,z,v)), \ (I[73] = (img)(_p3##x,_n4##y,z,v)), \ (I[2] = (img)(_p2##x,_p4##y,z,v)), \ (I[11] = (img)(_p2##x,_p3##y,z,v)), \ (I[20] = (img)(_p2##x,_p2##y,z,v)), \ (I[29] = (img)(_p2##x,_p1##y,z,v)), \ (I[38] = (img)(_p2##x,y,z,v)), \ (I[47] = (img)(_p2##x,_n1##y,z,v)), \ (I[56] = (img)(_p2##x,_n2##y,z,v)), \ (I[65] = (img)(_p2##x,_n3##y,z,v)), \ (I[74] = (img)(_p2##x,_n4##y,z,v)), \ (I[3] = (img)(_p1##x,_p4##y,z,v)), \ (I[12] = (img)(_p1##x,_p3##y,z,v)), \ (I[21] = (img)(_p1##x,_p2##y,z,v)), \ (I[30] = (img)(_p1##x,_p1##y,z,v)), \ (I[39] = (img)(_p1##x,y,z,v)), \ (I[48] = (img)(_p1##x,_n1##y,z,v)), \ (I[57] = (img)(_p1##x,_n2##y,z,v)), \ (I[66] = (img)(_p1##x,_n3##y,z,v)), \ (I[75] = (img)(_p1##x,_n4##y,z,v)), \ (I[4] = (img)(x,_p4##y,z,v)), \ (I[13] = (img)(x,_p3##y,z,v)), \ (I[22] = (img)(x,_p2##y,z,v)), \ (I[31] = (img)(x,_p1##y,z,v)), \ (I[40] = (img)(x,y,z,v)), \ (I[49] = (img)(x,_n1##y,z,v)), \ (I[58] = (img)(x,_n2##y,z,v)), \ (I[67] = (img)(x,_n3##y,z,v)), \ (I[76] = (img)(x,_n4##y,z,v)), \ (I[5] = (img)(_n1##x,_p4##y,z,v)), \ (I[14] = (img)(_n1##x,_p3##y,z,v)), \ (I[23] = (img)(_n1##x,_p2##y,z,v)), \ (I[32] = (img)(_n1##x,_p1##y,z,v)), \ (I[41] = (img)(_n1##x,y,z,v)), \ (I[50] = (img)(_n1##x,_n1##y,z,v)), \ (I[59] = (img)(_n1##x,_n2##y,z,v)), \ (I[68] = (img)(_n1##x,_n3##y,z,v)), \ (I[77] = (img)(_n1##x,_n4##y,z,v)), \ (I[6] = (img)(_n2##x,_p4##y,z,v)), \ (I[15] = (img)(_n2##x,_p3##y,z,v)), \ (I[24] = (img)(_n2##x,_p2##y,z,v)), \ (I[33] = (img)(_n2##x,_p1##y,z,v)), \ (I[42] = (img)(_n2##x,y,z,v)), \ (I[51] = (img)(_n2##x,_n1##y,z,v)), \ (I[60] = (img)(_n2##x,_n2##y,z,v)), \ (I[69] = (img)(_n2##x,_n3##y,z,v)), \ (I[78] = (img)(_n2##x,_n4##y,z,v)), \ (I[7] = (img)(_n3##x,_p4##y,z,v)), \ (I[16] = (img)(_n3##x,_p3##y,z,v)), \ (I[25] = (img)(_n3##x,_p2##y,z,v)), \ (I[34] = (img)(_n3##x,_p1##y,z,v)), \ (I[43] = (img)(_n3##x,y,z,v)), \ (I[52] = (img)(_n3##x,_n1##y,z,v)), \ (I[61] = (img)(_n3##x,_n2##y,z,v)), \ (I[70] = (img)(_n3##x,_n3##y,z,v)), \ (I[79] = (img)(_n3##x,_n4##y,z,v)), \ x+4>=(int)((img).width)?(int)((img).width)-1:x+4); \ x<=(int)(x1) && ((_n4##x<(int)((img).width) && ( \ (I[8] = (img)(_n4##x,_p4##y,z,v)), \ (I[17] = (img)(_n4##x,_p3##y,z,v)), \ (I[26] = (img)(_n4##x,_p2##y,z,v)), \ (I[35] = (img)(_n4##x,_p1##y,z,v)), \ (I[44] = (img)(_n4##x,y,z,v)), \ (I[53] = (img)(_n4##x,_n1##y,z,v)), \ (I[62] = (img)(_n4##x,_n2##y,z,v)), \ (I[71] = (img)(_n4##x,_n3##y,z,v)), \ (I[80] = (img)(_n4##x,_n4##y,z,v)),1)) || \ _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \ 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], \ I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \ I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], \ I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \ I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], \ I[45] = I[46], I[46] = I[47], I[47] = I[48], I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], \ I[54] = I[55], I[55] = I[56], I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], \ I[63] = I[64], I[64] = I[65], I[65] = I[66], I[66] = I[67], I[67] = I[68], I[68] = I[69], I[69] = I[70], I[70] = I[71], \ I[72] = I[73], I[73] = I[74], I[74] = I[75], I[75] = I[76], I[76] = I[77], I[77] = I[78], I[78] = I[79], I[79] = I[80], \ _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x) #define cimg_for2x2x2(img,x,y,z,v,I) \ cimg_for2((img).depth,z) cimg_for2((img).height,y) for (int x = 0, \ _n1##x = (int)( \ (I[0] = (img)(0,y,z,v)), \ (I[2] = (img)(0,_n1##y,z,v)), \ (I[4] = (img)(0,y,_n1##z,v)), \ (I[6] = (img)(0,_n1##y,_n1##z,v)), \ 1>=(img).width?(int)((img).width)-1:1); \ (_n1##x<(int)((img).width) && ( \ (I[1] = (img)(_n1##x,y,z,v)), \ (I[3] = (img)(_n1##x,_n1##y,z,v)), \ (I[5] = (img)(_n1##x,y,_n1##z,v)), \ (I[7] = (img)(_n1##x,_n1##y,_n1##z,v)),1)) || \ x==--_n1##x; \ I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \ ++x, ++_n1##x) #define cimg_for_in2x2x2(img,x0,y0,z0,x1,y1,z1,x,y,z,v,I) \ 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), \ _n1##x = (int)( \ (I[0] = (img)(x,y,z,v)), \ (I[2] = (img)(x,_n1##y,z,v)), \ (I[4] = (img)(x,y,_n1##z,v)), \ (I[6] = (img)(x,_n1##y,_n1##z,v)), \ x+1>=(int)(img).width?(int)((img).width)-1:x+1); \ x<=(int)(x1) && ((_n1##x<(int)((img).width) && ( \ (I[1] = (img)(_n1##x,y,z,v)), \ (I[3] = (img)(_n1##x,_n1##y,z,v)), \ (I[5] = (img)(_n1##x,y,_n1##z,v)), \ (I[7] = (img)(_n1##x,_n1##y,_n1##z,v)),1)) || \ x==--_n1##x); \ I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \ ++x, ++_n1##x) #define cimg_for3x3x3(img,x,y,z,v,I) \ cimg_for3((img).depth,z) cimg_for3((img).height,y) for (int x = 0, \ _p1##x = 0, \ _n1##x = (int)( \ (I[0] = I[1] = (img)(0,_p1##y,_p1##z,v)), \ (I[3] = I[4] = (img)(0,y,_p1##z,v)), \ (I[6] = I[7] = (img)(0,_n1##y,_p1##z,v)), \ (I[9] = I[10] = (img)(0,_p1##y,z,v)), \ (I[12] = I[13] = (img)(0,y,z,v)), \ (I[15] = I[16] = (img)(0,_n1##y,z,v)), \ (I[18] = I[19] = (img)(0,_p1##y,_n1##z,v)), \ (I[21] = I[22] = (img)(0,y,_n1##z,v)), \ (I[24] = I[25] = (img)(0,_n1##y,_n1##z,v)), \ 1>=(img).width?(int)((img).width)-1:1); \ (_n1##x<(int)((img).width) && ( \ (I[2] = (img)(_n1##x,_p1##y,_p1##z,v)), \ (I[5] = (img)(_n1##x,y,_p1##z,v)), \ (I[8] = (img)(_n1##x,_n1##y,_p1##z,v)), \ (I[11] = (img)(_n1##x,_p1##y,z,v)), \ (I[14] = (img)(_n1##x,y,z,v)), \ (I[17] = (img)(_n1##x,_n1##y,z,v)), \ (I[20] = (img)(_n1##x,_p1##y,_n1##z,v)), \ (I[23] = (img)(_n1##x,y,_n1##z,v)), \ (I[26] = (img)(_n1##x,_n1##y,_n1##z,v)),1)) || \ x==--_n1##x; \ I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \ I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \ I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \ _p1##x = x++, ++_n1##x) #define cimg_for_in3x3x3(img,x0,y0,z0,x1,y1,z1,x,y,z,v,I) \ 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), \ _p1##x = x-1<0?0:x-1, \ _n1##x = (int)( \ (I[0] = (img)(_p1##x,_p1##y,_p1##z,v)), \ (I[3] = (img)(_p1##x,y,_p1##z,v)), \ (I[6] = (img)(_p1##x,_n1##y,_p1##z,v)), \ (I[9] = (img)(_p1##x,_p1##y,z,v)), \ (I[12] = (img)(_p1##x,y,z,v)), \ (I[15] = (img)(_p1##x,_n1##y,z,v)), \ (I[18] = (img)(_p1##x,_p1##y,_n1##z,v)), \ (I[21] = (img)(_p1##x,y,_n1##z,v)), \ (I[24] = (img)(_p1##x,_n1##y,_n1##z,v)), \ (I[1] = (img)(x,_p1##y,_p1##z,v)), \ (I[4] = (img)(x,y,_p1##z,v)), \ (I[7] = (img)(x,_n1##y,_p1##z,v)), \ (I[10] = (img)(x,_p1##y,z,v)), \ (I[13] = (img)(x,y,z,v)), \ (I[16] = (img)(x,_n1##y,z,v)), \ (I[19] = (img)(x,_p1##y,_n1##z,v)), \ (I[22] = (img)(x,y,_n1##z,v)), \ (I[25] = (img)(x,_n1##y,_n1##z,v)), \ x+1>=(int)(img).width?(int)((img).width)-1:x+1); \ x<=(int)(x1) && ((_n1##x<(int)((img).width) && ( \ (I[2] = (img)(_n1##x,_p1##y,_p1##z,v)), \ (I[5] = (img)(_n1##x,y,_p1##z,v)), \ (I[8] = (img)(_n1##x,_n1##y,_p1##z,v)), \ (I[11] = (img)(_n1##x,_p1##y,z,v)), \ (I[14] = (img)(_n1##x,y,z,v)), \ (I[17] = (img)(_n1##x,_n1##y,z,v)), \ (I[20] = (img)(_n1##x,_p1##y,_n1##z,v)), \ (I[23] = (img)(_n1##x,y,_n1##z,v)), \ (I[26] = (img)(_n1##x,_n1##y,_n1##z,v)),1)) || \ x==--_n1##x); \ I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \ I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \ I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \ _p1##x = x++, ++_n1##x) /*------------------------------------------------ # # # Definition of the cimg_library:: namespace # # -------------------------------------------------*/ //! This namespace encompasses all classes and functions of the %CImg library. /** This namespace is defined to avoid functions and class names collisions that could happen with the include of other C++ header files. Anyway, it should not happen often and you should reasonnably start most of your %CImg-based programs with \code #include "CImg.h" using namespace cimg_library; \endcode to simplify the declaration of %CImg Library variables afterwards. **/ namespace cimg_library { // Declare the only four classes of the CImg Library. // template struct CImg; template struct CImgList; struct CImgDisplay; struct CImgException; // (Pre)declare the cimg namespace. // This is not the complete namespace declaration. It only contains some // necessary stuffs to ensure a correct declaration order of classes and functions // defined afterwards. // namespace cimg { #ifdef cimg_use_vt100 const char t_normal[] = { 0x1b,'[','0',';','0',';','0','m','\0' }; const char t_red[] = { 0x1b,'[','4',';','3','1',';','5','9','m','\0' }; const char t_bold[] = { 0x1b,'[','1','m','\0' }; const char t_purple[] = { 0x1b,'[','0',';','3','5',';','5','9','m','\0' }; const char t_green[] = { 0x1b,'[','0',';','3','2',';','5','9','m','\0' }; #else const char t_normal[] = { '\0' }; const char *const t_red = cimg::t_normal, *const t_bold = cimg::t_normal, *const t_purple = cimg::t_normal, *const t_green = cimg::t_normal; #endif inline void info(); //! Get/set the current CImg exception mode. /** The way error messages are handled by CImg can be changed dynamically, using this function. Possible values are : - 0 to hide debug messages (quiet mode, but exceptions are still thrown). - 1 to display debug messages on standard error (console). - 2 to display debug messages in modal windows (default behavior). - 3 to do as 1 + add extra warnings (may slow down the code !). - 4 to do as 2 + add extra warnings (may slow down the code !). **/ inline unsigned int& exception_mode() { static unsigned int mode = cimg_debug; return mode; } inline int dialog(const char *title, const char *msg, const char *button1_txt="OK", const char *button2_txt=0, const char *button3_txt=0, const char *button4_txt=0, const char *button5_txt=0, const char *button6_txt=0, const bool centering=false); } /*---------------------------------------------- # # Definition of the CImgException structures # ----------------------------------------------*/ //! Instances of this class are thrown when errors occur during a %CImg library function call. /** \section ex1 Overview CImgException is the base class of %CImg exceptions. Exceptions are thrown by the %CImg Library when an error occured in a %CImg library function call. CImgException is seldom thrown itself. Children classes that specify the kind of error encountered are generally used instead. These sub-classes are : - \b CImgInstanceException : Thrown when the instance associated to the called %CImg function is not correctly defined. Generally, this exception is thrown when one tries to process \a empty images. The example below will throw a \a CImgInstanceException. \code CImg img; // Construct an empty image. img.blur(10); // Try to blur the image. \endcode - \b CImgArgumentException : Thrown when one of the arguments given to the called %CImg function is not correct. Generally, this exception is thrown when arguments passed to the function are outside an admissible range of values. The example below will throw a \a CImgArgumentException. \code CImg img(100,100,1,3); // Define a 100x100 color image with float pixels. img = 0; // Try to fill pixels from the 0 pointer (invalid argument to operator=() ). \endcode - \b CImgIOException : Thrown when an error occured when trying to load or save image files. The example below will throw a \a CImgIOException. \code CImg img("file_doesnt_exist.jpg"); // Try to load a file that doesn't exist. \endcode - \b CImgDisplayException : Thrown when an error occured when trying to display an image in a window. This exception is thrown when image display request cannot be satisfied. The parent class CImgException may be thrown itself when errors that cannot be classified in one of the above type occur. It is recommended not to throw CImgExceptions yourself, since there are normally reserved to %CImg Library functions. \b CImgInstanceException, \b CImgArgumentException, \b CImgIOException and \b CImgDisplayException are simple subclasses of CImgException and are thus not detailled more in this reference documentation. \section ex2 Exception handling When an error occurs, the %CImg Library first displays the error in a modal window. Then, it throws an instance of the corresponding exception class, generally leading the program to stop (this is the default behavior). You can bypass this default behavior by handling the exceptions yourself, using a code block try { ... } catch() { ... }. In this case, you can avoid the apparition of the modal window, by defining the environment variable cimg_debug to 0 before including the %CImg header file. The example below shows how to cleanly handle %CImg Library exceptions : \code #define cimg_debug 0 // Disable modal window in CImg exceptions. #define "CImg.h" int main() { try { ...; // Here, do what you want. } catch (CImgInstanceException &e) { std::fprintf(stderr,"CImg Library Error : %s",e.message); // Display your own error message ... // Do what you want now. } } \endcode **/ struct CImgException { #define _cimg_exception_err(etype,disp_flag) \ cimg_std::va_list ap; va_start(ap,format); cimg_std::vsprintf(message,format,ap); va_end(ap); \ switch (cimg::exception_mode()) { \ case 0 : break; \ case 2 : case 4 : try { cimg::dialog(etype,message,"Abort"); } catch (CImgException&) { \ cimg_std::fprintf(cimg_stdout,"\n%s# %s%s :\n%s\n\n",cimg::t_red,etype,cimg::t_normal,message); \ } break; \ default : cimg_std::fprintf(cimg_stdout,"\n%s# %s%s :\n%s\n\n",cimg::t_red,etype,cimg::t_normal,message); \ } \ if (cimg::exception_mode()>=3) cimg_library::cimg::info(); char message[1024]; //!< Message associated with the error that thrown the exception. CImgException() { message[0]='\0'; } CImgException(const char *format, ...) { _cimg_exception_err("CImgException",true); } }; // The \ref CImgInstanceException class is used to throw an exception related // to a non suitable instance encountered in a library function call. struct CImgInstanceException: public CImgException { CImgInstanceException(const char *format, ...) { _cimg_exception_err("CImgInstanceException",true); } }; // The \ref CImgArgumentException class is used to throw an exception related // to invalid arguments encountered in a library function call. struct CImgArgumentException: public CImgException { CImgArgumentException(const char *format, ...) { _cimg_exception_err("CImgArgumentException",true); } }; // The \ref CImgIOException class is used to throw an exception related // to Input/Output file problems encountered in a library function call. struct CImgIOException: public CImgException { CImgIOException(const char *format, ...) { _cimg_exception_err("CImgIOException",true); } }; // The CImgDisplayException class is used to throw an exception related to display problems // encountered in a library function call. struct CImgDisplayException: public CImgException { CImgDisplayException(const char *format, ...) { _cimg_exception_err("CImgDisplayException",false); } }; // The CImgWarningException class is used to throw an exception for warnings // encountered in a library function call. struct CImgWarningException: public CImgException { CImgWarningException(const char *format, ...) { _cimg_exception_err("CImgWarningException",false); } }; /*------------------------------------- # # Definition of the namespace 'cimg' # --------------------------------------*/ //! Namespace that encompasses \a low-level functions and variables of the %CImg Library. /** Most of the functions and variables within this namespace are used by the library for low-level processing. Nevertheless, documented variables and functions of this namespace may be used safely in your own source code. \warning Never write using namespace cimg_library::cimg; in your source code, since a lot of functions of the cimg:: namespace have prototypes similar to standard C functions that could defined in the global namespace ::. **/ namespace cimg { // Define the traits that will be used to determine the best data type to work with. // template struct type { static const char* string() { static const char* s[] = { "unknown", "unknown8", "unknown16", "unknown24", "unknown32", "unknown40", "unknown48", "unknown56", "unknown64", "unknown72", "unknown80", "unknown88", "unknown96", "unknown104", "unknown112", "unknown120", "unknown128" }; return s[(sizeof(T)<17)?sizeof(T):0]; } static bool is_float() { return false; } static T min() { return (T)-1>0?(T)0:(T)-1<<(8*sizeof(T)-1); } static T max() { return (T)-1>0?(T)-1:~((T)-1<<(8*sizeof(T)-1)); } static const char* format() { return "%s"; } static const char* format(const T val) { static const char *s = "unknown"; return s; } }; template<> struct type { static const char* string() { static const char *const s = "bool"; return s; } static bool is_float() { return false; } static bool min() { return false; } static bool max() { return true; } static const char* format() { return "%s"; } static const char* format(const bool val) { static const char* s[] = { "false", "true" }; return s[val?1:0]; } }; template<> struct type { static const char* string() { static const char *const s = "unsigned char"; return s; } static bool is_float() { return false; } static unsigned char min() { return 0; } static unsigned char max() { return (unsigned char)~0U; } static const char* format() { return "%u"; } static unsigned int format(const unsigned char val) { return (unsigned int)val; } }; template<> struct type { static const char* string() { static const char *const s = "char"; return s; } static bool is_float() { return false; } static char min() { return (char)(-1L<<(8*sizeof(char)-1)); } static char max() { return ~((char)(-1L<<(8*sizeof(char)-1))); } static const char* format() { return "%d"; } static int format(const char val) { return (int)val; } }; template<> struct type { static const char* string() { static const char *const s = "signed char"; return s; } static bool is_float() { return false; } static signed char min() { return (signed char)(-1L<<(8*sizeof(signed char)-1)); } static signed char max() { return ~((signed char)(-1L<<(8*sizeof(signed char)-1))); } static const char* format() { return "%d"; } static unsigned int format(const signed char val) { return (int)val; } }; template<> struct type { static const char* string() { static const char *const s = "unsigned short"; return s; } static bool is_float() { return false; } static unsigned short min() { return 0; } static unsigned short max() { return (unsigned short)~0U; } static const char* format() { return "%u"; } static unsigned int format(const unsigned short val) { return (unsigned int)val; } }; template<> struct type { static const char* string() { static const char *const s = "short"; return s; } static bool is_float() { return false; } static short min() { return (short)(-1L<<(8*sizeof(short)-1)); } static short max() { return ~((short)(-1L<<(8*sizeof(short)-1))); } static const char* format() { return "%d"; } static int format(const short val) { return (int)val; } }; template<> struct type { static const char* string() { static const char *const s = "unsigned int"; return s; } static bool is_float() { return false; } static unsigned int min() { return 0; } static unsigned int max() { return (unsigned int)~0U; } static const char* format() { return "%u"; } static unsigned int format(const unsigned int val) { return val; } }; template<> struct type { static const char* string() { static const char *const s = "int"; return s; } static bool is_float() { return false; } static int min() { return (int)(-1L<<(8*sizeof(int)-1)); } static int max() { return ~((int)(-1L<<(8*sizeof(int)-1))); } static const char* format() { return "%d"; } static int format(const int val) { return val; } }; template<> struct type { static const char* string() { static const char *const s = "unsigned long"; return s; } static bool is_float() { return false; } static unsigned long min() { return 0; } static unsigned long max() { return (unsigned long)~0UL; } static const char* format() { return "%lu"; } static unsigned long format(const unsigned long val) { return val; } }; template<> struct type { static const char* string() { static const char *const s = "long"; return s; } static bool is_float() { return false; } static long min() { return (long)(-1L<<(8*sizeof(long)-1)); } static long max() { return ~((long)(-1L<<(8*sizeof(long)-1))); } static const char* format() { return "%ld"; } static long format(const long val) { return val; } }; template<> struct type { static const char* string() { static const char *const s = "float"; return s; } static bool is_float() { return true; } static float min() { return -3.4E38f; } static float max() { return 3.4E38f; } static const char* format() { return "%g"; } static double format(const float val) { return (double)val; } }; template<> struct type { static const char* string() { static const char *const s = "double"; return s; } static bool is_float() { return true; } static double min() { return -1.7E308; } static double max() { return 1.7E308; } static const char* format() { return "%g"; } static double format(const double val) { return val; } }; template struct superset { typedef T type; }; template<> struct superset { typedef unsigned char type; }; template<> struct superset { typedef char type; }; template<> struct superset { typedef signed char type; }; template<> struct superset { typedef unsigned short type; }; template<> struct superset { typedef short type; }; template<> struct superset { typedef unsigned int type; }; template<> struct superset { typedef int type; }; template<> struct superset { typedef unsigned long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef float type; }; template<> struct superset { typedef double type; }; template<> struct superset { typedef short type; }; template<> struct superset { typedef short type; }; template<> struct superset { typedef unsigned short type; }; template<> struct superset { typedef short type; }; template<> struct superset { typedef unsigned int type; }; template<> struct superset { typedef int type; }; template<> struct superset { typedef unsigned long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef float type; }; template<> struct superset { typedef double type; }; template<> struct superset { typedef short type; }; template<> struct superset { typedef short type; }; template<> struct superset { typedef int type; }; template<> struct superset { typedef short type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef int type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef float type; }; template<> struct superset { typedef double type; }; template<> struct superset { typedef short type; }; template<> struct superset { typedef short type; }; template<> struct superset { typedef int type; }; template<> struct superset { typedef short type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef int type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef float type; }; template<> struct superset { typedef double type; }; template<> struct superset { typedef int type; }; template<> struct superset { typedef int type; }; template<> struct superset { typedef int type; }; template<> struct superset { typedef unsigned int type; }; template<> struct superset { typedef int type; }; template<> struct superset { typedef unsigned long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef float type; }; template<> struct superset { typedef double type; }; template<> struct superset { typedef int type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef int type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef float type; }; template<> struct superset { typedef double type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef unsigned long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef float type; }; template<> struct superset { typedef double type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef float type; }; template<> struct superset { typedef double type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef float type; }; template<> struct superset { typedef double type; }; template<> struct superset { typedef float type; }; template<> struct superset { typedef double type; }; template<> struct superset { typedef double type; }; template struct superset2 { typedef typename superset::type>::type type; }; template struct superset3 { typedef typename superset::type>::type type; }; template struct last { typedef t2 type; }; #define _cimg_Tuchar typename cimg::superset::type #define _cimg_Tint typename cimg::superset::type #define _cimg_Tfloat typename cimg::superset::type #define _cimg_Tdouble typename cimg::superset::type #define _cimg_Tt typename cimg::superset::type // Define internal library variables. // #if cimg_display==1 struct X11info { volatile unsigned int nb_wins; pthread_t* event_thread; CImgDisplay* wins[1024]; Display* display; unsigned int nb_bits; GC* gc; bool blue_first; bool byte_order; bool shm_enabled; #ifdef cimg_use_xrandr XRRScreenSize *resolutions; Rotation curr_rotation; unsigned int curr_resolution; unsigned int nb_resolutions; #endif X11info():nb_wins(0),event_thread(0),display(0), nb_bits(0),gc(0),blue_first(false),byte_order(false),shm_enabled(false) { #ifdef cimg_use_xrandr resolutions = 0; curr_rotation = 0; curr_resolution = nb_resolutions = 0; #endif } }; #if defined(cimg_module) X11info& X11attr(); #elif defined(cimg_main) X11info& X11attr() { static X11info val; return val; } #else inline X11info& X11attr() { static X11info val; return val; } #endif #elif cimg_display==2 struct Win32info { HANDLE wait_event; Win32info() { wait_event = CreateEvent(0,FALSE,FALSE,0); } }; #if defined(cimg_module) Win32info& Win32attr(); #elif defined(cimg_main) Win32info& Win32attr() { static Win32info val; return val; } #else inline Win32info& Win32attr() { static Win32info val; return val; } #endif #elif cimg_display==3 struct CarbonInfo { MPCriticalRegionID windowListCR; // Protects access to the list of windows int windowCount; // Count of displays used on the screen pthread_t event_thread; // The background event thread MPSemaphoreID sync_event; // Event used to perform tasks synchronizations MPSemaphoreID wait_event; // Event used to notify that new events occured on the display MPQueueID com_queue; // The message queue CarbonInfo(): windowCount(0),event_thread(0),sync_event(0),com_queue(0) { if (MPCreateCriticalRegion(&windowListCR) != noErr) // Create the critical region throw CImgDisplayException("MPCreateCriticalRegion failed."); if (MPCreateSemaphore(1, 0, &sync_event) != noErr) // Create the inter-thread sync object throw CImgDisplayException("MPCreateSemaphore failed."); if (MPCreateSemaphore(1, 0, &wait_event) != noErr) // Create the event sync object throw CImgDisplayException("MPCreateSemaphore failed."); if (MPCreateQueue(&com_queue) != noErr) // Create the shared queue throw CImgDisplayException("MPCreateQueue failed."); } ~CarbonInfo() { if (event_thread != 0) { // Terminates the resident thread, if needed pthread_cancel(event_thread); pthread_join(event_thread, NULL); event_thread = 0; } if (MPDeleteCriticalRegion(windowListCR) != noErr) // Delete the critical region throw CImgDisplayException("MPDeleteCriticalRegion failed."); if (MPDeleteSemaphore(wait_event) != noErr) // Delete the event sync event throw CImgDisplayException("MPDeleteEvent failed."); if (MPDeleteSemaphore(sync_event) != noErr) // Delete the inter-thread sync event throw CImgDisplayException("MPDeleteEvent failed."); if (MPDeleteQueue(com_queue) != noErr) // Delete the shared queue throw CImgDisplayException("MPDeleteQueue failed."); } }; #if defined(cimg_module) CarbonInfo& CarbonAttr(); #elif defined(cimg_main) CarbonInfo CarbonAttr() { static CarbonInfo val; return val; } #else inline CarbonInfo& CarbonAttr() { static CarbonInfo val; return val; } #endif #endif #if cimg_display==1 // Keycodes for X11-based graphical systems. // const unsigned int keyESC = XK_Escape; const unsigned int keyF1 = XK_F1; const unsigned int keyF2 = XK_F2; const unsigned int keyF3 = XK_F3; const unsigned int keyF4 = XK_F4; const unsigned int keyF5 = XK_F5; const unsigned int keyF6 = XK_F6; const unsigned int keyF7 = XK_F7; const unsigned int keyF8 = XK_F8; const unsigned int keyF9 = XK_F9; const unsigned int keyF10 = XK_F10; const unsigned int keyF11 = XK_F11; const unsigned int keyF12 = XK_F12; const unsigned int keyPAUSE = XK_Pause; const unsigned int key1 = XK_1; const unsigned int key2 = XK_2; const unsigned int key3 = XK_3; const unsigned int key4 = XK_4; const unsigned int key5 = XK_5; const unsigned int key6 = XK_6; const unsigned int key7 = XK_7; const unsigned int key8 = XK_8; const unsigned int key9 = XK_9; const unsigned int key0 = XK_0; const unsigned int keyBACKSPACE = XK_BackSpace; const unsigned int keyINSERT = XK_Insert; const unsigned int keyHOME = XK_Home; const unsigned int keyPAGEUP = XK_Page_Up; const unsigned int keyTAB = XK_Tab; const unsigned int keyQ = XK_q; const unsigned int keyW = XK_w; const unsigned int keyE = XK_e; const unsigned int keyR = XK_r; const unsigned int keyT = XK_t; const unsigned int keyY = XK_y; const unsigned int keyU = XK_u; const unsigned int keyI = XK_i; const unsigned int keyO = XK_o; const unsigned int keyP = XK_p; const unsigned int keyDELETE = XK_Delete; const unsigned int keyEND = XK_End; const unsigned int keyPAGEDOWN = XK_Page_Down; const unsigned int keyCAPSLOCK = XK_Caps_Lock; const unsigned int keyA = XK_a; const unsigned int keyS = XK_s; const unsigned int keyD = XK_d; const unsigned int keyF = XK_f; const unsigned int keyG = XK_g; const unsigned int keyH = XK_h; const unsigned int keyJ = XK_j; const unsigned int keyK = XK_k; const unsigned int keyL = XK_l; const unsigned int keyENTER = XK_Return; const unsigned int keySHIFTLEFT = XK_Shift_L; const unsigned int keyZ = XK_z; const unsigned int keyX = XK_x; const unsigned int keyC = XK_c; const unsigned int keyV = XK_v; const unsigned int keyB = XK_b; const unsigned int keyN = XK_n; const unsigned int keyM = XK_m; const unsigned int keySHIFTRIGHT = XK_Shift_R; const unsigned int keyARROWUP = XK_Up; const unsigned int keyCTRLLEFT = XK_Control_L; const unsigned int keyAPPLEFT = XK_Super_L; const unsigned int keyALT = XK_Alt_L; const unsigned int keySPACE = XK_space; const unsigned int keyALTGR = XK_Alt_R; const unsigned int keyAPPRIGHT = XK_Super_R; const unsigned int keyMENU = XK_Menu; const unsigned int keyCTRLRIGHT = XK_Control_R; const unsigned int keyARROWLEFT = XK_Left; const unsigned int keyARROWDOWN = XK_Down; const unsigned int keyARROWRIGHT = XK_Right; const unsigned int keyPAD0 = XK_KP_0; const unsigned int keyPAD1 = XK_KP_1; const unsigned int keyPAD2 = XK_KP_2; const unsigned int keyPAD3 = XK_KP_3; const unsigned int keyPAD4 = XK_KP_4; const unsigned int keyPAD5 = XK_KP_5; const unsigned int keyPAD6 = XK_KP_6; const unsigned int keyPAD7 = XK_KP_7; const unsigned int keyPAD8 = XK_KP_8; const unsigned int keyPAD9 = XK_KP_9; const unsigned int keyPADADD = XK_KP_Add; const unsigned int keyPADSUB = XK_KP_Subtract; const unsigned int keyPADMUL = XK_KP_Multiply; const unsigned int keyPADDIV = XK_KP_Divide; #elif cimg_display==2 // Keycodes for Windows. // const unsigned int keyESC = VK_ESCAPE; const unsigned int keyF1 = VK_F1; const unsigned int keyF2 = VK_F2; const unsigned int keyF3 = VK_F3; const unsigned int keyF4 = VK_F4; const unsigned int keyF5 = VK_F5; const unsigned int keyF6 = VK_F6; const unsigned int keyF7 = VK_F7; const unsigned int keyF8 = VK_F8; const unsigned int keyF9 = VK_F9; const unsigned int keyF10 = VK_F10; const unsigned int keyF11 = VK_F11; const unsigned int keyF12 = VK_F12; const unsigned int keyPAUSE = VK_PAUSE; const unsigned int key1 = '1'; const unsigned int key2 = '2'; const unsigned int key3 = '3'; const unsigned int key4 = '4'; const unsigned int key5 = '5'; const unsigned int key6 = '6'; const unsigned int key7 = '7'; const unsigned int key8 = '8'; const unsigned int key9 = '9'; const unsigned int key0 = '0'; const unsigned int keyBACKSPACE = VK_BACK; const unsigned int keyINSERT = VK_INSERT; const unsigned int keyHOME = VK_HOME; const unsigned int keyPAGEUP = VK_PRIOR; const unsigned int keyTAB = VK_TAB; const unsigned int keyQ = 'Q'; const unsigned int keyW = 'W'; const unsigned int keyE = 'E'; const unsigned int keyR = 'R'; const unsigned int keyT = 'T'; const unsigned int keyY = 'Y'; const unsigned int keyU = 'U'; const unsigned int keyI = 'I'; const unsigned int keyO = 'O'; const unsigned int keyP = 'P'; const unsigned int keyDELETE = VK_DELETE; const unsigned int keyEND = VK_END; const unsigned int keyPAGEDOWN = VK_NEXT; const unsigned int keyCAPSLOCK = VK_CAPITAL; const unsigned int keyA = 'A'; const unsigned int keyS = 'S'; const unsigned int keyD = 'D'; const unsigned int keyF = 'F'; const unsigned int keyG = 'G'; const unsigned int keyH = 'H'; const unsigned int keyJ = 'J'; const unsigned int keyK = 'K'; const unsigned int keyL = 'L'; const unsigned int keyENTER = VK_RETURN; const unsigned int keySHIFTLEFT = VK_SHIFT; const unsigned int keyZ = 'Z'; const unsigned int keyX = 'X'; const unsigned int keyC = 'C'; const unsigned int keyV = 'V'; const unsigned int keyB = 'B'; const unsigned int keyN = 'N'; const unsigned int keyM = 'M'; const unsigned int keySHIFTRIGHT = VK_SHIFT; const unsigned int keyARROWUP = VK_UP; const unsigned int keyCTRLLEFT = VK_CONTROL; const unsigned int keyAPPLEFT = VK_LWIN; const unsigned int keyALT = VK_LMENU; const unsigned int keySPACE = VK_SPACE; const unsigned int keyALTGR = VK_CONTROL; const unsigned int keyAPPRIGHT = VK_RWIN; const unsigned int keyMENU = VK_APPS; const unsigned int keyCTRLRIGHT = VK_CONTROL; const unsigned int keyARROWLEFT = VK_LEFT; const unsigned int keyARROWDOWN = VK_DOWN; const unsigned int keyARROWRIGHT = VK_RIGHT; const unsigned int keyPAD0 = 0x60; const unsigned int keyPAD1 = 0x61; const unsigned int keyPAD2 = 0x62; const unsigned int keyPAD3 = 0x63; const unsigned int keyPAD4 = 0x64; const unsigned int keyPAD5 = 0x65; const unsigned int keyPAD6 = 0x66; const unsigned int keyPAD7 = 0x67; const unsigned int keyPAD8 = 0x68; const unsigned int keyPAD9 = 0x69; const unsigned int keyPADADD = VK_ADD; const unsigned int keyPADSUB = VK_SUBTRACT; const unsigned int keyPADMUL = VK_MULTIPLY; const unsigned int keyPADDIV = VK_DIVIDE; #elif cimg_display==3 // Keycodes for MacOSX, when using the Carbon framework. // const unsigned int keyESC = kEscapeCharCode; const unsigned int keyF1 = 2U; const unsigned int keyF2 = 3U; const unsigned int keyF3 = 4U; const unsigned int keyF4 = 5U; const unsigned int keyF5 = 6U; const unsigned int keyF6 = 7U; const unsigned int keyF7 = 8U; const unsigned int keyF8 = 9U; const unsigned int keyF9 = 10U; const unsigned int keyF10 = 11U; const unsigned int keyF11 = 12U; const unsigned int keyF12 = 13U; const unsigned int keyPAUSE = 14U; const unsigned int key1 = '1'; const unsigned int key2 = '2'; const unsigned int key3 = '3'; const unsigned int key4 = '4'; const unsigned int key5 = '5'; const unsigned int key6 = '6'; const unsigned int key7 = '7'; const unsigned int key8 = '8'; const unsigned int key9 = '9'; const unsigned int key0 = '0'; const unsigned int keyBACKSPACE = kBackspaceCharCode; const unsigned int keyINSERT = 26U; const unsigned int keyHOME = kHomeCharCode; const unsigned int keyPAGEUP = kPageUpCharCode; const unsigned int keyTAB = kTabCharCode; const unsigned int keyQ = 'q'; const unsigned int keyW = 'w'; const unsigned int keyE = 'e'; const unsigned int keyR = 'r'; const unsigned int keyT = 't'; const unsigned int keyY = 'y'; const unsigned int keyU = 'u'; const unsigned int keyI = 'i'; const unsigned int keyO = 'o'; const unsigned int keyP = 'p'; const unsigned int keyDELETE = kDeleteCharCode; const unsigned int keyEND = kEndCharCode; const unsigned int keyPAGEDOWN = kPageDownCharCode; const unsigned int keyCAPSLOCK = 43U; const unsigned int keyA = 'a'; const unsigned int keyS = 's'; const unsigned int keyD = 'd'; const unsigned int keyF = 'f'; const unsigned int keyG = 'g'; const unsigned int keyH = 'h'; const unsigned int keyJ = 'j'; const unsigned int keyK = 'k'; const unsigned int keyL = 'l'; const unsigned int keyENTER = kEnterCharCode; const unsigned int keySHIFTLEFT = 54U; //Macintosh modifier key, emulated const unsigned int keyZ = 'z'; const unsigned int keyX = 'x'; const unsigned int keyC = 'c'; const unsigned int keyV = 'v'; const unsigned int keyB = 'b'; const unsigned int keyN = 'n'; const unsigned int keyM = 'm'; const unsigned int keySHIFTRIGHT = 62U; //Macintosh modifier key, emulated const unsigned int keyARROWUP = kUpArrowCharCode; const unsigned int keyCTRLLEFT = 64U; //Macintosh modifier key, emulated const unsigned int keyAPPLEFT = 65U; //Macintosh modifier key, emulated const unsigned int keyALT = 66U; const unsigned int keySPACE = kSpaceCharCode; const unsigned int keyALTGR = 67U; //Macintosh modifier key, emulated const unsigned int keyAPPRIGHT = 68U; //Aliased on keyAPPLEFT const unsigned int keyMENU = 69U; const unsigned int keyCTRLRIGHT = 70U; //Macintosh modifier key, emulated const unsigned int keyARROWLEFT = kLeftArrowCharCode; const unsigned int keyARROWDOWN = kDownArrowCharCode; const unsigned int keyARROWRIGHT = kRightArrowCharCode; const unsigned int keyPAD0 = 74U; const unsigned int keyPAD1 = 75U; const unsigned int keyPAD2 = 76U; const unsigned int keyPAD3 = 77U; const unsigned int keyPAD4 = 78U; const unsigned int keyPAD5 = 79U; const unsigned int keyPAD6 = 80U; const unsigned int keyPAD7 = 81U; const unsigned int keyPAD8 = 82U; const unsigned int keyPAD9 = 83U; const unsigned int keyPADADD = 84U; const unsigned int keyPADSUB = 85U; const unsigned int keyPADMUL = 86U; const unsigned int keyPADDIV = 87U; #else // Define unknow keycodes when no display are available. // (should rarely be used then !). // const unsigned int keyESC = 1U; const unsigned int keyF1 = 2U; const unsigned int keyF2 = 3U; const unsigned int keyF3 = 4U; const unsigned int keyF4 = 5U; const unsigned int keyF5 = 6U; const unsigned int keyF6 = 7U; const unsigned int keyF7 = 8U; const unsigned int keyF8 = 9U; const unsigned int keyF9 = 10U; const unsigned int keyF10 = 11U; const unsigned int keyF11 = 12U; const unsigned int keyF12 = 13U; const unsigned int keyPAUSE = 14U; const unsigned int key1 = 15U; const unsigned int key2 = 16U; const unsigned int key3 = 17U; const unsigned int key4 = 18U; const unsigned int key5 = 19U; const unsigned int key6 = 20U; const unsigned int key7 = 21U; const unsigned int key8 = 22U; const unsigned int key9 = 23U; const unsigned int key0 = 24U; const unsigned int keyBACKSPACE = 25U; const unsigned int keyINSERT = 26U; const unsigned int keyHOME = 27U; const unsigned int keyPAGEUP = 28U; const unsigned int keyTAB = 29U; const unsigned int keyQ = 30U; const unsigned int keyW = 31U; const unsigned int keyE = 32U; const unsigned int keyR = 33U; const unsigned int keyT = 34U; const unsigned int keyY = 35U; const unsigned int keyU = 36U; const unsigned int keyI = 37U; const unsigned int keyO = 38U; const unsigned int keyP = 39U; const unsigned int keyDELETE = 40U; const unsigned int keyEND = 41U; const unsigned int keyPAGEDOWN = 42U; const unsigned int keyCAPSLOCK = 43U; const unsigned int keyA = 44U; const unsigned int keyS = 45U; const unsigned int keyD = 46U; const unsigned int keyF = 47U; const unsigned int keyG = 48U; const unsigned int keyH = 49U; const unsigned int keyJ = 50U; const unsigned int keyK = 51U; const unsigned int keyL = 52U; const unsigned int keyENTER = 53U; const unsigned int keySHIFTLEFT = 54U; const unsigned int keyZ = 55U; const unsigned int keyX = 56U; const unsigned int keyC = 57U; const unsigned int keyV = 58U; const unsigned int keyB = 59U; const unsigned int keyN = 60U; const unsigned int keyM = 61U; const unsigned int keySHIFTRIGHT = 62U; const unsigned int keyARROWUP = 63U; const unsigned int keyCTRLLEFT = 64U; const unsigned int keyAPPLEFT = 65U; const unsigned int keyALT = 66U; const unsigned int keySPACE = 67U; const unsigned int keyALTGR = 68U; const unsigned int keyAPPRIGHT = 69U; const unsigned int keyMENU = 70U; const unsigned int keyCTRLRIGHT = 71U; const unsigned int keyARROWLEFT = 72U; const unsigned int keyARROWDOWN = 73U; const unsigned int keyARROWRIGHT = 74U; const unsigned int keyPAD0 = 75U; const unsigned int keyPAD1 = 76U; const unsigned int keyPAD2 = 77U; const unsigned int keyPAD3 = 78U; const unsigned int keyPAD4 = 79U; const unsigned int keyPAD5 = 80U; const unsigned int keyPAD6 = 81U; const unsigned int keyPAD7 = 82U; const unsigned int keyPAD8 = 83U; const unsigned int keyPAD9 = 84U; const unsigned int keyPADADD = 85U; const unsigned int keyPADSUB = 86U; const unsigned int keyPADMUL = 87U; const unsigned int keyPADDIV = 88U; #endif const double valuePI = 3.14159265358979323846; //!< Definition of the mathematical constant PI // Definition of a 7x11 font, used to return a default font for drawing text. const unsigned int font7x11[7*11*256/32] = { 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x0,0x7f0000,0x40000,0x0,0x0,0x4010c0a4,0x82000040,0x11848402,0x18480050,0x80430292,0x8023,0x9008000, 0x40218140,0x4000040,0x21800402,0x18000051,0x1060500,0x8083,0x10000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x24002,0x4031,0x80000000,0x10000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x81c0400,0x40020000,0x80070080,0x40440e00,0x0,0x0,0x1,0x88180000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x200000,0x0,0x0,0x80000,0x0,0x0,0x20212140,0x5000020,0x22400204,0x240000a0,0x40848500,0x4044,0x80010038,0x20424285,0xa000020, 0x42428204,0x2428e0a0,0x82090a14,0x4104,0x85022014,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10240a7,0x88484040,0x40800000,0x270c3,0x87811e0e, 0x7c70e000,0x78,0x3c23c1ef,0x1f3e1e89,0xf1c44819,0xa23cf0f3,0xc3cff120,0xc18307f4,0x4040400,0x20000,0x80080080,0x40200,0x0, 0x40000,0x2,0x8040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8188,0x50603800,0xf3c00000,0x1c004003,0xc700003e,0x18180,0xc993880,0x10204081, 0x2071ef9,0xf3e7cf9f,0x3e7c7911,0xe3c78f1e,0x7d1224,0x48906048,0x0,0x4000000,0x0,0x9000,0x0,0x0,0x2000,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x10240aa,0x14944080,0x23610000,0x68940,0x40831010,0x8891306,0x802044,0x44522208,0x90202088,0x40448819,0xb242890a,0x24011111, 0x49448814,0x4040a00,0xe2c3c7,0x8e3f3cb9,0xc1c44216,0xee38b0f2,0xe78f9120,0xc18507e2,0x8040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x101c207,0x88a04001,0x9c00000,0x2200a041,0x8200113a,0x8240,0x50a3110,0x2850a142,0x850c2081,0x2040204,0x8104592,0x142850a1, 0x42cd1224,0x4888bc48,0x70e1c387,0xe3b3c70,0xe1c38e1c,0x38707171,0xc3870e1c,0x10791224,0x48906c41,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x10003ee,0x15140080,0x21810000,0x48840,0x40851020,0x8911306,0x31fd804,0x9c522408,0x90204088,0x4045081a,0xba42890a,0x24011111, 0x49285024,0x2041b00,0x132408,0x910844c8,0x4044821b,0x7244c913,0x24041111,0x49488822,0x8040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x28204,0x85006001,0x6a414000,0x3a004043,0xc700113a,0x8245,0x50a3a00,0x2850a142,0x850c4081,0x2040204,0x81045d2,0x142850a1, 0x24951224,0x48852250,0x8102040,0x81054089,0x12244204,0x8108992,0x24489122,0x991224,0x4888b222,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x1000143,0xa988080,0x2147c01f,0x88840,0x83091c2c,0x1070f000,0xc000608,0xa48bc408,0x9e3c46f8,0x40460816,0xaa42f10b,0xc3811111, 0x35102044,0x1041100,0xf22408,0x9f084488,0x40470212,0x62448912,0x6041111,0x55308846,0x8061c80,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x1028704,0x8f805801,0x4be28fdf,0x220001f0,0x111a,0x60000182,0x82c5c710,0x44891224,0x489640f1,0xe3c78204,0x810e552,0x142850a1, 0x18a51224,0x48822250,0x78f1e3c7,0x8f1f40f9,0xf3e7c204,0x8108912,0x24489122,0x7ea91224,0x4888a222,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x10007e2,0x85648080,0x20010000,0x88841,0x8f8232,0x20881000,0xc1fc610,0xbefa2408,0x90204288,0x40450816,0xa642810a,0x4041110a, 0x36282084,0x1042080,0x1122408,0x90084488,0x40450212,0x62448912,0x184110a,0x55305082,0x8042700,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x1028207,0x82004801,0x68050040,0x1c000040,0x110a,0x60000001,0x45484d10,0x7cf9f3e7,0xcf944081,0x2040204,0x8104532,0x142850a1, 0x18a51224,0x48822248,0x89122448,0x91244081,0x2040204,0x8108912,0x24489122,0xc91224,0x48852214,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x282, 0x89630080,0x20010c00,0x30108842,0x810222,0x20882306,0x3001800,0x408a2208,0x90202288,0x40448814,0xa642810a,0x2041110a,0x26442104, 0x840000,0x1122408,0x90084488,0x40448212,0x62448912,0x84130a,0x36485102,0x8040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x101c208,0x4f802801, 0x8028040,0x40,0x130a,0x2,0x85e897a0,0x44891224,0x489c2081,0x2040204,0x8104532,0x142850a1,0x24cd1224,0x48823c44,0x89122448, 0x91244081,0x2040204,0x8108912,0x24489122,0xc93264,0xc9852214,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100028f,0x109f0080,0x20010c00, 0x303071f3,0xc7011c1c,0x4071c306,0x802010,0x3907c1ef,0x1f201e89,0xf3844f90,0xa23c80f2,0x17810e04,0x228223f4,0x840000,0xfbc3c7, 0x8f083c88,0x40444212,0x6238f0f2,0x7039d04,0x228423e2,0x8040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1008780,0x2201800,0xf0014000,0x1f0, 0x1d0a,0x5,0x851e140,0x83060c18,0x30671ef9,0xf3e7cf9f,0x3e7c7911,0xe3c78f1e,0x42f8e1c3,0x8702205c,0x7cf9f3e7,0xcf9b3c78,0xf1e3c204, 0x8107111,0xc3870e1c,0x10f1d3a7,0x4e823c08,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x40,0x40000400,0x200000,0x0,0x2,0x0,0x0,0x0,0x0,0x18, 0x0,0x4,0x44007f,0x0,0x400,0x400000,0x8010,0x0,0x6002,0x8040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1000000,0x200800,0x0,0x0,0x100a, 0x400000,0x44,0x0,0x400,0x0,0x0,0x0,0x0,0x0,0x0,0x800,0x0,0x0,0x0,0x0,0x62018,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x31,0x80000800, 0x400000,0x0,0x4,0x0,0x0,0x0,0x0,0xc,0x0,0x7,0x3c0000,0x0,0x3800,0x3800000,0x8010,0x0,0x1c001,0x881c0000,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x207000,0x0,0x0,0x100a,0xc00000,0x3c,0x0,0xc00,0x0,0x0,0x0,0x0,0x0,0x0,0x1800,0x0,0x0,0x0,0x0,0x1c2070 }; // Definition of a 10x13 font (used in dialog boxes). const unsigned int font10x13[256*10*13/32] = { 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80100c0, 0x68000300,0x801,0xc00010,0x100c000,0x68100,0x100c0680,0x2,0x403000,0x1000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x0,0x0,0x0,0x0,0x0,0x4020120, 0x58120480,0x402,0x1205008,0x2012050,0x58080,0x20120581,0x40000001,0x804812,0x2000000,0x0,0x300,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x140,0x80000,0x200402,0x800000,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x7010,0x7000000,0x8000200,0x20000,0xc0002000,0x8008,0x0,0x0,0x0,0x0,0x808,0x4000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x80000000,0x0,0x0,0x0,0x40000,0x0,0x0,0x0,0x0,0x480,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x80100c0,0x68000480,0x1001, 0xc00010,0x1018000,0x68100,0x100c0680,0x4,0x403000,0x1020000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20140,0x28081883,0x200801, 0x2a00000,0x10,0x1c0201c0,0x70040f80,0xc0f81c07,0x0,0x70,0x3e0303c0,0x3c3c0f83,0xe03c2107,0xe08810,0x18c31070,0x3c0703c0, 0x783e0842,0x22222208,0x83e04010,0x1008000,0x4000200,0x20001,0x2002,0x408008,0x0,0x0,0x100000,0x0,0x1008,0x2000000,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20080,0x38000880,0x8078140f,0x81c00000,0x3e000,0xc020180,0x60080001,0xe0000002,0xc00042,0x108e2010, 0xc0300c0,0x300c0303,0xf83c3e0f,0x83e0f81c,0x701c070,0x3c0c41c0,0x701c0701,0xc0001d08,0x42108421,0x8820088,0x4020120,0x58140480, 0x802,0x1205008,0x3014050,0xc058080,0x20120581,0x40000002,0x804814,0x2020050,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20140, 0x281e2484,0x80200801,0x1c02000,0x10,0x22060220,0x880c0801,0x82208,0x80000001,0x20008,0x41030220,0x40220802,0x402102,0x209010, 0x18c31088,0x22088220,0x80080842,0x22222208,0x80204010,0x1014000,0x200,0x20001,0x2000,0x8008,0x0,0x0,0x100000,0x0,0x1008, 0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x40000500,0x80800010,0x40200000,0x41000,0x12020040,0x10000003,0xa0000006, 0x12000c4,0x31014000,0xc0300c0,0x300c0302,0x80402008,0x2008008,0x2008020,0x220c4220,0x88220882,0x20002208,0x42108421,0x8820088, 0x0,0x300,0x0,0x0,0x0,0x14000000,0x0,0x200200,0x0,0x20000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0xfc282504,0x80001000, 0x82a02000,0x20,0x22020020,0x8140802,0x102208,0x80801006,0x18008,0x9c848220,0x80210802,0x802102,0x20a010,0x15429104,0x22104220, 0x80080842,0x22221405,0x404008,0x1022000,0x703c0,0x381e0701,0xc0783c02,0xc09008,0x1d83c070,0x3c078140,0x381c0882,0x21242208, 0x81e01008,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x201e0,0x40220500,0x80800027,0x20e02800,0x9c800,0x12020040, 0x20000883,0xa0200002,0x120a044,0x11064010,0x12048120,0x48120484,0x80802008,0x2008008,0x2008020,0x210a4411,0x4411044,0x10884508, 0x42108421,0x503c0b0,0x1c0701c0,0x701c0707,0x70381c07,0x1c07008,0x2008020,0x20f01c0,0x701c0701,0xc0201c08,0x82208822,0x883c088, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0x50281903,0x20001000,0x80802000,0x20,0x22020040,0x30240f03,0xc0101c08,0x80801018, 0x1fc06010,0xa48483c0,0x80210f03,0xe0803f02,0x20c010,0x15429104,0x22104220,0x70080841,0x41540805,0x804008,0x1041000,0x8220, 0x40220881,0x882202,0x40a008,0x12422088,0x22088180,0x40100882,0x21241408,0x80201008,0x2031000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x20280,0x401c0200,0x700028,0x21205000,0x92800,0xc1fc080,0x10000883,0xa0200002,0x1205049,0x12c19010,0x12048120,0x48120484, 0xf0803c0f,0x3c0f008,0x2008020,0x790a4411,0x4411044,0x10504908,0x42108421,0x5022088,0x2008020,0x8020080,0x88402208,0x82208808, 0x2008020,0x1e088220,0x88220882,0x20002608,0x82208822,0x8822088,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0x501c0264, 0xa0001000,0x8001fc00,0x7000020,0x22020080,0x83e0082,0x20202207,0x80000020,0x1020,0xa4848220,0x80210802,0x9c2102,0x20c010, 0x12425104,0x3c1043c0,0x8080841,0x41540802,0x804008,0x1000000,0x78220,0x40220f81,0x882202,0x40c008,0x12422088,0x22088100, 0x60100881,0x41540805,0x406008,0x1849000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20280,0xf0140200,0x880028,0x20e0a03f,0x709c800, 0x201c0,0x60000881,0xa0000007,0xc0284b,0x122eb020,0x12048120,0x48120487,0x80802008,0x2008008,0x2008020,0x21094411,0x4411044, 0x10204908,0x42108421,0x2022088,0x1e0781e0,0x781e0787,0xf8403e0f,0x83e0f808,0x2008020,0x22088220,0x88220882,0x21fc2a08,0x82208822, 0x5022050,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20001,0xf80a0294,0x40001000,0x80002000,0x20,0x22020100,0x8040082,0x20202200, 0x80000018,0x1fc06020,0xa48fc220,0x80210802,0x842102,0x20a010,0x12425104,0x20104240,0x8080841,0x41541402,0x1004008,0x1000000, 0x88220,0x40220801,0x882202,0x40a008,0x12422088,0x22088100,0x18100881,0x41540805,0x801008,0x2046000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x20280,0x401c0f80,0x80880028,0x20005001,0x94800,0x20000,0x880,0xa0000000,0x5015,0x4215040,0x3f0fc3f0,0xfc3f0fc8, 0x80802008,0x2008008,0x2008020,0x21094411,0x4411044,0x10505108,0x42108421,0x203c088,0x22088220,0x88220888,0x80402008,0x2008008, 0x2008020,0x22088220,0x88220882,0x20002a08,0x82208822,0x5022050,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa00a0494,0x60001000, 0x80002004,0x8020,0x22020200,0x88040882,0x20402201,0x801006,0x18000,0x9f084220,0x40220802,0x442102,0x209010,0x10423088,0x20088220, 0x8080840,0x80882202,0x2004008,0x1000000,0x88220,0x40220881,0x882202,0x409008,0x12422088,0x22088100,0x8100880,0x80881402, 0x1001008,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20280,0x40220200,0x80700027,0x20002801,0x92800,0x1fc000,0x980, 0xa0000000,0xa017,0x84417840,0x21084210,0x84210848,0x80402008,0x2008008,0x2008020,0x2208c220,0x88220882,0x20882208,0x42108421, 0x2020088,0x22088220,0x88220888,0xc8402208,0x82208808,0x2008020,0x22088220,0x88220882,0x20203208,0x82208822,0x2022020,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0xa03c0463,0x90000801,0x2004,0x8040,0x1c0703e0,0x70040701,0xc0401c06,0x801001,0x20020, 0x400843c0,0x3c3c0f82,0x3c2107,0x1c0881e,0x10423070,0x20070210,0xf0080780,0x80882202,0x3e04004,0x1000000,0x783c0,0x381e0701, 0x782202,0x408808,0x12422070,0x3c078100,0x700c0780,0x80882202,0x1e01008,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x201e0, 0xf8000200,0x80080010,0x40000001,0x41000,0x0,0xe80,0xa0000000,0x21,0x8e21038,0x21084210,0x84210848,0xf83c3e0f,0x83e0f81c, 0x701c070,0x3c08c1c0,0x701c0701,0xc0005c07,0x81e0781e,0x20200b0,0x1e0781e0,0x781e0787,0x30381c07,0x1c07008,0x2008020,0x1c0881c0, 0x701c0701,0xc0201c07,0x81e0781e,0x203c020,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000,0x801,0x4,0x40,0x0,0x0,0x0,0x1000, 0x0,0x3c000000,0x0,0x0,0x0,0x0,0x10000,0x0,0x0,0x4004,0x1000000,0x0,0x0,0x80000,0x400000,0x0,0x20008000,0x0,0x4,0x1008,0x2000000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x0,0x8008000f,0x80000000,0x3e000,0x0,0x800,0xa0000400,0x0,0x0,0x0,0x0,0x80000,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100000,0x0,0x0,0x0,0x0,0x2000,0x0,0x4020040,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000, 0x402,0x8,0x40,0x0,0x0,0x0,0x2000,0x0,0x0,0x0,0x0,0x0,0x0,0xc000,0x0,0x0,0x7004,0x70000fc,0x0,0x0,0x700000,0x800000,0x0,0x20008000, 0x0,0x4,0x808,0x4000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x0,0x80f00000,0x0,0x0,0x0,0x800,0xa0001800,0x0,0x0,0x0,0x0, 0x300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600000,0x0,0x0,0x0,0x0,0x0,0x0,0x4020040 }; // Definition of a 8x17 font. const unsigned int font8x17[8*17*256/32] = { 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x2400,0x2400,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20081834,0x1c0000,0x20081800,0x20081800,0x342008, 0x18340000,0x200818,0x80000,0x0,0x180000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4200000,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x380000,0x4000,0x2000c00,0x40100840,0x70000000,0x0,0x0,0x1c,0x10700000,0x7,0x0, 0x1800,0x1800,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1010242c,0x14140000,0x10102414,0x10102414,0x2c1010,0x242c1400, 0x101024,0x14100038,0x0,0x240000,0x0,0x0,0x30000000,0x0,0x0,0x4000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0x0,0x8100000,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x80000,0x10004000,0x2001000,0x40000040,0x10000000,0x0,0x0,0x10,0x10100000,0x4, 0x0,0x18000000,0x0,0x0,0x0,0x34002400,0x2400,0x0,0x0,0x0,0x3c,0x0,0x8000000,0x0,0x60607800,0x0,0x140000,0x0,0x0,0x0,0x0,0x0, 0x44,0x10081834,0x240000,0x10081800,0x10081800,0x1c341008,0x18340000,0x100818,0x84000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x102812, 0x8601c10,0x8100800,0x2,0x1c383e3e,0x67e1e7f,0x3e3c0000,0x38,0x1e087e1e,0x7c7f7f1e,0x417c1c42,0x4063611c,0x7e1c7e3e,0xfe414181, 0x63827f10,0x40081000,0x8004000,0x2001000,0x40000040,0x10000000,0x0,0x10000000,0x10,0x10100000,0x3c000008,0x0,0x24003e00, 0x3f007f00,0x0,0x0,0x2ce91800,0x1882,0x10101c,0xc2103c,0x143c3c00,0x3c00,0x18003c3c,0x10001f00,0x181c00,0x20200810,0x8080808, 0x8083e1e,0x7f7f7f7f,0x7c7c7c7c,0x7c611c1c,0x1c1c1c00,0x1e414141,0x41824044,0x810242c,0x14180000,0x8102414,0x8102414,0x382c0810, 0x242c1400,0x81024,0x14104014,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x102816,0x3e902010,0x10084910,0x4,0x22084343,0xa402102,0x41620000, 0x44,0x33144121,0x42404021,0x41100444,0x40636122,0x43224361,0x10416381,0x22440310,0x20082800,0x4000,0x2001000,0x40000040, 0x10000000,0x0,0x10000000,0x10,0x10100000,0x24000008,0x0,0x606100,0x68000300,0x8106c,0x34000000,0x4f0000,0x44,0x101020,0x441040, 0x420200,0x4200,0x24000404,0x7d00,0x82200,0x20203010,0x14141414,0x14082821,0x40404040,0x10101010,0x42612222,0x22222200,0x23414141, 0x41447e48,0x0,0x0,0x0,0x0,0x4000000,0x18,0x0,0x4000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10287f,0x49902010,0x10083e10,0x4,0x41080101, 0x1a404002,0x41411818,0x1004004,0x21144140,0x41404040,0x41100448,0x40555141,0x41414140,0x10412281,0x14280610,0x20084400,0x1c7c1c, 0x3e3c7c3a,0x5c703844,0x107f5c3c,0x7c3e3c3c,0x7e424281,0x66427e10,0x10100000,0x40100008,0x1010,0xa04000,0x48100610,0x100c3024, 0x24000000,0x4f3c00,0x2c107e28,0x3820,0x42281060,0x9d1e12,0xbd00,0x24100818,0x427d00,0x82248,0x20200800,0x14141414,0x14142840, 0x40404040,0x10101010,0x41514141,0x41414142,0x43414141,0x41284350,0x1c1c1c1c,0x1c1c6c1c,0x3c3c3c3c,0x70707070,0x3c5c3c3c, 0x3c3c3c18,0x3e424242,0x42427c42,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x102824,0x48623010,0x10081c10,0x8,0x41080103,0x127c5e04, 0x41411818,0xe7f3808,0x4f144140,0x41404040,0x41100450,0x40555141,0x41414160,0x1041225a,0x1c280410,0x1008c600,0x226622,0x66661066, 0x62100848,0x10496266,0x66663242,0x10426681,0x24220260,0x100c0000,0xf8280008,0x1010,0x606000,0x48280428,0x28042014,0x48000000, 0x494200,0x52280228,0x105420,0x3cee1058,0xa12236,0xa500,0x18101004,0x427d00,0x8226c,0x76767e10,0x14141414,0x14142840,0x40404040, 0x10101010,0x41514141,0x41414124,0x45414141,0x41284150,0x22222222,0x22221222,0x66666666,0x10101010,0x66626666,0x66666600, 0x66424242,0x42226622,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100024,0x381c4900,0x10086bfe,0x8,0x4908021c,0x22036304,0x3e630000, 0x70000710,0x51227e40,0x417f7f43,0x7f100470,0x40554941,0x43417e3e,0x1041225a,0x8100810,0x10080000,0x24240,0x42421042,0x42100850, 0x10494242,0x42422040,0x1042245a,0x18240410,0x10103900,0x407c003e,0x1818,0x1c3e10,0x4f7c087c,0x7c002010,0x48000000,0x4008, 0x527c0410,0x105078,0x2410104c,0xa13e6c,0x7f00b900,0xfe3c3c,0x421d18,0x1c1c36,0x38383810,0x22222222,0x22144e40,0x7f7f7f7f, 0x10101010,0xf1494141,0x41414118,0x49414141,0x4110435c,0x2020202,0x2021240,0x42424242,0x10101010,0x42424242,0x424242ff,0x4e424242, 0x42244224,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1000fe,0xe664d00,0x10080810,0x380010,0x41080c03,0x42014108,0x633d0000,0x70000710, 0x51224140,0x41404041,0x41100448,0x40494541,0x7e414203,0x1041145a,0x14101010,0x10080000,0x3e4240,0x427e1042,0x42100870,0x10494242, 0x4242203c,0x1042245a,0x18241810,0x10104600,0xf8f60008,0x1010,0x600320,0x48f610f6,0xf6000000,0x187eff,0x3c04,0x5ef61810,0x105020, 0x24fe0064,0x9d006c,0x138ad00,0x100000,0x420518,0x36,0xc0c0c020,0x22222222,0x22224840,0x40404040,0x10101010,0x41454141,0x41414118, 0x51414141,0x41107e46,0x3e3e3e3e,0x3e3e7e40,0x7e7e7e7e,0x10101010,0x42424242,0x42424200,0x5a424242,0x42244224,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x28,0x9094500,0x10080010,0x10,0x41081801,0x7f014118,0x41010000,0xe7f3800,0x513e4140,0x41404041,0x41100444, 0x40414541,0x40414101,0x10411466,0x36103010,0x8080000,0x424240,0x42401042,0x42100848,0x10494242,0x42422002,0x10423c5a,0x18142010, 0x10100000,0x407c0010,0x1010,0x260140,0x487c307c,0x7c000000,0x180000,0x202,0x507c2010,0x105020,0x3c10003c,0x423e36,0x1004200, 0x100000,0x420500,0x3e6c,0x41e0440,0x3e3e3e3e,0x3e3e7840,0x40404040,0x10101010,0x41454141,0x41414124,0x61414141,0x41104042, 0x42424242,0x42425040,0x40404040,0x10101010,0x42424242,0x42424218,0x72424242,0x42144214,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100048, 0x49096200,0x8100010,0x18001810,0x22082043,0x2432310,0x61421818,0x1004010,0x4f634121,0x42404021,0x41104444,0x40414322,0x40234143, 0x10411466,0x22106010,0x8080000,0x466622,0x66621066,0x42100844,0x10494266,0x66662042,0x10461824,0x24184010,0x10100000,0x24381010, 0x34001018,0xda4320,0x68386038,0x38000000,0x0,0x4204,0x50384010,0x105420,0x4210100c,0x3c0012,0x3c00,0x0,0x460500,0x48,0xc020c44, 0x63636363,0x63228821,0x40404040,0x10101010,0x42432222,0x22222242,0x62414141,0x41104042,0x46464646,0x46465022,0x62626262, 0x10101010,0x66426666,0x66666618,0x66464646,0x46186618,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100048,0x3e063d00,0x8100000,0x18001820, 0x1c3e7f3e,0x23c1e20,0x3e3c1818,0x10,0x20417e1e,0x7c7f401e,0x417c3842,0x7f41431c,0x401e40be,0x103e0866,0x41107f10,0x4080000, 0x3a5c1c,0x3a3c103a,0x427c0842,0xe49423c,0x7c3e203c,0xe3a1824,0x66087e10,0x10100000,0x3c103010,0x245a1010,0x5a3e10,0x3f107f10, 0x10000000,0x0,0x3c08,0x2e107e10,0x1038fc,0x101004,0x0,0x0,0xfe0000,0x7f0500,0x0,0x14041438,0x41414141,0x41418e1e,0x7f7f7f7f, 0x7c7c7c7c,0x7c431c1c,0x1c1c1c00,0xbc3e3e3e,0x3e10405c,0x3a3a3a3a,0x3a3a6e1c,0x3c3c3c3c,0x7c7c7c7c,0x3c423c3c,0x3c3c3c00, 0x7c3a3a3a,0x3a087c08,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8000000,0x4200000,0x10000020,0x0,0x0,0x10,0x0,0x30000000,0x0, 0x0,0x0,0x60000,0x0,0x1c,0x4380000,0x0,0x2,0x800,0x0,0x40020000,0x0,0x8000c,0x10600000,0x2010,0x48000000,0x240000,0x0,0x0, 0x0,0x0,0x0,0x1000,0x1078,0x0,0x0,0x0,0x400500,0x0,0x1e081e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x84008,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8000000,0x0,0x20000040,0x0,0x0,0x20,0x0,0x1e000000,0x0,0x0,0x0,0x20000,0x0, 0x0,0x2000000,0x0,0x26,0x800,0x0,0x40020000,0x0,0x100000,0x10000000,0x2030,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1000,0x1000,0x0, 0x0,0x0,0x400000,0x8000000,0x41e0400,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x104010,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x0,0x1c,0x7000,0x0,0x40020000,0x0,0x300000, 0x0,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1000,0x0,0x0,0x0,0x400000,0x38000000,0x0,0x0,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x1c,0x0,0x0,0x0,0x0,0x0,0x304030,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 }; // Definition of a 10x19 font. const unsigned int font10x19[10*19*256/32] = { 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3600000,0x36000,0x0,0x0,0x0,0x0,0x6c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x180181c0,0xe81b0300,0x1801,0x81c06c18,0x181c06c,0xe8180,0x181c0e81,0xb0000006,0x60701b,0x1800000,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c00000,0x1c000,0x0,0x0,0x0,0x0,0x6c,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0xc030360,0xb81b0480,0xc03,0x3606c0c,0x303606c,0xb80c0,0x30360b81,0xb0000003,0xc0d81b,0x3000000,0x0, 0x300,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800,0x0,0x0,0x0,0x0,0x0,0x2200000, 0x22000,0x0,0x0,0x0,0x0,0x0,0x0,0x30000,0x0,0xe0,0x38078000,0x0,0x480,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3000c080,0x480,0x3000, 0xc0800030,0xc08000,0x300,0xc080000,0xc,0x302000,0xc00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20120,0x41c01,0xe020060c, 0x800000,0x4,0x1e0703e0,0xf8060fc1,0xe1fe1e07,0x80000000,0x78,0x307e0,0x3c7c1fe7,0xf83c408f,0x80f10440,0x18660878,0x7e0787e0, 0x78ff9024,0xa0140a0,0x27f83840,0x700e000,0x18000400,0x8000,0x70004002,0x410078,0x0,0x0,0x0,0x0,0x1808,0xc000000,0xf000000, 0xe000000,0x1400,0x1e0001f,0x8007f800,0x0,0x0,0x3a3b,0x61400000,0x14202,0x20000,0x38002020,0x3c1b00,0x3e00000,0xf8,0x1c0001c0, 0x78060001,0xf800000e,0x1e00020,0x8004020,0xc0300c0,0x300c0301,0xf83c7f9f,0xe7f9fe3e,0xf83e0f8,0x7c1821e0,0x781e0781,0xe0001f10, 0x24090240,0xa02400f8,0x18018140,0xe81b0480,0x1801,0x81406c18,0x181406c,0x190e8180,0x18140e81,0xb0000006,0x60501b,0x184006c, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20120,0x26042202,0x200c06,0x800000,0x8,0x210d0611,0x40e0803,0x10026188,0x40000000, 0x8c,0xf030418,0xc6431004,0xc64082,0x110840,0x18660884,0x41084410,0x8c081024,0xa012110,0x40082020,0x101b000,0xc000400,0x8000, 0x80004002,0x410008,0x0,0x0,0x100000,0x0,0x2008,0x2000000,0x18800000,0x10000000,0x2200,0x2300024,0x800,0x0,0x0,0x2e13,0x60800000, 0x8104,0x20040,0x64001040,0x80401b07,0x80100000,0x1e000,0x22000020,0x40c0003,0xc8000002,0x3300020,0x8004020,0xc0300c0,0x300c0301, 0x40c64010,0x4010008,0x2008020,0x43182210,0x84210842,0x10002190,0x24090240,0x9044018c,0xc030220,0xb81b0300,0xc03,0x2206c0c, 0x302206c,0x1e0b80c0,0x30220b81,0xb0000003,0xc0881b,0x304006c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20120,0x241f2202, 0x200802,0x4900000,0x8,0x21010408,0x20a0802,0x44090,0x20000000,0x4,0x11878408,0x80411004,0x804082,0x111040,0x1ce50986,0x40986409, 0x81022,0x12012108,0x80102020,0x1031800,0x400,0x8000,0x80004000,0x10008,0x0,0x0,0x100000,0x0,0x2008,0x2000000,0x10000000, 0x10000000,0x18,0x4000044,0x1000,0x30180,0xd81b0000,0x13,0xe0000000,0x88,0x40,0x400018c0,0x80400018,0x61f00000,0x61800,0x22020020, 0x4000007,0xc8000002,0x2100020,0x8038000,0x1e0781e0,0x781e0301,0x40804010,0x4010008,0x2008020,0x41142619,0x86619866,0x18002190, 0x24090240,0x8887e104,0x0,0x0,0x0,0x0,0x0,0x2000000,0x0,0x0,0x0,0x40000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20120,0x2434a202, 0x200802,0x3e00000,0x10,0x40810008,0x21a0804,0x44090,0x20000000,0x80040004,0x20848409,0x409004,0x1004082,0x112040,0x14a50902, 0x40902409,0x81022,0x11321208,0x80202010,0x1060c00,0x7c5e0,0x781e8783,0xf07a5f0e,0x1c10808,0xfc5f078,0x5e07a170,0x7c7e1024, 0xa016190,0x27f82008,0x2000000,0x20000000,0x10000000,0x80200024,0x4000044,0x2000,0x18180,0xc8320000,0x12,0xa1f00037,0x7f888, 0x1e0,0x40410880,0x80600017,0xa2100000,0x5e800,0x22020040,0x38001027,0xc8000002,0x2100020,0x8004020,0x12048120,0x48120482, 0x41004010,0x4010008,0x2008020,0x40942409,0x2409024,0x9044390,0x24090240,0x88841918,0x1f07c1f0,0x7c1f07c3,0x70781e07,0x81e07838, 0xe0380e0,0x1f17c1e0,0x781e0781,0xe0001f90,0x24090240,0x9025e102,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20001,0xff241c41, 0x1001,0x1c02000,0x10,0x40810008,0x6120f85,0xe0086190,0x20c03007,0x8007800c,0x27848419,0x409004,0x1004082,0x114040,0x14a48902, 0x40902409,0x81022,0x11321205,0x602010,0x1000000,0x86610,0x84218840,0x80866182,0x411008,0x9261884,0x61086189,0x82101022,0x12012108, 0x40082008,0x2000000,0x20030000,0x20000000,0x80200024,0x4000044,0x3006030,0xc018100,0x4c260000,0x12,0x26080048,0x83000850, 0x20250,0x403e0500,0x8078002c,0x12302200,0x92400,0x1c0200c0,0x4001027,0xc8000002,0x3308820,0x8004020,0x12048120,0x48120482, 0x41004010,0x4010008,0x2008020,0x40922409,0x2409024,0x8884690,0x24090240,0x85040920,0x21886218,0x86218860,0x88842108,0x42108408, 0x2008020,0x21186210,0x84210842,0x10302190,0x24090240,0x88461084,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0x4c240182, 0x80001001,0x6b02000,0x20,0x4c810010,0x78220846,0x10081e10,0x20c0301c,0x1fe0e018,0x4d8487e1,0x409fe7,0xf9007f82,0x11a040, 0x13248902,0x41102418,0xe0081022,0x11320c05,0x402008,0x1000000,0x2409,0x409020,0x81024082,0x412008,0x9240902,0x40902101,0x101022, 0x11321208,0x40102008,0x2000000,0x7e0c8000,0xfc000003,0xf0fc0018,0x43802047,0x8c8040c8,0x32008300,0x44240000,0x0,0x4000048, 0x8c801050,0x20440,0x40221dc0,0x808c0028,0x11d0667f,0x8009c400,0x1fc180,0x4001023,0xc8300002,0x1e0ccfb,0x3ec7b020,0x12048120, 0x48120482,0x79007f9f,0xe7f9fe08,0x2008020,0xf0922409,0x2409024,0x8504490,0x24090240,0x85040920,0x802008,0x2008020,0x89004090, 0x24090208,0x2008020,0x40902409,0x2409024,0x8304390,0x24090240,0x88440884,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000, 0x481c0606,0xc8001001,0x802000,0x20,0x4c810020,0x4220024,0x8102108,0x60000070,0x3820,0x48884419,0x409004,0x10e4082,0x112040, 0x13244902,0x7e1027e0,0x3c081021,0x21320c02,0x802008,0x1000000,0x7e409,0x409020,0x81024082,0x414008,0x9240902,0x40902101, 0x80101022,0x11320c08,0x40202008,0x2038800,0x200bc000,0x20000000,0x80200003,0x80f04044,0xbc080bc,0x2f000200,0x0,0x0,0x6001048, 0x8bc02020,0x20441,0xf8220200,0x80820028,0x1000cc00,0x80094400,0x201e0,0x78001021,0xc830000f,0x8000663c,0xf03c0c0,0x21084210, 0x84210846,0x41004010,0x4010008,0x2008020,0x40912409,0x2409024,0x8204890,0x24090240,0x82040930,0x1f87e1f8,0x7e1f87e0,0x89004090, 0x24090208,0x2008020,0x40902409,0x2409024,0x8004690,0x24090240,0x88440884,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000, 0x480719c4,0x48001001,0x81fc00,0x7800020,0x40810040,0x2420024,0x8104087,0xa0000070,0x3820,0x48884409,0x409004,0x1024082,0x111040, 0x13244902,0x40102410,0x2081021,0x214a1202,0x1802008,0x1000000,0x182409,0x409fe0,0x81024082,0x41a008,0x9240902,0x40902100, 0xf8101021,0x214a0c04,0x80c0c008,0x1847000,0x7c1ee000,0x20000000,0x8020000c,0x8c044,0x1ee181ee,0x7b800000,0x707,0xf3ff0000, 0x3e0084f,0x9ee0c020,0x20440,0x40221fc0,0xc2002c,0x13f11000,0x87892400,0x20000,0x1020,0x48000000,0x3f011c6,0x31cc6180,0x21084210, 0x84210844,0x41004010,0x4010008,0x2008020,0x40912409,0x2409024,0x8505090,0x24090240,0x8204191c,0x60982609,0x82609823,0xf9007f9f, 0xe7f9fe08,0x2008020,0x40902409,0x2409024,0x9fe4c90,0x24090240,0x84840848,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xfe048224, 0x28001001,0x2000,0x40,0x40810080,0x27f8024,0x8104080,0x2000001c,0x1fe0e020,0x488fc409,0x409004,0x1024082,0x110840,0x10242902, 0x40102408,0x2081021,0x214a1202,0x1002004,0x1000000,0x102409,0x409000,0x81024082,0x411008,0x9240902,0x40902100,0x6101021, 0x214a0c04,0x81002008,0x2000000,0x201dc000,0x20000000,0x80200000,0x98044,0x1dc101dc,0x77000000,0x700,0x0,0x180448,0x1dc10020, 0x20440,0x403e0200,0x620017,0xa000cc00,0x80052800,0x20000,0x1020,0x48000000,0x6606,0x206100,0x3f0fc3f0,0xfc3f0fc7,0xc1004010, 0x4010008,0x2008020,0x4090a409,0x2409024,0x8886090,0x24090240,0x8207e106,0x40902409,0x2409024,0x81004010,0x4010008,0x2008020, 0x40902409,0x2409024,0x8005890,0x24090240,0x84840848,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98048224,0x30001001,0x2000, 0x40,0x21010100,0x2020024,0x8204080,0x40000007,0x80078000,0x48884408,0x80411004,0x824082,0x110840,0x10242986,0x40086409,0x2081021, 0xe14a2102,0x2002004,0x1000000,0x106409,0x409000,0x81024082,0x410808,0x9240902,0x40902100,0x2101021,0x214a1202,0x82002008, 0x2000000,0x300f8000,0x20000000,0x80fc001d,0xe4088044,0xf8200f8,0x3e000000,0x300,0x0,0x80c48,0xf820020,0x20640,0x40410200, 0x803c0018,0x60006600,0x61800,0x0,0x1020,0x48000000,0xcc0a,0x20a100,0x21084210,0x84210844,0x40804010,0x4010008,0x2008020, 0x4110a619,0x86619866,0x19046110,0x24090240,0x82040102,0x41906419,0x6419064,0x81004010,0x4010008,0x2008020,0x40902409,0x2409024, 0x8307090,0x24090240,0x82840828,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0x90248222,0x30000802,0x200c,0xc080,0x21010301, 0x4021042,0x10202108,0xc0c03000,0x80040020,0x4d902418,0xc6431004,0xc24082,0x6210440,0x10241884,0x40084409,0x86080840,0xc0842102, 0x4002002,0x1000000,0x18e610,0x84218820,0x80864082,0x410408,0x9240884,0x61086101,0x6101860,0xc0842103,0x4002008,0x2000000, 0x10850180,0x20330000,0x80200013,0x26184024,0x5040050,0x14000000,0x0,0x0,0x4180848,0x85040020,0x20350,0x40000200,0x800c0007, 0x80002200,0x1e000,0x0,0x1860,0x48000000,0x880a,0x40a188,0x40902409,0x2409028,0x40c64010,0x4010008,0x2008020,0x43106210,0x84210842, 0x10006108,0x42108421,0x2040102,0x6398e639,0x8e6398e4,0x88842088,0x22088208,0x2008020,0x21102210,0x84210842,0x10306118,0x66198661, 0x83061030,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20001,0x901f01c1,0xe8000802,0xc,0xc080,0x1e07c7f8,0xf8020f81,0xe0401e07, 0x80c03000,0x20,0x279027e0,0x3c7c1fe4,0x3c408f,0x83c1027f,0x90241878,0x4007c404,0xf8080780,0xc0844082,0x7f82002,0x1000000, 0xfa5e0,0x781e87c0,0x807a409f,0xc0410207,0x9240878,0x5e07a100,0xf80e0fa0,0xc0846183,0x7f82008,0x2000000,0xf020100,0x40321360, 0x80200014,0xa3e0201f,0x8207f820,0x8000000,0x0,0x0,0x3e01037,0x207f820,0x201e1,0xfc000200,0x80040000,0x0,0x0,0x1fc000,0x17b0, 0x48000000,0x12,0xc120f0,0x40902409,0x2409028,0x783c7f9f,0xe7f9fe3e,0xf83e0f8,0x7c1061e0,0x781e0781,0xe000be07,0x81e0781e, 0x204017c,0x3e8fa3e8,0xfa3e8fa3,0x70781f07,0xc1f07c7f,0x1fc7f1fc,0x1e1021e0,0x781e0781,0xe0007e0f,0xa3e8fa3e,0x8305e030,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40000,0xc06,0xc,0x100,0x0,0x0,0x0,0x3000,0x0,0x20000000,0x0,0x0,0x0,0x0,0xc000, 0x0,0x0,0x2001,0x1000000,0x0,0x0,0x20000,0x400000,0x0,0x40002000,0x0,0x1,0x2008,0x2000000,0x100,0x40240000,0x80200008,0x40000000, 0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x0,0x80040000,0x0,0x0,0x0,0x1000,0x48000000,0x1f,0x181f000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1040010,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40000,0x60c,0x18,0x0, 0x0,0x0,0x0,0x6000,0x0,0x10000000,0x0,0x0,0x0,0x0,0x4000,0x0,0x0,0x3800,0x7000000,0x0,0x0,0x840000,0x400000,0x0,0x40002000, 0x0,0x2,0x2008,0x2000000,0x200,0x40440000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x0,0x80780000,0x0,0x0,0x0,0x1000,0x48000400, 0x2,0x1e02000,0x0,0x0,0x80000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000,0x0,0x0,0x0,0x0,0x0,0x0,0x2040020,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x4000,0x0,0xf000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x780000,0x3800000,0x0,0x40002000,0x0,0xe,0x1808,0xc000000,0x3,0x80000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000000, 0x0,0x0,0x0,0x1000,0x1c00,0x0,0x0,0x0,0x0,0x380000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x380000,0x0,0x0,0x0,0x0,0x0,0x0,0xe0400e0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3fc, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 }; // Definition of a 12x24 font. const unsigned int font12x24[12*24*256/32] = { 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x80000000,0x198000,0x0,0x0,0x0,0x0, 0x0,0x198,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc001806,0xc81980,0x60000000,0xc001806,0x1980c00,0x18060198,0xc80c, 0x180600,0xc8198000,0xc001,0x80601980,0x18000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x0,0xf0000,0x0,0x0,0x0,0x0,0x0,0x198,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x600300f,0x1301980,0x90000000,0x600300f,0x1980600,0x300f0198,0x13006,0x300f01,0x30198000,0x6003, 0xf01980,0x30000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x0,0x60000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7007,0x3c0000,0x3006019, 0x80000000,0x90000000,0x3006019,0x80000300,0x60198000,0x3,0x601980,0x0,0x3006,0x1980000,0x60000000,0x0,0x0,0xe0000000,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000000, 0x0,0x0,0x0,0x0,0x0,0xc800019,0x80000000,0x198000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x0,0x0,0x1001,0x420000,0x0,0x0,0x90000000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18000c06,0xc80001,0x10000000,0x18000c06,0x1800,0xc060000,0xc818,0xc0600,0xc8000000, 0x18000,0xc0600000,0xc000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x80660207,0x800f8060,0x300c004,0x0,0x6, 0xe00703f,0x3f00383,0xf80f07fc,0x1f01f000,0x0,0xf8,0x607f,0x7c7e07,0xfe7fe0f8,0x6063fc1f,0x86066007,0xe7060f0,0x7f80f07f, 0x81f8fff6,0x6606c03,0x70ee077f,0xe0786000,0xf0070000,0xc000060,0xc0,0x3e000,0x60006003,0x600fc00,0x0,0x0,0x0,0x0,0x0,0x3c0603, 0xc0000000,0x7800000,0xf0000,0x0,0xf00001f,0x80001fe0,0x7fe000,0x0,0x0,0x0,0x168fe609,0x0,0x90e07,0x6000,0x3c000e,0x70000f8, 0x1980001f,0x0,0x1f8,0xf00000f,0xf00180,0xfe000,0xe00e,0x1001,0x20060,0x6006006,0x600600,0x600fe07c,0x7fe7fe7f,0xe7fe3fc3, 0xfc3fc3fc,0x7e07060f,0xf00f00,0xf00f0000,0xf360660,0x6606606e,0x76001e0,0xc00180f,0x1681981,0x10000000,0xc00180f,0x1980c00, 0x180f0198,0x3801680c,0x180f01,0x68198000,0xc001,0x80f01980,0x18600198,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019, 0x8044020c,0xc01f8060,0x2004004,0x0,0xc,0x3f81f07f,0x87f80383,0xf81f87fc,0x3f83f800,0x0,0x1fc,0x780607f,0x81fe7f87,0xfe7fe1fc, 0x6063fc1f,0x860c6007,0xe7061f8,0x7fc1f87f,0xc3fcfff6,0x6606c03,0x30c6067f,0xe0783000,0xf00d8000,0x6000060,0xc0,0x7e000,0x60006003, 0x600fc00,0x0,0x0,0xc00,0x0,0x0,0x7c0603,0xe0000000,0xfc00000,0x1f0000,0x0,0x900003f,0xc0003fe0,0x7fe000,0x0,0x0,0x0,0x1302660f, 0x0,0xf0606,0x6004,0x7e0006,0x60601f8,0x19800001,0x80000000,0x1f8,0x19800010,0x81080300,0x3f2000,0x2011,0x1001,0x1c0060,0x6006006, 0x600600,0x601fe1fe,0x7fe7fe7f,0xe7fe3fc3,0xfc3fc3fc,0x7f87061f,0x81f81f81,0xf81f8000,0x3fa60660,0x66066066,0x66003f0,0x6003009, 0x1301981,0x10000000,0x6003009,0x1980600,0x30090198,0x1f013006,0x300901,0x30198000,0x6003,0x901980,0x30600198,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x80cc0f8c,0xc0180060,0x6006044,0x40000000,0xc,0x3181b041,0xc41c0783,0x388018, 0x71c71800,0x0,0x106,0x18c0f061,0xc38261c6,0x600384,0x60606001,0x86186007,0xe78630c,0x60e30c60,0xe7040606,0x630cc03,0x39c30c00, 0xc0603000,0x3018c000,0x3000060,0xc0,0x60000,0x60000000,0x6000c00,0x0,0x0,0xc00,0x0,0x0,0x600600,0x60000000,0x18400000,0x180000, 0x0,0x19800070,0x40003600,0xc000,0x0,0x0,0x0,0x25a06,0x0,0x6030c,0x4,0xe20007,0xe060180,0xf000,0x80000000,0xf0000,0x10800000, 0x80080600,0x7f2000,0x2020,0x80001001,0x20000,0xf00f00f,0xf00f00,0x601b0382,0x60060060,0x6000600,0x60060060,0x61c78630,0xc30c30c3, 0xc30c000,0x30e60660,0x66066063,0xc600738,0x3006019,0x80000000,0xe0000000,0x3006019,0x80000300,0x60198000,0x3e000003,0x601980, 0x0,0x3006,0x1980000,0x60600000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x80cc1fcc,0xc0180060,0x6006035,0x80000000, 0x18,0x71c03000,0xc00c0583,0x300018,0x60c60c00,0x0,0x6,0x3060f060,0xc30060c6,0x600300,0x60606001,0x86306007,0x9e78670e,0x60670e60, 0x66000606,0x630c606,0x19830c01,0xc0601800,0x30306000,0x60,0xc0,0x60000,0x60000000,0x6000c00,0x0,0x0,0xc00,0x0,0x0,0x600600, 0x60000000,0x18000000,0x300000,0x0,0x78060,0x6600,0x1c000,0x300c,0x39819c0,0x0,0x25a00,0x0,0x30c,0x4,0xc00003,0xc060180,0x30c1f, 0x80000000,0x30c000,0x10800001,0x80700000,0x7f2000,0x2020,0x80001001,0x20060,0xf00f00f,0xf00f00,0xf01b0300,0x60060060,0x6000600, 0x60060060,0x60c78670,0xe70e70e7,0xe70e000,0x70c60660,0x66066063,0xc7f8618,0x0,0x0,0x0,0x0,0x0,0x0,0x7000000,0x0,0x0,0x0, 0x0,0x600000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x87ff3a4c,0xc0180060,0x400600e,0x600000,0x18,0x60c03000, 0xc00c0d83,0x700018,0x60c60c00,0x20,0x400006,0x3060f060,0xc6006066,0x600600,0x60606001,0x86606006,0x966c6606,0x60660660,0x66000606, 0x630c666,0xf019801,0x80601800,0x30603000,0x1f06f,0xf01ec0,0xf03fe1ec,0x6703e01f,0x61c0c06,0xdc6701f0,0x6f01ec0c,0xe1f87fc6, 0xc60cc03,0x71c60c7f,0xc0600600,0x60000000,0x30000000,0x300000,0x40040,0x88060,0x6600,0x18000,0x300c,0x1981980,0x0,0x2421f, 0x80003ce0,0x7fc198,0x601f,0xc02021,0x980600c0,0x40230,0x80000000,0x402000,0x19806003,0x80006,0xc7f2000,0x2020,0x80001001, 0x420060,0xf00f00f,0xf00f00,0xf01b0600,0x60060060,0x6000600,0x60060060,0x6066c660,0x66066066,0x6606208,0x60e60660,0x66066061, 0x987fc670,0x1f01f01f,0x1f01f01,0xf039c0f0,0xf00f00f,0xf03e03,0xe03e03e0,0x1f06701f,0x1f01f01,0xf01f0060,0x1e660c60,0xc60c60c6, 0xc6f060c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x7ff3207,0x8c0c0000,0xc00300e,0x600000,0x30,0x60c03000, 0xc01c0983,0xf0600030,0x31860c06,0x6001e0,0x78000e,0x23e1f861,0xc6006066,0x600600,0x60606001,0x86c06006,0x966c6606,0x60660660, 0xe7000606,0x630c666,0xf01f803,0x600c00,0x30000000,0x3f87f,0x83f83fc3,0xf83fe3fc,0x7f83e01f,0x6380c07,0xfe7f83f8,0x7f83fc0d, 0xf3fc7fc6,0xc71cc03,0x3183187f,0xc0600600,0x60000000,0xff806000,0x300000,0x40040,0x88070,0x6600,0x60030060,0x6001818,0x1883180, 0x0,0x2423f,0xc0007ff0,0x607fc1f8,0x603f,0x80c01fc1,0xf80601e0,0x5f220,0x80420000,0x5f2000,0xf006006,0x80006,0xc7f2000,0x2020, 0x82107c07,0xc03c0060,0x1f81f81f,0x81f81f80,0xf03b0600,0x60060060,0x6000600,0x60060060,0x6066c660,0x66066066,0x660671c,0x61660660, 0x66066061,0xf860e6c0,0x3f83f83f,0x83f83f83,0xf87fe3f8,0x3f83f83f,0x83f83e03,0xe03e03e0,0x3f87f83f,0x83f83f83,0xf83f8060, 0x3fc60c60,0xc60c60c3,0x187f8318,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x883200,0x300c0000,0xc003035,0x80600000, 0x30,0x66c03001,0xc0f81983,0xf86f0030,0x1f071c06,0x600787,0xfe1e001c,0x6261987f,0x86006067,0xfe7fc600,0x7fe06001,0x87c06006, 0xf6646606,0x60e6067f,0xc3e00606,0x61986f6,0x600f007,0x600c00,0x30000000,0x21c71,0x830831c3,0x1c06031c,0x71c06003,0x6700c06, 0x6671c318,0x71831c0f,0x16040c06,0xc318606,0x1b031803,0x80600600,0x60000000,0x30009000,0x300000,0x40040,0x7003e,0x67e0,0x90070090, 0x9001818,0x8c3100,0x0,0x60,0x4000e730,0x900380f0,0x6034,0x80c018c7,0xfe060338,0xb0121,0x80c60000,0x909000,0x6008,0x1080006, 0xc3f2000,0x2011,0x3180060,0x60060e0,0x19819819,0x81981981,0x9833c600,0x7fe7fe7f,0xe7fe0600,0x60060060,0x60664660,0x66066066, 0x66063b8,0x62660660,0x66066060,0xf06066c0,0x21c21c21,0xc21c21c2,0x1c466308,0x31c31c31,0xc31c0600,0x60060060,0x31871c31,0x83183183, 0x18318000,0x71860c60,0xc60c60c3,0x18718318,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x1981a00,0xe03e0000,0xc003044, 0x40600000,0x60,0x66c03001,0x80f03182,0x1c7f8030,0x3f83fc06,0x601e07,0xfe078038,0x6661987f,0x86006067,0xfe7fc61e,0x7fe06001, 0x87e06006,0x66666606,0x7fc6067f,0x81f80606,0x61986f6,0x6006006,0x600600,0x30000000,0xc60,0xc60060c6,0xc06060c,0x60c06003, 0x6e00c06,0x6660c60c,0x60c60c0e,0x6000c06,0xc318666,0x1f031803,0x600600,0x603c2000,0x30016800,0x1fe0000,0x1f81f8,0x1c1f,0x804067e1, 0x68060168,0x16800810,0xc42300,0x0,0x60,0x20c331,0x68030060,0x6064,0x3fc1040,0xf006031c,0xa011e,0x818c7fe0,0x909000,0x7fe1f, 0x80f00006,0xc0f2060,0xf80e,0x18c0780,0x780781c0,0x19819819,0x81981981,0x9833c600,0x7fe7fe7f,0xe7fe0600,0x60060060,0xfc666660, 0x66066066,0x66061f0,0x66660660,0x66066060,0x606066e0,0xc00c00,0xc00c00c0,0xc066600,0x60c60c60,0xc60c0600,0x60060060,0x60c60c60, 0xc60c60c6,0xc60c000,0x61c60c60,0xc60c60c3,0x1860c318,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x1980f81,0x80373000, 0xc003004,0x7fe0001,0xf0000060,0x60c03003,0x183180,0xc71c060,0x3181ec00,0x7000,0xe070,0x66619860,0xc6006066,0x60061e,0x60606001, 0x87606006,0x66626606,0x7f860661,0xc01c0606,0x6198696,0xf00600e,0x600600,0x30000000,0x1fc60,0xc60060c7,0xfc06060c,0x60c06003, 0x7c00c06,0x6660c60c,0x60c60c0c,0x7f00c06,0xc3b8666,0xe01b007,0x3c00600,0x3c7fe000,0xff03ec00,0x1fe0000,0x40040,0xe001,0xc0806603, 0xec0e03ec,0x3ec00010,0x0,0x60000000,0x7f,0x10c3f3,0xec070060,0x6064,0x3fc1040,0x6000030c,0xa0100,0x3187fe1,0xf09f1000,0x7fe00, 0x6,0xc012060,0x0,0xc63c03,0xc03c0380,0x19819819,0x81981981,0x98330600,0x60060060,0x6000600,0x60060060,0xfc662660,0x66066066, 0x66060e0,0x6c660660,0x66066060,0x6060e630,0x1fc1fc1f,0xc1fc1fc1,0xfc3fe600,0x7fc7fc7f,0xc7fc0600,0x60060060,0x60c60c60,0xc60c60c6, 0xc60c7fe,0x62c60c60,0xc60c60c1,0xb060c1b0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0xffe02c6,0x3c633000,0xc003004, 0x7fe0001,0xf00000c0,0x60c03006,0xc6180,0xc60c060,0x60c00c00,0x7000,0xe060,0x66639c60,0x66006066,0x600606,0x60606001,0x86306006, 0x66636606,0x60060660,0xc0060606,0x61f8696,0xf00600c,0x600300,0x30000000,0x3fc60,0xc60060c7,0xfc06060c,0x60c06003,0x7c00c06, 0x6660c60c,0x60c60c0c,0x1f80c06,0xc1b0666,0xe01b00e,0x3c00600,0x3c43c000,0x3007de00,0x600000,0x40040,0x30000,0x61006607,0xde0c07de, 0x7de00000,0x0,0xf07fefff,0x1f,0x8008c3f7,0xde0e0060,0x6064,0xc01047,0xfe00018c,0xb013f,0x86300061,0xf0911000,0x6000,0x6, 0xc012060,0x3f,0x8063c0cc,0x3cc0c700,0x39c39c39,0xc39c39c1,0x98630600,0x60060060,0x6000600,0x60060060,0x60663660,0x66066066, 0x66061f0,0x78660660,0x66066060,0x607fc618,0x3fc3fc3f,0xc3fc3fc3,0xfc7fe600,0x7fc7fc7f,0xc7fc0600,0x60060060,0x60c60c60,0xc60c60c6, 0xc60c7fe,0x64c60c60,0xc60c60c1,0xb060c1b0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0xffe0260,0x6661b000,0xc003000, 0x600000,0xc0,0x60c0300c,0xc7fe0,0xc60c060,0x60c01c00,0x1e07,0xfe078060,0x6663fc60,0x66006066,0x600606,0x60606001,0x86386006, 0x6636606,0x60060660,0xe0060606,0x60f039c,0x1b806018,0x600300,0x30000000,0x70c60,0xc60060c6,0x6060c,0x60c06003,0x7600c06, 0x6660c60c,0x60c60c0c,0x1c0c06,0xc1b03fc,0xe01f01c,0xe00600,0x70000000,0x3007fc00,0x600000,0x40040,0x0,0x62006607,0xfc1807fc, 0x7fc00000,0x0,0xf0000000,0x1,0xc004c307,0xfc1c0060,0x6064,0xc018c0,0x600000d8,0x5f200,0x3180060,0x50a000,0x6000,0x6,0xc012000, 0x0,0xc601c0,0x4201c600,0x3fc3fc3f,0xc3fc3fc3,0xfc7f0600,0x60060060,0x6000600,0x60060060,0x60663660,0x66066066,0x66063b8, 0x70660660,0x66066060,0x607f860c,0x70c70c70,0xc70c70c7,0xcc60600,0x60060060,0x6000600,0x60060060,0x60c60c60,0xc60c60c6,0xc60c000, 0x68c60c60,0xc60c60c1,0xf060c1f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3300260,0x6661e000,0xc003000,0x600000, 0x180,0x71c03018,0xc7fe0,0xc60c0c0,0x60c01800,0x787,0xfe1e0060,0x6663fc60,0x630060c6,0x600306,0x60606001,0x86186006,0x661e70e, 0x60070c60,0x60060606,0x60f039c,0x19806038,0x600180,0x30000000,0x60c60,0xc60060c6,0x6060c,0x60c06003,0x6700c06,0x6660c60c, 0x60c60c0c,0xc0c06,0xc1b039c,0x1f00e018,0x600600,0x60000000,0x1803f800,0x600000,0x40040,0x39e00,0x63006603,0xf83803f8,0x3f800000, 0x0,0x60000000,0x0,0xc00cc303,0xf8180060,0x6064,0xc01fc0,0x60060070,0x40200,0x18c0060,0x402000,0x6000,0x6,0xc012000,0x0,0x18c0140, 0x2014600,0x3fc3fc3f,0xc3fc3fc3,0xfc7f0300,0x60060060,0x6000600,0x60060060,0x60c61e70,0xe70e70e7,0xe70e71c,0x60e60660,0x66066060, 0x6060060c,0x60c60c60,0xc60c60c6,0xcc60600,0x60060060,0x6000600,0x60060060,0x60c60c60,0xc60c60c6,0xc60c000,0x70c60c60,0xc60c60c0, 0xe060c0e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33022e0,0x6670c000,0xc003000,0x600600,0x60180,0x31803030, 0x41c0184,0x1831c0c0,0x71c23806,0x6001e0,0x780000,0x62630c60,0xe38261c6,0x600386,0x60606043,0x860c6006,0x661e30c,0x60030c60, 0x740e0607,0xe0f039c,0x31c06030,0x600180,0x30000000,0x61c71,0x830831c3,0x406031c,0x60c06003,0x6300c06,0x6660c318,0x71831c0c, 0x41c0c07,0x1c0e039c,0x1b00e030,0x600600,0x60000000,0x1c41b00e,0x601cc0,0x401f8,0x45240,0xe1803601,0xb03001b0,0x1b000000, 0x0,0x0,0x41,0xc008e711,0xb0300060,0x6034,0x80c02020,0x60060030,0x30c00,0xc60000,0x30c000,0x0,0x7,0x1c012000,0x0,0x3180240, 0x6024608,0x30c30c30,0xc30c30c3,0xc630382,0x60060060,0x6000600,0x60060060,0x61c61e30,0xc30c30c3,0xc30c208,0x70c70e70,0xe70e70e0, 0x6060068c,0x61c61c61,0xc61c61c6,0x1cc62308,0x30430430,0x43040600,0x60060060,0x31860c31,0x83183183,0x18318060,0x31c71c71, 0xc71c71c0,0xe07180e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x2203fc0,0x663f6000,0x6006000,0x600600,0x60300, 0x3f81fe7f,0xc7f80187,0xf83f80c0,0x3f83f006,0x600020,0x400060,0x33e6067f,0xc1fe7f87,0xfe6001fe,0x6063fc7f,0x60e7fe6,0x660e3f8, 0x6001f860,0x37fc0603,0xfc06030c,0x30c0607f,0xe06000c0,0x30000000,0x7fc7f,0x83f83fc3,0xfc0603fc,0x60c7fe03,0x61807c6,0x6660c3f8, 0x7f83fc0c,0x7f80fc3,0xfc0e039c,0x3180607f,0xc0600600,0x60000000,0xfc0e00c,0x601986,0x66040040,0x4527f,0xc0803fe0,0xe07fe0e0, 0xe000000,0x0,0x0,0x7f,0x80107ff0,0xe07fc060,0x603f,0x83fe0000,0x60060018,0xf000,0x420000,0xf0000,0x7fe00,0x7,0xfe012000, 0x0,0x2100640,0xc0643f8,0x60660660,0x66066067,0xec3e1fe,0x7fe7fe7f,0xe7fe3fc3,0xfc3fc3fc,0x7f860e3f,0x83f83f83,0xf83f8000, 0x5fc3fc3f,0xc3fc3fc0,0x606006fc,0x7fc7fc7f,0xc7fc7fc7,0xfcffe3f8,0x3fc3fc3f,0xc3fc7fe7,0xfe7fe7fe,0x3f860c3f,0x83f83f83, 0xf83f8060,0x7f83fc3f,0xc3fc3fc0,0x607f8060,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x2201f80,0x3c1e7000,0x6006000, 0x600,0x60300,0xe01fe7f,0xc3f00183,0xe01f0180,0x1f01e006,0x600000,0x60,0x3006067f,0x807c7e07,0xfe6000f8,0x6063fc3e,0x6067fe6, 0x660e0f0,0x6000f060,0x3bf80601,0xf806030c,0x60e0607f,0xe06000c0,0x30000000,0x1ec6f,0xf01ec0,0xf80601ec,0x60c7fe03,0x61c03c6, 0x6660c1f0,0x6f01ec0c,0x3f007c1,0xcc0e030c,0x71c0c07f,0xc0600600,0x60000000,0x7804018,0xe01186,0x66040040,0x39e3f,0x80401fe0, 0x407fe040,0x4000000,0x0,0x0,0x3f,0x203ce0,0x407fc060,0x601f,0x3fe0000,0x60060018,0x0,0x0,0x0,0x7fe00,0x6,0xe6012000,0x0, 0x7e0,0x1807e1f0,0x60660660,0x66066066,0x6c3e07c,0x7fe7fe7f,0xe7fe3fc3,0xfc3fc3fc,0x7e060e0f,0xf00f00,0xf00f0000,0x8f01f81f, 0x81f81f80,0x60600670,0x1ec1ec1e,0xc1ec1ec1,0xec79c0f0,0xf80f80f,0x80f87fe7,0xfe7fe7fe,0x1f060c1f,0x1f01f01,0xf01f0000,0x4f01cc1c, 0xc1cc1cc0,0xc06f00c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200,0x0,0x6006000,0x600,0x600,0x0,0x0,0x0,0x0, 0x600000,0x0,0x18000000,0x0,0x0,0x0,0x0,0x0,0x1800,0x0,0x0,0x0,0x600060,0x30000000,0x0,0x0,0xc,0x3,0x0,0x0,0x60000c00,0x0, 0x0,0xc000,0x600600,0x60000000,0x18,0xc03100,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x601f8,0x0,0x0,0x0,0x0,0x6, 0x12000,0x2000000,0x40,0x20004000,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0xc06000c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200,0x0,0x2004000,0xc00,0x0,0x0,0x0,0x0,0x0,0xc00000, 0x0,0x1c000000,0x0,0x0,0x0,0x0,0x0,0xc00,0x0,0x0,0x0,0x780000,0xf0000000,0x0,0x0,0x21c,0x3,0x0,0x0,0x60000c00,0x0,0x0,0xc000, 0x7c0603,0xe0000000,0x10,0xc02300,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x601f0,0x0,0x0,0x0,0x0,0x6,0x12000,0x1000000, 0x40,0x7e004000,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc06000c0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200,0x0,0x300c000,0xc00,0x0,0x0,0x0,0x0,0x0,0xc00000,0x0,0x7800000,0x0, 0x0,0x0,0x0,0x0,0x800,0x0,0x0,0x0,0x780000,0xf0000000,0x0,0x0,0x3f8,0x3e,0x0,0x0,0x60000c00,0x0,0x0,0x38000,0x3c0603,0xc0000000, 0x10,0xfc00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x60000,0x0,0x0,0x0,0x0,0x6,0x0,0x1000000,0x0,0x0,0x0,0x0, 0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x80600380,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xffc,0x0, 0x0,0x1f0,0x3c,0x0,0x0,0x60000c00,0x0,0x0,0x38000,0x600,0x0,0x0,0xf000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x6,0x0,0xe000000,0x0,0x0,0x0,0x0,0x70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x3,0x80600380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xffc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 }; // Definition of a 16x32 font. const unsigned int font16x32[16*32*256/32] = { 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc300000,0x0,0xc300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70000e0,0x3c00730,0xe7001c0,0x0,0x70000e0,0x3c00e70,0x70000e0,0x3c00e70,0x730,0x70000e0,0x3c00730, 0xe700000,0x700,0xe003c0,0xe7000e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x6600000,0x0,0x6600000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x18001c0,0x6600ff0,0xe7003e0,0x0,0x18001c0,0x6600e70,0x18001c0,0x6600e70,0xff0,0x18001c0,0x6600ff0,0xe700000,0x180, 0x1c00660,0xe7001c0,0x0,0x0,0x0,0x380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000, 0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c00380, 0xc300ce0,0xe700630,0x0,0x1c00380,0xc300e70,0x1c00380,0xc300e70,0xce0,0x1c00380,0xc300ce0,0xe700000,0x1c0,0x3800c30,0xe700380, 0x0,0x0,0x0,0x7c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0xe000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1800000,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0xc300000,0x0,0xc300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x700000,0x0,0x0,0x0,0x7c007c00,0x3e000000, 0x0,0x0,0x630,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe000070,0x1800000,0xc60,0x0,0xe000070,0x1800000,0xe000070, 0x1800000,0x0,0xe000070,0x1800000,0x0,0xe00,0x700180,0x70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x800000,0x0,0x600600,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x3f0,0xfc0,0x0,0x7000000,0x38000000,0x1c0000,0xfc0000,0x380001c0,0xe01c00,0x7f800000,0x0,0x0,0x0,0x0,0x0,0x0,0x7c, 0x1801f00,0x0,0x0,0x1c,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7300000,0x6600000,0x0,0x6600000,0x0,0x0,0x0,0x0,0xe700000, 0x0,0x0,0x0,0x0,0x0,0xe00000,0x0,0x0,0x0,0xc000c00,0x43800000,0x0,0x0,0x630,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0xf80,0x70000e0,0x3c00730,0xe700c60,0x0,0x70000e0,0x3c00e70,0x70000e0,0x3c00e70,0xe000730,0x70000e0,0x3c00730,0xe700000,0x700, 0xe003c0,0xe7000e0,0x38000e70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6300000,0x803c00,0x7c00180, 0xc00300,0x1000000,0x0,0x1c,0x3c007c0,0xfc007e0,0xe01ff8,0x3f03ffc,0x7e007c0,0x0,0x0,0x7c0,0x1c0,0x7f8003f0,0x7f007ff8,0x7ff803f0, 0x70381ffc,0xff0700e,0x7000783c,0x783807c0,0x7fc007c0,0x7fc00fc0,0x7fff7038,0x700ee007,0x780f780f,0x7ffc03f0,0x70000fc0,0x3c00000, 0x3000000,0x38000000,0x1c0000,0x1fc0000,0x380001c0,0xe01c00,0x7f800000,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x1801f80,0x0,0x1f80000, 0x7e,0x0,0x0,0x2400000,0xfc00000,0x7ff0000,0x7ffc0000,0x0,0x0,0x0,0x0,0xf30fb0c,0x2400000,0x0,0x240780f,0x1c0,0xfc,0x780f, 0x18003f0,0xe700000,0x7c00000,0x0,0xff0,0x3c00000,0x78007c0,0xc00000,0xff80000,0xf80,0x7c00000,0xc000c00,0x18001c0,0x1c001c0, 0x1c001c0,0x1c003e0,0x7fe03f0,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,0x1ffc1ffc,0x7f007838,0x7c007c0,0x7c007c0,0x7c00000,0x7c67038, 0x70387038,0x7038780f,0x70001fe0,0x30000c0,0x2400f30,0xe700c60,0x0,0x30000c0,0x2400e70,0x30000c0,0x2400e70,0xf700f30,0x30000c0, 0x2400f30,0xe700000,0x300,0xc00240,0xe7000c0,0x38000e70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0, 0x630018c,0x807e00,0xfe00180,0xc00300,0x1000000,0x0,0x38,0xff01fc0,0x3ff01ff0,0x1e01ff8,0x7f83ffc,0x1ff80ff0,0x0,0x0,0xff0, 0x1f003e0,0x7fe00ff8,0x7fc07ff8,0x7ff80ff8,0x70381ffc,0xff0701c,0x7000783c,0x78381ff0,0x7fe01ff0,0x7fe01ff0,0x7fff7038,0x781ee007, 0x3c1e380e,0x7ffc0380,0x380001c0,0x3c00000,0x1800000,0x38000000,0x1c0000,0x3c00000,0x380001c0,0xe01c00,0x3800000,0x0,0x0, 0x0,0x7000000,0x0,0x0,0x1e0,0x18003c0,0x0,0x3fc0000,0x70,0x0,0x0,0x6600000,0x1ff00000,0x1fff0000,0x7ffc0000,0x0,0x0,0x0,0x0, 0xcf0239c,0x3c00000,0x0,0x3c0380e,0x1c0,0x2001fe,0x380e,0x18007f8,0xe700000,0x8600000,0x0,0xff0,0x7e00000,0x8c00870,0x1800000, 0x1ff80000,0x180,0xc600000,0xc000c00,0x38001c0,0x3e003e0,0x3e003e0,0x3e001c0,0x7fe0ff8,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,0x1ffc1ffc, 0x7fc07838,0x1ff01ff0,0x1ff01ff0,0x1ff00000,0x1fec7038,0x70387038,0x7038380e,0x70003ce0,0x1800180,0x6600cf0,0xe7007c0,0x0, 0x1800180,0x6600e70,0x1800180,0x6600e70,0x7c00cf0,0x1800180,0x6600cf0,0xe700000,0x180,0x1800660,0xe700180,0x38000e70,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x630030c,0x3f0e700,0x1e200180,0x1800180,0x21100000,0x0, 0x38,0x1e7819c0,0x38781038,0x1e01c00,0xf080038,0x1c381c38,0x0,0x0,0x1878,0x7fc03e0,0x70e01e18,0x70e07000,0x70001e18,0x703801c0, 0x707038,0x70007c7c,0x7c381c70,0x70701c70,0x70703830,0x1c07038,0x381ce007,0x1c1c3c1e,0x3c0380,0x380001c0,0x7e00000,0xc00000, 0x38000000,0x1c0000,0x3800000,0x38000000,0x1c00,0x3800000,0x0,0x0,0x0,0x7000000,0x0,0x0,0x1c0,0x18001c0,0x0,0x70c0000,0xe0, 0x0,0x0,0xc300000,0x38300000,0x3c700000,0x3c0000,0x0,0x0,0x0,0x0,0xce022f4,0x1800000,0x0,0x1803c1e,0x1c0,0x2003c2,0x3c1e, 0x1800e08,0x7e0,0x300000,0x0,0x7e00000,0xe700000,0x600030,0x3000000,0x3f980000,0x180,0x18200000,0xc000c00,0x1e0001c0,0x3e003e0, 0x3e003e0,0x3e003e0,0xfe01e18,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70e07c38,0x1c701c70,0x1c701c70,0x1c700000,0x3c787038, 0x70387038,0x70383c1e,0x70003870,0xc00300,0xc300ce0,0x380,0x0,0xc00300,0xc300000,0xc00300,0xc300000,0xfc00ce0,0xc00300,0xc300ce0, 0x0,0xc0,0x3000c30,0x300,0x38000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x630031c,0xff8c300, 0x1c000180,0x1800180,0x39380000,0x0,0x70,0x1c3801c0,0x203c001c,0x3e01c00,0x1c000038,0x381c3838,0x0,0x0,0x1038,0xe0e03e0,0x70703c08, 0x70707000,0x70003808,0x703801c0,0x707070,0x70007c7c,0x7c383838,0x70383838,0x70387010,0x1c07038,0x381c700e,0x1e3c1c1c,0x780380, 0x1c0001c0,0xe700000,0x0,0x38000000,0x1c0000,0x3800000,0x38000000,0x1c00,0x3800000,0x0,0x0,0x0,0x7000000,0x0,0x0,0x1c0,0x18001c0, 0x0,0xe000000,0xe0,0x0,0x1000100,0x3800,0x70100000,0x38700000,0x780000,0x1c0,0x7801ce0,0xe380000,0x0,0x2264,0x0,0x0,0x1c1c, 0x0,0x200780,0x1c1c,0x1800c00,0x1818,0x7f00000,0x0,0x18180000,0xc300000,0x600070,0x0,0x7f980000,0x180,0x18300000,0xc000c00, 0x3000000,0x3e003e0,0x3e003e0,0x3e003e0,0xee03c08,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70707c38,0x38383838,0x38383838, 0x38380000,0x38387038,0x70387038,0x70381c1c,0x7fc03870,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc00000,0x0,0x0,0x0,0x0,0x0,0x0, 0x38000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6300318,0xe88c300,0x1c000180,0x38001c0, 0xfe00180,0x0,0x70,0x1c3801c0,0x1c001c,0x6e01c00,0x1c000078,0x381c3818,0x0,0x40000,0x40000038,0x1c0607e0,0x70703800,0x70707000, 0x70003800,0x703801c0,0x7070e0,0x70007c7c,0x7c383838,0x70383838,0x70387000,0x1c07038,0x381c700e,0xf780e38,0x700380,0x1c0001c0, 0x1c380000,0x0,0x38000000,0x1c0000,0x3800000,0x38000000,0x1c00,0x3800000,0x0,0x0,0x0,0x7000000,0x0,0x0,0x1c0,0x18001c0,0x0, 0xe000000,0xe0,0x0,0x1000100,0x4400,0x70000000,0x38700000,0x700000,0xe0,0x7001c70,0xe380000,0x0,0x2264,0x0,0x0,0xe38,0x0, 0x200700,0xe38,0x1800c00,0x300c,0xc300000,0x0,0x300c0000,0xc300180,0x6003c0,0x0,0x7f980000,0x180,0x18300000,0xc000c00,0x1800000, 0x7e007e0,0x7e007e0,0x7e003e0,0xee03800,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70707c38,0x38383838,0x38383838,0x38380000, 0x38387038,0x70387038,0x70380e38,0x7ff039f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e00000,0x0,0x0,0x0,0x40000,0x0,0x0,0x38000000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6300318,0x1c80e700,0x1c000180,0x38001c0,0x3800180, 0x0,0xe0,0x381c01c0,0x1c001c,0x6e01c00,0x38000070,0x381c381c,0x0,0x3c0000,0x78000078,0x38030770,0x70707800,0x70387000,0x70007000, 0x703801c0,0x7071c0,0x7000745c,0x7638701c,0x7038701c,0x70387000,0x1c07038,0x1c38718e,0x7700f78,0xf00380,0xe0001c0,0x381c0000, 0x7e0,0x39e003e0,0x79c03f0,0x3ffc079c,0x39e01fc0,0xfe01c1e,0x3807778,0x39e007e0,0x39e0079c,0x73c07e0,0x7ff83838,0x701ce007, 0x783c701c,0x1ffc01c0,0x18001c0,0x0,0x1c000100,0xe0,0x0,0x1000100,0x4200,0x70000000,0x70700100,0xf00100,0x10000e0,0x7000c70, 0xc700000,0x0,0x2204,0x7e00000,0x1e380100,0x1ffc0f78,0x0,0xf80700,0xf78,0x1800e00,0x63e6,0x18300000,0x0,0x6fe60000,0xe700180, 0xc00060,0x3838,0x7f980000,0x180,0x18300000,0xc000c00,0x18001c0,0x7700770,0x7700770,0x77007f0,0xee07800,0x70007000,0x70007000, 0x1c001c0,0x1c001c0,0x70387638,0x701c701c,0x701c701c,0x701c1008,0x707c7038,0x70387038,0x70380f78,0x707039c0,0x7e007e0,0x7e007e0, 0x7e007e0,0x1f3c03e0,0x3f003f0,0x3f003f0,0x1fc01fc0,0x1fc01fc0,0x7f039e0,0x7e007e0,0x7e007e0,0x7e00380,0x7ce3838,0x38383838, 0x3838701c,0x39e0701c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6307fff,0x1c807e0c,0xe000180, 0x30000c0,0x3800180,0x0,0xe0,0x381c01c0,0x1c001c,0xce01fe0,0x38000070,0x381c381c,0x3800380,0xfc0000,0x7e0000f0,0x30030770, 0x70707000,0x70387000,0x70007000,0x703801c0,0x707380,0x700076dc,0x7638701c,0x7038701c,0x70387800,0x1c07038,0x1c3873ce,0x7f00770, 0xe00380,0xe0001c0,0x700e0000,0x1ff8,0x3ff00ff0,0xffc0ff8,0x3ffc0ffc,0x3bf01fc0,0xfe01c3c,0x3807f78,0x3bf00ff0,0x3ff00ffc, 0x77e0ff0,0x7ff83838,0x3838e007,0x3c783838,0x1ffc01c0,0x18001c0,0x0,0x7ff00380,0x1e0,0x0,0x1000100,0x4200,0x78000000,0x70700380, 0xe00380,0x3800060,0xe000e30,0x1c600000,0x0,0x2204,0xff00000,0x7f7c0380,0x1ffc0770,0x1c0,0x3fc0700,0x18040770,0x1800780,0x4e12, 0x18300104,0x0,0x4c320000,0x7e00180,0x1c00030,0x3838,0x7f980000,0x180,0x18302080,0xc000c00,0x18001c0,0x7700770,0x7700770, 0x7700770,0x1ee07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70387638,0x701c701c,0x701c701c,0x701c381c,0x705c7038,0x70387038, 0x70380770,0x70383b80,0x1ff81ff8,0x1ff81ff8,0x1ff81ff8,0x3fbe0ff0,0xff80ff8,0xff80ff8,0x1fc01fc0,0x1fc01fc0,0xff83bf0,0xff00ff0, 0xff00ff0,0xff00380,0xffc3838,0x38383838,0x38383838,0x3ff03838,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x1c0,0x7fff,0x1c803c38,0xf000000,0x70000e0,0xfe00180,0x0,0x1c0,0x381c01c0,0x3c0078,0xce01ff0,0x39e000f0,0x1c38381c,0x3800380, 0x3e07ffc,0xf8001f0,0x307b0770,0x70e07000,0x70387000,0x70007000,0x703801c0,0x707700,0x700076dc,0x7638701c,0x7038701c,0x70387e00, 0x1c07038,0x1c3873ce,0x3e007f0,0x1e00380,0x70001c0,0x0,0x1038,0x3c381e18,0x1c7c1e3c,0x3801e3c,0x3c7801c0,0xe01c78,0x380739c, 0x3c781c38,0x3c381c3c,0x7c21e10,0x7003838,0x3838700e,0x1ef03838,0x3c01c0,0x18001c0,0x0,0x7fe007c0,0x1c0,0x0,0x1000100,0x6400, 0x7e000000,0x707007c0,0x1e007c0,0x7c00070,0xe000638,0x18600000,0x0,0x0,0x1e100000,0x73ce07c0,0x3c07f0,0x1c0,0x7240700,0x1ddc3ffe, 0x1800de0,0x8c01,0x1870030c,0x0,0x8c310000,0x3c00180,0x3800030,0x3838,0x7f980000,0x180,0x183030c0,0xc000c00,0x430001c0,0x7700770, 0x7700770,0x7700770,0x1ce07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70387638,0x701c701c,0x701c701c,0x701c1c38,0x70dc7038, 0x70387038,0x703807f0,0x70383b80,0x10381038,0x10381038,0x10381038,0x21e71e18,0x1e3c1e3c,0x1e3c1e3c,0x1c001c0,0x1c001c0,0x1e383c78, 0x1c381c38,0x1c381c38,0x1c380380,0x1c383838,0x38383838,0x38383838,0x3c383838,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x1c0,0x630,0x1e8000e0,0x1f000000,0x70000e0,0x39380180,0x0,0x1c0,0x3b9c01c0,0x3c07f0,0x18e01078,0x3bf800e0, 0x7e0383c,0x3800380,0x1f807ffc,0x3f001c0,0x61ff0e38,0x7fc07000,0x70387ff0,0x7ff07000,0x7ff801c0,0x707f00,0x7000729c,0x7338701c, 0x7070701c,0x70703fc0,0x1c07038,0x1e7873ce,0x1c003e0,0x3c00380,0x70001c0,0x0,0x1c,0x3c381c00,0x1c3c1c1c,0x3801c3c,0x383801c0, 0xe01cf0,0x380739c,0x38381c38,0x3c381c3c,0x7801c00,0x7003838,0x3838700e,0xfe03c78,0x7801c0,0x18001c0,0x0,0x1c000c20,0xff8, 0x0,0x1ff01ff0,0x3818,0x3fc00100,0x707e0c20,0x3c00c20,0xc200030,0xc000618,0x18c00000,0x0,0x0,0x1c000080,0xe1ce0c20,0x7803e0, 0x1c0,0xe200700,0xff83ffe,0x1801878,0x9801,0x1cf0071c,0x7ffc0000,0x8c310000,0x7ffe,0x7000030,0x3838,0x3f980380,0x180,0xc6038e0, 0x7f9c7f9c,0x3e1c01c0,0xe380e38,0xe380e38,0xe380f78,0x1cfc7000,0x7ff07ff0,0x7ff07ff0,0x1c001c0,0x1c001c0,0xfe387338,0x701c701c, 0x701c701c,0x701c0e70,0x719c7038,0x70387038,0x703803e0,0x70383b80,0x1c001c,0x1c001c,0x1c001c,0xe71c00,0x1c1c1c1c,0x1c1c1c1c, 0x1c001c0,0x1c001c0,0x1c383838,0x1c381c38,0x1c381c38,0x1c380000,0x3c383838,0x38383838,0x38383c78,0x3c383c78,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x630,0xf800380,0x3f830000,0x70000e0,0x31080180,0x0,0x380,0x3b9c01c0, 0x7807e0,0x38e00038,0x3c3800e0,0xff01c3c,0x3800380,0x7c000000,0x7c03c0,0x61870e38,0x7fc07000,0x70387ff0,0x7ff070fc,0x7ff801c0, 0x707f80,0x7000739c,0x7338701c,0x7ff0701c,0x7fe00ff0,0x1c07038,0xe7073ce,0x1c003e0,0x3800380,0x38001c0,0x0,0x1c,0x381c3800, 0x381c380e,0x380381c,0x383801c0,0xe01de0,0x380739c,0x3838381c,0x381c381c,0x7001e00,0x7003838,0x1c70718e,0x7e01c70,0xf00380, 0x18001e0,0x1e000000,0x1c001bb0,0xff8,0x0,0x1000100,0xe0,0xff00300,0x707e1bb0,0x3801bb0,0x1bb00010,0x8000308,0x30c00000,0x0, 0x0,0x1e0000c0,0xe1ce1bb0,0xf003e0,0x1c0,0x1c203ff8,0x63003e0,0x180181c,0x9801,0xfb00e38,0x7ffc0000,0x8fc10000,0x7ffe,0xe000860, 0x3838,0x1f980380,0x180,0x7c01c70,0x1f001f0,0x1f003c0,0xe380e38,0xe380e38,0xe380e38,0x1cfc7000,0x7ff07ff0,0x7ff07ff0,0x1c001c0, 0x1c001c0,0xfe387338,0x701c701c,0x701c701c,0x701c07e0,0x731c7038,0x70387038,0x703803e0,0x70383980,0x1c001c,0x1c001c,0x1c001c, 0xe73800,0x380e380e,0x380e380e,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0000,0x387c3838,0x38383838,0x38381c70, 0x381c1c70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0xc30,0x7f00e00,0x33c30000,0x70000e0,0x1007ffe, 0x0,0x380,0x3b9c01c0,0xf00078,0x30e0001c,0x3c1c01c0,0x1c381fdc,0x0,0x70000000,0x1c0380,0x63030e38,0x70707000,0x70387000,0x700070fc, 0x703801c0,0x707b80,0x7000739c,0x7338701c,0x7fc0701c,0x7fc001f0,0x1c07038,0xe703e5c,0x3e001c0,0x7800380,0x38001c0,0x0,0x7fc, 0x381c3800,0x381c380e,0x380381c,0x383801c0,0xe01fe0,0x380739c,0x3838381c,0x381c381c,0x7001fc0,0x7003838,0x1c70718e,0x7c01c70, 0xe01f00,0x180007c,0x7f8c0000,0x7fc03fb8,0x1c0,0x0,0x1000100,0x700,0x1f00600,0x70703fb8,0x7803fb8,0x3fb80000,0x8000000,0x180, 0x0,0x0,0x1fc00060,0xe1ce3fb8,0xe001c0,0x1c0,0x1c203ff8,0xc1801c0,0x180c,0x9801,0x1c70,0xc0000,0x8cc10000,0x180,0xfe007c0, 0x3838,0x7980380,0xff0,0xe38,0x3e003e00,0x3e000380,0xe380e38,0xe380e38,0xe380e38,0x38e07000,0x70007000,0x70007000,0x1c001c0, 0x1c001c0,0x70387338,0x701c701c,0x701c701c,0x701c03c0,0x731c7038,0x70387038,0x703801c0,0x703838e0,0x7fc07fc,0x7fc07fc,0x7fc07fc, 0xe73800,0x380e380e,0x380e380e,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c7ffc,0x38dc3838,0x38383838,0x38381c70, 0x381c1c70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0xc60,0xf83878,0x71e30000,0x70000e0,0x1007ffe, 0x7f0,0x380,0x381c01c0,0x1e0003c,0x60e0001c,0x381c01c0,0x381c079c,0x0,0x7c000000,0x7c0380,0x63031c1c,0x70307000,0x70387000, 0x7000701c,0x703801c0,0x7071c0,0x7000739c,0x71b8701c,0x7000701c,0x71e00078,0x1c07038,0xe703e7c,0x7e001c0,0xf000380,0x38001c0, 0x0,0x1ffc,0x381c3800,0x381c3ffe,0x380381c,0x383801c0,0xe01fc0,0x380739c,0x3838381c,0x381c381c,0x7000ff0,0x7003838,0x1ef03bdc, 0x3800ee0,0x1e01f00,0x180007c,0x61fc0000,0x7fc07f3c,0x1c0,0x0,0x1000100,0x1800,0x780c00,0x70707f3c,0xf007f3c,0x7f3c0000,0x0, 0x3c0,0x3ffcffff,0x0,0xff00030,0xe1fe7f3c,0x1e001c0,0x1c0,0x1c200700,0xc183ffe,0xe0c,0x9801,0x1ff038e0,0xc07f0,0x8c610000, 0x180,0x0,0x3838,0x1980380,0x0,0x1ff0071c,0xe000e000,0xe0000f80,0x1c1c1c1c,0x1c1c1c1c,0x1c1c1e38,0x38e07000,0x70007000,0x70007000, 0x1c001c0,0x1c001c0,0x703871b8,0x701c701c,0x701c701c,0x701c03c0,0x761c7038,0x70387038,0x703801c0,0x70703870,0x1ffc1ffc,0x1ffc1ffc, 0x1ffc1ffc,0xfff3800,0x3ffe3ffe,0x3ffe3ffe,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c7ffc,0x389c3838,0x38383838, 0x38380ee0,0x381c0ee0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0xfffc,0xbc60fc,0x70e30000,0x70000e0, 0x180,0x7f0,0x700,0x381c01c0,0x3e0001c,0x7ffc001c,0x381c03c0,0x381c001c,0x0,0x1f807ffc,0x3f00380,0x63031ffc,0x70387000,0x70387000, 0x7000701c,0x703801c0,0x7071e0,0x7000701c,0x71b8701c,0x7000701c,0x70f00038,0x1c07038,0x7e03e7c,0x77001c0,0xe000380,0x1c001c0, 0x0,0x3c1c,0x381c3800,0x381c3ffe,0x380381c,0x383801c0,0xe01fe0,0x380739c,0x3838381c,0x381c381c,0x70003f8,0x7003838,0xee03bdc, 0x3c00ee0,0x3c00380,0x18000e0,0xf00000,0x1c007e7c,0x3c0,0x0,0x1000100,0x0,0x381800,0x70707e7c,0xe007e7c,0x7e7c0000,0x0,0x7c0, 0x0,0x0,0x3f80018,0xe1fe7e7c,0x3c001c0,0x1c0,0x1c200700,0xc183ffe,0xf0c,0x8c01,0x38e0,0xc07f0,0x8c710000,0x180,0x0,0x3838, 0x1980000,0x0,0x71c,0x7000f0,0x700f00,0x1ffc1ffc,0x1ffc1ffc,0x1ffc1ffc,0x3fe07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0, 0x703871b8,0x701c701c,0x701c701c,0x701c07e0,0x7c1c7038,0x70387038,0x703801c0,0x7ff03838,0x3c1c3c1c,0x3c1c3c1c,0x3c1c3c1c, 0x3fff3800,0x3ffe3ffe,0x3ffe3ffe,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0000,0x391c3838,0x38383838,0x38380ee0, 0x381c0ee0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfffc,0x9c01ce,0x70f60000,0x70000e0,0x180, 0x0,0x700,0x381c01c0,0x780001c,0x7ffc001c,0x381c0380,0x381c003c,0x0,0x3e07ffc,0xf800380,0x63031ffc,0x70387000,0x70387000, 0x7000701c,0x703801c0,0x7070f0,0x7000701c,0x71b8701c,0x7000701c,0x70700038,0x1c07038,0x7e03e7c,0xf7801c0,0x1e000380,0x1c001c0, 0x0,0x381c,0x381c3800,0x381c3800,0x380381c,0x383801c0,0xe01fe0,0x380739c,0x3838381c,0x381c381c,0x7000078,0x7003838,0xee03a5c, 0x7c00fe0,0x78001c0,0x18001c0,0x0,0x1c003ef8,0x380,0x0,0x1000100,0x810,0x383000,0x70703ef8,0x1e003ef8,0x3ef80000,0x0,0x7c0, 0x0,0x0,0x78000c,0xe1c03ef8,0x78001c0,0x1c0,0x1c200700,0x63001c0,0x18003f8,0x4e12,0x1c70,0xc0000,0x4c320000,0x180,0x0,0x3838, 0x1980000,0x0,0xe38,0x700118,0x701e00,0x1ffc1ffc,0x1ffc1ffc,0x1ffc1ffc,0x7fe07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0, 0x703871b8,0x701c701c,0x701c701c,0x701c0e70,0x7c1c7038,0x70387038,0x703801c0,0x7fc0381c,0x381c381c,0x381c381c,0x381c381c, 0x78e03800,0x38003800,0x38003800,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0000,0x3b1c3838,0x38383838,0x38380fe0, 0x381c0fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1860,0x9c0186,0x707e0000,0x30000c0,0x180, 0x0,0xe00,0x183801c0,0xf00001c,0xe0001c,0x181c0380,0x381c0038,0x0,0xfc0000,0x7e000000,0x61873c1e,0x70383800,0x70707000,0x7000381c, 0x703801c0,0x707070,0x7000701c,0x70f83838,0x70003838,0x70780038,0x1c07038,0x7e03c3c,0xe3801c0,0x1c000380,0xe001c0,0x0,0x381c, 0x381c3800,0x381c3800,0x380381c,0x383801c0,0xe01ef0,0x380739c,0x3838381c,0x381c381c,0x7000038,0x7003838,0xfe03e7c,0xfe007c0, 0x70001c0,0x18001c0,0x0,0xe001ff0,0x380,0x0,0x1000100,0x162c,0x381800,0x30701ff0,0x1c001ff0,0x1ff00000,0x0,0x3c0,0x0,0x0, 0x380018,0xe1c01ff0,0x70001c0,0x1c0,0x1c200700,0xff801c0,0x18000f0,0x63e6,0xe38,0x0,0x6c3e0000,0x0,0x0,0x3838,0x1980000,0x0, 0x1c70,0xf0000c,0xf01c00,0x3c1e3c1e,0x3c1e3c1e,0x3c1e3c1c,0x70e03800,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x707070f8, 0x38383838,0x38383838,0x38381c38,0x38387038,0x70387038,0x703801c0,0x7000381c,0x381c381c,0x381c381c,0x381c381c,0x70e03800, 0x38003800,0x38003800,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0380,0x3e1c3838,0x38383838,0x383807c0,0x381c07c0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18c0,0x9c0186,0x783c0000,0x38001c0,0x180,0x3800000, 0x3800e00,0x1c3801c0,0x1e00003c,0xe00038,0x1c1c0780,0x381c0038,0x3800380,0x3c0000,0x78000000,0x61ff380e,0x70383808,0x70707000, 0x7000381c,0x703801c0,0x40707078,0x7000701c,0x70f83838,0x70003838,0x70384038,0x1c07038,0x7e03c3c,0x1e3c01c0,0x3c000380,0xe001c0, 0x0,0x383c,0x3c381c00,0x1c3c1c00,0x3801c3c,0x383801c0,0xe01c78,0x380739c,0x38381c38,0x3c381c3c,0x7000038,0x7003878,0x7c01e78, 0x1ef007c0,0xf0001c0,0x18001c0,0x0,0xe000ee0,0x7800380,0xe380000,0x1001ff0,0x2242,0x40380c00,0x38700ee0,0x3c000ee0,0xee00000, 0x0,0x0,0x0,0x0,0x380030,0xe1c00ee0,0xf0001c0,0x1c0,0xe200700,0xdd801c0,0x1800038,0x300c,0x71c,0x0,0x300c0000,0x0,0x0,0x3838, 0x1980000,0x0,0x38e0,0xb0000c,0xb01c08,0x380e380e,0x380e380e,0x380e380e,0x70e03808,0x70007000,0x70007000,0x1c001c0,0x1c001c0, 0x707070f8,0x38383838,0x38383838,0x3838381c,0x38387038,0x70387038,0x703801c0,0x7000381c,0x383c383c,0x383c383c,0x383c383c, 0x70e01c00,0x1c001c00,0x1c001c00,0x1c001c0,0x1c001c0,0x1c383838,0x1c381c38,0x1c381c38,0x1c380380,0x1c383878,0x38783878,0x387807c0, 0x3c3807c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x18c0,0x10b801ce,0x3c3e0000,0x38001c0,0x180, 0x3800000,0x3801c00,0x1e7801c0,0x3c002078,0xe02078,0x1c380700,0x1c3810f0,0x3800380,0x40000,0x40000380,0x307b380e,0x70701e18, 0x70e07000,0x70001c1c,0x703801c0,0x60e0703c,0x7000701c,0x70f83c78,0x70003c70,0x703c70f0,0x1c03870,0x3c01c3c,0x3c1c01c0,0x78000380, 0x7001c0,0x0,0x3c7c,0x3c381e18,0x1c7c1e0c,0x3801c3c,0x383801c0,0xe01c38,0x3c0739c,0x38381c38,0x3c381c3c,0x7001078,0x7803c78, 0x7c01c38,0x1c780380,0x1e0001c0,0x18001c0,0x0,0x70c06c0,0x7000380,0xe300000,0x1000100,0x2142,0x70f00600,0x3c7006c0,0x780006c0, 0x6c00000,0x0,0x0,0x0,0x0,0x10780060,0x73e206c0,0x1e0001c0,0x1c0,0x7240700,0x180c01c0,0x1800018,0x1818,0x30c,0x0,0x18180000, 0x0,0x0,0x3c78,0x1980000,0x0,0x30c0,0x130000c,0x1301c18,0x380e380e,0x380e380e,0x380e380e,0x70e01e18,0x70007000,0x70007000, 0x1c001c0,0x1c001c0,0x70e070f8,0x3c783c78,0x3c783c78,0x3c781008,0x7c783870,0x38703870,0x387001c0,0x70003a3c,0x3c7c3c7c,0x3c7c3c7c, 0x3c7c3c7c,0x79f11e18,0x1e0c1e0c,0x1e0c1e0c,0x1c001c0,0x1c001c0,0x1c783838,0x1c381c38,0x1c381c38,0x1c380380,0x1c383c78,0x3c783c78, 0x3c780380,0x3c380380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x38c0,0x1ff800fc,0x1fee0000, 0x1800180,0x180,0x3800000,0x3801c00,0xff01ffc,0x3ffc3ff0,0xe03ff0,0xff00700,0x1ff81fe0,0x3800380,0x0,0x380,0x3000780f,0x7ff00ff8, 0x7fc07ff8,0x70000ffc,0x70381ffc,0x7fe0701c,0x7ff8701c,0x70781ff0,0x70001ff0,0x701c7ff0,0x1c01fe0,0x3c01c38,0x380e01c0,0x7ffc0380, 0x7001c0,0x0,0x1fdc,0x3ff00ff0,0xffc0ffc,0x3800fdc,0x38383ffe,0xe01c3c,0x1fc739c,0x38380ff0,0x3ff00ffc,0x7001ff0,0x3f81fb8, 0x7c01c38,0x3c3c0380,0x1ffc01c0,0x18001c0,0x0,0x3fc0380,0x7000380,0xc70718c,0x1000100,0x2244,0x7ff00200,0x1fff0380,0x7ffc0380, 0x3800000,0x0,0x0,0x0,0x0,0x1ff000c0,0x7f7e0380,0x1ffc01c0,0x1c0,0x3fc3ffe,0x1c0,0x1800018,0x7e0,0x104,0x0,0x7e00000,0x7ffe, 0x0,0x3fde,0x1980000,0x0,0x2080,0x3300018,0x3300ff0,0x780f780f,0x780f780f,0x780f780e,0xf0fe0ff8,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc, 0x1ffc1ffc,0x7fc07078,0x1ff01ff0,0x1ff01ff0,0x1ff00000,0x7ff01fe0,0x1fe01fe0,0x1fe001c0,0x70003bf8,0x1fdc1fdc,0x1fdc1fdc, 0x1fdc1fdc,0x3fbf0ff0,0xffc0ffc,0xffc0ffc,0x3ffe3ffe,0x3ffe3ffe,0xff03838,0xff00ff0,0xff00ff0,0xff00000,0x3ff01fb8,0x1fb81fb8, 0x1fb80380,0x3ff00380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x31c0,0x7e00078,0x7cf0000,0x1800180, 0x0,0x3800000,0x3803800,0x3c01ffc,0x3ffc0fe0,0xe01fc0,0x3e00e00,0x7e00f80,0x3800380,0x0,0x380,0x18007007,0x7fc003f0,0x7f007ff8, 0x700003f0,0x70381ffc,0x3f80701e,0x7ff8701c,0x707807c0,0x700007c0,0x701e1fc0,0x1c00fc0,0x3c01818,0x780f01c0,0x7ffc0380,0x3801c0, 0x0,0xf9c,0x39e003e0,0x79c03f0,0x380079c,0x38383ffe,0xe01c1e,0x7c739c,0x383807e0,0x39e0079c,0x7000fc0,0x1f80f38,0x3801c38, 0x781e0380,0x1ffc01c0,0x18001c0,0x0,0x1f80100,0xe000700,0x1c60718c,0x1000100,0x1e3c,0x1fc00100,0x7ff0100,0x7ffc0100,0x1000000, 0x0,0x0,0x0,0x0,0xfc00080,0x3e3c0100,0x1ffc01c0,0x1c0,0xf83ffe,0x1c0,0x1800838,0x0,0x0,0x0,0x0,0x7ffe,0x0,0x3b9e,0x1980000, 0x0,0x0,0x2300038,0x23003e0,0x70077007,0x70077007,0x70077007,0xe0fe03f0,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,0x1ffc1ffc,0x7f007078, 0x7c007c0,0x7c007c0,0x7c00000,0xc7c00fc0,0xfc00fc0,0xfc001c0,0x700039f0,0xf9c0f9c,0xf9c0f9c,0xf9c0f9c,0x1f1e03e0,0x3f003f0, 0x3f003f0,0x3ffe3ffe,0x3ffe3ffe,0x7e03838,0x7e007e0,0x7e007e0,0x7e00000,0x63e00f38,0xf380f38,0xf380380,0x39e00380,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000,0x0,0xc00300,0x0,0x3000000,0x3800,0x0,0x0,0x0,0x0, 0x0,0x300,0x0,0x0,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0x0,0x0,0x0,0x0,0x380,0x3801c0,0x0,0x0,0x0,0x0,0x1c,0x0,0xe00000, 0x0,0x0,0x3800001c,0x0,0x0,0x0,0x700,0x1c0,0x18001c0,0x0,0x0,0xe000700,0x18600000,0x1000100,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x1800ff0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x1980000,0x1800000,0x0,0x6300070,0x6300000,0x0, 0x0,0x0,0xc0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40000000, 0x0,0x700,0x38000700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000,0x0,0xc00300,0x0,0x7000000, 0x7000,0x0,0x0,0x0,0x0,0x0,0x700,0x0,0x0,0xf040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x0,0x3f0,0x1c0fc0,0x0,0x0, 0x0,0x0,0x1c,0x0,0xe00000,0x0,0x0,0x3800001c,0x0,0x0,0x0,0x700,0x1e0,0x18003c0,0x0,0x0,0xc000700,0x18c00000,0x1000000,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x18007e0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x1980000,0xc00000, 0x0,0x7f800e0,0x7f80000,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x700,0x38000700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000, 0x0,0x600600,0x0,0x6000000,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x7fc0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0, 0x3f0,0xfc0,0x0,0x0,0x0,0x0,0x838,0x0,0x1e00000,0x0,0x0,0x3800001c,0x0,0x0,0x0,0xf00,0xfc,0x1801f80,0x0,0x0,0x8008e00,0x30c00000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x1800000,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x1980000,0xc00000, 0x0,0x3001c0,0x300000,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0xf00,0x38000f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0xff0,0x0,0x1fc00000,0x0,0x0,0x3800001c,0x0,0x0,0x0,0x3e00,0x7c,0x1801f00,0x0,0x0,0x800fe00,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x1800000,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x0,0x7c00000,0x0,0x3001fc,0x300000, 0x0,0x0,0x0,0x3e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x3e00,0x38003e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfff8,0x0,0x0,0x0,0x7e0,0x0,0x1f000000, 0x0,0x0,0x3800001c,0x0,0x0,0x0,0x3c00,0x0,0x1800000,0x0,0x0,0x7800,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x0,0x7800000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00,0x38003c00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfff8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1800000,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0 }; // Definition of a 19x38 font. const unsigned int font19x38[19*38*256/32] = { 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c380000,0x0,0x1c380,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800007,0x3c003,0x86000000, 0x1e00000,0x3,0x80000700,0x3c00000,0x380000,0x70003c00,0x0,0xe1800e,0x1c00,0xf000e18,0x0,0x0,0x700000e0,0x780000,0x7000,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe700000,0x0,0xe700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0000e,0x7e003,0xe60071c0,0x7f80000,0x1,0xc0000e00,0x7e0038e,0x1c0000, 0xe0007e00,0x38e00000,0xf98007,0x3800,0x1f800f98,0x1c70000,0x0,0x380001c0,0xfc0071,0xc000e000,0x0,0x0,0x0,0x0,0x3e00000,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x7e00000,0x0,0x7e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0001c,0xe7006,0x7c0071c0,0xe180000,0x0,0xe0001c00,0xe70038e,0xe0001,0xc000e700,0x38e00000, 0x19f0003,0x80007000,0x39c019f0,0x1c70000,0x0,0x1c000380,0x1ce0071,0xc001c000,0x0,0x0,0x0,0x0,0x7f00000,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000, 0x0,0x3c00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x700038,0x1c3806,0x3c0071c0,0xc0c0000,0x0,0x70003800,0x1c38038e,0x70003,0x8001c380,0x38e00000,0x18f0001,0xc000e000, 0x70e018f0,0x1c70000,0x0,0xe000700,0x3870071,0xc0038000,0x0,0x0,0x0,0x0,0xe380000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0xe000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60000000,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c38,0x0,0x1,0xc3800000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c00000,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0xc0c0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe000003,0x80018000,0x0,0xc180000, 0xe,0x380,0x1800000,0xe00000,0x38001800,0x0,0x38,0xe00,0x6000000,0x0,0x1,0xc0000070,0x300000,0x3800,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7000000,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78c00,0xc30, 0x0,0x0,0xc3000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800000,0x0,0x0,0x0,0xe0,0x1c000f,0xc0000000,0x0,0x0, 0x0,0xc0c0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7000007,0x3c003,0xc6000000,0xc180000,0x7,0x700, 0x3c00000,0x700000,0x70003c00,0x0,0xf1801c,0x1c00,0xf000f18,0x0,0x0,0xe00000e0,0x780000,0x7000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x1c007000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe0000,0xfe000,0x0,0x3800000,0x700000,0x38, 0x7,0xe000001c,0x1c00,0x1c00700,0x7fc0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf800e,0x3e0000,0x0,0x0,0x0,0x1e00000,0x0,0x1, 0xf8000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7cc00,0x660,0x0,0x0,0x66000000,0x0,0x0,0x0,0x0,0x7,0x1c000000,0x0,0x0,0x0,0x3fe00000, 0x0,0x0,0x7000000,0x0,0x0,0x0,0x3e0,0x7c001f,0xe0000000,0x0,0x0,0x0,0xe1c0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x1f80,0x380000e,0x7e007,0xe60071c0,0xc180000,0x3,0x80000e00,0x7e0038e,0x380000,0xe0007e00,0x38e00f00,0x1f9800e, 0x3800,0x1f801f98,0x1c70000,0x0,0x700001c0,0xfc0071,0xc000e007,0x38e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1c7000,0x61c00600,0x1e00007e,0x70000,0x18003000,0x1800000,0x0,0x0,0x1c01f0,0x7e003f,0xc003f800, 0x1e03ffc,0x7f01ff,0xfc03f000,0x7e000000,0x0,0x0,0xfc0,0x1e,0x7fe000,0x7e03fe00,0x3fff07ff,0xe007e038,0x383ffe0,0xff81c01, 0xe1c000f8,0xf8f00e0,0xfc01ffc,0x3f00ff,0xc000fe07,0xfffc7007,0x1c007700,0x73c01ef,0x78ffff,0xfe0380,0xfe000,0x38000000,0x1800000, 0x700000,0x38,0x1f,0xe000001c,0x1c00,0x1c00700,0x7fc0000,0x0,0x0,0x0,0x0,0x1c000000,0x0,0x0,0x0,0x3f800e,0x3f8000,0x0,0xfc0000, 0x0,0x7f00000,0x0,0x1,0x98000000,0x7f00000,0x3ffe00,0xffff0,0x0,0x0,0x0,0x0,0x0,0xcf81f,0xee3807e0,0x0,0x0,0x7e03c01e,0x1c, 0x0,0x1f800000,0xf0078038,0xfc007,0x1c000000,0xfe00000,0x0,0x0,0x3fe000f0,0xf,0xc001f800,0x6000000,0xffc000,0x0,0x1c0007e0, 0x360,0x6c0010,0x70000700,0xf0001e,0x3c000,0x78000f00,0x7f800ff,0xf007e01f,0xff83fff0,0x7ffe0fff,0xc1fff03f,0xfe07ffc0,0xfff83fc0, 0x7807007,0xe000fc00,0x1f8003f0,0x7e0000,0x1f867,0x70e00e,0x1c01c380,0x38f00787,0x3fe0,0x180000c,0x66006,0x7c0071c0,0xe380000, 0x1,0x80000c00,0x660038e,0x180000,0xc0006600,0x38e0078e,0x19f0006,0x3000,0x198019f0,0x1c70000,0x0,0x30000180,0xcc0071,0xc000c007, 0x38e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1c7000,0x61800600,0x7f8001ff,0x70000, 0x38003800,0x1800000,0x0,0x0,0x3807fc,0x1fe00ff,0xf00ffe00,0x3e03ffc,0xff81ff,0xfc07fc01,0xff800000,0x0,0x0,0x3fe0,0xfe001e, 0x7ff801,0xff83ff80,0x3fff07ff,0xe01ff838,0x383ffe0,0xff81c03,0xc1c000f8,0xf8f80e0,0x3ff01fff,0xffc0ff,0xf003ff87,0xfffc7007, 0x1e00f700,0x71c03c7,0x70ffff,0xfe01c0,0xfe000,0x7c000000,0xc00000,0x700000,0x38,0x3f,0xe000001c,0x1c00,0x1c00700,0x7fc0000, 0x0,0x0,0x0,0x0,0x1c000000,0x0,0x0,0x0,0x3f800e,0x3f8000,0x0,0x3fe0000,0x0,0xff00000,0x0,0x3,0xc000000,0x1ffc0000,0xfffe00, 0xffff0,0x0,0x0,0x0,0x0,0x0,0xc781f,0xee3803c0,0x0,0x0,0x3c01c01c,0x1c,0xc000,0x7fc00000,0x70070038,0x3fe007,0x1c000000,0x1ff80000, 0x0,0x0,0x3fe003fc,0x1f,0xe003fc00,0xc000000,0x3ffc000,0x0,0x7c000ff0,0x60,0xc0000,0x30000700,0xf0001e,0x3c000,0x78000f00, 0x3f000ff,0xf01ff81f,0xff83fff0,0x7ffe0fff,0xc1fff03f,0xfe07ffc0,0xfff83ff8,0x7c0701f,0xf803ff00,0x7fe00ffc,0x1ff8000,0x7fe67, 0x70e00e,0x1c01c380,0x38700707,0x7ff0,0xc00018,0xc3006,0x3c0071c0,0x7f00000,0x0,0xc0001800,0xc30038e,0xc0001,0x8000c300,0x38e003fc, 0x18f0003,0x6000,0x30c018f0,0x1c70000,0x0,0x18000300,0x1860071,0xc0018007,0x38e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1c7000,0xe1801fc0,0x618001ff,0x70000,0x30001800,0x21840000,0x0,0x0,0x380ffe,0x1fe00ff, 0xfc0fff00,0x3e03ffc,0x1ff81ff,0xfc0ffe03,0xffc00000,0x0,0x0,0x7ff0,0x3ff803f,0x7ffc03,0xffc3ffc0,0x3fff07ff,0xe03ffc38,0x383ffe0, 0xff81c07,0x81c000f8,0xf8f80e0,0x7ff81fff,0x81ffe0ff,0xf80fff87,0xfffc7007,0xe00e700,0x70e0387,0x80f0ffff,0xe001c0,0xe000, 0xfe000000,0xe00000,0x700000,0x38,0x3c,0x1c,0x1c00,0x1c00700,0x1c0000,0x0,0x0,0x0,0x0,0x1c000000,0x0,0x0,0x0,0x78000e,0x3c000, 0x0,0x7ff0000,0x0,0xf100000,0x0,0x7,0xe000000,0x7ffc0000,0x1fffe00,0xffff0,0x0,0x0,0x0,0x0,0x0,0x3,0xf780180,0x0,0x0,0x1801e03c, 0x1c,0xc000,0xffc00000,0x780f0038,0x786000,0x7f00,0x18380000,0x0,0xfe00,0x30c,0x10,0x70020e00,0x1c000000,0x7f8c000,0x0,0x6c001c38, 0x60,0xc0000,0x70000700,0x1f8003f,0x7e000,0xfc001f80,0x3f000ff,0xf03ffc1f,0xff83fff0,0x7ffe0fff,0xc1fff03f,0xfe07ffc0,0xfff83ffc, 0x7c0703f,0xfc07ff80,0xfff01ffe,0x3ffc000,0xffec7,0x70e00e,0x1c01c380,0x38780f07,0xf070,0xe00038,0x1c3800,0x0,0x3e00000,0x0, 0xe0003800,0x1c380000,0xe0003,0x8001c380,0x3e0,0x3,0x8000e000,0x70e00000,0x0,0x0,0x1c000700,0x3870000,0x38007,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1c7000,0xe3807ff0,0xc0c003c1,0x70000,0x70001c00, 0x718e0000,0x0,0x0,0x700f1e,0x1ce00c0,0x3c0c0f80,0x7e03800,0x3e08000,0x381e0f03,0xc1e00000,0x0,0x0,0x7078,0x783c03f,0x701e07, 0xc1c383e0,0x38000700,0x7c1c38,0x3801c00,0x381c0f,0x1c000fc,0x1f8f80e0,0x78781c07,0x81e1e0e0,0x780f0180,0xe007007,0xe00e380, 0xe0f0783,0x80e0000e,0xe000e0,0xe001,0xef000000,0x0,0x700000,0x38,0x38,0x1c,0x0,0x700,0x1c0000,0x0,0x0,0x0,0x0,0x1c000000, 0x0,0x0,0x0,0x70000e,0x1c000,0x0,0xf830000,0x0,0x1e000000,0x0,0x0,0x10000,0x780c0000,0x3e38000,0xe0,0x0,0x0,0x0,0x0,0x0,0x3, 0xd580000,0x0,0x0,0xe038,0x1c,0xc000,0xf0400000,0x380e0038,0x702000,0x1ffc0,0xc0000,0x0,0x3ff80,0x606,0x0,0x30000600,0x0, 0x7f8c000,0x0,0xc001818,0x60,0xc0003,0xe0000700,0x1f8003f,0x7e000,0xfc001f80,0x73801ee,0x7c1c1c,0x38000,0x70000e00,0xe0001, 0xc0003800,0x700383e,0x7c0703c,0x3c078780,0xf0f01e1e,0x3c3c000,0xf0f87,0x70e00e,0x1c01c380,0x38380e07,0xe038,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0xff0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x1c,0x1c7000,0xc380fff0,0xc0c00380,0x70000,0x70001c00,0x3dbc0070,0x0,0x0,0x701e0f,0xe0000,0x1e000380, 0x6e03800,0x7800000,0x781c0707,0x80e00000,0x0,0x0,0x4038,0xe00c03f,0x700e07,0x4380f0,0x38000700,0x700438,0x3801c00,0x381c0e, 0x1c000ec,0x1b8fc0e0,0xf03c1c03,0xc3c0f0e0,0x3c1e0000,0xe007007,0xe00e380,0xe070703,0xc1e0001e,0xe000e0,0xe001,0xc7000000, 0x0,0x700000,0x38,0x38,0x1c,0x0,0x700,0x1c0000,0x0,0x0,0x0,0x0,0x1c000000,0x0,0x0,0x0,0x70000e,0x1c000,0x0,0xe010000,0x0, 0x1c000000,0x10,0x20000,0x6c000,0xf0000000,0x3838000,0x1e0,0x0,0xf000f,0xf1e00,0x78f00000,0x0,0x3,0xdd80000,0x0,0x0,0xf078, 0x0,0xc001,0xe0000000,0x1c1c0038,0x700000,0x3c1e0,0xc0000,0x0,0x783c0,0x606,0x0,0x30000e00,0x0,0xff8c000,0x0,0xc00300c,0x60, 0xc0003,0xe0000000,0x1f8003f,0x7e000,0xfc001f80,0x73801ce,0x70041c,0x38000,0x70000e00,0xe0001,0xc0003800,0x700380f,0x7e07078, 0x1e0f03c1,0xe0783c0f,0x781e000,0x1c0787,0x70e00e,0x1c01c380,0x383c1e07,0xff00e038,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x878, 0x0,0x0,0x0,0x7,0x80000080,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c, 0x1c7000,0xc301e630,0xc0c00380,0x70000,0xe0000e00,0xff00070,0x0,0x0,0xe01c07,0xe0000,0xe000380,0xce03800,0x7000000,0x701c0707, 0x600000,0x0,0x4000010,0x38,0x1c00e07f,0x80700e0e,0x38070,0x38000700,0xe00038,0x3801c00,0x381c1c,0x1c000ec,0x1b8ec0e0,0xe01c1c01, 0xc38070e0,0x1c1c0000,0xe007007,0x701c380,0xe078e01,0xc1c0003c,0xe00070,0xe003,0x83800000,0x7f,0x71f000,0x3e003e38,0x3f007ff, 0xe01f1c1c,0x7801fc00,0x3fc00701,0xe01c0077,0x8f071e00,0xf801c7c,0x7c700e,0x3e01fc03,0xfff8380e,0xe007700,0x73c0787,0x387ffc, 0x70000e,0x1c000,0x0,0xe000000,0x0,0x1c000000,0x10,0x20000,0xc2000,0xe0000000,0x3838000,0x3c0,0x0,0xf000f,0x78e00,0x70e00000, 0x0,0x3,0xc980fe0,0x1f0,0xf8000007,0xffc07070,0x0,0x3f801,0xc0000000,0x1e3c0038,0x700000,0x70070,0x7fc0000,0x0,0xe00e0,0x606, 0x1c0000,0x70007c00,0x380e,0xff8c000,0x0,0xc00300c,0x60,0xc0000,0x70000000,0x3fc007f,0x800ff001,0xfe003fc0,0x73801ce,0xe0001c, 0x38000,0x70000e00,0xe0001,0xc0003800,0x7003807,0x7607070,0xe0e01c1,0xc0383807,0x700e000,0x1c0387,0x70e00e,0x1c01c380,0x381c1c07, 0xffc0e0f8,0x3f8007f,0xfe001,0xfc003f80,0x7f007e3,0xe003e001,0xf8003f00,0x7e000fc,0xfe001f,0xc003f800,0x7f00003c,0x38f0007, 0xc000f800,0x1f0003e0,0x7c0007,0x8003f0c3,0x80e0701c,0xe0381c0,0x70700387,0x1f01c00e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1c701f,0xfff1c600,0xc0c00380,0x70000,0xe0000e00,0x3c00070,0x0,0x0,0xe03c07, 0x800e0000,0xe000380,0x1ce03800,0x7000000,0x701c0707,0x7003c0,0x780000,0x3c00001e,0x38,0x18006073,0x80700e0e,0x38070,0x38000700, 0xe00038,0x3801c00,0x381c38,0x1c000ee,0x3b8ee0e1,0xe01e1c01,0xc78078e0,0x1c1c0000,0xe007007,0x701c387,0xe03de00,0xe3800038, 0xe00070,0xe007,0x1c00000,0x1ff,0xc077f801,0xff807fb8,0xff807ff,0xe03fdc1d,0xfc01fc00,0x3fc00703,0xc01c007f,0xdf877f00,0x3fe01dfe, 0xff700e,0xff07ff03,0xfff8380e,0x700f700,0x71e0f03,0x80707ffc,0x70000e,0x1c000,0x0,0x1c000008,0x0,0x1c000000,0x10,0x20000, 0x82000,0xe0000000,0x7038000,0x80000380,0x2000040,0x7000e,0x38700,0xf1e00000,0x0,0x3,0xc183ff8,0x3fd,0xfc008007,0xffc038e0, 0x0,0xffc01,0xc0008008,0xe380038,0x380000,0xe3e38,0x1ffc0040,0x80000000,0x1cfc70,0x606,0x1c0000,0xe0007c00,0x380e,0xff8c000, 0x0,0xc00300c,0x8100060,0xc0000,0x30000700,0x39c0073,0x800e7001,0xce0039c0,0x73801ce,0xe0001c,0x38000,0x70000e00,0xe0001, 0xc0003800,0x7003807,0x77070f0,0xf1e01e3,0xc03c7807,0x8f00f080,0x83c0787,0x70e00e,0x1c01c380,0x380e3807,0xffe0e1c0,0xffe01ff, 0xc03ff807,0xff00ffe0,0x1ffc0ff7,0xf01ff807,0xfc00ff80,0x1ff003fe,0xfe001f,0xc003f800,0x7f0003fc,0x3bf801f,0xf003fe00,0x7fc00ff8, 0x1ff0007,0x8007fd83,0x80e0701c,0xe0381c0,0x70380707,0x7f80e01c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x1c,0x1c701f,0xfff1c600,0x618081c0,0x70000,0xe0000e00,0x3c00070,0x0,0x0,0xe03803,0x800e0000,0xe000380,0x18e03800, 0xf000000,0xf01c0707,0x7003c0,0x780000,0xfc00001f,0x80000078,0x301e6073,0x80700e1c,0x38038,0x38000700,0x1c00038,0x3801c00, 0x381c70,0x1c000e6,0x338ee0e1,0xc00e1c01,0xc70038e0,0x1c1c0000,0xe007007,0x701c387,0xe01dc00,0xf7800078,0xe00070,0xe00e,0xe00000, 0x3ff,0xe07ffc03,0xffc0fff8,0x1ffc07ff,0xe07ffc1d,0xfe01fc00,0x3fc00707,0x801c007f,0xdf877f80,0x7ff01fff,0x1fff00e,0xff07ff03, 0xfff8380e,0x700e380,0xe0e0e03,0x80707ffc,0x70000e,0x1c000,0x0,0x7ffc001c,0x0,0x1c000000,0x10,0x20000,0x82000,0xe0000000, 0x7038001,0xc0000780,0x70000e0,0x3800e,0x38700,0xe1c00000,0x0,0x3,0xc183ff8,0x7ff,0xfc01c007,0xffc03de0,0x0,0x1ffc01,0xc001c01c, 0xf780038,0x3c0000,0xcff18,0x380c00c1,0x80000000,0x18fe30,0x30c,0x1c0001,0xc0000e00,0x380e,0xff8c000,0x0,0xc00300c,0xc180060, 0xc0000,0x30000700,0x39c0073,0x800e7001,0xce0039c0,0xe1c038e,0x1c0001c,0x38000,0x70000e00,0xe0001,0xc0003800,0x7003803,0x877070e0, 0x71c00e3,0x801c7003,0x8e0071c0,0x1c380fc7,0x70e00e,0x1c01c380,0x380f7807,0x1e0e380,0x1fff03ff,0xe07ffc0f,0xff81fff0,0x3ffe0fff, 0xf03ffc0f,0xfe01ffc0,0x3ff807ff,0xfe001f,0xc003f800,0x7f0007fe,0x3bfc03f,0xf807ff00,0xffe01ffc,0x3ff8007,0x800fff83,0x80e0701c, 0xe0381c0,0x70380707,0xffc0e01c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1c701f, 0xfff1c600,0x7f8381e0,0x70000,0xc0000600,0xff00070,0x0,0x0,0x1c03803,0x800e0000,0xe000f00,0x38e03fe0,0xe000000,0xe00e0e07, 0x7003c0,0x780007,0xf0ffff87,0xf00000f0,0x307fe0f3,0xc0703c1c,0x38038,0x38000700,0x1c00038,0x3801c00,0x381ce0,0x1c000e6,0x338e70e1, 0xc00e1c01,0xc70038e0,0x3c1e0000,0xe007007,0x783c38f,0x8e01fc00,0x770000f0,0xe00038,0xe01c,0x700000,0x381,0xe07c1e07,0xc0c1e0f8, 0x3c1e0038,0xf07c1f,0xe001c00,0x1c0070f,0x1c0079,0xf3c7c380,0xf0781f07,0x83c1f00f,0xc10f0300,0x1c00380e,0x700e380,0xe0f1e03, 0xc0f00078,0x70000e,0x1c000,0x0,0xfff8003e,0x0,0x3c000000,0x10,0x20000,0xc6000,0xf0000000,0x7038003,0xe0000f00,0xf8001f0, 0x3801c,0x18300,0xe1800000,0x0,0x3,0xc187818,0x70f,0x9e03e000,0x7801dc0,0x1c,0x3cc401,0xc000efb8,0x7f7f0038,0x3f0000,0x1ce11c, 0x300c01c3,0x80000000,0x38c638,0x3fc,0x1c0003,0x80000600,0x380e,0xff8c000,0x0,0xc00300c,0xe1c0060,0xc0010,0x70000700,0x79e00f3, 0xc01e7803,0xcf0079e0,0xe1c038e,0x1c0001c,0x38000,0x70000e00,0xe0001,0xc0003800,0x7003803,0x873870e0,0x71c00e3,0x801c7003, 0x8e0070e0,0x38381dc7,0x70e00e,0x1c01c380,0x38077007,0xf0e700,0x1c0f0381,0xe0703c0e,0x781c0f0,0x381e083e,0x787c0c1e,0xf03c1e0, 0x783c0f07,0x800e0001,0xc0003800,0x7000fff,0x3e1c078,0x3c0f0781,0xe0f03c1e,0x783c000,0x1e0f03,0x80e0701c,0xe0381c0,0x70380f07, 0xc1e0e03c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1,0x8701c600,0x1e0f01e0,0x1, 0xc0000700,0x3dbc0070,0x0,0x0,0x1c03803,0x800e0000,0x1e01fe00,0x70e03ff8,0xe3e0001,0xe007fc07,0x80f003c0,0x78001f,0xc0ffff81, 0xfc0001e0,0x30e1e0e1,0xc07ff81c,0x38038,0x3ffe07ff,0xc1c0003f,0xff801c00,0x381de0,0x1c000e7,0x738e70e1,0xc00e1c03,0xc70038e0, 0x780f8000,0xe007007,0x383838d,0x8e00f800,0x7f0000e0,0xe00038,0xe000,0x0,0x200,0xf0780e07,0x8041c078,0x380e0038,0xe03c1e, 0xf001c00,0x1c0071e,0x1c0070,0xe1c783c0,0xe0381e03,0x8380f00f,0xe0000,0x1c00380e,0x381c380,0xe07bc01,0xc0e00078,0x70000e, 0x1c000,0x0,0x1c000061,0x0,0x38000000,0x10,0x20000,0x7c000,0x7c000000,0x703fc06,0x10000e00,0x18400308,0x1801c,0x1c381,0xc3800000, 0x0,0x0,0x7000,0xe0f,0xe061000,0x7801fc0,0x1c,0x38c001,0xc0007ff0,0x7fff0038,0x77c000,0x19c00c,0x301c0387,0x0,0x30c618,0xf0, 0x1c0007,0x600,0x380e,0x7f8c007,0x80000000,0xc001818,0x70e03fc,0x387f871f,0xe0e00700,0x70e00e1,0xc01c3803,0x870070e0,0xe1c038f, 0xe1c0001f,0xff03ffe0,0x7ffc0fff,0x800e0001,0xc0003800,0x7003803,0x873870e0,0x71c00e3,0x801c7003,0x8e007070,0x703839c7,0x70e00e, 0x1c01c380,0x3807f007,0x70e700,0x10078200,0xf0401e08,0x3c10078,0x200f001c,0x3878041c,0x70380e0,0x701c0e03,0x800e0001,0xc0003800, 0x7001e0f,0x3c1e070,0x1c0e0381,0xc070380e,0x701c000,0x1c0f03,0x80e0701c,0xe0381c0,0x701c0e07,0x80e07038,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x3,0x8600e600,0x7803f0,0x1,0xc0000700,0x718e0070,0x0,0x0,0x38038c3, 0x800e0000,0x3c01f800,0x60e03ffc,0xeff8001,0xc001f003,0xc1f003c0,0x7800fe,0xffff80,0x3f8003c0,0x60c0e0e1,0xc07fe01c,0x38038, 0x3ffe07ff,0xc1c07e3f,0xff801c00,0x381fe0,0x1c000e3,0x638e30e1,0xc00e1c07,0x870038ff,0xf00ff800,0xe007007,0x38381cd,0x9c007000, 0x3e0001e0,0xe0001c,0xe000,0x0,0x0,0x70780f0f,0x3c078,0x70070038,0x1e03c1c,0x7001c00,0x1c0073c,0x1c0070,0xe1c701c1,0xe03c1e03, 0xc780f00f,0xe0000,0x1c00380e,0x381c387,0xe03f801,0xc0e000f0,0x70000e,0x1c007,0xe0100000,0x1c0000cd,0x80000003,0xffc00000, 0x3ff,0x807ff000,0xe0,0x7fc00060,0x703fc0c,0xd8001e00,0x3360066c,0x1c018,0xc181,0x83000000,0x0,0x0,0x7000,0x300e07,0xe0cd800, 0xf000f80,0x1c,0x78c00f,0xff0038e0,0x3e00038,0xe1e000,0x19800c,0x383c070e,0x7fffc00,0x30fc18,0x0,0xffff80e,0x20e00,0x380e, 0x7f8c007,0x80000000,0xc001c38,0x38703ff,0xf87fff0f,0xcfe00f00,0x70e00e1,0xc01c3803,0x870070e0,0x1e1e078f,0xe1c0001f,0xff03ffe0, 0x7ffc0fff,0x800e0001,0xc0003800,0x700ff83,0x871870e0,0x71c00e3,0x801c7003,0x8e007038,0xe03871c7,0x70e00e,0x1c01c380,0x3803e007, 0x70e700,0x38000,0x70000e00,0x1c00038,0x7001c,0x38f00038,0x3870070,0xe00e1c01,0xc00e0001,0xc0003800,0x7001c07,0x380e0f0,0x1e1e03c3, 0xc078780f,0xf01e000,0x3c0f03,0x80e0701c,0xe0381c0,0x701c0e07,0x80f07038,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x3,0x8600ff00,0x1e00778,0x38000001,0xc0000700,0x21843fff,0xe0000000,0x0,0x38039e3,0x800e0000, 0x7c01fe00,0xe0e0203e,0xeffc001,0xc00ffe03,0xff700000,0x7f0,0x0,0x7f00380,0x618060e1,0xc07ffc1c,0x38038,0x3ffe07ff,0xc1c07e3f, 0xff801c00,0x381ff0,0x1c000e3,0x638e38e1,0xc00e1fff,0x870038ff,0xc003fe00,0xe007007,0x38381cd,0x9c00f800,0x3e0003c0,0xe0001c, 0xe000,0x0,0x0,0x7070070e,0x38038,0x70070038,0x1c01c1c,0x7001c00,0x1c00778,0x1c0070,0xe1c701c1,0xc01c1c01,0xc700700e,0xfc000, 0x1c00380e,0x381c3c7,0x1e01f001,0xe1e001e0,0xf0000e,0x1e01f,0xf8300000,0x1c00019c,0xc0000003,0xffc00000,0x10,0x20000,0x700, 0x1ff000c0,0x703fc19,0xcc003c00,0x67300ce6,0xc038,0xc181,0x83000000,0x0,0x0,0x7e00,0x180e07,0xe19cc00,0x1e000f80,0x1c,0x70c00f, 0xff007070,0x3e00038,0xe0f000,0x19800c,0x1fec0e1c,0x7fffc00,0x30f818,0x0,0xffff81f,0xf003fc00,0x380e,0x3f8c007,0x80000000, 0x7f800ff0,0x1c3803f,0xe007fc00,0xff800e00,0x70e00e1,0xc01c3803,0x870070e0,0x1c0e070f,0xe1c0001f,0xff03ffe0,0x7ffc0fff,0x800e0001, 0xc0003800,0x700ff83,0x871c70e0,0x71c00e3,0x801c7003,0x8e00701d,0xc038e1c7,0x70e00e,0x1c01c380,0x3803e007,0x70e3c0,0x38000, 0x70000e00,0x1c00038,0x7001c,0x38e00038,0x3870070,0xe00e1c01,0xc00e0001,0xc0003800,0x7003c07,0x8380e0e0,0xe1c01c3,0x80387007, 0xe00e1ff,0xfe381b83,0x80e0701c,0xe0381c0,0x701e1e07,0x707878,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x1c,0x3,0xe007fe0,0x7800e3c,0x38000001,0xc0000700,0x1803fff,0xe0000000,0x0,0x70039c3,0x800e0000,0xf8000f80, 0xc0e0000e,0xf83c003,0xc01e0f01,0xff700000,0x7c0,0x0,0x1f00780,0x618061c0,0xe0701e1c,0x38038,0x38000700,0x1c07e38,0x3801c00, 0x381e78,0x1c000e3,0xe38e18e1,0xc00e1fff,0x70038ff,0xe0007f80,0xe007007,0x1c701dd,0x9c00f800,0x1c000780,0xe0000e,0xe000,0x0, 0x7f,0xf070070e,0x38038,0x7fff0038,0x1c01c1c,0x7001c00,0x1c007f8,0x1c0070,0xe1c701c1,0xc01c1c01,0xc700700e,0x7fc00,0x1c00380e, 0x1c381c7,0x1c01f000,0xe1c001c0,0xfe0000e,0xfe1f,0xfff00000,0x7ff003fc,0xe0000003,0xffc00000,0x10,0x20000,0x3800,0x3fc0180, 0x703803f,0xce007800,0xff381fe7,0x30,0x0,0xc0,0x0,0x0,0x3fe0,0xc0e07,0xfe3fce00,0x1c000700,0x1c,0x70c00f,0xff006030,0x1c00000, 0xe07800,0x19800c,0xfcc1c38,0x7fffc00,0x30d818,0x0,0xffff81f,0xf001f800,0x380e,0xf8c007,0x80000000,0x7f8007e0,0xe1c3fe,0x7fc00f, 0xf8001e00,0xe0701c0,0xe0381c07,0x380e070,0x1c0e070e,0x1c0001c,0x38000,0x70000e00,0xe0001,0xc0003800,0x700ff83,0x870c70e0, 0x71c00e3,0x801c7003,0x8e00700f,0x8038c1c7,0x70e00e,0x1c01c380,0x3801c007,0xf0e3e0,0x3ff807f,0xf00ffe01,0xffc03ff8,0x7ff03ff, 0xf8e0003f,0xff87fff0,0xfffe1fff,0xc00e0001,0xc0003800,0x7003803,0x8380e0e0,0xe1c01c3,0x80387007,0xe00e1ff,0xfe383383,0x80e0701c, 0xe0381c0,0x700e1c07,0x703870,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x3,0xc000ff0, 0x3c1e1c1c,0x38000001,0xc0000700,0x1803fff,0xe0000007,0xf8000000,0x7003803,0x800e0001,0xf0000381,0xc0e00007,0xf01e003,0x801c0700, 0x7c700000,0x7c0,0x0,0x1f00700,0x618061c0,0xe0700e1c,0x38038,0x38000700,0x1c00e38,0x3801c00,0x381e38,0x1c000e1,0xc38e1ce1, 0xc00e1ffc,0x70038e0,0xf0000780,0xe007007,0x1c701dd,0xdc01fc00,0x1c000780,0xe0000e,0xe000,0x0,0x1ff,0xf070070e,0x38038,0x7fff0038, 0x1c01c1c,0x7001c00,0x1c007f8,0x1c0070,0xe1c701c1,0xc01c1c01,0xc700700e,0x3ff00,0x1c00380e,0x1c381cd,0x9c00e000,0xe1c003c0, 0xf80000e,0x3e18,0x3ff00000,0xffe007fd,0xf0000000,0x38000000,0x10,0x20000,0x1c000,0x3c0300,0x703807f,0xdf007801,0xff7c3fef, 0x80000000,0x0,0x3e0,0x7ffe7ff,0xff000000,0x1ff8,0x60e07,0xfe7fdf00,0x3c000700,0x1c,0x70c001,0xc0006030,0x7fff0000,0xf03800, 0x19800c,0x1c38,0x1c07,0xf830cc18,0x0,0x1c0000,0x0,0x380e,0x18c007,0x80000000,0x0,0xe1cfe0,0x1fc003f,0x80003c00,0xe0701c0, 0xe0381c07,0x380e070,0x1c0e070e,0x1c0001c,0x38000,0x70000e00,0xe0001,0xc0003800,0x7003803,0x870e70e0,0x71c00e3,0x801c7003, 0x8e007007,0x3981c7,0x70e00e,0x1c01c380,0x3801c007,0x1e0e0f8,0xfff81ff,0xf03ffe07,0xffc0fff8,0x1fff07ff,0xf8e0003f,0xff87fff0, 0xfffe1fff,0xc00e0001,0xc0003800,0x7003803,0x8380e0e0,0xe1c01c3,0x80387007,0xe00e1ff,0xfe386383,0x80e0701c,0xe0381c0,0x700e1c07, 0x703870,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x7f,0xffc00678,0x707f9c1e,0x38000001, 0xc0000700,0x70,0x7,0xf8000000,0xe003803,0x800e0003,0xe00001c3,0x80e00007,0xe00e007,0x80380380,0x700000,0x7f0,0x0,0x7f00700, 0x618061ff,0xe070071c,0x38038,0x38000700,0x1c00e38,0x3801c00,0x381c3c,0x1c000e1,0xc38e1ce1,0xc00e1c00,0x70038e0,0x700003c0, 0xe007007,0x1c701d8,0xdc03dc00,0x1c000f00,0xe00007,0xe000,0x0,0x3ff,0xf070070e,0x38038,0x7fff0038,0x1c01c1c,0x7001c00,0x1c007fc, 0x1c0070,0xe1c701c1,0xc01c1c01,0xc700700e,0x3f00,0x1c00380e,0x1c381cd,0x9c01f000,0x73800780,0xfe0000e,0xfe10,0x7c00000,0x1c000ffb, 0xf8000000,0x38000000,0x10,0x20000,0x20000,0x1e0700,0x70380ff,0xbf80f003,0xfefe7fdf,0xc0000000,0x0,0x3f0,0x7ffe7ff,0xff000000, 0x1f8,0x30e07,0xfeffbf80,0x78000700,0x1c,0x70c001,0xc0006030,0x7fff0000,0x783800,0x1ce11c,0xe1c,0x1c07,0xf838ce38,0x0,0x1c0000, 0x0,0x380e,0x18c000,0x0,0x0,0x1c38c00,0x1800030,0x7800,0xfff01ff,0xe03ffc07,0xff80fff0,0x3fff0ffe,0x1c0001c,0x38000,0x70000e00, 0xe0001,0xc0003800,0x7003803,0x870e70e0,0x71c00e3,0x801c7003,0x8e00700f,0x803b81c7,0x70e00e,0x1c01c380,0x3801c007,0xffe0e03c, 0x1fff83ff,0xf07ffe0f,0xffc1fff8,0x3fff0fff,0xf8e0003f,0xff87fff0,0xfffe1fff,0xc00e0001,0xc0003800,0x7003803,0x8380e0e0,0xe1c01c3, 0x80387007,0xe00e000,0x38c383,0x80e0701c,0xe0381c0,0x70073807,0x701ce0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0xffc0063c,0x40619c0f,0x30000001,0xc0000700,0x70,0x7,0xf8000000,0xe003803,0x800e0007,0xc00001c3, 0xfffc0007,0xe00e007,0x380380,0xf00000,0xfe,0xffff80,0x3f800700,0x618063ff,0xf070071c,0x38038,0x38000700,0x1c00e38,0x3801c00, 0x381c1e,0x1c000e0,0x38e0ee1,0xc00e1c00,0x70038e0,0x380001c0,0xe007007,0x1ef01d8,0xdc038e00,0x1c001e00,0xe00007,0xe000,0x0, 0x7c0,0x7070070e,0x38038,0x70000038,0x1c01c1c,0x7001c00,0x1c0079e,0x1c0070,0xe1c701c1,0xc01c1c01,0xc700700e,0x780,0x1c00380e, 0xe701cd,0x9c01f000,0x73800f00,0xe0000e,0xe000,0x0,0x1c0007f7,0xf0000000,0x70000000,0x10,0x20000,0x0,0xe0e00,0x703807f,0x7f01e001, 0xfdfc3fbf,0x80000000,0x0,0x7f0,0x0,0x0,0x3c,0x18e07,0x7f7f00,0xf0000700,0x1c,0x70c001,0xc0007070,0x1c00000,0x3e7000,0xcff18, 0x3ffc070e,0x1c07,0xf818c630,0x0,0x1c0000,0x0,0x380e,0x18c000,0x0,0x3ffc,0x3870000,0xe000fc00,0x380f000,0x1fff83ff,0xf07ffe0f, 0xffc1fff8,0x3fff0ffe,0x1c0001c,0x38000,0x70000e00,0xe0001,0xc0003800,0x7003803,0x870770e0,0x71c00e3,0x801c7003,0x8e00701d, 0xc03f01c7,0x70e00e,0x1c01c380,0x3801c007,0xffc0e01c,0x3e0387c0,0x70f80e1f,0x1c3e038,0x7c071e1c,0xe00038,0x70000,0xe0001c00, 0xe0001,0xc0003800,0x7003803,0x8380e0e0,0xe1c01c3,0x80387007,0xe00e000,0x398383,0x80e0701c,0xe0381c0,0x70073807,0x701ce0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0xffc0061c,0xc0dc07,0xf0000001,0xc0000700, 0x70,0x0,0x0,0x1c003c07,0x800e000f,0x1c3,0xfffc0007,0xe00e007,0x380380,0xe00000,0x1f,0xc0ffff81,0xfc000700,0x618063ff,0xf070070e, 0x38070,0x38000700,0xe00e38,0x3801c00,0x381c0e,0x1c000e0,0x38e0ee1,0xe01e1c00,0x78078e0,0x380001c0,0xe007007,0xee01f8,0xfc078f00, 0x1c001c00,0xe00003,0x8000e000,0x0,0x700,0x7070070e,0x38038,0x70000038,0x1c01c1c,0x7001c00,0x1c0070e,0x1c0070,0xe1c701c1, 0xc01c1c01,0xc700700e,0x380,0x1c00380e,0xe700ed,0xb803f800,0x77800f00,0x70000e,0x1c000,0x0,0xe0003f7,0xe0000000,0x70000000, 0x10,0x20000,0x1c0e0,0xe1c00,0x703803f,0x7e01c000,0xfdf81fbf,0x0,0x0,0x3f0,0x0,0x0,0x1c,0x1ce07,0x3f7e00,0xf0000700,0x1c, 0x70c001,0xc00038e0,0x1c00038,0xf7000,0xe3e38,0x3ffc0387,0x1c00,0x1cc770,0x0,0x1c0000,0x0,0x380e,0x18c000,0x0,0x3ffc,0x70e0001, 0xe001fe00,0x780e000,0x1fff83ff,0xf07ffe0f,0xffc1fff8,0x3fff0ffe,0xe0001c,0x38000,0x70000e00,0xe0001,0xc0003800,0x7003807, 0x70770f0,0xf1e01e3,0xc03c7807,0x8f00f038,0xe03e03c7,0x70e00e,0x1c01c380,0x3801c007,0xff00e00e,0x38038700,0x70e00e1c,0x1c38038, 0x70071c1c,0xe00038,0x70000,0xe0001c00,0xe0001,0xc0003800,0x7003803,0x8380e0e0,0xe1c01c3,0x80387007,0xe00e000,0x3b0383,0x80e0701c, 0xe0381c0,0x70077807,0x701de0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x1c00061c, 0xc0de03,0xe0000001,0xc0000700,0x70,0x0,0x0,0x1c001c07,0xe001e,0x1c3,0xfffc0007,0x600e00e,0x380380,0xe00000,0x7,0xf0ffff87, 0xf0000000,0x60c0e380,0x7070070e,0x38070,0x38000700,0xe00e38,0x3801c00,0x381c0f,0x1c000e0,0x38e06e0,0xe01c1c00,0x38070e0, 0x1c0001c0,0xe007007,0xee00f8,0xf80f0700,0x1c003c00,0xe00003,0x8000e000,0x0,0x700,0x70780f0f,0x3c078,0x70000038,0x1e03c1c, 0x7001c00,0x1c0070f,0x1c0070,0xe1c701c1,0xe03c1e03,0xc780f00e,0x380,0x1c00380e,0xe700f8,0xf807bc00,0x3f001e00,0x70000e,0x1c000, 0x0,0xe0001ff,0xc0000000,0x70000000,0x10,0x20000,0x33110,0xe0e00,0x383801f,0xfc03c000,0x7ff00ffe,0x0,0x0,0x3e0,0x0,0x0,0x1c, 0x38e07,0x1ffc01,0xe0000700,0x1c,0x78c001,0xc0007ff0,0x1c00038,0x7c000,0x70070,0x1c3,0x80001c00,0xe00e0,0x0,0x1c0000,0x0, 0x380e,0x18c000,0x0,0x0,0xe1c0001,0xe0010700,0x780e000,0x1c038380,0x70700e0e,0x1c1c038,0x78070e0e,0xe0001c,0x38000,0x70000e00, 0xe0001,0xc0003800,0x7003807,0x7037070,0xe0e01c1,0xc0383807,0x700e070,0x701c0387,0x70e00e,0x1c01c380,0x3801c007,0xe00e,0x38038700, 0x70e00e1c,0x1c38038,0x70071c1c,0xf00038,0x70000,0xe0001c00,0xe0001,0xc0003800,0x7003c07,0x8380e0f0,0x1e1e03c3,0xc078780f, 0xf01e007,0x803e0783,0x80e0701c,0xe0381c0,0x7003f007,0x80f00fc0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x6,0x1800061c,0xc0de01,0xc0000000,0xc0000e00,0x70,0xf0000,0x3c00,0x38001c0f,0xe003c,0x3c0,0xe0000e,0x701e00e, 0x3c0780,0x1e003c0,0x780000,0xfc00001f,0x80000000,0x60e1e780,0x78700f07,0x4380f0,0x38000700,0xf00e38,0x3801c00,0xc0781c07, 0x81c000e0,0x38e07e0,0xe03c1c00,0x380f0e0,0x1e0003c0,0xe00780f,0xee00f0,0x780e0780,0x1c007800,0xe00001,0xc000e000,0x0,0x700, 0xf0780e07,0x8041c078,0x38020038,0xe03c1c,0x7001c00,0x1c00707,0x801c0070,0xe1c701c0,0xe0381e03,0x8380f00e,0x80380,0x1c003c1e, 0x7e00f8,0xf80f1e00,0x3f003c00,0x70000e,0x1c000,0x0,0xf0100f7,0x80078000,0x700078f0,0x10,0x7ff000,0x61208,0x1e0700,0x383800f, 0x78078000,0x3de007bc,0x0,0x0,0x0,0x0,0x0,0x401c,0x70e0f,0xf7803,0xc0000700,0x1c,0x38c001,0xc000efb8,0x1c00038,0x1e000,0x3c1e0, 0xc1,0x80000000,0x783c0,0x0,0x0,0x0,0x3c1e,0x18c000,0x0,0x0,0xc180003,0x60000300,0xd80e010,0x3c03c780,0x78f00f1e,0x1e3c03c, 0x70039c0e,0x70041c,0x38000,0x70000e00,0xe0001,0xc0003800,0x700380f,0x703f070,0x1e0e03c1,0xc078380f,0x701e0e0,0x381c0787, 0x80f0f01e,0x1e03c3c0,0x7801c007,0xe00e,0x38078700,0xf0e01e1c,0x3c38078,0x700f1c1c,0x78041c,0x1038020,0x70040e00,0x800e0001, 0xc0003800,0x7001c07,0x380e070,0x1c0e0381,0xc070380e,0x701c007,0x801e0703,0xc1e0783c,0xf0781e0,0xf003f007,0x80e00fc0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0xe,0x1801867c,0xc0cf83,0xe0000000,0xe0000e00, 0x70,0xf0000,0x3c00,0x38000f1e,0xe0070,0x180780,0xe0603e,0x783c01e,0x1e0f01,0x7c003c0,0x780000,0x3c00001e,0x700,0x307fe700, 0x38701e07,0xc1c383e0,0x38000700,0x7c1e38,0x3801c00,0xe0f01c03,0x81c000e0,0x38e03e0,0x78781c00,0x1e1e0e0,0xe180780,0xe003c1e, 0x7c00f0,0x781e03c0,0x1c007000,0xe00001,0xc000e000,0x0,0x783,0xf07c1e07,0xc0c1e0f8,0x3e0e0038,0xf07c1c,0x7001c00,0x1c00703, 0xc01e0070,0xe1c701c0,0xf0781f07,0x83c1f00e,0xe0f80,0x1e003c3e,0x7e00f8,0xf80e0e00,0x3f003800,0x70000e,0x1c000,0x0,0x7830077, 0xf0000,0x700078f0,0x10,0x20000,0x41208,0xc03c0380,0x3c38007,0x70070000,0x1dc003b8,0x0,0x0,0x0,0x0,0x0,0x707c,0x6070f,0x86077003, 0x80000700,0x1c,0x3ec401,0xc001c01c,0x1c00038,0xf000,0x1ffc0,0x40,0x80000000,0x3ff80,0x0,0x0,0x0,0x3e3e,0x18c000,0x0,0x0, 0x8100006,0x60000300,0x1980f070,0x3801c700,0x38e0071c,0xe3801c,0x70039c0e,0x7c1c1c,0x38000,0x70000e00,0xe0001,0xc0003800, 0x700383e,0x701f03c,0x3c078780,0xf0f01e1e,0x3c3c1c0,0x1c3f0f03,0xc1e0783c,0xf0781e0,0xf001c007,0xe81e,0x3c1f8783,0xf0f07e1e, 0xfc3c1f8,0x783f1e3e,0x187c0c1f,0x703e0e0,0x7c1c0f83,0x800e0001,0xc0003800,0x7001e0f,0x380e078,0x3c0f0781,0xe0f03c1e,0x783c007, 0x801e0f03,0xc3e0787c,0xf0f81e1,0xf003f007,0xc1e00fc0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x1c,0xe,0x3801fff8,0x6187ff,0xe0000000,0xe0000e00,0x70,0xf0000,0x3c00,0x38000ffe,0x1fff0ff,0xfe1fff80,0xe07ffc,0x3ffc01c, 0x1fff01,0xff8003c0,0x780000,0x4000010,0x700,0x301e6700,0x387ffe03,0xffc3ffc0,0x3fff0700,0x3ffe38,0x383ffe0,0xfff01c03,0xc1fff8e0, 0x38e03e0,0x7ff81c00,0x1ffe0e0,0xf1fff80,0xe003ffe,0x7c00f0,0x781c01c0,0x1c00ffff,0xe00001,0xc000e000,0x0,0x3ff,0x707ffc03, 0xffc0fff8,0x1ffe0038,0x7ffc1c,0x707fff0,0x1c00701,0xc00ff070,0xe1c701c0,0x7ff01fff,0x1fff00e,0xfff00,0xff81fee,0x7e00f0, 0x781e0f00,0x1e007ffc,0x70000e,0x1c000,0x0,0x3ff003e,0xf0000,0xe00070e0,0x60830010,0x20000,0x41208,0xfffc01c0,0x1fffe03,0xe00ffff0, 0xf8001f0,0x0,0x0,0x0,0x0,0x0,0x7ff8,0xc07fd,0xfe03e007,0xffc00700,0x1c,0x1ffc1f,0xffc08008,0x1c00038,0x7000,0x7f00,0x0,0x0, 0xfe00,0x0,0xffff800,0x0,0x3ff7,0x8018c000,0x0,0x0,0x6,0x60000700,0x19807ff0,0x3801c700,0x38e0071c,0xe3801c,0x70039c0f,0xf03ffc1f, 0xff83fff0,0x7ffe0fff,0xc1fff03f,0xfe07ffc0,0xfff83ffc,0x701f03f,0xfc07ff80,0xfff01ffe,0x3ffc080,0x83fff03,0xffe07ffc,0xfff81ff, 0xf001c007,0xeffc,0x1ffb83ff,0x707fee0f,0xfdc1ffb8,0x3ff70ff7,0xf83ffc0f,0xff01ffe0,0x3ffc07ff,0x83fff87f,0xff0fffe1,0xfffc0ffe, 0x380e03f,0xf807ff00,0xffe01ffc,0x3ff8007,0x803ffe01,0xfee03fdc,0x7fb80ff,0x7001e007,0xffc00780,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0xc,0x3801fff0,0x7f83fe,0x70000000,0xe0000e00,0x0,0xf0000,0x3c00,0x700007fc, 0x1fff0ff,0xfe1ffe00,0xe07ff8,0x1ff801c,0xffe01,0xff0003c0,0x780000,0x0,0x700,0x38000f00,0x3c7ffc01,0xff83ff80,0x3fff0700, 0x1ffc38,0x383ffe0,0x7fe01c01,0xe1fff8e0,0x38e03e0,0x3ff01c00,0xffc0e0,0x71fff00,0xe001ffc,0x7c00f0,0x783c01e0,0x1c00ffff, 0xe00000,0xe000e000,0x0,0x1ff,0x7077f801,0xff807fb8,0xffc0038,0x3fdc1c,0x707fff0,0x1c00701,0xe007f070,0xe1c701c0,0x3fe01dfe, 0xff700e,0x7fe00,0xff80fee,0x3c0070,0x703c0780,0x1e007ffc,0x70000e,0x1c000,0x0,0x1fe001c,0xe0000,0xe000e1c0,0x71c78010,0x20000, 0x21318,0xfff800c0,0xfffe01,0xc00ffff0,0x70000e0,0x0,0x0,0x0,0x0,0x0,0x3ff0,0x1803fd,0xfe01c007,0xffc00700,0x1c,0xffc1f,0xffc00000, 0x1c00038,0x7000,0x0,0x0,0x0,0x0,0x0,0xffff800,0x0,0x3ff7,0x8018c000,0x0,0x0,0xc,0x60000e00,0x31803fe0,0x7801ef00,0x3de007bc, 0xf7801e,0xf003fc0f,0xf01ff81f,0xff83fff0,0x7ffe0fff,0xc1fff03f,0xfe07ffc0,0xfff83ff8,0x701f01f,0xf803ff00,0x7fe00ffc,0x1ff8000, 0x67fe01,0xffc03ff8,0x7ff00ff,0xe001c007,0xeff8,0xffb81ff,0x703fee07,0xfdc0ffb8,0x1ff70ff7,0xf81ff807,0xfe00ffc0,0x1ff803ff, 0x3fff87f,0xff0fffe1,0xfffc07fc,0x380e01f,0xf003fe00,0x7fc00ff8,0x1ff0000,0x37fc00,0xfee01fdc,0x3fb807f,0x7001e007,0x7f800780, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0xc,0x30007fc0,0x1e00f8,0x78000000,0x70001c00, 0x0,0xe0000,0x3c00,0x700001f0,0x1fff0ff,0xfe07f800,0xe01fe0,0x7e0038,0x3f800,0xfc0003c0,0x700000,0x0,0x700,0x18000e00,0x1c7ff000, 0x7e03fe00,0x3fff0700,0x7f038,0x383ffe0,0x1f801c00,0xf1fff8e0,0x38e01e0,0xfc01c00,0x3f80e0,0x787fc00,0xe0007f0,0x7c00f0,0x387800f0, 0x1c00ffff,0xe00000,0xe000e000,0x0,0xfc,0x7071f000,0x3f003e38,0x3f00038,0x1f1c1c,0x707fff0,0x1c00700,0xf003f070,0xe1c701c0, 0x1f801c7c,0x7c700e,0x1f800,0x3f8078e,0x3c0070,0x707803c0,0x1c007ffc,0x70000e,0x1c000,0x0,0x7c0008,0x1e0000,0xe000e1c0,0x71c30010, 0x20000,0x1e1f0,0x3fe00020,0x3ffe00,0x800ffff0,0x2000040,0x0,0x0,0x0,0x0,0x0,0xfc0,0x3001f0,0x78008007,0xffc00700,0x1c,0x3f81f, 0xffc00000,0x1c00038,0x407000,0x0,0x0,0x0,0x0,0x0,0xffff800,0x0,0x39c7,0x18c000,0x0,0x0,0x18,0x60001c00,0x61801f80,0x7000ee00, 0x1dc003b8,0x77000e,0xe001f80f,0xf007e01f,0xff83fff0,0x7ffe0fff,0xc1fff03f,0xfe07ffc0,0xfff83fc0,0x700f007,0xe000fc00,0x1f8003f0, 0x7e0000,0xe1f800,0x7f000fe0,0x1fc003f,0x8001c007,0xe7f0,0x7e380fc,0x701f8e03,0xf1c07e38,0xfc703c1,0xe003f001,0xf8003f00, 0x7e000fc,0x3fff87f,0xff0fffe1,0xfffc03f8,0x380e00f,0xc001f800,0x3f0007e0,0xfc0000,0x61f800,0x78e00f1c,0x1e3803c,0x7001c007, 0x1f000700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x70001c00,0x0, 0x1c0000,0x0,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0xe00000,0x0,0x0,0xc000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0, 0x0,0x0,0x0,0x0,0xe00000,0x7000e000,0x0,0x0,0x0,0x0,0x0,0x1c00,0x0,0x1c00000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x0,0x1c000000, 0x70000e,0x1c000,0x0,0x0,0x1c0000,0xe000c180,0x10,0x20000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc000, 0x0,0x38,0x70e000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x18c000,0x2000,0x0,0x1f,0xf8003800,0x7fe00000,0x0,0x0,0x0,0x0,0x4000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x400000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x400000, 0x0,0x0,0x1c007,0x700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x30001800, 0x0,0x1c0000,0x0,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0xe00000,0x0,0x0,0xe000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e000, 0x0,0x0,0x0,0x0,0x0,0xe00000,0x7000e000,0x0,0x0,0x0,0x0,0x0,0x1c00,0x0,0x1c00000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x0,0x1c000000, 0x70000e,0x1c000,0x0,0x0,0x1c0001,0xe001c380,0x10,0x20000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc000, 0x0,0x38,0x7fe000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x18c000,0x3000,0x0,0x1f,0xf8007000,0x7fe00000,0x0,0x0,0x0,0x0,0x6000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x1c007,0x700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x38003800, 0x0,0x380000,0x1,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x1c00000,0x0,0x0,0x3c18000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf000, 0x0,0x0,0x0,0x0,0x0,0xfe0000,0x380fe000,0x0,0x0,0x0,0x0,0x0,0x3800,0x0,0x1c00000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x0,0x38000000, 0x78000e,0x3c000,0x0,0x0,0x180001,0xc0018300,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc000,0x0, 0x38,0x1f8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x18c000,0x1800,0x0,0x0,0x6000e000,0x1800000,0x0,0x0,0x0,0x0,0x3000,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x38007,0xe00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x18003000, 0x0,0x300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1800000,0x0,0x0,0x1ff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4000,0x0,0x0, 0x0,0x0,0x0,0xfe0000,0xfe000,0x0,0x0,0x0,0x0,0x0,0x607800,0x0,0x3c00000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x0,0x78000000, 0x3f800e,0x3f8000,0x0,0x0,0x300043,0xc0018200,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc000, 0x0,0x38,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x0,0x11800,0x0,0x0,0x6001ff00,0x1800000,0x0,0x0,0x0,0x0,0x23000,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78007, 0x1e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x1c007000,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe0000, 0xfe000,0x0,0x0,0x0,0x0,0x0,0x7ff000,0x0,0x7f800000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x3,0xf8000000,0x3f800e,0x3f8000,0x0, 0x0,0x10007f,0x80000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc000,0x0,0x38,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x3800,0x0,0x1f800,0x0,0x0,0x6001ff00,0x1800000,0x0,0x0,0x0,0x0,0x3f000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f8007,0xfe00,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7fff8,0x0,0x0,0x0,0x0,0x7fe000,0x0, 0x7f000000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x3,0xf0000000,0xf800e,0x3e0000,0x0,0x0,0x7f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x0,0x1f000,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x3e000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e000,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x3f0007,0xfc00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x7fff8,0x0,0x0,0x0,0x0,0x1fc000,0x0,0x7e000000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x3,0xc0000000,0xe,0x0, 0x0,0x0,0x3e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x3800,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c0007,0xf000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7fff8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 }; // Definition of a 29x57 font. const unsigned int font29x57[29*57*256/32] = { 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x781e00,0x0,0x0,0x7,0x81e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c0000,0xf8000,0x7e00000,0x0,0x7, 0xc0000000,0x0,0x7c00,0xf80,0x7e000,0x0,0x7c00000,0xf80000,0x7e000000,0x0,0x0,0x1f00,0x3e0,0x1f800,0x0,0x0,0x0,0x3,0xe0000000, 0x7c00003f,0x0,0xf8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x3c3c00,0x0,0x0,0x3,0xc3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e0000, 0x1f0000,0x7e00000,0xf838001f,0xf80001f,0xf0000000,0x0,0x3e00,0x1f00,0x7e000,0x3e1f000,0x3e00000,0x1f00000,0x7e00003e,0x1f000000, 0x3e0,0xe0000f80,0x7c0,0x1f800,0x3e0e00,0x7c3e000,0x0,0x1,0xf0000000,0xf800003f,0x1f0f,0x800001f0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e7800,0x0,0x0, 0x1,0xe7800000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e0000,0x1e0000,0xff00001,0xfe38001f,0xf80003f, 0xf8000000,0x0,0x1e00,0x1e00,0xff000,0x3e1f000,0x1e00000,0x1e00000,0xff00003e,0x1f000000,0x7f8,0xe0000780,0x780,0x3fc00,0x7f8e00, 0x7c3e000,0x0,0x0,0xf0000000,0xf000007f,0x80001f0f,0x800001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xef000,0x0,0x0,0x0,0xef000000,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0000,0x3c0000,0x1e780003,0xfff8001f,0xf80003c,0x78000000,0x0,0xf00,0x3c00,0x1e7800, 0x3e1f000,0xf00000,0x3c00001,0xe780003e,0x1f000000,0xfff,0xe00003c0,0xf00,0x79e00,0xfffe00,0x7c3e000,0x0,0x0,0x78000001,0xe00000f3, 0xc0001f0f,0x800003c0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e000,0x0,0x0,0x0,0x7e000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x78000,0x780000,0x3c3c0003,0x8ff0001f,0xf800078,0x3c000000,0x0,0x780,0x7800,0x3c3c00,0x3e1f000,0x780000,0x7800003,0xc3c0003e, 0x1f000000,0xe3f,0xc00001e0,0x1e00,0xf0f00,0xe3fc00,0x7c3e000,0x0,0x0,0x3c000003,0xc00001e1,0xe0001f0f,0x80000780,0x0,0x0, 0x0,0x0,0x0,0x0,0x1f,0xf0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x7e000,0x0,0x0,0x0,0x7e000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc00,0x7e000,0xfe000,0x0,0x3c000,0xf00000,0x781e0003, 0x83e0001f,0xf800070,0x1c000000,0x0,0x3c0,0xf000,0x781e00,0x3e1f000,0x3c0000,0xf000007,0x81e0003e,0x1f000000,0xe0f,0x800000f0, 0x3c00,0x1e0780,0xe0f800,0x7c3e000,0x0,0x0,0x1e000007,0x800003c0,0xf0001f0f,0x80000f00,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf8000000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3fc00,0x1fe000,0x3ff800,0x0,0x0,0x0,0x0,0x0,0x70,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x78000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x78,0xf000000,0x0,0x0,0x780f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c0, 0x0,0x0,0x0,0x0,0x0,0x0,0x3fc00,0x1fe000,0x3ffc00,0x0,0x0,0x0,0x0,0x0,0x70,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f00000,0x3e000,0x3e00000,0x0,0x78,0x3c000000,0x0,0x1f000,0x3e0, 0x3e000,0x0,0x1f000000,0x3e0000,0x3e000000,0x0,0x0,0x7c00,0xf8,0xf800,0x0,0x0,0x0,0xf,0x80000000,0x1f00001f,0x0,0x3e,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x30000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0xf80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x781c0000,0x38,0xe000000,0x0,0x0,0x380e0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80,0x0,0x0,0x0,0x0,0x0,0x0,0x39c00,0x1ce000,0x303e00, 0x0,0x0,0x0,0x0,0x0,0x78,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4000,0x0,0x0,0x0,0x0, 0x0,0x0,0xf80000,0x7c000,0x3e00000,0xf0380000,0x70,0x1c000000,0x0,0xf800,0x7c0,0x3e000,0x0,0xf800000,0x7c0000,0x3e000000, 0x0,0x3c0,0xe0003e00,0x1f0,0xf800,0x3c0e00,0x0,0x0,0x7,0xc0000000,0x3e00001f,0x0,0x7c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0xff,0x0, 0xf8,0xf8000,0x1c000,0x0,0x0,0x0,0x0,0x1f,0xc0000000,0x1ff8,0xff00,0x0,0x0,0x3fe000,0x0,0x1fc00001,0xfe000000,0x0,0x0,0x0, 0x0,0x7f800,0x0,0x0,0x0,0xff00000,0x0,0x0,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xf8000000,0xfe,0x0,0x7f80,0x0,0x0,0x0,0x0,0x0, 0x0,0x3f,0xf0000000,0x7fe0,0x0,0x0,0x780000,0x1,0xe0000000,0x0,0x780000,0x3,0xfe000000,0x78000,0x3c00,0xf000,0x7800003,0xffe00000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc0000f0,0x3f000,0x0,0x0,0x3fc00,0x0,0x0,0x1fc000,0x0,0x0,0x0,0x1fc0, 0x0,0xff000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe1c0000,0x1c,0x1c000000,0x0,0x0,0x1c1c0,0x0,0x0,0x0,0x0,0x1fe0000, 0x0,0x0,0x1ff,0x1f0f8,0x0,0xff000,0x0,0x0,0x0,0x3f,0xff00000f,0x80000000,0xfe0,0x3f80,0xf00,0x0,0x0,0x0,0x1,0xf8000003,0xe0000000, 0x1c00,0xe000,0xe00,0x0,0x0,0x0,0x0,0x0,0x3c,0x78000000,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f0,0x3f80,0x1fc00,0xfe000, 0x7f0000,0x0,0x1fc07000,0x0,0x0,0x0,0x0,0x0,0x3f800,0x780000,0x78000,0x7f00001,0xfc38001f,0xf800070,0x1c000000,0x0,0x7800, 0x780,0x7f000,0x3e1f000,0x7800000,0x780000,0x7f00003e,0x1f0003f0,0x7f0,0xe0001e00,0x1e0,0x1fc00,0x7f0e00,0x7c3e000,0x0,0x3, 0xc0000000,0x3c00003f,0x80001f0f,0x80000078,0x1e0000,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x1e078000,0x30000000,0x3ff,0xc00001e0,0xf0, 0x78000,0x1c000,0x0,0x0,0x0,0x0,0x1e0007f,0xf000007e,0x1ffff,0x7ffe0,0x1f80,0x3ffff80,0xfff803,0xfffff800,0xfff80007,0xff800000, 0x0,0x0,0x0,0x0,0x1ffe00,0x0,0xfe0003,0xfff80000,0x3ffe01ff,0xe00003ff,0xffe01fff,0xff0003ff,0xe01e0007,0x803ffff0,0xfff80, 0x3c000fc0,0x7800001f,0x8003f07e,0x1e000f,0xfe0007ff,0xf00003ff,0x8007ffe0,0x1fff8,0x7fffffe,0xf0003c1,0xe000079e,0xf1f,0x1f3e0, 0x1f01ff,0xfff8003f,0xf003c000,0x7fe0,0x3f00,0x0,0x3c0000,0x1,0xe0000000,0x0,0x780000,0xf,0xfe000000,0x78000,0x3c00,0xf000, 0x7800003,0xffe00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xfc0000f0,0x3fe00,0x0,0x0,0xfff00,0x0,0x0,0x3fe000, 0x0,0x0,0x0,0x1dc0,0x0,0x3fff00,0x0,0x3ffff80,0x1f,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff1c07ff,0x3c0f001e,0x3c000000, 0x0,0x0,0x1e3c0,0xf80007c,0x0,0x780000,0x0,0xfff8000,0x3e00,0x1f00000,0x7ff,0xc001f0f8,0x0,0x3ffc00,0x0,0x0,0x0,0x3f,0xff00003f, 0xe0000000,0x3ff8,0xffe0,0x1e00,0x0,0xfffc00,0x0,0x7,0xf800000f,0xf8000000,0x1c00,0xe000,0xe00,0xf000,0x1fc000,0xfe0000,0x7f00000, 0x3f800001,0xfc00003f,0xf80000ff,0xffc003ff,0xe007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01ffc, 0xfc00,0x3c001ffc,0xffe0,0x7ff00,0x3ff800,0x1ffc000,0x0,0x7ff8f0f0,0x3c0780,0x1e03c00,0xf01e000,0x783e0001,0xf01e0000,0xffe00, 0x3c0000,0xf0000,0x7700001,0xfe38001f,0xf800070,0x1c000000,0x0,0x3c00,0xf00,0x77000,0x3e1f000,0x3c00000,0xf00000,0x7700003e, 0x1f0000f8,0xc0007f8,0xe0000f00,0x3c0,0x1dc00,0x7f8e00,0x7c3e000,0x0,0x1,0xe0000000,0x7800003b,0x80001f0f,0x800000f0,0x1e0000, 0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x780000,0x3c1e0000,0x1e070000,0x300001f0,0x7ff,0xc00001e0,0x1e0,0x7c000,0x1c000,0x0,0x0,0x0,0x0,0x3c000ff,0xf80007fe, 0x3ffff,0x801ffff8,0x1f80,0x3ffff80,0x3fff803,0xfffff801,0xfffc000f,0xffc00000,0x0,0x0,0x0,0x0,0x7fff80,0x0,0xfe0003,0xffff0000, 0xffff01ff,0xfc0003ff,0xffe01fff,0xff000fff,0xf01e0007,0x803ffff0,0xfff80,0x3c001f80,0x7800001f,0xc007f07e,0x1e001f,0xff0007ff, 0xfc0007ff,0xc007fffc,0x3fffc,0x7fffffe,0xf0003c1,0xf0000f9e,0xf0f,0x8003e1e0,0x1e01ff,0xfff8003f,0xf001e000,0x7fe0,0x3f00, 0x0,0x1e0000,0x1,0xe0000000,0x0,0x780000,0x1f,0xfe000000,0x78000,0x3c00,0xf000,0x7800003,0xffe00000,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x1fff80,0x0,0x0,0xffe000,0x0,0x0,0x0,0x3de0,0x0,0x7fff80,0x0,0xfffff80, 0x1f,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xe7bc07ff,0x3e1f000f,0x78000000,0x0,0x0,0xf780,0x7800078,0x0,0x780000,0x180000, 0x1fff8000,0x1e00,0x1e0003c,0xfff,0xc001f0f8,0x0,0x7ffe00,0x0,0x0,0x0,0x3f,0xff00007f,0xf0000000,0x3ffc,0xfff0,0x3c00,0x0, 0x7fffc00,0x0,0x7,0xf800003f,0xfe000000,0x1c00,0xe000,0xe00,0xf000,0x1fc000,0xfe0000,0x7f00000,0x3f800001,0xfc00001f,0xe00001ff, 0xffc00fff,0xf007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xc000fc00,0x3c003ffe,0x1fff0, 0xfff80,0x7ffc00,0x3ffe000,0x0,0xfffce0f0,0x3c0780,0x1e03c00,0xf01e000,0x781e0001,0xe01e0000,0x3fff00,0x1e0000,0x1e0000,0xf780003, 0xcf78001f,0xf800078,0x3c000000,0x0,0x1e00,0x1e00,0xf7800,0x3e1f000,0x1e00000,0x1e00000,0xf780003e,0x1f0000fc,0x7c000f3d, 0xe0000780,0x780,0x3de00,0xf3de00,0x7c3e000,0x0,0x0,0xf0000000,0xf000007b,0xc0001f0f,0x800001e0,0x1e0000,0x3e1f00,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000, 0x3c1e0000,0x1e0f0000,0x300007fc,0xfff,0xc00001e0,0x1e0,0x3c000,0x1c000,0x0,0x0,0x0,0x0,0x3c001ff,0xfc001ffe,0x3ffff,0xc01ffffc, 0x3f80,0x3ffff80,0x7fff803,0xfffff803,0xfffe001f,0xffe00000,0x0,0x0,0x0,0x0,0xffff80,0x7f800,0xfe0003,0xffff8001,0xffff01ff, 0xff0003ff,0xffe01fff,0xff001fff,0xf01e0007,0x803ffff0,0xfff80,0x3c003f00,0x7800001f,0xc007f07f,0x1e003f,0xff8007ff,0xff000fff, 0xe007ffff,0x7fffc,0x7fffffe,0xf0003c0,0xf0000f1e,0xf07,0x8003c1f0,0x3e01ff,0xfff8003f,0xf001e000,0x7fe0,0x7f80,0x0,0xe0000, 0x1,0xe0000000,0x0,0x780000,0x1f,0xfe000000,0x78000,0x3c00,0xf000,0x7800003,0xffe00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0, 0x0,0x0,0x0,0x0,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x3fff80,0x0,0x0,0xffe000,0x0,0x0,0x0,0x78f0,0x0,0xffff80,0x0,0x3fffff80,0x1f, 0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xc7f80070,0x3e1f0007,0x70000000,0x0,0x0,0x7700,0x7c000f8,0x0,0x780000,0x180000, 0x3fff8000,0x1f00,0x3e0003c,0x1f03,0xc001f0f8,0x0,0x703f00,0x0,0x0,0x0,0x3f,0xff0000f0,0xf8000000,0x303e,0xc0f8,0x7800,0x0, 0xffffc00,0x0,0x7,0x3800003e,0x3e000000,0x1c00,0xe000,0x3c00,0xf000,0x1fc000,0xfe0000,0x7f00000,0x3f800001,0xfc00000f,0xe00001ff, 0xffc01fff,0xf007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xf000fe00,0x3c007fff,0x3fff8, 0x1fffc0,0xfffe00,0x7fff000,0x1,0xffffc0f0,0x3c0780,0x1e03c00,0xf01e000,0x781f0003,0xe01e0000,0x3fff80,0xe0000,0x3c0000,0x1e3c0003, 0x8ff0001f,0xf80003c,0x78000000,0x0,0xe00,0x3c00,0x1e3c00,0x3e1f000,0xe00000,0x3c00001,0xe3c0003e,0x1f00007f,0xf8000e3f,0xc0000380, 0xf00,0x78f00,0xe3fc00,0x7c3e000,0x0,0x0,0x70000001,0xe00000f1,0xe0001f0f,0x800003c0,0x1e0000,0x3e1f00,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x3c0f0000, 0x30000ffe,0xf80,0xc00001e0,0x3c0,0x1e000,0x101c040,0x0,0x0,0x0,0x0,0x78003f0,0x7e001ffe,0x3f807,0xe01f00fe,0x3f80,0x3ffff80, 0x7e01803,0xfffff007,0xe03f003f,0x3f00000,0x0,0x0,0x0,0x0,0xfc0fc0,0x3ffe00,0xfe0003,0xffffc003,0xf81f01ff,0xff8003ff,0xffe01fff, 0xff003f01,0xf01e0007,0x803ffff0,0xfff80,0x3c007e00,0x7800001f,0xc007f07f,0x1e007e,0xfc007ff,0xff801f83,0xf007ffff,0x800fc07c, 0x7fffffe,0xf0003c0,0xf0000f0f,0x1e07,0xc007c0f8,0x7c01ff,0xfff8003c,0xf000,0x1e0,0xffc0,0x0,0xf0000,0x1,0xe0000000,0x0,0x780000, 0x3e,0x0,0x78000,0x3c00,0xf000,0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1f,0x800000f0,0x1f80, 0x0,0x0,0x7e0780,0x0,0x0,0x1f82000,0x0,0x0,0x0,0x7070,0x0,0x1f80f80,0x0,0x7fffff80,0x1f,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x1,0xc3f80070,0x3f3f0007,0xf0000000,0x0,0x0,0x7f00,0x3e001f0,0x0,0x780000,0x180000,0x7f018000,0xf80,0x7c0003c,0x3e00, 0x4001f0f8,0xfe00,0x400f00,0x0,0x0,0x0,0x7f000000,0xe0,0x38000000,0x1e,0x38,0x7800,0x0,0x1ffe1c00,0x0,0x0,0x38000078,0xf000000, 0x1c00,0xe000,0x7f800,0xf000,0x1fc000,0xfe0000,0x7f00000,0x3f800001,0xfc00001f,0xf00001ff,0xffc03f81,0xf007ffff,0xc03ffffe, 0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xf800fe00,0x3c00fc1f,0x8007e0fc,0x3f07e0,0x1f83f00,0xfc1f800, 0x3,0xf07fc0f0,0x3c0780,0x1e03c00,0xf01e000,0x780f8007,0xc01e0000,0x7e0fc0,0xf0000,0x3c0000,0x1c1c0003,0x87f0001f,0xf80003f, 0xf8000000,0x0,0xf00,0x3c00,0x1c1c00,0x3e1f000,0xf00000,0x3c00001,0xc1c0003e,0x1f00003f,0xc0000e1f,0xc00003c0,0xf00,0x70700, 0xe1fc00,0x7c3e000,0x0,0x0,0x78000001,0xe00000e0,0xe0001f0f,0x800003c0,0x1e0000,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x3c0f0001,0xff801e0f, 0x1f00,0x1e0,0x3c0,0x1e000,0x3c1c1e0,0x0,0x0,0x0,0x0,0x78007c0,0x1f001f9e,0x3c001,0xf010003e,0x7780,0x3c00000,0xf800000,0xf007, 0xc01f007c,0x1f80000,0x0,0x0,0x0,0x0,0xe003e0,0x7fff00,0x1ef0003,0xc007e007,0xc00301e0,0x1fc003c0,0x1e00,0x7c00,0x301e0007, 0x80007800,0x780,0x3c00fc00,0x7800001f,0xe00ff07f,0x1e00f8,0x3e00780,0x1fc03e00,0xf807801f,0xc01f001c,0xf000,0xf0003c0,0xf0000f0f, 0x1e03,0xc00f8078,0x780000,0xf0003c,0xf000,0x1e0,0x1f3e0,0x0,0x78000,0x1,0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0, 0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1f,0xf0,0xf80,0x0,0x0,0xf80180,0x0,0x0,0x1e00000, 0x0,0x0,0x0,0xe038,0x0,0x3e00380,0x0,0xfe0f0000,0x0,0xf0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xc0f00070,0x3b370003,0xe0000000, 0x0,0x0,0x3e00,0x1e001e0,0x0,0x780000,0x180000,0x7c000000,0x780,0x780003c,0x3c00,0x0,0x7ffc0,0x780,0x0,0x0,0x3,0xffe00000, 0x1c0,0x3c000000,0xe,0x38,0xf000,0x0,0x3ffe1c00,0x0,0x0,0x38000078,0xf000000,0x1c00,0xe000,0x7f000,0xf000,0x3de000,0x1ef0000, 0xf780000,0x7bc00003,0xde00001e,0xf00003e7,0x80007c00,0x30078000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001, 0xe0001e03,0xfc00fe00,0x3c01f007,0xc00f803e,0x7c01f0,0x3e00f80,0x1f007c00,0x7,0xc01f80f0,0x3c0780,0x1e03c00,0xf01e000,0x78078007, 0x801e0000,0x7803c0,0x78000,0x780000,0x380e0003,0x81e00000,0x1f,0xf0000000,0x0,0x780,0x7800,0x380e00,0x0,0x780000,0x7800003, 0x80e00000,0x1ff,0x80000e07,0x800001e0,0x1e00,0xe0380,0xe07800,0x0,0x0,0x0,0x3c000003,0xc00001c0,0x70000000,0x780,0x1e0000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x780000,0x3c1e0000,0x3c0e0007,0xfff01c07,0x1e00,0x1e0,0x780,0xf000,0x3e1c3e0,0x0,0x0,0x0,0x0,0xf0007c0,0x1f00181e,0x20000, 0xf000001f,0xf780,0x3c00000,0x1f000000,0x1f00f,0x800f8078,0xf80000,0x0,0x0,0x0,0x0,0x8003e0,0x1fc0f80,0x1ef0003,0xc001e007, 0x800101e0,0x7e003c0,0x1e00,0x7800,0x101e0007,0x80007800,0x780,0x3c00f800,0x7800001e,0xe00ef07f,0x801e00f0,0x1e00780,0x7c03c00, 0x78078007,0xc01e0004,0xf000,0xf0003c0,0x78001e0f,0x1e03,0xe00f807c,0xf80000,0x1f0003c,0x7800,0x1e0,0x3e1f0,0x0,0x3c000,0x1, 0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0, 0x1e,0xf0,0x780,0x0,0x0,0x1f00080,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x1e03c,0x0,0x3c00080,0x0,0xf80f0000,0x0,0x1f0000,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x3bf70003,0xe0000000,0x0,0x0,0x3e00,0x1f003e0,0x0,0x780000,0x180000,0x78000000,0x7c0,0xf80003c, 0x3c00,0x0,0x1f01f0,0x780,0x0,0x0,0xf,0x80f80000,0x1c0,0x1c000000,0xe,0x38,0x1e000,0x0,0x7ffe1c00,0x0,0x0,0x380000f0,0x7800000, 0x1c00,0xe000,0x7fc00,0xf000,0x3de000,0x1ef0000,0xf780000,0x7bc00003,0xde00001e,0xf00003c7,0x80007800,0x10078000,0x3c0000, 0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x7e00ff00,0x3c01e003,0xc00f001e,0x7800f0,0x3c00780,0x1e003c00, 0x7,0x800f00f0,0x3c0780,0x1e03c00,0xf01e000,0x7807c00f,0x801e0000,0xf803c0,0x3c000,0xf00000,0x780f0000,0x0,0x7,0xc0000000, 0x0,0x3c0,0xf000,0x780f00,0x0,0x3c0000,0xf000007,0x80f00000,0x7ff,0xc0000000,0xf0,0x3c00,0x1e03c0,0x0,0x0,0x0,0x0,0x1e000007, 0x800003c0,0x78000000,0xf00,0x1e0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x3c1e001f,0xfff03803,0x80001e00,0x1e0,0x780,0xf000,0xf9cf80, 0x0,0x0,0x0,0x0,0xf000780,0xf00001e,0x0,0xf800000f,0xe780,0x3c00000,0x1e000000,0x1e00f,0x78078,0x7c0000,0x0,0x0,0x0,0x0,0x1e0, 0x3f003c0,0x1ef0003,0xc000f00f,0x800001e0,0x1f003c0,0x1e00,0xf000,0x1e0007,0x80007800,0x780,0x3c01f000,0x7800001e,0xe00ef07f, 0x801e01f0,0x1e00780,0x3c07c00,0x78078003,0xc03e0000,0xf000,0xf0003c0,0x78001e0f,0x1e01,0xf01f003c,0xf00000,0x3e0003c,0x7800, 0x1e0,0x7c0f8,0x0,0x0,0x1,0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000, 0x0,0x0,0x0,0x0,0x0,0x1e,0xf0,0x780,0x0,0x0,0x1e00000,0x0,0x0,0x3c00000,0x0,0x8,0x40,0x0,0x7e0000,0x7c00000,0x1,0xf00f0000, 0x0,0x3e0000,0x0,0x3f,0xfc0,0xfc3f0,0xfc3f0,0x0,0x0,0x0,0x70,0x39e70000,0x0,0x0,0x0,0x0,0xf003c0,0x0,0x0,0x180000,0xf8000000, 0x3c0,0xf00003c,0x3c00,0x0,0x3c0078,0x7ff80,0x0,0x0,0x1e,0x3c0000,0x1c0,0x1c000000,0xe,0xf0,0x0,0x0,0x7ffe1c00,0x0,0x0,0x380000f0, 0x7800000,0x1c00,0xe000,0x3c00,0x0,0x3de000,0x1ef0000,0xf780000,0x7bc00003,0xde00001e,0xf00003c7,0x8000f800,0x78000,0x3c0000, 0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x1f00ff00,0x3c03e003,0xc01f001e,0xf800f0,0x7c00780,0x3e003c00, 0xf,0x800f80f0,0x3c0780,0x1e03c00,0xf01e000,0x7803c00f,0x1fffc0,0xf001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x307,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x781e003f,0xfff03803, 0x80001e00,0x1e0,0xf80,0xf000,0x3dde00,0x0,0x0,0x0,0x0,0xf000f00,0x780001e,0x0,0x7800000f,0x1e780,0x3c00000,0x3e000000,0x3e00f, 0x780f0,0x7c0000,0x0,0x0,0x0,0x0,0x1e0,0x7c001e0,0x3ef8003,0xc000f00f,0x1e0,0xf003c0,0x1e00,0xf000,0x1e0007,0x80007800,0x780, 0x3c03e000,0x7800001e,0xf01ef07b,0xc01e01e0,0xf00780,0x3e07800,0x3c078003,0xe03c0000,0xf000,0xf0003c0,0x78001e0f,0x1e00,0xf01e003e, 0x1f00000,0x3c0003c,0x7800,0x1e0,0x78078,0x0,0x0,0x1,0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,0x7800000,0x1e00000, 0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1e,0xf0,0x780,0x0,0x0,0x1e00000,0x0,0x0,0x3c00000,0x0,0x18,0xc0,0x0, 0xe70000,0x7800000,0x1,0xe00f0000,0x0,0x3c0000,0x0,0x3f,0xfc0,0xfc1f0,0x1f83f0,0x0,0x0,0x0,0x70,0x39e70000,0x0,0x0,0x0,0x0, 0xf807c0,0x0,0x0,0x180000,0xf0000000,0x3e0,0x1f00003c,0x3e00,0x0,0x70001c,0x3fff80,0x0,0x0,0x38,0xe0000,0x1c0,0x1c000078, 0x1c,0x1fe0,0x0,0x0,0xfffe1c00,0x0,0x0,0x380000f0,0x7800000,0x1c00,0xe000,0xe00,0x0,0x7df000,0x3ef8000,0x1f7c0000,0xfbe00007, 0xdf00003c,0x780003c7,0x8000f000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0xf00f780, 0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,0xf,0xf80f0,0x3c0780,0x1e03c00,0xf01e000,0x7803e01f,0x1ffff8,0xf001e0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0xc000,0x0,0x0,0x0,0x0,0x1e0000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x780000,0x3c1e0000,0x781e003e,0x30703803,0x80001e00,0x1e0,0xf00,0x7800,0xff800,0x1e0000,0x0,0x0,0x0,0x1e000f00,0x780001e, 0x0,0x7800000f,0x3c780,0x3c00000,0x3c000000,0x3c00f,0x780f0,0x3c0000,0x0,0x0,0x2000000,0x800000,0x1e0,0x78000e0,0x3c78003, 0xc000f01e,0x1e0,0xf803c0,0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c07c000,0x7800001e,0x701cf07b,0xc01e01e0,0xf00780,0x1e07800, 0x3c078001,0xe03c0000,0xf000,0xf0003c0,0x7c003e0f,0x1e00,0xf83e001e,0x1e00000,0x7c0003c,0x3c00,0x1e0,0xf807c,0x0,0x0,0x1fe0001, 0xe1fc0000,0x7f00003,0xf8780007,0xf000003c,0x7f0,0x783f0,0x0,0x0,0x7800000,0x1e00000,0x3e0f8000,0xfc00007,0xf8000007,0xf00001fc, 0xf,0xc0003fc0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1e,0xf0,0x780,0x0,0x0,0x3c00000,0x0,0x0,0x3c00000,0x0,0x18,0xc0,0x0,0x1818000, 0x7800000,0x1,0xe00f0000,0x0,0x7c0000,0x0,0x1f,0x80001f80,0x7c1f8,0x1f83e0,0x0,0x0,0x0,0x70,0x38c70007,0xf8000000,0x7f03, 0xf0000000,0x0,0x780780,0x0,0x0,0xfe0000,0xf0000000,0x1e0,0x1e00003c,0x3f00,0x0,0xe07f0e,0x7fff80,0x0,0x0,0x70,0x70000,0x1c0, 0x1c000078,0x3c,0x1fc0,0x0,0x0,0xfffe1c00,0x0,0x0,0x380000f0,0x7800000,0x1c00,0xe000,0xe00,0x0,0x78f000,0x3c78000,0x1e3c0000, 0xf1e00007,0x8f00003c,0x78000787,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00, 0xf80f780,0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,0xf,0x1f80f0,0x3c0780,0x1e03c00,0xf01e000,0x7801e01e,0x1ffffc, 0xf007e0,0x3fc000,0x1fe0000,0xff00000,0x7f800003,0xfc00001f,0xe0000fc0,0xfc00007f,0xfe0,0x7f00,0x3f800,0x1fc000,0x0,0x0,0x0, 0x1,0xf000001f,0x80000ff0,0x7f80,0x3fc00,0x1fe000,0xff0000,0x1f80000,0x1fc1e000,0x0,0x0,0x0,0x0,0x1e1fc0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000, 0x781c007c,0x30003803,0x80001f00,0x1e0,0xf00,0x7800,0x7f000,0x1e0000,0x0,0x0,0x0,0x1e000f00,0x780001e,0x0,0x7800000f,0x3c780, 0x3c00000,0x3c000000,0x3c00f,0x780f0,0x3c0000,0x0,0x0,0x1e000000,0xf00000,0x3e0,0xf0000e0,0x3c78003,0xc000f01e,0x1e0,0x7803c0, 0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c0f8000,0x7800001e,0x701cf079,0xe01e01e0,0xf00780,0x1e07800,0x3c078001,0xe03c0000, 0xf000,0xf0003c0,0x3c003c0f,0x3e00,0x787c001f,0x3e00000,0xf80003c,0x3c00,0x1e0,0x1f003e,0x0,0x0,0x1fffc001,0xe7ff0000,0x3ffe000f, 0xfe78003f,0xfc001fff,0xfe001ffc,0xf0078ffc,0x1ffc00,0x7ff000,0x7800f80,0x1e0000f,0x7f1fc01e,0x3ff0001f,0xfe00079f,0xfc0007ff, 0x3c003c7f,0xf001fff8,0x1fffff0,0x3c003c0,0xf0000f1e,0xf1f,0x7c1f0,0x1f00ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x3c00000,0x100000, 0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1818000,0x7800000,0x1,0xe00f0000,0x1000000,0xf80000,0x40000002,0xf,0x80001f00,0x7e0f8,0x1f07c0, 0x0,0x0,0x0,0x70,0x38c7003f,0xff000000,0xff8f,0xf8000100,0xffffe,0x7c0f80,0x0,0x0,0x3ffc000,0xf0000020,0x1001f0,0x3c00003c, 0x1f80,0x0,0x1c3ffc7,0x7c0780,0x0,0x0,0xe3,0xff038000,0xe0,0x38000078,0x78,0x1ff0,0x0,0x3c003c0,0xfffe1c00,0x0,0x0,0x380000f0, 0x7800000,0x1c00,0xe000,0xe00,0xf000,0x78f000,0x3c78000,0x1e3c0000,0xf1e00007,0x8f00003c,0x78000787,0x8001e000,0x78000,0x3c0000, 0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f3c0,0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00, 0x4000200f,0x3f80f0,0x3c0780,0x1e03c00,0xf01e000,0x7801f03e,0x1ffffe,0xf01fe0,0x3fff800,0x1fffc000,0xfffe0007,0xfff0003f, 0xff8001ff,0xfc003ff3,0xfe0003ff,0xe0007ff8,0x3ffc0,0x1ffe00,0xfff000,0x3ff80001,0xffc0000f,0xfe00007f,0xf000003f,0xf8003c7f, 0xe0003ffc,0x1ffe0,0xfff00,0x7ff800,0x3ffc000,0x1f80000,0xfff1c03c,0x3c01e0,0x1e00f00,0xf007800,0x781f0001,0xf01e7ff0,0x7c0007c, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000, 0x3c1e003f,0xfffff078,0x30003803,0x80000f00,0x1e0,0x1f00,0x7800,0x7f000,0x1e0000,0x0,0x0,0x0,0x3c000f00,0x780001e,0x0,0x7800000f, 0x78780,0x3c00000,0x3c000000,0x7c00f,0x780f0,0x3c0007,0xe000003f,0x0,0xfe000000,0xfe0000,0x3c0,0x1f000070,0x7c7c003,0xc000f01e, 0x1e0,0x7803c0,0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c1f0000,0x7800001e,0x783cf079,0xe01e03c0,0xf00780,0x1e0f000,0x3c078001, 0xe03c0000,0xf000,0xf0003c0,0x3c003c07,0x81f03c00,0x7c7c000f,0x87c00000,0xf00003c,0x1e00,0x1e0,0x3e001f,0x0,0x0,0x3fffe001, 0xefff8000,0x7fff001f,0xff78007f,0xfe001fff,0xfe003ffe,0xf0079ffe,0x1ffc00,0x7ff000,0x7801f00,0x1e0000f,0xffbfe01e,0x7ff8003f, 0xff0007bf,0xfe000fff,0xbc003cff,0xf803fffc,0x1fffff0,0x3c003c0,0x78001e1e,0xf0f,0x800f80f0,0x1e00ff,0xffe0001e,0xf0,0x780, 0x0,0x0,0x3c00000,0x380000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1008000,0x7800000,0x3,0xe00f0000,0x3800000,0xf00000,0xe0000007, 0xf,0x80001f00,0x3e0f8,0x1e07c0,0x0,0x0,0x0,0x70,0x3807007f,0xff800000,0x1ffdf,0xfc000380,0xffffe,0x3e1f00,0x0,0x0,0xfffe000, 0xf0000030,0x3800f8,0x7c00003c,0xfc0,0x0,0x18780c3,0xf00780,0x80100,0x0,0xc3,0xffc18000,0xf0,0x78000078,0xf0,0xf0,0x0,0x3c003c0, 0xfffe1c00,0x0,0x0,0x380000f0,0x7800801,0x1c00,0xe000,0x1e00,0xf000,0xf8f800,0x7c7c000,0x3e3e0001,0xf1f0000f,0x8f80007c,0x7c000787, 0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f3c0,0x3c078001,0xe03c000f, 0x1e00078,0xf0003c0,0x78001e00,0xe000701f,0x3fc0f0,0x3c0780,0x1e03c00,0xf01e000,0x7800f87c,0x1e007f,0xf07e00,0x7fffc00,0x3fffe001, 0xffff000f,0xfff8007f,0xffc003ff,0xfe007ff7,0xff0007ff,0xf000fffc,0x7ffe0,0x3fff00,0x1fff800,0x3ff80001,0xffc0000f,0xfe00007f, 0xf00000ff,0xf8003cff,0xf0007ffe,0x3fff0,0x1fff80,0xfffc00,0x7ffe000,0x1f80001,0xfffb803c,0x3c01e0,0x1e00f00,0xf007800,0x780f0001, 0xe01efff8,0x3c00078,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e003f,0xfffff078,0x30001c07,0xf80,0x1e0,0x1e00,0x3c00,0xff800,0x1e0000,0x0,0x0,0x0,0x3c001e00, 0x3c0001e,0x0,0x7800001e,0x70780,0x3c00000,0x78000000,0x78007,0x800f00f0,0x3e0007,0xe000003f,0x3,0xfe000000,0xff8000,0x7c0, 0x1e000070,0x783c003,0xc001f01e,0x1e0,0x7803c0,0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c3e0000,0x7800001e,0x3838f079, 0xe01e03c0,0x780780,0x1e0f000,0x1e078001,0xe03c0000,0xf000,0xf0003c0,0x3c007c07,0x81f03c00,0x3ef80007,0x87800000,0x1f00003c, 0x1e00,0x1e0,0x7c000f,0x80000000,0x0,0x3ffff001,0xffffc000,0xffff003f,0xff7800ff,0xff001fff,0xfe007ffe,0xf007bffe,0x1ffc00, 0x7ff000,0x7803e00,0x1e0000f,0xffffe01e,0xfff8007f,0xff8007ff,0xff001fff,0xbc003dff,0xf807fffc,0x1fffff0,0x3c003c0,0x78001e0f, 0x1e07,0xc01f00f0,0x1e00ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x7c00000,0x7c0000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1018000,0x7800000, 0x3,0xc00f0000,0x7c00000,0x1f00001,0xf000000f,0x80000007,0xc0003e00,0x1e07c,0x3e0780,0x0,0x0,0x0,0x70,0x380700ff,0xff800000, 0x3ffff,0xfe0007c0,0xffffe,0x1e1e00,0x0,0x780000,0x1fffe000,0xf0000078,0x7c0078,0x7800003c,0xff0,0x0,0x38e0003,0x80f00780, 0x180300,0x0,0x1c3,0x81e1c000,0x7f,0xf0000078,0x1e0,0x38,0x0,0x3c003c0,0xfffe1c00,0x0,0x0,0x380000f0,0x7800c01,0x80001c00, 0xe000,0x603e00,0xf000,0xf07800,0x783c000,0x3c1e0001,0xe0f0000f,0x7800078,0x3c000f87,0x8001e000,0x78000,0x3c0000,0x1e00000, 0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f3c0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f01,0xf000f81e, 0x7bc0f0,0x3c0780,0x1e03c00,0xf01e000,0x78007878,0x1e001f,0xf0f800,0x7fffe00,0x3ffff001,0xffff800f,0xfffc007f,0xffe003ff, 0xff007fff,0xff800fff,0xf001fffe,0xffff0,0x7fff80,0x3fffc00,0x3ff80001,0xffc0000f,0xfe00007f,0xf00001ff,0xfc003dff,0xf000ffff, 0x7fff8,0x3fffc0,0x1fffe00,0xffff000,0x1f80003,0xffff803c,0x3c01e0,0x1e00f00,0xf007800,0x780f0001,0xe01ffffc,0x3c00078,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000, 0x3c1e003f,0xfffff078,0x30001e0f,0x300780,0x1e0,0x1e00,0x3c00,0x3dde00,0x1e0000,0x0,0x0,0x0,0x78001e00,0x3c0001e,0x0,0xf800003e, 0xf0780,0x3dfc000,0x783f8000,0xf8007,0xc01f00f0,0x3e0007,0xe000003f,0x1f,0xfc000000,0x7ff000,0xf80,0x3e007c70,0x783c003,0xc001e03c, 0x1e0,0x3c03c0,0x1e00,0x3c000,0x1e0007,0x80007800,0x780,0x3c7c0000,0x7800001e,0x3878f078,0xf01e03c0,0x780780,0x1e0f000,0x1e078001, 0xe03e0000,0xf000,0xf0003c0,0x1e007807,0x83f03c00,0x3ef00007,0xcf800000,0x3e00003c,0xf00,0x1e0,0xf80007,0xc0000000,0x0,0x3e01f801, 0xfe07e001,0xf80f007e,0x7f801f8,0x1f801fff,0xfe00fc0f,0xf007f83f,0x1ffc00,0x7ff000,0x7807c00,0x1e0000f,0x87e1e01f,0xe0fc00fc, 0xfc007f8,0x1f803f03,0xfc003df0,0x3807e03c,0x1fffff0,0x3c003c0,0x78003e0f,0x1e03,0xe03e00f8,0x3e00ff,0xffe0001e,0xf0,0x780, 0x0,0x0,0x7800000,0xfe0000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1818000,0x7c00000,0x3,0xc00f0000,0xfe00000,0x3e00003,0xf800001f, 0xc0000007,0xc0003e00,0x1e03c,0x3c0f80,0x0,0x0,0x0,0x70,0x380700fc,0x7800000,0x7c1fe,0x3e000fe0,0xffffe,0x1f3e00,0x0,0x780000, 0x3f98e000,0xf000003c,0xfcf8007c,0xf800003c,0x3ffc,0x0,0x31c0001,0x80f00f80,0x380700,0x0,0x183,0x80e0c000,0x3f,0xe0000078, 0x3c0,0x38,0x0,0x3c003c0,0xfffe1c00,0x0,0x0,0x38000078,0xf000e01,0xc003ffe0,0x1fff00,0x7ffc00,0xf000,0xf07800,0x783c000,0x3c1e0001, 0xe0f0000f,0x7800078,0x3c000f07,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00, 0x3c0f1e0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0xf801f01e,0xf3c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78007cf8, 0x1e000f,0x80f0f000,0x7c03f00,0x3e01f801,0xf00fc00f,0x807e007c,0x3f003e0,0x1f80707f,0x8f801f80,0xf003f03f,0x1f81f8,0xfc0fc0, 0x7e07e00,0x3ff80001,0xffc0000f,0xfe00007f,0xf00003ff,0xfc003fc1,0xf801f81f,0x800fc0fc,0x7e07e0,0x3f03f00,0x1f81f800,0x1f80007, 0xe07f003c,0x3c01e0,0x1e00f00,0xf007800,0x780f8003,0xe01fe07e,0x3e000f8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3f,0xfffff078,0x30000ffe,0x1f007c0,0x0,0x1e00, 0x3c00,0xf9cf80,0x1e0000,0x0,0x0,0x0,0x78001e00,0x3c0001e,0x0,0xf00000fc,0x1e0780,0x3fff800,0x78ffe000,0xf0003,0xe03e00f0, 0x3e0007,0xe000003f,0x7f,0xe01fffff,0xf00ffc00,0x1f80,0x3c01ff70,0x783c003,0xc007e03c,0x1e0,0x3c03c0,0x1e00,0x3c000,0x1e0007, 0x80007800,0x780,0x3cfc0000,0x7800001e,0x3c78f078,0xf01e03c0,0x780780,0x3e0f000,0x1e078003,0xc01f0000,0xf000,0xf0003c0,0x1e007807, 0x83f83c00,0x1ff00003,0xcf000000,0x3e00003c,0xf00,0x1e0,0x0,0x0,0x0,0x20007801,0xfc03e003,0xe003007c,0x3f803e0,0x7c0003c, 0xf807,0xf007e00f,0x3c00,0xf000,0x780f800,0x1e0000f,0x87e1f01f,0x803c00f8,0x7c007f0,0xf803e01,0xfc003f80,0x80f8004,0x3c000, 0x3c003c0,0x3c003c0f,0x1e03,0xe03e0078,0x3c0000,0x7c0001e,0xf0,0x780,0x0,0x0,0x3ffff800,0x1ff0000,0x0,0x7800000,0x0,0x18, 0xc0,0x0,0x1818000,0x3e00000,0x3,0xc00f0000,0x1ff00000,0x3e00007,0xfc00003f,0xe0000003,0xc0003c00,0xf03c,0x3c0f00,0x0,0x0, 0x0,0x70,0x380701f0,0x800000,0x780fc,0x1e001ff0,0x7c,0xf3c00,0x0,0x780000,0x7e182000,0xf000001f,0xfff00ffc,0xffc0003c,0x3cfe, 0x0,0x31c0001,0x80f01f80,0x780f00,0x0,0x183,0x80e0c000,0xf,0x80000078,0x780,0x38,0x0,0x3c003c0,0x7ffe1c00,0x0,0x0,0x38000078, 0xf000f01,0xe003ffe0,0x1fff00,0x7ff800,0xf000,0xf07800,0x783c000,0x3c1e0001,0xe0f0000f,0x78000f8,0x3e000f07,0x8003c000,0x78000, 0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x3c0f1e0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0, 0x78000f00,0x7c03e01e,0x1e3c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78003cf0,0x1e0007,0x80f1e000,0x4000f00,0x20007801,0x3c008, 0x1e0040,0xf00200,0x780403f,0x7803e00,0x3007c00f,0x803e007c,0x1f003e0,0xf801f00,0x780000,0x3c00000,0x1e000000,0xf00007f0, 0x3e003f00,0x7801f00f,0x800f807c,0x7c03e0,0x3e01f00,0x1f00f800,0x1f80007,0xc03e003c,0x3c01e0,0x1e00f00,0xf007800,0x78078003, 0xc01fc03e,0x1e000f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x780000,0x0,0xf078007c,0x300007fc,0x7e00fe0,0x0,0x1e00,0x3c00,0x3e1c3e0,0x1e0000,0x0,0x0,0x0,0xf0001e00, 0x3c0001e,0x1,0xf000fff8,0x1e0780,0x3fffe00,0x79fff000,0x1f0001,0xfffc00f0,0x7e0007,0xe000003f,0x3ff,0x801fffff,0xf003ff80, 0x3f00,0x3c03fff0,0xf01e003,0xffffc03c,0x1e0,0x3c03ff,0xffc01fff,0xfe03c000,0x1fffff,0x80007800,0x780,0x3df80000,0x7800001e, 0x1c70f078,0x781e03c0,0x780780,0x3c0f000,0x1e078007,0xc01f8000,0xf000,0xf0003c0,0x1e007807,0x83f83c00,0xfe00003,0xff000000, 0x7c00003c,0x780,0x1e0,0x0,0x0,0x0,0x7c01,0xf801f007,0xc00100f8,0x1f803c0,0x3c0003c,0x1f003,0xf007c00f,0x80003c00,0xf000, 0x783f000,0x1e0000f,0x3c0f01f,0x3e01f0,0x3e007e0,0x7c07c00,0xfc003f00,0xf0000,0x3c000,0x3c003c0,0x3c003c0f,0x1e01,0xf07c007c, 0x7c0000,0xfc0001e,0xf0,0x780,0x0,0x0,0x3ffff000,0x3838000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0xff0000,0x3f00000,0x3,0xc00fff00, 0x38380000,0x7c0000e,0xe000070,0x70000001,0xe0003c00,0xf01e,0x780e00,0x0,0x0,0x0,0x0,0x1e0,0x0,0x780f8,0xf003838,0xfc,0xffc00, 0x0,0x780000,0x7c180000,0xf000000f,0xffe00fff,0xffc0003c,0x783f,0x80000000,0x6380000,0xc0f83f80,0xf81f00,0x0,0x303,0x80e06000, 0x0,0x78,0xf00,0x78,0x0,0x3c003c0,0x7ffe1c00,0x0,0x0,0x3800003c,0x3e000f81,0xf003ffe0,0x1fff00,0x1fc000,0xf000,0x1e03c00, 0xf01e000,0x780f0003,0xc078001e,0x3c000f0,0x1e000f07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,0xfffff00,0xf00000,0x7800000, 0x3c000001,0xe0001e00,0x3c0f0f0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x3e07c01e,0x1e3c0f0,0x3c0780,0x1e03c00, 0xf01e000,0x78003ff0,0x1e0007,0x80f1e000,0xf80,0x7c00,0x3e000,0x1f0000,0xf80000,0x7c0001e,0x3c07c00,0x10078007,0x803c003c, 0x1e001e0,0xf000f00,0x780000,0x3c00000,0x1e000000,0xf00007c0,0x1e003e00,0x7c03e007,0xc01f003e,0xf801f0,0x7c00f80,0x3e007c00, 0xf,0x801f003c,0x3c01e0,0x1e00f00,0xf007800,0x7807c007,0xc01f801f,0x1f001f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x0,0xe078003c,0x300001f0,0x3f801ff0,0x0, 0x3c00,0x1e00,0x3c1c1e0,0x1e0000,0x0,0x0,0x0,0xf0001e0f,0x3c0001e,0x3,0xe000fff0,0x3c0780,0x3ffff00,0x7bfff800,0x1e0000,0x7ff00078, 0x7e0007,0xe000003f,0x1ffc,0x1fffff,0xf0007ff0,0x7e00,0x3c07c3f0,0xf01e003,0xffff003c,0x1e0,0x3c03ff,0xffc01fff,0xfe03c000, 0x1fffff,0x80007800,0x780,0x3ffc0000,0x7800001e,0x1ef0f078,0x781e03c0,0x780780,0x7c0f000,0x1e07801f,0x800ff000,0xf000,0xf0003c0, 0xf00f807,0x83b83c00,0xfc00001,0xfe000000,0xf800003c,0x780,0x1e0,0x0,0x0,0x0,0x3c01,0xf000f007,0xc00000f0,0xf80780,0x3c0003c, 0x1e001,0xf007c007,0x80003c00,0xf000,0x787e000,0x1e0000f,0x3c0f01f,0x1e01e0,0x1e007c0,0x3c07800,0x7c003f00,0xf0000,0x3c000, 0x3c003c0,0x3e007c07,0x80003c00,0xf8f8003c,0x780000,0xf80001e,0xf0,0x780,0x0,0x0,0x7ffff000,0x601c000,0x3,0xffff0000,0x0, 0xfff,0xf8007fff,0xc0000000,0x7e003c,0x1fe0000,0xc0003,0xc00fff00,0x601c0000,0xf800018,0x70000c0,0x38000001,0xe0007800,0x701e, 0x701e00,0x0,0x0,0x0,0x0,0x1e0,0x6,0x700f8,0xf00601c,0xf8,0x7f800,0x0,0x780000,0xf8180000,0xf000000f,0x87c00fff,0xffc0003c, 0xf01f,0xc0000000,0x6380000,0xc07ff780,0x1f03e03,0xfffffe00,0x303,0x81c06000,0x0,0x1ffff,0xfe001e00,0x180f8,0x0,0x3c003c0, 0x3ffe1c00,0x3f00000,0x0,0x3800003f,0xfe0007c0,0xf8000000,0x18000000,0xc0000006,0x1f000,0x1e03c00,0xf01e000,0x780f0003,0xc078001e, 0x3c000f0,0x1e001f07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,0xfffff00,0xf00000,0x7800000,0x3c000001,0xe000fff8,0x3c0f0f0, 0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x1f0f801e,0x3c3c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78001fe0,0x1e0007, 0x80f1e000,0x780,0x3c00,0x1e000,0xf0000,0x780000,0x3c0001e,0x3c07c00,0xf0007,0x8078003c,0x3c001e0,0x1e000f00,0x780000,0x3c00000, 0x1e000000,0xf0000f80,0x1f003e00,0x3c03c003,0xc01e001e,0xf000f0,0x7800780,0x3c003c00,0xf,0x3f003c,0x3c01e0,0x1e00f00,0xf007800, 0x7803c007,0x801f000f,0xf001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1,0xe078003f,0xb0000000,0xfc003cf0,0x0,0x3c00,0x1e00,0x101c040,0x1e0000,0x0,0x0,0x1, 0xe0001e1f,0x83c0001e,0x7,0xe000fff0,0x3c0780,0x3c03f80,0x7fc0fc00,0x1e0000,0xfff80078,0xfe0007,0xe000003f,0x7fe0,0x1fffff, 0xf0000ffc,0xfc00,0x780f81f0,0xf01e003,0xffff003c,0x1e0,0x3c03ff,0xffc01fff,0xfe03c000,0x1fffff,0x80007800,0x780,0x3ffc0000, 0x7800001e,0x1ef0f078,0x3c1e03c0,0x780780,0x1fc0f000,0x1e07ffff,0x7ff00,0xf000,0xf0003c0,0xf00f007,0xc3b87c00,0x7c00001,0xfe000000, 0xf800003c,0x3c0,0x1e0,0x0,0x0,0x0,0x3c01,0xf000f007,0x800000f0,0xf80780,0x1e0003c,0x1e001,0xf0078007,0x80003c00,0xf000,0x78fc000, 0x1e0000f,0x3c0f01e,0x1e01e0,0x1e007c0,0x3c07800,0x7c003e00,0xf0000,0x3c000,0x3c003c0,0x1e007807,0x80003c00,0x7df0003c,0x780000, 0x1f00001e,0xf0,0x780,0x0,0x0,0x7800000,0xe7ce000,0x3,0xffff0000,0x0,0xfff,0xf8007fff,0xc0000000,0x1f0,0xffe000,0x1c0003, 0xc00fff00,0xe7ce0000,0xf800039,0xf38001cf,0x9c000000,0xe0007800,0x780e,0x701c00,0x0,0x0,0x0,0x0,0x1e0,0x7,0xf0078,0xf00e7ce, 0x1f0,0x7f800,0x0,0x780000,0xf0180000,0xf000000e,0x1c0001f,0xe000003c,0xf007,0xe0000000,0x6380000,0xc03fe780,0x3e07c03,0xfffffe00, 0x303,0xffc06000,0x0,0x1ffff,0xfe003ffe,0x1fff0,0x0,0x3c003c0,0x1ffe1c00,0x3f00000,0x7,0xffc0001f,0xfc0003e0,0x7c000001,0xfc00000f, 0xe000007f,0x1e000,0x1e03c00,0xf01e000,0x780f0003,0xc078001e,0x3c000f0,0x1e001e07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0, 0xfffff00,0xf00000,0x7800000,0x3c000001,0xe000fff8,0x3c0f078,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0xf9f001e, 0x783c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78001fe0,0x1e0007,0x80f1e000,0x780,0x3c00,0x1e000,0xf0000,0x780000,0x3c0001e,0x3c07800, 0xf0003,0xc078001e,0x3c000f0,0x1e000780,0x780000,0x3c00000,0x1e000000,0xf0000f00,0xf003c00,0x3c03c003,0xc01e001e,0xf000f0, 0x7800780,0x3c003c00,0xf,0x7f003c,0x3c01e0,0x1e00f00,0xf007800,0x7803c007,0x801f000f,0xf001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1,0xe070001f,0xf8000007, 0xf0007cf8,0x7800000,0x3c00,0x1e00,0x1c000,0x1e0000,0x0,0x0,0x1,0xe0001e1f,0x83c0001e,0xf,0xc000fff8,0x780780,0x2000f80,0x7f803e00, 0x3e0003,0xfffe007c,0x1fe0000,0x0,0x3ff00,0x0,0x1ff,0x8001f000,0x780f00f0,0x1f00f003,0xffffc03c,0x1e0,0x3c03ff,0xffc01fff, 0xfe03c00f,0xf81fffff,0x80007800,0x780,0x3ffe0000,0x7800001e,0xee0f078,0x3c1e03c0,0x7807ff,0xff80f000,0x1e07fffe,0x3ffe0, 0xf000,0xf0003c0,0xf00f003,0xc7bc7800,0xfc00000,0xfc000001,0xf000003c,0x3c0,0x1e0,0x0,0x0,0x0,0x3c01,0xe000f80f,0x800001e0, 0xf80f00,0x1e0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x79f8000,0x1e0000f,0x3c0f01e,0x1e03c0,0x1f00780,0x3e0f000,0x7c003e00, 0xf0000,0x3c000,0x3c003c0,0x1e007807,0x81e03c00,0x7df0003e,0xf80000,0x3e00003e,0xf0,0x7c0,0xfc000,0x80000000,0x7800000,0x1e7cf000, 0x3,0xffff0000,0x0,0x18,0xc0,0x0,0xf80,0x7ffc00,0x380003,0xc00fff01,0xe7cf0000,0x1f000079,0xf3c003cf,0x9e000000,0xe0007000, 0x380e,0xe01c00,0x0,0x0,0x0,0x0,0x1e0,0x3,0x800f0078,0xf01e7cf,0x3e0,0x3f000,0x0,0x780000,0xf018001f,0xfff8001e,0x1e0000f, 0xc000003c,0xf003,0xe0000000,0x6380000,0xc00fc780,0x7c0f803,0xfffffe00,0x303,0xfe006000,0x0,0x1ffff,0xfe003ffe,0x1ffe0,0x0, 0x3c003c0,0xffe1c00,0x3f00000,0x7,0xffc00007,0xf00001f0,0x3e00001f,0xfc0000ff,0xe00007ff,0x3e000,0x3e01e00,0x1f00f000,0xf8078007, 0xc03c003e,0x1e001e0,0xf001e07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,0xfffff00,0xf00000,0x7800000,0x3c000001,0xe000fff8, 0x3c0f078,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x7fe001e,0xf03c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78000fc0, 0x1e0007,0x80f1f000,0x780,0x3c00,0x1e000,0xf0000,0x780000,0x3c0001e,0x3c0f800,0x1e0003,0xc0f0001e,0x78000f0,0x3c000780,0x780000, 0x3c00000,0x1e000000,0xf0000f00,0xf003c00,0x3c078003,0xe03c001f,0x1e000f8,0xf0007c0,0x78003e00,0x1e,0xf7803c,0x3c01e0,0x1e00f00, 0xf007800,0x7803e00f,0x801e000f,0x80f803e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1,0xe0f0000f,0xff00001f,0x8000f87c,0x7800000,0x3c00,0x1e00,0x1c000,0x7fffff80, 0x0,0x0,0x3,0xc0001e1f,0x83c0001e,0x1f,0x800000fe,0xf00780,0x7c0,0x7f001e00,0x3c0007,0xe03f003f,0x3fe0000,0x0,0x3fc00,0x0, 0x7f,0x8001e000,0x781f00f0,0x1e00f003,0xc007e03c,0x1e0,0x3c03c0,0x1e00,0x3c00f,0xf81e0007,0x80007800,0x780,0x3f9f0000,0x7800001e, 0xfe0f078,0x3c1e03c0,0x7807ff,0xff00f000,0x1e07fff8,0xfff8,0xf000,0xf0003c0,0xf81f003,0xc7bc7800,0xfe00000,0x78000003,0xe000003c, 0x1e0,0x1e0,0x0,0x0,0x0,0x1fffc01,0xe000780f,0x1e0,0x780f00,0x1e0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x7bf0000,0x1e0000f, 0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0xf8000,0x3c000,0x3c003c0,0x1f00f807,0x81f03c00,0x3fe0001e,0xf00000,0x7c00007c, 0xf0,0x3e0,0x3ff801,0x80000000,0x7800000,0x3cfcf800,0x3,0xffff0000,0x0,0x18,0xc0,0x0,0x7c00,0x1fff00,0x700003,0xc00f0003, 0xcfcf8000,0x3e0000f3,0xf3e0079f,0x9f000000,0xf000,0x1000,0x0,0x0,0x0,0x0,0x0,0x1f0,0x1,0xc00f0078,0xf03cfcf,0x800007c0,0x1e000, 0x0,0x780001,0xe018001f,0xfff8001c,0xe00007,0x8000003c,0xf001,0xf0000000,0x6380000,0xc0000000,0xf81f003,0xfffffe00,0x303, 0x87006000,0x0,0x1ffff,0xfe003ffe,0x7f00,0x0,0x3c003c0,0x3fe1c00,0x3f00000,0x7,0xffc00000,0xf8,0x1f0001ff,0xf0000fff,0x80007ffc, 0xfc000,0x3c01e00,0x1e00f000,0xf0078007,0x803c003c,0x1e001e0,0xf001e07,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000, 0x7800000,0x3c000001,0xe000fff8,0x3c0f078,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x3fc001e,0x1e03c0f0,0x3c0780, 0x1e03c00,0xf01e000,0x78000780,0x1e0007,0x80f0fc00,0x3fff80,0x1fffc00,0xfffe000,0x7fff0003,0xfff8001f,0xffc0001e,0x3c0f000, 0x1e0003,0xc0f0001e,0x78000f0,0x3c000780,0x780000,0x3c00000,0x1e000000,0xf0001e00,0xf803c00,0x3c078001,0xe03c000f,0x1e00078, 0xf0003c0,0x78001e07,0xfffffe1e,0x1e7803c,0x3c01e0,0x1e00f00,0xf007800,0x7801e00f,0x1e0007,0x807803c0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3,0xc0f00007, 0xffc0007e,0xf03e,0x7800000,0x3c00,0x1e00,0x1c000,0x7fffff80,0x0,0x0,0x3,0xc0001e1f,0x83c0001e,0x3f,0x3e,0xf00780,0x3c0,0x7e001e00, 0x7c000f,0x800f001f,0xffde0000,0x0,0x3e000,0x0,0xf,0x8003e000,0x781e0070,0x1e00f003,0xc001f03c,0x1e0,0x3c03c0,0x1e00,0x3c00f, 0xf81e0007,0x80007800,0x780,0x3f1f0000,0x7800001e,0x7c0f078,0x1e1e03c0,0x7807ff,0xfc00f000,0x1e07fffe,0xffc,0xf000,0xf0003c0, 0x781e003,0xc71c7800,0x1ff00000,0x78000003,0xe000003c,0x1e0,0x1e0,0x0,0x0,0x0,0xffffc01,0xe000780f,0x1e0,0x780fff,0xffe0003c, 0x3c000,0xf0078007,0x80003c00,0xf000,0x7ff0000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x7f000,0x3c000, 0x3c003c0,0xf00f007,0xc1f07c00,0x1fc0001f,0x1f00000,0xfc000ff8,0xf0,0x1ff,0xfffe07,0x80000000,0x7800000,0x7ffcfc00,0x0,0xf000000, 0x0,0x18,0xc0,0x0,0x3e000,0x1ff80,0xe00003,0xc00f0007,0xffcfc000,0x3e0001ff,0xf3f00fff,0x9f800000,0x6000,0x0,0x0,0x7c000, 0x0,0x0,0x0,0xfe,0x0,0xe00f007f,0xff07ffcf,0xc0000fc0,0x1e000,0x0,0x780001,0xe018001f,0xfff8001c,0xe00007,0x80000000,0xf800, 0xf0000000,0x6380000,0xc0000000,0x1f03c000,0x1e00,0x303,0x83806000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xfe1c00,0x3f00000,0x0, 0x0,0x3c,0xf801fff,0xfff8,0x7ffc0,0x1f8000,0x3c01e00,0x1e00f000,0xf0078007,0x803c003c,0x1e001e0,0xf003c07,0x8003c000,0x78000, 0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x3c0f03c,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0, 0x78000f00,0x1f8001e,0x1e03c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e000f,0x80f0ff00,0x1ffff80,0xffffc00,0x7fffe003, 0xffff001f,0xfff800ff,0xffc007ff,0xffc0f000,0x1fffff,0xc0fffffe,0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,0xf0001e00, 0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e07,0xfffffe1e,0x3c7803c,0x3c01e0,0x1e00f00,0xf007800,0x7801f01f, 0x1e0007,0x807c07c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x780000,0x3,0xc0f00000,0xfff003f0,0x1f00f03e,0x7800000,0x3c00,0x1e00,0x1c000,0x7fffff80,0x0,0x7ff80000,0x3, 0xc0001e0f,0x3c0001e,0x7e,0x1f,0x1e00780,0x3e0,0x7e000f00,0x78000f,0x7800f,0xff9e0000,0x0,0x3fc00,0x0,0x7f,0x8003c000,0x781e0070, 0x3e00f803,0xc000f03c,0x1e0,0x3c03c0,0x1e00,0x3c00f,0xf81e0007,0x80007800,0x780,0x3e0f8000,0x7800001e,0x7c0f078,0x1e1e03c0, 0x7807ff,0xf000f000,0x1e07807f,0xfe,0xf000,0xf0003c0,0x781e003,0xc71c7800,0x3ef00000,0x78000007,0xc000003c,0x1e0,0x1e0,0x0, 0x0,0x0,0x1ffffc01,0xe000780f,0x1e0,0x780fff,0xffe0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x7ff0000,0x1e0000f,0x3c0f01e, 0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x7ff80,0x3c000,0x3c003c0,0xf00f003,0xc1f07800,0x1fc0000f,0x1e00000,0xf8000ff0,0xf0, 0xff,0xffffff,0x80000000,0x3fffc000,0xfff9fe00,0x0,0xf000000,0x0,0x18,0xc0,0x0,0x1f0000,0x1fc0,0x1c00003,0xc00f000f,0xff9fe000, 0x7c0003ff,0xe7f81fff,0x3fc00000,0x0,0x0,0x0,0xfe000,0x1ffffc0f,0xfffffc00,0x0,0xff,0xf0000000,0x700f007f,0xff0fff9f,0xe0000f80, 0x1e000,0x0,0x780001,0xe018001f,0xfff8001c,0xe00fff,0xffc00000,0xf800,0xf0000000,0x6380000,0xc0ffff80,0x3e078000,0x1e00,0x7ff80303, 0x83c06000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x3f00000,0x0,0x7f,0xff00001e,0x7c1fff0,0xfff80,0x7ffc00,0x3f0000,0x7c01f00, 0x3e00f801,0xf007c00f,0x803e007c,0x1f003e0,0xf803c07,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001, 0xe0001e00,0x3c0f03c,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x1f8001e,0x3c03c0f0,0x3c0780,0x1e03c00,0xf01e000, 0x78000780,0x1e001f,0xf07f80,0x3ffff80,0x1ffffc00,0xffffe007,0xffff003f,0xfff801ff,0xffc03fff,0xffc0f000,0x1fffff,0xc0fffffe, 0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e07, 0xfffffe1e,0x787803c,0x3c01e0,0x1e00f00,0xf007800,0x7800f01e,0x1e0007,0x803c0780,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1ff,0xffff8000,0x3ff80fc0,0x7fc1e01f, 0x7800000,0x3c00,0x1e00,0x0,0x7fffff80,0x0,0x7ff80000,0x7,0x80001e00,0x3c0001e,0xfc,0xf,0x1e00780,0x1e0,0x7c000f00,0x78000f, 0x78007,0xff1e0000,0x0,0x3ff00,0x0,0x1ff,0x8003c000,0x781e0070,0x3c007803,0xc000f03c,0x1e0,0x3c03c0,0x1e00,0x3c000,0x781e0007, 0x80007800,0x780,0x3c07c000,0x7800001e,0x7c0f078,0xf1e03c0,0x780780,0xf000,0x1e07801f,0x3e,0xf000,0xf0003c0,0x781e003,0xcf1c7800, 0x3cf80000,0x7800000f,0x8000003c,0xf0,0x1e0,0x0,0x0,0x0,0x3ffffc01,0xe000780f,0x1e0,0x780fff,0xffe0003c,0x3c000,0xf0078007, 0x80003c00,0xf000,0x7ff8000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x3fff0,0x3c000,0x3c003c0,0xf81f003, 0xc3b87800,0xf80000f,0x1e00001,0xf0000ff0,0xf0,0xff,0xf03fff,0x80000000,0x3fff8001,0xfff1ff00,0x0,0xf000000,0x0,0x18,0xc0, 0x0,0x380000,0x7c0,0x3c00003,0xc00f001f,0xff1ff000,0xf80007ff,0xc7fc3ffe,0x3fe00000,0x0,0x0,0x0,0x1ff000,0x7ffffe1f,0xffffff00, 0x0,0x7f,0xfe000000,0x780f007f,0xff1fff1f,0xf0001f00,0x1e000,0x0,0x780001,0xe0180000,0xf000001c,0xe00fff,0xffc00000,0x7c00, 0xf0000000,0x31c0001,0x80ffff80,0x3e078000,0x1e00,0x7ff80183,0x81c0c000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x3f00000, 0x0,0x7f,0xff00001e,0x7c7ff03,0xc03ff8fe,0x1ffc0f0,0x7e0000,0x7800f00,0x3c007801,0xe003c00f,0x1e0078,0xf003c0,0x7803c07,0x8003c000, 0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x3c0f01e,0x3c078000,0xf03c0007,0x81e0003c, 0xf0001e0,0x78000f00,0x3fc001e,0x7803c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e007f,0xf03fe0,0x7ffff80,0x3ffffc01, 0xffffe00f,0xffff007f,0xfff803ff,0xffc07fff,0xffc0f000,0x1fffff,0xc0fffffe,0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000, 0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e07,0xfffffe1e,0x707803c,0x3c01e0,0x1e00f00,0xf007800, 0x7800f01e,0x1e0007,0x803c0780,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1ff,0xffff8000,0x30f81f00,0xffe1e00f,0x87800000,0x3c00,0x1e00,0x0,0x1e0000,0x0,0x7ff80000, 0x7,0x80001e00,0x3c0001e,0x1f8,0x7,0x83c00780,0x1e0,0x7c000f00,0xf8001e,0x3c001,0xfc1e0000,0x0,0x7fe0,0x0,0xffc,0x3c000,0x781e0070, 0x3ffff803,0xc000783c,0x1e0,0x3c03c0,0x1e00,0x3c000,0x781e0007,0x80007800,0x780,0x3c07c000,0x7800001e,0x380f078,0xf1e03c0, 0x780780,0xf000,0x1e07800f,0x8000001e,0xf000,0xf0003c0,0x3c3c003,0xcf1e7800,0x7c780000,0x7800000f,0x8000003c,0xf0,0x1e0,0x0, 0x0,0x0,0x7f003c01,0xe000780f,0x1e0,0x780fff,0xffe0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x7f7c000,0x1e0000f,0x3c0f01e, 0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0xfff8,0x3c000,0x3c003c0,0x781e003,0xc3b87800,0x1fc00007,0x83e00003,0xe0000ff8,0xf0, 0x1ff,0xc007fe,0x0,0x7fff8001,0xffe3ff00,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x0,0x3c0,0x7800003,0xc00f001f,0xfe3ff000,0xf80007ff, 0x8ffc3ffc,0x7fe00000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x1f,0xff000000,0x3c0f007f,0xff1ffe3f,0xf0003e00,0x1e000,0x0,0x780001, 0xe0180000,0xf000001e,0x1e00fff,0xffc00000,0x3f00,0xf0000000,0x31c0001,0x80ffff80,0x1f03c000,0x1e00,0x7ff80183,0x81c0c000, 0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x7f,0xff00003c,0xf87f007,0xc03f83ff,0x81fc01f0,0x7c0000,0x7ffff00,0x3ffff801, 0xffffc00f,0xfffe007f,0xfff003ff,0xff807fff,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001, 0xe0001e00,0x3c0f01e,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x7fe001e,0xf003c0f0,0x3c0780,0x1e03c00,0xf01e000, 0x78000780,0x1ffffe,0xf00ff0,0xfe00780,0x7f003c03,0xf801e01f,0xc00f00fe,0x7807f0,0x3c0ffff,0xffc0f000,0x1fffff,0xc0fffffe, 0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00, 0x1e,0xf07803c,0x3c01e0,0x1e00f00,0xf007800,0x7800783e,0x1e0007,0x801e0f80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1ff,0xffff8000,0x307c0801,0xe1f1e00f,0x87000000, 0x3c00,0x1e00,0x0,0x1e0000,0x0,0x7ff80000,0xf,0x1e00,0x3c0001e,0x3f0,0x7,0x83fffffc,0x1e0,0x7c000f00,0xf0001e,0x3c000,0x3e0000, 0x0,0x1ffc,0x1fffff,0xf0007ff0,0x3c000,0x781e0070,0x7ffffc03,0xc000781e,0x1e0,0x7803c0,0x1e00,0x3c000,0x781e0007,0x80007800, 0x780,0x3c03e000,0x7800001e,0xf078,0x79e03c0,0x780780,0xf000,0x1e078007,0x8000000f,0xf000,0xf0003c0,0x3c3c001,0xee0ef000, 0xf87c0000,0x7800001f,0x3c,0x78,0x1e0,0x0,0x0,0x0,0x7c003c01,0xe000780f,0x1e0,0x780f00,0x3c,0x3c000,0xf0078007,0x80003c00, 0xf000,0x7e3e000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x1ffc,0x3c000,0x3c003c0,0x781e003,0xe3b8f800, 0x1fc00007,0x83c00007,0xc00000fc,0xf0,0x3e0,0x8001f8,0x0,0x7800000,0xffc7fe00,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x0,0x1e0, 0xf000003,0xc00f000f,0xfc7fe001,0xf00003ff,0x1ff81ff8,0xffc00000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x3,0xff800000,0x1e0f0078, 0xffc7f,0xe0007c00,0x1e000,0x0,0x780001,0xe0180000,0xf000000e,0x1c00007,0x80000000,0x1f81,0xe0000000,0x38e0003,0x80000000, 0xf81f000,0x1e00,0x7ff801c3,0x80e1c000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0xf8,0x1f070007,0xc03803ff,0xc1c001f0, 0xf80000,0xfffff00,0x7ffff803,0xffffc01f,0xfffe00ff,0xfff007ff,0xffc07fff,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000, 0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f00f,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0xf9f001e,0xf003c0f0, 0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1ffffc,0xf003f8,0xf800780,0x7c003c03,0xe001e01f,0xf00f8,0x7807c0,0x3c0fc1e,0xf000, 0x1e0000,0xf00000,0x7800000,0x3c000000,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078, 0xf0003c0,0x78001e00,0x1e,0x1e07803c,0x3c01e0,0x1e00f00,0xf007800,0x7800783c,0x1e0007,0x801e0f00,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0xffff8000,0x303c0001, 0xc071e007,0xcf000000,0x3c00,0x1e00,0x0,0x1e0000,0x0,0x0,0xf,0xf00,0x780001e,0x7e0,0x7,0x83fffffc,0x1e0,0x7c000f00,0x1f0001e, 0x3c000,0x3c0000,0x0,0x3ff,0x801fffff,0xf003ff80,0x3c000,0x781e0070,0x7ffffc03,0xc000781e,0x1e0,0x7803c0,0x1e00,0x1e000,0x781e0007, 0x80007800,0x780,0x3c01f000,0x7800001e,0xf078,0x79e03c0,0xf00780,0xf000,0x3e078007,0xc000000f,0xf000,0xf0003c0,0x3c3c001, 0xee0ef000,0xf03e0000,0x7800003e,0x3c,0x78,0x1e0,0x0,0x0,0x0,0xf8003c01,0xe000780f,0x1e0,0x780f00,0x3c,0x3c000,0xf0078007, 0x80003c00,0xf000,0x7c3e000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0xfc,0x3c000,0x3c003c0,0x3c3e001,0xe7b8f000, 0x3fe00007,0xc7c0000f,0xc000003e,0xf0,0x7c0,0x0,0x0,0x7c00000,0x7fcffc00,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x0,0x1e0,0x1e000003, 0xc00f0007,0xfcffc003,0xe00001ff,0x3ff00ff9,0xff800000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x0,0x1f800000,0xf0f0078,0x7fcff, 0xc000fc00,0x1e000,0x0,0x780001,0xe0180000,0xf000000f,0x87c00007,0x80000000,0xfe3,0xe0000000,0x18780c3,0x0,0x7c0f800,0x1e00, 0xc3,0x80e18000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0x1f0,0x3e00000f,0xc0000303,0xe00003f0,0xf00000,0xfffff80, 0x7ffffc03,0xffffe01f,0xffff00ff,0xfff807ff,0xffc07fff,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000, 0x3c000001,0xe0001e00,0x780f00f,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00,0x1f0f801f,0xe00780f0,0x3c0780,0x1e03c00, 0xf01e000,0x78000780,0x1ffff8,0xf000f8,0x1f000780,0xf8003c07,0xc001e03e,0xf01f0,0x780f80,0x3c1f01e,0xf000,0x1e0000,0xf00000, 0x7800000,0x3c000000,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00, 0x1e,0x3c07803c,0x3c01e0,0x1e00f00,0xf007800,0x78007c7c,0x1e0007,0x801f1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x81c00000,0x303c0003,0x8039e003,0xef000000, 0x3c00,0x1e00,0x0,0x1e0000,0x0,0x0,0x1e,0xf00,0x780001e,0xfc0,0x7,0x83fffffc,0x1e0,0x3c000f00,0x1e0001e,0x3c000,0x3c0000, 0x0,0x7f,0xe01fffff,0xf00ffc00,0x3c000,0x781f00f0,0x7ffffc03,0xc000781e,0x1e0,0x7803c0,0x1e00,0x1e000,0x781e0007,0x80007800, 0x780,0x3c01f000,0x7800001e,0xf078,0x7de01e0,0xf00780,0x7800,0x3c078003,0xc000000f,0xf000,0xf0003c0,0x3e7c001,0xee0ef001, 0xf01e0000,0x7800003e,0x3c,0x3c,0x1e0,0x0,0x0,0x0,0xf0003c01,0xe000780f,0x1e0,0x780f00,0x3c,0x3c000,0xf0078007,0x80003c00, 0xf000,0x781f000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x3e,0x3c000,0x3c003c0,0x3c3c001,0xe71cf000,0x7df00003, 0xc780000f,0x8000003e,0xf0,0x780,0x0,0x0,0x3c00000,0x3fcff800,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x1f00fc,0x1e0,0x1e000001, 0xe00f0003,0xfcff8003,0xe00000ff,0x3fe007f9,0xff000000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x0,0x7c00000,0xf0f0078,0x3fcff,0x8000f800, 0x1e000,0x0,0x780001,0xe0180000,0xf000001f,0xffe00007,0x8000003c,0x7ff,0xc0000000,0x1c3ffc7,0x0,0x3e07c00,0x1e00,0xe3,0x80738000, 0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0x3e0,0x7c00001d,0xc0000001,0xe0000770,0x1f00000,0xfffff80,0x7ffffc03, 0xffffe01f,0xffff00ff,0xfff807ff,0xffc07fff,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001, 0xe0001e00,0x780f00f,0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,0x3e07c01f,0xc00780f0,0x3c0780,0x1e03c00,0xf01e000, 0x78000780,0x1fffc0,0xf0007c,0x1e000780,0xf0003c07,0x8001e03c,0xf01e0,0x780f00,0x3c1e01e,0xf000,0x1e0000,0xf00000,0x7800000, 0x3c000000,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00,0x1e,0x7807803c, 0x3c01e0,0x1e00f00,0xf007800,0x78003c78,0x1e0007,0x800f1e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x83c00000,0x303c0003,0x8039e001,0xee000000,0x1e00,0x3c00, 0x0,0x1e0000,0x0,0x0,0x1e,0xf00,0x780001e,0x1f80,0x7,0x83fffffc,0x1e0,0x3c000f00,0x1e0001e,0x3c000,0x3c0000,0x0,0x1f,0xfc1fffff, 0xf07ff000,0x0,0x780f00f0,0x78003c03,0xc000781e,0x1e0,0xf803c0,0x1e00,0x1e000,0x781e0007,0x80007800,0x780,0x3c00f800,0x7800001e, 0xf078,0x3de01e0,0xf00780,0x7800,0x3c078003,0xe000000f,0xf000,0xf0003c0,0x1e78001,0xfe0ff003,0xe01f0000,0x7800007c,0x3c,0x3c, 0x1e0,0x0,0x0,0x0,0xf0007c01,0xe000f80f,0x800001e0,0xf80f00,0x3c,0x1e001,0xf0078007,0x80003c00,0xf000,0x780f800,0x1e0000f, 0x3c0f01e,0x1e03c0,0x1f00780,0x3e0f000,0x7c003c00,0x1e,0x3c000,0x3c003c0,0x3c3c001,0xe71cf000,0xf8f80003,0xe780001f,0x1e, 0xf0,0x780,0x0,0x0,0x3c00000,0x1ffff000,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x3bc1de,0x1e0,0xf000001,0xe00f0001,0xffff0007,0xc000007f, 0xffc003ff,0xfe000000,0x0,0x0,0x0,0xfe000,0x0,0x0,0x0,0x0,0x3c00000,0x1e0f0078,0x1ffff,0x1f000,0x1e000,0x0,0x780000,0xf0180000, 0xf000001f,0xfff00007,0x8000003c,0x1ff,0x80000000,0xe0ff0e,0x0,0x1f03e00,0x1e00,0x70,0x70000,0x0,0x78,0x0,0x0,0x0,0x3c003c0, 0xe1c00,0x0,0x0,0x0,0x7c0,0xf8000019,0xc0000000,0xe0000670,0x1e00000,0xf000780,0x78003c03,0xc001e01e,0xf00f0,0x780780,0x3c0f807, 0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0xf80f007,0xbc03c001,0xe01e000f, 0xf00078,0x78003c0,0x3c001e00,0x7c03e00f,0x800780f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e0000,0xf0003c,0x1e000f80, 0xf0007c07,0x8003e03c,0x1f01e0,0xf80f00,0x7c1e01e,0xf800,0x1e0000,0xf00000,0x7800000,0x3c000000,0x780000,0x3c00000,0x1e000000, 0xf0001e00,0x7803c00,0x3c078003,0xe03c001f,0x1e000f8,0xf0007c0,0x78003e00,0x1f8001f,0xf00f803c,0x3c01e0,0x1e00f00,0xf007800, 0x78003e78,0x1e000f,0x800f9e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x3c00000,0x303c0003,0x8039f001,0xfe000000,0x1e00,0x3c00,0x0,0x1e0000,0x0,0x0,0x3c,0xf00, 0x780001e,0x3f00,0x7,0x80000780,0x3e0,0x3e000f00,0x3c0001e,0x3c000,0x7c0000,0x0,0x3,0xfe000000,0xff8000,0x0,0x3c0f81f0,0xf0001e03, 0xc000780f,0x1e0,0xf003c0,0x1e00,0xf000,0x781e0007,0x80007800,0x780,0x3c007c00,0x7800001e,0xf078,0x3de01e0,0xf00780,0x7800, 0x3c078001,0xe000000f,0xf000,0xf0003c0,0x1e78001,0xfc07f003,0xe00f0000,0x78000078,0x3c,0x1e,0x1e0,0x0,0x0,0x0,0xf0007c01, 0xf000f007,0x800000f0,0xf80780,0x3c,0x1e001,0xf0078007,0x80003c00,0xf000,0x7807c00,0x1e0000f,0x3c0f01e,0x1e01e0,0x1e007c0, 0x3c07800,0x7c003c00,0x1e,0x3c000,0x3c007c0,0x1e78001,0xe71df000,0xf8f80001,0xef80003e,0x1e,0xf0,0x780,0x0,0x0,0x3c00000, 0xfffe000,0x0,0x3e000000,0x0,0x18,0x7fff,0xc0000000,0x60c306,0x1e0,0x7800001,0xe00f0000,0xfffe0007,0x8000003f,0xff8001ff, 0xfc000000,0x0,0x0,0x0,0x7c000,0x0,0x0,0x0,0x0,0x3c00000,0x3c0f0078,0xfffe,0x3e000,0x1e000,0x0,0x780000,0xf0180000,0xf000003c, 0xfcf80007,0x8000003c,0x7f,0x0,0x70001c,0x0,0xf81f00,0x0,0x38,0xe0000,0x0,0x0,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0xf81, 0xf0000039,0xc0000000,0xe0000e70,0x1e00000,0x1e0003c0,0xf0001e07,0x8000f03c,0x781e0,0x3c0f00,0x1e0f007,0x8000f000,0x78000, 0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0xf00f007,0xbc03c001,0xe01e000f,0xf00078,0x78003c0, 0x3c001e00,0xf801f00f,0x800780f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e0000,0xf0003c,0x1e000f80,0xf0007c07,0x8003e03c, 0x1f01e0,0xf80f00,0x7c1e01e,0x7800,0xf0000,0x780000,0x3c00000,0x1e000000,0x780000,0x3c00000,0x1e000000,0xf0000f00,0xf003c00, 0x3c03c003,0xc01e001e,0xf000f0,0x7800780,0x3c003c00,0x1f8000f,0xe00f003c,0x7c01e0,0x3e00f00,0x1f007800,0xf8001ef8,0x1f000f, 0x7be00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0xf,0x3c00000,0x307c0003,0x8038f000,0xfc000000,0x1e00,0x3c00,0x0,0x1e0000,0xfc0000,0x0,0x7e00003c,0x780,0xf00001e, 0x7e00,0xf,0x80000780,0x3c0,0x3e001e00,0x3c0001f,0x7c000,0x780007,0xe000003f,0x0,0xfe000000,0xfe0000,0x0,0x3c07c3f0,0xf0001e03, 0xc000f80f,0x800001e0,0x1f003c0,0x1e00,0xf000,0x781e0007,0x80007800,0x4000f80,0x3c003c00,0x7800001e,0xf078,0x1fe01f0,0x1f00780, 0x7c00,0x7c078001,0xf000001f,0xf000,0xf0003c0,0x1e78001,0xfc07f007,0xc00f8000,0x780000f8,0x3c,0x1e,0x1e0,0x0,0x0,0x0,0xf0007c01, 0xf000f007,0xc00000f0,0xf80780,0x3c,0x1f003,0xf0078007,0x80003c00,0xf000,0x7807c00,0x1e0000f,0x3c0f01e,0x1e01e0,0x1e007c0, 0x3c07800,0x7c003c00,0x1e,0x3c000,0x3c007c0,0x1e78000,0xfe0fe001,0xf07c0001,0xef00007c,0x1e,0xf0,0x780,0x0,0x0,0x1e00000, 0x7cfc000,0xfc00000,0x3c00000f,0xc3f00000,0x18,0x7fff,0xc0000000,0x406303,0x3e0,0x3c00001,0xf00f0000,0x7cfc000f,0x8000001f, 0x3f0000f9,0xf8000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x780700f8,0x7cfc,0x7c000,0x1e000,0x0,0x780000,0xf8180000, 0xf0000070,0x3c0007,0x8000003c,0x3f,0x80000000,0x3c0078,0x0,0x780f00,0x0,0x1e,0x3c0000,0x0,0x0,0x0,0x0,0x0,0x3e007c0,0xe1c00, 0x0,0x0,0x0,0xf01,0xe0000071,0xc0000000,0xe0001c70,0x1e00000,0x1e0003c0,0xf0001e07,0x8000f03c,0x781e0,0x3c0f00,0x1e0f007, 0x8000f800,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x1f00f003,0xfc03e003,0xe01f001f, 0xf800f8,0x7c007c0,0x3e003e01,0xf000f80f,0xf00f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e0000,0xf0003c,0x1e000f80,0xf0007c07, 0x8003e03c,0x1f01e0,0xf80f00,0x7c1e01e,0x7c00,0xf0000,0x780000,0x3c00000,0x1e000000,0x780000,0x3c00000,0x1e000000,0xf0000f00, 0xf003c00,0x3c03c003,0xc01e001e,0xf000f0,0x7800780,0x3c003c00,0x1f8000f,0xc00f003c,0x7c01e0,0x3e00f00,0x1f007800,0xf8001ef0, 0x1f000f,0x7bc00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x780000,0xf,0x3800040,0x30780003,0x8038f800,0x78000000,0x1e00,0x3c00,0x0,0x1e0000,0xfc0000,0x0,0x7e000078, 0x780,0x1f00001e,0xfc00,0x20001f,0x780,0x80007c0,0x1f001e00,0x7c0000f,0x78000,0xf80007,0xe000003f,0x0,0x1e000000,0xf00000, 0x3c000,0x3c03fff0,0xf0001e03,0xc001f007,0x800101e0,0x7e003c0,0x1e00,0x7800,0x781e0007,0x80007800,0x6000f00,0x3c003e00,0x7800001e, 0xf078,0x1fe00f0,0x1e00780,0x3c00,0x78078000,0xf020001e,0xf000,0x7800780,0xff0001,0xfc07f00f,0x8007c000,0x780001f0,0x3c,0xf, 0x1e0,0x0,0x0,0x0,0xf800fc01,0xf801f007,0xc00100f8,0x1f807c0,0x40003c,0xf807,0xf0078007,0x80003c00,0xf000,0x7803e00,0x1f0000f, 0x3c0f01e,0x1e01f0,0x3e007e0,0x7c07c00,0xfc003c00,0x1e,0x3e000,0x3e007c0,0x1ff8000,0xfe0fe003,0xe03e0001,0xff0000fc,0x1e, 0xf0,0x780,0x0,0x0,0x1f00080,0x3cf8000,0xfc00000,0x3c00001f,0x83f00000,0x18,0xc0,0x0,0xc06203,0x40003c0,0x1c00000,0xf80f0000, 0x3cf8001f,0xf,0x3e000079,0xf0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x700780fc,0x3cf8,0xfc000,0x1e000,0x0,0x780000, 0x7c180000,0xf0000020,0x100007,0x8000003c,0xf,0x80000000,0x1f01f0,0x0,0x380700,0x0,0xf,0x80f80000,0x0,0x0,0x0,0x0,0x0,0x3e007c0, 0xe1c00,0x0,0x0,0x0,0xe01,0xc0000071,0xc0000001,0xc0001c70,0x1e00040,0x1e0003c0,0xf0001e07,0x8000f03c,0x781e0,0x3c0f00,0x1e0f007, 0x80007800,0x10078000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x7e00f003,0xfc01e003,0xc00f001e, 0x7800f0,0x3c00780,0x1e003c00,0xe000700f,0x800f0078,0x7803c0,0x3c01e00,0x1e00f000,0xf0000780,0x1e0000,0xf0003c,0x1f001f80, 0xf800fc07,0xc007e03e,0x3f01f0,0x1f80f80,0xfc1e01f,0x7c00,0x100f8000,0x807c0004,0x3e00020,0x1f000100,0x780000,0x3c00000,0x1e000000, 0xf0000f80,0x1f003c00,0x3c03e007,0xc01f003e,0xf801f0,0x7c00f80,0x3e007c00,0x1f8000f,0x801f003e,0x7c01f0,0x3e00f80,0x1f007c00, 0xf8001ff0,0x1f801f,0x7fc00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0xf,0x7800078,0x31f80001,0xc070fc00,0xfc000000,0x1e00,0x7c00,0x0,0x1e0000,0xfc0000,0x0,0x7e000078, 0x7c0,0x1f00001e,0x1f000,0x38003f,0x780,0xe000f80,0x1f803e00,0x780000f,0x800f8000,0x1f00007,0xe000003f,0x0,0x2000000,0x800000, 0x3c000,0x3e01ff71,0xf0001f03,0xc007f007,0xc00301e0,0x1fc003c0,0x1e00,0x7c00,0x781e0007,0x80007800,0x7801f00,0x3c001f00,0x7800001e, 0xf078,0xfe00f8,0x3e00780,0x3e00,0xf8078000,0xf838003e,0xf000,0x7c00f80,0xff0000,0xfc07e00f,0x8003c000,0x780001e0,0x3c,0xf, 0x1e0,0x0,0x0,0x0,0xf801fc01,0xfc03e003,0xe003007c,0x3f803e0,0x1c0003c,0xfc0f,0xf0078007,0x80003c00,0xf000,0x7801f00,0xf8000f, 0x3c0f01e,0x1e00f8,0x7c007f0,0xf803e01,0xfc003c00,0x8003e,0x1f000,0x1e00fc0,0xff0000,0xfe0fe007,0xc01f0000,0xfe0000f8,0x1e, 0xf0,0x780,0x0,0x0,0xf80180,0x1cf0000,0x1f800000,0x3c00001f,0x83e00000,0x18,0xc0,0x0,0xc06203,0x70007c0,0xe00000,0x7e0f0000, 0x1cf0001e,0x7,0x3c000039,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100,0x7c00000,0xe00780fc,0x2001cf0,0xf8000,0x1e000,0x0, 0x780000,0x7e182000,0xf0000000,0x7,0x8000003c,0x7,0xc0000000,0x7ffc0,0x0,0x180300,0x0,0x3,0xffe00000,0x0,0x0,0x0,0x0,0x0, 0x3f00fc0,0xe1c00,0x0,0x0,0x0,0xc01,0x800000e1,0xc0000003,0xc0003870,0x1f001c0,0x3e0003e1,0xf0001f0f,0x8000f87c,0x7c3e0,0x3e1f00, 0x1f1e007,0x80007c00,0x30078000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e03,0xfc00f001,0xfc01f007, 0xc00f803e,0x7c01f0,0x3e00f80,0x1f007c00,0x4000201f,0xc01f007c,0xf803e0,0x7c01f00,0x3e00f801,0xf0000780,0x1e0000,0xf0007c, 0x1f003f80,0xf801fc07,0xc00fe03e,0x7f01f0,0x3f80f80,0x1fc1f03f,0x803e00,0x3007c003,0x803e001c,0x1f000e0,0xf800700,0x780000, 0x3c00000,0x1e000000,0xf00007c0,0x3e003c00,0x3c01f00f,0x800f807c,0x7c03e0,0x3e01f00,0x1f00f800,0x1f80007,0xc03e001e,0xfc00f0, 0x7e00780,0x3f003c01,0xf8000fe0,0x1fc03e,0x3f800,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0x780007f,0xfff00001,0xe0f07f03,0xfe000000,0xf00,0x7800,0x0, 0x1e0000,0xfc0000,0x0,0x7e0000f0,0x3f0,0x7e000fff,0xfc03ffff,0xf83f00fe,0x780,0xfc03f80,0xfc0fc00,0xf800007,0xe03f0018,0x7e00007, 0xe000003f,0x0,0x0,0x0,0x3c000,0x1e007c71,0xe0000f03,0xffffe003,0xf01f01ff,0xff8003ff,0xffe01e00,0x3f01,0xf81e0007,0x803ffff0, 0x7e03f00,0x3c000f00,0x7ffffe1e,0xf078,0xfe007e,0xfc00780,0x1f83,0xf0078000,0x783f00fe,0xf000,0x3f03f00,0xff0000,0xfc07e01f, 0x3e000,0x780003ff,0xfffc003c,0x7,0x800001e0,0x0,0x0,0x0,0x7e07fc01,0xfe07e001,0xf80f007e,0x7f801f8,0xfc0003c,0x7ffe,0xf0078007, 0x807ffffe,0xf000,0x7801f00,0xfff00f,0x3c0f01e,0x1e00fc,0xfc007f8,0x1f803f03,0xfc003c00,0xf80fc,0x1fff0,0x1f83fc0,0xff0000, 0xfc07e007,0xc01f0000,0xfe0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0xfe0780,0xfe0000,0x1f000000,0x3c00001f,0x7c00e03,0x81c00018, 0xc0,0x0,0x406203,0x7e01fc0,0x700000,0x7fffff80,0xfe0003f,0xffffc003,0xf800001f,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f0, 0x1f800001,0xc007c1fe,0x6000fe0,0x1ffffe,0x1e000,0x0,0x780000,0x3f98e03f,0xffff8000,0x7,0x8000003c,0x7,0xc0000000,0xfe00, 0x0,0x80100,0x0,0x0,0x7f000000,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3f83fe8,0xe1c00,0x0,0x0,0x0,0x801,0xc1,0xc0000007,0x80003070, 0xfc0fc0,0x3c0001e1,0xe0000f0f,0x7878,0x3c3c0,0x1e1e00,0xf1e007,0xffc03f01,0xf007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003, 0xffff001f,0xfff800ff,0xffc01fff,0xf800f001,0xfc00fc1f,0x8007e0fc,0x3f07e0,0x1f83f00,0xfc1f800,0x1f,0xf07e003f,0x3f001f8, 0x1f800fc0,0xfc007e07,0xe0000780,0x1e0000,0xf301f8,0xfc0ff80,0x7e07fc03,0xf03fe01f,0x81ff00fc,0xff807e0,0x7fc0f87f,0x81801f80, 0xf003f01f,0x801f80fc,0xfc07e0,0x7e03f00,0xfffffc07,0xffffe03f,0xffff01ff,0xfff807e0,0x7e003c00,0x3c01f81f,0x800fc0fc,0x7e07e0, 0x3f03f00,0x1f81f800,0x1f8000f,0xe07e001f,0x83fc00fc,0x1fe007e0,0xff003f07,0xf8000fe0,0x1fe07e,0x3f800,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0x780007f, 0xffe00000,0xffe03fff,0xdf000000,0xf00,0x7800,0x0,0x0,0xfc0000,0x0,0x7e0000f0,0x1ff,0xfc000fff,0xfc03ffff,0xf83ffffc,0x780, 0xfffff00,0x7fff800,0xf000007,0xffff001f,0xffe00007,0xe000003f,0x0,0x0,0x0,0x3c000,0x1e000001,0xe0000f03,0xffffc001,0xffff01ff, 0xff0003ff,0xffe01e00,0x1fff,0xf81e0007,0x803ffff0,0x7fffe00,0x3c000f80,0x7ffffe1e,0xf078,0xfe003f,0xff800780,0xfff,0xf0078000, 0x7c3ffffc,0xf000,0x3ffff00,0xff0000,0xf803e01e,0x1e000,0x780003ff,0xfffc003c,0x7,0x800001e0,0x0,0x0,0x0,0x7fffbc01,0xffffc000, 0xffff003f,0xfff800ff,0xffc0003c,0x3ffe,0xf0078007,0x807ffffe,0xf000,0x7800f80,0x7ff00f,0x3c0f01e,0x1e007f,0xff8007ff,0xff001fff, 0xbc003c00,0xffffc,0x1fff0,0x1fffbc0,0xff0000,0x7c07c00f,0x800f8000,0x7e0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x7fff80,0x7c0000, 0x1f000000,0x3c00001e,0x7c00f07,0xc1e00018,0xc0,0x0,0x60e303,0x7ffff80,0x380000,0x3fffff80,0x7c0003f,0xffffc001,0xf000000f, 0x80000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0xff800003,0x8003ffff,0xfe0007c0,0x1ffffe,0x1e000,0x0,0x780000,0x1fffe03f,0xffff8000, 0x7,0x8000003c,0x3,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3fffdf8,0xe1c00,0x0,0x0,0x0,0x0,0x1c1, 0xc000000f,0x7070,0x7fffc0,0x3c0001e1,0xe0000f0f,0x7878,0x3c3c0,0x1e1e00,0xf1e007,0xffc01fff,0xf007ffff,0xc03ffffe,0x1fffff0, 0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xf000f001,0xfc007fff,0x3fff8,0x1fffc0,0xfffe00,0x7fff000,0x3b,0xfffc003f, 0xfff001ff,0xff800fff,0xfc007fff,0xe0000780,0x1e0000,0xf3fff8,0xffff780,0x7fffbc03,0xfffde01f,0xffef00ff,0xff7807ff,0xfbc0ffff, 0xff800fff,0xf001ffff,0x800ffffc,0x7fffe0,0x3ffff00,0xfffffc07,0xffffe03f,0xffff01ff,0xfff803ff,0xfc003c00,0x3c00ffff,0x7fff8, 0x3fffc0,0x1fffe00,0xffff000,0x1f,0xfffc001f,0xffbc00ff,0xfde007ff,0xef003fff,0x780007e0,0x1ffffc,0x1f800,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0x700003f, 0xffc00000,0x7fc01fff,0x9f800000,0xf80,0xf800,0x0,0x0,0xfc0000,0x0,0x7e0000f0,0xff,0xf8000fff,0xfc03ffff,0xf83ffff8,0x780, 0xffffe00,0x7fff000,0xf000003,0xfffe001f,0xffc00007,0xe000003f,0x0,0x0,0x0,0x3c000,0xf000003,0xe0000f83,0xffff0000,0xffff01ff, 0xfc0003ff,0xffe01e00,0xfff,0xf01e0007,0x803ffff0,0x7fffc00,0x3c0007c0,0x7ffffe1e,0xf078,0x7e003f,0xff000780,0x7ff,0xe0078000, 0x3c3ffff8,0xf000,0x1fffe00,0x7e0000,0xf803e03e,0x1f000,0x780003ff,0xfffc003c,0x7,0x800001e0,0x0,0x0,0x0,0x3fff3c01,0xefff8000, 0x7ffe001f,0xff78007f,0xff80003c,0x1ffc,0xf0078007,0x807ffffe,0xf000,0x78007c0,0x3ff00f,0x3c0f01e,0x1e003f,0xff0007bf,0xfe000fff, 0xbc003c00,0xffff8,0xfff0,0xfff3c0,0x7e0000,0x7c07c01f,0x7c000,0x7c0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x3fff80,0x380000, 0x3e000000,0x7c00003e,0x7801f07,0xc1e00018,0xc0,0x0,0x39c1ce,0x7ffff00,0x1c0000,0xfffff80,0x380003f,0xffffc000,0xe0000007, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0xff000007,0x1ffcf,0xfe000380,0x1ffffe,0x1e000,0x0,0x780000,0xfffe03f,0xffff8000,0x7, 0x8000003c,0x3,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3dffdf8,0xe1c00,0x0,0x0,0x0,0x0,0x381, 0xc000001e,0xe070,0x7fff80,0x7c0001f3,0xe0000f9f,0x7cf8,0x3e7c0,0x1f3e00,0xfbe007,0xffc00fff,0xf007ffff,0xc03ffffe,0x1fffff0, 0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xc000f000,0xfc007ffe,0x3fff0,0x1fff80,0xfffc00,0x7ffe000,0x79,0xfff8001f, 0xffe000ff,0xff0007ff,0xf8003fff,0xc0000780,0x1e0000,0xf3fff0,0x7ffe780,0x3fff3c01,0xfff9e00f,0xffcf007f,0xfe7803ff,0xf3c07ff3, 0xff8007ff,0xe000ffff,0x7fff8,0x3fffc0,0x1fffe00,0xfffffc07,0xffffe03f,0xffff01ff,0xfff801ff,0xf8003c00,0x3c007ffe,0x3fff0, 0x1fff80,0xfffc00,0x7ffe000,0x1d,0xfff8000f,0xff3c007f,0xf9e003ff,0xcf001ffe,0x780007c0,0x1efff8,0x1f000,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0xf000003, 0xfe000000,0x1f000fff,0xfc00000,0x780,0xf000,0x0,0x0,0xf80000,0x0,0x7e0001e0,0x7f,0xf0000fff,0xfc03ffff,0xf81ffff0,0x780, 0x7fff800,0x1ffe000,0x1f000000,0xfff8001f,0xff000007,0xe000003e,0x0,0x0,0x0,0x3c000,0xf800003,0xc0000783,0xfff80000,0x3ffe01ff, 0xe00003ff,0xffe01e00,0x7ff,0xc01e0007,0x803ffff0,0x3fff800,0x3c0003c0,0x7ffffe1e,0xf078,0x7e000f,0xfe000780,0x3ff,0xc0078000, 0x3e1fffe0,0xf000,0x7ff800,0x7e0000,0xf803e07c,0xf800,0x780003ff,0xfffc003c,0x3,0xc00001e0,0x0,0x0,0x0,0xffe3c01,0xe7ff0000, 0x3ffc000f,0xfe78003f,0xfe00003c,0x7f0,0xf0078007,0x807ffffe,0xf000,0x78003e0,0xff00f,0x3c0f01e,0x1e001f,0xfe00079f,0xfc0007ff, 0x3c003c00,0x7ffe0,0x1ff0,0x7fe3c0,0x7e0000,0x7c07c03e,0x3e000,0x7c0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0xfff00,0x100000, 0x3e000000,0x7800003c,0xf800f07,0xc1e00018,0xc0,0x0,0x1f80fc,0x3fffc00,0xc0000,0x3ffff80,0x100003f,0xffffc000,0x40000002, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xfc000006,0xff87,0xfc000100,0x1ffffe,0x1e000,0x0,0x780000,0x3ffc03f,0xffff8000,0x7, 0x8000003c,0x3,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3dff9f8,0xe1c00,0x0,0x0,0x0,0x0,0x3ff, 0xf800003c,0xfffe,0x1ffe00,0x780000f3,0xc000079e,0x3cf0,0x1e780,0xf3c00,0x7bc007,0xffc003ff,0xe007ffff,0xc03ffffe,0x1fffff0, 0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01ffc,0xf000,0xfc001ffc,0xffe0,0x7ff00,0x3ff800,0x1ffc000,0x70,0xfff00007, 0xff80003f,0xfc0001ff,0xe0000fff,0x780,0x1e0000,0xf3ffe0,0x1ffc780,0xffe3c00,0x7ff1e003,0xff8f001f,0xfc7800ff,0xe3c03fe1, 0xff0003ff,0xc0007ffc,0x3ffe0,0x1fff00,0xfff800,0xfffffc07,0xffffe03f,0xffff01ff,0xfff800ff,0xf0003c00,0x3c003ffc,0x1ffe0, 0xfff00,0x7ff800,0x3ffc000,0x38,0xfff00007,0xfe3c003f,0xf1e001ff,0x8f000ffc,0x780007c0,0x1e7ff0,0x1f000,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000, 0x1fc,0x0,0x780,0xf000,0x0,0x0,0x1f80000,0x0,0x1e0,0x1f,0xc0000000,0x0,0x1ff80,0x0,0xffc000,0x7f8000,0x0,0x3fe00007,0xfc000000, 0x7e,0x0,0x0,0x0,0x0,0x7c00000,0x0,0x0,0xff00000,0x0,0x0,0xfe,0x0,0x0,0x3fc000,0x0,0x0,0x0,0x3,0xf8000000,0xff,0xc0000000, 0x1ff00,0x0,0x1fe000,0x0,0x0,0x0,0x0,0x3c,0x3,0xc00001e0,0x0,0x0,0x0,0x3f80000,0x1fc0000,0x7f00003,0xf8000007,0xf0000000, 0x0,0xf0000000,0x0,0xf000,0x0,0x0,0x0,0x7,0xf8000787,0xf00001fc,0x3c000000,0x7f80,0x0,0x1f8000,0x0,0x0,0x0,0x7c000000,0x1e, 0xf0,0x780,0x0,0x0,0x3fc00,0x0,0x3c000000,0x7800003c,0xf000601,0xc00018,0xc0,0x0,0x0,0x3fe000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0xf,0xf0000000,0x7e03,0xf0000000,0x0,0x0,0x0,0x0,0xfe0000,0x0,0x0,0x3c,0x2007,0x80000000,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c7e0f0,0xe1c00,0x0,0x3800000,0x0,0x0,0x3ff,0xf8000078,0xfffe,0x7f800,0x0,0x0,0x0,0x0, 0x0,0x0,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f0,0x3f80,0x1fc00,0xfe000,0x7f0000,0x70,0x3fc00001,0xfe00000f,0xf000007f, 0x800003fc,0x0,0x0,0xff00,0x7f0000,0x3f80000,0x1fc00000,0xfe000007,0xf000003f,0x80001f80,0xfc00007f,0xfe0,0x7f00,0x3f800, 0x1fc000,0x0,0x0,0x0,0x3f,0xc0000000,0xff0,0x7f80,0x3fc00,0x1fe000,0xff0000,0x78,0x3fc00001,0xf800000f,0xc000007e,0x3f0,0x7c0, 0x1e1fc0,0x1f000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0x3c0,0x1e000,0x0,0x0,0x1f00000,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0x3e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xe0000000,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x3c,0x1,0xe00001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0000000,0x0,0xf000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000, 0x0,0x0,0x0,0x0,0x0,0x0,0x78000000,0x1e,0xf0,0x780,0x0,0x0,0x0,0x0,0x3c000000,0x78000078,0xf000000,0x18,0xc0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x3c0f,0x80000000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0xe1c00,0x0,0x1800000,0x0,0x0,0x3ff,0xf80000f0,0xfffe,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x780,0x1e0000,0x1e000,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000, 0x0,0x0,0x3c0,0x1e000,0x0,0x0,0x1f00000,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0x1f80000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xf0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x1,0xe00001e0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xe0000000,0x0,0xf000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x0,0xf8000000, 0x1f,0xf0,0xf80,0x0,0x0,0x0,0x0,0x78000000,0xf8000078,0x1e000000,0x8,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x3fff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x3c00000,0xe1c00,0x0,0x1c00000,0x0,0x0,0x1,0xc00001e0,0x70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80,0x1e0000,0x3e000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0x1e0,0x3c000,0x0,0x0,0x1f00000, 0x0,0x780,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0xfe0100,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0xf8000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf0000000,0xf0007fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xe0000000, 0x0,0xf000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x0,0xf0000000,0x1f,0x800000f0,0x1f80,0x0,0x0,0x0,0x0, 0x78000000,0xf0000070,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x3ffe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0xe1c00,0x0,0xe00000, 0x0,0x0,0x1,0xc00003ff,0xe0000070,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0xf00,0x1e0000,0x3c000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0x1e0,0x7c000,0x0,0x0,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x0,0x7fff80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78000000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf0000000,0x7fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4003,0xe0000000,0x0,0x1f000,0x0,0x0, 0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x1,0xf0000000,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x0,0x0,0x70000001,0xf00000e0, 0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000, 0x0,0x0,0x3c,0xff8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0xe1c00,0x0,0xe00000,0x0,0x0,0x1,0xc00003ff, 0xe0000070,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f00,0x1e0000, 0x7c000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0xf0,0x78000,0x0,0x0,0x3e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x0, 0x0,0x0,0x0,0x1fff80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f, 0xf0000000,0x7fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780f,0xc0000000,0x0,0x3e000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0, 0x0,0x0,0x0,0x0,0x3,0xe0000000,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x0,0x0,0xf0000103,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x21e00000,0x0,0x0,0x1,0xc00003ff,0xe0000070,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10f, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10f,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e00,0x1e0000,0xf8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0, 0xf8,0xf8000,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x0,0x0,0x0,0x0,0x1fe00,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf0000000,0x7fe0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x7fff,0xc0000000,0x0,0x3ffe000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x7f,0xe0000000,0x7,0xfc0000f0, 0x3fe00,0x0,0x0,0x0,0x0,0x600001ff,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0, 0x3fe00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x7fe00,0x1e0000,0x1ff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x7fff,0x80000000,0x0,0x3ffc000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0, 0x0,0x0,0x0,0x0,0x7f,0xc0000000,0x0,0xfc0000f0,0x3f000,0x0,0x0,0x0,0x0,0x1ff,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x3fc00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fe,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fe,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7fc00,0x1e0000,0x1ff0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x3ffe,0x0,0x0,0x3ff8000,0x0,0x0,0x0, 0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x7f,0x80000000,0x0,0xf0,0x0,0x0,0x0,0x0,0x0,0x1ff,0x80000000,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x3f800000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fc,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fc,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f800,0x1e0000,0x1fe0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f8,0x0,0x0,0x3fe0000, 0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x7e,0x0,0x0,0xf0,0x0,0x0,0x0,0x0,0x0,0xfe,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e000,0x1e0000,0x1f80000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 }; // Definition of a 40x38 'danger' color logo. const unsigned char logo40x38[4576] = { 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 200,200,200,91,49,124,118,124,71,32,124,95,49,56,114,52,82,121,0}; //! Display a warning message. /** \param format is a C-string describing the format of the message, as in std::printf(). **/ inline void warn(const char *format, ...) { if (cimg::exception_mode()>=1) { char message[8192]; cimg_std::va_list ap; va_start(ap,format); cimg_std::vsprintf(message,format,ap); va_end(ap); #ifdef cimg_strict_warnings throw CImgWarningException(message); #else cimg_std::fprintf(cimg_stdout,"\n%s# CImg Warning%s :\n%s\n",cimg::t_red,cimg::t_normal,message); #endif } } // Execute an external system command. /** \note This function is similar to std::system() and is here because using the std:: version on Windows may open undesired consoles. **/ inline int system(const char *const command, const char *const module_name=0) { #if cimg_OS==2 PROCESS_INFORMATION pi; STARTUPINFO si; cimg_std::memset(&pi,0,sizeof(PROCESS_INFORMATION)); cimg_std::memset(&si,0,sizeof(STARTUPINFO)); GetStartupInfo(&si); si.cb = sizeof(si); si.wShowWindow = SW_HIDE; si.dwFlags |= SW_HIDE; const BOOL res = CreateProcess((LPCTSTR)module_name,(LPTSTR)command,0,0,FALSE,0,0,0,&si,&pi); if (res) { WaitForSingleObject(pi.hProcess, INFINITE); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return 0; } else #endif return cimg_std::system(command); return module_name?0:1; } //! Return a reference to a temporary variable of type T. template inline T& temporary(const T&) { static T temp; return temp; } //! Exchange values of variables \p a and \p b. template inline void swap(T& a, T& b) { T t = a; a = b; b = t; } //! Exchange values of variables (\p a1,\p a2) and (\p b1,\p b2). template inline void swap(T1& a1, T1& b1, T2& a2, T2& b2) { cimg::swap(a1,b1); cimg::swap(a2,b2); } //! Exchange values of variables (\p a1,\p a2,\p a3) and (\p b1,\p b2,\p b3). template inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3) { cimg::swap(a1,b1,a2,b2); cimg::swap(a3,b3); } //! Exchange values of variables (\p a1,\p a2,...,\p a4) and (\p b1,\p b2,...,\p b4). template inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4) { cimg::swap(a1,b1,a2,b2,a3,b3); cimg::swap(a4,b4); } //! Exchange values of variables (\p a1,\p a2,...,\p a5) and (\p b1,\p b2,...,\p b5). template inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5) { cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4); cimg::swap(a5,b5); } //! Exchange values of variables (\p a1,\p a2,...,\p a6) and (\p b1,\p b2,...,\p b6). template 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) { cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5); cimg::swap(a6,b6); } //! Exchange values of variables (\p a1,\p a2,...,\p a7) and (\p b1,\p b2,...,\p b7). template 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, T7& a7, T7& b7) { cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6); cimg::swap(a7,b7); } //! Exchange values of variables (\p a1,\p a2,...,\p a8) and (\p b1,\p b2,...,\p b8). template 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, T7& a7, T7& b7, T8& a8, T8& b8) { cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6,a7,b7); cimg::swap(a8,b8); } //! Return the current endianness of the CPU. /** \return \c false for "Little Endian", \c true for "Big Endian". **/ inline bool endianness() { const int x = 1; return ((unsigned char*)&x)[0]?false:true; } //! Invert endianness of a memory buffer. template inline void invert_endianness(T* const buffer, const unsigned int size) { if (size) switch (sizeof(T)) { case 1 : break; case 2 : { for (unsigned short *ptr = (unsigned short*)buffer+size; ptr>(unsigned short*)buffer; ) { const unsigned short val = *(--ptr); *ptr = (unsigned short)((val>>8)|((val<<8))); }} break; case 4 : { for (unsigned int *ptr = (unsigned int*)buffer+size; ptr>(unsigned int*)buffer; ) { const unsigned int val = *(--ptr); *ptr = (val>>24)|((val>>8)&0xff00)|((val<<8)&0xff0000)|(val<<24); }} break; default : { for (T* ptr = buffer+size; ptr>buffer; ) { unsigned char *pb = (unsigned char*)(--ptr), *pe = pb + sizeof(T); for (int i=0; i<(int)sizeof(T)/2; ++i) swap(*(pb++),*(--pe)); }} } } //! Invert endianness of a single variable. template inline T& invert_endianness(T& a) { invert_endianness(&a,1); return a; } //! Get the value of a system timer with a millisecond precision. inline unsigned long time() { #if cimg_OS==1 struct timeval st_time; gettimeofday(&st_time,0); return (unsigned long)(st_time.tv_usec/1000 + st_time.tv_sec*1000); #elif cimg_OS==2 static SYSTEMTIME st_time; GetSystemTime(&st_time); return (unsigned long)(st_time.wMilliseconds + 1000*(st_time.wSecond + 60*(st_time.wMinute + 60*st_time.wHour))); #else return 0; #endif } //! Sleep for a certain numbers of milliseconds. /** This function frees the CPU ressources during the sleeping time. It may be used to temporize your program properly, without wasting CPU time. **/ inline void sleep(const unsigned int milliseconds) { #if cimg_OS==1 struct timespec tv; tv.tv_sec = milliseconds/1000; tv.tv_nsec = (milliseconds%1000)*1000000; nanosleep(&tv,0); #elif cimg_OS==2 Sleep(milliseconds); #endif } inline unsigned int _sleep(const unsigned int milliseconds, unsigned long& timer) { if (!timer) timer = cimg::time(); const unsigned long current_time = cimg::time(); if (current_time>=timer+milliseconds) { timer = current_time; return 0; } const unsigned long time_diff = timer + milliseconds - current_time; timer = current_time + time_diff; cimg::sleep(time_diff); return (unsigned int)time_diff; } //! Wait for a certain number of milliseconds since the last call. /** This function is equivalent to sleep() but the waiting time is computed with regard to the last call of wait(). It may be used to temporize your program properly. **/ inline unsigned int wait(const unsigned int milliseconds) { static unsigned long timer = 0; if (!timer) timer = cimg::time(); return _sleep(milliseconds,timer); } // Use a specific srand initialization to avoid multi-threads to have to the // same series of random numbers (executed only once for a single program). inline void srand() { static bool first_time = true; if (first_time) { cimg_std::srand(cimg::time()); unsigned char *const rand_ptr = new unsigned char[1+cimg_std::rand()%2048]; cimg_std::srand((unsigned int)cimg_std::rand() + *(unsigned int*)(void*)rand_ptr); delete[] rand_ptr; first_time = false; } } //! Return a left bitwise-rotated number. template inline const T rol(const T a, const unsigned int n=1) { return n?(T)((a<>((sizeof(T)<<3)-n))):a; } //! Return a right bitwise-rotated number. template inline const T ror(const T a, const unsigned int n=1) { return n?(T)((a>>n)|(a<<((sizeof(T)<<3)-n))):a; } //! Return the absolute value of a number. /** \note This function is different from std::abs() or std::fabs() because it is able to consider a variable of any type, without cast needed. **/ template inline T abs(const T a) { return a>=0?a:-a; } inline bool abs(const bool a) { return a; } inline unsigned char abs(const unsigned char a) { return a; } inline unsigned short abs(const unsigned short a) { return a; } inline unsigned int abs(const unsigned int a) { return a; } inline unsigned long abs(const unsigned long a) { return a; } inline double abs(const double a) { return cimg_std::fabs(a); } inline float abs(const float a) { return (float)cimg_std::fabs((double)a); } inline int abs(const int a) { return cimg_std::abs(a); } //! Return the square of a number. template inline T sqr(const T val) { return val*val; } //! Return 1 + log_10(x). inline int xln(const int x) { return x>0?(int)(1+cimg_std::log10((double)x)):1; } //! Return the minimum value between two numbers. template inline typename cimg::superset::type min(const t1& a, const t2& b) { typedef typename cimg::superset::type t1t2; return (t1t2)(a<=b?a:b); } //! Return the minimum value between three numbers. template inline typename cimg::superset2::type min(const t1& a, const t2& b, const t3& c) { typedef typename cimg::superset2::type t1t2t3; return (t1t2t3)cimg::min(cimg::min(a,b),c); } //! Return the minimum value between four numbers. template inline typename cimg::superset3::type min(const t1& a, const t2& b, const t3& c, const t4& d) { typedef typename cimg::superset3::type t1t2t3t4; return (t1t2t3t4)cimg::min(cimg::min(a,b,c),d); } //! Return the maximum value between two numbers. template inline typename cimg::superset::type max(const t1& a, const t2& b) { typedef typename cimg::superset::type t1t2; return (t1t2)(a>=b?a:b); } //! Return the maximum value between three numbers. template inline typename cimg::superset2::type max(const t1& a, const t2& b, const t3& c) { typedef typename cimg::superset2::type t1t2t3; return (t1t2t3)cimg::max(cimg::max(a,b),c); } //! Return the maximum value between four numbers. template inline typename cimg::superset3::type max(const t1& a, const t2& b, const t3& c, const t4& d) { typedef typename cimg::superset3::type t1t2t3t4; return (t1t2t3t4)cimg::max(cimg::max(a,b,c),d); } //! Return the sign of a number. template inline T sign(const T x) { return (x<0)?(T)(-1):(x==0?(T)0:(T)1); } //! Return the nearest power of 2 higher than a given number. template inline unsigned long nearest_pow2(const T x) { unsigned long i = 1; while (x>i) i<<=1; return i; } //! Return the modulo of a number. /** \note This modulo function accepts negative and floating-points modulo numbers, as well as variable of any type. **/ template inline T mod(const T& x, const T& m) { const double dx = (double)x, dm = (double)m; if (x<0) { return (T)(dm+dx+dm*cimg_std::floor(-dx/dm)); } return (T)(dx-dm*cimg_std::floor(dx/dm)); } inline int mod(const bool x, const bool m) { return m?(x?1:0):0; } inline int mod(const char x, const char m) { return x>=0?x%m:(x%m?m+x%m:0); } inline int mod(const short x, const short m) { return x>=0?x%m:(x%m?m+x%m:0); } inline int mod(const int x, const int m) { return x>=0?x%m:(x%m?m+x%m:0); } inline int mod(const long x, const long m) { return x>=0?x%m:(x%m?m+x%m:0); } inline int mod(const unsigned char x, const unsigned char m) { return x%m; } inline int mod(const unsigned short x, const unsigned short m) { return x%m; } inline int mod(const unsigned int x, const unsigned int m) { return x%m; } inline int mod(const unsigned long x, const unsigned long m) { return x%m; } //! Return the minmod of two numbers. /** minmod(\p a,\p b) is defined to be : - minmod(\p a,\p b) = min(\p a,\p b), if \p a and \p b have the same sign. - minmod(\p a,\p b) = 0, if \p a and \p b have different signs. **/ template inline T minmod(const T a, const T b) { return a*b<=0?0:(a>0?(a=1.0); return x1*cimg_std::sqrt((-2*cimg_std::log(w))/w); } //! Return a random variable following a Poisson distribution of parameter z. inline unsigned int prand(const double z) { if (z<=1.0e-10) return 0; if (z>100.0) return (unsigned int)((std::sqrt(z) * cimg::grand()) + z); unsigned int k = 0; const double y = std::exp(-z); for (double s = 1.0; s>=y; ++k) s*=cimg::rand(); return k-1; } //! Return a rounded number. /** \param x is the number to be rounded. \param y is the rounding precision. \param rounding_type defines the type of rounding (0=nearest, -1=backward, 1=forward). **/ inline double round(const double x, const double y, const int rounding_type=0) { if (y<=0) return x; const double delta = cimg::mod(x,y); if (delta==0.0) return x; const double backward = x - delta, forward = backward + y; return rounding_type<0?backward:(rounding_type>0?forward:(2*deltaabsb) { const double tmp = absb/absa; return absa*cimg_std::sqrt(1.0+tmp*tmp); } else { const double tmp = absa/absb; return (absb==0?0:absb*cimg_std::sqrt(1.0+tmp*tmp)); } } //! Remove the 'case' of an ASCII character. inline char uncase(const char x) { return (char)((x<'A'||x>'Z')?x:x-'A'+'a'); } //! Remove the 'case' of a C string. /** Acts in-place. **/ inline void uncase(char *const string) { if (string) for (char *ptr = string; *ptr; ++ptr) *ptr = uncase(*ptr); } //! Read a float number from a C-string. /** \note This function is quite similar to std::atof(), but that it allows the retrieval of fractions as in "1/2". **/ inline float atof(const char *const str) { float x = 0,y = 1; if (!str) return 0; else { cimg_std::sscanf(str,"%g/%g",&x,&y); return x/y; } } //! Compute the length of a C-string. /** \note This function is similar to std::strlen() and is here because some old compilers do not define the std:: version. **/ inline int strlen(const char *const s) { if (!s) return -1; int k = 0; for (const char *ns = s; *ns; ++ns) ++k; return k; } //! Compare the first \p n characters of two C-strings. /** \note This function is similar to std::strncmp() and is here because some old compilers do not define the std:: version. **/ inline int strncmp(const char *const s1, const char *const s2, const int l) { if (!s1) return s2?-1:0; const char *ns1 = s1, *ns2 = s2; int k, diff = 0; for (k = 0; kstd::strncasecmp() and is here because some old compilers do not define the std:: version. **/ inline int strncasecmp(const char *const s1, const char *const s2, const int l) { if (!s1) return s2?-1:0; const char *ns1 = s1, *ns2 = s2; int k, diff = 0; for (k = 0; kstd::strcmp() and is here because some old compilers do not define the std:: version. **/ inline int strcmp(const char *const s1, const char *const s2) { const int l1 = cimg::strlen(s1), l2 = cimg::strlen(s2); return cimg::strncmp(s1,s2,1+(l1std::strcasecmp() and is here because some old compilers do not define the std:: version. **/ inline int strcasecmp(const char *const s1, const char *const s2) { const int l1 = cimg::strlen(s1), l2 = cimg::strlen(s2); return cimg::strncasecmp(s1,s2,1+(l1=0 && s[l]!=c; --l) {} return l; } //! Remove useless delimiters on the borders of a C-string inline bool strpare(char *const s, const char delimiter=' ', const bool symmetric=false) { if (!s) return false; const int l = cimg::strlen(s); int p, q; if (symmetric) for (p = 0, q = l-1; pp && s[q]==delimiter; ) --q; } const int n = q - p + 1; if (n!=l) { cimg_std::memmove(s,s+p,n); s[n] = '\0'; return true; } return false; } //! Remove useless spaces and symmetric delimiters ', " and ` from a C-string. inline void strclean(char *const s) { if (!s) return; strpare(s,' ',false); for (bool need_iter = true; need_iter; ) { need_iter = false; need_iter |= strpare(s,'\'',true); need_iter |= strpare(s,'\"',true); need_iter |= strpare(s,'`',true); } } //! Replace explicit escape sequences '\x' in C-strings (where x in [ntvbrfa?'"0]). inline void strescape(char *const s) { #define cimg_strescape(ci,co) case ci: *nd = co; break; char *ns, *nd; for (ns = nd = s; *ns; ++ns, ++nd) if (*ns=='\\') switch (*(++ns)) { cimg_strescape('n','\n'); cimg_strescape('t','\t'); cimg_strescape('v','\v'); cimg_strescape('b','\b'); cimg_strescape('r','\r'); cimg_strescape('f','\f'); cimg_strescape('a','\a'); cimg_strescape('\\','\\'); cimg_strescape('\?','\?'); cimg_strescape('\'','\''); cimg_strescape('\"','\"'); cimg_strescape('\0','\0'); } else *nd = *ns; *nd = 0; } //! Compute the basename of a filename. inline const char* basename(const char *const s) { return (cimg_OS!=2)?(s?s+1+cimg::strfind(s,'/'):0):(s?s+1+cimg::strfind(s,'\\'):0); } // Generate a random filename. inline const char* filenamerand() { static char id[9] = { 0,0,0,0,0,0,0,0,0 }; cimg::srand(); for (unsigned int k=0; k<8; ++k) { const int v = (int)cimg_std::rand()%3; id[k] = (char)(v==0?('0'+(cimg_std::rand()%10)):(v==1?('a'+(cimg_std::rand()%26)):('A'+(cimg_std::rand()%26)))); } return id; } // Convert filename into a Windows-style filename. inline void winformat_string(char *const s) { if (s && s[0]) { #if cimg_OS==2 char *const ns = new char[MAX_PATH]; if (GetShortPathNameA(s,ns,MAX_PATH)) cimg_std::strcpy(s,ns); #endif } } //! Return or set path to store temporary files. inline const char* temporary_path(const char *const user_path=0, const bool reinit_path=false) { #define _cimg_test_temporary_path(p) \ if (!path_found) { \ cimg_std::sprintf(st_path,"%s",p); \ cimg_std::sprintf(tmp,"%s%s%s",st_path,cimg_OS==2?"\\":"/",filetmp); \ if ((file=cimg_std::fopen(tmp,"wb"))!=0) { cimg_std::fclose(file); cimg_std::remove(tmp); path_found = true; } \ } static char *st_path = 0; if (reinit_path && st_path) { delete[] st_path; st_path = 0; } if (user_path) { if (!st_path) st_path = new char[1024]; cimg_std::memset(st_path,0,1024); cimg_std::strncpy(st_path,user_path,1023); } else if (!st_path) { st_path = new char[1024]; cimg_std::memset(st_path,0,1024); bool path_found = false; char tmp[1024], filetmp[512]; cimg_std::FILE *file = 0; cimg_std::sprintf(filetmp,"%s.tmp",cimg::filenamerand()); char *tmpPath = getenv("TMP"); if (!tmpPath) { tmpPath = getenv("TEMP"); winformat_string(tmpPath); } if (tmpPath) _cimg_test_temporary_path(tmpPath); #if cimg_OS==2 _cimg_test_temporary_path("C:\\WINNT\\Temp"); _cimg_test_temporary_path("C:\\WINDOWS\\Temp"); _cimg_test_temporary_path("C:\\Temp"); _cimg_test_temporary_path("C:"); _cimg_test_temporary_path("D:\\WINNT\\Temp"); _cimg_test_temporary_path("D:\\WINDOWS\\Temp"); _cimg_test_temporary_path("D:\\Temp"); _cimg_test_temporary_path("D:"); #else _cimg_test_temporary_path("/tmp"); _cimg_test_temporary_path("/var/tmp"); #endif if (!path_found) { st_path[0]='\0'; cimg_std::strcpy(tmp,filetmp); if ((file=cimg_std::fopen(tmp,"wb"))!=0) { cimg_std::fclose(file); cimg_std::remove(tmp); path_found = true; } } if (!path_found) throw CImgIOException("cimg::temporary_path() : Unable to find a temporary path accessible for writing\n" "you have to set the macro 'cimg_temporary_path' to a valid path where you have writing access :\n" "#define cimg_temporary_path \"path\" (before including 'CImg.h')"); } return st_path; } // Return or set path to the "Program files/" directory (windows only). #if cimg_OS==2 inline const char* programfiles_path(const char *const user_path=0, const bool reinit_path=false) { static char *st_path = 0; if (reinit_path && st_path) { delete[] st_path; st_path = 0; } if (user_path) { if (!st_path) st_path = new char[1024]; cimg_std::memset(st_path,0,1024); cimg_std::strncpy(st_path,user_path,1023); } else if (!st_path) { st_path = new char[MAX_PATH]; cimg_std::memset(st_path,0,MAX_PATH); // Note : in the following line, 0x26 = CSIDL_PROGRAM_FILES (not defined on every compiler). #if !defined(__INTEL_COMPILER) if (!SHGetSpecialFolderPathA(0,st_path,0x0026,false)) { const char *pfPath = getenv("PROGRAMFILES"); if (pfPath) cimg_std::strncpy(st_path,pfPath,MAX_PATH-1); else cimg_std::strcpy(st_path,"C:\\PROGRA~1"); } #else cimg_std::strcpy(st_path,"C:\\PROGRA~1"); #endif } return st_path; } #endif //! Return or set path to the ImageMagick's \c convert tool. inline const char* imagemagick_path(const char *const user_path=0, const bool reinit_path=false) { static char *st_path = 0; if (reinit_path && st_path) { delete[] st_path; st_path = 0; } if (user_path) { if (!st_path) st_path = new char[1024]; cimg_std::memset(st_path,0,1024); cimg_std::strncpy(st_path,user_path,1023); } else if (!st_path) { st_path = new char[1024]; cimg_std::memset(st_path,0,1024); bool path_found = false; cimg_std::FILE *file = 0; #if cimg_OS==2 const char *pf_path = programfiles_path(); if (!path_found) { cimg_std::sprintf(st_path,".\\convert.exe"); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } } { for (int k=32; k>=10 && !path_found; --k) { cimg_std::sprintf(st_path,"%s\\IMAGEM~1.%.2d-\\convert.exe",pf_path,k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=9; k>=0 && !path_found; --k) { cimg_std::sprintf(st_path,"%s\\IMAGEM~1.%d-Q\\convert.exe",pf_path,k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=32; k>=0 && !path_found; --k) { cimg_std::sprintf(st_path,"%s\\IMAGEM~1.%d\\convert.exe",pf_path,k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=32; k>=10 && !path_found; --k) { cimg_std::sprintf(st_path,"%s\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",pf_path,k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=9; k>=0 && !path_found; --k) { cimg_std::sprintf(st_path,"%s\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",pf_path,k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=32; k>=0 && !path_found; --k) { cimg_std::sprintf(st_path,"%s\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",pf_path,k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=32; k>=10 && !path_found; --k) { cimg_std::sprintf(st_path,"C:\\IMAGEM~1.%.2d-\\convert.exe",k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=9; k>=0 && !path_found; --k) { cimg_std::sprintf(st_path,"C:\\IMAGEM~1.%d-Q\\convert.exe",k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=32; k>=0 && !path_found; --k) { cimg_std::sprintf(st_path,"C:\\IMAGEM~1.%d\\convert.exe",k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=32; k>=10 && !path_found; --k) { cimg_std::sprintf(st_path,"C:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=9; k>=0 && !path_found; --k) { cimg_std::sprintf(st_path,"C:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=32; k>=0 && !path_found; --k) { cimg_std::sprintf(st_path,"C:\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=32; k>=10 && !path_found; --k) { cimg_std::sprintf(st_path,"D:\\IMAGEM~1.%.2d-\\convert.exe",k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=9; k>=0 && !path_found; --k) { cimg_std::sprintf(st_path,"D:\\IMAGEM~1.%d-Q\\convert.exe",k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=32; k>=0 && !path_found; --k) { cimg_std::sprintf(st_path,"D:\\IMAGEM~1.%d\\convert.exe",k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=32; k>=10 && !path_found; --k) { cimg_std::sprintf(st_path,"D:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=9; k>=0 && !path_found; --k) { cimg_std::sprintf(st_path,"D:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=32; k>=0 && !path_found; --k) { cimg_std::sprintf(st_path,"D:\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} if (!path_found) cimg_std::strcpy(st_path,"convert.exe"); #else if (!path_found) { cimg_std::sprintf(st_path,"./convert"); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } } if (!path_found) cimg_std::strcpy(st_path,"convert"); #endif winformat_string(st_path); } return st_path; } //! Return path of the GraphicsMagick's \c gm tool. inline const char* graphicsmagick_path(const char *const user_path=0, const bool reinit_path=false) { static char *st_path = 0; if (reinit_path && st_path) { delete[] st_path; st_path = 0; } if (user_path) { if (!st_path) st_path = new char[1024]; cimg_std::memset(st_path,0,1024); cimg_std::strncpy(st_path,user_path,1023); } else if (!st_path) { st_path = new char[1024]; cimg_std::memset(st_path,0,1024); bool path_found = false; cimg_std::FILE *file = 0; #if cimg_OS==2 const char* pf_path = programfiles_path(); if (!path_found) { cimg_std::sprintf(st_path,".\\gm.exe"); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } } { for (int k=32; k>=10 && !path_found; --k) { cimg_std::sprintf(st_path,"%s\\GRAPHI~1.%.2d-\\gm.exe",pf_path,k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=9; k>=0 && !path_found; --k) { cimg_std::sprintf(st_path,"%s\\GRAPHI~1.%d-Q\\gm.exe",pf_path,k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=32; k>=0 && !path_found; --k) { cimg_std::sprintf(st_path,"%s\\GRAPHI~1.%d\\gm.exe",pf_path,k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=32; k>=10 && !path_found; --k) { cimg_std::sprintf(st_path,"%s\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",pf_path,k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=9; k>=0 && !path_found; --k) { cimg_std::sprintf(st_path,"%s\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",pf_path,k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=32; k>=0 && !path_found; --k) { cimg_std::sprintf(st_path,"%s\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",pf_path,k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=32; k>=10 && !path_found; --k) { cimg_std::sprintf(st_path,"C:\\GRAPHI~1.%.2d-\\gm.exe",k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=9; k>=0 && !path_found; --k) { cimg_std::sprintf(st_path,"C:\\GRAPHI~1.%d-Q\\gm.exe",k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=32; k>=0 && !path_found; --k) { cimg_std::sprintf(st_path,"C:\\GRAPHI~1.%d\\gm.exe",k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=32; k>=10 && !path_found; --k) { cimg_std::sprintf(st_path,"C:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=9; k>=0 && !path_found; --k) { cimg_std::sprintf(st_path,"C:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=32; k>=0 && !path_found; --k) { cimg_std::sprintf(st_path,"C:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=32; k>=10 && !path_found; --k) { cimg_std::sprintf(st_path,"D:\\GRAPHI~1.%.2d-\\gm.exe",k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=9; k>=0 && !path_found; --k) { cimg_std::sprintf(st_path,"D:\\GRAPHI~1.%d-Q\\gm.exe",k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=32; k>=0 && !path_found; --k) { cimg_std::sprintf(st_path,"D:\\GRAPHI~1.%d\\gm.exe",k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=32; k>=10 && !path_found; --k) { cimg_std::sprintf(st_path,"D:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=9; k>=0 && !path_found; --k) { cimg_std::sprintf(st_path,"D:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} { for (int k=32; k>=0 && !path_found; --k) { cimg_std::sprintf(st_path,"D:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } }} if (!path_found) cimg_std::strcpy(st_path,"gm.exe"); #else if (!path_found) { cimg_std::sprintf(st_path,"./gm"); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } } if (!path_found) cimg_std::strcpy(st_path,"gm"); #endif winformat_string(st_path); } return st_path; } //! Return or set path of the \c XMedcon tool. inline const char* medcon_path(const char *const user_path=0, const bool reinit_path=false) { static char *st_path = 0; if (reinit_path && st_path) { delete[] st_path; st_path = 0; } if (user_path) { if (!st_path) st_path = new char[1024]; cimg_std::memset(st_path,0,1024); cimg_std::strncpy(st_path,user_path,1023); } else if (!st_path) { st_path = new char[1024]; cimg_std::memset(st_path,0,1024); bool path_found = false; cimg_std::FILE *file = 0; #if cimg_OS==2 const char* pf_path = programfiles_path(); if (!path_found) { cimg_std::sprintf(st_path,".\\medcon.bat"); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } } if (!path_found) { cimg_std::sprintf(st_path,".\\medcon.exe"); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } } if (!path_found) { cimg_std::sprintf(st_path,"%s\\XMedCon\\bin\\medcon.bat",pf_path); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } } if (!path_found) { cimg_std::sprintf(st_path,"%s\\XMedCon\\bin\\medcon.exe",pf_path); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } } if (!path_found) cimg_std::strcpy(st_path,"medcon.bat"); #else if (!path_found) { cimg_std::sprintf(st_path,"./medcon"); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } } if (!path_found) cimg_std::strcpy(st_path,"medcon"); #endif winformat_string(st_path); } return st_path; } //! Return or set path to the 'ffmpeg' command. inline const char *ffmpeg_path(const char *const user_path=0, const bool reinit_path=false) { static char *st_path = 0; if (reinit_path && st_path) { delete[] st_path; st_path = 0; } if (user_path) { if (!st_path) st_path = new char[1024]; cimg_std::memset(st_path,0,1024); cimg_std::strncpy(st_path,user_path,1023); } else if (!st_path) { st_path = new char[1024]; cimg_std::memset(st_path,0,1024); bool path_found = false; cimg_std::FILE *file = 0; #if cimg_OS==2 if (!path_found) { cimg_std::sprintf(st_path,".\\ffmpeg.exe"); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } } if (!path_found) cimg_std::strcpy(st_path,"ffmpeg.exe"); #else if (!path_found) { cimg_std::sprintf(st_path,"./ffmpeg"); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } } if (!path_found) cimg_std::strcpy(st_path,"ffmpeg"); #endif winformat_string(st_path); } return st_path; } //! Return or set path to the 'gzip' command. inline const char *gzip_path(const char *const user_path=0, const bool reinit_path=false) { static char *st_path = 0; if (reinit_path && st_path) { delete[] st_path; st_path = 0; } if (user_path) { if (!st_path) st_path = new char[1024]; cimg_std::memset(st_path,0,1024); cimg_std::strncpy(st_path,user_path,1023); } else if (!st_path) { st_path = new char[1024]; cimg_std::memset(st_path,0,1024); bool path_found = false; cimg_std::FILE *file = 0; #if cimg_OS==2 if (!path_found) { cimg_std::sprintf(st_path,".\\gzip.exe"); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } } if (!path_found) cimg_std::strcpy(st_path,"gzip.exe"); #else if (!path_found) { cimg_std::sprintf(st_path,"./gzip"); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } } if (!path_found) cimg_std::strcpy(st_path,"gzip"); #endif winformat_string(st_path); } return st_path; } //! Return or set path to the 'gunzip' command. inline const char *gunzip_path(const char *const user_path=0, const bool reinit_path=false) { static char *st_path = 0; if (reinit_path && st_path) { delete[] st_path; st_path = 0; } if (user_path) { if (!st_path) st_path = new char[1024]; cimg_std::memset(st_path,0,1024); cimg_std::strncpy(st_path,user_path,1023); } else if (!st_path) { st_path = new char[1024]; cimg_std::memset(st_path,0,1024); bool path_found = false; cimg_std::FILE *file = 0; #if cimg_OS==2 if (!path_found) { cimg_std::sprintf(st_path,".\\gunzip.exe"); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } } if (!path_found) cimg_std::strcpy(st_path,"gunzip.exe"); #else if (!path_found) { cimg_std::sprintf(st_path,"./gunzip"); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } } if (!path_found) cimg_std::strcpy(st_path,"gunzip"); #endif winformat_string(st_path); } return st_path; } //! Return or set path to the 'dcraw' command. inline const char *dcraw_path(const char *const user_path=0, const bool reinit_path=false) { static char *st_path = 0; if (reinit_path && st_path) { delete[] st_path; st_path = 0; } if (user_path) { if (!st_path) st_path = new char[1024]; cimg_std::memset(st_path,0,1024); cimg_std::strncpy(st_path,user_path,1023); } else if (!st_path) { st_path = new char[1024]; cimg_std::memset(st_path,0,1024); bool path_found = false; cimg_std::FILE *file = 0; #if cimg_OS==2 if (!path_found) { cimg_std::sprintf(st_path,".\\dcraw.exe"); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } } if (!path_found) cimg_std::strcpy(st_path,"dcraw.exe"); #else if (!path_found) { cimg_std::sprintf(st_path,"./dcraw"); if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } } if (!path_found) cimg_std::strcpy(st_path,"dcraw"); #endif winformat_string(st_path); } return st_path; } //! Split a filename into two strings 'body' and 'extension'. inline const char *split_filename(const char *const filename, char *const body=0) { if (!filename) { if (body) body[0]='\0'; return 0; } int l = cimg::strfind(filename,'.'); if (l>=0) { if (body) { cimg_std::strncpy(body,filename,l); body[l]='\0'; }} else { if (body) cimg_std::strcpy(body,filename); l = (int)cimg::strlen(filename)-1; } return filename+l+1; } //! Create a numbered version of a filename. inline char* number_filename(const char *const filename, const int number, const unsigned int n, char *const string) { if (!filename) { if (string) string[0]='\0'; return 0; } char format[1024],body[1024]; const char *ext = cimg::split_filename(filename,body); if (n>0) cimg_std::sprintf(format,"%s_%%.%ud.%s",body,n,ext); else cimg_std::sprintf(format,"%s_%%d.%s",body,ext); cimg_std::sprintf(string,format,number); return string; } //! Open a file, and check for possible errors. inline cimg_std::FILE *fopen(const char *const path, const char *const mode) { if(!path || !mode) throw CImgArgumentException("cimg::fopen() : File '%s', cannot open with mode '%s'.", path?path:"(null)",mode?mode:"(null)"); if (path[0]=='-') return (mode[0]=='r')?stdin:stdout; cimg_std::FILE *dest = cimg_std::fopen(path,mode); if (!dest) throw CImgIOException("cimg::fopen() : File '%s', cannot open file %s", path,mode[0]=='r'?"for reading.":(mode[0]=='w'?"for writing.":"."),path); return dest; } //! Close a file, and check for possible errors. inline int fclose(cimg_std::FILE *file) { if (!file) warn("cimg::fclose() : Can't close (null) file"); if (!file || file==stdin || file==stdout) return 0; const int errn = cimg_std::fclose(file); if (errn!=0) warn("cimg::fclose() : Error %d during file closing",errn); return errn; } //! Try to guess the image format of a filename, using its magick numbers. inline const char *file_type(cimg_std::FILE *const file, const char *const filename) { static const char *const _pnm = "pnm", *const _bmp = "bmp", *const _gif = "gif", *const _jpeg = "jpeg", *const _off = "off", *const _pan = "pan", *const _png = "png", *const _tiff = "tiff"; if (!filename && !file) throw CImgArgumentException("cimg::file_type() : Cannot load (null) filename."); cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); const char *ftype = 0, *head; char header[2048], item[1024]; const unsigned char *const uheader = (unsigned char*)header; int err; const unsigned int siz = (unsigned int)cimg_std::fread(header,2048,1,nfile); // Read first 2048 bytes. if (!file) cimg::fclose(nfile); if (!ftype) { // Check for BMP format. if (header[0]=='B' && header[1]=='M') ftype = _bmp; } if (!ftype) { // Check for GIF format. if (header[0]=='G' && header[1]=='I' && header[2]=='F' && header[3]=='8' && header[5]=='a' && (header[4]=='7' || header[4]=='9')) ftype = _gif; } if (!ftype) { // Check for JPEG format. if (uheader[0]==0xFF && uheader[1]==0xD8 && uheader[2]==0xFF) ftype = _jpeg; } if (!ftype) { // Check for OFF format. if (header[0]=='O' && header[1]=='F' && header[2]=='F' && header[3]=='\n') ftype = _off; } if (!ftype) { // Check for PAN format. if (header[0]=='P' && header[1]=='A' && header[2]=='N' && header[3]=='D' && header[4]=='O' && header[5]=='R' && header[6]=='E') ftype = _pan; } if (!ftype) { // Check for PNG format. if (uheader[0]==0x89 && uheader[1]==0x50 && uheader[2]==0x4E && uheader[3]==0x47 && uheader[4]==0x0D && uheader[5]==0x0A && uheader[6]==0x1A && uheader[7]==0x0A) ftype = _png; } if (!ftype) { // Check for PNM format. head = header; while (head inline int fread(T *const ptr, const unsigned int nmemb, cimg_std::FILE *stream) { if (!ptr || nmemb<=0 || !stream) throw CImgArgumentException("cimg::fread() : Can't read %u x %u bytes of file pointer '%p' in buffer '%p'", nmemb,sizeof(T),stream,ptr); const unsigned long wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T); unsigned int toread = nmemb, alread = 0, ltoread = 0, lalread = 0; do { ltoread = (toread*sizeof(T))0); if (toread>0) warn("cimg::fread() : File reading problems, only %u/%u elements read",alread,nmemb); return alread; } //! Write data to a file, and check for possible errors. template inline int fwrite(const T *ptr, const unsigned int nmemb, cimg_std::FILE *stream) { if (!ptr || !stream) throw CImgArgumentException("cimg::fwrite() : Can't write %u x %u bytes of file pointer '%p' from buffer '%p'", nmemb,sizeof(T),stream,ptr); if (nmemb<=0) return 0; const unsigned long wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T); unsigned int towrite = nmemb, alwrite = 0, ltowrite = 0, lalwrite = 0; do { ltowrite = (towrite*sizeof(T))0); if (towrite>0) warn("cimg::fwrite() : File writing problems, only %u/%u elements written",alwrite,nmemb); return alwrite; } inline const char* option(const char *const name, const int argc, const char *const *const argv, const char *defaut, const char *const usage=0) { static bool first = true, visu = false; const char *res = 0; if (first) { first=false; visu = (cimg::option("-h",argc,argv,(char*)0)!=0); visu |= (cimg::option("-help",argc,argv,(char*)0)!=0); visu |= (cimg::option("--help",argc,argv,(char*)0)!=0); } if (!name && visu) { if (usage) { cimg_std::fprintf(cimg_stdout,"\n %s%s%s",cimg::t_red,cimg::basename(argv[0]),cimg::t_normal); cimg_std::fprintf(cimg_stdout," : %s",usage); cimg_std::fprintf(cimg_stdout," (%s, %s)\n\n",__DATE__,__TIME__); } if (defaut) cimg_std::fprintf(cimg_stdout,"%s\n",defaut); } if (name) { if (argc>0) { int k = 0; while (k Operating System : %s%-13s%s %s('cimg_OS'=%d)%s\n", cimg::t_bold, cimg_OS==1?"Unix":(cimg_OS==2?"Windows":"Unknow"), cimg::t_normal,cimg::t_green, cimg_OS, cimg::t_normal); cimg_std::fprintf(cimg_stdout," > CPU endianness : %s%s Endian%s\n", cimg::t_bold, cimg::endianness()?"Big":"Little", cimg::t_normal); #ifdef cimg_use_visualcpp6 cimg_std::fprintf(cimg_stdout," > Using Visual C++ 6.0 : %s%-13s%s %s('cimg_use_visualcpp6' defined)%s\n", cimg::t_bold,"Yes",cimg::t_normal,cimg::t_green,cimg::t_normal); #endif cimg_std::fprintf(cimg_stdout," > Debug messages : %s%-13s%s %s('cimg_debug'=%d)%s\n", cimg::t_bold, cimg_debug==0?"Quiet":(cimg_debug==1?"Console":(cimg_debug==2?"Dialog":(cimg_debug==3?"Console+Warnings":"Dialog+Warnings"))), cimg::t_normal,cimg::t_green, cimg_debug, cimg::t_normal); cimg_std::fprintf(cimg_stdout," > Stricts warnings : %s%-13s%s %s('cimg_strict_warnings' %s)%s\n", cimg::t_bold, #ifdef cimg_strict_warnings "Yes",cimg::t_normal,cimg::t_green,"defined", #else "No",cimg::t_normal,cimg::t_green,"undefined", #endif cimg::t_normal); cimg_std::fprintf(cimg_stdout," > Using VT100 messages : %s%-13s%s %s('cimg_use_vt100' %s)%s\n", cimg::t_bold, #ifdef cimg_use_vt100 "Yes",cimg::t_normal,cimg::t_green,"defined", #else "No",cimg::t_normal,cimg::t_green,"undefined", #endif cimg::t_normal); cimg_std::fprintf(cimg_stdout," > Display type : %s%-13s%s %s('cimg_display'=%d)%s\n", cimg::t_bold, cimg_display==0?"No display": (cimg_display==1?"X11": (cimg_display==2?"Windows GDI": (cimg_display==3?"Carbon":"Unknow"))), cimg::t_normal,cimg::t_green, cimg_display, cimg::t_normal); #if cimg_display==1 cimg_std::fprintf(cimg_stdout," > Using XShm for X11 : %s%-13s%s %s('cimg_use_xshm' %s)%s\n", cimg::t_bold, #ifdef cimg_use_xshm "Yes",cimg::t_normal,cimg::t_green,"defined", #else "No",cimg::t_normal,cimg::t_green,"undefined", #endif cimg::t_normal); cimg_std::fprintf(cimg_stdout," > Using XRand for X11 : %s%-13s%s %s('cimg_use_xrandr' %s)%s\n", cimg::t_bold, #ifdef cimg_use_xrandr "Yes",cimg::t_normal,cimg::t_green,"defined", #else "No",cimg::t_normal,cimg::t_green,"undefined", #endif cimg::t_normal); #endif cimg_std::fprintf(cimg_stdout," > Using OpenMP : %s%-13s%s %s('cimg_use_openmp' %s)%s\n", cimg::t_bold, #ifdef cimg_use_openmp "Yes",cimg::t_normal,cimg::t_green,"defined", #else "No",cimg::t_normal,cimg::t_green,"undefined", #endif cimg::t_normal); cimg_std::fprintf(cimg_stdout," > Using PNG library : %s%-13s%s %s('cimg_use_png' %s)%s\n", cimg::t_bold, #ifdef cimg_use_png "Yes",cimg::t_normal,cimg::t_green,"defined", #else "No",cimg::t_normal,cimg::t_green,"undefined", #endif cimg::t_normal); cimg_std::fprintf(cimg_stdout," > Using JPEG library : %s%-13s%s %s('cimg_use_jpeg' %s)%s\n", cimg::t_bold, #ifdef cimg_use_jpeg "Yes",cimg::t_normal,cimg::t_green,"defined", #else "No",cimg::t_normal,cimg::t_green,"undefined", #endif cimg::t_normal); cimg_std::fprintf(cimg_stdout," > Using TIFF library : %s%-13s%s %s('cimg_use_tiff' %s)%s\n", cimg::t_bold, #ifdef cimg_use_tiff "Yes",cimg::t_normal,cimg::t_green,"defined", #else "No",cimg::t_normal,cimg::t_green,"undefined", #endif cimg::t_normal); cimg_std::fprintf(cimg_stdout," > Using Magick++ library : %s%-13s%s %s('cimg_use_magick' %s)%s\n", cimg::t_bold, #ifdef cimg_use_magick "Yes",cimg::t_normal,cimg::t_green,"defined", #else "No",cimg::t_normal,cimg::t_green,"undefined", #endif cimg::t_normal); cimg_std::fprintf(cimg_stdout," > Using FFTW3 library : %s%-13s%s %s('cimg_use_fftw3' %s)%s\n", cimg::t_bold, #ifdef cimg_use_fftw3 "Yes",cimg::t_normal,cimg::t_green,"defined", #else "No",cimg::t_normal,cimg::t_green,"undefined", #endif cimg::t_normal); cimg_std::fprintf(cimg_stdout," > Using LAPACK library : %s%-13s%s %s('cimg_use_lapack' %s)%s\n", cimg::t_bold, #ifdef cimg_use_lapack "Yes",cimg::t_normal,cimg::t_green,"defined", #else "No",cimg::t_normal,cimg::t_green,"undefined", #endif cimg::t_normal); cimg_std::sprintf(tmp,"\"%.1020s\"",cimg::imagemagick_path()); cimg_std::fprintf(cimg_stdout," > Path of ImageMagick : %s%-13s%s\n", cimg::t_bold, tmp, cimg::t_normal); cimg_std::sprintf(tmp,"\"%.1020s\"",cimg::graphicsmagick_path()); cimg_std::fprintf(cimg_stdout," > Path of GraphicsMagick : %s%-13s%s\n", cimg::t_bold, tmp, cimg::t_normal); cimg_std::sprintf(tmp,"\"%.1020s\"",cimg::medcon_path()); cimg_std::fprintf(cimg_stdout," > Path of 'medcon' : %s%-13s%s\n", cimg::t_bold, tmp, cimg::t_normal); cimg_std::sprintf(tmp,"\"%.1020s\"",cimg::temporary_path()); cimg_std::fprintf(cimg_stdout," > Temporary path : %s%-13s%s\n", cimg::t_bold, tmp, cimg::t_normal); cimg_std::fprintf(cimg_stdout,"\n"); } // Declare LAPACK function signatures if necessary. // #ifdef cimg_use_lapack template inline void getrf(int &N, T *lapA, int *IPIV, int &INFO) { dgetrf_(&N,&N,lapA,&N,IPIV,&INFO); } inline void getrf(int &N, float *lapA, int *IPIV, int &INFO) { sgetrf_(&N,&N,lapA,&N,IPIV,&INFO); } template inline void getri(int &N, T *lapA, int *IPIV, T* WORK, int &LWORK, int &INFO) { dgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO); } inline void getri(int &N, float *lapA, int *IPIV, float* WORK, int &LWORK, int &INFO) { sgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO); } template inline void gesvd(char &JOB, int &M, int &N, T *lapA, int &MN, T *lapS, T *lapU, T *lapV, T *WORK, int &LWORK, int &INFO) { dgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO); } inline void gesvd(char &JOB, int &M, int &N, float *lapA, int &MN, float *lapS, float *lapU, float *lapV, float *WORK, int &LWORK, int &INFO) { sgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO); } template inline void getrs(char &TRANS, int &N, T *lapA, int *IPIV, T *lapB, int &INFO) { int one = 1; dgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO); } inline void getrs(char &TRANS, int &N, float *lapA, int *IPIV, float *lapB, int &INFO) { int one = 1; sgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO); } template inline void syev(char &JOB, char &UPLO, int &N, T *lapA, T *lapW, T *WORK, int &LWORK, int &INFO) { dsyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO); } inline void syev(char &JOB, char &UPLO, int &N, float *lapA, float *lapW, float *WORK, int &LWORK, int &INFO) { ssyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO); } #endif // End of the 'cimg' namespace } /*------------------------------------------------ # # # Definition of mathematical operators and # external functions. # # -------------------------------------------------*/ // // These functions are extern to any classes and can be used for a "functional-style" programming, // such as writting : // cos(img); // instead of img.get_cos(); // // Note that only the arithmetic operators and functions are implemented here. // #ifdef cimg_use_visualcpp6 template inline CImg operator+(const CImg& img, const t val) { return CImg(img,false)+=val; } #else template inline CImg::type> operator+(const CImg& img, const t2 val) { typedef typename cimg::superset::type t1t2; return CImg(img,false)+=val; } #endif #ifdef cimg_use_visualcpp6 template inline CImg operator+(const t val, const CImg& img) { return img + val; } #else template inline CImg::type> operator+(const t1 val, const CImg& img) { return img + val; } #endif #ifdef cimg_use_visualcpp6 template inline CImgList operator+(const CImgList& list, const t val) { return CImgList(list)+=val; } #else template inline CImgList::type> operator+(const CImgList& list, const t2 val) { typedef typename cimg::superset::type t1t2; return CImgList(list)+=val; } #endif #ifdef cimg_use_visualcpp6 template inline CImgList operator+(const t val, const CImgList& list) { return list + val; } #else template inline CImgList::type> operator+(const t1 val, const CImgList& list) { return list + val; } #endif template inline CImg::type> operator+(const CImg& img1, const CImg& img2) { typedef typename cimg::superset::type t1t2; return CImg(img1,false)+=img2; } template inline CImgList::type> operator+(const CImg& img, const CImgList& list) { typedef typename cimg::superset::type t1t2; return CImgList(list)+=img; } template inline CImgList::type> operator+(const CImgList& list, const CImg& img) { return img + list; } template inline CImgList::type> operator+(const CImgList& list1, const CImgList& list2) { typedef typename cimg::superset::type t1t2; return CImgList(list1)+=list2; } #ifdef cimg_use_visualcpp6 template inline CImg operator-(const CImg& img, const t val) { return CImg(img,false)-=val; } #else template inline CImg::type> operator-(const CImg& img, const t2 val) { typedef typename cimg::superset::type t1t2; return CImg(img,false)-=val; } #endif #ifdef cimg_use_visualcpp6 template inline CImg operator-(const t val, const CImg& img) { return CImg(img.width,img.height,img.depth,img.dim,val)-=img; } #else template inline CImg::type> operator-(const t1 val, const CImg& img) { typedef typename cimg::superset::type t1t2; return CImg(img.width,img.height,img.depth,img.dim,(t1t2)val)-=img; } #endif #ifdef cimg_use_visualcpp6 template inline CImgList operator-(const CImgList& list, const t val) { return CImgList(list)-=val; } #else template inline CImgList::type> operator-(const CImgList& list, const t2 val) { typedef typename cimg::superset::type t1t2; return CImgList(list)-=val; } #endif #ifdef cimg_use_visualcpp6 template inline CImgList operator-(const t val, const CImgList& list) { CImgList res(list.size); cimglist_for(res,l) res[l] = val - list[l]; return res; } #else template inline CImgList::type> operator-(const t1 val, const CImgList& list) { typedef typename cimg::superset::type t1t2; CImgList res(list.size); cimglist_for(res,l) res[l] = val - list[l]; return res; } #endif template inline CImg::type> operator-(const CImg& img1, const CImg& img2) { typedef typename cimg::superset::type t1t2; return CImg(img1,false)-=img2; } template inline CImgList::type> operator-(const CImg& img, const CImgList& list) { typedef typename cimg::superset::type t1t2; CImgList res(list.size); cimglist_for(res,l) res[l] = img - list[l]; return res; } template inline CImgList::type> operator-(const CImgList& list, const CImg& img) { typedef typename cimg::superset::type t1t2; return CImgList(list)-=img; } template inline CImgList::type> operator-(const CImgList& list1, const CImgList& list2) { typedef typename cimg::superset::type t1t2; return CImgList(list1)-=list2; } #ifdef cimg_use_visualcpp6 template inline CImg operator*(const CImg& img, const double val) { return CImg(img,false)*=val; } #else template inline CImg::type> operator*(const CImg& img, const t2 val) { typedef typename cimg::superset::type t1t2; return CImg(img,false)*=val; } #endif #ifdef cimg_use_visualcpp6 template inline CImg operator*(const double val, const CImg& img) { return img*val; } #else template inline CImg::type> operator*(const t1 val, const CImg& img) { return img*val; } #endif #ifdef cimg_use_visualcpp6 template inline CImgList operator*(const CImgList& list, const double val) { return CImgList(list)*=val; } #else template inline CImgList::type> operator*(const CImgList& list, const t2 val) { typedef typename cimg::superset::type t1t2; return CImgList(list)*=val; } #endif #ifdef cimg_use_visualcpp6 template inline CImgList operator*(const double val, const CImgList& list) { return list*val; } #else template inline CImgList::type> operator*(const t1 val, const CImgList& list) { return list*val; } #endif template inline CImg::type> operator*(const CImg& img1, const CImg& img2) { typedef typename cimg::superset::type t1t2; if (img1.width!=img2.height) throw CImgArgumentException("operator*() : can't multiply a matrix (%ux%u) by a matrix (%ux%u)", img1.width,img1.height,img2.width,img2.height); CImg res(img2.width,img1.height); t1t2 val; #ifdef cimg_use_openmp #pragma omp parallel for if (img1.size()>=1000 && img2.size()>=1000) private(val) #endif cimg_forXY(res,i,j) { val = 0; cimg_forX(img1,k) val+=img1(k,j)*img2(i,k); res(i,j) = val; } return res; } template inline CImgList::type> operator*(const CImg& img, const CImgList& list) { typedef typename cimg::superset::type t1t2; CImgList res(list.size); cimglist_for(res,l) res[l] = img*list[l]; return res; } template inline CImgList::type> operator*(const CImgList& list, const CImg& img) { typedef typename cimg::superset::type t1t2; CImgList res(list.size); cimglist_for(res,l) res[l] = list[l]*img; return res; } template inline CImgList::type> operator*(const CImgList& list1, const CImgList& list2) { typedef typename cimg::superset::type t1t2; CImgList res(cimg::min(list1.size,list2.size)); cimglist_for(res,l) res[l] = list1[l]*list2[l]; return res; } #ifdef cimg_use_visualcpp6 template inline CImg operator/(const CImg& img, const double val) { return CImg(img,false)/=val; } #else template inline CImg::type> operator/(const CImg& img, const t2 val) { typedef typename cimg::superset::type t1t2; return CImg(img,false)/=val; } #endif #ifdef cimg_use_visualcpp6 template inline CImg operator/(const double val, CImg& img) { return val*img.get_invert(); } #else template inline CImg::type> operator/(const t1 val, CImg& img) { return val*img.get_invert(); } #endif #ifdef cimg_use_visualcpp6 template inline CImgList operator/(const CImgList& list, const double val) { return CImgList(list)/=val; } #else template inline CImgList::type> operator/(const CImgList& list, const t2 val) { typedef typename cimg::superset::type t1t2; return CImgList(list)/=val; } #endif #ifdef cimg_use_visualcpp6 template inline CImgList operator/(const double val, const CImgList& list) { CImgList res(list.size); cimglist_for(res,l) res[l] = val/list[l]; return res; } #else template inline CImgList::type> operator/(const t1 val, const CImgList& list) { typedef typename cimg::superset::type t1t2; CImgList res(list.size); cimglist_for(res,l) res[l] = val/list[l]; return res; } #endif template inline CImg::type> operator/(const CImg& img1, const CImg& img2) { typedef typename cimg::superset::type t1t2; return CImg(img1,false)*=img2.get_invert(); } template inline CImg::type> operator/(const CImg& img, const CImgList& list) { typedef typename cimg::superset::type t1t2; CImgList res(list.size); cimglist_for(res,l) res[l] = img/list[l]; return res; } template inline CImgList::type> operator/(const CImgList& list, const CImg& img) { typedef typename cimg::superset::type t1t2; return CImgList(list)/=img; } template inline CImgList::type> operator/(const CImgList& list1, const CImgList& list2) { typedef typename cimg::superset::type t1t2; return CImgList(list1)/=list2; } template inline CImg<_cimg_Tfloat> sqr(const CImg& instance) { return instance.get_sqr(); } template inline CImg<_cimg_Tfloat> sqrt(const CImg& instance) { return instance.get_sqrt(); } template inline CImg<_cimg_Tfloat> exp(const CImg& instance) { return instance.get_exp(); } template inline CImg<_cimg_Tfloat> log(const CImg& instance) { return instance.get_log(); } template inline CImg<_cimg_Tfloat> log10(const CImg& instance) { return instance.get_log10(); } template inline CImg<_cimg_Tfloat> abs(const CImg& instance) { return instance.get_abs(); } template inline CImg<_cimg_Tfloat> cos(const CImg& instance) { return instance.get_cos(); } template inline CImg<_cimg_Tfloat> sin(const CImg& instance) { return instance.get_sin(); } template inline CImg<_cimg_Tfloat> tan(const CImg& instance) { return instance.get_tan(); } template inline CImg<_cimg_Tfloat> acos(const CImg& instance) { return instance.get_acos(); } template inline CImg<_cimg_Tfloat> asin(const CImg& instance) { return instance.get_asin(); } template inline CImg<_cimg_Tfloat> atan(const CImg& instance) { return instance.get_atan(); } template inline CImg transpose(const CImg& instance) { return instance.get_transpose(); } template inline CImg<_cimg_Tfloat> invert(const CImg& instance) { return instance.get_invert(); } template inline CImg<_cimg_Tfloat> pseudoinvert(const CImg& instance) { return instance.get_pseudoinvert(); } /*------------------------------------------- # # # # Definition of the CImgDisplay structure # # # --------------------------------------------*/ //! This class represents a window which can display \ref CImg images and handles mouse and keyboard events. /** Creating a \c CImgDisplay instance opens a window that can be used to display a \c CImg image of a \c CImgList image list inside. When a display is created, associated window events (such as mouse motion, keyboard and window size changes) are handled and can be easily detected by testing specific \c CImgDisplay data fields. See \ref cimg_displays for a complete tutorial on using the \c CImgDisplay class. **/ struct CImgDisplay { //! Width of the display unsigned int width; //! Height of the display unsigned int height; //! Normalization type used for the display unsigned int normalization; //! Display title char* title; //! X-pos of the display on the screen volatile int window_x; //! Y-pos of the display on the screen volatile int window_y; //! Width of the underlying window volatile unsigned int window_width; //! Height of the underlying window volatile unsigned int window_height; //! X-coordinate of the mouse pointer on the display volatile int mouse_x; //! Y-coordinate of the mouse pointer on the display volatile int mouse_y; //! Button state of the mouse volatile unsigned int buttons[512]; volatile unsigned int& button; //! Wheel state of the mouse volatile int wheel; //! Key value if pressed volatile unsigned int& key; volatile unsigned int keys[512]; //! Key value if released volatile unsigned int& released_key; volatile unsigned int released_keys[512]; //! Closed state of the window volatile bool is_closed; //! Resized state of the window volatile bool is_resized; //! Moved state of the window volatile bool is_moved; //! Event state of the window volatile bool is_event; //! Current state of the corresponding key (exists for all referenced keys). volatile bool is_keyESC; volatile bool is_keyF1; volatile bool is_keyF2; volatile bool is_keyF3; volatile bool is_keyF4; volatile bool is_keyF5; volatile bool is_keyF6; volatile bool is_keyF7; volatile bool is_keyF8; volatile bool is_keyF9; volatile bool is_keyF10; volatile bool is_keyF11; volatile bool is_keyF12; volatile bool is_keyPAUSE; volatile bool is_key1; volatile bool is_key2; volatile bool is_key3; volatile bool is_key4; volatile bool is_key5; volatile bool is_key6; volatile bool is_key7; volatile bool is_key8; volatile bool is_key9; volatile bool is_key0; volatile bool is_keyBACKSPACE; volatile bool is_keyINSERT; volatile bool is_keyHOME; volatile bool is_keyPAGEUP; volatile bool is_keyTAB; volatile bool is_keyQ; volatile bool is_keyW; volatile bool is_keyE; volatile bool is_keyR; volatile bool is_keyT; volatile bool is_keyY; volatile bool is_keyU; volatile bool is_keyI; volatile bool is_keyO; volatile bool is_keyP; volatile bool is_keyDELETE; volatile bool is_keyEND; volatile bool is_keyPAGEDOWN; volatile bool is_keyCAPSLOCK; volatile bool is_keyA; volatile bool is_keyS; volatile bool is_keyD; volatile bool is_keyF; volatile bool is_keyG; volatile bool is_keyH; volatile bool is_keyJ; volatile bool is_keyK; volatile bool is_keyL; volatile bool is_keyENTER; volatile bool is_keySHIFTLEFT; volatile bool is_keyZ; volatile bool is_keyX; volatile bool is_keyC; volatile bool is_keyV; volatile bool is_keyB; volatile bool is_keyN; volatile bool is_keyM; volatile bool is_keySHIFTRIGHT; volatile bool is_keyARROWUP; volatile bool is_keyCTRLLEFT; volatile bool is_keyAPPLEFT; volatile bool is_keyALT; volatile bool is_keySPACE; volatile bool is_keyALTGR; volatile bool is_keyAPPRIGHT; volatile bool is_keyMENU; volatile bool is_keyCTRLRIGHT; volatile bool is_keyARROWLEFT; volatile bool is_keyARROWDOWN; volatile bool is_keyARROWRIGHT; volatile bool is_keyPAD0; volatile bool is_keyPAD1; volatile bool is_keyPAD2; volatile bool is_keyPAD3; volatile bool is_keyPAD4; volatile bool is_keyPAD5; volatile bool is_keyPAD6; volatile bool is_keyPAD7; volatile bool is_keyPAD8; volatile bool is_keyPAD9; volatile bool is_keyPADADD; volatile bool is_keyPADSUB; volatile bool is_keyPADMUL; volatile bool is_keyPADDIV; //! Fullscreen state of the display bool is_fullscreen; float fps_fps, min, max; unsigned long timer, fps_frames, fps_timer; #ifdef cimgdisplay_plugin #include cimgdisplay_plugin #endif #ifdef cimgdisplay_plugin1 #include cimgdisplay_plugin1 #endif #ifdef cimgdisplay_plugin2 #include cimgdisplay_plugin2 #endif #ifdef cimgdisplay_plugin3 #include cimgdisplay_plugin3 #endif #ifdef cimgdisplay_plugin4 #include cimgdisplay_plugin4 #endif #ifdef cimgdisplay_plugin5 #include cimgdisplay_plugin5 #endif #ifdef cimgdisplay_plugin6 #include cimgdisplay_plugin6 #endif #ifdef cimgdisplay_plugin7 #include cimgdisplay_plugin7 #endif #ifdef cimgdisplay_plugin8 #include cimgdisplay_plugin8 #endif //! Create an empty display window. CImgDisplay(): width(0),height(0),normalization(0),title(0), window_x(0),window_y(0),window_width(0),window_height(0), mouse_x(0),mouse_y(0),button(*buttons),wheel(0),key(*keys),released_key(*released_keys), is_closed(true),is_resized(false),is_moved(false),is_event(false),is_fullscreen(false), min(0),max(0) {} //! Create a display window with a specified size \p pwidth x \p height. /** \param dimw Width of the display window. \param dimh Height of the display window. \param title Title of the display window. \param normalization_type Normalization type of the display window (0=none, 1=always, 2=once). \param fullscreen_flag : Fullscreen mode. \param closed_flag : Initially visible mode. A black image will be initially displayed in the display window. **/ CImgDisplay(const unsigned int dimw, const unsigned int dimh, const char *title=0, const unsigned int normalization_type=3, const bool fullscreen_flag=false, const bool closed_flag=false): width(0),height(0),normalization(0),title(0), window_x(0),window_y(0),window_width(0),window_height(0), mouse_x(0),mouse_y(0),button(*buttons),wheel(0),key(*keys),released_key(*released_keys), is_closed(true),is_resized(false),is_moved(false),is_event(false),is_fullscreen(false), min(0),max(0) { assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag); } //! Create a display window from an image. /** \param img : Image that will be used to create the display window. \param title : Title of the display window \param normalization_type : Normalization type of the display window. \param fullscreen_flag : Fullscreen mode. \param closed_flag : Initially visible mode. **/ template CImgDisplay(const CImg& img, const char *title=0, const unsigned int normalization_type=3, const bool fullscreen_flag=false, const bool closed_flag=false): width(0),height(0),normalization(0),title(0), window_x(0),window_y(0),window_width(0),window_height(0), mouse_x(0),mouse_y(0),button(*buttons),wheel(0),key(*keys),released_key(*released_keys), is_closed(true),is_resized(false),is_moved(false),is_event(false),is_fullscreen(false),min(0),max(0) { assign(img,title,normalization_type,fullscreen_flag,closed_flag); } //! Create a display window from an image list. /** \param list : The list of images to display. \param title : Title of the display window \param normalization_type : Normalization type of the display window. \param fullscreen_flag : Fullscreen mode. \param closed_flag : Initially visible mode. **/ template CImgDisplay(const CImgList& list, const char *title=0, const unsigned int normalization_type=3, const bool fullscreen_flag=false, const bool closed_flag=false): width(0),height(0),normalization(0),title(0), window_x(0),window_y(0),window_width(0),window_height(0), mouse_x(0),mouse_y(0),button(*buttons),wheel(0),key(*keys),released_key(*released_keys), is_closed(true),is_resized(false),is_moved(false),is_event(false),is_fullscreen(false),min(0),max(0) { assign(list,title,normalization_type,fullscreen_flag,closed_flag); } //! Create a display window by copying another one. /** \param disp : Display window to copy. **/ CImgDisplay(const CImgDisplay& disp): width(0),height(0),normalization(0),title(0), window_x(0),window_y(0),window_width(0),window_height(0), mouse_x(0),mouse_y(0),button(*buttons),wheel(0),key(*keys),released_key(*released_keys), is_closed(true),is_resized(false),is_moved(false),is_event(false),is_fullscreen(false),min(0),max(0) { assign(disp); } //! Destructor. ~CImgDisplay() { assign(); } //! Assignment operator. CImgDisplay& operator=(const CImgDisplay& disp) { return assign(disp); } //! Return true is display is empty. bool is_empty() const { return (!width || !height); } //! Return true if display is not empty. operator bool() const { return !is_empty(); } //! Return display width. int dimx() const { return (int)width; } //! Return display height. int dimy() const { return (int)height; } //! Return display window width. int window_dimx() const { return (int)window_width; } //! Return display window height. int window_dimy() const { return (int)window_height; } //! Return X-coordinate of the window. int window_posx() const { return window_x; } //! Return Y-coordinate of the window. int window_posy() const { return window_y; } //! Synchronized waiting function. Same as cimg::wait(). CImgDisplay& wait(const unsigned int milliseconds) { cimg::_sleep(milliseconds,timer); return *this; } //! Wait for an event occuring on the current display. CImgDisplay& wait() { if (!is_empty()) wait(*this); return *this; } //! Wait for any event occuring on the display \c disp1. static void wait(CImgDisplay& disp1) { disp1.is_event = 0; while (!disp1.is_event) wait_all(); } //! Wait for any event occuring either on the display \c disp1 or \c disp2. static void wait(CImgDisplay& disp1, CImgDisplay& disp2) { disp1.is_event = disp2.is_event = 0; while (!disp1.is_event && !disp2.is_event) wait_all(); } //! Wait for any event occuring either on the display \c disp1, \c disp2 or \c disp3. static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3) { disp1.is_event = disp2.is_event = disp3.is_event = 0; while (!disp1.is_event && !disp2.is_event && !disp3.is_event) wait_all(); } //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3 or \c disp4. static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4) { disp1.is_event = disp2.is_event = disp3.is_event = disp4.is_event = 0; while (!disp1.is_event && !disp2.is_event && !disp3.is_event && !disp4.is_event) wait_all(); } //! Return the frame per second rate. float frames_per_second() { if (!fps_timer) fps_timer = cimg::time(); const float delta = (cimg::time()-fps_timer)/1000.0f; ++fps_frames; if (delta>=1) { fps_fps = fps_frames/delta; fps_frames = 0; fps_timer = cimg::time(); } return fps_fps; } //! Display an image list CImgList into a display window. /** First, all images of the list are appended into a single image used for visualization, then this image is displayed in the current display window. \param list : The list of images to display. \param axis : The axis used to append the image for visualization. Can be 'x' (default),'y','z' or 'v'. \param align : Defines the relative alignment of images when displaying images of different sizes. Can be '\p c' (centered, which is the default), '\p p' (top alignment) and '\p n' (bottom aligment). **/ template CImgDisplay& display(const CImgList& list, const char axis='x', const char align='p') { return display(list.get_append(axis,align)); } //! Display an image CImg into a display window. template CImgDisplay& operator<<(const CImg& img) { return display(img); } //! Display an image CImg into a display window. template CImgDisplay& operator<<(const CImgList& list) { return display(list); } //! Resize a display window with the size of an image. /** \param img : Input image. \p image.width and \p image.height give the new dimensions of the display window. \param redraw : If \p true (default), the current displayed image in the display window will be bloc-interpolated to fit the new dimensions. If \p false, a black image will be drawn in the resized window. **/ template CImgDisplay& resize(const CImg& img, const bool redraw=true) { return resize(img.width,img.height,redraw); } //! Resize a display window using the size of the given display \p disp. CImgDisplay& resize(const CImgDisplay& disp, const bool redraw=true) { return resize(disp.width,disp.height,redraw); } //! Resize a display window in its current size. CImgDisplay& resize(const bool redraw=true) { resize(window_width,window_height,redraw); return *this; } //! Set fullscreen mode. CImgDisplay& fullscreen(const bool redraw=true) { if (is_empty() || is_fullscreen) return *this; return toggle_fullscreen(redraw); } //! Set normal screen mode. CImgDisplay& normalscreen(const bool redraw=true) { if (is_empty() || !is_fullscreen) return *this; return toggle_fullscreen(redraw); } // Inner routine used for fast resizing of buffer to display size. template static void _render_resize(const T *ptrs, const unsigned int ws, const unsigned int hs, t *ptrd, const unsigned int wd, const unsigned int hd) { unsigned int *const offx = new unsigned int[wd], *const offy = new unsigned int[hd+1], *poffx, *poffy; float s, curr, old; s = (float)ws/wd; poffx = offx; curr = 0; for (unsigned int x=0; x=keys; --ptrs) if (*ptrs) { if (remove) *ptrs = 0; return true; } return false; } //! Test if a key has been pressed. bool is_key(const unsigned int key1, const bool remove) { for (unsigned int *ptrs=(unsigned int*)keys+512-1; ptrs>=keys; --ptrs) if (*ptrs==key1) { if (remove) *ptrs = 0; return true; } return false; } //! Test if a key sequence has been typed. bool is_key(const unsigned int key1, const unsigned int key2, const bool remove) { const unsigned int seq[] = { key1, key2 }; return is_key(seq,2,remove); } //! Test if a key sequence has been typed. bool is_key(const unsigned int key1, const unsigned int key2, const unsigned int key3, const bool remove) { const unsigned int seq[] = { key1, key2, key3 }; return is_key(seq,3,remove); } //! Test if a key sequence has been typed. bool is_key(const unsigned int key1, const unsigned int key2, const unsigned int key3, const unsigned int key4, const bool remove) { const unsigned int seq[] = { key1, key2, key3, key4 }; return is_key(seq,4,remove); } //! Test if a key sequence has been typed. bool is_key(const unsigned int key1, const unsigned int key2, const unsigned int key3, const unsigned int key4, const unsigned int key5, const bool remove) { const unsigned int seq[] = { key1, key2, key3, key4, key5 }; return is_key(seq,5,remove); } //! Test if a key sequence has been typed. bool is_key(const unsigned int key1, const unsigned int key2, const unsigned int key3, const unsigned int key4, const unsigned int key5, const unsigned int key6, const bool remove) { const unsigned int seq[] = { key1, key2, key3, key4, key5, key6 }; return is_key(seq,6,remove); } //! Test if a key sequence has been typed. bool is_key(const unsigned int key1, const unsigned int key2, const unsigned int key3, const unsigned int key4, const unsigned int key5, const unsigned int key6, const unsigned int key7, const bool remove) { const unsigned int seq[] = { key1, key2, key3, key4, key5, key6, key7 }; return is_key(seq,7,remove); } //! Test if a key sequence has been typed. bool is_key(const unsigned int key1, const unsigned int key2, const unsigned int key3, const unsigned int key4, const unsigned int key5, const unsigned int key6, const unsigned int key7, const unsigned int key8, const bool remove) { const unsigned int seq[] = { key1, key2, key3, key4, key5, key6, key7, key8 }; return is_key(seq,8,remove); } //! Test if a key sequence has been typed. bool is_key(const unsigned int key1, const unsigned int key2, const unsigned int key3, const unsigned int key4, const unsigned int key5, const unsigned int key6, const unsigned int key7, const unsigned int key8, const unsigned int key9, const bool remove) { const unsigned int seq[] = { key1, key2, key3, key4, key5, key6, key7, key8, key9 }; return is_key(seq,9,remove); } //! Test if a key sequence has been typed. bool is_key(const unsigned int *const keyseq, const unsigned int N, const bool remove=true) { if (keyseq && N) { const unsigned int *const ps_end = keyseq+N-1, k = *ps_end, *const pk_end = (unsigned int*)keys+1+512-N; for (unsigned int *pk = (unsigned int*)keys; pk1?dz:0), nh = dy + (dz>1?dz:0); const unsigned int sw = CImgDisplay::screen_dimx(), sh = CImgDisplay::screen_dimy(), mw = dmin<0?(unsigned int)(sw*-dmin/100):(unsigned int)dmin, mh = dmin<0?(unsigned int)(sh*-dmin/100):(unsigned int)dmin, Mw = dmax<0?(unsigned int)(sw*-dmax/100):(unsigned int)dmax, Mh = dmax<0?(unsigned int)(sh*-dmax/100):(unsigned int)dmax; if (nwMw) { nh = nh*Mw/nw; nh+=(nh==0); nw = Mw; } if (nh>Mh) { nw = nw*Mh/nh; nw+=(nw==0); nh = Mh; } if (nw CImgDisplay& assign(const CImg& img, const char *title=0, const unsigned int normalization_type=3, const bool fullscreen_flag=false, const bool closed_flag=false) { throw CImgDisplayException("CImgDisplay()::assign() : Display has been required but is not available (cimg_display=0)"); const char* avoid_warning = title + img.width + normalization_type + (int)fullscreen_flag + (int)closed_flag; avoid_warning = 0; return assign(0,0); } //! In-place version of the previous constructor. template CImgDisplay& assign(const CImgList& list, const char *title=0, const unsigned int normalization_type=3, const bool fullscreen_flag=false, const bool closed_flag=false) { throw CImgDisplayException("CImgDisplay()::assign() : Display has been required but is not available (cimg_display=0)"); const char* avoid_warning = title + list.size + normalization_type + (int)fullscreen_flag + (int)closed_flag; avoid_warning = 0; return assign(0,0); } //! In-place version of the previous constructor. CImgDisplay& assign(const CImgDisplay &disp) { return assign(disp.width,disp.height); } //! Resize window. CImgDisplay& resize(const int width, const int height, const bool redraw=true) { int avoid_warning = width | height | (int)redraw; avoid_warning = 0; return *this; } //! Toggle fullscreen mode. CImgDisplay& toggle_fullscreen(const bool redraw=true) { bool avoid_warning = redraw; avoid_warning = false; return *this; } //! Show a closed display. CImgDisplay& show() { return *this; } //! Close a visible display. CImgDisplay& close() { return *this; } //! Move window. CImgDisplay& move(const int posx, const int posy) { int avoid_warning = posx | posy; avoid_warning = 0; return *this; } //! Show mouse pointer. CImgDisplay& show_mouse() { return *this; } //! Hide mouse pointer. CImgDisplay& hide_mouse() { return *this; } //! Move mouse pointer to a specific location. CImgDisplay& set_mouse(const int posx, const int posy) { int avoid_warning = posx | posy; avoid_warning = 0; return *this; } //! Set the window title. CImgDisplay& set_title(const char *format, ...) { const char *avoid_warning = format; avoid_warning = 0; return *this; } //! Display an image in a window. template CImgDisplay& display(const CImg& img) { unsigned int avoid_warning = img.width; avoid_warning = 0; return *this; } //! Re-paint image content in window. CImgDisplay& paint() { return *this; } //! Render image buffer into GDI native image format. template CImgDisplay& render(const CImg& img) { unsigned int avoid_warning = img.width; avoid_warning = 0; return *this; } //! Take a snapshot of the display in the specified image. template const CImgDisplay& snapshot(CImg& img) const { img.assign(width,height,1,3,0); return *this; } // X11-based display //------------------- #elif cimg_display==1 Atom wm_delete_window, wm_delete_protocol; Window window, background_window; Colormap colormap; XImage *image; void *data; #ifdef cimg_use_xshm XShmSegmentInfo *shminfo; #endif static int screen_dimx() { int res = 0; if (!cimg::X11attr().display) { Display *disp = XOpenDisplay((cimg_std::getenv("DISPLAY")?cimg_std::getenv("DISPLAY"):":0.0")); if (!disp) throw CImgDisplayException("CImgDisplay::screen_dimx() : Can't open X11 display."); res = DisplayWidth(disp,DefaultScreen(disp)); XCloseDisplay(disp); } else { #ifdef cimg_use_xrandr if (cimg::X11attr().resolutions && cimg::X11attr().curr_resolution) res = cimg::X11attr().resolutions[cimg::X11attr().curr_resolution].width; else #endif res = DisplayWidth(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)); } return res; } static int screen_dimy() { int res = 0; if (!cimg::X11attr().display) { Display *disp = XOpenDisplay((cimg_std::getenv("DISPLAY") ? cimg_std::getenv("DISPLAY") : ":0.0")); if (!disp) throw CImgDisplayException("CImgDisplay::screen_dimy() : Can't open X11 display."); res = DisplayHeight(disp,DefaultScreen(disp)); XCloseDisplay(disp); } else { #ifdef cimg_use_xrandr if (cimg::X11attr().resolutions && cimg::X11attr().curr_resolution) res = cimg::X11attr().resolutions[cimg::X11attr().curr_resolution].height; else #endif res = DisplayHeight(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)); } return res; } static void wait_all() { if (cimg::X11attr().display) { XLockDisplay(cimg::X11attr().display); bool flag = true; XEvent event; while (flag) { XNextEvent(cimg::X11attr().display, &event); for (unsigned int i = 0; iis_closed && event.xany.window==cimg::X11attr().wins[i]->window) { cimg::X11attr().wins[i]->_handle_events(&event); if (cimg::X11attr().wins[i]->is_event) flag = false; } } XUnlockDisplay(cimg::X11attr().display); } } void _handle_events(const XEvent *const pevent) { XEvent event = *pevent; switch (event.type) { case ClientMessage : { if ((int)event.xclient.message_type==(int)wm_delete_protocol && (int)event.xclient.data.l[0]==(int)wm_delete_window) { XUnmapWindow(cimg::X11attr().display,window); mouse_x = mouse_y = -1; if (button) { cimg_std::memmove((void*)(buttons+1),(void*)buttons,512-1); button = 0; } if (key) { cimg_std::memmove((void*)(keys+1),(void*)keys,512-1); key = 0; } if (released_key) { cimg_std::memmove((void*)(released_keys+1),(void*)released_keys,512-1); released_key = 0; } is_closed = is_event = true; } } break; case ConfigureNotify : { while (XCheckWindowEvent(cimg::X11attr().display,window,StructureNotifyMask,&event)) {} const unsigned int nw = event.xconfigure.width, nh = event.xconfigure.height; const int nx = event.xconfigure.x, ny = event.xconfigure.y; if (nw && nh && (nw!=window_width || nh!=window_height)) { window_width = nw; window_height = nh; mouse_x = mouse_y = -1; XResizeWindow(cimg::X11attr().display,window,window_width,window_height); is_resized = is_event = true; } if (nx!=window_x || ny!=window_y) { window_x = nx; window_y = ny; is_moved = is_event = true; } } break; case Expose : { while (XCheckWindowEvent(cimg::X11attr().display,window,ExposureMask,&event)) {} _paint(false); if (is_fullscreen) { XWindowAttributes attr; XGetWindowAttributes(cimg::X11attr().display, window, &attr); while (attr.map_state != IsViewable) XSync(cimg::X11attr().display, False); XSetInputFocus(cimg::X11attr().display, window, RevertToParent, CurrentTime); } } break; case ButtonPress : { do { mouse_x = event.xmotion.x; mouse_y = event.xmotion.y; if (mouse_x<0 || mouse_y<0 || mouse_x>=dimx() || mouse_y>=dimy()) mouse_x = mouse_y = -1; switch (event.xbutton.button) { case 1 : cimg_std::memmove((void*)(buttons+1),(void*)buttons,512-1); button|=1; is_event = true; break; case 2 : cimg_std::memmove((void*)(buttons+1),(void*)buttons,512-1); button|=4; is_event = true; break; case 3 : cimg_std::memmove((void*)(buttons+1),(void*)buttons,512-1); button|=2; is_event = true; break; } } while (XCheckWindowEvent(cimg::X11attr().display,window,ButtonPressMask,&event)); } break; case ButtonRelease : { do { mouse_x = event.xmotion.x; mouse_y = event.xmotion.y; if (mouse_x<0 || mouse_y<0 || mouse_x>=dimx() || mouse_y>=dimy()) mouse_x = mouse_y = -1; switch (event.xbutton.button) { case 1 : cimg_std::memmove((void*)(buttons+1),(void*)buttons,512-1); button&=~1U; is_event = true; break; case 2 : cimg_std::memmove((void*)(buttons+1),(void*)buttons,512-1); button&=~4U; is_event = true; break; case 3 : cimg_std::memmove((void*)(buttons+1),(void*)buttons,512-1); button&=~2U; is_event = true; break; case 4 : ++wheel; is_event = true; break; case 5 : --wheel; is_event = true; break; } } while (XCheckWindowEvent(cimg::X11attr().display,window,ButtonReleaseMask,&event)); } break; case KeyPress : { char tmp; KeySym ksym; XLookupString(&event.xkey,&tmp,1,&ksym,0); update_iskey((unsigned int)ksym,true); if (key) cimg_std::memmove((void*)(keys+1),(void*)keys,512-1); key = (unsigned int)ksym; if (released_key) { cimg_std::memmove((void*)(released_keys+1),(void*)released_keys,512-1); released_key = 0; } is_event = true; } break; case KeyRelease : { char tmp; KeySym ksym; XLookupString(&event.xkey,&tmp,1,&ksym,0); update_iskey((unsigned int)ksym,false); if (key) { cimg_std::memmove((void*)(keys+1),(void*)keys,512-1); key = 0; } if (released_key) cimg_std::memmove((void*)(released_keys+1),(void*)released_keys,512-1); released_key = (unsigned int)ksym; is_event = true; } break; case EnterNotify: { while (XCheckWindowEvent(cimg::X11attr().display,window,EnterWindowMask,&event)) {} mouse_x = event.xmotion.x; mouse_y = event.xmotion.y; if (mouse_x<0 || mouse_y<0 || mouse_x>=dimx() || mouse_y>=dimy()) mouse_x = mouse_y = -1; } break; case LeaveNotify : { while (XCheckWindowEvent(cimg::X11attr().display,window,LeaveWindowMask,&event)) {} mouse_x = mouse_y =-1; is_event = true; } break; case MotionNotify : { while (XCheckWindowEvent(cimg::X11attr().display,window,PointerMotionMask,&event)) {} mouse_x = event.xmotion.x; mouse_y = event.xmotion.y; if (mouse_x<0 || mouse_y<0 || mouse_x>=dimx() || mouse_y>=dimy()) mouse_x = mouse_y = -1; is_event = true; } break; } } static void* _events_thread(void *arg) { arg = 0; XEvent event; pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0); for (;;) { XLockDisplay(cimg::X11attr().display); bool event_flag = XCheckTypedEvent(cimg::X11attr().display, ClientMessage, &event); if (!event_flag) event_flag = XCheckMaskEvent(cimg::X11attr().display, ExposureMask|StructureNotifyMask|ButtonPressMask| KeyPressMask|PointerMotionMask|EnterWindowMask|LeaveWindowMask| ButtonReleaseMask|KeyReleaseMask,&event); if (event_flag) { for (unsigned int i=0; iis_closed && event.xany.window==cimg::X11attr().wins[i]->window) cimg::X11attr().wins[i]->_handle_events(&event); } XUnlockDisplay(cimg::X11attr().display); pthread_testcancel(); cimg::sleep(7); } return 0; } void _set_colormap(Colormap& colormap, const unsigned int dim) { XColor palette[256]; switch (dim) { case 1 : { // palette for greyscale images for (unsigned int index=0; index<256; ++index) { palette[index].pixel = index; palette[index].red = palette[index].green = palette[index].blue = (unsigned short)(index<<8); palette[index].flags = DoRed | DoGreen | DoBlue; } } break; case 2 : { // palette for RG images for (unsigned int index=0, r=8; r<256; r+=16) for (unsigned int g=8; g<256; g+=16) { palette[index].pixel = index; palette[index].red = palette[index].blue = (unsigned short)(r<<8); palette[index].green = (unsigned short)(g<<8); palette[index++].flags = DoRed | DoGreen | DoBlue; } } break; default : { // palette for RGB images for (unsigned int index=0, r=16; r<256; r+=32) for (unsigned int g=16; g<256; g+=32) for (unsigned int b=32; b<256; b+=64) { palette[index].pixel = index; palette[index].red = (unsigned short)(r<<8); palette[index].green = (unsigned short)(g<<8); palette[index].blue = (unsigned short)(b<<8); palette[index++].flags = DoRed | DoGreen | DoBlue; } } } XStoreColors(cimg::X11attr().display,colormap,palette,256); } void _map_window() { XWindowAttributes attr; XEvent event; bool exposed = false, mapped = false; XMapRaised(cimg::X11attr().display,window); XSync(cimg::X11attr().display,False); do { XWindowEvent(cimg::X11attr().display,window,StructureNotifyMask | ExposureMask,&event); switch (event.type) { case MapNotify : mapped = true; break; case Expose : exposed = true; break; default : XSync(cimg::X11attr().display, False); cimg::sleep(10); } } while (!(exposed && mapped)); do { XGetWindowAttributes(cimg::X11attr().display, window, &attr); if (attr.map_state!=IsViewable) { XSync(cimg::X11attr().display,False); cimg::sleep(10); } } while (attr.map_state != IsViewable); window_x = attr.x; window_y = attr.y; } void _paint(const bool wait_expose=true) { if (!is_closed) { if (wait_expose) { static XEvent event; event.xexpose.type = Expose; event.xexpose.serial = 0; event.xexpose.send_event = True; event.xexpose.display = cimg::X11attr().display; event.xexpose.window = window; event.xexpose.x = 0; event.xexpose.y = 0; event.xexpose.width = dimx(); event.xexpose.height = dimy(); event.xexpose.count = 0; XSendEvent(cimg::X11attr().display, window, False, 0, &event); } else { #ifdef cimg_use_xshm if (shminfo) XShmPutImage(cimg::X11attr().display,window,*cimg::X11attr().gc,image,0,0,0,0,width,height,False); else #endif XPutImage(cimg::X11attr().display,window,*cimg::X11attr().gc,image,0,0,0,0,width,height); XSync(cimg::X11attr().display, False); } } } template void _resize(T foo, const unsigned int ndimx, const unsigned int ndimy, const bool redraw) { foo = 0; #ifdef cimg_use_xshm if (shminfo) { XShmSegmentInfo *nshminfo = new XShmSegmentInfo; XImage *nimage = XShmCreateImage(cimg::X11attr().display,DefaultVisual(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), cimg::X11attr().nb_bits,ZPixmap,0,nshminfo,ndimx,ndimy); if (!nimage) { delete nshminfo; return; } else { nshminfo->shmid = shmget(IPC_PRIVATE, ndimx*ndimy*sizeof(T), IPC_CREAT | 0777); if (nshminfo->shmid==-1) { XDestroyImage(nimage); delete nshminfo; return; } else { nshminfo->shmaddr = nimage->data = (char*)shmat(nshminfo->shmid,0,0); if (nshminfo->shmaddr==(char*)-1) { shmctl(nshminfo->shmid,IPC_RMID,0); XDestroyImage(nimage); delete nshminfo; return; } else { nshminfo->readOnly = False; cimg::X11attr().shm_enabled = true; XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm); XShmAttach(cimg::X11attr().display, nshminfo); XSync(cimg::X11attr().display, False); XSetErrorHandler(oldXErrorHandler); if (!cimg::X11attr().shm_enabled) { shmdt(nshminfo->shmaddr); shmctl(nshminfo->shmid,IPC_RMID,0); XDestroyImage(nimage); delete nshminfo; return; } else { T *const ndata = (T*)nimage->data; if (redraw) _render_resize((T*)data,width,height,ndata,ndimx,ndimy); else cimg_std::memset(ndata,0,sizeof(T)*ndimx*ndimy); XShmDetach(cimg::X11attr().display, shminfo); XDestroyImage(image); shmdt(shminfo->shmaddr); shmctl(shminfo->shmid,IPC_RMID,0); delete shminfo; shminfo = nshminfo; image = nimage; data = (void*)ndata; } } } } } else #endif { T *ndata = (T*)cimg_std::malloc(ndimx*ndimy*sizeof(T)); if (redraw) _render_resize((T*)data,width,height,ndata,ndimx,ndimy); else cimg_std::memset(ndata,0,sizeof(T)*ndimx*ndimy); data = (void*)ndata; XDestroyImage(image); image = XCreateImage(cimg::X11attr().display,DefaultVisual(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), cimg::X11attr().nb_bits,ZPixmap,0,(char*)data,ndimx,ndimy,8,0); } } void _init_fullscreen() { background_window = 0; if (is_fullscreen && !is_closed) { #ifdef cimg_use_xrandr int foo; if (XRRQueryExtension(cimg::X11attr().display,&foo,&foo)) { XRRRotations(cimg::X11attr().display, DefaultScreen(cimg::X11attr().display), &cimg::X11attr().curr_rotation); if (!cimg::X11attr().resolutions) { cimg::X11attr().resolutions = XRRSizes(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display),&foo); cimg::X11attr().nb_resolutions = (unsigned int)foo; } if (cimg::X11attr().resolutions) { cimg::X11attr().curr_resolution = 0; for (unsigned int i=0; i=width && nh>=height && nw<=(unsigned int)(cimg::X11attr().resolutions[cimg::X11attr().curr_resolution].width) && nh<=(unsigned int)(cimg::X11attr().resolutions[cimg::X11attr().curr_resolution].height)) cimg::X11attr().curr_resolution = i; } if (cimg::X11attr().curr_resolution>0) { XRRScreenConfiguration *config = XRRGetScreenInfo(cimg::X11attr().display, DefaultRootWindow(cimg::X11attr().display)); XRRSetScreenConfig(cimg::X11attr().display, config, DefaultRootWindow(cimg::X11attr().display), cimg::X11attr().curr_resolution, cimg::X11attr().curr_rotation, CurrentTime); XRRFreeScreenConfigInfo(config); XSync(cimg::X11attr().display, False); } } } if (!cimg::X11attr().resolutions) cimg::warn("CImgDisplay::_create_window() : Xrandr extension is not supported by the X server."); #endif const unsigned int sx = screen_dimx(), sy = screen_dimy(); XSetWindowAttributes winattr; winattr.override_redirect = True; if (sx!=width || sy!=height) { background_window = XCreateWindow(cimg::X11attr().display, RootWindow(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)),0,0, sx,sy,0,0,InputOutput,CopyFromParent,CWOverrideRedirect,&winattr); const unsigned int bufsize = sx*sy*(cimg::X11attr().nb_bits==8?1:(cimg::X11attr().nb_bits==16?2:4)); void *background_data = cimg_std::malloc(bufsize); cimg_std::memset(background_data,0,bufsize); XImage *background_image = XCreateImage(cimg::X11attr().display,DefaultVisual(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), cimg::X11attr().nb_bits,ZPixmap,0,(char*)background_data,sx,sy,8,0); XEvent event; XSelectInput(cimg::X11attr().display,background_window,StructureNotifyMask); XMapRaised(cimg::X11attr().display,background_window); do XWindowEvent(cimg::X11attr().display,background_window,StructureNotifyMask,&event); while (event.type!=MapNotify); #ifdef cimg_use_xshm if (shminfo) XShmPutImage(cimg::X11attr().display,background_window,*cimg::X11attr().gc,background_image,0,0,0,0,sx,sy,False); else #endif XPutImage(cimg::X11attr().display,background_window,*cimg::X11attr().gc,background_image,0,0,0,0,sx,sy); XWindowAttributes attr; XGetWindowAttributes(cimg::X11attr().display, background_window, &attr); while (attr.map_state != IsViewable) XSync(cimg::X11attr().display, False); XDestroyImage(background_image); } } } void _desinit_fullscreen() { if (is_fullscreen) { XUngrabKeyboard(cimg::X11attr().display,CurrentTime); #ifdef cimg_use_xrandr if (cimg::X11attr().resolutions && cimg::X11attr().curr_resolution) { XRRScreenConfiguration *config = XRRGetScreenInfo(cimg::X11attr().display, DefaultRootWindow(cimg::X11attr().display)); XRRSetScreenConfig(cimg::X11attr().display, config, DefaultRootWindow(cimg::X11attr().display), 0, cimg::X11attr().curr_rotation, CurrentTime); XRRFreeScreenConfigInfo(config); XSync(cimg::X11attr().display, False); cimg::X11attr().curr_resolution = 0; } #endif if (background_window) XDestroyWindow(cimg::X11attr().display,background_window); background_window = 0; is_fullscreen = false; } } static int _assign_xshm(Display *dpy, XErrorEvent *error) { dpy = 0; error = 0; cimg::X11attr().shm_enabled = false; return 0; } void _assign(const unsigned int dimw, const unsigned int dimh, const char *ptitle=0, const unsigned int normalization_type=3, const bool fullscreen_flag=false, const bool closed_flag=false) { // Allocate space for window title const int s = cimg::strlen(ptitle)+1; char *tmp_title = s?new char[s]:0; if (s) cimg_std::memcpy(tmp_title,ptitle,s*sizeof(char)); // Destroy previous display window if existing if (!is_empty()) assign(); // Open X11 display if necessary. if (!cimg::X11attr().display) { static bool xinit_threads = false; if (!xinit_threads) { XInitThreads(); xinit_threads = true; } cimg::X11attr().nb_wins = 0; cimg::X11attr().display = XOpenDisplay((cimg_std::getenv("DISPLAY")?cimg_std::getenv("DISPLAY"):":0.0")); if (!cimg::X11attr().display) throw CImgDisplayException("CImgDisplay::_create_window() : Can't open X11 display"); cimg::X11attr().nb_bits = DefaultDepth(cimg::X11attr().display, DefaultScreen(cimg::X11attr().display)); if (cimg::X11attr().nb_bits!=8 && cimg::X11attr().nb_bits!=16 && cimg::X11attr().nb_bits!=24 && cimg::X11attr().nb_bits!=32) throw CImgDisplayException("CImgDisplay::_create_window() : %u bits mode is not supported " "(only 8, 16, 24 and 32 bits modes are supported)",cimg::X11attr().nb_bits); cimg::X11attr().gc = new GC; *cimg::X11attr().gc = DefaultGC(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)); Visual *visual = DefaultVisual(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)); XVisualInfo vtemplate; vtemplate.visualid = XVisualIDFromVisual(visual); int nb_visuals; XVisualInfo *vinfo = XGetVisualInfo(cimg::X11attr().display,VisualIDMask,&vtemplate,&nb_visuals); if (vinfo && vinfo->red_maskblue_mask) cimg::X11attr().blue_first = true; cimg::X11attr().byte_order = ImageByteOrder(cimg::X11attr().display); XFree(vinfo); XLockDisplay(cimg::X11attr().display); cimg::X11attr().event_thread = new pthread_t; pthread_create(cimg::X11attr().event_thread,0,_events_thread,0); } else XLockDisplay(cimg::X11attr().display); // Set display variables width = cimg::min(dimw,(unsigned int)screen_dimx()); height = cimg::min(dimh,(unsigned int)screen_dimy()); normalization = normalization_type<4?normalization_type:3; is_fullscreen = fullscreen_flag; window_x = window_y = 0; is_closed = closed_flag; title = tmp_title; flush(); // Create X11 window and palette (if 8bits display) if (is_fullscreen) { if (!is_closed) _init_fullscreen(); const unsigned int sx = screen_dimx(), sy = screen_dimy(); XSetWindowAttributes winattr; winattr.override_redirect = True; window = XCreateWindow(cimg::X11attr().display, RootWindow(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), (sx-width)/2,(sy-height)/2, width,height,0,0,InputOutput,CopyFromParent,CWOverrideRedirect,&winattr); } else window = XCreateSimpleWindow(cimg::X11attr().display, RootWindow(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), 0,0,width,height,2,0,0x0L); XStoreName(cimg::X11attr().display,window,title?title:" "); if (cimg::X11attr().nb_bits==8) { colormap = XCreateColormap(cimg::X11attr().display,window,DefaultVisual(cimg::X11attr().display, DefaultScreen(cimg::X11attr().display)),AllocAll); _set_colormap(colormap,3); XSetWindowColormap(cimg::X11attr().display,window,colormap); } window_width = width; window_height = height; // Create XImage const unsigned int bufsize = width*height*(cimg::X11attr().nb_bits==8?1:(cimg::X11attr().nb_bits==16?2:4)); #ifdef cimg_use_xshm shminfo = 0; if (XShmQueryExtension(cimg::X11attr().display)) { shminfo = new XShmSegmentInfo; image = XShmCreateImage(cimg::X11attr().display,DefaultVisual(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), cimg::X11attr().nb_bits,ZPixmap,0,shminfo,width,height); if (!image) { delete shminfo; shminfo = 0; } else { shminfo->shmid = shmget(IPC_PRIVATE, bufsize, IPC_CREAT | 0777); if (shminfo->shmid==-1) { XDestroyImage(image); delete shminfo; shminfo = 0; } else { shminfo->shmaddr = image->data = (char*)(data = shmat(shminfo->shmid,0,0)); if (shminfo->shmaddr==(char*)-1) { shmctl(shminfo->shmid,IPC_RMID,0); XDestroyImage(image); delete shminfo; shminfo = 0; } else { shminfo->readOnly = False; cimg::X11attr().shm_enabled = true; XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm); XShmAttach(cimg::X11attr().display, shminfo); XSync(cimg::X11attr().display, False); XSetErrorHandler(oldXErrorHandler); if (!cimg::X11attr().shm_enabled) { shmdt(shminfo->shmaddr); shmctl(shminfo->shmid,IPC_RMID,0); XDestroyImage(image); delete shminfo; shminfo = 0; } } } } } if (!shminfo) #endif { data = cimg_std::malloc(bufsize); image = XCreateImage(cimg::X11attr().display,DefaultVisual(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), cimg::X11attr().nb_bits,ZPixmap,0,(char*)data,width,height,8,0); } wm_delete_window = XInternAtom(cimg::X11attr().display, "WM_DELETE_WINDOW", False); wm_delete_protocol = XInternAtom(cimg::X11attr().display, "WM_PROTOCOLS", False); XSetWMProtocols(cimg::X11attr().display, window, &wm_delete_window, 1); XSelectInput(cimg::X11attr().display,window, ExposureMask | StructureNotifyMask | ButtonPressMask | KeyPressMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask); if (is_fullscreen) XGrabKeyboard(cimg::X11attr().display, window, True, GrabModeAsync, GrabModeAsync, CurrentTime); cimg::X11attr().wins[cimg::X11attr().nb_wins++]=this; if (!is_closed) _map_window(); else { window_x = window_y = cimg::type::min(); } XUnlockDisplay(cimg::X11attr().display); } CImgDisplay& assign() { if (is_empty()) return *this; XLockDisplay(cimg::X11attr().display); // Remove display window from event thread list. unsigned int i; for (i = 0; ishmaddr); shmctl(shminfo->shmid,IPC_RMID,0); delete shminfo; shminfo = 0; } else #endif XDestroyImage(image); data = 0; image = 0; if (cimg::X11attr().nb_bits==8) XFreeColormap(cimg::X11attr().display,colormap); colormap = 0; XSync(cimg::X11attr().display, False); // Reset display variables if (title) delete[] title; width = height = normalization = window_width = window_height = 0; window_x = window_y = 0; is_fullscreen = false; is_closed = true; min = max = 0; title = 0; flush(); // End event thread and close display if necessary XUnlockDisplay(cimg::X11attr().display); /* The code below was used to close the X11 display when not used anymore, unfortunately, since the latest Xorg versions, it randomely hangs, so I prefer to remove it. A fix would be needed anyway. if (!cimg::X11attr().nb_wins) { // Kill event thread pthread_cancel(*cimg::X11attr().event_thread); XUnlockDisplay(cimg::X11attr().display); pthread_join(*cimg::X11attr().event_thread,0); delete cimg::X11attr().event_thread; cimg::X11attr().event_thread = 0; XCloseDisplay(cimg::X11attr().display); cimg::X11attr().display = 0; delete cimg::X11attr().gc; cimg::X11attr().gc = 0; } else XUnlockDisplay(cimg::X11attr().display); */ return *this; } CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *title=0, const unsigned int normalization_type=3, const bool fullscreen_flag=false, const bool closed_flag=false) { if (!dimw || !dimh) return assign(); _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag); min = max = 0; cimg_std::memset(data,0,(cimg::X11attr().nb_bits==8?sizeof(unsigned char): (cimg::X11attr().nb_bits==16?sizeof(unsigned short):sizeof(unsigned int)))*width*height); return paint(); } template CImgDisplay& assign(const CImg& img, const char *title=0, const unsigned int normalization_type=3, const bool fullscreen_flag=false, const bool closed_flag=false) { if (!img) return assign(); CImg tmp; const CImg& nimg = (img.depth==1)?img:(tmp=img.get_projections2d(img.width/2,img.height/2,img.depth/2)); _assign(nimg.width,nimg.height,title,normalization_type,fullscreen_flag,closed_flag); if (normalization==2) min = (float)nimg.minmax(max); return render(nimg).paint(); } template CImgDisplay& assign(const CImgList& list, const char *title=0, const unsigned int normalization_type=3, const bool fullscreen_flag=false, const bool closed_flag=false) { if (!list) return assign(); CImg tmp; const CImg img = list.get_append('x','p'), &nimg = (img.depth==1)?img:(tmp=img.get_projections2d(img.width/2,img.height/2,img.depth/2)); _assign(nimg.width,nimg.height,title,normalization_type,fullscreen_flag,closed_flag); if (normalization==2) min = (float)nimg.minmax(max); return render(nimg).paint(); } CImgDisplay& assign(const CImgDisplay& win) { if (!win) return assign(); _assign(win.width,win.height,win.title,win.normalization,win.is_fullscreen,win.is_closed); cimg_std::memcpy(data,win.data,(cimg::X11attr().nb_bits==8?sizeof(unsigned char): cimg::X11attr().nb_bits==16?sizeof(unsigned short): sizeof(unsigned int))*width*height); return paint(); } CImgDisplay& resize(const int nwidth, const int nheight, const bool redraw=true) { if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign(); if (is_empty()) return assign(nwidth,nheight); const unsigned int tmpdimx = (nwidth>0)?nwidth:(-nwidth*width/100), tmpdimy = (nheight>0)?nheight:(-nheight*height/100), dimx = tmpdimx?tmpdimx:1, dimy = tmpdimy?tmpdimy:1; XLockDisplay(cimg::X11attr().display); if (window_width!=dimx || window_height!=dimy) XResizeWindow(cimg::X11attr().display,window,dimx,dimy); if (width!=dimx || height!=dimy) switch (cimg::X11attr().nb_bits) { case 8 : { unsigned char foo = 0; _resize(foo,dimx,dimy,redraw); } break; case 16 : { unsigned short foo = 0; _resize(foo,dimx,dimy,redraw); } break; default : { unsigned int foo = 0; _resize(foo,dimx,dimy,redraw); } } window_width = width = dimx; window_height = height = dimy; is_resized = false; XUnlockDisplay(cimg::X11attr().display); if (is_fullscreen) move((screen_dimx()-width)/2,(screen_dimy()-height)/2); if (redraw) return paint(); return *this; } CImgDisplay& toggle_fullscreen(const bool redraw=true) { if (is_empty()) return *this; if (redraw) { const unsigned int bufsize = width*height*(cimg::X11attr().nb_bits==8?1:(cimg::X11attr().nb_bits==16?2:4)); void *odata = cimg_std::malloc(bufsize); cimg_std::memcpy(odata,data,bufsize); assign(width,height,title,normalization,!is_fullscreen,false); cimg_std::memcpy(data,odata,bufsize); cimg_std::free(odata); return paint(false); } return assign(width,height,title,normalization,!is_fullscreen,false); } CImgDisplay& show() { if (!is_empty() && is_closed) { XLockDisplay(cimg::X11attr().display); if (is_fullscreen) _init_fullscreen(); _map_window(); is_closed = false; XUnlockDisplay(cimg::X11attr().display); return paint(); } return *this; } CImgDisplay& close() { if (!is_empty() && !is_closed) { XLockDisplay(cimg::X11attr().display); if (is_fullscreen) _desinit_fullscreen(); XUnmapWindow(cimg::X11attr().display,window); window_x = window_y = -1; is_closed = true; XUnlockDisplay(cimg::X11attr().display); } return *this; } CImgDisplay& move(const int posx, const int posy) { if (is_empty()) return *this; show(); XLockDisplay(cimg::X11attr().display); XMoveWindow(cimg::X11attr().display,window,posx,posy); window_x = posx; window_y = posy; is_moved = false; XUnlockDisplay(cimg::X11attr().display); return paint(); } CImgDisplay& show_mouse() { if (is_empty()) return *this; XLockDisplay(cimg::X11attr().display); XDefineCursor(cimg::X11attr().display,window,None); XUnlockDisplay(cimg::X11attr().display); return *this; } CImgDisplay& hide_mouse() { if (is_empty()) return *this; XLockDisplay(cimg::X11attr().display); const char pix_data[8] = { 0 }; XColor col; col.red = col.green = col.blue = 0; Pixmap pix = XCreateBitmapFromData(cimg::X11attr().display,window,pix_data,8,8); Cursor cur = XCreatePixmapCursor(cimg::X11attr().display,pix,pix,&col,&col,0,0); XFreePixmap(cimg::X11attr().display,pix); XDefineCursor(cimg::X11attr().display,window,cur); XUnlockDisplay(cimg::X11attr().display); return *this; } CImgDisplay& set_mouse(const int posx, const int posy) { if (is_empty() || is_closed) return *this; XLockDisplay(cimg::X11attr().display); XWarpPointer(cimg::X11attr().display,None,window,0,0,0,0,posx,posy); mouse_x = posx; mouse_y = posy; is_moved = false; XSync(cimg::X11attr().display, False); XUnlockDisplay(cimg::X11attr().display); return *this; } CImgDisplay& set_title(const char *format, ...) { if (is_empty()) return *this; char tmp[1024] = {0}; va_list ap; va_start(ap, format); cimg_std::vsprintf(tmp,format,ap); va_end(ap); if (title) delete[] title; const int s = cimg::strlen(tmp)+1; title = new char[s]; cimg_std::memcpy(title,tmp,s*sizeof(char)); XLockDisplay(cimg::X11attr().display); XStoreName(cimg::X11attr().display,window,tmp); XUnlockDisplay(cimg::X11attr().display); return *this; } template CImgDisplay& display(const CImg& img) { if (img.is_empty()) throw CImgArgumentException("CImgDisplay::display() : Cannot display empty image."); if (is_empty()) assign(img.width,img.height); return render(img).paint(false); } CImgDisplay& paint(const bool wait_expose=true) { if (is_empty()) return *this; XLockDisplay(cimg::X11attr().display); _paint(wait_expose); XUnlockDisplay(cimg::X11attr().display); return *this; } template CImgDisplay& render(const CImg& img, const bool flag8=false) { if (is_empty()) return *this; if (!img) throw CImgArgumentException("CImgDisplay::_render_image() : Specified input image (%u,%u,%u,%u,%p) is empty.", img.width,img.height,img.depth,img.dim,img.data); if (img.depth!=1) return render(img.get_projections2d(img.width/2,img.height/2,img.depth/2)); if (cimg::X11attr().nb_bits==8 && (img.width!=width || img.height!=height)) return render(img.get_resize(width,height,1,-100,1)); if (cimg::X11attr().nb_bits==8 && !flag8 && img.dim==3) return render(img.get_RGBtoLUT(true),true); const T *data1 = img.data, *data2 = (img.dim>1)?img.ptr(0,0,0,1):data1, *data3 = (img.dim>2)?img.ptr(0,0,0,2):data1; if (cimg::X11attr().blue_first) cimg::swap(data1,data3); XLockDisplay(cimg::X11attr().display); if (!normalization || (normalization==3 && cimg::type::string()==cimg::type::string())) { min = max = 0; switch (cimg::X11attr().nb_bits) { case 8 : { // 256 color palette, no normalization _set_colormap(colormap,img.dim); unsigned char *const ndata = (img.width==width && img.height==height)?(unsigned char*)data:new unsigned char[img.width*img.height]; unsigned char *ptrd = (unsigned char*)ndata; switch (img.dim) { case 1 : for (unsigned int xy = img.width*img.height; xy>0; --xy) (*ptrd++) = (unsigned char)*(data1++); break; case 2 : for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char R = (unsigned char)*(data1++), G = (unsigned char)*(data2++); (*ptrd++) = (R&0xf0) | (G>>4); } break; default : for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char R = (unsigned char)*(data1++), G = (unsigned char)*(data2++), B = (unsigned char)*(data3++); (*ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6); } } if (ndata!=data) { _render_resize(ndata,img.width,img.height,(unsigned char*)data,width,height); delete[] ndata; } } break; case 16 : { // 16 bits colors, no normalization unsigned short *const ndata = (img.width==width && img.height==height)?(unsigned short*)data:new unsigned short[img.width*img.height]; unsigned char *ptrd = (unsigned char*)ndata; const unsigned int M = 248; switch (img.dim) { case 1 : if (cimg::X11attr().byte_order) for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char val = (unsigned char)*(data1++), G = val>>2; *(ptrd++) = (val&M) | (G>>3); *(ptrd++) = (G<<5) | (G>>1); } else for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char val = (unsigned char)*(data1++), G = val>>2; *(ptrd++) = (G<<5) | (G>>1); *(ptrd++) = (val&M) | (G>>3); } break; case 2 : if (cimg::X11attr().byte_order) for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char G = (unsigned char)*(data2++)>>2; *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3); *(ptrd++) = (G<<5); } else for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char G = (unsigned char)*(data2++)>>2; *(ptrd++) = (G<<5); *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3); } break; default : if (cimg::X11attr().byte_order) for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char G = (unsigned char)*(data2++)>>2; *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3); *(ptrd++) = (G<<5) | ((unsigned char)*(data3++)>>3); } else for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char G = (unsigned char)*(data2++)>>2; *(ptrd++) = (G<<5) | ((unsigned char)*(data3++)>>3); *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3); } } if (ndata!=data) { _render_resize(ndata,img.width,img.height,(unsigned short*)data,width,height); delete[] ndata; } } break; default : { // 24 bits colors, no normalization unsigned int *const ndata = (img.width==width && img.height==height)?(unsigned int*)data:new unsigned int[img.width*img.height]; if (sizeof(int)==4) { // 32 bits int uses optimized version unsigned int *ptrd = ndata; switch (img.dim) { case 1 : if (cimg::X11attr().byte_order==cimg::endianness()) for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char val = (unsigned char)*(data1++); *(ptrd++) = (val<<16) | (val<<8) | val; } else for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char val = (unsigned char)*(data1++)<<8; *(ptrd++) = (val<<16) | (val<<8) | val; } break; case 2 : if (cimg::X11attr().byte_order==cimg::endianness()) for (unsigned int xy = img.width*img.height; xy>0; --xy) *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8); else for (unsigned int xy = img.width*img.height; xy>0; --xy) *(ptrd++) = ((unsigned char)*(data2++)<<16) | ((unsigned char)*(data1++)<<8); break; default : if (cimg::X11attr().byte_order==cimg::endianness()) for (unsigned int xy = img.width*img.height; xy>0; --xy) *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) | (unsigned char)*(data3++); else for (unsigned int xy = img.width*img.height; xy>0; --xy) *(ptrd++) = ((unsigned char)*(data3++)<<24) | ((unsigned char)*(data2++)<<16) | ((unsigned char)*(data1++)<<8); } } else { unsigned char *ptrd = (unsigned char*)ndata; switch (img.dim) { case 1 : if (cimg::X11attr().byte_order) for (unsigned int xy = img.width*img.height; xy>0; --xy) { *(ptrd++) = 0; *(ptrd++) = (unsigned char)*(data1++); *(ptrd++) = 0; *(ptrd++) = 0; } else for (unsigned int xy = img.width*img.height; xy>0; --xy) { *(ptrd++) = 0; *(ptrd++) = 0; *(ptrd++) = (unsigned char)*(data1++); *(ptrd++) = 0; } break; case 2 : if (cimg::X11attr().byte_order) cimg::swap(data1,data2); for (unsigned int xy = img.width*img.height; xy>0; --xy) { *(ptrd++) = 0; *(ptrd++) = (unsigned char)*(data2++); *(ptrd++) = (unsigned char)*(data1++); *(ptrd++) = 0; } break; default : if (cimg::X11attr().byte_order) for (unsigned int xy = img.width*img.height; xy>0; --xy) { *(ptrd++) = 0; *(ptrd++) = (unsigned char)*(data1++); *(ptrd++) = (unsigned char)*(data2++); *(ptrd++) = (unsigned char)*(data3++); } else for (unsigned int xy = img.width*img.height; xy>0; --xy) { *(ptrd++) = (unsigned char)*(data3++); *(ptrd++) = (unsigned char)*(data2++); *(ptrd++) = (unsigned char)*(data1++); *(ptrd++) = 0; } } } if (ndata!=data) { _render_resize(ndata,img.width,img.height,(unsigned int*)data,width,height); delete[] ndata; } } }; } else { if (normalization==3) { if (cimg::type::is_float()) min = (float)img.minmax(max); else { min = (float)cimg::type::min(); max = (float)cimg::type::max(); } } else if ((min>max) || normalization==1) min = (float)img.minmax(max); const float delta = max-min, mm = delta?delta:1.0f; switch (cimg::X11attr().nb_bits) { case 8 : { // 256 color palette, with normalization _set_colormap(colormap,img.dim); unsigned char *const ndata = (img.width==width && img.height==height)?(unsigned char*)data:new unsigned char[img.width*img.height]; unsigned char *ptrd = (unsigned char*)ndata; switch (img.dim) { case 1 : for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char R = (unsigned char)(255*(*(data1++)-min)/mm); *(ptrd++) = R; } break; case 2 : for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char R = (unsigned char)(255*(*(data1++)-min)/mm), G = (unsigned char)(255*(*(data2++)-min)/mm); (*ptrd++) = (R&0xf0) | (G>>4); } break; default : for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char R = (unsigned char)(255*(*(data1++)-min)/mm), G = (unsigned char)(255*(*(data2++)-min)/mm), B = (unsigned char)(255*(*(data3++)-min)/mm); *(ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6); } } if (ndata!=data) { _render_resize(ndata,img.width,img.height,(unsigned char*)data,width,height); delete[] ndata; } } break; case 16 : { // 16 bits colors, with normalization unsigned short *const ndata = (img.width==width && img.height==height)?(unsigned short*)data:new unsigned short[img.width*img.height]; unsigned char *ptrd = (unsigned char*)ndata; const unsigned int M = 248; switch (img.dim) { case 1 : if (cimg::X11attr().byte_order) for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char val = (unsigned char)(255*(*(data1++)-min)/mm), G = val>>2; *(ptrd++) = (val&M) | (G>>3); *(ptrd++) = (G<<5) | (val>>3); } else for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char val = (unsigned char)(255*(*(data1++)-min)/mm), G = val>>2; *(ptrd++) = (G<<5) | (val>>3); *(ptrd++) = (val&M) | (G>>3); } break; case 2 : if (cimg::X11attr().byte_order) for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char G = (unsigned char)(255*(*(data2++)-min)/mm)>>2; *(ptrd++) = ((unsigned char)(255*(*(data1++)-min)/mm)&M) | (G>>3); *(ptrd++) = (G<<5); } else for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char G = (unsigned char)(255*(*(data2++)-min)/mm)>>2; *(ptrd++) = (G<<5); *(ptrd++) = ((unsigned char)(255*(*(data1++)-min)/mm)&M) | (G>>3); } break; default : if (cimg::X11attr().byte_order) for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char G = (unsigned char)(255*(*(data2++)-min)/mm)>>2; *(ptrd++) = ((unsigned char)(255*(*(data1++)-min)/mm)&M) | (G>>3); *(ptrd++) = (G<<5) | ((unsigned char)(255*(*(data3++)-min)/mm)>>3); } else for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char G = (unsigned char)(255*(*(data2++)-min)/mm)>>2; *(ptrd++) = (G<<5) | ((unsigned char)(255*(*(data3++)-min)/mm)>>3); *(ptrd++) = ((unsigned char)(255*(*(data1++)-min)/mm)&M) | (G>>3); } } if (ndata!=data) { _render_resize(ndata,img.width,img.height,(unsigned short*)data,width,height); delete[] ndata; } } break; default : { // 24 bits colors, with normalization unsigned int *const ndata = (img.width==width && img.height==height)?(unsigned int*)data:new unsigned int[img.width*img.height]; if (sizeof(int)==4) { // 32 bits int uses optimized version unsigned int *ptrd = ndata; switch (img.dim) { case 1 : if (cimg::X11attr().byte_order==cimg::endianness()) for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char val = (unsigned char)(255*(*(data1++)-min)/mm); *(ptrd++) = (val<<16) | (val<<8) | val; } else for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char val = (unsigned char)(255*(*(data1++)-min)/mm); *(ptrd++) = (val<<24) | (val<<16) | (val<<8); } break; case 2 : if (cimg::X11attr().byte_order==cimg::endianness()) for (unsigned int xy = img.width*img.height; xy>0; --xy) *(ptrd++) = ((unsigned char)(255*(*(data1++)-min)/mm)<<16) | ((unsigned char)(255*(*(data2++)-min)/mm)<<8); else for (unsigned int xy = img.width*img.height; xy>0; --xy) *(ptrd++) = ((unsigned char)(255*(*(data2++)-min)/mm)<<16) | ((unsigned char)(255*(*(data1++)-min)/mm)<<8); break; default : if (cimg::X11attr().byte_order==cimg::endianness()) for (unsigned int xy = img.width*img.height; xy>0; --xy) *(ptrd++) = ((unsigned char)(255*(*(data1++)-min)/mm)<<16) | ((unsigned char)(255*(*(data2++)-min)/mm)<<8) | (unsigned char)(255*(*(data3++)-min)/mm); else for (unsigned int xy = img.width*img.height; xy>0; --xy) *(ptrd++) = ((unsigned char)(255*(*(data3++)-min)/mm)<<24) | ((unsigned char)(255*(*(data2++)-min)/mm)<<16) | ((unsigned char)(255*(*(data1++)-min)/mm)<<8); } } else { unsigned char *ptrd = (unsigned char*)ndata; switch (img.dim) { case 1 : if (cimg::X11attr().byte_order) for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char val = (unsigned char)(255*(*(data1++)-min)/mm); (*ptrd++) = 0; (*ptrd++) = val; (*ptrd++) = val; (*ptrd++) = val; } else for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char val = (unsigned char)(255*(*(data1++)-min)/mm); (*ptrd++) = val; (*ptrd++) = val; (*ptrd++) = val; (*ptrd++) = 0; } break; case 2 : if (cimg::X11attr().byte_order) cimg::swap(data1,data2); for (unsigned int xy = img.width*img.height; xy>0; --xy) { (*ptrd++) = 0; (*ptrd++) = (unsigned char)(255*(*(data2++)-min)/mm); (*ptrd++) = (unsigned char)(255*(*(data1++)-min)/mm); (*ptrd++) = 0; } break; default : if (cimg::X11attr().byte_order) for (unsigned int xy = img.width*img.height; xy>0; --xy) { (*ptrd++) = 0; (*ptrd++) = (unsigned char)(255*(*(data1++)-min)/mm); (*ptrd++) = (unsigned char)(255*(*(data2++)-min)/mm); (*ptrd++) = (unsigned char)(255*(*(data3++)-min)/mm); } else for (unsigned int xy = img.width*img.height; xy>0; --xy) { (*ptrd++) = (unsigned char)(255*(*(data3++)-min)/mm); (*ptrd++) = (unsigned char)(255*(*(data2++)-min)/mm); (*ptrd++) = (unsigned char)(255*(*(data1++)-min)/mm); (*ptrd++) = 0; } } } if (ndata!=data) { _render_resize(ndata,img.width,img.height,(unsigned int*)data,width,height); delete[] ndata; } } } } XUnlockDisplay(cimg::X11attr().display); return *this; } template const CImgDisplay& snapshot(CImg& img) const { if (is_empty()) img.assign(); else { img.assign(width,height,1,3); T *data1 = img.ptr(0,0,0,0), *data2 = img.ptr(0,0,0,1), *data3 = img.ptr(0,0,0,2); if (cimg::X11attr().blue_first) cimg::swap(data1,data3); switch (cimg::X11attr().nb_bits) { case 8 : { unsigned char *ptrs = (unsigned char*)data; for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char val = *(ptrs++); *(data1++) = val&0xe0; *(data2++) = (val&0x1c)<<3; *(data3++) = val<<6; } } break; case 16 : { unsigned char *ptrs = (unsigned char*)data; if (cimg::X11attr().byte_order) for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char val0 = *(ptrs++), val1 = *(ptrs++); *(data1++) = val0&0xf8; *(data2++) = (val0<<5) | ((val1&0xe0)>>5); *(data3++) = val1<<3; } else for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned short val0 = *(ptrs++), val1 = *(ptrs++); *(data1++) = val1&0xf8; *(data2++) = (val1<<5) | ((val0&0xe0)>>5); *(data3++) = val0<<3; } } break; default : { unsigned char *ptrs = (unsigned char*)data; if (cimg::X11attr().byte_order) for (unsigned int xy = img.width*img.height; xy>0; --xy) { ++ptrs; *(data1++) = *(ptrs++); *(data2++) = *(ptrs++); *(data3++) = *(ptrs++); } else for (unsigned int xy = img.width*img.height; xy>0; --xy) { *(data3++) = *(ptrs++); *(data2++) = *(ptrs++); *(data1++) = *(ptrs++); ++ptrs; } } } } return *this; } // Windows-based display //----------------------- #elif cimg_display==2 CLIENTCREATESTRUCT ccs; BITMAPINFO bmi; unsigned int *data; DEVMODE curr_mode; HWND window; HWND background_window; HDC hdc; HANDLE thread; HANDLE created; HANDLE mutex; bool mouse_tracking; bool visible_cursor; static int screen_dimx() { DEVMODE mode; mode.dmSize = sizeof(DEVMODE); mode.dmDriverExtra = 0; EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode); return mode.dmPelsWidth; } static int screen_dimy() { DEVMODE mode; mode.dmSize = sizeof(DEVMODE); mode.dmDriverExtra = 0; EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode); return mode.dmPelsHeight; } static void wait_all() { WaitForSingleObject(cimg::Win32attr().wait_event,INFINITE); } static LRESULT APIENTRY _handle_events(HWND window,UINT msg,WPARAM wParam,LPARAM lParam) { #ifdef _WIN64 CImgDisplay* disp = (CImgDisplay*)GetWindowLongPtr(window,GWLP_USERDATA); #else CImgDisplay* disp = (CImgDisplay*)GetWindowLong(window,GWL_USERDATA); #endif MSG st_msg; switch (msg) { case WM_CLOSE : disp->mouse_x = disp->mouse_y = -1; disp->window_x = disp->window_y = 0; if (disp->button) { cimg_std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,512-1); disp->button = 0; } if (disp->key) { cimg_std::memmove((void*)(disp->keys+1),(void*)disp->keys,512-1); disp->key = 0; } if (disp->released_key) { cimg_std::memmove((void*)(disp->released_keys+1),(void*)disp->released_keys,512-1); disp->released_key = 0; } disp->is_closed = true; ReleaseMutex(disp->mutex); ShowWindow(disp->window,SW_HIDE); disp->is_event = true; SetEvent(cimg::Win32attr().wait_event); return 0; case WM_SIZE : { while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {} WaitForSingleObject(disp->mutex,INFINITE); const unsigned int nw = LOWORD(lParam),nh = HIWORD(lParam); if (nw && nh && (nw!=disp->width || nh!=disp->height)) { disp->window_width = nw; disp->window_height = nh; disp->mouse_x = disp->mouse_y = -1; disp->is_resized = disp->is_event = true; SetEvent(cimg::Win32attr().wait_event); } ReleaseMutex(disp->mutex); } break; case WM_MOVE : { while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {} WaitForSingleObject(disp->mutex,INFINITE); const int nx = (int)(short)(LOWORD(lParam)), ny = (int)(short)(HIWORD(lParam)); if (nx!=disp->window_x || ny!=disp->window_y) { disp->window_x = nx; disp->window_y = ny; disp->is_moved = disp->is_event = true; SetEvent(cimg::Win32attr().wait_event); } ReleaseMutex(disp->mutex); } break; case WM_PAINT : disp->paint(); break; case WM_KEYDOWN : disp->update_iskey((unsigned int)wParam,true); if (disp->key) cimg_std::memmove((void*)(disp->keys+1),(void*)disp->keys,512-1); disp->key = (unsigned int)wParam; if (disp->released_key) { cimg_std::memmove((void*)(disp->released_keys+1),(void*)disp->released_keys,512-1); disp->released_key = 0; } disp->is_event = true; SetEvent(cimg::Win32attr().wait_event); break; case WM_MOUSEMOVE : { while (PeekMessage(&st_msg,window,WM_MOUSEMOVE,WM_MOUSEMOVE,PM_REMOVE)) {} disp->mouse_x = LOWORD(lParam); disp->mouse_y = HIWORD(lParam); #if (_WIN32_WINNT>=0x0400) && !defined(NOTRACKMOUSEEVENT) if (!disp->mouse_tracking) { TRACKMOUSEEVENT tme; tme.cbSize = sizeof(TRACKMOUSEEVENT); tme.dwFlags = TME_LEAVE; tme.hwndTrack = disp->window; if (TrackMouseEvent(&tme)) disp->mouse_tracking = true; } #endif if (disp->mouse_x<0 || disp->mouse_y<0 || disp->mouse_x>=disp->dimx() || disp->mouse_y>=disp->dimy()) disp->mouse_x = disp->mouse_y = -1; disp->is_event = true; SetEvent(cimg::Win32attr().wait_event); } break; case WM_MOUSELEAVE : { disp->mouse_x = disp->mouse_y = -1; disp->mouse_tracking = false; } break; case WM_LBUTTONDOWN : cimg_std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,512-1); disp->button|=1U; disp->is_event = true; SetEvent(cimg::Win32attr().wait_event); break; case WM_RBUTTONDOWN : cimg_std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,512-1); disp->button|=2U; disp->is_event = true; SetEvent(cimg::Win32attr().wait_event); break; case WM_MBUTTONDOWN : cimg_std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,512-1); disp->button|=4U; disp->is_event = true; SetEvent(cimg::Win32attr().wait_event); break; case 0x020A : // WM_MOUSEWHEEL: disp->wheel+=(int)((short)HIWORD(wParam))/120; disp->is_event = true; SetEvent(cimg::Win32attr().wait_event); case WM_KEYUP : disp->update_iskey((unsigned int)wParam,false); if (disp->key) { cimg_std::memmove((void*)(disp->keys+1),(void*)disp->keys,512-1); disp->key = 0; } if (disp->released_key) cimg_std::memmove((void*)(disp->released_keys+1),(void*)disp->released_keys,512-1); disp->released_key = (unsigned int)wParam; disp->is_event = true; SetEvent(cimg::Win32attr().wait_event); break; case WM_LBUTTONUP : cimg_std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,512-1); disp->button&=~1U; disp->is_event = true; SetEvent(cimg::Win32attr().wait_event); break; case WM_RBUTTONUP : cimg_std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,512-1); disp->button&=~2U; disp->is_event = true; SetEvent(cimg::Win32attr().wait_event); break; case WM_MBUTTONUP : cimg_std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,512-1); disp->button&=~4U; disp->is_event = true; SetEvent(cimg::Win32attr().wait_event); break; case WM_SETCURSOR : if (disp->visible_cursor) ShowCursor(TRUE); else ShowCursor(FALSE); break; } return DefWindowProc(window,msg,wParam,lParam); } static DWORD WINAPI _events_thread(void* arg) { CImgDisplay *disp = (CImgDisplay*)(((void**)arg)[0]); const char *title = (const char*)(((void**)arg)[1]); MSG msg; delete[] (void**)arg; disp->bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); disp->bmi.bmiHeader.biWidth = disp->width; disp->bmi.bmiHeader.biHeight = -(int)disp->height; disp->bmi.bmiHeader.biPlanes = 1; disp->bmi.bmiHeader.biBitCount = 32; disp->bmi.bmiHeader.biCompression = BI_RGB; disp->bmi.bmiHeader.biSizeImage = 0; disp->bmi.bmiHeader.biXPelsPerMeter = 1; disp->bmi.bmiHeader.biYPelsPerMeter = 1; disp->bmi.bmiHeader.biClrUsed = 0; disp->bmi.bmiHeader.biClrImportant = 0; disp->data = new unsigned int[disp->width*disp->height]; if (!disp->is_fullscreen) { // Normal window RECT rect; rect.left = rect.top = 0; rect.right = disp->width-1; rect.bottom = disp->height-1; AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); const int border1 = (rect.right-rect.left+1-disp->width)/2, border2 = rect.bottom-rect.top+1-disp->height-border1; disp->window = CreateWindowA("MDICLIENT",title?title:" ", WS_OVERLAPPEDWINDOW | (disp->is_closed?0:WS_VISIBLE), CW_USEDEFAULT,CW_USEDEFAULT, disp->width + 2*border1, disp->height + border1 + border2, 0,0,0,&(disp->ccs)); if (!disp->is_closed) { GetWindowRect(disp->window,&rect); disp->window_x = rect.left + border1; disp->window_y = rect.top + border2; } else disp->window_x = disp->window_y = 0; } else { // Fullscreen window const unsigned int sx = screen_dimx(), sy = screen_dimy(); disp->window = CreateWindowA("MDICLIENT",title?title:" ", WS_POPUP | (disp->is_closed?0:WS_VISIBLE), (sx-disp->width)/2, (sy-disp->height)/2, disp->width,disp->height,0,0,0,&(disp->ccs)); disp->window_x = disp->window_y = 0; } SetForegroundWindow(disp->window); disp->hdc = GetDC(disp->window); disp->window_width = disp->width; disp->window_height = disp->height; disp->flush(); #ifdef _WIN64 SetWindowLongPtr(disp->window,GWLP_USERDATA,(LONG_PTR)disp); SetWindowLongPtr(disp->window,GWLP_WNDPROC,(LONG_PTR)_handle_events); #else SetWindowLong(disp->window,GWL_USERDATA,(LONG)disp); SetWindowLong(disp->window,GWL_WNDPROC,(LONG)_handle_events); #endif SetEvent(disp->created); while (GetMessage(&msg,0,0,0)) DispatchMessage(&msg); return 0; } CImgDisplay& _update_window_pos() { if (!is_closed) { RECT rect; rect.left = rect.top = 0; rect.right = width-1; rect.bottom = height-1; AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); const int border1 = (rect.right-rect.left+1-width)/2, border2 = rect.bottom-rect.top+1-height-border1; GetWindowRect(window,&rect); window_x = rect.left + border1; window_y = rect.top + border2; } else window_x = window_y = -1; return *this; } void _init_fullscreen() { background_window = 0; if (is_fullscreen && !is_closed) { DEVMODE mode; unsigned int imode = 0, ibest = 0, bestbpp = 0, bw = ~0U, bh = ~0U; for (mode.dmSize = sizeof(DEVMODE), mode.dmDriverExtra = 0; EnumDisplaySettings(0,imode,&mode); ++imode) { const unsigned int nw = mode.dmPelsWidth, nh = mode.dmPelsHeight; if (nw>=width && nh>=height && mode.dmBitsPerPel>=bestbpp && nw<=bw && nh<=bh) { bestbpp = mode.dmBitsPerPel; ibest = imode; bw = nw; bh = nh; } } if (bestbpp) { curr_mode.dmSize = sizeof(DEVMODE); curr_mode.dmDriverExtra = 0; EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&curr_mode); EnumDisplaySettings(0,ibest,&mode); ChangeDisplaySettings(&mode,0); } else curr_mode.dmSize = 0; const unsigned int sx = screen_dimx(), sy = screen_dimy(); if (sx!=width || sy!=height) { CLIENTCREATESTRUCT background_ccs; background_window = CreateWindowA("MDICLIENT","",WS_POPUP | WS_VISIBLE, 0,0,sx,sy,0,0,0,&background_ccs); SetForegroundWindow(background_window); } } else curr_mode.dmSize = 0; } void _desinit_fullscreen() { if (is_fullscreen) { if (background_window) DestroyWindow(background_window); background_window = 0; if (curr_mode.dmSize) ChangeDisplaySettings(&curr_mode,0); is_fullscreen = false; } } CImgDisplay& _assign(const unsigned int dimw, const unsigned int dimh, const char *ptitle=0, const unsigned int normalization_type=3, const bool fullscreen_flag=false, const bool closed_flag=false) { // Allocate space for window title const int s = cimg::strlen(ptitle)+1; char *tmp_title = s?new char[s]:0; if (s) cimg_std::memcpy(tmp_title,ptitle,s*sizeof(char)); // Destroy previous window if existing if (!is_empty()) assign(); // Set display variables width = cimg::min(dimw,(unsigned int)screen_dimx()); height = cimg::min(dimh,(unsigned int)screen_dimy()); normalization = normalization_type<4?normalization_type:3; is_fullscreen = fullscreen_flag; window_x = window_y = 0; is_closed = closed_flag; visible_cursor = true; mouse_tracking = false; title = tmp_title; flush(); if (is_fullscreen) _init_fullscreen(); // Create event thread void *arg = (void*)(new void*[2]); ((void**)arg)[0]=(void*)this; ((void**)arg)[1]=(void*)title; unsigned long ThreadID = 0; mutex = CreateMutex(0,FALSE,0); created = CreateEvent(0,FALSE,FALSE,0); thread = CreateThread(0,0,_events_thread,arg,0,&ThreadID); WaitForSingleObject(created,INFINITE); return *this; } CImgDisplay& assign() { if (is_empty()) return *this; DestroyWindow(window); TerminateThread(thread,0); if (data) delete[] data; if (title) delete[] title; if (is_fullscreen) _desinit_fullscreen(); width = height = normalization = window_width = window_height = 0; window_x = window_y = 0; is_fullscreen = false; is_closed = true; min = max = 0; title = 0; flush(); return *this; } CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *title=0, const unsigned int normalization_type=3, const bool fullscreen_flag=false, const bool closed_flag=false) { if (!dimw || !dimh) return assign(); _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag); min = max = 0; cimg_std::memset(data,0,sizeof(unsigned int)*width*height); return paint(); } template CImgDisplay& assign(const CImg& img, const char *title=0, const unsigned int normalization_type=3, const bool fullscreen_flag=false, const bool closed_flag=false) { if (!img) return assign(); CImg tmp; const CImg& nimg = (img.depth==1)?img:(tmp=img.get_projections2d(img.width/2,img.height/2,img.depth/2)); _assign(nimg.width,nimg.height,title,normalization_type,fullscreen_flag,closed_flag); if (normalization==2) min = (float)nimg.minmax(max); return display(nimg); } template CImgDisplay& assign(const CImgList& list, const char *title=0, const unsigned int normalization_type=3, const bool fullscreen_flag=false, const bool closed_flag=false) { if (!list) return assign(); CImg tmp; const CImg img = list.get_append('x','p'), &nimg = (img.depth==1)?img:(tmp=img.get_projections2d(img.width/2,img.height/2,img.depth/2)); _assign(nimg.width,nimg.height,title,normalization_type,fullscreen_flag,closed_flag); if (normalization==2) min = (float)nimg.minmax(max); return display(nimg); } CImgDisplay& assign(const CImgDisplay& win) { if (!win) return assign(); _assign(win.width,win.height,win.title,win.normalization,win.is_fullscreen,win.is_closed); cimg_std::memcpy(data,win.data,sizeof(unsigned int)*width*height); return paint(); } CImgDisplay& resize(const int nwidth, const int nheight, const bool redraw=true) { if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign(); if (is_empty()) return assign(nwidth,nheight); const unsigned int tmpdimx=(nwidth>0)?nwidth:(-nwidth*width/100), tmpdimy=(nheight>0)?nheight:(-nheight*height/100), dimx = tmpdimx?tmpdimx:1, dimy = tmpdimy?tmpdimy:1; if (window_width!=dimx || window_height!=dimy) { RECT rect; rect.left = rect.top = 0; rect.right = dimx-1; rect.bottom = dimy-1; AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); const int cwidth = rect.right-rect.left+1, cheight = rect.bottom-rect.top+1; SetWindowPos(window,0,0,0,cwidth,cheight,SWP_NOMOVE | SWP_NOZORDER | SWP_NOCOPYBITS); } if (width!=dimx || height!=dimy) { unsigned int *ndata = new unsigned int[dimx*dimy]; if (redraw) _render_resize(data,width,height,ndata,dimx,dimy); else cimg_std::memset(ndata,0x80,sizeof(unsigned int)*dimx*dimy); delete[] data; data = ndata; bmi.bmiHeader.biWidth = dimx; bmi.bmiHeader.biHeight = -(int)dimy; width = dimx; height = dimy; } window_width = dimx; window_height = dimy; is_resized = false; if (is_fullscreen) move((screen_dimx()-width)/2,(screen_dimy()-height)/2); if (redraw) return paint(); return *this; } CImgDisplay& toggle_fullscreen(const bool redraw=true) { if (is_empty()) return *this; if (redraw) { const unsigned int bufsize = width*height*4; void *odata = cimg_std::malloc(bufsize); cimg_std::memcpy(odata,data,bufsize); assign(width,height,title,normalization,!is_fullscreen,false); cimg_std::memcpy(data,odata,bufsize); cimg_std::free(odata); return paint(); } return assign(width,height,title,normalization,!is_fullscreen,false); } CImgDisplay& show() { if (is_empty()) return *this; if (is_closed) { is_closed = false; if (is_fullscreen) _init_fullscreen(); ShowWindow(window,SW_SHOW); _update_window_pos(); } return paint(); } CImgDisplay& close() { if (is_empty()) return *this; if (!is_closed && !is_fullscreen) { if (is_fullscreen) _desinit_fullscreen(); ShowWindow(window,SW_HIDE); is_closed = true; window_x = window_y = 0; } return *this; } CImgDisplay& move(const int posx, const int posy) { if (is_empty()) return *this; if (!is_fullscreen) { RECT rect; rect.left = rect.top = 0; rect.right=window_width-1; rect.bottom=window_height-1; AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); const int border1 = (rect.right-rect.left+1-width)/2, border2 = rect.bottom-rect.top+1-height-border1; SetWindowPos(window,0,posx-border1,posy-border2,0,0,SWP_NOSIZE | SWP_NOZORDER); } else SetWindowPos(window,0,posx,posy,0,0,SWP_NOSIZE | SWP_NOZORDER); window_x = posx; window_y = posy; is_moved = false; return show(); } CImgDisplay& show_mouse() { if (is_empty()) return *this; visible_cursor = true; ShowCursor(TRUE); SendMessage(window,WM_SETCURSOR,0,0); return *this; } CImgDisplay& hide_mouse() { if (is_empty()) return *this; visible_cursor = false; ShowCursor(FALSE); SendMessage(window,WM_SETCURSOR,0,0); return *this; } CImgDisplay& set_mouse(const int posx, const int posy) { if (!is_closed && posx>=0 && posy>=0) { _update_window_pos(); const int res = (int)SetCursorPos(window_x+posx,window_y+posy); if (res) { mouse_x = posx; mouse_y = posy; } } return *this; } CImgDisplay& set_title(const char *format, ...) { if (is_empty()) return *this; char tmp[1024] = {0}; va_list ap; va_start(ap, format); cimg_std::vsprintf(tmp,format,ap); va_end(ap); if (title) delete[] title; const int s = cimg::strlen(tmp)+1; title = new char[s]; cimg_std::memcpy(title,tmp,s*sizeof(char)); SetWindowTextA(window, tmp); return *this; } template CImgDisplay& display(const CImg& img) { if (img.is_empty()) throw CImgArgumentException("CImgDisplay::display() : Cannot display empty image."); if (is_empty()) assign(img.width,img.height); return render(img).paint(); } CImgDisplay& paint() { if (!is_closed) { WaitForSingleObject(mutex,INFINITE); SetDIBitsToDevice(hdc,0,0,width,height,0,0,0,height,data,&bmi,DIB_RGB_COLORS); ReleaseMutex(mutex); } return *this; } template CImgDisplay& render(const CImg& img) { if (is_empty()) return *this; if (!img) throw CImgArgumentException("CImgDisplay::_render_image() : Specified input image (%u,%u,%u,%u,%p) is empty.", img.width,img.height,img.depth,img.dim,img.data); if (img.depth!=1) return render(img.get_projections2d(img.width/2,img.height/2,img.depth/2)); const T *data1 = img.data, *data2 = (img.dim>=2)?img.ptr(0,0,0,1):data1, *data3 = (img.dim>=3)?img.ptr(0,0,0,2):data1; WaitForSingleObject(mutex,INFINITE); unsigned int *const ndata = (img.width==width && img.height==height)?data:new unsigned int[img.width*img.height], *ptrd = ndata; if (!normalization || (normalization==3 && cimg::type::string()==cimg::type::string())) { min = max = 0; switch (img.dim) { case 1 : { for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char val = (unsigned char)*(data1++); *(ptrd++) = (val<<16) | (val<<8) | val; }} break; case 2 : { for (unsigned int xy = img.width*img.height; xy>0; --xy) *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8); } break; default : { for (unsigned int xy = img.width*img.height; xy>0; --xy) *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) | (unsigned char)*(data3++); } } } else { if (normalization==3) { if (cimg::type::is_float()) min = (float)img.minmax(max); else { min = (float)cimg::type::min(); max = (float)cimg::type::max(); } } else if ((min>max) || normalization==1) min = (float)img.minmax(max); const float delta = max-min, mm = delta?delta:1.0f; switch (img.dim) { case 1 : { for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char val = (unsigned char)(255*(*(data1++)-min)/mm); *(ptrd++) = (val<<16) | (val<<8) | val; }} break; case 2 : { for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char R = (unsigned char)(255*(*(data1++)-min)/mm), G = (unsigned char)(255*(*(data2++)-min)/mm); *(ptrd++) = (R<<16) | (G<<8); }} break; default : { for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char R = (unsigned char)(255*(*(data1++)-min)/mm), G = (unsigned char)(255*(*(data2++)-min)/mm), B = (unsigned char)(255*(*(data3++)-min)/mm); *(ptrd++) = (R<<16) | (G<<8) | B; }} } } if (ndata!=data) { _render_resize(ndata,img.width,img.height,data,width,height); delete[] ndata; } ReleaseMutex(mutex); return *this; } template const CImgDisplay& snapshot(CImg& img) const { if (is_empty()) img.assign(); else { img.assign(width,height,1,3); T *data1 = img.ptr(0,0,0,0), *data2 = img.ptr(0,0,0,1), *data3 = img.ptr(0,0,0,2); unsigned int *ptrs = data; for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned int val = *(ptrs++); *(data1++) = (unsigned char)(val>>16); *(data2++) = (unsigned char)((val>>8)&0xFF); *(data3++) = (unsigned char)(val&0xFF); } } return *this; } // MacOSX - Carbon-based display //------------------------------- // (Code by Adrien Reboisson && Romain Blei, supervised by Jean-Marie Favreau) // #elif cimg_display==3 unsigned int *data; // The bits of the picture WindowRef carbonWindow; // The opaque carbon window struct associated with the display MPCriticalRegionID paintCriticalRegion; // Critical section used when drawing CGColorSpaceRef csr; // Needed for painting CGDataProviderRef dataProvider; // Needed for painting CGImageRef imageRef; // The image UInt32 lastKeyModifiers; // Buffer storing modifiers state // Define the kind of the queries which can be serialized using the event thread. typedef enum { COM_CREATEWINDOW = 0, // Create window query COM_RELEASEWINDOW, // Release window query COM_SHOWWINDOW, // Show window query COM_HIDEWINDOW, // Hide window query COM_SHOWMOUSE, // Show mouse query COM_HIDEMOUSE, // Hide mouse query COM_RESIZEWINDOW, // Resize window query COM_MOVEWINDOW, // Move window query COM_SETTITLE, // Set window title query COM_SETMOUSEPOS // Set cursor position query } CImgCarbonQueryKind; // The query destructor send to the event thread. struct CbSerializedQuery { CImgDisplay* sender; // Query's sender CImgCarbonQueryKind kind; // The kind of the query sent to the background thread short x, y; // X:Y values for move/resize operations char *c; // Char values for window title bool createFullScreenWindow; // Boolean value used for full-screen window creation bool createClosedWindow; // Boolean value used for closed-window creation bool update; // Boolean value used for resize bool success; // Succes or failure of the message, used as return value CbSerializedQuery(CImgDisplay *s, CImgCarbonQueryKind k):sender(s),kind(k),success(false) {}; inline static CbSerializedQuery BuildReleaseWindowQuery(CImgDisplay* sender) { return CbSerializedQuery(sender, COM_RELEASEWINDOW); } inline static CbSerializedQuery BuildCreateWindowQuery(CImgDisplay* sender, const bool fullscreen, const bool closed) { CbSerializedQuery q(sender, COM_CREATEWINDOW); q.createFullScreenWindow = fullscreen; q.createClosedWindow = closed; return q; } inline static CbSerializedQuery BuildShowWindowQuery(CImgDisplay* sender) { return CbSerializedQuery(sender, COM_SHOWWINDOW); } inline static CbSerializedQuery BuildHideWindowQuery(CImgDisplay* sender) { return CbSerializedQuery(sender, COM_HIDEWINDOW); } inline static CbSerializedQuery BuildShowMouseQuery(CImgDisplay* sender) { return CbSerializedQuery(sender, COM_SHOWMOUSE); } inline static CbSerializedQuery BuildHideMouseQuery(CImgDisplay* sender) { return CbSerializedQuery(sender, COM_HIDEMOUSE); } inline static CbSerializedQuery BuildResizeWindowQuery(CImgDisplay* sender, const int x, const int y, bool update) { CbSerializedQuery q(sender, COM_RESIZEWINDOW); q.x = x, q.y = y; q.update = update; return q; } inline static CbSerializedQuery BuildMoveWindowQuery(CImgDisplay* sender, const int x, const int y) { CbSerializedQuery q(sender, COM_MOVEWINDOW); q.x = x, q.y = y; return q; } inline static CbSerializedQuery BuildSetWindowTitleQuery(CImgDisplay* sender, char* c) { CbSerializedQuery q(sender, COM_SETTITLE); q.c = c; return q; } inline static CbSerializedQuery BuildSetWindowPosQuery(CImgDisplay* sender, const int x, const int y) { CbSerializedQuery q(sender, COM_SETMOUSEPOS); q.x = x, q.y = y; return q; } }; // Send a serialized query in a synchroneous way. // @param c Application Carbon global settings. // @param m The query to send. // @result Success/failure of the operation returned by the event thread. bool _CbSendMsg(cimg::CarbonInfo& c, CbSerializedQuery m) { MPNotifyQueue(c.com_queue,&m,0,0); // Send the given message MPWaitOnSemaphore(c.sync_event,kDurationForever); // Wait end of processing notification return m.success; } // Free the window attached to the current display. // @param c Application Carbon global settings. // @result Success/failure of the operation. bool _CbFreeAttachedWindow(cimg::CarbonInfo& c) { if (!_CbSendMsg(c, CbSerializedQuery::BuildReleaseWindowQuery(this))) // Ask the main thread to free the given window throw CImgDisplayException("Cannot release window associated with the current display."); // If a window existed, ask to release it MPEnterCriticalRegion(c.windowListCR,kDurationForever); // Lock the list of the windows --c.windowCount; //Decrement the window count MPExitCriticalRegion(c.windowListCR); // Unlock the list return c.windowCount == 0; } // Create the window attached to the current display. // @param c Application Carbon global settings. // @param title The window title, if any. // @param fullscreen Shoud we start in fullscreen mode ? // @param create_closed If true, the window is created but not displayed. // @result Success/failure of the operation. void _CbCreateAttachedWindow(cimg::CarbonInfo& c, const char* title, const bool fullscreen, const bool create_closed) { if (!_CbSendMsg(c,CbSerializedQuery::BuildCreateWindowQuery(this,fullscreen,create_closed))) // Ask the main thread to create the window throw CImgDisplayException("Cannot create the window associated with the current display."); if (title) set_title(title); // Set the title, if any // Now we can register the window MPEnterCriticalRegion(c.windowListCR,kDurationForever); // Lock the list of the windows ++c.windowCount; //Increment the window count MPExitCriticalRegion(c.windowListCR); // Unlock the list } // Destroy graphic objects previously allocated. We free the image, the data provider, then the colorspace. void _CbFinalizeGraphics() { CGImageRelease (imageRef); // Release the picture CGDataProviderRelease(dataProvider); // Release the DP CGColorSpaceRelease(csr); // Free the cs } // Create graphic objects associated to a display. We have to create a colormap, a data provider, and the image. void _CbInitializeGraphics() { csr = CGColorSpaceCreateDeviceRGB(); // Create the color space first if (!csr) throw CImgDisplayException("CGColorSpaceCreateDeviceRGB() failed."); // Create the DP dataProvider = CGDataProviderCreateWithData(0,data,height*width*sizeof(unsigned int),0); if (!dataProvider) throw CImgDisplayException("CGDataProviderCreateWithData() failed."); // ... and finally the image. if (cimg::endianness()) imageRef = CGImageCreate(width,height,8,32,width*sizeof(unsigned int),csr, kCGImageAlphaNoneSkipFirst,dataProvider,0,false,kCGRenderingIntentDefault); else imageRef = CGImageCreate(width,height,8,32,width*sizeof(unsigned int),csr, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host,dataProvider,0,false,kCGRenderingIntentDefault); if (!imageRef) throw CImgDisplayException("CGImageCreate() failed."); } // Reinit graphic objects. Free them, then reallocate all. // This is used when image bounds are changed or when data source get invalid. void _CbReinitGraphics() { MPEnterCriticalRegion(paintCriticalRegion, kDurationForever); _CbFinalizeGraphics(); _CbInitializeGraphics(); MPExitCriticalRegion(paintCriticalRegion); } // Convert a point having global coordonates into the window coordonates. // We use this function to replace the deprecated GlobalToLocal QuickDraw API. // @param mouseEvent The mouse event which triggered the event handler. // @param window The window where the event occured. // @param point The modified point struct. // @result True if the point struct has been converted successfully. static bool _CbToLocalPointFromMouseEvent(EventRef mouseEvent, WindowRef window, HIPoint* point) { Rect bounds; if (GetWindowBounds(window,kWindowStructureRgn,&bounds)==noErr) { point->x -= bounds.left; point->y -= bounds.top; HIViewRef view = NULL; if (HIViewGetViewForMouseEvent(HIViewGetRoot(window),mouseEvent,&view)==noErr) return HIViewConvertPoint(point, NULL, view) == noErr; } return false; } static int screen_dimx() { return CGDisplayPixelsWide(kCGDirectMainDisplay); } static int screen_dimy() { return CGDisplayPixelsHigh(kCGDirectMainDisplay); } CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *title=0, const unsigned int normalization_type=3, const bool fullscreen_flag=false, const bool closed_flag=false) { if (!dimw || !dimh) return assign(); _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag); min = max = 0; cimg_std::memset(data,0,sizeof(unsigned int)*width*height); return paint(); } template CImgDisplay& assign(const CImg& img, const char *title=0, const unsigned int normalization_type=3, const bool fullscreen_flag=false, const bool closed_flag=false) { if (!img) return assign(); CImg tmp; const CImg& nimg = (img.depth==1)?img:(tmp=img.get_projections2d(img.width/2,img.height/2,img.depth/2)); _assign(nimg.width,nimg.height,title,normalization_type,fullscreen_flag,closed_flag); if (normalization==2) min = (float)nimg.minmax(max); return display(nimg); } template CImgDisplay& assign(const CImgList& list, const char *title=0, const unsigned int normalization_type=3, const bool fullscreen_flag=false, const bool closed_flag=false) { if (!list) return assign(); CImg tmp; const CImg img = list.get_append('x','p'), &nimg = (img.depth==1)?img:(tmp=img.get_projections2d(img.width/2,img.height/2,img.depth/2)); _assign(nimg.width,nimg.height,title,normalization_type,fullscreen_flag,closed_flag); if (normalization==2) min = (float)nimg.minmax(max); return display(nimg); } CImgDisplay& assign(const CImgDisplay &win) { if (!win) return assign(); _assign(win.width,win.height,win.title,win.normalization,win.is_fullscreen,win.is_closed); cimg_std::memcpy(data,win.data,sizeof(unsigned int)*width*height); return paint(); } template CImgDisplay& display(const CImg& img) { if (is_empty()) assign(img.width,img.height); return render(img).paint(); } CImgDisplay& resize(const int nwidth, const int nheight, const bool redraw=true) { if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign(); if (is_empty()) return assign(nwidth,nheight); const unsigned int tmpdimx = (nwidth>0)?nwidth:(-nwidth*width/100), tmpdimy = (nheight>0)?nheight:(-nheight*height/100), dimx = tmpdimx?tmpdimx:1, dimy = tmpdimy?tmpdimy:1; cimg::CarbonInfo& c = cimg::CarbonAttr(); if ((window_width!=dimx || window_height!=dimy) && !_CbSendMsg(c,CbSerializedQuery::BuildResizeWindowQuery(this,dimx,dimy,redraw))) throw CImgDisplayException("CImgDisplay::resize() : Cannot resize the window associated to the current display."); if (width!=dimx || height!=dimy) { unsigned int *ndata = new unsigned int[dimx*dimy]; if (redraw) _render_resize(data,width,height,ndata,dimx,dimy); else cimg_std::memset(ndata,0x80,sizeof(unsigned int)*dimx*dimy); unsigned int const* old_data = data; data = ndata; delete[] old_data; _CbReinitGraphics(); } window_width = width = dimx; window_height = height = dimy; is_resized = false; if (is_fullscreen) move((screen_dimx()-width)/2,(screen_dimy()-height)/2); if (redraw) return paint(); return *this; } CImgDisplay& move(const int posx, const int posy) { if (is_empty()) return *this; if (!is_fullscreen) { // If the operation succeeds, window_x and window_y are updated by the event thread cimg::CarbonInfo& c = cimg::CarbonAttr(); // Send the query if (!_CbSendMsg(c,CbSerializedQuery::BuildMoveWindowQuery(this,posx,posy))) throw CImgDisplayException("CImgDisplay::move() : Cannot move the window associated to the current display."); } return show(); } CImgDisplay& set_mouse(const int posx, const int posy) { if (!is_closed && posx>=0 && posy>=0) { // If the operation succeeds, mouse_x and mouse_y are updated by the event thread cimg::CarbonInfo& c = cimg::CarbonAttr(); // Send the query if (!_CbSendMsg(c,CbSerializedQuery::BuildSetWindowPosQuery(this,posx,posy))) throw CImgDisplayException("CImgDisplay::set_mouse() : Cannot set the mouse position to the current display."); } return *this; } CImgDisplay& hide_mouse() { if (is_empty()) return *this; cimg::CarbonInfo& c = cimg::CarbonAttr(); // Send the query if (!_CbSendMsg(c,CbSerializedQuery::BuildHideMouseQuery(this))) throw CImgDisplayException("CImgDisplay::hide_mouse() : Cannot hide the mouse associated to the current display."); return *this; } CImgDisplay& show_mouse() { if (is_empty()) return *this; cimg::CarbonInfo& c = cimg::CarbonAttr(); // Send the query if (!_CbSendMsg(c,CbSerializedQuery::BuildShowMouseQuery(this))) throw CImgDisplayException("CImgDisplay::show_mouse() : Cannot show the mouse associated to the current display."); return *this; } static void wait_all() { cimg::CarbonInfo& c = cimg::CarbonAttr(); MPWaitOnSemaphore(c.wait_event,kDurationForever); } CImgDisplay& show() { if (is_empty()) return *this; if (is_closed) { cimg::CarbonInfo& c = cimg::CarbonAttr(); if (!_CbSendMsg(c,CbSerializedQuery::BuildShowWindowQuery(this))) throw CImgDisplayException("CImgDisplay::show() : Cannot show the window associated to the current display."); } return paint(); } CImgDisplay& close() { if (is_empty()) return *this; if (!is_closed && !is_fullscreen) { cimg::CarbonInfo& c = cimg::CarbonAttr(); // If the operation succeeds, window_x and window_y are updated on the event thread if (!_CbSendMsg(c,CbSerializedQuery::BuildHideWindowQuery(this))) throw CImgDisplayException("CImgDisplay::close() : Cannot hide the window associated to the current display."); } return *this; } CImgDisplay& set_title(const char *format, ...) { if (is_empty()) return *this; char tmp[1024] = {0}; va_list ap; va_start(ap, format); cimg_std::vsprintf(tmp,format,ap); va_end(ap); if (title) delete[] title; const int s = cimg::strlen(tmp)+1; title = new char[s]; cimg_std::memcpy(title,tmp,s*sizeof(char)); cimg::CarbonInfo& c = cimg::CarbonAttr(); if (!_CbSendMsg(c,CbSerializedQuery::BuildSetWindowTitleQuery(this,tmp))) throw CImgDisplayException("CImgDisplay::set_title() : Cannot set the window title associated to the current display."); return *this; } CImgDisplay& paint() { if (!is_closed) { MPEnterCriticalRegion(paintCriticalRegion,kDurationForever); CGrafPtr portPtr = GetWindowPort(carbonWindow); CGContextRef currentContext = 0; TQDBeginCGContext(portPtr,¤tContext); CGContextSetRGBFillColor(currentContext,255,255,255,255); CGContextFillRect(currentContext,CGRectMake(0,0,window_width,window_height)); CGContextDrawImage(currentContext,CGRectMake(0,int(window_height-height)<0?0:window_height-height,width,height),imageRef); CGContextFlush(currentContext); TQDEndCGContext(portPtr, ¤tContext); MPExitCriticalRegion(paintCriticalRegion); } return *this; } template CImgDisplay& render(const CImg& img) { if (is_empty()) return *this; if (!img) throw CImgArgumentException("CImgDisplay::_render_image() : Specified input image (%u,%u,%u,%u,%p) is empty.", img.width,img.height,img.depth,img.dim,img.data); if (img.depth!=1) return render(img.get_projections2d(img.width/2,img.height/2,img.depth/2)); const T *data1 = img.data, *data2 = (img.dim>=2)?img.ptr(0,0,0,1):data1, *data3 = (img.dim>=3)?img.ptr(0,0,0,2):data1; MPEnterCriticalRegion(paintCriticalRegion, kDurationForever); unsigned int *const ndata = (img.width==width && img.height==height)?data:new unsigned int[img.width*img.height], *ptrd = ndata; if (!normalization || (normalization==3 && cimg::type::string()==cimg::type::string())) { min = max = 0; for (unsigned int xy = img.width*img.height; xy>0; --xy) *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) | (unsigned char)*(data3++); } else { if (normalization==3) { if (cimg::type::is_float()) min = (float)img.minmax(max); else { min = (float)cimg::type::min(); max = (float)cimg::type::max(); } } else if ((min>max) || normalization==1) min = (float)img.minmax(max); const float delta = max-min, mm = delta?delta:1.0f; for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char R = (unsigned char)(255*(*(data1++)-min)/mm), G = (unsigned char)(255*(*(data2++)-min)/mm), B = (unsigned char)(255*(*(data3++)-min)/mm); *(ptrd++) = (R<<16) | (G<<8) | (B); } } if (ndata!=data) { _render_resize(ndata,img.width,img.height,data,width,height); delete[] ndata; } MPExitCriticalRegion(paintCriticalRegion); return *this; } template const CImgDisplay& snapshot(CImg& img) const { if (is_empty()) img.assign(); else { img.assign(width,height,1,3); T *data1 = img.ptr(0,0,0,0), *data2 = img.ptr(0,0,0,1), *data3 = img.ptr(0,0,0,2); unsigned int *ptrs = data; for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned int val = *(ptrs++); *(data1++) = (unsigned char)(val>>16); *(data2++) = (unsigned char)((val>>8)&0xFF); *(data3++) = (unsigned char)(val&0xFF); } } return *this; } CImgDisplay& toggle_fullscreen(const bool redraw=true) { if (is_empty()) return *this; if (redraw) { const unsigned int bufsize = width*height*4; void *odata = cimg_std::malloc(bufsize); cimg_std::memcpy(odata,data,bufsize); assign(width,height,title,normalization,!is_fullscreen,false); cimg_std::memcpy(data,odata,bufsize); cimg_std::free(odata); return paint(); } return assign(width,height,title,normalization,!is_fullscreen,false); } static OSStatus CarbonEventHandler(EventHandlerCallRef myHandler, EventRef theEvent, void* userData) { OSStatus result = eventNotHandledErr; CImgDisplay* disp = (CImgDisplay*) userData; (void)myHandler; // Avoid "unused parameter" cimg::CarbonInfo& c = cimg::CarbonAttr(); // Gets the associated display if (disp) { // Window events are always handled if (GetEventClass(theEvent)==kEventClassWindow) switch (GetEventKind (theEvent)) { case kEventWindowClose : disp->mouse_x = disp->mouse_y = -1; disp->window_x = disp->window_y = 0; if (disp->button) { cimg_std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,512-1); disp->button = 0; } if (disp->key) { cimg_std::memmove((void*)(disp->keys+1),(void*)disp->keys,512-1); disp->key = 0; } if (disp->released_key) { cimg_std::memmove((void*)(disp->released_keys+1),(void*)disp->released_keys,512-1); disp->released_key = 0; } disp->is_closed = true; HideWindow(disp->carbonWindow); disp->is_event = true; MPSignalSemaphore(c.wait_event); result = noErr; break; // There is a lot of case where we have to redraw our window case kEventWindowBoundsChanging : case kEventWindowResizeStarted : case kEventWindowCollapsed : //Not sure it's really needed :-) break; case kEventWindowZoomed : case kEventWindowExpanded : case kEventWindowResizeCompleted : { MPEnterCriticalRegion(disp->paintCriticalRegion, kDurationForever); // Now we retrieve the new size of the window Rect newContentRect; GetWindowBounds(disp->carbonWindow,kWindowContentRgn,&newContentRect); const unsigned int nw = (unsigned int)(newContentRect.right - newContentRect.left), nh = (unsigned int)(newContentRect.bottom - newContentRect.top); // Then we update CImg internal settings if (nw && nh && (nw!=disp->width || nh!=disp->height)) { disp->window_width = nw; disp->window_height = nh; disp->mouse_x = disp->mouse_y = -1; disp->is_resized = true; } disp->is_event = true; MPExitCriticalRegion(disp->paintCriticalRegion); disp->paint(); // Coords changed, must update the screen MPSignalSemaphore(c.wait_event); result = noErr; } break; case kEventWindowDragStarted : case kEventWindowDragCompleted : { MPEnterCriticalRegion(disp->paintCriticalRegion, kDurationForever); // Now we retrieve the new size of the window Rect newContentRect ; GetWindowBounds(disp->carbonWindow,kWindowStructureRgn,&newContentRect); const int nx = (int)(newContentRect.left), ny = (int)(newContentRect.top); // Then we update CImg internal settings if (nx!=disp->window_x || ny!=disp->window_y) { disp->window_x = nx; disp->window_y = ny; disp->is_moved = true; } disp->is_event = true; MPExitCriticalRegion(disp->paintCriticalRegion); disp->paint(); // Coords changed, must update the screen MPSignalSemaphore(c.wait_event); result = noErr; } break; case kEventWindowPaint : disp->paint(); break; } switch (GetEventClass(theEvent)) { case kEventClassKeyboard : { if (GetEventKind(theEvent)==kEventRawKeyModifiersChanged) { // Apple has special keys named "notifiers", we have to convert this (exotic ?) key handling into the regular CImg processing. UInt32 newModifiers; if (GetEventParameter(theEvent,kEventParamKeyModifiers,typeUInt32,0,sizeof(UInt32),0,&newModifiers)==noErr) { int newKeyCode = -1; UInt32 changed = disp->lastKeyModifiers^newModifiers; // Find what changed here if ((changed & rightShiftKey)!=0) newKeyCode = cimg::keySHIFTRIGHT; if ((changed & shiftKey)!=0) newKeyCode = cimg::keySHIFTLEFT; // On the Mac, the "option" key = the ALT key if ((changed & (optionKey | rightOptionKey))!=0) newKeyCode = cimg::keyALTGR; if ((changed & controlKey)!=0) newKeyCode = cimg::keyCTRLLEFT; if ((changed & rightControlKey)!=0) newKeyCode = cimg::keyCTRLRIGHT; if ((changed & cmdKey)!=0) newKeyCode = cimg::keyAPPLEFT; if ((changed & alphaLock)!=0) newKeyCode = cimg::keyCAPSLOCK; if (newKeyCode != -1) { // Simulate keystroke if (disp->key) cimg_std::memmove((void*)(disp->keys+1),(void*)disp->keys,512-1); disp->key = (int)newKeyCode; } disp->lastKeyModifiers = newModifiers; // Save current state } disp->is_event = true; MPSignalSemaphore(c.wait_event); } if (GetEventKind(theEvent)==kEventRawKeyDown || GetEventKind(theEvent)==kEventRawKeyRepeat) { char keyCode; if (GetEventParameter(theEvent,kEventParamKeyMacCharCodes,typeChar,0,sizeof(keyCode),0,&keyCode)==noErr) { disp->update_iskey((unsigned int)keyCode,true); if (disp->key) cimg_std::memmove((void*)(disp->keys+1),(void*)disp->keys,512-1); disp->key = (unsigned int)keyCode; if (disp->released_key) { cimg_std::memmove((void*)(disp->released_keys+1),(void*)disp->released_keys,512-1); disp->released_key = 0; } } disp->is_event = true; MPSignalSemaphore(c.wait_event); } } break; case kEventClassMouse : switch (GetEventKind(theEvent)) { case kEventMouseDragged : // When you push the main button on the Apple mouse while moving it, you got NO kEventMouseMoved msg, // but a kEventMouseDragged one. So we merge them here. case kEventMouseMoved : HIPoint point; if (GetEventParameter(theEvent,kEventParamMouseLocation,typeHIPoint,0,sizeof(point),0,&point)==noErr) { if (_CbToLocalPointFromMouseEvent(theEvent,disp->carbonWindow,&point)) { disp->mouse_x = (int)point.x; disp->mouse_y = (int)point.y; if (disp->mouse_x<0 || disp->mouse_y<0 || disp->mouse_x>=disp->dimx() || disp->mouse_y>=disp->dimy()) disp->mouse_x = disp->mouse_y = -1; } else disp->mouse_x = disp->mouse_y = -1; } disp->is_event = true; MPSignalSemaphore(c.wait_event); break; case kEventMouseDown : UInt16 btn; if (GetEventParameter(theEvent,kEventParamMouseButton,typeMouseButton,0,sizeof(btn),0,&btn)==noErr) { cimg_std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,512-1); if (btn==kEventMouseButtonPrimary) disp->button|=1U; // For those who don't have a multi-mouse button (as me), I think it's better to allow the user // to emulate a right click by using the Control key if ((disp->lastKeyModifiers & (controlKey | rightControlKey))!=0) cimg::warn("CImgDisplay::CarbonEventHandler() : Will emulate right click now [Down]"); if (btn==kEventMouseButtonSecondary || ((disp->lastKeyModifiers & (controlKey | rightControlKey))!=0)) disp->button|=2U; if (btn==kEventMouseButtonTertiary) disp->button|=4U; } disp->is_event = true; MPSignalSemaphore(c.wait_event); break; case kEventMouseWheelMoved : EventMouseWheelAxis wheelax; SInt32 delta; if (GetEventParameter(theEvent,kEventParamMouseWheelAxis,typeMouseWheelAxis,0,sizeof(wheelax),0,&wheelax)==noErr) if (wheelax==kEventMouseWheelAxisY) { if (GetEventParameter(theEvent,kEventParamMouseWheelDelta,typeLongInteger,0,sizeof(delta),0,&delta)==noErr) if (delta>0) disp->wheel+=delta/120; //FIXME: why 120 ? disp->is_event = true; MPSignalSemaphore(c.wait_event); } break; } } switch (GetEventClass(theEvent)) { case kEventClassKeyboard : if (GetEventKind(theEvent)==kEventRawKeyUp) { UInt32 keyCode; if (GetEventParameter(theEvent,kEventParamKeyCode,typeUInt32,0,sizeof(keyCode),0,&keyCode)==noErr) { disp->update_iskey((unsigned int)keyCode,false); if (disp->key) { cimg_std::memmove((void*)(disp->keys+1),(void*)disp->keys,512-1); disp->key = 0; } if (disp->released_key) cimg_std::memmove((void*)(disp->released_keys+1),(void*)disp->released_keys,512-1); disp->released_key = (int)keyCode; } disp->is_event = true; MPSignalSemaphore(c.wait_event); } break; case kEventClassMouse : switch (GetEventKind(theEvent)) { case kEventMouseUp : UInt16 btn; if (GetEventParameter(theEvent,kEventParamMouseButton,typeMouseButton,0,sizeof(btn),0,&btn)==noErr) { cimg_std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,512-1); if (btn==kEventMouseButtonPrimary) disp->button&=~1U; // See note in kEventMouseDown handler. if ((disp->lastKeyModifiers & (controlKey | rightControlKey))!=0) cimg::warn("CImgDisplay::CarbonEventHandler() : Will emulate right click now [Up]"); if (btn==kEventMouseButtonSecondary || ((disp->lastKeyModifiers & (controlKey | rightControlKey))!=0)) disp->button&=~2U; if (btn==kEventMouseButtonTertiary) disp->button&=~2U; } disp->is_event = true; MPSignalSemaphore(c.wait_event); break; } } } return (result); } static void* _events_thread(void* args) { (void)args; // Make the compiler happy cimg::CarbonInfo& c = cimg::CarbonAttr(); pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0); MPSignalSemaphore(c.sync_event); // Notify the caller that all goes fine EventRef theEvent; EventTargetRef theTarget; OSStatus err; CbSerializedQuery* query; theTarget = GetEventDispatcherTarget(); // Enter in the main loop while (true) { pthread_testcancel(); /* Check if cancelation happens */ err = ReceiveNextEvent(0,0,kDurationImmediate,true,&theEvent); // Fetch new events if (err==noErr) { // Received a carbon event, so process it ! SendEventToEventTarget (theEvent, theTarget); ReleaseEvent(theEvent); } else if (err == eventLoopTimedOutErr) { // There is no event to process, so check if there is new messages to process OSStatus r =MPWaitOnQueue(c.com_queue,(void**)&query,0,0,10*kDurationMillisecond); if (r!=noErr) continue; //nothing in the queue or an error.., bye // If we're here, we've something to do now. if (query) { switch (query->kind) { case COM_SETMOUSEPOS : { // change the cursor position query->success = CGDisplayMoveCursorToPoint(kCGDirectMainDisplay,CGPointMake(query->sender->window_x+query->x,query->sender->window_y+query->y)) == kCGErrorSuccess; if (query->success) { query->sender->mouse_x = query->x; query->sender->mouse_y = query->y; } else cimg::warn("CImgDisplay::_events_thread() : CGDisplayMoveCursorToPoint failed."); } break; case COM_SETTITLE : { // change the title bar caption CFStringRef windowTitle = CFStringCreateWithCString(0,query->c,kCFStringEncodingMacRoman); query->success = SetWindowTitleWithCFString(query->sender->carbonWindow,windowTitle)==noErr; if (!query->success) cimg::warn("CImgDisplay::_events_thread() : SetWindowTitleWithCFString failed."); CFRelease(windowTitle); } break; case COM_RESIZEWINDOW : { // Resize a window SizeWindow(query->sender->carbonWindow,query->x,query->y,query->update); // If the window has been resized successfully, update display informations query->sender->window_width = query->x; query->sender->window_height = query->y; query->success = true; } break; case COM_MOVEWINDOW : { // Move a window MoveWindow(query->sender->carbonWindow,query->x,query->y,false); query->sender->window_x = query->x; query->sender->window_y = query->y; query->sender->is_moved = false; query->success = true; } break; case COM_SHOWMOUSE : { // Show the mouse query->success = CGDisplayShowCursor(kCGDirectMainDisplay)==noErr; if (!query->success) cimg::warn("CImgDisplay::_events_thread() : CGDisplayShowCursor failed."); } break; case COM_HIDEMOUSE : { // Hide the mouse query->success = CGDisplayHideCursor(kCGDirectMainDisplay)==noErr; if (!query->success) cimg::warn("CImgDisplay::_events_thread() : CGDisplayHideCursor failed."); } break; case COM_SHOWWINDOW : { // We've to show a window ShowWindow(query->sender->carbonWindow); query->success = true; query->sender->is_closed = false; } break; case COM_HIDEWINDOW : { // We've to show a window HideWindow(query->sender->carbonWindow); query->sender->is_closed = true; query->sender->window_x = query->sender->window_y = 0; query->success = true; } break; case COM_RELEASEWINDOW : { // We have to release a given window handle query->success = true; CFRelease(query->sender->carbonWindow); } break; case COM_CREATEWINDOW : { // We have to create a window query->success = true; WindowAttributes windowAttrs; Rect contentRect; if (query->createFullScreenWindow) { // To simulate a "true" full screen, we remove menus and close boxes windowAttrs = (1L << 9); //Why ? kWindowNoTitleBarAttribute seems to be not defined on 10.3 // Define a full screen bound rect SetRect(&contentRect,0,0,CGDisplayPixelsWide(kCGDirectMainDisplay),CGDisplayPixelsHigh(kCGDirectMainDisplay)); } else { // Set the window size SetRect(&contentRect,0,0,query->sender->width,query->sender->height); // Window will be centered with RepositionWindow. // Use default attributes windowAttrs = kWindowStandardDocumentAttributes | kWindowStandardHandlerAttribute | kWindowInWindowMenuAttribute | kWindowLiveResizeAttribute; } // Update window position if (query->createClosedWindow) query->sender->window_x = query->sender->window_y = 0; else { query->sender->window_x = contentRect.left; query->sender->window_y = contentRect.top; } // Update window flags query->sender->window_width = query->sender->width; query->sender->window_height = query->sender->height; query->sender->flush(); // Create the window if (CreateNewWindow(kDocumentWindowClass,windowAttrs,&contentRect,&query->sender->carbonWindow)!=noErr) { query->success = false; cimg::warn("CImgDisplay::_events_thread() : CreateNewWindow() failed."); } // Send it to the foreground if (RepositionWindow(query->sender->carbonWindow,0,kWindowCenterOnMainScreen)!=noErr) query->success = false; // Show it, if needed if (!query->createClosedWindow) ShowWindow(query->sender->carbonWindow); // Associate a valid event handler EventTypeSpec eventList[] = { { kEventClassWindow, kEventWindowClose }, { kEventClassWindow, kEventWindowResizeStarted }, { kEventClassWindow, kEventWindowResizeCompleted }, { kEventClassWindow, kEventWindowDragStarted}, { kEventClassWindow, kEventWindowDragCompleted }, { kEventClassWindow, kEventWindowPaint }, { kEventClassWindow, kEventWindowBoundsChanging }, { kEventClassWindow, kEventWindowCollapsed }, { kEventClassWindow, kEventWindowExpanded }, { kEventClassWindow, kEventWindowZoomed }, { kEventClassKeyboard, kEventRawKeyDown }, { kEventClassKeyboard, kEventRawKeyUp }, { kEventClassKeyboard, kEventRawKeyRepeat }, { kEventClassKeyboard, kEventRawKeyModifiersChanged }, { kEventClassMouse, kEventMouseMoved }, { kEventClassMouse, kEventMouseDown }, { kEventClassMouse, kEventMouseUp }, { kEventClassMouse, kEventMouseDragged } }; // Set up the handler if (InstallWindowEventHandler(query->sender->carbonWindow,NewEventHandlerUPP(CarbonEventHandler),GetEventTypeCount(eventList), eventList,(void*)query->sender,0)!=noErr) { query->success = false; cimg::warn("CImgDisplay::_events_thread() : InstallWindowEventHandler failed."); } // Paint query->sender->paint(); } break; default : cimg::warn("CImgDisplay::_events_thread() : Received unknow code %d.",query->kind); } // Signal that the message has been processed MPSignalSemaphore(c.sync_event); } } } // If we are here, the application is now finished pthread_exit(0); } CImgDisplay& assign() { if (is_empty()) return *this; cimg::CarbonInfo& c = cimg::CarbonAttr(); // Destroy the window associated to the display _CbFreeAttachedWindow(c); // Don't destroy the background thread here. // If you check whether _CbFreeAttachedWindow() returned true, // - saying that there were no window left on screen - and // you destroy the background thread here, ReceiveNextEvent won't // work anymore if you create a new window after. So the // background thread must be killed (pthread_cancel() + pthread_join()) // only on the application shutdown. // Finalize graphics _CbFinalizeGraphics(); // Do some cleanup if (data) delete[] data; if (title) delete[] title; width = height = normalization = window_width = window_height = 0; window_x = window_y = 0; is_fullscreen = false; is_closed = true; min = max = 0; title = 0; flush(); if (MPDeleteCriticalRegion(paintCriticalRegion)!=noErr) throw CImgDisplayException("CImgDisplay()::assign() : MPDeleteCriticalRegion failed."); return *this; } CImgDisplay& _assign(const unsigned int dimw, const unsigned int dimh, const char *ptitle=0, const unsigned int normalization_type=3, const bool fullscreen_flag=false, const bool closed_flag=false) { cimg::CarbonInfo& c = cimg::CarbonAttr(); // Allocate space for window title const int s = cimg::strlen(ptitle)+1; char *tmp_title = s?new char[s]:0; if (s) cimg_std::memcpy(tmp_title,ptitle,s*sizeof(char)); // Destroy previous window if existing if (!is_empty()) assign(); // Set display variables width = cimg::min(dimw,(unsigned int)screen_dimx()); height = cimg::min(dimh,(unsigned int)screen_dimy()); normalization = normalization_type<4?normalization_type:3; is_fullscreen = fullscreen_flag; is_closed = closed_flag; lastKeyModifiers = 0; title = tmp_title; flush(); // Create the paint CR if (MPCreateCriticalRegion(&paintCriticalRegion) != noErr) throw CImgDisplayException("CImgDisplay::_assign() : MPCreateCriticalRegion() failed."); // Create the thread if it's not already created if (c.event_thread==0) { // Background thread does not exists, so create it ! if (pthread_create(&c.event_thread,0,_events_thread,0)!=0) throw CImgDisplayException("CImgDisplay::_assign() : pthread_create() failed."); // Wait for thread initialization MPWaitOnSemaphore(c.sync_event, kDurationForever); } // Init disp. graphics data = new unsigned int[width*height]; _CbInitializeGraphics(); // Now ask the thread to create the window _CbCreateAttachedWindow(c,ptitle,fullscreen_flag,closed_flag); return *this; } #endif }; /* #-------------------------------------- # # # # Definition of the CImg structure # # # #-------------------------------------- */ //! Class representing an image (up to 4 dimensions wide), each pixel being of type \c T. /** This is the main class of the %CImg Library. It declares and constructs an image, allows access to its pixel values, and is able to perform various image operations. \par Image representation A %CImg image is defined as an instance of the container \ref CImg<\c T>, which contains a regular grid of pixels, each pixel value being of type \c T. The image grid can have up to 4 dimensions : width, height, depth and number of channels. Usually, the three first dimensions are used to describe spatial coordinates (x,y,z), while the number of channels is rather used as a vector-valued dimension (it may describe the R,G,B color channels for instance). If you need a fifth dimension, you can use image lists \ref CImgList<\c T> rather than simple images \ref CImg<\c T>. Thus, the \ref CImg<\c T> class is able to represent volumetric images of vector-valued pixels, as well as images with less dimensions (1D scalar signal, 2D color images, ...). Most member functions of the class CImg<\c T> are designed to handle this maximum case of (3+1) dimensions. Concerning the pixel value type \c T : fully supported template types are the basic C++ types : unsigned char, char, short, unsigned int, int, unsigned long, long, float, double, ... . Typically, fast image display can be done using CImg images, while complex image processing algorithms may be rather coded using CImg or CImg images that have floating-point pixel values. The default value for the template T is \c float. Using your own template types may be possible. However, you will certainly have to define the complete set of arithmetic and logical operators for your class. \par Image structure The \ref CImg<\c T> structure contains \a six fields : - \ref width defines the number of \a columns of the image (size along the X-axis). - \ref height defines the number of \a rows of the image (size along the Y-axis). - \ref depth defines the number of \a slices of the image (size along the Z-axis). - \ref dim defines the number of \a channels of the image (size along the V-axis). - \ref data defines a \a pointer to the \a pixel \a data (of type \c T). - \ref is_shared is a boolean that tells if the memory buffer \ref data is shared with another image. You can access these fields publicly although it is recommended to use the dedicated functions dimx(), dimy(), dimz(), dimv() and ptr() to do so. Image dimensions are not limited to a specific range (as long as you got enough available memory). A value of \e 1 usually means that the corresponding dimension is \a flat. If one of the dimensions is \e 0, or if the data pointer is null, the image is considered as \e empty. Empty images should not contain any pixel data and thus, will not be processed by CImg member functions (a CImgInstanceException will be thrown instead). Pixel data are stored in memory, in a non interlaced mode (See \ref cimg_storage). \par Image declaration and construction Declaring an image can be done by using one of the several available constructors. Here is a list of the most used : - Construct images from arbitrary dimensions : - CImg img; declares an empty image. - CImg img(128,128); declares a 128x128 greyscale image with \c unsigned \c char pixel values. - CImg img(3,3); declares a 3x3 matrix with \c double coefficients. - CImg img(256,256,1,3); declares a 256x256x1x3 (color) image (colors are stored as an image with three channels). - CImg img(128,128,128); declares a 128x128x128 volumetric and greyscale image (with \c double pixel values). - CImg<> img(128,128,128,3); declares a 128x128x128 volumetric color image (with \c float pixels, which is the default value of the template parameter \c T). - \b Note : images pixels are not automatically initialized to 0. You may use the function \ref fill() to do it, or use the specific constructor taking 5 parameters like this : CImg<> img(128,128,128,3,0); declares a 128x128x128 volumetric color image with all pixel values to 0. - Construct images from filenames : - CImg img("image.jpg"); reads a JPEG color image from the file "image.jpg". - CImg img("analyze.hdr"); reads a volumetric image (ANALYZE7.5 format) from the file "analyze.hdr". - \b Note : You need to install ImageMagick to be able to read common compressed image formats (JPG,PNG, ...) (See \ref cimg_files_io). - Construct images from C-style arrays : - CImg img(data_buffer,256,256); constructs a 256x256 greyscale image from a \c int* buffer \c data_buffer (of size 256x256=65536). - CImg img(data_buffer,256,256,1,3,false); constructs a 256x256 color image from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels follow each others). - CImg img(data_buffer,256,256,1,3,true); constructs a 256x256 color image from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels are multiplexed). The complete list of constructors can be found here. \par Most useful functions The \ref CImg<\c T> class contains a lot of functions that operates on images. Some of the most useful are : - operator()(), operator[]() : allows to access or write pixel values. - display() : displays the image in a new window. **/ template struct CImg { //! Variable representing the width of the instance image (i.e. dimensions along the X-axis). /** \remark - Prefer using the function CImg::dimx() to get information about the width of an image. - Use function CImg::resize() to set a new width for an image. Setting directly the variable \c width would probably result in a library crash. - Empty images have \c width defined to \c 0. **/ unsigned int width; //! Variable representing the height of the instance image (i.e. dimensions along the Y-axis). /** \remark - Prefer using the function CImg::dimy() to get information about the height of an image. - Use function CImg::resize() to set a new height for an image. Setting directly the variable \c height would probably result in a library crash. - 1D signals have \c height defined to \c 1. - Empty images have \c height defined to \c 0. **/ unsigned int height; //! Variable representing the depth of the instance image (i.e. dimensions along the Z-axis). /** \remark - Prefer using the function CImg::dimz() to get information about the depth of an image. - Use function CImg::resize() to set a new depth for an image. Setting directly the variable \c depth would probably result in a library crash. - Classical 2D images have \c depth defined to \c 1. - Empty images have \c depth defined to \c 0. **/ unsigned int depth; //! Variable representing the number of channels of the instance image (i.e. dimensions along the V-axis). /** \remark - Prefer using the function CImg::dimv() to get information about the depth of an image. - Use function CImg::resize() to set a new vector dimension for an image. Setting directly the variable \c dim would probably result in a library crash. - Scalar-valued images (one value per pixel) have \c dim defined to \c 1. - Empty images have \c depth defined to \c 0. **/ unsigned int dim; //! Variable telling if pixel buffer of the instance image is shared with another one. bool is_shared; //! Pointer to the first pixel of the pixel buffer. T *data; //! Iterator type for CImg. /** \remark - An \p iterator is a T* pointer (address of a pixel value in the pixel buffer). - Iterators are not directly used in %CImg functions, they have been introduced for compatibility with the STL. **/ typedef T* iterator; //! Const iterator type for CImg. /** \remark - A \p const_iterator is a const T* pointer (address of a pixel value in the pixel buffer). - Iterators are not directly used in %CImg functions, they have been introduced for compatibility with the STL. **/ typedef const T* const_iterator; //! Get value type typedef T value_type; // Define common T-dependant types. typedef typename cimg::superset::type Tbool; typedef typename cimg::superset::type Tuchar; typedef typename cimg::superset::type Tchar; typedef typename cimg::superset::type Tushort; typedef typename cimg::superset::type Tshort; typedef typename cimg::superset::type Tuint; typedef typename cimg::superset::type Tint; typedef typename cimg::superset::type Tulong; typedef typename cimg::superset::type Tlong; typedef typename cimg::superset::type Tfloat; typedef typename cimg::superset::type Tdouble; typedef typename cimg::last::type boolT; typedef typename cimg::last::type ucharT; typedef typename cimg::last::type charT; typedef typename cimg::last::type ushortT; typedef typename cimg::last::type shortT; typedef typename cimg::last::type uintT; typedef typename cimg::last::type intT; typedef typename cimg::last::type ulongT; typedef typename cimg::last::type longT; typedef typename cimg::last::type floatT; typedef typename cimg::last::type doubleT; //@} //--------------------------- // //! \name Plugins //@{ //--------------------------- #ifdef cimg_plugin #include cimg_plugin #endif #ifdef cimg_plugin1 #include cimg_plugin1 #endif #ifdef cimg_plugin2 #include cimg_plugin2 #endif #ifdef cimg_plugin3 #include cimg_plugin3 #endif #ifdef cimg_plugin4 #include cimg_plugin4 #endif #ifdef cimg_plugin5 #include cimg_plugin5 #endif #ifdef cimg_plugin6 #include cimg_plugin6 #endif #ifdef cimg_plugin7 #include cimg_plugin7 #endif #ifdef cimg_plugin8 #include cimg_plugin8 #endif #ifndef cimg_plugin_greycstoration #define cimg_plugin_greycstoration_count #endif #ifndef cimg_plugin_greycstoration_lock #define cimg_plugin_greycstoration_lock #endif #ifndef cimg_plugin_greycstoration_unlock #define cimg_plugin_greycstoration_unlock #endif //@} //-------------------------------------- // //! \name Constructors-Destructor-Copy //@{ //-------------------------------------- //! Destructor. /** The destructor destroys the instance image. \remark - Destructing an empty or shared image does nothing. - Otherwise, all memory used to store the pixel data of the instance image is freed. - When destroying a non-shared image, be sure that every shared instances of the same image are also destroyed to avoid further access to desallocated memory buffers. **/ ~CImg() { if (data && !is_shared) delete[] data; } //! Default constructor. /** The default constructor creates an empty instance image. \remark - An empty image does not contain any data and has all of its dimensions \ref width, \ref height, \ref depth, \ref dim set to 0 as well as its pointer to the pixel buffer \ref data. - An empty image is non-shared. **/ CImg(): width(0),height(0),depth(0),dim(0),is_shared(false),data(0) {} //! Constructs a new image with given size (\p dx,\p dy,\p dz,\p dv). /** This constructors create an instance image of size (\p dx,\p dy,\p dz,\p dv) with pixels of type \p T. \param dx Desired size along the X-axis, i.e. the \ref width of the image. \param dy Desired size along the Y-axis, i.e. the \ref height of the image. \param dz Desired size along the Z-axis, i.e. the \ref depth of the image. \param dv Desired size along the V-axis, i.e. the number of image channels \ref dim. \remark - If one of the input dimension \p dx,\p dy,\p dz or \p dv is set to 0, the created image is empty and all has its dimensions set to 0. No memory for pixel data is then allocated. - This constructor creates only non-shared images. - Image pixels allocated by this constructor are \b not \b initialized. Use the constructor CImg(const unsigned int,const unsigned int,const unsigned int,const unsigned int,const T) to get an image of desired size with pixels set to a particular value. **/ explicit CImg(const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dv=1): is_shared(false) { const unsigned long siz = dx*dy*dz*dv; if (siz) { width = dx; height = dy; depth = dz; dim = dv; data = new T[siz]; } else { width = height = depth = dim = 0; data = 0; } } //! Construct an image with given size (\p dx,\p dy,\p dz,\p dv) and with pixel having a default value \p val. /** This constructor creates an instance image of size (\p dx,\p dy,\p dz,\p dv) with pixels of type \p T and sets all pixel values of the created instance image to \p val. \param dx Desired size along the X-axis, i.e. the \ref width of the image. \param dy Desired size along the Y-axis, i.e. the \ref height of the image. \param dz Desired size along the Z-axis, i.e. the \ref depth of the image. \param dv Desired size along the V-axis, i.e. the number of image channels \p dim. \param val Default value for image pixels. \remark - This constructor has the same properties as CImg(const unsigned int,const unsigned int,const unsigned int,const unsigned int). **/ CImg(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, const T val): is_shared(false) { const unsigned long siz = dx*dy*dz*dv; if (siz) { width = dx; height = dy; depth = dz; dim = dv; data = new T[siz]; fill(val); } else { width = height = depth = dim = 0; data = 0; } } //! Construct an image with given size (\p dx,\p dy,\p dz,\p dv) and with specified pixel values (int version). CImg(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, const int val0, const int val1, ...):width(0),height(0),depth(0),dim(0),is_shared(false),data(0) { #define _CImg_stdarg(img,a0,a1,N,t) { \ unsigned int _siz = (unsigned int)N; \ if (_siz--) { \ va_list ap; \ va_start(ap,a1); \ T *ptrd = (img).data; \ *(ptrd++) = (T)a0; \ if (_siz--) { \ *(ptrd++) = (T)a1; \ for (; _siz; --_siz) *(ptrd++) = (T)va_arg(ap,t); \ } \ va_end(ap); \ }} assign(dx,dy,dz,dv); _CImg_stdarg(*this,val0,val1,dx*dy*dz*dv,int); } //! Construct an image with given size (\p dx,\p dy,\p dz,\p dv) and with specified pixel values (double version). CImg(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, const double val0, const double val1, ...):width(0),height(0),depth(0),dim(0),is_shared(false),data(0) { assign(dx,dy,dz,dv); _CImg_stdarg(*this,val0,val1,dx*dy*dz*dv,double); } //! Construct an image with given size and with specified values given in a string. CImg(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, const char *const values, const bool repeat_pattern):is_shared(false) { const unsigned long siz = dx*dy*dz*dv; if (siz) { width = dx; height = dy; depth = dz; dim = dv; data = new T[siz]; fill(values,repeat_pattern); } else { width = height = depth = dim = 0; data = 0; } } //! Construct an image from a raw memory buffer. /** This constructor creates an instance image of size (\p dx,\p dy,\p dz,\p dv) and fill its pixel buffer by copying data values from the input raw pixel buffer \p data_buffer. **/ template CImg(const t *const data_buffer, const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dv=1, const bool shared=false):is_shared(false) { if (shared) throw CImgArgumentException("CImg<%s>::CImg() : Cannot construct a shared instance image from a (%s*) buffer " "(different pixel types).", pixel_type(),CImg::pixel_type()); const unsigned long siz = dx*dy*dz*dv; if (data_buffer && siz) { width = dx; height = dy; depth = dz; dim = dv; data = new T[siz]; const t *ptrs = data_buffer + siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs); } else { width = height = depth = dim = 0; data = 0; } } #ifndef cimg_use_visualcpp6 CImg(const T *const data_buffer, const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dv=1, const bool shared=false) #else CImg(const T *const data_buffer, const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, const bool shared) #endif { const unsigned long siz = dx*dy*dz*dv; if (data_buffer && siz) { width = dx; height = dy; depth = dz; dim = dv; is_shared = shared; if (is_shared) data = const_cast(data_buffer); else { data = new T[siz]; cimg_std::memcpy(data,data_buffer,siz*sizeof(T)); } } else { width = height = depth = dim = 0; is_shared = false; data = 0; } } //! Default copy constructor. /** The default copy constructor creates a new instance image having same dimensions (\ref width, \ref height, \ref depth, \ref dim) and same pixel values as the input image \p img. \param img The input image to copy. \remark - If the input image \p img is non-shared or have a different template type \p t != \p T, the default copy constructor allocates a new pixel buffer and copy the pixel data of \p img into it. In this case, the pointers \ref data to the pixel buffers of the two images are different and the resulting instance image is non-shared. - If the input image \p img is shared and has the same template type \p t == \p T, the default copy constructor does not allocate a new pixel buffer and the resulting instance image shares its pixel buffer with the input image \p img, which means that modifying pixels of \p img also modifies the created instance image. - Copying an image having a different template type \p t != \p T performs a crude static cast conversion of each pixel value from type \p t to type \p T. - Copying an image having the same template type \p t == \p T is significantly faster. **/ template CImg(const CImg& img):is_shared(false) { const unsigned int siz = img.size(); if (img.data && siz) { width = img.width; height = img.height; depth = img.depth; dim = img.dim; data = new T[siz]; const t *ptrs = img.data + siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs); } else { width = height = depth = dim = 0; data = 0; } } CImg(const CImg& img) { const unsigned int siz = img.size(); if (img.data && siz) { width = img.width; height = img.height; depth = img.depth; dim = img.dim; is_shared = img.is_shared; if (is_shared) data = const_cast(img.data); else { data = new T[siz]; cimg_std::memcpy(data,img.data,siz*sizeof(T)); } } else { width = height = depth = dim = 0; is_shared = false; data = 0; } } //! Advanced copy constructor. /** The advanced copy constructor - as the default constructor CImg(const CImg< t >&) - creates a new instance image having same dimensions \ref width, \ref height, \ref depth, \ref dim and same pixel values as the input image \p img. But it also decides if the created instance image shares its memory with the input image \p img (if the input parameter \p shared is set to \p true) or not (if the input parameter \p shared is set to \p false). \param img The input image to copy. \param shared Boolean flag that decides if the copy is shared on non-shared. \remark - It is not possible to create a shared copy if the input image \p img is empty or has a different pixel type \p t != \p T. - If a non-shared copy of the input image \p img is created, a new memory buffer is allocated for pixel data. - If a shared copy of the input image \p img is created, no extra memory is allocated and the pixel buffer of the instance image is the same as the one used by the input image \p img. **/ template CImg(const CImg& img, const bool shared):is_shared(false) { if (shared) throw CImgArgumentException("CImg<%s>::CImg() : Cannot construct a shared instance image from a CImg<%s> instance " "(different pixel types).", pixel_type(),CImg::pixel_type()); const unsigned int siz = img.size(); if (img.data && siz) { width = img.width; height = img.height; depth = img.depth; dim = img.dim; data = new T[siz]; const t *ptrs = img.data + siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs); } else { width = height = depth = dim = 0; data = 0; } } CImg(const CImg& img, const bool shared) { const unsigned int siz = img.size(); if (img.data && siz) { width = img.width; height = img.height; depth = img.depth; dim = img.dim; is_shared = shared; if (is_shared) data = const_cast(img.data); else { data = new T[siz]; cimg_std::memcpy(data,img.data,siz*sizeof(T)); } } else { width = height = depth = dim = 0; is_shared = false; data = 0; } } //! Construct an image using dimensions of another image template CImg(const CImg& img, const char *const dimensions):width(0),height(0),depth(0),dim(0),is_shared(false),data(0) { assign(img,dimensions); } //! Construct an image using dimensions of another image, and fill it with a default value template CImg(const CImg& img, const char *const dimensions, const T val): width(0),height(0),depth(0),dim(0),is_shared(false),data(0) { assign(img,dimensions).fill(val); } //! Construct an image from an image file. /** This constructor creates an instance image by reading it from a file. \param filename Filename of the image file. \remark - The image format is deduced from the filename only by looking for the filename extension i.e. without analyzing the file itself. - Recognized image formats depend on the tools installed on your system or the external libraries you use to link your code with. More informations on this topic can be found in cimg_files_io. - If the filename is not found, a CImgIOException is thrown by this constructor. **/ CImg(const char *const filename):width(0),height(0),depth(0),dim(0),is_shared(false),data(0) { assign(filename); } //! Construct an image from the content of a CImgDisplay instance. CImg(const CImgDisplay &disp):width(0),height(0),depth(0),dim(0),is_shared(false),data(0) { disp.snapshot(*this); } //! In-place version of the default constructor/destructor. /** This function replaces the instance image by an empty image. \remark - Memory used by the previous content of the instance image is freed if necessary. - If the instance image was initially shared, it is replaced by a (non-shared) empty image. - This function is useful to free memory used by an image that is not of use, but which has been created in the current code scope (i.e. not destroyed yet). **/ CImg& assign() { if (data && !is_shared) delete[] data; width = height = depth = dim = 0; is_shared = false; data = 0; return *this; } //! In-place version of the default constructor. /** This function is strictly equivalent to \ref assign() and has been introduced for having a STL-compliant function name. **/ CImg& clear() { return assign(); } //! In-place version of the previous constructor. /** This function replaces the instance image by a new image of size (\p dx,\p dy,\p dz,\p dv) with pixels of type \p T. \param dx Desired size along the X-axis, i.e. the \ref width of the image. \param dy Desired size along the Y-axis, i.e. the \ref height of the image. \param dz Desired size along the Z-axis, i.e. the \ref depth of the image. \param dv Desired size along the V-axis, i.e. the number of image channels \p dim. - If one of the input dimension \p dx,\p dy,\p dz or \p dv is set to 0, the instance image becomes empty and all has its dimensions set to 0. No memory for pixel data is then allocated. - Memory buffer used to store previous pixel values is freed if necessary. - If the instance image is shared, this constructor actually does nothing more than verifying that new and old image dimensions fit. - Image pixels allocated by this function are \b not \b initialized. Use the function assign(const unsigned int,const unsigned int,const unsigned int,const unsigned int,const T) to assign an image of desired size with pixels set to a particular value. **/ CImg& assign(const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dv=1) { const unsigned long siz = dx*dy*dz*dv; if (!siz) return assign(); const unsigned long curr_siz = size(); if (siz!=curr_siz) { if (is_shared) throw CImgArgumentException("CImg<%s>::assign() : Cannot assign image (%u,%u,%u,%u) to shared instance image (%u,%u,%u,%u,%p).", pixel_type(),dx,dy,dz,dv,width,height,depth,dim,data); else { if (data) delete[] data; data = new T[siz]; } } width = dx; height = dy; depth = dz; dim = dv; return *this; } //! In-place version of the previous constructor. /** This function replaces the instance image by a new image of size (\p dx,\p dy,\p dz,\p dv) with pixels of type \p T and sets all pixel values of the instance image to \p val. \param dx Desired size along the X-axis, i.e. the \ref width of the image. \param dy Desired size along the Y-axis, i.e. the \ref height of the image. \param dz Desired size along the Z-axis, i.e. the \ref depth of the image. \param dv Desired size along the V-axis, i.e. the number of image channels \p dim. \param val Default value for image pixels. \remark - This function has the same properties as assign(const unsigned int,const unsigned int,const unsigned int,const unsigned int). **/ CImg& assign(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, const T val) { return assign(dx,dy,dz,dv).fill(val); } //! In-place version of the previous constructor. CImg& assign(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, const int val0, const int val1, ...) { assign(dx,dy,dz,dv); _CImg_stdarg(*this,val0,val1,dx*dy*dz*dv,int); return *this; } //! In-place version of the previous constructor. CImg& assign(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, const double val0, const double val1, ...) { assign(dx,dy,dz,dv); _CImg_stdarg(*this,val0,val1,dx*dy*dz*dv,double); return *this; } //! In-place version of the previous constructor. template CImg& assign(const t *const data_buffer, const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dv=1) { const unsigned long siz = dx*dy*dz*dv; if (!data_buffer || !siz) return assign(); assign(dx,dy,dz,dv); const t *ptrs = data_buffer + siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs); return *this; } #ifndef cimg_use_visualcpp6 CImg& assign(const T *const data_buffer, const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dv=1) #else CImg& assign(const T *const data_buffer, const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv) #endif { const unsigned long siz = dx*dy*dz*dv; if (!data_buffer || !siz) return assign(); const unsigned long curr_siz = size(); if (data_buffer==data && siz==curr_siz) return assign(dx,dy,dz,dv); if (is_shared || data_buffer+siz=data+size()) { assign(dx,dy,dz,dv); if (is_shared) cimg_std::memmove(data,data_buffer,siz*sizeof(T)); else cimg_std::memcpy(data,data_buffer,siz*sizeof(T)); } else { T *new_data = new T[siz]; cimg_std::memcpy(new_data,data_buffer,siz*sizeof(T)); delete[] data; data = new_data; width = dx; height = dy; depth = dz; dim = dv; } return *this; } //! In-place version of the previous constructor, allowing to force the shared state of the instance image. template CImg& assign(const t *const data_buffer, const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, const bool shared) { if (shared) throw CImgArgumentException("CImg<%s>::assign() : Cannot assign buffer (%s*) to shared instance image (%u,%u,%u,%u,%p)" "(different pixel types).", pixel_type(),CImg::pixel_type(),width,height,depth,dim,data); return assign(data_buffer,dx,dy,dz,dv); } CImg& assign(const T *const data_buffer, const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, const bool shared) { const unsigned long siz = dx*dy*dz*dv; if (!data_buffer || !siz) return assign(); if (!shared) { if (is_shared) assign(); assign(data_buffer,dx,dy,dz,dv); } else { if (!is_shared) { if (data_buffer+siz=data+size()) assign(); else cimg::warn("CImg<%s>::assign() : Shared instance image has overlapping memory !", pixel_type()); } width = dx; height = dy; depth = dz; dim = dv; is_shared = true; data = const_cast(data_buffer); } return *this; } //! In-place version of the default copy constructor. /** This function assigns a copy of the input image \p img to the current instance image. \param img The input image to copy. \remark - If the instance image is not shared, the content of the input image \p img is copied into a new buffer becoming the new pixel buffer of the instance image, while the old pixel buffer is freed if necessary. - If the instance image is shared, the content of the input image \p img is copied into the current (shared) pixel buffer of the instance image, modifying then the image referenced by the shared instance image. The instance image still remains shared. **/ template CImg& assign(const CImg& img) { return assign(img.data,img.width,img.height,img.depth,img.dim); } //! In-place version of the advanced constructor. /** This function - as the simpler function assign(const CImg< t >&) - assigns a copy of the input image \p img to the current instance image. But it also decides if the copy is shared (if the input parameter \p shared is set to \c true) or non-shared (if the input parameter \p shared is set to \c false). \param img The input image to copy. \param shared Boolean flag that decides if the copy is shared or non-shared. \remark - It is not possible to assign a shared copy if the input image \p img is empty or has a different pixel type \p t != \p T. - If a non-shared copy of the input image \p img is assigned, a new memory buffer is allocated for pixel data. - If a shared copy of the input image \p img is assigned, no extra memory is allocated and the pixel buffer of the instance image is the same as the one used by the input image \p img. **/ template CImg& assign(const CImg& img, const bool shared) { return assign(img.data,img.width,img.height,img.depth,img.dim,shared); } //! In-place version of the previous constructor. template CImg& assign(const CImg& img, const char *const dimensions) { if (dimensions) { unsigned int siz[4] = { 0,1,1,1 }; const char *s = dimensions; char tmp[256] = { 0 }, c = 0; int val = 0; for (unsigned int k=0; k<4; ++k) { const int err = cimg_std::sscanf(s,"%[-0-9]%c",tmp,&c); if (err>=1) { const int err = cimg_std::sscanf(s,"%d",&val); if (err==1) { int val2 = val<0?-val:(c=='%'?val:-1); if (val2>=0) { val = (int)((k==0?img.width:(k==1?img.height:(k==2?img.depth:img.dim)))*val2/100); if (c!='%' && !val) val = 1; } siz[k] = val; } s+=cimg::strlen(tmp); if (c=='%') ++s; } if (!err) { if (!cimg::strncasecmp(s,"x",1)) { ++s; siz[k] = img.width; } else if (!cimg::strncasecmp(s,"y",1)) { ++s; siz[k] = img.height; } else if (!cimg::strncasecmp(s,"z",1)) { ++s; siz[k] = img.depth; } else if (!cimg::strncasecmp(s,"v",1)) { ++s; siz[k] = img.dim; } else if (!cimg::strncasecmp(s,"dx",2)) { s+=2; siz[k] = img.width; } else if (!cimg::strncasecmp(s,"dy",2)) { s+=2; siz[k] = img.height; } else if (!cimg::strncasecmp(s,"dz",2)) { s+=2; siz[k] = img.depth; } else if (!cimg::strncasecmp(s,"dv",2)) { s+=2; siz[k] = img.dim; } else if (!cimg::strncasecmp(s,"dimx",4)) { s+=4; siz[k] = img.width; } else if (!cimg::strncasecmp(s,"dimy",4)) { s+=4; siz[k] = img.height; } else if (!cimg::strncasecmp(s,"dimz",4)) { s+=4; siz[k] = img.depth; } else if (!cimg::strncasecmp(s,"dimv",4)) { s+=4; siz[k] = img.dim; } else if (!cimg::strncasecmp(s,"width",5)) { s+=5; siz[k] = img.width; } else if (!cimg::strncasecmp(s,"height",6)) { s+=6; siz[k] = img.height; } else if (!cimg::strncasecmp(s,"depth",5)) { s+=5; siz[k] = img.depth; } else if (!cimg::strncasecmp(s,"dim",3)) { s+=3; siz[k] = img.dim; } else { ++s; --k; } } } return assign(siz[0],siz[1],siz[2],siz[3]); } return assign(); } //! In-place version of the previous constructor. template CImg& assign(const CImg& img, const char *const dimensions, const T val) { return assign(img,dimensions).fill(val); } //! In-place version of the previous constructor. /** This function replaces the instance image by the one that have been read from the given file. \param filename Filename of the image file. - The image format is deduced from the filename only by looking for the filename extension i.e. without analyzing the file itself. - Recognized image formats depend on the tools installed on your system or the external libraries you use to link your code with. More informations on this topic can be found in cimg_files_io. - If the filename is not found, a CImgIOException is thrown by this constructor. **/ CImg& assign(const char *const filename) { return load(filename); } //! In-place version of the previous constructor. CImg& assign(const CImgDisplay &disp) { disp.snapshot(*this); return *this; } //! Transfer the content of the instance image into another one in a way that memory copies are avoided if possible. /** The instance image is always empty after a call to this function. **/ template CImg& transfer_to(CImg& img) { img.assign(*this); assign(); return img; } CImg& transfer_to(CImg& img) { if (is_shared || img.is_shared) { img.assign(*this); assign(); } else { img.assign(); swap(img); } return img; } //! Swap all fields of two images. Use with care ! CImg& swap(CImg& img) { cimg::swap(width,img.width); cimg::swap(height,img.height); cimg::swap(depth,img.depth); cimg::swap(dim,img.dim); cimg::swap(data,img.data); cimg::swap(is_shared,img.is_shared); return img; } //@} //------------------------------------- // //! \name Image Informations //@{ //------------------------------------- //! Return the type of the pixel values. /** \return a string describing the type of the image pixels (template parameter \p T). - The string returned may contains spaces ("unsigned char"). - If the template parameter T does not correspond to a registered type, the string "unknown" is returned. **/ static const char* pixel_type() { return cimg::type::string(); } //! Return the total number of pixel values in an image. /** - Equivalent to : dimx() * dimy() * dimz() * dimv(). \par example: \code CImg<> img(100,100,1,3); if (img.size()==100*100*3) std::fprintf(stderr,"This statement is true"); \endcode **/ unsigned long size() const { return width*height*depth*dim; } //! Return the number of columns of the instance image (size along the X-axis, i.e image width). int dimx() const { return (int)width; } //! Return the number of rows of the instance image (size along the Y-axis, i.e image height). int dimy() const { return (int)height; } //! Return the number of slices of the instance image (size along the Z-axis). int dimz() const { return (int)depth; } //! Return the number of vector channels of the instance image (size along the V-axis). int dimv() const { return (int)dim; } //! Return \c true if image (*this) has the specified width. bool is_sameX(const unsigned int dx) const { return (width==dx); } //! Return \c true if images \c (*this) and \c img have same width. template bool is_sameX(const CImg& img) const { return is_sameX(img.width); } //! Return \c true if images \c (*this) and the display \c disp have same width. bool is_sameX(const CImgDisplay& disp) const { return is_sameX(disp.width); } //! Return \c true if image (*this) has the specified height. bool is_sameY(const unsigned int dy) const { return (height==dy); } //! Return \c true if images \c (*this) and \c img have same height. template bool is_sameY(const CImg& img) const { return is_sameY(img.height); } //! Return \c true if images \c (*this) and the display \c disp have same height. bool is_sameY(const CImgDisplay& disp) const { return is_sameY(disp.height); } //! Return \c true if image (*this) has the specified depth. bool is_sameZ(const unsigned int dz) const { return (depth==dz); } //! Return \c true if images \c (*this) and \c img have same depth. template bool is_sameZ(const CImg& img) const { return is_sameZ(img.depth); } //! Return \c true if image (*this) has the specified number of channels. bool is_sameV(const unsigned int dv) const { return (dim==dv); } //! Return \c true if images \c (*this) and \c img have same dim. template bool is_sameV(const CImg& img) const { return is_sameV(img.dim); } //! Return \c true if image (*this) has the specified width and height. bool is_sameXY(const unsigned int dx, const unsigned int dy) const { return (is_sameX(dx) && is_sameY(dy)); } //! Return \c true if images have same width and same height. template bool is_sameXY(const CImg& img) const { return (is_sameX(img) && is_sameY(img)); } //! Return \c true if image \c (*this) and the display \c disp have same width and same height. bool is_sameXY(const CImgDisplay& disp) const { return (is_sameX(disp) && is_sameY(disp)); } //! Return \c true if image (*this) has the specified width and depth. bool is_sameXZ(const unsigned int dx, const unsigned int dz) const { return (is_sameX(dx) && is_sameZ(dz)); } //! Return \c true if images have same width and same depth. template bool is_sameXZ(const CImg& img) const { return (is_sameX(img) && is_sameZ(img)); } //! Return \c true if image (*this) has the specified width and number of channels. bool is_sameXV(const unsigned int dx, const unsigned int dv) const { return (is_sameX(dx) && is_sameV(dv)); } //! Return \c true if images have same width and same number of channels. template bool is_sameXV(const CImg& img) const { return (is_sameX(img) && is_sameV(img)); } //! Return \c true if image (*this) has the specified height and depth. bool is_sameYZ(const unsigned int dy, const unsigned int dz) const { return (is_sameY(dy) && is_sameZ(dz)); } //! Return \c true if images have same height and same depth. template bool is_sameYZ(const CImg& img) const { return (is_sameY(img) && is_sameZ(img)); } //! Return \c true if image (*this) has the specified height and number of channels. bool is_sameYV(const unsigned int dy, const unsigned int dv) const { return (is_sameY(dy) && is_sameV(dv)); } //! Return \c true if images have same height and same number of channels. template bool is_sameYV(const CImg& img) const { return (is_sameY(img) && is_sameV(img)); } //! Return \c true if image (*this) has the specified depth and number of channels. bool is_sameZV(const unsigned int dz, const unsigned int dv) const { return (is_sameZ(dz) && is_sameV(dv)); } //! Return \c true if images have same depth and same number of channels. template bool is_sameZV(const CImg& img) const { return (is_sameZ(img) && is_sameV(img)); } //! Return \c true if image (*this) has the specified width, height and depth. bool is_sameXYZ(const unsigned int dx, const unsigned int dy, const unsigned int dz) const { return (is_sameXY(dx,dy) && is_sameZ(dz)); } //! Return \c true if images have same width, same height and same depth. template bool is_sameXYZ(const CImg& img) const { return (is_sameXY(img) && is_sameZ(img)); } //! Return \c true if image (*this) has the specified width, height and depth. bool is_sameXYV(const unsigned int dx, const unsigned int dy, const unsigned int dv) const { return (is_sameXY(dx,dy) && is_sameV(dv)); } //! Return \c true if images have same width, same height and same number of channels. template bool is_sameXYV(const CImg& img) const { return (is_sameXY(img) && is_sameV(img)); } //! Return \c true if image (*this) has the specified width, height and number of channels. bool is_sameXZV(const unsigned int dx, const unsigned int dz, const unsigned int dv) const { return (is_sameXZ(dx,dz) && is_sameV(dv)); } //! Return \c true if images have same width, same depth and same number of channels. template bool is_sameXZV(const CImg& img) const { return (is_sameXZ(img) && is_sameV(img)); } //! Return \c true if image (*this) has the specified height, depth and number of channels. bool is_sameYZV(const unsigned int dy, const unsigned int dz, const unsigned int dv) const { return (is_sameYZ(dy,dz) && is_sameV(dv)); } //! Return \c true if images have same height, same depth and same number of channels. template bool is_sameYZV(const CImg& img) const { return (is_sameYZ(img) && is_sameV(img)); } //! Return \c true if image (*this) has the specified width, height, depth and number of channels. bool is_sameXYZV(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv) const { return (is_sameXYZ(dx,dy,dz) && is_sameV(dv)); } //! Return \c true if images \c (*this) and \c img have same width, same height, same depth and same number of channels. template bool is_sameXYZV(const CImg& img) const { return (is_sameXYZ(img) && is_sameV(img)); } //! Return \c true if current image is empty. bool is_empty() const { return !(data && width && height && depth && dim); } //! Return \p true if image is not empty. operator bool() const { return !is_empty(); } //! Return an iterator to the first image pixel iterator begin() { return data; } const_iterator begin() const { return data; } //! Return reference to the first image pixel const T& first() const { return *data; } T& first() { return *data; } //! Return an iterator pointing after the last image pixel iterator end() { return data + size(); } const_iterator end() const { return data + size(); } //! Return a reference to the last image pixel const T& last() const { return data[size() - 1]; } T& last() { return data[size() - 1]; } //! Return a pointer to the pixel buffer. T* ptr() { return data; } const T* ptr() const { return data; } //! Return a pointer to the pixel value located at (\p x,\p y,\p z,\p v). /** \param x X-coordinate of the pixel. \param y Y-coordinate of the pixel. \param z Z-coordinate of the pixel. \param v V-coordinate of the pixel. - When called without parameters, ptr() returns a pointer to the begining of the pixel buffer. - If the macro \c 'cimg_debug'>=3, boundary checking is performed and warning messages may appear if given coordinates are outside the image range (but function performances decrease). \par example: \code CImg img(100,100,1,1,0); // Define a 100x100 greyscale image with float-valued pixels. float *ptr = ptr(10,10); // Get a pointer to the pixel located at (10,10). float val = *ptr; // Get the pixel value. \endcode **/ #if cimg_debug>=3 T* ptr(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) { const long off = offset(x,y,z,v); if (off<0 || off>=(long)size()) { cimg::warn("CImg<%s>::ptr() : Asked for a pointer at coordinates (%u,%u,%u,%u) (offset=%ld), " "outside image range (%u,%u,%u,%u) (size=%lu)", pixel_type(),x,y,z,v,off,width,height,depth,dim,size()); return data; } return data + off; } const T* ptr(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) const { return const_cast*>(this)->ptr(x,y,z,v); } #else T* ptr(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) { return data + (long)x + (long)y*width + (long)z*width*height + (long)v*width*height*depth; } const T* ptr(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) const { return data + (long)x + (long)y*width + (long)z*width*height + (long)v*width*height*depth; } #endif //! Return \c true if the memory buffers of the two images overlaps. /** May happen when using shared images. **/ template bool is_overlapped(const CImg& img) const { const unsigned long csiz = size(), isiz = img.size(); return !((void*)(data+csiz)<=(void*)img.data || (void*)data>=(void*)(img.data+isiz)); } //! Return the offset of the pixel coordinates (\p x,\p y,\p z,\p v) with respect to the data pointer \c data. /** \param x X-coordinate of the pixel. \param y Y-coordinate of the pixel. \param z Z-coordinate of the pixel. \param v V-coordinate of the pixel. - No checking is done on the validity of the given coordinates. \par Example: \code CImg img(100,100,1,3,0); // Define a 100x100 color image with float-valued black pixels. long off = img.offset(10,10,0,2); // Get the offset of the blue value of the pixel located at (10,10). float val = img[off]; // Get the blue value of the pixel. \endcode **/ long offset(const int x, const int y=0, const int z=0, const int v=0) const { return (long)x + (long)y*width + (long)z*width*height + (long)v*width*height*depth; } //! Fast access to pixel value for reading or writing. /** \param x X-coordinate of the pixel. \param y Y-coordinate of the pixel. \param z Z-coordinate of the pixel. \param v V-coordinate of the pixel. - If one image dimension is equal to 1, it can be omitted in the coordinate list (see example below). - If the macro \c 'cimg_debug'>=3, boundary checking is performed and warning messages may appear (but function performances decrease). \par example: \code CImg img(100,100,1,3,0); // Define a 100x100 color image with float-valued black pixels. const float valR = img(10,10,0,0); // Read the red component at coordinates (10,10). const float valG = img(10,10,0,1); // Read the green component at coordinates (10,10) const float valB = img(10,10,2); // Read the blue component at coordinates (10,10) (Z-coordinate omitted here). const float avg = (valR + valG + valB)/3; // Compute average pixel value. img(10,10,0) = img(10,10,1) = img(10,10,2) = avg; // Replace the pixel (10,10) by the average grey value. \endcode **/ #if cimg_debug>=3 T& operator()(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) { const long off = offset(x,y,z,v); if (!data || off>=(long)size()) { cimg::warn("CImg<%s>::operator() : Pixel access requested at (%u,%u,%u,%u) (offset=%ld) " "outside the image range (%u,%u,%u,%u) (size=%lu)", pixel_type(),x,y,z,v,off,width,height,depth,dim,size()); return *data; } else return data[off]; } const T& operator()(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) const { return const_cast*>(this)->operator()(x,y,z,v); } #else T& operator()(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) { return data[(long)x + (long)y*width + (long)z*width*height + (long)v*width*height*depth]; } const T& operator()(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) const { return data[(long)x + (long)y*width + (long)z*width*height + (long)v*width*height*depth]; } #endif //! Fast access to pixel value for reading or writing, using an offset to the image pixel. /** \param off Offset of the pixel according to the begining of the pixel buffer, given by ptr(). - If the macro \c 'cimg_debug'>=3, boundary checking is performed and warning messages may appear (but function performances decrease). - As pixel values are aligned in memory, this operator can sometime useful to access values easier than with operator()() (see example below). \par example: \code CImg vec(1,10); // Define a vector of float values (10 lines, 1 row). const float val1 = vec(0,4); // Get the fifth element using operator()(). const float val2 = vec[4]; // Get the fifth element using operator[]. Here, val2==val1. \endcode **/ #if cimg_debug>=3 T& operator[](const unsigned long off) { if (!data || off>=size()) { cimg::warn("CImg<%s>::operator[] : Pixel access requested at offset=%lu " "outside the image range (%u,%u,%u,%u) (size=%lu)", pixel_type(),off,width,height,depth,dim,size()); return *data; } else return data[off]; } const T& operator[](const unsigned long off) const { return const_cast*>(this)->operator[](off); } #else T& operator[](const unsigned long off) { return data[off]; } const T& operator[](const unsigned long off) const { return data[off]; } #endif //! Return a reference to the last image value T& back() { return operator()(size()-1); } const T& back() const { return operator()(size()-1); } //! Return a reference to the first image value T& front() { return *data; } const T& front() const { return *data; } //! Return \c true if pixel (x,y,z,v) is inside image boundaries. bool containsXYZV(const int x, const int y=0, const int z=0, const int v=0) const { return !is_empty() && x>=0 && x=0 && y=0 && z=0 && v bool contains(const T& pixel, t& x, t& y, t& z, t& v) const { const unsigned long wh = width*height, whz = wh*depth, siz = whz*dim; const T *const ppixel = &pixel; if (is_empty() || ppixel=data+siz) return false; unsigned long off = (unsigned long)(ppixel - data); const unsigned long nv = off/whz; off%=whz; const unsigned long nz = off/wh; off%=wh; const unsigned long ny = off/width, nx = off%width; x = (t)nx; y = (t)ny; z = (t)nz; v = (t)nv; return true; } //! Return \c true if specified referenced value is inside image boundaries. If true, returns pixel coordinates in (x,y,z). template bool contains(const T& pixel, t& x, t& y, t& z) const { const unsigned long wh = width*height, whz = wh*depth, siz = whz*dim; const T *const ppixel = &pixel; if (is_empty() || ppixel=data+siz) return false; unsigned long off = ((unsigned long)(ppixel - data))%whz; const unsigned long nz = off/wh; off%=wh; const unsigned long ny = off/width, nx = off%width; x = (t)nx; y = (t)ny; z = (t)nz; return true; } //! Return \c true if specified referenced value is inside image boundaries. If true, returns pixel coordinates in (x,y). template bool contains(const T& pixel, t& x, t& y) const { const unsigned long wh = width*height, siz = wh*depth*dim; const T *const ppixel = &pixel; if (is_empty() || ppixel=data+siz) return false; unsigned long off = ((unsigned long)(ppixel - data))%wh; const unsigned long ny = off/width, nx = off%width; x = (t)nx; y = (t)ny; return true; } //! Return \c true if specified referenced value is inside image boundaries. If true, returns pixel coordinates in (x). template bool contains(const T& pixel, t& x) const { const T *const ppixel = &pixel; if (is_empty() || ppixel=data+size()) return false; x = (t)(((unsigned long)(ppixel - data))%width); return true; } //! Return \c true if specified referenced value is inside the image boundaries. bool contains(const T& pixel) const { const T *const ppixel = &pixel; return !is_empty() && ppixel>=data && ppixel=(int)size())?(cimg::temporary(out_val)=out_val):(*this)[off]; } T at(const int off, const T out_val) const { return (off<0 || off>=(int)size())?out_val:(*this)[off]; } //! Read a pixel value with Neumann boundary conditions. T& at(const int off) { if (!size()) throw CImgInstanceException("CImg<%s>::at() : Instance image is empty.", pixel_type()); return _at(off); } T at(const int off) const { if (!size()) throw CImgInstanceException("CImg<%s>::at() : Instance image is empty.", pixel_type()); return _at(off); } T& _at(const int off) { const unsigned int siz = (unsigned int)size(); return (*this)[off<0?0:(unsigned int)off>=siz?siz-1:off]; } T _at(const int off) const { const unsigned int siz = (unsigned int)size(); return (*this)[off<0?0:(unsigned int)off>=siz?siz-1:off]; } //! Read a pixel value with Dirichlet boundary conditions. T& atXYZV(const int x, const int y, const int z, const int v, const T out_val) { return (x<0 || y<0 || z<0 || v<0 || x>=dimx() || y>=dimy() || z>=dimz() || v>=dimv())? (cimg::temporary(out_val)=out_val):(*this)(x,y,z,v); } T atXYZV(const int x, const int y, const int z, const int v, const T out_val) const { return (x<0 || y<0 || z<0 || v<0 || x>=dimx() || y>=dimy() || z>=dimz() || v>=dimv())?out_val:(*this)(x,y,z,v); } //! Read a pixel value with Neumann boundary conditions. T& atXYZV(const int x, const int y, const int z, const int v) { if (is_empty()) throw CImgInstanceException("CImg<%s>::atXYZV() : Instance image is empty.", pixel_type()); return _atXYZV(x,y,z,v); } T atXYZV(const int x, const int y, const int z, const int v) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::atXYZV() : Instance image is empty.", pixel_type()); return _atXYZV(x,y,z,v); } T& _atXYZV(const int x, const int y, const int z, const int v) { return (*this)(x<0?0:(x>=dimx()?dimx()-1:x), y<0?0:(y>=dimy()?dimy()-1:y), z<0?0:(z>=dimz()?dimz()-1:z), v<0?0:(v>=dimv()?dimv()-1:v)); } T _atXYZV(const int x, const int y, const int z, const int v) const { return (*this)(x<0?0:(x>=dimx()?dimx()-1:x), y<0?0:(y>=dimy()?dimy()-1:y), z<0?0:(z>=dimz()?dimz()-1:z), v<0?0:(v>=dimv()?dimv()-1:v)); } //! Read a pixel value with Dirichlet boundary conditions for the three first coordinates (\c x,\c y,\c z). T& atXYZ(const int x, const int y, const int z, const int v, const T out_val) { return (x<0 || y<0 || z<0 || x>=dimx() || y>=dimy() || z>=dimz())? (cimg::temporary(out_val)=out_val):(*this)(x,y,z,v); } T atXYZ(const int x, const int y, const int z, const int v, const T out_val) const { return (x<0 || y<0 || z<0 || x>=dimx() || y>=dimy() || z>=dimz())?out_val:(*this)(x,y,z,v); } //! Read a pixel value with Neumann boundary conditions for the three first coordinates (\c x,\c y,\c z). T& atXYZ(const int x, const int y, const int z, const int v=0) { if (is_empty()) throw CImgInstanceException("CImg<%s>::atXYZ() : Instance image is empty.", pixel_type()); return _atXYZ(x,y,z,v); } T atXYZ(const int x, const int y, const int z, const int v=0) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::atXYZ() : Instance image is empty.", pixel_type()); return _atXYZ(x,y,z,v); } T& _atXYZ(const int x, const int y, const int z, const int v=0) { return (*this)(x<0?0:(x>=dimx()?dimx()-1:x),y<0?0:(y>=dimy()?dimy()-1:y), z<0?0:(z>=dimz()?dimz()-1:z),v); } T _atXYZ(const int x, const int y, const int z, const int v=0) const { return (*this)(x<0?0:(x>=dimx()?dimx()-1:x),y<0?0:(y>=dimy()?dimy()-1:y), z<0?0:(z>=dimz()?dimz()-1:z),v); } //! Read a pixel value with Dirichlet boundary conditions for the two first coordinates (\c x,\c y). T& atXY(const int x, const int y, const int z, const int v, const T out_val) { return (x<0 || y<0 || x>=dimx() || y>=dimy())?(cimg::temporary(out_val)=out_val):(*this)(x,y,z,v); } T atXY(const int x, const int y, const int z, const int v, const T out_val) const { return (x<0 || y<0 || x>=dimx() || y>=dimy())?out_val:(*this)(x,y,z,v); } //! Read a pixel value with Neumann boundary conditions for the two first coordinates (\c x,\c y). T& atXY(const int x, const int y, const int z=0, const int v=0) { if (is_empty()) throw CImgInstanceException("CImg<%s>::atXY() : Instance image is empty.", pixel_type()); return _atXY(x,y,z,v); } T atXY(const int x, const int y, const int z=0, const int v=0) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::atXY() : Instance image is empty.", pixel_type()); return _atXY(x,y,z,v); } T& _atXY(const int x, const int y, const int z=0, const int v=0) { return (*this)(x<0?0:(x>=dimx()?dimx()-1:x), y<0?0:(y>=dimy()?dimy()-1:y),z,v); } T _atXY(const int x, const int y, const int z=0, const int v=0) const { return (*this)(x<0?0:(x>=dimx()?dimx()-1:x), y<0?0:(y>=dimy()?dimy()-1:y),z,v); } //! Read a pixel value with Dirichlet boundary conditions for the first coordinates (\c x). T& atX(const int x, const int y, const int z, const int v, const T out_val) { return (x<0 || x>=dimx())?(cimg::temporary(out_val)=out_val):(*this)(x,y,z,v); } T atX(const int x, const int y, const int z, const int v, const T out_val) const { return (x<0 || x>=dimx())?out_val:(*this)(x,y,z,v); } //! Read a pixel value with Neumann boundary conditions for the first coordinates (\c x). T& atX(const int x, const int y=0, const int z=0, const int v=0) { if (is_empty()) throw CImgInstanceException("CImg<%s>::atX() : Instance image is empty.", pixel_type()); return _atX(x,y,z,v); } T atX(const int x, const int y=0, const int z=0, const int v=0) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::atX() : Instance image is empty.", pixel_type()); return _atX(x,y,z,v); } T& _atX(const int x, const int y=0, const int z=0, const int v=0) { return (*this)(x<0?0:(x>=dimx()?dimx()-1:x),y,z,v); } T _atX(const int x, const int y=0, const int z=0, const int v=0) const { return (*this)(x<0?0:(x>=dimx()?dimx()-1:x),y,z,v); } //! Read a pixel value using linear interpolation and Dirichlet boundary conditions. Tfloat linear_atXYZV(const float fx, const float fy, const float fz, const float fv, const T out_val) const { const int x = (int)fx-(fx>=0?0:1), nx = x+1, y = (int)fy-(fy>=0?0:1), ny = y+1, z = (int)fz-(fz>=0?0:1), nz = z+1, v = (int)fv-(fv>=0?0:1), nv = v+1; const float dx = fx-x, dy = fy-y, dz = fz-z, dv = fv-v; const Tfloat Icccc = (Tfloat)atXYZV(x,y,z,v,out_val), Inccc = (Tfloat)atXYZV(nx,y,z,v,out_val), Icncc = (Tfloat)atXYZV(x,ny,z,v,out_val), Inncc = (Tfloat)atXYZV(nx,ny,z,v,out_val), Iccnc = (Tfloat)atXYZV(x,y,nz,v,out_val), Incnc = (Tfloat)atXYZV(nx,y,nz,v,out_val), Icnnc = (Tfloat)atXYZV(x,ny,nz,v,out_val), Innnc = (Tfloat)atXYZV(nx,ny,nz,v,out_val), Icccn = (Tfloat)atXYZV(x,y,z,nv,out_val), Inccn = (Tfloat)atXYZV(nx,y,z,nv,out_val), Icncn = (Tfloat)atXYZV(x,ny,z,nv,out_val), Inncn = (Tfloat)atXYZV(nx,ny,z,nv,out_val), Iccnn = (Tfloat)atXYZV(x,y,nz,nv,out_val), Incnn = (Tfloat)atXYZV(nx,y,nz,nv,out_val), Icnnn = (Tfloat)atXYZV(x,ny,nz,nv,out_val), Innnn = (Tfloat)atXYZV(nx,ny,nz,nv,out_val); return Icccc + dx*(Inccc-Icccc + dy*(Icccc+Inncc-Icncc-Inccc + dz*(Iccnc+Innnc+Icncc+Inccc-Icnnc-Incnc-Icccc-Inncc + dv*(Iccnn+Innnn+Icncn+Inccn+Icnnc+Incnc+Icccc+Inncc-Icnnn-Incnn-Icccn-Inncn-Iccnc-Innnc-Icncc-Inccc)) + dv*(Icccn+Inncn+Icncc+Inccc-Icncn-Inccn-Icccc-Inncc)) + dz*(Icccc+Incnc-Iccnc-Inccc + dv*(Icccn+Incnn+Iccnc+Inccc-Iccnn-Inccn-Icccc-Incnc)) + dv*(Icccc+Inccn-Inccc-Icccn)) + dy*(Icncc-Icccc + dz*(Icccc+Icnnc-Iccnc-Icncc + dv*(Icccn+Icnnn+Iccnc+Icncc-Iccnn-Icncn-Icccc-Icnnc)) + dv*(Icccc+Icncn-Icncc-Icccn)) + dz*(Iccnc-Icccc + dv*(Icccc+Iccnn-Iccnc-Icccn)) + dv*(Icccn-Icccc); } //! Read a pixel value using linear interpolation and Neumann boundary conditions. Tfloat linear_atXYZV(const float fx, const float fy=0, const float fz=0, const float fv=0) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::linear_atXYZV() : Instance image is empty.", pixel_type()); return _linear_atXYZV(fx,fy,fz,fv); } Tfloat _linear_atXYZV(const float fx, const float fy=0, const float fz=0, const float fv=0) const { const float nfx = fx<0?0:(fx>width-1?width-1:fx), nfy = fy<0?0:(fy>height-1?height-1:fy), nfz = fz<0?0:(fz>depth-1?depth-1:fz), nfv = fv<0?0:(fv>dim-1?dim-1:fv); const unsigned int x = (unsigned int)nfx, y = (unsigned int)nfy, z = (unsigned int)nfz, v = (unsigned int)nfv; const float dx = nfx-x, dy = nfy-y, dz = nfz-z, dv = nfv-v; const unsigned int nx = dx>0?x+1:x, ny = dy>0?y+1:y, nz = dz>0?z+1:z, nv = dv>0?v+1:v; const Tfloat Icccc = (Tfloat)(*this)(x,y,z,v), Inccc = (Tfloat)(*this)(nx,y,z,v), Icncc = (Tfloat)(*this)(x,ny,z,v), Inncc = (Tfloat)(*this)(nx,ny,z,v), Iccnc = (Tfloat)(*this)(x,y,nz,v), Incnc = (Tfloat)(*this)(nx,y,nz,v), Icnnc = (Tfloat)(*this)(x,ny,nz,v), Innnc = (Tfloat)(*this)(nx,ny,nz,v), Icccn = (Tfloat)(*this)(x,y,z,nv), Inccn = (Tfloat)(*this)(nx,y,z,nv), Icncn = (Tfloat)(*this)(x,ny,z,nv), Inncn = (Tfloat)(*this)(nx,ny,z,nv), Iccnn = (Tfloat)(*this)(x,y,nz,nv), Incnn = (Tfloat)(*this)(nx,y,nz,nv), Icnnn = (Tfloat)(*this)(x,ny,nz,nv), Innnn = (Tfloat)(*this)(nx,ny,nz,nv); return Icccc + dx*(Inccc-Icccc + dy*(Icccc+Inncc-Icncc-Inccc + dz*(Iccnc+Innnc+Icncc+Inccc-Icnnc-Incnc-Icccc-Inncc + dv*(Iccnn+Innnn+Icncn+Inccn+Icnnc+Incnc+Icccc+Inncc-Icnnn-Incnn-Icccn-Inncn-Iccnc-Innnc-Icncc-Inccc)) + dv*(Icccn+Inncn+Icncc+Inccc-Icncn-Inccn-Icccc-Inncc)) + dz*(Icccc+Incnc-Iccnc-Inccc + dv*(Icccn+Incnn+Iccnc+Inccc-Iccnn-Inccn-Icccc-Incnc)) + dv*(Icccc+Inccn-Inccc-Icccn)) + dy*(Icncc-Icccc + dz*(Icccc+Icnnc-Iccnc-Icncc + dv*(Icccn+Icnnn+Iccnc+Icncc-Iccnn-Icncn-Icccc-Icnnc)) + dv*(Icccc+Icncn-Icncc-Icccn)) + dz*(Iccnc-Icccc + dv*(Icccc+Iccnn-Iccnc-Icccn)) + dv*(Icccn-Icccc); } //! Read a pixel value using linear interpolation and Dirichlet boundary conditions (first three coordinates). Tfloat linear_atXYZ(const float fx, const float fy, const float fz, const int v, const T out_val) const { const int x = (int)fx-(fx>=0?0:1), nx = x+1, y = (int)fy-(fy>=0?0:1), ny = y+1, z = (int)fz-(fz>=0?0:1), nz = z+1; const float dx = fx-x, dy = fy-y, dz = fz-z; const Tfloat Iccc = (Tfloat)atXYZ(x,y,z,v,out_val), Incc = (Tfloat)atXYZ(nx,y,z,v,out_val), Icnc = (Tfloat)atXYZ(x,ny,z,v,out_val), Innc = (Tfloat)atXYZ(nx,ny,z,v,out_val), Iccn = (Tfloat)atXYZ(x,y,nz,v,out_val), Incn = (Tfloat)atXYZ(nx,y,nz,v,out_val), Icnn = (Tfloat)atXYZ(x,ny,nz,v,out_val), Innn = (Tfloat)atXYZ(nx,ny,nz,v,out_val); return Iccc + dx*(Incc-Iccc + dy*(Iccc+Innc-Icnc-Incc + dz*(Iccn+Innn+Icnc+Incc-Icnn-Incn-Iccc-Innc)) + dz*(Iccc+Incn-Iccn-Incc)) + dy*(Icnc-Iccc + dz*(Iccc+Icnn-Iccn-Icnc)) + dz*(Iccn-Iccc); } //! Read a pixel value using linear interpolation and Neumann boundary conditions (first three coordinates). Tfloat linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int v=0) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::linear_atXYZ() : Instance image is empty.", pixel_type()); return _linear_atXYZ(fx,fy,fz,v); } Tfloat _linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int v=0) const { const float nfx = fx<0?0:(fx>width-1?width-1:fx), nfy = fy<0?0:(fy>height-1?height-1:fy), nfz = fz<0?0:(fz>depth-1?depth-1:fz); const unsigned int x = (unsigned int)nfx, y = (unsigned int)nfy, z = (unsigned int)nfz; const float dx = nfx-x, dy = nfy-y, dz = nfz-z; const unsigned int nx = dx>0?x+1:x, ny = dy>0?y+1:y, nz = dz>0?z+1:z; const Tfloat Iccc = (Tfloat)(*this)(x,y,z,v), Incc = (Tfloat)(*this)(nx,y,z,v), Icnc = (Tfloat)(*this)(x,ny,z,v), Innc = (Tfloat)(*this)(nx,ny,z,v), Iccn = (Tfloat)(*this)(x,y,nz,v), Incn = (Tfloat)(*this)(nx,y,nz,v), Icnn = (Tfloat)(*this)(x,ny,nz,v), Innn = (Tfloat)(*this)(nx,ny,nz,v); return Iccc + dx*(Incc-Iccc + dy*(Iccc+Innc-Icnc-Incc + dz*(Iccn+Innn+Icnc+Incc-Icnn-Incn-Iccc-Innc)) + dz*(Iccc+Incn-Iccn-Incc)) + dy*(Icnc-Iccc + dz*(Iccc+Icnn-Iccn-Icnc)) + dz*(Iccn-Iccc); } //! Read a pixel value using linear interpolation and Dirichlet boundary conditions (first two coordinates). Tfloat linear_atXY(const float fx, const float fy, const int z, const int v, const T out_val) const { const int x = (int)fx-(fx>=0?0:1), nx = x+1, y = (int)fy-(fy>=0?0:1), ny = y+1; const float dx = fx-x, dy = fy-y; const Tfloat Icc = (Tfloat)atXY(x,y,z,v,out_val), Inc = (Tfloat)atXY(nx,y,z,v,out_val), Icn = (Tfloat)atXY(x,ny,z,v,out_val), Inn = (Tfloat)atXY(nx,ny,z,v,out_val); return Icc + dx*(Inc-Icc + dy*(Icc+Inn-Icn-Inc)) + dy*(Icn-Icc); } //! Read a pixel value using linear interpolation and Neumann boundary conditions (first two coordinates). Tfloat linear_atXY(const float fx, const float fy, const int z=0, const int v=0) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::linear_atXY() : Instance image is empty.", pixel_type()); return _linear_atXY(fx,fy,z,v); } Tfloat _linear_atXY(const float fx, const float fy, const int z=0, const int v=0) const { const float nfx = fx<0?0:(fx>width-1?width-1:fx), nfy = fy<0?0:(fy>height-1?height-1:fy); const unsigned int x = (unsigned int)nfx, y = (unsigned int)nfy; const float dx = nfx-x, dy = nfy-y; const unsigned int nx = dx>0?x+1:x, ny = dy>0?y+1:y; const Tfloat Icc = (Tfloat)(*this)(x,y,z,v), Inc = (Tfloat)(*this)(nx,y,z,v), Icn = (Tfloat)(*this)(x,ny,z,v), Inn = (Tfloat)(*this)(nx,ny,z,v); return Icc + dx*(Inc-Icc + dy*(Icc+Inn-Icn-Inc)) + dy*(Icn-Icc); } //! Read a pixel value using linear interpolation and Dirichlet boundary conditions (first coordinate). Tfloat linear_atX(const float fx, const int y, const int z, const int v, const T out_val) const { const int x = (int)fx-(fx>=0?0:1), nx = x+1; const float dx = fx-x; const Tfloat Ic = (Tfloat)atX(x,y,z,v,out_val), In = (Tfloat)atXY(nx,y,z,v,out_val); return Ic + dx*(In-Ic); } //! Read a pixel value using linear interpolation and Neumann boundary conditions (first coordinate). Tfloat linear_atX(const float fx, const int y=0, const int z=0, const int v=0) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::linear_atX() : Instance image is empty.", pixel_type()); return _linear_atX(fx,y,z,v); } Tfloat _linear_atX(const float fx, const int y=0, const int z=0, const int v=0) const { const float nfx = fx<0?0:(fx>width-1?width-1:fx); const unsigned int x = (unsigned int)nfx; const float dx = nfx-x; const unsigned int nx = dx>0?x+1:x; const Tfloat Ic = (Tfloat)(*this)(x,y,z,v), In = (Tfloat)(*this)(nx,y,z,v); return Ic + dx*(In-Ic); } //! Read a pixel value using cubic interpolation and Dirichlet boundary conditions. Tfloat cubic_atXY(const float fx, const float fy, const int z, const int v, const T out_val) const { const int x = (int)fx-(fx>=0?0:1), px = x-1, nx = x+1, ax = x+2, y = (int)fy-(fy>=0?0:1), py = y-1, ny = y+1, ay = y+2; const float dx = fx-x, dx2 = dx*dx, dx3 = dx2*dx, dy = fy-y; const Tfloat Ipp = (Tfloat)atXY(px,py,z,v,out_val), Icp = (Tfloat)atXY(x,py,z,v,out_val), Inp = (Tfloat)atXY(nx,py,z,v,out_val), Iap = (Tfloat)atXY(ax,py,z,v,out_val), Ipc = (Tfloat)atXY(px,y,z,v,out_val), Icc = (Tfloat)atXY(x,y,z,v,out_val), Inc = (Tfloat)atXY(nx,y,z,v,out_val), Iac = (Tfloat)atXY(ax,y,z,v,out_val), Ipn = (Tfloat)atXY(px,ny,z,v,out_val), Icn = (Tfloat)atXY(x,ny,z,v,out_val), Inn = (Tfloat)atXY(nx,ny,z,v,out_val), Ian = (Tfloat)atXY(ax,ny,z,v,out_val), Ipa = (Tfloat)atXY(px,ay,z,v,out_val), Ica = (Tfloat)atXY(x,ay,z,v,out_val), Ina = (Tfloat)atXY(nx,ay,z,v,out_val), Iaa = (Tfloat)atXY(ax,ay,z,v,out_val), valm = cimg::min(cimg::min(Ipp,Icp,Inp,Iap),cimg::min(Ipc,Icc,Inc,Iac),cimg::min(Ipn,Icn,Inn,Ian),cimg::min(Ipa,Ica,Ina,Iaa)), valM = cimg::max(cimg::max(Ipp,Icp,Inp,Iap),cimg::max(Ipc,Icc,Inc,Iac),cimg::max(Ipn,Icn,Inn,Ian),cimg::max(Ipa,Ica,Ina,Iaa)), u0p = Icp - Ipp, u1p = Iap - Inp, ap = 2*(Icp-Inp) + u0p + u1p, bp = 3*(Inp-Icp) - 2*u0p - u1p, u0c = Icc - Ipc, u1c = Iac - Inc, ac = 2*(Icc-Inc) + u0c + u1c, bc = 3*(Inc-Icc) - 2*u0c - u1c, u0n = Icn - Ipn, u1n = Ian - Inn, an = 2*(Icn-Inn) + u0n + u1n, bn = 3*(Inn-Icn) - 2*u0n - u1n, u0a = Ica - Ipa, u1a = Iaa - Ina, aa = 2*(Ica-Ina) + u0a + u1a, ba = 3*(Ina-Ica) - 2*u0a - u1a, valp = ap*dx3 + bp*dx2 + u0p*dx + Icp, valc = ac*dx3 + bc*dx2 + u0c*dx + Icc, valn = an*dx3 + bn*dx2 + u0n*dx + Icn, vala = aa*dx3 + ba*dx2 + u0a*dx + Ica, u0 = valc - valp, u1 = vala - valn, a = 2*(valc-valn) + u0 + u1, b = 3*(valn-valc) - 2*u0 - u1, val = a*dy*dy*dy + b*dy*dy + u0*dy + valc; return valvalM?valM:val); } //! Read a pixel value using cubic interpolation and Neumann boundary conditions. Tfloat cubic_atXY(const float fx, const float fy, const int z=0, const int v=0) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::cubic_atXY() : Instance image is empty.", pixel_type()); return _cubic_atXY(fx,fy,z,v); } Tfloat _cubic_atXY(const float fx, const float fy, const int z=0, const int v=0) const { const float nfx = fx<0?0:(fx>width-1?width-1:fx), nfy = fy<0?0:(fy>height-1?height-1:fy); const int x = (int)nfx, y = (int)nfy; const float dx = nfx-x, dx2 = dx*dx, dx3 = dx2*dx, dy = nfy-y; const int px = x-1<0?0:x-1, nx = dx>0?x+1:x, ax = x+2>=dimx()?dimx()-1:x+2, py = y-1<0?0:y-1, ny = dy>0?y+1:y, ay = y+2>=dimy()?dimy()-1:y+2; const Tfloat Ipp = (Tfloat)(*this)(px,py,z,v), Icp = (Tfloat)(*this)(x,py,z,v), Inp = (Tfloat)(*this)(nx,py,z,v), Iap = (Tfloat)(*this)(ax,py,z,v), Ipc = (Tfloat)(*this)(px,y,z,v), Icc = (Tfloat)(*this)(x,y,z,v), Inc = (Tfloat)(*this)(nx,y,z,v), Iac = (Tfloat)(*this)(ax,y,z,v), Ipn = (Tfloat)(*this)(px,ny,z,v), Icn = (Tfloat)(*this)(x,ny,z,v), Inn = (Tfloat)(*this)(nx,ny,z,v), Ian = (Tfloat)(*this)(ax,ny,z,v), Ipa = (Tfloat)(*this)(px,ay,z,v), Ica = (Tfloat)(*this)(x,ay,z,v), Ina = (Tfloat)(*this)(nx,ay,z,v), Iaa = (Tfloat)(*this)(ax,ay,z,v), valm = cimg::min(cimg::min(Ipp,Icp,Inp,Iap),cimg::min(Ipc,Icc,Inc,Iac),cimg::min(Ipn,Icn,Inn,Ian),cimg::min(Ipa,Ica,Ina,Iaa)), valM = cimg::max(cimg::max(Ipp,Icp,Inp,Iap),cimg::max(Ipc,Icc,Inc,Iac),cimg::max(Ipn,Icn,Inn,Ian),cimg::max(Ipa,Ica,Ina,Iaa)), u0p = Icp - Ipp, u1p = Iap - Inp, ap = 2*(Icp-Inp) + u0p + u1p, bp = 3*(Inp-Icp) - 2*u0p - u1p, u0c = Icc - Ipc, u1c = Iac - Inc, ac = 2*(Icc-Inc) + u0c + u1c, bc = 3*(Inc-Icc) - 2*u0c - u1c, u0n = Icn - Ipn, u1n = Ian - Inn, an = 2*(Icn-Inn) + u0n + u1n, bn = 3*(Inn-Icn) - 2*u0n - u1n, u0a = Ica - Ipa, u1a = Iaa - Ina, aa = 2*(Ica-Ina) + u0a + u1a, ba = 3*(Ina-Ica) - 2*u0a - u1a, valp = ap*dx3 + bp*dx2 + u0p*dx + Icp, valc = ac*dx3 + bc*dx2 + u0c*dx + Icc, valn = an*dx3 + bn*dx2 + u0n*dx + Icn, vala = aa*dx3 + ba*dx2 + u0a*dx + Ica, u0 = valc - valp, u1 = vala - valn, a = 2*(valc-valn) + u0 + u1, b = 3*(valn-valc) - 2*u0 - u1, val = a*dy*dy*dy + b*dy*dy + u0*dy + valc; return valvalM?valM:val); } //! Read a pixel value using cubic interpolation and Dirichlet boundary conditions (first coordinates). Tfloat cubic_atX(const float fx, const int y, const int z, const int v, const T out_val) const { const int x = (int)fx-(fx>=0?0:1), px = x-1, nx = x+1, ax = x+2; const float dx = fx-x; const Tfloat Ip = (Tfloat)atX(px,y,z,v,out_val), Ic = (Tfloat)atX(x,y,z,v,out_val), In = (Tfloat)atX(nx,y,z,v,out_val), Ia = (Tfloat)atX(ax,y,z,v,out_val), valm = cimg::min(Ip,In,Ic,Ia), valM = cimg::max(Ip,In,Ic,Ia), u0 = Ic - Ip, u1 = Ia - In, a = 2*(Ic-In) + u0 + u1, b = 3*(In-Ic) - 2*u0 - u1, val = a*dx*dx*dx + b*dx*dx + u0*dx + Ic; return valvalM?valM:val); } //! Read a pixel value using cubic interpolation and Neumann boundary conditions (first coordinates). Tfloat cubic_atX(const float fx, const int y=0, const int z=0, const int v=0) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::cubic_atX() : Instance image is empty.", pixel_type()); return _cubic_atX(fx,y,z,v); } Tfloat _cubic_atX(const float fx, const int y=0, const int z=0, const int v=0) const { const float nfx = fx<0?0:(fx>width-1?width-1:fx); const int x = (int)nfx; const float dx = nfx-x; const int px = x-1<0?0:x-1, nx = dx>0?x+1:x, ax = x+2>=dimx()?dimx()-1:x+2; const Tfloat Ip = (Tfloat)(*this)(px,y,z,v), Ic = (Tfloat)(*this)(x,y,z,v), In = (Tfloat)(*this)(nx,y,z,v), Ia = (Tfloat)(*this)(ax,y,z,v), valm = cimg::min(Ip,In,Ic,Ia), valM = cimg::max(Ip,In,Ic,Ia), u0 = Ic - Ip, u1 = Ia - In, a = 2*(Ic-In) + u0 + u1, b = 3*(In-Ic) - 2*u0 - u1, val = a*dx*dx*dx + b*dx*dx + u0*dx + Ic; return valvalM?valM:val); } //! Set a pixel value, with 3D float coordinates, using linear interpolation. CImg& set_linear_atXYZ(const T& val, const float fx, const float fy=0, const float fz=0, const int v=0, const bool add=false) { const int x = (int)fx-(fx>=0?0:1), nx = x+1, y = (int)fy-(fy>=0?0:1), ny = y+1, z = (int)fz-(fz>=0?0:1), nz = z+1; const float dx = fx-x, dy = fy-y, dz = fz-z; if (v>=0 && v=0 && z=0 && y=0 && x=0 && nx=0 && ny=0 && x=0 && nx=0 && nz=0 && y=0 && x=0 && nx=0 && ny=0 && x=0 && nx=0?0:1), nx = x+1, y = (int)fy-(fy>=0?0:1), ny = y+1; const float dx = fx-x, dy = fy-y; if (z>=0 && z=0 && v=0 && y=0 && x=0 && nx=0 && ny=0 && x=0 && nx::min() : Instance image is empty.", pixel_type()); const T *ptrmin = data; T min_value = *ptrmin; cimg_for(*this,ptr,T) if ((*ptr)::min() : Instance image is empty.", pixel_type()); T *ptrmin = data; T min_value = *ptrmin; cimg_for(*this,ptr,T) if ((*ptr)::max() : Instance image is empty.", pixel_type()); const T *ptrmax = data; T max_value = *ptrmax; cimg_for(*this,ptr,T) if ((*ptr)>max_value) max_value = *(ptrmax=ptr); return *ptrmax; } //! Return a reference to the maximum pixel value of the instance image T& max() { if (is_empty()) throw CImgInstanceException("CImg<%s>::max() : Instance image is empty.", pixel_type()); T *ptrmax = data; T max_value = *ptrmax; cimg_for(*this,ptr,T) if ((*ptr)>max_value) max_value = *(ptrmax=ptr); return *ptrmax; } //! Return a reference to the minimum pixel value and return also the maximum pixel value. template const T& minmax(t& max_val) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::minmax() : Instance image is empty.", pixel_type()); const T *ptrmin = data; T min_value = *ptrmin, max_value = min_value; cimg_for(*this,ptr,T) { const T val = *ptr; if (valmax_value) max_value = val; } max_val = (t)max_value; return *ptrmin; } //! Return a reference to the minimum pixel value and return also the maximum pixel value. template T& minmax(t& max_val) { if (is_empty()) throw CImgInstanceException("CImg<%s>::minmax() : Instance image is empty.", pixel_type()); T *ptrmin = data; T min_value = *ptrmin, max_value = min_value; cimg_for(*this,ptr,T) { const T val = *ptr; if (valmax_value) max_value = val; } max_val = (t)max_value; return *ptrmin; } //! Return a reference to the maximum pixel value and return also the minimum pixel value. template const T& maxmin(t& min_val) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::maxmin() : Instance image is empty.", pixel_type()); const T *ptrmax = data; T max_value = *ptrmax, min_value = max_value; cimg_for(*this,ptr,T) { const T val = *ptr; if (val>max_value) { max_value = val; ptrmax = ptr; } if (val T& maxmin(t& min_val) { if (is_empty()) throw CImgInstanceException("CImg<%s>::maxmin() : Instance image is empty.", pixel_type()); T *ptrmax = data; T max_value = *ptrmax, min_value = max_value; cimg_for(*this,ptr,T) { const T val = *ptr; if (val>max_value) { max_value = val; ptrmax = ptr; } if (val::sum() : Instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),width,height,depth,dim,data); Tfloat res = 0; cimg_for(*this,ptr,T) res+=*ptr; return res; } //! Return the mean pixel value of the instance image. Tfloat mean() const { if (is_empty()) throw CImgInstanceException("CImg<%s>::mean() : Instance image is empty.", pixel_type()); Tfloat val = 0; cimg_for(*this,ptr,T) val+=*ptr; return val/size(); } //! Return the variance of the image. /** @param variance_method Determines how to calculate the variance
0 Second moment: @f$ v = 1/N \sum\limits_{k=1}^{N} (x_k - \bar x)^2 = 1/N \left( \sum\limits_{k=1}^N x_k^2 - \left( \sum\limits_{k=1}^N x_k \right)^2 / N \right) @f$ with @f$ \bar x = 1/N \sum\limits_{k=1}^N x_k \f$
1 Best unbiased estimator: @f$ v = \frac{1}{N-1} \sum\limits_{k=1}^{N} (x_k - \bar x)^2 @f$
2 Least median of squares
3 Least trimmed of squares
*/ Tfloat variance(const unsigned int variance_method=1) const { Tfloat foo; return variancemean(variance_method,foo); } //! Return the variance and the mean of the image. template Tfloat variancemean(const unsigned int variance_method, t& mean) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::variance() : Instance image is empty.", pixel_type()); Tfloat variance = 0, average = 0; const unsigned int siz = size(); switch (variance_method) { case 3 : { // Least trimmed of Squares CImg buf(*this); const unsigned int siz2 = siz>>1; { cimg_for(buf,ptrs,Tfloat) { const Tfloat val = *ptrs; (*ptrs)*=val; average+=val; }} buf.sort(); Tfloat a = 0; const Tfloat *ptrs = buf.ptr(); for (unsigned int j = 0; j buf(*this); buf.sort(); const unsigned int siz2 = siz>>1; const Tfloat med_i = buf[siz2]; cimg_for(buf,ptrs,Tfloat) { const Tfloat val = *ptrs; *ptrs = cimg::abs(val - med_i); average+=val; } buf.sort(); const Tfloat sig = (Tfloat)(1.4828*buf[siz2]); variance = sig*sig; } break; case 1 : { // Least mean square (robust definition) Tfloat S = 0, S2 = 0; cimg_for(*this,ptr,T) { const Tfloat val = (Tfloat)*ptr; S+=val; S2+=val*val; } variance = siz>1?(S2 - S*S/siz)/(siz - 1):0; average = S; } break; case 0 :{ // Least mean square (standard definition) Tfloat S = 0, S2 = 0; cimg_for(*this,ptr,T) { const Tfloat val = (Tfloat)*ptr; S+=val; S2+=val*val; } variance = (S2 - S*S/siz)/siz; average = S; } break; default : throw CImgArgumentException("CImg<%s>::variancemean() : Incorrect parameter 'variance_method = %d' (correct values are 0,1,2 or 3).", pixel_type(),variance_method); } mean = (t)(average/siz); return variance>0?variance:0; } //! Return the kth smallest element of the image. // (Adapted from the numerical recipies for CImg) T kth_smallest(const unsigned int k) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::kth_smallest() : Instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),width,height,depth,dim,data); CImg arr(*this); unsigned long l = 0, ir = size()-1; for (;;) { if (ir<=l+1) { if (ir==l+1 && arr[ir]>1; cimg::swap(arr[mid],arr[l+1]); if (arr[l]>arr[ir]) cimg::swap(arr[l],arr[ir]); if (arr[l+1]>arr[ir]) cimg::swap(arr[l+1],arr[ir]); if (arr[l]>arr[l+1]) cimg::swap(arr[l],arr[l+1]); unsigned long i = l+1, j = ir; const T pivot = arr[l+1]; for (;;) { do ++i; while (arr[i]pivot); if (j=k) ir=j-1; if (j<=k) l=i; } } return 0; } //! Compute a statistics vector (min,max,mean,variance,xmin,ymin,zmin,vmin,xmax,ymax,zmax,vmax). CImg& stats(const unsigned int variance_method=1) { return get_stats(variance_method).transfer_to(*this); } CImg get_stats(const unsigned int variance_method=1) const { if (is_empty()) return CImg(); const unsigned long siz = size(); const T *const odata = data; const T *pm = odata, *pM = odata; Tfloat S = 0, S2 = 0; T m = *pm, M = m; cimg_for(*this,ptr,T) { const T val = *ptr; const Tfloat fval = (Tfloat)val; if (valM) { M = val; pM = ptr; } S+=fval; S2+=fval*fval; } const Tfloat mean_value = S/siz, _variance_value = variance_method==0?(S2 - S*S/siz)/siz: (variance_method==1?(siz>1?(S2 - S*S/siz)/(siz - 1):0): variance(variance_method)), variance_value = _variance_value>0?_variance_value:0; int xm = 0, ym = 0, zm = 0, vm = 0, xM = 0, yM = 0, zM = 0, vM = 0; contains(*pm,xm,ym,zm,vm); contains(*pM,xM,yM,zM,vM); return CImg(1,12).fill((Tfloat)m,(Tfloat)M,mean_value,variance_value, (Tfloat)xm,(Tfloat)ym,(Tfloat)zm,(Tfloat)vm, (Tfloat)xM,(Tfloat)yM,(Tfloat)zM,(Tfloat)vM); } //! Return the median value of the image. T median() const { const unsigned int s = size(); const T res = kth_smallest(s>>1); return (s%2)?res:((res+kth_smallest((s>>1)-1))/2); } //! Compute the MSE (Mean-Squared Error) between two images. template Tfloat MSE(const CImg& img) const { if (img.size()!=size()) throw CImgArgumentException("CImg<%s>::MSE() : Instance image (%u,%u,%u,%u) and given image (%u,%u,%u,%u) have different dimensions.", pixel_type(),width,height,depth,dim,img.width,img.height,img.depth,img.dim); Tfloat vMSE = 0; const t* ptr2 = img.end(); cimg_for(*this,ptr1,T) { const Tfloat diff = (Tfloat)*ptr1 - (Tfloat)*(--ptr2); vMSE += diff*diff; } vMSE/=img.size(); return vMSE; } //! Compute the PSNR between two images. template Tfloat PSNR(const CImg& img, const Tfloat valmax=(Tfloat)255) const { const Tfloat vMSE = (Tfloat)cimg_std::sqrt(MSE(img)); return (vMSE!=0)?(Tfloat)(20*cimg_std::log10(valmax/vMSE)):(Tfloat)(cimg::type::max()); } //! Return the trace of the image, viewed as a matrix. Tfloat trace() const { if (is_empty()) throw CImgInstanceException("CImg<%s>::trace() : Instance matrix (%u,%u,%u,%u,%p) is empty.", pixel_type(),width,height,depth,dim,data); Tfloat res = 0; cimg_forX(*this,k) res+=(*this)(k,k); return res; } //! Return the dot product of the current vector/matrix with the vector/matrix \p img. template Tfloat dot(const CImg& img) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::dot() : Instance object (%u,%u,%u,%u,%p) is empty.", pixel_type(),width,height,depth,dim,data); if (!img) throw CImgArgumentException("CImg<%s>::trace() : Specified argument (%u,%u,%u,%u,%p) is empty.", pixel_type(),img.width,img.height,img.depth,img.dim,img.data); const unsigned long nb = cimg::min(size(),img.size()); Tfloat res = 0; for (unsigned long off = 0; off::det() : Instance matrix (%u,%u,%u,%u,%p) is not square or is empty.", pixel_type(),width,height,depth,dim,data); switch (width) { case 1 : return (Tfloat)((*this)(0,0)); case 2 : return (Tfloat)((*this)(0,0))*(Tfloat)((*this)(1,1)) - (Tfloat)((*this)(0,1))*(Tfloat)((*this)(1,0)); case 3 : { const Tfloat a = (Tfloat)data[0], d = (Tfloat)data[1], g = (Tfloat)data[2], b = (Tfloat)data[3], e = (Tfloat)data[4], h = (Tfloat)data[5], c = (Tfloat)data[6], f = (Tfloat)data[7], i = (Tfloat)data[8]; return i*a*e - a*h*f - i*b*d + b*g*f + c*d*h - c*g*e; } default : { CImg lu(*this); CImg indx; bool d; lu._LU(indx,d); Tfloat res = d?(Tfloat)1:(Tfloat)-1; cimg_forX(lu,i) res*=lu(i,i); return res; } } return 0; } //! Return the norm of the current vector/matrix. \p ntype = norm type (0=L2, 1=L1, -1=Linf). Tfloat norm(const int norm_type=2) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::norm() : Instance object (%u,%u,%u,%u,%p) is empty.", pixel_type(),width,height,depth,dim,data); Tfloat res = 0; switch (norm_type) { case -1 : { cimg_foroff(*this,off) { const Tfloat tmp = cimg::abs((Tfloat)data[off]); if (tmp>res) res = tmp; } return res; } break; case 1 : { cimg_foroff(*this,off) res+=cimg::abs((Tfloat)data[off]); return res; } break; case 2 : return (Tfloat)cimg_std::sqrt(dot(*this)); break; default : throw CImgArgumentException("CImg<%s>::norm() : Incorrect parameter 'norm_type=%d' (correct values are -1,1 or 2).", pixel_type(),norm_type); } return 0; } //! Return a C-string containing the values of the instance image. CImg value_string(const char separator=',', const unsigned int max_size=0) const { if (is_empty()) return CImg(1,1,1,1,0); const unsigned int siz = (unsigned int)size(); CImgList items; char item[256] = { 0 }; const T *ptrs = ptr(); for (unsigned int off = 0; off::format(),cimg::type::format(*(ptrs++))); const int l = cimg::strlen(item); items.insert(CImg(item,l+1)); items[items.size-1](l) = separator; } cimg_std::sprintf(item,cimg::type::format(),cimg::type::format(*ptrs)); items.insert(CImg(item,cimg::strlen(item)+1)); CImg res = items.get_append('x'); if (max_size) { res.crop(0,max_size); res(max_size) = 0; } return res; } //! Display informations about the image on the standard error output. /** \param title Name for the considered image (optional). \param display_stats Compute and display image statistics (optional). **/ const CImg& print(const char *title=0, const bool display_stats=true) const { int xm = 0, ym = 0, zm = 0, vm = 0, xM = 0, yM = 0, zM = 0, vM = 0; static CImg st; if (!is_empty() && display_stats) { st = get_stats(); xm = (int)st[4]; ym = (int)st[5], zm = (int)st[6], vm = (int)st[7]; xM = (int)st[8]; yM = (int)st[9], zM = (int)st[10], vM = (int)st[11]; } const unsigned long siz = size(), msiz = siz*sizeof(T), siz1 = siz-1; const unsigned int mdisp = msiz<8*1024?0:(msiz<8*1024*1024?1:2), width1 = width-1; char ntitle[64] = { 0 }; if (!title) cimg_std::sprintf(ntitle,"CImg<%s>",pixel_type()); cimg_std::fprintf(cimg_stdout,"%s: this = %p, size = (%u,%u,%u,%u) [%lu %s], data = (%s*)%p (%s) = [ ", title?title:ntitle,(void*)this,width,height,depth,dim, mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)), mdisp==0?"b":(mdisp==1?"Kb":"Mb"), pixel_type(),(void*)data,is_shared?"shared":"not shared"); if (!is_empty()) cimg_foroff(*this,off) { cimg_std::fprintf(cimg_stdout,cimg::type::format(),cimg::type::format(data[off])); if (off!=siz1) cimg_std::fprintf(cimg_stdout,"%s",off%width==width1?" ; ":" "); if (off==7 && siz>16) { off = siz1-8; if (off!=7) cimg_std::fprintf(cimg_stdout,"... "); } } if (!is_empty() && display_stats) cimg_std::fprintf(cimg_stdout," ], min = %g, max = %g, mean = %g, std = %g, coords(min) = (%u,%u,%u,%u), coords(max) = (%u,%u,%u,%u).\n", st[0],st[1],st[2],cimg_std::sqrt(st[3]),xm,ym,zm,vm,xM,yM,zM,vM); else cimg_std::fprintf(cimg_stdout,"%s].\n",is_empty()?"":" "); return *this; } //@} //------------------------------------------ // //! \name Arithmetic and Boolean Operators //@{ //------------------------------------------ //! Assignment operator. /** This operator assigns a copy of the input image \p img to the current instance image. \param img The input image to copy. \remark - This operator is strictly equivalent to the function assign(const CImg< t >&) and has exactly the same properties. **/ template CImg& operator=(const CImg& img) { return assign(img); } CImg& operator=(const CImg& img) { return assign(img); } //! Assign values of a C-array to the instance image. /** \param buf Pointer to a C-style array having a size of (at least) this->size(). - Replace pixel values by the content of the array \c buf. - Warning : the value types in the array and in the image must be the same. \par example: \code float tab[4*4] = { 1,2,3,4, 5,6,7,8, 9,10,11,12, 13,14,15,16 }; // Define a 4x4 matrix in C-style. CImg matrice(4,4); // Define a 4x4 greyscale image. matrice = tab; // Fill the image by the values in tab. \endcode **/ CImg& operator=(const T *buf) { return assign(buf,width,height,depth,dim); } //! Assign a value to each image pixel of the instance image. CImg& operator=(const T val) { return fill(val); } //! Operator+ /** \remark - This operator can be used to get a non-shared copy of an image. **/ CImg operator+() const { return CImg(*this,false); } //! Operator+=; #ifdef cimg_use_visualcpp6 CImg& operator+=(const T val) #else template CImg& operator+=(const t val) #endif { cimg_for(*this,ptr,T) (*ptr) = (T)((*ptr)+val); return *this; } //! Operator+= template CImg& operator+=(const CImg& img) { if (is_overlapped(img)) return *this+=+img; const unsigned int smin = cimg::min(size(),img.size()); t *ptrs = img.data + smin; for (T *ptrd = data + smin; ptrd>data; --ptrd, (*ptrd)=(T)((*ptrd)+(*(--ptrs)))) {} return *this; } //! Operator++ (prefix) CImg& operator++() { cimg_for(*this,ptr,T) ++(*ptr); return *this; } //! Operator++ (postfix) CImg operator++(int) { const CImg copy(*this,false); ++*this; return copy; } //! Operator-. CImg operator-() const { return CImg(width,height,depth,dim,0)-=*this; } //! Operator-=. #ifdef cimg_use_visualcpp6 CImg& operator-=(const T val) #else template CImg& operator-=(const t val) #endif { cimg_for(*this,ptr,T) (*ptr) = (T)((*ptr)-val); return *this; } //! Operator-=. template CImg& operator-=(const CImg& img) { if (is_overlapped(img)) return *this-=+img; const unsigned int smin = cimg::min(size(),img.size()); t *ptrs = img.data+smin; for (T *ptrd = data+smin; ptrd>data; --ptrd, (*ptrd) = (T)((*ptrd)-(*(--ptrs)))) {} return *this; } //! Operator-- (prefix). CImg& operator--() { cimg_for(*this,ptr,T) *ptr = *ptr-(T)1; return *this; } //! Operator-- (postfix). CImg operator--(int) { CImg copy(*this,false); --*this; return copy; } //! Operator*=. #ifdef cimg_use_visualcpp6 CImg& operator*=(const double val) #else template CImg& operator*=(const t val) #endif { cimg_for(*this,ptr,T) (*ptr) = (T)((*ptr)*val); return *this; } //! Operator*=. template CImg& operator*=(const CImg& img) { return ((*this)*img).transfer_to(*this); } //! Operator/=. #ifdef cimg_use_visualcpp6 CImg& operator/=(const double val) #else template CImg& operator/=(const t val) #endif { cimg_for(*this,ptr,T) (*ptr) = (T)((*ptr)/val); return *this; } //! Operator/=. template CImg& operator/=(const CImg& img) { return assign(*this*img.get_invert()); } //! Modulo. template CImg::type> operator%(const CImg& img) const { typedef typename cimg::superset::type Tt; return CImg(*this,false)%=img; } //! Modulo. CImg operator%(const T val) const { return (+*this)%=val; } //! In-place modulo. CImg& operator%=(const T val) { cimg_for(*this,ptr,T) (*ptr) = (T)cimg::mod(*ptr,val); return *this; } //! In-place modulo. template CImg& operator%=(const CImg& img) { if (is_overlapped(img)) return *this%=+img; typedef typename cimg::superset::type Tt; const unsigned int smin = cimg::min(size(),img.size()); const t *ptrs = img.data + smin; for (T *ptrd = data + smin; ptrd>data; ) { T& val = *(--ptrd); val = (T)cimg::mod((Tt)val,(Tt)*(--ptrs)); } return *this; } //! Bitwise AND. template CImg::type> operator&(const CImg& img) const { typedef typename cimg::superset::type Tt; return CImg(*this,false)&=img; } //! Bitwise AND. CImg operator&(const T val) const { return (+*this)&=val; } //! In-place bitwise AND. template CImg& operator&=(const CImg& img) { if (is_overlapped(img)) return *this&=+img; const unsigned int smin = cimg::min(size(),img.size()); const t *ptrs = img.data + smin; for (T *ptrd = data + smin; ptrd>data; ) { T& val = *(--ptrd); val = (T)((unsigned long)val & (unsigned long)*(--ptrs)); } return *this; } //! In-place bitwise AND. CImg& operator&=(const T val) { cimg_for(*this,ptr,T) *ptr = (T)((unsigned long)*ptr & (unsigned long)val); return *this; } //! Bitwise OR. template CImg::type> operator|(const CImg& img) const { typedef typename cimg::superset::type Tt; return CImg(*this,false)|=img; } //! Bitwise OR. CImg operator|(const T val) const { return (+*this)|=val; } //! In-place bitwise OR. template CImg& operator|=(const CImg& img) { if (is_overlapped(img)) return *this|=+img; const unsigned int smin = cimg::min(size(),img.size()); const t *ptrs = img.data + smin; for (T *ptrd = data + smin; ptrd>data; ) { T& val = *(--ptrd); val = (T)((unsigned long)val | (unsigned long)*(--ptrs)); } return *this; } //! In-place bitwise OR. CImg& operator|=(const T val) { cimg_for(*this,ptr,T) *ptr = (T)((unsigned long)*ptr | (unsigned long)val); return *this; } //! Bitwise XOR. template CImg::type> operator^(const CImg& img) const { typedef typename cimg::superset::type Tt; return CImg(*this,false)^=img; } //! Bitwise XOR. CImg operator^(const T val) const { return (+*this)^=val; } //! In-place bitwise XOR. template CImg& operator^=(const CImg& img) { if (is_overlapped(img)) return *this^=+img; const unsigned int smin = cimg::min(size(),img.size()); const t *ptrs = img.data + smin; for (T *ptrd = data+smin; ptrd>data; ) { T& val = *(--ptrd); val =(T)((unsigned long)val ^ (unsigned long)*(--ptrs)); } return *this; } //! In-place bitwise XOR. CImg& operator^=(const T val) { cimg_for(*this,ptr,T) *ptr = (T)((unsigned long)*ptr ^ (unsigned long)val); return *this; } //! Bitwise NOT. CImg operator~() const { CImg res(width,height,depth,dim); const T *ptrs = end(); cimg_for(res,ptrd,T) { const unsigned long val = (unsigned long)*(--ptrs); *ptrd = (T)~val; } return res; } //! Bitwise left shift. CImg& operator<<=(const int n) { cimg_for(*this,ptr,T) *ptr = (T)(((long)*ptr)< operator<<(const int n) const { return (+*this)<<=n; } //! Bitwise right shift. CImg& operator>>=(const int n) { cimg_for(*this,ptr,T) *ptr = (T)(((long)*ptr)>>n); return *this; } //! Bitwise right shift. CImg operator>>(const int n) const { return (+*this)>>=n; } //! Boolean equality. template bool operator==(const CImg& img) const { const unsigned int siz = size(); bool vequal = true; if (siz!=img.size()) return false; t *ptrs = img.data + siz; for (T *ptrd = data + siz; vequal && ptrd>data; vequal = vequal && ((*(--ptrd))==(*(--ptrs)))) {} return vequal; } //! Boolean difference. template bool operator!=(const CImg& img) const { return !((*this)==img); } //! Return a list of two images { *this, img }. template CImgList::type> operator<<(const CImg& img) const { typedef typename cimg::superset::type Tt; return CImgList(*this,img); } //! Return a copy of \p list, where image *this has been inserted at first position. template CImgList::type> operator<<(const CImgList& list) const { typedef typename cimg::superset::type Tt; return CImgList(list).insert(*this,0); } //! Return a list of two images { *this, img }. template CImgList::type> operator>>(const CImg& img) const { return (*this)< CImgList& operator>>(const CImgList& list) const { return list.insert(*this,0); } //! Display an image into a CImgDisplay. const CImg& operator>>(CImgDisplay& disp) const { return display(disp); } //@} //--------------------------------------- // //! \name Usual Mathematics Functions //@{ //--------------------------------------- //! Apply a R->R function on all pixel values. template CImg& apply(t& func) { cimg_for(*this,ptr,T) *ptr = func(*ptr); return *this; } template CImg get_apply(t& func) const { return (+*this).apply(func); } //! Pointwise multiplication between two images. template CImg& mul(const CImg& img) { if (is_overlapped(img)) return mul(+img); t *ptrs = img.data; T *ptrf = data + cimg::min(size(),img.size()); for (T* ptrd = data; ptrd CImg::type> get_mul(const CImg& img) const { typedef typename cimg::superset::type Tt; return CImg(*this,false).mul(img); } //! Pointwise division between two images. template CImg& div(const CImg& img) { if (is_overlapped(img)) return div(+img); t *ptrs = img.data; T *ptrf = data + cimg::min(size(),img.size()); for (T* ptrd = data; ptrd CImg::type> get_div(const CImg& img) const { typedef typename cimg::superset::type Tt; return CImg(*this,false).div(img); } //! Pointwise max operator between two images. template CImg& max(const CImg& img) { if (is_overlapped(img)) return max(+img); t *ptrs = img.data; T *ptrf = data + cimg::min(size(),img.size()); for (T* ptrd = data; ptrd CImg::type> get_max(const CImg& img) const { typedef typename cimg::superset::type Tt; return CImg(*this,false).max(img); } //! Pointwise max operator between an image and a value. CImg& max(const T val) { cimg_for(*this,ptr,T) (*ptr) = cimg::max(*ptr,val); return *this; } CImg get_max(const T val) const { return (+*this).max(val); } //! Pointwise min operator between two images. template CImg& min(const CImg& img) { if (is_overlapped(img)) return min(+img); t *ptrs = img.data; T *ptrf = data + cimg::min(size(),img.size()); for (T* ptrd = data; ptrd CImg::type> get_min(const CImg& img) const { typedef typename cimg::superset::type Tt; return CImg(*this,false).min(img); } //! Pointwise min operator between an image and a value. CImg& min(const T val) { cimg_for(*this,ptr,T) (*ptr) = cimg::min(*ptr,val); return *this; } CImg get_min(const T val) const { return (+*this).min(val); } //! Compute the square value of each pixel. CImg& sqr() { cimg_for(*this,ptr,T) { const T val = *ptr; *ptr = (T)(val*val); }; return *this; } CImg get_sqr() const { return CImg(*this,false).sqr(); } //! Compute the square root of each pixel value. CImg& sqrt() { cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::sqrt((double)(*ptr)); return *this; } CImg get_sqrt() const { return CImg(*this,false).sqrt(); } //! Compute the exponential of each pixel value. CImg& exp() { cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::exp((double)(*ptr)); return *this; } CImg get_exp() const { return CImg(*this,false).exp(); } //! Compute the log of each each pixel value. CImg& log() { cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::log((double)(*ptr)); return *this; } CImg get_log() const { return CImg(*this,false).log(); } //! Compute the log10 of each each pixel value. CImg& log10() { cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::log10((double)(*ptr)); return *this; } CImg get_log10() const { return CImg(*this,false).log10(); } //! Compute the power by p of each pixel value. CImg& pow(const double p) { if (p==0) return fill(1); if (p==0.5) { cimg_for(*this,ptr,T) { const T val = *ptr; *ptr = (T)cimg_std::sqrt((double)val); } return *this; } if (p==1) return *this; if (p==2) { cimg_for(*this,ptr,T) { const T val = *ptr; *ptr = val*val; } return *this; } if (p==3) { cimg_for(*this,ptr,T) { const T val = *ptr; *ptr = val*val*val; } return *this; } if (p==4) { cimg_for(*this,ptr,T) { const T val = *ptr; *ptr = val*val*val*val; } return *this; } cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::pow((double)(*ptr),p); return *this; } CImg get_pow(const double p) const { return CImg(*this,false).pow(p); } //! Compute the power of each pixel value. template CImg& pow(const CImg& img) { if (is_overlapped(img)) return pow(+img); t *ptrs = img.data; T *ptrf = data + cimg::min(size(),img.size()); for (T* ptrd = data; ptrd CImg get_pow(const CImg& img) const { return CImg(*this,false).pow(img); } //! Compute the absolute value of each pixel value. CImg& abs() { cimg_for(*this,ptr,T) (*ptr) = cimg::abs(*ptr); return *this; } CImg get_abs() const { return CImg(*this,false).abs(); } //! Compute the cosinus of each pixel value. CImg& cos() { cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::cos((double)(*ptr)); return *this; } CImg get_cos() const { return CImg(*this,false).cos(); } //! Compute the sinus of each pixel value. CImg& sin() { cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::sin((double)(*ptr)); return *this; } CImg get_sin() const { return CImg(*this,false).sin(); } //! Compute the tangent of each pixel. CImg& tan() { cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::tan((double)(*ptr)); return *this; } CImg get_tan() const { return CImg(*this,false).tan(); } //! Compute the arc-cosine of each pixel value. CImg& acos() { cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::acos((double)(*ptr)); return *this; } CImg get_acos() const { return CImg(*this,false).acos(); } //! Compute the arc-sinus of each pixel value. CImg& asin() { cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::asin((double)(*ptr)); return *this; } CImg get_asin() const { return CImg(*this,false).asin(); } //! Compute the arc-tangent of each pixel. CImg& atan() { cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::atan((double)(*ptr)); return *this; } CImg get_atan() const { return CImg(*this,false).atan(); } //! Compute image with rounded pixel values. /** \param x Rounding precision. \param rounding_type Roundin type, can be 0 (nearest), 1 (forward), -1(backward). **/ CImg& round(const float x, const int rounding_type=0) { cimg_for(*this,ptr,T) (*ptr) = (T)cimg::round(*ptr,x,rounding_type); return *this; } CImg get_round(const float x, const unsigned int rounding_type=0) const { return (+*this).round(x,rounding_type); } //! Fill the instance image with random values between specified range. CImg& rand(const T val_min, const T val_max) { const float delta = (float)val_max - (float)val_min; cimg_for(*this,ptr,T) *ptr = (T)(val_min + cimg::rand()*delta); return *this; } CImg get_rand(const T val_min, const T val_max) const { return (+*this).rand(val_min,val_max); } //@} //----------------------------------- // //! \name Usual Image Transformations //@{ //----------------------------------- //! Fill an image by a value \p val. /** \param val = fill value \note All pixel values of the instance image will be initialized by \p val. **/ CImg& fill(const T val) { if (is_empty()) return *this; if (val && sizeof(T)!=1) cimg_for(*this,ptr,T) *ptr = val; else cimg_std::memset(data,(int)val,size()*sizeof(T)); return *this; } CImg get_fill(const T val) const { return CImg(width,height,depth,dim).fill(val); } //! Fill sequentially all pixel values with values \a val0 and \a val1 respectively. CImg& fill(const T val0, const T val1) { if (is_empty()) return *this; T *ptr, *ptr_end = end()-1; for (ptr = data; ptr get_fill(const T val0, const T val1) const { return CImg(width,height,depth,dim).fill(val0,val1); } //! Fill sequentially all pixel values with values \a val0 and \a val1 and \a val2. CImg& fill(const T val0, const T val1, const T val2) { if (is_empty()) return *this; T *ptr, *ptr_end = end()-2; for (ptr = data; ptr get_fill(const T val0, const T val1, const T val2) const { return CImg(width,height,depth,dim).fill(val0,val1,val2); } //! Fill sequentially all pixel values with values \a val0 and \a val1 and \a val2 and \a val3. CImg& fill(const T val0, const T val1, const T val2, const T val3) { if (is_empty()) return *this; T *ptr, *ptr_end = end()-3; for (ptr = data; ptr get_fill(const T val0, const T val1, const T val2, const T val3) const { return CImg(width,height,depth,dim).fill(val0,val1,val2,val3); } //! Fill sequentially all pixel values with values \a val0 and \a val1 and \a val2 and \a val3 and \a val4. CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4) { if (is_empty()) return *this; T *ptr, *ptr_end = end()-4; for (ptr = data; ptr get_fill(const T val0, const T val1, const T val2, const T val3, const T val4) const { return CImg(width,height,depth,dim).fill(val0,val1,val2,val3,val4); } //! Fill sequentially all pixel values with values \a val0 and \a val1 and \a val2 and \a val3 and \a val4 and \a val5. CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5) { if (is_empty()) return *this; T *ptr, *ptr_end = end()-5; for (ptr = data; ptr get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5) const { return CImg(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5); } //! Fill sequentially pixel values. CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6) { if (is_empty()) return *this; T *ptr, *ptr_end = end()-6; for (ptr = data; ptr get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6) const { return CImg(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6); } //! Fill sequentially pixel values. CImg& 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) { if (is_empty()) return *this; T *ptr, *ptr_end = end()-7; for (ptr = data; ptr 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 { return CImg(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7); } //! Fill sequentially pixel values. CImg& 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) { if (is_empty()) return *this; T *ptr, *ptr_end = end()-8; for (ptr = data; ptr 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 { return CImg(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8); } //! Fill sequentially pixel values. CImg& 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) { if (is_empty()) return *this; T *ptr, *ptr_end = end()-9; for (ptr = data; ptr 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 { return CImg(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9); } //! Fill sequentially pixel values. CImg& 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) { if (is_empty()) return *this; T *ptr, *ptr_end = end()-10; for (ptr = data; ptr 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 { return CImg(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10); } //! Fill sequentially pixel values. CImg& 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) { if (is_empty()) return *this; T *ptr, *ptr_end = end()-11; for (ptr = data; ptr 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 { return CImg(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11); } //! Fill sequentially pixel values. CImg& 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) { if (is_empty()) return *this; T *ptr, *ptr_end = end()-12; for (ptr = data; ptr 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 { return CImg(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12); } //! Fill sequentially pixel values. CImg& 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) { if (is_empty()) return *this; T *ptr, *ptr_end = end()-13; for (ptr = data; ptr 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 { return CImg(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12, val13); } //! Fill sequentially pixel values. CImg& 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) { if (is_empty()) return *this; T *ptr, *ptr_end = end()-14; for (ptr = data; ptr 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 { return CImg(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12, val13,val14); } //! Fill sequentially pixel values. CImg& 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) { if (is_empty()) return *this; T *ptr, *ptr_end = end()-15; for (ptr = data; ptr 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 { return CImg(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12, val13,val14,val15); } //! Fill image values according to the values found in the specified string. CImg& fill(const char *const values, const bool repeat_pattern) { if (is_empty() || !values) return *this; T *ptrd = data, *ptr_end = data + size(); const char *nvalues = values; const unsigned int siz = size(); char cval[64] = { 0 }, sep = 0; int err = 0; double val = 0; unsigned int nb = 0; while ((err=cimg_std::sscanf(nvalues,"%63[ \n\t0-9e.+-]%c",cval,&sep))>0 && cimg_std::sscanf(cval,"%lf",&val)>0 && nb get_fill(const char *const values, const bool repeat_pattern) const { return repeat_pattern?CImg(width,height,depth,dim).fill(values,repeat_pattern):(+*this).fill(values,repeat_pattern); } //! Fill image values according to the values found in the specified image. template CImg& fill(const CImg& values, const bool repeat_pattern=true) { if (is_empty() || !values) return *this; T *ptrd = data, *ptrd_end = ptrd + size(); for (t *ptrs = values.data, *ptrs_end = ptrs + values.size(); ptrs CImg get_fill(const CImg& values, const bool repeat_pattern=true) const { return repeat_pattern?CImg(width,height,depth,dim).fill(values,repeat_pattern):(+*this).fill(values,repeat_pattern); } //! Fill image values along the X-axis at the specified pixel position (y,z,v). CImg& fillX(const unsigned int y, const unsigned int z, const unsigned int v, const int a0, ...) { #define _cimg_fill1(x,y,z,v,off,siz,t) { \ va_list ap; va_start(ap,a0); T *ptrd = ptr(x,y,z,v); *ptrd = (T)a0; \ for (unsigned int k = 1; k& fillX(const unsigned int y, const unsigned int z, const unsigned int v, const double a0, ...) { if (y& fillY(const unsigned int x, const unsigned int z, const unsigned int v, const int a0, ...) { if (x& fillY(const unsigned int x, const unsigned int z, const unsigned int v, const double a0, ...) { if (x& fillZ(const unsigned int x, const unsigned int y, const unsigned int v, const int a0, ...) { const unsigned int wh = width*height; if (x& fillZ(const unsigned int x, const unsigned int y, const unsigned int v, const double a0, ...) { const unsigned int wh = width*height; if (x& fillV(const unsigned int x, const unsigned int y, const unsigned int z, const int a0, ...) { const unsigned int whz = width*height*depth; if (x& fillV(const unsigned int x, const unsigned int y, const unsigned int z, const double a0, ...) { const unsigned int whz = width*height*depth; if (x& normalize(const T a, const T b) { if (is_empty()) return *this; const T na = a get_normalize(const T a, const T b) const { return (+*this).normalize(a,b); } //! Cut pixel values between \a a and \a b. CImg& cut(const T a, const T b) { if (is_empty()) return *this; const T na = anb)?nb:*ptr); return *this; } CImg get_cut(const T a, const T b) const { return (+*this).cut(a,b); } //! Quantize pixel values into \n levels. CImg& quantize(const unsigned int n, const bool keep_range=true) { if (is_empty()) return *this; if (!n) throw CImgArgumentException("CImg<%s>::quantize() : Cannot quantize image to 0 values.", pixel_type()); Tfloat m, M = (Tfloat)maxmin(m), range = M - m; if (range>0) { if (keep_range) cimg_for(*this,ptr,T) { const unsigned int val = (unsigned int)((*ptr-m)*n/range); *ptr = (T)(m + cimg::min(val,n-1)*range/n); } else cimg_for(*this,ptr,T) { const unsigned int val = (unsigned int)((*ptr-m)*n/range); *ptr = (T)cimg::min(val,n-1); } } return *this; } CImg get_quantize(const unsigned int n, const bool keep_range=true) const { return (+*this).quantize(n,keep_range); } //! Threshold the image. /** \param value Threshold value. \param soft Enable soft thresholding. \param strict Tells if the threshold is strict. **/ CImg& threshold(const T value, const bool soft=false, const bool strict=false) { if (is_empty()) return *this; if (strict) { if (soft) cimg_for(*this,ptr,T) { const T v = *ptr; *ptr = v>value?(T)(v-value):v<-value?(T)(v+value):(T)0; } else cimg_for(*this,ptr,T) *ptr = *ptr>value?(T)1:(T)0; } else { if (soft) cimg_for(*this,ptr,T) { const T v = *ptr; *ptr = v>=value?(T)(v-value):v<=-value?(T)(v+value):(T)0; } else cimg_for(*this,ptr,T) *ptr = *ptr>=value?(T)1:(T)0; } return *this; } CImg get_threshold(const T value, const bool soft=false, const bool strict=false) const { return (+*this).threshold(value,soft,strict); } //! Rotate an image. /** \param angle = rotation angle (in degrees). \param cond = rotation type. can be : - 0 = zero-value at borders - 1 = nearest pixel. - 2 = Fourier style. \note Returned image will probably have a different size than the instance image *this. **/ CImg& rotate(const float angle, const unsigned int border_conditions=3, const unsigned int interpolation=1) { return get_rotate(angle,border_conditions,interpolation).transfer_to(*this); } CImg get_rotate(const float angle, const unsigned int border_conditions=3, const unsigned int interpolation=1) const { if (is_empty()) return *this; CImg dest; const float nangle = cimg::mod(angle,360.0f); if (border_conditions!=1 && cimg::mod(nangle,90.0f)==0) { // optimized version for orthogonal angles const int wm1 = dimx()-1, hm1 = dimy()-1; const int iangle = (int)nangle/90; switch (iangle) { case 1 : { dest.assign(height,width,depth,dim); cimg_forXYZV(dest,x,y,z,v) dest(x,y,z,v) = (*this)(y,hm1-x,z,v); } break; case 2 : { dest.assign(width,height,depth,dim); cimg_forXYZV(dest,x,y,z,v) dest(x,y,z,v) = (*this)(wm1-x,hm1-y,z,v); } break; case 3 : { dest.assign(height,width,depth,dim); cimg_forXYZV(dest,x,y,z,v) dest(x,y,z,v) = (*this)(wm1-y,x,z,v); } break; default : return *this; } } else { // generic version const float rad = (float)(nangle*cimg::valuePI/180.0), ca = (float)cimg_std::cos(rad), sa = (float)cimg_std::sin(rad), ux = cimg::abs(width*ca), uy = cimg::abs(width*sa), vx = cimg::abs(height*sa), vy = cimg::abs(height*ca), w2 = 0.5f*width, h2 = 0.5f*height, dw2 = 0.5f*(ux+vx), dh2 = 0.5f*(uy+vy); dest.assign((int)(ux+vx), (int)(uy+vy),depth,dim); switch (border_conditions) { case 0 : { switch (interpolation) { case 2 : { cimg_forXY(dest,x,y) cimg_forZV(*this,z,v) dest(x,y,z,v) = (T)cubic_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,v,0); } break; case 1 : { cimg_forXY(dest,x,y) cimg_forZV(*this,z,v) dest(x,y,z,v) = (T)linear_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,v,0); } break; default : { cimg_forXY(dest,x,y) cimg_forZV(*this,z,v) dest(x,y,z,v) = atXY((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),(int)(h2 - (x-dw2)*sa + (y-dh2)*ca),z,v,0); } } } break; case 1 : { switch (interpolation) { case 2 : cimg_forXY(dest,x,y) cimg_forZV(*this,z,v) dest(x,y,z,v) = (T)cubic_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,v); break; case 1 : cimg_forXY(dest,x,y) cimg_forZV(*this,z,v) dest(x,y,z,v) = (T)linear_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,v); break; default : cimg_forXY(dest,x,y) cimg_forZV(*this,z,v) dest(x,y,z,v) = atXY((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),(int)(h2 - (x-dw2)*sa + (y-dh2)*ca),z,v); } } break; case 2 : { switch (interpolation) { case 2 : cimg_forXY(dest,x,y) cimg_forZV(*this,z,v) dest(x,y,z,v) = (T)cubic_atXY(cimg::mod(w2 + (x-dw2)*ca + (y-dh2)*sa,(float)dimx()), cimg::mod(h2 - (x-dw2)*sa + (y-dh2)*ca,(float)dimy()),z,v); break; case 1 : cimg_forXY(dest,x,y) cimg_forZV(*this,z,v) dest(x,y,z,v) = (T)linear_atXY(cimg::mod(w2 + (x-dw2)*ca + (y-dh2)*sa,(float)dimx()), cimg::mod(h2 - (x-dw2)*sa + (y-dh2)*ca,(float)dimy()),z,v); break; default : cimg_forXY(dest,x,y) cimg_forZV(*this,z,v) dest(x,y,z,v) = (*this)(cimg::mod((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),dimx()), cimg::mod((int)(h2 - (x-dw2)*sa + (y-dh2)*ca),dimy()),z,v); } } break; default : throw CImgArgumentException("CImg<%s>::get_rotate() : Invalid border conditions %d (should be 0,1 or 2).", pixel_type(),border_conditions); } } return dest; } //! Rotate an image around a center point (\c cx,\c cy). /** \param angle = rotation angle (in degrees). \param cx = X-coordinate of the rotation center. \param cy = Y-coordinate of the rotation center. \param zoom = zoom. \param cond = rotation type. can be : - 0 = zero-value at borders - 1 = repeat image at borders - 2 = zero-value at borders and linear interpolation **/ CImg& rotate(const float angle, const float cx, const float cy, const float zoom, const unsigned int border_conditions=3, const unsigned int interpolation=1) { return get_rotate(angle,cx,cy,zoom,border_conditions,interpolation).transfer_to(*this); } CImg get_rotate(const float angle, const float cx, const float cy, const float zoom, const unsigned int border_conditions=3, const unsigned int interpolation=1) const { if (interpolation>2) throw CImgArgumentException("CImg<%s>::get_rotate() : Invalid interpolation parameter %d (should be {0=none, 1=linear or 2=cubic}).", pixel_type(),interpolation); if (is_empty()) return *this; CImg dest(width,height,depth,dim); const float nangle = cimg::mod(angle,360.0f); if (border_conditions!=1 && zoom==1 && cimg::mod(nangle,90.0f)==0) { // optimized version for orthogonal angles const int iangle = (int)nangle/90; switch (iangle) { case 1 : { dest.fill(0); const unsigned int xmin = cimg::max(0,(dimx()-dimy())/2), xmax = cimg::min(width,xmin+height), ymin = cimg::max(0,(dimy()-dimx())/2), ymax = cimg::min(height,ymin+width), xoff = xmin + cimg::min(0,(dimx()-dimy())/2), yoff = ymin + cimg::min(0,(dimy()-dimx())/2); cimg_forZV(dest,z,v) for (unsigned int y = ymin; y::get_rotate() : Incorrect border conditions %d (should be 0,1 or 2).", pixel_type(),border_conditions); } } return dest; } //! Resize an image. /** \param pdx Number of columns (new size along the X-axis). \param pdy Number of rows (new size along the Y-axis). \param pdz Number of slices (new size along the Z-axis). \param pdv Number of vector-channels (new size along the V-axis). \param interpolation_type Method of interpolation : - -1 = no interpolation : raw memory resizing. - 0 = no interpolation : additional space is filled according to \p border_condition. - 1 = bloc interpolation (nearest point). - 2 = moving average interpolation. - 3 = linear interpolation. - 4 = grid interpolation. - 5 = bi-cubic interpolation. \param border_condition Border condition type. \param center Set centering type (only if \p interpolation_type=0). \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100). **/ CImg& resize(const int pdx, const int pdy=-100, const int pdz=-100, const int pdv=-100, const int interpolation_type=1, const int border_condition=-1, const bool center=false) { if (!pdx || !pdy || !pdz || !pdv) return assign(); const unsigned int tdx = pdx<0?-pdx*width/100:pdx, tdy = pdy<0?-pdy*height/100:pdy, tdz = pdz<0?-pdz*depth/100:pdz, tdv = pdv<0?-pdv*dim/100:pdv, dx = tdx?tdx:1, dy = tdy?tdy:1, dz = tdz?tdz:1, dv = tdv?tdv:1; if (width==dx && height==dy && depth==dz && dim==dv) return *this; if (interpolation_type==-1 && dx*dy*dz*dv==size()) { width = dx; height = dy; depth = dz; dim = dv; return *this; } return get_resize(dx,dy,dz,dv,interpolation_type,border_condition,center).transfer_to(*this); } CImg get_resize(const int pdx, const int pdy=-100, const int pdz=-100, const int pdv=-100, const int interpolation_type=1, const int border_condition=-1, const bool center=false) const { if (!pdx || !pdy || !pdz || !pdv) return CImg(); const unsigned int tdx = pdx<0?-pdx*width/100:pdx, tdy = pdy<0?-pdy*height/100:pdy, tdz = pdz<0?-pdz*depth/100:pdz, tdv = pdv<0?-pdv*dim/100:pdv, dx = tdx?tdx:1, dy = tdy?tdy:1, dz = tdz?tdz:1, dv = tdv?tdv:1; if (width==dx && height==dy && depth==dz && dim==dv) return +*this; if (is_empty()) return CImg(dx,dy,dz,dv,0); CImg res; switch (interpolation_type) { case -1 : // Raw resizing cimg_std::memcpy(res.assign(dx,dy,dz,dv,0).data,data,sizeof(T)*cimg::min(size(),(long unsigned int)dx*dy*dz*dv)); break; case 0 : { // No interpolation const unsigned int bx = width-1, by = height-1, bz = depth-1, bv = dim-1; res.assign(dx,dy,dz,dv); switch (border_condition) { case 1 : { if (center) { const int x0 = (res.dimx()-dimx())/2, y0 = (res.dimy()-dimy())/2, z0 = (res.dimz()-dimz())/2, v0 = (res.dimv()-dimv())/2, x1 = x0 + (int)bx, y1 = y0 + (int)by, z1 = z0 + (int)bz, v1 = v0 + (int)bv; res.draw_image(x0,y0,z0,v0,*this); cimg_for_outXYZV(res,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) res(x,y,z,v) = _atXYZV(x-x0,y-y0,z-z0,v-v0); } else { res.draw_image(*this); cimg_for_outXYZV(res,0,0,0,0,bx,by,bz,bv,x,y,z,v) res(x,y,z,v) = _atXYZV(x,y,z,v); } } break; case 2 : { int nx0 = 0, ny0 = 0, nz0 = 0, nv0 = 0; if (center) { const int x0 = (res.dimx()-dimx())/2, y0 = (res.dimy()-dimy())/2, z0 = (res.dimz()-dimz())/2, v0 = (res.dimv()-dimv())/2; nx0 = x0>0?x0-(1+x0/width)*width:x0; ny0 = y0>0?y0-(1+y0/height)*height:y0; nz0 = z0>0?z0-(1+z0/depth)*depth:z0; nv0 = v0>0?v0-(1+v0/dim)*dim:v0; } for (int k = nv0; k<(int)dv; k+=dimv()) for (int z = nz0; z<(int)dz; z+=dimz()) for (int y = ny0; y<(int)dy; y+=dimy()) for (int x = nx0; x<(int)dx; x+=dimx()) res.draw_image(x,y,z,k,*this); } break; default : { res.fill(0); if (center) res.draw_image((res.dimx()-dimx())/2,(res.dimy()-dimy())/2,(res.dimz()-dimz())/2,(res.dimv()-dimv())/2,*this); else res.draw_image(*this); } } } break; case 1 : { // Nearest-neighbor interpolation res.assign(dx,dy,dz,dv); unsigned int *const offx = new unsigned int[dx], *const offy = new unsigned int[dy+1], *const offz = new unsigned int[dz+1], *const offv = new unsigned int[dv+1], *poffx, *poffy, *poffz, *poffv, curr, old; const unsigned int wh = width*height, whd = width*height*depth, rwh = dx*dy, rwhd = dx*dy*dz; poffx = offx; curr = 0; { cimg_forX(res,x) { old=curr; curr=(x+1)*width/dx; *(poffx++) = (unsigned int)curr-(unsigned int)old; }} poffy = offy; curr = 0; { cimg_forY(res,y) { old=curr; curr=(y+1)*height/dy; *(poffy++) = width*((unsigned int)curr-(unsigned int)old); }} *poffy=0; poffz = offz; curr = 0; { cimg_forZ(res,z) { old=curr; curr=(z+1)*depth/dz; *(poffz++) = wh*((unsigned int)curr-(unsigned int)old); }} *poffz=0; poffv = offv; curr = 0; { cimg_forV(res,k) { old=curr; curr=(k+1)*dim/dv; *(poffv++) = whd*((unsigned int)curr-(unsigned int)old); }} *poffv=0; T *ptrd = res.data; const T* ptrv = data; poffv = offv; for (unsigned int k=0; k tmp(dx,height,depth,dim,0); for (unsigned int a = width*dx, b = width, c = dx, s = 0, t = 0; a; ) { const unsigned int d = cimg::min(b,c); a-=d; b-=d; c-=d; cimg_forYZV(tmp,y,z,v) tmp(t,y,z,v)+=(Tfloat)(*this)(s,y,z,v)*d; if (!b) { cimg_forYZV(tmp,y,z,v) tmp(t,y,z,v)/=width; ++t; b = width; } if (!c) { ++s; c = dx; } } tmp.transfer_to(res); instance_first = false; } if (dy!=height) { CImg tmp(dx,dy,depth,dim,0); for (unsigned int a = height*dy, b = height, c = dy, s = 0, t = 0; a; ) { const unsigned int d = cimg::min(b,c); a-=d; b-=d; c-=d; if (instance_first) cimg_forXZV(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)(*this)(x,s,z,v)*d; else cimg_forXZV(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)res(x,s,z,v)*d; if (!b) { cimg_forXZV(tmp,x,z,v) tmp(x,t,z,v)/=height; ++t; b = height; } if (!c) { ++s; c = dy; } } tmp.transfer_to(res); instance_first = false; } if (dz!=depth) { CImg tmp(dx,dy,dz,dim,0); for (unsigned int a = depth*dz, b = depth, c = dz, s = 0, t = 0; a; ) { const unsigned int d = cimg::min(b,c); a-=d; b-=d; c-=d; if (instance_first) cimg_forXYV(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)(*this)(x,y,s,v)*d; else cimg_forXYV(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)res(x,y,s,v)*d; if (!b) { cimg_forXYV(tmp,x,y,v) tmp(x,y,t,v)/=depth; ++t; b = depth; } if (!c) { ++s; c = dz; } } tmp.transfer_to(res); instance_first = false; } if (dv!=dim) { CImg tmp(dx,dy,dz,dv,0); for (unsigned int a = dim*dv, b = dim, c = dv, s = 0, t = 0; a; ) { const unsigned int d = cimg::min(b,c); a-=d; b-=d; c-=d; if (instance_first) cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(Tfloat)(*this)(x,y,z,s)*d; else cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(Tfloat)res(x,y,z,s)*d; if (!b) { cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)/=dim; ++t; b = dim; } if (!c) { ++s; c = dv; } } tmp.transfer_to(res); instance_first = false; } } break; case 3 : { // Linear interpolation const unsigned int dimmax = cimg::max(dx,dy,dz,dv); const float sx = (border_condition<0 && dx>width )?(dx>1?(width-1.0f)/(dx-1) :0):(float)width/dx, sy = (border_condition<0 && dy>height)?(dy>1?(height-1.0f)/(dy-1):0):(float)height/dy, sz = (border_condition<0 && dz>depth )?(dz>1?(depth-1.0f)/(dz-1) :0):(float)depth/dz, sv = (border_condition<0 && dv>dim )?(dv>1?(dim-1.0f)/(dv-1) :0):(float)dim/dv; unsigned int *const off = new unsigned int[dimmax], *poff; float *const foff = new float[dimmax], *pfoff, old, curr; CImg resx, resy, resz, resv; T *ptrd; if (dx!=width) { if (width==1) resx = get_resize(dx,height,depth,dim,1,0); else { resx.assign(dx,height,depth,dim); curr = old = 0; poff = off; pfoff = foff; cimg_forX(resx,x) { *(pfoff++) = curr-(unsigned int)curr; old = curr; curr+=sx; *(poff++) = (unsigned int)curr-(unsigned int)old; } ptrd = resx.data; const T *ptrs0 = data; cimg_forYZV(resx,y,z,k) { poff = off; pfoff = foff; const T *ptrs = ptrs0, *const ptrsmax = ptrs0 + (width-1); cimg_forX(resx,x) { const float alpha = *(pfoff++); const T val1 = *ptrs, val2 = ptrswidth )?(dx>1?(width-1.0f)/(dx-1) :0):(float)width/dx, sy = (border_condition<0 && dy>height)?(dy>1?(height-1.0f)/(dy-1):0):(float)height/dy, sz = (border_condition<0 && dz>depth )?(dz>1?(depth-1.0f)/(dz-1) :0):(float)depth/dz, sv = (border_condition<0 && dv>dim )?(dv>1?(dim-1.0f)/(dv-1) :0):(float)dim/dv; res.assign(dx,dy,dz,dv); T *ptrd = res.ptr(); float cx, cy, cz, ck = 0; cimg_forV(res,k) { cz = 0; cimg_forZ(res,z) { cy = 0; cimg_forY(res,y) { cx = 0; cimg_forX(res,x) { *(ptrd++) = (T)(border_condition?_cubic_atXY(cx,cy,(int)cz,(int)ck):cubic_atXY(cx,cy,(int)cz,(int)ck,0)); cx+=sx; } cy+=sy; } cz+=sz; } ck+=sv; } } break; default : // Invalid interpolation method throw CImgArgumentException("CImg<%s>::resize() : Invalid interpolation_type %d " "(should be { -1=raw, 0=zero, 1=nearest, 2=average, 3=linear, 4=grid, 5=bicubic}).", pixel_type(),interpolation_type); } return res; } //! Resize an image. /** \param src Image giving the geometry of the resize. \param interpolation_type Interpolation method : - 1 = raw memory - 0 = no interpolation : additional space is filled with 0. - 1 = bloc interpolation (nearest point). - 2 = mosaic : image is repeated if necessary. - 3 = linear interpolation. - 4 = grid interpolation. - 5 = bi-cubic interpolation. \param border_condition Border condition type. \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100). **/ template CImg& resize(const CImg& src, const int interpolation_type=1, const int border_condition=-1, const bool center=false) { return resize(src.width,src.height,src.depth,src.dim,interpolation_type,border_condition,center); } template CImg get_resize(const CImg& src, const int interpolation_type=1, const int border_condition=-1, const bool center=false) const { return get_resize(src.width,src.height,src.depth,src.dim,interpolation_type,border_condition,center); } //! Resize an image. /** \param disp = Display giving the geometry of the resize. \param interpolation_type = Resizing type : - 0 = no interpolation : additional space is filled with 0. - 1 = bloc interpolation (nearest point). - 2 = mosaic : image is repeated if necessary. - 3 = linear interpolation. - 4 = grid interpolation. - 5 = bi-cubic interpolation. - 6 = moving average (best quality for photographs) \param border_condition Border condition type. \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100). **/ CImg& resize(const CImgDisplay& disp, const int interpolation_type=1, const int border_condition=-1, const bool center=false) { return resize(disp.width,disp.height,depth,dim,interpolation_type,border_condition,center); } CImg get_resize(const CImgDisplay& disp, const int interpolation_type=1, const int border_condition=-1, const bool center=false) const { return get_resize(disp.width,disp.height,depth,dim,interpolation_type,border_condition,center); } //! Half-resize an image, using a special optimized filter. CImg& resize_halfXY() { return get_resize_halfXY().transfer_to(*this); } CImg get_resize_halfXY() const { if (is_empty()) return *this; const Tfloat mask[9] = { 0.07842776544f, 0.1231940459f, 0.07842776544f, 0.1231940459f, 0.1935127547f, 0.1231940459f, 0.07842776544f, 0.1231940459f, 0.07842776544f }; T I[9] = { 0 }; CImg dest(width/2,height/2,depth,dim); cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,z,k,I) if (x%2 && y%2) dest(x/2,y/2,z,k) = (T) (I[0]*mask[0] + I[1]*mask[1] + I[2]*mask[2] + I[3]*mask[3] + I[4]*mask[4] + I[5]*mask[5] + I[6]*mask[6] + I[7]*mask[7] + I[8]*mask[8]); return dest; } //! Upscale an image by a factor 2x. /** Use anisotropic upscaling algorithm described at http://scale2x.sourceforge.net/algorithm.html **/ CImg& resize_doubleXY() { return get_resize_doubleXY().transfer_to(*this); } CImg get_resize_doubleXY() const { #define _cimg_gs2x_for3(bound,i) \ for (int i = 0, _p1##i = 0, \ _n1##i = 1>=(bound)?(int)(bound)-1:1; \ _n1##i<(int)(bound) || i==--_n1##i; \ _p1##i = i++, ++_n1##i, ptrd1+=(res).width, ptrd2+=(res).width) #define _cimg_gs2x_for3x3(img,x,y,z,v,I) \ _cimg_gs2x_for3((img).height,y) for (int x = 0, \ _p1##x = 0, \ _n1##x = (int)( \ (I[1] = (img)(0,_p1##y,z,v)), \ (I[3] = I[4] = (img)(0,y,z,v)), \ (I[7] = (img)(0,_n1##y,z,v)), \ 1>=(img).width?(int)((img).width)-1:1); \ (_n1##x<(int)((img).width) && ( \ (I[2] = (img)(_n1##x,_p1##y,z,v)), \ (I[5] = (img)(_n1##x,y,z,v)), \ (I[8] = (img)(_n1##x,_n1##y,z,v)),1)) || \ x==--_n1##x; \ I[1] = I[2], \ I[3] = I[4], I[4] = I[5], \ I[7] = I[8], \ _p1##x = x++, ++_n1##x) if (is_empty()) return *this; CImg res(2*width,2*height,depth,dim); CImg_3x3(I,T); cimg_forZV(*this,z,k) { T *ptrd1 = res.ptr(0,0,0,k), *ptrd2 = ptrd1 + res.width; _cimg_gs2x_for3x3(*this,x,y,0,k,I) { if (Icp!=Icn && Ipc!=Inc) { *(ptrd1++) = Ipc==Icp?Ipc:Icc; *(ptrd1++) = Icp==Inc?Inc:Icc; *(ptrd2++) = Ipc==Icn?Ipc:Icc; *(ptrd2++) = Icn==Inc?Inc:Icc; } else { *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc; } } } return res; } //! Upscale an image by a factor 3x. /** Use anisotropic upscaling algorithm described at http://scale2x.sourceforge.net/algorithm.html **/ CImg& resize_tripleXY() { return get_resize_tripleXY().transfer_to(*this); } CImg get_resize_tripleXY() const { #define _cimg_gs3x_for3(bound,i) \ for (int i = 0, _p1##i = 0, \ _n1##i = 1>=(bound)?(int)(bound)-1:1; \ _n1##i<(int)(bound) || i==--_n1##i; \ _p1##i = i++, ++_n1##i, ptrd1+=2*(res).width, ptrd2+=2*(res).width, ptrd3+=2*(res).width) #define _cimg_gs3x_for3x3(img,x,y,z,v,I) \ _cimg_gs3x_for3((img).height,y) for (int x = 0, \ _p1##x = 0, \ _n1##x = (int)( \ (I[0] = I[1] = (img)(0,_p1##y,z,v)), \ (I[3] = I[4] = (img)(0,y,z,v)), \ (I[6] = I[7] = (img)(0,_n1##y,z,v)), \ 1>=(img).width?(int)((img).width)-1:1); \ (_n1##x<(int)((img).width) && ( \ (I[2] = (img)(_n1##x,_p1##y,z,v)), \ (I[5] = (img)(_n1##x,y,z,v)), \ (I[8] = (img)(_n1##x,_n1##y,z,v)),1)) || \ x==--_n1##x; \ I[0] = I[1], I[1] = I[2], \ I[3] = I[4], I[4] = I[5], \ I[6] = I[7], I[7] = I[8], \ _p1##x = x++, ++_n1##x) if (is_empty()) return *this; CImg res(3*width,3*height,depth,dim); CImg_3x3(I,T); cimg_forZV(*this,z,k) { T *ptrd1 = res.ptr(0,0,0,k), *ptrd2 = ptrd1 + res.width, *ptrd3 = ptrd2 + res.width; _cimg_gs3x_for3x3(*this,x,y,0,k,I) { if (Icp != Icn && Ipc != Inc) { *(ptrd1++) = Ipc==Icp?Ipc:Icc; *(ptrd1++) = (Ipc==Icp && Icc!=Inp) || (Icp==Inc && Icc!=Ipp)?Icp:Icc; *(ptrd1++) = Icp==Inc?Inc:Icc; *(ptrd2++) = (Ipc==Icp && Icc!=Ipn) || (Ipc==Icn && Icc!=Ipp)?Ipc:Icc; *(ptrd2++) = Icc; *(ptrd2++) = (Icp==Inc && Icc!=Inn) || (Icn==Inc && Icc!=Inp)?Inc:Icc; *(ptrd3++) = Ipc==Icn?Ipc:Icc; *(ptrd3++) = (Ipc==Icn && Icc!=Inn) || (Icn==Inc && Icc!=Ipn)?Icn:Icc; *(ptrd3++) = Icn==Inc?Inc:Icc; } else { *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc; *(ptrd3++) = Icc; *(ptrd3++) = Icc; *(ptrd3++) = Icc; } } } return res; } // Warp an image. template CImg& warp(const CImg& warp, const bool relative=false, const bool interpolation=true, const unsigned int border_conditions=0) { return get_warp(warp,relative,interpolation,border_conditions).transfer_to(*this); } template CImg get_warp(const CImg& warp, const bool relative=false, const bool interpolation=true, const unsigned int border_conditions=0) const { if (is_empty() || !warp) return *this; if (!is_sameXYZ(warp)) throw CImgArgumentException("CImg<%s>::warp() : Instance image (%u,%u,%u,%u,%p) and warping field (%u,%u,%u,%u,%p) " "have different XYZ dimensions.", pixel_type(),width,height,depth,dim,data, warp.width,warp.height,warp.depth,warp.dim,warp.data); CImg res(width,height,depth,dim); switch (warp.dim) { case 1 : // 1D warping. if (relative) { // Relative warp coordinates if (interpolation) switch (border_conditions) { case 2 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (T)_linear_atX(cimg::mod(x-(float)warp(x,y,z,0),(float)width),y,z,v); } break; case 1 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (T)_linear_atX(x-(float)warp(x,y,z,0),y,z,v); } break; default : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (T)linear_atX(x-(float)warp(x,y,z,0),y,z,v,0); } } else switch (border_conditions) { case 2 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (*this)(cimg::mod(x-(int)warp(x,y,z,0),(int)width),y,z,v); } break; case 1 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = _atX(x-(int)warp(x,y,z,0),y,z,v); } break; default : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = atX(x-(int)warp(x,y,z,0),y,z,v,0); } } } else { // Absolute warp coordinates if (interpolation) switch (border_conditions) { case 2 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (T)_linear_atX(cimg::mod((float)warp(x,y,z,0),(float)width),y,z,v); } break; case 1 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (T)_linear_atX((float)warp(x,y,z,0),y,z,v); } break; default : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (T)linear_atX((float)warp(x,y,z,0),y,z,v,0); } } else switch (border_conditions) { case 2 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (*this)(cimg::mod((int)warp(x,y,z,0),(int)width),y,z,v); } break; case 1 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = _atX((int)warp(x,y,z,0),y,z,v); } break; default : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = atX((int)warp(x,y,z,0),y,z,v,0); } } } break; case 2 : // 2D warping if (relative) { // Relative warp coordinates if (interpolation) switch (border_conditions) { case 2 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (T)_linear_atXY(cimg::mod(x-(float)warp(x,y,z,0),(float)width), cimg::mod(y-(float)warp(x,y,z,1),(float)height),z,v); } break; case 1 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (T)_linear_atXY(x-(float)warp(x,y,z,0),y-(float)warp(x,y,z,1),z,v); } break; default : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (T)linear_atXY(x-(float)warp(x,y,z,0),y-(float)warp(x,y,z,1),z,v,0); } } else switch (border_conditions) { case 2 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (*this)(cimg::mod(x-(int)warp(x,y,z,0),(int)width), cimg::mod(y-(int)warp(x,y,z,1),(int)height),z,v); } break; case 1 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = _atXY(x-(int)warp(x,y,z,0),y-(int)warp(x,y,z,1),z,v); } break; default : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = atXY(x-(int)warp(x,y,z,0),y-(int)warp(x,y,z,1),z,v,0); } } } else { // Absolute warp coordinates if (interpolation) switch (border_conditions) { case 2 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (T)_linear_atXY(cimg::mod((float)warp(x,y,z,0),(float)width), cimg::mod((float)warp(x,y,z,1),(float)height),z,v); } break; case 1 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (T)_linear_atXY((float)warp(x,y,z,0),(float)warp(x,y,z,1),z,v); } break; default : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (T)linear_atXY((float)warp(x,y,z,0),(float)warp(x,y,z,1),z,v,0); } } else switch (border_conditions) { case 2 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (*this)(cimg::mod((int)warp(x,y,z,0),(int)width), cimg::mod((int)warp(x,y,z,1),(int)depth),z,v); } break; case 1 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = _atXY((int)warp(x,y,z,0),(int)warp(x,y,z,1),z,v); } break; default : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = atXY((int)warp(x,y,z,0),(int)warp(x,y,z,1),z,v,0); } } } break; case 3 : // 3D warping if (relative) { // Relative warp coordinates if (interpolation) switch (border_conditions) { case 2 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (T)_linear_atXYZ(cimg::mod(x-(float)warp(x,y,z,0),(float)width), cimg::mod(y-(float)warp(x,y,z,1),(float)height), cimg::mod(z-(float)warp(x,y,z,2),(float)depth),v); } break; case 1 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (T)_linear_atXYZ(x-(float)warp(x,y,z,0),y-(float)warp(x,y,z,1),z-(float)warp(x,y,z,2),v); } break; default : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (T)linear_atXYZ(x-(float)warp(x,y,z,0),y-(float)warp(x,y,z,1),z-(float)warp(x,y,z,2),v,0); } } else switch (border_conditions) { case 2 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (*this)(cimg::mod(x-(int)warp(x,y,z,0),(int)width), cimg::mod(y-(int)warp(x,y,z,1),(int)height), cimg::mod(z-(int)warp(x,y,z,2),(int)depth),v); } break; case 1 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = _atXYZ(x-(int)warp(x,y,z,0),y-(int)warp(x,y,z,1),z-(int)warp(x,y,z,2),v); } break; default : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = atXYZ(x-(int)warp(x,y,z,0),y-(int)warp(x,y,z,1),z-(int)warp(x,y,z,2),v,0); } } } else { // Absolute warp coordinates if (interpolation) switch (border_conditions) { case 2 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (T)_linear_atXYZ(cimg::mod((float)warp(x,y,z,0),(float)width), cimg::mod((float)warp(x,y,z,1),(float)height), cimg::mod((float)warp(x,y,z,2),(float)depth),v); } break; case 1 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (T)_linear_atXYZ((float)warp(x,y,z,0),(float)warp(x,y,z,1),(float)warp(x,y,z,2),v); } break; default : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (T)linear_atXYZ((float)warp(x,y,z,0),(float)warp(x,y,z,1),(float)warp(x,y,z,2),v,0); } } else switch (border_conditions) { case 2 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (*this)(cimg::mod((int)warp(x,y,z,0),(int)width), cimg::mod((int)warp(x,y,z,1),(int)height), cimg::mod((int)warp(x,y,z,2),(int)depth),v); } break; case 1 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = _atXYZ((int)warp(x,y,z,0),(int)warp(x,y,z,1),(int)warp(x,y,z,2),v); } break; default : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = atXYZ((int)warp(x,y,z,0),(int)warp(x,y,z,1),(int)warp(x,y,z,2),v,0); } } } break; default : // 4D warping if (relative) { // Relative warp coordinates if (interpolation) switch (border_conditions) { case 2 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (T)_linear_atXYZV(cimg::mod(x-(float)warp(x,y,z,0),(float)width), cimg::mod(y-(float)warp(x,y,z,1),(float)height), cimg::mod(z-(float)warp(x,y,z,2),(float)depth), cimg::mod(z-(float)warp(x,y,z,3),(float)dim)); } break; case 1 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (T)_linear_atXYZV(x-(float)warp(x,y,z,0),y-(float)warp(x,y,z,1),z-(float)warp(x,y,z,2),v-(float)warp(x,y,z,3)); } break; default : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (T)linear_atXYZV(x-(float)warp(x,y,z,0),y-(float)warp(x,y,z,1),z-(float)warp(x,y,z,2),v-(float)warp(x,y,z,3),0); } } else switch (border_conditions) { case 2 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (*this)(cimg::mod(x-(int)warp(x,y,z,0),(int)width), cimg::mod(y-(int)warp(x,y,z,1),(int)height), cimg::mod(z-(int)warp(x,y,z,2),(int)depth), cimg::mod(v-(int)warp(x,y,z,3),(int)dim)); } break; case 1 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = _atXYZV(x-(int)warp(x,y,z,0),y-(int)warp(x,y,z,1),z-(int)warp(x,y,z,2),v-(int)warp(x,y,z,3)); } break; default : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = atXYZ(x-(int)warp(x,y,z,0),y-(int)warp(x,y,z,1),z-(int)warp(x,y,z,2),v-(int)warp(x,y,z,3),0); } } } else { // Absolute warp coordinates if (interpolation) switch (border_conditions) { case 2 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (T)_linear_atXYZV(cimg::mod((float)warp(x,y,z,0),(float)width), cimg::mod((float)warp(x,y,z,1),(float)height), cimg::mod((float)warp(x,y,z,2),(float)depth), cimg::mod((float)warp(x,y,z,3),(float)dim)); } break; case 1 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (T)_linear_atXYZV((float)warp(x,y,z,0),(float)warp(x,y,z,1),(float)warp(x,y,z,2),(float)warp(x,y,z,3)); } break; default : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (T)linear_atXYZV((float)warp(x,y,z,0),(float)warp(x,y,z,1),(float)warp(x,y,z,2),(float)warp(x,y,z,3),0); } } else switch (border_conditions) { case 2 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = (*this)(cimg::mod((int)warp(x,y,z,0),(int)width), cimg::mod((int)warp(x,y,z,1),(int)height), cimg::mod((int)warp(x,y,z,2),(int)depth), cimg::mod((int)warp(x,y,z,3),(int)dim)); } break; case 1 : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = _atXYZV((int)warp(x,y,z,0),(int)warp(x,y,z,1),(int)warp(x,y,z,2),(int)warp(x,y,z,3)); } break; default : { cimg_forXYZV(*this,x,y,z,v) res(x,y,z,v) = atXYZV((int)warp(x,y,z,0),(int)warp(x,y,z,1),(int)warp(x,y,z,2),(int)warp(x,y,z,3),0); } } } } return res; } // Permute axes order (internal). template CImg _get_permute_axes(const char *permut, const t&) const { if (is_empty() || !permut) return CImg(*this,false); CImg res; const T* ptrs = data; if (!cimg::strncasecmp(permut,"xyzv",4)) return (+*this); if (!cimg::strncasecmp(permut,"xyvz",4)) { res.assign(width,height,dim,depth); cimg_forXYZV(*this,x,y,z,v) res(x,y,v,z) = (t)*(ptrs++); } if (!cimg::strncasecmp(permut,"xzyv",4)) { res.assign(width,depth,height,dim); cimg_forXYZV(*this,x,y,z,v) res(x,z,y,v) = (t)*(ptrs++); } if (!cimg::strncasecmp(permut,"xzvy",4)) { res.assign(width,depth,dim,height); cimg_forXYZV(*this,x,y,z,v) res(x,z,v,y) = (t)*(ptrs++); } if (!cimg::strncasecmp(permut,"xvyz",4)) { res.assign(width,dim,height,depth); cimg_forXYZV(*this,x,y,z,v) res(x,v,y,z) = (t)*(ptrs++); } if (!cimg::strncasecmp(permut,"xvzy",4)) { res.assign(width,dim,depth,height); cimg_forXYZV(*this,x,y,z,v) res(x,v,z,y) = (t)*(ptrs++); } if (!cimg::strncasecmp(permut,"yxzv",4)) { res.assign(height,width,depth,dim); cimg_forXYZV(*this,x,y,z,v) res(y,x,z,v) = (t)*(ptrs++); } if (!cimg::strncasecmp(permut,"yxvz",4)) { res.assign(height,width,dim,depth); cimg_forXYZV(*this,x,y,z,v) res(y,x,v,z) = (t)*(ptrs++); } if (!cimg::strncasecmp(permut,"yzxv",4)) { res.assign(height,depth,width,dim); cimg_forXYZV(*this,x,y,z,v) res(y,z,x,v) = (t)*(ptrs++); } if (!cimg::strncasecmp(permut,"yzvx",4)) { res.assign(height,depth,dim,width); switch (width) { case 1 : { t *ptrR = res.ptr(0,0,0,0); for (unsigned long siz = height*depth*dim; siz; --siz) { *(ptrR++) = (t)*(ptrs++); } } break; case 2 : { t *ptrR = res.ptr(0,0,0,0), *ptrG = res.ptr(0,0,0,1); for (unsigned long siz = height*depth*dim; siz; --siz) { *(ptrR++) = (t)*(ptrs++); *(ptrG++) = (t)*(ptrs++); } } break; case 3 : { // Optimization for the classical conversion from interleaved RGB to planar RGB t *ptrR = res.ptr(0,0,0,0), *ptrG = res.ptr(0,0,0,1), *ptrB = res.ptr(0,0,0,2); for (unsigned long siz = height*depth*dim; siz; --siz) { *(ptrR++) = (t)*(ptrs++); *(ptrG++) = (t)*(ptrs++); *(ptrB++) = (t)*(ptrs++); } } break; case 4 : { // Optimization for the classical conversion from interleaved RGBA to planar RGBA t *ptrR = res.ptr(0,0,0,0), *ptrG = res.ptr(0,0,0,1), *ptrB = res.ptr(0,0,0,2), *ptrA = res.ptr(0,0,0,3); for (unsigned long siz = height*depth*dim; siz; --siz) { *(ptrR++) = (t)*(ptrs++); *(ptrG++) = (t)*(ptrs++); *(ptrB++) = (t)*(ptrs++); *(ptrA++) = (t)*(ptrs++); } } break; default : { cimg_forXYZV(*this,x,y,z,v) res(y,z,v,x) = *(ptrs++); return res; } } } if (!cimg::strncasecmp(permut,"yvxz",4)) { res.assign(height,dim,width,depth); cimg_forXYZV(*this,x,y,z,v) res(y,v,x,z) = (t)*(ptrs++); } if (!cimg::strncasecmp(permut,"yvzx",4)) { res.assign(height,dim,depth,width); cimg_forXYZV(*this,x,y,z,v) res(y,v,z,x) = (t)*(ptrs++); } if (!cimg::strncasecmp(permut,"zxyv",4)) { res.assign(depth,width,height,dim); cimg_forXYZV(*this,x,y,z,v) res(z,x,y,v) = (t)*(ptrs++); } if (!cimg::strncasecmp(permut,"zxvy",4)) { res.assign(depth,width,dim,height); cimg_forXYZV(*this,x,y,z,v) res(z,x,v,y) = (t)*(ptrs++); } if (!cimg::strncasecmp(permut,"zyxv",4)) { res.assign(depth,height,width,dim); cimg_forXYZV(*this,x,y,z,v) res(z,y,x,v) = (t)*(ptrs++); } if (!cimg::strncasecmp(permut,"zyvx",4)) { res.assign(depth,height,dim,width); cimg_forXYZV(*this,x,y,z,v) res(z,y,v,x) = (t)*(ptrs++); } if (!cimg::strncasecmp(permut,"zvxy",4)) { res.assign(depth,dim,width,height); cimg_forXYZV(*this,x,y,z,v) res(z,v,x,y) = (t)*(ptrs++); } if (!cimg::strncasecmp(permut,"zvyx",4)) { res.assign(depth,dim,height,width); cimg_forXYZV(*this,x,y,z,v) res(z,v,y,x) = (t)*(ptrs++); } if (!cimg::strncasecmp(permut,"vxyz",4)) { res.assign(dim,width,height,depth); switch (dim) { case 1 : { const T *ptrR = ptr(0,0,0,0); t *ptrd = res.ptr(); for (unsigned long siz = width*height*depth; siz; --siz) { *(ptrd++) = (t)*(ptrR++); } } break; case 2 : { const T *ptrR = ptr(0,0,0,0), *ptrG = ptr(0,0,0,1); t *ptrd = res.ptr(); for (unsigned long siz = width*height*depth; siz; --siz) { *(ptrd++) = (t)*(ptrR++); *(ptrd++) = (t)*(ptrG++); } } break; case 3 : { // Optimization for the classical conversion from planar RGB to interleaved RGB const T *ptrR = ptr(0,0,0,0), *ptrG = ptr(0,0,0,1), *ptrB = ptr(0,0,0,2); t *ptrd = res.ptr(); for (unsigned long siz = width*height*depth; siz; --siz) { *(ptrd++) = (t)*(ptrR++); *(ptrd++) = (t)*(ptrG++); *(ptrd++) = (t)*(ptrB++); } } break; case 4 : { // Optimization for the classical conversion from planar RGBA to interleaved RGBA const T *ptrR = ptr(0,0,0,0), *ptrG = ptr(0,0,0,1), *ptrB = ptr(0,0,0,2), *ptrA = ptr(0,0,0,3); t *ptrd = res.ptr(); for (unsigned long siz = width*height*depth; siz; --siz) { *(ptrd++) = (t)*(ptrR++); *(ptrd++) = (t)*(ptrG++); *(ptrd++) = (t)*(ptrB++); *(ptrd++) = (t)*(ptrA++); } } break; default : { cimg_forXYZV(*this,x,y,z,v) res(v,x,y,z) = (t)*(ptrs++); } } } if (!cimg::strncasecmp(permut,"vxzy",4)) { res.assign(dim,width,depth,height); cimg_forXYZV(*this,x,y,z,v) res(v,x,z,y) = (t)*(ptrs++); } if (!cimg::strncasecmp(permut,"vyxz",4)) { res.assign(dim,height,width,depth); cimg_forXYZV(*this,x,y,z,v) res(v,y,x,z) = (t)*(ptrs++); } if (!cimg::strncasecmp(permut,"vyzx",4)) { res.assign(dim,height,depth,width); cimg_forXYZV(*this,x,y,z,v) res(v,y,z,x) = (t)*(ptrs++); } if (!cimg::strncasecmp(permut,"vzxy",4)) { res.assign(dim,depth,width,height); cimg_forXYZV(*this,x,y,z,v) res(v,z,x,y) = (t)*(ptrs++); } if (!cimg::strncasecmp(permut,"vzyx",4)) { res.assign(dim,depth,height,width); cimg_forXYZV(*this,x,y,z,v) res(v,z,y,x) = (t)*(ptrs++); } if (!res) throw CImgArgumentException("CImg<%s>::permute_axes() : Invalid input permutation '%s'.", pixel_type(),permut); return res; } //! Permute axes order. /** This function permutes image axes. \param permut = String describing the permutation (4 characters). **/ CImg& permute_axes(const char *order) { return get_permute_axes(order).transfer_to(*this); } CImg get_permute_axes(const char *order) const { const T foo = (T)0; return _get_permute_axes(order,foo); } //! Invert endianness. CImg& invert_endianness() { cimg::invert_endianness(data,size()); return *this; } CImg get_invert_endianness() const { return (+*this).invert_endianness(); } //! Mirror an image along the specified axis. CImg& mirror(const char axis) { if (is_empty()) return *this; T *pf, *pb, *buf = 0; switch (cimg::uncase(axis)) { case 'x' : { pf = data; pb = ptr(width-1); const unsigned int width2 = width/2; for (unsigned int yzv = 0; yzv::mirror() : unknow axis '%c', must be 'x','y','z' or 'v'.", pixel_type(),axis); } if (buf) delete[] buf; return *this; } CImg get_mirror(const char axis) const { return (+*this).mirror(axis); } //! Translate the image. /** \param deltax Amount of displacement along the X-axis. \param deltay Amount of displacement along the Y-axis. \param deltaz Amount of displacement along the Z-axis. \param deltav Amount of displacement along the V-axis. \param border_condition Border condition. - \c border_condition can be : - 0 : Zero border condition (Dirichlet). - 1 : Nearest neighbors (Neumann). - 2 : Repeat Pattern (Fourier style). **/ CImg& translate(const int deltax, const int deltay=0, const int deltaz=0, const int deltav=0, const int border_condition=0) { if (is_empty()) return *this; if (deltax) // Translate along X-axis switch (border_condition) { case 0 : if (cimg::abs(deltax)>=dimx()) return fill(0); if (deltax>0) cimg_forYZV(*this,y,z,k) { cimg_std::memmove(ptr(0,y,z,k),ptr(deltax,y,z,k),(width-deltax)*sizeof(T)); cimg_std::memset(ptr(width-deltax,y,z,k),0,deltax*sizeof(T)); } else cimg_forYZV(*this,y,z,k) { cimg_std::memmove(ptr(-deltax,y,z,k),ptr(0,y,z,k),(width+deltax)*sizeof(T)); cimg_std::memset(ptr(0,y,z,k),0,-deltax*sizeof(T)); } break; case 1 : if (deltax>0) { const int ndeltax = (deltax>=dimx())?width-1:deltax; if (!ndeltax) return *this; cimg_forYZV(*this,y,z,k) { cimg_std::memmove(ptr(0,y,z,k),ptr(ndeltax,y,z,k),(width-ndeltax)*sizeof(T)); T *ptrd = ptr(width-1,y,z,k); const T val = *ptrd; for (int l = 0; l=dimx())?width-1:-deltax; if (!ndeltax) return *this; cimg_forYZV(*this,y,z,k) { cimg_std::memmove(ptr(ndeltax,y,z,k),ptr(0,y,z,k),(width-ndeltax)*sizeof(T)); T *ptrd = ptr(0,y,z,k); const T val = *ptrd; for (int l = 0; l0) cimg_forYZV(*this,y,z,k) { cimg_std::memcpy(buf,ptr(0,y,z,k),ndeltax*sizeof(T)); cimg_std::memmove(ptr(0,y,z,k),ptr(ndeltax,y,z,k),(width-ndeltax)*sizeof(T)); cimg_std::memcpy(ptr(width-ndeltax,y,z,k),buf,ndeltax*sizeof(T)); } else cimg_forYZV(*this,y,z,k) { cimg_std::memcpy(buf,ptr(width+ndeltax,y,z,k),-ndeltax*sizeof(T)); cimg_std::memmove(ptr(-ndeltax,y,z,k),ptr(0,y,z,k),(width+ndeltax)*sizeof(T)); cimg_std::memcpy(ptr(0,y,z,k),buf,-ndeltax*sizeof(T)); } delete[] buf; } break; } if (deltay) // Translate along Y-axis switch (border_condition) { case 0 : if (cimg::abs(deltay)>=dimy()) return fill(0); if (deltay>0) cimg_forZV(*this,z,k) { cimg_std::memmove(ptr(0,0,z,k),ptr(0,deltay,z,k),width*(height-deltay)*sizeof(T)); cimg_std::memset(ptr(0,height-deltay,z,k),0,width*deltay*sizeof(T)); } else cimg_forZV(*this,z,k) { cimg_std::memmove(ptr(0,-deltay,z,k),ptr(0,0,z,k),width*(height+deltay)*sizeof(T)); cimg_std::memset(ptr(0,0,z,k),0,-deltay*width*sizeof(T)); } break; case 1 : if (deltay>0) { const int ndeltay = (deltay>=dimy())?height-1:deltay; if (!ndeltay) return *this; cimg_forZV(*this,z,k) { cimg_std::memmove(ptr(0,0,z,k),ptr(0,ndeltay,z,k),width*(height-ndeltay)*sizeof(T)); T *ptrd = ptr(0,height-ndeltay,z,k), *ptrs = ptr(0,height-1,z,k); for (int l = 0; l=dimy())?height-1:-deltay; if (!ndeltay) return *this; cimg_forZV(*this,z,k) { cimg_std::memmove(ptr(0,ndeltay,z,k),ptr(0,0,z,k),width*(height-ndeltay)*sizeof(T)); T *ptrd = ptr(0,1,z,k), *ptrs = ptr(0,0,z,k); for (int l = 0; l0) cimg_forZV(*this,z,k) { cimg_std::memcpy(buf,ptr(0,0,z,k),width*ndeltay*sizeof(T)); cimg_std::memmove(ptr(0,0,z,k),ptr(0,ndeltay,z,k),width*(height-ndeltay)*sizeof(T)); cimg_std::memcpy(ptr(0,height-ndeltay,z,k),buf,width*ndeltay*sizeof(T)); } else cimg_forZV(*this,z,k) { cimg_std::memcpy(buf,ptr(0,height+ndeltay,z,k),-ndeltay*width*sizeof(T)); cimg_std::memmove(ptr(0,-ndeltay,z,k),ptr(0,0,z,k),width*(height+ndeltay)*sizeof(T)); cimg_std::memcpy(ptr(0,0,z,k),buf,-ndeltay*width*sizeof(T)); } delete[] buf; } break; } if (deltaz) // Translate along Z-axis switch (border_condition) { case 0 : if (cimg::abs(deltaz)>=dimz()) return fill(0); if (deltaz>0) cimg_forV(*this,k) { cimg_std::memmove(ptr(0,0,0,k),ptr(0,0,deltaz,k),width*height*(depth-deltaz)*sizeof(T)); cimg_std::memset(ptr(0,0,depth-deltaz,k),0,width*height*deltaz*sizeof(T)); } else cimg_forV(*this,k) { cimg_std::memmove(ptr(0,0,-deltaz,k),ptr(0,0,0,k),width*height*(depth+deltaz)*sizeof(T)); cimg_std::memset(ptr(0,0,0,k),0,-deltaz*width*height*sizeof(T)); } break; case 1 : if (deltaz>0) { const int ndeltaz = (deltaz>=dimz())?depth-1:deltaz; if (!ndeltaz) return *this; cimg_forV(*this,k) { cimg_std::memmove(ptr(0,0,0,k),ptr(0,0,ndeltaz,k),width*height*(depth-ndeltaz)*sizeof(T)); T *ptrd = ptr(0,0,depth-ndeltaz,k), *ptrs = ptr(0,0,depth-1,k); for (int l = 0; l=dimz())?depth-1:-deltaz; if (!ndeltaz) return *this; cimg_forV(*this,k) { cimg_std::memmove(ptr(0,0,ndeltaz,k),ptr(0,0,0,k),width*height*(depth-ndeltaz)*sizeof(T)); T *ptrd = ptr(0,0,1,k), *ptrs = ptr(0,0,0,k); for (int l = 0; l0) cimg_forV(*this,k) { cimg_std::memcpy(buf,ptr(0,0,0,k),width*height*ndeltaz*sizeof(T)); cimg_std::memmove(ptr(0,0,0,k),ptr(0,0,ndeltaz,k),width*height*(depth-ndeltaz)*sizeof(T)); cimg_std::memcpy(ptr(0,0,depth-ndeltaz,k),buf,width*height*ndeltaz*sizeof(T)); } else cimg_forV(*this,k) { cimg_std::memcpy(buf,ptr(0,0,depth+ndeltaz,k),-ndeltaz*width*height*sizeof(T)); cimg_std::memmove(ptr(0,0,-ndeltaz,k),ptr(0,0,0,k),width*height*(depth+ndeltaz)*sizeof(T)); cimg_std::memcpy(ptr(0,0,0,k),buf,-ndeltaz*width*height*sizeof(T)); } delete[] buf; } break; } if (deltav) // Translate along V-axis switch (border_condition) { case 0 : if (cimg::abs(deltav)>=dimv()) return fill(0); if (deltav>0) { cimg_std::memmove(data,ptr(0,0,0,deltav),width*height*depth*(dim-deltav)*sizeof(T)); cimg_std::memset(ptr(0,0,0,dim-deltav),0,width*height*depth*deltav*sizeof(T)); } else cimg_forV(*this,k) { cimg_std::memmove(ptr(0,0,0,-deltav),data,width*height*depth*(dim+deltav)*sizeof(T)); cimg_std::memset(data,0,-deltav*width*height*depth*sizeof(T)); } break; case 1 : if (deltav>0) { const int ndeltav = (deltav>=dimv())?dim-1:deltav; if (!ndeltav) return *this; cimg_std::memmove(data,ptr(0,0,0,ndeltav),width*height*depth*(dim-ndeltav)*sizeof(T)); T *ptrd = ptr(0,0,0,dim-ndeltav), *ptrs = ptr(0,0,0,dim-1); for (int l = 0; l=dimv())?dim-1:-deltav; if (!ndeltav) return *this; cimg_std::memmove(ptr(0,0,0,ndeltav),data,width*height*depth*(dim-ndeltav)*sizeof(T)); T *ptrd = ptr(0,0,0,1); for (int l = 0; l0) { cimg_std::memcpy(buf,data,width*height*depth*ndeltav*sizeof(T)); cimg_std::memmove(data,ptr(0,0,0,ndeltav),width*height*depth*(dim-ndeltav)*sizeof(T)); cimg_std::memcpy(ptr(0,0,0,dim-ndeltav),buf,width*height*depth*ndeltav*sizeof(T)); } else { cimg_std::memcpy(buf,ptr(0,0,0,dim+ndeltav),-ndeltav*width*height*depth*sizeof(T)); cimg_std::memmove(ptr(0,0,0,-ndeltav),data,width*height*depth*(dim+ndeltav)*sizeof(T)); cimg_std::memcpy(data,buf,-ndeltav*width*height*depth*sizeof(T)); } delete[] buf; } break; } return *this; } CImg get_translate(const int deltax, const int deltay=0, const int deltaz=0, const int deltav=0, const int border_condition=0) const { return (+*this).translate(deltax,deltay,deltaz,deltav,border_condition); } //! Get a square region of the image. /** \param x0 = X-coordinate of the upper-left crop rectangle corner. \param y0 = Y-coordinate of the upper-left crop rectangle corner. \param z0 = Z-coordinate of the upper-left crop rectangle corner. \param v0 = V-coordinate of the upper-left crop rectangle corner. \param x1 = X-coordinate of the lower-right crop rectangle corner. \param y1 = Y-coordinate of the lower-right crop rectangle corner. \param z1 = Z-coordinate of the lower-right crop rectangle corner. \param v1 = V-coordinate of the lower-right crop rectangle corner. \param border_condition = Dirichlet (false) or Neumann border conditions. **/ CImg& crop(const int x0, const int y0, const int z0, const int v0, const int x1, const int y1, const int z1, const int v1, const bool border_condition=false) { return get_crop(x0,y0,z0,v0,x1,y1,z1,v1,border_condition).transfer_to(*this); } CImg get_crop(const int x0, const int y0, const int z0, const int v0, const int x1, const int y1, const int z1, const int v1, const bool border_condition=false) const { if (is_empty()) return *this; const int nx0 = x0 dest(1U+nx1-nx0,1U+ny1-ny0,1U+nz1-nz0,1U+nv1-nv0); if (nx0<0 || nx1>=dimx() || ny0<0 || ny1>=dimy() || nz0<0 || nz1>=dimz() || nv0<0 || nv1>=dimv()) { if (border_condition) cimg_forXYZV(dest,x,y,z,v) dest(x,y,z,v) = _atXYZV(nx0+x,ny0+y,nz0+z,nv0+v); else dest.fill(0).draw_image(-nx0,-ny0,-nz0,-nv0,*this); } else dest.draw_image(-nx0,-ny0,-nz0,-nv0,*this); return dest; } //! Get a rectangular part of the instance image. /** \param x0 = X-coordinate of the upper-left crop rectangle corner. \param y0 = Y-coordinate of the upper-left crop rectangle corner. \param z0 = Z-coordinate of the upper-left crop rectangle corner. \param x1 = X-coordinate of the lower-right crop rectangle corner. \param y1 = Y-coordinate of the lower-right crop rectangle corner. \param z1 = Z-coordinate of the lower-right crop rectangle corner. \param border_condition = determine the type of border condition if some of the desired region is outside the image. **/ CImg& crop(const int x0, const int y0, const int z0, const int x1, const int y1, const int z1, const bool border_condition=false) { return crop(x0,y0,z0,0,x1,y1,z1,dim-1,border_condition); } CImg get_crop(const int x0, const int y0, const int z0, const int x1, const int y1, const int z1, const bool border_condition=false) const { return get_crop(x0,y0,z0,0,x1,y1,z1,dim-1,border_condition); } //! Get a rectangular part of the instance image. /** \param x0 = X-coordinate of the upper-left crop rectangle corner. \param y0 = Y-coordinate of the upper-left crop rectangle corner. \param x1 = X-coordinate of the lower-right crop rectangle corner. \param y1 = Y-coordinate of the lower-right crop rectangle corner. \param border_condition = determine the type of border condition if some of the desired region is outside the image. **/ CImg& crop(const int x0, const int y0, const int x1, const int y1, const bool border_condition=false) { return crop(x0,y0,0,0,x1,y1,depth-1,dim-1,border_condition); } CImg get_crop(const int x0, const int y0, const int x1, const int y1, const bool border_condition=false) const { return get_crop(x0,y0,0,0,x1,y1,depth-1,dim-1,border_condition); } //! Get a rectangular part of the instance image. /** \param x0 = X-coordinate of the upper-left crop rectangle corner. \param x1 = X-coordinate of the lower-right crop rectangle corner. \param border_condition = determine the type of border condition if some of the desired region is outside the image. **/ CImg& crop(const int x0, const int x1, const bool border_condition=false) { return crop(x0,0,0,0,x1,height-1,depth-1,dim-1,border_condition); } CImg get_crop(const int x0, const int x1, const bool border_condition=false) const { return get_crop(x0,0,0,0,x1,height-1,depth-1,dim-1,border_condition); } //! Autocrop an image, regarding of the specified backround value. CImg& autocrop(const T value, const char *const axes="vzyx") { if (is_empty()) return *this; const int lmax = cimg::strlen(axes); for (int l = 0; l get_autocrop(const T value, const char *const axes="vzyx") const { return (+*this).autocrop(value,axes); } //! Autocrop an image, regarding of the specified backround color. CImg& autocrop(const T *const color, const char *const axes="zyx") { if (is_empty()) return *this; const int lmax = cimg::strlen(axes); for (int l = 0; l get_autocrop(const T *const color, const char *const axes="zyx") const { return (+*this).autocrop(color,axes); } //! Autocrop an image, regarding of the specified backround color. template CImg& autocrop(const CImg& color, const char *const axes="zyx") { return get_autocrop(color,axes).transfer_to(*this); } template CImg get_autocrop(const CImg& color, const char *const axes="zyx") const { return get_autocrop(color.data,axes); } //! Autocrop an image along specified axis, regarding of the specified backround value. CImg& autocrop(const T value, const char axis) { return get_autocrop(value,axis).transfer_to(*this); } CImg get_autocrop(const T value, const char axis) const { if (is_empty()) return *this; CImg res; const CImg coords = _get_autocrop(value,axis); switch (cimg::uncase(axis)) { case 'x' : { const int x0 = coords[0], x1 = coords[1]; if (x0>=0 && x1>=0) res = get_crop(x0,x1); } break; case 'y' : { const int y0 = coords[0], y1 = coords[1]; if (y0>=0 && y1>=0) res = get_crop(0,y0,width-1,y1); } break; case 'z' : { const int z0 = coords[0], z1 = coords[1]; if (z0>=0 && z1>=0) res = get_crop(0,0,z0,width-1,height-1,z1); } break; case 'v' : { const int v0 = coords[0], v1 = coords[1]; if (v0>=0 && v1>=0) res = get_crop(0,0,0,v0,width-1,height-1,depth-1,v1); } break; } return res; } //! Autocrop an image along specified axis, regarding of the specified backround color. CImg& autocrop(const T *const color, const char axis) { return get_autocrop(color,axis).transfer_to(*this); } CImg get_autocrop(const T *const color, const char axis) const { if (is_empty()) return *this; CImg res; switch (cimg::uncase(axis)) { case 'x' : { int x0 = width, x1 = -1; cimg_forV(*this,k) { const CImg coords = get_shared_channel(k)._get_autocrop(color[k],axis); const int nx0 = coords[0], nx1 = coords[1]; if (nx0>=0 && nx1>=0) { x0 = cimg::min(x0,nx0); x1 = cimg::max(x1,nx1); } } if (x0<=x1) res = get_crop(x0,x1); } break; case 'y' : { int y0 = height, y1 = -1; cimg_forV(*this,k) { const CImg coords = get_shared_channel(k)._get_autocrop(color[k],axis); const int ny0 = coords[0], ny1 = coords[1]; if (ny0>=0 && ny1>=0) { y0 = cimg::min(y0,ny0); y1 = cimg::max(y1,ny1); } } if (y0<=y1) res = get_crop(0,y0,width-1,y1); } break; case 'z' : { int z0 = depth, z1 = -1; cimg_forV(*this,k) { const CImg coords = get_shared_channel(k)._get_autocrop(color[k],axis); const int nz0 = coords[0], nz1 = coords[1]; if (nz0>=0 && nz1>=0) { z0 = cimg::min(z0,nz0); z1 = cimg::max(z1,nz1); } } if (z0<=z1) res = get_crop(0,0,z0,width-1,height-1,z1); } break; default : throw CImgArgumentException("CImg<%s>::autocrop() : Invalid axis '%c', must be 'x','y' or 'z'.", pixel_type(),axis); } return res; } //! Autocrop an image along specified axis, regarding of the specified backround color. template CImg& autocrop(const CImg& color, const char axis) { return get_autocrop(color,axis).transfer_to(*this); } template CImg get_autocrop(const CImg& color, const char axis) const { return get_autocrop(color.data,axis); } CImg _get_autocrop(const T value, const char axis) const { CImg res; int x0 = -1, y0 = -1, z0 = -1, v0 = -1, x1 = -1, y1 = -1, z1 = -1, v1 = -1; switch (cimg::uncase(axis)) { case 'x' : { cimg_forX(*this,x) cimg_forYZV(*this,y,z,v) if ((*this)(x,y,z,v)!=value) { x0 = x; x = dimx(); y = dimy(); z = dimz(); v = dimv(); } if (x0>=0) { for (int x = dimx()-1; x>=0; --x) cimg_forYZV(*this,y,z,v) if ((*this)(x,y,z,v)!=value) { x1 = x; x = 0; y = dimy(); z = dimz(); v = dimv(); } } res = CImg::vector(x0,x1); } break; case 'y' : { cimg_forY(*this,y) cimg_forXZV(*this,x,z,v) if ((*this)(x,y,z,v)!=value) { y0 = y; x = dimx(); y = dimy(); z = dimz(); v = dimv(); } if (y0>=0) { for (int y = dimy()-1; y>=0; --y) cimg_forXZV(*this,x,z,v) if ((*this)(x,y,z,v)!=value) { y1 = y; x = dimx(); y = 0; z = dimz(); v = dimv(); } } res = CImg::vector(y0,y1); } break; case 'z' : { cimg_forZ(*this,z) cimg_forXYV(*this,x,y,v) if ((*this)(x,y,z,v)!=value) { z0 = z; x = dimx(); y = dimy(); z = dimz(); v = dimv(); } if (z0>=0) { for (int z = dimz()-1; z>=0; --z) cimg_forXYV(*this,x,y,v) if ((*this)(x,y,z,v)!=value) { z1 = z; x = dimx(); y = dimy(); z = 0; v = dimv(); } } res = CImg::vector(z0,z1); } break; case 'v' : { cimg_forV(*this,v) cimg_forXYZ(*this,x,y,z) if ((*this)(x,y,z,v)!=value) { v0 = v; x = dimx(); y = dimy(); z = dimz(); v = dimv(); } if (v0>=0) { for (int v = dimv()-1; v>=0; --v) cimg_forXYZ(*this,x,y,z) if ((*this)(x,y,z,v)!=value) { v1 = v; x = dimx(); y = dimy(); z = dimz(); v = 0; } } res = CImg::vector(v0,v1); } break; default : throw CImgArgumentException("CImg<%s>::autocrop() : unknow axis '%c', must be 'x','y','z' or 'v'", pixel_type(),axis); } return res; } //! Get a set of columns. CImg& columns(const unsigned int x0, const unsigned int x1) { return get_columns(x0,x1).transfer_to(*this); } CImg get_columns(const unsigned int x0, const unsigned int x1) const { return get_crop((int)x0,0,0,0,(int)x1,dimy()-1,dimz()-1,dimv()-1); } //! Get one column. CImg& column(const unsigned int x0) { return columns(x0,x0); } CImg get_column(const unsigned int x0) const { return get_columns(x0,x0); } //! Get a set of lines. CImg& lines(const unsigned int y0, const unsigned int y1) { return get_lines(y0,y1).transfer_to(*this); } CImg get_lines(const unsigned int y0, const unsigned int y1) const { return get_crop(0,(int)y0,0,0,dimx()-1,(int)y1,dimz()-1,dimv()-1); } //! Get a line. CImg& line(const unsigned int y0) { return lines(y0,y0); } CImg get_line(const unsigned int y0) const { return get_lines(y0,y0); } //! Get a set of slices. CImg& slices(const unsigned int z0, const unsigned int z1) { return get_slices(z0,z1).transfer_to(*this); } CImg get_slices(const unsigned int z0, const unsigned int z1) const { return get_crop(0,0,(int)z0,0,dimx()-1,dimy()-1,(int)z1,dimv()-1); } //! Get a slice. CImg& slice(const unsigned int z0) { return slices(z0,z0); } CImg get_slice(const unsigned int z0) const { return get_slices(z0,z0); } //! Get a set of channels. CImg& channels(const unsigned int v0, const unsigned int v1) { return get_channels(v0,v1).transfer_to(*this); } CImg get_channels(const unsigned int v0, const unsigned int v1) const { return get_crop(0,0,0,(int)v0,dimx()-1,dimy()-1,dimz()-1,(int)v1); } //! Get a channel. CImg& channel(const unsigned int v0) { return channels(v0,v0); } CImg get_channel(const unsigned int v0) const { return get_channels(v0,v0); } //! Get a shared-memory image referencing a set of points of the instance image. CImg get_shared_points(const unsigned int x0, const unsigned int x1, const unsigned int y0=0, const unsigned int z0=0, const unsigned int v0=0) { const unsigned long beg = offset(x0,y0,z0,v0), end = offset(x1,y0,z0,v0); if (beg>end || beg>=size() || end>=size()) throw CImgArgumentException("CImg<%s>::get_shared_points() : Cannot return a shared-memory subset (%u->%u,%u,%u,%u) from " "a (%u,%u,%u,%u) image.", pixel_type(),x0,x1,y0,z0,v0,width,height,depth,dim); return CImg(data+beg,x1-x0+1,1,1,1,true); } const CImg get_shared_points(const unsigned int x0, const unsigned int x1, const unsigned int y0=0, const unsigned int z0=0, const unsigned int v0=0) const { const unsigned long beg = offset(x0,y0,z0,v0), end = offset(x1,y0,z0,v0); if (beg>end || beg>=size() || end>=size()) throw CImgArgumentException("CImg<%s>::get_shared_points() : Cannot return a shared-memory subset (%u->%u,%u,%u,%u) from " "a (%u,%u,%u,%u) image.", pixel_type(),x0,x1,y0,z0,v0,width,height,depth,dim); return CImg(data+beg,x1-x0+1,1,1,1,true); } //! Return a shared-memory image referencing a set of lines of the instance image. CImg get_shared_lines(const unsigned int y0, const unsigned int y1, const unsigned int z0=0, const unsigned int v0=0) { const unsigned long beg = offset(0,y0,z0,v0), end = offset(0,y1,z0,v0); if (beg>end || beg>=size() || end>=size()) throw CImgArgumentException("CImg<%s>::get_shared_lines() : Cannot return a shared-memory subset (0->%u,%u->%u,%u,%u) from " "a (%u,%u,%u,%u) image.", pixel_type(),width-1,y0,y1,z0,v0,width,height,depth,dim); return CImg(data+beg,width,y1-y0+1,1,1,true); } const CImg get_shared_lines(const unsigned int y0, const unsigned int y1, const unsigned int z0=0, const unsigned int v0=0) const { const unsigned long beg = offset(0,y0,z0,v0), end = offset(0,y1,z0,v0); if (beg>end || beg>=size() || end>=size()) throw CImgArgumentException("CImg<%s>::get_shared_lines() : Cannot return a shared-memory subset (0->%u,%u->%u,%u,%u) from " "a (%u,%u,%u,%u) image.", pixel_type(),width-1,y0,y1,z0,v0,width,height,depth,dim); return CImg(data+beg,width,y1-y0+1,1,1,true); } //! Return a shared-memory image referencing one particular line (y0,z0,v0) of the instance image. CImg get_shared_line(const unsigned int y0, const unsigned int z0=0, const unsigned int v0=0) { return get_shared_lines(y0,y0,z0,v0); } const CImg get_shared_line(const unsigned int y0, const unsigned int z0=0, const unsigned int v0=0) const { return get_shared_lines(y0,y0,z0,v0); } //! Return a shared memory image referencing a set of planes (z0->z1,v0) of the instance image. CImg get_shared_planes(const unsigned int z0, const unsigned int z1, const unsigned int v0=0) { const unsigned long beg = offset(0,0,z0,v0), end = offset(0,0,z1,v0); if (beg>end || beg>=size() || end>=size()) throw CImgArgumentException("CImg<%s>::get_shared_planes() : Cannot return a shared-memory subset (0->%u,0->%u,%u->%u,%u) from " "a (%u,%u,%u,%u) image.", pixel_type(),width-1,height-1,z0,z1,v0,width,height,depth,dim); return CImg(data+beg,width,height,z1-z0+1,1,true); } const CImg get_shared_planes(const unsigned int z0, const unsigned int z1, const unsigned int v0=0) const { const unsigned long beg = offset(0,0,z0,v0), end = offset(0,0,z1,v0); if (beg>end || beg>=size() || end>=size()) throw CImgArgumentException("CImg<%s>::get_shared_planes() : Cannot return a shared-memory subset (0->%u,0->%u,%u->%u,%u) from " "a (%u,%u,%u,%u) image.", pixel_type(),width-1,height-1,z0,z1,v0,width,height,depth,dim); return CImg(data+beg,width,height,z1-z0+1,1,true); } //! Return a shared-memory image referencing one plane (z0,v0) of the instance image. CImg get_shared_plane(const unsigned int z0, const unsigned int v0=0) { return get_shared_planes(z0,z0,v0); } const CImg get_shared_plane(const unsigned int z0, const unsigned int v0=0) const { return get_shared_planes(z0,z0,v0); } //! Return a shared-memory image referencing a set of channels (v0->v1) of the instance image. CImg get_shared_channels(const unsigned int v0, const unsigned int v1) { const unsigned long beg = offset(0,0,0,v0), end = offset(0,0,0,v1); if (beg>end || beg>=size() || end>=size()) throw CImgArgumentException("CImg<%s>::get_shared_channels() : Cannot return a shared-memory subset (0->%u,0->%u,0->%u,%u->%u) from " "a (%u,%u,%u,%u) image.", pixel_type(),width-1,height-1,depth-1,v0,v1,width,height,depth,dim); return CImg(data+beg,width,height,depth,v1-v0+1,true); } const CImg get_shared_channels(const unsigned int v0, const unsigned int v1) const { const unsigned long beg = offset(0,0,0,v0), end = offset(0,0,0,v1); if (beg>end || beg>=size() || end>=size()) throw CImgArgumentException("CImg<%s>::get_shared_channels() : Cannot return a shared-memory subset (0->%u,0->%u,0->%u,%u->%u) from " "a (%u,%u,%u,%u) image.", pixel_type(),width-1,height-1,depth-1,v0,v1,width,height,depth,dim); return CImg(data+beg,width,height,depth,v1-v0+1,true); } //! Return a shared-memory image referencing one channel v0 of the instance image. CImg get_shared_channel(const unsigned int v0) { return get_shared_channels(v0,v0); } const CImg get_shared_channel(const unsigned int v0) const { return get_shared_channels(v0,v0); } //! Return a shared version of the instance image. CImg get_shared() { return CImg(data,width,height,depth,dim,true); } const CImg get_shared() const { return CImg(data,width,height,depth,dim,true); } //! Return a 2D representation of a 3D image, with three slices. CImg& projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0, const int dx=-100, const int dy=-100, const int dz=-100) { return get_projections2d(x0,y0,z0,dx,dy,dz).transfer_to(*this); } CImg get_projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0, const int dx=-100, const int dy=-100, const int dz=-100) const { if (is_empty()) return *this; const unsigned int nx0 = (x0>=width)?width-1:x0, ny0 = (y0>=height)?height-1:y0, nz0 = (z0>=depth)?depth-1:z0; CImg imgxy(width,height,1,dim), imgzy(depth,height,1,dim), imgxz(width,depth,1,dim); { cimg_forXYV(*this,x,y,k) imgxy(x,y,k) = (*this)(x,y,nz0,k); } { cimg_forYZV(*this,y,z,k) imgzy(z,y,k) = (*this)(nx0,y,z,k); } { cimg_forXZV(*this,x,z,k) imgxz(x,z,k) = (*this)(x,ny0,z,k); } imgxy.resize(dx,dy,1,dim,1); imgzy.resize(dz,dy,1,dim,1); imgxz.resize(dx,dz,1,dim,1); return CImg(imgxy.width+imgzy.width,imgxy.height+imgxz.height,1,dim,0). draw_image(imgxy).draw_image(imgxy.width,imgzy).draw_image(0,imgxy.height,imgxz); } //! Compute the image histogram. /** The histogram H of an image I is a 1D-function where H(x) is the number of occurences of the value x in I. \param nblevels = Number of different levels of the computed histogram. For classical images, this value is 256. You should specify more levels if you are working with CImg or images with high range of pixel values. \param val_min = Minimum value considered for the histogram computation. All pixel values lower than val_min won't be counted. \param val_max = Maximum value considered for the histogram computation. All pixel values higher than val_max won't be counted. \note If val_min==val_max==0 (default values), the function first estimates the minimum and maximum pixel values of the current image, then uses these values for the histogram computation. \result The histogram is returned as a 1D CImg image H, having a size of (nblevels,1,1,1) such that H(0) and H(nblevels-1) are respectively equal to the number of occurences of the values val_min and val_max in I. \note Histogram computation always returns a 1D function. Histogram of multi-valued (such as color) images are not multi-dimensional. **/ CImg& histogram(const unsigned int nblevels, const T val_min=(T)0, const T val_max=(T)0) { return get_histogram(nblevels,val_min,val_max).transfer_to(*this); } CImg get_histogram(const unsigned int nblevels, const T val_min=(T)0, const T val_max=(T)0) const { if (is_empty()) return CImg(); if (!nblevels) throw CImgArgumentException("CImg<%s>::get_histogram() : Can't compute an histogram with 0 levels", pixel_type()); T vmin = val_min, vmax = val_max; CImg res(nblevels,1,1,1,0); if (vmin>=vmax && vmin==0) vmin = minmax(vmax); if (vmin=0 && pos<(int)nblevels) ++res[pos]; } else res[0]+=size(); return res; } //! Compute the histogram-equalized version of the instance image. /** The histogram equalization is a classical image processing algorithm that enhances the image contrast by expanding its histogram. \param nblevels = Number of different levels of the computed histogram. For classical images, this value is 256. You should specify more levels if you are working with CImg or images with high range of pixel values. \param val_min = Minimum value considered for the histogram computation. All pixel values lower than val_min won't be changed. \param val_max = Maximum value considered for the histogram computation. All pixel values higher than val_max won't be changed. \note If val_min==val_max==0 (default values), the function acts on all pixel values of the image. \return A new image with same size is returned, where pixels have been equalized. **/ CImg& equalize(const unsigned int nblevels, const T val_min=(T)0, const T val_max=(T)0) { if (is_empty()) return *this; T vmin = val_min, vmax = val_max; if (vmin==vmax && vmin==0) vmin = minmax(vmax); if (vmin hist = get_histogram(nblevels,vmin,vmax); float cumul = 0; cimg_forX(hist,pos) { cumul+=hist[pos]; hist[pos]=cumul; } cimg_for(*this,ptr,T) { const int pos = (unsigned int)((*ptr-vmin)*(nblevels-1)/(vmax-vmin)); if (pos>=0 && pos<(int)nblevels) *ptr = (T)(vmin + (vmax-vmin)*hist[pos]/size()); } } return *this; } CImg get_equalize(const unsigned int nblevels, const T val_min=(T)0, const T val_max=(T)0) const { return (+*this).equalize(nblevels,val_min,val_max); } //! Get a label map of disconnected regions with same intensities. CImg& label_regions() { return get_label_regions().transfer_to(*this); } CImg get_label_regions() const { #define _cimg_get_label_test(p,q) { \ flag = true; \ const T *ptr1 = ptr(x,y) + siz, *ptr2 = ptr(p,q) + siz; \ for (unsigned int i = dim; flag && i; --i) { ptr1-=wh; ptr2-=wh; flag = (*ptr1==*ptr2); } \ } if (depth>1) throw CImgInstanceException("CImg<%s>::label_regions() : Instance image must be a 2D image"); CImg res(width,height,depth,1,0); unsigned int label = 1; const unsigned int wh = width*height, siz = width*height*dim; const int W1 = dimx()-1, H1 = dimy()-1; bool flag; cimg_forXY(*this,x,y) { bool done = false; if (y) { _cimg_get_label_test(x,y-1); if (flag) { const unsigned int lab = (res(x,y) = res(x,y-1)); done = true; if (x && res(x-1,y)!=lab) { _cimg_get_label_test(x-1,y); if (flag) { const unsigned int lold = res(x-1,y), *const cptr = res.ptr(x,y); for (unsigned int *ptr = res.ptr(); ptr=0; --y) for (int x=W1; x>=0; --x) { bool done = false; if (ycptr; --ptr) if (*ptr==lold) *ptr = lab; } } } } if (x1), this function computes the L1,L2 or Linf norm of each vector-valued pixel. \param norm_type = Type of the norm being computed (1 = L1, 2 = L2, -1 = Linf). \return A scalar-valued image CImg with size (dimx(),dimy(),dimz(),1), where each pixel is the norm of the corresponding pixels in the original vector-valued image. **/ CImg& pointwise_norm(int norm_type=2) { return get_pointwise_norm(norm_type).transfer_to(*this); } CImg get_pointwise_norm(int norm_type=2) const { if (is_empty()) return *this; if (dim==1) return get_abs(); CImg res(width,height,depth); switch (norm_type) { case -1 : { // Linf norm cimg_forXYZ(*this,x,y,z) { Tfloat n = 0; cimg_forV(*this,v) { const Tfloat tmp = (Tfloat)cimg::abs((*this)(x,y,z,v)); if (tmp>n) n=tmp; res(x,y,z) = n; } } } break; case 1 : { // L1 norm cimg_forXYZ(*this,x,y,z) { Tfloat n = 0; cimg_forV(*this,v) n+=cimg::abs((*this)(x,y,z,v)); res(x,y,z) = n; } } break; default : { // L2 norm cimg_forXYZ(*this,x,y,z) { Tfloat n = 0; cimg_forV(*this,v) n+=(*this)(x,y,z,v)*(*this)(x,y,z,v); res(x,y,z) = (Tfloat)cimg_std::sqrt((double)n); } } } return res; } //! Compute the image of normalized vectors. /** When dealing with vector-valued images (i.e images with dimv()>1), this function return the image of normalized vectors (unit vectors). Null vectors are unchanged. The L2-norm is computed for the normalization. \return A new vector-valued image with same size, where each vector-valued pixels have been normalized. **/ CImg& pointwise_orientation() { cimg_forXYZ(*this,x,y,z) { float n = 0; cimg_forV(*this,v) n+=(float)((*this)(x,y,z,v)*(*this)(x,y,z,v)); n = (float)cimg_std::sqrt(n); if (n>0) cimg_forV(*this,v) (*this)(x,y,z,v) = (T)((*this)(x,y,z,v)/n); else cimg_forV(*this,v) (*this)(x,y,z,v) = 0; } return *this; } CImg get_pointwise_orientation() const { if (is_empty()) return *this; return CImg(*this,false).pointwise_orientation(); } //! Split image into a list. CImgList get_split(const char axis, const unsigned int nb=0) const { if (is_empty()) return CImgList(); CImgList res; switch (cimg::uncase(axis)) { case 'x' : { if (nb>width) throw CImgArgumentException("CImg<%s>::get_split() : Cannot split instance image (%u,%u,%u,%u,%p) along 'x' into %u images.", pixel_type(),width,height,depth,dim,data,nb); res.assign(nb?nb:width); const unsigned int delta = (unsigned int)cimg::round((float)width/res.size,1); unsigned int l, x; for (l = 0, x = 0; lheight) throw CImgArgumentException("CImg<%s>::get_split() : Cannot split instance image (%u,%u,%u,%u,%p) along 'y' into %u images.", pixel_type(),width,height,depth,dim,data,nb); res.assign(nb?nb:height); const unsigned int delta = (unsigned int)cimg::round((float)height/res.size,1); unsigned int l, y; for (l = 0, y = 0; ldepth) throw CImgArgumentException("CImg<%s>::get_split() : Cannot split instance image (%u,%u,%u,%u,%p) along 'z' into %u images.", pixel_type(),width,height,depth,dim,data,nb); res.assign(nb?nb:depth); const unsigned int delta = (unsigned int)cimg::round((float)depth/res.size,1); unsigned int l, z; for (l = 0, z = 0; ldim) throw CImgArgumentException("CImg<%s>::get_split() : Cannot split instance image (%u,%u,%u,%u,%p) along 'v' into %u images.", pixel_type(),width,height,depth,dim,data,nb); res.assign(nb?nb:dim); const unsigned int delta = (unsigned int)cimg::round((float)dim/res.size,1); unsigned int l, v; for (l = 0, v = 0; l::get_split() : Unknow axis '%c', must be 'x','y','z' or 'v'", pixel_type(),axis); } return res; } // Split image into a list of vectors, according to a given splitting value. CImgList get_split(const T value, const bool keep_values, const bool shared) const { CImgList res; const T *ptr0 = data, *const ptr_end = data + size(); while (ptr0(ptr0,1,siz0,1,1,shared)); ptr0 = ptr1; while (ptr1(ptr0,1,siz1,1,1,shared),~0U,shared); ptr0 = ptr1; } return res; } //! Append an image to another one. CImg& append(const CImg& img, const char axis, const char align='p') { if (!img) return *this; if (is_empty()) return (*this=img); return get_append(img,axis,align).transfer_to(*this); } CImg get_append(const CImg& img, const char axis, const char align='p') const { if (!img) return *this; if (is_empty()) return img; CImgList temp(2); temp[0].width = width; temp[0].height = height; temp[0].depth = depth; temp[0].dim = dim; temp[0].data = data; temp[1].width = img.width; temp[1].height = img.height; temp[1].depth = img.depth; temp[1].dim = img.dim; temp[1].data = img.data; const CImg res = temp.get_append(axis,align); temp[0].width = temp[0].height = temp[0].depth = temp[0].dim = 0; temp[0].data = 0; temp[1].width = temp[1].height = temp[1].depth = temp[1].dim = 0; temp[1].data = 0; return res; } //! Compute the list of images, corresponding to the XY-gradients of an image. /** \param scheme = Numerical scheme used for the gradient computation : - -1 = Backward finite differences - 0 = Centered finite differences - 1 = Forward finite differences - 2 = Using Sobel masks - 3 = Using rotation invariant masks - 4 = Using Deriche recusrsive filter. **/ CImgList get_gradient(const char *const axes=0, const int scheme=3) const { CImgList grad(2,width,height,depth,dim); bool threed = false; if (axes) { for (unsigned int a = 0; axes[a]; ++a) { const char axis = cimg::uncase(axes[a]); switch (axis) { case 'x' : case 'y' : break; case 'z' : threed = true; break; default : throw CImgArgumentException("CImg<%s>::get_gradient() : Unknown specified axis '%c'.", pixel_type(),axis); } } } else threed = (depth>1); if (threed) { grad.insert(1); grad[2].assign(width,height,depth,dim); switch (scheme) { // Compute 3D gradient case -1 : { // backward finite differences CImg_3x3x3(I,T); cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) { grad[0](x,y,z,k) = (Tfloat)Iccc - Ipcc; grad[1](x,y,z,k) = (Tfloat)Iccc - Icpc; grad[2](x,y,z,k) = (Tfloat)Iccc - Iccp; } } break; case 1 : { // forward finite differences CImg_2x2x2(I,T); cimg_forV(*this,k) cimg_for2x2x2(*this,x,y,z,k,I) { grad[0](x,y,z,k) = (Tfloat)Incc - Iccc; grad[1](x,y,z,k) = (Tfloat)Icnc - Iccc; grad[2](x,y,z,k) = (Tfloat)Iccn - Iccc; } } break; case 4 : { // using Deriche filter with low standard variation grad[0] = get_deriche(0,1,'x'); grad[1] = get_deriche(0,1,'y'); grad[2] = get_deriche(0,1,'z'); } break; default : { // central finite differences CImg_3x3x3(I,T); cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) { grad[0](x,y,z,k) = 0.5f*((Tfloat)Incc - Ipcc); grad[1](x,y,z,k) = 0.5f*((Tfloat)Icnc - Icpc); grad[2](x,y,z,k) = 0.5f*((Tfloat)Iccn - Iccp); } } } } else switch (scheme) { // Compute 2D-gradient case -1 : { // backward finite differences CImg_3x3(I,T); cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,z,k,I) { grad[0](x,y,z,k) = (Tfloat)Icc - Ipc; grad[1](x,y,z,k) = (Tfloat)Icc - Icp; } } break; case 1 : { // forward finite differences CImg_2x2(I,T); cimg_forZV(*this,z,k) cimg_for2x2(*this,x,y,z,k,I) { grad[0](x,y,0,k) = (Tfloat)Inc - Icc; grad[1](x,y,z,k) = (Tfloat)Icn - Icc; } } break; case 2 : { // using Sobel mask CImg_3x3(I,T); const Tfloat a = 1, b = 2; cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,z,k,I) { grad[0](x,y,z,k) = -a*Ipp - b*Ipc - a*Ipn + a*Inp + b*Inc + a*Inn; grad[1](x,y,z,k) = -a*Ipp - b*Icp - a*Inp + a*Ipn + b*Icn + a*Inn; } } break; case 3 : { // using rotation invariant mask CImg_3x3(I,T); const Tfloat a = (Tfloat)(0.25f*(2-cimg_std::sqrt(2.0f))), b = (Tfloat)(0.5f*(cimg_std::sqrt(2.0f)-1)); cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,z,k,I) { grad[0](x,y,z,k) = -a*Ipp - b*Ipc - a*Ipn + a*Inp + b*Inc + a*Inn; grad[1](x,y,z,k) = -a*Ipp - b*Icp - a*Inp + a*Ipn + b*Icn + a*Inn; } } break; case 4 : { // using Deriche filter with low standard variation grad[0] = get_deriche(0,1,'x'); grad[1] = get_deriche(0,1,'y'); } break; default : { // central finite differences CImg_3x3(I,T); cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,z,k,I) { grad[0](x,y,z,k) = 0.5f*((Tfloat)Inc - Ipc); grad[1](x,y,z,k) = 0.5f*((Tfloat)Icn - Icp); } } } if (!axes) return grad; CImgList res; for (unsigned int l = 0; axes[l]; ++l) { const char axis = cimg::uncase(axes[l]); switch (axis) { case 'x' : res.insert(grad[0]); break; case 'y' : res.insert(grad[1]); break; case 'z' : res.insert(grad[2]); break; } } grad.assign(); return res; } //! Compute the structure tensor field of an image. CImg& structure_tensor(const bool central_scheme=false) { return get_structure_tensor(central_scheme).transfer_to(*this); } CImg get_structure_tensor(const bool central_scheme=false) const { if (is_empty()) return *this; CImg res; if (depth>1) { // 3D version res.assign(width,height,depth,6,0); CImg_3x3x3(I,T); if (central_scheme) cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) { // classical central finite differences const Tfloat ix = 0.5f*((Tfloat)Incc - Ipcc), iy = 0.5f*((Tfloat)Icnc - Icpc), iz = 0.5f*((Tfloat)Iccn - Iccp); res(x,y,z,0)+=ix*ix; res(x,y,z,1)+=ix*iy; res(x,y,z,2)+=ix*iz; res(x,y,z,3)+=iy*iy; res(x,y,z,4)+=iy*iz; res(x,y,z,5)+=iz*iz; } else cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) { // Precise forward/backward finite differences const Tfloat ixf = (Tfloat)Incc - Iccc, ixb = (Tfloat)Iccc - Ipcc, iyf = (Tfloat)Icnc - Iccc, iyb = (Tfloat)Iccc - Icpc, izf = (Tfloat)Iccn - Iccc, izb = (Tfloat)Iccc - Iccp; res(x,y,z,0) += 0.5f*(ixf*ixf + ixb*ixb); res(x,y,z,1) += 0.25f*(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb); res(x,y,z,2) += 0.25f*(ixf*izf + ixf*izb + ixb*izf + ixb*izb); res(x,y,z,3) += 0.5f*(iyf*iyf + iyb*iyb); res(x,y,z,4) += 0.25f*(iyf*izf + iyf*izb + iyb*izf + iyb*izb); res(x,y,z,5) += 0.5f*(izf*izf + izb*izb); } } else { // 2D version res.assign(width,height,depth,3,0); CImg_3x3(I,T); if (central_scheme) cimg_forV(*this,k) cimg_for3x3(*this,x,y,0,k,I) { // classical central finite differences const Tfloat ix = 0.5f*((Tfloat)Inc - Ipc), iy = 0.5f*((Tfloat)Icn - Icp); res(x,y,0,0)+=ix*ix; res(x,y,0,1)+=ix*iy; res(x,y,0,2)+=iy*iy; } else cimg_forV(*this,k) cimg_for3x3(*this,x,y,0,k,I) { // Precise forward/backward finite differences const Tfloat ixf = (Tfloat)Inc - Icc, ixb = (Tfloat)Icc - Ipc, iyf = (Tfloat)Icn - Icc, iyb = (Tfloat)Icc - Icp; res(x,y,0,0) += 0.5f*(ixf*ixf+ixb*ixb); res(x,y,0,1) += 0.25f*(ixf*iyf+ixf*iyb+ixb*iyf+ixb*iyb); res(x,y,0,2) += 0.5f*(iyf*iyf+iyb*iyb); } } return res; } //! Get components of the Hessian matrix of an image. CImgList get_hessian(const char *const axes=0) const { const char *naxes = axes, *const def_axes2d = "xxxyyy", *const def_axes3d = "xxxyxzyyyzzz"; if (!axes) naxes = depth>1?def_axes3d:def_axes2d; CImgList res; const int lmax = cimg::strlen(naxes); if (lmax%2) throw CImgArgumentException("CImg<%s>::get_hessian() : Incomplete parameter axes = '%s'.", pixel_type(),naxes); res.assign(lmax/2,width,height,depth,dim); if (!cimg::strcasecmp(naxes,def_axes3d)) { // Default 3D version CImg_3x3x3(I,T); cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) { res[0](x,y,z,k) = (Tfloat)Ipcc + Incc - 2*Iccc; // Ixx res[1](x,y,z,k) = 0.25f*((Tfloat)Ippc + Innc - Ipnc - Inpc); // Ixy res[2](x,y,z,k) = 0.25f*((Tfloat)Ipcp + Incn - Ipcn - Incp); // Ixz res[3](x,y,z,k) = (Tfloat)Icpc + Icnc - 2*Iccc; // Iyy res[4](x,y,z,k) = 0.25f*((Tfloat)Icpp + Icnn - Icpn - Icnp); // Iyz res[5](x,y,z,k) = (Tfloat)Iccn + Iccp - 2*Iccc; // Izz } } else if (!cimg::strcasecmp(naxes,def_axes2d)) { // Default 2D version CImg_3x3(I,T); cimg_forV(*this,k) cimg_for3x3(*this,x,y,0,k,I) { res[0](x,y,0,k) = (Tfloat)Ipc + Inc - 2*Icc; // Ixx res[1](x,y,0,k) = 0.25f*((Tfloat)Ipp + Inn - Ipn - Inp); // Ixy res[2](x,y,0,k) = (Tfloat)Icp + Icn - 2*Icc; // Iyy } } else for (int l = 0; laxis2) cimg::swap(axis1,axis2); bool valid_axis = false; if (axis1=='x' && axis2=='x') { // Ixx valid_axis = true; CImg_3x3(I,T); cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,z,k,I) res[l2](x,y,z,k) = (Tfloat)Ipc + Inc - 2*Icc; } else if (axis1=='x' && axis2=='y') { // Ixy valid_axis = true; CImg_3x3(I,T); cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,z,k,I) res[l2](x,y,z,k) = 0.25f*((Tfloat)Ipp + Inn - Ipn - Inp); } else if (axis1=='x' && axis2=='z') { // Ixz valid_axis = true; CImg_3x3x3(I,T); cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) res[l2](x,y,z,k) = 0.25f*((Tfloat)Ipcp + Incn - Ipcn - Incp); } else if (axis1=='y' && axis2=='y') { // Iyy valid_axis = true; CImg_3x3(I,T); cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,z,k,I) res[l2](x,y,z,k) = (Tfloat)Icp + Icn - 2*Icc; } else if (axis1=='y' && axis2=='z') { // Iyz valid_axis = true; CImg_3x3x3(I,T); cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) res[l2](x,y,z,k) = 0.25f*((Tfloat)Icpp + Icnn - Icpn - Icnp); } else if (axis1=='z' && axis2=='z') { // Izz valid_axis = true; CImg_3x3x3(I,T); cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) res[l2](x,y,z,k) = (Tfloat)Iccn + Iccp - 2*Iccc; } else if (!valid_axis) throw CImgArgumentException("CImg<%s>::get_hessian() : Invalid parameter axes = '%s'.", pixel_type(),naxes); } return res; } //! Compute distance function from 0-valued isophotes by the application of an Hamilton-Jacobi PDE. CImg& distance_hamilton(const unsigned int nb_iter, const float band_size=0, const float precision=0.5f) { if (is_empty()) return *this; CImg veloc(*this); for (unsigned int iter = 0; iter1) { // 3D version CImg_3x3x3(I,T); cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) if (band_size<=0 || cimg::abs(Iccc)0?(Tfloat)Incc - Iccc:(Tfloat)Iccc - Ipcc, iy = gy*sgn>0?(Tfloat)Icnc - Iccc:(Tfloat)Iccc - Icpc, iz = gz*sgn>0?(Tfloat)Iccn - Iccc:(Tfloat)Iccc - Iccp, ng = 1e-5f + (Tfloat)cimg_std::sqrt(gx*gx + gy*gy + gz*gz), ngx = gx/ng, ngy = gy/ng, ngz = gz/ng; veloc(x,y,z,k) = sgn*(ngx*ix + ngy*iy + ngz*iz - 1); } } else { // 2D version CImg_3x3(I,T); cimg_forV(*this,k) cimg_for3x3(*this,x,y,0,k,I) if (band_size<=0 || cimg::abs(Icc)0?(Tfloat)Inc - Icc:(Tfloat)Icc - Ipc, iy = gy*sgn>0?(Tfloat)Icn - Icc:(Tfloat)Icc - Icp, ng = 1e-5f + (Tfloat)cimg_std::sqrt(gx*gx + gy*gy), ngx = gx/ng, ngy = gy/ng; veloc(x,y,k) = sgn*(ngx*ix + ngy*iy - 1); } } float m, M = (float)veloc.maxmin(m), xdt = precision/(float)cimg::max(cimg::abs(m),cimg::abs(M)); *this+=(veloc*=xdt); } return *this; } CImg get_distance_hamilton(const unsigned int nb_iter, const float band_size=0, const float precision=0.5f) const { return CImg(*this,false).distance_hamilton(nb_iter,band_size,precision); } //! Compute the Euclidean distance map to a shape of specified isovalue. CImg& distance(const T isovalue, const float sizex=1, const float sizey=1, const float sizez=1, const bool compute_sqrt=true) { return get_distance(isovalue,sizex,sizey,sizez,compute_sqrt).transfer_to(*this); } CImg get_distance(const T isovalue, const float sizex=1, const float sizey=1, const float sizez=1, const bool compute_sqrt=true) const { if (is_empty()) return *this; const int dx = dimx(), dy = dimy(), dz = dimz(); CImg res(dx,dy,dz,dim); const float maxdist = (float)cimg_std::sqrt((float)dx*dx + dy*dy + dz*dz); cimg_forV(*this,k) { bool is_isophote = false; if (depth>1) { // 3D version { cimg_forYZ(*this,y,z) { if ((*this)(0,y,z,k)==isovalue) { is_isophote = true; res(0,y,z,k) = 0; } else res(0,y,z,k) = maxdist; for (int x = 1; x=0; --x) if (res(x+1,y,z,k)::max()); continue; } CImg tmp(cimg::max(dy,dz)); CImg s(tmp.width), t(s.width); { cimg_forXZ(*this,x,z) { { cimg_forY(*this,y) tmp[y] = res(x,y,z,k); } int q = s[0] = t[0] = 0; { for (int y = 1; y=0 && _distance_f(t[q],s[q],cimg::sqr(tmp[s[q]]),sizey)>_distance_f(t[q],y,val2,sizey)) --q; if (q<0) { q = 0; s[0] = y; } else { const int w = 1 + _distance_sep(s[q],y,(int)cimg::sqr(tmp[s[q]]),(int)val2,sizey); if (w=0; --y) { res(x,y,z,k) = _distance_f(y,s[q],cimg::sqr(tmp[s[q]]),sizey); if (y==t[q]) --q; }} }} { cimg_forXY(*this,x,y) { { cimg_forZ(*this,z) tmp[z] = res(x,y,z,k); } int q = s[0] = t[0] = 0; { for (int z = 1; z=0 && _distance_f(t(q),s[q],tmp[s[q]],sizez)>_distance_f(t[q],z,tmp[z],sizez)) --q; if (q<0) { q = 0; s[0] = z; } else { const int w = 1 + _distance_sep(s[q],z,(int)tmp[s[q]],(int)val,sizez); if (w=0; --z) { const float val = _distance_f(z,s[q],tmp[s[q]],sizez); res(x,y,z,k) = compute_sqrt?(float)cimg_std::sqrt(val):val; if (z==t[q]) --q; }} }} } else { // 2D version (with small optimizations) cimg_forX(*this,x) { const T *ptrs = ptr(x,0,0,k); float *ptrd = res.ptr(x,0,0,k), d = *ptrd = *ptrs==isovalue?(is_isophote=true),0:maxdist; for (int y = 1; y=0; --y) { ptrd-=width; if (d<*ptrd) *ptrd = (d+=sizey); else d = *ptrd; }} } if (!is_isophote) { res.get_shared_channel(k).fill(cimg::type::max()); continue; } CImg tmp(dx); CImg s(dx), t(dx); cimg_forY(*this,y) { float *ptmp = tmp.ptr(); cimg_std::memcpy(ptmp,res.ptr(0,y,0,k),sizeof(float)*dx); int q = s[0] = t[0] = 0; for (int x = 1; x=0 && _distance_f(t[q],s[q],cimg::sqr(tmp[s[q]]),sizex)>_distance_f(t[q],x,val2,sizex)) --q; if (q<0) { q = 0; s[0] = x; } else { const int w = 1 + _distance_sep(s[q],x,(int)cimg::sqr(tmp[s[q]]),(int)val2,sizex); if (w=0; --x) { const float val = _distance_f(x,s[q],cimg::sqr(tmp[s[q]]),sizex); *(--pres) = compute_sqrt?(float)cimg_std::sqrt(val):val; if (x==t[q]) --q; }} } } } return res; } static float _distance_f(const int x, const int i, const float gi2, const float fact) { const float xmi = fact*((float)x - i); return xmi*xmi + gi2; } static int _distance_sep(const int i, const int u, const int gi2, const int gu2, const float fact) { const float fact2 = fact*fact; return (int)(fact2*(u*u - i*i) + gu2 - gi2)/(int)(2*fact2*(u - i)); } //! Compute minimal path in a graph, using the Dijkstra algorithm. /** \param distance An object having operator()(unsigned int i, unsigned int j) which returns distance between two nodes (i,j). \param nb_nodes Number of graph nodes. \param starting_node Indice of the starting node. \param ending_node Indice of the ending node (set to ~0U to ignore ending node). \param previous Array that gives the previous node indice in the path to the starting node (optional parameter). \return Array of distances of each node to the starting node. **/ template static CImg dijkstra(const tf& distance, const unsigned int nb_nodes, const unsigned int starting_node, const unsigned int ending_node, CImg& previous) { CImg dist(1,nb_nodes,1,1,cimg::type::max()); dist(starting_node) = 0; previous.assign(1,nb_nodes,1,1,(t)-1); previous(starting_node) = (t)starting_node; CImg Q(nb_nodes); cimg_forX(Q,u) Q(u) = u; cimg::swap(Q(starting_node),Q(0)); unsigned int sizeQ = nb_nodes; while (sizeQ) { // Update neighbors from minimal vertex const unsigned int umin = Q(0); if (umin==ending_node) sizeQ = 0; else { const T dmin = dist(umin); const T infty = cimg::type::max(); for (unsigned int q=1; qdist(Q(left))) || (rightdist(Q(right)));) { if (right static CImg dijkstra(const tf& distance, const unsigned int nb_nodes, const unsigned int starting_node, const unsigned int ending_node=~0U) { CImg foo; return dijkstra(distance,nb_nodes,starting_node,ending_node,foo); } //! Return minimal path in a graph, using the Dijkstra algorithm. /** Instance image corresponds to the adjacency matrix of the graph. \param starting_node Indice of the starting node. \param previous Array that gives the previous node indice in the path to the starting node (optional parameter). \return Array of distances of each node to the starting node. **/ template CImg& dijkstra(const unsigned int starting_node, const unsigned int ending_node, CImg& previous) { return get_dijkstra(starting_node,ending_node,previous).transfer_to(*this); } template CImg get_dijkstra(const unsigned int starting_node, const unsigned int ending_node, CImg& previous) const { if (width!=height || depth!=1 || dim!=1) throw CImgInstanceException("CImg<%s>::dijkstra() : Instance image (%u,%u,%u,%u,%p) is not a graph adjacency matrix", pixel_type(),width,height,depth,dim,data); return dijkstra(*this,width,starting_node,ending_node,previous); } //! Return minimal path in a graph, using the Dijkstra algorithm. CImg& dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) { return get_dijkstra(starting_node,ending_node).transfer_to(*this); } CImg get_dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) const { CImg foo; return get_dijkstra(starting_node,ending_node,foo); } //@} //------------------------------------- // //! \name Meshes and Triangulations //@{ //------------------------------------- //! Return a 3D centered cube. template static CImg cube3d(CImgList& primitives, const float size=100) { const double s = size/2.0; 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); return CImg(8,3,1,1, -s,s,s,-s,-s,s,s,-s, -s,-s,s,s,-s,-s,s,s, -s,-s,-s,-s,s,s,s,s); } //! Return a 3D centered cuboid. template static CImg cuboid3d(CImgList& primitives, const float sizex=200, const float sizey=100, const float sizez=100) { const double sx = sizex/2.0, sy = sizey/2.0, sz = sizez/2.0; 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); return CImg(8,3,1,1, -sx,sx,sx,-sx,-sx,sx,sx,-sx, -sy,-sy,sy,sy,-sy,-sy,sy,sy, -sz,-sz,-sz,-sz,sz,sz,sz,sz); } //! Return a 3D centered cone. template static CImg cone3d(CImgList& primitives, const float radius=50, const float height=100, const unsigned int subdivisions=24, const bool symetrize=false) { primitives.assign(); if (!subdivisions) return CImg(); const double r = (double)radius, h = (double)height/2; CImgList points(2,1,3,1,1, 0.0,0.0,h, 0.0,0.0,-h); const float delta = 360.0f/subdivisions, nh = symetrize?0:-(float)h; for (float angle = 0; angle<360; angle+=delta) { const float a = (float)(angle*cimg::valuePI/180); points.insert(CImg::vector((float)(r*cimg_std::cos(a)),(float)(r*cimg_std::sin(a)),nh)); } const unsigned int nbr = points.size-2; for (unsigned int p = 0; p::vector(1,next,curr)). insert(CImg::vector(0,curr,next)); } return points.get_append('x'); } //! Return a 3D centered cylinder. template static CImg cylinder3d(CImgList& primitives, const float radius=50, const float height=100, const unsigned int subdivisions=24) { primitives.assign(); if (!subdivisions) return CImg(); const double r = (double)radius, h = (double)height/2; CImgList points(2,1,3,1,1, 0.0,0.0,-h, 0.0,0.0,h); const float delta = 360.0f/subdivisions; for (float angle = 0; angle<360; angle+=delta) { const float a = (float)(angle*cimg::valuePI/180); points.insert(CImg::vector((float)(r*cimg_std::cos(a)),(float)(r*cimg_std::sin(a)),-(float)h)); points.insert(CImg::vector((float)(r*cimg_std::cos(a)),(float)(r*cimg_std::sin(a)),(float)h)); } const unsigned int nbr = (points.size-2)/2; for (unsigned int p = 0; p::vector(0,next,curr)). insert(CImg::vector(1,curr+1,next+1)). insert(CImg::vector(curr,next,next+1,curr+1)); } return points.get_append('x'); } //! Return a 3D centered torus. template static CImg torus3d(CImgList& primitives, const float radius1=100, const float radius2=30, const unsigned int subdivisions1=24, const unsigned int subdivisions2=12) { primitives.assign(); if (!subdivisions1 || !subdivisions2) return CImg(); CImgList points; for (unsigned int v = 0; v::vector(x,y,z)); } } for (unsigned int vv = 0; vv::vector(svv+nu,svv+uu,snv+uu)); primitives.insert(CImg::vector(svv+nu,snv+uu,snv+nu)); } } return points.get_append('x'); } //! Return a 3D centered XY plane. template static CImg plane3d(CImgList& primitives, const float sizex=100, const float sizey=100, const unsigned int subdivisionsx=3, const unsigned int subdivisionsy=3, const bool double_sided=false) { primitives.assign(); if (!subdivisionsx || !subdivisionsy) return CImg(); CImgList points; const unsigned int w = subdivisionsx + 1, h = subdivisionsy + 1; const float w2 = subdivisionsx/2.0f, h2 = subdivisionsy/2.0f, fx = (float)sizex/w, fy = (float)sizey/h; for (unsigned int yy = 0; yy::vector(fx*(xx-w2),fy*(yy-h2),0)); for (unsigned int y = 0; y::vector(off1,off4,off3,off2)); if (double_sided) primitives.insert(CImg::vector(off1,off2,off3,off4)); } return points.get_append('x'); } //! Return a 3D centered sphere. template static CImg sphere3d(CImgList& primitives, const float radius=50, const unsigned int subdivisions=3) { // Create initial icosahedron primitives.assign(); if (!subdivisions) return CImg(); const double tmp = (1+cimg_std::sqrt(5.0f))/2, a = 1.0/cimg_std::sqrt(1+tmp*tmp), b = tmp*a; CImgList points(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, -a,0.0,-b, -a,0.0,b, 0.0,b,a, 0.0,-b,a, 0.0,-b,-a, 0.0,b,-a); 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, 8,0,11, 8,11,1, 9,10,3, 9,2,10, 8,4,0, 11,0,5, 4,9,3, 5,3,10, 7,8,1, 6,1,11, 7,2,9, 6,10,2); // Recurse subdivisions for (unsigned int i = 0; i::vector(nx0,ny0,nz0)); i0 = points.size-1; } if (i1<0) { points.insert(CImg::vector(nx1,ny1,nz1)); i1 = points.size-1; } if (i2<0) { points.insert(CImg::vector(nx2,ny2,nz2)); i2 = points.size-1; } primitives.remove(0); primitives.insert(CImg::vector(p0,i0,i1)). insert(CImg::vector((tf)i0,(tf)p1,(tf)i2)). insert(CImg::vector((tf)i1,(tf)i2,(tf)p2)). insert(CImg::vector((tf)i1,(tf)i0,(tf)i2)); } } return points.get_append('x')*=radius; } //! Return a 3D centered ellipsoid. template static CImg ellipsoid3d(CImgList& primitives, const CImg& tensor, const unsigned int subdivisions=3) { primitives.assign(); if (!subdivisions) return CImg(); typedef typename cimg::superset::type tfloat; CImg S,V; tensor.symmetric_eigen(S,V); const tfloat l0 = S[0], l1 = S[1], l2 = S[2]; CImg points = sphere(primitives,subdivisions); cimg_forX(points,p) { points(p,0) = (float)(points(p,0)*l0); points(p,1) = (float)(points(p,1)*l1); points(p,2) = (float)(points(p,2)*l2); } V.transpose(); points = V*points; return points; } //! Return a 3D elevation object of the instance image. template CImg get_elevation3d(CImgList& primitives, CImgList& colors, const CImg& elevation) const { primitives.assign(); colors.assign(); if (is_empty()) return *this; if (depth>1) throw CImgInstanceException("CImg<%s>::get_elevation3d() : Instance image (%u,%u,%u,%u,%p) is not a 2D image.", pixel_type(),width,height,depth,dim,data); if (!is_sameXY(elevation)) throw CImgArgumentException("CImg<%s>::get_elevation3d() : Elevation image (%u,%u,%u,%u,%p) and instance image (%u,%u,%u,%u,%p) " "have different sizes.",pixel_type(), elevation.width,elevation.height,elevation.depth,elevation.dim,elevation.data, width,height,depth,dim,data,pixel_type()); float m, M = (float)maxmin(m); if (M==m) ++M; const unsigned int w = width + 1, h = height + 1; CImg points(w*h,3); cimg_forXY(*this,x,y) { const int yw = y*w, xpyw = x + yw, xpyww = xpyw + w; points(xpyw,0) = points(xpyw+1,0) = points(xpyww+1,0) = points(xpyww,0) = (float)x; points(xpyw,1) = points(xpyw+1,1) = points(xpyww+1,1) = points(xpyww,1) = (float)y; points(xpyw,2) = points(xpyw+1,2) = points(xpyww+1,2) = points(xpyww,2) = (float)elevation(x,y); primitives.insert(CImg::vector(xpyw,xpyw+1,xpyww+1,xpyww)); const unsigned char r = (unsigned char)(((*this)(x,y,0) - m)*255/(M-m)), g = dim>1?(unsigned char)(((*this)(x,y,1) - m)*255/(M-m)):r, b = dim>2?(unsigned char)(((*this)(x,y,2) - m)*255/(M-m)):(dim>1?0:r); colors.insert(CImg::vector((tc)r,(tc)g,(tc)b)); } return points; } // Inner routine used by the Marching square algorithm. template static int _marching_squares_indice(const unsigned int edge, const CImg& indices1, const CImg& indices2, const unsigned int x, const unsigned int nx) { switch (edge) { case 0 : return (int)indices1(x,0); case 1 : return (int)indices1(nx,1); case 2 : return (int)indices2(x,0); case 3 : return (int)indices1(x,1); } return 0; } //! Polygonize an implicit 2D function by the marching squares algorithm. template static CImg marching_squares(CImgList& primitives, const tfunc& func, const float isovalue, const float x0, const float y0, const float x1, const float y1, const float resx, const float resy) { static unsigned int edges[16] = { 0x0, 0x9, 0x3, 0xa, 0x6, 0xf, 0x5, 0xc, 0xc, 0x5, 0xf, 0x6, 0xa, 0x3, 0x9, 0x0 }; static int segments[16][4] = { { -1,-1,-1,-1 }, { 0,3,-1,-1 }, { 0,1,-1,-1 }, { 1,3,-1,-1 }, { 1,2,-1,-1 }, { 0,1,2,3 }, { 0,2,-1,-1 }, { 2,3,-1,-1 }, { 2,3,-1,-1 }, { 0,2,-1,-1}, { 0,3,1,2 }, { 1,2,-1,-1 }, { 1,3,-1,-1 }, { 0,1,-1,-1}, { 0,3,-1,-1}, { -1,-1,-1,-1 } }; const unsigned int nx = (unsigned int)((x1-x0+1)/resx), nxm1 = nx-1, ny = (unsigned int)((y1-y0+1)/resy), nym1 = ny-1; if (!nxm1 || !nym1) return CImg(); primitives.assign(); CImgList points; CImg indices1(nx,1,1,2,-1), indices2(nx,1,1,2); CImg values1(nx), values2(nx); float X = 0, Y = 0, nX = 0, nY = 0; // Fill first line with values cimg_forX(values1,x) { values1(x) = (float)func(X,Y); X+=resx; } // Run the marching squares algorithm Y = y0; nY = Y + resy; for (unsigned int yi = 0, nyi = 1; yi::vector(Xi,Y)); } if ((edge&2) && indices1(nxi,1)<0) { const float Yi = Y + (isovalue-val1)*resy/(val2-val1); indices1(nxi,1) = points.size; points.insert(CImg::vector(nX,Yi)); } if ((edge&4) && indices2(xi,0)<0) { const float Xi = X + (isovalue-val3)*resx/(val2-val3); indices2(xi,0) = points.size; points.insert(CImg::vector(Xi,nY)); } if ((edge&8) && indices1(xi,1)<0) { const float Yi = Y + (isovalue-val0)*resy/(val3-val0); indices1(xi,1) = points.size; points.insert(CImg::vector(X,Yi)); } // Create segments for (int *segment = segments[configuration]; *segment!=-1; ) { const unsigned int p0 = *(segment++), p1 = *(segment++); const tf i0 = (tf)(_marching_squares_indice(p0,indices1,indices2,xi,nxi)), i1 = (tf)(_marching_squares_indice(p1,indices1,indices2,xi,nxi)); primitives.insert(CImg::vector(i0,i1)); } } } values1.swap(values2); indices1.swap(indices2); } return points.get_append('x'); } // Inner routine used by the Marching cube algorithm. template static int _marching_cubes_indice(const unsigned int edge, const CImg& indices1, const CImg& indices2, const unsigned int x, const unsigned int y, const unsigned int nx, const unsigned int ny) { switch (edge) { case 0 : return indices1(x,y,0); case 1 : return indices1(nx,y,1); case 2 : return indices1(x,ny,0); case 3 : return indices1(x,y,1); case 4 : return indices2(x,y,0); case 5 : return indices2(nx,y,1); case 6 : return indices2(x,ny,0); case 7 : return indices2(x,y,1); case 8 : return indices1(x,y,2); case 9 : return indices1(nx,y,2); case 10 : return indices1(nx,ny,2); case 11 : return indices1(x,ny,2); } return 0; } //! Polygonize an implicit function // This function uses the Marching Cubes Tables published on the web page : // http://astronomy.swin.edu.au/~pbourke/modelling/polygonise/ template static CImg marching_cubes(CImgList& 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 float resx, const float resy, const float resz, const bool invert_faces=false) { static unsigned int edges[256] = { 0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, 0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, 0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, 0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, 0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460, 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0, 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230, 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190, 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000 }; static int triangles[256][16] = {{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, { 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1 }, { 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, { 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, { 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1 }, { 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, { 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1 }, { 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, { 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, { 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1 }, { 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1 }, { 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1 }, { 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, { 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 }, { 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, { 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1 }, { 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 }, { 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, { 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1 }, { 10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1 }, { 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1 }, { 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, { 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1 }, { 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, { 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, { 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1 }, { 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1 }, { 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1 }, { 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, { 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1 }, { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1 }, { 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1 }, { 11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, { 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1 }, { 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1 }, { 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1 }, { 11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, { 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1 }, { 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1 }, { 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, { 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, { 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1 }, { 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, { 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1 }, { 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, { 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1 }, { 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, { 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1 }, { 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1 }, { 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1 }, { 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1 }, { 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, { 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1 }, { 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1 }, { 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1 }, { 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, { 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1 }, { 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1 }, { 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1 }, { 10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1 }, { 10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, { 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1 }, { 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, { 10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1 }, { 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, { 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1 }, { 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1 }, { 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1 }, { 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, { 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, { 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1 }, { 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1 }, { 10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, { 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1 }, { 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, { 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, { 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1 }, { 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1 }, { 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1 }, { 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1 }, { 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1 }, { 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 }, { 10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, { 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, { 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1 }, { 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, { 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1 }, { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1 }, { 10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, { 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1 }, { 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1 }, { 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, { 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, { 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1 }, { 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1 }, { 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1 }, { 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1 }, { 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, { 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1 }, { 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1 }, { 10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, { 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1 }, { 10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 }, { 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, { 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1 }, { 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, { 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1 }, { 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1 }, { 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1 }, { 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1 }, { 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1 }, { 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1 }, { 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1 }, { 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1 }, { 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1 }, { 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1 }, { 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1 }, { 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, { 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1 }, { 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1 }, { 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1 }, { 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1 }, { 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1 }, { 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1 }, { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1 }, { 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, { 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1 }, { 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1 }, { 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1 }, { 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1 }, { 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1 }, { 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1 }, { 11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1 }, { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1 }, { 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1 }, { 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, { 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1 }, { 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1 }, { 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1 }, { 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, { 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, { 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, { 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1 }, { 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1 }, { 10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1 }, { 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1 }, { 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1 }, { 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1 }, { 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1 }, { 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, { 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1 }, { 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1 }, { 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1 }, { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1 }, { 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1 }, { 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1 }, { 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1 }, { 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1 }, { 11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, { 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1 }, { 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1 }, { 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1 }, { 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1 }, { 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, { 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1 }, { 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, { 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, { 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1 }, { 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, { 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1 }, { 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }}; const unsigned int nx = (unsigned int)((x1-x0+1)/resx), nxm1 = nx-1, ny = (unsigned int)((y1-y0+1)/resy), nym1 = ny-1, nz = (unsigned int)((z1-z0+1)/resz), nzm1 = nz-1; if (!nxm1 || !nym1 || !nzm1) return CImg(); primitives.assign(); CImgList points; CImg indices1(nx,ny,1,3,-1), indices2(indices1); CImg values1(nx,ny), values2(nx,ny); float X = 0, Y = 0, Z = 0, nX = 0, nY = 0, nZ = 0; // Fill the first plane with function values Y = y0; cimg_forY(values1,y) { X = x0; cimg_forX(values1,x) { values1(x,y) = (float)func(X,Y,z0); X+=resx; } Y+=resy; } // Run Marching Cubes algorithm Z = z0; nZ = Z + resz; for (unsigned int zi = 0; zi::vector(Xi,Y,Z)); } if ((edge&2) && indices1(nxi,yi,1)<0) { const float Yi = Y + (isovalue-val1)*resy/(val2-val1); indices1(nxi,yi,1) = points.size; points.insert(CImg::vector(nX,Yi,Z)); } if ((edge&4) && indices1(xi,nyi,0)<0) { const float Xi = X + (isovalue-val3)*resx/(val2-val3); indices1(xi,nyi,0) = points.size; points.insert(CImg::vector(Xi,nY,Z)); } if ((edge&8) && indices1(xi,yi,1)<0) { const float Yi = Y + (isovalue-val0)*resy/(val3-val0); indices1(xi,yi,1) = points.size; points.insert(CImg::vector(X,Yi,Z)); } if ((edge&16) && indices2(xi,yi,0)<0) { const float Xi = X + (isovalue-val4)*resx/(val5-val4); indices2(xi,yi,0) = points.size; points.insert(CImg::vector(Xi,Y,nZ)); } if ((edge&32) && indices2(nxi,yi,1)<0) { const float Yi = Y + (isovalue-val5)*resy/(val6-val5); indices2(nxi,yi,1) = points.size; points.insert(CImg::vector(nX,Yi,nZ)); } if ((edge&64) && indices2(xi,nyi,0)<0) { const float Xi = X + (isovalue-val7)*resx/(val6-val7); indices2(xi,nyi,0) = points.size; points.insert(CImg::vector(Xi,nY,nZ)); } if ((edge&128) && indices2(xi,yi,1)<0) { const float Yi = Y + (isovalue-val4)*resy/(val7-val4); indices2(xi,yi,1) = points.size; points.insert(CImg::vector(X,Yi,nZ)); } if ((edge&256) && indices1(xi,yi,2)<0) { const float Zi = Z+ (isovalue-val0)*resz/(val4-val0); indices1(xi,yi,2) = points.size; points.insert(CImg::vector(X,Y,Zi)); } if ((edge&512) && indices1(nxi,yi,2)<0) { const float Zi = Z + (isovalue-val1)*resz/(val5-val1); indices1(nxi,yi,2) = points.size; points.insert(CImg::vector(nX,Y,Zi)); } if ((edge&1024) && indices1(nxi,nyi,2)<0) { const float Zi = Z + (isovalue-val2)*resz/(val6-val2); indices1(nxi,nyi,2) = points.size; points.insert(CImg::vector(nX,nY,Zi)); } if ((edge&2048) && indices1(xi,nyi,2)<0) { const float Zi = Z + (isovalue-val3)*resz/(val7-val3); indices1(xi,nyi,2) = points.size; points.insert(CImg::vector(X,nY,Zi)); } // Create triangles for (int *triangle = triangles[configuration]; *triangle!=-1; ) { const unsigned int p0 = *(triangle++), p1 = *(triangle++), p2 = *(triangle++); const tf i0 = (tf)(_marching_cubes_indice(p0,indices1,indices2,xi,yi,nxi,nyi)), i1 = (tf)(_marching_cubes_indice(p1,indices1,indices2,xi,yi,nxi,nyi)), i2 = (tf)(_marching_cubes_indice(p2,indices1,indices2,xi,yi,nxi,nyi)); if (invert_faces) primitives.insert(CImg::vector(i0,i1,i2)); else primitives.insert(CImg::vector(i0,i2,i1)); } } } } cimg::swap(values1,values2); cimg::swap(indices1,indices2); } return points.get_append('x'); } struct _marching_squares_func { const CImg& ref; _marching_squares_func(const CImg& pref):ref(pref) {} float operator()(const float x, const float y) const { return (float)ref((int)x,(int)y); } }; struct _marching_cubes_func { const CImg& ref; _marching_cubes_func(const CImg& pref):ref(pref) {} float operator()(const float x, const float y, const float z) const { return (float)ref((int)x,(int)y,(int)z); } }; struct _marching_squares_func_float { const CImg& ref; _marching_squares_func_float(const CImg& pref):ref(pref) {} float operator()(const float x, const float y) const { return (float)ref._linear_atXY(x,y); } }; struct _marching_cubes_func_float { const CImg& ref; _marching_cubes_func_float(const CImg& pref):ref(pref) {} float operator()(const float x, const float y, const float z) const { return (float)ref._linear_atXYZ(x,y,z); } }; //! Compute a vectorization of an implicit function. template CImg get_isovalue3d(CImgList& primitives, const float isovalue, const float resx=1, const float resy=1, const float resz=1, const bool invert_faces=false) const { primitives.assign(); if (is_empty()) return *this; if (dim>1) throw CImgInstanceException("CImg<%s>::get_isovalue3d() : Instance image (%u,%u,%u,%u,%p) is not a scalar image.", pixel_type(),width,height,depth,dim,data); CImg points; if (depth>1) { if (resx==1 && resy==1 && resz==1) { const _marching_cubes_func func(*this); points = marching_cubes(primitives,func,isovalue,0,0,0,dimx()-1.0f,dimy()-1.0f,dimz()-1.0f,resx,resy,resz,invert_faces); } else { const _marching_cubes_func_float func(*this); points = marching_cubes(primitives,func,isovalue,0,0,0,dimx()-1.0f,dimy()-1.0f,dimz()-1.0f,resx,resy,resz,invert_faces); } } else { if (resx==1 && resy==1) { const _marching_squares_func func(*this); points = marching_squares(primitives,func,isovalue,0,0,dimx()-1.0f,dimy()-1.0f,resx,resy); } else { const _marching_squares_func_float func(*this); points = marching_squares(primitives,func,isovalue,0,0,dimx()-1.0f,dimy()-1.0f,resx,resy); } if (points) points.resize(-100,3,1,1,0); } return points; } //! Translate a 3D object. CImg& translate_object3d(const float tx, const float ty=0, const float tz=0) { get_shared_line(0)+=tx; get_shared_line(1)+=ty; get_shared_line(2)+=tz; return *this; } CImg get_translate_object3d(const float tx, const float ty=0, const float tz=0) const { return CImg(*this,false).translate_object3d(tx,ty,tz); } //! Translate a 3D object so that it becomes centered. CImg& translate_object3d() { CImg xcoords = get_shared_line(0), ycoords = get_shared_line(1), zcoords = get_shared_line(2); float xm, xM = (float)xcoords.maxmin(xm), ym, yM = (float)ycoords.maxmin(ym), zm, zM = (float)zcoords.maxmin(zm); xcoords-=(xm + xM)/2; ycoords-=(ym + yM)/2; zcoords-=(zm + zM)/2; return *this; } CImg get_translate_object3d() const { return CImg(*this,false).translate_object3d(); } //! Resize a 3D object. CImg& resize_object3d(const float sx, const float sy=-100, const float sz=-100) { CImg xcoords = get_shared_line(0), ycoords = get_shared_line(1), zcoords = get_shared_line(2); float xm, xM = (float)xcoords.maxmin(xm), ym, yM = (float)ycoords.maxmin(ym), zm, zM = (float)zcoords.maxmin(zm); if (xm0) xcoords*=sx/(xM-xm); else xcoords*=-sx/100; } if (ym0) ycoords*=sy/(yM-ym); else ycoords*=-sy/100; } if (zm0) zcoords*=sz/(zM-zm); else zcoords*=-sz/100; } return *this; } CImg get_resize_object3d(const float sx, const float sy=-100, const float sz=-100) const { return CImg(*this,false).resize_object3d(sx,sy,sz); } // Resize a 3D object so that its max dimension if one. CImg resize_object3d() const { CImg xcoords = get_shared_line(0), ycoords = get_shared_line(1), zcoords = get_shared_line(2); float xm, xM = (float)xcoords.maxmin(xm), ym, yM = (float)ycoords.maxmin(ym), zm, zM = (float)zcoords.maxmin(zm); const float dx = xM - xm, dy = yM - ym, dz = zM - zm, dmax = cimg::max(dx,dy,dz); if (dmax>0) { xcoords/=dmax; ycoords/=dmax; zcoords/=dmax; } return *this; } CImg get_resize_object3d() const { return CImg(*this,false).resize_object3d(); } //! Append a 3D object to another one. template CImg& append_object3d(CImgList& primitives, const CImg& obj_points, const CImgList& obj_primitives) { const unsigned int P = width; append(obj_points,'x'); const unsigned int N = primitives.size; primitives.insert(obj_primitives); for (unsigned int i = N; i &p = primitives[i]; if (p.size()!=5) p+=P; else { p[0]+=P; if (p[2]==0) p[1]+=P; } } return *this; } //@} //---------------------------- // //! \name Color bases //@{ //---------------------------- //! Return a default indexed color palette with 256 (R,G,B) entries. /** The default color palette is used by %CImg when displaying images on 256 colors displays. It consists in the quantification of the (R,G,B) color space using 3:3:2 bits for color coding (i.e 8 levels for the Red and Green and 4 levels for the Blue). \return a 1x256x1x3 color image defining the palette entries. **/ static CImg default_LUT8() { static CImg palette; if (!palette) { palette.assign(1,256,1,3); for (unsigned int index = 0, r = 16; r<256; r+=32) for (unsigned int g = 16; g<256; g+=32) for (unsigned int b = 32; b<256; b+=64) { palette(0,index,0) = (Tuchar)r; palette(0,index,1) = (Tuchar)g; palette(0,index++,2) = (Tuchar)b; } } return palette; } //! Return a rainbow color palette with 256 (R,G,B) entries. static CImg rainbow_LUT8() { static CImg palette; if (!palette) { CImg tmp(1,256,1,3,1); tmp.get_shared_channel(0).sequence(0,359); palette = tmp.HSVtoRGB(); } return palette; } //! Return a contrasted color palette with 256 (R,G,B) entries. static CImg contrast_LUT8() { static const unsigned char pal[] = { 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 23,168,19,50,240,244,185,0,1,144,10,168,31,82,1,13 }; static const CImg palette(pal,1,256,1,3,false); return palette; } //! Convert (R,G,B) color image to indexed color image. template CImg& RGBtoLUT(const CImg& palette, const bool dithering=true, const bool indexing=false) { return get_RGBtoLUT(palette,dithering,indexing).transfer_to(*this); } template CImg get_RGBtoLUT(const CImg& palette, const bool dithering=true, const bool indexing=false) const { if (is_empty()) return CImg(); if (dim!=3) throw CImgInstanceException("CImg<%s>::RGBtoLUT() : Input image dimension is dim=%u, " "should be a (R,G,B) image.", pixel_type(),dim); if (palette.data && palette.dim!=3) throw CImgArgumentException("CImg<%s>::RGBtoLUT() : Given palette dimension is dim=%u, " "should be a (R,G,B) palette", pixel_type(),palette.dim); CImg res(width,height,depth,indexing?1:3); float *line1 = new float[3*width], *line2 = new float[3*width]; t *pRd = res.ptr(0,0,0,0), *pGd = indexing?pRd:res.ptr(0,0,0,1), *pBd = indexing?pRd:res.ptr(0,0,0,2); cimg_forZ(*this,z) { const T *pRs = ptr(0,0,z,0), *pGs = ptr(0,0,z,1), *pBs = ptr(0,0,z,2); float *ptrd = line2; cimg_forX(*this,x) { *(ptrd++) = (float)*(pRs++); *(ptrd++) = (float)*(pGs++); *(ptrd++) = (float)*(pBs++); } cimg_forY(*this,y) { cimg::swap(line1,line2); if (y255?255:R); G = G<0?0:(G>255?255:G); B = B<0?0:(B>255?255:B); t Rbest = 0, Gbest = 0, Bbest = 0; int best_index = 0; if (palette) { // find best match in given color palette const t *pRs = palette.ptr(0,0,0,0), *pGs = palette.ptr(0,0,0,1), *pBs = palette.ptr(0,0,0,2); const unsigned int Npal = palette.width*palette.height*palette.depth; float min = cimg::type::max(); for (unsigned int off = 0; off>3) | ((unsigned char)Bbest>>6); } if (indexing) *(pRd++) = (t)best_index; else { *(pRd++) = Rbest; *(pGd++) = Gbest; *(pBd++) = Bbest; } if (dithering) { // apply dithering to neighborhood pixels if needed const float dR = (float)(R-Rbest), dG = (float)(G-Gbest), dB = (float)(B-Bbest); if (x0) { *(--ptr2)+= dB*3/16; *(--ptr2)+= dG*3/16; *(--ptr2)+= dR*3/16; ptr2+=3; } if (x& RGBtoLUT(const bool dithering=true, const bool indexing=false) { return get_RGBtoLUT(dithering,indexing).transfer_to(*this); } CImg get_RGBtoLUT(const bool dithering=true, const bool indexing=false) const { static const CImg empty; return get_RGBtoLUT(empty,dithering,indexing); } //! Convert an indexed image to a (R,G,B) image using the specified color palette. CImg& LUTtoRGB(const CImg& palette) { return get_LUTtoRGB(palette).transfer_to(*this); } template CImg get_LUTtoRGB(const CImg& palette) const { if (is_empty()) return CImg(); if (dim!=1) throw CImgInstanceException("CImg<%s>::LUTtoRGB() : Input image dimension is dim=%u, " "should be a LUT image", pixel_type(),dim); if (palette.data && palette.dim!=3) throw CImgArgumentException("CImg<%s>::LUTtoRGB() : Given palette dimension is dim=%u, " "should be a (R,G,B) palette", pixel_type(),palette.dim); const CImg pal = palette.data?palette:CImg(default_LUT8()); CImg res(width,height,depth,3); const t *pRs = pal.ptr(0,0,0,0), *pGs = pal.ptr(0,0,0,1), *pBs = pal.ptr(0,0,0,2); t *pRd = res.ptr(0,0,0,1), *pGd = pRd + width*height*depth, *pBd = pGd + width*height*depth; const unsigned int Npal = palette.width*palette.height*palette.depth; cimg_for(*this,ptr,T) { const unsigned int index = ((unsigned int)*ptr)%Npal; *(--pRd) = pRs[index]; *(--pGd) = pGs[index]; *(--pBd) = pBs[index]; } return res; } //! Convert an indexed image (with the default palette) to a (R,G,B) image. CImg& LUTtoRGB() { return get_LUTtoRGB().transfer_to(*this); } CImg get_LUTtoRGB() const { static const CImg empty; return get_LUTtoRGB(empty); } //! Convert color pixels from (R,G,B) to (H,S,V). CImg& RGBtoHSV() { if (is_empty()) return *this; if (dim!=3) throw CImgInstanceException("CImg<%s>::RGBtoHSV() : Input image dimension is dim=%u, " "should be a (R,G,B) image.", pixel_type(),dim); T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); for (unsigned long N = width*height*depth; N; --N) { const Tfloat R = (Tfloat)*p1, G = (Tfloat)*p2, B = (Tfloat)*p3, nR = (R<0?0:(R>255?255:R))/255, nG = (G<0?0:(G>255?255:G))/255, nB = (B<0?0:(B>255?255:B))/255, m = cimg::min(nR,nG,nB), M = cimg::max(nR,nG,nB); Tfloat H = 0, S = 0; if (M!=m) { const Tfloat f = (nR==m)?(nG-nB):((nG==m)?(nB-nR):(nR-nG)), i = (Tfloat)((nR==m)?3:((nG==m)?5:1)); H = (i-f/(M-m)); if (H>=6) H-=6; H*=60; S = (M-m)/M; } *(p1++) = (T)H; *(p2++) = (T)S; *(p3++) = (T)M; } return *this; } CImg get_RGBtoHSV() const { return CImg(*this,false).RGBtoHSV(); } //! Convert color pixels from (H,S,V) to (R,G,B). CImg& HSVtoRGB() { if (is_empty()) return *this; if (dim!=3) throw CImgInstanceException("CImg<%s>::HSVtoRGB() : Input image dimension is dim=%u, " "should be a (H,S,V) image", pixel_type(),dim); T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); for (unsigned long N = width*height*depth; N; --N) { Tfloat H = (Tfloat)*p1, S = (Tfloat)*p2, V = (Tfloat)*p3, R = 0, G = 0, B = 0; if (H==0 && S==0) R = G = B = V; else { H/=60; const int i = (int)cimg_std::floor(H); const Tfloat f = (i&1)?(H-i):(1-H+i), m = V*(1-S), n = V*(1-S*f); switch (i) { case 6 : case 0 : R = V; G = n; B = m; break; case 1 : R = n; G = V; B = m; break; case 2 : R = m; G = V; B = n; break; case 3 : R = m; G = n; B = V; break; case 4 : R = n; G = m; B = V; break; case 5 : R = V; G = m; B = n; break; } } R*=255; G*=255; B*=255; *(p1++) = (T)(R<0?0:(R>255?255:R)); *(p2++) = (T)(G<0?0:(G>255?255:G)); *(p3++) = (T)(B<0?0:(B>255?255:B)); } return *this; } CImg get_HSVtoRGB() const { return CImg(*this,false).HSVtoRGB(); } //! Convert color pixels from (R,G,B) to (H,S,L). CImg& RGBtoHSL() { if (is_empty()) return *this; if (dim!=3) throw CImgInstanceException("CImg<%s>::RGBtoHSL() : Input image dimension is dim=%u, " "should be a (R,G,B) image.", pixel_type(),dim); T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); for (unsigned long N = width*height*depth; N; --N) { const Tfloat R = (Tfloat)*p1, G = (Tfloat)*p2, B = (Tfloat)*p3, nR = (R<0?0:(R>255?255:R))/255, nG = (G<0?0:(G>255?255:G))/255, nB = (B<0?0:(B>255?255:B))/255, m = cimg::min(nR,nG,nB), M = cimg::max(nR,nG,nB), L = (m+M)/2; Tfloat H = 0, S = 0; if (M==m) H = S = 0; else { const Tfloat f = (nR==m)?(nG-nB):((nG==m)?(nB-nR):(nR-nG)), i = (nR==m)?3.0f:((nG==m)?5.0f:1.0f); H = (i-f/(M-m)); if (H>=6) H-=6; H*=60; S = (2*L<=1)?((M-m)/(M+m)):((M-m)/(2-M-m)); } *(p1++) = (T)H; *(p2++) = (T)S; *(p3++) = (T)L; } return *this; } CImg get_RGBtoHSL() const { return CImg< Tfloat>(*this,false).RGBtoHSL(); } //! Convert color pixels from (H,S,L) to (R,G,B). CImg& HSLtoRGB() { if (is_empty()) return *this; if (dim!=3) throw CImgInstanceException("CImg<%s>::HSLtoRGB() : Input image dimension is dim=%u, " "should be a (H,S,V) image", pixel_type(),dim); T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); for (unsigned long N = width*height*depth; N; --N) { const Tfloat H = (Tfloat)*p1, S = (Tfloat)*p2, L = (Tfloat)*p3, q = 2*L<1?L*(1+S):(L+S-L*S), p = 2*L-q, h = H/360, tr = h + 1.0f/3, tg = h, tb = h - 1.0f/3, ntr = tr<0?tr+1:(tr>1?tr-1:tr), ntg = tg<0?tg+1:(tg>1?tg-1:tg), ntb = tb<0?tb+1:(tb>1?tb-1:tb), 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))), 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))), 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))); *(p1++) = (T)(R<0?0:(R>255?255:R)); *(p2++) = (T)(G<0?0:(G>255?255:G)); *(p3++) = (T)(B<0?0:(B>255?255:B)); } return *this; } CImg get_HSLtoRGB() const { return CImg(*this,false).HSLtoRGB(); } //! Convert color pixels from (R,G,B) to (H,S,I). //! Reference: "Digital Image Processing, 2nd. edition", R. Gonzalez and R. Woods. Prentice Hall, 2002. CImg& RGBtoHSI() { if (is_empty()) return *this; if (dim!=3) throw CImgInstanceException("CImg<%s>::RGBtoHSI() : Input image dimension is dim=%u, " "should be a (R,G,B) image.", pixel_type(),dim); T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); for (unsigned long N = width*height*depth; N; --N) { const Tfloat R = (Tfloat)*p1, G = (Tfloat)*p2, B = (Tfloat)*p3, nR = (R<0?0:(R>255?255:R))/255, nG = (G<0?0:(G>255?255:G))/255, nB = (B<0?0:(B>255?255:B))/255, m = cimg::min(nR,nG,nB), theta = (Tfloat)(cimg_std::acos(0.5f*((nR-nG)+(nR-nB))/cimg_std::sqrt(cimg_std::pow(nR-nG,2)+(nR-nB)*(nG-nB)))*180/cimg::valuePI), sum = nR + nG + nB; Tfloat H = 0, S = 0, I = 0; if (theta>0) H = (nB<=nG)?theta:360-theta; if (sum>0) S = 1 - 3/sum*m; I = sum/3; *(p1++) = (T)H; *(p2++) = (T)S; *(p3++) = (T)I; } return *this; } CImg get_RGBtoHSI() const { return CImg(*this,false).RGBtoHSI(); } //! Convert color pixels from (H,S,I) to (R,G,B). CImg& HSItoRGB() { if (is_empty()) return *this; if (dim!=3) throw CImgInstanceException("CImg<%s>::HSItoRGB() : Input image dimension is dim=%u, " "should be a (H,S,I) image", pixel_type(),dim); T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); for (unsigned long N = width*height*depth; N; --N) { Tfloat H = (Tfloat)*p1, S = (Tfloat)*p2, I = (Tfloat)*p3, a = I*(1-S), R = 0, G = 0, B = 0; if (H<120) { B = a; R = (Tfloat)(I*(1+S*cimg_std::cos(H*cimg::valuePI/180)/cimg_std::cos((60-H)*cimg::valuePI/180))); G = 3*I-(R+B); } else if (H<240) { H-=120; R = a; G = (Tfloat)(I*(1+S*cimg_std::cos(H*cimg::valuePI/180)/cimg_std::cos((60-H)*cimg::valuePI/180))); B = 3*I-(R+G); } else { H-=240; G = a; B = (Tfloat)(I*(1+S*cimg_std::cos(H*cimg::valuePI/180)/cimg_std::cos((60-H)*cimg::valuePI/180))); R = 3*I-(G+B); } R*=255; G*=255; B*=255; *(p1++) = (T)(R<0?0:(R>255?255:R)); *(p2++) = (T)(G<0?0:(G>255?255:G)); *(p3++) = (T)(B<0?0:(B>255?255:B)); } return *this; } CImg get_HSItoRGB() const { return CImg< Tuchar>(*this,false).HSItoRGB(); } //! Convert color pixels from (R,G,B) to (Y,Cb,Cr)_8. CImg& RGBtoYCbCr() { if (is_empty()) return *this; if (dim!=3) throw CImgInstanceException("CImg<%s>::RGBtoYCbCr() : Input image dimension is dim=%u, " "should be a (R,G,B) image (dim=3)", pixel_type(),dim); T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); for (unsigned long N = width*height*depth; N; --N) { const Tfloat R = (Tfloat)*p1, G = (Tfloat)*p2, B = (Tfloat)*p3, Y = (66*R + 129*G + 25*B + 128)/256 + 16, Cb = (-38*R - 74*G + 112*B + 128)/256 + 128, Cr = (112*R - 94*G - 18*B + 128)/256 + 128; *(p1++) = (T)(Y<0?0:(Y>255?255:Y)); *(p2++) = (T)(Cb<0?0:(Cb>255?255:Cb)); *(p3++) = (T)(Cr<0?0:(Cr>255?255:Cr)); } return *this; } CImg get_RGBtoYCbCr() const { return CImg(*this,false).RGBtoYCbCr(); } //! Convert color pixels from (R,G,B) to (Y,Cb,Cr)_8. CImg& YCbCrtoRGB() { if (is_empty()) return *this; if (dim!=3) throw CImgInstanceException("CImg<%s>::YCbCrtoRGB() : Input image dimension is dim=%u, " "should be a (Y,Cb,Cr)_8 image (dim=3)", pixel_type(),dim); T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); for (unsigned long N = width*height*depth; N; --N) { const Tfloat Y = (Tfloat)*p1 - 16, Cb = (Tfloat)*p2 - 128, Cr = (Tfloat)*p3 - 128, R = (298*Y + 409*Cr + 128)/256, G = (298*Y - 100*Cb - 208*Cr + 128)/256, B = (298*Y + 516*Cb + 128)/256; *(p1++) = (T)(R<0?0:(R>255?255:R)); *(p2++) = (T)(G<0?0:(G>255?255:G)); *(p3++) = (T)(B<0?0:(B>255?255:B)); } return *this; } CImg get_YCbCrtoRGB() const { return CImg(*this,false).YCbCrtoRGB(); } //! Convert color pixels from (R,G,B) to (Y,U,V). CImg& RGBtoYUV() { if (is_empty()) return *this; if (dim!=3) throw CImgInstanceException("CImg<%s>::RGBtoYUV() : Input image dimension is dim=%u, " "should be a (R,G,B) image (dim=3)", pixel_type(),dim); T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); for (unsigned long N = width*height*depth; N; --N) { const Tfloat R = (Tfloat)*p1/255, G = (Tfloat)*p2/255, B = (Tfloat)*p3/255, Y = 0.299f*R + 0.587f*G + 0.114f*B; *(p1++) = (T)Y; *(p2++) = (T)(0.492f*(B-Y)); *(p3++) = (T)(0.877*(R-Y)); } return *this; } CImg get_RGBtoYUV() const { return CImg(*this,false).RGBtoYUV(); } //! Convert color pixels from (Y,U,V) to (R,G,B). CImg& YUVtoRGB() { if (is_empty()) return *this; if (dim!=3) throw CImgInstanceException("CImg<%s>::YUVtoRGB() : Input image dimension is dim=%u, " "should be a (Y,U,V) image (dim=3)", pixel_type(),dim); T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); for (unsigned long N = width*height*depth; N; --N) { const Tfloat Y = (Tfloat)*p1, U = (Tfloat)*p2, V = (Tfloat)*p3, R = (Y + 1.140f*V)*255, G = (Y - 0.395f*U - 0.581f*V)*255, B = (Y + 2.032f*U)*255; *(p1++) = (T)(R<0?0:(R>255?255:R)); *(p2++) = (T)(G<0?0:(G>255?255:G)); *(p3++) = (T)(B<0?0:(B>255?255:B)); } return *this; } CImg get_YUVtoRGB() const { return CImg< Tuchar>(*this,false).YUVtoRGB(); } //! Convert color pixels from (R,G,B) to (C,M,Y). CImg& RGBtoCMY() { if (is_empty()) return *this; if (dim!=3) throw CImgInstanceException("CImg<%s>::RGBtoCMY() : Input image dimension is dim=%u, " "should be a (R,G,B) image (dim=3)", pixel_type(),dim); T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); for (unsigned long N = width*height*depth; N; --N) { const Tfloat R = (Tfloat)*p1/255, G = (Tfloat)*p2/255, B = (Tfloat)*p3/255; *(p1++) = (T)(1 - R); *(p2++) = (T)(1 - G); *(p3++) = (T)(1 - B); } return *this; } CImg get_RGBtoCMY() const { return CImg(*this,false).RGBtoCMY(); } //! Convert (C,M,Y) pixels of a color image into the (R,G,B) color space. CImg& CMYtoRGB() { if (is_empty()) return *this; if (dim!=3) throw CImgInstanceException("CImg<%s>::CMYtoRGB() : Input image dimension is dim=%u, " "should be a (C,M,Y) image (dim=3)", pixel_type(),dim); T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); for (unsigned long N = width*height*depth; N; --N) { const Tfloat C = (Tfloat)*p1, M = (Tfloat)*p2, Y = (Tfloat)*p3, R = 255*(1 - C), G = 255*(1 - M), B = 255*(1 - Y); *(p1++) = (T)(R<0?0:(R>255?255:R)); *(p2++) = (T)(G<0?0:(G>255?255:G)); *(p3++) = (T)(B<0?0:(B>255?255:B)); } return *this; } CImg get_CMYtoRGB() const { return CImg(*this,false).CMYtoRGB(); } //! Convert color pixels from (C,M,Y) to (C,M,Y,K). CImg& CMYtoCMYK() { return get_CMYtoCMYK().transfer_to(*this); } CImg get_CMYtoCMYK() const { if (is_empty()) return *this; if (dim!=3) throw CImgInstanceException("CImg<%s>::CMYtoCMYK() : Input image dimension is dim=%u, " "should be a (C,M,Y) image (dim=3)", pixel_type(),dim); CImg res(width,height,depth,4); const T *ps1 = ptr(0,0,0,0), *ps2 = ptr(0,0,0,1), *ps3 = ptr(0,0,0,2); Tfloat *pd1 = res.ptr(0,0,0,0), *pd2 = res.ptr(0,0,0,1), *pd3 = res.ptr(0,0,0,2), *pd4 = res.ptr(0,0,0,3); for (unsigned long N = width*height*depth; N; --N) { Tfloat C = (Tfloat)*(ps1++), M = (Tfloat)*(ps2++), Y = (Tfloat)*(ps3++), K = cimg::min(C,M,Y); if (K==1) C = M = Y = 0; else { const Tfloat K1 = 1 - K; C = (C - K)/K1; M = (M - K)/K1; Y = (Y - K)/K1; } *(pd1++) = C; *(pd2++) = M; *(pd3++) = Y; *(pd4++) = K; } return res; } //! Convert (C,M,Y,K) pixels of a color image into the (C,M,Y) color space. CImg& CMYKtoCMY() { return get_CMYKtoCMY().transfer_to(*this); } CImg get_CMYKtoCMY() const { if (is_empty()) return *this; if (dim!=4) throw CImgInstanceException("CImg<%s>::CMYKtoCMY() : Input image dimension is dim=%u, " "should be a (C,M,Y,K) image (dim=4)", pixel_type(),dim); CImg res(width,height,depth,3); const T *ps1 = ptr(0,0,0,0), *ps2 = ptr(0,0,0,1), *ps3 = ptr(0,0,0,2), *ps4 = ptr(0,0,0,3); Tfloat *pd1 = res.ptr(0,0,0,0), *pd2 = res.ptr(0,0,0,1), *pd3 = res.ptr(0,0,0,2); for (unsigned long N = width*height*depth; N; --N) { const Tfloat C = (Tfloat)*ps1, M = (Tfloat)*ps2, Y = (Tfloat)*ps3, K = (Tfloat)*ps4, K1 = 1 - K; *(pd1++) = C*K1 + K; *(pd2++) = M*K1 + K; *(pd3++) = Y*K1 + K; } return res; } //! Convert color pixels from (R,G,B) to (X,Y,Z)_709. CImg& RGBtoXYZ() { if (is_empty()) return *this; if (dim!=3) throw CImgInstanceException("CImg<%s>::RGBtoXYZ() : Input image dimension is dim=%u, " "should be a (R,G,B) image (dim=3)", pixel_type(),dim); T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); for (unsigned long N = width*height*depth; N; --N) { const Tfloat R = (Tfloat)*p1/255, G = (Tfloat)*p2/255, B = (Tfloat)*p3/255; *(p1++) = (T)(0.412453f*R + 0.357580f*G + 0.180423f*B); *(p2++) = (T)(0.212671f*R + 0.715160f*G + 0.072169f*B); *(p3++) = (T)(0.019334f*R + 0.119193f*G + 0.950227f*B); } return *this; } CImg get_RGBtoXYZ() const { return CImg(*this,false).RGBtoXYZ(); } //! Convert (X,Y,Z)_709 pixels of a color image into the (R,G,B) color space. CImg& XYZtoRGB() { if (is_empty()) return *this; if (dim!=3) throw CImgInstanceException("CImg<%s>::XYZtoRGB() : Input image dimension is dim=%u, " "should be a (X,Y,Z) image (dim=3)", pixel_type(),dim); T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); for (unsigned long N = width*height*depth; N; --N) { const Tfloat X = (Tfloat)*p1*255, Y = (Tfloat)*p2*255, Z = (Tfloat)*p3*255, R = 3.240479f*X - 1.537150f*Y - 0.498535f*Z, G = -0.969256f*X + 1.875992f*Y + 0.041556f*Z, B = 0.055648f*X - 0.204043f*Y + 1.057311f*Z; *(p1++) = (T)(R<0?0:(R>255?255:R)); *(p2++) = (T)(G<0?0:(G>255?255:G)); *(p3++) = (T)(B<0?0:(B>255?255:B)); } return *this; } CImg get_XYZtoRGB() const { return CImg(*this,false).XYZtoRGB(); } //! Convert (X,Y,Z)_709 pixels of a color image into the (L*,a*,b*) color space. CImg& XYZtoLab() { #define _cimg_Labf(x) ((x)>=0.008856f?(cimg_std::pow(x,(Tfloat)1/3)):(7.787f*(x)+16.0f/116)) if (is_empty()) return *this; if (dim!=3) throw CImgInstanceException("CImg<%s>::XYZtoLab() : Input image dimension is dim=%u, " "should be a (X,Y,Z) image (dim=3)", pixel_type(),dim); const Tfloat Xn = (Tfloat)(0.412453f + 0.357580f + 0.180423f), Yn = (Tfloat)(0.212671f + 0.715160f + 0.072169f), Zn = (Tfloat)(0.019334f + 0.119193f + 0.950227f); T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); for (unsigned long N = width*height*depth; N; --N) { const Tfloat X = (Tfloat)*p1, Y = (Tfloat)*p2, Z = (Tfloat)*p3, XXn = X/Xn, YYn = Y/Yn, ZZn = Z/Zn, fX = (Tfloat)_cimg_Labf(XXn), fY = (Tfloat)_cimg_Labf(YYn), fZ = (Tfloat)_cimg_Labf(ZZn); *(p1++) = (T)(116*fY - 16); *(p2++) = (T)(500*(fX - fY)); *(p3++) = (T)(200*(fY - fZ)); } return *this; } CImg get_XYZtoLab() const { return CImg(*this,false).XYZtoLab(); } //! Convert (L,a,b) pixels of a color image into the (X,Y,Z) color space. CImg& LabtoXYZ() { #define _cimg_Labfi(x) ((x)>=0.206893f?((x)*(x)*(x)):(((x)-16.0f/116)/7.787f)) if (is_empty()) return *this; if (dim!=3) throw CImgInstanceException("CImg<%s>::LabtoXYZ() : Input image dimension is dim=%u, " "should be a (X,Y,Z) image (dim=3)", pixel_type(),dim); const Tfloat Xn = (Tfloat)(0.412453f + 0.357580f + 0.180423f), Yn = (Tfloat)(0.212671f + 0.715160f + 0.072169f), Zn = (Tfloat)(0.019334f + 0.119193f + 0.950227f); T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); for (unsigned long N = width*height*depth; N; --N) { const Tfloat L = (Tfloat)*p1, a = (Tfloat)*p2, b = (Tfloat)*p3, cY = (L + 16)/116, Y = (Tfloat)(Yn*_cimg_Labfi(cY)), pY = (Tfloat)cimg_std::pow(Y/Yn,(Tfloat)1/3), cX = a/500 + pY, X = Xn*cX*cX*cX, cZ = pY - b/200, Z = Zn*cZ*cZ*cZ; *(p1++) = (T)(X); *(p2++) = (T)(Y); *(p3++) = (T)(Z); } return *this; } CImg get_LabtoXYZ() const { return CImg(*this,false).LabtoXYZ(); } //! Convert (X,Y,Z)_709 pixels of a color image into the (x,y,Y) color space. CImg& XYZtoxyY() { if (is_empty()) return *this; if (dim!=3) throw CImgInstanceException("CImg<%s>::XYZtoxyY() : Input image dimension is dim=%u, " "should be a (X,Y,Z) image (dim=3)", pixel_type(),dim); T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); for (unsigned long N = width*height*depth; N; --N) { const Tfloat X = (Tfloat)*p1, Y = (Tfloat)*p2, Z = (Tfloat)*p3, sum = (X+Y+Z), nsum = sum>0?sum:1; *(p1++) = (T)(X/nsum); *(p2++) = (T)(Y/nsum); *(p3++) = (T)Y; } return *this; } CImg get_XYZtoxyY() const { return CImg(*this,false).XYZtoxyY(); } //! Convert (x,y,Y) pixels of a color image into the (X,Y,Z)_709 color space. CImg& xyYtoXYZ() { if (is_empty()) return *this; if (dim!=3) throw CImgInstanceException("CImg<%s>::xyYtoXYZ() : Input image dimension is dim=%u, " "should be a (x,y,Y) image (dim=3)", pixel_type(),dim); T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); for (unsigned long N = width*height*depth; N; --N) { const Tfloat px = (Tfloat)*p1, py = (Tfloat)*p2, Y = (Tfloat)*p3, ny = py>0?py:1; *(p1++) = (T)(px*Y/ny); *(p2++) = (T)Y; *(p3++) = (T)((1-px-py)*Y/ny); } return *this; } CImg get_xyYtoXYZ() const { return CImg(*this,false).xyYtoXYZ(); } //! Convert a (R,G,B) image to a (L,a,b) one. CImg& RGBtoLab() { return RGBtoXYZ().XYZtoLab(); } CImg get_RGBtoLab() const { return CImg(*this,false).RGBtoLab(); } //! Convert a (L,a,b) image to a (R,G,B) one. CImg& LabtoRGB() { return LabtoXYZ().XYZtoRGB(); } CImg get_LabtoRGB() const { return CImg(*this,false).LabtoRGB(); } //! Convert a (R,G,B) image to a (x,y,Y) one. CImg& RGBtoxyY() { return RGBtoXYZ().XYZtoxyY(); } CImg get_RGBtoxyY() const { return CImg(*this,false).RGBtoxyY(); } //! Convert a (x,y,Y) image to a (R,G,B) one. CImg& xyYtoRGB() { return xyYtoXYZ().XYZtoRGB(); } CImg get_xyYtoRGB() const { return CImg(*this,false).xyYtoRGB(); } //! Convert a (R,G,B) image to a (C,M,Y,K) one. CImg& RGBtoCMYK() { return RGBtoCMY().CMYtoCMYK(); } CImg get_RGBtoCMYK() const { return CImg(*this,false).RGBtoCMYK(); } //! Convert a (C,M,Y,K) image to a (R,G,B) one. CImg& CMYKtoRGB() { return CMYKtoCMY().CMYtoRGB(); } CImg get_CMYKtoRGB() const { return CImg(*this,false).CMYKtoRGB(); } //! Convert a (R,G,B) image to a Bayer-coded representation. /** \note First (upper-left) pixel if the red component of the pixel color. **/ CImg& RGBtoBayer() { return get_RGBtoBayer().transfer_to(*this); } CImg get_RGBtoBayer() const { if (is_empty()) return *this; if (dim!=3) throw CImgInstanceException("CImg<%s>::RGBtoBayer() : Input image dimension is dim=%u, " "should be a (R,G,B) image (dim=3)", pixel_type(),dim); CImg res(width,height,depth,1); const T *pR = ptr(0,0,0,0), *pG = ptr(0,0,0,1), *pB = ptr(0,0,0,2); T *ptrd = res.data; cimg_forXYZ(*this,x,y,z) { if (y%2) { if (x%2) *(ptrd++) = *pB; else *(ptrd++) = *pG; } else { if (x%2) *(ptrd++) = *pG; else *(ptrd++) = *pR; } ++pR; ++pG; ++pB; } return res; } //! Convert a Bayer-coded image to a (R,G,B) color image. CImg& BayertoRGB(const unsigned int interpolation_type=3) { return get_BayertoRGB(interpolation_type).transfer_to(*this); } CImg get_BayertoRGB(const unsigned int interpolation_type=3) const { if (is_empty()) return *this; if (dim!=1) throw CImgInstanceException("CImg<%s>::BayertoRGB() : Input image dimension is dim=%u, " "should be a Bayer image (dim=1)", pixel_type(),dim); CImg res(width,height,depth,3); CImg_3x3(I,T); Tuchar *pR = res.ptr(0,0,0,0), *pG = res.ptr(0,0,0,1), *pB = res.ptr(0,0,0,2); switch (interpolation_type) { case 3 : { // Edge-directed CImg_3x3(R,T); CImg_3x3(G,T); CImg_3x3(B,T); cimg_forXYZ(*this,x,y,z) { const int _p1x = x?x-1:1, _p1y = y?y-1:1, _n1x = x CImg& _draw_scanline(const int x0, const int x1, const int y, const tc *const color, const float opacity=1, const float brightness=1, const bool init=false) { static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); static float nopacity = 0, copacity = 0; static unsigned int whz = 0; static const tc *col = 0; if (init) { nopacity = cimg::abs(opacity); copacity = 1 - cimg::max(opacity,0); whz = width*height*depth; } else { const int nx0 = x0>0?x0:0, nx1 = x1=0) { col = color; const unsigned int off = whz-dx-1; T *ptrd = ptr(nx0,y); if (opacity>=1) { // ** Opaque drawing ** if (brightness==1) { // Brightness==1 if (sizeof(T)!=1) cimg_forV(*this,k) { const T val = (T)*(col++); for (int x = dx; x>=0; --x) *(ptrd++) = val; ptrd+=off; } else cimg_forV(*this,k) { const T val = (T)*(col++); cimg_std::memset(ptrd,(int)val,dx+1); ptrd+=whz; } } else if (brightness<1) { // Brightness<1 if (sizeof(T)!=1) cimg_forV(*this,k) { const T val = (T)(*(col++)*brightness); for (int x = dx; x>=0; --x) *(ptrd++) = val; ptrd+=off; } else cimg_forV(*this,k) { const T val = (T)(*(col++)*brightness); cimg_std::memset(ptrd,(int)val,dx+1); ptrd+=whz; } } else { // Brightness>1 if (sizeof(T)!=1) cimg_forV(*this,k) { const T val = (T)((2-brightness)**(col++) + (brightness-1)*maxval); for (int x = dx; x>=0; --x) *(ptrd++) = val; ptrd+=off; } else cimg_forV(*this,k) { const T val = (T)((2-brightness)**(col++) + (brightness-1)*maxval); cimg_std::memset(ptrd,(int)val,dx+1); ptrd+=whz; } } } else { // ** Transparent drawing ** if (brightness==1) { // Brightness==1 cimg_forV(*this,k) { const T val = (T)*(col++); for (int x = dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ++ptrd; } ptrd+=off; } } else if (brightness<=1) { // Brightness<1 cimg_forV(*this,k) { const T val = (T)(*(col++)*brightness); for (int x = dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ++ptrd; } ptrd+=off; } } else { // Brightness>1 cimg_forV(*this,k) { const T val = (T)((2-brightness)**(col++) + (brightness-1)*maxval); for (int x = dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ++ptrd; } ptrd+=off; } } } } } return *this; } template CImg& _draw_scanline(const tc *const color, const float opacity=1) { return _draw_scanline(0,0,0,color,opacity,0,true); } //! Draw a 2D colored point (pixel). /** \param x0 X-coordinate of the point. \param y0 Y-coordinate of the point. \param color Pointer to \c dimv() consecutive values, defining the color values. \param opacity Drawing opacity (optional). \note - Clipping is supported. - To set pixel values without clipping needs, you should use the faster CImg::operator()() function. \par Example: \code CImg img(100,100,1,3,0); const unsigned char color[] = { 255,128,64 }; img.draw_point(50,50,color); \endcode **/ template CImg& draw_point(const int x0, const int y0, const tc *const color, const float opacity=1) { return draw_point(x0,y0,0,color,opacity); } //! Draw a 2D colored point (pixel). template CImg& draw_point(const int x0, const int y0, const CImg& color, const float opacity=1) { return draw_point(x0,y0,color.data,opacity); } //! Draw a 3D colored point (voxel). template CImg& draw_point(const int x0, const int y0, const int z0, const tc *const color, const float opacity=1) { if (is_empty()) return *this; if (!color) throw CImgArgumentException("CImg<%s>::draw_point() : Specified color is (null)", pixel_type()); if (x0>=0 && y0>=0 && z0>=0 && x0=1) cimg_forV(*this,k) { *ptrd = (T)*(col++); ptrd+=whz; } else cimg_forV(*this,k) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whz; } } return *this; } //! Draw a 3D colored point (voxel). template CImg& draw_point(const int x0, const int y0, const int z0, const CImg& color, const float opacity=1) { return draw_point(x0,y0,z0,color.data,opacity); } // Draw a cloud of colored point (internal). template CImg& _draw_point(const t& points, const unsigned int W, const unsigned int H, const tc *const color, const float opacity) { if (is_empty() || !points || !W) return *this; switch (H) { case 0 : case 1 : throw CImgArgumentException("CImg<%s>::draw_point() : Given list of points is not valid.", pixel_type()); case 2 : { for (unsigned int i = 0; i img(100,100,1,3,0); const unsigned char color[] = { 255,128,64 }; CImgList points; points.insert(CImg::vector(0,0)). .insert(CImg::vector(70,10)). .insert(CImg::vector(80,60)). .insert(CImg::vector(10,90)); img.draw_point(points,color); \endcode **/ template CImg& draw_point(const CImgList& points, const tc *const color, const float opacity=1) { unsigned int H = ~0U; cimglist_for(points,p) H = cimg::min(H,(unsigned int)(points[p].size())); return _draw_point(points,points.size,H,color,opacity); } //! Draw a cloud of colored points. template CImg& draw_point(const CImgList& points, const CImg& color, const float opacity=1) { return draw_point(points,color.data,opacity); } //! Draw a cloud of colored points. /** \note - Similar to the previous function, where the N vertex coordinates are stored as a Nx2 or Nx3 image (sequence of vectors aligned along the x-axis). **/ template CImg& draw_point(const CImg& points, const tc *const color, const float opacity=1) { return _draw_point(points,points.width,points.height,color,opacity); } //! Draw a cloud of colored points. template CImg& draw_point(const CImg& points, const CImg& color, const float opacity=1) { return draw_point(points,color.data,opacity); } //! Draw a 2D colored line. /** \param x0 X-coordinate of the starting line point. \param y0 Y-coordinate of the starting line point. \param x1 X-coordinate of the ending line point. \param y1 Y-coordinate of the ending line point. \param color Pointer to \c dimv() consecutive values of type \c T, defining the drawing color. \param opacity Drawing opacity (optional). \param pattern An integer whose bits describe the line pattern (optional). \param init_hatch Flag telling if a reinitialization of the hash state must be done (optional). \note - Clipping is supported. - Line routine uses Bresenham's algorithm. - Set \p init_hatch = false to draw consecutive hatched segments without breaking the line pattern. \par Example: \code CImg img(100,100,1,3,0); const unsigned char color[] = { 255,128,64 }; img.draw_line(40,40,80,70,color); \endcode **/ template CImg& 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) { if (is_empty()) return *this; if (!color) throw CImgArgumentException("CImg<%s>::draw_line() : Specified color is (null)", pixel_type()); static unsigned int hatch = ~0U - (~0U>>1); if (init_hatch) hatch = ~0U - (~0U>>1); const bool xdir = x0=dimx()) return *this; if (xleft<0) { yleft-=xleft*(yright - yleft)/(xright - xleft); xleft = 0; } if (xright>=dimx()) { yright-=(xright - dimx())*(yright - yleft)/(xright - xleft); xright = dimx()-1; } if (ydown<0 || yup>=dimy()) return *this; if (yup<0) { xup-=yup*(xdown - xup)/(ydown - yup); yup = 0; } if (ydown>=dimy()) { xdown-=(ydown - dimy())*(xdown - xup)/(ydown - yup); ydown = dimy()-1; } T *ptrd0 = ptr(nx0,ny0); int dx = xright - xleft, dy = ydown - yup; const bool steep = dy>dx; if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy); const int offx = (nx0=1) { if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { if (pattern&hatch) { T *ptrd = ptrd0; const tc* col = color; cimg_forV(*this,k) { *ptrd = (T)*(col++); ptrd+=wh; }} hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); ptrd0+=offx; if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } } else for (int error = dx>>1, x = 0; x<=dx; ++x) { T *ptrd = ptrd0; const tc* col = color; cimg_forV(*this,k) { *ptrd = (T)*(col++); ptrd+=wh; } ptrd0+=offx; if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } } } else { const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { if (pattern&hatch) { T *ptrd = ptrd0; const tc* col = color; cimg_forV(*this,k) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; } } hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); ptrd0+=offx; if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } } else for (int error = dx>>1, x = 0; x<=dx; ++x) { T *ptrd = ptrd0; const tc* col = color; cimg_forV(*this,k) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; } ptrd0+=offx; if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } } } return *this; } //! Draw a 2D colored line. template CImg& draw_line(const int x0, const int y0, const int x1, const int y1, const CImg& color, const float opacity=1, const unsigned int pattern=~0U, const bool init_hatch=true) { return draw_line(x0,y0,x1,y1,color.data,opacity,pattern,init_hatch); } //! Draw a 2D colored line, with z-buffering. template CImg& draw_line(float *const 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) { if (!is_empty() && z0>0 && z1>0) { if (!color) throw CImgArgumentException("CImg<%s>::draw_line() : Specified color is (null).", pixel_type()); static unsigned int hatch = ~0U - (~0U>>1); if (init_hatch) hatch = ~0U - (~0U>>1); const bool xdir = x0=dimx()) return *this; if (xleft<0) { const int D = xright - xleft; yleft-=xleft*(yright - yleft)/D; zleft-=xleft*(zright - zleft)/D; xleft = 0; } if (xright>=dimx()) { const int d = xright - dimx(), D = xright - xleft; yright-=d*(yright - yleft)/D; zright-=d*(zright - zleft)/D; xright = dimx()-1; } if (ydown<0 || yup>=dimy()) return *this; if (yup<0) { const int D = ydown - yup; xup-=yup*(xdown - xup)/D; zup-=yup*(zdown - zup)/D; yup = 0; } if (ydown>=dimy()) { const int d = ydown - dimy(), D = ydown - yup; xdown-=d*(xdown - xup)/D; zdown-=d*(zdown - zup)/D; ydown = dimy()-1; } T *ptrd0 = ptr(nx0,ny0); float *ptrz = zbuffer + nx0 + ny0*width; int dx = xright - xleft, dy = ydown - yup; const bool steep = dy>dx; if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy); const int offx = (nx00?dx:1; if (opacity>=1) { if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { const float z = Z0 + x*dz/ndx; if (z>*ptrz && pattern&hatch) { *ptrz = z; T *ptrd = ptrd0; const tc *col = color; cimg_forV(*this,k) { *ptrd = (T)*(col++); ptrd+=wh; } } hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); ptrd0+=offx; ptrz+=offx; if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; } } else for (int error = dx>>1, x = 0; x<=dx; ++x) { const float z = Z0 + x*dz/ndx; if (z>*ptrz) { *ptrz = z; T *ptrd = ptrd0; const tc *col = color; cimg_forV(*this,k) { *ptrd = (T)*(col++); ptrd+=wh; } } ptrd0+=offx; ptrz+=offx; if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; } } } else { const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { const float z = Z0 + x*dz/ndx; if (z>*ptrz && pattern&hatch) { *ptrz = z; T *ptrd = ptrd0; const tc *col = color; cimg_forV(*this,k) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; } } hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); ptrd0+=offx; ptrz+=offx; if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; } } else for (int error = dx>>1, x = 0; x<=dx; ++x) { const float z = Z0 + x*dz/ndx; if (z>*ptrz) { *ptrz = z; T *ptrd = ptrd0; const tc *col = color; cimg_forV(*this,k) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; } } ptrd0+=offx; ptrz+=offx; if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; } } } } return *this; } //! Draw a 2D colored line, with z-buffering. template CImg& draw_line(float *const zbuffer, const int x0, const int y0, const float z0, const int x1, const int y1, const float z1, const CImg& color, const float opacity=1, const unsigned int pattern=~0U, const bool init_hatch=true) { return draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color.data,opacity,pattern,init_hatch); } //! Draw a 3D colored line. template CImg& 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) { if (is_empty()) return *this; if (!color) throw CImgArgumentException("CImg<%s>::draw_line() : Specified color is (null)", pixel_type()); static unsigned int hatch = ~0U - (~0U>>1); if (init_hatch) hatch = ~0U - (~0U>>1); int nx0 = x0, ny0 = y0, nz0 = z0, nx1 = x1, ny1 = y1, nz1 = z1; if (nx0>nx1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1); if (nx1<0 || nx0>=dimx()) return *this; if (nx0<0) { const int D = 1 + nx1 - nx0; ny0-=nx0*(1 + ny1 - ny0)/D; nz0-=nx0*(1 + nz1 - nz0)/D; nx0 = 0; } if (nx1>=dimx()) { const int d = nx1-dimx(), D = 1 + nx1 - nx0; ny1+=d*(1 + ny0 - ny1)/D; nz1+=d*(1 + nz0 - nz1)/D; nx1 = dimx()-1; } if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1); if (ny1<0 || ny0>=dimy()) return *this; if (ny0<0) { const int D = 1 + ny1 - ny0; nx0-=ny0*(1 + nx1 - nx0)/D; nz0-=ny0*(1 + nz1 - nz0)/D; ny0 = 0; } if (ny1>=dimy()) { const int d = ny1-dimy(), D = 1 + ny1 - ny0; nx1+=d*(1 + nx0 - nx1)/D; nz1+=d*(1 + nz0 - nz1)/D; ny1 = dimy()-1; } if (nz0>nz1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1); if (nz1<0 || nz0>=dimz()) return *this; if (nz0<0) { const int D = 1 + nz1 - nz0; nx0-=nz0*(1 + nx1 - nx0)/D; ny0-=nz0*(1 + ny1 - ny0)/D; nz0 = 0; } if (nz1>=dimz()) { const int d = nz1-dimz(), D = 1 + nz1 - nz0; nx1+=d*(1 + nx0 - nx1)/D; ny1+=d*(1 + ny0 - ny1)/D; nz1 = dimz()-1; } const unsigned int dmax = cimg::max(cimg::abs(nx1 - nx0),cimg::abs(ny1 - ny0),nz1 - nz0), whz = width*height*depth; const float px = (nx1 - nx0)/(float)dmax, py = (ny1 - ny0)/(float)dmax, pz = (nz1 - nz0)/(float)dmax; float x = (float)nx0, y = (float)ny0, z = (float)nz0; if (opacity>=1) for (unsigned int t = 0; t<=dmax; ++t) { if (!(~pattern) || (~pattern && pattern&hatch)) { T* ptrd = ptr((unsigned int)x,(unsigned int)y,(unsigned int)z); const tc *col = color; cimg_forV(*this,k) { *ptrd = (T)*(col++); ptrd+=whz; } } x+=px; y+=py; z+=pz; if (pattern) { hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); } } else { const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); for (unsigned int t = 0; t<=dmax; ++t) { if (!(~pattern) || (~pattern && pattern&hatch)) { T* ptrd = ptr((unsigned int)x,(unsigned int)y,(unsigned int)z); const tc *col = color; cimg_forV(*this,k) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whz; } } x+=px; y+=py; z+=pz; if (pattern) { hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); } } } return *this; } //! Draw a 3D colored line. template CImg& draw_line(const int x0, const int y0, const int z0, const int x1, const int y1, const int z1, const CImg& color, const float opacity=1, const unsigned int pattern=~0U, const bool init_hatch=true) { return draw_line(x0,y0,z0,x1,y1,z1,color.data,opacity,pattern,init_hatch); } //! Draw a 2D textured line. /** \param x0 X-coordinate of the starting line point. \param y0 Y-coordinate of the starting line point. \param x1 X-coordinate of the ending line point. \param y1 Y-coordinate of the ending line point. \param texture Texture image defining the pixel colors. \param tx0 X-coordinate of the starting texture point. \param ty0 Y-coordinate of the starting texture point. \param tx1 X-coordinate of the ending texture point. \param ty1 Y-coordinate of the ending texture point. \param opacity Drawing opacity (optional). \param pattern An integer whose bits describe the line pattern (optional). \param init_hatch Flag telling if the hash variable must be reinitialized (optional). \note - Clipping is supported but not for texture coordinates. - Line routine uses the well known Bresenham's algorithm. \par Example: \code CImg img(100,100,1,3,0), texture("texture256x256.ppm"); const unsigned char color[] = { 255,128,64 }; img.draw_line(40,40,80,70,texture,0,0,255,255); \endcode **/ template CImg& draw_line(const int x0, const int y0, const int x1, const int y1, const CImg& 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) { if (is_empty()) return *this; if (!texture || texture.dim::draw_line() : Specified texture (%u,%u,%u,%u,%p) is not a valid argument.", pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); if (is_overlapped(texture)) return draw_line(x0,y0,x1,y1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch); static unsigned int hatch = ~0U - (~0U>>1); if (init_hatch) hatch = ~0U - (~0U>>1); const bool xdir = x0=dimx()) return *this; if (xleft<0) { const int D = xright - xleft; yleft-=xleft*(yright - yleft)/D; txleft-=xleft*(txright - txleft)/D; tyleft-=xleft*(tyright - tyleft)/D; xleft = 0; } if (xright>=dimx()) { const int d = xright - dimx(), D = xright - xleft; yright-=d*(yright - yleft)/D; txright-=d*(txright - txleft)/D; tyright-=d*(tyright - tyleft)/D; xright = dimx()-1; } if (ydown<0 || yup>=dimy()) return *this; if (yup<0) { const int D = ydown - yup; xup-=yup*(xdown - xup)/D; txup-=yup*(txdown - txup)/D; tyup-=yup*(tydown - tyup)/D; yup = 0; } if (ydown>=dimy()) { const int d = ydown - dimy(), D = ydown - yup; xdown-=d*(xdown - xup)/D; txdown-=d*(txdown - txup)/D; tydown-=d*(tydown - tyup)/D; ydown = dimy()-1; } T *ptrd0 = ptr(nx0,ny0); int dx = xright - xleft, dy = ydown - yup; const bool steep = dy>dx; if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy); const int offx = (nx00?dx:1; if (opacity>=1) { if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { if (pattern&hatch) { T *ptrd = ptrd0; const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx; cimg_forV(*this,k) { *ptrd = (T)texture(tx,ty,0,k); ptrd+=wh; } } hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); ptrd0+=offx; if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } } else for (int error = dx>>1, x = 0; x<=dx; ++x) { T *ptrd = ptrd0; const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx; cimg_forV(*this,k) { *ptrd = (T)texture(tx,ty,0,k); ptrd+=wh; } ptrd0+=offx; if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } } } else { const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { T *ptrd = ptrd0; if (pattern&hatch) { const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx; cimg_forV(*this,k) { *ptrd = (T)(nopacity*texture(tx,ty,0,k) + *ptrd*copacity); ptrd+=wh; } } hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); ptrd0+=offx; if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } } else for (int error = dx>>1, x = 0; x<=dx; ++x) { T *ptrd = ptrd0; const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx; cimg_forV(*this,k) { *ptrd = (T)(nopacity*texture(tx,ty,0,k) + *ptrd*copacity); ptrd+=wh; } ptrd0+=offx; if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } } } return *this; } //! Draw a 2D textured line, with perspective correction. template CImg& draw_line(const int x0, const int y0, const float z0, const int x1, const int y1, const float z1, const CImg& 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) { if (is_empty() && z0<=0 && z1<=0) return *this; if (!texture || texture.dim::draw_line() : Specified texture (%u,%u,%u,%u,%p) is not a valid argument.", pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); if (is_overlapped(texture)) return draw_line(x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch); static unsigned int hatch = ~0U - (~0U>>1); if (init_hatch) hatch = ~0U - (~0U>>1); const bool xdir = x0=dimx()) return *this; if (xleft<0) { const int D = xright - xleft; yleft-=xleft*(yright - yleft)/D; zleft-=xleft*(zright - zleft)/D; txleft-=xleft*(txright - txleft)/D; tyleft-=xleft*(tyright - tyleft)/D; xleft = 0; } if (xright>=dimx()) { const int d = xright - dimx(), D = xright - xleft; yright-=d*(yright - yleft)/D; zright-=d*(zright - zleft)/D; txright-=d*(txright - txleft)/D; tyright-=d*(tyright - tyleft)/D; xright = dimx()-1; } if (ydown<0 || yup>=dimy()) return *this; if (yup<0) { const int D = ydown - yup; xup-=yup*(xdown - xup)/D; zup-=yup*(zdown - zup)/D; txup-=yup*(txdown - txup)/D; tyup-=yup*(tydown - tyup)/D; yup = 0; } if (ydown>=dimy()) { const int d = ydown - dimy(), D = ydown - yup; xdown-=d*(xdown - xup)/D; zdown-=d*(zdown - zup)/D; txdown-=d*(txdown - txup)/D; tydown-=d*(tydown - tyup)/D; ydown = dimy()-1; } T *ptrd0 = ptr(nx0,ny0); int dx = xright - xleft, dy = ydown - yup; const bool steep = dy>dx; if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy); const int offx = (nx00?dx:1; if (opacity>=1) { if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { if (pattern&hatch) { const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; T *ptrd = ptrd0; cimg_forV(*this,k) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,k); ptrd+=wh; } } hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); ptrd0+=offx; if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } } else for (int error = dx>>1, x = 0; x<=dx; ++x) { const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; T *ptrd = ptrd0; cimg_forV(*this,k) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,k); ptrd+=wh; } ptrd0+=offx; if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } } } else { const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { if (pattern&hatch) { const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; T *ptrd = ptrd0; cimg_forV(*this,k) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,k) + *ptrd*copacity); ptrd+=wh; } } hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); ptrd0+=offx; if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } } else for (int error = dx>>1, x = 0; x<=dx; ++x) { const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; T *ptrd = ptrd0; cimg_forV(*this,k) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,k) + *ptrd*copacity); ptrd+=wh; } ptrd0+=offx; if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } } } return *this; } //! Draw a 2D textured line, with z-buffering and perspective correction. template CImg& draw_line(float *const zbuffer, const int x0, const int y0, const float z0, const int x1, const int y1, const float z1, const CImg& 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) { if (!is_empty() && z0>0 && z1>0) { if (!texture || texture.dim::draw_line() : Specified texture (%u,%u,%u,%u,%p) is not a valid argument.", pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); if (is_overlapped(texture)) return draw_line(zbuffer,x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch); static unsigned int hatch = ~0U - (~0U>>1); if (init_hatch) hatch = ~0U - (~0U>>1); const bool xdir = x0=dimx()) return *this; if (xleft<0) { const int D = xright - xleft; yleft-=xleft*(yright - yleft)/D; zleft-=xleft*(zright - zleft)/D; txleft-=xleft*(txright - txleft)/D; tyleft-=xleft*(tyright - tyleft)/D; xleft = 0; } if (xright>=dimx()) { const int d = xright - dimx(), D = xright - xleft; yright-=d*(yright - yleft)/D; zright-=d*(zright - zleft)/D; txright-=d*(txright - txleft)/D; tyright-=d*(tyright - tyleft)/D; xright = dimx()-1; } if (ydown<0 || yup>=dimy()) return *this; if (yup<0) { const int D = ydown - yup; xup-=yup*(xdown - xup)/D; zup-=yup*(zdown - zup)/D; txup-=yup*(txdown - txup)/D; tyup-=yup*(tydown - tyup)/D; yup = 0; } if (ydown>=dimy()) { const int d = ydown - dimy(), D = ydown - yup; xdown-=d*(xdown - xup)/D; zdown-=d*(zdown - zup)/D; txdown-=d*(txdown - txup)/D; tydown-=d*(tydown - tyup)/D; ydown = dimy()-1; } T *ptrd0 = ptr(nx0,ny0); float *ptrz = zbuffer + nx0 + ny0*width; int dx = xright - xleft, dy = ydown - yup; const bool steep = dy>dx; if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy); const int offx = (nx00?dx:1; if (opacity>=1) { if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { if (pattern&hatch) { const float z = Z0 + x*dz/ndx; if (z>*ptrz) { *ptrz = z; const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; T *ptrd = ptrd0; cimg_forV(*this,k) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,k); ptrd+=wh; } } } hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); ptrd0+=offx; ptrz+=offx; if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; } } else for (int error = dx>>1, x = 0; x<=dx; ++x) { const float z = Z0 + x*dz/ndx; if (z>*ptrz) { *ptrz = z; const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; T *ptrd = ptrd0; cimg_forV(*this,k) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,k); ptrd+=wh; } } ptrd0+=offx; ptrz+=offx; if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; } } } else { const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { if (pattern&hatch) { const float z = Z0 + x*dz/ndx; if (z>*ptrz) { *ptrz = z; const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; T *ptrd = ptrd0; cimg_forV(*this,k) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,k) + *ptrd*copacity); ptrd+=wh; } } } hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); ptrd0+=offx; ptrz+=offx; if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; } } else for (int error = dx>>1, x = 0; x<=dx; ++x) { const float z = Z0 + x*dz/ndx; if (z>*ptrz) { *ptrz = z; const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; T *ptrd = ptrd0; cimg_forV(*this,k) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,k) + *ptrd*copacity); ptrd+=wh; } } ptrd0+=offx; ptrz+=offx; if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offx; error+=dx; } } } } return *this; } // Inner routine for drawing set of consecutive lines with generic type for coordinates. template CImg& _draw_line(const t& points, const unsigned int W, const unsigned int H, const tc *const color, const float opacity, const unsigned int pattern, const bool init_hatch) { if (is_empty() || !points || W<2) return *this; bool ninit_hatch = init_hatch; switch (H) { case 0 : case 1 : throw CImgArgumentException("CImg<%s>::draw_line() : Given list of points is not valid.", pixel_type()); case 2 : { const int x0 = (int)points(0,0), y0 = (int)points(0,1); int ox = x0, oy = y0; for (unsigned int i = 1; i img(100,100,1,3,0); const unsigned char color[] = { 255,128,64 }; CImgList points; points.insert(CImg::vector(0,0)). .insert(CImg::vector(70,10)). .insert(CImg::vector(80,60)). .insert(CImg::vector(10,90)); img.draw_line(points,color); \endcode **/ template CImg& draw_line(const CImgList& points, const tc *const color, const float opacity=1, const unsigned int pattern=~0U, const bool init_hatch=true) { unsigned int H = ~0U; cimglist_for(points,p) H = cimg::min(H,(unsigned int)(points[p].size())); return _draw_line(points,points.size,H,color,opacity,pattern,init_hatch); } //! Draw a set of consecutive colored lines in the instance image. template CImg& draw_line(const CImgList& points, const CImg& color, const float opacity=1, const unsigned int pattern=~0U, const bool init_hatch=true) { return draw_line(points,color.data,opacity,pattern,init_hatch); } //! Draw a set of consecutive colored lines in the instance image. /** \note - Similar to the previous function, where the N vertex coordinates are stored as a Nx2 or Nx3 image (sequence of vectors aligned along the x-axis). **/ template CImg& draw_line(const CImg& points, const tc *const color, const float opacity=1, const unsigned int pattern=~0U, const bool init_hatch=true) { return _draw_line(points,points.width,points.height,color,opacity,pattern,init_hatch); } //! Draw a set of consecutive colored lines in the instance image. template CImg& draw_line(const CImg& points, const CImg& color, const float opacity=1, const unsigned int pattern=~0U, const bool init_hatch=true) { return draw_line(points,color.data,opacity,pattern,init_hatch); } // Inner routine for a drawing filled polygon with generic type for coordinates. template CImg& _draw_polygon(const t& points, const unsigned int N, const tc *const color, const float opacity) { if (is_empty() || !points || N<3) return *this; if (!color) throw CImgArgumentException("CImg<%s>::draw_polygon() : Specified color is (null).", pixel_type()); _draw_scanline(color,opacity); int xmin = (int)(~0U>>1), xmax = 0, ymin = (int)(~0U>>1), ymax = 0; { for (unsigned int p = 0; pxmax) xmax = x; if (yymax) ymax = y; }} if (xmax<0 || xmin>=dimx() || ymax<0 || ymin>=dimy()) return *this; const unsigned int nymin = ymin<0?0:(unsigned int)ymin, nymax = ymax>=dimy()?height-1:(unsigned int)ymax, dy = 1 + nymax - nymin; CImg X(1+2*N,dy,1,1,0), tmp; int cx = (int)points(0,0), cy = (int)points(0,1); for (unsigned int cp = 0, p = 0; pay && cy>ny))?1:0; for (int x = cx, y = y0, _sx = 1, _sy = 1, _dx = nx>cx?nx-cx:((_sx=-1),cx-nx), _dy = y1>y0?y1-y0:((_sy=-1),y0-y1), _counter = ((_dx-=_dy?_dy*(_dx/_dy):0),_dy), _err = _dx>>1, _rx = _dy?(nx-cx)/_dy:0; _counter>=countermin; --_counter, y+=_sy, x+=_rx + ((_err-=_dx)<0?_err+=_dy,_sx:0)) if (y>=0 && y<(int)dy) X(++X(0,y),y) = x; cp = np; cx = nx; cy = ny; } else { const int pp = (cp?cp-1:N-1), py = (int)points(pp,1); if ((cy>py && ay>cy) || (cy CImg& draw_polygon(const CImgList& points, const tc *const color, const float opacity=1) { if (!points.is_sameY(2)) throw CImgArgumentException("CImg<%s>::draw_polygon() : Given list of points is not valid.", pixel_type()); return _draw_polygon(points,points.size,color,opacity); } //! Draw a filled polygon in the instance image. template CImg& draw_polygon(const CImgList& points, const CImg& color, const float opacity=1) { return draw_polygon(points,color.data,opacity); } //! Draw a filled polygon in the instance image. template CImg& draw_polygon(const CImg& points, const tc *const color, const float opacity=1) { if (points.height<2) throw CImgArgumentException("CImg<%s>::draw_polygon() : Given list of points is not valid.", pixel_type()); return _draw_polygon(points,points.width,color,opacity); } //! Draw a filled polygon in the instance image. template CImg& draw_polygon(const CImg& points, const CImg& color, const float opacity=1) { return draw_polygon(points,color.data,opacity); } // Inner routine for drawing an outlined polygon with generic point coordinates. template CImg& _draw_polygon(const t& points, const unsigned int W, const unsigned int H, const tc *const color, const float opacity, const unsigned int pattern) { if (is_empty() || !points || W<3) return *this; bool ninit_hatch = true; switch (H) { case 0 : case 1 : throw CImgArgumentException("CImg<%s>::draw_polygon() : Given list of points is not valid.", pixel_type()); case 2 : { const int x0 = (int)points(0,0), y0 = (int)points(0,1); int ox = x0, oy = y0; for (unsigned int i = 1; i CImg& draw_polygon(const CImgList& points, const tc *const color, const float opacity, const unsigned int pattern) { unsigned int H = ~0U; cimglist_for(points,p) H = cimg::min(H,(unsigned int)(points[p].size())); return _draw_polygon(points,points.size,H,color,opacity,pattern); } //! Draw a polygon outline. template CImg& draw_polygon(const CImgList& points, const CImg& color, const float opacity, const unsigned int pattern) { return draw_polygon(points,color.data,opacity,pattern); } //! Draw a polygon outline. template CImg& draw_polygon(const CImg& points, const tc *const color, const float opacity, const unsigned int pattern) { return _draw_polygon(points,points.width,points.height,color,opacity,pattern); } //! Draw a polygon outline. template CImg& draw_polygon(const CImg& points, const CImg& color, const float opacity, const unsigned int pattern) { return draw_polygon(points,color.data,opacity,pattern); } //! Draw a cubic spline curve in the instance image. /** \param x0 X-coordinate of the starting curve point \param y0 Y-coordinate of the starting curve point \param u0 X-coordinate of the starting velocity \param v0 Y-coordinate of the starting velocity \param x1 X-coordinate of the ending curve point \param y1 Y-coordinate of the ending curve point \param u1 X-coordinate of the ending velocity \param v1 Y-coordinate of the ending velocity \param color Pointer to \c dimv() consecutive values of type \c T, defining the drawing color. \param precision Curve drawing precision (optional). \param opacity Drawing opacity (optional). \param pattern An integer whose bits describe the line pattern (optional). \param init_hatch If \c true, init hatch motif. \note - The curve is a 2D cubic Bezier spline, from the set of specified starting/ending points and corresponding velocity vectors. - The spline is drawn as a serie of connected segments. The \p precision parameter sets the average number of pixels in each drawn segment. - A cubic Bezier curve is sometimes defined by a set of 4 points { (\p x0,\p y0), (\p xa,\p ya), (\p xb,\p yb), (\p x1,\p y1) } where (\p x0,\p y0) is the starting point, (\p x1,\p y1) is the ending point and (\p xa,\p ya), (\p xb,\p yb) are two \e control points. The starting and ending velocities (\p u0,\p v0) and (\p u1,\p v1) can be deduced easily from the control points as \p u0 = (\p xa - \p x0), \p v0 = (\p ya - \p y0), \p u1 = (\p x1 - \p xb) and \p v1 = (\p y1 - \p yb). \par Example: \code CImg img(100,100,1,3,0); const unsigned char color[] = { 255,255,255 }; img.draw_spline(30,30,0,100,90,40,0,-100,color); \endcode **/ template CImg& 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=4, const unsigned int pattern=~0U, const bool init_hatch=true) { if (is_empty()) return *this; if (!color) throw CImgArgumentException("CImg<%s>::draw_spline() : Specified color is (null)", pixel_type()); bool ninit_hatch = init_hatch; const float dx = (float)(x1 - x0), dy = (float)(y1 - y0), dmax = cimg::max(cimg::abs(dx),cimg::abs(dy)), ax = -2*dx + u0 + u1, bx = 3*dx - 2*u0 - u1, ay = -2*dy + v0 + v1, by = 3*dy - 2*v0 - v1, xprecision = dmax>0?precision/dmax:1.0f, tmax = 1 + (dmax>0?xprecision:0.0f); int ox = x0, oy = y0; for (float t = 0; t CImg& 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& color, const float opacity=1, const float precision=4, const unsigned int pattern=~0U, const bool init_hatch=true) { return draw_spline(x0,y0,u0,v0,x1,y1,u1,v1,color.data,opacity,precision,pattern,init_hatch); } //! Draw a cubic spline curve in the instance image (for volumetric images). /** \note - Similar to CImg::draw_spline() for a 3D spline in a volumetric image. **/ template CImg& 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) { if (is_empty()) return *this; if (!color) throw CImgArgumentException("CImg<%s>::draw_spline() : Specified color is (null)", pixel_type()); bool ninit_hatch = init_hatch; const float dx = (float)(x1 - x0), dy = (float)(y1 - y0), dz = (float)(z1 - z0), dmax = cimg::max(cimg::abs(dx),cimg::abs(dy),cimg::abs(dz)), ax = -2*dx + u0 + u1, bx = 3*dx - 2*u0 - u1, ay = -2*dy + v0 + v1, by = 3*dy - 2*v0 - v1, az = -2*dz + w0 + w1, bz = 3*dz - 2*w0 - w1, xprecision = dmax>0?precision/dmax:1.0f, tmax = 1 + (dmax>0?xprecision:0.0f); int ox = x0, oy = y0, oz = z0; for (float t = 0; t CImg& 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 CImg& color, const float opacity=1, const float precision=4, const unsigned int pattern=~0U, const bool init_hatch=true) { return draw_spline(x0,y0,z0,u0,v0,w0,x1,y1,z1,u1,v1,w1,color.data,opacity,precision,pattern,init_hatch); } //! Draw a cubic spline curve in the instance image. /** \param x0 X-coordinate of the starting curve point \param y0 Y-coordinate of the starting curve point \param u0 X-coordinate of the starting velocity \param v0 Y-coordinate of the starting velocity \param x1 X-coordinate of the ending curve point \param y1 Y-coordinate of the ending curve point \param u1 X-coordinate of the ending velocity \param v1 Y-coordinate of the ending velocity \param texture Texture image defining line pixel colors. \param tx0 X-coordinate of the starting texture point. \param ty0 Y-coordinate of the starting texture point. \param tx1 X-coordinate of the ending texture point. \param ty1 Y-coordinate of the ending texture point. \param precision Curve drawing precision (optional). \param opacity Drawing opacity (optional). \param pattern An integer whose bits describe the line pattern (optional). \param init_hatch if \c true, reinit hatch motif. **/ template CImg& 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& 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) { if (is_empty()) return *this; if (!texture || texture.dim::draw_line() : Specified texture (%u,%u,%u,%u,%p) is not a valid argument.", pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); if (is_overlapped(texture)) return draw_spline(x0,y0,u0,v0,x1,y1,u1,v1,+texture,tx0,ty0,tx1,ty1,precision,opacity,pattern,init_hatch); bool ninit_hatch = true; const float dx = (float)(x1 - x0), dy = (float)(y1 - y0), dmax = cimg::max(cimg::abs(dx),cimg::abs(dy)), ax = -2*dx + u0 + u1, bx = 3*dx - 2*u0 - u1, ay = -2*dy + v0 + v1, by = 3*dy - 2*v0 - v1, xprecision = dmax>0?precision/dmax:1.0f, tmax = 1 + (dmax>0?xprecision:0.0f); int ox = x0, oy = y0, otx = tx0, oty = ty0; for (float t1 = 0; t1 CImg& _draw_spline(const tp& points, const tt& tangents, const unsigned int W, const unsigned int H, const tc *const color, const float opacity, const bool close_set, const float precision, const unsigned int pattern, const bool init_hatch) { if (is_empty() || !points || !tangents || W<2) return *this; bool ninit_hatch = init_hatch; switch (H) { case 0 : case 1 : throw CImgArgumentException("CImg<%s>::draw_spline() : Given list of points or tangents is not valid.", pixel_type()); case 2 : { const int x0 = (int)points(0,0), y0 = (int)points(0,1); const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1); int ox = x0, oy = y0; float ou = u0, ov = v0; for (unsigned int i = 1; i CImg& _draw_spline(const tp& points, const unsigned int W, const unsigned int H, const tc *const color, const float opacity, const bool close_set, const float precision, const unsigned int pattern, const bool init_hatch) { if (is_empty() || !points || W<2) return *this; CImg tangents; switch (H) { case 0 : case 1 : throw CImgArgumentException("CImg<%s>::draw_spline() : Given list of points or tangents is not valid.", pixel_type()); case 2 : { tangents.assign(W,H); for (unsigned int p = 0; p CImg& draw_spline(const CImgList& points, const CImgList& tangents, const tc *const color, const float opacity=1, const bool close_set=false, const float precision=4, const unsigned int pattern=~0U, const bool init_hatch=true) { unsigned int H = ~0U; cimglist_for(points,p) H = cimg::min(H,(unsigned int)(points[p].size()),(unsigned int)(tangents[p].size())); return _draw_spline(points,tangents,color,opacity,close_set,precision,pattern,init_hatch,points.size,H); } //! Draw a set of consecutive colored splines in the instance image. template CImg& draw_spline(const CImgList& points, const CImgList& tangents, const CImg& color, const float opacity=1, const bool close_set=false, const float precision=4, const unsigned int pattern=~0U, const bool init_hatch=true) { return draw_spline(points,tangents,color.data,opacity,close_set,precision,pattern,init_hatch); } //! Draw a set of consecutive colored splines in the instance image. template CImg& draw_spline(const CImg& points, const CImg& tangents, const tc *const color, const float opacity=1, const bool close_set=false, const float precision=4, const unsigned int pattern=~0U, const bool init_hatch=true) { return _draw_spline(points,tangents,color,opacity,close_set,precision,pattern,init_hatch,points.width,points.height); } //! Draw a set of consecutive colored splines in the instance image. template CImg& draw_spline(const CImg& points, const CImg& tangents, const CImg& color, const float opacity=1, const bool close_set=false, const float precision=4, const unsigned int pattern=~0U, const bool init_hatch=true) { return draw_spline(points,tangents,color.data,opacity,close_set,precision,pattern,init_hatch); } //! Draw a set of consecutive colored splines in the instance image. template CImg& draw_spline(const CImgList& points, const tc *const color, const float opacity=1, const bool close_set=false, const float precision=4, const unsigned int pattern=~0U, const bool init_hatch=true) { unsigned int H = ~0U; cimglist_for(points,p) { const unsigned int s = points[p].size(); if (s CImg& draw_spline(const CImgList& points, CImg& color, const float opacity=1, const bool close_set=false, const float precision=4, const unsigned int pattern=~0U, const bool init_hatch=true) { return draw_spline(points,color.data,opacity,close_set,precision,pattern,init_hatch); } //! Draw a set of consecutive colored lines in the instance image. template CImg& draw_spline(const CImg& points, const tc *const color, const float opacity=1, const bool close_set=false, const float precision=4, const unsigned int pattern=~0U, const bool init_hatch=true) { return _draw_spline(points,color,opacity,close_set,precision,pattern,init_hatch,points.width,points.height); } //! Draw a set of consecutive colored lines in the instance image. template CImg& draw_spline(const CImg& points, const CImg& color, const float opacity=1, const bool close_set=false, const float precision=4, const unsigned int pattern=~0U, const bool init_hatch=true) { return draw_spline(points,color.data,opacity,close_set,precision,pattern,init_hatch); } //! Draw a colored arrow in the instance image. /** \param x0 X-coordinate of the starting arrow point (tail). \param y0 Y-coordinate of the starting arrow point (tail). \param x1 X-coordinate of the ending arrow point (head). \param y1 Y-coordinate of the ending arrow point (head). \param color Pointer to \c dimv() consecutive values of type \c T, defining the drawing color. \param angle Aperture angle of the arrow head (optional). \param length Length of the arrow head. If negative, describes a percentage of the arrow length (optional). \param opacity Drawing opacity (optional). \param pattern An integer whose bits describe the line pattern (optional). \note - Clipping is supported. **/ template CImg& 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) { if (is_empty()) return *this; const float u = (float)(x0 - x1), v = (float)(y0 - y1), sq = u*u + v*v, deg = (float)(angle*cimg::valuePI/180), ang = (sq>0)?(float)cimg_std::atan2(v,u):0.0f, l = (length>=0)?length:-length*(float)cimg_std::sqrt(sq)/100; if (sq>0) { const float cl = (float)cimg_std::cos(ang - deg), sl = (float)cimg_std::sin(ang - deg), cr = (float)cimg_std::cos(ang + deg), sr = (float)cimg_std::sin(ang + deg); const int xl = x1 + (int)(l*cl), yl = y1 + (int)(l*sl), xr = x1 + (int)(l*cr), yr = y1 + (int)(l*sr), xc = x1 + (int)((l+1)*(cl+cr))/2, yc = y1 + (int)((l+1)*(sl+sr))/2; draw_line(x0,y0,xc,yc,color,opacity,pattern).draw_triangle(x1,y1,xl,yl,xr,yr,color,opacity); } else draw_point(x0,y0,color,opacity); return *this; } //! Draw a colored arrow in the instance image. template CImg& draw_arrow(const int x0, const int y0, const int x1, const int y1, const CImg& color, const float opacity=1, const float angle=30, const float length=-10, const unsigned int pattern=~0U) { return draw_arrow(x0,y0,x1,y1,color.data,opacity,angle,length,pattern); } //! Draw an image. /** \param sprite Sprite image. \param x0 X-coordinate of the sprite position. \param y0 Y-coordinate of the sprite position. \param z0 Z-coordinate of the sprite position. \param v0 V-coordinate of the sprite position. \param opacity Drawing opacity (optional). \note - Clipping is supported. **/ template CImg& draw_image(const int x0, const int y0, const int z0, const int v0, const CImg& sprite, const float opacity=1) { if (is_empty()) return *this; if (!sprite) throw CImgArgumentException("CImg<%s>::draw_image() : Specified sprite image (%u,%u,%u,%u,%p) is empty.", pixel_type(),sprite.width,sprite.height,sprite.depth,sprite.dim,sprite.data); if (is_overlapped(sprite)) return draw_image(x0,y0,z0,v0,+sprite,opacity); const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bv = (v0<0); const int lX = sprite.dimx() - (x0 + sprite.dimx()>dimx()?x0 + sprite.dimx() - dimx():0) + (bx?x0:0), lY = sprite.dimy() - (y0 + sprite.dimy()>dimy()?y0 + sprite.dimy() - dimy():0) + (by?y0:0), lZ = sprite.dimz() - (z0 + sprite.dimz()>dimz()?z0 + sprite.dimz() - dimz():0) + (bz?z0:0), lV = sprite.dimv() - (v0 + sprite.dimv()>dimv()?v0 + sprite.dimv() - dimv():0) + (bv?v0:0); const t *ptrs = sprite.data - (bx?x0:0) - (by?y0*sprite.dimx():0) - (bz?z0*sprite.dimx()*sprite.dimy():0) - (bv?v0*sprite.dimx()*sprite.dimy()*sprite.dimz():0); const unsigned int offX = width - lX, soffX = sprite.width - lX, offY = width*(height - lY), soffY = sprite.width*(sprite.height - lY), offZ = width*height*(depth - lZ), soffZ = sprite.width*sprite.height*(sprite.depth - lZ); const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); if (lX>0 && lY>0 && lZ>0 && lV>0) { T *ptrd = ptr(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,v0<0?0:v0); for (int v = 0; v=1) for (int x = 0; x& draw_image(const int x0, const int y0, const int z0, const int v0, const CImg& sprite, const float opacity=1) { if (is_empty()) return *this; if (!sprite) throw CImgArgumentException("CImg<%s>::draw_image() : Specified sprite image (%u,%u,%u,%u,%p) is empty.", pixel_type(),sprite.width,sprite.height,sprite.depth,sprite.dim,sprite.data); if (is_overlapped(sprite)) return draw_image(x0,y0,z0,v0,+sprite,opacity); const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bv = (v0<0); const int lX = sprite.dimx() - (x0 + sprite.dimx()>dimx()?x0 + sprite.dimx() - dimx():0) + (bx?x0:0), lY = sprite.dimy() - (y0 + sprite.dimy()>dimy()?y0 + sprite.dimy() - dimy():0) + (by?y0:0), lZ = sprite.dimz() - (z0 + sprite.dimz()>dimz()?z0 + sprite.dimz() - dimz():0) + (bz?z0:0), lV = sprite.dimv() - (v0 + sprite.dimv()>dimv()?v0 + sprite.dimv() - dimv():0) + (bv?v0:0); const T *ptrs = sprite.data - (bx?x0:0) - (by?y0*sprite.dimx():0) - (bz?z0*sprite.dimx()*sprite.dimy():0) - (bv?v0*sprite.dimx()*sprite.dimy()*sprite.dimz():0); const unsigned int offX = width - lX, soffX = sprite.width - lX, offY = width*(height - lY), soffY = sprite.width*(sprite.height - lY), offZ = width*height*(depth - lZ), soffZ = sprite.width*sprite.height*(sprite.depth - lZ), slX = lX*sizeof(T); const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); if (lX>0 && lY>0 && lZ>0 && lV>0) { T *ptrd = ptr(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,v0<0?0:v0); for (int v = 0; v=1) for (int y = 0; y CImg& draw_image(const int x0, const int y0, const int z0, const CImg& sprite, const float opacity=1) { return draw_image(x0,y0,z0,0,sprite,opacity); } //! Draw an image. template CImg& draw_image(const int x0, const int y0, const CImg& sprite, const float opacity=1) { return draw_image(x0,y0,0,sprite,opacity); } //! Draw an image. template CImg& draw_image(const int x0, const CImg& sprite, const float opacity=1) { return draw_image(x0,0,sprite,opacity); } //! Draw an image. template CImg& draw_image(const CImg& sprite, const float opacity=1) { return draw_image(0,sprite,opacity); } //! Draw a sprite image in the instance image (masked version). /** \param sprite Sprite image. \param mask Mask image. \param x0 X-coordinate of the sprite position in the instance image. \param y0 Y-coordinate of the sprite position in the instance image. \param z0 Z-coordinate of the sprite position in the instance image. \param v0 V-coordinate of the sprite position in the instance image. \param mask_valmax Maximum pixel value of the mask image \c mask (optional). \param opacity Drawing opacity. \note - Pixel values of \c mask set the opacity of the corresponding pixels in \c sprite. - Clipping is supported. - Dimensions along x,y and z of \p sprite and \p mask must be the same. **/ template CImg& draw_image(const int x0, const int y0, const int z0, const int v0, const CImg& sprite, const CImg& mask, const float opacity=1, const float mask_valmax=1) { if (is_empty()) return *this; if (!sprite) throw CImgArgumentException("CImg<%s>::draw_image() : Specified sprite image (%u,%u,%u,%u,%p) is empty.", pixel_type(),sprite.width,sprite.height,sprite.depth,sprite.dim,sprite.data); if (!mask) throw CImgArgumentException("CImg<%s>::draw_image() : Specified mask image (%u,%u,%u,%u,%p) is empty.", pixel_type(),mask.width,mask.height,mask.depth,mask.dim,mask.data); if (is_overlapped(sprite)) return draw_image(x0,y0,z0,v0,+sprite,mask,opacity,mask_valmax); if (is_overlapped(mask)) return draw_image(x0,y0,z0,v0,sprite,+mask,opacity,mask_valmax); if (mask.width!=sprite.width || mask.height!=sprite.height || mask.depth!=sprite.depth) throw CImgArgumentException("CImg<%s>::draw_image() : Mask dimension is (%u,%u,%u,%u), while sprite is (%u,%u,%u,%u)", pixel_type(),mask.width,mask.height,mask.depth,mask.dim,sprite.width,sprite.height,sprite.depth,sprite.dim); const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bv = (v0<0); const int lX = sprite.dimx() - (x0 + sprite.dimx()>dimx()?x0 + sprite.dimx() - dimx():0) + (bx?x0:0), lY = sprite.dimy() - (y0 + sprite.dimy()>dimy()?y0 + sprite.dimy() - dimy():0) + (by?y0:0), lZ = sprite.dimz() - (z0 + sprite.dimz()>dimz()?z0 + sprite.dimz() - dimz():0) + (bz?z0:0), lV = sprite.dimv() - (v0 + sprite.dimv()>dimv()?v0 + sprite.dimv() - dimv():0) + (bv?v0:0); const int coff = -(bx?x0:0)-(by?y0*mask.dimx():0)-(bz?z0*mask.dimx()*mask.dimy():0)-(bv?v0*mask.dimx()*mask.dimy()*mask.dimz():0), ssize = mask.dimx()*mask.dimy()*mask.dimz(); const ti *ptrs = sprite.data + coff; const tm *ptrm = mask.data + coff; const unsigned int offX = width - lX, soffX = sprite.width - lX, offY = width*(height - lY), soffY = sprite.width*(sprite.height - lY), offZ = width*height*(depth - lZ), soffZ = sprite.width*sprite.height*(sprite.depth - lZ); if (lX>0 && lY>0 && lZ>0 && lV>0) { T *ptrd = ptr(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,v0<0?0:v0); for (int v = 0; v CImg& draw_image(const int x0, const int y0, const int z0, const CImg& sprite, const CImg& mask, const float opacity=1, const float mask_valmax=1) { return draw_image(x0,y0,z0,0,sprite,mask,opacity,mask_valmax); } //! Draw an image. template CImg& draw_image(const int x0, const int y0, const CImg& sprite, const CImg& mask, const float opacity=1, const float mask_valmax=1) { return draw_image(x0,y0,0,sprite,mask,opacity,mask_valmax); } //! Draw an image. template CImg& draw_image(const int x0, const CImg& sprite, const CImg& mask, const float opacity=1, const float mask_valmax=1) { return draw_image(x0,0,sprite,mask,opacity,mask_valmax); } //! Draw an image. template CImg& draw_image(const CImg& sprite, const CImg& mask, const float opacity=1, const float mask_valmax=1) { return draw_image(0,sprite,mask,opacity,mask_valmax); } //! Draw a 4D filled rectangle in the instance image, at coordinates (\c x0,\c y0,\c z0,\c v0)-(\c x1,\c y1,\c z1,\c v1). /** \param x0 X-coordinate of the upper-left rectangle corner. \param y0 Y-coordinate of the upper-left rectangle corner. \param z0 Z-coordinate of the upper-left rectangle corner. \param v0 V-coordinate of the upper-left rectangle corner. \param x1 X-coordinate of the lower-right rectangle corner. \param y1 Y-coordinate of the lower-right rectangle corner. \param z1 Z-coordinate of the lower-right rectangle corner. \param v1 V-coordinate of the lower-right rectangle corner. \param val Scalar value used to fill the rectangle area. \param opacity Drawing opacity (optional). \note - Clipping is supported. **/ CImg& draw_rectangle(const int x0, const int y0, const int z0, const int v0, const int x1, const int y1, const int z1, const int v1, const T val, const float opacity=1) { if (is_empty()) return *this; const bool bx = (x0=dimx()?dimx() - 1 - nx1:0) + (nx0<0?nx0:0), lY = (1 + ny1 - ny0) + (ny1>=dimy()?dimy() - 1 - ny1:0) + (ny0<0?ny0:0), lZ = (1 + nz1 - nz0) + (nz1>=dimz()?dimz() - 1 - nz1:0) + (nz0<0?nz0:0), lV = (1 + nv1 - nv0) + (nv1>=dimv()?dimv() - 1 - nv1:0) + (nv0<0?nv0:0); const unsigned int offX = width - lX, offY = width*(height - lY), offZ = width*height*(depth - lZ); const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); T *ptrd = ptr(nx0<0?0:nx0,ny0<0?0:ny0,nz0<0?0:nz0,nv0<0?0:nv0); if (lX>0 && lY>0 && lZ>0 && lV>0) for (int v = 0; v=1) { if (sizeof(T)!=1) { for (int x = 0; x CImg& 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) { if (!color) throw CImgArgumentException("CImg<%s>::draw_rectangle : specified color is (null)", pixel_type()); cimg_forV(*this,k) draw_rectangle(x0,y0,z0,k,x1,y1,z1,k,color[k],opacity); return *this; } //! Draw a 3D filled colored rectangle in the instance image, at coordinates (\c x0,\c y0,\c z0)-(\c x1,\c y1,\c z1). template CImg& draw_rectangle(const int x0, const int y0, const int z0, const int x1, const int y1, const int z1, const CImg& color, const float opacity=1) { return draw_rectangle(x0,y0,z0,x1,y1,z1,color.data,opacity); } //! Draw a 3D outlined colored rectangle in the instance image. template CImg& 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) { return draw_line(x0,y0,z0,x1,y0,z0,color,opacity,pattern,true). draw_line(x1,y0,z0,x1,y1,z0,color,opacity,pattern,false). draw_line(x1,y1,z0,x0,y1,z0,color,opacity,pattern,false). draw_line(x0,y1,z0,x0,y0,z0,color,opacity,pattern,false). draw_line(x0,y0,z1,x1,y0,z1,color,opacity,pattern,true). draw_line(x1,y0,z1,x1,y1,z1,color,opacity,pattern,false). draw_line(x1,y1,z1,x0,y1,z1,color,opacity,pattern,false). draw_line(x0,y1,z1,x0,y0,z1,color,opacity,pattern,false). draw_line(x0,y0,z0,x0,y0,z1,color,opacity,pattern,true). draw_line(x1,y0,z0,x1,y0,z1,color,opacity,pattern,true). draw_line(x1,y1,z0,x1,y1,z1,color,opacity,pattern,true). draw_line(x0,y1,z0,x0,y1,z1,color,opacity,pattern,true); } //! Draw a 3D outlined colored rectangle in the instance image. template CImg& draw_rectangle(const int x0, const int y0, const int z0, const int x1, const int y1, const int z1, const CImg& color, const float opacity, const unsigned int pattern) { return draw_rectangle(x0,y0,z0,x1,y1,z1,color.data,opacity,pattern); } //! Draw a 2D filled colored rectangle in the instance image, at coordinates (\c x0,\c y0)-(\c x1,\c y1). /** \param x0 X-coordinate of the upper-left rectangle corner. \param y0 Y-coordinate of the upper-left rectangle corner. \param x1 X-coordinate of the lower-right rectangle corner. \param y1 Y-coordinate of the lower-right rectangle corner. \param color Pointer to \c dimv() consecutive values of type \c T, defining the drawing color. \param opacity Drawing opacity (optional). \note - Clipping is supported. **/ template CImg& draw_rectangle(const int x0, const int y0, const int x1, const int y1, const tc *const color, const float opacity=1) { return draw_rectangle(x0,y0,0,x1,y1,depth-1,color,opacity); } //! Draw a 2D filled colored rectangle in the instance image, at coordinates (\c x0,\c y0)-(\c x1,\c y1). template CImg& draw_rectangle(const int x0, const int y0, const int x1, const int y1, const CImg& color, const float opacity=1) { return draw_rectangle(x0,y0,x1,y1,color.data,opacity); } //! Draw a 2D outlined colored rectangle. template CImg& 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) { if (is_empty()) return *this; if (y0==y1) return draw_line(x0,y0,x1,y0,color,opacity,pattern,true); if (x0==x1) return draw_line(x0,y0,x0,y1,color,opacity,pattern,true); const bool bx = (x0 CImg& draw_rectangle(const int x0, const int y0, const int x1, const int y1, const CImg& color, const float opacity, const unsigned int pattern) { return draw_rectangle(x0,y0,x1,y1,color.data,opacity,pattern); } // Inner macro for drawing triangles. #define _cimg_for_triangle1(img,xl,xr,y,x0,y0,x1,y1,x2,y2) \ for (int y = y0<0?0:y0, \ xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \ xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \ _sxn=1, \ _sxr=1, \ _sxl=1, \ _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \ _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \ _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \ _dyn = y2-y1, \ _dyr = y2-y0, \ _dyl = y1-y0, \ _counter = (_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \ _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \ _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \ cimg::min((int)(img).height-y-1,y2-y)), \ _errn = _dyn/2, \ _errr = _dyr/2, \ _errl = _dyl/2, \ _rxn = _dyn?(x2-x1)/_dyn:0, \ _rxr = _dyr?(x2-x0)/_dyr:0, \ _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \ (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn); \ _counter>=0; --_counter, ++y, \ xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \ xl+=(y!=y1)?_rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0): \ (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl)) #define _cimg_for_triangle2(img,xl,cl,xr,cr,y,x0,y0,c0,x1,y1,c1,x2,y2,c2) \ for (int y = y0<0?0:y0, \ xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \ cr = y0>=0?c0:(c0-y0*(c2-c0)/(y2-y0)), \ xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \ cl = y1>=0?(y0>=0?(y0==y1?c1:c0):(c0-y0*(c1-c0)/(y1-y0))):(c1-y1*(c2-c1)/(y2-y1)), \ _sxn=1, _scn=1, \ _sxr=1, _scr=1, \ _sxl=1, _scl=1, \ _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \ _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \ _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \ _dcn = c2>c1?c2-c1:(_scn=-1,c1-c2), \ _dcr = c2>c0?c2-c0:(_scr=-1,c0-c2), \ _dcl = c1>c0?c1-c0:(_scl=-1,c0-c1), \ _dyn = y2-y1, \ _dyr = y2-y0, \ _dyl = y1-y0, \ _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \ _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \ _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \ _dcn-=_dyn?_dyn*(_dcn/_dyn):0, \ _dcr-=_dyr?_dyr*(_dcr/_dyr):0, \ _dcl-=_dyl?_dyl*(_dcl/_dyl):0, \ cimg::min((int)(img).height-y-1,y2-y)), \ _errn = _dyn/2, _errcn = _errn, \ _errr = _dyr/2, _errcr = _errr, \ _errl = _dyl/2, _errcl = _errl, \ _rxn = _dyn?(x2-x1)/_dyn:0, \ _rcn = _dyn?(c2-c1)/_dyn:0, \ _rxr = _dyr?(x2-x0)/_dyr:0, \ _rcr = _dyr?(c2-c0)/_dyr:0, \ _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \ (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \ _rcl = (y0!=y1 && y1>0)?(_dyl?(c1-c0)/_dyl:0): \ (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcn ); \ _counter>=0; --_counter, ++y, \ xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \ cr+=_rcr+((_errcr-=_dcr)<0?_errcr+=_dyr,_scr:0), \ xl+=(y!=y1)?(cl+=_rcl+((_errcl-=_dcl)<0?(_errcl+=_dyl,_scl):0), \ _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \ (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcl=_rcn, cl=c1, \ _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl)) #define _cimg_for_triangle3(img,xl,txl,tyl,xr,txr,tyr,y,x0,y0,tx0,ty0,x1,y1,tx1,ty1,x2,y2,tx2,ty2) \ for (int y = y0<0?0:y0, \ xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \ txr = y0>=0?tx0:(tx0-y0*(tx2-tx0)/(y2-y0)), \ tyr = y0>=0?ty0:(ty0-y0*(ty2-ty0)/(y2-y0)), \ xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \ txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0-y0*(tx1-tx0)/(y1-y0))):(tx1-y1*(tx2-tx1)/(y2-y1)), \ tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0-y0*(ty1-ty0)/(y1-y0))):(ty1-y1*(ty2-ty1)/(y2-y1)), \ _sxn=1, _stxn=1, _styn=1, \ _sxr=1, _stxr=1, _styr=1, \ _sxl=1, _stxl=1, _styl=1, \ _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \ _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \ _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \ _dtxn = tx2>tx1?tx2-tx1:(_stxn=-1,tx1-tx2), \ _dtxr = tx2>tx0?tx2-tx0:(_stxr=-1,tx0-tx2), \ _dtxl = tx1>tx0?tx1-tx0:(_stxl=-1,tx0-tx1), \ _dtyn = ty2>ty1?ty2-ty1:(_styn=-1,ty1-ty2), \ _dtyr = ty2>ty0?ty2-ty0:(_styr=-1,ty0-ty2), \ _dtyl = ty1>ty0?ty1-ty0:(_styl=-1,ty0-ty1), \ _dyn = y2-y1, \ _dyr = y2-y0, \ _dyl = y1-y0, \ _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \ _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \ _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \ _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \ _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \ _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \ _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \ _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \ _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \ cimg::min((int)(img).height-y-1,y2-y)), \ _errn = _dyn/2, _errtxn = _errn, _errtyn = _errn, \ _errr = _dyr/2, _errtxr = _errr, _errtyr = _errr, \ _errl = _dyl/2, _errtxl = _errl, _errtyl = _errl, \ _rxn = _dyn?(x2-x1)/_dyn:0, \ _rtxn = _dyn?(tx2-tx1)/_dyn:0, \ _rtyn = _dyn?(ty2-ty1)/_dyn:0, \ _rxr = _dyr?(x2-x0)/_dyr:0, \ _rtxr = _dyr?(tx2-tx0)/_dyr:0, \ _rtyr = _dyr?(ty2-ty0)/_dyr:0, \ _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \ (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \ _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1-tx0)/_dyl:0): \ (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \ _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1-ty0)/_dyl:0): \ (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ); \ _counter>=0; --_counter, ++y, \ xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \ txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \ tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \ xl+=(y!=y1)?(txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \ tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \ _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \ (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \ _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1,\ _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl)) #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) \ for (int y = y0<0?0:y0, \ xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \ cr = y0>=0?c0:(c0-y0*(c2-c0)/(y2-y0)), \ txr = y0>=0?tx0:(tx0-y0*(tx2-tx0)/(y2-y0)), \ tyr = y0>=0?ty0:(ty0-y0*(ty2-ty0)/(y2-y0)), \ xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \ cl = y1>=0?(y0>=0?(y0==y1?c1:c0):(c0-y0*(c1-c0)/(y1-y0))):(c1-y1*(c2-c1)/(y2-y1)), \ txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0-y0*(tx1-tx0)/(y1-y0))):(tx1-y1*(tx2-tx1)/(y2-y1)), \ tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0-y0*(ty1-ty0)/(y1-y0))):(ty1-y1*(ty2-ty1)/(y2-y1)), \ _sxn=1, _scn=1, _stxn=1, _styn=1, \ _sxr=1, _scr=1, _stxr=1, _styr=1, \ _sxl=1, _scl=1, _stxl=1, _styl=1, \ _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \ _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \ _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \ _dcn = c2>c1?c2-c1:(_scn=-1,c1-c2), \ _dcr = c2>c0?c2-c0:(_scr=-1,c0-c2), \ _dcl = c1>c0?c1-c0:(_scl=-1,c0-c1), \ _dtxn = tx2>tx1?tx2-tx1:(_stxn=-1,tx1-tx2), \ _dtxr = tx2>tx0?tx2-tx0:(_stxr=-1,tx0-tx2), \ _dtxl = tx1>tx0?tx1-tx0:(_stxl=-1,tx0-tx1), \ _dtyn = ty2>ty1?ty2-ty1:(_styn=-1,ty1-ty2), \ _dtyr = ty2>ty0?ty2-ty0:(_styr=-1,ty0-ty2), \ _dtyl = ty1>ty0?ty1-ty0:(_styl=-1,ty0-ty1), \ _dyn = y2-y1, \ _dyr = y2-y0, \ _dyl = y1-y0, \ _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \ _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \ _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \ _dcn-=_dyn?_dyn*(_dcn/_dyn):0, \ _dcr-=_dyr?_dyr*(_dcr/_dyr):0, \ _dcl-=_dyl?_dyl*(_dcl/_dyl):0, \ _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \ _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \ _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \ _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \ _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \ _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \ cimg::min((int)(img).height-y-1,y2-y)), \ _errn = _dyn/2, _errcn = _errn, _errtxn = _errn, _errtyn = _errn, \ _errr = _dyr/2, _errcr = _errr, _errtxr = _errr, _errtyr = _errr, \ _errl = _dyl/2, _errcl = _errl, _errtxl = _errl, _errtyl = _errl, \ _rxn = _dyn?(x2-x1)/_dyn:0, \ _rcn = _dyn?(c2-c1)/_dyn:0, \ _rtxn = _dyn?(tx2-tx1)/_dyn:0, \ _rtyn = _dyn?(ty2-ty1)/_dyn:0, \ _rxr = _dyr?(x2-x0)/_dyr:0, \ _rcr = _dyr?(c2-c0)/_dyr:0, \ _rtxr = _dyr?(tx2-tx0)/_dyr:0, \ _rtyr = _dyr?(ty2-ty0)/_dyr:0, \ _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \ (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \ _rcl = (y0!=y1 && y1>0)?(_dyl?(c1-c0)/_dyl:0): \ (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcn ), \ _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1-tx0)/_dyl:0): \ (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \ _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1-ty0)/_dyl:0): \ (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ); \ _counter>=0; --_counter, ++y, \ xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \ cr+=_rcr+((_errcr-=_dcr)<0?_errcr+=_dyr,_scr:0), \ txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \ tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \ xl+=(y!=y1)?(cl+=_rcl+((_errcl-=_dcl)<0?(_errcl+=_dyl,_scl):0), \ txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \ tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \ _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \ (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcl=_rcn, cl=c1, \ _errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \ _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1, \ _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl)) #define _cimg_for_triangle5(img,xl,txl,tyl,lxl,lyl,xr,txr,tyr,lxr,lyr,y,x0,y0,tx0,ty0,lx0,ly0,x1,y1,tx1,ty1,lx1,ly1,x2,y2,tx2,ty2,lx2,ly2) \ for (int y = y0<0?0:y0, \ xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \ txr = y0>=0?tx0:(tx0-y0*(tx2-tx0)/(y2-y0)), \ tyr = y0>=0?ty0:(ty0-y0*(ty2-ty0)/(y2-y0)), \ lxr = y0>=0?lx0:(lx0-y0*(lx2-lx0)/(y2-y0)), \ lyr = y0>=0?ly0:(ly0-y0*(ly2-ly0)/(y2-y0)), \ xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \ txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0-y0*(tx1-tx0)/(y1-y0))):(tx1-y1*(tx2-tx1)/(y2-y1)), \ tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0-y0*(ty1-ty0)/(y1-y0))):(ty1-y1*(ty2-ty1)/(y2-y1)), \ lxl = y1>=0?(y0>=0?(y0==y1?lx1:lx0):(lx0-y0*(lx1-lx0)/(y1-y0))):(lx1-y1*(lx2-lx1)/(y2-y1)), \ lyl = y1>=0?(y0>=0?(y0==y1?ly1:ly0):(ly0-y0*(ly1-ly0)/(y1-y0))):(ly1-y1*(ly2-ly1)/(y2-y1)), \ _sxn=1, _stxn=1, _styn=1, _slxn=1, _slyn=1, \ _sxr=1, _stxr=1, _styr=1, _slxr=1, _slyr=1, \ _sxl=1, _stxl=1, _styl=1, _slxl=1, _slyl=1, \ _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), _dyn = y2-y1, \ _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), _dyr = y2-y0, \ _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), _dyl = y1-y0, \ _dtxn = tx2>tx1?tx2-tx1:(_stxn=-1,tx1-tx2), \ _dtxr = tx2>tx0?tx2-tx0:(_stxr=-1,tx0-tx2), \ _dtxl = tx1>tx0?tx1-tx0:(_stxl=-1,tx0-tx1), \ _dtyn = ty2>ty1?ty2-ty1:(_styn=-1,ty1-ty2), \ _dtyr = ty2>ty0?ty2-ty0:(_styr=-1,ty0-ty2), \ _dtyl = ty1>ty0?ty1-ty0:(_styl=-1,ty0-ty1), \ _dlxn = lx2>lx1?lx2-lx1:(_slxn=-1,lx1-lx2), \ _dlxr = lx2>lx0?lx2-lx0:(_slxr=-1,lx0-lx2), \ _dlxl = lx1>lx0?lx1-lx0:(_slxl=-1,lx0-lx1), \ _dlyn = ly2>ly1?ly2-ly1:(_slyn=-1,ly1-ly2), \ _dlyr = ly2>ly0?ly2-ly0:(_slyr=-1,ly0-ly2), \ _dlyl = ly1>ly0?ly1-ly0:(_slyl=-1,ly0-ly1), \ _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \ _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \ _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \ _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \ _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \ _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \ _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \ _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \ _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \ _dlxn-=_dyn?_dyn*(_dlxn/_dyn):0, \ _dlxr-=_dyr?_dyr*(_dlxr/_dyr):0, \ _dlxl-=_dyl?_dyl*(_dlxl/_dyl):0, \ _dlyn-=_dyn?_dyn*(_dlyn/_dyn):0, \ _dlyr-=_dyr?_dyr*(_dlyr/_dyr):0, \ _dlyl-=_dyl?_dyl*(_dlyl/_dyl):0, \ cimg::min((int)(img).height-y-1,y2-y)), \ _errn = _dyn/2, _errtxn = _errn, _errtyn = _errn, _errlxn = _errn, _errlyn = _errn, \ _errr = _dyr/2, _errtxr = _errr, _errtyr = _errr, _errlxr = _errr, _errlyr = _errr, \ _errl = _dyl/2, _errtxl = _errl, _errtyl = _errl, _errlxl = _errl, _errlyl = _errl, \ _rxn = _dyn?(x2-x1)/_dyn:0, \ _rtxn = _dyn?(tx2-tx1)/_dyn:0, \ _rtyn = _dyn?(ty2-ty1)/_dyn:0, \ _rlxn = _dyn?(lx2-lx1)/_dyn:0, \ _rlyn = _dyn?(ly2-ly1)/_dyn:0, \ _rxr = _dyr?(x2-x0)/_dyr:0, \ _rtxr = _dyr?(tx2-tx0)/_dyr:0, \ _rtyr = _dyr?(ty2-ty0)/_dyr:0, \ _rlxr = _dyr?(lx2-lx0)/_dyr:0, \ _rlyr = _dyr?(ly2-ly0)/_dyr:0, \ _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \ (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \ _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1-tx0)/_dyl:0): \ (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \ _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1-ty0)/_dyl:0): \ (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ), \ _rlxl = (y0!=y1 && y1>0)?(_dyl?(lx1-lx0)/_dyl:0): \ (_errlxl=_errlxn, _dlxl=_dlxn, _dyl=_dyn, _slxl=_slxn, _rlxn ), \ _rlyl = (y0!=y1 && y1>0)?(_dyl?(ly1-ly0)/_dyl:0): \ (_errlyl=_errlyn, _dlyl=_dlyn, _dyl=_dyn, _slyl=_slyn, _rlyn ); \ _counter>=0; --_counter, ++y, \ xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \ txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \ tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \ lxr+=_rlxr+((_errlxr-=_dlxr)<0?_errlxr+=_dyr,_slxr:0), \ lyr+=_rlyr+((_errlyr-=_dlyr)<0?_errlyr+=_dyr,_slyr:0), \ xl+=(y!=y1)?(txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \ tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \ lxl+=_rlxl+((_errlxl-=_dlxl)<0?(_errlxl+=_dyl,_slxl):0), \ lyl+=_rlyl+((_errlyl-=_dlyl)<0?(_errlyl+=_dyl,_slyl):0), \ _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \ (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \ _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1, \ _errlxl=_errlxn, _dlxl=_dlxn, _dyl=_dyn, _slxl=_slxn, _rlxl=_rlxn, lxl=lx1, \ _errlyl=_errlyn, _dlyl=_dlyn, _dyl=_dyn, _slyl=_slyn, _rlyl=_rlyn, lyl=ly1, \ _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl)) // Draw a colored triangle (inner routine, uses bresenham's algorithm). template CImg& _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 float brightness) { _draw_scanline(color,opacity); const float nbrightness = brightness<0?0:(brightness>2?2:brightness); int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2; if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1); if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2); if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2); if (ny0=0) { if ((nx1 - nx0)*(ny2 - ny0) - (nx2 - nx0)*(ny1 - ny0)<0) _cimg_for_triangle1(*this,xl,xr,y,nx0,ny0,nx1,ny1,nx2,ny2) _draw_scanline(xl,xr,y,color,opacity,nbrightness); else _cimg_for_triangle1(*this,xl,xr,y,nx0,ny0,nx1,ny1,nx2,ny2) _draw_scanline(xr,xl,y,color,opacity,nbrightness); } return *this; } //! Draw a 2D filled colored triangle. template CImg& 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) { if (is_empty()) return *this; if (!color) throw CImgArgumentException("CImg<%s>::draw_triangle : Specified color is (null).", pixel_type()); _draw_triangle(x0,y0,x1,y1,x2,y2,color,opacity,1); return *this; } //! Draw a 2D filled colored triangle. template CImg& draw_triangle(const int x0, const int y0, const int x1, const int y1, const int x2, const int y2, const CImg& color, const float opacity=1) { return draw_triangle(x0,y0,x1,y1,x2,y2,color.data,opacity); } //! Draw a 2D outlined colored triangle. template CImg& 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) { if (is_empty()) return *this; if (!color) throw CImgArgumentException("CImg<%s>::draw_triangle : Specified color is (null).", pixel_type()); draw_line(x0,y0,x1,y1,color,opacity,pattern,true). draw_line(x1,y1,x2,y2,color,opacity,pattern,false). draw_line(x2,y2,x0,y0,color,opacity,pattern,false); return *this; } //! Draw a 2D outlined colored triangle. template CImg& draw_triangle(const int x0, const int y0, const int x1, const int y1, const int x2, const int y2, const CImg& color, const float opacity, const unsigned int pattern) { return draw_triangle(x0,y0,x1,y1,x2,y2,color.data,opacity,pattern); } //! Draw a 2D filled colored triangle, with z-buffering. template CImg& draw_triangle(float *const 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) { if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; if (!color) throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified color is (null).", pixel_type()); static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0), nbrightness = brightness<0?0:(brightness>2?2:brightness); const int whz = width*height*depth, offx = dim*whz; int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2; float nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2; if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1); if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nz0,nz2); if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nz1,nz2); if (ny0>=dimy() || ny2<0) return *this; float pzl = (nz1 - nz0)/(ny1 - ny0), pzr = (nz2 - nz0)/(ny2 - ny0), pzn = (nz2 - nz1)/(ny2 - ny1), zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))); _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) { if (y==ny1) { zl = nz1; pzl = pzn; } int xleft = xleft0, xright = xright0; float zleft = zl, zright = zr; if (xright=dimx()-1) xright = dimx()-1; T* ptrd = ptr(xleft,y,0,0); float *ptrz = zbuffer + xleft + y*width; if (opacity>=1) { if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { if (zleft>*ptrz) { *ptrz = zleft; const tc *col = color; cimg_forV(*this,k) { *ptrd = (T)*(col++); ptrd+=whz; } ptrd-=offx; } zleft+=pentez; } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { if (zleft>*ptrz) { *ptrz = zleft; const tc *col = color; cimg_forV(*this,k) { *ptrd = (T)(nbrightness*(*col++)); ptrd+=whz; } ptrd-=offx; } zleft+=pentez; } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { if (zleft>*ptrz) { *ptrz = zleft; const tc *col = color; cimg_forV(*this,k) { *ptrd = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval); ptrd+=whz; } ptrd-=offx; } zleft+=pentez; } } else { if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { if (zleft>*ptrz) { *ptrz = zleft; const tc *col = color; cimg_forV(*this,k) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=whz; } ptrd-=offx; } zleft+=pentez; } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { if (zleft>*ptrz) { *ptrz = zleft; const tc *col = color; cimg_forV(*this,k) { *ptrd = (T)(nopacity*nbrightness**(col++) + *ptrd*copacity); ptrd+=whz; } ptrd-=offx; } zleft+=pentez; } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { if (zleft>*ptrz) { *ptrz = zleft; const tc *col = color; cimg_forV(*this,k) { const T val = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval); *ptrd = (T)(nopacity*val + *ptrd*copacity); ptrd+=whz; } ptrd-=offx; } zleft+=pentez; } } zr+=pzr; zl+=pzl; } return *this; } //! Draw a 2D filled colored triangle, with z-buffering. template CImg& draw_triangle(float *const 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& color, const float opacity=1, const float brightness=1) { return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color.data,opacity,brightness); } //! Draw a 2D Gouraud-shaded colored triangle. /** \param x0 = X-coordinate of the first corner in the instance image. \param y0 = Y-coordinate of the first corner in the instance image. \param x1 = X-coordinate of the second corner in the instance image. \param y1 = Y-coordinate of the second corner in the instance image. \param x2 = X-coordinate of the third corner in the instance image. \param y2 = Y-coordinate of the third corner in the instance image. \param color = array of dimv() values of type \c T, defining the global drawing color. \param brightness0 = brightness of the first corner (in [0,2]). \param brightness1 = brightness of the second corner (in [0,2]). \param brightness2 = brightness of the third corner (in [0,2]). \param opacity = opacity of the drawing. \note Clipping is supported. **/ template CImg& 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) { if (is_empty()) return *this; if (!color) throw CImgArgumentException("CImg<%s>::draw_triangle : Specified color is (null).", pixel_type()); static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); const int whz = width*height*depth, offx = dim*whz-1; int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, nc0 = (int)((brightness0<0?0:(brightness0>2?2:brightness0))*256), nc1 = (int)((brightness1<0?0:(brightness1>2?2:brightness1))*256), nc2 = (int)((brightness2<0?0:(brightness2>2?2:brightness2))*256); if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nc0,nc1); if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nc0,nc2); if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nc1,nc2); if (ny0>=dimy() || ny2<0) return *this; _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) { int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0; if (xrightcleft?cright - cleft:cleft - cright, rc = dx?(cright - cleft)/dx:0, sc = cright>cleft?1:-1, ndc = dc-(dx?dx*(dc/dx):0); int errc = dx>>1; if (xleft<0 && dx) cleft-=xleft*(cright - cleft)/dx; if (xleft<0) xleft = 0; if (xright>=dimx()-1) xright = dimx()-1; T* ptrd = ptr(xleft,y); if (opacity>=1) for (int x = xleft; x<=xright; ++x) { const tc *col = color; cimg_forV(*this,k) { *ptrd = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256); ptrd+=whz; } ptrd-=offx; cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); } else for (int x = xleft; x<=xright; ++x) { const tc *col = color; cimg_forV(*this,k) { const T val = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256); *ptrd = (T)(nopacity*val + *ptrd*copacity); ptrd+=whz; } ptrd-=offx; cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); } } return *this; } //! Draw a 2D Gouraud-shaded colored triangle. template CImg& draw_triangle(const int x0, const int y0, const int x1, const int y1, const int x2, const int y2, const CImg& color, const float brightness0, const float brightness1, const float brightness2, const float opacity=1) { return draw_triangle(x0,y0,x1,y1,x2,y2,color.data,brightness0,brightness1,brightness2,opacity); } //! Draw a 2D Gouraud-shaded colored triangle, with z-buffering. template CImg& draw_triangle(float *const 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) { if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; if (!color) throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified color is (null).", pixel_type()); static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); const int whz = width*height*depth, offx = dim*whz; int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, nc0 = (int)((brightness0<0?0:(brightness0>2?2:brightness0))*256), nc1 = (int)((brightness1<0?0:(brightness1>2?2:brightness1))*256), nc2 = (int)((brightness2<0?0:(brightness2>2?2:brightness2))*256); float nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2; if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1,nc0,nc1); if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nz0,nz2,nc0,nc2); if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nz1,nz2,nc1,nc2); if (ny0>=dimy() || ny2<0) return *this; float pzl = (nz1 - nz0)/(ny1 - ny0), pzr = (nz2 - nz0)/(ny2 - ny0), pzn = (nz2 - nz1)/(ny2 - ny1), zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))); _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) { if (y==ny1) { zl = nz1; pzl = pzn; } int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0; float zleft = zl, zright = zr; if (xrightcleft?cright - cleft:cleft - cright, rc = dx?(cright-cleft)/dx:0, sc = cright>cleft?1:-1, ndc = dc-(dx?dx*(dc/dx):0); const float pentez = (zright - zleft)/dx; int errc = dx>>1; if (xleft<0 && dx) { cleft-=xleft*(cright - cleft)/dx; zleft-=xleft*(zright - zleft)/dx; } if (xleft<0) xleft = 0; if (xright>=dimx()-1) xright = dimx()-1; T *ptrd = ptr(xleft,y); float *ptrz = zbuffer + xleft + y*width; if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) { if (zleft>*ptrz) { *ptrz = zleft; const tc *col = color; cimg_forV(*this,k) { *ptrd = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256); ptrd+=whz; } ptrd-=offx; } zleft+=pentez; cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); } else for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) { if (zleft>*ptrz) { *ptrz = zleft; const tc *col = color; cimg_forV(*this,k) { const T val = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256); *ptrd = (T)(nopacity*val + *ptrd*copacity); ptrd+=whz; } ptrd-=offx; } zleft+=pentez; cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); } zr+=pzr; zl+=pzl; } return *this; } //! Draw a Gouraud triangle with z-buffer consideration. template CImg& draw_triangle(float *const 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& color, const float brightness0, const float brightness1, const float brightness2, const float opacity=1) { return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color.data,brightness0,brightness1,brightness2,opacity); } //! Draw a 2D textured triangle. /** \param x0 = X-coordinate of the first corner in the instance image. \param y0 = Y-coordinate of the first corner in the instance image. \param x1 = X-coordinate of the second corner in the instance image. \param y1 = Y-coordinate of the second corner in the instance image. \param x2 = X-coordinate of the third corner in the instance image. \param y2 = Y-coordinate of the third corner in the instance image. \param texture = texture image used to fill the triangle. \param tx0 = X-coordinate of the first corner in the texture image. \param ty0 = Y-coordinate of the first corner in the texture image. \param tx1 = X-coordinate of the second corner in the texture image. \param ty1 = Y-coordinate of the second corner in the texture image. \param tx2 = X-coordinate of the third corner in the texture image. \param ty2 = Y-coordinate of the third corner in the texture image. \param opacity = opacity of the drawing. \param brightness = brightness of the drawing (in [0,2]). \note Clipping is supported, but texture coordinates do not support clipping. **/ template CImg& draw_triangle(const int x0, const int y0, const int x1, const int y1, const int x2, const int y2, const CImg& 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) { if (is_empty()) return *this; if (!texture || texture.dim::draw_triangle() : Specified texture (%u,%u,%u,%u,%p) is not a valid argument.", pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); if (is_overlapped(texture)) return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness); static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0), nbrightness = brightness<0?0:(brightness>2?2:brightness); const int whz = width*height*depth, twhz = texture.width*texture.height*texture.depth, offx = dim*whz-1; int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2; if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1); if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2); if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2); if (ny0>=dimy() || ny2<0) return *this; _cimg_for_triangle3(*this,xleft0,txleft0,tyleft0,xright0,txright0,tyright0,y, nx0,ny0,ntx0,nty0,nx1,ny1,ntx1,nty1,nx2,ny2,ntx2,nty2) { int xleft = xleft0, xright = xright0, txleft = txleft0, txright = txright0, tyleft = tyleft0, tyright = tyright0; if (xrighttxleft?txright - txleft:txleft - txright, dty = tyright>tyleft?tyright - tyleft:tyleft - tyright, rtx = dx?(txright - txleft)/dx:0, rty = dx?(tyright - tyleft)/dx:0, stx = txright>txleft?1:-1, sty = tyright>tyleft?1:-1, ndtx = dtx - (dx?dx*(dtx/dx):0), ndty = dty - (dx?dx*(dty/dx):0); int errtx = dx>>1, errty = errtx; if (xleft<0 && dx) { txleft-=xleft*(txright - txleft)/dx; tyleft-=xleft*(tyright - tyleft)/dx; } if (xleft<0) xleft = 0; if (xright>=dimx()-1) xright = dimx()-1; T* ptrd = ptr(xleft,y,0,0); if (opacity>=1) { if (nbrightness==1) for (int x = xleft; x<=xright; ++x) { const tc *col = texture.ptr(txleft,tyleft); cimg_forV(*this,k) { *ptrd = (T)*col; ptrd+=whz; col+=twhz; } ptrd-=offx; txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) { const tc *col = texture.ptr(txleft,tyleft); cimg_forV(*this,k) { *ptrd = (T)(nbrightness**col); ptrd+=whz; col+=twhz; } ptrd-=offx; txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); } else for (int x = xleft; x<=xright; ++x) { const tc *col = texture.ptr(txleft,tyleft); cimg_forV(*this,k) { *ptrd = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval); ptrd+=whz; col+=twhz; } ptrd-=offx; txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); } } else { if (nbrightness==1) for (int x = xleft; x<=xright; ++x) { const tc *col = texture.ptr(txleft,tyleft); cimg_forV(*this,k) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whz; col+=twhz; } ptrd-=offx; txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) { const tc *col = texture.ptr(txleft,tyleft); cimg_forV(*this,k) { *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity); ptrd+=whz; col+=twhz; } ptrd-=offx; txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); } else for (int x = xleft; x<=xright; ++x) { const tc *col = texture.ptr(txleft,tyleft); cimg_forV(*this,k) { const T val = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval); *ptrd = (T)(nopacity*val + *ptrd*copacity); ptrd+=whz; col+=twhz; } ptrd-=offx; txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); } } } return *this; } //! Draw a 2D textured triangle, with perspective correction. template CImg& 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& 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) { if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; if (!texture || texture.dim::draw_triangle() : Specified texture (%u,%u,%u,%u,%p) is not a valid argument.", pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness); static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0), nbrightness = brightness<0?0:(brightness>2?2:brightness); const int whz = width*height*depth, twhz = texture.width*texture.height*texture.depth, offx = dim*whz-1; int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2; float ntx0 = tx0/z0, nty0 = ty0/z0, ntx1 = tx1/z1, nty1 = ty1/z1, ntx2 = tx2/z2, nty2 = ty2/z2, nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2; if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1); if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2); if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2); if (ny0>=dimy() || ny2<0) return *this; float ptxl = (ntx1 - ntx0)/(ny1 - ny0), ptxr = (ntx2 - ntx0)/(ny2 - ny0), ptxn = (ntx2 - ntx1)/(ny2 - ny1), ptyl = (nty1 - nty0)/(ny1 - ny0), ptyr = (nty2 - nty0)/(ny2 - ny0), ptyn = (nty2 - nty1)/(ny2 - ny1), pzl = (nz1 - nz0)/(ny1 - ny0), pzr = (nz2 - nz0)/(ny2 - ny0), pzn = (nz2 - nz1)/(ny2 - ny1), zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)), tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)), zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))), txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))), tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1))); _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) { if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; } int xleft = xleft0, xright = xright0; float zleft = zl, zright = zr, txleft = txl, txright = txr, tyleft = tyl, tyright = tyr; if (xright=dimx()-1) xright = dimx()-1; T* ptrd = ptr(xleft,y,0,0); if (opacity>=1) { if (nbrightness==1) for (int x = xleft; x<=xright; ++x) { const float invz = 1/zleft; const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { *ptrd = (T)*col; ptrd+=whz; col+=twhz; } ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; } else if (nbrightness<1) for (int x=xleft; x<=xright; ++x) { const float invz = 1/zleft; const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { *ptrd = (T)(nbrightness**col); ptrd+=whz; col+=twhz; } ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; } else for (int x = xleft; x<=xright; ++x) { const float invz = 1/zleft; const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { *ptrd = (T)((2-nbrightness)**col + (nbrightness-1)*maxval); ptrd+=whz; col+=twhz; } ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; } } else { if (nbrightness==1) for (int x = xleft; x<=xright; ++x) { const float invz = 1/zleft; const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whz; col+=twhz; } ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) { const float invz = 1/zleft; const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity); ptrd+=whz; col+=twhz; } ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; } else for (int x = xleft; x<=xright; ++x) { const float invz = 1/zleft; const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { const T val = (T)((2-nbrightness)**col + (nbrightness-1)*maxval); *ptrd = (T)(nopacity*val + *ptrd*copacity); ptrd+=whz; col+=twhz; } ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; } } zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl; } return *this; } //! Draw a 2D textured triangle, with z-buffering and perspective correction. template CImg& draw_triangle(float *const 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& 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) { if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; if (!texture || texture.dim::draw_triangle() : Specified texture (%u,%u,%u,%u,%p) is not a valid argument.", pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); if (is_overlapped(texture)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness); static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0), nbrightness = brightness<0?0:(brightness>2?2:brightness); const int whz = width*height*depth, twhz = texture.width*texture.height*texture.depth, offx = dim*whz; int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2; float ntx0 = tx0/z0, nty0 = ty0/z0, ntx1 = tx1/z1, nty1 = ty1/z1, ntx2 = tx2/z2, nty2 = ty2/z2, nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2; if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1); if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2); if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2); if (ny0>=dimy() || ny2<0) return *this; float ptxl = (ntx1 - ntx0)/(ny1 - ny0), ptxr = (ntx2 - ntx0)/(ny2 - ny0), ptxn = (ntx2 - ntx1)/(ny2 - ny1), ptyl = (nty1 - nty0)/(ny1 - ny0), ptyr = (nty2 - nty0)/(ny2 - ny0), ptyn = (nty2 - nty1)/(ny2 - ny1), pzl = (nz1 - nz0)/(ny1 - ny0), pzr = (nz2 - nz0)/(ny2 - ny0), pzn = (nz2 - nz1)/(ny2 - ny1), zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)), tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)), zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))), txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))), tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1))); _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) { if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; } int xleft = xleft0, xright = xright0; float zleft = zl, zright = zr, txleft = txl, txright = txr, tyleft = tyl, tyright = tyr; if (xright=dimx()-1) xright = dimx()-1; T *ptrd = ptr(xleft,y,0,0); float *ptrz = zbuffer + xleft + y*width; if (opacity>=1) { if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { if (zleft>*ptrz) { *ptrz = zleft; const float invz = 1/zleft; const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { *ptrd = (T)*col; ptrd+=whz; col+=twhz; } ptrd-=offx; } zleft+=pentez; txleft+=pentetx; tyleft+=pentety; } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { if (zleft>*ptrz) { *ptrz = zleft; const float invz = 1/zleft; const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { *ptrd = (T)(nbrightness**col); ptrd+=whz; col+=twhz; } ptrd-=offx; } zleft+=pentez; txleft+=pentetx; tyleft+=pentety; } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { if (zleft>*ptrz) { *ptrz = zleft; const float invz = 1/zleft; const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { *ptrd = (T)((2-nbrightness)**col + (nbrightness-1)*maxval); ptrd+=whz; col+=twhz; } ptrd-=offx; } zleft+=pentez; txleft+=pentetx; tyleft+=pentety; } } else { if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { if (zleft>*ptrz) { *ptrz = zleft; const float invz = 1/zleft; const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whz; col+=twhz; } ptrd-=offx; } zleft+=pentez; txleft+=pentetx; tyleft+=pentety; } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { if (zleft>*ptrz) { *ptrz = zleft; const float invz = 1/zleft; const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity); ptrd+=whz; col+=twhz; } ptrd-=offx; } zleft+=pentez; txleft+=pentetx; tyleft+=pentety; } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { if (zleft>*ptrz) { *ptrz = zleft; const float invz = 1/zleft; const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { const T val = (T)((2-nbrightness)**col + (nbrightness-1)*maxval); *ptrd = (T)(nopacity*val + *ptrd*copacity); ptrd+=whz; col+=twhz; } ptrd-=offx; } zleft+=pentez; txleft+=pentetx; tyleft+=pentety; } } zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl; } return *this; } //! Draw a 2D Pseudo-Phong-shaded triangle. /** \param x0 = X-coordinate of the first corner in the instance image. \param y0 = Y-coordinate of the first corner in the instance image. \param x1 = X-coordinate of the second corner in the instance image. \param y1 = Y-coordinate of the second corner in the instance image. \param x2 = X-coordinate of the third corner in the instance image. \param y2 = Y-coordinate of the third corner in the instance image. \param color = array of dimv() values of type \c T, defining the global drawing color. \param light = light image. \param lx0 = X-coordinate of the first corner in the light image. \param ly0 = Y-coordinate of the first corner in the light image. \param lx1 = X-coordinate of the second corner in the light image. \param ly1 = Y-coordinate of the second corner in the light image. \param lx2 = X-coordinate of the third corner in the light image. \param ly2 = Y-coordinate of the third corner in the light image. \param opacity = opacity of the drawing. \note Clipping is supported, but texture coordinates do not support clipping. **/ template CImg& 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& light, const int lx0, const int ly0, const int lx1, const int ly1, const int lx2, const int ly2, const float opacity=1) { if (is_empty()) return *this; if (!color) throw CImgArgumentException("CImg<%s>::draw_triangle : Specified color is (null).", pixel_type()); if (!light) throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified light texture (%u,%u,%u,%u,%p) is empty.", pixel_type(),light.width,light.height,light.depth,light.dim,light.data); if (is_overlapped(light)) return draw_triangle(x0,y0,x1,y1,x2,y2,color,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2; const int whz = width*height*depth, offx = dim*whz-1; if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nlx0,nlx1,nly0,nly1); if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nlx0,nlx2,nly0,nly2); if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nlx1,nlx2,nly1,nly2); if (ny0>=dimy() || ny2<0) return *this; _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y, nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) { int xleft = xleft0, xright = xright0, lxleft = lxleft0, lxright = lxright0, lyleft = lyleft0, lyright = lyright0; if (xrightlxleft?lxright - lxleft:lxleft - lxright, dly = lyright>lyleft?lyright - lyleft:lyleft - lyright, rlx = dx?(lxright - lxleft)/dx:0, rly = dx?(lyright - lyleft)/dx:0, slx = lxright>lxleft?1:-1, sly = lyright>lyleft?1:-1, ndlx = dlx - (dx?dx*(dlx/dx):0), ndly = dly - (dx?dx*(dly/dx):0); int errlx = dx>>1, errly = errlx; if (xleft<0 && dx) { lxleft-=xleft*(lxright - lxleft)/dx; lyleft-=xleft*(lyright - lyleft)/dx; } if (xleft<0) xleft = 0; if (xright>=dimx()-1) xright = dimx()-1; T* ptrd = ptr(xleft,y,0,0); if (opacity>=1) for (int x = xleft; x<=xright; ++x) { const tl l = light(lxleft,lyleft); const tc *col = color; cimg_forV(*this,k) { *ptrd = (T)(l<1?l**(col++):((2-l)**(col++)+(l-1)*maxval)); ptrd+=whz; } ptrd-=offx; lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); } else for (int x = xleft; x<=xright; ++x) { const tl l = light(lxleft,lyleft); const tc *col = color; cimg_forV(*this,k) { const T val = (T)(l<1?l**(col++):((2-l)**(col++)+(l-1)*maxval)); *ptrd = (T)(nopacity*val + *ptrd*copacity); ptrd+=whz; } ptrd-=offx; lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); } } return *this; } //! Draw a 2D Pseudo-Phong-shaded triangle. template CImg& draw_triangle(const int x0, const int y0, const int x1, const int y1, const int x2, const int y2, const CImg& color, const CImg& light, const int lx0, const int ly0, const int lx1, const int ly1, const int lx2, const int ly2, const float opacity=1) { return draw_triangle(x0,y0,x1,y1,x2,y2,color.data,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); } //! Draw a 2D Pseudo-Phong-shaded triangle, with z-buffering. template CImg& draw_triangle(float *const 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& light, const int lx0, const int ly0, const int lx1, const int ly1, const int lx2, const int ly2, const float opacity=1) { if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; if (!color) throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified color is (null).", pixel_type()); if (!light) throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified light texture (%u,%u,%u,%u,%p) is empty.", pixel_type(),light.width,light.height,light.depth,light.dim,light.data); if (is_overlapped(light)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color, +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); const int whz = width*height*depth, offx = dim*whz; int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2; float nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2; if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nlx0,nlx1,nly0,nly1,nz0,nz1); if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nlx0,nlx2,nly0,nly2,nz0,nz2); if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nlx1,nlx2,nly1,nly2,nz1,nz2); if (ny0>=dimy() || ny2<0) return *this; float pzl = (nz1 - nz0)/(ny1 - ny0), pzr = (nz2 - nz0)/(ny2 - ny0), pzn = (nz2 - nz1)/(ny2 - ny1), zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))); _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y, nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) { if (y==ny1) { zl = nz1; pzl = pzn; } int xleft = xleft0, xright = xright0, lxleft = lxleft0, lxright = lxright0, lyleft = lyleft0, lyright = lyright0; float zleft = zl, zright = zr; if (xrightlxleft?lxright - lxleft:lxleft - lxright, dly = lyright>lyleft?lyright - lyleft:lyleft - lyright, rlx = dx?(lxright - lxleft)/dx:0, rly = dx?(lyright - lyleft)/dx:0, slx = lxright>lxleft?1:-1, sly = lyright>lyleft?1:-1, ndlx = dlx - (dx?dx*(dlx/dx):0), ndly = dly - (dx?dx*(dly/dx):0); const float pentez = (zright - zleft)/dx; int errlx = dx>>1, errly = errlx; if (xleft<0 && dx) { zleft-=xleft*(zright - zleft)/dx; lxleft-=xleft*(lxright - lxleft)/dx; lyleft-=xleft*(lyright - lyleft)/dx; } if (xleft<0) xleft = 0; if (xright>=dimx()-1) xright = dimx()-1; T *ptrd = ptr(xleft,y,0,0); float *ptrz = zbuffer + xleft + y*width; if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { if (zleft>*ptrz) { *ptrz = zleft; const tl l = light(lxleft,lyleft); const tc *col = color; cimg_forV(*this,k) { const tc cval = *(col++); *ptrd = (T)(l<1?l*cval:(2-l)*cval+(l-1)*maxval); ptrd+=whz; } ptrd-=offx; } zleft+=pentez; lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { if (zleft>*ptrz) { *ptrz = zleft; const tl l = light(lxleft,lyleft); const tc *col = color; cimg_forV(*this,k) { const tc cval = *(col++); const T val = (T)(l<1?l*cval:(2-l)*cval+(l-1)*maxval); *ptrd = (T)(nopacity*val + *ptrd*copacity); ptrd+=whz; } ptrd-=offx; } zleft+=pentez; lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); } zr+=pzr; zl+=pzl; } return *this; } //! Draw a 2D Pseudo-Phong-shaded triangle, with z-buffering. template CImg& draw_triangle(float *const 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& color, const CImg& light, const int lx0, const int ly0, const int lx1, const int ly1, const int lx2, const int ly2, const float opacity=1) { return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color.data,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); } //! Draw a 2D Gouraud-shaded textured triangle. /** \param x0 = X-coordinate of the first corner in the instance image. \param y0 = Y-coordinate of the first corner in the instance image. \param x1 = X-coordinate of the second corner in the instance image. \param y1 = Y-coordinate of the second corner in the instance image. \param x2 = X-coordinate of the third corner in the instance image. \param y2 = Y-coordinate of the third corner in the instance image. \param texture = texture image used to fill the triangle. \param tx0 = X-coordinate of the first corner in the texture image. \param ty0 = Y-coordinate of the first corner in the texture image. \param tx1 = X-coordinate of the second corner in the texture image. \param ty1 = Y-coordinate of the second corner in the texture image. \param tx2 = X-coordinate of the third corner in the texture image. \param ty2 = Y-coordinate of the third corner in the texture image. \param brightness0 = brightness value of the first corner. \param brightness1 = brightness value of the second corner. \param brightness2 = brightness value of the third corner. \param opacity = opacity of the drawing. \note Clipping is supported, but texture coordinates do not support clipping. **/ template CImg& draw_triangle(const int x0, const int y0, const int x1, const int y1, const int x2, const int y2, const CImg& 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) { if (is_empty()) return *this; if (!texture || texture.dim::draw_triangle() : Specified texture (%u,%u,%u,%u,%p) is not a valid argument.", pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); if (is_overlapped(texture)) return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,brightness0,brightness1,brightness2,opacity); static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); const int whz = width*height*depth, twhz = texture.width*texture.height*texture.depth, offx = dim*whz-1; int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2, nc0 = (int)((brightness0<0?0:(brightness0>2?2:brightness0))*256), nc1 = (int)((brightness1<0?0:(brightness1>2?2:brightness1))*256), nc2 = (int)((brightness2<0?0:(brightness2>2?2:brightness2))*256); if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nc0,nc1); if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nc0,nc2); if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nc1,nc2); if (ny0>=dimy() || ny2<0) return *this; _cimg_for_triangle4(*this,xleft0,cleft0,txleft0,tyleft0,xright0,cright0,txright0,tyright0,y, nx0,ny0,nc0,ntx0,nty0,nx1,ny1,nc1,ntx1,nty1,nx2,ny2,nc2,ntx2,nty2) { int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0, txleft = txleft0, txright = txright0, tyleft = tyleft0, tyright = tyright0; if (xrightcleft?cright - cleft:cleft - cright, dtx = txright>txleft?txright - txleft:txleft - txright, dty = tyright>tyleft?tyright - tyleft:tyleft - tyright, rc = dx?(cright - cleft)/dx:0, rtx = dx?(txright - txleft)/dx:0, rty = dx?(tyright - tyleft)/dx:0, sc = cright>cleft?1:-1, stx = txright>txleft?1:-1, sty = tyright>tyleft?1:-1, ndc = dc - (dx?dx*(dc/dx):0), ndtx = dtx - (dx?dx*(dtx/dx):0), ndty = dty - (dx?dx*(dty/dx):0); int errc = dx>>1, errtx = errc, errty = errc; if (xleft<0 && dx) { cleft-=xleft*(cright - cleft)/dx; txleft-=xleft*(txright - txleft)/dx; tyleft-=xleft*(tyright - tyleft)/dx; } if (xleft<0) xleft = 0; if (xright>=dimx()-1) xright = dimx()-1; T* ptrd = ptr(xleft,y,0,0); if (opacity>=1) for (int x = xleft; x<=xright; ++x) { const tc *col = texture.ptr(txleft,tyleft); cimg_forV(*this,k) { *ptrd = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256); ptrd+=whz; col+=twhz; } ptrd-=offx; cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); } else for (int x = xleft; x<=xright; ++x) { const tc *col = texture.ptr(txleft,tyleft); cimg_forV(*this,k) { const T val = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256); *ptrd = (T)(nopacity*val + *ptrd*copacity); ptrd+=whz; col+=twhz; } ptrd-=offx; cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); } } return *this; } //! Draw a 2D Gouraud-shaded textured triangle, with perspective correction. template CImg& 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& 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) { if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; if (!texture || texture.dim::draw_triangle() : Specified texture (%u,%u,%u,%u,%p) is not a valid argument.", pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2, brightness0,brightness1,brightness2,opacity); static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); const int whz = width*height*depth, twhz = texture.width*texture.height*texture.depth, offx = dim*whz-1; int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, nc0 = (int)((brightness0<0?0:(brightness0>2?2:brightness0))*256), nc1 = (int)((brightness1<0?0:(brightness1>2?2:brightness1))*256), nc2 = (int)((brightness2<0?0:(brightness2>2?2:brightness2))*256); float ntx0 = tx0/z0, nty0 = ty0/z0, ntx1 = tx1/z1, nty1 = ty1/z1, ntx2 = tx2/z2, nty2 = ty2/z2, nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2; if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1,nc0,nc1); if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2,nc0,nc2); if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2,nc1,nc2); if (ny0>=dimy() || ny2<0) return *this; float ptxl = (ntx1 - ntx0)/(ny1 - ny0), ptxr = (ntx2 - ntx0)/(ny2 - ny0), ptxn = (ntx2 - ntx1)/(ny2 - ny1), ptyl = (nty1 - nty0)/(ny1 - ny0), ptyr = (nty2 - nty0)/(ny2 - ny0), ptyn = (nty2 - nty1)/(ny2 - ny1), pzl = (nz1 - nz0)/(ny1 - ny0), pzr = (nz2 - nz0)/(ny2 - ny0), pzn = (nz2 - nz1)/(ny2 - ny1), zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)), tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)), zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))), txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))), tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1))); _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) { if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; } int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0; float zleft = zl, zright = zr, txleft = txl, txright = txr, tyleft = tyl, tyright = tyr; if (xrightcleft?cright - cleft:cleft - cright, rc = dx?(cright - cleft)/dx:0, sc = cright>cleft?1:-1, ndc = dc - (dx?dx*(dc/dx):0); const float pentez = (zright - zleft)/dx, pentetx = (txright - txleft)/dx, pentety = (tyright - tyleft)/dx; int errc = dx>>1; if (xleft<0 && dx) { cleft-=xleft*(cright - cleft)/dx; zleft-=xleft*(zright - zleft)/dx; txleft-=xleft*(txright - txleft)/dx; tyleft-=xleft*(tyright - tyleft)/dx; } if (xleft<0) xleft = 0; if (xright>=dimx()-1) xright = dimx()-1; T* ptrd = ptr(xleft,y,0,0); if (opacity>=1) for (int x = xleft; x<=xright; ++x) { const float invz = 1/zleft; const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { *ptrd = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256); ptrd+=whz; col+=twhz; } ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); } else for (int x = xleft; x<=xright; ++x) { const float invz = 1/zleft; const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { const T val = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256); *ptrd = (T)(nopacity*val + *ptrd*copacity); ptrd+=whz; col+=twhz; } ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); } zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl; } return *this; } //! Draw a 2D Gouraud-shaded textured triangle, with z-buffering and perspective correction. template CImg& draw_triangle(float *const 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& 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) { if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; if (!texture || texture.dim::draw_triangle() : Specified texture (%u,%u,%u,%u,%p) is not a valid argument.", pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); if (is_overlapped(texture)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2, brightness0,brightness1,brightness2,opacity); static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); const int whz = width*height*depth, twhz = texture.width*texture.height*texture.depth, offx = dim*whz; int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, nc0 = (int)((brightness0<0?0:(brightness0>2?2:brightness0))*256), nc1 = (int)((brightness1<0?0:(brightness1>2?2:brightness1))*256), nc2 = (int)((brightness2<0?0:(brightness2>2?2:brightness2))*256); float ntx0 = tx0/z0, nty0 = ty0/z0, ntx1 = tx1/z1, nty1 = ty1/z1, ntx2 = tx2/z2, nty2 = ty2/z2, nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2; if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1,nc0,nc1); if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2,nc0,nc2); if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2,nc1,nc2); if (ny0>=dimy() || ny2<0) return *this; float ptxl = (ntx1 - ntx0)/(ny1 - ny0), ptxr = (ntx2 - ntx0)/(ny2 - ny0), ptxn = (ntx2 - ntx1)/(ny2 - ny1), ptyl = (nty1 - nty0)/(ny1 - ny0), ptyr = (nty2 - nty0)/(ny2 - ny0), ptyn = (nty2 - nty1)/(ny2 - ny1), pzl = (nz1 - nz0)/(ny1 - ny0), pzr = (nz2 - nz0)/(ny2 - ny0), pzn = (nz2 - nz1)/(ny2 - ny1), zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)), tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)), zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))), txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))), tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1))); _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) { if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; } int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0; float zleft = zl, zright = zr, txleft = txl, txright = txr, tyleft = tyl, tyright = tyr; if (xrightcleft?cright - cleft:cleft - cright, rc = dx?(cright - cleft)/dx:0, sc = cright>cleft?1:-1, ndc = dc - (dx?dx*(dc/dx):0); const float pentez = (zright - zleft)/dx, pentetx = (txright - txleft)/dx, pentety = (tyright - tyleft)/dx; int errc = dx>>1; if (xleft<0 && dx) { cleft-=xleft*(cright - cleft)/dx; zleft-=xleft*(zright - zleft)/dx; txleft-=xleft*(txright - txleft)/dx; tyleft-=xleft*(tyright - tyleft)/dx; } if (xleft<0) xleft = 0; if (xright>=dimx()-1) xright = dimx()-1; T* ptrd = ptr(xleft,y); float *ptrz = zbuffer + xleft + y*width; if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) { if (zleft>*ptrz) { *ptrz = zleft; const float invz = 1/zleft; const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { *ptrd = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256); ptrd+=whz; col+=twhz; } ptrd-=offx; } zleft+=pentez; txleft+=pentetx; tyleft+=pentety; cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); } else for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) { if (zleft>*ptrz) { *ptrz = zleft; const float invz = 1/zleft; const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { const T val = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256); *ptrd = (T)(nopacity*val + *ptrd*copacity); ptrd+=whz; col+=twhz; } ptrd-=offx; } zleft+=pentez; txleft+=pentetx; tyleft+=pentety; cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); } zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl; } return *this; } //! Draw a 2D Pseudo-Phong-shaded textured triangle. /** \param x0 = X-coordinate of the first corner in the instance image. \param y0 = Y-coordinate of the first corner in the instance image. \param x1 = X-coordinate of the second corner in the instance image. \param y1 = Y-coordinate of the second corner in the instance image. \param x2 = X-coordinate of the third corner in the instance image. \param y2 = Y-coordinate of the third corner in the instance image. \param texture = texture image used to fill the triangle. \param tx0 = X-coordinate of the first corner in the texture image. \param ty0 = Y-coordinate of the first corner in the texture image. \param tx1 = X-coordinate of the second corner in the texture image. \param ty1 = Y-coordinate of the second corner in the texture image. \param tx2 = X-coordinate of the third corner in the texture image. \param ty2 = Y-coordinate of the third corner in the texture image. \param light = light image. \param lx0 = X-coordinate of the first corner in the light image. \param ly0 = Y-coordinate of the first corner in the light image. \param lx1 = X-coordinate of the second corner in the light image. \param ly1 = Y-coordinate of the second corner in the light image. \param lx2 = X-coordinate of the third corner in the light image. \param ly2 = Y-coordinate of the third corner in the light image. \param opacity = opacity of the drawing. \note Clipping is supported, but texture coordinates do not support clipping. **/ template CImg& draw_triangle(const int x0, const int y0, const int x1, const int y1, const int x2, const int y2, const CImg& texture, const int tx0, const int ty0, const int tx1, const int ty1, const int tx2, const int ty2, const CImg& light, const int lx0, const int ly0, const int lx1, const int ly1, const int lx2, const int ly2, const float opacity=1) { if (is_empty()) return *this; if (!texture || texture.dim::draw_triangle() : Specified texture (%u,%u,%u,%u,%p) is not a valid argument.", pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); if (!light) throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified light texture (%u,%u,%u,%u,%p) is empty.", pixel_type(),light.width,light.height,light.depth,light.dim,light.data); if (is_overlapped(texture)) return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); if (is_overlapped(light)) return draw_triangle(x0,y0,x1,y1,x2,y2,texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); const int whz = width*height*depth, twhz = texture.width*texture.height*texture.depth, offx = dim*whz-1; int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2, nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2; if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1); if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2); if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2); if (ny0>=dimy() || ny2<0) return *this; _cimg_for_triangle5(*this,xleft0,lxleft0,lyleft0,txleft0,tyleft0,xright0,lxright0,lyright0,txright0,tyright0,y, nx0,ny0,nlx0,nly0,ntx0,nty0,nx1,ny1,nlx1,nly1,ntx1,nty1,nx2,ny2,nlx2,nly2,ntx2,nty2) { int xleft = xleft0, xright = xright0, lxleft = lxleft0, lxright = lxright0, lyleft = lyleft0, lyright = lyright0, txleft = txleft0, txright = txright0, tyleft = tyleft0, tyright = tyright0; if (xrightlxleft?lxright - lxleft:lxleft - lxright, dly = lyright>lyleft?lyright - lyleft:lyleft - lyright, dtx = txright>txleft?txright - txleft:txleft - txright, dty = tyright>tyleft?tyright - tyleft:tyleft - tyright, rlx = dx?(lxright - lxleft)/dx:0, rly = dx?(lyright - lyleft)/dx:0, rtx = dx?(txright - txleft)/dx:0, rty = dx?(tyright - tyleft)/dx:0, slx = lxright>lxleft?1:-1, sly = lyright>lyleft?1:-1, stx = txright>txleft?1:-1, sty = tyright>tyleft?1:-1, ndlx = dlx - (dx?dx*(dlx/dx):0), ndly = dly - (dx?dx*(dly/dx):0), ndtx = dtx - (dx?dx*(dtx/dx):0), ndty = dty - (dx?dx*(dty/dx):0); int errlx = dx>>1, errly = errlx, errtx = errlx, errty = errlx; if (xleft<0 && dx) { lxleft-=xleft*(lxright - lxleft)/dx; lyleft-=xleft*(lyright - lyleft)/dx; txleft-=xleft*(txright - txleft)/dx; tyleft-=xleft*(tyright - tyleft)/dx; } if (xleft<0) xleft = 0; if (xright>=dimx()-1) xright = dimx()-1; T* ptrd = ptr(xleft,y,0,0); if (opacity>=1) for (int x = xleft; x<=xright; ++x) { const tl l = light(lxleft,lyleft); const tc *col = texture.ptr(txleft,tyleft); cimg_forV(*this,k) { *ptrd = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval); ptrd+=whz; col+=twhz; } ptrd-=offx; lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); } else for (int x = xleft; x<=xright; ++x) { const tl l = light(lxleft,lyleft); const tc *col = texture.ptr(txleft,tyleft); cimg_forV(*this,k) { const T val = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval); *ptrd = (T)(nopacity*val + *ptrd*copacity); ptrd+=whz; col+=twhz; } ptrd-=offx; lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); } } return *this; } //! Draw a 2D Pseudo-Phong-shaded textured triangle, with perspective correction. template CImg& 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& texture, const int tx0, const int ty0, const int tx1, const int ty1, const int tx2, const int ty2, const CImg& light, const int lx0, const int ly0, const int lx1, const int ly1, const int lx2, const int ly2, const float opacity=1) { if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; if (!texture || texture.dim::draw_triangle() : Specified texture (%u,%u,%u,%u,%p) is not a valid argument.", pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); if (!light) throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified light texture (%u,%u,%u,%u,%p) is empty.", pixel_type(),light.width,light.height,light.depth,light.dim,light.data); if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); if (is_overlapped(light)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); const int whz = width*height*depth, twhz = texture.width*texture.height*texture.depth, offx = dim*whz-1; int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2; float ntx0 = tx0/z0, nty0 = ty0/z0, ntx1 = tx1/z1, nty1 = ty1/z1, ntx2 = tx2/z2, nty2 = ty2/z2, nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2; if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1,nz0,nz1); if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2,nz0,nz2); if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2,nz1,nz2); if (ny0>=dimy() || ny2<0) return *this; float ptxl = (ntx1 - ntx0)/(ny1 - ny0), ptxr = (ntx2 - ntx0)/(ny2 - ny0), ptxn = (ntx2 - ntx1)/(ny2 - ny1), ptyl = (nty1 - nty0)/(ny1 - ny0), ptyr = (nty2 - nty0)/(ny2 - ny0), ptyn = (nty2 - nty1)/(ny2 - ny1), pzl = (nz1 - nz0)/(ny1 - ny0), pzr = (nz2 - nz0)/(ny2 - ny0), pzn = (nz2 - nz1)/(ny2 - ny1), zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)), tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)), zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))), txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))), tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1))); _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y, nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) { if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; } int xleft = xleft0, xright = xright0, lxleft = lxleft0, lxright = lxright0, lyleft = lyleft0, lyright = lyright0; float zleft = zl, zright = zr, txleft = txl, txright = txr, tyleft = tyl, tyright = tyr; if (xrightlxleft?lxright - lxleft:lxleft - lxright, dly = lyright>lyleft?lyright - lyleft:lyleft - lyright, rlx = dx?(lxright - lxleft)/dx:0, rly = dx?(lyright - lyleft)/dx:0, slx = lxright>lxleft?1:-1, sly = lyright>lyleft?1:-1, ndlx = dlx - (dx?dx*(dlx/dx):0), ndly = dly - (dx?dx*(dly/dx):0); const float pentez = (zright - zleft)/dx, pentetx = (txright - txleft)/dx, pentety = (tyright - tyleft)/dx; int errlx = dx>>1, errly = errlx; if (xleft<0 && dx) { zleft-=xleft*(zright - zleft)/dx; lxleft-=xleft*(lxright - lxleft)/dx; lyleft-=xleft*(lyright - lyleft)/dx; txleft-=xleft*(txright - txleft)/dx; tyleft-=xleft*(tyright - tyleft)/dx; } if (xleft<0) xleft = 0; if (xright>=dimx()-1) xright = dimx()-1; T* ptrd = ptr(xleft,y,0,0); if (opacity>=1) for (int x = xleft; x<=xright; ++x) { const float invz = 1/zleft; const tl l = light(lxleft,lyleft); const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { *ptrd = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval); ptrd+=whz; col+=twhz; } ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); } else for (int x = xleft; x<=xright; ++x) { const float invz = 1/zleft; const tl l = light(lxleft,lyleft); const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { const T val = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval); *ptrd = (T)(nopacity*val + *ptrd*copacity); ptrd+=whz; col+=twhz; } ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); } zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl; } return *this; } //! Draw a 2D Pseudo-Phong-shaded textured triangle, with z-buffering and perspective correction. template CImg& draw_triangle(float *const 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& texture, const int tx0, const int ty0, const int tx1, const int ty1, const int tx2, const int ty2, const CImg& light, const int lx0, const int ly0, const int lx1, const int ly1, const int lx2, const int ly2, const float opacity=1) { if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; if (!texture || texture.dim::draw_triangle() : Specified texture (%u,%u,%u,%u,%p) is not a valid argument.", pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); if (!light) throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified light texture (%u,%u,%u,%u,%p) is empty.", pixel_type(),light.width,light.height,light.depth,light.dim,light.data); if (is_overlapped(texture)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2, +texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); if (is_overlapped(light)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2, texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); const int whz = width*height*depth, twhz = texture.width*texture.height*texture.depth, offx = dim*whz; int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2; float ntx0 = tx0/z0, nty0 = ty0/z0, ntx1 = tx1/z1, nty1 = ty1/z1, ntx2 = tx2/z2, nty2 = ty2/z2, nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2; if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1,nz0,nz1); if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2,nz0,nz2); if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2,nz1,nz2); if (ny0>=dimy() || ny2<0) return *this; float ptxl = (ntx1 - ntx0)/(ny1 - ny0), ptxr = (ntx2 - ntx0)/(ny2 - ny0), ptxn = (ntx2 - ntx1)/(ny2 - ny1), ptyl = (nty1 - nty0)/(ny1 - ny0), ptyr = (nty2 - nty0)/(ny2 - ny0), ptyn = (nty2 - nty1)/(ny2 - ny1), pzl = (nz1 - nz0)/(ny1 - ny0), pzr = (nz2 - nz0)/(ny2 - ny0), pzn = (nz2 - nz1)/(ny2 - ny1), zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)), tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)), zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))), txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))), tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1))); _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y, nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) { if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; } int xleft = xleft0, xright = xright0, lxleft = lxleft0, lxright = lxright0, lyleft = lyleft0, lyright = lyright0; float zleft = zl, zright = zr, txleft = txl, txright = txr, tyleft = tyl, tyright = tyr; if (xrightlxleft?lxright - lxleft:lxleft - lxright, dly = lyright>lyleft?lyright - lyleft:lyleft - lyright, rlx = dx?(lxright - lxleft)/dx:0, rly = dx?(lyright - lyleft)/dx:0, slx = lxright>lxleft?1:-1, sly = lyright>lyleft?1:-1, ndlx = dlx - (dx?dx*(dlx/dx):0), ndly = dly - (dx?dx*(dly/dx):0); const float pentez = (zright - zleft)/dx, pentetx = (txright - txleft)/dx, pentety = (tyright - tyleft)/dx; int errlx = dx>>1, errly = errlx; if (xleft<0 && dx) { zleft-=xleft*(zright - zleft)/dx; lxleft-=xleft*(lxright - lxleft)/dx; lyleft-=xleft*(lyright - lyleft)/dx; txleft-=xleft*(txright - txleft)/dx; tyleft-=xleft*(tyright - tyleft)/dx; } if (xleft<0) xleft = 0; if (xright>=dimx()-1) xright = dimx()-1; T* ptrd = ptr(xleft,y); float *ptrz = zbuffer + xleft + y*width; if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { if (zleft>*ptrz) { *ptrz = zleft; const float invz = 1/zleft; const tl l = light(lxleft,lyleft); const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { *ptrd = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval); ptrd+=whz; col+=twhz; } ptrd-=offx; } zleft+=pentez; txleft+=pentetx; tyleft+=pentety; lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { if (zleft>*ptrz) { *ptrz = zleft; const float invz = 1/zleft; const tl l = light(lxleft,lyleft); const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { const T val = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval); *ptrd = (T)(nopacity*val + *ptrd*copacity); ptrd+=whz; col+=twhz; } ptrd-=offx; } zleft+=pentez; txleft+=pentetx; tyleft+=pentety; lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); } zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl; } return *this; } // Draw a 2D ellipse (inner routine). template CImg& _draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float ru, const float rv, const tc *const color, const float opacity, const unsigned int pattern) { if (is_empty()) return *this; if (!color) throw CImgArgumentException("CImg<%s>::draw_ellipse : Specified color is (null).", pixel_type()); _draw_scanline(color,opacity); const float nr1 = cimg::abs(r1), nr2 = cimg::abs(r2), norm = (float)cimg_std::sqrt(ru*ru+rv*rv), u = norm>0?ru/norm:1, v = norm>0?rv/norm:0, rmax = cimg::max(nr1,nr2), l1 = (float)cimg_std::pow(rmax/(nr1>0?nr1:1e-6),2), l2 = (float)cimg_std::pow(rmax/(nr2>0?nr2:1e-6),2), a = l1*u*u + l2*v*v, b = u*v*(l1-l2), c = l1*v*v + l2*u*u; const int yb = (int)cimg_std::sqrt(a*rmax*rmax/(a*c-b*b)), tymin = y0 - yb - 1, tymax = y0 + yb + 1, ymin = tymin<0?0:tymin, ymax = tymax>=dimy()?height-1:tymax; int oxmin = 0, oxmax = 0; bool first_line = true; for (int y = ymin; y<=ymax; ++y) { const float Y = y-y0 + (y0?(float)cimg_std::sqrt(delta)/a:0.0f, bY = b*Y/a, fxmin = x0-0.5f-bY-sdelta, fxmax = x0+0.5f-bY+sdelta; const int xmin = (int)fxmin, xmax = (int)fxmax; if (!pattern) _draw_scanline(xmin,xmax,y,color,opacity); else { if (first_line) { if (y0-yb>=0) _draw_scanline(xmin,xmax,y,color,opacity); else draw_point(xmin,y,color,opacity).draw_point(xmax,y,color,opacity); first_line = false; } else { if (xmin CImg& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float ru, const float rv, const tc *const color, const float opacity=1) { return _draw_ellipse(x0,y0,r1,r2,ru,rv,color,opacity,0U); } //! Draw a filled ellipse. template CImg& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float ru, const float rv, const CImg& color, const float opacity=1) { return draw_ellipse(x0,y0,r1,r2,ru,rv,color.data,opacity); } //! Draw a filled ellipse. /** \param x0 = X-coordinate of the ellipse center. \param y0 = Y-coordinate of the ellipse center. \param tensor = Diffusion tensor describing the ellipse. \param color = array of dimv() values of type \c T, defining the drawing color. \param opacity = opacity of the drawing. **/ template CImg& draw_ellipse(const int x0, const int y0, const CImg &tensor, const tc *const color, const float opacity=1) { CImgList eig = tensor.get_symmetric_eigen(); const CImg &val = eig[0], &vec = eig[1]; return draw_ellipse(x0,y0,val(0),val(1),vec(0,0),vec(0,1),color,opacity); } //! Draw a filled ellipse. template CImg& draw_ellipse(const int x0, const int y0, const CImg &tensor, const CImg& color, const float opacity=1) { return draw_ellipse(x0,y0,tensor,color.data,opacity); } //! Draw an outlined ellipse. /** \param x0 = X-coordinate of the ellipse center. \param y0 = Y-coordinate of the ellipse center. \param r1 = First radius of the ellipse. \param r2 = Second radius of the ellipse. \param ru = X-coordinate of the orientation vector related to the first radius. \param rv = Y-coordinate of the orientation vector related to the first radius. \param color = array of dimv() values of type \c T, defining the drawing color. \param pattern = If zero, the ellipse is filled, else pattern is an integer whose bits describe the outline pattern. \param opacity = opacity of the drawing. **/ template CImg& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float ru, const float rv, const tc *const color, const float opacity, const unsigned int pattern) { if (pattern) _draw_ellipse(x0,y0,r1,r2,ru,rv,color,opacity,pattern); return *this; } //! Draw an outlined ellipse. template CImg& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float ru, const float rv, const CImg& color, const float opacity, const unsigned int pattern) { return draw_ellipse(x0,y0,r1,r2,ru,rv,color.data,opacity,pattern); } //! Draw an outlined ellipse. /** \param x0 = X-coordinate of the ellipse center. \param y0 = Y-coordinate of the ellipse center. \param tensor = Diffusion tensor describing the ellipse. \param color = array of dimv() values of type \c T, defining the drawing color. \param pattern = If zero, the ellipse is filled, else pattern is an integer whose bits describe the outline pattern. \param opacity = opacity of the drawing. **/ template CImg& draw_ellipse(const int x0, const int y0, const CImg &tensor, const tc *const color, const float opacity, const unsigned int pattern) { CImgList eig = tensor.get_symmetric_eigen(); const CImg &val = eig[0], &vec = eig[1]; return draw_ellipse(x0,y0,val(0),val(1),vec(0,0),vec(0,1),color,opacity,pattern); } //! Draw an outlined ellipse. template CImg& draw_ellipse(const int x0, const int y0, const CImg &tensor, const CImg& color, const float opacity, const unsigned int pattern) { return draw_ellipse(x0,y0,tensor,color.data,opacity,pattern); } //! Draw a filled circle. /** \param x0 X-coordinate of the circle center. \param y0 Y-coordinate of the circle center. \param radius Circle radius. \param color Array of dimv() values of type \c T, defining the drawing color. \param opacity Drawing opacity. \note - Circle version of the Bresenham's algorithm is used. **/ template CImg& draw_circle(const int x0, const int y0, int radius, const tc *const color, const float opacity=1) { if (!is_empty()) { if (!color) throw CImgArgumentException("CImg<%s>::draw_circle : Specified color is (null).", pixel_type()); _draw_scanline(color,opacity); if (radius<0 || x0-radius>=dimx() || y0+radius<0 || y0-radius>=dimy()) return *this; if (y0>=0 && y0=0) { const int x1 = x0-x, x2 = x0+x, y1 = y0-y, y2 = y0+y; if (y1>=0 && y1=0 && y2=0 && y1=0 && y2 CImg& draw_circle(const int x0, const int y0, int radius, const CImg& color, const float opacity=1) { return draw_circle(x0,y0,radius,color.data,opacity); } //! Draw an outlined circle. /** \param x0 X-coordinate of the circle center. \param y0 Y-coordinate of the circle center. \param radius Circle radius. \param color Array of dimv() values of type \c T, defining the drawing color. \param opacity Drawing opacity. **/ template CImg& draw_circle(const int x0, const int y0, int radius, const tc *const color, const float opacity, const unsigned int) { if (!is_empty()) { if (!color) throw CImgArgumentException("CImg<%s>::draw_circle : Specified color is (null).", pixel_type()); if (radius<0 || x0-radius>=dimx() || y0+radius<0 || y0-radius>=dimy()) return *this; if (!radius) return draw_point(x0,y0,color,opacity); draw_point(x0-radius,y0,color,opacity).draw_point(x0+radius,y0,color,opacity). draw_point(x0,y0-radius,color,opacity).draw_point(x0,y0+radius,color,opacity); if (radius==1) return *this; for (int f=1-radius, ddFx=0, ddFy=-(radius<<1), x=0, y=radius; x=0) { f+=(ddFy+=2); --y; } ++x; ++(f+=(ddFx+=2)); if (x!=y+1) { 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; draw_point(x1,y1,color,opacity).draw_point(x1,y2,color,opacity). draw_point(x2,y1,color,opacity).draw_point(x2,y2,color,opacity); if (x!=y) draw_point(x3,y3,color,opacity).draw_point(x4,y4,color,opacity). draw_point(x4,y3,color,opacity).draw_point(x3,y4,color,opacity); } } } return *this; } //! Draw an outlined circle. template CImg& draw_circle(const int x0, const int y0, int radius, const CImg& color, const float opacity, const unsigned int pattern) { return draw_circle(x0,y0,radius,color.data,opacity,pattern); } // Draw a text (internal). template CImg& _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& font) { if (!text) return *this; if (!font) throw CImgArgumentException("CImg<%s>::draw_text() : Specified font (%u,%p) is empty.", pixel_type(),font.size,font.data); const int text_length = cimg::strlen(text); if (is_empty()) { // If needed, pre-compute necessary size of the image int x = 0, y = 0, w = 0; unsigned char c = 0; for (int i = 0; iw) w = x; x = 0; break; case '\t' : x+=4*font[' '].width; break; default : if (cw) w=x; y+=font[' '].height; } assign(x0+w,y0+y,1,font[' '].dim,0); if (background_color) cimg_forV(*this,k) get_shared_channel(k).fill((T)background_color[k]); } int x = x0, y = y0; CImg letter; for (int i = 0; i& mask = (c+256)<(int)font.size?font[c+256]:font[c]; if (foreground_color) for (unsigned int p = 0; p=512) draw_image(x,y,letter,mask,opacity,(T)1); else draw_image(x,y,letter,opacity); x+=letter.width; } } } return *this; } //! Draw a text. /** \param x0 X-coordinate of the text in the instance image. \param y0 Y-coordinate of the text in the instance image. \param foreground_color Array of dimv() values of type \c T, defining the foreground color (0 means 'transparent'). \param background_color Array of dimv() values of type \c T, defining the background color (0 means 'transparent'). \param font Font used for drawing text. \param opacity Drawing opacity. \param format 'printf'-style format string, followed by arguments. \note Clipping is supported. **/ template CImg& 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& font, ...) { char tmp[2048] = { 0 }; cimg_std::va_list ap; va_start(ap,font); cimg_std::vsprintf(tmp,text,ap); va_end(ap); return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font); } //! Draw a text. template CImg& draw_text(const int x0, const int y0, const char *const text, const CImg& foreground_color, const CImg& background_color, const float opacity, const CImgList& font, ...) { char tmp[2048] = { 0 }; cimg_std::va_list ap; va_start(ap,font); cimg_std::vsprintf(tmp,text,ap); va_end(ap); return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font); } //! Draw a text. template CImg& draw_text(const int x0, const int y0, const char *const text, const tc *const foreground_color, const int background_color, const float opacity, const CImgList& font, ...) { char tmp[2048] = { 0 }; cimg_std::va_list ap; va_start(ap,font); cimg_std::vsprintf(tmp,text,ap); va_end(ap); return _draw_text(x0,y0,tmp,foreground_color,(tc*)background_color,opacity,font); } //! Draw a text. template CImg& draw_text(const int x0, const int y0, const char *const text, const int foreground_color, const tc *const background_color, const float opacity, const CImgList& font, ...) { char tmp[2048] = { 0 }; cimg_std::va_list ap; va_start(ap,font); cimg_std::vsprintf(tmp,text,ap); va_end(ap); return _draw_text(x0,y0,tmp,(tc*)foreground_color,background_color,opacity,font); } //! Draw a text. /** \param x0 X-coordinate of the text in the instance image. \param y0 Y-coordinate of the text in the instance image. \param foreground_color Array of dimv() values of type \c T, defining the foreground color (0 means 'transparent'). \param background_color Array of dimv() values of type \c T, defining the background color (0 means 'transparent'). \param font_size Size of the font (nearest match). \param opacity Drawing opacity. \param format 'printf'-style format string, followed by arguments. \note Clipping is supported. **/ template CImg& 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_size=11, ...) { static CImgList font; static unsigned int fsize = 0; if (fsize!=font_size) { font = CImgList::font(font_size); fsize = font_size; } char tmp[2048] = { 0 }; cimg_std::va_list ap; va_start(ap,font_size); cimg_std::vsprintf(tmp,text,ap); va_end(ap); return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font); } //! Draw a text. template CImg& draw_text(const int x0, const int y0, const char *const text, const CImg& foreground_color, const CImg& background_color, const float opacity=1, const unsigned int font_size=11, ...) { static CImgList font; static unsigned int fsize = 0; if (fsize!=font_size) { font = CImgList::font(font_size); fsize = font_size; } char tmp[2048] = { 0 }; cimg_std::va_list ap; va_start(ap,font_size); cimg_std::vsprintf(tmp,text,ap); va_end(ap); return _draw_text(x0,y0,tmp,foreground_color.data,background_color.data,opacity,font); } //! Draw a text. template CImg& 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_size=11, ...) { static CImgList font; static unsigned int fsize = 0; if (fsize!=font_size) { font = CImgList::font(font_size); fsize = font_size; } char tmp[2048] = { 0 }; cimg_std::va_list ap; va_start(ap,font_size); cimg_std::vsprintf(tmp,text,ap); va_end(ap); return _draw_text(x0,y0,tmp,foreground_color,(const tc*)background_color,opacity,font); } //! Draw a text. template CImg& draw_text(const int x0, const int y0, const char *const text, const int foreground_color, const tc *const background_color, const float opacity=1, const unsigned int font_size=11, ...) { static CImgList font; static unsigned int fsize = 0; if (fsize!=font_size) { font = CImgList::font(font_size); fsize = font_size; } char tmp[2048] = { 0 }; cimg_std::va_list ap; va_start(ap,font_size); cimg_std::vsprintf(tmp,text,ap); va_end(ap); return _draw_text(x0,y0,tmp,(tc*)foreground_color,background_color,opacity,font); } //! Draw a vector field in the instance image, using a colormap. /** \param flow Image of 2d vectors used as input data. \param color Image of dimv()-D vectors corresponding to the color of each arrow. \param sampling Length (in pixels) between each arrow. \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length). \param quiver_type Type of plot. Can be 0 (arrows) or 1 (segments). \param opacity Opacity of the drawing. \param pattern Used pattern to draw lines. \note Clipping is supported. **/ template CImg& draw_quiver(const CImg& flow, const t2 *const color, const float opacity=1, const unsigned int sampling=25, const float factor=-20, const int quiver_type=0, const unsigned int pattern=~0U) { return draw_quiver(flow,CImg(color,dim,1,1,1,true),opacity,sampling,factor,quiver_type,pattern); } //! Draw a vector field in the instance image, using a colormap. /** \param flow Image of 2d vectors used as input data. \param color Image of dimv()-D vectors corresponding to the color of each arrow. \param sampling Length (in pixels) between each arrow. \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length). \param quiver_type Type of plot. Can be 0 (arrows) or 1 (segments). \param opacity Opacity of the drawing. \param pattern Used pattern to draw lines. \note Clipping is supported. **/ template CImg& draw_quiver(const CImg& flow, const CImg& color, const float opacity=1, const unsigned int sampling=25, const float factor=-20, const int quiver_type=0, const unsigned int pattern=~0U) { if (!is_empty()) { if (!flow || flow.dim!=2) throw CImgArgumentException("CImg<%s>::draw_quiver() : Specified flow (%u,%u,%u,%u,%p) has wrong dimensions.", pixel_type(),flow.width,flow.height,flow.depth,flow.dim,flow.data); if (sampling<=0) throw CImgArgumentException("CImg<%s>::draw_quiver() : Incorrect sampling value = %g", pixel_type(),sampling); const bool colorfield = (color.width==flow.width && color.height==flow.height && color.depth==1 && color.dim==dim); if (is_overlapped(flow)) return draw_quiver(+flow,color,opacity,sampling,factor,quiver_type,pattern); float vmax,fact; if (factor<=0) { float m, M = (float)flow.get_pointwise_norm(2).maxmin(m); vmax = (float)cimg::max(cimg::abs(m),cimg::abs(M)); fact = -factor; } else { fact = factor; vmax = 1; } for (unsigned int y=sampling/2; y CImg& draw_graph(const CImg& data, const tc *const color, const float opacity=1, const unsigned int plot_type=1, const unsigned int vertex_type=1, const double ymin=0, const double ymax=0, const unsigned int pattern=~0U) { if (is_empty() || height<=1) return *this;; const unsigned long siz = data.size(); if (!color) throw CImgArgumentException("CImg<%s>::draw_graph() : Specified color is (null)", pixel_type()); tc *color1 = 0, *color2 = 0; if (plot_type==3) { color1 = new tc[dim]; color2 = new tc[dim]; cimg_forV(*this,k) { color1[k] = (tc)(color[k]*0.6f); color2[k] = (tc)(color[k]*0.3f); } } double m = ymin, M = ymax; if (ymin==ymax) m = (double)data.maxmin(M); if (m==M) { --m; ++M; } const float ca = (float)(M-m)/(height-1); bool init_hatch = true; // Draw graph edges switch (plot_type%4) { case 1 : { // Segments int oX = 0, oY = (int)((data[0]-m)/ca); for (unsigned long off = 1; off ndata = data.get_shared_points(0,siz-1); int oY = (int)((data[0]-m)/ca); cimg_forX(*this,x) { const int Y = (int)((ndata._cubic_atX((float)x*ndata.width/width)-m)/ca); if (x>0) draw_line(x,oY,x+1,Y,color,opacity,pattern,init_hatch); init_hatch = false; oY = Y; } } break; case 3 : { // Bars const int Y0 = (int)(-m/ca); int oX = 0; cimg_foroff(data,off) { const int X = (off+1)*width/siz-1, Y = (int)((data[off]-m)/ca); draw_rectangle(oX,Y0,X,Y,color1,opacity). draw_line(oX,Y,oX,Y0,color2,opacity). draw_line(oX,Y0,X,Y0,Y<=Y0?color2:color,opacity). draw_line(X,Y,X,Y0,color,opacity). draw_line(oX,Y,X,Y,Y<=Y0?color:color2,opacity); oX = X+1; } } break; default : break; // No edges } // Draw graph points switch (vertex_type%8) { case 1 : { // Point cimg_foroff(data,off) { const int X = off*width/siz, Y = (int)((data[off]-m)/ca); draw_point(X,Y,color,opacity); } } break; case 2 : { // Standard Cross cimg_foroff(data,off) { const int X = off*width/siz, Y = (int)((data[off]-m)/ca); draw_line(X-3,Y,X+3,Y,color,opacity).draw_line(X,Y-3,X,Y+3,color,opacity); } } break; case 3 : { // Rotated Cross cimg_foroff(data,off) { const int X = off*width/siz, Y = (int)((data[off]-m)/ca); draw_line(X-3,Y-3,X+3,Y+3,color,opacity).draw_line(X-3,Y+3,X+3,Y-3,color,opacity); } } break; case 4 : { // Filled Circle cimg_foroff(data,off) { const int X = off*width/siz, Y = (int)((data[off]-m)/ca); draw_circle(X,Y,3,color,opacity); } } break; case 5 : { // Outlined circle cimg_foroff(data,off) { const int X = off*width/siz, Y = (int)((data[off]-m)/ca); draw_circle(X,Y,3,color,opacity,0U); } } break; case 6 : { // Square cimg_foroff(data,off) { const int X = off*width/siz, Y = (int)((data[off]-m)/ca); draw_rectangle(X-3,Y-3,X+3,Y+3,color,opacity,~0U); } } break; case 7 : { // Diamond cimg_foroff(data,off) { const int X = off*width/siz, Y = (int)((data[off]-m)/ca); draw_line(X,Y-4,X+4,Y,color,opacity). draw_line(X+4,Y,X,Y+4,color,opacity). draw_line(X,Y+4,X-4,Y,color,opacity). draw_line(X-4,Y,X,Y-4,color,opacity); } } break; default : break; // No vertices } if (color1) delete[] color1; if (color2) delete[] color2; return *this; } //! Draw a 1D graph on the instance image. template CImg& draw_graph(const CImg& data, const CImg& color, const float opacity=1, const unsigned int plot_type=1, const unsigned int vertex_type=1, const double ymin=0, const double ymax=0, const unsigned int pattern=~0U) { return draw_graph(data,color.data,opacity,plot_type,vertex_type,ymin,ymax,pattern); } //! Draw a labeled horizontal axis on the instance image. /** \param xvalues Lower bound of the x-range. \param y Y-coordinate of the horizontal axis in the instance image. \param color Array of dimv() values of type \c T, defining the drawing color. \param opacity Drawing opacity. \param pattern Drawing pattern. \param opacity_out Drawing opacity of 'outside' axes. \note if \c precision==0, precision of the labels is automatically computed. **/ template CImg& draw_axis(const CImg& xvalues, const int y, const tc *const color, const float opacity=1, const unsigned int pattern=~0U) { if (!is_empty()) { int siz = (int)xvalues.size()-1; if (siz<=0) draw_line(0,y,width-1,y,color,opacity,pattern); else { if (xvalues[0] CImg& draw_axis(const CImg& xvalues, const int y, const CImg& color, const float opacity=1, const unsigned int pattern=~0U) { return draw_axis(xvalues,y,color.data,opacity,pattern); } //! Draw a labeled vertical axis on the instance image. template CImg& draw_axis(const int x, const CImg& yvalues, const tc *const color, const float opacity=1, const unsigned int pattern=~0U) { if (!is_empty()) { int siz = (int)yvalues.size()-1; if (siz<=0) draw_line(x,0,x,height-1,color,opacity,pattern); else { if (yvalues[0]=dimy()-11?dimy()-11:tmp), xt = x-(int)cimg::strlen(txt)*7; draw_point(x-1,yi,color,opacity).draw_point(x+1,yi,color,opacity); if (xt>0) draw_text(xt,nyi,txt,color,(tc*)0,opacity,11); else draw_text(x+3,nyi,txt,color,(tc*)0,opacity,11); } } } return *this; } //! Draw a labeled vertical axis on the instance image. template CImg& draw_axis(const int x, const CImg& yvalues, const CImg& color, const float opacity=1, const unsigned int pattern=~0U) { return draw_axis(x,yvalues,color.data,opacity,pattern); } //! Draw a labeled horizontal+vertical axis on the instance image. template CImg& draw_axis(const CImg& xvalues, const CImg& yvalues, const tc *const color, const float opacity=1, const unsigned int patternx=~0U, const unsigned int patterny=~0U) { if (!is_empty()) { const CImg nxvalues(xvalues.data,xvalues.size(),1,1,1,true); const int sizx = (int)xvalues.size()-1, wm1 = (int)(width)-1; if (sizx>0) { float ox = (float)nxvalues[0]; for (unsigned int x = 1; x nyvalues(yvalues.data,yvalues.size(),1,1,1,true); const int sizy = (int)yvalues.size()-1, hm1 = (int)(height)-1; if (sizy>0) { float oy = (float)nyvalues[0]; for (unsigned int y = 1; y CImg& draw_axis(const CImg& xvalues, const CImg& yvalues, const CImg& color, const float opacity=1, const unsigned int patternx=~0U, const unsigned int patterny=~0U) { return draw_axis(xvalues,yvalues,color.data,opacity,patternx,patterny); } //! Draw a labeled horizontal+vertical axis on the instance image. template CImg& draw_axis(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 patternx=~0U, const unsigned int patterny=~0U) { if (!is_empty()) { const float dx = cimg::abs(x1-x0), dy = cimg::abs(y1-y0), px = (precisionx==0)?(float)cimg_std::pow(10.0,(int)cimg_std::log10(dx)-2.0):precisionx, py = (precisiony==0)?(float)cimg_std::pow(10.0,(int)cimg_std::log10(dy)-2.0):precisiony; draw_axis(CImg::sequence(subdivisionx>0?subdivisionx:1-dimx()/subdivisionx,x0,x1).round(px), CImg::sequence(subdivisiony>0?subdivisiony:1-dimy()/subdivisiony,y0,y1).round(py), color,opacity,patternx,patterny); } return *this; } //! Draw a labeled horizontal+vertical axis on the instance image. template CImg& draw_axis(const float x0, const float x1, const float y0, const float y1, const CImg& color, const float opacity=1, const int subdivisionx=-60, const int subdivisiony=-60, const float precisionx=0, const float precisiony=0, const unsigned int patternx=~0U, const unsigned int patterny=~0U) { return draw_axis(x0,x1,y0,y1,color.data,opacity,subdivisionx,subdivisiony,precisionx,precisiony,patternx,patterny); } //! Draw grid. template CImg& draw_grid(const CImg& xvalues, const CImg& yvalues, const tc *const color, const float opacity=1, const unsigned int patternx=~0U, const unsigned int patterny=~0U) { if (!is_empty()) { if (xvalues) cimg_foroff(xvalues,x) { const int xi = (int)xvalues[x]; if (xi>=0 && xi=0 && yi CImg& draw_grid(const CImg& xvalues, const CImg& yvalues, const CImg& color, const float opacity=1, const unsigned int patternx=~0U, const unsigned int patterny=~0U) { return draw_grid(xvalues,yvalues,color.data,opacity,patternx,patterny); } //! Draw grid. template CImg& draw_grid(const float deltax, const float deltay, const float offsetx, const float offsety, const bool invertx, const bool inverty, const tc *const color, const float opacity=1, const unsigned int patternx=~0U, const unsigned int patterny=~0U) { CImg seqx, seqy; if (deltax!=0) { const float dx = deltax>0?deltax:width*-deltax/100; const unsigned int nx = (unsigned int)(width/dx); seqx = CImg::sequence(1+nx,0,(unsigned int)(dx*nx)); if (offsetx) cimg_foroff(seqx,x) seqx(x) = (unsigned int)cimg::mod(seqx(x)+offsetx,(float)width); if (invertx) cimg_foroff(seqx,x) seqx(x) = width-1-seqx(x); } if (deltay!=0) { const float dy = deltay>0?deltay:height*-deltay/100; const unsigned int ny = (unsigned int)(height/dy); seqy = CImg::sequence(1+ny,0,(unsigned int)(dy*ny)); if (offsety) cimg_foroff(seqy,y) seqy(y) = (unsigned int)cimg::mod(seqy(y)+offsety,(float)height); if (inverty) cimg_foroff(seqy,y) seqy(y) = height-1-seqy(y); } return draw_grid(seqx,seqy,color,opacity,patternx,patterny); } //! Draw grid. template CImg& draw_grid(const float deltax, const float deltay, const float offsetx, const float offsety, const bool invertx, const bool inverty, const CImg& color, const float opacity=1, const unsigned int patternx=~0U, const unsigned int patterny=~0U) { return draw_grid(deltax,deltay,offsetx,offsety,invertx,inverty,color.data,opacity,patternx,patterny); } //! Draw a 3D filled region starting from a point (\c x,\c y,\ z) in the instance image. /** \param x X-coordinate of the starting point of the region to fill. \param y Y-coordinate of the starting point of the region to fill. \param z Z-coordinate of the starting point of the region to fill. \param color An array of dimv() values of type \c T, defining the drawing color. \param region Image that will contain the mask of the filled region mask, as an output. \param sigma Tolerance concerning neighborhood values. \param opacity Opacity of the drawing. \param high_connexity Tells if 8-connexity must be used (only for 2D images). \return \p region is initialized with the binary mask of the filled region. **/ template CImg& draw_fill(const int x, const int y, const int z, const tc *const color, const float opacity, CImg& region, const float sigma=0, const bool high_connexity=false) { #define _cimg_draw_fill_test(x,y,z,res) if (region(x,y,z)) res = false; else { \ res = true; \ const T *reference_col = reference_color.ptr() + dim, *ptrs = ptr(x,y,z) + siz; \ for (unsigned int i = dim; res && i; --i) { ptrs-=whz; res = (cimg::abs(*ptrs - *(--reference_col))<=sigma); } \ region(x,y,z) = (t)(res?1:noregion); \ } #define _cimg_draw_fill_set(x,y,z) { \ const tc *col = color; \ T *ptrd = ptr(x,y,z); \ if (opacity>=1) cimg_forV(*this,k) { *ptrd = (T)*(col++); ptrd+=whz; } \ else cimg_forV(*this,k) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whz; } \ } #define _cimg_draw_fill_insert(x,y,z) { \ if (posr1>=remaining.height) remaining.resize(3,remaining.height<<1,1,1,0); \ unsigned int *ptrr = remaining.ptr(0,posr1); \ *(ptrr++) = x; *(ptrr++) = y; *(ptrr++) = z; ++posr1; \ } #define _cimg_draw_fill_test_neighbor(x,y,z,cond) if (cond) { \ const unsigned int tx = x, ty = y, tz = z; \ _cimg_draw_fill_test(tx,ty,tz,res); if (res) _cimg_draw_fill_insert(tx,ty,tz); \ } if (!color) throw CImgArgumentException("CImg<%s>::draw_fill() : Specified color is (null).", pixel_type()); region.assign(width,height,depth,1,(t)0); if (x>=0 && x=0 && y=0 && z1; const CImg reference_color = get_vector_at(x,y,z); CImg remaining(3,512,1,1,0); remaining(0,0) = x; remaining(1,0) = y; remaining(2,0) = z; unsigned int posr0 = 0, posr1 = 1; region(x,y,z) = (t)1; const t noregion = ((t)1==(t)2)?(t)0:(t)(-1); if (threed) do { // 3D version of the filling algorithm const unsigned int *pcurr = remaining.ptr(0,posr0++), xc = *(pcurr++), yc = *(pcurr++), zc = *(pcurr++); if (posr0>=512) { remaining.translate(0,posr0); posr1-=posr0; posr0 = 0; } bool cont, res; unsigned int nxc = xc; do { // X-backward _cimg_draw_fill_set(nxc,yc,zc); _cimg_draw_fill_test_neighbor(nxc,yc-1,zc,yc!=0); _cimg_draw_fill_test_neighbor(nxc,yc+1,zc,ycposr0); else do { // 2D version of the filling algorithm const unsigned int *pcurr = remaining.ptr(0,posr0++), xc = *(pcurr++), yc = *(pcurr++); if (posr0>=512) { remaining.translate(0,posr0); posr1-=posr0; posr0 = 0; } bool cont, res; unsigned int nxc = xc; do { // X-backward _cimg_draw_fill_set(nxc,yc,0); _cimg_draw_fill_test_neighbor(nxc,yc-1,0,yc!=0); _cimg_draw_fill_test_neighbor(nxc,yc+1,0,ycposr0); if (noregion) cimg_for(region,ptr,t) if (*ptr==noregion) *ptr = (t)0; } return *this; } //! Draw a 3D filled region starting from a point (\c x,\c y,\ z) in the instance image. template CImg& draw_fill(const int x, const int y, const int z, const CImg& color, const float opacity, CImg& region, const float sigma=0, const bool high_connexity=false) { return draw_fill(x,y,z,color.data,opacity,region,sigma,high_connexity); } //! Draw a 3D filled region starting from a point (\c x,\c y,\ z) in the instance image. /** \param x = X-coordinate of the starting point of the region to fill. \param y = Y-coordinate of the starting point of the region to fill. \param z = Z-coordinate of the starting point of the region to fill. \param color = an array of dimv() values of type \c T, defining the drawing color. \param sigma = tolerance concerning neighborhood values. \param opacity = opacity of the drawing. **/ template CImg& 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 high_connexity=false) { CImg tmp; return draw_fill(x,y,z,color,opacity,tmp,sigma,high_connexity); } //! Draw a 3D filled region starting from a point (\c x,\c y,\ z) in the instance image. template CImg& draw_fill(const int x, const int y, const int z, const CImg& color, const float opacity=1, const float sigma=0, const bool high_connexity=false) { return draw_fill(x,y,z,color.data,opacity,sigma,high_connexity); } //! Draw a 2D filled region starting from a point (\c x,\c y) in the instance image. /** \param x = X-coordinate of the starting point of the region to fill. \param y = Y-coordinate of the starting point of the region to fill. \param color = an array of dimv() values of type \c T, defining the drawing color. \param sigma = tolerance concerning neighborhood values. \param opacity = opacity of the drawing. **/ template CImg& draw_fill(const int x, const int y, const tc *const color, const float opacity=1, const float sigma=0, const bool high_connexity=false) { CImg tmp; return draw_fill(x,y,0,color,opacity,tmp,sigma,high_connexity); } //! Draw a 2D filled region starting from a point (\c x,\c y) in the instance image. template CImg& draw_fill(const int x, const int y, const CImg& color, const float opacity=1, const float sigma=0, const bool high_connexity=false) { return draw_fill(x,y,color.data,opacity,sigma,high_connexity); } //! Draw a plasma random texture. /** \param x0 = X-coordinate of the upper-left corner of the plasma. \param y0 = Y-coordinate of the upper-left corner of the plasma. \param x1 = X-coordinate of the lower-right corner of the plasma. \param y1 = Y-coordinate of the lower-right corner of the plasma. \param alpha = Alpha-parameter of the plasma. \param beta = Beta-parameter of the plasma. \param opacity = opacity of the drawing. **/ CImg& draw_plasma(const int x0, const int y0, const int x1, const int y1, const float alpha=1, const float beta=1, const float opacity=1) { if (!is_empty()) { const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); int nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1; if (nx1=dimx()) nx1 = width-1; if (ny0<0) ny0 = 0; if (ny1>=dimy()) ny1 = height-1; const int xc = (nx0+nx1)/2, yc = (ny0+ny1)/2, dx = (xc-nx0), dy = (yc-ny0); const Tfloat dc = (Tfloat)(cimg_std::sqrt((float)(dx*dx+dy*dy))*alpha + beta); Tfloat val = 0; cimg_forV(*this,k) { if (opacity>=1) { const Tfloat val0 = (Tfloat)((*this)(nx0,ny0,0,k)), val1 = (Tfloat)((*this)(nx1,ny0,0,k)), val2 = (Tfloat)((*this)(nx0,ny1,0,k)), val3 = (Tfloat)((*this)(nx1,ny1,0,k)); (*this)(xc,ny0,0,k) = (T)((val0+val1)/2); (*this)(xc,ny1,0,k) = (T)((val2+val3)/2); (*this)(nx0,yc,0,k) = (T)((val0+val2)/2); (*this)(nx1,yc,0,k) = (T)((val1+val3)/2); do { val = (Tfloat)(0.25f*((Tfloat)((*this)(nx0,ny0,0,k)) + (Tfloat)((*this)(nx1,ny0,0,k)) + (Tfloat)((*this)(nx1,ny1,0,k)) + (Tfloat)((*this)(nx0,ny1,0,k))) + dc*cimg::grand()); } while (val<(Tfloat)cimg::type::min() || val>(Tfloat)cimg::type::max()); (*this)(xc,yc,0,k) = (T)val; } else { const Tfloat val0 = (Tfloat)((*this)(nx0,ny0,0,k)), val1 = (Tfloat)((*this)(nx1,ny0,0,k)), val2 = (Tfloat)((*this)(nx0,ny1,0,k)), val3 = (Tfloat)((*this)(nx1,ny1,0,k)); (*this)(xc,ny0,0,k) = (T)(((val0+val1)*nopacity + copacity*(*this)(xc,ny0,0,k))/2); (*this)(xc,ny1,0,k) = (T)(((val2+val3)*nopacity + copacity*(*this)(xc,ny1,0,k))/2); (*this)(nx0,yc,0,k) = (T)(((val0+val2)*nopacity + copacity*(*this)(nx0,yc,0,k))/2); (*this)(nx1,yc,0,k) = (T)(((val1+val3)*nopacity + copacity*(*this)(nx1,yc,0,k))/2); do { val = (Tfloat)(0.25f*(((Tfloat)((*this)(nx0,ny0,0,k)) + (Tfloat)((*this)(nx1,ny0,0,k)) + (Tfloat)((*this)(nx1,ny1,0,k)) + (Tfloat)((*this)(nx0,ny1,0,k))) + dc*cimg::grand())*nopacity + copacity*(*this)(xc,yc,0,k)); } while (val<(Tfloat)cimg::type::min() || val>(Tfloat)cimg::type::max()); (*this)(xc,yc,0,k) = (T)val; } } if (xc!=nx0 || yc!=ny0) { draw_plasma(nx0,ny0,xc,yc,alpha,beta,opacity); draw_plasma(xc,ny0,nx1,yc,alpha,beta,opacity); draw_plasma(nx0,yc,xc,ny1,alpha,beta,opacity); draw_plasma(xc,yc,nx1,ny1,alpha,beta,opacity); } } return *this; } //! Draw a plasma random texture. /** \param alpha = Alpha-parameter of the plasma. \param beta = Beta-parameter of the plasma. \param opacity = opacity of the drawing. **/ CImg& draw_plasma(const float alpha=1, const float beta=1, const float opacity=1) { return draw_plasma(0,0,width-1,height-1,alpha,beta,opacity); } //! Draw a quadratic Mandelbrot or Julia fractal set, computed using the Escape Time Algorithm. template CImg& draw_mandelbrot(const int x0, const int y0, const int x1, const int y1, const CImg& color_palette, const float opacity=1, const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2, const unsigned int itermax=255, const bool normalized_iteration=false, const bool julia_set=false, const double paramr=0, const double parami=0) { if (is_empty()) return *this; CImg palette; if (color_palette) palette.assign(color_palette.data,color_palette.size()/color_palette.dim,1,1,color_palette.dim,true); if (palette && palette.dim!=dim) throw CImgArgumentException("CImg<%s>::draw_mandelbrot() : Specified color palette (%u,%u,%u,%u,%p) is not \n" "compatible with instance image (%u,%u,%u,%u,%p).", pixel_type(),color_palette.width,color_palette.height,color_palette.depth,color_palette.dim, color_palette.data,width,height,depth,dim,data); const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0), ln2 = (float)cimg_std::log(2.0); unsigned int iter = 0; cimg_for_inXY(*this,x0,y0,x1,y1,p,q) { const double x = z0r + p*(z1r-z0r)/width, y = z0i + q*(z1i-z0i)/height; double zr, zi, cr, ci; if (julia_set) { zr = x; zi = y; cr = paramr; ci = parami; } else { zr = paramr; zi = parami; cr = x; ci = y; } for (iter=1; zr*zr + zi*zi<=4 && iter<=itermax; ++iter) { const double temp = zr*zr - zi*zi + cr; zi = 2*zr*zi + ci; zr = temp; } if (iter>itermax) { if (palette) { if (opacity>=1) cimg_forV(*this,k) (*this)(p,q,0,k) = (T)palette(0,k); else cimg_forV(*this,k) (*this)(p,q,0,k) = (T)(palette(0,k)*nopacity + (*this)(p,q,0,k)*copacity); } else { if (opacity>=1) cimg_forV(*this,k) (*this)(p,q,0,k) = (T)0; else cimg_forV(*this,k) (*this)(p,q,0,k) = (T)((*this)(p,q,0,k)*copacity); } } else if (normalized_iteration) { const float normz = (float)cimg::abs(zr*zr+zi*zi), niter = (float)(iter + 1 - cimg_std::log(cimg_std::log(normz))/ln2); if (palette) { if (opacity>=1) cimg_forV(*this,k) (*this)(p,q,0,k) = (T)palette._linear_atX(niter,k); else cimg_forV(*this,k) (*this)(p,q,0,k) = (T)(palette._linear_atX(niter,k)*nopacity + (*this)(p,q,0,k)*copacity); } else { if (opacity>=1) cimg_forV(*this,k) (*this)(p,q,0,k) = (T)niter; else cimg_forV(*this,k) (*this)(p,q,0,k) = (T)(niter*nopacity + (*this)(p,q,0,k)*copacity); } } else { if (palette) { if (opacity>=1) cimg_forV(*this,k) (*this)(p,q,0,k) = (T)palette._atX(iter,k); else cimg_forV(*this,k) (*this)(p,q,0,k) = (T)(palette(iter,k)*nopacity + (*this)(p,q,0,k)*copacity); } else { if (opacity>=1) cimg_forV(*this,k) (*this)(p,q,0,k) = (T)iter; else cimg_forV(*this,k) (*this)(p,q,0,k) = (T)(iter*nopacity + (*this)(p,q,0,k)*copacity); } } } return *this; } //! Draw a quadratic Mandelbrot or Julia fractal set, computed using the Escape Time Algorithm. template CImg& draw_mandelbrot(const CImg& color_palette, const float opacity=1, const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2, const unsigned int itermax=255, const bool normalized_iteration=false, const bool julia_set=false, const double paramr=0, const double parami=0) { return draw_mandelbrot(0,0,width-1,height-1,color_palette,opacity,z0r,z0i,z1r,z1i,itermax,normalized_iteration,julia_set,paramr,parami); } //! Draw a 1D gaussian function in the instance image. /** \param xc = X-coordinate of the gaussian center. \param sigma = Standard variation of the gaussian distribution. \param color = array of dimv() values of type \c T, defining the drawing color. \param opacity = opacity of the drawing. **/ template CImg& draw_gaussian(const float xc, const float sigma, const tc *const color, const float opacity=1) { if (is_empty()) return *this; if (!color) throw CImgArgumentException("CImg<%s>::draw_gaussian() : Specified color is (null)", pixel_type()); const float sigma2 = 2*sigma*sigma, nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); const unsigned int whz = width*height*depth; const tc *col = color; cimg_forX(*this,x) { const float dx = (x - xc), val = (float)cimg_std::exp(-dx*dx/sigma2); T *ptrd = ptr(x,0,0,0); if (opacity>=1) cimg_forV(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whz; } else cimg_forV(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whz; } col-=dim; } return *this; } //! Draw a 1D gaussian function in the instance image. template CImg& draw_gaussian(const float xc, const float sigma, const CImg& color, const float opacity=1) { return draw_gaussian(xc,sigma,color.data,opacity); } //! Draw an anisotropic 2D gaussian function. /** \param xc = X-coordinate of the gaussian center. \param yc = Y-coordinate of the gaussian center. \param tensor = 2x2 covariance matrix. \param color = array of dimv() values of type \c T, defining the drawing color. \param opacity = opacity of the drawing. **/ template CImg& draw_gaussian(const float xc, const float yc, const CImg& tensor, const tc *const color, const float opacity=1) { if (is_empty()) return *this; typedef typename cimg::superset::type tfloat; if (tensor.width!=2 || tensor.height!=2 || tensor.depth!=1 || tensor.dim!=1) throw CImgArgumentException("CImg<%s>::draw_gaussian() : Tensor parameter (%u,%u,%u,%u,%p) is not a 2x2 matrix.", pixel_type(),tensor.width,tensor.height,tensor.depth,tensor.dim,tensor.data); if (!color) throw CImgArgumentException("CImg<%s>::draw_gaussian() : Specified color is (null)", pixel_type()); const CImg invT = tensor.get_invert(), invT2 = (invT*invT)/(-2.0); const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = invT2(1,1); const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); const unsigned int whz = width*height*depth; const tc *col = color; float dy = -yc; cimg_forY(*this,y) { float dx = -xc; cimg_forX(*this,x) { const float val = (float)cimg_std::exp(a*dx*dx + b*dx*dy + c*dy*dy); T *ptrd = ptr(x,y,0,0); if (opacity>=1) cimg_forV(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whz; } else cimg_forV(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whz; } col-=dim; ++dx; } ++dy; } return *this; } //! Draw an anisotropic 2D gaussian function. template CImg& draw_gaussian(const float xc, const float yc, const CImg& tensor, const CImg& color, const float opacity=1) { return draw_gaussian(xc,yc,tensor,color.data,opacity); } //! Draw an anisotropic 2D gaussian function. template CImg& 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) { const double a = r1*ru*ru + r2*rv*rv, b = (r1-r2)*ru*rv, c = r1*rv*rv + r2*ru*ru; const CImg tensor(2,2,1,1, a,b,b,c); return draw_gaussian(xc,yc,tensor,color,opacity); } //! Draw an anisotropic 2D gaussian function. template CImg& draw_gaussian(const int xc, const int yc, const float r1, const float r2, const float ru, const float rv, const CImg& color, const float opacity=1) { return draw_gaussian(xc,yc,r1,r2,ru,rv,color.data,opacity); } //! Draw an isotropic 2D gaussian function. /** \param xc = X-coordinate of the gaussian center. \param yc = Y-coordinate of the gaussian center. \param sigma = standard variation of the gaussian distribution. \param color = array of dimv() values of type \c T, defining the drawing color. \param opacity = opacity of the drawing. **/ template CImg& draw_gaussian(const float xc, const float yc, const float sigma, const tc *const color, const float opacity=1) { return draw_gaussian(xc,yc,CImg::diagonal(sigma,sigma),color,opacity); } //! Draw an isotropic 2D gaussian function. template CImg& draw_gaussian(const float xc, const float yc, const float sigma, const CImg& color, const float opacity=1) { return draw_gaussian(xc,yc,sigma,color.data,opacity); } //! Draw an anisotropic 3D gaussian function. /** \param xc = X-coordinate of the gaussian center. \param yc = Y-coordinate of the gaussian center. \param zc = Z-coordinate of the gaussian center. \param tensor = 3x3 covariance matrix. \param color = array of dimv() values of type \c T, defining the drawing color. \param opacity = opacity of the drawing. **/ template CImg& draw_gaussian(const float xc, const float yc, const float zc, const CImg& tensor, const tc *const color, const float opacity=1) { if (is_empty()) return *this; typedef typename cimg::superset::type tfloat; if (tensor.width!=3 || tensor.height!=3 || tensor.depth!=1 || tensor.dim!=1) throw CImgArgumentException("CImg<%s>::draw_gaussian() : Tensor parameter (%u,%u,%u,%u,%p) is not a 3x3 matrix.", pixel_type(),tensor.width,tensor.height,tensor.depth,tensor.dim,tensor.data); const CImg invT = tensor.get_invert(), invT2 = (invT*invT)/(-2.0); const tfloat a = invT(0,0), b = 2*invT(1,0), c = 2*invT(2,0), d = invT(1,1), e = 2*invT(2,1), f = invT(2,2); const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); const unsigned int whz = width*height*depth; const tc *col = color; cimg_forXYZ(*this,x,y,z) { const float dx = (x - xc), dy = (y - yc), dz = (z - zc), val = (float)cimg_std::exp(a*dx*dx + b*dx*dy + c*dx*dz + d*dy*dy + e*dy*dz + f*dz*dz); T *ptrd = ptr(x,y,z,0); if (opacity>=1) cimg_forV(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whz; } else cimg_forV(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whz; } col-=dim; } return *this; } //! Draw an anisotropic 3D gaussian function. template CImg& draw_gaussian(const float xc, const float yc, const float zc, const CImg& tensor, const CImg& color, const float opacity=1) { return draw_gaussian(xc,yc,zc,tensor,color.data,opacity); } //! Draw an isotropic 3D gaussian function. /** \param xc = X-coordinate of the gaussian center. \param yc = Y-coordinate of the gaussian center. \param zc = Z-coordinate of the gaussian center. \param sigma = standard variation of the gaussian distribution. \param color = array of dimv() values of type \c T, defining the drawing color. \param opacity = opacity of the drawing. **/ template CImg& draw_gaussian(const float xc, const float yc, const float zc, const float sigma, const tc *const color, const float opacity=1) { return draw_gaussian(xc,yc,zc,CImg::diagonal(sigma,sigma,sigma),color,opacity); } //! Draw an isotropic 3D gaussian function. template CImg& draw_gaussian(const float xc, const float yc, const float zc, const float sigma, const CImg& color, const float opacity=1) { return draw_gaussian(xc,yc,zc,sigma,color.data,opacity); } // Draw a 3D object (internal) template void _draw_object3d_sprite(const int x, const int y, const CImg& color, const CImg& opacity, const CImg& sprite) { if (opacity.width==color.width && opacity.height==color.height) draw_image(x,y,sprite,opacity.get_resize(sprite.width,sprite.height,1,sprite.dim,1)); else draw_image(x,y,sprite,opacity(0)); } template void _draw_object3d_sprite(const int x, const int y, const CImg& color, const float opacity, const CImg& sprite) { if (color) draw_image(x,y,sprite,opacity); } template CImg& _draw_object3d(void *const pboard, float *const zbuffer, const float X, const float Y, const float Z, const tp& points, const unsigned int nb_points, const CImgList& primitives, const CImgList& colors, const to& opacities, const unsigned int nb_opacities, const unsigned int render_type, const bool double_sided, const float focale, const float lightx, const float lighty, const float lightz, const float specular_light, const float specular_shine) { if (is_empty()) return *this; #ifndef cimg_use_board if (pboard) return *this; #endif const float nspec = 1-(specular_light<0?0:(specular_light>1?1:specular_light)), nspec2 = 1+(specular_shine<0?0:specular_shine), nsl1 = (nspec2-1)/cimg::sqr(nspec-1), nsl2 = (1-2*nsl1*nspec), nsl3 = nspec2-nsl1-nsl2; // Create light texture for phong-like rendering static CImg light_texture; if (render_type==5) { if (colors.size>primitives.size) light_texture.assign(colors[primitives.size])/=255; else { static float olightx = 0, olighty = 0, olightz = 0, ospecular_shine = 0; if (!light_texture || lightx!=olightx || lighty!=olighty || lightz!=olightz || specular_shine!=ospecular_shine) { light_texture.assign(512,512); const float white[] = { 1 }, dlx = lightx-X, dly = lighty-Y, dlz = lightz-Z, nl = (float)cimg_std::sqrt(dlx*dlx+dly*dly+dlz*dlz), nlx = light_texture.width/2*(1+dlx/nl), nly = light_texture.height/2*(1+dly/nl); light_texture.draw_gaussian(nlx,nly,light_texture.width/3.0f,white); cimg_forXY(light_texture,x,y) { const float factor = light_texture(x,y); if (factor>nspec) light_texture(x,y) = cimg::min(2,nsl1*factor*factor+nsl2*factor+nsl3); } olightx = lightx; olighty = lighty; olightz = lightz; ospecular_shine = specular_shine; } } } // Compute 3D to 2D projection CImg projections(nb_points,2); cimg_forX(projections,l) { const float x = (float)points(l,0), y = (float)points(l,1), z = (float)points(l,2); const float projectedz = z + Z + focale; projections(l,1) = Y + focale*y/projectedz; projections(l,0) = X + focale*x/projectedz; } // Compute and sort visible primitives CImg visibles(primitives.size); CImg zrange(primitives.size); unsigned int nb_visibles = 0; const float zmin = -focale+1.5f; { cimglist_for(primitives,l) { const CImg& primitive = primitives[l]; switch (primitive.size()) { case 1 : { // Point const unsigned int i0 = (unsigned int)primitive(0); const float x0 = projections(i0,0), y0 = projections(i0,1), z0 = (float)(Z+points(i0,2)); if (z0>zmin && x0>=0 && x0=0 && y0zmin && x0+radius>=0 && x0-radius=0 && y0-radiuszmin && z1>zmin && xM>=0 && xm=0 && ymxM) xM = x2; if (y0yM) yM = y2; if (z0>zmin && z1>zmin && z2>zmin && xM>=0 && xm=0 && ymxM) xM = x2; if (x3xM) xM = x3; if (y0yM) yM = y2; if (y3yM) yM = y3; if (z0>zmin && z1>zmin && z2>zmin && z3>zmin && xM>=0 && xm=0 && ym::draw_object3d() : Primitive %u is invalid (size = %u, can be 1,2,3,4,5,6,9 or 12)", pixel_type(),l,primitive.size()); }} } if (nb_visibles<=0) return *this; CImg permutations; CImg(zrange.data,nb_visibles,1,1,1,true).sort(permutations,false); // Compute light properties CImg lightprops; switch (render_type) { case 3 : { // Flat Shading lightprops.assign(nb_visibles); cimg_forX(lightprops,l) { const CImg& primitive = primitives(visibles(permutations(l))); const unsigned int psize = primitive.size(); if (psize==3 || psize==4 || psize==9 || psize==12) { const unsigned int i0 = (unsigned int)primitive(0), i1 = (unsigned int)primitive(1), i2 = (unsigned int)primitive(2); const float x0 = (float)points(i0,0), y0 = (float)points(i0,1), z0 = (float)points(i0,2), x1 = (float)points(i1,0), y1 = (float)points(i1,1), z1 = (float)points(i1,2), x2 = (float)points(i2,0), y2 = (float)points(i2,1), z2 = (float)points(i2,2), dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0, dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0, nx = dy1*dz2 - dz1*dy2, ny = dz1*dx2 - dx1*dz2, nz = dx1*dy2 - dy1*dx2, norm = (float)cimg_std::sqrt(1e-5f + nx*nx + ny*ny + nz*nz), lx = X + (x0 + x1 + x2)/3 - lightx, ly = Y + (y0 + y1 + y2)/3 - lighty, lz = Z + (z0 + z1 + z2)/3 - lightz, nl = (float)cimg_std::sqrt(1e-5f + lx*lx + ly*ly + lz*lz), factor = cimg::max(cimg::abs(-lx*nx-ly*ny-lz*nz)/(norm*nl),0); lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3); } else lightprops[l] = 1; } } break; case 4 : // Gouraud Shading case 5 : { // Phong-Shading CImg points_normals(nb_points,3,1,1,0); for (unsigned int l=0; l& primitive = primitives[visibles(l)]; const unsigned int psize = primitive.size(); const bool triangle_flag = (psize==3) || (psize==9), rectangle_flag = (psize==4) || (psize==12); if (triangle_flag || rectangle_flag) { const unsigned int i0 = (unsigned int)primitive(0), i1 = (unsigned int)primitive(1), i2 = (unsigned int)primitive(2), i3 = rectangle_flag?(unsigned int)primitive(3):0; const float x0 = (float)points(i0,0), y0 = (float)points(i0,1), z0 = (float)points(i0,2), x1 = (float)points(i1,0), y1 = (float)points(i1,1), z1 = (float)points(i1,2), x2 = (float)points(i2,0), y2 = (float)points(i2,1), z2 = (float)points(i2,2), dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0, dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0, nnx = dy1*dz2 - dz1*dy2, nny = dz1*dx2 - dx1*dz2, nnz = dx1*dy2 - dy1*dx2, norm = 1e-5f + (float)cimg_std::sqrt(nnx*nnx + nny*nny + nnz*nnz), nx = nnx/norm, ny = nny/norm, nz = nnz/norm; points_normals(i0,0)+=nx; points_normals(i0,1)+=ny; points_normals(i0,2)+=nz; points_normals(i1,0)+=nx; points_normals(i1,1)+=ny; points_normals(i1,2)+=nz; points_normals(i2,0)+=nx; points_normals(i2,1)+=ny; points_normals(i2,2)+=nz; if (rectangle_flag) { points_normals(i3,0)+=nx; points_normals(i3,1)+=ny; points_normals(i3,2)+=nz; } } } if (double_sided) cimg_forX(points_normals,p) if (points_normals(p,2)>0) { points_normals(p,0) = -points_normals(p,0); points_normals(p,1) = -points_normals(p,1); points_normals(p,2) = -points_normals(p,2); } if (render_type==4) { lightprops.assign(nb_points); cimg_forX(lightprops,ll) { const float nx = points_normals(ll,0), ny = points_normals(ll,1), nz = points_normals(ll,2), norm = (float)cimg_std::sqrt(1e-5f + nx*nx + ny*ny + nz*nz), lx = (float)(X + points(ll,0) - lightx), ly = (float)(Y + points(ll,1) - lighty), lz = (float)(Z + points(ll,2) - lightz), nl = (float)cimg_std::sqrt(1e-5f + lx*lx + ly*ly + lz*lz), factor = cimg::max((-lx*nx-ly*ny-lz*nz)/(norm*nl),0); lightprops[ll] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3); } } else { const unsigned int lw2 = light_texture.width/2 - 1, lh2 = light_texture.height/2 - 1; lightprops.assign(nb_points,2); cimg_forX(lightprops,ll) { const float nx = points_normals(ll,0), ny = points_normals(ll,1), nz = points_normals(ll,2), norm = (float)cimg_std::sqrt(1e-5f + nx*nx + ny*ny + nz*nz), nnx = nx/norm, nny = ny/norm; lightprops(ll,0) = lw2*(1 + nnx); lightprops(ll,1) = lh2*(1 + nny); } } } break; } // Draw visible primitives const CImg default_color(1,dim,1,1,(tc)200); { for (unsigned int l = 0; l& primitive = primitives[n_primitive]; const CImg& color = n_primitive=0 && x0-sw=0 && y0-sh sprite = color.get_resize(-factor,-factor,1,-100,render_type<=3?1:3); _draw_object3d_sprite(x0-sw,y0-sh,color,opacities[n_primitive%nb_opacities],sprite); #ifdef cimg_use_board if (pboard) { board.setPenColorRGBi(128,128,128); board.setFillColor(BoardLib::Color::none); board.drawRectangle((float)x0-sw,dimy()-(float)y0+sh,sw,sh); } #endif } } } break; case 2 : { // Colored line const unsigned int n0 = (unsigned int)primitive[0], n1 = (unsigned int)primitive[1]; const int x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), x1 = (int)projections(n1,0), y1 = (int)projections(n1,1); const float z0 = points(n0,2) + Z + focale, z1 = points(n1,2) + Z + focale; if (render_type) { if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,opac); else draw_line(x0,y0,x1,y1,color,opac); #ifdef cimg_use_board if (pboard) { board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255)); board.drawLine((float)x0,dimy()-(float)y0,x1,dimy()-(float)y1); } #endif } else { draw_point(x0,y0,color,opac).draw_point(x1,y1,color,opac); #ifdef cimg_use_board if (pboard) { board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255)); board.drawCircle((float)x0,dimy()-(float)y0,0); board.drawCircle((float)x1,dimy()-(float)y1,0); } #endif } } break; case 5 : { // Colored sphere const unsigned int n0 = (unsigned int)primitive[0], n1 = (unsigned int)primitive[1], n2 = (unsigned int)primitive[2]; const int x0 = (int)projections(n0,0), y0 = (int)projections(n0,1); int radius; if (n2) radius = (int)(n2*focale/(Z+points(n0,2)+focale)); else { const int x1 = (int)projections(n1,0), y1 = (int)projections(n1,1), deltax = x1-x0, deltay = y1-y0; radius = (int)cimg_std::sqrt((float)(deltax*deltax + deltay*deltay)); } switch (render_type) { case 0 : draw_point(x0,y0,color,opac); #ifdef cimg_use_board if (pboard) { board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255)); board.fillCircle((float)x0,dimy()-(float)y0,0); } #endif break; case 1 : draw_circle(x0,y0,radius,color,opac,~0U); #ifdef cimg_use_board if (pboard) { board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255)); board.setFillColor(BoardLib::Color::none); board.drawCircle((float)x0,dimy()-(float)y0,(float)radius); } #endif break; default : draw_circle(x0,y0,radius,color,opac); #ifdef cimg_use_board if (pboard) { board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255)); board.fillCircle((float)x0,dimy()-(float)y0,(float)radius); } #endif break; } } break; case 6 : { // Textured line const unsigned int n0 = (unsigned int)primitive[0], n1 = (unsigned int)primitive[1], tx0 = (unsigned int)primitive[2], ty0 = (unsigned int)primitive[3], tx1 = (unsigned int)primitive[4], ty1 = (unsigned int)primitive[5]; const int x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), x1 = (int)projections(n1,0), y1 = (int)projections(n1,1); const float z0 = points(n0,2) + Z + focale, z1 = points(n1,2) + Z + focale; if (render_type) { if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac); else draw_line(x0,y0,x1,y1,color,tx0,ty0,tx1,ty1,opac); #ifdef cimg_use_board if (pboard) { board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255)); board.drawLine((float)x0,dimy()-(float)y0,(float)x1,dimy()-(float)y1); } #endif } else { draw_point(x0,y0,color.get_vector_at(tx0,ty0),opac). draw_point(x1,y1,color.get_vector_at(tx1,ty1),opac); #ifdef cimg_use_board if (pboard) { board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255)); board.drawCircle((float)x0,dimy()-(float)y0,0); board.drawCircle((float)x1,dimy()-(float)y1,0); } #endif } } break; case 3 : { // Colored triangle const unsigned int n0 = (unsigned int)primitive[0], n1 = (unsigned int)primitive[1], n2 = (unsigned int)primitive[2]; const int x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), x1 = (int)projections(n1,0), y1 = (int)projections(n1,1), x2 = (int)projections(n2,0), y2 = (int)projections(n2,1); const float z0 = points(n0,2) + Z + focale, z1 = points(n1,2) + Z + focale, z2 = points(n2,2) + Z + focale; switch (render_type) { case 0 : draw_point(x0,y0,color,opac).draw_point(x1,y1,color,opac).draw_point(x2,y2,color,opac); #ifdef cimg_use_board if (pboard) { board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255)); board.drawCircle((float)x0,dimy()-(float)y0,0); board.drawCircle((float)x1,dimy()-(float)y1,0); board.drawCircle((float)x2,dimy()-(float)y2,0); } #endif break; case 1 : if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,opac).draw_line(zbuffer,x0,y0,z0,x2,y2,z2,color,opac). draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,opac); else draw_line(x0,y0,x1,y1,color,opac).draw_line(x0,y0,x2,y2,color,opac). draw_line(x1,y1,x2,y2,color,opac); #ifdef cimg_use_board if (pboard) { board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255)); board.drawLine((float)x0,dimy()-(float)y0,(float)x1,dimy()-(float)y1); board.drawLine((float)x0,dimy()-(float)y0,(float)x2,dimy()-(float)y2); board.drawLine((float)x1,dimy()-(float)y1,(float)x2,dimy()-(float)y2); } #endif break; case 2 : if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,opac); else draw_triangle(x0,y0,x1,y1,x2,y2,color,opac); #ifdef cimg_use_board if (pboard) { board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255)); board.fillTriangle((float)x0,dimy()-(float)y0,(float)x1,dimy()-(float)y1,(float)x2,dimy()-(float)y2); } #endif break; case 3 : if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color.data,opac,lightprops(l)); else _draw_triangle(x0,y0,x1,y1,x2,y2,color.data,opac,lightprops(l)); #ifdef cimg_use_board if (pboard) { const float lp = cimg::min(lightprops(l),1); board.setPenColorRGBi((unsigned char)(color[0]*lp), (unsigned char)(color[1]*lp), (unsigned char)(color[2]*lp), (unsigned char)(opac*255)); board.fillTriangle((float)x0,dimy()-(float)y0,(float)x1,dimy()-(float)y1,(float)x2,dimy()-(float)y2); } #endif break; case 4 : if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,lightprops(n0),lightprops(n1),lightprops(n2),opac); else draw_triangle(x0,y0,x1,y1,x2,y2,color,lightprops(n0),lightprops(n1),lightprops(n2),opac); #ifdef cimg_use_board if (pboard) { board.setPenColorRGBi((unsigned char)(color[0]), (unsigned char)(color[1]), (unsigned char)(color[2]), (unsigned char)(opac*255)); board.fillGouraudTriangle((float)x0,dimy()-(float)y0,lightprops(n0), (float)x1,dimy()-(float)y1,lightprops(n1), (float)x2,dimy()-(float)y2,lightprops(n2)); } #endif break; case 5 : { const unsigned int lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1), lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1), lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1); if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac); else draw_triangle(x0,y0,x1,y1,x2,y2,color,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac); #ifdef cimg_use_board if (pboard) { const float l0 = light_texture((int)(light_texture.dimx()/2*(1+lightprops(n0,0))), (int)(light_texture.dimy()/2*(1+lightprops(n0,1)))), l1 = light_texture((int)(light_texture.dimx()/2*(1+lightprops(n1,0))), (int)(light_texture.dimy()/2*(1+lightprops(n1,1)))), l2 = light_texture((int)(light_texture.dimx()/2*(1+lightprops(n2,0))), (int)(light_texture.dimy()/2*(1+lightprops(n2,1)))); board.setPenColorRGBi((unsigned char)(color[0]), (unsigned char)(color[1]), (unsigned char)(color[2]), (unsigned char)(opac*255)); board.fillGouraudTriangle((float)x0,dimy()-(float)y0,l0, (float)x1,dimy()-(float)y1,l1, (float)x2,dimy()-(float)y2,l2); } #endif } break; } } break; case 4 : { // Colored rectangle const unsigned int n0 = (unsigned int)primitive[0], n1 = (unsigned int)primitive[1], n2 = (unsigned int)primitive[2], n3 = (unsigned int)primitive[3]; const int x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), x1 = (int)projections(n1,0), y1 = (int)projections(n1,1), x2 = (int)projections(n2,0), y2 = (int)projections(n2,1), x3 = (int)projections(n3,0), y3 = (int)projections(n3,1); const float z0 = points(n0,2) + Z + focale, z1 = points(n1,2) + Z + focale, z2 = points(n2,2) + Z + focale, z3 = points(n3,2) + Z + focale; switch (render_type) { case 0 : draw_point(x0,y0,color,opac).draw_point(x1,y1,color,opac). draw_point(x2,y2,color,opac).draw_point(x3,y3,color,opac); #ifdef cimg_use_board if (pboard) { board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255)); board.drawCircle((float)x0,dimy()-(float)y0,0); board.drawCircle((float)x1,dimy()-(float)y1,0); board.drawCircle((float)x2,dimy()-(float)y2,0); board.drawCircle((float)x3,dimy()-(float)y3,0); } #endif break; case 1 : if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,opac).draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,opac). draw_line(zbuffer,x2,y2,z2,x3,y3,z3,color,opac).draw_line(zbuffer,x3,y3,z3,x0,y0,z0,color,opac); else draw_line(x0,y0,x1,y1,color,opac).draw_line(x1,y1,x2,y2,color,opac). draw_line(x2,y2,x3,y3,color,opac).draw_line(x3,y3,x0,y0,color,opac); #ifdef cimg_use_board if (pboard) { board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255)); board.drawLine((float)x0,dimy()-(float)y0,(float)x1,dimy()-(float)y1); board.drawLine((float)x1,dimy()-(float)y1,(float)x2,dimy()-(float)y2); board.drawLine((float)x2,dimy()-(float)y2,(float)x3,dimy()-(float)y3); board.drawLine((float)x3,dimy()-(float)y3,(float)x0,dimy()-(float)y0); } #endif break; case 2 : if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,opac).draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,opac); else draw_triangle(x0,y0,x1,y1,x2,y2,color,opac).draw_triangle(x0,y0,x2,y2,x3,y3,color,opac); #ifdef cimg_use_board if (pboard) { board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255)); board.fillTriangle((float)x0,dimy()-(float)y0,(float)x1,dimy()-(float)y1,(float)x2,dimy()-(float)y2); board.fillTriangle((float)x0,dimy()-(float)y0,(float)x2,dimy()-(float)y2,(float)x3,dimy()-(float)y3); } #endif break; case 3 : if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color.data,opac,lightprops(l)). draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color.data,opac,lightprops(l)); else _draw_triangle(x0,y0,x1,y1,x2,y2,color.data,opac,lightprops(l)). _draw_triangle(x0,y0,x2,y2,x3,y3,color.data,opac,lightprops(l)); #ifdef cimg_use_board if (pboard) { const float lp = cimg::min(lightprops(l),1); board.setPenColorRGBi((unsigned char)(color[0]*lp), (unsigned char)(color[1]*lp), (unsigned char)(color[2]*lp),(unsigned char)(opac*255)); board.fillTriangle((float)x0,dimy()-(float)y0,(float)x1,dimy()-(float)y1,(float)x2,dimy()-(float)y2); board.fillTriangle((float)x0,dimy()-(float)y0,(float)x2,dimy()-(float)y2,(float)x3,dimy()-(float)y3); } #endif break; case 4 : { const float lightprop0 = lightprops(n0), lightprop1 = lightprops(n1), lightprop2 = lightprops(n2), lightprop3 = lightprops(n3); if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,lightprop0,lightprop1,lightprop2,opac). draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,lightprop0,lightprop2,lightprop3,opac); else draw_triangle(x0,y0,x1,y1,x2,y2,color,lightprop0,lightprop1,lightprop2,opac). draw_triangle(x0,y0,x2,y2,x3,y3,color,lightprop0,lightprop2,lightprop3,opac); #ifdef cimg_use_board if (pboard) { board.setPenColorRGBi((unsigned char)(color[0]), (unsigned char)(color[1]), (unsigned char)(color[2]), (unsigned char)(opac*255)); board.fillGouraudTriangle((float)x0,dimy()-(float)y0,lightprop0, (float)x1,dimy()-(float)y1,lightprop1, (float)x2,dimy()-(float)y2,lightprop2); board.fillGouraudTriangle((float)x0,dimy()-(float)y0,lightprop0, (float)x2,dimy()-(float)y2,lightprop2, (float)x3,dimy()-(float)y3,lightprop3); } #endif } break; case 5 : { const unsigned int lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1), lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1), lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1), lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1); if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac). draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac); else draw_triangle(x0,y0,x1,y1,x2,y2,color,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac). draw_triangle(x0,y0,x2,y2,x3,y3,color,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac); #ifdef cimg_use_board if (pboard) { const float l0 = light_texture((int)(light_texture.dimx()/2*(1+lx0)), (int)(light_texture.dimy()/2*(1+ly0))), l1 = light_texture((int)(light_texture.dimx()/2*(1+lx1)), (int)(light_texture.dimy()/2*(1+ly1))), l2 = light_texture((int)(light_texture.dimx()/2*(1+lx2)), (int)(light_texture.dimy()/2*(1+ly2))), l3 = light_texture((int)(light_texture.dimx()/2*(1+lx3)), (int)(light_texture.dimy()/2*(1+ly3))); board.setPenColorRGBi((unsigned char)(color[0]), (unsigned char)(color[1]), (unsigned char)(color[2]), (unsigned char)(opac*255)); board.fillGouraudTriangle((float)x0,dimy()-(float)y0,l0, (float)x1,dimy()-(float)y1,l1, (float)x2,dimy()-(float)y2,l2); board.fillGouraudTriangle((float)x0,dimy()-(float)y0,l0, (float)x2,dimy()-(float)y2,l2, (float)x3,dimy()-(float)y3,l3); } #endif } break; } } break; case 9 : { // Textured triangle const unsigned int n0 = (unsigned int)primitive[0], n1 = (unsigned int)primitive[1], n2 = (unsigned int)primitive[2], tx0 = (unsigned int)primitive[3], ty0 = (unsigned int)primitive[4], tx1 = (unsigned int)primitive[5], ty1 = (unsigned int)primitive[6], tx2 = (unsigned int)primitive[7], ty2 = (unsigned int)primitive[8]; const int x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), x1 = (int)projections(n1,0), y1 = (int)projections(n1,1), x2 = (int)projections(n2,0), y2 = (int)projections(n2,1); const float z0 = points(n0,2) + Z + focale, z1 = points(n1,2) + Z + focale, z2 = points(n2,2) + Z + focale; switch (render_type) { case 0 : draw_point(x0,y0,color.get_vector_at(tx0,ty0),opac). draw_point(x1,y1,color.get_vector_at(tx1,ty1),opac). draw_point(x2,y2,color.get_vector_at(tx2,ty2),opac); #ifdef cimg_use_board if (pboard) { board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255)); board.drawCircle((float)x0,dimy()-(float)y0,0); board.drawCircle((float)x1,dimy()-(float)y1,0); board.drawCircle((float)x2,dimy()-(float)y2,0); } #endif break; case 1 : if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac). draw_line(zbuffer,x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opac). draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac); else draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac). draw_line(x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opac). draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac); #ifdef cimg_use_board if (pboard) { board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255)); board.drawLine((float)x0,dimy()-(float)y0,(float)x1,dimy()-(float)y1); board.drawLine((float)x0,dimy()-(float)y0,(float)x2,dimy()-(float)y2); board.drawLine((float)x1,dimy()-(float)y1,(float)x2,dimy()-(float)y2); } #endif break; case 2 : if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac); else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac); #ifdef cimg_use_board if (pboard) { board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255)); board.fillTriangle((float)x0,dimy()-(float)y0,(float)x1,dimy()-(float)y1,(float)x2,dimy()-(float)y2); } #endif break; case 3 : if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l)); else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l)); #ifdef cimg_use_board if (pboard) { const float lp = cimg::min(lightprops(l),1); board.setPenColorRGBi((unsigned char)(128*lp), (unsigned char)(128*lp), (unsigned char)(128*lp), (unsigned char)(opac*255)); board.fillTriangle((float)x0,dimy()-(float)y0,(float)x1,dimy()-(float)y1,(float)x2,dimy()-(float)y2); } #endif break; case 4 : if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprops(n0),lightprops(n1),lightprops(n2),opac); else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprops(n0),lightprops(n1),lightprops(n2),opac); #ifdef cimg_use_board if (pboard) { board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255)); board.fillGouraudTriangle((float)x0,dimy()-(float)y0,lightprops(n0), (float)x1,dimy()-(float)y1,lightprops(n1), (float)x2,dimy()-(float)y2,lightprops(n2)); } #endif break; case 5 : if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture, (unsigned int)lightprops(n0,0), (unsigned int)lightprops(n0,1), (unsigned int)lightprops(n1,0), (unsigned int)lightprops(n1,1), (unsigned int)lightprops(n2,0), (unsigned int)lightprops(n2,1), opac); else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture, (unsigned int)lightprops(n0,0), (unsigned int)lightprops(n0,1), (unsigned int)lightprops(n1,0), (unsigned int)lightprops(n1,1), (unsigned int)lightprops(n2,0), (unsigned int)lightprops(n2,1), opac); #ifdef cimg_use_board if (pboard) { const float l0 = light_texture((int)(light_texture.dimx()/2*(1+lightprops(n0,0))), (int)(light_texture.dimy()/2*(1+lightprops(n0,1)))), l1 = light_texture((int)(light_texture.dimx()/2*(1+lightprops(n1,0))), (int)(light_texture.dimy()/2*(1+lightprops(n1,1)))), l2 = light_texture((int)(light_texture.dimx()/2*(1+lightprops(n2,0))), (int)(light_texture.dimy()/2*(1+lightprops(n2,1)))); board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255)); board.fillGouraudTriangle((float)x0,dimy()-(float)y0,l0,(float)x1,dimy()-(float)y1,l1,(float)x2,dimy()-(float)y2,l2); } #endif break; } } break; case 12 : { // Textured rectangle const unsigned int n0 = (unsigned int)primitive[0], n1 = (unsigned int)primitive[1], n2 = (unsigned int)primitive[2], n3 = (unsigned int)primitive[3], tx0 = (unsigned int)primitive[4], ty0 = (unsigned int)primitive[5], tx1 = (unsigned int)primitive[6], ty1 = (unsigned int)primitive[7], tx2 = (unsigned int)primitive[8], ty2 = (unsigned int)primitive[9], tx3 = (unsigned int)primitive[10], ty3 = (unsigned int)primitive[11]; const int x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), x1 = (int)projections(n1,0), y1 = (int)projections(n1,1), x2 = (int)projections(n2,0), y2 = (int)projections(n2,1), x3 = (int)projections(n3,0), y3 = (int)projections(n3,1); const float z0 = points(n0,2) + Z + focale, z1 = points(n1,2) + Z + focale, z2 = points(n2,2) + Z + focale, z3 = points(n3,2) + Z + focale; switch (render_type) { case 0 : draw_point(x0,y0,color.get_vector_at(tx0,ty0),opac). draw_point(x1,y1,color.get_vector_at(tx1,ty1),opac). draw_point(x2,y2,color.get_vector_at(tx2,ty2),opac). draw_point(x3,y3,color.get_vector_at(tx3,ty3),opac); #ifdef cimg_use_board if (pboard) { board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255)); board.drawCircle((float)x0,dimy()-(float)y0,0); board.drawCircle((float)x1,dimy()-(float)y1,0); board.drawCircle((float)x2,dimy()-(float)y2,0); board.drawCircle((float)x3,dimy()-(float)y3,0); } #endif break; case 1 : if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac). draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac). draw_line(zbuffer,x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opac). draw_line(zbuffer,x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opac); else draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac). draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac). draw_line(x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opac). draw_line(x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opac); #ifdef cimg_use_board if (pboard) { board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255)); board.drawLine((float)x0,dimy()-(float)y0,(float)x1,dimy()-(float)y1); board.drawLine((float)x1,dimy()-(float)y1,(float)x2,dimy()-(float)y2); board.drawLine((float)x2,dimy()-(float)y2,(float)x3,dimy()-(float)y3); board.drawLine((float)x3,dimy()-(float)y3,(float)x0,dimy()-(float)y0); } #endif break; case 2 : if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac). draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac); else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac). draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac); #ifdef cimg_use_board if (pboard) { board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255)); board.fillTriangle((float)x0,dimy()-(float)y0,(float)x1,dimy()-(float)y1,(float)x2,dimy()-(float)y2); board.fillTriangle((float)x0,dimy()-(float)y0,(float)x2,dimy()-(float)y2,(float)x3,dimy()-(float)y3); } #endif break; case 3 : if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l)). draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac,lightprops(l)); else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l)). draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac,lightprops(l)); #ifdef cimg_use_board if (pboard) { const float lp = cimg::min(lightprops(l),1); board.setPenColorRGBi((unsigned char)(128*lp), (unsigned char)(128*lp), (unsigned char)(128*lp), (unsigned char)(opac*255)); board.fillTriangle((float)x0,dimy()-(float)y0,(float)x1,dimy()-(float)y1,(float)x2,dimy()-(float)y2); board.fillTriangle((float)x0,dimy()-(float)y0,(float)x2,dimy()-(float)y2,(float)x3,dimy()-(float)y3); } #endif break; case 4 : { const float lightprop0 = lightprops(n0), lightprop1 = lightprops(n1), lightprop2 = lightprops(n2), lightprop3 = lightprops(n3); if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprop0,lightprop1,lightprop2,opac). draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,lightprop0,lightprop2,lightprop3,opac); else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprop0,lightprop1,lightprop2,opac). draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,lightprop0,lightprop2,lightprop3,opac); #ifdef cimg_use_board if (pboard) { board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255)); board.fillGouraudTriangle((float)x0,dimy()-(float)y0,lightprop0, (float)x1,dimy()-(float)y1,lightprop1, (float)x2,dimy()-(float)y2,lightprop2); board.fillGouraudTriangle((float)x0,dimy()-(float)y0,lightprop0, (float)x2,dimy()-(float)y2,lightprop2, (float)x3,dimy()-(float)y3,lightprop3); } #endif } break; case 5 : { const unsigned int lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1), lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1), lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1), lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1); if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac). draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac); else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac). draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac); #ifdef cimg_use_board if (pboard) { const float l0 = light_texture((int)(light_texture.dimx()/2*(1+lx0)), (int)(light_texture.dimy()/2*(1+ly0))), l1 = light_texture((int)(light_texture.dimx()/2*(1+lx1)), (int)(light_texture.dimy()/2*(1+ly1))), l2 = light_texture((int)(light_texture.dimx()/2*(1+lx2)), (int)(light_texture.dimy()/2*(1+ly2))), l3 = light_texture((int)(light_texture.dimx()/2*(1+lx3)), (int)(light_texture.dimy()/2*(1+ly3))); board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255)); board.fillGouraudTriangle((float)x0,dimy()-(float)y0,l0, (float)x1,dimy()-(float)y1,l1, (float)x2,dimy()-(float)y2,l2); board.fillGouraudTriangle((float)x0,dimy()-(float)y0,l0, (float)x2,dimy()-(float)y2,l2, (float)x3,dimy()-(float)y3,l3); } #endif } break; } } break; } } } return *this; } //! Draw a 3D object. /** \param X = X-coordinate of the 3d object position \param Y = Y-coordinate of the 3d object position \param Z = Z-coordinate of the 3d object position \param points = Image N*3 describing 3D point coordinates \param primitives = List of P primitives \param colors = List of P color (or textures) \param opacities = Image of P opacities \param render_type = Render type (0=Points, 1=Lines, 2=Faces (no light), 3=Faces (flat), 4=Faces(Gouraud) \param double_sided = Tell if object faces have two sides or are oriented. \param focale = length of the focale \param lightx = X-coordinate of the light \param lighty = Y-coordinate of the light \param lightz = Z-coordinate of the light \param specular_shine = Shininess of the object **/ template CImg& draw_object3d(const float x0, const float y0, const float z0, const CImg& points, const CImgList& primitives, const CImgList& colors, const CImgList& opacities, const unsigned int render_type=4, const bool double_sided=false, const float focale=500, const float lightx=0, const float lighty=0, const float lightz=-5000, const float specular_light=0.2f, const float specular_shine=0.1f, float *const zbuffer=0) { if (!points) return *this; return _draw_object3d(0,zbuffer,x0,y0,z0,points.height<3?points:points.get_resize(-100,3,1,1,0),points.width, primitives,colors,opacities,opacities.size, render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine); } #ifdef cimg_use_board template CImg& draw_object3d(BoardLib::Board& board, const float x0, const float y0, const float z0, const CImg& points, const CImgList& primitives, const CImgList& colors, const CImgList& opacities, const unsigned int render_type=4, const bool double_sided=false, const float focale=500, const float lightx=0, const float lighty=0, const float lightz=-5000, const float specular_light=0.2f, const float specular_shine=0.1f, float *const zbuffer=0) { if (!points) return *this; return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,points.height<3?points:points.get_resize(-100,3,1,1,0),points.width, primitives,colors,opacities,opacities.size, render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine); } #endif //! Draw a 3D object. template CImg& draw_object3d(const float x0, const float y0, const float z0, const CImgList& points, const CImgList& primitives, const CImgList& colors, const CImgList& opacities, const unsigned int render_type=4, const bool double_sided=false, const float focale=500, const float lightx=0, const float lighty=0, const float lightz=-5000, const float specular_light=0.2f, const float specular_shine=0.1f, float *const zbuffer=0) { if (!points) return *this; return _draw_object3d(0,zbuffer,x0,y0,z0,points,points.size,primitives,colors,opacities,opacities.size, render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine); } #ifdef cimg_use_board template CImg& draw_object3d(BoardLib::Board& board, const float x0, const float y0, const float z0, const CImgList& points, const CImgList& primitives, const CImgList& colors, const CImgList& opacities, const unsigned int render_type=4, const bool double_sided=false, const float focale=500, const float lightx=0, const float lighty=0, const float lightz=-5000, const float specular_light=0.2f, const float specular_shine=0.1f, float *const zbuffer=0) { if (!points) return *this; return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,points,points.size,primitives,colors,opacities,opacities.size, render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine); } #endif //! Draw a 3D object. template CImg& draw_object3d(const float x0, const float y0, const float z0, const CImg& points, const CImgList& primitives, const CImgList& colors, const CImg& opacities, const unsigned int render_type=4, const bool double_sided=false, const float focale=500, const float lightx=0, const float lighty=0, const float lightz=-5000, const float specular_light=0.2f, const float specular_shine=0.1f, float *const zbuffer=0) { if (!points) return *this; return _draw_object3d(0,zbuffer,x0,y0,z0,points.height<3?points:points.get_resize(-100,3,1,1,0),points.width, primitives,colors,opacities,opacities.size(), render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine); } #ifdef cimg_use_board template CImg& draw_object3d(BoardLib::Board& board, const float x0, const float y0, const float z0, const CImg& points, const CImgList& primitives, const CImgList& colors, const CImg& opacities, const unsigned int render_type=4, const bool double_sided=false, const float focale=500, const float lightx=0, const float lighty=0, const float lightz=-5000, const float specular_light=0.2f, const float specular_shine=0.1f, float *const zbuffer=0) { if (!points) return *this; return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,points.height<3?points:points.get_resize(-100,3,1,1,0),points.width ,primitives,colors,opacities,opacities.size(), render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine); } #endif //! Draw a 3D object. template CImg& draw_object3d(const float x0, const float y0, const float z0, const CImgList& points, const CImgList& primitives, const CImgList& colors, const CImg& opacities, const unsigned int render_type=4, const bool double_sided=false, const float focale=500, const float lightx=0, const float lighty=0, const float lightz=-5000, const float specular_light=0.2f, const float specular_shine=0.1f, float *const zbuffer=0) { if (!points) return *this; return _draw_object3d(0,zbuffer,x0,y0,z0,points,points.size,primitives,colors,opacities,opacities.size(), render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine); } #ifdef cimg_use_board template CImg& draw_object3d(BoardLib::Board& board, const float x0, const float y0, const float z0, const CImgList& points, const CImgList& primitives, const CImgList& colors, const CImg& opacities, const unsigned int render_type=4, const bool double_sided=false, const float focale=500, const float lightx=0, const float lighty=0, const float lightz=-5000, const float specular_light=0.2f, const float specular_shine=0.1f, float *const zbuffer=0) { if (!points) return *this; return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,points,points.size,primitives,colors,opacities,opacities.size(), render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine); } #endif //! Draw a 3D object. template CImg& draw_object3d(const float x0, const float y0, const float z0, const tp& points, const CImgList& primitives, const CImgList& colors, const unsigned int render_type=4, const bool double_sided=false, const float focale=500, const float lightx=0, const float lighty=0, const float lightz=-5000, const float specular_light=0.2f, const float specular_shine=0.1f, float *const zbuffer=0) { static const CImg opacities; return draw_object3d(x0,y0,z0,points,primitives,colors,opacities, render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,zbuffer); } #ifdef cimg_use_board template CImg& draw_object3d(BoardLib::Board& board, const float x0, const float y0, const float z0, const tp& points, const CImgList& primitives, const CImgList& colors, const unsigned int render_type=4, const bool double_sided=false, const float focale=500, const float lightx=0, const float lighty=0, const float lightz=-5000, const float specular_light=0.2f, const float specular_shine=0.1f, float *const zbuffer=0) { static const CImg opacities; return draw_object3d(x0,y0,z0,points,primitives,colors,opacities, render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,zbuffer); } #endif //@} //---------------------------- // //! \name Image Filtering //@{ //---------------------------- //! Compute the correlation of the instance image by a mask. /** The correlation of the instance image \p *this by the mask \p mask is defined to be : res(x,y,z) = sum_{i,j,k} (*this)(x+i,y+j,z+k)*mask(i,j,k) \param mask = the correlation kernel. \param cond = the border condition type (0=zero, 1=dirichlet) \param weighted_correl = enable local normalization. **/ template CImg& correlate(const CImg& mask, const unsigned int cond=1, const bool weighted_correl=false) { return get_correlate(mask,cond,weighted_correl).transfer_to(*this); } template CImg::type> get_correlate(const CImg& mask, const unsigned int cond=1, const bool weighted_correl=false) const { typedef typename cimg::superset2::type Ttfloat; if (is_empty()) return *this; if (!mask || mask.dim!=1) throw CImgArgumentException("CImg<%s>::correlate() : Specified mask (%u,%u,%u,%u,%p) is not scalar.", pixel_type(),mask.width,mask.height,mask.depth,mask.dim,mask.data); CImg dest(width,height,depth,dim); if (cond && mask.width==mask.height && ((mask.depth==1 && mask.width<=5) || (mask.depth==mask.width && mask.width<=3))) { // A special optimization is done for 2x2, 3x3, 4x4, 5x5, 2x2x2 and 3x3x3 mask (with cond=1) switch (mask.depth) { case 3 : { T I[27] = { 0 }; cimg_forZV(*this,z,v) cimg_for3x3x3(*this,x,y,z,v,I) dest(x,y,z,v) = (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] + I[ 6]*mask[ 6] + I[ 7]*mask[ 7] + I[ 8]*mask[ 8] + I[ 9]*mask[ 9] + I[10]*mask[10] + I[11]*mask[11] + I[12]*mask[12] + I[13]*mask[13] + I[14]*mask[14] + I[15]*mask[15] + I[16]*mask[16] + I[17]*mask[17] + I[18]*mask[18] + I[19]*mask[19] + I[20]*mask[20] + I[21]*mask[21] + I[22]*mask[22] + I[23]*mask[23] + I[24]*mask[24] + I[25]*mask[25] + I[26]*mask[26]); if (weighted_correl) cimg_forZV(*this,z,v) cimg_for3x3x3(*this,x,y,z,v,I) { const double weight = (double)(I[ 0]*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]*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]*I[15] + I[16]*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]*I[23] + I[24]*I[24] + I[25]*I[25] + I[26]*I[26]); if (weight>0) dest(x,y,z,v)/=(Ttfloat)cimg_std::sqrt(weight); } } break; case 2 : { T I[8] = { 0 }; cimg_forZV(*this,z,v) cimg_for2x2x2(*this,x,y,z,v,I) dest(x,y,z,v) = (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] + I[6]*mask[6] + I[7]*mask[7]); if (weighted_correl) cimg_forZV(*this,z,v) cimg_for2x2x2(*this,x,y,z,v,I) { const double weight = (double)(I[0]*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]); if (weight>0) dest(x,y,z,v)/=(Ttfloat)cimg_std::sqrt(weight); } } break; default : case 1 : switch (mask.width) { case 6 : { T I[36] = { 0 }; cimg_forZV(*this,z,v) cimg_for6x6(*this,x,y,z,v,I) dest(x,y,z,v) = (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] + I[ 6]*mask[ 6] + I[ 7]*mask[ 7] + I[ 8]*mask[ 8] + I[ 9]*mask[ 9] + I[10]*mask[10] + I[11]*mask[11] + I[12]*mask[12] + I[13]*mask[13] + I[14]*mask[14] + I[15]*mask[15] + I[16]*mask[16] + I[17]*mask[17] + I[18]*mask[18] + I[19]*mask[19] + I[20]*mask[20] + I[21]*mask[21] + I[22]*mask[22] + I[23]*mask[23] + I[24]*mask[24] + I[25]*mask[25] + I[26]*mask[26] + I[27]*mask[27] + I[28]*mask[28] + I[29]*mask[29] + I[30]*mask[30] + I[31]*mask[31] + I[32]*mask[32] + I[33]*mask[33] + I[34]*mask[34] + I[35]*mask[35]); if (weighted_correl) cimg_forZV(*this,z,v) cimg_for5x5(*this,x,y,z,v,I) { const double weight = (double)(I[ 0]*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]*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]*I[15] + I[16]*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]*I[23] + I[24]*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]*I[31] + I[32]*I[32] + I[33]*I[33] + I[34]*I[34] + I[35]*I[35]); if (weight>0) dest(x,y,z,v)/=(Ttfloat)cimg_std::sqrt(weight); } } break; case 5 : { T I[25] = { 0 }; cimg_forZV(*this,z,v) cimg_for5x5(*this,x,y,z,v,I) dest(x,y,z,v) = (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] + I[ 6]*mask[ 6] + I[ 7]*mask[ 7] + I[ 8]*mask[ 8] + I[ 9]*mask[ 9] + I[10]*mask[10] + I[11]*mask[11] + I[12]*mask[12] + I[13]*mask[13] + I[14]*mask[14] + I[15]*mask[15] + I[16]*mask[16] + I[17]*mask[17] + I[18]*mask[18] + I[19]*mask[19] + I[20]*mask[20] + I[21]*mask[21] + I[22]*mask[22] + I[23]*mask[23] + I[24]*mask[24]); if (weighted_correl) cimg_forZV(*this,z,v) cimg_for5x5(*this,x,y,z,v,I) { const double weight = (double)(I[ 0]*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]*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]*I[15] + I[16]*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]*I[23] + I[24]*I[24]); if (weight>0) dest(x,y,z,v)/=(Ttfloat)cimg_std::sqrt(weight); } } break; case 4 : { T I[16] = { 0 }; cimg_forZV(*this,z,v) cimg_for4x4(*this,x,y,z,v,I) dest(x,y,z,v) = (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] + I[ 6]*mask[ 6] + I[ 7]*mask[ 7] + I[ 8]*mask[ 8] + I[ 9]*mask[ 9] + I[10]*mask[10] + I[11]*mask[11] + I[12]*mask[12] + I[13]*mask[13] + I[14]*mask[14] + I[15]*mask[15]); if (weighted_correl) cimg_forZV(*this,z,v) cimg_for4x4(*this,x,y,z,v,I) { const double weight = (double)(I[ 0]*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]*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]*I[15]); if (weight>0) dest(x,y,z,v)/=(Ttfloat)cimg_std::sqrt(weight); } } break; case 3 : { T I[9] = { 0 }; cimg_forZV(*this,z,v) cimg_for3x3(*this,x,y,z,v,I) dest(x,y,z,v) = (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] + I[6]*mask[6] + I[7]*mask[7] + I[8]*mask[8]); if (weighted_correl) cimg_forZV(*this,z,v) cimg_for3x3(*this,x,y,z,v,I) { const double weight = (double)(I[0]*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]*I[8]); if (weight>0) dest(x,y,z,v)/=(Ttfloat)cimg_std::sqrt(weight); } } break; case 2 : { T I[4] = { 0 }; cimg_forZV(*this,z,v) cimg_for2x2(*this,x,y,z,v,I) dest(x,y,z,v) = (Ttfloat) (I[0]*mask[0] + I[1]*mask[1] + I[2]*mask[2] + I[3]*mask[3]); if (weighted_correl) cimg_forZV(*this,z,v) cimg_for2x2(*this,x,y,z,v,I) { const double weight = (double)(I[0]*I[0] + I[1]*I[1] + I[2]*I[2] + I[3]*I[3]); if (weight>0) dest(x,y,z,v)/=(Ttfloat)cimg_std::sqrt(weight); } } break; case 1 : (dest.assign(*this))*=mask(0); break; } } } else { // Generic version for other masks const int mx2 = mask.dimx()/2, my2 = mask.dimy()/2, mz2 = mask.dimz()/2, mx1 = mx2 - 1 + (mask.dimx()%2), my1 = my2 - 1 + (mask.dimy()%2), mz1 = mz2 - 1 + (mask.dimz()%2), mxe = dimx() - mx2, mye = dimy() - my2, mze = dimz() - mz2; cimg_forV(*this,v) if (!weighted_correl) { // Classical correlation for (int z = mz1; z=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { Ttfloat val = 0; for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) val+=_atXYZ(x+xm,y+ym,z+zm,v)*mask(mx1+xm,my1+ym,mz1+zm); dest(x,y,z,v) = (Ttfloat)val; } else cimg_forYZV(*this,y,z,v) for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { Ttfloat val = 0; for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) val+=atXYZ(x+xm,y+ym,z+zm,v,0)*mask(mx1+xm,my1+ym,mz1+zm); dest(x,y,z,v) = (Ttfloat)val; } } else { // Weighted correlation for (int z = mz1; z(Ttfloat)0)?(Ttfloat)(val/cimg_std::sqrt((double)weight)):(Ttfloat)0; } if (cond) cimg_forYZV(*this,y,z,v) for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { Ttfloat val = 0, weight = 0; for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { const Ttfloat cval = (Ttfloat)_atXYZ(x+xm,y+ym,z+zm,v); val+=cval*mask(mx1+xm,my1+ym,mz1+zm); weight+=cval*cval; } dest(x,y,z,v) = (weight>(Ttfloat)0)?(Ttfloat)(val/cimg_std::sqrt((double)weight)):(Ttfloat)0; } else cimg_forYZV(*this,y,z,v) for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { Ttfloat val = 0, weight = 0; for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { const Ttfloat cval = (Ttfloat)atXYZ(x+xm,y+ym,z+zm,v,0); val+=cval*mask(mx1+xm,my1+ym,mz1+zm); weight+=cval*cval; } dest(x,y,z,v) = (weight>(Ttfloat)0)?(Ttfloat)(val/cimg_std::sqrt((double)weight)):(Ttfloat)0; } } } return dest; } //! Compute the convolution of the image by a mask. /** The result \p res of the convolution of an image \p img by a mask \p mask is defined to be : res(x,y,z) = sum_{i,j,k} img(x-i,y-j,z-k)*mask(i,j,k) \param mask = the correlation kernel. \param cond = the border condition type (0=zero, 1=dirichlet) \param weighted_convol = enable local normalization. **/ template CImg& convolve(const CImg& mask, const unsigned int cond=1, const bool weighted_convol=false) { return get_convolve(mask,cond,weighted_convol).transfer_to(*this); } template CImg::type> get_convolve(const CImg& mask, const unsigned int cond=1, const bool weighted_convol=false) const { typedef typename cimg::superset2::type Ttfloat; if (is_empty()) return *this; if (!mask || mask.dim!=1) throw CImgArgumentException("CImg<%s>::convolve() : Specified mask (%u,%u,%u,%u,%p) is not scalar.", pixel_type(),mask.width,mask.height,mask.depth,mask.dim,mask.data); return get_correlate(CImg(mask.ptr(),mask.size(),1,1,1,true).get_mirror('x').resize(mask,-1),cond,weighted_convol); } //! Return the erosion of the image by a structuring element. template CImg& erode(const CImg& mask, const unsigned int cond=1, const bool weighted_erosion=false) { return get_erode(mask,cond,weighted_erosion).transfer_to(*this); } template CImg::type> get_erode(const CImg& mask, const unsigned int cond=1, const bool weighted_erosion=false) const { typedef typename cimg::superset::type Tt; if (is_empty()) return *this; if (!mask || mask.dim!=1) throw CImgArgumentException("CImg<%s>::erode() : Specified mask (%u,%u,%u,%u,%p) is not a scalar image.", pixel_type(),mask.width,mask.height,mask.depth,mask.dim,mask.data); CImg dest(width,height,depth,dim); const int mx2 = mask.dimx()/2, my2 = mask.dimy()/2, mz2 = mask.dimz()/2, mx1 = mx2 - 1 + (mask.dimx()%2), my1 = my2 - 1 + (mask.dimy()%2), mz1 = mz2 - 1 + (mask.dimz()%2), mxe = dimx() - mx2, mye = dimy() - my2, mze = dimz() - mz2; cimg_forV(*this,v) if (!weighted_erosion) { // Classical erosion for (int z = mz1; z::max(); for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { const Tt cval = (Tt)(*this)(x+xm,y+ym,z+zm,v); if (mask(mx1+xm,my1+ym,mz1+zm) && cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { Tt min_val = cimg::type::max(); for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { const T cval = (Tt)_atXYZ(x+xm,y+ym,z+zm,v); if (mask(mx1+xm,my1+ym,mz1+zm) && cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { Tt min_val = cimg::type::max(); for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { const T cval = (Tt)atXYZ(x+xm,y+ym,z+zm,v,0); if (mask(mx1+xm,my1+ym,mz1+zm) && cval::max(); for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { const t mval = mask(mx1+xm,my1+ym,mz1+zm); const Tt cval = (Tt)((*this)(x+xm,y+ym,z+zm,v) + mval); if (mval && cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { Tt min_val = cimg::type::max(); for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { const t mval = mask(mx1+xm,my1+ym,mz1+zm); const Tt cval = (Tt)(_atXYZ(x+xm,y+ym,z+zm,v) + mval); if (mval && cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { Tt min_val = cimg::type::max(); for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { const t mval = mask(mx1+xm,my1+ym,mz1+zm); const Tt cval = (Tt)(atXYZ(x+xm,y+ym,z+zm,v,0) + mval); if (mval && cval& erode(const unsigned int n, const unsigned int cond=1) { if (n<2) return *this; return get_erode(n,cond).transfer_to(*this); } CImg get_erode(const unsigned int n, const unsigned int cond=1) const { static CImg mask; if (n<2) return *this; if (mask.width!=n) mask.assign(n,n,1,1,1); const CImg res = get_erode(mask,cond,false); if (n>20) mask.assign(); return res; } //! Dilate the image by a structuring element. template CImg& dilate(const CImg& mask, const unsigned int cond=1, const bool weighted_dilatation=false) { return get_dilate(mask,cond,weighted_dilatation).transfer_to(*this); } template CImg::type> get_dilate(const CImg& mask, const unsigned int cond=1, const bool weighted_dilatation=false) const { typedef typename cimg::superset::type Tt; if (is_empty()) return *this; if (!mask || mask.dim!=1) throw CImgArgumentException("CImg<%s>::dilate() : Specified mask (%u,%u,%u,%u,%p) is not a scalar image.", pixel_type(),mask.width,mask.height,mask.depth,mask.dim,mask.data); CImg dest(width,height,depth,dim); const int mx2 = mask.dimx()/2, my2 = mask.dimy()/2, mz2 = mask.dimz()/2, mx1 = mx2 - 1 + (mask.dimx()%2), my1 = my2 - 1 + (mask.dimy()%2), mz1 = mz2 - 1 + (mask.dimz()%2), mxe = dimx() - mx2, mye = dimy() - my2, mze = dimz() - mz2; cimg_forV(*this,v) if (!weighted_dilatation) { // Classical dilatation for (int z = mz1; z::min(); for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { const Tt cval = (Tt)(*this)(x+xm,y+ym,z+zm,v); if (mask(mx1+xm,my1+ym,mz1+zm) && cval>max_val) max_val = cval; } dest(x,y,z,v) = max_val; } if (cond) cimg_forYZV(*this,y,z,v) for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { Tt max_val = cimg::type::min(); for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { const T cval = (Tt)_atXYZ(x+xm,y+ym,z+zm,v); if (mask(mx1+xm,my1+ym,mz1+zm) && cval>max_val) max_val = cval; } dest(x,y,z,v) = max_val; } else cimg_forYZV(*this,y,z,v) for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { Tt max_val = cimg::type::min(); for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { const T cval = (Tt)atXYZ(x+xm,y+ym,z+zm,v,0); if (mask(mx1+xm,my1+ym,mz1+zm) && cval>max_val) max_val = cval; } dest(x,y,z,v) = max_val; } } else { // Weighted dilatation for (int z = mz1; z::min(); for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { const t mval = mask(mx1+xm,my1+ym,mz1+zm); const Tt cval = (Tt)((*this)(x+xm,y+ym,z+zm,v) - mval); if (mval && cval>max_val) max_val = cval; } dest(x,y,z,v) = max_val; } if (cond) cimg_forYZV(*this,y,z,v) for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { Tt max_val = cimg::type::min(); for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { const t mval = mask(mx1+xm,my1+ym,mz1+zm); const Tt cval = (Tt)(_atXYZ(x+xm,y+ym,z+zm,v) - mval); if (mval && cval>max_val) max_val = cval; } dest(x,y,z,v) = max_val; } else cimg_forYZV(*this,y,z,v) for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { Tt max_val = cimg::type::min(); for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { const t mval = mask(mx1+xm,my1+ym,mz1+zm); const Tt cval = (Tt)(atXYZ(x+xm,y+ym,z+zm,v,0) - mval); if (mval && cval>max_val) max_val = cval; } dest(x,y,z,v) = max_val; } } return dest; } //! Dilate the image by a square structuring element of size n. CImg& dilate(const unsigned int n, const unsigned int cond=1) { if (n<2) return *this; return get_dilate(n,cond).transfer_to(*this); } CImg get_dilate(const unsigned int n, const unsigned int cond=1) const { static CImg mask; if (n<2) return *this; if (mask.width!=n) mask.assign(n,n,1,1,1); const CImg res = get_dilate(mask,cond,false); if (n>20) mask.assign(); return res; } //! Add noise to the image. /** \param sigma = power of the noise. if sigma<0, it corresponds to the percentage of the maximum image value. \param ntype = noise type. can be 0=gaussian, 1=uniform or 2=Salt and Pepper, 3=Poisson, 4=Rician. \return A noisy version of the instance image. **/ CImg& noise(const double sigma, const unsigned int noise_type=0) { if (!is_empty()) { double nsigma = sigma, max = (double)cimg::type::max(), min = (double)cimg::type::min(); Tfloat m = 0, M = 0; if (nsigma==0 && noise_type!=3) return *this; if (nsigma<0 || noise_type==2) m = (Tfloat)minmax(M); if (nsigma<0) nsigma = -nsigma*(M-m)/100.0; switch (noise_type) { case 0 : { // Gaussian noise cimg_for(*this,ptr,T) { double val = *ptr + nsigma*cimg::grand(); if (val>max) val = max; if (valmax) val = max; if (val::is_float()?1:cimg::type::max()); } cimg_for(*this,ptr,T) if (cimg::rand()*100max) val = max; if (val::noise() : Invalid noise type %d " "(should be {0=Gaussian, 1=Uniform, 2=Salt&Pepper, 3=Poisson}).",pixel_type(),noise_type); } } return *this; } CImg get_noise(const double sigma, const unsigned int noise_type=0) const { return (+*this).noise(sigma,noise_type); } //! Compute the result of the Deriche filter. /** The Canny-Deriche filter is a recursive algorithm allowing to compute blurred derivatives of order 0,1 or 2 of an image. **/ CImg& deriche(const float sigma, const int order=0, const char axis='x', const bool cond=true) { #define _cimg_deriche2_apply \ Tfloat *ptrY = Y.data, yb = 0, yp = 0; \ T xp = (T)0; \ if (cond) { xp = *ptrX; yb = yp = (Tfloat)(coefp*xp); } \ for (int m=0; m=0; --n) { \ const T xc = *(ptrX-=off); \ const Tfloat yc = (Tfloat)(a2*xn + a3*xa - b1*yn - b2*ya); \ xa = xn; xn = xc; ya = yn; yn = yc; \ *ptrX = (T)(*(--ptrY)+yc); \ } if (sigma<0) throw CImgArgumentException("CImg<%s>::deriche() : Given filter variance (sigma = %g) is negative", pixel_type(),sigma); if (is_empty() || (sigma<0.1 && !order)) return *this; const float nsigma = sigma<0.1f?0.1f:sigma, alpha = 1.695f/nsigma, ema = (float)cimg_std::exp(-alpha), ema2 = (float)cimg_std::exp(-2*alpha), b1 = -2*ema, b2 = ema2; float a0 = 0, a1 = 0, a2 = 0, a3 = 0, coefp = 0, coefn = 0; switch (order) { case 0 : { const float k = (1-ema)*(1-ema)/(1+2*alpha*ema-ema2); a0 = k; a1 = k*(alpha-1)*ema; a2 = k*(alpha+1)*ema; a3 = -k*ema2; } break; case 1 : { const float k = (1-ema)*(1-ema)/ema; a0 = k*ema; a1 = a3 = 0; a2 = -a0; } break; case 2 : { const float ea = (float)cimg_std::exp(-alpha), k = -(ema2-1)/(2*alpha*ema), kn = (-2*(-1+3*ea-3*ea*ea+ea*ea*ea)/(3*ea+1+3*ea*ea+ea*ea*ea)); a0 = kn; a1 = -kn*(1+k*alpha)*ema; a2 = kn*(1-k*alpha)*ema; a3 = -kn*ema2; } break; default : throw CImgArgumentException("CImg<%s>::deriche() : Given filter order (order = %u) must be 0,1 or 2", pixel_type(),order); } coefp = (a0+a1)/(1+b1+b2); coefn = (a2+a3)/(1+b1+b2); switch (cimg::uncase(axis)) { case 'x' : { const int N = width, off = 1; CImg Y(N); cimg_forYZV(*this,y,z,v) { T *ptrX = ptr(0,y,z,v); _cimg_deriche2_apply; } } break; case 'y' : { const int N = height, off = width; CImg Y(N); cimg_forXZV(*this,x,z,v) { T *ptrX = ptr(x,0,z,v); _cimg_deriche2_apply; } } break; case 'z' : { const int N = depth, off = width*height; CImg Y(N); cimg_forXYV(*this,x,y,v) { T *ptrX = ptr(x,y,0,v); _cimg_deriche2_apply; } } break; case 'v' : { const int N = dim, off = width*height*depth; CImg Y(N); cimg_forXYZ(*this,x,y,z) { T *ptrX = ptr(x,y,z,0); _cimg_deriche2_apply; } } break; } return *this; } CImg get_deriche(const float sigma, const int order=0, const char axis='x', const bool cond=true) const { return CImg(*this,false).deriche(sigma,order,axis,cond); } //! Return a blurred version of the image, using a Canny-Deriche filter. /** Blur the image with an anisotropic exponential filter (Deriche filter of order 0). **/ CImg& blur(const float sigmax, const float sigmay, const float sigmaz, const bool cond=true) { if (!is_empty()) { if (width>1 && sigmax>0) deriche(sigmax,0,'x',cond); if (height>1 && sigmay>0) deriche(sigmay,0,'y',cond); if (depth>1 && sigmaz>0) deriche(sigmaz,0,'z',cond); } return *this; } CImg get_blur(const float sigmax, const float sigmay, const float sigmaz, const bool cond=true) const { return CImg(*this,false).blur(sigmax,sigmay,sigmaz,cond); } //! Return a blurred version of the image, using a Canny-Deriche filter. CImg& blur(const float sigma, const bool cond=true) { return blur(sigma,sigma,sigma,cond); } CImg get_blur(const float sigma, const bool cond=true) const { return CImg(*this,false).blur(sigma,cond); } //! Blur the image anisotropically following a field of diffusion tensors. /** \param G = Field of square roots of diffusion tensors used to drive the smoothing. \param amplitude = amplitude of the smoothing. \param dl = spatial discretization. \param da = angular discretization. \param gauss_prec = precision of the gaussian function. \param interpolation Used interpolation scheme (0 = nearest-neighbor, 1 = linear, 2 = Runge-Kutta) \param fast_approx = Tell to use the fast approximation or not. **/ template CImg& blur_anisotropic(const CImg& 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 fast_approx=true) { #define _cimg_valign2d(i,j) \ { Tfloat &u = W(i,j,0,0), &v = W(i,j,0,1); \ if (u*curru + v*currv<0) { u=-u; v=-v; }} #define _cimg_valign3d(i,j,k) \ { Tfloat &u = W(i,j,k,0), &v = W(i,j,k,1), &w = W(i,j,k,2); \ if (u*curru + v*currv + w*currw<0) { u=-u; v=-v; w=-w; }} // Check arguments and init variables if (!is_empty() && amplitude>0) { if (!G || (G.dim!=3 && G.dim!=6) || G.width!=width || G.height!=height || G.depth!=depth) throw CImgArgumentException("CImg<%s>::blur_anisotropic() : Specified tensor field (%u,%u,%u,%u) is not valid.", pixel_type(),G.width,G.height,G.depth,G.dim); const float sqrt2amplitude = (float)cimg_std::sqrt(2*amplitude); const bool threed = (G.dim>=6); const int dx1 = dimx()-1, dy1 = dimy()-1, dz1 = dimz()-1; CImg dest(width,height,depth,dim,0), W(width,height,depth,threed?4:3), tmp(dim); int N = 0; if (threed) // 3D version of the algorithm for (float phi=(180%(int)da)/2.0f; phi<=180; phi+=da) { const float phir = (float)(phi*cimg::valuePI/180), datmp = (float)(da/cimg_std::cos(phir)), da2 = datmp<1?360.0f:datmp; for (float theta=0; theta<360; (theta+=da2),++N) { const float thetar = (float)(theta*cimg::valuePI/180), vx = (float)(cimg_std::cos(thetar)*cimg_std::cos(phir)), vy = (float)(cimg_std::sin(thetar)*cimg_std::cos(phir)), vz = (float)cimg_std::sin(phir); const t *pa = G.ptr(0,0,0,0), *pb = G.ptr(0,0,0,1), *pc = G.ptr(0,0,0,2), *pd = G.ptr(0,0,0,3), *pe = G.ptr(0,0,0,4), *pf = G.ptr(0,0,0,5); Tfloat *pd0 = W.ptr(0,0,0,0), *pd1 = W.ptr(0,0,0,1), *pd2 = W.ptr(0,0,0,2), *pd3 = W.ptr(0,0,0,3); cimg_forXYZ(G,xg,yg,zg) { const t a = *(pa++), b = *(pb++), c = *(pc++), d = *(pd++), e = *(pe++), f = *(pf++); const float u = (float)(a*vx + b*vy + c*vz), v = (float)(b*vx + d*vy + e*vz), w = (float)(c*vx + e*vy + f*vz), n = (float)cimg_std::sqrt(1e-5+u*u+v*v+w*w), dln = dl/n; *(pd0++) = (Tfloat)(u*dln); *(pd1++) = (Tfloat)(v*dln); *(pd2++) = (Tfloat)(w*dln); *(pd3++) = (Tfloat)n; } cimg_forXYZ(*this,x,y,z) { tmp.fill(0); const float cu = (float)W(x,y,z,0), cv = (float)W(x,y,z,1), cw = (float)W(x,y,z,2), n = (float)W(x,y,z,3), fsigma = (float)(n*sqrt2amplitude), length = gauss_prec*fsigma, fsigma2 = 2*fsigma*fsigma; float S = 0, pu = cu, pv = cv, pw = cw, X = (float)x, Y = (float)y, Z = (float)z; switch (interpolation_type) { case 0 : { // Nearest neighbor for (float l=0; l=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) { const int cx = (int)(X+0.5f), cy = (int)(Y+0.5f), cz = (int)(Z+0.5f); float u = (float)W(cx,cy,cz,0), v = (float)W(cx,cy,cz,1), w = (float)W(cx,cy,cz,2); if ((pu*u + pv*v + pw*w)<0) { u=-u; v=-v; w=-w; } if (fast_approx) { cimg_forV(*this,k) tmp[k]+=(Tfloat)(*this)(cx,cy,cz,k); ++S; } else { const float coef = (float)cimg_std::exp(-l*l/fsigma2); cimg_forV(*this,k) tmp[k]+=(Tfloat)(coef*(*this)(cx,cy,cz,k)); S+=coef; } X+=(pu=u); Y+=(pv=v); Z+=(pw=w); } } break; case 1 : { // Linear interpolation for (float l=0; l=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) { const int cx = (int)X, px = (cx-1<0)?0:cx-1, nx = (cx+1>dx1)?dx1:cx+1, cy = (int)Y, py = (cy-1<0)?0:cy-1, ny = (cy+1>dy1)?dy1:cy+1, cz = (int)Z, pz = (cz-1<0)?0:cz-1, nz = (cz+1>dz1)?dz1:cz+1; const float curru = (float)W(cx,cy,cz,0), currv = (float)W(cx,cy,cz,1), currw = (float)W(cx,cy,cz,2); _cimg_valign3d(px,py,pz); _cimg_valign3d(cx,py,pz); _cimg_valign3d(nx,py,pz); _cimg_valign3d(px,cy,pz); _cimg_valign3d(cx,cy,pz); _cimg_valign3d(nx,cy,pz); _cimg_valign3d(px,ny,pz); _cimg_valign3d(cx,ny,pz); _cimg_valign3d(nx,ny,pz); _cimg_valign3d(px,py,cz); _cimg_valign3d(cx,py,cz); _cimg_valign3d(nx,py,cz); _cimg_valign3d(px,cy,cz); _cimg_valign3d(nx,cy,cz); _cimg_valign3d(px,ny,cz); _cimg_valign3d(cx,ny,cz); _cimg_valign3d(nx,ny,cz); _cimg_valign3d(px,py,nz); _cimg_valign3d(cx,py,nz); _cimg_valign3d(nx,py,nz); _cimg_valign3d(px,cy,nz); _cimg_valign3d(cx,cy,nz); _cimg_valign3d(nx,cy,nz); _cimg_valign3d(px,ny,nz); _cimg_valign3d(cx,ny,nz); _cimg_valign3d(nx,ny,nz); float u = (float)(W._linear_atXYZ(X,Y,Z,0)), v = (float)(W._linear_atXYZ(X,Y,Z,1)), w = (float)(W._linear_atXYZ(X,Y,Z,2)); if ((pu*u + pv*v + pw*w)<0) { u=-u; v=-v; w=-w; } if (fast_approx) { cimg_forV(*this,k) tmp[k]+=(Tfloat)_linear_atXYZ(X,Y,Z,k); ++S; } else { const float coef = (float)cimg_std::exp(-l*l/fsigma2); cimg_forV(*this,k) tmp[k]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,k)); S+=coef; } X+=(pu=u); Y+=(pv=v); Z+=(pw=w); } } break; default : { // 2nd order Runge Kutta for (float l=0; l=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) { const int cx = (int)X, px = (cx-1<0)?0:cx-1, nx = (cx+1>dx1)?dx1:cx+1, cy = (int)Y, py = (cy-1<0)?0:cy-1, ny = (cy+1>dy1)?dy1:cy+1, cz = (int)Z, pz = (cz-1<0)?0:cz-1, nz = (cz+1>dz1)?dz1:cz+1; const float curru = (float)W(cx,cy,cz,0), currv = (float)W(cx,cy,cz,1), currw = (float)W(cx,cy,cz,2); _cimg_valign3d(px,py,pz); _cimg_valign3d(cx,py,pz); _cimg_valign3d(nx,py,pz); _cimg_valign3d(px,cy,pz); _cimg_valign3d(cx,cy,pz); _cimg_valign3d(nx,cy,pz); _cimg_valign3d(px,ny,pz); _cimg_valign3d(cx,ny,pz); _cimg_valign3d(nx,ny,pz); _cimg_valign3d(px,py,cz); _cimg_valign3d(cx,py,cz); _cimg_valign3d(nx,py,cz); _cimg_valign3d(px,cy,cz); _cimg_valign3d(nx,cy,cz); _cimg_valign3d(px,ny,cz); _cimg_valign3d(cx,ny,cz); _cimg_valign3d(nx,ny,cz); _cimg_valign3d(px,py,nz); _cimg_valign3d(cx,py,nz); _cimg_valign3d(nx,py,nz); _cimg_valign3d(px,cy,nz); _cimg_valign3d(cx,cy,nz); _cimg_valign3d(nx,cy,nz); _cimg_valign3d(px,ny,nz); _cimg_valign3d(cx,ny,nz); _cimg_valign3d(nx,ny,nz); const float u0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,0)), v0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,1)), w0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,2)); float u = (float)(W._linear_atXYZ(X+u0,Y+v0,Z+w0,0)), v = (float)(W._linear_atXYZ(X+u0,Y+v0,Z+w0,1)), w = (float)(W._linear_atXYZ(X+u0,Y+v0,Z+w0,2)); if ((pu*u + pv*v + pw*w)<0) { u=-u; v=-v; w=-w; } if (fast_approx) { cimg_forV(*this,k) tmp[k]+=(Tfloat)_linear_atXYZ(X,Y,Z,k); ++S; } else { const float coef = (float)cimg_std::exp(-l*l/fsigma2); cimg_forV(*this,k) tmp[k]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,k)); S+=coef; } X+=(pu=u); Y+=(pv=v); Z+=(pw=w); } } break; } if (S>0) cimg_forV(dest,k) dest(x,y,z,k)+=tmp[k]/S; else cimg_forV(dest,k) dest(x,y,z,k)+=(Tfloat)((*this)(x,y,z,k)); cimg_plugin_greycstoration_count; } } } else // 2D version of the algorithm for (float theta=(360%(int)da)/2.0f; theta<360; (theta+=da),++N) { const float thetar = (float)(theta*cimg::valuePI/180), vx = (float)(cimg_std::cos(thetar)), vy = (float)(cimg_std::sin(thetar)); const t *pa = G.ptr(0,0,0,0), *pb = G.ptr(0,0,0,1), *pc = G.ptr(0,0,0,2); Tfloat *pd0 = W.ptr(0,0,0,0), *pd1 = W.ptr(0,0,0,1), *pd2 = W.ptr(0,0,0,2); cimg_forXY(G,xg,yg) { const t a = *(pa++), b = *(pb++), c = *(pc++); const float u = (float)(a*vx + b*vy), v = (float)(b*vx + c*vy), n = (float)cimg_std::sqrt(1e-5+u*u+v*v), dln = dl/n; *(pd0++) = (Tfloat)(u*dln); *(pd1++) = (Tfloat)(v*dln); *(pd2++) = (Tfloat)n; } cimg_forXY(*this,x,y) { tmp.fill(0); const float cu = (float)W(x,y,0,0), cv = (float)W(x,y,0,1), n = (float)W(x,y,0,2), fsigma = (float)(n*sqrt2amplitude), length = gauss_prec*fsigma, fsigma2 = 2*fsigma*fsigma; float S = 0, pu = cu, pv = cv, X = (float)x, Y = (float)y; switch (interpolation_type) { case 0 : { // Nearest-neighbor interpolation for 2D images for (float l=0; l=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) { const int cx = (int)(X+0.5f), cy = (int)(Y+0.5f); float u = (float)W(cx,cy,0,0), v = (float)W(cx,cy,0,1); if ((pu*u + pv*v)<0) { u=-u; v=-v; } if (fast_approx) { cimg_forV(*this,k) tmp[k]+=(Tfloat)(*this)(cx,cy,0,k); ++S; } else { const float coef = (float)cimg_std::exp(-l*l/fsigma2); cimg_forV(*this,k) tmp[k]+=(Tfloat)(coef*(*this)(cx,cy,0,k)); S+=coef; } X+=(pu=u); Y+=(pv=v); } } break; case 1 : { // Linear interpolation for 2D images for (float l=0; l=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) { const int cx = (int)X, px = (cx-1<0)?0:cx-1, nx = (cx+1>dx1)?dx1:cx+1, cy = (int)Y, py = (cy-1<0)?0:cy-1, ny = (cy+1>dy1)?dy1:cy+1; const float curru = (float)W(cx,cy,0,0), currv = (float)W(cx,cy,0,1); _cimg_valign2d(px,py); _cimg_valign2d(cx,py); _cimg_valign2d(nx,py); _cimg_valign2d(px,cy); _cimg_valign2d(nx,cy); _cimg_valign2d(px,ny); _cimg_valign2d(cx,ny); _cimg_valign2d(nx,ny); float u = (float)(W._linear_atXY(X,Y,0,0)), v = (float)(W._linear_atXY(X,Y,0,1)); if ((pu*u + pv*v)<0) { u=-u; v=-v; } if (fast_approx) { cimg_forV(*this,k) tmp[k]+=(Tfloat)_linear_atXY(X,Y,0,k); ++S; } else { const float coef = (float)cimg_std::exp(-l*l/fsigma2); cimg_forV(*this,k) tmp[k]+=(Tfloat)(coef*_linear_atXY(X,Y,0,k)); S+=coef; } X+=(pu=u); Y+=(pv=v); } } break; default : { // 2nd-order Runge-kutta interpolation for 2D images for (float l=0; l=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) { const int cx = (int)X, px = (cx-1<0)?0:cx-1, nx = (cx+1>dx1)?dx1:cx+1, cy = (int)Y, py = (cy-1<0)?0:cy-1, ny = (cy+1>dy1)?dy1:cy+1; const float curru = (float)W(cx,cy,0,0), currv = (float)W(cx,cy,0,1); _cimg_valign2d(px,py); _cimg_valign2d(cx,py); _cimg_valign2d(nx,py); _cimg_valign2d(px,cy); _cimg_valign2d(nx,cy); _cimg_valign2d(px,ny); _cimg_valign2d(cx,ny); _cimg_valign2d(nx,ny); const float u0 = (float)(0.5f*W._linear_atXY(X,Y,0,0)), v0 = (float)(0.5f*W._linear_atXY(X,Y,0,1)); float u = (float)(W._linear_atXY(X+u0,Y+v0,0,0)), v = (float)(W._linear_atXY(X+u0,Y+v0,0,1)); if ((pu*u + pv*v)<0) { u=-u; v=-v; } if (fast_approx) { cimg_forV(*this,k) tmp[k]+=(Tfloat)_linear_atXY(X,Y,0,k); ++S; } else { const float coef = (float)cimg_std::exp(-l*l/fsigma2); cimg_forV(*this,k) tmp[k]+=(Tfloat)(coef*_linear_atXY(X,Y,0,k)); S+=coef; } X+=(pu=u); Y+=(pv=v); } } } if (S>0) cimg_forV(dest,k) dest(x,y,0,k)+=tmp[k]/S; else cimg_forV(dest,k) dest(x,y,0,k)+=(Tfloat)((*this)(x,y,0,k)); cimg_plugin_greycstoration_count; } } const Tfloat *ptrs = dest.data+dest.size(); const T m = cimg::type::min(), M = cimg::type::max(); cimg_for(*this,ptrd,T) { const Tfloat val = *(--ptrs)/N; *ptrd = valM?M:(T)val); } } return *this; } template CImg get_blur_anisotropic(const CImg& 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 fast_approx=true) const { return (+*this).blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation_type,fast_approx); } //! Blur an image in an anisotropic way. /** \param mask Binary mask. \param amplitude Amplitude of the anisotropic blur. \param sharpness Contour preservation. \param anisotropy Smoothing anisotropy. \param alpha Image pre-blurring (gaussian). \param sigma Regularity of the tensor-valued geometry. \param dl Spatial discretization. \param da Angular discretization. \param gauss_prec Precision of the gaussian function. \param interpolation_type Used interpolation scheme (0 = nearest-neighbor, 1 = linear, 2 = Runge-Kutta) \param fast_approx Tell to use the fast approximation or not \param geom_factor Geometry factor. **/ template CImg& blur_anisotropic(const CImg& mask, const float amplitude, const float sharpness=0.7f, const float anisotropy=0.3f, 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 fast_approx=true, const float geom_factor=1) { if (!is_empty() && amplitude>0) { if (amplitude==0) return *this; if (amplitude<0 || sharpness<0 || anisotropy<0 || anisotropy>1 || alpha<0 || sigma<0 || dl<0 || da<0 || gauss_prec<0) throw CImgArgumentException("CImg<%s>::blur_anisotropic() : Given parameters are amplitude(%g), sharpness(%g), " "anisotropy(%g), alpha(%g), sigma(%g), dl(%g), da(%g), gauss_prec(%g).\n" "Admissible parameters are in the range : amplitude>0, sharpness>0, anisotropy in [0,1], " "alpha>0, sigma>0, dl>0, da>0, gauss_prec>0.", pixel_type(),amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec); const bool threed = (depth>1), no_mask = mask.is_empty(); const float nsharpness = cimg::max(sharpness,1e-5f), power1 = 0.5f*nsharpness, power2 = power1/(1e-7f+1-anisotropy); CImg blurred = CImg(*this,false).blur(alpha); if (geom_factor>0) blurred*=geom_factor; else blurred.normalize(0,-geom_factor); if (threed) { // Field for 3D volumes cimg_plugin_greycstoration_lock; CImg val(3), vec(3,3), G(blurred.get_structure_tensor()); if (sigma>0) G.blur(sigma); cimg_forXYZ(*this,x,y,z) { if (no_mask || mask(x,y,z)) { G.get_tensor_at(x,y,z).symmetric_eigen(val,vec); const float l1 = val[2], l2 = val[1], l3 = val[0], ux = vec(0,0), uy = vec(0,1), uz = vec(0,2), vx = vec(1,0), vy = vec(1,1), vz = vec(1,2), wx = vec(2,0), wy = vec(2,1), wz = vec(2,2), n1 = (float)cimg_std::pow(1+l1+l2+l3,-power1), n2 = (float)cimg_std::pow(1+l1+l2+l3,-power2); G(x,y,z,0) = n1*(ux*ux + vx*vx) + n2*wx*wx; G(x,y,z,1) = n1*(ux*uy + vx*vy) + n2*wx*wy; G(x,y,z,2) = n1*(ux*uz + vx*vz) + n2*wx*wz; G(x,y,z,3) = n1*(uy*uy + vy*vy) + n2*wy*wy; G(x,y,z,4) = n1*(uy*uz + vy*vz) + n2*wy*wz; G(x,y,z,5) = n1*(uz*uz + vz*vz) + n2*wz*wz; } else G(x,y,z,0) = G(x,y,z,1) = G(x,y,z,2) = G(x,y,z,3) = G(x,y,z,4) = G(x,y,z,5) = 0; cimg_plugin_greycstoration_count; } cimg_plugin_greycstoration_unlock; blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation_type,fast_approx); } else { // Field for 2D images cimg_plugin_greycstoration_lock; CImg val(2), vec(2,2), G(blurred.get_structure_tensor()); if (sigma>0) G.blur(sigma); cimg_forXY(*this,x,y) { if (no_mask || mask(x,y)) { G.get_tensor_at(x,y).symmetric_eigen(val,vec); const float l1 = val[1], l2 = val[0], ux = vec(1,0), uy = vec(1,1), vx = vec(0,0), vy = vec(0,1), n1 = (float)cimg_std::pow(1+l1+l2,-power1), n2 = (float)cimg_std::pow(1+l1+l2,-power2); G(x,y,0,0) = n1*ux*ux + n2*vx*vx; G(x,y,0,1) = n1*ux*uy + n2*vx*vy; G(x,y,0,2) = n1*uy*uy + n2*vy*vy; } else G(x,y,0,0) = G(x,y,0,1) = G(x,y,0,2) = 0; cimg_plugin_greycstoration_count; } cimg_plugin_greycstoration_unlock; blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation_type,fast_approx); } } return *this; } template CImg get_blur_anisotropic(const CImg& mask, const float amplitude, const float sharpness=0.7f, const float anisotropy=0.3f, 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 fast_approx=true, const float geom_factor=1) const { return (+*this).blur_anisotropic(mask,amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,interpolation_type,fast_approx,geom_factor); } //! Blur an image following in an anisotropic way. CImg& blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.3f, 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 fast_approx=true, const float geom_factor=1) { return blur_anisotropic(CImg(),amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,interpolation_type,fast_approx,geom_factor); } CImg get_blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.3f, 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 fast_approx=true, const float geom_factor=1) const { return (+*this).blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,interpolation_type,fast_approx,geom_factor); } //! Blur an image using the bilateral filter. /** \param sigmax Amount of blur along the X-axis. \param sigmay Amount of blur along the Y-axis. \param sigmaz Amount of blur along the Z-axis. \param sigmar Amount of blur along the range axis. \param bgridx Size of the bilateral grid along the X-axis. \param bgridy Size of the bilateral grid along the Y-axis. \param bgridz Size of the bilateral grid along the Z-axis. \param bgridr Size of the bilateral grid along the range axis. \param interpolation_type Use interpolation for image slicing. \note This algorithm uses the optimisation technique proposed by S. Paris and F. Durand, in ECCV'2006 (extended for 3D volumetric images). **/ CImg& blur_bilateral(const float sigmax, const float sigmay, const float sigmaz, const float sigmar, const int bgridx, const int bgridy, const int bgridz, const int bgridr, const bool interpolation_type=true) { T m, M = maxmin(m); const float range = (float)(1.0f+M-m); const unsigned int bx0 = bgridx>=0?bgridx:width*(-bgridx)/100, by0 = bgridy>=0?bgridy:height*(-bgridy)/100, bz0 = bgridz>=0?bgridz:depth*(-bgridz)/100, br0 = bgridr>=0?bgridr:(int)(-range*bgridr/100), bx = bx0>0?bx0:1, by = by0>0?by0:1, bz = bz0>0?bz0:1, br = br0>0?br0:1; const float nsigmax = sigmax*bx/width, nsigmay = sigmay*by/height, nsigmaz = sigmaz*bz/depth, nsigmar = sigmar*br/range; if (nsigmax>0 || nsigmay>0 || nsigmaz>0 || nsigmar>0) { const bool threed = depth>1; if (threed) { // 3d version of the algorithm CImg bgrid(bx,by,bz,br), bgridw(bx,by,bz,br); cimg_forV(*this,k) { bgrid.fill(0); bgridw.fill(0); cimg_forXYZ(*this,x,y,z) { const T val = (*this)(x,y,z,k); const int X = x*bx/width, Y = y*by/height, Z = z*bz/depth, R = (int)((val-m)*br/range); bgrid(X,Y,Z,R) = (float)val; bgridw(X,Y,Z,R) = 1; } bgrid.blur(nsigmax,nsigmay,nsigmaz,true).deriche(nsigmar,0,'v',false); bgridw.blur(nsigmax,nsigmay,nsigmaz,true).deriche(nsigmar,0,'v',false); if (interpolation_type) cimg_forXYZ(*this,x,y,z) { const T val = (*this)(x,y,z,k); const float X = (float)x*bx/width, Y = (float)y*by/height, Z = (float)z*bz/depth, R = (float)((val-m)*br/range), bval0 = bgrid._linear_atXYZV(X,Y,Z,R), bval1 = bgridw._linear_atXYZV(X,Y,Z,R); (*this)(x,y,z,k) = (T)(bval0/bval1); } else cimg_forXYZ(*this,x,y,z) { const T val = (*this)(x,y,z,k); const int X = x*bx/width, Y = y*by/height, Z = z*bz/depth, R = (int)((val-m)*br/range); const float bval0 = bgrid(X,Y,Z,R), bval1 = bgridw(X,Y,Z,R); (*this)(x,y,z,k) = (T)(bval0/bval1); } } } else { // 2d version of the algorithm CImg bgrid(bx,by,br,2); cimg_forV(*this,k) { bgrid.fill(0); cimg_forXY(*this,x,y) { const T val = (*this)(x,y,k); const int X = x*bx/width, Y = y*by/height, R = (int)((val-m)*br/range); bgrid(X,Y,R,0) = (float)val; bgrid(X,Y,R,1) = 1; } bgrid.blur(nsigmax,nsigmay,0,true).blur(0,0,nsigmar,false); if (interpolation_type) cimg_forXY(*this,x,y) { const T val = (*this)(x,y,k); const float X = (float)x*bx/width, Y = (float)y*by/height, R = (float)((val-m)*br/range), bval0 = bgrid._linear_atXYZ(X,Y,R,0), bval1 = bgrid._linear_atXYZ(X,Y,R,1); (*this)(x,y,k) = (T)(bval0/bval1); } else cimg_forXY(*this,x,y) { const T val = (*this)(x,y,k); const int X = x*bx/width, Y = y*by/height, R = (int)((val-m)*br/range); const float bval0 = bgrid(X,Y,R,0), bval1 = bgrid(X,Y,R,1); (*this)(x,y,k) = (T)(bval0/bval1); } } } } return *this; } CImg get_blur_bilateral(const float sigmax, const float sigmay, const float sigmaz, const float sigmar, const int bgridx, const int bgridy, const int bgridz, const int bgridr, const bool interpolation_type=true) const { return (+*this).blur_bilateral(sigmax,sigmay,sigmaz,sigmar,bgridx,bgridy,bgridz,bgridr,interpolation_type); } //! Blur an image using the bilateral filter. CImg& blur_bilateral(const float sigmas, const float sigmar, const int bgrids=-33, const int bgridr=32, const bool interpolation_type=true) { return blur_bilateral(sigmas,sigmas,sigmas,sigmar,bgrids,bgrids,bgrids,bgridr,interpolation_type); } CImg get_blur_bilateral(const float sigmas, const float sigmar, const int bgrids=-33, const int bgridr=32, const bool interpolation_type=true) const { return (+*this).blur_bilateral(sigmas,sigmas,sigmas,sigmar,bgrids,bgrids,bgrids,bgridr,interpolation_type); } //! Blur an image in its patch-based space. CImg& blur_patch(const unsigned int patch_size, const float sigma_p, const float sigma_s=10, const unsigned int lookup_size=4, const bool fast_approx=true) { #define _cimg_blur_patch_fastfunc(x) ((x)>3?0:1) #define _cimg_blur_patch_slowfunc(x) cimg_std::exp(-(x)) #define _cimg_blur_patch3d(N,func) { \ const unsigned int N3 = N*N*N; \ cimg_for##N##XYZ(*this,x,y,z) { \ cimg_plugin_greycstoration_count; \ cimg_forV(*this,k) cimg_get##N##x##N##x##N(*this,x,y,z,k,P.ptr(N3*k)); \ const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \ float sum_weights = 0; \ cimg_for_in##N##XYZ(*this,x0,y0,z0,x1,y1,z1,p,q,r) { \ cimg_forV(*this,k) cimg_get##N##x##N##x##N(*this,p,q,r,k,Q.ptr(N3*k)); \ float distance2 = 0; \ const T *pQ = Q.end(); \ cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(--pQ); distance2+=dI*dI; } \ distance2/=Pnorm; \ const float dx = (float)p - x, dy = (float)q - y, dz = (float)r - z, \ alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = (float)func(alldist); \ sum_weights+=weight; \ { cimg_forV(*this,k) res(x,y,z,k)+=weight*(*this)(p,q,r,k); } \ } \ if (sum_weights>0) cimg_forV(*this,k) res(x,y,z,k)/=sum_weights; else cimg_forV(*this,k) res(x,y,z,k) = (Tfloat)((*this)(x,y,z,k)); \ }} #define _cimg_blur_patch2d(N,func) { \ const unsigned int N2 = N*N; \ cimg_for##N##XY(*this,x,y) { \ cimg_plugin_greycstoration_count; \ cimg_forV(*this,k) cimg_get##N##x##N(*this,x,y,0,k,P.ptr(N2*k)); \ const int x0 = x-rsize1, y0 = y-rsize1, x1 = x+rsize2, y1 = y+rsize2; \ float sum_weights = 0; \ cimg_for_in##N##XY(*this,x0,y0,x1,y1,p,q) { \ cimg_forV(*this,k) cimg_get##N##x##N(*this,p,q,0,k,Q.ptr(N2*k)); \ float distance2 = 0; \ const T *pQ = Q.end(); \ cimg_for(P,pP,T) { const float dI = (float)*pP-(float)*(--pQ); distance2+=dI*dI; } \ distance2/=Pnorm; \ const float dx = (float)p-x, dy = (float)q-y, \ alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = (float)func(alldist); \ sum_weights+=weight; \ { cimg_forV(*this,k) res(x,y,k)+=weight*(*this)(p,q,k); } \ } \ if (sum_weights>0) cimg_forV(*this,k) res(x,y,k)/=sum_weights; else cimg_forV(*this,k) res(x,y,k) = (Tfloat)((*this)(x,y,k)); \ }} CImg res(width,height,depth,dim,0); CImg P(patch_size*patch_size*dim), Q(P); const float sigma_s2 = sigma_s*sigma_s, sigma_p2 = sigma_p*sigma_p, Pnorm = P.size()*sigma_p2; const int rsize2 = (int)lookup_size/2, rsize1 = rsize2-1+(lookup_size%2); if (depth>1) switch (patch_size) { // 3D version case 2 : if (fast_approx) { _cimg_blur_patch3d(2,_cimg_blur_patch_fastfunc); } else { _cimg_blur_patch3d(2,_cimg_blur_patch_slowfunc); } break; case 3 : if (fast_approx) { _cimg_blur_patch3d(3,_cimg_blur_patch_fastfunc); } else { _cimg_blur_patch3d(3,_cimg_blur_patch_slowfunc); } break; default : { const int psize1 = (int)patch_size/2, psize0 = psize1-1+(patch_size%2); cimg_forXYZ(*this,x,y,z) { cimg_plugin_greycstoration_count; P = get_crop(x - psize0,y - psize0,z - psize0,x + psize1,y + psize1,z + psize1,true); const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; float sum_weights = 0; cimg_for_inXYZ(*this,x0,y0,z0,x1,y1,z1,p,q,r) { (Q = get_crop(p - psize0,q - psize0,r - psize0,p + psize1,q + psize1,r + psize1,true))-=P; const float dx = (float)x - p, dy = (float)y - q, dz = (float)z - r, distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2), weight = (float)cimg_std::exp(-distance2); sum_weights+=weight; cimg_forV(*this,k) res(x,y,z,k)+=weight*(*this)(p,q,r,k); } if (sum_weights>0) cimg_forV(*this,k) res(x,y,z,k)/=sum_weights; else cimg_forV(*this,k) res(x,y,z,k) = (Tfloat)((*this)(x,y,z,k)); } } } else switch (patch_size) { // 2D version case 2 : if (fast_approx) { _cimg_blur_patch2d(2,_cimg_blur_patch_fastfunc); } else { _cimg_blur_patch2d(2,_cimg_blur_patch_slowfunc); } break; case 3 : if (fast_approx) { _cimg_blur_patch2d(3,_cimg_blur_patch_fastfunc); } else { _cimg_blur_patch2d(3,_cimg_blur_patch_slowfunc); } break; case 4 : if (fast_approx) { _cimg_blur_patch2d(4,_cimg_blur_patch_fastfunc); } else { _cimg_blur_patch2d(4,_cimg_blur_patch_slowfunc); } break; case 5 : if (fast_approx) { _cimg_blur_patch2d(5,_cimg_blur_patch_fastfunc); } else { _cimg_blur_patch2d(5,_cimg_blur_patch_slowfunc); } break; case 6 : if (fast_approx) { _cimg_blur_patch2d(6,_cimg_blur_patch_fastfunc); } else { _cimg_blur_patch2d(6,_cimg_blur_patch_slowfunc); } break; case 7 : if (fast_approx) { _cimg_blur_patch2d(7,_cimg_blur_patch_fastfunc); } else { _cimg_blur_patch2d(7,_cimg_blur_patch_slowfunc); } break; case 8 : if (fast_approx) { _cimg_blur_patch2d(8,_cimg_blur_patch_fastfunc); } else { _cimg_blur_patch2d(8,_cimg_blur_patch_slowfunc); } break; case 9 : if (fast_approx) { _cimg_blur_patch2d(9,_cimg_blur_patch_fastfunc); } else { _cimg_blur_patch2d(9,_cimg_blur_patch_slowfunc); } break; default : { const int psize1 = (int)patch_size/2, psize0 = psize1-1+(patch_size%2); cimg_forXY(*this,x,y) { cimg_plugin_greycstoration_count; P = get_crop(x - psize0,y - psize0,x + psize1,y + psize1,true); const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; float sum_weights = 0; cimg_for_inXY(*this,x0,y0,x1,y1,p,q) { (Q = get_crop(p - psize0,q - psize0,p + psize1,q + psize1,true))-=P; const float dx = (float)x - p, dy = (float)y - q, distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2), weight = (float)cimg_std::exp(-distance2); sum_weights+=weight; cimg_forV(*this,k) res(x,y,0,k)+=weight*(*this)(p,q,0,k); } if (sum_weights>0) cimg_forV(*this,k) res(x,y,0,k)/=sum_weights; else cimg_forV(*this,k) res(x,y,0,k) = (Tfloat)((*this)(x,y,0,k)); } } } return res.transfer_to(*this); } CImg get_blur_patch(const unsigned int patch_size, const float sigma_p, const float sigma_s=10, const unsigned int lookup_size=4, const bool fast_approx=true) const { return (+*this).blur_patch(patch_size,sigma_p,sigma_s,lookup_size,fast_approx); } //! Compute the Fast Fourier Transform of an image (along a specified axis). CImgList get_FFT(const char axis, const bool invert=false) const { return CImgList(*this).FFT(axis,invert); } //! Compute the Fast Fourier Transform on an image. CImgList get_FFT(const bool invert=false) const { return CImgList(*this).FFT(invert); } //! Apply a median filter. CImg& blur_median(const unsigned int n) { return get_blur_median(n).transfer_to(*this); } CImg get_blur_median(const unsigned int n) { CImg res(width,height,depth,dim); if (!n || n==1) return *this; const int hl=n/2, hr=hl-1+n%2; if (res.depth!=1) { // 3D median filter CImg vois; cimg_forXYZV(*this,x,y,z,k) { const int x0 = x - hl, y0 = y - hl, z0 = z-hl, x1 = x + hr, y1 = y + hr, z1 = z+hr, nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0, nx1 = x1>=dimx()?dimx()-1:x1, ny1 = y1>=dimy()?dimy()-1:y1, nz1 = z1>=dimz()?dimz()-1:z1; vois = get_crop(nx0,ny0,nz0,k,nx1,ny1,nz1,k); res(x,y,z,k) = vois.median(); } } else { #define _cimg_median_sort(a,b) if ((a)>(b)) cimg::swap(a,b) if (res.height!=1) switch (n) { // 2D median filter case 3 : { T I[9] = { 0 }; CImg_3x3(J,T); cimg_forV(*this,k) cimg_for3x3(*this,x,y,0,k,I) { cimg_std::memcpy(J,I,9*sizeof(T)); _cimg_median_sort(Jcp, Jnp); _cimg_median_sort(Jcc, Jnc); _cimg_median_sort(Jcn, Jnn); _cimg_median_sort(Jpp, Jcp); _cimg_median_sort(Jpc, Jcc); _cimg_median_sort(Jpn, Jcn); _cimg_median_sort(Jcp, Jnp); _cimg_median_sort(Jcc, Jnc); _cimg_median_sort(Jcn, Jnn); _cimg_median_sort(Jpp, Jpc); _cimg_median_sort(Jnc, Jnn); _cimg_median_sort(Jcc, Jcn); _cimg_median_sort(Jpc, Jpn); _cimg_median_sort(Jcp, Jcc); _cimg_median_sort(Jnp, Jnc); _cimg_median_sort(Jcc, Jcn); _cimg_median_sort(Jcc, Jnp); _cimg_median_sort(Jpn, Jcc); _cimg_median_sort(Jcc, Jnp); res(x,y,0,k) = Jcc; } } break; case 5 : { T I[25] = { 0 }; CImg_5x5(J,T); cimg_forV(*this,k) cimg_for5x5(*this,x,y,0,k,I) { cimg_std::memcpy(J,I,25*sizeof(T)); _cimg_median_sort(Jbb, Jpb); _cimg_median_sort(Jnb, Jab); _cimg_median_sort(Jcb, Jab); _cimg_median_sort(Jcb, Jnb); _cimg_median_sort(Jpp, Jcp); _cimg_median_sort(Jbp, Jcp); _cimg_median_sort(Jbp, Jpp); _cimg_median_sort(Jap, Jbc); _cimg_median_sort(Jnp, Jbc); _cimg_median_sort(Jnp, Jap); _cimg_median_sort(Jcc, Jnc); _cimg_median_sort(Jpc, Jnc); _cimg_median_sort(Jpc, Jcc); _cimg_median_sort(Jbn, Jpn); _cimg_median_sort(Jac, Jpn); _cimg_median_sort(Jac, Jbn); _cimg_median_sort(Jnn, Jan); _cimg_median_sort(Jcn, Jan); _cimg_median_sort(Jcn, Jnn); _cimg_median_sort(Jpa, Jca); _cimg_median_sort(Jba, Jca); _cimg_median_sort(Jba, Jpa); _cimg_median_sort(Jna, Jaa); _cimg_median_sort(Jcb, Jbp); _cimg_median_sort(Jnb, Jpp); _cimg_median_sort(Jbb, Jpp); _cimg_median_sort(Jbb, Jnb); _cimg_median_sort(Jab, Jcp); _cimg_median_sort(Jpb, Jcp); _cimg_median_sort(Jpb, Jab); _cimg_median_sort(Jpc, Jac); _cimg_median_sort(Jnp, Jac); _cimg_median_sort(Jnp, Jpc); _cimg_median_sort(Jcc, Jbn); _cimg_median_sort(Jap, Jbn); _cimg_median_sort(Jap, Jcc); _cimg_median_sort(Jnc, Jpn); _cimg_median_sort(Jbc, Jpn); _cimg_median_sort(Jbc, Jnc); _cimg_median_sort(Jba, Jna); _cimg_median_sort(Jcn, Jna); _cimg_median_sort(Jcn, Jba); _cimg_median_sort(Jpa, Jaa); _cimg_median_sort(Jnn, Jaa); _cimg_median_sort(Jnn, Jpa); _cimg_median_sort(Jan, Jca); _cimg_median_sort(Jnp, Jcn); _cimg_median_sort(Jap, Jnn); _cimg_median_sort(Jbb, Jnn); _cimg_median_sort(Jbb, Jap); _cimg_median_sort(Jbc, Jan); _cimg_median_sort(Jpb, Jan); _cimg_median_sort(Jpb, Jbc); _cimg_median_sort(Jpc, Jba); _cimg_median_sort(Jcb, Jba); _cimg_median_sort(Jcb, Jpc); _cimg_median_sort(Jcc, Jpa); _cimg_median_sort(Jnb, Jpa); _cimg_median_sort(Jnb, Jcc); _cimg_median_sort(Jnc, Jca); _cimg_median_sort(Jab, Jca); _cimg_median_sort(Jab, Jnc); _cimg_median_sort(Jac, Jna); _cimg_median_sort(Jbp, Jna); _cimg_median_sort(Jbp, Jac); _cimg_median_sort(Jbn, Jaa); _cimg_median_sort(Jpp, Jaa); _cimg_median_sort(Jpp, Jbn); _cimg_median_sort(Jcp, Jpn); _cimg_median_sort(Jcp, Jan); _cimg_median_sort(Jnc, Jpa); _cimg_median_sort(Jbn, Jna); _cimg_median_sort(Jcp, Jnc); _cimg_median_sort(Jcp, Jbn); _cimg_median_sort(Jpb, Jap); _cimg_median_sort(Jnb, Jpc); _cimg_median_sort(Jbp, Jcn); _cimg_median_sort(Jpc, Jcn); _cimg_median_sort(Jap, Jcn); _cimg_median_sort(Jab, Jbc); _cimg_median_sort(Jpp, Jcc); _cimg_median_sort(Jcp, Jac); _cimg_median_sort(Jab, Jpp); _cimg_median_sort(Jab, Jcp); _cimg_median_sort(Jcc, Jac); _cimg_median_sort(Jbc, Jac); _cimg_median_sort(Jpp, Jcp); _cimg_median_sort(Jbc, Jcc); _cimg_median_sort(Jpp, Jbc); _cimg_median_sort(Jpp, Jcn); _cimg_median_sort(Jcc, Jcn); _cimg_median_sort(Jcp, Jcn); _cimg_median_sort(Jcp, Jbc); _cimg_median_sort(Jcc, Jnn); _cimg_median_sort(Jcp, Jcc); _cimg_median_sort(Jbc, Jnn); _cimg_median_sort(Jcc, Jba); _cimg_median_sort(Jbc, Jba); _cimg_median_sort(Jbc, Jcc); res(x,y,0,k) = Jcc; } } break; default : { CImg vois; cimg_forXYV(*this,x,y,k) { const int x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr, nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nx1 = x1>=dimx()?dimx()-1:x1, ny1 = y1>=dimy()?dimy()-1:y1; vois = get_crop(nx0,ny0,0,k,nx1,ny1,0,k); res(x,y,0,k) = vois.median(); } } } else switch (n) { // 1D median filter case 2 : { T I[4] = { 0 }; cimg_forV(*this,k) cimg_for2x2(*this,x,y,0,k,I) res(x,0,0,k) = (T)(0.5f*(I[0]+I[1])); } break; case 3 : { T I[9] = { 0 }; cimg_forV(*this,k) cimg_for3x3(*this,x,y,0,k,I) { res(x,0,0,k) = I[3] vois; cimg_forXV(*this,x,k) { const int x0 = x - hl, x1 = x + hr, nx0 = x0<0?0:x0, nx1 = x1>=dimx()?dimx()-1:x1; vois = get_crop(nx0,0,0,k,nx1,0,0,k); res(x,0,0,k) = vois.median(); } } } } return res; } //! Sharpen image using anisotropic shock filters or inverse diffusion. CImg& sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1, const float alpha=0, const float sigma=0) { if (is_empty()) return *this; T valm, valM = maxmin(valm); const bool threed = (depth>1); const float nedge = 0.5f*edge; CImg val, vec, veloc(width,height,depth,dim); if (threed) { CImg_3x3x3(I,T); if (sharpen_type) { // 3D Shock filter. CImg G = (alpha>0?get_blur(alpha).get_structure_tensor():get_structure_tensor()); if (sigma>0) G.blur(sigma); cimg_forXYZ(G,x,y,z) { G.get_tensor_at(x,y,z).symmetric_eigen(val,vec); G(x,y,z,0) = vec(0,0); G(x,y,z,1) = vec(0,1); G(x,y,z,2) = vec(0,2); G(x,y,z,3) = 1 - (Tfloat)cimg_std::pow(1+val[0]+val[1]+val[2],-(Tfloat)nedge); } cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) { const Tfloat u = G(x,y,z,0), v = G(x,y,z,1), w = G(x,y,z,2), amp = G(x,y,z,3), ixx = (Tfloat)Incc + Ipcc - 2*Iccc, ixy = 0.25f*((Tfloat)Innc + Ippc - Inpc - Ipnc), ixz = 0.25f*((Tfloat)Incn + Ipcp - Incp - Ipcn), iyy = (Tfloat)Icnc + Icpc - 2*Iccc, iyz = 0.25f*((Tfloat)Icnn + Icpp - Icnp - Icpn), izz = (Tfloat)Iccn + Iccp - 2*Iccc, ixf = (Tfloat)Incc - Iccc, ixb = (Tfloat)Iccc - Ipcc, iyf = (Tfloat)Icnc - Iccc, iyb = (Tfloat)Iccc - Icpc, izf = (Tfloat)Iccn - Iccc, izb = (Tfloat)Iccc - Iccp, itt = u*u*ixx + v*v*iyy + w*w*izz + 2*u*v*ixy + 2*u*w*ixz + 2*v*w*iyz, it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb) + w*cimg::minmod(izf,izb); veloc(x,y,z,k) = -amp*cimg::sign(itt)*cimg::abs(it); } } else cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) veloc(x,y,z,k) = -(Tfloat)Ipcc-Incc-Icpc-Icnc-Iccp-Iccn+6*Iccc; // 3D Inverse diffusion. } else { CImg_3x3(I,T); if (sharpen_type) { // 2D Shock filter. CImg G = (alpha>0?get_blur(alpha).get_structure_tensor():get_structure_tensor()); if (sigma>0) G.blur(sigma); cimg_forXY(G,x,y) { G.get_tensor_at(x,y).symmetric_eigen(val,vec); G(x,y,0) = vec(0,0); G(x,y,1) = vec(0,1); G(x,y,2) = 1 - (Tfloat)cimg_std::pow(1+val[0]+val[1],-(Tfloat)nedge); } cimg_forV(*this,k) cimg_for3x3(*this,x,y,0,k,I) { const Tfloat u = G(x,y,0), v = G(x,y,1), amp = G(x,y,2), ixx = (Tfloat)Inc + Ipc - 2*Icc, ixy = 0.25f*((Tfloat)Inn + Ipp - Inp - Ipn), iyy = (Tfloat)Icn + Icp - 2*Icc, ixf = (Tfloat)Inc - Icc, ixb = (Tfloat)Icc - Ipc, iyf = (Tfloat)Icn - Icc, iyb = (Tfloat)Icc - Icp, itt = u*u*ixx + v*v*iyy + 2*u*v*ixy, it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb); veloc(x,y,k) = -amp*cimg::sign(itt)*cimg::abs(it); } } else cimg_forV(*this,k) cimg_for3x3(*this,x,y,0,k,I) veloc(x,y,k) = -(Tfloat)Ipc-Inc-Icp-Icn+4*Icc; // 3D Inverse diffusion. } float m, M = (float)veloc.maxmin(m); const float vmax = (float)cimg::max(cimg::abs(m),cimg::abs(M)); if (vmax!=0) { veloc*=amplitude/vmax; (*this)+=veloc; } return cut(valm,valM); } CImg get_sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1, const float alpha=0, const float sigma=0) const { return (+*this).sharpen(amplitude,sharpen_type,edge,alpha,sigma); } //! Compute the Haar multiscale wavelet transform (monodimensional version). /** \param axis Axis considered for the transform. \param invert Set inverse of direct transform. \param nb_scales Number of scales used for the transform. **/ CImg& haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) { return get_haar(axis,invert,nb_scales).transfer_to(*this); } CImg get_haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) const { if (is_empty() || !nb_scales) return *this; CImg res; if (nb_scales==1) { switch (cimg::uncase(axis)) { // Single scale transform case 'x' : { const unsigned int w = width/2; if (w) { if (w%2) throw CImgInstanceException("CImg<%s>::haar() : Sub-image width = %u is not even at a particular scale (=%u).", pixel_type(),w); res.assign(width,height,depth,dim); if (invert) cimg_forYZV(*this,y,z,v) { // Inverse transform along X for (unsigned int x=0, xw=w, x2=0; x::haar() : Sub-image height = %u is not even at a particular scale.", pixel_type(),h); res.assign(width,height,depth,dim); if (invert) cimg_forXZV(*this,x,z,v) { // Inverse transform along Y for (unsigned int y=0, yh=h, y2=0; y::haar() : Sub-image depth = %u is not even at a particular scale.", pixel_type(),d); res.assign(width,height,depth,dim); if (invert) cimg_forXYV(*this,x,y,v) { // Inverse transform along Z for (unsigned int z=0, zd=d, z2=0; z::haar() : Invalid axis '%c', must be 'x','y' or 'z'.", pixel_type(),axis); } } else { // Multi-scale version if (invert) { res.assign(*this); switch (cimg::uncase(axis)) { case 'x' : { unsigned int w = width; for (unsigned int s=1; w && s::haar() : Invalid axis '%c', must be 'x','y' or 'z'.", pixel_type(),axis); } } else { // Direct transform res = get_haar(axis,false,1); switch (cimg::uncase(axis)) { case 'x' : { for (unsigned int s=1, w=width/2; w && s::haar() : Invalid axis '%c', must be 'x','y' or 'z'.", pixel_type(),axis); } } } return res; } //! Compute the Haar multiscale wavelet transform. /** \param invert Set inverse of direct transform. \param nb_scales Number of scales used for the transform. **/ CImg& haar(const bool invert=false, const unsigned int nb_scales=1) { return get_haar(invert,nb_scales).transfer_to(*this); } CImg get_haar(const bool invert=false, const unsigned int nb_scales=1) const { CImg res; if (nb_scales==1) { // Single scale transform if (width>1) get_haar('x',invert,1).transfer_to(res); if (height>1) { if (res) res.get_haar('y',invert,1).transfer_to(res); else get_haar('y',invert,1).transfer_to(res); } if (depth>1) { if (res) res.get_haar('z',invert,1).transfer_to(res); else get_haar('z',invert,1).transfer_to(res); } if (res) return res; } else { // Multi-scale transform if (invert) { // Inverse transform res.assign(*this); if (width>1) { if (height>1) { if (depth>1) { unsigned int w = width, h = height, d = depth; for (unsigned int s=1; w && h && d && s1) { unsigned int w = width, d = depth; for (unsigned int s=1; w && d && s1) { if (depth>1) { unsigned int h = height, d = depth; for (unsigned int s=1; h && d && s1) { unsigned int d = depth; for (unsigned int s=1; d && s1) { if (height>1) { if (depth>1) for (unsigned int s=1, w=width/2, h=height/2, d=depth/2; w && h && d && s1) for (unsigned int s=1, w=width/2, d=depth/2; w && d && s1) { if (depth>1) for (unsigned int s=1, h=height/2, d=depth/2; h && d && s1) for (unsigned int s=1, d=depth/2; d && s& displacement_field(const CImg& target, const float smooth=0.1f, const float precision=0.1f, const unsigned int nb_scales=0, const unsigned int itermax=10000) { return get_displacement_field(target,smooth,precision,nb_scales,itermax).transfer_to(*this); } CImg get_displacement_field(const CImg& target, const float smoothness=0.1f, const float precision=0.1f, const unsigned int nb_scales=0, const unsigned int itermax=10000) const { if (is_empty() || !target) return *this; if (!is_sameXYZV(target)) throw CImgArgumentException("CImg<%s>::displacement_field() : Instance image (%u,%u,%u,%u,%p) and target image (%u,%u,%u,%u,%p) " "have different size.", pixel_type(),width,height,depth,dim,data, target.width,target.height,target.depth,target.dim,target.data); if (smoothness<0) throw CImgArgumentException("CImg<%s>::displacement_field() : Smoothness parameter %g is negative.", pixel_type(),smoothness); if (precision<0) throw CImgArgumentException("CImg<%s>::displacement_field() : Precision parameter %g is negative.", pixel_type(),precision); const unsigned int nscales = nb_scales>0?nb_scales:(unsigned int)(2*cimg_std::log((double)(cimg::max(width,height,depth)))); Tfloat m1, M1 = (Tfloat)maxmin(m1), m2, M2 = (Tfloat)target.maxmin(m2); const Tfloat factor = cimg::max(cimg::abs(m1),cimg::abs(M1),cimg::abs(m2),cimg::abs(M2)); CImg U0; const bool threed = (depth>1); // Begin multi-scale motion estimation for (int scale = (int)nscales-1; scale>=0; --scale) { const float sfactor = (float)cimg_std::pow(1.5f,(float)scale), sprecision = (float)(precision/cimg_std::pow(2.25,1+scale)); const int sw = (int)(width/sfactor), sh = (int)(height/sfactor), sd = (int)(depth/sfactor), swidth = sw?sw:1, sheight = sh?sh:1, sdepth = sd?sd:1; CImg I1 = get_resize(swidth,sheight,sdepth,-100,2), I2 = target.get_resize(swidth,sheight,sdepth,-100,2); I1/=factor; I2/=factor; CImg U; if (U0) U = (U0*=1.5f).get_resize(I1.dimx(),I1.dimy(),I1.dimz(),-100,3); else U.assign(I1.dimx(),I1.dimy(),I1.dimz(),threed?3:2,0); // Begin single-scale motion estimation CImg veloc(U); float dt = 2, Energy = cimg::type::max(); const CImgList dI = I2.get_gradient(); for (unsigned int iter=0; iter vector(const T& a0) { static CImg r(1,1); r[0] = a0; return r; } //! Return a vector with specified coefficients. static CImg vector(const T& a0, const T& a1) { static CImg r(1,2); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; return r; } //! Return a vector with specified coefficients. static CImg vector(const T& a0, const T& a1, const T& a2) { static CImg r(1,3); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; return r; } //! Return a vector with specified coefficients. static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3) { static CImg r(1,4); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; return r; } //! Return a vector with specified coefficients. static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) { static CImg r(1,5); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; return r; } //! Return a vector with specified coefficients. static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) { static CImg r(1,6); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; return r; } //! Return a vector with specified coefficients. static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5, const T& a6) { static CImg r(1,7); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; return r; } //! Return a vector with specified coefficients. static CImg 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) { static CImg r(1,8); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; return r; } //! Return a vector with specified coefficients. static CImg 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) { static CImg r(1,9); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; return r; } //! Return a vector with specified coefficients. static CImg 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) { static CImg r(1,10); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9; return r; } //! Return a vector with specified coefficients. static CImg 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) { static CImg r(1,11); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; return r; } //! Return a vector with specified coefficients. static CImg 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) { static CImg r(1,12); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; return r; } //! Return a vector with specified coefficients. static CImg 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) { static CImg r(1,13); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; return r; } //! Return a vector with specified coefficients. static CImg 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) { static CImg r(1,14); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; return r; } //! Return a vector with specified coefficients. static CImg 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) { static CImg r(1,15); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; return r; } //! Return a vector with specified coefficients. static CImg 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) { static CImg r(1,16); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15; return r; } //! Return a 1x1 square matrix with specified coefficients. static CImg matrix(const T& a0) { return vector(a0); } //! Return a 2x2 square matrix with specified coefficients. static CImg matrix(const T& a0, const T& a1, const T& a2, const T& a3) { static CImg r(2,2); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; return r; } //! Return a 3x3 square matrix with specified coefficients. static CImg 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) { static CImg r(3,3); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; return r; } //! Return a 4x4 square matrix with specified coefficients. static CImg 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) { static CImg r(4,4); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15; return r; } //! Return a 5x5 square matrix with specified coefficients. static CImg 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) { static CImg r(5,5); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15; *(ptr++) = a16; *(ptr++) = a17; *(ptr++) = a18; *(ptr++) = a19; *(ptr++) = a20; *(ptr++) = a21; *(ptr++) = a22; *(ptr++) = a23; *(ptr++) = a24; return r; } //! Return a 1x1 symmetric matrix with specified coefficients. static CImg tensor(const T& a1) { return matrix(a1); } //! Return a 2x2 symmetric matrix tensor with specified coefficients. static CImg tensor(const T& a1, const T& a2, const T& a3) { return matrix(a1,a2,a2,a3); } //! Return a 3x3 symmetric matrix with specified coefficients. static CImg tensor(const T& a1, const T& a2, const T& a3, const T& a4, const T& a5, const T& a6) { return matrix(a1,a2,a3,a2,a4,a5,a3,a5,a6); } //! Return a 1x1 diagonal matrix with specified coefficients. static CImg diagonal(const T& a0) { return matrix(a0); } //! Return a 2x2 diagonal matrix with specified coefficients. static CImg diagonal(const T& a0, const T& a1) { return matrix(a0,0,0,a1); } //! Return a 3x3 diagonal matrix with specified coefficients. static CImg diagonal(const T& a0, const T& a1, const T& a2) { return matrix(a0,0,0,0,a1,0,0,0,a2); } //! Return a 4x4 diagonal matrix with specified coefficients. static CImg diagonal(const T& a0, const T& a1, const T& a2, const T& a3) { return matrix(a0,0,0,0,0,a1,0,0,0,0,a2,0,0,0,0,a3); } //! Return a 5x5 diagonal matrix with specified coefficients. static CImg diagonal(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) { 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); } //! Return a NxN identity matrix. static CImg identity_matrix(const unsigned int N) { CImg res(N,N,1,1,0); cimg_forX(res,x) res(x,x) = 1; return res; } //! Return a N-numbered sequence vector from \p a0 to \p a1. static CImg sequence(const unsigned int N, const T a0, const T a1) { if (N) return CImg(1,N).sequence(a0,a1); return CImg(); } //! Return a 3x3 rotation matrix along the (x,y,z)-axis with an angle w. static CImg rotation_matrix(const float x, const float y, const float z, const float w, const bool quaternion_data=false) { float X,Y,Z,W; if (!quaternion_data) { const float norm = (float)cimg_std::sqrt(x*x + y*y + z*z), nx = norm>0?x/norm:0, ny = norm>0?y/norm:0, nz = norm>0?z/norm:1, nw = norm>0?w:0, sina = (float)cimg_std::sin(nw/2), cosa = (float)cimg_std::cos(nw/2); X = nx*sina; Y = ny*sina; Z = nz*sina; W = cosa; } else { const float norm = (float)cimg_std::sqrt(x*x + y*y + z*z + w*w); if (norm>0) { X = x/norm; Y = y/norm; Z = z/norm; W = w/norm; } else { X = Y = Z = 0; W = 1; } } 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; return CImg::matrix((T)(1-2*(yy+zz)), (T)(2*(xy+zw)), (T)(2*(xz-yw)), (T)(2*(xy-zw)), (T)(1-2*(xx+zz)), (T)(2*(yz+xw)), (T)(2*(xz+yw)), (T)(2*(yz-xw)), (T)(1-2*(xx+yy))); } //! Return a new image corresponding to the vector located at (\p x,\p y,\p z) of the current vector-valued image. CImg get_vector_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const { static CImg dest; if (dest.height!=dim) dest.assign(1,dim); const unsigned int whz = width*height*depth; const T *ptrs = ptr(x,y,z); T *ptrd = dest.data; cimg_forV(*this,k) { *(ptrd++) = *ptrs; ptrs+=whz; } return dest; } //! Set the image \p vec as the \a vector \a valued pixel located at (\p x,\p y,\p z) of the current vector-valued image. template CImg& set_vector_at(const CImg& vec, const unsigned int x, const unsigned int y=0, const unsigned int z=0) { if (x get_matrix_at(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const { const int n = (int)cimg_std::sqrt((double)dim); CImg dest(n,n); cimg_forV(*this,k) dest[k]=(*this)(x,y,z,k); return dest; } //! Set the image \p vec as the \a square \a matrix-valued pixel located at (\p x,\p y,\p z) of the current vector-valued image. template CImg& set_matrix_at(const CImg& mat, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) { return set_vector_at(mat,x,y,z); } //! Return a new image corresponding to the \a diffusion \a tensor located at (\p x,\p y,\p z) of the current vector-valued image. CImg get_tensor_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const { if (dim==6) return tensor((*this)(x,y,z,0),(*this)(x,y,z,1),(*this)(x,y,z,2), (*this)(x,y,z,3),(*this)(x,y,z,4),(*this)(x,y,z,5)); if (dim==3) return tensor((*this)(x,y,z,0),(*this)(x,y,z,1),(*this)(x,y,z,2)); return tensor((*this)(x,y,z,0)); } //! Set the image \p vec as the \a tensor \a valued pixel located at (\p x,\p y,\p z) of the current vector-valued image. template CImg& set_tensor_at(const CImg& ten, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) { if (ten.height==2) { (*this)(x,y,z,0) = (T)ten[0]; (*this)(x,y,z,1) = (T)ten[1]; (*this)(x,y,z,2) = (T)ten[3]; } else { (*this)(x,y,z,0) = (T)ten[0]; (*this)(x,y,z,1) = (T)ten[1]; (*this)(x,y,z,2) = (T)ten[2]; (*this)(x,y,z,3) = (T)ten[4]; (*this)(x,y,z,4) = (T)ten[5]; (*this)(x,y,z,5) = (T)ten[8]; } return *this; } //! Unroll all images values into a one-column vector. CImg& vector() { return unroll('y'); } CImg get_vector() const { return get_unroll('y'); } //! Realign pixel values of the instance image as a square matrix CImg& matrix() { const unsigned int siz = size(); switch (siz) { case 1 : break; case 4 : width = height = 2; break; case 9 : width = height = 3; break; case 16 : width = height = 4; break; case 25 : width = height = 5; break; case 36 : width = height = 6; break; case 49 : width = height = 7; break; case 64 : width = height = 8; break; case 81 : width = height = 9; break; case 100 : width = height = 10; break; default : { unsigned int i = 11, i2 = i*i; while (i2::matrix() : Image size = %u is not a square number", pixel_type(),siz); } } return *this; } CImg get_matrix() const { return (+*this).matrix(); } //! Realign pixel values of the instance image as a symmetric tensor. CImg& tensor() { return get_tensor().transfer_to(*this); } CImg get_tensor() const { CImg res; const unsigned int siz = size(); switch (siz) { case 1 : break; case 3 : res.assign(2,2); res(0,0) = (*this)(0); res(1,0) = res(0,1) = (*this)(1); res(1,1) = (*this)(2); break; case 6 : res.assign(3,3); res(0,0) = (*this)(0); res(1,0) = res(0,1) = (*this)(1); res(2,0) = res(0,2) = (*this)(2); res(1,1) = (*this)(3); res(2,1) = res(1,2) = (*this)(4); res(2,2) = (*this)(5); break; default : throw CImgInstanceException("CImg<%s>::tensor() : Wrong vector dimension = %u in instance image.", pixel_type(), dim); } return res; } //! Unroll all images values into specified axis. CImg& unroll(const char axis) { const unsigned int siz = size(); if (siz) switch (axis) { case 'x' : width = siz; height=depth=dim=1; break; case 'y' : height = siz; width=depth=dim=1; break; case 'z' : depth = siz; width=height=dim=1; break; case 'v' : dim = siz; width=height=depth=1; break; default : throw CImgArgumentException("CImg<%s>::unroll() : Given axis is '%c' which is not 'x','y','z' or 'v'", pixel_type(),axis); } return *this; } CImg get_unroll(const char axis) const { return (+*this).unroll(axis); } //! Get a diagonal matrix, whose diagonal coefficients are the coefficients of the input image. CImg& diagonal() { return get_diagonal().transfer_to(*this); } CImg get_diagonal() const { if (is_empty()) return *this; CImg res(size(),size(),1,1,0); cimg_foroff(*this,off) res(off,off) = (*this)(off); return res; } //! Get an identity matrix having same dimension than instance image. CImg& identity_matrix() { return identity_matrix(cimg::max(width,height)).transfer_to(*this); } CImg get_identity_matrix() const { return identity_matrix(cimg::max(width,height)); } //! Return a N-numbered sequence vector from \p a0 to \p a1. CImg& sequence(const T a0, const T a1) { if (is_empty()) return *this; const unsigned int siz = size() - 1; T* ptr = data; if (siz) { const Tfloat delta = (Tfloat)a1 - a0; cimg_foroff(*this,l) *(ptr++) = (T)(a0 + delta*l/siz); } else *ptr = a0; return *this; } CImg get_sequence(const T a0, const T a1) const { return (+*this).sequence(a0,a1); } //! Transpose the current matrix. CImg& transpose() { if (width==1) { width=height; height=1; return *this; } if (height==1) { height=width; width=1; return *this; } if (width==height) { cimg_forYZV(*this,y,z,v) for (int x=y; x get_transpose() const { return get_permute_axes("yxzv"); } //! Invert the current matrix. CImg& invert(const bool use_LU=true) { if (!is_empty()) { if (width!=height || depth!=1 || dim!=1) throw CImgInstanceException("CImg<%s>::invert() : Instance matrix (%u,%u,%u,%u,%p) is not square.", pixel_type(),width,height,depth,dim,data); #ifdef cimg_use_lapack int INFO = (int)use_LU, N = width, LWORK = 4*N, *IPIV = new int[N]; Tfloat *lapA = new Tfloat[N*N], *WORK = new Tfloat[LWORK]; cimg_forXY(*this,k,l) lapA[k*N+l] = (Tfloat)((*this)(k,l)); cimg::getrf(N,lapA,IPIV,INFO); if (INFO) cimg::warn("CImg<%s>::invert() : LAPACK library function dgetrf_() returned error code %d.", pixel_type(),INFO); else { cimg::getri(N,lapA,IPIV,WORK,LWORK,INFO); if (INFO) cimg::warn("CImg<%s>::invert() : LAPACK library function dgetri_() returned Error code %d", pixel_type(),INFO); } if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)(lapA[k*N+l]); else fill(0); delete[] IPIV; delete[] lapA; delete[] WORK; #else const double dete = width>3?-1.0:det(); if (dete!=0.0 && width==2) { const double a = data[0], c = data[1], b = data[2], d = data[3]; data[0] = (T)(d/dete); data[1] = (T)(-c/dete); data[2] = (T)(-b/dete); data[3] = (T)(a/dete); } else if (dete!=0.0 && width==3) { const double a = data[0], d = data[1], g = data[2], b = data[3], e = data[4], h = data[5], c = data[6], f = data[7], i = data[8]; data[0] = (T)((i*e-f*h)/dete), data[1] = (T)((g*f-i*d)/dete), data[2] = (T)((d*h-g*e)/dete); data[3] = (T)((h*c-i*b)/dete), data[4] = (T)((i*a-c*g)/dete), data[5] = (T)((g*b-a*h)/dete); data[6] = (T)((b*f-e*c)/dete), data[7] = (T)((d*c-a*f)/dete), data[8] = (T)((a*e-d*b)/dete); } else { if (use_LU) { // LU-based inverse computation CImg A(*this), indx, col(1,width); bool d; A._LU(indx,d); cimg_forX(*this,j) { col.fill(0); col(j) = 1; col._solve(A,indx); cimg_forX(*this,i) (*this)(j,i) = (T)col(i); } } else { // SVD-based inverse computation CImg U(width,width), S(1,width), V(width,width); SVD(U,S,V,false); U.transpose(); cimg_forY(S,k) if (S[k]!=0) S[k]=1/S[k]; S.diagonal(); *this = V*S*U; } } #endif } return *this; } CImg get_invert(const bool use_LU=true) const { return CImg(*this,false).invert(use_LU); } //! Compute the pseudo-inverse (Moore-Penrose) of the matrix. CImg& pseudoinvert() { return get_pseudoinvert().transfer_to(*this); } CImg get_pseudoinvert() const { CImg U, S, V; SVD(U,S,V); cimg_forX(V,x) { const Tfloat s = S(x), invs = s!=0?1/s:(Tfloat)0; cimg_forY(V,y) V(x,y)*=invs; } return V*U.transpose(); } //! Compute the cross product between two 3d vectors. template CImg& cross(const CImg& img) { if (width!=1 || height<3 || img.width!=1 || img.height<3) throw CImgInstanceException("CImg<%s>::cross() : Arguments (%u,%u,%u,%u,%p) and (%u,%u,%u,%u,%p) must be both 3d vectors.", pixel_type(),width,height,depth,dim,data,img.width,img.height,img.depth,img.dim,img.data); const T x = (*this)[0], y = (*this)[1], z = (*this)[2]; (*this)[0] = (T)(y*img[2]-z*img[1]); (*this)[1] = (T)(z*img[0]-x*img[2]); (*this)[2] = (T)(x*img[1]-y*img[0]); return *this; } template CImg::type> get_cross(const CImg& img) const { typedef typename cimg::superset::type Tt; return CImg(*this).cross(img); } //! Solve a linear system AX=B where B=*this. template CImg& solve(const CImg& A) { if (width!=1 || depth!=1 || dim!=1 || height!=A.height || A.depth!=1 || A.dim!=1) throw CImgArgumentException("CImg<%s>::solve() : Instance matrix size is (%u,%u,%u,%u) while " "size of given matrix A is (%u,%u,%u,%u).", pixel_type(),width,height,depth,dim,A.width,A.height,A.depth,A.dim); typedef typename cimg::superset2::type Ttfloat; if (A.width==A.height) { #ifdef cimg_use_lapack char TRANS='N'; int INFO, N = height, LWORK = 4*N, one = 1, *IPIV = new int[N]; Ttfloat *lapA = new Ttfloat[N*N], *lapB = new Ttfloat[N], *WORK = new Ttfloat[LWORK]; cimg_forXY(A,k,l) lapA[k*N+l] = (Ttfloat)(A(k,l)); cimg_forY(*this,i) lapB[i] = (Ttfloat)((*this)(i)); cimg::getrf(N,lapA,IPIV,INFO); if (INFO) cimg::warn("CImg<%s>::solve() : LAPACK library function dgetrf_() returned error code %d.", pixel_type(),INFO); if (!INFO) { cimg::getrs(TRANS,N,lapA,IPIV,lapB,INFO); if (INFO) cimg::warn("CImg<%s>::solve() : LAPACK library function dgetrs_() returned Error code %d", pixel_type(),INFO); } if (!INFO) cimg_forY(*this,i) (*this)(i) = (T)(lapB[i]); else fill(0); delete[] IPIV; delete[] lapA; delete[] lapB; delete[] WORK; #else CImg lu(A); CImg indx; bool d; lu._LU(indx,d); _solve(lu,indx); #endif } else assign(A.get_pseudoinvert()*(*this)); return *this; } template CImg::type> get_solve(const CImg& A) const { typedef typename cimg::superset2::type Ttfloat; return CImg(*this,false).solve(A); } template CImg& _solve(const CImg& A, const CImg& indx) { typedef typename cimg::superset2::type Ttfloat; const int N = size(); int ii = -1; Ttfloat sum; for (int i=0; i=0) for (int j=ii; j<=i-1; ++j) sum-=A(j,i)*(*this)(j); else if (sum!=0) ii=i; (*this)(i) = (T)sum; } { for (int i=N-1; i>=0; --i) { sum = (*this)(i); for (int j=i+1; j CImg& solve_tridiagonal(const CImg& a, const CImg& b, const CImg& c) { const int siz = (int)size(); if ((int)a.size()!=siz || (int)b.size()!=siz || (int)c.size()!=siz) throw CImgArgumentException("CImg<%s>::solve_tridiagonal() : arrays of triagonal coefficients have different size.",pixel_type); typedef typename cimg::superset2::type Ttfloat; CImg nc(siz); const T *ptra = a.data, *ptrb = b.data, *ptrc = c.data; T *ptrnc = nc.data, *ptrd = data; const Ttfloat valb0 = (Ttfloat)*(ptrb++); *ptrnc = *(ptrc++)/valb0; Ttfloat vald = (Ttfloat)(*(ptrd++)/=valb0); for (int i = 1; i=0; --i) vald = (*(--ptrd)-=*(--ptrnc)*vald); return *this; } template CImg::type> get_solve_tridiagonal(const CImg& a, const CImg& b, const CImg& c) const { typedef typename cimg::superset2::type Ttfloat; return CImg(*this,false).solve_tridiagonal(a,b,c); } //! Sort values of a vector and get permutations. template CImg& sort(CImg& permutations, const bool increasing=true) { if (is_empty()) permutations.assign(); else { if (permutations.size()!=size()) permutations.assign(size()); cimg_foroff(permutations,off) permutations[off] = (t)off; _quicksort(0,size()-1,permutations,increasing); } return *this; } template CImg get_sort(CImg& permutations, const bool increasing=true) const { return (+*this).sort(permutations,increasing); } // Sort image values. CImg& sort(const bool increasing=true) { CImg foo; return sort(foo,increasing); } CImg get_sort(const bool increasing=true) const { return (+*this).sort(increasing); } template CImg& _quicksort(const int min, const int max, CImg& permutations, const bool increasing) { if (min(*this)[mid]) { cimg::swap((*this)[min],(*this)[mid]); cimg::swap(permutations[min],permutations[mid]); } if ((*this)[mid]>(*this)[max]) { cimg::swap((*this)[max],(*this)[mid]); cimg::swap(permutations[max],permutations[mid]); } if ((*this)[min]>(*this)[mid]) { cimg::swap((*this)[min],(*this)[mid]); cimg::swap(permutations[min],permutations[mid]); } } else { if ((*this)[min]<(*this)[mid]) { cimg::swap((*this)[min],(*this)[mid]); cimg::swap(permutations[min],permutations[mid]); } if ((*this)[mid]<(*this)[max]) { cimg::swap((*this)[max],(*this)[mid]); cimg::swap(permutations[max],permutations[mid]); } if ((*this)[min]<(*this)[mid]) { cimg::swap((*this)[min],(*this)[mid]); cimg::swap(permutations[min],permutations[mid]); } } if (max-min>=3) { const T pivot = (*this)[mid]; int i = min, j = max; if (increasing) { do { while ((*this)[i]pivot) --j; if (i<=j) { cimg::swap((*this)[i],(*this)[j]); cimg::swap(permutations[i++],permutations[j--]); } } while (i<=j); } else { do { while ((*this)[i]>pivot) ++i; while ((*this)[j] CImg& permute(const CImg& permutation) { return get_permute(permutation).transfer_to(*this); } template CImg get_permute(const CImg& permutation) const { if (permutation.size()!=size()) throw CImgArgumentException("CImg<%s>::permute() : Instance image (%u,%u,%u,%u,%p) and permutation (%u,%u,%u,%u,%p)" "have different sizes.", pixel_type(),width,height,depth,dim,data, permutation.width,permutation.height,permutation.depth,permutation.dim,permutation.data); CImg res(width,height,depth,dim); const t *p = permutation.ptr(permutation.size()); cimg_for(res,ptr,T) *ptr = (*this)[*(--p)]; return res; } //! Compute the SVD of a general matrix. template const CImg& SVD(CImg& U, CImg& S, CImg& V, const bool sorting=true, const unsigned int max_iter=40, const float lambda=0) const { if (is_empty()) { U.assign(); S.assign(); V.assign(); } else { U = *this; if (lambda!=0) { const unsigned int delta = cimg::min(U.width,U.height); for (unsigned int i=0; i rv1(width); t anorm = 0, c, f, g = 0, h, s, scale = 0; int l = 0, nm = 0; cimg_forX(U,i) { l = i+1; rv1[i] = scale*g; g = s = scale = 0; if (i=0?-1:1)*cimg_std::sqrt(s)); h=f*g-s; U(i,i) = f-g; for (int j=l; j=0?-1:1)*cimg_std::sqrt(s)); h = f*g-s; U(l,i) = f-g; { for (int k=l; k=0; --i) { if (i=0; --i) { l = i+1; g = S[i]; for (int j=l; j=0; --k) { for (unsigned int its=0; its=1; --l) { nm = l-1; if ((cimg::abs(rv1[l])+anorm)==anorm) { flag = false; break; } if ((cimg::abs(S[nm])+anorm)==anorm) break; } if (flag) { c = 0; s = 1; for (int i=l; i<=k; ++i) { f = s*rv1[i]; rv1[i] = c*rv1[i]; if ((cimg::abs(f)+anorm)==anorm) break; g = S[i]; h = (t)cimg::_pythagore(f,g); S[i] = h; h = 1/h; c = g*h; s = -f*h; 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; } } } const t z = S[k]; if (l==k) { if (z<0) { S[k] = -z; cimg_forX(U,j) V(k,j) = -V(k,j); } break; } nm = k-1; t x = S[l], y = S[nm]; g = rv1[nm]; h = rv1[k]; f = ((y-z)*(y+z)+(g-h)*(g+h))/(2*h*y); g = (t)cimg::_pythagore(f,1.0); f = ((x-z)*(x+z)+h*((y/(f+ (f>=0?g:-g)))-h))/x; c = s = 1; for (int j=l; j<=nm; ++j) { const int i = j+1; g = rv1[i]; h = s*g; g = c*g; t y = S[i]; t z = (t)cimg::_pythagore(f,h); rv1[j] = z; c = f/z; s = h/z; f = x*c+g*s; g = g*c-x*s; h = y*s; y*=c; 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; } z = (t)cimg::_pythagore(f,h); S[j] = z; if (z) { z = 1/z; c = f*z; s = h*z; } f = c*g+s*y; x = c*y-s*g; { 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; }} } rv1[l] = 0; rv1[k]=f; S[k]=x; } } if (sorting) { CImg permutations(width); CImg tmp(width); S.sort(permutations,false); cimg_forY(U,k) { cimg_forX(permutations,x) tmp(x) = U(permutations(x),k); cimg_std::memcpy(U.ptr(0,k),tmp.data,sizeof(t)*width); } { cimg_forY(V,k) { cimg_forX(permutations,x) tmp(x) = V(permutations(x),k); cimg_std::memcpy(V.ptr(0,k),tmp.data,sizeof(t)*width); }} } } return *this; } //! Compute the SVD of a general matrix. template const CImg& SVD(CImgList& USV) const { if (USV.size<3) USV.assign(3); return SVD(USV[0],USV[1],USV[2]); } //! Compute the SVD of a general matrix. CImgList get_SVD(const bool sorting=true) const { CImgList res(3); SVD(res[0],res[1],res[2],sorting); return res; } // INNER ROUTINE : Compute the LU decomposition of a permuted matrix (c.f. numerical recipies) template CImg& _LU(CImg& indx, bool& d) { const int N = dimx(); int imax = 0; CImg vv(N); indx.assign(N); d = true; cimg_forX(*this,i) { Tfloat vmax = 0; cimg_forX(*this,j) { const Tfloat tmp = cimg::abs((*this)(j,i)); if (tmp>vmax) vmax = tmp; } if (vmax==0) { indx.fill(0); return fill(0); } vv[i] = 1/vmax; } cimg_forX(*this,j) { for (int i=0; i=vmax) { vmax=tmp; imax=i; } }} if (j!=imax) { cimg_forX(*this,k) cimg::swap((*this)(k,imax),(*this)(k,j)); d =!d; vv[imax] = vv[j]; } indx[j] = (t)imax; if ((*this)(j,j)==0) (*this)(j,j) = (T)1e-20; if (j const CImg& eigen(CImg& val, CImg &vec) const { if (is_empty()) { val.assign(); vec.assign(); } else { if (width!=height || depth>1 || dim>1) throw CImgInstanceException("CImg<%s>::eigen() : Instance object (%u,%u,%u,%u,%p) is empty.", pixel_type(),width,height,depth,dim,data); if (val.size()::eigen() : Complex eigenvalues", pixel_type()); f = cimg_std::sqrt(f); const double l1 = 0.5*(e-f), l2 = 0.5*(e+f); const double theta1 = cimg_std::atan2(l2-a,b), theta2 = cimg_std::atan2(l1-a,b); val[0]=(t)l2; val[1]=(t)l1; vec(0,0) = (t)cimg_std::cos(theta1); vec(0,1) = (t)cimg_std::sin(theta1); vec(1,0) = (t)cimg_std::cos(theta2); vec(1,1) = (t)cimg_std::sin(theta2); } break; default : throw CImgInstanceException("CImg<%s>::eigen() : Eigenvalues computation of general matrices is limited" "to 2x2 matrices (given is %ux%u)", pixel_type(),width,height); } } return *this; } //! Compute the eigenvalues and eigenvectors of a matrix. CImgList get_eigen() const { CImgList res(2); eigen(res[0],res[1]); return res; } //! Compute the eigenvalues and eigenvectors of a symmetric matrix. template const CImg& symmetric_eigen(CImg& val, CImg& vec) const { if (is_empty()) { val.assign(); vec.assign(); } else { #ifdef cimg_use_lapack char JOB = 'V', UPLO = 'U'; int N = width, LWORK = 4*N, INFO; Tfloat *lapA = new Tfloat[N*N], *lapW = new Tfloat[N], *WORK = new Tfloat[LWORK]; cimg_forXY(*this,k,l) lapA[k*N+l] = (Tfloat)((*this)(k,l)); cimg::syev(JOB,UPLO,N,lapA,lapW,WORK,LWORK,INFO); if (INFO) cimg::warn("CImg<%s>::symmetric_eigen() : LAPACK library function dsyev_() returned error code %d.", pixel_type(),INFO); val.assign(1,N); vec.assign(N,N); if (!INFO) { cimg_forY(val,i) val(i) = (T)lapW[N-1-i]; cimg_forXY(vec,k,l) vec(k,l) = (T)(lapA[(N-1-k)*N+l]); } else { val.fill(0); vec.fill(0); } delete[] lapA; delete[] lapW; delete[] WORK; #else if (width!=height || depth>1 || dim>1) throw CImgInstanceException("CImg<%s>::eigen() : Instance object (%u,%u,%u,%u,%p) is empty.", pixel_type(),width,height,depth,dim,data); val.assign(1,width); if (vec.data) vec.assign(width,width); if (width<3) return eigen(val,vec); CImg V(width,width); SVD(vec,val,V,false); bool ambiguous = false; float eig = 0; cimg_forY(val,p) { // check for ambiguous cases. if (val[p]>eig) eig = (float)val[p]; t scal = 0; cimg_forY(vec,y) scal+=vec(p,y)*V(p,y); if (cimg::abs(scal)<0.9f) ambiguous = true; if (scal<0) val[p] = -val[p]; } if (ambiguous) { (eig*=2)++; SVD(vec,val,V,false,40,eig); val-=eig; } CImg permutations(width); // sort eigenvalues in decreasing order CImg tmp(width); val.sort(permutations,false); cimg_forY(vec,k) { cimg_forX(permutations,x) tmp(x) = vec(permutations(x),k); cimg_std::memcpy(vec.ptr(0,k),tmp.data,sizeof(t)*width); } #endif } return *this; } //! Compute the eigenvalues and eigenvectors of a symmetric matrix. CImgList get_symmetric_eigen() const { CImgList res(2); symmetric_eigen(res[0],res[1]); return res; } //@} //------------------- // //! \name Display //@{ //------------------- //! Display an image into a CImgDisplay window. const CImg& display(CImgDisplay& disp) const { disp.display(*this); return *this; } //! Display an image in a window with a title \p title, and wait a 'is_closed' or 'keyboard' event.\n const CImg& display(CImgDisplay &disp, const bool display_info) const { return _display(disp,0,display_info); } //! Display an image in a window with a title \p title, and wait a 'is_closed' or 'keyboard' event.\n const CImg& display(const char *const title=0, const bool display_info=true) const { CImgDisplay disp; return _display(disp,title,display_info); } const CImg& _display(CImgDisplay &disp, const char *const title, const bool display_info) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::display() : Instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),width,height,depth,dim,data); unsigned int oldw = 0, oldh = 0, XYZ[3], key = 0, mkey = 0; int x0 = 0, y0 = 0, z0 = 0, x1 = dimx()-1, y1 = dimy()-1, z1 = dimz()-1; float frametiming = 5; char ntitle[256] = { 0 }; if (!disp) { if (!title) cimg_std::sprintf(ntitle,"CImg<%s>",pixel_type()); disp.assign(cimg_fitscreen(width,height,depth),title?title:ntitle,1); } cimg_std::strncpy(ntitle,disp.title,255); if (display_info) print(ntitle); CImg zoom; for (bool reset_view = true, resize_disp = false; !key && !disp.is_closed; ) { if (reset_view) { XYZ[0] = (x0 + x1)/2; XYZ[1] = (y0 + y1)/2; XYZ[2] = (z0 + z1)/2; x0 = 0; y0 = 0; z0 = 0; x1 = width-1; y1 = height-1; z1 = depth-1; oldw = disp.width; oldh = disp.height; reset_view = false; } if (!x0 && !y0 && !z0 && x1==dimx()-1 && y1==dimy()-1 && z1==dimz()-1) zoom.assign(); else zoom = get_crop(x0,y0,z0,x1,y1,z1); const unsigned int dx = 1 + x1 - x0, dy = 1 + y1 - y0, dz = 1 + z1 - z0, tw = dx + (dz>1?dz:0), th = dy + (dz>1?dz:0); if (resize_disp) { const unsigned int ttw = tw*disp.width/oldw, tth = th*disp.height/oldh, dM = cimg::max(ttw,tth), diM = cimg::max(disp.width,disp.height), imgw = cimg::max(16U,ttw*diM/dM), imgh = cimg::max(16U,tth*diM/dM); disp.normalscreen().resize(cimg_fitscreen(imgw,imgh,1),false); resize_disp = false; } oldw = tw; oldh = th; bool go_up = false, go_down = false, go_left = false, go_right = false, go_inc = false, go_dec = false, go_in = false, go_out = false, go_in_center = false; const CImg& visu = zoom?zoom:*this; const CImg selection = visu._get_select(disp,0,2,XYZ,0,x0,y0,z0); if (disp.wheel) { if (disp.is_keyCTRLLEFT) { if (!mkey || mkey==1) go_out = !(go_in = disp.wheel>0); go_in_center = false; mkey = 1; } else if (disp.is_keySHIFTLEFT) { if (!mkey || mkey==2) go_right = !(go_left = disp.wheel>0); mkey = 2; } else if (disp.is_keyALT || depth==1) { if (!mkey || mkey==3) go_down = !(go_up = disp.wheel>0); mkey = 3; } else mkey = 0; disp.wheel = 0; } else mkey = 0; const int sx0 = selection(0), sy0 = selection(1), sz0 = selection(2), sx1 = selection(3), sy1 = selection(4), sz1 = selection(5); if (sx0>=0 && sy0>=0 && sz0>=0 && sx1>=0 && sy1>=0 && sz1>=0) { x1 = x0 + sx1; y1 = y0 + sy1; z1 = z0 + sz1; x0+=sx0; y0+=sy0; z0+=sz0; if (sx0==sx1 && sy0==sy1 && sz0==sz1) reset_view = true; resize_disp = true; } else switch (key = disp.key) { case 0 : case cimg::keyCTRLLEFT : case cimg::keyPAD5 : case cimg::keySHIFTLEFT : case cimg::keyALT : disp.key = key = 0; break; case cimg::keyP : if (visu.depth>1 && disp.is_keyCTRLLEFT) { // Special mode : play stack of frames const unsigned int w1 = visu.width*disp.width/(visu.width+(visu.depth>1?visu.depth:0)), h1 = visu.height*disp.height/(visu.height+(visu.depth>1?visu.depth:0)); disp.resize(cimg_fitscreen(w1,h1,1),false).key = disp.wheel = key = 0; for (unsigned int timer = 0; !key && !disp.is_closed && !disp.button; ) { if (disp.is_resized) disp.resize(); if (!timer) { visu.get_slice(XYZ[2]).display(disp.set_title("%s | z=%d",ntitle,XYZ[2])); if (++XYZ[2]>=visu.depth) XYZ[2] = 0; } if (++timer>(unsigned int)frametiming) timer = 0; if (disp.wheel) { frametiming-=disp.wheel/3.0f; disp.wheel = 0; } switch (key = disp.key) { case 0 : case cimg::keyCTRLLEFT : disp.key = key = 0; break; case cimg::keyPAGEUP : frametiming-=0.3f; key = 0; break; case cimg::keyPAGEDOWN : frametiming+=0.3f; key = 0; break; case cimg::keyD : if (disp.is_keyCTRLLEFT) { disp.normalscreen().resize(CImgDisplay::_fitscreen(3*disp.width/2,3*disp.height/2,1,128,-100,false), CImgDisplay::_fitscreen(3*disp.width/2,3*disp.height/2,1,128,-100,true),false); disp.key = key = 0; } break; case cimg::keyC : if (disp.is_keyCTRLLEFT) { disp.normalscreen().resize(cimg_fitscreen(2*disp.width/3,2*disp.height/3,1),false); disp.key = key = 0; } break; case cimg::keyR : if (disp.is_keyCTRLLEFT) { disp.normalscreen().resize(cimg_fitscreen(width,height,depth),false); disp.key = key = 0; } break; case cimg::keyF : if (disp.is_keyCTRLLEFT) { disp.resize(disp.screen_dimx(),disp.screen_dimy()).toggle_fullscreen(); disp.key = key = 0; } break; } frametiming = frametiming<1?1:(frametiming>39?39:frametiming); disp.wait(20); } const unsigned int w2 = (visu.width + (visu.depth>1?visu.depth:0))*disp.width/visu.width, h2 = (visu.height + (visu.depth>1?visu.depth:0))*disp.height/visu.height; disp.resize(cimg_fitscreen(w2,h2,1),false).set_title(ntitle); key = disp.key = disp.button = disp.wheel = 0; } break; case cimg::keyHOME : case cimg::keyBACKSPACE : reset_view = resize_disp = true; key = 0; break; case cimg::keyPADADD : go_in = true; go_in_center = true; key = 0; break; case cimg::keyPADSUB : go_out = true; key = 0; break; case cimg::keyARROWLEFT : case cimg::keyPAD4: go_left = true; key = 0; break; case cimg::keyARROWRIGHT : case cimg::keyPAD6: go_right = true; key = 0; break; case cimg::keyARROWUP : case cimg::keyPAD8: go_up = true; key = 0; break; case cimg::keyARROWDOWN : case cimg::keyPAD2: go_down = true; key = 0; break; case cimg::keyPAD7 : go_up = go_left = true; key = 0; break; case cimg::keyPAD9 : go_up = go_right = true; key = 0; break; case cimg::keyPAD1 : go_down = go_left = true; key = 0; break; case cimg::keyPAD3 : go_down = go_right = true; key = 0; break; case cimg::keyPAGEUP : go_inc = true; key = 0; break; case cimg::keyPAGEDOWN : go_dec = true; key = 0; break; } if (go_in) { const int mx = go_in_center?disp.dimx()/2:disp.mouse_x, my = go_in_center?disp.dimy()/2:disp.mouse_y, mX = mx*(width+(depth>1?depth:0))/disp.width, mY = my*(height+(depth>1?depth:0))/disp.height; int X = XYZ[0], Y = XYZ[1], Z = XYZ[2]; if (mX=dimy()) { X = x0 + mX*(1+x1-x0)/width; Z = z0 + (mY-height)*(1+z1-z0)/depth; Y = XYZ[1]; } if (mX>=dimx() && mY4) { x0 = X - 7*(X-x0)/8; x1 = X + 7*(x1-X)/8; } if (y1-y0>4) { y0 = Y - 7*(Y-y0)/8; y1 = Y + 7*(y1-Y)/8; } if (z1-z0>4) { z0 = Z - 7*(Z-z0)/8; z1 = Z + 7*(z1-Z)/8; } } if (go_out) { const int deltax = (x1-x0)/8, deltay = (y1-y0)/8, deltaz = (z1-z0)/8, ndeltax = deltax?deltax:(width>1?1:0), ndeltay = deltay?deltay:(height>1?1:0), ndeltaz = deltaz?deltaz:(depth>1?1:0); x0-=ndeltax; y0-=ndeltay; z0-=ndeltaz; x1+=ndeltax; y1+=ndeltay; z1+=ndeltaz; if (x0<0) { x1-=x0; x0 = 0; if (x1>=dimx()) x1 = dimx()-1; } if (y0<0) { y1-=y0; y0 = 0; if (y1>=dimy()) y1 = dimy()-1; } if (z0<0) { z1-=z0; z0 = 0; if (z1>=dimz()) z1 = dimz()-1; } if (x1>=dimx()) { x0-=(x1-dimx()+1); x1 = dimx()-1; if (x0<0) x0 = 0; } if (y1>=dimy()) { y0-=(y1-dimy()+1); y1 = dimy()-1; if (y0<0) y0 = 0; } if (z1>=dimz()) { z0-=(z1-dimz()+1); z1 = dimz()-1; if (z0<0) z0 = 0; } } if (go_left) { const int delta = (x1-x0)/5, ndelta = delta?delta:(width>1?1:0); if (x0-ndelta>=0) { x0-=ndelta; x1-=ndelta; } else { x1-=x0; x0 = 0; } } if (go_right) { const int delta = (x1-x0)/5, ndelta = delta?delta:(width>1?1:0); if (x1+ndelta1?1:0); if (y0-ndelta>=0) { y0-=ndelta; y1-=ndelta; } else { y1-=y0; y0 = 0; } } if (go_down) { const int delta = (y1-y0)/5, ndelta = delta?delta:(height>1?1:0); if (y1+ndelta1?1:0); if (z0-ndelta>=0) { z0-=ndelta; z1-=ndelta; } else { z1-=z0; z0 = 0; } } if (go_dec) { const int delta = (z1-z0)/5, ndelta = delta?delta:(depth>1?1:0); if (z1+ndelta& select(CImgDisplay &disp, const int select_type=2, unsigned int *const XYZ=0, const unsigned char *const color=0) { return get_select(disp,select_type,XYZ,color).transfer_to(*this); } //! Simple interface to select a shape from an image. CImg& select(const char *const title, const int select_type=2, unsigned int *const XYZ=0, const unsigned char *const color=0) { return get_select(title,select_type,XYZ,color).transfer_to(*this); } //! Simple interface to select a shape from an image. CImg get_select(CImgDisplay &disp, const int select_type=2, unsigned int *const XYZ=0, const unsigned char *const color=0) const { return _get_select(disp,0,select_type,XYZ,color,0,0,0); } //! Simple interface to select a shape from an image. CImg get_select(const char *const title, const int select_type=2, unsigned int *const XYZ=0, const unsigned char *const color=0) const { CImgDisplay disp; return _get_select(disp,title,select_type,XYZ,color,0,0,0); } CImg _get_select(CImgDisplay &disp, const char *const title, const int coords_type, unsigned int *const XYZ, const unsigned char *const color, const int origX, const int origY, const int origZ) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::select() : Instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),width,height,depth,dim,data); if (!disp) { char ntitle[64] = { 0 }; if (!title) { cimg_std::sprintf(ntitle,"CImg<%s>",pixel_type()); } disp.assign(cimg_fitscreen(width,height,depth),title?title:ntitle,1); } const unsigned int old_normalization = disp.normalization, hatch = 0x55555555; bool old_is_resized = disp.is_resized; disp.normalization = 0; disp.show().key = 0; unsigned char foreground_color[] = { 255,255,105 }, background_color[] = { 0,0,0 }; if (color) cimg_std::memcpy(foreground_color,color,sizeof(unsigned char)*cimg::min(3,dimv())); int area = 0, clicked_area = 0, phase = 0, X0 = (int)((XYZ?XYZ[0]:width/2)%width), Y0 = (int)((XYZ?XYZ[1]:height/2)%height), Z0 = (int)((XYZ?XYZ[2]:depth/2)%depth), X1 =-1, Y1 = -1, Z1 = -1, X = -1, Y = -1, Z = -1, oX = X, oY = Y, oZ = Z; unsigned int old_button = 0, key = 0; bool shape_selected = false, text_down = false; CImg visu, visu0; char text[1024] = { 0 }; while (!key && !disp.is_closed && !shape_selected) { // Handle mouse motion and selection oX = X; oY = Y; oZ = Z; int mx = disp.mouse_x, my = disp.mouse_y; const int mX = mx*(width+(depth>1?depth:0))/disp.width, mY = my*(height+(depth>1?depth:0))/disp.height; area = 0; if (mX=dimy()) { area = 2; X = mX; Z = mY-height; Y = phase?Y1:Y0; } if (mX>=dimx() && mY=2) { switch (clicked_area) { case 1 : Z1 = Z; break; case 2 : Y1 = Y; break; case 3 : X1 = X; break; } } if (disp.button&2) { if (phase) { X1 = X; Y1 = Y; Z1 = Z; } else { X0 = X; Y0 = Y; Z0 = Z; } } if (disp.button&4) { oX = X = X0; oY = Y = Y0; oZ = Z = Z0; phase = 0; visu.assign(); } if (disp.wheel) { if (depth>1 && !disp.is_keyCTRLLEFT && !disp.is_keySHIFTLEFT && !disp.is_keyALT) { switch (area) { case 1 : if (phase) Z = (Z1+=disp.wheel); else Z = (Z0+=disp.wheel); break; case 2 : if (phase) Y = (Y1+=disp.wheel); else Y = (Y0+=disp.wheel); break; case 3 : if (phase) X = (X1+=disp.wheel); else X = (X0+=disp.wheel); break; } disp.wheel = 0; } else key = ~0U; } if ((disp.button&1)!=old_button) { switch (phase++) { case 0 : X0 = X1 = X; Y0 = Y1 = Y; Z0 = Z1 = Z; clicked_area = area; break; case 1 : X1 = X; Y1 = Y; Z1 = Z; break; } old_button = disp.button&1; } if (depth>1 && (X!=oX || Y!=oY || Z!=oZ)) visu0.assign(); } if (phase) { if (!coords_type) shape_selected = phase?true:false; else { if (depth>1) shape_selected = (phase==3)?true:false; else shape_selected = (phase==2)?true:false; } } if (X0<0) X0 = 0; if (X0>=dimx()) X0 = dimx()-1; if (Y0<0) Y0 = 0; if (Y0>=dimy()) Y0 = dimy()-1; if (Z0<0) Z0 = 0; if (Z0>=dimz()) Z0 = dimz()-1; if (X1<1) X1 = 0; if (X1>=dimx()) X1 = dimx()-1; if (Y1<0) Y1 = 0; if (Y1>=dimy()) Y1 = dimy()-1; if (Z1<0) Z1 = 0; if (Z1>=dimz()) Z1 = dimz()-1; // Draw visualization image on the display if (oX!=X || oY!=Y || oZ!=Z || !visu0) { if (!visu0) { CImg tmp, tmp0; if (depth!=1) { tmp0 = (!phase)?get_projections2d(X0,Y0,Z0):get_projections2d(X1,Y1,Z1); tmp = tmp0.get_channels(0,cimg::min(2U,dim-1)); } else tmp = get_channels(0,cimg::min(2U,dim-1)); switch (old_normalization) { case 0 : visu0 = tmp; break; case 3 : if (cimg::type::is_float()) visu0 = tmp.normalize(0,(T)255); else { const float m = (float)cimg::type::min(), M = (float)cimg::type::max(); visu0.assign(tmp.width,tmp.height,1,tmp.dim); unsigned char *ptrd = visu0.end(); cimg_for(tmp,ptrs,Tuchar) *(--ptrd) = (unsigned char)((*ptrs-m)*255.0f/(M-m)); } break; default : visu0 = tmp.normalize(0,255); } visu0.resize(disp); } visu = visu0; if (!color) { if (visu.mean()<200) { foreground_color[0] = foreground_color[1] = foreground_color[2] = 255; background_color[0] = background_color[1] = background_color[2] = 0; } else { foreground_color[0] = foreground_color[1] = foreground_color[2] = 0; background_color[0] = background_color[1] = background_color[2] = 255; } } const int d = (depth>1)?depth:0; if (phase) switch (coords_type) { case 1 : { const int x0 = (int)((X0+0.5f)*disp.width/(width+d)), y0 = (int)((Y0+0.5f)*disp.height/(height+d)), x1 = (int)((X1+0.5f)*disp.width/(width+d)), y1 = (int)((Y1+0.5f)*disp.height/(height+d)); visu.draw_arrow(x0,y0,x1,y1,foreground_color,0.6f,30,5,hatch); if (d) { const int zx0 = (int)((width+Z0+0.5f)*disp.width/(width+d)), zx1 = (int)((width+Z1+0.5f)*disp.width/(width+d)), zy0 = (int)((height+Z0+0.5f)*disp.height/(height+d)), zy1 = (int)((height+Z1+0.5f)*disp.height/(height+d)); visu.draw_arrow(zx0,y0,zx1,y1,foreground_color,0.6f,30,5,hatch). draw_arrow(x0,zy0,x1,zy1,foreground_color,0.6f,30,5,hatch); } } break; case 2 : { const int x0 = (X0=4 && y1-y0>=4) visu.draw_rectangle(x0,y0,x1,y1,foreground_color,0.4f,~0U); } if (my<12) text_down = true; if (my>=visu.dimy()-11) text_down = false; if (!coords_type || !phase) { if (X>=0 && Y>=0 && Z>=0 && X1) cimg_std::sprintf(text,"Point (%d,%d,%d) = [ ",origX+X,origY+Y,origZ+Z); else cimg_std::sprintf(text,"Point (%d,%d) = [ ",origX+X,origY+Y); char *ctext = text + cimg::strlen(text), *const ltext = text + 512; for (unsigned int k=0; k::format(),cimg::type::format((*this)(X,Y,Z,k))); ctext = text + cimg::strlen(text); *(ctext++) = ' '; *ctext = '\0'; } cimg_std::sprintf(text + cimg::strlen(text),"]"); } } else switch (coords_type) { case 1 : { const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1), norm = cimg_std::sqrt(dX*dX+dY*dY+dZ*dZ); if (depth>1) cimg_std::sprintf(text,"Vect (%d,%d,%d)-(%d,%d,%d), Norm = %g", origX+X0,origY+Y0,origZ+Z0,origX+X1,origY+Y1,origZ+Z1,norm); else cimg_std::sprintf(text,"Vect (%d,%d)-(%d,%d), Norm = %g", origX+X0,origY+Y0,origX+X1,origY+Y1,norm); } break; case 2 : if (depth>1) cimg_std::sprintf(text,"Box (%d,%d,%d)-(%d,%d,%d), Size = (%d,%d,%d)", origX+(X01) cimg_std::sprintf(text,"Ellipse (%d,%d,%d)-(%d,%d,%d), Radii = (%d,%d,%d)", origX+X0,origY+Y0,origZ+Z0,origX+X1,origY+Y1,origZ+Z1, 1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1),1+cimg::abs(Z0-Z1)); else cimg_std::sprintf(text,"Ellipse (%d,%d)-(%d,%d), Radii = (%d,%d)", origX+X0,origY+Y0,origX+X1,origY+Y1,1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1)); } if (phase || (mx>=0 && my>=0)) visu.draw_text(0,text_down?visu.dimy()-11:0,text,foreground_color,background_color,0.7f,11); disp.display(visu).wait(25); } else if (!shape_selected) disp.wait(); if (disp.is_resized) { disp.resize(false); old_is_resized = true; disp.is_resized = false; visu0.assign(); } } // Return result CImg res(1,6,1,1,-1); if (XYZ) { XYZ[0] = (unsigned int)X0; XYZ[1] = (unsigned int)Y0; XYZ[2] = (unsigned int)Z0; } if (shape_selected) { if (coords_type==2) { if (X0>X1) cimg::swap(X0,X1); if (Y0>Y1) cimg::swap(Y0,Y1); if (Z0>Z1) cimg::swap(Z0,Z1); } if (X1<0 || Y1<0 || Z1<0) X0 = Y0 = Z0 = X1 = Y1 = Z1 = -1; switch (coords_type) { case 1 : case 2 : res[3] = X1; res[4] = Y1; res[5] = Z1; default : res[0] = X0; res[1] = Y0; res[2] = Z0; } } disp.button = 0; disp.normalization = old_normalization; disp.is_resized = old_is_resized; if (key!=~0U) disp.key = key; return res; } //! High-level interface for displaying a 3d object. template const CImg& display_object3d(CImgDisplay& disp, const CImg& points, const CImgList& primitives, const CImgList& colors, const to& opacities, const bool centering=true, const int render_static=4, const int render_motion=1, const bool double_sided=false, const float focale=500, const float specular_light=0.2f, const float specular_shine=0.1f, const bool display_axes=true, float *const pose_matrix=0) const { return _display_object3d(disp,0,points,points.width,primitives,colors,opacities,centering,render_static, render_motion,double_sided,focale,specular_light,specular_shine, display_axes,pose_matrix); } //! High-level interface for displaying a 3d object. template const CImg& display_object3d(const char *const title, const CImg& points, const CImgList& primitives, const CImgList& colors, const to& opacities, const bool centering=true, const int render_static=4, const int render_motion=1, const bool double_sided=false, const float focale=500, const float specular_light=0.2f, const float specular_shine=0.1f, const bool display_axes=true, float *const pose_matrix=0) const { CImgDisplay disp; return _display_object3d(disp,title,points,points.width,primitives,colors,opacities,centering,render_static, render_motion,double_sided,focale,specular_light,specular_shine, display_axes,pose_matrix); } //! High-level interface for displaying a 3d object. template const CImg& display_object3d(CImgDisplay& disp, const CImgList& points, const CImgList& primitives, const CImgList& colors, const to& opacities, const bool centering=true, const int render_static=4, const int render_motion=1, const bool double_sided=false, const float focale=500, const float specular_light=0.2f, const float specular_shine=0.1f, const bool display_axes=true, float *const pose_matrix=0) const { return _display_object3d(disp,0,points,points.size,primitives,colors,opacities,centering,render_static, render_motion,double_sided,focale,specular_light,specular_shine, display_axes,pose_matrix); } //! High-level interface for displaying a 3d object. template const CImg& display_object3d(const char *const title, const CImgList& points, const CImgList& primitives, const CImgList& colors, const to& opacities, const bool centering=true, const int render_static=4, const int render_motion=1, const bool double_sided=false, const float focale=500, const float specular_light=0.2f, const float specular_shine=0.1f, const bool display_axes=true, float *const pose_matrix=0) const { CImgDisplay disp; return _display_object3d(disp,title,points,points.size,primitives,colors,opacities,centering,render_static, render_motion,double_sided,focale,specular_light,specular_shine, display_axes,pose_matrix); } //! High-level interface for displaying a 3d object. template const CImg& display_object3d(CImgDisplay &disp, const tp& points, const CImgList& primitives, const CImgList& colors, const bool centering=true, const int render_static=4, const int render_motion=1, const bool double_sided=false, const float focale=500, const float specular_light=0.2f, const float specular_shine=0.1f, const bool display_axes=true, float *const pose_matrix=0) const { return display_object3d(disp,points,primitives,colors,CImg(),centering, render_static,render_motion,double_sided,focale,specular_light,specular_shine, display_axes,pose_matrix); } //! High-level interface for displaying a 3d object. template const CImg& display_object3d(const char *const title, const tp& points, const CImgList& primitives, const CImgList& colors, const bool centering=true, const int render_static=4, const int render_motion=1, const bool double_sided=false, const float focale=500, const float specular_light=0.2f, const float specular_shine=0.1f, const bool display_axes=true, float *const pose_matrix=0) const { return display_object3d(title,points,primitives,colors,CImg(),centering, render_static,render_motion,double_sided,focale,specular_light,specular_shine, display_axes,pose_matrix); } //! High-level interface for displaying a 3d object. template const CImg& display_object3d(CImgDisplay &disp, const tp& points, const CImgList& primitives, const bool centering=true, const int render_static=4, const int render_motion=1, const bool double_sided=false, const float focale=500, const float specular_light=0.2f, const float specular_shine=0.1f, const bool display_axes=true, float *const pose_matrix=0) const { return display_object3d(disp,points,primitives,CImgList(),centering, render_static,render_motion,double_sided,focale,specular_light,specular_shine, display_axes,pose_matrix); } //! High-level interface for displaying a 3d object. template const CImg& display_object3d(const char *const title, const tp& points, const CImgList& primitives, const bool centering=true, const int render_static=4, const int render_motion=1, const bool double_sided=false, const float focale=500, const float specular_light=0.2f, const float specular_shine=0.1f, const bool display_axes=true, float *const pose_matrix=0) const { return display_object3d(title,points,primitives,CImgList(),centering, render_static,render_motion,double_sided,focale,specular_light,specular_shine, display_axes,pose_matrix); } //! High-level interface for displaying a 3d object. template const CImg& display_object3d(CImgDisplay &disp, const tp& points, const bool centering=true, const int render_static=4, const int render_motion=1, const bool double_sided=false, const float focale=500, const float specular_light=0.2f, const float specular_shine=0.1f, const bool display_axes=true, float *const pose_matrix=0) const { return display_object3d(disp,points,CImgList(),centering, render_static,render_motion,double_sided,focale,specular_light,specular_shine, display_axes,pose_matrix); } //! High-level interface for displaying a 3d object. template const CImg& display_object3d(const char *const title, const tp& points, const bool centering=true, const int render_static=4, const int render_motion=1, const bool double_sided=false, const float focale=500, const float specular_light=0.2f, const float specular_shine=0.1f, const bool display_axes=true, float *const pose_matrix=0) const { return display_object3d(title,points,CImgList(),centering, render_static,render_motion,double_sided,focale,specular_light,specular_shine, display_axes,pose_matrix); } T _display_object3d_at2(const int i, const int j) const { return atXY(i,j,0,0,0); } template const CImg& _display_object3d(CImgDisplay& disp, const char *const title, const tp& points, const unsigned int Npoints, const CImgList& primitives, const CImgList& colors, const to& opacities, const bool centering, const int render_static, const int render_motion, const bool double_sided, const float focale, const float specular_light, const float specular_shine, const bool display_axes, float *const pose_matrix) const { // Check input arguments if (!points || !Npoints) throw CImgArgumentException("CImg<%s>::display_object3d() : Given points are empty.", pixel_type()); if (is_empty()) { if (disp) return CImg(disp.width,disp.height,1,colors[0].size(),0). _display_object3d(disp,title,points,Npoints,primitives,colors,opacities,centering, render_static,render_motion,double_sided,focale,specular_light,specular_shine, display_axes,pose_matrix); else return CImg(cimg_fitscreen(640,480,1),1,colors[0].size(),0). _display_object3d(disp,title,points,Npoints,primitives,colors,opacities,centering, render_static,render_motion,double_sided,focale,specular_light,specular_shine, display_axes,pose_matrix); } if (!primitives) { CImgList nprimitives(Npoints,1,1,1,1); cimglist_for(nprimitives,l) nprimitives(l,0) = l; return _display_object3d(disp,title,points,Npoints,nprimitives,colors,opacities, centering,render_static,render_motion,double_sided,focale,specular_light,specular_shine, display_axes,pose_matrix); } if (!disp) { char ntitle[64] = { 0 }; if (!title) { cimg_std::sprintf(ntitle,"CImg<%s>",pixel_type()); } disp.assign(cimg_fitscreen(width,height,depth),title?title:ntitle,1); } CImgList _colors; if (!colors) _colors.insert(primitives.size,CImg::vector(200,200,200)); const CImgList &ncolors = colors?colors:_colors; // Init 3D objects and compute object statistics CImg pose, rot_mat, zbuffer, centered_points = centering?CImg(Npoints,3):CImg(), rotated_points(Npoints,3), bbox_points, rotated_bbox_points, axes_points, rotated_axes_points, bbox_opacities, axes_opacities; CImgList bbox_primitives, axes_primitives; CImgList bbox_colors, bbox_colors2, axes_colors; float dx = 0, dy = 0, dz = 0, ratio = 1; T minval = (T)0, maxval = (T)255; if (disp.normalization && colors) { minval = colors.minmax(maxval); if (minval==maxval) { minval = (T)0; maxval = (T)255; } } const float meanval = (float)mean(); bool color_model = true; if (cimg::abs(meanval-minval)>cimg::abs(meanval-maxval)) color_model = false; const CImg background_color(1,1,1,dim,color_model?minval:maxval), foreground_color(1,1,1,dim,color_model?maxval:minval); float xm = cimg::type::max(), xM = 0, ym = xm, yM = 0, zm = xm, zM = 0; for (unsigned int i = 0; ixM) xM = x; if (yyM) yM = y; if (zzM) zM = z; } const float delta = cimg::max(xM-xm,yM-ym,zM-zm); if (display_axes) { rotated_axes_points = axes_points.assign(7,3,1,1, 0,20,0,0,22,-6,-6, 0,0,20,0,-6,22,-6, 0,0,0,20,0,0,22); axes_opacities.assign(3,1,1,1,1); axes_colors.assign(3,dim,1,1,1,foreground_color[0]); axes_primitives.assign(3,1,2,1,1, 0,1, 0,2, 0,3); } // Begin user interaction loop CImg visu0(*this), visu; bool init = true, clicked = false, redraw = true; unsigned int key = 0; int x0 = 0, y0 = 0, x1 = 0, y1 = 0; disp.show().flush(); while (!disp.is_closed && !key) { // Init object position and scale if necessary if (init) { ratio = delta>0?(2.0f*cimg::min(disp.width,disp.height)/(3.0f*delta)):0; dx = 0.5f*(xM + xm); dy = 0.5f*(yM + ym); dz = 0.5f*(zM + zm); if (centering) { cimg_forX(centered_points,l) { centered_points(l,0) = (float)((points(l,0) - dx)*ratio); centered_points(l,1) = (float)((points(l,1) - dy)*ratio); centered_points(l,2) = (float)((points(l,2) - dz)*ratio); } } if (render_static<0 || render_motion<0) { rotated_bbox_points = bbox_points.assign(8,3,1,1, xm,xM,xM,xm,xm,xM,xM,xm, ym,ym,yM,yM,ym,ym,yM,yM, zm,zm,zm,zm,zM,zM,zM,zM); 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); bbox_colors.assign(6,dim,1,1,1,background_color[0]); bbox_colors2.assign(6,dim,1,1,1,foreground_color[0]); bbox_opacities.assign(bbox_colors.size,1,1,1,0.3f); } if (!pose) { if (pose_matrix) pose = CImg(pose_matrix,4,4,1,1,false); else pose = CImg::identity_matrix(4); } init = false; redraw = true; } // Rotate and Draw 3D object if (redraw) { const float r00 = pose(0,0), r10 = pose(1,0), r20 = pose(2,0), r30 = pose(3,0), r01 = pose(0,1), r11 = pose(1,1), r21 = pose(2,1), r31 = pose(3,1), r02 = pose(0,2), r12 = pose(1,2), r22 = pose(2,2), r32 = pose(3,2); if ((clicked && render_motion>=0) || (!clicked && render_static>=0)) { if (centering) cimg_forX(centered_points,l) { const float x = centered_points(l,0), y = centered_points(l,1), z = centered_points(l,2); rotated_points(l,0) = r00*x + r10*y + r20*z + r30; rotated_points(l,1) = r01*x + r11*y + r21*z + r31; rotated_points(l,2) = r02*x + r12*y + r22*z + r32; } else for (unsigned int l = 0; l0)?zbuffer.fill(0).ptr():0); // Draw axes if (display_axes) { const float Xaxes = 25, Yaxes = visu.height - 35.0f; cimg_forX(axes_points,l) { const float x = axes_points(l,0), y = axes_points(l,1), z = axes_points(l,2); rotated_axes_points(l,0) = r00*x + r10*y + r20*z; rotated_axes_points(l,1) = r01*x + r11*y + r21*z; rotated_axes_points(l,2) = r02*x + r12*y + r22*z; } axes_opacities(0,0) = (rotated_axes_points(1,2)>0)?0.5f:1.0f; axes_opacities(1,0) = (rotated_axes_points(2,2)>0)?0.5f:1.0f; axes_opacities(2,0) = (rotated_axes_points(3,2)>0)?0.5f:1.0f; visu.draw_object3d(Xaxes,Yaxes,0,rotated_axes_points,axes_primitives,axes_colors,axes_opacities,1,false,focale). draw_text((int)(Xaxes+rotated_axes_points(4,0)), (int)(Yaxes+rotated_axes_points(4,1)), "X",axes_colors[0].data,0,axes_opacities(0,0),11). draw_text((int)(Xaxes+rotated_axes_points(5,0)), (int)(Yaxes+rotated_axes_points(5,1)), "Y",axes_colors[1].data,0,axes_opacities(1,0),11). draw_text((int)(Xaxes+rotated_axes_points(6,0)), (int)(Yaxes+rotated_axes_points(6,1)), "Z",axes_colors[2].data,0,axes_opacities(2,0),11); } visu.display(disp); if (!clicked || render_motion==render_static) redraw = false; } // Handle user interaction disp.wait(); if ((disp.button || disp.wheel) && disp.mouse_x>=0 && disp.mouse_y>=0) { redraw = true; if (!clicked) { x0 = x1 = disp.mouse_x; y0 = y1 = disp.mouse_y; if (!disp.wheel) clicked = true; } else { x1 = disp.mouse_x; y1 = disp.mouse_y; } if (disp.button&1) { const float R = 0.45f*cimg::min(disp.width,disp.height), R2 = R*R, u0 = (float)(x0-disp.dimx()/2), v0 = (float)(y0-disp.dimy()/2), u1 = (float)(x1-disp.dimx()/2), v1 = (float)(y1-disp.dimy()/2), n0 = (float)cimg_std::sqrt(u0*u0+v0*v0), n1 = (float)cimg_std::sqrt(u1*u1+v1*v1), nu0 = n0>R?(u0*R/n0):u0, nv0 = n0>R?(v0*R/n0):v0, nw0 = (float)cimg_std::sqrt(cimg::max(0,R2-nu0*nu0-nv0*nv0)), nu1 = n1>R?(u1*R/n1):u1, nv1 = n1>R?(v1*R/n1):v1, nw1 = (float)cimg_std::sqrt(cimg::max(0,R2-nu1*nu1-nv1*nv1)), u = nv0*nw1-nw0*nv1, v = nw0*nu1-nu0*nw1, w = nv0*nu1-nu0*nv1, n = (float)cimg_std::sqrt(u*u+v*v+w*w), alpha = (float)cimg_std::asin(n/R2); rot_mat = CImg::rotation_matrix(u,v,w,alpha); rot_mat *= pose.get_crop(0,0,2,2); pose.draw_image(rot_mat); x0=x1; y0=y1; } if (disp.button&2) { pose(3,2)+=(y1-y0); x0 = x1; y0 = y1; } if (disp.wheel) { pose(3,2)-=focale*disp.wheel/10; disp.wheel = 0; } if (disp.button&4) { pose(3,0)+=(x1-x0); pose(3,1)+=(y1-y0); x0 = x1; y0 = y1; } if ((disp.button&1) && (disp.button&2)) { init = true; disp.button = 0; x0 = x1; y0 = y1; pose = CImg::identity_matrix(4); } } else if (clicked) { x0 = x1; y0 = y1; clicked = false; redraw = true; } switch (key = disp.key) { case 0 : case cimg::keyCTRLLEFT : disp.key = key = 0; break; case cimg::keyD: if (disp.is_keyCTRLLEFT) { disp.normalscreen().resize(CImgDisplay::_fitscreen(3*disp.width/2,3*disp.height/2,1,128,-100,false), CImgDisplay::_fitscreen(3*disp.width/2,3*disp.height/2,1,128,-100,true),false).is_resized = true; disp.key = key = 0; } break; case cimg::keyC : if (disp.is_keyCTRLLEFT) { disp.normalscreen().resize(cimg_fitscreen(2*disp.width/3,2*disp.height/3,1),false).is_resized = true; disp.key = key = 0; } break; case cimg::keyR : if (disp.is_keyCTRLLEFT) { disp.normalscreen().resize(cimg_fitscreen(width,height,depth),false).is_resized = true; disp.key = key = 0; } break; case cimg::keyF : if (disp.is_keyCTRLLEFT) { disp.resize(disp.screen_dimx(),disp.screen_dimy()).toggle_fullscreen().is_resized = true; disp.key = key = 0; } break; case cimg::keyZ : if (disp.is_keyCTRLLEFT) { // Enable/Disable Z-buffer if (zbuffer) zbuffer.assign(); else zbuffer.assign(disp.width,disp.height); disp.key = key = 0; redraw = true; } break; case cimg::keyS : if (disp.is_keyCTRLLEFT) { // Save snapshot static unsigned int snap_number = 0; char filename[32] = { 0 }; cimg_std::FILE *file; do { cimg_std::sprintf(filename,"CImg_%.4u.bmp",snap_number++); if ((file=cimg_std::fopen(filename,"r"))!=0) cimg_std::fclose(file); } while (file); (+visu).draw_text(2,2,"Saving BMP snapshot...",foreground_color,background_color,1,11).display(disp); visu.save(filename); visu.draw_text(2,2,"Snapshot '%s' saved.",foreground_color,background_color,1,11,filename).display(disp); disp.key = key = 0; } break; case cimg::keyO : if (disp.is_keyCTRLLEFT) { // Save object as an .OFF file static unsigned int snap_number = 0; char filename[32] = { 0 }; cimg_std::FILE *file; do { cimg_std::sprintf(filename,"CImg_%.4u.off",snap_number++); if ((file=cimg_std::fopen(filename,"r"))!=0) cimg_std::fclose(file); } while (file); visu.draw_text(2,2,"Saving object...",foreground_color,background_color,1,11).display(disp); points.save_off(filename,primitives,ncolors); visu.draw_text(2,2,"Object '%s' saved.",foreground_color,background_color,1,11,filename).display(disp); disp.key = key = 0; } break; #ifdef cimg_use_board case cimg::keyP : if (disp.is_keyCTRLLEFT) { // Save object as a .EPS file static unsigned int snap_number = 0; char filename[32] = { 0 }; cimg_std::FILE *file; do { cimg_std::sprintf(filename,"CImg_%.4u.eps",snap_number++); if ((file=cimg_std::fopen(filename,"r"))!=0) cimg_std::fclose(file); } while (file); visu.draw_text(2,2,"Saving EPS snapshot...",foreground_color,background_color,1,11).display(disp); BoardLib::Board board; (+visu).draw_object3d(board,visu.width/2.0f, visu.height/2.0f, 0, rotated_points,primitives,ncolors,opacities,clicked?render_motion:render_static, double_sided,focale,visu.dimx()/2.0f,visu.dimy()/2.0f,-5000,specular_light,specular_shine, zbuffer.fill(0).ptr()); board.saveEPS(filename); visu.draw_text(2,2,"Object '%s' saved.",foreground_color,background_color,1,11,filename).display(disp); disp.key = key = 0; } break; case cimg::keyV : if (disp.is_keyCTRLLEFT) { // Save object as a .SVG file static unsigned int snap_number = 0; char filename[32] = { 0 }; cimg_std::FILE *file; do { cimg_std::sprintf(filename,"CImg_%.4u.svg",snap_number++); if ((file=cimg_std::fopen(filename,"r"))!=0) cimg_std::fclose(file); } while (file); visu.draw_text(2,2,"Saving SVG snapshot...",foreground_color,background_color,1,11).display(disp); BoardLib::Board board; (+visu).draw_object3d(board,visu.width/2.0f, visu.height/2.0f, 0, rotated_points,primitives,ncolors,opacities,clicked?render_motion:render_static, double_sided,focale,visu.dimx()/2.0f,visu.dimy()/2.0f,-5000,specular_light,specular_shine, zbuffer.fill(0).ptr()); board.saveSVG(filename); visu.draw_text(2,2,"Object '%s' saved.",foreground_color,background_color,1,11,filename).display(disp); disp.key = key = 0; } break; #endif } if (disp.is_resized) { disp.resize(false); visu0 = get_resize(disp,1); if (zbuffer) zbuffer.assign(disp.width,disp.height); redraw = true; } } if (pose_matrix) cimg_std::memcpy(pose_matrix,pose.data,16*sizeof(float)); disp.button = 0; disp.key = key; return *this; } //! High-level interface for displaying a graph. const CImg& 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 { if (is_empty()) throw CImgInstanceException("CImg<%s>::display_graph() : Instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),width,height,depth,dim,data); const unsigned int siz = width*height*depth, onormalization = disp.normalization; if (!disp) { char ntitle[64] = { 0 }; cimg_std::sprintf(ntitle,"CImg<%s>",pixel_type()); disp.assign(640,480,ntitle,0); } disp.show().flush().normalization = 0; double y0 = ymin, y1 = ymax, nxmin = xmin, nxmax = xmax; if (nxmin==nxmax) { nxmin = 0; nxmax = siz - 1.0; } int x0 = 0, x1 = size()/dimv()-1, key = 0; for (bool reset_view = true, resize_disp = false; !key && !disp.is_closed; ) { if (reset_view) { x0 = 0; x1 = size()/dimv()-1; y0 = ymin; y1 = ymax; reset_view = false; } CImg zoom(x1-x0+1,1,1,dimv()); cimg_forV(*this,k) zoom.get_shared_channel(k) = CImg(ptr(x0,0,0,k),x1-x0+1,1,1,1,true); if (y0==y1) y0 = zoom.minmax(y1); if (y0==y1) { --y0; ++y1; } const CImg selection = zoom.get_select_graph(disp,plot_type,vertex_type, labelx,nxmin + x0*(nxmax-nxmin)/siz,nxmin + x1*(nxmax-nxmin)/siz, labely,y0,y1); const int mouse_x = disp.mouse_x, mouse_y = disp.mouse_y; if (selection[0]>=0 && selection[2]>=0) { x1 = x0 + selection[2]; x0 += selection[0]; if (x0==x1) reset_view = true; if (selection[1]>=0 && selection[3]>=0) { y0 = y1 - selection[3]*(y1-y0)/(disp.dimy()-32); y1 -= selection[1]*(y1-y0)/(disp.dimy()-32); } } else { bool go_in = false, go_out = false, go_left = false, go_right = false, go_up = false, go_down = false; switch (key = disp.key) { case cimg::keyHOME : case cimg::keyBACKSPACE : reset_view = resize_disp = true; key = 0; break; case cimg::keyPADADD : go_in = true; key = 0; break; case cimg::keyPADSUB : go_out = true; key = 0; break; case cimg::keyARROWLEFT : case cimg::keyPAD4 : go_left = true; key = 0; break; case cimg::keyARROWRIGHT : case cimg::keyPAD6 : go_right = true; key = 0; break; case cimg::keyARROWUP : case cimg::keyPAD8 : go_up = true; key = 0; break; case cimg::keyARROWDOWN : case cimg::keyPAD2 : go_down = true; key = 0; break; case cimg::keyPAD7 : go_left = true; go_up = true; key = 0; break; case cimg::keyPAD9 : go_right = true; go_up = true; key = 0; break; case cimg::keyPAD1 : go_left = true; go_down = true; key = 0; break; case cimg::keyPAD3 : go_right = true; go_down = true; key = 0; break; } if (disp.wheel) go_out = !(go_in = disp.wheel>0); if (go_in) { const int xsiz = x1 - x0, mx = (mouse_x-16)*xsiz/(disp.dimx()-32), cx = x0 + (mx<0?0:(mx>=xsiz?xsiz:mx)); if (x1-x0>4) { x0 = cx - 7*(cx-x0)/8; x1 = cx + 7*(x1-cx)/8; if (disp.is_keyCTRLLEFT) { const double ysiz = y1 - y0, my = (mouse_y-16)*ysiz/(disp.dimy()-32), cy = y1 - (my<0?0:(my>=ysiz?ysiz:my)); y0 = cy - 7*(cy-y0)/8; y1 = cy + 7*(y1-cy)/8; } else y0 = y1 = 0; } } if (go_out) { const int deltax = (x1-x0)/8, ndeltax = deltax?deltax:(siz>1?1:0); x0-=ndeltax; x1+=ndeltax; if (x0<0) { x1-=x0; x0 = 0; if (x1>=(int)siz) x1 = (int)siz-1; } if (x1>=(int)siz) { x0-=(x1-siz+1); x1 = (int)siz-1; if (x0<0) x0 = 0; } if (disp.is_keyCTRLLEFT) { const double deltay = (y1-y0)/8, ndeltay = deltay?deltay:0.01; y0-=ndeltay; y1+=ndeltay; } } if (go_left) { const int delta = (x1-x0)/5, ndelta = delta?delta:1; if (x0-ndelta>=0) { x0-=ndelta; x1-=ndelta; } else { x1-=x0; x0 = 0; } go_left = false; } if (go_right) { const int delta = (x1-x0)/5, ndelta = delta?delta:1; if (x1+ndelta<(int)siz) { x0+=ndelta; x1+=ndelta; } else { x0+=(siz-1-x1); x1 = siz-1; } go_right = false; } if (go_up) { const double delta = (y1-y0)/10, ndelta = delta?delta:1; y0+=ndelta; y1+=ndelta; go_up = false; } if (go_down) { const double delta = (y1-y0)/10, ndelta = delta?delta:1; y0-=ndelta; y1-=ndelta; go_down = false; } } } disp.normalization = onormalization; return *this; } //! High-level interface for displaying a graph. const CImg& 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 { if (is_empty()) throw CImgInstanceException("CImg<%s>::display_graph() : Instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),width,height,depth,dim,data); char ntitle[64] = { 0 }; if (!title) cimg_std::sprintf(ntitle,"CImg<%s>",pixel_type()); CImgDisplay disp(cimg_fitscreen(640,480,1),title?title:ntitle,0); return display_graph(disp,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax); } //! Select sub-graph in a graph. CImg 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 { if (is_empty()) throw CImgInstanceException("CImg<%s>::display_graph() : Instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),width,height,depth,dim,data); const unsigned int siz = width*height*depth, onormalization = disp.normalization; if (!disp) { char ntitle[64] = { 0 }; cimg_std::sprintf(ntitle,"CImg<%s>",pixel_type()); disp.assign(640,480,ntitle,0); } disp.show().key = disp.normalization = disp.button = disp.wheel = 0; // Must keep 'key' field unchanged. double nymin = ymin, nymax = ymax, nxmin = xmin, nxmax = xmax; if (nymin==nymax) nymin = (Tfloat)minmax(nymax); if (nymin==nymax) { --nymin; ++nymax; } if (nxmin==nxmax && nxmin==0) { nxmin = 0; nxmax = siz - 1.0; } const unsigned char black[] = { 0,0,0 }, white[] = { 255,255,255 }, gray[] = { 220,220,220 }; const unsigned char gray2[] = { 110,110,110 }, ngray[] = { 35,35,35 }; static unsigned int odimv = 0; static CImg palette; if (odimv!=dim) { odimv = dim; palette = CImg(3,dim,1,1,120).noise(70,1); if (dim==1) { palette[0] = palette[1] = 120; palette[2] = 200; } else { palette(0,0) = 220; palette(1,0) = 10; palette(2,0) = 10; if (dim>1) { palette(0,1) = 10; palette(1,1) = 220; palette(2,1) = 10; } if (dim>2) { palette(0,2) = 10; palette(1,2) = 10; palette(2,2) = 220; } } } CImg visu0, visu, graph, text, axes; const unsigned int whz = width*height*depth; int x0 = -1, x1 = -1, y0 = -1, y1 = -1, omouse_x = -2, omouse_y = -2; char message[1024] = { 0 }; unsigned int okey = 0, obutton = 0; CImg_3x3(I,unsigned char); for (bool selected = false; !selected && !disp.is_closed && !okey && !disp.wheel; ) { const int mouse_x = disp.mouse_x, mouse_y = disp.mouse_y; const unsigned int key = disp.key, button = disp.button; // Generate graph representation. if (!visu0) { visu0.assign(disp.dimx(),disp.dimy(),1,3,220); const int gdimx = disp.dimx() - 32, gdimy = disp.dimy() - 32; if (gdimx>0 && gdimy>0) { graph.assign(gdimx,gdimy,1,3,255); graph.draw_grid(-10,-10,0,0,false,true,black,0.2f,0x33333333,0x33333333); cimg_forV(*this,k) graph.draw_graph(get_shared_channel(k),&palette(0,k),(plot_type!=3 || dim==1)?1:0.6f, plot_type,vertex_type,nymax,nymin); axes.assign(gdimx,gdimy,1,1,0); const float dx = (float)cimg::abs(nxmax-nxmin), dy = (float)cimg::abs(nymax-nymin), px = (float)cimg_std::pow(10.0,(int)cimg_std::log10(dx)-2.0), py = (float)cimg_std::pow(10.0,(int)cimg_std::log10(dy)-2.0); const CImg seqx = CImg::sequence(1 + gdimx/60,nxmin,nxmax).round(px), seqy = CImg::sequence(1 + gdimy/60,nymax,nymin).round(py); axes.draw_axis(seqx,seqy,white); if (nymin>0) axes.draw_axis(seqx,gdimy-1,gray); if (nymax<0) axes.draw_axis(seqx,0,gray); if (nxmin>0) axes.draw_axis(0,seqy,gray); if (nxmax<0) axes.draw_axis(gdimx-1,seqy,gray); cimg_for3x3(axes,x,y,0,0,I) if (Icc) { if (Icc==255) cimg_forV(graph,k) graph(x,y,k) = 0; else cimg_forV(graph,k) graph(x,y,k) = (unsigned char)(2*graph(x,y,k)/3); } else if (Ipc || Inc || Icp || Icn || Ipp || Inn || Ipn || Inp) cimg_forV(graph,k) graph(x,y,k) = (graph(x,y,k)+255)/2; visu0.draw_image(16,16,graph); visu0.draw_line(15,15,16+gdimx,15,gray2).draw_line(16+gdimx,15,16+gdimx,16+gdimy,gray2). draw_line(16+gdimx,16+gdimy,15,16+gdimy,white).draw_line(15,16+gdimy,15,15,white); } else graph.assign(); text.assign().draw_text(0,0,labelx?labelx:"X-axis",white,ngray,1); visu0.draw_image((visu0.dimx()-text.dimx())/2,visu0.dimy()-14,~text); text.assign().draw_text(0,0,labely?labely:"Y-axis",white,ngray,1).rotate(-90); visu0.draw_image(2,(visu0.dimy()-text.dimy())/2,~text); visu.assign(); } // Generate and display current view. if (!visu) { visu.assign(visu0); if (graph && x0>=0 && x1>=0) { const int nx0 = x0<=x1?x0:x1, nx1 = x0<=x1?x1:x0, ny0 = y0<=y1?y0:y1, ny1 = y0<=y1?y1:y0, sx0 = 16 + nx0*(visu.dimx()-32)/whz, sx1 = 15 + (nx1+1)*(visu.dimx()-32)/whz, sy0 = 16 + ny0, sy1 = 16 + ny1; if (y0>=0 && y1>=0) visu.draw_rectangle(sx0,sy0,sx1,sy1,gray,0.5f).draw_rectangle(sx0,sy0,sx1,sy1,black,0.5f,0xCCCCCCCCU); else visu.draw_rectangle(sx0,0,sx1,visu.dimy()-17,gray,0.5f). draw_line(sx0,16,sx0,visu.dimy()-17,black,0.5f,0xCCCCCCCCU). draw_line(sx1,16,sx1,visu.dimy()-17,black,0.5f,0xCCCCCCCCU); } if (mouse_x>=16 && mouse_y>=16 && mouse_x=7) cimg_std::sprintf(message,"Value[%g] = ( %g %g %g ... %g %g %g )",cx, (double)(*this)(x,0,0,0),(double)(*this)(x,0,0,1),(double)(*this)(x,0,0,2), (double)(*this)(x,0,0,dim-4),(double)(*this)(x,0,0,dim-3),(double)(*this)(x,0,0,dim-1)); else { cimg_std::sprintf(message,"Value[%g] = ( ",cx); cimg_forV(*this,k) cimg_std::sprintf(message+cimg::strlen(message),"%g ",(double)(*this)(x,0,0,k)); cimg_std::sprintf(message+cimg::strlen(message),")"); } if (x0>=0 && x1>=0) { const int nx0 = x0<=x1?x0:x1, nx1 = x0<=x1?x1:x0, ny0 = y0<=y1?y0:y1, ny1 = y0<=y1?y1:y0; const double cx0 = nxmin + nx0*(nxmax-nxmin)/(visu.dimx()-32), cx1 = nxmin + nx1*(nxmax-nxmin)/(visu.dimx()-32), cy0 = nymax - ny0*(nymax-nymin)/(visu.dimy()-32), cy1 = nymax - ny1*(nymax-nymin)/(visu.dimy()-32); if (y0>=0 && y1>=0) cimg_std::sprintf(message+cimg::strlen(message)," - Range ( %g, %g ) - ( %g, %g )",cx0,cy0,cx1,cy1); else cimg_std::sprintf(message+cimg::strlen(message)," - Range [ %g - %g ]",cx0,cx1); } text.assign().draw_text(0,0,message,white,ngray,1); visu.draw_image((visu.dimx()-text.dimx())/2,2,~text); } visu.display(disp); } // Test keys. switch (okey = key) { case cimg::keyCTRLLEFT : okey = 0; break; case cimg::keyD : if (disp.is_keyCTRLLEFT) { disp.normalscreen().resize(CImgDisplay::_fitscreen(3*disp.width/2,3*disp.height/2,1,128,-100,false), CImgDisplay::_fitscreen(3*disp.width/2,3*disp.height/2,1,128,-100,true),false).is_resized = true; disp.key = okey = 0; } break; case cimg::keyC : if (disp.is_keyCTRLLEFT) { disp.normalscreen().resize(cimg_fitscreen(2*disp.width/3,2*disp.height/3,1),false).is_resized = true; disp.key = okey = 0; } break; case cimg::keyR : if (disp.is_keyCTRLLEFT) { disp.normalscreen().resize(cimg_fitscreen(640,480,1),false).is_resized = true; disp.key = okey = 0; } break; case cimg::keyF : if (disp.is_keyCTRLLEFT) { disp.resize(disp.screen_dimx(),disp.screen_dimy()).toggle_fullscreen().is_resized = true; disp.key = okey = 0; } break; case cimg::keyS : if (disp.is_keyCTRLLEFT) { static unsigned int snap_number = 0; if (visu || visu0) { CImg &screen = visu?visu:visu0; char filename[32] = { 0 }; cimg_std::FILE *file; do { cimg_std::sprintf(filename,"CImg_%.4u.bmp",snap_number++); if ((file=cimg_std::fopen(filename,"r"))!=0) cimg_std::fclose(file); } while (file); (+screen).draw_text(2,2,"Saving BMP snapshot...",black,gray,1,11).display(disp); screen.save(filename); screen.draw_text(2,2,"Snapshot '%s' saved.",black,gray,1,11,filename).display(disp); } disp.key = okey = 0; } break; } // Handle mouse motion and mouse buttons if (obutton!=button || omouse_x!=mouse_x || omouse_y!=mouse_y) { visu.assign(); if (disp.mouse_x>=0 && disp.mouse_y>=0) { const int mx = (mouse_x-16)*(int)whz/(disp.dimx()-32), cx = mx<0?0:(mx>=(int)whz?whz-1:mx), my = mouse_y-16, cy = my<=0?0:(my>=(disp.dimy()-32)?(disp.dimy()-32):my); if (button&1) { if (!obutton) { x0 = cx; y0 = -1; } else { x1 = cx; y1 = -1; }} else if (button&2) { if (!obutton) { x0 = cx; y0 = cy; } else { x1 = cx; y1 = cy; }} else if (obutton) { x1 = cx; y1 = y1>=0?cy:-1; selected = true; } } else if (!button && obutton) selected = true; obutton = button; omouse_x = mouse_x; omouse_y = mouse_y; } if (disp.is_resized) { disp.resize(false); visu0.assign(); } if (visu && visu0) disp.wait(); } disp.normalization = onormalization; if (x1(4,1,1,1,x0,y0,x1,y1); } //@} //--------------------------- // //! \name Image File Loading //@{ //--------------------------- //! Load an image from a file. /** \param filename is the name of the image file to load. \note The extension of \c filename defines the file format. If no filename extension is provided, CImg::get_load() will try to load a .cimg file. **/ CImg& load(const char *const filename) { if (!filename) throw CImgArgumentException("CImg<%s>::load() : Cannot load (null) filename.", pixel_type()); const char *ext = cimg::split_filename(filename); const unsigned int odebug = cimg::exception_mode(); cimg::exception_mode() = 0; assign(); try { #ifdef cimg_load_plugin cimg_load_plugin(filename); #endif #ifdef cimg_load_plugin1 cimg_load_plugin1(filename); #endif #ifdef cimg_load_plugin2 cimg_load_plugin2(filename); #endif #ifdef cimg_load_plugin3 cimg_load_plugin3(filename); #endif #ifdef cimg_load_plugin4 cimg_load_plugin4(filename); #endif #ifdef cimg_load_plugin5 cimg_load_plugin5(filename); #endif #ifdef cimg_load_plugin6 cimg_load_plugin6(filename); #endif #ifdef cimg_load_plugin7 cimg_load_plugin7(filename); #endif #ifdef cimg_load_plugin8 cimg_load_plugin8(filename); #endif // ASCII formats if (!cimg::strcasecmp(ext,"asc")) load_ascii(filename); if (!cimg::strcasecmp(ext,"dlm") || !cimg::strcasecmp(ext,"txt")) load_dlm(filename); // 2D binary formats if (!cimg::strcasecmp(ext,"bmp")) load_bmp(filename); if (!cimg::strcasecmp(ext,"jpg") || !cimg::strcasecmp(ext,"jpeg") || !cimg::strcasecmp(ext,"jpe") || !cimg::strcasecmp(ext,"jfif") || !cimg::strcasecmp(ext,"jif")) load_jpeg(filename); if (!cimg::strcasecmp(ext,"png")) load_png(filename); if (!cimg::strcasecmp(ext,"ppm") || !cimg::strcasecmp(ext,"pgm") || !cimg::strcasecmp(ext,"pnm")) load_pnm(filename); if (!cimg::strcasecmp(ext,"tif") || !cimg::strcasecmp(ext,"tiff")) load_tiff(filename); if (!cimg::strcasecmp(ext,"cr2") || !cimg::strcasecmp(ext,"crw") || !cimg::strcasecmp(ext,"dcr") || !cimg::strcasecmp(ext,"mrw") || !cimg::strcasecmp(ext,"nef") || !cimg::strcasecmp(ext,"orf") || !cimg::strcasecmp(ext,"pix") || !cimg::strcasecmp(ext,"ptx") || !cimg::strcasecmp(ext,"raf") || !cimg::strcasecmp(ext,"srf")) load_dcraw_external(filename); // 3D binary formats if (!cimg::strcasecmp(ext,"dcm") || !cimg::strcasecmp(ext,"dicom")) load_medcon_external(filename); if (!cimg::strcasecmp(ext,"hdr") || !cimg::strcasecmp(ext,"nii")) load_analyze(filename); if (!cimg::strcasecmp(ext,"par") || !cimg::strcasecmp(ext,"rec")) load_parrec(filename); if (!cimg::strcasecmp(ext,"inr")) load_inr(filename); if (!cimg::strcasecmp(ext,"pan")) load_pandore(filename); if (!cimg::strcasecmp(ext,"cimg") || !cimg::strcasecmp(ext,"cimgz") || *ext=='\0') return load_cimg(filename); // Archive files if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename); // Image sequences if (!cimg::strcasecmp(ext,"avi") || !cimg::strcasecmp(ext,"mov") || !cimg::strcasecmp(ext,"asf") || !cimg::strcasecmp(ext,"divx") || !cimg::strcasecmp(ext,"flv") || !cimg::strcasecmp(ext,"mpg") || !cimg::strcasecmp(ext,"m1v") || !cimg::strcasecmp(ext,"m2v") || !cimg::strcasecmp(ext,"m4v") || !cimg::strcasecmp(ext,"mjp") || !cimg::strcasecmp(ext,"mkv") || !cimg::strcasecmp(ext,"mpe") || !cimg::strcasecmp(ext,"movie") || !cimg::strcasecmp(ext,"ogm") || !cimg::strcasecmp(ext,"qt") || !cimg::strcasecmp(ext,"rm") || !cimg::strcasecmp(ext,"vob") || !cimg::strcasecmp(ext,"wmv") || !cimg::strcasecmp(ext,"xvid") || !cimg::strcasecmp(ext,"mpeg")) load_ffmpeg(filename); if (is_empty()) throw CImgIOException("CImg<%s>::load()",pixel_type()); } catch (CImgException& e) { if (!cimg::strncasecmp(e.message,"cimg::fopen()",13)) { cimg::exception_mode() = odebug; throw CImgIOException("CImg<%s>::load() : File '%s' cannot be opened.",pixel_type(),filename); } else try { const char *const ftype = cimg::file_type(0,filename); assign(); if (!cimg::strcmp(ftype,"pnm")) load_pnm(filename); if (!cimg::strcmp(ftype,"bmp")) load_bmp(filename); if (!cimg::strcmp(ftype,"jpeg")) load_jpeg(filename); if (!cimg::strcmp(ftype,"pan")) load_pandore(filename); if (!cimg::strcmp(ftype,"png")) load_png(filename); if (!cimg::strcmp(ftype,"tiff")) load_tiff(filename); if (is_empty()) throw CImgIOException("CImg<%s>::load()",pixel_type()); } catch (CImgException&) { try { load_other(filename); } catch (CImgException&) { assign(); } } } cimg::exception_mode() = odebug; if (is_empty()) throw CImgIOException("CImg<%s>::load() : File '%s', format not recognized.",pixel_type(),filename); return *this; } static CImg get_load(const char *const filename) { return CImg().load(filename); } //! Load an image from an ASCII file. CImg& load_ascii(const char *const filename) { return _load_ascii(0,filename); } static CImg get_load_ascii(const char *const filename) { return CImg().load_ascii(filename); } //! Load an image from an ASCII file. CImg& load_ascii(cimg_std::FILE *const file) { return _load_ascii(file,0); } static CImg get_load_ascii(cimg_std::FILE *const file) { return CImg().load_ascii(file); } CImg& _load_ascii(cimg_std::FILE *const file, const char *const filename) { if (!filename && !file) throw CImgArgumentException("CImg<%s>::load_ascii() : Cannot load (null) filename.", pixel_type()); cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); char line[256] = { 0 }; int err = cimg_std::fscanf(nfile,"%*[^0-9]%255[^\n]",line); unsigned int off, dx = 0, dy = 1, dz = 1, dv = 1; cimg_std::sscanf(line,"%u%*c%u%*c%u%*c%u",&dx,&dy,&dz,&dv); err = cimg_std::fscanf(nfile,"%*[^0-9.+-]"); if (!dx || !dy || !dz || !dv) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_ascii() : File '%s', invalid .ASC header, specified image dimensions are (%u,%u,%u,%u).", pixel_type(),filename?filename:"(FILE*)",dx,dy,dz,dv); } assign(dx,dy,dz,dv); const unsigned long siz = size(); double val; T *ptr = data; for (err = 1, off = 0; off::load_ascii() : File '%s', only %u/%lu values read.", pixel_type(),filename?filename:"(FILE*)",off-1,siz); if (!file) cimg::fclose(nfile); return *this; } //! Load an image from a DLM file. CImg& load_dlm(const char *const filename) { return _load_dlm(0,filename); } static CImg get_load_dlm(const char *const filename) { return CImg().load_dlm(filename); } //! Load an image from a DLM file. CImg& load_dlm(cimg_std::FILE *const file) { return _load_dlm(file,0); } static CImg get_load_dlm(cimg_std::FILE *const file) { return CImg().load_dlm(file); } CImg& _load_dlm(cimg_std::FILE *const file, const char *const filename) { if (!filename && !file) throw CImgArgumentException("CImg<%s>::load_dlm() : Cannot load (null) filename.", pixel_type()); cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"r"); assign(256,256); char c, delimiter[256] = { 0 }, tmp[256]; unsigned int cdx = 0, dx = 0, dy = 0; int oerr = 0, err; double val; while ((err = cimg_std::fscanf(nfile,"%lf%255[^0-9.+-]",&val,delimiter))!=EOF) { oerr = err; if (err>0) (*this)(cdx++,dy) = (T)val; if (cdx>=width) resize(width+256,1,1,1,0); c = 0; if (!cimg_std::sscanf(delimiter,"%255[^\n]%c",tmp,&c) || c=='\n') { dx = cimg::max(cdx,dx); ++dy; if (dy>=height) resize(width,height+256,1,1,0); cdx = 0; } } if (cdx && oerr==1) { dx=cdx; ++dy; } if (!dx || !dy) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_dlm() : File '%s', invalid DLM file, specified image dimensions are (%u,%u).", pixel_type(),filename?filename:"(FILE*)",dx,dy); } resize(dx,dy,1,1,0); if (!file) cimg::fclose(nfile); return *this; } //! Load an image from a BMP file. CImg& load_bmp(const char *const filename) { return _load_bmp(0,filename); } static CImg get_load_bmp(const char *const filename) { return CImg().load_bmp(filename); } //! Load an image from a BMP file. CImg& load_bmp(cimg_std::FILE *const file) { return _load_bmp(file,0); } static CImg get_load_bmp(cimg_std::FILE *const file) { return CImg().load_bmp(file); } CImg& _load_bmp(cimg_std::FILE *const file, const char *const filename) { if (!filename && !file) throw CImgArgumentException("CImg<%s>::load_bmp() : Cannot load (null) filename.", pixel_type()); cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); unsigned char header[64]; cimg::fread(header,54,nfile); if (header[0]!='B' || header[1]!='M') { if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_bmp() : Invalid valid BMP file (filename '%s').", pixel_type(),filename?filename:"(FILE*)"); } assign(); // Read header and pixel buffer int file_size = header[0x02] + (header[0x03]<<8) + (header[0x04]<<16) + (header[0x05]<<24), offset = header[0x0A] + (header[0x0B]<<8) + (header[0x0C]<<16) + (header[0x0D]<<24), dx = header[0x12] + (header[0x13]<<8) + (header[0x14]<<16) + (header[0x15]<<24), dy = header[0x16] + (header[0x17]<<8) + (header[0x18]<<16) + (header[0x19]<<24), compression = header[0x1E] + (header[0x1F]<<8) + (header[0x20]<<16) + (header[0x21]<<24), nb_colors = header[0x2E] + (header[0x2F]<<8) + (header[0x30]<<16) + (header[0x31]<<24), bpp = header[0x1C] + (header[0x1D]<<8), *palette = 0; const int dx_bytes = (bpp==1)?(dx/8+(dx%8?1:0)):((bpp==4)?(dx/2+(dx%2?1:0)):(dx*bpp/8)), align = (4-dx_bytes%4)%4, buf_size = cimg::min(cimg::abs(dy)*(dx_bytes+align),file_size-offset); if (bpp<16) { if (!nb_colors) nb_colors=1<0) cimg_std::fseek(nfile,xoffset,SEEK_CUR); unsigned char *buffer = new unsigned char[buf_size], *ptrs = buffer; cimg::fread(buffer,buf_size,nfile); if (!file) cimg::fclose(nfile); // Decompress buffer (if necessary) if (compression) { delete[] buffer; if (file) { throw CImgIOException("CImg<%s>::load_bmp() : Not able to read a compressed BMP file using a *FILE input", pixel_type()); } else return load_other(filename); } // Read pixel data assign(dx,cimg::abs(dy),1,3); switch (bpp) { case 1 : { // Monochrome for (int y=height-1; y>=0; --y) { unsigned char mask = 0x80, val = 0; cimg_forX(*this,x) { if (mask==0x80) val = *(ptrs++); const unsigned char *col = (unsigned char*)(palette+(val&mask?1:0)); (*this)(x,y,2) = (T)*(col++); (*this)(x,y,1) = (T)*(col++); (*this)(x,y,0) = (T)*(col++); mask = cimg::ror(mask); } ptrs+=align; } } break; case 4 : { // 16 colors for (int y=height-1; y>=0; --y) { unsigned char mask = 0xF0, val = 0; cimg_forX(*this,x) { if (mask==0xF0) val = *(ptrs++); const unsigned char color = (unsigned char)((mask<16)?(val&mask):((val&mask)>>4)); unsigned char *col = (unsigned char*)(palette+color); (*this)(x,y,2) = (T)*(col++); (*this)(x,y,1) = (T)*(col++); (*this)(x,y,0) = (T)*(col++); mask = cimg::ror(mask,4); } ptrs+=align; } } break; case 8 : { // 256 colors for (int y=height-1; y>=0; --y) { cimg_forX(*this,x) { const unsigned char *col = (unsigned char*)(palette+*(ptrs++)); (*this)(x,y,2) = (T)*(col++); (*this)(x,y,1) = (T)*(col++); (*this)(x,y,0) = (T)*(col++); } ptrs+=align; } } break; case 16 : { // 16 bits colors for (int y=height-1; y>=0; --y) { cimg_forX(*this,x) { const unsigned char c1 = *(ptrs++), c2 = *(ptrs++); const unsigned short col = (unsigned short)(c1|(c2<<8)); (*this)(x,y,2) = (T)(col&0x1F); (*this)(x,y,1) = (T)((col>>5)&0x1F); (*this)(x,y,0) = (T)((col>>10)&0x1F); } ptrs+=align; } } break; case 24 : { // 24 bits colors for (int y=height-1; y>=0; --y) { cimg_forX(*this,x) { (*this)(x,y,2) = (T)*(ptrs++); (*this)(x,y,1) = (T)*(ptrs++); (*this)(x,y,0) = (T)*(ptrs++); } ptrs+=align; } } break; case 32 : { // 32 bits colors for (int y=height-1; y>=0; --y) { cimg_forX(*this,x) { (*this)(x,y,2) = (T)*(ptrs++); (*this)(x,y,1) = (T)*(ptrs++); (*this)(x,y,0) = (T)*(ptrs++); ++ptrs; } ptrs+=align; } } break; } if (palette) delete[] palette; delete[] buffer; if (dy<0) mirror('y'); return *this; } //! Load an image from a JPEG file. CImg& load_jpeg(const char *const filename) { return _load_jpeg(0,filename); } static CImg get_load_jpeg(const char *const filename) { return CImg().load_jpeg(filename); } //! Load an image from a JPEG file. CImg& load_jpeg(cimg_std::FILE *const file) { return _load_jpeg(file,0); } static CImg get_load_jpeg(cimg_std::FILE *const file) { return CImg().load_jpeg(file); } CImg& _load_jpeg(cimg_std::FILE *const file, const char *const filename) { if (!filename && !file) throw CImgArgumentException("CImg<%s>::load_jpeg() : Cannot load (null) filename.", pixel_type()); #ifndef cimg_use_jpeg if (file) throw CImgIOException("CImg<%s>::load_jpeg() : File '(FILE*)' cannot be read without using libjpeg.", pixel_type()); else return load_other(filename); #else struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); cinfo.err = jpeg_std_error(&jerr); jpeg_create_decompress(&cinfo); jpeg_stdio_src(&cinfo,nfile); jpeg_read_header(&cinfo,TRUE); jpeg_start_decompress(&cinfo); if (cinfo.output_components!=1 && cinfo.output_components!=3 && cinfo.output_components!=4) { cimg::warn("CImg<%s>::load_jpeg() : Don't know how to read image '%s' with libpeg, trying ImageMagick's convert", pixel_type(),filename?filename:"(FILE*)"); if (!file) return load_other(filename); else { if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_jpeg() : Cannot read JPEG image '%s' using a *FILE input.", pixel_type(),filename?filename:"(FILE*)"); } } const unsigned int row_stride = cinfo.output_width * cinfo.output_components; unsigned char *buf = new unsigned char[cinfo.output_width*cinfo.output_height*cinfo.output_components], *buf2 = buf; JSAMPROW row_pointer[1]; while (cinfo.output_scanline < cinfo.output_height) { row_pointer[0] = &buf[cinfo.output_scanline*row_stride]; jpeg_read_scanlines(&cinfo,row_pointer,1); } jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); if (!file) cimg::fclose(nfile); assign(cinfo.output_width,cinfo.output_height,1,cinfo.output_components); switch (dim) { case 1 : { T *ptr_g = data; cimg_forXY(*this,x,y) *(ptr_g++) = (T)*(buf2++); } break; case 3 : { T *ptr_r = ptr(0,0,0,0), *ptr_g = ptr(0,0,0,1), *ptr_b = ptr(0,0,0,2); cimg_forXY(*this,x,y) { *(ptr_r++) = (T)*(buf2++); *(ptr_g++) = (T)*(buf2++); *(ptr_b++) = (T)*(buf2++); } } break; case 4 : { T *ptr_r = ptr(0,0,0,0), *ptr_g = ptr(0,0,0,1), *ptr_b = ptr(0,0,0,2), *ptr_a = ptr(0,0,0,3); cimg_forXY(*this,x,y) { *(ptr_r++) = (T)*(buf2++); *(ptr_g++) = (T)*(buf2++); *(ptr_b++) = (T)*(buf2++); *(ptr_a++) = (T)*(buf2++); } } break; } delete[] buf; return *this; #endif } //! Load an image from a file, using Magick++ library. // Added April/may 2006 by Christoph Hormann // This is experimental code, not much tested, use with care. CImg& load_magick(const char *const filename) { if (!filename) throw CImgArgumentException("CImg<%s>::load_magick() : Cannot load (null) filename.", pixel_type()); #ifdef cimg_use_magick Magick::Image image(filename); const unsigned int W = image.size().width(), H = image.size().height(); switch (image.type()) { case Magick::PaletteMatteType : case Magick::TrueColorMatteType : case Magick::ColorSeparationType : { assign(W,H,1,4); T *rdata = ptr(0,0,0,0), *gdata = ptr(0,0,0,1), *bdata = ptr(0,0,0,2), *adata = ptr(0,0,0,3); Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); for (unsigned int off = W*H; off; --off) { *(rdata++) = (T)(pixels->red); *(gdata++) = (T)(pixels->green); *(bdata++) = (T)(pixels->blue); *(adata++) = (T)(pixels->opacity); ++pixels; } } break; case Magick::PaletteType : case Magick::TrueColorType : { assign(W,H,1,3); T *rdata = ptr(0,0,0,0), *gdata = ptr(0,0,0,1), *bdata = ptr(0,0,0,2); Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); for (unsigned int off = W*H; off; --off) { *(rdata++) = (T)(pixels->red); *(gdata++) = (T)(pixels->green); *(bdata++) = (T)(pixels->blue); ++pixels; } } break; case Magick::GrayscaleMatteType : { assign(W,H,1,2); T *data = ptr(0,0,0,0), *adata = ptr(0,0,0,1); Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); for (unsigned int off = W*H; off; --off) { *(data++) = (T)(pixels->red); *(adata++) = (T)(pixels->opacity); ++pixels; } } break; default : { assign(W,H,1,1); T *data = ptr(0,0,0,0); Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); for (unsigned int off = W*H; off; --off) { *(data++) = (T)(pixels->red); ++pixels; } } } #else throw CImgIOException("CImg<%s>::load_magick() : File '%s', Magick++ library has not been linked.", pixel_type(),filename); #endif return *this; } static CImg get_load_magick(const char *const filename) { return CImg().load_magick(filename); } //! Load an image from a PNG file. CImg& load_png(const char *const filename) { return _load_png(0,filename); } static CImg get_load_png(const char *const filename) { return CImg().load_png(filename); } //! Load an image from a PNG file. CImg& load_png(cimg_std::FILE *const file) { return _load_png(file,0); } static CImg get_load_png(cimg_std::FILE *const file) { return CImg().load_png(file); } // (Note : Most of this function has been written by Eric Fausett) CImg& _load_png(cimg_std::FILE *const file, const char *const filename) { if (!filename && !file) throw CImgArgumentException("CImg<%s>::load_png() : Cannot load (null) filename.", pixel_type()); #ifndef cimg_use_png if (file) throw CImgIOException("CImg<%s>::load_png() : File '(FILE*)' cannot be read without using libpng.", pixel_type()); else return load_other(filename); #else // Open file and check for PNG validity const char *volatile nfilename = filename; // two 'volatile' here to remove a g++ warning due to 'setjmp'. cimg_std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"rb"); unsigned char pngCheck[8]; cimg::fread(pngCheck,8,(cimg_std::FILE*)nfile); if (png_sig_cmp(pngCheck,0,8)) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_png() : File '%s' is not a valid PNG file.", pixel_type(),nfilename?nfilename:"(FILE*)"); } // Setup PNG structures for read png_voidp user_error_ptr = 0; png_error_ptr user_error_fn = 0, user_warning_fn = 0; png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,user_error_ptr,user_error_fn,user_warning_fn); if (!png_ptr) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_png() : File '%s', trouble initializing 'png_ptr' data structure.", pixel_type(),nfilename?nfilename:"(FILE*)"); } png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { if (!file) cimg::fclose(nfile); png_destroy_read_struct(&png_ptr,(png_infopp)0,(png_infopp)0); throw CImgIOException("CImg<%s>::load_png() : File '%s', trouble initializing 'info_ptr' data structure.", pixel_type(),nfilename?nfilename:"(FILE*)"); } png_infop end_info = png_create_info_struct(png_ptr); if (!end_info) { if (!file) cimg::fclose(nfile); png_destroy_read_struct(&png_ptr,&info_ptr,(png_infopp)0); throw CImgIOException("CImg<%s>::load_png() : File '%s', trouble initializing 'end_info' data structure.", pixel_type(),nfilename?nfilename:"(FILE*)"); } // Error handling callback for png file reading if (setjmp(png_jmpbuf(png_ptr))) { if (!file) cimg::fclose((cimg_std::FILE*)nfile); png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0); throw CImgIOException("CImg<%s>::load_png() : File '%s', unknown fatal error.", pixel_type(),nfilename?nfilename:"(FILE*)"); } png_init_io(png_ptr, nfile); png_set_sig_bytes(png_ptr, 8); // Get PNG Header Info up to data block png_read_info(png_ptr,info_ptr); png_uint_32 W, H; int bit_depth, color_type, interlace_type; png_get_IHDR(png_ptr,info_ptr,&W,&H,&bit_depth,&color_type,&interlace_type,int_p_NULL,int_p_NULL); int new_bit_depth = bit_depth; int new_color_type = color_type; // Transforms to unify image data if (new_color_type == PNG_COLOR_TYPE_PALETTE){ png_set_palette_to_rgb(png_ptr); new_color_type -= PNG_COLOR_MASK_PALETTE; new_bit_depth = 8; } if (new_color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8){ png_set_expand_gray_1_2_4_to_8(png_ptr); new_bit_depth = 8; } if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr); if (new_color_type == PNG_COLOR_TYPE_GRAY || new_color_type == PNG_COLOR_TYPE_GRAY_ALPHA){ png_set_gray_to_rgb(png_ptr); new_color_type |= PNG_COLOR_MASK_COLOR; } if (new_color_type == PNG_COLOR_TYPE_RGB) png_set_filler(png_ptr, 0xffffU, PNG_FILLER_AFTER); png_read_update_info(png_ptr,info_ptr); if (!(new_bit_depth==8 || new_bit_depth==16)) { if (!file) cimg::fclose(nfile); png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0); throw CImgIOException("CImg<%s>::load_png() : File '%s', wrong bit coding (bit_depth=%u)", pixel_type(),nfilename?nfilename:"(FILE*)",new_bit_depth); } const int byte_depth = new_bit_depth>>3; // Allocate Memory for Image Read png_bytep *imgData = new png_bytep[H]; for (unsigned int row = 0; row::load_png() : File '%s', wrong color coding (new_color_type=%u)", pixel_type(),nfilename?nfilename:"(FILE*)",new_color_type); } const bool no_alpha_channel = (new_color_type==PNG_COLOR_TYPE_RGB); assign(W,H,1,no_alpha_channel?3:4); T *ptr1 = ptr(0,0,0,0), *ptr2 = ptr(0,0,0,1), *ptr3 = ptr(0,0,0,2), *ptr4 = ptr(0,0,0,3); switch (new_bit_depth) { case 8 : { cimg_forY(*this,y){ const unsigned char *ptrs = (unsigned char*)imgData[y]; cimg_forX(*this,x){ *(ptr1++) = (T)*(ptrs++); *(ptr2++) = (T)*(ptrs++); *(ptr3++) = (T)*(ptrs++); if (no_alpha_channel) ++ptrs; else *(ptr4++) = (T)*(ptrs++); } } } break; case 16 : { cimg_forY(*this,y){ const unsigned short *ptrs = (unsigned short*)(imgData[y]); if (!cimg::endianness()) cimg::invert_endianness(ptrs,4*width); cimg_forX(*this,x){ *(ptr1++) = (T)*(ptrs++); *(ptr2++) = (T)*(ptrs++); *(ptr3++) = (T)*(ptrs++); if (no_alpha_channel) ++ptrs; else *(ptr4++) = (T)*(ptrs++); } } } break; } png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); // Deallocate Image Read Memory cimg_forY(*this,n) delete[] imgData[n]; delete[] imgData; if (!file) cimg::fclose(nfile); return *this; #endif } //! Load an image from a PNM file. CImg& load_pnm(const char *const filename) { return _load_pnm(0,filename); } static CImg get_load_pnm(const char *const filename) { return CImg().load_pnm(filename); } //! Load an image from a PNM file. CImg& load_pnm(cimg_std::FILE *const file) { return _load_pnm(file,0); } static CImg get_load_pnm(cimg_std::FILE *const file) { return CImg().load_pnm(file); } CImg& _load_pnm(cimg_std::FILE *const file, const char *const filename) { if (!filename && !file) throw CImgArgumentException("CImg<%s>::load_pnm() : Cannot load (null) filename.", pixel_type()); cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); unsigned int ppm_type, W, H, colormax = 255; char item[1024] = { 0 }; int err, rval, gval, bval; const int cimg_iobuffer = 12*1024*1024; while ((err=cimg_std::fscanf(nfile,"%1023[^\n]",item))!=EOF && (item[0]=='#' || !err)) cimg_std::fgetc(nfile); if (cimg_std::sscanf(item," P%u",&ppm_type)!=1) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_pnm() : File '%s', PNM header 'P?' not found.", pixel_type(),filename?filename:"(FILE*)"); } while ((err=cimg_std::fscanf(nfile," %1023[^\n]",item))!=EOF && (item[0]=='#' || !err)) cimg_std::fgetc(nfile); if ((err=cimg_std::sscanf(item," %u %u %u",&W,&H,&colormax))<2) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_pnm() : File '%s', WIDTH and HEIGHT fields are not defined in PNM header.", pixel_type(),filename?filename:"(FILE*)"); } if (err==2) { while ((err=cimg_std::fscanf(nfile," %1023[^\n]",item))!=EOF && (item[0]=='#' || !err)) cimg_std::fgetc(nfile); if (cimg_std::sscanf(item,"%u",&colormax)!=1) cimg::warn("CImg<%s>::load_pnm() : File '%s', COLORMAX field is not defined in PNM header.", pixel_type(),filename?filename:"(FILE*)"); } cimg_std::fgetc(nfile); assign(); switch (ppm_type) { case 2 : { // Grey Ascii assign(W,H,1,1); T* rdata = data; cimg_foroff(*this,off) { if (cimg_std::fscanf(nfile,"%d",&rval)>0) *(rdata++) = (T)rval; else break; } } break; case 3 : { // Color Ascii assign(W,H,1,3); T *rdata = ptr(0,0,0,0), *gdata = ptr(0,0,0,1), *bdata = ptr(0,0,0,2); cimg_forXY(*this,x,y) { if (cimg_std::fscanf(nfile,"%d %d %d",&rval,&gval,&bval)==3) { *(rdata++) = (T)rval; *(gdata++) = (T)gval; *(bdata++) = (T)bval; } else break; } } break; case 5 : { // Grey Binary if (colormax<256) { // 8 bits CImg raw; assign(W,H,1,1); T *ptrd = ptr(0,0,0,0); for (int toread = (int)size(); toread>0; ) { raw.assign(cimg::min(toread,cimg_iobuffer)); cimg::fread(raw.data,raw.width,nfile); toread-=raw.width; const unsigned char *ptrs = raw.data; for (unsigned int off = raw.width; off; --off) *(ptrd++) = (T)*(ptrs++); } } else { // 16 bits CImg raw; assign(W,H,1,1); T *ptrd = ptr(0,0,0,0); for (int toread = (int)size(); toread>0; ) { raw.assign(cimg::min(toread,cimg_iobuffer/2)); cimg::fread(raw.data,raw.width,nfile); if (!cimg::endianness()) cimg::invert_endianness(raw.data,raw.width); toread-=raw.width; const unsigned short *ptrs = raw.data; for (unsigned int off = raw.width; off; --off) *(ptrd++) = (T)*(ptrs++); } } } break; case 6 : { // Color Binary if (colormax<256) { // 8 bits CImg raw; assign(W,H,1,3); T *ptr_r = ptr(0,0,0,0), *ptr_g = ptr(0,0,0,1), *ptr_b = ptr(0,0,0,2); for (int toread = (int)size(); toread>0; ) { raw.assign(cimg::min(toread,cimg_iobuffer)); cimg::fread(raw.data,raw.width,nfile); toread-=raw.width; const unsigned char *ptrs = raw.data; for (unsigned int off = raw.width/3; off; --off) { *(ptr_r++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_b++) = (T)*(ptrs++); } } } else { // 16 bits CImg raw; assign(W,H,1,3); T *ptr_r = ptr(0,0,0,0), *ptr_g = ptr(0,0,0,1), *ptr_b = ptr(0,0,0,2); for (int toread = (int)size(); toread>0; ) { raw.assign(cimg::min(toread,cimg_iobuffer/2)); cimg::fread(raw.data,raw.width,nfile); if (!cimg::endianness()) cimg::invert_endianness(raw.data,raw.width); toread-=raw.width; const unsigned short *ptrs = raw.data; for (unsigned int off = raw.width/3; off; --off) { *(ptr_r++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_b++) = (T)*(ptrs++); } } } } break; default : if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_pnm() : File '%s', PPM type 'P%d' not supported.", pixel_type(),filename?filename:"(FILE*)",ppm_type); } if (!file) cimg::fclose(nfile); return *this; } //! Load an image from a RGB file. CImg& load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { return _load_rgb(0,filename,dimw,dimh); } static CImg get_load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { return CImg().load_rgb(filename,dimw,dimh); } //! Load an image from a RGB file. CImg& load_rgb(cimg_std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { return _load_rgb(file,0,dimw,dimh); } static CImg get_load_rgb(cimg_std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { return CImg().load_rgb(file,dimw,dimh); } CImg& _load_rgb(cimg_std::FILE *const file, const char *const filename, const unsigned int dimw, const unsigned int dimh) { if (!filename && !file) throw CImgArgumentException("CImg<%s>::load_rgb() : Cannot load (null) filename.", pixel_type()); if (!dimw || !dimh) return assign(); const int cimg_iobuffer = 12*1024*1024; cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); CImg raw; assign(dimw,dimh,1,3); T *ptr_r = ptr(0,0,0,0), *ptr_g = ptr(0,0,0,1), *ptr_b = ptr(0,0,0,2); for (int toread = (int)size(); toread>0; ) { raw.assign(cimg::min(toread,cimg_iobuffer)); cimg::fread(raw.data,raw.width,nfile); toread-=raw.width; const unsigned char *ptrs = raw.data; for (unsigned int off = raw.width/3; off; --off) { *(ptr_r++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_b++) = (T)*(ptrs++); } } if (!file) cimg::fclose(nfile); return *this; } //! Load an image from a RGBA file. CImg& load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { return _load_rgba(0,filename,dimw,dimh); } static CImg get_load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { return CImg().load_rgba(filename,dimw,dimh); } //! Load an image from a RGBA file. CImg& load_rgba(cimg_std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { return _load_rgba(file,0,dimw,dimh); } static CImg get_load_rgba(cimg_std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { return CImg().load_rgba(file,dimw,dimh); } CImg& _load_rgba(cimg_std::FILE *const file, const char *const filename, const unsigned int dimw, const unsigned int dimh) { if (!filename && !file) throw CImgArgumentException("CImg<%s>::load_rgba() : Cannot load (null) filename.", pixel_type()); if (!dimw || !dimh) return assign(); const int cimg_iobuffer = 12*1024*1024; cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); CImg raw; assign(dimw,dimh,1,4); T *ptr_r = ptr(0,0,0,0), *ptr_g = ptr(0,0,0,1), *ptr_b = ptr(0,0,0,2), *ptr_a = ptr(0,0,0,3); for (int toread = (int)size(); toread>0; ) { raw.assign(cimg::min(toread,cimg_iobuffer)); cimg::fread(raw.data,raw.width,nfile); toread-=raw.width; const unsigned char *ptrs = raw.data; for (unsigned int off = raw.width/4; off; --off) { *(ptr_r++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_b++) = (T)*(ptrs++); *(ptr_a++) = (T)*(ptrs++); } } if (!file) cimg::fclose(nfile); return *this; } //! Load an image from a TIFF file. CImg& load_tiff(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1) { if (!filename) throw CImgArgumentException("CImg<%s>::load_tiff() : Cannot load (null) filename.", pixel_type()); const unsigned int nfirst_frame = first_frame1) throw CImgArgumentException("CImg<%s>::load_tiff() : File '%s', reading sub-images from a tiff file requires the use of libtiff.\n" "('cimg_use_tiff' must be defined).", pixel_type(),filename); return load_other(filename); #else TIFF *tif = TIFFOpen(filename,"r"); if (tif) { unsigned int nb_images = 0; do ++nb_images; while (TIFFReadDirectory(tif)); if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images)) cimg::warn("CImg<%s>::load_tiff() : File '%s' contains %u image(s), specified frame range is [%u,%u] (step %u).", pixel_type(),filename,nb_images,nfirst_frame,nlast_frame,nstep_frame); if (nfirst_frame>=nb_images) return assign(); if (nlast_frame>=nb_images) nlast_frame = nb_images-1; TIFFSetDirectory(tif,0); CImg frame; for (unsigned int l = nfirst_frame; l<=nlast_frame; l+=nstep_frame) { frame._load_tiff(tif,l); if (l==nfirst_frame) assign(frame.width,frame.height,1+(nlast_frame-nfirst_frame)/nstep_frame,frame.dim); if (frame.width>width || frame.height>height || frame.dim>dim) resize(cimg::max(frame.width,width),cimg::max(frame.height,height),-100,cimg::max(frame.dim,dim),0); draw_image(0,0,(l-nfirst_frame)/nstep_frame,frame); } TIFFClose(tif); } else throw CImgException("CImg<%s>::load_tiff() : File '%s' cannot be opened.", pixel_type(),filename); return *this; #endif } static CImg 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) { return CImg().load_tiff(filename,first_frame,last_frame,step_frame); } // (Original contribution by Jerome Boulanger). #ifdef cimg_use_tiff CImg& _load_tiff(TIFF *tif, const unsigned int directory) { if (!TIFFSetDirectory(tif,directory)) return assign(); uint16 samplesperpixel, bitspersample; uint32 nx,ny; const char *const filename = TIFFFileName(tif); TIFFGetField(tif,TIFFTAG_IMAGEWIDTH,&nx); TIFFGetField(tif,TIFFTAG_IMAGELENGTH,&ny); TIFFGetField(tif,TIFFTAG_SAMPLESPERPIXEL,&samplesperpixel); if (samplesperpixel!=1 && samplesperpixel!=3 && samplesperpixel!=4) { cimg::warn("CImg<%s>::load_tiff() : File '%s', unknow value for tag : TIFFTAG_SAMPLESPERPIXEL, will force it to 1.", pixel_type(),filename); samplesperpixel = 1; } TIFFGetFieldDefaulted(tif,TIFFTAG_BITSPERSAMPLE,&bitspersample); assign(nx,ny,1,samplesperpixel); if (bitspersample!=8 || !(samplesperpixel==3 || samplesperpixel==4)) { uint16 photo, config; TIFFGetField(tif,TIFFTAG_PLANARCONFIG,&config); TIFFGetField(tif,TIFFTAG_PHOTOMETRIC,&photo); if (TIFFIsTiled(tif)) { uint32 tw, th; TIFFGetField(tif,TIFFTAG_TILEWIDTH,&tw); TIFFGetField(tif,TIFFTAG_TILELENGTH,&th); if (config==PLANARCONFIG_CONTIG) switch (bitspersample) { case 8 : { unsigned char *buf = (unsigned char*)_TIFFmalloc(TIFFTileSize(tif)); if (buf) { for (unsigned int row = 0; row::load_tiff() : File '%s', an error occure while reading a tile.", pixel_type(),filename); } else { unsigned char *ptr = buf; for (unsigned int rr = row; rr::load_tiff() : File '%s', an error occure while reading a tile.", pixel_type(),filename); } else { unsigned short *ptr = buf; for (unsigned int rr = row; rr::load_tiff() : File '%s', an error occure while reading a tile.", pixel_type(),filename); } else { float *ptr = buf; for (unsigned int rr = row; rr::load_tiff() : File '%s', an error occure while reading a tile.", pixel_type(),filename); } else { unsigned char *ptr = buf; for (unsigned int rr = row; rr::load_tiff() : File '%s', an error occure while reading a tile.", pixel_type(),filename); } else { unsigned short *ptr = buf; for (unsigned int rr = row; rr::load_tiff() : File '%s', an error occure while reading a tile.", pixel_type(),filename); } else { float *ptr = buf; for (unsigned int rr = row; rrny?ny-row:rowsperstrip); tstrip_t strip = TIFFComputeStrip(tif, row, 0); if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { _TIFFfree(buf); TIFFClose(tif); throw CImgException("CImg<%s>::load_tiff() : File '%s', an error occure while reading a strip.", pixel_type(),filename); } unsigned char *ptr = buf; for (unsigned int rr = 0; rrny?ny-row:rowsperstrip); tstrip_t strip = TIFFComputeStrip(tif, row, 0); if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { _TIFFfree(buf); TIFFClose(tif); throw CImgException("CImg<%s>::load_tiff() : File '%s', error while reading a strip.", pixel_type(),filename); } unsigned short *ptr = buf; for (unsigned int rr = 0; rrny?ny-row:rowsperstrip); tstrip_t strip = TIFFComputeStrip(tif, row, 0); if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { _TIFFfree(buf); TIFFClose(tif); throw CImgException("CImg<%s>::load_tiff() : File '%s', error while reading a strip.", pixel_type(),filename); } float *ptr = buf; for (unsigned int rr = 0; rrny?ny-row:rowsperstrip); tstrip_t strip = TIFFComputeStrip(tif, row, vv); if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { _TIFFfree(buf); TIFFClose(tif); throw CImgException("CImg<%s>::load_tiff() : File '%s', an error occure while reading a strip.", pixel_type(),filename); } unsigned char *ptr = buf; for (unsigned int rr = 0;rrny?ny-row:rowsperstrip); tstrip_t strip = TIFFComputeStrip(tif, row, vv); if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { _TIFFfree(buf); TIFFClose(tif); throw CImgException("CImg<%s>::load_tiff() : File '%s', error while reading a strip.", pixel_type(),filename); } unsigned short *ptr = buf; for (unsigned int rr = 0; rrny?ny-row:rowsperstrip); tstrip_t strip = TIFFComputeStrip(tif, row, vv); if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { _TIFFfree(buf); TIFFClose(tif); throw CImgException("CImg<%s>::load_tiff() : File '%s', error while reading a strip.", pixel_type(),filename); } float *ptr = buf; for (unsigned int rr = 0; rr::load_tiff() : File '%s', not enough memory for buffer allocation.", pixel_type(),filename); } TIFFReadRGBAImage(tif,nx,ny,raster,0); switch (samplesperpixel) { case 1 : { cimg_forXY(*this,x,y) (*this)(x,y) = (T)(float)((raster[nx*(ny-1-y)+x]+ 128) / 257); } break; case 3 : { cimg_forXY(*this,x,y) { (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny-1-y)+x]); (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny-1-y)+x]); (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny-1-y)+x]); } } break; case 4 : { cimg_forXY(*this,x,y) { (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny-1-y)+x]); (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny-1-y)+x]); (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny-1-y)+x]); (*this)(x,y,3) = (T)(float)TIFFGetA(raster[nx*(ny-1-y)+x]); } } break; } _TIFFfree(raster); } return *this; } #endif //! Load an image from an ANALYZE7.5/NIFTI file. CImg& load_analyze(const char *const filename, float *const voxsize=0) { return _load_analyze(0,filename,voxsize); } static CImg get_load_analyze(const char *const filename, float *const voxsize=0) { return CImg().load_analyze(filename,voxsize); } //! Load an image from an ANALYZE7.5/NIFTI file. CImg& load_analyze(cimg_std::FILE *const file, float *const voxsize=0) { return _load_analyze(file,0,voxsize); } static CImg get_load_analyze(cimg_std::FILE *const file, float *const voxsize=0) { return CImg().load_analyze(file,voxsize); } CImg& _load_analyze(cimg_std::FILE *const file, const char *const filename, float *const voxsize=0) { if (!filename && !file) throw CImgArgumentException("CImg<%s>::load_analyze() : Cannot load (null) filename.", pixel_type()); cimg_std::FILE *nfile_header = 0, *nfile = 0; if (!file) { char body[1024]; const char *ext = cimg::split_filename(filename,body); if (!cimg::strcasecmp(ext,"hdr")) { // File is an Analyze header file. nfile_header = cimg::fopen(filename,"rb"); cimg_std::sprintf(body+cimg::strlen(body),".img"); nfile = cimg::fopen(body,"rb"); } else if (!cimg::strcasecmp(ext,"img")) { // File is an Analyze data file. nfile = cimg::fopen(filename,"rb"); cimg_std::sprintf(body+cimg::strlen(body),".hdr"); nfile_header = cimg::fopen(body,"rb"); } else nfile_header = nfile = cimg::fopen(filename,"rb"); // File is a Niftii file. } else nfile_header = nfile = file; // File is a Niftii file. if (!nfile || !nfile_header) throw CImgIOException("CImg<%s>::load_analyze() : File '%s', not recognized as an Analyze7.5 or NIFTI file.", pixel_type(),filename?filename:"(FILE*)"); // Read header. bool endian = false; unsigned int header_size; cimg::fread(&header_size,1,nfile_header); if (!header_size) throw CImgIOException("CImg<%s>::load_analyze() : File '%s', zero-sized header found.", pixel_type(),filename?filename:"(FILE*)"); if (header_size>=4096) { endian = true; cimg::invert_endianness(header_size); } unsigned char *header = new unsigned char[header_size]; cimg::fread(header+4,header_size-4,nfile_header); if (!file && nfile_header!=nfile) cimg::fclose(nfile_header); if (endian) { cimg::invert_endianness((short*)(header+40),5); cimg::invert_endianness((short*)(header+70),1); cimg::invert_endianness((short*)(header+72),1); cimg::invert_endianness((float*)(header+76),4); cimg::invert_endianness((float*)(header+112),1); } unsigned short *dim = (unsigned short*)(header+40), dimx = 1, dimy = 1, dimz = 1, dimv = 1; if (!dim[0]) cimg::warn("CImg<%s>::load_analyze() : File '%s', tells that image has zero dimensions.", pixel_type(),filename?filename:"(FILE*)"); if (dim[0]>4) cimg::warn("CImg<%s>::load_analyze() : File '%s', number of image dimension is %u, reading only the 4 first dimensions", pixel_type(),filename?filename:"(FILE*)",dim[0]); if (dim[0]>=1) dimx = dim[1]; if (dim[0]>=2) dimy = dim[2]; if (dim[0]>=3) dimz = dim[3]; if (dim[0]>=4) dimv = dim[4]; float scalefactor = *(float*)(header+112); if (scalefactor==0) scalefactor=1; const unsigned short datatype = *(short*)(header+70); if (voxsize) { const float *vsize = (float*)(header+76); voxsize[0] = vsize[1]; voxsize[1] = vsize[2]; voxsize[2] = vsize[3]; } delete[] header; // Read pixel data. assign(dimx,dimy,dimz,dimv); switch (datatype) { case 2 : { unsigned char *buffer = new unsigned char[dimx*dimy*dimz*dimv]; cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile); cimg_foroff(*this,off) data[off] = (T)(buffer[off]*scalefactor); delete[] buffer; } break; case 4 : { short *buffer = new short[dimx*dimy*dimz*dimv]; cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile); if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv); cimg_foroff(*this,off) data[off] = (T)(buffer[off]*scalefactor); delete[] buffer; } break; case 8 : { int *buffer = new int[dimx*dimy*dimz*dimv]; cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile); if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv); cimg_foroff(*this,off) data[off] = (T)(buffer[off]*scalefactor); delete[] buffer; } break; case 16 : { float *buffer = new float[dimx*dimy*dimz*dimv]; cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile); if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv); cimg_foroff(*this,off) data[off] = (T)(buffer[off]*scalefactor); delete[] buffer; } break; case 64 : { double *buffer = new double[dimx*dimy*dimz*dimv]; cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile); if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv); cimg_foroff(*this,off) data[off] = (T)(buffer[off]*scalefactor); delete[] buffer; } break; default : if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_analyze() : File '%s', cannot read images with 'datatype = %d'", pixel_type(),filename?filename:"(FILE*)",datatype); } if (!file) cimg::fclose(nfile); return *this; } //! Load an image (list) from a .cimg file. CImg& load_cimg(const char *const filename, const char axis='z', const char align='p') { CImgList list; list.load_cimg(filename); if (list.size==1) return list[0].transfer_to(*this); return assign(list.get_append(axis,align)); } static CImg get_load_cimg(const char *const filename, const char axis='z', const char align='p') { return CImg().load_cimg(filename,axis,align); } //! Load an image (list) from a .cimg file. CImg& load_cimg(cimg_std::FILE *const file, const char axis='z', const char align='p') { CImgList list; list.load_cimg(file); if (list.size==1) return list[0].transfer_to(*this); return assign(list.get_append(axis,align)); } static CImg get_load_cimg(cimg_std::FILE *const file, const char axis='z', const char align='p') { return CImg().load_cimg(file,axis,align); } //! Load a sub-image (list) from a .cimg file. CImg& 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 v0, const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1, const char axis='z', const char align='p') { CImgList list; list.load_cimg(filename,n0,n1,x0,y0,z0,v0,x1,y1,z1,v1); if (list.size==1) return list[0].transfer_to(*this); return assign(list.get_append(axis,align)); } static CImg 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 v0, const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1, const char axis='z', const char align='p') { return CImg().load_cimg(filename,n0,n1,x0,y0,z0,v0,x1,y1,z1,v1,axis,align); } //! Load a sub-image (list) from a non-compressed .cimg file. CImg& load_cimg(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 v0, const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1, const char axis='z', const char align='p') { CImgList list; list.load_cimg(file,n0,n1,x0,y0,z0,v0,x1,y1,z1,v1); if (list.size==1) return list[0].transfer_to(*this); return assign(list.get_append(axis,align)); } static CImg get_load_cimg(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 v0, const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1, const char axis='z', const char align='p') { return CImg().load_cimg(file,n0,n1,x0,y0,z0,v0,x1,y1,z1,v1,axis,align); } //! Load an image from an INRIMAGE-4 file. CImg& load_inr(const char *const filename, float *const voxsize=0) { return _load_inr(0,filename,voxsize); } static CImg get_load_inr(const char *const filename, float *const voxsize=0) { return CImg().load_inr(filename,voxsize); } //! Load an image from an INRIMAGE-4 file. CImg& load_inr(cimg_std::FILE *const file, float *const voxsize=0) { return _load_inr(file,0,voxsize); } static CImg get_load_inr(cimg_std::FILE *const file, float *voxsize=0) { return CImg().load_inr(file,voxsize); } // Load an image from an INRIMAGE-4 file (internal). static void _load_inr_header(cimg_std::FILE *file, int out[8], float *const voxsize) { char item[1024], tmp1[64], tmp2[64]; out[0] = cimg_std::fscanf(file,"%63s",item); out[0] = out[1] = out[2] = out[3] = out[5] = 1; out[4] = out[6] = out[7] = -1; if(cimg::strncasecmp(item,"#INRIMAGE-4#{",13)!=0) throw CImgIOException("CImg<%s>::load_inr() : File does not appear to be a valid INR file.\n" "(INRIMAGE-4 identifier not found)", pixel_type()); while (cimg_std::fscanf(file," %63[^\n]%*c",item)!=EOF && cimg::strncmp(item,"##}",3)) { cimg_std::sscanf(item," XDIM%*[^0-9]%d",out); cimg_std::sscanf(item," YDIM%*[^0-9]%d",out+1); cimg_std::sscanf(item," ZDIM%*[^0-9]%d",out+2); cimg_std::sscanf(item," VDIM%*[^0-9]%d",out+3); cimg_std::sscanf(item," PIXSIZE%*[^0-9]%d",out+6); if (voxsize) { cimg_std::sscanf(item," VX%*[^0-9.+-]%f",voxsize); cimg_std::sscanf(item," VY%*[^0-9.+-]%f",voxsize+1); cimg_std::sscanf(item," VZ%*[^0-9.+-]%f",voxsize+2); } if (cimg_std::sscanf(item," CPU%*[ =]%s",tmp1)) out[7]=cimg::strncasecmp(tmp1,"sun",3)?0:1; switch (cimg_std::sscanf(item," TYPE%*[ =]%s %s",tmp1,tmp2)) { case 0 : break; case 2 : out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0; cimg_std::strcpy(tmp1,tmp2); case 1 : if (!cimg::strncasecmp(tmp1,"int",3) || !cimg::strncasecmp(tmp1,"fixed",5)) out[4] = 0; if (!cimg::strncasecmp(tmp1,"float",5) || !cimg::strncasecmp(tmp1,"double",6)) out[4] = 1; if (!cimg::strncasecmp(tmp1,"packed",6)) out[4] = 2; if (out[4]>=0) break; default : throw CImgIOException("cimg::inr_header_read() : Invalid TYPE '%s'",tmp2); } } if(out[0]<0 || out[1]<0 || out[2]<0 || out[3]<0) throw CImgIOException("CImg<%s>::load_inr() : Bad dimensions in .inr file = ( %d , %d , %d , %d )", pixel_type(),out[0],out[1],out[2],out[3]); if(out[4]<0 || out[5]<0) throw CImgIOException("CImg<%s>::load_inr() : TYPE is not fully defined", pixel_type()); if(out[6]<0) throw CImgIOException("CImg<%s>::load_inr() : PIXSIZE is not fully defined", pixel_type()); if(out[7]<0) throw CImgIOException("CImg<%s>::load_inr() : Big/Little Endian coding type is not defined", pixel_type()); } CImg& _load_inr(cimg_std::FILE *const file, const char *const filename, float *const voxsize) { #define _cimg_load_inr_case(Tf,sign,pixsize,Ts) \ if (!loaded && fopt[6]==pixsize && fopt[4]==Tf && fopt[5]==sign) { \ Ts *xval, *val = new Ts[fopt[0]*fopt[3]]; \ cimg_forYZ(*this,y,z) { \ cimg::fread(val,fopt[0]*fopt[3],nfile); \ if (fopt[7]!=endian) cimg::invert_endianness(val,fopt[0]*fopt[3]); \ xval = val; cimg_forX(*this,x) cimg_forV(*this,k) (*this)(x,y,z,k) = (T)*(xval++); \ } \ delete[] val; \ loaded = true; \ } if (!filename && !file) throw CImgArgumentException("CImg<%s>::load_inr() : Cannot load (null) filename.", pixel_type()); cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); int fopt[8], endian=cimg::endianness()?1:0; bool loaded = false; if (voxsize) voxsize[0]=voxsize[1]=voxsize[2]=1; _load_inr_header(nfile,fopt,voxsize); assign(fopt[0],fopt[1],fopt[2],fopt[3]); _cimg_load_inr_case(0,0,8, unsigned char); _cimg_load_inr_case(0,1,8, char); _cimg_load_inr_case(0,0,16,unsigned short); _cimg_load_inr_case(0,1,16,short); _cimg_load_inr_case(0,0,32,unsigned int); _cimg_load_inr_case(0,1,32,int); _cimg_load_inr_case(1,0,32,float); _cimg_load_inr_case(1,1,32,float); _cimg_load_inr_case(1,0,64,double); _cimg_load_inr_case(1,1,64,double); if (!loaded) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_inr() : File '%s', cannot read images of the type specified in the file", pixel_type(),filename?filename:"(FILE*)"); } if (!file) cimg::fclose(nfile); return *this; } //! Load an image from a PANDORE file. CImg& load_pandore(const char *const filename) { return _load_pandore(0,filename); } static CImg get_load_pandore(const char *const filename) { return CImg().load_pandore(filename); } //! Load an image from a PANDORE file. CImg& load_pandore(cimg_std::FILE *const file) { return _load_pandore(file,0); } static CImg get_load_pandore(cimg_std::FILE *const file) { return CImg().load_pandore(file); } CImg& _load_pandore(cimg_std::FILE *const file, const char *const filename) { #define __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,ndim,stype) \ cimg::fread(dims,nbdim,nfile); \ if (endian) cimg::invert_endianness(dims,nbdim); \ assign(nwidth,nheight,ndepth,ndim); \ const unsigned int siz = size(); \ stype *buffer = new stype[siz]; \ cimg::fread(buffer,siz,nfile); \ if (endian) cimg::invert_endianness(buffer,siz); \ T *ptrd = data; \ cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); \ buffer-=siz; \ delete[] buffer #define _cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1,stype2,stype3,ltype) { \ if (sizeof(stype1)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1); } \ else if (sizeof(stype2)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype2); } \ else if (sizeof(stype3)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype3); } \ else throw CImgIOException("CImg<%s>::load_pandore() : File '%s' cannot be read, datatype not supported on this architecture.", \ pixel_type(),filename?filename:"(FILE*)"); } if (!filename && !file) throw CImgArgumentException("CImg<%s>::load_pandore() : Cannot load (null) filename.", pixel_type()); cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned int uint; typedef unsigned long ulong; char header[32]; cimg::fread(header,12,nfile); if (cimg::strncasecmp("PANDORE",header,7)) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_pandore() : File '%s' is not a valid PANDORE file, " "(PANDORE identifier not found).", pixel_type(),filename?filename:"(FILE*)"); } unsigned int imageid, dims[8]; cimg::fread(&imageid,1,nfile); const bool endian = (imageid>255); if (endian) cimg::invert_endianness(imageid); cimg::fread(header,20,nfile); switch (imageid) { case 2: _cimg_load_pandore_case(2,dims[1],1,1,1,uchar,uchar,uchar,1); break; case 3: _cimg_load_pandore_case(2,dims[1],1,1,1,long,int,short,4); break; case 4: _cimg_load_pandore_case(2,dims[1],1,1,1,double,float,float,4); break; case 5: _cimg_load_pandore_case(3,dims[2],dims[1],1,1,uchar,uchar,uchar,1); break; case 6: _cimg_load_pandore_case(3,dims[2],dims[1],1,1,long,int,short,4); break; case 7: _cimg_load_pandore_case(3,dims[2],dims[1],1,1,double,float,float,4); break; case 8: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,uchar,uchar,uchar,1); break; case 9: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,long,int,short,4); break; case 10: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,double,float,float,4); break; case 11 : { // Region 1D cimg::fread(dims,3,nfile); if (endian) cimg::invert_endianness(dims,3); assign(dims[1],1,1,1); const unsigned siz = size(); if (dims[2]<256) { unsigned char *buffer = new unsigned char[siz]; cimg::fread(buffer,siz,nfile); T *ptrd = data; cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); buffer-=siz; delete[] buffer; } else { if (dims[2]<65536) { unsigned short *buffer = new unsigned short[siz]; cimg::fread(buffer,siz,nfile); if (endian) cimg::invert_endianness(buffer,siz); T *ptrd = data; cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); buffer-=siz; delete[] buffer; } else { unsigned int *buffer = new unsigned int[siz]; cimg::fread(buffer,siz,nfile); if (endian) cimg::invert_endianness(buffer,siz); T *ptrd = data; cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); buffer-=siz; delete[] buffer; } } } break; case 12 : { // Region 2D cimg::fread(dims,4,nfile); if (endian) cimg::invert_endianness(dims,4); assign(dims[2],dims[1],1,1); const unsigned int siz = size(); if (dims[3]<256) { unsigned char *buffer = new unsigned char[siz]; cimg::fread(buffer,siz,nfile); T *ptrd = data; cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); buffer-=siz; delete[] buffer; } else { if (dims[3]<65536) { unsigned short *buffer = new unsigned short[siz]; cimg::fread(buffer,siz,nfile); if (endian) cimg::invert_endianness(buffer,siz); T *ptrd = data; cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); buffer-=siz; delete[] buffer; } else { unsigned long *buffer = new unsigned long[siz]; cimg::fread(buffer,siz,nfile); if (endian) cimg::invert_endianness(buffer,siz); T *ptrd = data; cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); buffer-=siz; delete[] buffer; } } } break; case 13 : { // Region 3D cimg::fread(dims,5,nfile); if (endian) cimg::invert_endianness(dims,5); assign(dims[3],dims[2],dims[1],1); const unsigned int siz = size(); if (dims[4]<256) { unsigned char *buffer = new unsigned char[siz]; cimg::fread(buffer,siz,nfile); T *ptrd = data; cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); buffer-=siz; delete[] buffer; } else { if (dims[4]<65536) { unsigned short *buffer = new unsigned short[siz]; cimg::fread(buffer,siz,nfile); if (endian) cimg::invert_endianness(buffer,siz); T *ptrd = data; cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); buffer-=siz; delete[] buffer; } else { unsigned int *buffer = new unsigned int[siz]; cimg::fread(buffer,siz,nfile); if (endian) cimg::invert_endianness(buffer,siz); T *ptrd = data; cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); buffer-=siz; delete[] buffer; } } } break; case 16: _cimg_load_pandore_case(4,dims[2],dims[1],1,3,uchar,uchar,uchar,1); break; case 17: _cimg_load_pandore_case(4,dims[2],dims[1],1,3,long,int,short,4); break; case 18: _cimg_load_pandore_case(4,dims[2],dims[1],1,3,double,float,float,4); break; case 19: _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,uchar,uchar,uchar,1); break; case 20: _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,long,int,short,4); break; case 21: _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,double,float,float,4); break; case 22: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],uchar,uchar,uchar,1); break; case 23: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],long,int,short,4); case 24: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],ulong,uint,ushort,4); break; case 25: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],double,float,float,4); break; case 26: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],uchar,uchar,uchar,1); break; case 27: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],long,int,short,4); break; case 28: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],ulong,uint,ushort,4); break; case 29: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],double,float,float,4); break; case 30: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],uchar,uchar,uchar,1); break; case 31: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],long,int,short,4); break; case 32: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],ulong,uint,ushort,4); break; case 33: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],double,float,float,4); break; case 34 : { // Points 1D int ptbuf[4]; cimg::fread(ptbuf,1,nfile); if (endian) cimg::invert_endianness(ptbuf,1); assign(1); (*this)(0) = (T)ptbuf[0]; } break; case 35 : { // Points 2D int ptbuf[4]; cimg::fread(ptbuf,2,nfile); if (endian) cimg::invert_endianness(ptbuf,2); assign(2); (*this)(0) = (T)ptbuf[1]; (*this)(1) = (T)ptbuf[0]; } break; case 36 : { // Points 3D int ptbuf[4]; cimg::fread(ptbuf,3,nfile); if (endian) cimg::invert_endianness(ptbuf,3); assign(3); (*this)(0) = (T)ptbuf[2]; (*this)(1) = (T)ptbuf[1]; (*this)(2) = (T)ptbuf[0]; } break; default : if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_pandore() : File '%s', cannot read images with ID_type = %u", pixel_type(),filename?filename:"(FILE*)",imageid); } if (!file) cimg::fclose(nfile); return *this; } //! Load an image from a PAR-REC (Philips) file. CImg& load_parrec(const char *const filename, const char axis='v', const char align='p') { CImgList list; list.load_parrec(filename); if (list.size==1) return list[0].transfer_to(*this); return assign(list.get_append(axis,align)); } static CImg get_load_parrec(const char *const filename, const char axis='v', const char align='p') { return CImg().load_parrec(filename,axis,align); } //! Load an image from a .RAW file. CImg& load_raw(const char *const filename, const unsigned int sizex, const unsigned int sizey=1, const unsigned int sizez=1, const unsigned int sizev=1, const bool multiplexed=false, const bool invert_endianness=false) { return _load_raw(0,filename,sizex,sizey,sizez,sizev,multiplexed,invert_endianness); } static CImg get_load_raw(const char *const filename, const unsigned int sizex, const unsigned int sizey=1, const unsigned int sizez=1, const unsigned int sizev=1, const bool multiplexed=false, const bool invert_endianness=false) { return CImg().load_raw(filename,sizex,sizey,sizez,sizev,multiplexed,invert_endianness); } //! Load an image from a .RAW file. CImg& load_raw(cimg_std::FILE *const file, const unsigned int sizex, const unsigned int sizey=1, const unsigned int sizez=1, const unsigned int sizev=1, const bool multiplexed=false, const bool invert_endianness=false) { return _load_raw(file,0,sizex,sizey,sizez,sizev,multiplexed,invert_endianness); } static CImg get_load_raw(cimg_std::FILE *const file, const unsigned int sizex, const unsigned int sizey=1, const unsigned int sizez=1, const unsigned int sizev=1, const bool multiplexed=false, const bool invert_endianness=false) { return CImg().load_raw(file,sizex,sizey,sizez,sizev,multiplexed,invert_endianness); } CImg& _load_raw(cimg_std::FILE *const file, const char *const filename, const unsigned int sizex, const unsigned int sizey, const unsigned int sizez, const unsigned int sizev, const bool multiplexed, const bool invert_endianness) { if (!filename && !file) throw CImgArgumentException("CImg<%s>::load_raw() : Cannot load (null) filename.", pixel_type()); assign(sizex,sizey,sizez,sizev,0); const unsigned int siz = size(); if (siz) { cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); if (!multiplexed) { cimg::fread(data,siz,nfile); if (invert_endianness) cimg::invert_endianness(data,siz); } else { CImg buf(1,1,1,sizev); cimg_forXYZ(*this,x,y,z) { cimg::fread(buf.data,sizev,nfile); if (invert_endianness) cimg::invert_endianness(buf.data,sizev); set_vector_at(buf,x,y,z); } } if (!file) cimg::fclose(nfile); } return *this; } //! Load a video sequence using FFMPEG av's libraries. CImg& 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 char align='p') { return get_load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format,resume,axis,align).transfer_to(*this); } static CImg 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 char align='p') { return CImgList().load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format,resume).get_append(axis,align); } //! Load an image sequence from a YUV file. CImg& load_yuv(const char *const filename, const unsigned int sizex, const unsigned int sizey=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', const char align='p') { return get_load_yuv(filename,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb,axis,align).transfer_to(*this); } static CImg get_load_yuv(const char *const filename, const unsigned int sizex, const unsigned int sizey=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', const char align='p') { return CImgList().load_yuv(filename,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb).get_append(axis,align); } //! Load an image sequence from a YUV file. CImg& load_yuv(cimg_std::FILE *const file, const unsigned int sizex, const unsigned int sizey=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', const char align='p') { return get_load_yuv(file,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb,axis,align).transfer_to(*this); } static CImg get_load_yuv(cimg_std::FILE *const file, const unsigned int sizex, const unsigned int sizey=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', const char align='p') { return CImgList().load_yuv(file,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb).get_append(axis,align); } //! Load a 3D object from a .OFF file. template CImg& load_off(const char *const filename, CImgList& primitives, CImgList& colors, const bool invert_faces=false) { return _load_off(0,filename,primitives,colors,invert_faces); } template static CImg get_load_off(const char *const filename, CImgList& primitives, CImgList& colors, const bool invert_faces=false) { return CImg().load_off(filename,primitives,colors,invert_faces); } //! Load a 3D object from a .OFF file. template CImg& load_off(cimg_std::FILE *const file, CImgList& primitives, CImgList& colors, const bool invert_faces=false) { return _load_off(file,0,primitives,colors,invert_faces); } template static CImg get_load_off(cimg_std::FILE *const file, CImgList& primitives, CImgList& colors, const bool invert_faces=false) { return CImg().load_off(file,primitives,colors,invert_faces); } template CImg& _load_off(cimg_std::FILE *const file, const char *const filename, CImgList& primitives, CImgList& colors, const bool invert_faces) { if (!filename && !file) throw CImgArgumentException("CImg<%s>::load_off() : Cannot load (null) filename.", pixel_type()); cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"r"); unsigned int nb_points = 0, nb_primitives = 0, nb_read = 0; char line[256] = { 0 }; int err; // Skip comments, and read magic string OFF do { err = cimg_std::fscanf(nfile,"%255[^\n] ",line); } while (!err || (err==1 && line[0]=='#')); if (cimg::strncasecmp(line,"OFF",3) && cimg::strncasecmp(line,"COFF",4)) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_off() : File '%s', keyword 'OFF' not found.", pixel_type(),filename?filename:"(FILE*)"); } do { err = cimg_std::fscanf(nfile,"%255[^\n] ",line); } while (!err || (err==1 && line[0]=='#')); if ((err = cimg_std::sscanf(line,"%u%u%*[^\n] ",&nb_points,&nb_primitives))!=2) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_off() : File '%s', invalid vertices/primitives numbers.", pixel_type(),filename?filename:"(FILE*)"); } // Read points data assign(nb_points,3); float X = 0, Y = 0, Z = 0; cimg_forX(*this,l) { do { err = cimg_std::fscanf(nfile,"%255[^\n] ",line); } while (!err || (err==1 && line[0]=='#')); if ((err = cimg_std::sscanf(line,"%f%f%f%*[^\n] ",&X,&Y,&Z))!=3) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_off() : File '%s', cannot read point %u/%u.\n", pixel_type(),filename?filename:"(FILE*)",l+1,nb_points); } (*this)(l,0) = (T)X; (*this)(l,1) = (T)Y; (*this)(l,2) = (T)Z; } // Read primitive data primitives.assign(); colors.assign(); bool stopflag = false; while (!stopflag) { float c0 = 0.7f, c1 = 0.7f, c2 = 0.7f; unsigned int prim = 0, i0 = 0, i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, i6 = 0, i7 = 0; line[0]='\0'; if ((err = cimg_std::fscanf(nfile,"%u",&prim))!=1) stopflag=true; else { ++nb_read; switch (prim) { case 1 : { if ((err = cimg_std::fscanf(nfile,"%u%255[^\n] ",&i0,line))<2) { cimg::warn("CImg<%s>::load_off() : File '%s', invalid primitive %u/%u.", pixel_type(),filename?filename:"(FILE*)",nb_read,nb_primitives); err = cimg_std::fscanf(nfile,"%*[^\n] "); } else { err = cimg_std::sscanf(line,"%f%f%f",&c0,&c1,&c2); primitives.insert(CImg::vector(i0)); colors.insert(CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); } } break; case 2 : { if ((err = cimg_std::fscanf(nfile,"%u%u%255[^\n] ",&i0,&i1,line))<2) { cimg::warn("CImg<%s>::load_off() : File '%s', invalid primitive %u/%u.", pixel_type(),filename?filename:"(FILE*)",nb_read,nb_primitives); err = cimg_std::fscanf(nfile,"%*[^\n] "); } else { err = cimg_std::sscanf(line,"%f%f%f",&c0,&c1,&c2); primitives.insert(CImg::vector(i0,i1)); colors.insert(CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); } } break; case 3 : { if ((err = cimg_std::fscanf(nfile,"%u%u%u%255[^\n] ",&i0,&i1,&i2,line))<3) { cimg::warn("CImg<%s>::load_off() : File '%s', invalid primitive %u/%u.", pixel_type(),filename?filename:"(FILE*)",nb_read,nb_primitives); err = cimg_std::fscanf(nfile,"%*[^\n] "); } else { err = cimg_std::sscanf(line,"%f%f%f",&c0,&c1,&c2); if (invert_faces) primitives.insert(CImg::vector(i0,i1,i2)); else primitives.insert(CImg::vector(i0,i2,i1)); colors.insert(CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); } } break; case 4 : { if ((err = cimg_std::fscanf(nfile,"%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,line))<4) { cimg::warn("CImg<%s>::load_off() : File '%s', invalid primitive %u/%u.", pixel_type(),filename?filename:"(FILE*)",nb_read,nb_primitives); err = cimg_std::fscanf(nfile,"%*[^\n] "); } else { err = cimg_std::sscanf(line,"%f%f%f",&c0,&c1,&c2); if (invert_faces) primitives.insert(CImg::vector(i0,i1,i2,i3)); else primitives.insert(CImg::vector(i0,i3,i2,i1)); colors.insert(CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255),(tc)(c2*255))); } } break; case 5 : { if ((err = cimg_std::fscanf(nfile,"%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,line))<5) { cimg::warn("CImg<%s>::load_off() : File '%s', invalid primitive %u/%u.", pixel_type(),filename?filename:"(FILE*)",nb_read,nb_primitives); err = cimg_std::fscanf(nfile,"%*[^\n] "); } else { err = cimg_std::sscanf(line,"%f%f%f",&c0,&c1,&c2); if (invert_faces) { primitives.insert(CImg::vector(i0,i1,i2,i3)); primitives.insert(CImg::vector(i0,i3,i4)); } else { primitives.insert(CImg::vector(i0,i3,i2,i1)); primitives.insert(CImg::vector(i0,i4,i3)); } colors.insert(2,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255),(tc)(c2*255))); ++nb_primitives; } } break; case 6 : { if ((err = cimg_std::fscanf(nfile,"%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,line))<6) { cimg::warn("CImg<%s>::load_off() : File '%s', invalid primitive %u/%u.", pixel_type(),filename?filename:"(FILE*)",nb_read,nb_primitives); err = cimg_std::fscanf(nfile,"%*[^\n] "); } else { err = cimg_std::sscanf(line,"%f%f%f",&c0,&c1,&c2); if (invert_faces) { primitives.insert(CImg::vector(i0,i1,i2,i3)); primitives.insert(CImg::vector(i0,i3,i4,i5)); } else { primitives.insert(CImg::vector(i0,i3,i2,i1)); primitives.insert(CImg::vector(i0,i5,i4,i3)); } colors.insert(2,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255),(tc)(c2*255))); ++nb_primitives; } } break; case 7 : { if ((err = cimg_std::fscanf(nfile,"%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,line))<7) { cimg::warn("CImg<%s>::load_off() : File '%s', invalid primitive %u/%u.", pixel_type(),filename?filename:"(FILE*)",nb_read,nb_primitives); err = cimg_std::fscanf(nfile,"%*[^\n] "); } else { err = cimg_std::sscanf(line,"%f%f%f",&c0,&c1,&c2); if (invert_faces) { primitives.insert(CImg::vector(i0,i1,i3,i4)); primitives.insert(CImg::vector(i0,i4,i5,i6)); primitives.insert(CImg::vector(i1,i2,i3)); } else { primitives.insert(CImg::vector(i0,i4,i3,i1)); primitives.insert(CImg::vector(i0,i6,i5,i4)); primitives.insert(CImg::vector(i3,i2,i1)); } colors.insert(2,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255),(tc)(c2*255))); ++(++nb_primitives); } } break; case 8 : { if ((err = cimg_std::fscanf(nfile,"%u%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,&i7,line))<7) { cimg::warn("CImg<%s>::load_off() : File '%s', invalid primitive %u/%u.", pixel_type(),filename?filename:"(FILE*)",nb_read,nb_primitives); err = cimg_std::fscanf(nfile,"%*[^\n] "); } else { err = cimg_std::sscanf(line,"%f%f%f",&c0,&c1,&c2); if (invert_faces) { primitives.insert(CImg::vector(i0,i1,i2,i3)); primitives.insert(CImg::vector(i0,i3,i4,i5)); primitives.insert(CImg::vector(i0,i5,i6,i7)); } else { primitives.insert(CImg::vector(i0,i3,i2,i1)); primitives.insert(CImg::vector(i0,i5,i4,i3)); primitives.insert(CImg::vector(i0,i7,i6,i5)); } colors.insert(2,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255),(tc)(c2*255))); ++(++nb_primitives); } } break; default : cimg::warn("CImg<%s>::load_off() : File '%s', invalid primitive %u/%u (%u vertices).", pixel_type(),filename?filename:"(FILE*)",nb_read,nb_primitives,prim); err = cimg_std::fscanf(nfile,"%*[^\n] "); } } } if (!file) cimg::fclose(nfile); if (primitives.size!=nb_primitives) cimg::warn("CImg<%s>::load_off() : File '%s', read only %u primitives instead of %u as claimed in the header.", pixel_type(),filename?filename:"(FILE*)",primitives.size,nb_primitives); return *this; } //! Load a video sequence using FFMPEG's external tool 'ffmpeg'. CImg& load_ffmpeg_external(const char *const filename, const char axis='z', const char align='p') { return get_load_ffmpeg_external(filename,axis,align).transfer_to(*this); } static CImg get_load_ffmpeg_external(const char *const filename, const char axis='z', const char align='p') { return CImgList().load_ffmpeg_external(filename).get_append(axis,align); } //! Load an image using GraphicsMagick's external tool 'gm'. CImg& load_graphicsmagick_external(const char *const filename) { if (!filename) throw CImgArgumentException("CImg<%s>::load_graphicsmagick_external() : Cannot load (null) filename.", pixel_type()); char command[1024], filetmp[512]; cimg_std::FILE *file = 0; #if cimg_OS==1 cimg_std::sprintf(command,"%s convert \"%s\" ppm:-",cimg::graphicsmagick_path(),filename); file = popen(command,"r"); if (file) { load_pnm(file); pclose(file); return *this; } #endif do { cimg_std::sprintf(filetmp,"%s%s%s.ppm",cimg::temporary_path(),cimg_OS==2?"\\":"/",cimg::filenamerand()); if ((file=cimg_std::fopen(filetmp,"rb"))!=0) cimg_std::fclose(file); } while (file); cimg_std::sprintf(command,"%s convert \"%s\" %s",cimg::graphicsmagick_path(),filename,filetmp); cimg::system(command,cimg::graphicsmagick_path()); if (!(file = cimg_std::fopen(filetmp,"rb"))) { cimg::fclose(cimg::fopen(filename,"r")); throw CImgIOException("CImg<%s>::load_graphicsmagick_external() : Failed to open image '%s'.\n\n" "Path of 'GraphicsMagick's gm' : \"%s\"\n" "Path of temporary filename : \"%s\"", pixel_type(),filename,cimg::graphicsmagick_path(),filetmp); } else cimg::fclose(file); load_pnm(filetmp); cimg_std::remove(filetmp); return *this; } static CImg get_load_graphicsmagick_external(const char *const filename) { return CImg().load_graphicsmagick_external(filename); } //! Load a gzipped image file, using external tool 'gunzip'. CImg& load_gzip_external(const char *const filename) { if (!filename) throw CImgIOException("CImg<%s>::load_gzip_external() : Cannot load (null) filename.", pixel_type()); char command[1024], filetmp[512], body[512]; const char *ext = cimg::split_filename(filename,body), *ext2 = cimg::split_filename(body,0); cimg_std::FILE *file = 0; do { if (!cimg::strcasecmp(ext,"gz")) { if (*ext2) cimg_std::sprintf(filetmp,"%s%s%s.%s",cimg::temporary_path(),cimg_OS==2?"\\":"/", cimg::filenamerand(),ext2); else cimg_std::sprintf(filetmp,"%s%s%s",cimg::temporary_path(),cimg_OS==2?"\\":"/", cimg::filenamerand()); } else { if (*ext) cimg_std::sprintf(filetmp,"%s%s%s.%s",cimg::temporary_path(),cimg_OS==2?"\\":"/", cimg::filenamerand(),ext); else cimg_std::sprintf(filetmp,"%s%s%s",cimg::temporary_path(),cimg_OS==2?"\\":"/", cimg::filenamerand()); } if ((file=cimg_std::fopen(filetmp,"rb"))!=0) cimg_std::fclose(file); } while (file); cimg_std::sprintf(command,"%s -c \"%s\" > %s",cimg::gunzip_path(),filename,filetmp); cimg::system(command); if (!(file = cimg_std::fopen(filetmp,"rb"))) { cimg::fclose(cimg::fopen(filename,"r")); throw CImgIOException("CImg<%s>::load_gzip_external() : File '%s' cannot be opened.", pixel_type(),filename); } else cimg::fclose(file); load(filetmp); cimg_std::remove(filetmp); return *this; } static CImg get_load_gzip_external(const char *const filename) { return CImg().load_gzip_external(filename); } //! Load an image using ImageMagick's external tool 'convert'. CImg& load_imagemagick_external(const char *const filename) { if (!filename) throw CImgArgumentException("CImg<%s>::load_imagemagick_external() : Cannot load (null) filename.", pixel_type()); char command[1024], filetmp[512]; cimg_std::FILE *file = 0; #if cimg_OS==1 cimg_std::sprintf(command,"%s \"%s\" ppm:-",cimg::imagemagick_path(),filename); file = popen(command,"r"); if (file) { load_pnm(file); pclose(file); return *this; } #endif do { cimg_std::sprintf(filetmp,"%s%s%s.ppm",cimg::temporary_path(),cimg_OS==2?"\\":"/",cimg::filenamerand()); if ((file=cimg_std::fopen(filetmp,"rb"))!=0) cimg_std::fclose(file); } while (file); cimg_std::sprintf(command,"%s \"%s\" %s",cimg::imagemagick_path(),filename,filetmp); cimg::system(command,cimg::imagemagick_path()); if (!(file = cimg_std::fopen(filetmp,"rb"))) { cimg::fclose(cimg::fopen(filename,"r")); throw CImgIOException("CImg<%s>::load_imagemagick_external() : Failed to open image '%s'.\n\n" "Path of 'ImageMagick's convert' : \"%s\"\n" "Path of temporary filename : \"%s\"", pixel_type(),filename,cimg::imagemagick_path(),filetmp); } else cimg::fclose(file); load_pnm(filetmp); cimg_std::remove(filetmp); return *this; } static CImg get_load_imagemagick_external(const char *const filename) { return CImg().load_imagemagick_external(filename); } //! Load a DICOM image file, using XMedcon's external tool 'medcon'. CImg& load_medcon_external(const char *const filename) { if (!filename) throw CImgArgumentException("CImg<%s>::load_medcon_external() : Cannot load (null) filename.", pixel_type()); char command[1024], filetmp[512], body[512]; cimg::fclose(cimg::fopen(filename,"r")); cimg_std::FILE *file = 0; do { cimg_std::sprintf(filetmp,"%s.hdr",cimg::filenamerand()); if ((file=cimg_std::fopen(filetmp,"rb"))!=0) cimg_std::fclose(file); } while (file); cimg_std::sprintf(command,"%s -w -c anlz -o %s -f %s",cimg::medcon_path(),filetmp,filename); cimg::system(command); cimg::split_filename(filetmp,body); cimg_std::sprintf(command,"m000-%s.hdr",body); file = cimg_std::fopen(command,"rb"); if (!file) { throw CImgIOException("CImg<%s>::load_medcon_external() : Failed to open image '%s'.\n\n" "Path of 'medcon' : \"%s\"\n" "Path of temporary filename : \"%s\"", pixel_type(),filename,cimg::medcon_path(),filetmp); } else cimg::fclose(file); load_analyze(command); cimg_std::remove(command); cimg_std::sprintf(command,"m000-%s.img",body); cimg_std::remove(command); return *this; } static CImg get_load_medcon_external(const char *const filename) { return CImg().load_medcon_external(filename); } //! Load a RAW Color Camera image file, using external tool 'dcraw'. CImg& load_dcraw_external(const char *const filename) { if (!filename) throw CImgArgumentException("CImg<%s>::load_dcraw_external() : Cannot load (null) filename.", pixel_type()); char command[1024], filetmp[512]; cimg_std::FILE *file = 0; #if cimg_OS==1 cimg_std::sprintf(command,"%s -4 -c \"%s\"",cimg::dcraw_path(),filename); file = popen(command,"r"); if (file) { load_pnm(file); pclose(file); return *this; } #endif do { cimg_std::sprintf(filetmp,"%s%s%s.ppm",cimg::temporary_path(),cimg_OS==2?"\\":"/",cimg::filenamerand()); if ((file=cimg_std::fopen(filetmp,"rb"))!=0) cimg_std::fclose(file); } while (file); cimg_std::sprintf(command,"%s -4 -c \"%s\" > %s",cimg::dcraw_path(),filename,filetmp); cimg::system(command,cimg::dcraw_path()); if (!(file = cimg_std::fopen(filetmp,"rb"))) { cimg::fclose(cimg::fopen(filename,"r")); throw CImgIOException("CImg<%s>::load_dcraw_external() : Failed to open image '%s'.\n\n" "Path of 'dcraw' : \"%s\"\n" "Path of temporary filename : \"%s\"", pixel_type(),filename,cimg::dcraw_path(),filetmp); } else cimg::fclose(file); load_pnm(filetmp); cimg_std::remove(filetmp); return *this; } static CImg get_load_dcraw_external(const char *const filename) { return CImg().load_dcraw_external(filename); } //! Load an image using ImageMagick's or GraphicsMagick's executables. CImg& load_other(const char *const filename) { if (!filename) throw CImgArgumentException("CImg<%s>::load_other() : Cannot load (null) filename.", pixel_type()); const unsigned int odebug = cimg::exception_mode(); cimg::exception_mode() = 0; try { load_magick(filename); } catch (CImgException&) { try { load_imagemagick_external(filename); } catch (CImgException&) { try { load_graphicsmagick_external(filename); } catch (CImgException&) { assign(); } } } cimg::exception_mode() = odebug; if (is_empty()) throw CImgIOException("CImg<%s>::load_other() : File '%s' cannot be opened.", pixel_type(),filename); return *this; } static CImg get_load_other(const char *const filename) { return CImg().load_other(filename); } //@} //--------------------------- // //! \name Image File Saving //@{ //--------------------------- //! Save the image as a file. /** The used file format is defined by the file extension in the filename \p filename. Parameter \p number can be used to add a 6-digit number to the filename before saving. **/ const CImg& save(const char *const filename, const int number=-1) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),filename?filename:"(null)",width,height,depth,dim,data); if (!filename) throw CImgArgumentException("CImg<%s>::save() : Instance image (%u,%u,%u,%u,%p) cannot be saved as a (null) filename.", pixel_type(),width,height,depth,dim,data); const char *ext = cimg::split_filename(filename); char nfilename[1024]; const char *const fn = (number>=0)?cimg::number_filename(filename,number,6,nfilename):filename; #ifdef cimg_save_plugin cimg_save_plugin(fn); #endif #ifdef cimg_save_plugin1 cimg_save_plugin1(fn); #endif #ifdef cimg_save_plugin2 cimg_save_plugin2(fn); #endif #ifdef cimg_save_plugin3 cimg_save_plugin3(fn); #endif #ifdef cimg_save_plugin4 cimg_save_plugin4(fn); #endif #ifdef cimg_save_plugin5 cimg_save_plugin5(fn); #endif #ifdef cimg_save_plugin6 cimg_save_plugin6(fn); #endif #ifdef cimg_save_plugin7 cimg_save_plugin7(fn); #endif #ifdef cimg_save_plugin8 cimg_save_plugin8(fn); #endif // ASCII formats if (!cimg::strcasecmp(ext,"asc")) return save_ascii(fn); if (!cimg::strcasecmp(ext,"dlm") || !cimg::strcasecmp(ext,"txt")) return save_dlm(fn); if (!cimg::strcasecmp(ext,"cpp") || !cimg::strcasecmp(ext,"hpp") || !cimg::strcasecmp(ext,"h") || !cimg::strcasecmp(ext,"c")) return save_cpp(fn); // 2D binary formats if (!cimg::strcasecmp(ext,"bmp")) return save_bmp(fn); if (!cimg::strcasecmp(ext,"jpg") || !cimg::strcasecmp(ext,"jpeg") || !cimg::strcasecmp(ext,"jpe") || !cimg::strcasecmp(ext,"jfif") || !cimg::strcasecmp(ext,"jif")) return save_jpeg(fn); if (!cimg::strcasecmp(ext,"rgb")) return save_rgb(fn); if (!cimg::strcasecmp(ext,"rgba")) return save_rgba(fn); if (!cimg::strcasecmp(ext,"png")) return save_png(fn); if (!cimg::strcasecmp(ext,"pgm") || !cimg::strcasecmp(ext,"ppm") || !cimg::strcasecmp(ext,"pnm")) return save_pnm(fn); if (!cimg::strcasecmp(ext,"tif") || !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn); // 3D binary formats if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true); if (!cimg::strcasecmp(ext,"cimg") || ext[0]=='\0') return save_cimg(fn,false); if (!cimg::strcasecmp(ext,"dcm")) return save_medcon_external(fn); if (!cimg::strcasecmp(ext,"hdr") || !cimg::strcasecmp(ext,"nii")) return save_analyze(fn); if (!cimg::strcasecmp(ext,"inr")) return save_inr(fn); if (!cimg::strcasecmp(ext,"pan")) return save_pandore(fn); if (!cimg::strcasecmp(ext,"raw")) return save_raw(fn); // Archive files if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn); // Image sequences if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,true); if (!cimg::strcasecmp(ext,"avi") || !cimg::strcasecmp(ext,"mov") || !cimg::strcasecmp(ext,"asf") || !cimg::strcasecmp(ext,"divx") || !cimg::strcasecmp(ext,"flv") || !cimg::strcasecmp(ext,"mpg") || !cimg::strcasecmp(ext,"m1v") || !cimg::strcasecmp(ext,"m2v") || !cimg::strcasecmp(ext,"m4v") || !cimg::strcasecmp(ext,"mjp") || !cimg::strcasecmp(ext,"mkv") || !cimg::strcasecmp(ext,"mpe") || !cimg::strcasecmp(ext,"movie") || !cimg::strcasecmp(ext,"ogm") || !cimg::strcasecmp(ext,"qt") || !cimg::strcasecmp(ext,"rm") || !cimg::strcasecmp(ext,"vob") || !cimg::strcasecmp(ext,"wmv") || !cimg::strcasecmp(ext,"xvid") || !cimg::strcasecmp(ext,"mpeg")) return save_ffmpeg(fn); return save_other(fn); } // Save the image as an ASCII file (ASCII Raw + simple header) (internal). const CImg& _save_ascii(cimg_std::FILE *const file, const char *const filename) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_ascii() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_ascii() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", pixel_type(),width,height,depth,dim,data); cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); cimg_std::fprintf(nfile,"%u %u %u %u\n",width,height,depth,dim); const T* ptrs = data; cimg_forYZV(*this,y,z,v) { cimg_forX(*this,x) cimg_std::fprintf(nfile,"%g ",(double)*(ptrs++)); cimg_std::fputc('\n',nfile); } if (!file) cimg::fclose(nfile); return *this; } //! Save the image as an ASCII file (ASCII Raw + simple header). const CImg& save_ascii(const char *const filename) const { return _save_ascii(0,filename); } //! Save the image as an ASCII file (ASCII Raw + simple header). const CImg& save_ascii(cimg_std::FILE *const file) const { return _save_ascii(file,0); } // Save the image as a C or CPP source file (internal). const CImg& _save_cpp(cimg_std::FILE *const file, const char *const filename) const { if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_cpp() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", pixel_type(),width,height,depth,dim,data); if (is_empty()) throw CImgInstanceException("CImg<%s>::save_cpp() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); char varname[1024] = { 0 }; if (filename) cimg_std::sscanf(cimg::basename(filename),"%1023[a-zA-Z0-9_]",varname); if (varname[0]=='\0') cimg_std::sprintf(varname,"unnamed"); cimg_std::fprintf(nfile, "/* Define image '%s' of size %ux%ux%ux%u and type '%s' */\n" "%s data_%s[] = { \n ", varname,width,height,depth,dim,pixel_type(),pixel_type(),varname); for (unsigned long off = 0, siz = size()-1; off<=siz; ++off) { cimg_std::fprintf(nfile,cimg::type::format(),cimg::type::format((*this)[off])); if (off==siz) cimg_std::fprintf(nfile," };\n"); else if (!((off+1)%16)) cimg_std::fprintf(nfile,",\n "); else cimg_std::fprintf(nfile,", "); } if (!file) cimg::fclose(nfile); return *this; } //! Save the image as a CPP source file. const CImg& save_cpp(const char *const filename) const { return _save_cpp(0,filename); } //! Save the image as a CPP source file. const CImg& save_cpp(cimg_std::FILE *const file) const { return _save_cpp(file,0); } // Save the image as a DLM file (internal). const CImg& _save_dlm(cimg_std::FILE *const file, const char *const filename) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_dlm() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_dlm() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", pixel_type(),width,height,depth,dim,data); if (depth>1) cimg::warn("CImg<%s>::save_dlm() : File '%s', instance image (%u,%u,%u,%u,%p) is volumetric. Pixel values along Z will be unrolled.", pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); if (dim>1) cimg::warn("CImg<%s>::save_dlm() : File '%s', instance image (%u,%u,%u,%u,%p) is multispectral. " "Pixel values along V will be unrolled.", pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); const T* ptrs = data; cimg_forYZV(*this,y,z,v) { cimg_forX(*this,x) cimg_std::fprintf(nfile,"%g%s",(double)*(ptrs++),(x==dimx()-1)?"":","); cimg_std::fputc('\n',nfile); } if (!file) cimg::fclose(nfile); return *this; } //! Save the image as a DLM file. const CImg& save_dlm(const char *const filename) const { return _save_dlm(0,filename); } //! Save the image as a DLM file. const CImg& save_dlm(cimg_std::FILE *const file) const { return _save_dlm(file,0); } // Save the image as a BMP file (internal). const CImg& _save_bmp(cimg_std::FILE *const file, const char *const filename) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_bmp() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_bmp() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", pixel_type(),width,height,depth,dim,data); if (depth>1) cimg::warn("CImg<%s>::save_bmp() : File '%s', instance image (%u,%u,%u,%u,%p) is volumetric. Only the first slice will be saved.", pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); if (dim>3) cimg::warn("CImg<%s>::save_bmp() : File '%s', instance image (%u,%u,%u,%u,%p) is multispectral. Only the three first channels will be saved.", pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); unsigned char header[54] = { 0 }, align_buf[4] = { 0 }; const unsigned int align = (4 - (3*width)%4)%4, buf_size = (3*width+align)*dimy(), file_size = 54 + buf_size; header[0] = 'B'; header[1] = 'M'; header[0x02] = file_size&0xFF; header[0x03] = (file_size>>8)&0xFF; header[0x04] = (file_size>>16)&0xFF; header[0x05] = (file_size>>24)&0xFF; header[0x0A] = 0x36; header[0x0E] = 0x28; header[0x12] = width&0xFF; header[0x13] = (width>>8)&0xFF; header[0x14] = (width>>16)&0xFF; header[0x15] = (width>>24)&0xFF; header[0x16] = height&0xFF; header[0x17] = (height>>8)&0xFF; header[0x18] = (height>>16)&0xFF; header[0x19] = (height>>24)&0xFF; header[0x1A] = 1; header[0x1B] = 0; header[0x1C] = 24; header[0x1D] = 0; header[0x22] = buf_size&0xFF; header[0x23] = (buf_size>>8)&0xFF; header[0x24] = (buf_size>>16)&0xFF; header[0x25] = (buf_size>>24)&0xFF; header[0x27] = 0x1; header[0x2B] = 0x1; cimg::fwrite(header,54,nfile); const T *pR = ptr(0,height-1,0,0), *pG = (dim>=2)?ptr(0,height-1,0,1):0, *pB = (dim>=3)?ptr(0,height-1,0,2):0; switch (dim) { case 1 : { cimg_forY(*this,y) { cimg_forX(*this,x) { const unsigned char val = (unsigned char)*(pR++); cimg_std::fputc(val,nfile); cimg_std::fputc(val,nfile); cimg_std::fputc(val,nfile); } cimg::fwrite(align_buf,align,nfile); pR-=2*width; }} break; case 2 : { cimg_forY(*this,y) { cimg_forX(*this,x) { cimg_std::fputc(0,nfile); cimg_std::fputc((unsigned char)(*(pG++)),nfile); cimg_std::fputc((unsigned char)(*(pR++)),nfile); } cimg::fwrite(align_buf,align,nfile); pR-=2*width; pG-=2*width; }} break; default : { cimg_forY(*this,y) { cimg_forX(*this,x) { cimg_std::fputc((unsigned char)(*(pB++)),nfile); cimg_std::fputc((unsigned char)(*(pG++)),nfile); cimg_std::fputc((unsigned char)(*(pR++)),nfile); } cimg::fwrite(align_buf,align,nfile); pR-=2*width; pG-=2*width; pB-=2*width; } } } if (!file) cimg::fclose(nfile); return *this; } //! Save the image as a BMP file. const CImg& save_bmp(const char *const filename) const { return _save_bmp(0,filename); } //! Save the image as a BMP file. const CImg& save_bmp(cimg_std::FILE *const file) const { return _save_bmp(file,0); } // Save a file in JPEG format (internal). const CImg& _save_jpeg(cimg_std::FILE *const file, const char *const filename, const unsigned int quality) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_jpeg() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_jpeg() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", pixel_type(),width,height,depth,dim,data); if (depth>1) cimg::warn("CImg<%s>::save_jpeg() : File '%s, instance image (%u,%u,%u,%u,%p) is volumetric. Only the first slice will be saved.", pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); #ifndef cimg_use_jpeg if (!file) return save_other(filename,quality); else throw CImgIOException("CImg<%s>::save_jpeg() : Cannot save a JPEG image in a *FILE output. Use libjpeg instead.", pixel_type()); #else // Fill pixel buffer unsigned char *buf; unsigned int dimbuf = 0; J_COLOR_SPACE colortype = JCS_RGB; switch (dim) { case 1 : { // Greyscale images unsigned char *buf2 = buf = new unsigned char[width*height*(dimbuf=1)]; colortype = JCS_GRAYSCALE; const T *ptr_g = data; cimg_forXY(*this,x,y) *(buf2++) = (unsigned char)*(ptr_g++); } break; case 2 : { // RG images unsigned char *buf2 = buf = new unsigned char[width*height*(dimbuf=3)]; const T *ptr_r = ptr(0,0,0,0), *ptr_g = ptr(0,0,0,1); colortype = JCS_RGB; cimg_forXY(*this,x,y) { *(buf2++) = (unsigned char)*(ptr_r++); *(buf2++) = (unsigned char)*(ptr_g++); *(buf2++) = 0; } } break; case 3 : { // RGB images unsigned char *buf2 = buf = new unsigned char[width*height*(dimbuf=3)]; const T *ptr_r = ptr(0,0,0,0), *ptr_g = ptr(0,0,0,1), *ptr_b = ptr(0,0,0,2); colortype = JCS_RGB; cimg_forXY(*this,x,y) { *(buf2++) = (unsigned char)*(ptr_r++); *(buf2++) = (unsigned char)*(ptr_g++); *(buf2++) = (unsigned char)*(ptr_b++); } } break; default : { // CMYK images unsigned char *buf2 = buf = new unsigned char[width*height*(dimbuf=4)]; const T *ptr_r = ptr(0,0,0,0), *ptr_g = ptr(0,0,0,1), *ptr_b = ptr(0,0,0,2), *ptr_a = ptr(0,0,0,3); colortype = JCS_CMYK; cimg_forXY(*this,x,y) { *(buf2++) = (unsigned char)*(ptr_r++); *(buf2++) = (unsigned char)*(ptr_g++); *(buf2++) = (unsigned char)*(ptr_b++); *(buf2++) = (unsigned char)*(ptr_a++); } } } // Call libjpeg functions struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; cinfo.err = jpeg_std_error(&jerr); jpeg_create_compress(&cinfo); cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); jpeg_stdio_dest(&cinfo,nfile); cinfo.image_width = width; cinfo.image_height = height; cinfo.input_components = dimbuf; cinfo.in_color_space = colortype; jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo,quality<100?quality:100,TRUE); jpeg_start_compress(&cinfo,TRUE); const unsigned int row_stride = width*dimbuf; JSAMPROW row_pointer[1]; while (cinfo.next_scanline < cinfo.image_height) { row_pointer[0] = &buf[cinfo.next_scanline*row_stride]; jpeg_write_scanlines(&cinfo,row_pointer,1); } jpeg_finish_compress(&cinfo); delete[] buf; if (!file) cimg::fclose(nfile); jpeg_destroy_compress(&cinfo); return *this; #endif } //! Save a file in JPEG format. const CImg& save_jpeg(const char *const filename, const unsigned int quality=100) const { return _save_jpeg(0,filename,quality); } //! Save a file in JPEG format. const CImg& save_jpeg(cimg_std::FILE *const file, const unsigned int quality=100) const { return _save_jpeg(file,0,quality); } //! Save the image using built-in ImageMagick++ library. const CImg& save_magick(const char *const filename) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_magick() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),filename?filename:"(null)",width,height,depth,dim,data); if (!filename) throw CImgArgumentException("CImg<%s>::save_magick() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", pixel_type(),width,height,depth,dim,data); #ifdef cimg_use_magick Magick::Image image(Magick::Geometry(width,height),"black"); image.type(Magick::TrueColorType); const T *rdata = ptr(0,0,0,0), *gdata = dim>1?ptr(0,0,0,1):0, *bdata = dim>2?ptr(0,0,0,2):0; Magick::PixelPacket *pixels = image.getPixels(0,0,width,height); switch (dim) { case 1 : // Scalar images for (unsigned int off = width*height; off; --off) { pixels->red = pixels->green = pixels->blue = Magick::Color::scaleDoubleToQuantum(*(rdata++)/255.0); ++pixels; } break; case 2 : // RG images for (unsigned int off = width*height; off; --off) { pixels->red = Magick::Color::scaleDoubleToQuantum(*(rdata++)/255.0); pixels->green = Magick::Color::scaleDoubleToQuantum(*(gdata++)/255.0); pixels->blue = 0; ++pixels; } break; default : // RGB images for (unsigned int off = width*height; off; --off) { pixels->red = Magick::Color::scaleDoubleToQuantum(*(rdata++)/255.0); pixels->green = Magick::Color::scaleDoubleToQuantum(*(gdata++)/255.0); pixels->blue = Magick::Color::scaleDoubleToQuantum(*(bdata++)/255.0); ++pixels; } } image.syncPixels(); image.write(filename); #else throw CImgIOException("CImg<%s>::save_magick() : File '%s', Magick++ library has not been linked.", pixel_type(),filename); #endif return *this; } // Save an image to a PNG file (internal). // Most of this function has been written by Eric Fausett const CImg& _save_png(cimg_std::FILE *const file, const char *const filename) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_png() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); if (!filename) throw CImgArgumentException("CImg<%s>::save_png() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", pixel_type(),width,height,depth,dim,data); if (depth>1) cimg::warn("CImg<%s>::save_png() : File '%s', instance image (%u,%u,%u,%u,%p) is volumetric. Only the first slice will be saved.", pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); #ifndef cimg_use_png if (!file) return save_other(filename); else throw CImgIOException("CImg<%s>::save_png() : Cannot save a PNG image in a *FILE output. You must use 'libpng' to do this instead.", pixel_type()); #else const char *volatile nfilename = filename; // two 'volatile' here to remove a g++ warning due to 'setjmp'. cimg_std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"wb"); // Setup PNG structures for write png_voidp user_error_ptr = 0; png_error_ptr user_error_fn = 0, user_warning_fn = 0; png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,user_error_ptr, user_error_fn, user_warning_fn); if(!png_ptr){ if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::save_png() : File '%s', error when initializing 'png_ptr' data structure.", pixel_type(),nfilename?nfilename:"(FILE*)"); } png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr,(png_infopp)0); if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::save_png() : File '%s', error when initializing 'info_ptr' data structure.", pixel_type(),nfilename?nfilename:"(FILE*)"); } if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::save_png() : File '%s', unknown fatal error.", pixel_type(),nfilename?nfilename:"(FILE*)"); } png_init_io(png_ptr, nfile); png_uint_32 width = dimx(), height = dimy(); float vmin, vmax = (float)maxmin(vmin); const int bit_depth = (vmin<0 || vmax>=256)?16:8; int color_type; switch (dimv()) { case 1 : color_type = PNG_COLOR_TYPE_GRAY; break; case 2 : color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break; case 3 : color_type = PNG_COLOR_TYPE_RGB; break; default : color_type = PNG_COLOR_TYPE_RGB_ALPHA; } const int interlace_type = PNG_INTERLACE_NONE; const int compression_type = PNG_COMPRESSION_TYPE_DEFAULT; const int filter_method = PNG_FILTER_TYPE_DEFAULT; png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, interlace_type,compression_type, filter_method); png_write_info(png_ptr, info_ptr); const int byte_depth = bit_depth>>3; const int numChan = dimv()>4?4:dimv(); const int pixel_bit_depth_flag = numChan * (bit_depth-1); // Allocate Memory for Image Save and Fill pixel data png_bytep *imgData = new png_byte*[height]; for (unsigned int row = 0; row::save_png() : File '%s', unknown fatal error.", pixel_type(),nfilename?nfilename:"(FILE*)"); } png_write_image(png_ptr, imgData); png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); // Deallocate Image Write Memory cimg_forY(*this,n) delete[] imgData[n]; delete[] imgData; if (!file) cimg::fclose(nfile); return *this; #endif } //! Save a file in PNG format const CImg& save_png(const char *const filename) const { return _save_png(0,filename); } //! Save a file in PNG format const CImg& save_png(cimg_std::FILE *const file) const { return _save_png(file,0); } // Save the image as a PNM file (internal function). const CImg& _save_pnm(cimg_std::FILE *const file, const char *const filename) const { if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_pnm() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", pixel_type(),width,height,depth,dim,data); if (is_empty()) throw CImgInstanceException("CImg<%s>::save_pnm() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); double stmin, stmax = (double)maxmin(stmin); if (depth>1) cimg::warn("CImg<%s>::save_pnm() : File '%s', instance image (%u,%u,%u,%u,%p) is volumetric. Only the first slice will be saved.", pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); if (dim>3) cimg::warn("CImg<%s>::save_pnm() : File '%s', instance image (%u,%u,%u,%u,%p) is multispectral. Only the three first channels will be saved.", pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); if (stmin<0 || stmax>65535) cimg::warn("CImg<%s>::save_pnm() : File '%s', instance image (%u,%u,%u,%u,%p) has pixel values in [%g,%g]. Probable type overflow.", pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data,stmin,stmax); cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); const T *ptrR = ptr(0,0,0,0), *ptrG = (dim>=2)?ptr(0,0,0,1):0, *ptrB = (dim>=3)?ptr(0,0,0,2):0; const unsigned int buf_size = width*height*(dim==1?1:3); cimg_std::fprintf(nfile,"P%c\n# CREATOR: CImg Library (original size = %ux%ux%ux%u)\n%u %u\n%u\n", (dim==1?'5':'6'),width,height,depth,dim,width,height,stmax<256?255:(stmax<4096?4095:65535)); switch (dim) { case 1 : { // Scalar image if (stmax<256) { // Binary PGM 8 bits unsigned char *ptrd = new unsigned char[buf_size], *xptrd = ptrd; cimg_forXY(*this,x,y) *(xptrd++) = (unsigned char)*(ptrR++); cimg::fwrite(ptrd,buf_size,nfile); delete[] ptrd; } else { // Binary PGM 16 bits unsigned short *ptrd = new unsigned short[buf_size], *xptrd = ptrd; cimg_forXY(*this,x,y) *(xptrd++) = (unsigned short)*(ptrR++); if (!cimg::endianness()) cimg::invert_endianness(ptrd,buf_size); cimg::fwrite(ptrd,buf_size,nfile); delete[] ptrd; } } break; case 2 : { // RG image if (stmax<256) { // Binary PPM 8 bits unsigned char *ptrd = new unsigned char[buf_size], *xptrd = ptrd; cimg_forXY(*this,x,y) { *(xptrd++) = (unsigned char)*(ptrR++); *(xptrd++) = (unsigned char)*(ptrG++); *(xptrd++) = 0; } cimg::fwrite(ptrd,buf_size,nfile); delete[] ptrd; } else { // Binary PPM 16 bits unsigned short *ptrd = new unsigned short[buf_size], *xptrd = ptrd; cimg_forXY(*this,x,y) { *(xptrd++) = (unsigned short)*(ptrR++); *(xptrd++) = (unsigned short)*(ptrG++); *(xptrd++) = 0; } if (!cimg::endianness()) cimg::invert_endianness(ptrd,buf_size); cimg::fwrite(ptrd,buf_size,nfile); delete[] ptrd; } } break; default : { // RGB image if (stmax<256) { // Binary PPM 8 bits unsigned char *ptrd = new unsigned char[buf_size], *xptrd = ptrd; cimg_forXY(*this,x,y) { *(xptrd++) = (unsigned char)*(ptrR++); *(xptrd++) = (unsigned char)*(ptrG++); *(xptrd++) = (unsigned char)*(ptrB++); } cimg::fwrite(ptrd,buf_size,nfile); delete[] ptrd; } else { // Binary PPM 16 bits unsigned short *ptrd = new unsigned short[buf_size], *xptrd = ptrd; cimg_forXY(*this,x,y) { *(xptrd++) = (unsigned short)*(ptrR++); *(xptrd++) = (unsigned short)*(ptrG++); *(xptrd++) = (unsigned short)*(ptrB++); } if (!cimg::endianness()) cimg::invert_endianness(ptrd,buf_size); cimg::fwrite(ptrd,buf_size,nfile); delete[] ptrd; } } } if (!file) cimg::fclose(nfile); return *this; } //! Save the image as a PNM file. const CImg& save_pnm(const char *const filename) const { return _save_pnm(0,filename); } //! Save the image as a PNM file. const CImg& save_pnm(cimg_std::FILE *const file) const { return _save_pnm(file,0); } // Save the image as a RGB file (internal). const CImg& _save_rgb(cimg_std::FILE *const file, const char *const filename) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_rgb() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_rgb() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", pixel_type(),width,height,depth,dim,data); if (dim!=3) cimg::warn("CImg<%s>::save_rgb() : File '%s', instance image (%u,%u,%u,%u,%p) has not exactly 3 channels.", pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); const unsigned int wh = width*height; unsigned char *buffer = new unsigned char[3*wh], *nbuffer=buffer; const T *ptr1 = ptr(0,0,0,0), *ptr2 = dim>1?ptr(0,0,0,1):0, *ptr3 = dim>2?ptr(0,0,0,2):0; switch (dim) { case 1 : { // Scalar image for (unsigned int k=0; k& save_rgb(const char *const filename) const { return _save_rgb(0,filename); } //! Save the image as a RGB file. const CImg& save_rgb(cimg_std::FILE *const file) const { return _save_rgb(file,0); } // Save the image as a RGBA file (internal). const CImg& _save_rgba(cimg_std::FILE *const file, const char *const filename) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_rgba() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_rgba() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", pixel_type(),width,height,depth,dim,data); if (dim!=4) cimg::warn("CImg<%s>::save_rgba() : File '%s, instance image (%u,%u,%u,%u,%p) has not exactly 4 channels.", pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); const unsigned int wh = width*height; unsigned char *buffer = new unsigned char[4*wh], *nbuffer=buffer; const T *ptr1 = ptr(0,0,0,0), *ptr2 = dim>1?ptr(0,0,0,1):0, *ptr3 = dim>2?ptr(0,0,0,2):0, *ptr4 = dim>3?ptr(0,0,0,3):0; switch (dim) { case 1 : { // Scalar images for (unsigned int k=0; k& save_rgba(const char *const filename) const { return _save_rgba(0,filename); } //! Save the image as a RGBA file. const CImg& save_rgba(cimg_std::FILE *const file) const { return _save_rgba(file,0); } // Save a plane into a tiff file #ifdef cimg_use_tiff const CImg& _save_tiff(TIFF *tif, const unsigned int directory) const { if (is_empty() || !tif) return *this; const char *const filename = TIFFFileName(tif); uint32 rowsperstrip = (uint32)-1; uint16 spp = dim, bpp = sizeof(T)*8, photometric, compression = COMPRESSION_NONE; if (spp==3 || spp==4) photometric = PHOTOMETRIC_RGB; else photometric = PHOTOMETRIC_MINISBLACK; TIFFSetDirectory(tif,directory); TIFFSetField(tif,TIFFTAG_IMAGEWIDTH,width); TIFFSetField(tif,TIFFTAG_IMAGELENGTH,height); TIFFSetField(tif,TIFFTAG_ORIENTATION,ORIENTATION_TOPLEFT); TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,spp); if (cimg::type::is_float()) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,3); else if (cimg::type::min()==0) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,1); else TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,2); TIFFSetField(tif,TIFFTAG_BITSPERSAMPLE,bpp); TIFFSetField(tif,TIFFTAG_PLANARCONFIG,PLANARCONFIG_CONTIG); TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,photometric); TIFFSetField(tif,TIFFTAG_COMPRESSION,compression); rowsperstrip = TIFFDefaultStripSize(tif,rowsperstrip); TIFFSetField(tif,TIFFTAG_ROWSPERSTRIP,rowsperstrip); TIFFSetField(tif,TIFFTAG_FILLORDER,FILLORDER_MSB2LSB); TIFFSetField(tif,TIFFTAG_SOFTWARE,"CImg"); T *buf = (T*)_TIFFmalloc(TIFFStripSize(tif)); if (buf){ for (unsigned int row = 0; rowheight?height-row:rowsperstrip); tstrip_t strip = TIFFComputeStrip(tif,row,0); tsize_t i = 0; for (unsigned int rr = 0; rr::save_tiff() : File '%s', an error has occured while writing a strip.", pixel_type(),filename?filename:"(FILE*)"); } _TIFFfree(buf); } TIFFWriteDirectory(tif); return (*this); } #endif //! Save a file in TIFF format. const CImg& save_tiff(const char *const filename) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_tiff() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),filename?filename:"(null)",width,height,depth,dim,data); if (!filename) throw CImgArgumentException("CImg<%s>::save_tiff() : Specified filename is (null) for instance image (%u,%u,%u,%u,%p).", pixel_type(),width,height,depth,dim,data); #ifdef cimg_use_tiff TIFF *tif = TIFFOpen(filename,"w"); if (tif) { cimg_forZ(*this,z) get_slice(z)._save_tiff(tif,z); TIFFClose(tif); } else throw CImgException("CImg<%s>::save_tiff() : File '%s', error while opening file stream for writing.", pixel_type(),filename); #else return save_other(filename); #endif return *this; } //! Save the image as an ANALYZE7.5 or NIFTI file. const CImg& save_analyze(const char *const filename, const float *const voxsize=0) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_analyze() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),filename?filename:"(null)",width,height,depth,dim,data); if (!filename) throw CImgArgumentException("CImg<%s>::save_analyze() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", pixel_type(),width,height,depth,dim,data); cimg_std::FILE *file; char header[348], hname[1024], iname[1024]; const char *ext = cimg::split_filename(filename); short datatype=-1; cimg_std::memset(header,0,348); if (!ext[0]) { cimg_std::sprintf(hname,"%s.hdr",filename); cimg_std::sprintf(iname,"%s.img",filename); } if (!cimg::strncasecmp(ext,"hdr",3)) { cimg_std::strcpy(hname,filename); cimg_std::strcpy(iname,filename); cimg_std::sprintf(iname+cimg::strlen(iname)-3,"img"); } if (!cimg::strncasecmp(ext,"img",3)) { cimg_std::strcpy(hname,filename); cimg_std::strcpy(iname,filename); cimg_std::sprintf(hname+cimg::strlen(iname)-3,"hdr"); } if (!cimg::strncasecmp(ext,"nii",3)) { cimg_std::strcpy(hname,filename); iname[0] = 0; } ((int*)(header))[0] = 348; cimg_std::sprintf(header+4,"CImg"); cimg_std::sprintf(header+14," "); ((short*)(header+36))[0] = 4096; ((char*)(header+38))[0] = 114; ((short*)(header+40))[0] = 4; ((short*)(header+40))[1] = width; ((short*)(header+40))[2] = height; ((short*)(header+40))[3] = depth; ((short*)(header+40))[4] = dim; if (!cimg::strcasecmp(pixel_type(),"bool")) datatype = 2; if (!cimg::strcasecmp(pixel_type(),"unsigned char")) datatype = 2; if (!cimg::strcasecmp(pixel_type(),"char")) datatype = 2; if (!cimg::strcasecmp(pixel_type(),"unsigned short")) datatype = 4; if (!cimg::strcasecmp(pixel_type(),"short")) datatype = 4; if (!cimg::strcasecmp(pixel_type(),"unsigned int")) datatype = 8; if (!cimg::strcasecmp(pixel_type(),"int")) datatype = 8; if (!cimg::strcasecmp(pixel_type(),"unsigned long")) datatype = 8; if (!cimg::strcasecmp(pixel_type(),"long")) datatype = 8; if (!cimg::strcasecmp(pixel_type(),"float")) datatype = 16; if (!cimg::strcasecmp(pixel_type(),"double")) datatype = 64; if (datatype<0) throw CImgIOException("CImg<%s>::save_analyze() : Cannot save image '%s' since pixel type (%s)" "is not handled in Analyze7.5 specifications.\n", pixel_type(),filename,pixel_type()); ((short*)(header+70))[0] = datatype; ((short*)(header+72))[0] = sizeof(T); ((float*)(header+112))[0] = 1; ((float*)(header+76))[0] = 0; if (voxsize) { ((float*)(header+76))[1] = voxsize[0]; ((float*)(header+76))[2] = voxsize[1]; ((float*)(header+76))[3] = voxsize[2]; } else ((float*)(header+76))[1] = ((float*)(header+76))[2] = ((float*)(header+76))[3] = 1; file = cimg::fopen(hname,"wb"); cimg::fwrite(header,348,file); if (iname[0]) { cimg::fclose(file); file = cimg::fopen(iname,"wb"); } cimg::fwrite(data,size(),file); cimg::fclose(file); return *this; } //! Save the image as a .cimg file. const CImg& save_cimg(const char *const filename, const bool compress=false) const { CImgList(*this,true).save_cimg(filename,compress); return *this; } // Save the image as a .cimg file. const CImg& save_cimg(cimg_std::FILE *const file, const bool compress=false) const { CImgList(*this,true).save_cimg(file,compress); return *this; } //! Insert the image into an existing .cimg file, at specified coordinates. const CImg& 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 v0) const { CImgList(*this,true).save_cimg(filename,n0,x0,y0,z0,v0); return *this; } //! Insert the image into an existing .cimg file, at specified coordinates. const CImg& save_cimg(cimg_std::FILE *const file, const unsigned int n0, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int v0) const { CImgList(*this,true).save_cimg(file,n0,x0,y0,z0,v0); return *this; } //! Save an empty .cimg file with specified dimensions. 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 dv=1) { return CImgList::save_empty_cimg(filename,1,dx,dy,dz,dv); } //! Save an empty .cimg file with specified dimensions. static void save_empty_cimg(cimg_std::FILE *const file, const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dv=1) { return CImgList::save_empty_cimg(file,1,dx,dy,dz,dv); } // Save the image as an INRIMAGE-4 file (internal). const CImg& _save_inr(cimg_std::FILE *const file, const char *const filename, const float *const voxsize) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_inr() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); if (!filename) throw CImgArgumentException("CImg<%s>::save_inr() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", pixel_type(),width,height,depth,dim,data); int inrpixsize=-1; const char *inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; if (!cimg::strcasecmp(pixel_type(),"unsigned char")) { inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1; } if (!cimg::strcasecmp(pixel_type(),"char")) { inrtype = "fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1; } if (!cimg::strcasecmp(pixel_type(),"unsigned short")) { inrtype = "unsigned fixed\nPIXSIZE=16 bits\nSCALE=2**0";inrpixsize = 2; } if (!cimg::strcasecmp(pixel_type(),"short")) { inrtype = "fixed\nPIXSIZE=16 bits\nSCALE=2**0"; inrpixsize = 2; } if (!cimg::strcasecmp(pixel_type(),"unsigned int")) { inrtype = "unsigned fixed\nPIXSIZE=32 bits\nSCALE=2**0";inrpixsize = 4; } if (!cimg::strcasecmp(pixel_type(),"int")) { inrtype = "fixed\nPIXSIZE=32 bits\nSCALE=2**0"; inrpixsize = 4; } if (!cimg::strcasecmp(pixel_type(),"float")) { inrtype = "float\nPIXSIZE=32 bits"; inrpixsize = 4; } if (!cimg::strcasecmp(pixel_type(),"double")) { inrtype = "float\nPIXSIZE=64 bits"; inrpixsize = 8; } if (inrpixsize<=0) throw CImgIOException("CImg<%s>::save_inr() : Don't know how to save images of '%s'", pixel_type(),pixel_type()); cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); char header[257]; int err = cimg_std::sprintf(header,"#INRIMAGE-4#{\nXDIM=%u\nYDIM=%u\nZDIM=%u\nVDIM=%u\n",width,height,depth,dim); if (voxsize) err += cimg_std::sprintf(header+err,"VX=%g\nVY=%g\nVZ=%g\n",voxsize[0],voxsize[1],voxsize[2]); err += cimg_std::sprintf(header+err,"TYPE=%s\nCPU=%s\n",inrtype,cimg::endianness()?"sun":"decm"); cimg_std::memset(header+err,'\n',252-err); cimg_std::memcpy(header+252,"##}\n",4); cimg::fwrite(header,256,nfile); cimg_forXYZ(*this,x,y,z) cimg_forV(*this,k) cimg::fwrite(&((*this)(x,y,z,k)),1,nfile); if (!file) cimg::fclose(nfile); return *this; } //! Save the image as an INRIMAGE-4 file. const CImg& save_inr(const char *const filename, const float *const voxsize=0) const { return _save_inr(0,filename,voxsize); } //! Save the image as an INRIMAGE-4 file. const CImg& save_inr(cimg_std::FILE *const file, const float *const voxsize=0) const { return _save_inr(file,0,voxsize); } // Save the image as a PANDORE-5 file (internal). unsigned int _save_pandore_header_length(unsigned int id, unsigned int *dims, const unsigned int colorspace) const { unsigned int nbdims = 0; if (id==2 || id==3 || id==4) { dims[0] = 1; dims[1] = width; nbdims = 2; } if (id==5 || id==6 || id==7) { dims[0] = 1; dims[1] = height; dims[2] = width; nbdims=3; } if (id==8 || id==9 || id==10) { dims[0] = dim; dims[1] = depth; dims[2] = height; dims[3] = width; nbdims = 4; } if (id==16 || id==17 || id==18) { dims[0] = 3; dims[1] = height; dims[2] = width; dims[3] = colorspace; nbdims = 4; } if (id==19 || id==20 || id==21) { dims[0] = 3; dims[1] = depth; dims[2] = height; dims[3] = width; dims[4] = colorspace; nbdims = 5; } if (id==22 || id==23 || id==25) { dims[0] = dim; dims[1] = width; nbdims = 2; } if (id==26 || id==27 || id==29) { dims[0] = dim; dims[1] = height; dims[2] = width; nbdims=3; } if (id==30 || id==31 || id==33) { dims[0] = dim; dims[1] = depth; dims[2] = height; dims[3] = width; nbdims = 4; } return nbdims; } const CImg& _save_pandore(cimg_std::FILE *const file, const char *const filename, const unsigned int colorspace) const { typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned int uint; typedef unsigned long ulong; #define __cimg_save_pandore_case(dtype) \ dtype *buffer = new dtype[size()]; \ const T *ptrs = data; \ cimg_foroff(*this,off) *(buffer++) = (dtype)(*(ptrs++)); \ buffer-=size(); \ cimg::fwrite(buffer,size(),nfile); \ delete[] buffer #define _cimg_save_pandore_case(sy,sz,sv,stype,id) \ if (!saved && (sy?(sy==height):true) && (sz?(sz==depth):true) && (sv?(sv==dim):true) && !cimg::strcmp(stype,pixel_type())) { \ unsigned int *iheader = (unsigned int*)(header+12); \ nbdims = _save_pandore_header_length((*iheader=id),dims,colorspace); \ cimg::fwrite(header,36,nfile); \ if (sizeof(ulong)==4) { ulong ndims[5]; for (int d = 0; d<5; ++d) ndims[d] = (ulong)dims[d]; cimg::fwrite(ndims,nbdims,nfile); } \ else if (sizeof(uint)==4) { uint ndims[5]; for (int d = 0; d<5; ++d) ndims[d] = (uint)dims[d]; cimg::fwrite(ndims,nbdims,nfile); } \ else if (sizeof(ushort)==4) { ushort ndims[5]; for (int d = 0; d<5; ++d) ndims[d] = (ushort)dims[d]; cimg::fwrite(ndims,nbdims,nfile); } \ else throw CImgIOException("CImg<%s>::save_pandore() : File '%s', instance image (%u,%u,%u,%u,%p), output type is not" \ "supported on this architecture.",pixel_type(),filename?filename:"(FILE*)",width,height, \ depth,dim,data); \ if (id==2 || id==5 || id==8 || id==16 || id==19 || id==22 || id==26 || id==30) { \ __cimg_save_pandore_case(uchar); \ } else if (id==3 || id==6 || id==9 || id==17 || id==20 || id==23 || id==27 || id==31) { \ if (sizeof(ulong)==4) { __cimg_save_pandore_case(ulong); } \ else if (sizeof(uint)==4) { __cimg_save_pandore_case(uint); } \ else if (sizeof(ushort)==4) { __cimg_save_pandore_case(ushort); } \ else throw CImgIOException("CImg<%s>::save_pandore() : File '%s', instance image (%u,%u,%u,%u,%p), output type is not" \ "supported on this architecture.",pixel_type(),filename?filename:"(FILE*)",width,height, \ depth,dim,data); \ } else if (id==4 || id==7 || id==10 || id==18 || id==21 || id==25 || id==29 || id==33) { \ if (sizeof(double)==4) { __cimg_save_pandore_case(double); } \ else if (sizeof(float)==4) { __cimg_save_pandore_case(float); } \ else throw CImgIOException("CImg<%s>::save_pandore() : File '%s', instance image (%u,%u,%u,%u,%p), output type is not" \ "supported on this architecture.",pixel_type(),filename?filename:"(FILE*)",width,height, \ depth,dim,data); \ } \ saved = true; \ } if (is_empty()) throw CImgInstanceException("CImg<%s>::save_pandore() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_pandore() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", pixel_type(),width,height,depth,dim,data); cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); unsigned char header[36] = { 'P','A','N','D','O','R','E','0','4',0,0,0, 0,0,0,0,'C','I','m','g',0,0,0,0,0,'N','o',' ','d','a','t','e',0,0,0,0 }; unsigned int nbdims, dims[5]; bool saved = false; _cimg_save_pandore_case(1,1,1,"unsigned char",2); _cimg_save_pandore_case(1,1,1,"char",3); _cimg_save_pandore_case(1,1,1,"short",3); _cimg_save_pandore_case(1,1,1,"unsigned short",3); _cimg_save_pandore_case(1,1,1,"unsigned int",3); _cimg_save_pandore_case(1,1,1,"int",3); _cimg_save_pandore_case(1,1,1,"unsigned long",4); _cimg_save_pandore_case(1,1,1,"long",3); _cimg_save_pandore_case(1,1,1,"float",4); _cimg_save_pandore_case(1,1,1,"double",4); _cimg_save_pandore_case(0,1,1,"unsigned char",5); _cimg_save_pandore_case(0,1,1,"char",6); _cimg_save_pandore_case(0,1,1,"short",6); _cimg_save_pandore_case(0,1,1,"unsigned short",6); _cimg_save_pandore_case(0,1,1,"unsigned int",6); _cimg_save_pandore_case(0,1,1,"int",6); _cimg_save_pandore_case(0,1,1,"unsigned long",7); _cimg_save_pandore_case(0,1,1,"long",6); _cimg_save_pandore_case(0,1,1,"float",7); _cimg_save_pandore_case(0,1,1,"double",7); _cimg_save_pandore_case(0,0,1,"unsigned char",8); _cimg_save_pandore_case(0,0,1,"char",9); _cimg_save_pandore_case(0,0,1,"short",9); _cimg_save_pandore_case(0,0,1,"unsigned short",9); _cimg_save_pandore_case(0,0,1,"unsigned int",9); _cimg_save_pandore_case(0,0,1,"int",9); _cimg_save_pandore_case(0,0,1,"unsigned long",10); _cimg_save_pandore_case(0,0,1,"long",9); _cimg_save_pandore_case(0,0,1,"float",10); _cimg_save_pandore_case(0,0,1,"double",10); _cimg_save_pandore_case(0,1,3,"unsigned char",16); _cimg_save_pandore_case(0,1,3,"char",17); _cimg_save_pandore_case(0,1,3,"short",17); _cimg_save_pandore_case(0,1,3,"unsigned short",17); _cimg_save_pandore_case(0,1,3,"unsigned int",17); _cimg_save_pandore_case(0,1,3,"int",17); _cimg_save_pandore_case(0,1,3,"unsigned long",18); _cimg_save_pandore_case(0,1,3,"long",17); _cimg_save_pandore_case(0,1,3,"float",18); _cimg_save_pandore_case(0,1,3,"double",18); _cimg_save_pandore_case(0,0,3,"unsigned char",19); _cimg_save_pandore_case(0,0,3,"char",20); _cimg_save_pandore_case(0,0,3,"short",20); _cimg_save_pandore_case(0,0,3,"unsigned short",20); _cimg_save_pandore_case(0,0,3,"unsigned int",20); _cimg_save_pandore_case(0,0,3,"int",20); _cimg_save_pandore_case(0,0,3,"unsigned long",21); _cimg_save_pandore_case(0,0,3,"long",20); _cimg_save_pandore_case(0,0,3,"float",21); _cimg_save_pandore_case(0,0,3,"double",21); _cimg_save_pandore_case(1,1,0,"unsigned char",22); _cimg_save_pandore_case(1,1,0,"char",23); _cimg_save_pandore_case(1,1,0,"short",23); _cimg_save_pandore_case(1,1,0,"unsigned short",23); _cimg_save_pandore_case(1,1,0,"unsigned int",23); _cimg_save_pandore_case(1,1,0,"int",23); _cimg_save_pandore_case(1,1,0,"unsigned long",25); _cimg_save_pandore_case(1,1,0,"long",23); _cimg_save_pandore_case(1,1,0,"float",25); _cimg_save_pandore_case(1,1,0,"double",25); _cimg_save_pandore_case(0,1,0,"unsigned char",26); _cimg_save_pandore_case(0,1,0,"char",27); _cimg_save_pandore_case(0,1,0,"short",27); _cimg_save_pandore_case(0,1,0,"unsigned short",27); _cimg_save_pandore_case(0,1,0,"unsigned int",27); _cimg_save_pandore_case(0,1,0,"int",27); _cimg_save_pandore_case(0,1,0,"unsigned long",29); _cimg_save_pandore_case(0,1,0,"long",27); _cimg_save_pandore_case(0,1,0,"float",29); _cimg_save_pandore_case(0,1,0,"double",29); _cimg_save_pandore_case(0,0,0,"unsigned char",30); _cimg_save_pandore_case(0,0,0,"char",31); _cimg_save_pandore_case(0,0,0,"short",31); _cimg_save_pandore_case(0,0,0,"unsigned short",31); _cimg_save_pandore_case(0,0,0,"unsigned int",31); _cimg_save_pandore_case(0,0,0,"int",31); _cimg_save_pandore_case(0,0,0,"unsigned long",33); _cimg_save_pandore_case(0,0,0,"long",31); _cimg_save_pandore_case(0,0,0,"float",33); _cimg_save_pandore_case(0,0,0,"double",33); if (!file) cimg::fclose(nfile); return *this; } //! Save the image as a PANDORE-5 file. const CImg& save_pandore(const char *const filename, const unsigned int colorspace=0) const { return _save_pandore(0,filename,colorspace); } //! Save the image as a PANDORE-5 file. const CImg& save_pandore(cimg_std::FILE *const file, const unsigned int colorspace=0) const { return _save_pandore(file,0,colorspace); } // Save the image as a RAW file (internal). const CImg& _save_raw(cimg_std::FILE *const file, const char *const filename, const bool multiplexed) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_raw() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_raw() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", pixel_type(),width,height,depth,dim,data); cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); if (!multiplexed) cimg::fwrite(data,size(),nfile); else { CImg buf(dim); cimg_forXYZ(*this,x,y,z) { cimg_forV(*this,k) buf[k] = (*this)(x,y,z,k); cimg::fwrite(buf.data,dim,nfile); } } if (!file) cimg::fclose(nfile); return *this; } //! Save the image as a RAW file. const CImg& save_raw(const char *const filename, const bool multiplexed=false) const { return _save_raw(0,filename,multiplexed); } //! Save the image as a RAW file. const CImg& save_raw(cimg_std::FILE *const file, const bool multiplexed=false) const { return _save_raw(file,0,multiplexed); } //! Save the image as a video sequence file, using FFMPEG library. const CImg& save_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int fps=25) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_ffmpeg() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),filename?filename:"(null)",width,height,depth,dim,data); if (!filename) throw CImgArgumentException("CImg<%s>::save_ffmpeg() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", pixel_type(),width,height,depth,dim,data); if (!fps) throw CImgArgumentException("CImg<%s>::save_ffmpeg() : File '%s', specified framerate is 0.", pixel_type(),filename); #ifndef cimg_use_ffmpeg return save_ffmpeg_external(filename,first_frame,last_frame); #else get_split('z').save_ffmpeg(filename,first_frame,last_frame,fps); #endif return *this; } //! Save the image as a YUV video sequence file. const CImg& save_yuv(const char *const filename, const bool rgb2yuv=true) const { get_split('z').save_yuv(filename,rgb2yuv); return *this; } //! Save the image as a YUV video sequence file. const CImg& save_yuv(cimg_std::FILE *const file, const bool rgb2yuv=true) const { get_split('z').save_yuv(file,rgb2yuv); return *this; } // Save OFF files (internal). template const CImg& _save_off(cimg_std::FILE *const file, const char *const filename, const CImgList& primitives, const CImgList& colors, const bool invert_faces) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_off() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_off() : Specified filename is (null).", pixel_type()); if (height<3) return get_resize(-100,3,1,1,0)._save_off(file,filename,primitives,colors,invert_faces); CImgList _colors; if (!colors) _colors.insert(primitives.size,CImg::vector(200,200,200)); const CImgList& ncolors = colors?colors:_colors; cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); cimg_std::fprintf(nfile,"OFF\n%u %u %u\n",width,primitives.size,3*primitives.size); cimg_forX(*this,i) cimg_std::fprintf(nfile,"%f %f %f\n",(float)((*this)(i,0)),(float)((*this)(i,1)),(float)((*this)(i,2))); cimglist_for(primitives,l) { const unsigned int prim = primitives[l].size(); const bool textured = (prim>4); const CImg& color = ncolors[l]; const unsigned int s = textured?color.dimv():color.size(); const float r = textured?(s>0?(float)(color.get_shared_channel(0).mean()/255.0f):1.0f):(s>0?(float)(color(0)/255.0f):1.0f), g = textured?(s>1?(float)(color.get_shared_channel(1).mean()/255.0f):r) :(s>1?(float)(color(1)/255.0f):r), b = textured?(s>2?(float)(color.get_shared_channel(2).mean()/255.0f):r) :(s>2?(float)(color(2)/255.0f):r); switch (prim) { case 1 : cimg_std::fprintf(nfile,"1 %u %f %f %f\n",(unsigned int)primitives(l,0),r,g,b); break; case 2 : case 6 : cimg_std::fprintf(nfile,"2 %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break; case 3 : case 9 : if (invert_faces) cimg_std::fprintf(nfile,"3 %u %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,1),(unsigned int)primitives(l,2),r,g,b); else cimg_std::fprintf(nfile,"3 %u %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,2),(unsigned int)primitives(l,1),r,g,b); break; case 4 : case 12 : if (invert_faces) cimg_std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n", (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),(unsigned int)primitives(l,2),(unsigned int)primitives(l,3),r,g,b); else cimg_std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n", (unsigned int)primitives(l,0),(unsigned int)primitives(l,3),(unsigned int)primitives(l,2),(unsigned int)primitives(l,1),r,g,b); break; } } if (!file) cimg::fclose(nfile); return *this; } //! Save OFF files. template const CImg& save_off(const char *const filename, const CImgList& primitives, const CImgList& colors, const bool invert_faces=false) const { return _save_off(0,filename,primitives,colors,invert_faces); } //! Save OFF files. template const CImg& save_off(cimg_std::FILE *const file, const CImgList& primitives, const CImgList& colors, const bool invert_faces=false) const { return _save_off(file,0,primitives,colors,invert_faces); } //! Save the image as a video sequence file, using the external tool 'ffmpeg'. const CImg& save_ffmpeg_external(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const char *const codec="mpeg2video") const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_ffmpeg_external() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),filename?filename:"(null)",width,height,depth,dim,data); if (!filename) throw CImgArgumentException("CImg<%s>::save_ffmpeg_external() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", pixel_type(),width,height,depth,dim,data); get_split('z').save_ffmpeg_external(filename,first_frame,last_frame,codec); return *this; } //! Save the image using GraphicsMagick's gm. /** Function that saves the image for other file formats that are not natively handled by CImg, using the tool 'gm' from the GraphicsMagick package.\n This is the case for all compressed image formats (GIF,PNG,JPG,TIF, ...). You need to install the GraphicsMagick package in order to get this function working properly (see http://www.graphicsmagick.org ). **/ const CImg& save_graphicsmagick_external(const char *const filename, const unsigned int quality=100) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_graphicsmagick_external() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),filename?filename:"(null)",width,height,depth,dim,data); if (!filename) throw CImgArgumentException("CImg<%s>::save_graphicsmagick_external() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", pixel_type(),width,height,depth,dim,data); char command[1024],filetmp[512]; cimg_std::FILE *file; do { if (dim==1) cimg_std::sprintf(filetmp,"%s%s%s.pgm",cimg::temporary_path(),cimg_OS==2?"\\":"/",cimg::filenamerand()); else cimg_std::sprintf(filetmp,"%s%s%s.ppm",cimg::temporary_path(),cimg_OS==2?"\\":"/",cimg::filenamerand()); if ((file=cimg_std::fopen(filetmp,"rb"))!=0) cimg_std::fclose(file); } while (file); save_pnm(filetmp); cimg_std::sprintf(command,"%s -quality %u%% %s \"%s\"",cimg::graphicsmagick_path(),quality,filetmp,filename); cimg::system(command); file = cimg_std::fopen(filename,"rb"); if (!file) throw CImgIOException("CImg<%s>::save_graphicsmagick_external() : Failed to save image '%s'.\n\n" "Path of 'gm' : \"%s\"\n" "Path of temporary filename : \"%s\"\n", pixel_type(),filename,cimg::graphicsmagick_path(),filetmp); if (file) cimg::fclose(file); cimg_std::remove(filetmp); return *this; } //! Save an image as a gzipped file, using external tool 'gzip'. const CImg& save_gzip_external(const char *const filename) const { if (!filename) throw CImgIOException("CImg<%s>::save_gzip_external() : Cannot save (null) filename.", pixel_type()); char command[1024], filetmp[512], body[512]; const char *ext = cimg::split_filename(filename,body), *ext2 = cimg::split_filename(body,0); cimg_std::FILE *file; do { if (!cimg::strcasecmp(ext,"gz")) { if (*ext2) cimg_std::sprintf(filetmp,"%s%s%s.%s",cimg::temporary_path(),cimg_OS==2?"\\":"/", cimg::filenamerand(),ext2); else cimg_std::sprintf(filetmp,"%s%s%s.cimg",cimg::temporary_path(),cimg_OS==2?"\\":"/", cimg::filenamerand()); } else { if (*ext) cimg_std::sprintf(filetmp,"%s%s%s.%s",cimg::temporary_path(),cimg_OS==2?"\\":"/", cimg::filenamerand(),ext); else cimg_std::sprintf(filetmp,"%s%s%s.cimg",cimg::temporary_path(),cimg_OS==2?"\\":"/", cimg::filenamerand()); } if ((file=cimg_std::fopen(filetmp,"rb"))!=0) cimg_std::fclose(file); } while (file); save(filetmp); cimg_std::sprintf(command,"%s -c %s > \"%s\"",cimg::gzip_path(),filetmp,filename); cimg::system(command); file = cimg_std::fopen(filename,"rb"); if (!file) throw CImgIOException("CImgList<%s>::save_gzip_external() : File '%s' cannot be saved.", pixel_type(),filename); else cimg::fclose(file); cimg_std::remove(filetmp); return *this; } //! Save the image using ImageMagick's convert. /** Function that saves the image for other file formats that are not natively handled by CImg, using the tool 'convert' from the ImageMagick package.\n This is the case for all compressed image formats (GIF,PNG,JPG,TIF, ...). You need to install the ImageMagick package in order to get this function working properly (see http://www.imagemagick.org ). **/ const CImg& save_imagemagick_external(const char *const filename, const unsigned int quality=100) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_imagemagick_external() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),filename?filename:"(null)",width,height,depth,dim,data); if (!filename) throw CImgArgumentException("CImg<%s>::save_imagemagick_external() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", pixel_type(),width,height,depth,dim,data); char command[1024], filetmp[512]; cimg_std::FILE *file; do { cimg_std::sprintf(filetmp,"%s%s%s.%s",cimg::temporary_path(),cimg_OS==2?"\\":"/",cimg::filenamerand(),dim==1?"pgm":"ppm"); if ((file=cimg_std::fopen(filetmp,"rb"))!=0) cimg_std::fclose(file); } while (file); save_pnm(filetmp); cimg_std::sprintf(command,"%s -quality %u%% %s \"%s\"",cimg::imagemagick_path(),quality,filetmp,filename); cimg::system(command); file = cimg_std::fopen(filename,"rb"); if (!file) throw CImgIOException("CImg<%s>::save_imagemagick_external() : Failed to save image '%s'.\n\n" "Path of 'convert' : \"%s\"\n" "Path of temporary filename : \"%s\"\n", pixel_type(),filename,cimg::imagemagick_path(),filetmp); if (file) cimg::fclose(file); cimg_std::remove(filetmp); return *this; } //! Save an image as a Dicom file (need '(X)Medcon' : http://xmedcon.sourceforge.net ) const CImg& save_medcon_external(const char *const filename) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_medcon_external() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),filename?filename:"(null)",width,height,depth,dim,data); if (!filename) throw CImgArgumentException("CImg<%s>::save_medcon_external() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", pixel_type(),width,height,depth,dim,data); char command[1024], filetmp[512], body[512]; cimg_std::FILE *file; do { cimg_std::sprintf(filetmp,"%s.hdr",cimg::filenamerand()); if ((file=cimg_std::fopen(filetmp,"rb"))!=0) cimg_std::fclose(file); } while (file); save_analyze(filetmp); cimg_std::sprintf(command,"%s -w -c dicom -o %s -f %s",cimg::medcon_path(),filename,filetmp); cimg::system(command); cimg_std::remove(filetmp); cimg::split_filename(filetmp,body); cimg_std::sprintf(filetmp,"%s.img",body); cimg_std::remove(filetmp); cimg_std::sprintf(command,"m000-%s",filename); file = cimg_std::fopen(command,"rb"); if (!file) { cimg::fclose(cimg::fopen(filename,"r")); throw CImgIOException("CImg<%s>::save_medcon_external() : Failed to save image '%s'.\n\n" "Path of 'medcon' : \"%s\"\n" "Path of temporary filename : \"%s\"", pixel_type(),filename,cimg::medcon_path(),filetmp); } else cimg::fclose(file); cimg_std::rename(command,filename); return *this; } // Try to save the image if other extension is provided. const CImg& save_other(const char *const filename, const unsigned int quality=100) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_other() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),filename?filename:"(null)",width,height,depth,dim,data); if (!filename) throw CImgIOException("CImg<%s>::save_other() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", pixel_type()); const unsigned int odebug = cimg::exception_mode(); bool is_saved = true; cimg::exception_mode() = 0; try { save_magick(filename); } catch (CImgException&) { try { save_imagemagick_external(filename,quality); } catch (CImgException&) { try { save_graphicsmagick_external(filename,quality); } catch (CImgException&) { is_saved = false; } } } cimg::exception_mode() = odebug; if (!is_saved) throw CImgIOException("CImg<%s>::save_other() : File '%s' cannot be saved.\n" "Check you have either the ImageMagick or GraphicsMagick package installed.", pixel_type(),filename); return *this; } // Get a 40x38 color logo of a 'danger' item (internal). static CImg logo40x38() { static bool first_time = true; static CImg res(40,38,1,3); if (first_time) { const unsigned char *ptrs = cimg::logo40x38; T *ptr1 = res.ptr(0,0,0,0), *ptr2 = res.ptr(0,0,0,1), *ptr3 = res.ptr(0,0,0,2); for (unsigned int off = 0; off structure # # # #------------------------------------------ */ //! Class representing list of images CImg. template struct CImgList { //! Size of the list (number of elements inside). unsigned int size; //! Allocation size of the list. unsigned int allocsize; //! Pointer to the first list element. CImg *data; //! Define a CImgList::iterator. typedef CImg* iterator; //! Define a CImgList::const_iterator. typedef const CImg* const_iterator; //! Get value type. typedef T value_type; // Define common T-dependant types. typedef typename cimg::superset::type Tbool; typedef typename cimg::superset::type Tuchar; typedef typename cimg::superset::type Tchar; typedef typename cimg::superset::type Tushort; typedef typename cimg::superset::type Tshort; typedef typename cimg::superset::type Tuint; typedef typename cimg::superset::type Tint; typedef typename cimg::superset::type Tulong; typedef typename cimg::superset::type Tlong; typedef typename cimg::superset::type Tfloat; typedef typename cimg::superset::type Tdouble; typedef typename cimg::last::type boolT; typedef typename cimg::last::type ucharT; typedef typename cimg::last::type charT; typedef typename cimg::last::type ushortT; typedef typename cimg::last::type shortT; typedef typename cimg::last::type uintT; typedef typename cimg::last::type intT; typedef typename cimg::last::type ulongT; typedef typename cimg::last::type longT; typedef typename cimg::last::type floatT; typedef typename cimg::last::type doubleT; //@} //--------------------------- // //! \name Plugins //@{ //--------------------------- #ifdef cimglist_plugin #include cimglist_plugin #endif #ifdef cimglist_plugin1 #include cimglist_plugin1 #endif #ifdef cimglist_plugin2 #include cimglist_plugin2 #endif #ifdef cimglist_plugin3 #include cimglist_plugin3 #endif #ifdef cimglist_plugin4 #include cimglist_plugin4 #endif #ifdef cimglist_plugin5 #include cimglist_plugin5 #endif #ifdef cimglist_plugin6 #include cimglist_plugin6 #endif #ifdef cimglist_plugin7 #include cimglist_plugin7 #endif #ifdef cimglist_plugin8 #include cimglist_plugin8 #endif //@} //------------------------------------------ // //! \name Constructors - Destructor - Copy //@{ //------------------------------------------ //! Destructor. ~CImgList() { if (data) delete[] data; } //! Default constructor. CImgList(): size(0),allocsize(0),data(0) {} //! Construct an image list containing n empty images. explicit CImgList(const unsigned int n): size(n) { data = new CImg[allocsize = cimg::max(16UL,cimg::nearest_pow2(n))]; } //! Default copy constructor. template CImgList(const CImgList& list): size(0),allocsize(0),data(0) { assign(list.size); cimglist_for(*this,l) data[l].assign(list[l],false); } CImgList(const CImgList& list): size(0),allocsize(0),data(0) { assign(list.size); cimglist_for(*this,l) data[l].assign(list[l],list[l].is_shared); } //! Advanced copy constructor. template CImgList(const CImgList& list, const bool shared): size(0),allocsize(0),data(0) { assign(list.size); if (shared) throw CImgArgumentException("CImgList<%s>::CImgList() : Cannot construct a list instance with shared images from " "a CImgList<%s> (different pixel types).", pixel_type(),CImgList::pixel_type()); cimglist_for(*this,l) data[l].assign(list[l],false); } CImgList(const CImgList& list, const bool shared): size(0),allocsize(0),data(0) { assign(list.size); cimglist_for(*this,l) data[l].assign(list[l],shared); } //! Construct an image list containing n images with specified size. CImgList(const unsigned int n, const unsigned int width, const unsigned int height=1, const unsigned int depth=1, const unsigned int dim=1): size(0),allocsize(0),data(0) { assign(n); cimglist_for(*this,l) data[l].assign(width,height,depth,dim); } //! Construct an image list containing n images with specified size, filled with specified value. CImgList(const unsigned int n, const unsigned int width, const unsigned int height, const unsigned int depth, const unsigned int dim, const T val): size(0),allocsize(0),data(0) { assign(n); cimglist_for(*this,l) data[l].assign(width,height,depth,dim,val); } //! Construct an image list containing n images with specified size and specified pixel values (int version). CImgList(const unsigned int n, const unsigned int width, const unsigned int height, const unsigned int depth, const unsigned int dim, const int val0, const int val1, ...): size(0),allocsize(0),data(0) { #define _CImgList_stdarg(t) { \ assign(n,width,height,depth,dim); \ const unsigned int siz = width*height*depth*dim, nsiz = siz*n; \ T *ptrd = data->data; \ va_list ap; \ va_start(ap,val1); \ for (unsigned int l=0, s=0, i=0; i CImgList(const unsigned int n, const CImg& img): size(0),allocsize(0),data(0) { assign(n); cimglist_for(*this,l) data[l].assign(img,img.is_shared); } //! Construct a list containing n copies of the image img, forcing the shared state. template CImgList(const unsigned int n, const CImg& img, const bool shared): size(0),allocsize(0),data(0) { assign(n); cimglist_for(*this,l) data[l].assign(img,shared); } //! Construct an image list from one image. template explicit CImgList(const CImg& img): size(0),allocsize(0),data(0) { assign(1); data[0].assign(img,img.is_shared); } //! Construct an image list from one image, forcing the shared state. template explicit CImgList(const CImg& img, const bool shared): size(0),allocsize(0),data(0) { assign(1); data[0].assign(img,shared); } //! Construct an image list from two images. template CImgList(const CImg& img1, const CImg& img2): size(0),allocsize(0),data(0) { assign(2); data[0].assign(img1,img1.is_shared); data[1].assign(img2,img2.is_shared); } //! Construct an image list from two images, forcing the shared state. template CImgList(const CImg& img1, const CImg& img2, const bool shared): size(0),allocsize(0),data(0) { assign(2); data[0].assign(img1,shared); data[1].assign(img2,shared); } //! Construct an image list from three images. template CImgList(const CImg& img1, const CImg& img2, const CImg& img3): size(0),allocsize(0),data(0) { assign(3); data[0].assign(img1,img1.is_shared); data[1].assign(img2,img2.is_shared); data[2].assign(img3,img3.is_shared); } //! Construct an image list from three images, forcing the shared state. template CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const bool shared): size(0),allocsize(0),data(0) { assign(3); data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); } //! Construct an image list from four images. template CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4): size(0),allocsize(0),data(0) { assign(4); data[0].assign(img1,img1.is_shared); data[1].assign(img2,img2.is_shared); data[2].assign(img3,img3.is_shared); data[3].assign(img4,img4.is_shared); } //! Construct an image list from four images, forcing the shared state. template CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const bool shared): size(0),allocsize(0),data(0) { assign(4); data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); } //! Construct an image list from five images. template CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5): size(0),allocsize(0),data(0) { assign(5); data[0].assign(img1,img1.is_shared); data[1].assign(img2,img2.is_shared); data[2].assign(img3,img3.is_shared); data[3].assign(img4,img4.is_shared); data[4].assign(img5,img5.is_shared); } //! Construct an image list from five images, forcing the shared state. template CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, const bool shared): size(0),allocsize(0),data(0) { assign(5); data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); data[4].assign(img5,shared); } //! Construct an image list from six images. template CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, const CImg& img6): size(0),allocsize(0),data(0) { assign(6); data[0].assign(img1,img1.is_shared); data[1].assign(img2,img2.is_shared); data[2].assign(img3,img3.is_shared); data[3].assign(img4,img4.is_shared); data[4].assign(img5,img5.is_shared); data[5].assign(img6,img6.is_shared); } //! Construct an image list from six images, forcing the shared state. template CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, const CImg& img6, const bool shared): size(0),allocsize(0),data(0) { assign(6); data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); data[4].assign(img5,shared); data[5].assign(img6,shared); } //! Construct an image list from seven images. template CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, const CImg& img6, const CImg& img7): size(0),allocsize(0),data(0) { assign(7); data[0].assign(img1,img1.is_shared); data[1].assign(img2,img2.is_shared); data[2].assign(img3,img3.is_shared); data[3].assign(img4,img4.is_shared); data[4].assign(img5,img5.is_shared); data[5].assign(img6,img6.is_shared); data[6].assign(img7,img7.is_shared); } //! Construct an image list from seven images, forcing the shared state. template CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, const CImg& img6, const CImg& img7, const bool shared): size(0),allocsize(0),data(0) { assign(7); data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); data[4].assign(img5,shared); data[5].assign(img6,shared); data[6].assign(img7,shared); } //! Construct an image list from eight images. template CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, const CImg& img6, const CImg& img7, const CImg& img8): size(0),allocsize(0),data(0) { assign(8); data[0].assign(img1,img1.is_shared); data[1].assign(img2,img2.is_shared); data[2].assign(img3,img3.is_shared); data[3].assign(img4,img4.is_shared); data[4].assign(img5,img5.is_shared); data[5].assign(img6,img6.is_shared); data[6].assign(img7,img7.is_shared); data[7].assign(img8,img8.is_shared); } //! Construct an image list from eight images, forcing the shared state. template CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, const CImg& img6, const CImg& img7, const CImg& img8, const bool shared): size(0),allocsize(0),data(0) { assign(8); data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); data[4].assign(img5,shared); data[5].assign(img6,shared); data[6].assign(img7,shared); data[7].assign(img8,shared); } //! Construct an image list from a filename. CImgList(const char *const filename): size(0),allocsize(0),data(0) { assign(filename); } //! In-place version of the default constructor and default destructor. CImgList& assign() { if (data) delete[] data; size = allocsize = 0; data = 0; return *this; } //! Equivalent to assign() (STL-compliant name). CImgList& clear() { return assign(); } //! In-place version of the corresponding constructor. CImgList& assign(const unsigned int n) { if (n) { if (allocsize(n<<2)) { if (data) delete[] data; data = new CImg[allocsize=cimg::max(16UL,cimg::nearest_pow2(n))]; } size = n; } else assign(); return *this; } //! In-place version of the corresponding constructor. CImgList& assign(const unsigned int n, const unsigned int width, const unsigned int height=1, const unsigned int depth=1, const unsigned int dim=1) { assign(n); cimglist_for(*this,l) data[l].assign(width,height,depth,dim); return *this; } //! In-place version of the corresponding constructor. CImgList& assign(const unsigned int n, const unsigned int width, const unsigned int height, const unsigned int depth, const unsigned int dim, const T val) { assign(n); cimglist_for(*this,l) data[l].assign(width,height,depth,dim,val); return *this; } //! In-place version of the corresponding constructor. CImgList& assign(const unsigned int n, const unsigned int width, const unsigned int height, const unsigned int depth, const unsigned int dim, const int val0, const int val1, ...) { _CImgList_stdarg(int); return *this; } //! In-place version of the corresponding constructor. CImgList& assign(const unsigned int n, const unsigned int width, const unsigned int height, const unsigned int depth, const unsigned int dim, const double val0, const double val1, ...) { _CImgList_stdarg(double); return *this; } //! In-place version of the copy constructor. template CImgList& assign(const CImgList& list) { assign(list.size); cimglist_for(*this,l) data[l].assign(list[l],list[l].is_shared); return *this; } //! In-place version of the copy constructor. template CImgList& assign(const CImgList& list, const bool shared) { assign(list.size); cimglist_for(*this,l) data[l].assign(list[l],shared); return *this; } //! In-place version of the corresponding constructor. template CImgList& assign(const unsigned int n, const CImg& img, const bool shared=false) { assign(n); cimglist_for(*this,l) data[l].assign(img,shared); return *this; } //! In-place version of the corresponding constructor. template CImgList& assign(const CImg& img, const bool shared=false) { assign(1); data[0].assign(img,shared); return *this; } //! In-place version of the corresponding constructor. template CImgList& assign(const CImg& img1, const CImg& img2, const bool shared=false) { assign(2); data[0].assign(img1,shared); data[1].assign(img2,shared); return *this; } //! In-place version of the corresponding constructor. template CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const bool shared=false) { assign(3); data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); return *this; } //! In-place version of the corresponding constructor. template CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const bool shared=false) { assign(4); data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); return *this; } //! In-place version of the corresponding constructor. template CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, const bool shared=false) { assign(5); data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); data[4].assign(img5,shared); return *this; } //! In-place version of the corresponding constructor. template CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, const CImg& img6, const bool shared=false) { assign(6); data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); data[4].assign(img5,shared); data[5].assign(img6,shared); return *this; } //! In-place version of the corresponding constructor. template CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, const CImg& img6, const CImg& img7, const bool shared=false) { assign(7); data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); data[4].assign(img5,shared); data[5].assign(img6,shared); data[6].assign(img7,shared); return *this; } //! In-place version of the corresponding constructor. template CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, const CImg& img6, const CImg& img7, const CImg& img8, const bool shared=false) { assign(8); data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); data[4].assign(img5,shared); data[5].assign(img6,shared); data[6].assign(img7,shared); data[7].assign(img8,shared); return *this; } //! In-place version of the corresponding constructor. CImgList& assign(const char *const filename) { return load(filename); } //! Transfer the content of the instance image list into another one. template CImgList& transfer_to(CImgList& list) { list.assign(*this); assign(); return list; } CImgList& transfer_to(CImgList& list) { list.assign(); return swap(list); } //! Swap all fields of two CImgList instances (use with care !) CImgList& swap(CImgList& list) { cimg::swap(size,list.size); cimg::swap(allocsize,list.allocsize); cimg::swap(data,list.data); return list; } //! Return a string describing the type of the image pixels in the list (template parameter \p T). static const char* pixel_type() { return cimg::type::string(); } //! Return \p true if list is empty. bool is_empty() const { return (!data || !size); } //! Return \p true if list is not empty. operator bool() const { return !is_empty(); } //! Return \p true if list if of specified size. bool is_sameN(const unsigned int n) const { return (size==n); } //! Return \p true if list if of specified size. template bool is_sameN(const CImgList& list) const { return (size==list.size); } // Define useful dimension check functions. // (not documented because they are macro-generated). #define _cimglist_def_is_same1(axis) \ bool is_same##axis(const unsigned int val) const { \ bool res = true; for (unsigned int l = 0; l bool is_same##axis(const CImg& img) const { \ bool res = true; for (unsigned int l = 0; l bool is_same##axis(const CImgList& list) const { \ const unsigned int lmin = cimg::min(size,list.size); \ bool res = true; for (unsigned int l = 0; l bool is_sameN##axis(const unsigned int n, const CImg& img) const { \ return (is_sameN(n) && is_same##axis(img)); \ } \ template bool is_sameN##axis(const CImgList& list) const { \ return (is_sameN(list) && is_same##axis(list)); \ } _cimglist_def_is_same(XY) _cimglist_def_is_same(XZ) _cimglist_def_is_same(XV) _cimglist_def_is_same(YZ) _cimglist_def_is_same(YV) _cimglist_def_is_same(XYZ) _cimglist_def_is_same(XYV) _cimglist_def_is_same(YZV) _cimglist_def_is_same(XYZV) _cimglist_def_is_same1(X) _cimglist_def_is_same1(Y) _cimglist_def_is_same1(Z) _cimglist_def_is_same1(V) _cimglist_def_is_same2(X,Y) _cimglist_def_is_same2(X,Z) _cimglist_def_is_same2(X,V) _cimglist_def_is_same2(Y,Z) _cimglist_def_is_same2(Y,V) _cimglist_def_is_same2(Z,V) _cimglist_def_is_same3(X,Y,Z) _cimglist_def_is_same3(X,Y,V) _cimglist_def_is_same3(X,Z,V) _cimglist_def_is_same3(Y,Z,V) bool is_sameXYZV(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv) const { bool res = true; for (unsigned int l = 0; l=0 && n<(int)size && x>=0 && x=0 && y=0 && z=0 && v=0 && n<(int)size; } //! Return \c true if one of the image list contains the specified referenced value. If true, set coordinates (n,x,y,z,v). template bool contains(const T& pixel, t& n, t& x, t&y, t& z, t& v) const { if (is_empty()) return false; cimglist_for(*this,l) if (data[l].contains(pixel,x,y,z,v)) { n = (t)l; return true; } return false; } //! Return \c true if one of the image list contains the specified referenced value. If true, set coordinates (n,x,y,z). template bool contains(const T& pixel, t& n, t& x, t&y, t& z) const { t v; return contains(pixel,n,x,y,z,v); } //! Return \c true if one of the image list contains the specified referenced value. If true, set coordinates (n,x,y). template bool contains(const T& pixel, t& n, t& x, t&y) const { t z,v; return contains(pixel,n,x,y,z,v); } //! Return \c true if one of the image list contains the specified referenced value. If true, set coordinates (n,x). template bool contains(const T& pixel, t& n, t& x) const { t y,z,v; return contains(pixel,n,x,y,z,v); } //! Return \c true if one of the image list contains the specified referenced value. If true, set coordinates (n). template bool contains(const T& pixel, t& n) const { t x,y,z,v; return contains(pixel,n,x,y,z,v); } //! Return \c true if one of the image list contains the specified referenced value. bool contains(const T& pixel) const { unsigned int n,x,y,z,v; return contains(pixel,n,x,y,z,v); } //! Return \c true if the list contains the image 'img'. If true, returns the position (n) of the image in the list. template bool contains(const CImg& img, t& n) const { if (is_empty()) return false; const CImg *const ptr = &img; cimglist_for(*this,i) if (data+i==ptr) { n = (t)i; return true; } return false; } //! Return \c true if the list contains the image img. bool contains(const CImg& img) const { unsigned int n; return contains(img,n); } //@} //------------------------------ // //! \name Arithmetics Operators //@{ //------------------------------ //! Assignment operator template CImgList& operator=(const CImgList& list) { return assign(list); } CImgList& operator=(const CImgList& list) { return assign(list); } //! Assignment operator. template CImgList& operator=(const CImg& img) { cimglist_for(*this,l) data[l] = img; return *this; } //! Assignment operator. CImgList& operator=(const T val) { cimglist_for(*this,l) data[l].fill(val); return *this; } //! Operator+. CImgList operator+() const { return CImgList(*this); } //! Operator+=. #ifdef cimg_use_visualcpp6 CImgList& operator+=(const T val) #else template CImgList& operator+=(const t val) #endif { cimglist_for(*this,l) (*this)[l]+=val; return *this; } //! Operator+=. template CImgList& operator+=(const CImgList& list) { const unsigned int sizemax = cimg::min(size,list.size); for (unsigned int l=0; l& operator++() { cimglist_for(*this,l) ++(*this)[l]; return *this; } //! Operator++ (postfix). CImgList operator++(int) { CImgList copy(*this); ++*this; return copy; } //! Operator-. CImgList operator-() const { CImgList res(size); cimglist_for(res,l) res[l].assign(-data[l]); return res; } //! Operator-=. #ifdef cimg_use_visualcpp6 CImgList& operator-=(const T val) #else template CImgList& operator-=(const t val) #endif { cimglist_for(*this,l) (*this)[l]-=val; return *this; } //! Operator-=. template CImgList& operator-=(const CImgList& list) { const unsigned int sizemax = min(size,list.size); for (unsigned int l=0; l& operator--() { cimglist_for(*this,l) --(*this)[l]; return *this; } //! Operator-- (postfix). CImgList operator--(int) { CImgList copy(*this); --*this; return copy; } //! Operator*=. #ifdef cimg_use_visualcpp6 CImgList& operator*=(const double val) #else template CImgList& operator*=(const t val) #endif { cimglist_for(*this,l) (*this)[l]*=val; return *this; } //! Operator*=. template CImgList& operator*=(const CImgList& list) { const unsigned int N = cimg::min(size,list.size); for (unsigned int l=0; l& operator/=(const double val) #else template CImgList& operator/=(const t val) #endif { cimglist_for(*this,l) (*this)[l]/=val; return *this; } //! Operator/=. template CImgList& operator/=(const CImgList& list) { const unsigned int N = cimg::min(size,list.size); for (unsigned int l=0; l::max() : Instance image list is empty.", pixel_type()); const T *ptrmax = data->data; T max_value = *ptrmax; cimglist_for(*this,l) { const CImg& img = data[l]; cimg_for(img,ptr,T) if ((*ptr)>max_value) max_value = *(ptrmax=ptr); } return *ptrmax; } //! Return a reference to the maximum pixel value of the instance list. T& max() { if (is_empty()) throw CImgInstanceException("CImgList<%s>::max() : Instance image list is empty.", pixel_type()); T *ptrmax = data->data; T max_value = *ptrmax; cimglist_for(*this,l) { const CImg& img = data[l]; cimg_for(img,ptr,T) if ((*ptr)>max_value) max_value = *(ptrmax=ptr); } return *ptrmax; } //! Return a reference to the minimum pixel value of the instance list. const T& min() const { if (is_empty()) throw CImgInstanceException("CImgList<%s>::min() : Instance image list is empty.", pixel_type()); const T *ptrmin = data->data; T min_value = *ptrmin; cimglist_for(*this,l) { const CImg& img = data[l]; cimg_for(img,ptr,T) if ((*ptr)::min() : Instance image list is empty.", pixel_type()); T *ptrmin = data->data; T min_value = *ptrmin; cimglist_for(*this,l) { const CImg& img = data[l]; cimg_for(img,ptr,T) if ((*ptr) const T& minmax(t& max_val) const { if (is_empty()) throw CImgInstanceException("CImgList<%s>::minmax() : Instance image list is empty.", pixel_type()); const T *ptrmin = data->data; T min_value = *ptrmin, max_value = min_value; cimglist_for(*this,l) { const CImg& img = data[l]; cimg_for(img,ptr,T) { const T val = *ptr; if (valmax_value) max_value = val; } } max_val = (t)max_value; return *ptrmin; } //! Return a reference to the minimum pixel value of the instance list. template T& minmax(t& max_val) { if (is_empty()) throw CImgInstanceException("CImgList<%s>::minmax() : Instance image list is empty.", pixel_type()); T *ptrmin = data->data; T min_value = *ptrmin, max_value = min_value; cimglist_for(*this,l) { const CImg& img = data[l]; cimg_for(img,ptr,T) { const T val = *ptr; if (valmax_value) max_value = val; } } max_val = (t)max_value; return *ptrmin; } //! Return a reference to the minimum pixel value of the instance list. template const T& maxmin(t& min_val) const { if (is_empty()) throw CImgInstanceException("CImgList<%s>::maxmin() : Instance image list is empty.", pixel_type()); const T *ptrmax = data->data; T min_value = *ptrmax, max_value = min_value; cimglist_for(*this,l) { const CImg& img = data[l]; cimg_for(img,ptr,T) { const T val = *ptr; if (val>max_value) { max_value = val; ptrmax = ptr; } if (val T& maxmin(t& min_val) { if (is_empty()) throw CImgInstanceException("CImgList<%s>::maxmin() : Instance image list is empty.", pixel_type()); T *ptrmax = data->data; T min_value = *ptrmax, max_value = min_value; cimglist_for(*this,l) { const CImg& img = data[l]; cimg_for(img,ptr,T) { const T val = *ptr; if (val>max_value) { max_value = val; ptrmax = ptr; } if (val::mean() : Instance image list is empty.", pixel_type()); double val = 0; unsigned int siz = 0; cimglist_for(*this,l) { const CImg& img = data[l]; cimg_for(img,ptr,T) val+=(double)*ptr; siz+=img.size(); } return val/siz; } //! Return the variance of the instance list. double variance() { if (is_empty()) throw CImgInstanceException("CImgList<%s>::variance() : Instance image list is empty.", pixel_type()); double res = 0; unsigned int siz = 0; double S = 0, S2 = 0; cimglist_for(*this,l) { const CImg& img = data[l]; cimg_for(img,ptr,T) { const double val = (double)*ptr; S+=val; S2+=val*val; } siz+=img.size(); } res = (S2 - S*S/siz)/siz; return res; } //! Compute a list of statistics vectors (min,max,mean,variance,xmin,ymin,zmin,vmin,xmax,ymax,zmax,vmax). CImgList& stats(const unsigned int variance_method=1) { if (is_empty()) return *this; cimglist_for(*this,l) data[l].stats(variance_method); return *this; } CImgList get_stats(const unsigned int variance_method=1) const { CImgList res(size); cimglist_for(*this,l) res[l] = data[l].get_stats(variance_method); return res; } //@} //------------------------- // //! \name List Manipulation //@{ //------------------------- //! Return a reference to the i-th element of the image list. CImg& operator[](const unsigned int pos) { #if cimg_debug>=3 if (pos>=size) { cimg::warn("CImgList<%s>::operator[] : bad list position %u, in a list of %u images", pixel_type(),pos,size); return *data; } #endif return data[pos]; } const CImg& operator[](const unsigned int pos) const { #if cimg_debug>=3 if (pos>=size) { cimg::warn("CImgList<%s>::operator[] : bad list position %u, in a list of %u images", pixel_type(),pos,size); return *data; } #endif return data[pos]; } //! Equivalent to CImgList::operator[] CImg& operator()(const unsigned int pos) { return (*this)[pos]; } const CImg& operator()(const unsigned int pos) const { return (*this)[pos]; } //! Return a reference to (x,y,z,v) pixel of the pos-th image of the list T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) { return (*this)[pos](x,y,z,v); } const T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) const { return (*this)[pos](x,y,z,v); } // This function is only here for template tricks. T _display_object3d_at2(const int i, const int j) const { return atNXY(i,0,j,0,0,0); } //! Read an image in specified position. CImg& at(const int pos) { if (is_empty()) throw CImgInstanceException("CImgList<%s>::at() : Instance list is empty.", pixel_type()); return data[pos<0?0:pos>=(int)size?(int)size-1:pos]; } //! Read a pixel value with Dirichlet boundary conditions. T& atNXYZV(const int pos, const int x, const int y, const int z, const int v, const T out_val) { return (pos<0 || pos>=(int)size)?(cimg::temporary(out_val)=out_val):data[pos].atXYZV(x,y,z,v,out_val); } T atNXYZV(const int pos, const int x, const int y, const int z, const int v, const T out_val) const { return (pos<0 || pos>=(int)size)?out_val:data[pos].atXYZV(x,y,z,v,out_val); } //! Read a pixel value with Neumann boundary conditions. T& atNXYZV(const int pos, const int x, const int y, const int z, const int v) { if (is_empty()) throw CImgInstanceException("CImgList<%s>::atNXYZV() : Instance list is empty.", pixel_type()); return _atNXYZV(pos,x,y,z,v); } T atNXYZV(const int pos, const int x, const int y, const int z, const int v) const { if (is_empty()) throw CImgInstanceException("CImgList<%s>::atNXYZV() : Instance list is empty.", pixel_type()); return _atNXYZV(pos,x,y,z,v); } T& _atNXYZV(const int pos, const int x, const int y, const int z, const int v) { return data[pos<0?0:(pos>=(int)size?(int)size-1:pos)].atXYZV(x,y,z,v); } T _atNXYZV(const int pos, const int x, const int y, const int z, const int v) const { return data[pos<0?0:(pos>=(int)size?(int)size-1:pos)].atXYZV(x,y,z,v); } //! Read a pixel value with Dirichlet boundary conditions for the four first coordinates (\c pos, \c x,\c y,\c z). T& atNXYZ(const int pos, const int x, const int y, const int z, const int v, const T out_val) { return (pos<0 || pos>=(int)size)?(cimg::temporary(out_val)=out_val):data[pos].atXYZ(x,y,z,v,out_val); } T atNXYZ(const int pos, const int x, const int y, const int z, const int v, const T out_val) const { return (pos<0 || pos>=(int)size)?out_val:data[pos].atXYZ(x,y,z,v,out_val); } //! Read a pixel value with Neumann boundary conditions for the four first coordinates (\c pos, \c x,\c y,\c z). T& atNXYZ(const int pos, const int x, const int y, const int z, const int v=0) { if (is_empty()) throw CImgInstanceException("CImgList<%s>::atNXYZ() : Instance list is empty.", pixel_type()); return _atNXYZ(pos,x,y,z,v); } T atNXYZ(const int pos, const int x, const int y, const int z, const int v=0) const { if (is_empty()) throw CImgInstanceException("CImgList<%s>::atNXYZ() : Instance list is empty.", pixel_type()); return _atNXYZ(pos,x,y,z,v); } T& _atNXYZ(const int pos, const int x, const int y, const int z, const int v=0) { return data[pos<0?0:(pos>=(int)size?(int)size-1:pos)].atXYZ(x,y,z,v); } T _atNXYZ(const int pos, const int x, const int y, const int z, const int v=0) const { return data[pos<0?0:(pos>=(int)size?(int)size-1:pos)].atXYZ(x,y,z,v); } //! Read a pixel value with Dirichlet boundary conditions for the three first coordinates (\c pos, \c x,\c y). T& atNXY(const int pos, const int x, const int y, const int z, const int v, const T out_val) { return (pos<0 || pos>=(int)size)?(cimg::temporary(out_val)=out_val):data[pos].atXY(x,y,z,v,out_val); } T atNXY(const int pos, const int x, const int y, const int z, const int v, const T out_val) const { return (pos<0 || pos>=(int)size)?out_val:data[pos].atXY(x,y,z,v,out_val); } //! Read a pixel value with Neumann boundary conditions for the three first coordinates (\c pos, \c x,\c y). T& atNXY(const int pos, const int x, const int y, const int z=0, const int v=0) { if (is_empty()) throw CImgInstanceException("CImgList<%s>::atNXY() : Instance list is empty.", pixel_type()); return _atNXY(pos,x,y,z,v); } T atNXY(const int pos, const int x, const int y, const int z=0, const int v=0) const { if (is_empty()) throw CImgInstanceException("CImgList<%s>::atNXY() : Instance list is empty.", pixel_type()); return _atNXY(pos,x,y,z,v); } T& _atNXY(const int pos, const int x, const int y, const int z=0, const int v=0) { return data[pos<0?0:(pos>=(int)size?(int)size-1:pos)].atXY(x,y,z,v); } T _atNXY(const int pos, const int x, const int y, const int z=0, const int v=0) const { return data[pos<0?0:(pos>=(int)size?(int)size-1:pos)].atXY(x,y,z,v); } //! Read a pixel value with Dirichlet boundary conditions for the two first coordinates (\c pos,\c x). T& atNX(const int pos, const int x, const int y, const int z, const int v, const T out_val) { return (pos<0 || pos>=(int)size)?(cimg::temporary(out_val)=out_val):data[pos].atX(x,y,z,v,out_val); } T atNX(const int pos, const int x, const int y, const int z, const int v, const T out_val) const { return (pos<0 || pos>=(int)size)?out_val:data[pos].atX(x,y,z,v,out_val); } //! Read a pixel value with Neumann boundary conditions for the two first coordinates (\c pos, \c x). T& atNX(const int pos, const int x, const int y=0, const int z=0, const int v=0) { if (is_empty()) throw CImgInstanceException("CImgList<%s>::atNX() : Instance list is empty.", pixel_type()); return _atNX(pos,x,y,z,v); } T atNX(const int pos, const int x, const int y=0, const int z=0, const int v=0) const { if (is_empty()) throw CImgInstanceException("CImgList<%s>::atNX() : Instance list is empty.", pixel_type()); return _atNX(pos,x,y,z,v); } T& _atNX(const int pos, const int x, const int y=0, const int z=0, const int v=0) { return data[pos<0?0:(pos>=(int)size?(int)size-1:pos)].atX(x,y,z,v); } T _atNX(const int pos, const int x, const int y=0, const int z=0, const int v=0) const { return data[pos<0?0:(pos>=(int)size?(int)size-1:pos)].atX(x,y,z,v); } //! Read a pixel value with Dirichlet boundary conditions for the first coordinates (\c pos). T& atN(const int pos, const int x, const int y, const int z, const int v, const T out_val) { return (pos<0 || pos>=(int)size)?(cimg::temporary(out_val)=out_val):(*this)(pos,x,y,z,v); } T atN(const int pos, const int x, const int y, const int z, const int v, const T out_val) const { return (pos<0 || pos>=(int)size)?out_val:(*this)(pos,x,y,z,v); } //! Read a pixel value with Neumann boundary conditions for the first coordinates (\c pos). T& atN(const int pos, const int x=0, const int y=0, const int z=0, const int v=0) { if (is_empty()) throw CImgInstanceException("CImgList<%s>::atN() : Instance list is empty.", pixel_type()); return _atN(pos,x,y,z,v); } T atN(const int pos, const int x=0, const int y=0, const int z=0, const int v=0) const { if (is_empty()) throw CImgInstanceException("CImgList<%s>::atN() : Instance list is empty.", pixel_type()); return _atN(pos,x,y,z,v); } T& _atN(const int pos, const int x=0, const int y=0, const int z=0, const int v=0) { return data[pos<0?0:(pos>=(int)size?(int)size-1:pos)](x,y,z,v); } T _atN(const int pos, const int x=0, const int y=0, const int z=0, const int v=0) const { return data[pos<0?0:(pos>=(int)size?(int)size-1:pos)](x,y,z,v); } //! Returns a reference to the last element. CImg& back() { return (*this)(size-1); } const CImg& back() const { return (*this)(size-1); } //! Returns a reference to the first element. CImg& front() { return *data; } const CImg& front() const { return *data; } //! Returns an iterator to the beginning of the vector. iterator begin() { return data; } const_iterator begin() const { return data; } //! Return a reference to the first image. const CImg& first() const { return *data; } CImg& first() { return *data; } //! Returns an iterator just past the last element. iterator end() { return data + size; } const_iterator end() const { return data + size; } //! Return a reference to the last image. const CImg& last() const { return data[size - 1]; } CImg& last() { return data[size - 1]; } //! Insert a copy of the image \p img into the current image list, at position \p pos. template CImgList& insert(const CImg& img, const unsigned int pos, const bool shared) { const unsigned int npos = pos==~0U?size:pos; if (npos>size) throw CImgArgumentException("CImgList<%s>::insert() : Cannot insert at position %u into a list with %u elements", pixel_type(),npos,size); if (shared) throw CImgArgumentException("CImgList<%s>::insert(): Cannot insert a shared image CImg<%s> into a CImgList<%s>", pixel_type(),img.pixel_type(),pixel_type()); CImg *new_data = (++size>allocsize)?new CImg[allocsize?(allocsize<<=1):(allocsize=16)]:0; if (!size || !data) { data = new_data; *data = img; } else { if (new_data) { if (npos) cimg_std::memcpy(new_data,data,sizeof(CImg)*npos); if (npos!=size-1) cimg_std::memcpy(new_data+npos+1,data+npos,sizeof(CImg)*(size-1-npos)); cimg_std::memset(data,0,sizeof(CImg)*(size-1)); delete[] data; data = new_data; } else if (npos!=size-1) cimg_std::memmove(data+npos+1,data+npos,sizeof(CImg)*(size-1-npos)); data[npos].width = data[npos].height = data[npos].depth = data[npos].dim = 0; data[npos].data = 0; data[npos] = img; } return *this; } CImgList& insert(const CImg& img, const unsigned int pos, const bool shared) { const unsigned int npos = pos==~0U?size:pos; if (npos>size) throw CImgArgumentException("CImgList<%s>::insert() : Can't insert at position %u into a list with %u elements", pixel_type(),npos,size); if (&img>=data && &img *new_data = (++size>allocsize)?new CImg[allocsize?(allocsize<<=1):(allocsize=16)]:0; if (!size || !data) { data = new_data; if (shared && img) { data->width = img.width; data->height = img.height; data->depth = img.depth; data->dim = img.dim; data->is_shared = true; data->data = img.data; } else *data = img; } else { if (new_data) { if (npos) cimg_std::memcpy(new_data,data,sizeof(CImg)*npos); if (npos!=size-1) cimg_std::memcpy(new_data+npos+1,data+npos,sizeof(CImg)*(size-1-npos)); if (shared && img) { new_data[npos].width = img.width; new_data[npos].height = img.height; new_data[npos].depth = img.depth; new_data[npos].dim = img.dim; new_data[npos].is_shared = true; new_data[npos].data = img.data; } else { new_data[npos].width = new_data[npos].height = new_data[npos].depth = new_data[npos].dim = 0; new_data[npos].data = 0; new_data[npos] = img; } cimg_std::memset(data,0,sizeof(CImg)*(size-1)); delete[] data; data = new_data; } else { if (npos!=size-1) cimg_std::memmove(data+npos+1,data+npos,sizeof(CImg)*(size-1-npos)); if (shared && img) { data[npos].width = img.width; data[npos].height = img.height; data[npos].depth = img.depth; data[npos].dim = img.dim; data[npos].is_shared = true; data[npos].data = img.data; } else { data[npos].width = data[npos].height = data[npos].depth = data[npos].dim = 0; data[npos].data = 0; data[npos] = img; } } } return *this; } // The two functions below are necessary due to Visual C++ 6.0 function overloading bugs, when // default parameters are used in function signatures. template CImgList& insert(const CImg& img, const unsigned int pos) { return insert(img,pos,false); } //! Insert a copy of the image \p img into the current image list, at position \p pos. template CImgList& insert(const CImg& img) { return insert(img,~0U,false); } template CImgList get_insert(const CImg& img, const unsigned int pos=~0U, const bool shared=false) const { return (+*this).insert(img,pos,shared); } //! Insert n empty images img into the current image list, at position \p pos. CImgList& insert(const unsigned int n, const unsigned int pos=~0U) { CImg foo; if (!n) return *this; const unsigned int npos = pos==~0U?size:pos; for (unsigned int i=0; i get_insert(const unsigned int n, const unsigned int pos=~0U) const { return (+*this).insert(n,pos); } //! Insert n copies of the image \p img into the current image list, at position \p pos. template CImgList& insert(const unsigned int n, const CImg& img, const unsigned int pos=~0U, const bool shared=false) { if (!n) return *this; const unsigned int npos = pos==~0U?size:pos; insert(img,npos,shared); for (unsigned int i=1; i CImgList get_insert(const unsigned int n, const CImg& img, const unsigned int pos=~0U, const bool shared=false) const { return (+*this).insert(n,img,pos,shared); } //! Insert a copy of the image list \p list into the current image list, starting from position \p pos. template CImgList& insert(const CImgList& list, const unsigned int pos=~0U, const bool shared=false) { const unsigned int npos = pos==~0U?size:pos; if ((void*)this!=(void*)&list) cimglist_for(list,l) insert(list[l],npos+l,shared); else insert(CImgList(list),npos,shared); return *this; } template CImgList get_insert(const CImgList& list, const unsigned int pos=~0U, const bool shared=false) const { return (+*this).insert(list,pos,shared); } //! Insert n copies of the list \p list at position \p pos of the current list. template CImgList& insert(const unsigned int n, const CImgList& list, const unsigned int pos=~0U, const bool shared=false) { if (!n) return *this; const unsigned int npos = pos==~0U?size:pos; for (unsigned int i=0; i CImgList get_insert(const unsigned int n, const CImgList& list, const unsigned int pos=~0U, const bool shared=false) const { return (+*this).insert(n,list,pos,shared); } //! Insert a copy of the image \p img at the end of the current image list. template CImgList& operator<<(const CImg& img) { return insert(img); } //! Insert a copy of the image list \p list at the end of the current image list. template CImgList& operator<<(const CImgList& list) { return insert(list); } //! Return a copy of the current image list, where the image \p img has been inserted at the end. template CImgList& operator>>(CImg& img) const { typedef typename cimg::superset::type Tt; return CImgList(*this).insert(img); } //! Insert a copy of the current image list at the beginning of the image list \p list. template CImgList& operator>>(CImgList& list) const { return list.insert(*this,0); } //! Remove the images at positions \p pos1 to \p pos2 from the image list. CImgList& remove(const unsigned int pos1, const unsigned int pos2) { const unsigned int npos1 = pos1=size) cimg::warn("CImgList<%s>::remove() : Cannot remove images from a list (%p,%u), at positions %u->%u.", pixel_type(),data,size,npos1,tpos2); else { if (tpos2>=size) cimg::warn("CImgList<%s>::remove() : Cannot remove all images from a list (%p,%u), at positions %u->%u.", pixel_type(),data,size,npos1,tpos2); for (unsigned int k = npos1; k<=npos2; ++k) data[k].assign(); const unsigned int nb = 1 + npos2 - npos1; if (!(size-=nb)) return assign(); if (size>(allocsize>>2) || allocsize<=8) { // Removing items without reallocation. if (npos1!=size) cimg_std::memmove(data+npos1,data+npos2+1,sizeof(CImg)*(size-npos1)); cimg_std::memset(data+size,0,sizeof(CImg)*nb); } else { // Removing items with reallocation. allocsize>>=2; while (allocsize>8 && size<(allocsize>>1)) allocsize>>=1; CImg *new_data = new CImg[allocsize]; if (npos1) cimg_std::memcpy(new_data,data,sizeof(CImg)*npos1); if (npos1!=size) cimg_std::memcpy(new_data+npos1,data+npos2+1,sizeof(CImg)*(size-npos1)); if (size!=allocsize) cimg_std::memset(new_data+size,0,sizeof(allocsize-size)); cimg_std::memset(data,0,sizeof(CImg)*(size+nb)); delete[] data; data = new_data; } } return *this; } CImgList get_remove(const unsigned int pos1, const unsigned int pos2) const { return (+*this).remove(pos1,pos2); } //! Remove the image at position \p pos from the image list. CImgList& remove(const unsigned int pos) { return remove(pos,pos); } CImgList get_remove(const unsigned int pos) const { return (+*this).remove(pos); } //! Remove the last image from the image list. CImgList& remove() { if (size) return remove(size-1); else cimg::warn("CImgList<%s>::remove() : List is empty", pixel_type()); return *this; } CImgList get_remove() const { return (+*this).remove(); } //! Reverse list order. CImgList& reverse() { for (unsigned int l=0; l get_reverse() const { return (+*this).reverse(); } //! Get a sub-list. CImgList& crop(const unsigned int i0, const unsigned int i1, const bool shared=false) { return get_crop(i0,i1,shared).transfer_to(*this); } CImgList get_crop(const unsigned int i0, const unsigned int i1, const bool shared=false) const { if (i0>i1 || i1>=size) throw CImgArgumentException("CImgList<%s>::crop() : Cannot crop a sub-list (%u->%u) from a list (%u,%p)", pixel_type(),i0,i1,size,data); CImgList res(i1-i0+1); cimglist_for(res,l) res[l].assign((*this)[i0+l],shared); return res; } //! Get sub-images of a sublist. CImgList& crop(const unsigned int i0, const unsigned int i1, const int x0, const int y0, const int z0, const int v0, const int x1, const int y1, const int z1, const int v1) { return get_crop(i0,i1,x0,y0,z0,v0,x1,y1,z1,v1).transfer_to(*this); } CImgList get_crop(const unsigned int i0, const unsigned int i1, const int x0, const int y0, const int z0, const int v0, const int x1, const int y1, const int z1, const int v1) const { if (i0>i1 || i1>=size) throw CImgArgumentException("CImgList<%s>::crop() : Cannot crop a sub-list (%u->%u) from a list (%u,%p)", pixel_type(),i0,i1,size,data); CImgList res(i1-i0+1); cimglist_for(res,l) res[l] = (*this)[i0+l].get_crop(x0,y0,z0,v0,x1,y1,z1,v1); return res; } //! Get sub-images of a sublist. CImgList& crop(const unsigned int i0, const unsigned int i1, const int x0, const int y0, const int z0, const int x1, const int y1, const int z1) { return get_crop(i0,i1,x0,y0,z0,x1,y1,z1).transfer_to(*this); } CImgList get_crop(const unsigned int i0, const unsigned int i1, const int x0, const int y0, const int z0, const int x1, const int y1, const int z1) const { if (i0>i1 || i1>=size) throw CImgArgumentException("CImgList<%s>::crop() : Cannot crop a sub-list (%u->%u) from a list (%u,%p)", pixel_type(),i0,i1,size,data); CImgList res(i1-i0+1); cimglist_for(res,l) res[l] = (*this)[i0+l].get_crop(x0,y0,z0,x1,y1,z1); return res; } //! Get sub-images of a sublist. CImgList& crop(const unsigned int i0, const unsigned int i1, const int x0, const int y0, const int x1, const int y1) { return get_crop(i0,i1,x0,y0,x1,y1).transfer_to(*this); } CImgList get_crop(const unsigned int i0, const unsigned int i1, const int x0, const int y0, const int x1, const int y1) const { if (i0>i1 || i1>=size) throw CImgArgumentException("CImgList<%s>::crop() : Cannot crop a sub-list (%u->%u) from a list (%u,%p)", pixel_type(),i0,i1,size,data); CImgList res(i1-i0+1); cimglist_for(res,l) res[l] = (*this)[i0+l].get_crop(x0,y0,x1,y1); return res; } //! Get sub-images of a sublist. CImgList& crop(const unsigned int i0, const unsigned int i1, const int x0, const int x1) { return get_crop(i0,i1,x0,x1).transfer_to(*this); } CImgList get_crop(const unsigned int i0, const unsigned int i1, const int x0, const int x1) const { if (i0>i1 || i1>=size) throw CImgArgumentException("CImgList<%s>::crop() : Cannot crop a sub-list (%u->%u) from a list (%u,%p)", pixel_type(),i0,i1,size,data); CImgList res(i1-i0+1); cimglist_for(res,l) res[l] = (*this)[i0+l].get_crop(x0,x1); return res; } //! Display an image list into a CImgDisplay. const CImgList& operator>>(CImgDisplay& disp) const { return display(disp); } //! Insert image \p img at the end of the list. template CImgList& push_back(const CImg& img) { return insert(img); } //! Insert image \p img at the front of the list. template CImgList& push_front(const CImg& img) { return insert(img,0); } //! Insert list \p list at the end of the current list. template CImgList& push_back(const CImgList& list) { return insert(list); } //! Insert list \p list at the front of the current list. template CImgList& push_front(const CImgList& list) { return insert(list,0); } //! Remove last element of the list. CImgList& pop_back() { return remove(size-1); } //! Remove first element of the list. CImgList& pop_front() { return remove(0); } //! Remove the element pointed by iterator \p iter. CImgList& erase(const iterator iter) { return remove(iter-data); } //@} //---------------------------- // //! \name Fourier Transforms //@{ //---------------------------- //! Compute the Fast Fourier Transform (along the specified axis). CImgList& FFT(const char axis, const bool invert=false) { if (is_empty()) throw CImgInstanceException("CImgList<%s>::FFT() : Instance list (%u,%p) is empty", pixel_type(),size,data); if (!data[0]) throw CImgInstanceException("CImgList<%s>::FFT() : Real part (%u,%u,%u,%u,%p) is empty", pixel_type(),data[0].width,data[0].height,data[0].depth,data[0].dim,data[0].data); if (size>2) cimg::warn("CImgList<%s>::FFT() : Instance list (%u,%p) have more than 2 images", pixel_type(),size,data); if (size==1) insert(CImg(data[0].width,data[0].height,data[0].depth,data[0].dim,0)); CImg &Ir = data[0], &Ii = data[1]; if (Ir.width!=Ii.width || Ir.height!=Ii.height || Ir.depth!=Ii.depth || Ir.dim!=Ii.dim) throw CImgInstanceException("CImgList<%s>::FFT() : Real part (%u,%u,%u,%u,%p) and imaginary part (%u,%u,%u,%u,%p)" "have different dimensions", pixel_type(),Ir.width,Ir.height,Ir.depth,Ir.dim,Ir.data,Ii.width,Ii.height,Ii.depth,Ii.dim,Ii.data); #ifdef cimg_use_fftw3 fftw_complex *data_in; fftw_plan data_plan; switch (cimg::uncase(axis)) { case 'x' : { data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*Ir.width); data_plan = fftw_plan_dft_1d(Ir.width,data_in,data_in,invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); cimg_forYZV(Ir,y,z,k) { T *ptrr = Ir.ptr(0,y,z,k), *ptri = Ii.ptr(0,y,z,k); double *ptrd = (double*)data_in; cimg_forX(Ir,x) { *(ptrd++) = (double)*(ptrr++); *(ptrd++) = (double)*(ptri++); } fftw_execute(data_plan); const unsigned int fact = Ir.width; if (invert) { cimg_forX(Ir,x) { *(--ptri) = (T)(*(--ptrd)/fact); *(--ptrr) = (T)(*(--ptrd)/fact); }} else { cimg_forX(Ir,x) { *(--ptri) = (T)*(--ptrd); *(--ptrr) = (T)*(--ptrd); }} } } break; case 'y' : { data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * Ir.height); data_plan = fftw_plan_dft_1d(Ir.height,data_in,data_in,invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); const unsigned int off = Ir.width; cimg_forXZV(Ir,x,z,k) { T *ptrr = Ir.ptr(x,0,z,k), *ptri = Ii.ptr(x,0,z,k); double *ptrd = (double*)data_in; cimg_forY(Ir,y) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; } fftw_execute(data_plan); const unsigned int fact = Ir.height; if (invert) { cimg_forY(Ir,y) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); }} else { cimg_forY(Ir,y) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); }} } } break; case 'z' : { data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * Ir.depth); data_plan = fftw_plan_dft_1d(Ir.depth,data_in,data_in,invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); const unsigned int off = Ir.width*Ir.height; cimg_forXYV(Ir,x,y,k) { T *ptrr = Ir.ptr(x,y,0,k), *ptri = Ii.ptr(x,y,0,k); double *ptrd = (double*)data_in; cimg_forZ(Ir,z) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; } fftw_execute(data_plan); const unsigned int fact = Ir.depth; if (invert) { cimg_forZ(Ir,z) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); }} else { cimg_forZ(Ir,z) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); }} } } break; case 'v' : { data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * Ir.dim); data_plan = fftw_plan_dft_1d(Ir.dim,data_in,data_in,invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); const unsigned int off = Ir.width*Ir.height*Ir.depth; cimg_forXYZ(Ir,x,y,z) { T *ptrr = Ir.ptr(x,y,z,0), *ptri = Ii.ptr(x,y,z,0); double *ptrd = (double*)data_in; cimg_forV(Ir,k) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; } fftw_execute(data_plan); const unsigned int fact = Ir.dim; if (invert) { cimg_forV(Ir,k) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); }} else { cimg_forV(Ir,k) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); }} } } break; } fftw_destroy_plan(data_plan); fftw_free(data_in); #else switch (cimg::uncase(axis)) { case 'x' : { // Fourier along X const unsigned int N = Ir.width, N2 = (N>>1); if (((N-1)&N) && N!=1) throw CImgInstanceException("CImgList<%s>::FFT() : Dimension of instance image along 'x' is %d != 2^N", pixel_type(),N); for (unsigned int i=0, j=0; ii) cimg_forYZV(Ir,y,z,v) { cimg::swap(Ir(i,y,z,v),Ir(j,y,z,v)); cimg::swap(Ii(i,y,z,v),Ii(j,y,z,v)); if (j=m; j-=m, m=n, n>>=1) {} } for (unsigned int delta=2; delta<=N; delta<<=1) { const unsigned int delta2 = (delta>>1); for (unsigned int i=0; i>1); if (((N-1)&N) && N!=1) throw CImgInstanceException("CImgList<%s>::FFT() : Dimension of instance image(s) along 'y' is %d != 2^N", pixel_type(),N); for (unsigned int i=0, j=0; ii) cimg_forXZV(Ir,x,z,v) { cimg::swap(Ir(x,i,z,v),Ir(x,j,z,v)); cimg::swap(Ii(x,i,z,v),Ii(x,j,z,v)); if (j=m; j-=m, m=n, n>>=1) {} } for (unsigned int delta=2; delta<=N; delta<<=1) { const unsigned int delta2 = (delta>>1); for (unsigned int i=0; i>1); if (((N-1)&N) && N!=1) throw CImgInstanceException("CImgList<%s>::FFT() : Dimension of instance image(s) along 'z' is %d != 2^N", pixel_type(),N); for (unsigned int i=0, j=0; ii) cimg_forXYV(Ir,x,y,v) { cimg::swap(Ir(x,y,i,v),Ir(x,y,j,v)); cimg::swap(Ii(x,y,i,v),Ii(x,y,j,v)); if (j=m; j-=m, m=n, n>>=1) {} } for (unsigned int delta=2; delta<=N; delta<<=1) { const unsigned int delta2 = (delta>>1); for (unsigned int i=0; i::FFT() : Invalid axis '%c', must be 'x','y' or 'z'."); } #endif return *this; } CImgList get_FFT(const char axis, const bool invert=false) const { return CImgList(*this).FFT(axis,invert); } //! Compute the Fast Fourier Transform of a complex image. CImgList& FFT(const bool invert=false) { if (is_empty()) throw CImgInstanceException("CImgList<%s>::FFT() : Instance list (%u,%p) is empty", pixel_type(),size,data); if (size>2) cimg::warn("CImgList<%s>::FFT() : Instance list (%u,%p) have more than 2 images", pixel_type(),size,data); if (size==1) insert(CImg(data->width,data->height,data->depth,data->dim,0)); CImg &Ir = data[0], &Ii = data[1]; if (Ii.width!=Ir.width || Ii.height!=Ir.height || Ii.depth!=Ir.depth || Ii.dim!=Ir.dim) throw CImgInstanceException("CImgList<%s>::FFT() : Real (%u,%u,%u,%u,%p) and Imaginary (%u,%u,%u,%u,%p) parts " "of the instance image have different dimensions", pixel_type(),Ir.width,Ir.height,Ir.depth,Ir.dim,Ir.data, Ii.width,Ii.height,Ii.depth,Ii.dim,Ii.data); #ifdef cimg_use_fftw3 fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * Ir.width*Ir.height*Ir.depth); fftw_plan data_plan; const unsigned int w = Ir.width, wh = w*Ir.height, whd = wh*Ir.depth; data_plan = fftw_plan_dft_3d(Ir.width,Ir.height,Ir.depth,data_in,data_in,invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); cimg_forV(Ir,k) { T *ptrr = Ir.ptr(0,0,0,k), *ptri = Ii.ptr(0,0,0,k); double *ptrd = (double*)data_in; for (unsigned int x = 0; x1) FFT('z',invert); if (Ir.height>1) FFT('y',invert); if (Ir.width>1) FFT('x',invert); #endif return *this; } CImgList get_FFT(const bool invert=false) const { return CImgList(*this).FFT(invert); } // Return a list where each image has been split along the specified axis. CImgList& split(const char axis) { return get_split(axis).transfer_to(*this); } CImgList get_split(const char axis) const { CImgList res; cimglist_for(*this,l) { CImgList tmp = data[l].get_split(axis); const unsigned int pos = res.size; res.insert(tmp.size); cimglist_for(tmp,i) tmp[i].transfer_to(data[pos+i]); } return res; } //! Return a single image which is the concatenation of all images of the current CImgList instance. /** \param axis : specify the axis for image concatenation. Can be 'x','y','z' or 'v'. \param align : specify the alignment for image concatenation. Can be 'p' (top), 'c' (center) or 'n' (bottom). \return A CImg image corresponding to the concatenation is returned. **/ CImg get_append(const char axis, const char align='p') const { if (is_empty()) return CImg(); if (size==1) return +((*this)[0]); unsigned int dx = 0, dy = 0, dz = 0, dv = 0, pos = 0; CImg res; switch (cimg::uncase(axis)) { case 'x' : { switch (cimg::uncase(align)) { case 'x' : { dy = dz = dv = 1; cimglist_for(*this,l) dx+=(*this)[l].size(); } break; case 'y' : { dx = size; dz = dv = 1; cimglist_for(*this,l) dy = cimg::max(dy,(unsigned int)(*this)[l].size()); } break; case 'z' : { dx = size; dy = dv = 1; cimglist_for(*this,l) dz = cimg::max(dz,(unsigned int)(*this)[l].size()); } break; case 'v' : { dx = size; dy = dz = 1; cimglist_for(*this,l) dv = cimg::max(dz,(unsigned int)(*this)[l].size()); } break; default : cimglist_for(*this,l) { const CImg& img = (*this)[l]; dx += img.width; dy = cimg::max(dy,img.height); dz = cimg::max(dz,img.depth); dv = cimg::max(dv,img.dim); } } res.assign(dx,dy,dz,dv,0); switch (cimg::uncase(align)) { case 'x' : { cimglist_for(*this,l) { res.draw_image(pos,CImg((*this)[l],true).unroll('x')); pos+=(*this)[l].size(); } } break; case 'y' : { cimglist_for(*this,l) res.draw_image(pos++,CImg((*this)[l],true).unroll('y')); } break; case 'z' : { cimglist_for(*this,l) res.draw_image(pos++,CImg((*this)[l],true).unroll('z')); } break; case 'v' : { cimglist_for(*this,l) res.draw_image(pos++,CImg((*this)[l],true).unroll('v')); } break; case 'p' : { cimglist_for(*this,l) { res.draw_image(pos,(*this)[l]); pos+=(*this)[l].width; } } break; case 'n' : { cimglist_for(*this,l) { res.draw_image(pos,dy-(*this)[l].height,dz-(*this)[l].depth,dv-(*this)[l].dim,(*this)[l]); pos+=(*this)[l].width; } } break; default : { cimglist_for(*this,l) { res.draw_image(pos,(dy-(*this)[l].height)/2,(dz-(*this)[l].depth)/2,(dv-(*this)[l].dim)/2,(*this)[l]); pos+=(*this)[l].width; } } break; } } break; case 'y' : { switch (cimg::uncase(align)) { case 'x' : { dy = size; dz = dv = 1; cimglist_for(*this,l) dx = cimg::max(dx,(unsigned int)(*this)[l].size()); } break; case 'y' : { dx = dz = dv = 1; cimglist_for(*this,l) dy+=(*this)[l].size(); } break; case 'z' : { dy = size; dx = dv = 1; cimglist_for(*this,l) dz = cimg::max(dz,(unsigned int)(*this)[l].size()); } break; case 'v' : { dy = size; dx = dz = 1; cimglist_for(*this,l) dv = cimg::max(dv,(unsigned int)(*this)[l].size()); } break; default : cimglist_for(*this,l) { const CImg& img = (*this)[l]; dx = cimg::max(dx,img.width); dy += img.height; dz = cimg::max(dz,img.depth); dv = cimg::max(dv,img.dim); } } res.assign(dx,dy,dz,dv,0); switch (cimg::uncase(align)) { case 'x' : { cimglist_for(*this,l) res.draw_image(0,++pos,CImg((*this)[l],true).unroll('x')); } break; case 'y' : { cimglist_for(*this,l) { res.draw_image(0,pos,CImg((*this)[l],true).unroll('y')); pos+=(*this)[l].size(); } } break; case 'z' : { cimglist_for(*this,l) res.draw_image(0,pos++,CImg((*this)[l],true).unroll('z')); } break; case 'v' : { cimglist_for(*this,l) res.draw_image(0,pos++,CImg((*this)[l],true).unroll('v')); } break; case 'p' : { cimglist_for(*this,l) { res.draw_image(0,pos,(*this)[l]); pos+=(*this)[l].height; } } break; case 'n' : { cimglist_for(*this,l) { res.draw_image(dx-(*this)[l].width,pos,dz-(*this)[l].depth,dv-(*this)[l].dim,(*this)[l]); pos+=(*this)[l].height; } } break; default : { cimglist_for(*this,l) { res.draw_image((dx-(*this)[l].width)/2,pos,(dz-(*this)[l].depth)/2,(dv-(*this)[l].dim)/2,(*this)[l]); pos+=(*this)[l].height; } } break; } } break; case 'z' : { switch (cimg::uncase(align)) { case 'x' : { dz = size; dy = dv = 1; cimglist_for(*this,l) dx = cimg::max(dx,(unsigned int)(*this)[l].size()); } break; case 'y' : { dz = size; dx = dv = 1; cimglist_for(*this,l) dy = cimg::max(dz,(unsigned int)(*this)[l].size()); } break; case 'z' : { dx = dy = dv = 1; cimglist_for(*this,l) dz+=(*this)[l].size(); } break; case 'v' : { dz = size; dx = dz = 1; cimglist_for(*this,l) dv = cimg::max(dv,(unsigned int)(*this)[l].size()); } break; default : cimglist_for(*this,l) { const CImg& img = (*this)[l]; dx = cimg::max(dx,img.width); dy = cimg::max(dy,img.height); dz += img.depth; dv = cimg::max(dv,img.dim); } } res.assign(dx,dy,dz,dv,0); switch (cimg::uncase(align)) { case 'x' : { cimglist_for(*this,l) res.draw_image(0,0,pos++,CImg((*this)[l],true).unroll('x')); } break; case 'y' : { cimglist_for(*this,l) res.draw_image(0,0,pos++,CImg((*this)[l],true).unroll('y')); } break; case 'z' : { cimglist_for(*this,l) { res.draw_image(0,0,pos,CImg((*this)[l],true).unroll('z')); pos+=(*this)[l].size(); } } break; case 'v' : { cimglist_for(*this,l) res.draw_image(0,0,pos++,CImg((*this)[l],true).unroll('v')); } break; case 'p' : { cimglist_for(*this,l) { res.draw_image(0,0,pos,(*this)[l]); pos+=(*this)[l].depth; } } break; case 'n' : { cimglist_for(*this,l) { res.draw_image(dx-(*this)[l].width,dy-(*this)[l].height,pos,dv-(*this)[l].dim,(*this)[l]); pos+=(*this)[l].depth; } } break; case 'c' : { cimglist_for(*this,l) { res.draw_image((dx-(*this)[l].width)/2,(dy-(*this)[l].height)/2,pos,(dv-(*this)[l].dim)/2,(*this)[l]); pos+=(*this)[l].depth; } } break; } } break; case 'v' : { switch (cimg::uncase(align)) { case 'x' : { dv = size; dy = dv = 1; cimglist_for(*this,l) dx = cimg::max(dx,(unsigned int)(*this)[l].size()); } break; case 'y' : { dv = size; dx = dv = 1; cimglist_for(*this,l) dy = cimg::max(dz,(unsigned int)(*this)[l].size()); } break; case 'z' : { dv = size; dx = dv = 1; cimglist_for(*this,l) dz = cimg::max(dv,(unsigned int)(*this)[l].size()); } break; case 'v' : { dx = dy = dz = 1; cimglist_for(*this,l) dv+=(*this)[l].size(); } break; default : cimglist_for(*this,l) { const CImg& img = (*this)[l]; dx = cimg::max(dx,img.width); dy = cimg::max(dy,img.height); dz = cimg::max(dz,img.depth); dv += img.dim; } } res.assign(dx,dy,dz,dv,0); switch (cimg::uncase(align)) { case 'x' : { cimglist_for(*this,l) res.draw_image(0,0,0,pos++,CImg((*this)[l],true).unroll('x')); } break; case 'y' : { cimglist_for(*this,l) res.draw_image(0,0,0,pos++,CImg((*this)[l],true).unroll('y')); } break; case 'z' : { cimglist_for(*this,l) res.draw_image(0,0,0,pos++,CImg((*this)[l],true).unroll('v')); } break; case 'v' : { cimglist_for(*this,l) { res.draw_image(0,0,0,pos,CImg((*this)[l],true).unroll('z')); pos+=(*this)[l].size(); } } break; case 'p' : { cimglist_for(*this,l) { res.draw_image(0,0,0,pos,(*this)[l]); pos+=(*this)[l].dim; } } break; case 'n' : { cimglist_for(*this,l) { res.draw_image(dx-(*this)[l].width,dy-(*this)[l].height,dz-(*this)[l].depth,pos,(*this)[l]); pos+=(*this)[l].dim; } } break; case 'c' : { cimglist_for(*this,l) { res.draw_image((dx-(*this)[l].width)/2,(dy-(*this)[l].height)/2,(dz-(*this)[l].depth)/2,pos,(*this)[l]); pos+=(*this)[l].dim; } } break; } } break; default : throw CImgArgumentException("CImgList<%s>::get_append() : unknow axis '%c', must be 'x','y','z' or 'v'", pixel_type(),axis); } return res; } //! Create an auto-cropped font (along the X axis) from a input font \p font. CImgList& crop_font() { return get_crop_font().transfer_to(*this); } CImgList get_crop_font() const { CImgList res; cimglist_for(*this,l) { const CImg& letter = (*this)[l]; int xmin = letter.width, xmax = 0; cimg_forXY(letter,x,y) if (letter(x,y)) { if (xxmax) xmax=x; } if (xmin>xmax) res.insert(CImg(letter.width,letter.height,1,letter.dim,0)); else res.insert(letter.get_crop(xmin,0,xmax,letter.height-1)); } res[' '].resize(res['f'].width); res[' '+256].resize(res['f'].width); return res; } //! Invert primitives orientation of a 3D object. CImgList& invert_object3d() { cimglist_for(*this,l) { CImg& p = data[l]; const unsigned int siz = p.size(); if (siz==2 || siz==3 || siz==6 || siz==9) cimg::swap(p[0],p[1]); else if (siz==4 || siz==12) cimg::swap(p[0],p[3],p[1],p[2]); } return *this; } CImgList get_invert_object3d() const { return (+*this).invert_object3d(); } //! Return a CImg pre-defined font with desired size. /** \param font_height = height of the desired font (can be 11,13,24,38 or 57) \param fixed_size = tell if the font has a fixed or variable width. **/ static CImgList font(const unsigned int font_width, const bool variable_size=true) { if (font_width<=11) { static CImgList font7x11, nfont7x11; if (!variable_size && !font7x11) font7x11 = _font(cimg::font7x11,7,11,1,0,false); if (variable_size && !nfont7x11) nfont7x11 = _font(cimg::font7x11,7,11,1,0,true); return variable_size?nfont7x11:font7x11; } if (font_width<=13) { static CImgList font10x13, nfont10x13; if (!variable_size && !font10x13) font10x13 = _font(cimg::font10x13,10,13,1,0,false); if (variable_size && !nfont10x13) nfont10x13 = _font(cimg::font10x13,10,13,1,0,true); return variable_size?nfont10x13:font10x13; } if (font_width<=17) { static CImgList font8x17, nfont8x17; if (!variable_size && !font8x17) font8x17 = _font(cimg::font8x17,8,17,1,0,false); if (variable_size && !nfont8x17) nfont8x17 = _font(cimg::font8x17,8,17,1,0,true); return variable_size?nfont8x17:font8x17; } if (font_width<=19) { static CImgList font10x19, nfont10x19; if (!variable_size && !font10x19) font10x19 = _font(cimg::font10x19,10,19,2,0,false); if (variable_size && !nfont10x19) nfont10x19 = _font(cimg::font10x19,10,19,2,0,true); return variable_size?nfont10x19:font10x19; } if (font_width<=24) { static CImgList font12x24, nfont12x24; if (!variable_size && !font12x24) font12x24 = _font(cimg::font12x24,12,24,2,0,false); if (variable_size && !nfont12x24) nfont12x24 = _font(cimg::font12x24,12,24,2,0,true); return variable_size?nfont12x24:font12x24; } if (font_width<=32) { static CImgList font16x32, nfont16x32; if (!variable_size && !font16x32) font16x32 = _font(cimg::font16x32,16,32,2,0,false); if (variable_size && !nfont16x32) nfont16x32 = _font(cimg::font16x32,16,32,2,0,true); return variable_size?nfont16x32:font16x32; } if (font_width<=38) { static CImgList font19x38, nfont19x38; if (!variable_size && !font19x38) font19x38 = _font(cimg::font19x38,19,38,3,0,false); if (variable_size && !nfont19x38) nfont19x38 = _font(cimg::font19x38,19,38,3,0,true); return variable_size?nfont19x38:font19x38; } static CImgList font29x57, nfont29x57; if (!variable_size && !font29x57) font29x57 = _font(cimg::font29x57,29,57,5,0,false); if (variable_size && !nfont29x57) nfont29x57 = _font(cimg::font29x57,29,57,5,0,true); return variable_size?nfont29x57:font29x57; } static CImgList _font(const unsigned int *const font, const unsigned int w, const unsigned int h, const unsigned int paddingx, const unsigned int paddingy, const bool variable_size=true) { CImgList res = CImgList(256,w,h,1,3).insert(CImgList(256,w,h,1,1)); const unsigned int *ptr = font; unsigned int m = 0, val = 0; for (unsigned int y=0; y>=1; if (!m) { m = 0x80000000; val = *(ptr++); } CImg& img = res[x/w], &mask = res[x/w+256]; unsigned int xm = x%w; img(xm,y,0) = img(xm,y,1) = img(xm,y,2) = mask(xm,y,0) = (T)((val&m)?1:0); } if (variable_size) res.crop_font(); if (paddingx || paddingy) cimglist_for(res,l) res[l].resize(res[l].dimx()+paddingx, res[l].dimy()+paddingy,1,-100,0); return res; } //! Display the current CImgList instance in an existing CImgDisplay window (by reference). /** This function displays the list images of the current CImgList instance into an existing CImgDisplay window. Images of the list are concatenated in a single temporarly image for visualization purposes. The function returns immediately. \param disp : reference to an existing CImgDisplay instance, where the current image list will be displayed. \param axis : specify the axis for image concatenation. Can be 'x','y','z' or 'v'. \param align : specify the alignment for image concatenation. Can be 'p' (top), 'c' (center) or 'n' (bottom). \return A reference to the current CImgList instance is returned. **/ const CImgList& display(CImgDisplay& disp, const char axis='x', const char align='p') const { get_append(axis,align).display(disp); return *this; } //! Display the current CImgList instance in a new display window. /** This function opens a new window with a specific title and displays the list images of the current CImgList instance into it. Images of the list are concatenated in a single temporarly image for visualization purposes. The function returns when a key is pressed or the display window is closed by the user. \param title : specify the title of the opening display window. \param axis : specify the axis for image concatenation. Can be 'x','y','z' or 'v'. \param align : specify the alignment for image concatenation. Can be 'p' (top), 'c' (center) or 'n' (bottom). \return A reference to the current CImgList instance is returned. **/ const CImgList& display(CImgDisplay &disp, const bool display_info, const char axis='x', const char align='p') const { if (is_empty()) throw CImgInstanceException("CImgList<%s>::display() : Instance list (%u,%u) is empty.", pixel_type(),size,data); const CImg visu = get_append(axis,align); if (display_info) print(disp.title); visu.display(disp,false); return *this; } //! Display the current CImgList instance in a new display window. const CImgList& display(const char *const title=0, const bool display_info=true, const char axis='x', const char align='p') const { const CImg visu = get_append(axis,align); char ntitle[64] = { 0 }; if (!title) cimg_std::sprintf(ntitle,"CImgList<%s>",pixel_type()); if (display_info) print(title?title:ntitle); visu.display(title?title:ntitle,false); return *this; } //@} //---------------------------------- // //! \name Input-Output //@{ //---------------------------------- //! Return a C-string containing the values of all images in the instance list. CImg value_string(const char separator=',', const unsigned int max_size=0) const { if (is_empty()) return CImg(1,1,1,1,0); CImgList items; for (unsigned int l = 0; l item = data[l].value_string(separator,0); item[item.size()-1] = separator; items.insert(item); } items.insert(data[size-1].value_string(separator,0)); CImg res = items.get_append('x'); if (max_size) { res.crop(0,max_size); res(max_size) = 0; } return res; } //! Print informations about the list on the standard output. const CImgList& print(const char* title=0, const bool display_stats=true) const { unsigned long msiz = 0; cimglist_for(*this,l) msiz += data[l].size(); msiz*=sizeof(T); const unsigned int mdisp = msiz<8*1024?0:(msiz<8*1024*1024?1:2); char ntitle[64] = { 0 }; if (!title) cimg_std::sprintf(ntitle,"CImgList<%s>",pixel_type()); cimg_std::fprintf(cimg_stdout,"%s: this = %p, size = %u [%lu %s], data = (CImg<%s>*)%p.\n", title?title:ntitle,(void*)this,size, mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)), mdisp==0?"b":(mdisp==1?"Kb":"Mb"), pixel_type(),(void*)data); char tmp[16] = { 0 }; cimglist_for(*this,ll) { cimg_std::sprintf(tmp,"[%d]",ll); cimg_std::fprintf(cimg_stdout," "); data[ll].print(tmp,display_stats); if (ll==3 && size>8) { ll = size-5; cimg_std::fprintf(cimg_stdout," ...\n"); } } return *this; } //! Load an image list from a file. CImgList& load(const char *const filename) { const char *ext = cimg::split_filename(filename); const unsigned int odebug = cimg::exception_mode(); cimg::exception_mode() = 0; assign(); try { #ifdef cimglist_load_plugin cimglist_load_plugin(filename); #endif #ifdef cimglist_load_plugin1 cimglist_load_plugin1(filename); #endif #ifdef cimglist_load_plugin2 cimglist_load_plugin2(filename); #endif #ifdef cimglist_load_plugin3 cimglist_load_plugin3(filename); #endif #ifdef cimglist_load_plugin4 cimglist_load_plugin4(filename); #endif #ifdef cimglist_load_plugin5 cimglist_load_plugin5(filename); #endif #ifdef cimglist_load_plugin6 cimglist_load_plugin6(filename); #endif #ifdef cimglist_load_plugin7 cimglist_load_plugin7(filename); #endif #ifdef cimglist_load_plugin8 cimglist_load_plugin8(filename); #endif if (!cimg::strcasecmp(ext,"tif") || !cimg::strcasecmp(ext,"tiff")) load_tiff(filename); if (!cimg::strcasecmp(ext,"cimg") || !cimg::strcasecmp(ext,"cimgz") || !ext[0]) load_cimg(filename); if (!cimg::strcasecmp(ext,"rec") || !cimg::strcasecmp(ext,"par")) load_parrec(filename); if (!cimg::strcasecmp(ext,"avi") || !cimg::strcasecmp(ext,"mov") || !cimg::strcasecmp(ext,"asf") || !cimg::strcasecmp(ext,"divx") || !cimg::strcasecmp(ext,"flv") || !cimg::strcasecmp(ext,"mpg") || !cimg::strcasecmp(ext,"m1v") || !cimg::strcasecmp(ext,"m2v") || !cimg::strcasecmp(ext,"m4v") || !cimg::strcasecmp(ext,"mjp") || !cimg::strcasecmp(ext,"mkv") || !cimg::strcasecmp(ext,"mpe") || !cimg::strcasecmp(ext,"movie") || !cimg::strcasecmp(ext,"ogm") || !cimg::strcasecmp(ext,"qt") || !cimg::strcasecmp(ext,"rm") || !cimg::strcasecmp(ext,"vob") || !cimg::strcasecmp(ext,"wmv") || !cimg::strcasecmp(ext,"xvid") || !cimg::strcasecmp(ext,"mpeg")) load_ffmpeg(filename); if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename); if (is_empty()) throw CImgIOException("CImgList<%s>::load()",pixel_type()); } catch (CImgIOException& e) { if (!cimg::strncasecmp(e.message,"cimg::fopen()",13)) { cimg::exception_mode() = odebug; throw CImgIOException("CImgList<%s>::load() : File '%s' cannot be opened.",pixel_type(),filename); } else try { assign(1); data->load(filename); } catch (CImgException&) { assign(); } } cimg::exception_mode() = odebug; if (is_empty()) throw CImgIOException("CImgList<%s>::load() : File '%s', format not recognized.",pixel_type(),filename); return *this; } static CImgList get_load(const char *const filename) { return CImgList().load(filename); } //! Load an image list from a .cimg file. CImgList& load_cimg(const char *const filename) { return _load_cimg(0,filename); } static CImgList get_load_cimg(const char *const filename) { return CImgList().load_cimg(filename); } //! Load an image list from a .cimg file. CImgList& load_cimg(cimg_std::FILE *const file) { return _load_cimg(file,0); } static CImgList get_load_cimg(cimg_std::FILE *const file) { return CImgList().load_cimg(file); } CImgList& _load_cimg(cimg_std::FILE *const file, const char *const filename) { #ifdef cimg_use_zlib #define _cimgz_load_cimg_case(Tss) { \ Bytef *const cbuf = new Bytef[csiz]; \ cimg::fread(cbuf,csiz,nfile); \ raw.assign(W,H,D,V); \ unsigned long destlen = raw.size()*sizeof(T); \ uncompress((Bytef*)raw.data,&destlen,cbuf,csiz); \ delete[] cbuf; \ const Tss *ptrs = raw.data; \ for (unsigned int off = raw.size(); off; --off) *(ptrd++) = (T)*(ptrs++); \ } #else #define _cimgz_load_cimg_case(Tss) \ throw CImgIOException("CImgList<%s>::load_cimg() : File '%s' contains compressed data, zlib must be used",\ pixel_type(),filename?filename:"(FILE*)"); #endif #define _cimg_load_cimg_case(Ts,Tss) \ if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \ for (unsigned int l = 0; l=0) tmp[j++] = (char)i; tmp[j] = '\0'; \ W = H = D = V = 0; csiz = 0; \ if ((err = cimg_std::sscanf(tmp,"%u %u %u %u #%u",&W,&H,&D,&V,&csiz))<4) \ throw CImgIOException("CImgList<%s>::load_cimg() : File '%s', Image %u has an invalid size (%u,%u,%u,%u)\n", \ pixel_type(),filename?filename:("(FILE*)"),W,H,D,V); \ if (W*H*D*V>0) { \ CImg raw; \ CImg &img = data[l]; \ img.assign(W,H,D,V); \ T *ptrd = img.data; \ if (err==5) _cimgz_load_cimg_case(Tss) \ else for (int toread = (int)img.size(); toread>0; ) { \ raw.assign(cimg::min(toread,cimg_iobuffer)); \ cimg::fread(raw.data,raw.width,nfile); \ if (endian!=cimg::endianness()) cimg::invert_endianness(raw.data,raw.width); \ toread-=raw.width; \ const Tss *ptrs = raw.data; \ for (unsigned int off = raw.width; off; --off) *(ptrd++) = (T)*(ptrs++); \ } \ } \ } \ loaded = true; \ } if (!filename && !file) throw CImgArgumentException("CImgList<%s>::load_cimg() : Cannot load (null) filename.", pixel_type()); typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned int uint; typedef unsigned long ulong; const int cimg_iobuffer = 12*1024*1024; cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); bool loaded = false, endian = cimg::endianness(); char tmp[256], str_pixeltype[256], str_endian[256]; unsigned int j, err, N = 0, W, H, D, V, csiz; int i; j = 0; while((i=cimg_std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = '\0'; err = cimg_std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype,str_endian); if (err<2) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImgList<%s>::load_cimg() : File '%s', Unknow CImg RAW header.", pixel_type(),filename?filename:"(FILE*)"); } if (!cimg::strncasecmp("little",str_endian,6)) endian = false; else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; assign(N); _cimg_load_cimg_case("bool",bool); _cimg_load_cimg_case("unsigned_char",uchar); _cimg_load_cimg_case("uchar",uchar); _cimg_load_cimg_case("char",char); _cimg_load_cimg_case("unsigned_short",ushort); _cimg_load_cimg_case("ushort",ushort); _cimg_load_cimg_case("short",short); _cimg_load_cimg_case("unsigned_int",uint); _cimg_load_cimg_case("uint",uint); _cimg_load_cimg_case("int",int); _cimg_load_cimg_case("unsigned_long",ulong); _cimg_load_cimg_case("ulong",ulong); _cimg_load_cimg_case("long",long); _cimg_load_cimg_case("float",float); _cimg_load_cimg_case("double",double); if (!loaded) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImgList<%s>::load_cimg() : File '%s', cannot read images of pixels coded as '%s'.", pixel_type(),filename?filename:"(FILE*)",str_pixeltype); } if (!file) cimg::fclose(nfile); return *this; } //! Load a sub-image list from a non compressed .cimg file. CImgList& 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 v0, const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1) { return _load_cimg(0,filename,n0,n1,x0,y0,z0,v0,x1,y1,z1,v1); } static CImgList 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 v0, const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1) { return CImgList().load_cimg(filename,n0,n1,x0,y0,z0,v0,x1,y1,z1,v1); } //! Load a sub-image list from a non compressed .cimg file. CImgList& load_cimg(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 v0, const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1) { return _load_cimg(file,0,n0,n1,x0,y0,z0,v0,x1,y1,z1,v1); } static CImgList get_load_cimg(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 v0, const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1) { return CImgList().load_cimg(file,n0,n1,x0,y0,z0,v0,x1,y1,z1,v1); } CImgList& _load_cimg(cimg_std::FILE *const file, 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 v0, const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1) { #define _cimg_load_cimg_case2(Ts,Tss) \ if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \ for (unsigned int l = 0; l<=nn1; ++l) { \ j = 0; while ((i=cimg_std::fgetc(nfile))!='\n' && i>=0) tmp[j++] = (char)i; tmp[j] = '\0'; \ W = H = D = V = 0; \ if (cimg_std::sscanf(tmp,"%u %u %u %u",&W,&H,&D,&V)!=4) \ throw CImgIOException("CImgList<%s>::load_cimg() : File '%s', Image %u has an invalid size (%u,%u,%u,%u)\n", \ pixel_type(), filename?filename:("(FILE*)"), W, H, D, V); \ if (W*H*D*V>0) { \ if (l=W || y0>=H || z0>=D || v0>=D) cimg_std::fseek(nfile,W*H*D*V*sizeof(Tss),SEEK_CUR); \ else { \ const unsigned int \ nx1 = x1>=W?W-1:x1, \ ny1 = y1>=H?H-1:y1, \ nz1 = z1>=D?D-1:z1, \ nv1 = v1>=V?V-1:v1; \ CImg raw(1+nx1-x0); \ CImg &img = data[l-n0]; \ img.assign(1+nx1-x0,1+ny1-y0,1+nz1-z0,1+nv1-v0); \ T *ptrd = img.data; \ const unsigned int skipvb = v0*W*H*D*sizeof(Tss); \ if (skipvb) cimg_std::fseek(nfile,skipvb,SEEK_CUR); \ for (unsigned int v=1+nv1-v0; v; --v) { \ const unsigned int skipzb = z0*W*H*sizeof(Tss); \ if (skipzb) cimg_std::fseek(nfile,skipzb,SEEK_CUR); \ for (unsigned int z=1+nz1-z0; z; --z) { \ const unsigned int skipyb = y0*W*sizeof(Tss); \ if (skipyb) cimg_std::fseek(nfile,skipyb,SEEK_CUR); \ for (unsigned int y=1+ny1-y0; y; --y) { \ const unsigned int skipxb = x0*sizeof(Tss); \ if (skipxb) cimg_std::fseek(nfile,skipxb,SEEK_CUR); \ cimg::fread(raw.data,raw.width,nfile); \ if (endian!=cimg::endianness()) cimg::invert_endianness(raw.data,raw.width); \ const Tss *ptrs = raw.data; \ for (unsigned int off = raw.width; off; --off) *(ptrd++) = (T)*(ptrs++); \ const unsigned int skipxe = (W-1-nx1)*sizeof(Tss); \ if (skipxe) cimg_std::fseek(nfile,skipxe,SEEK_CUR); \ } \ const unsigned int skipye = (H-1-ny1)*W*sizeof(Tss); \ if (skipye) cimg_std::fseek(nfile,skipye,SEEK_CUR); \ } \ const unsigned int skipze = (D-1-nz1)*W*H*sizeof(Tss); \ if (skipze) cimg_std::fseek(nfile,skipze,SEEK_CUR); \ } \ const unsigned int skipve = (V-1-nv1)*W*H*D*sizeof(Tss); \ if (skipve) cimg_std::fseek(nfile,skipve,SEEK_CUR); \ } \ } \ } \ loaded = true; \ } if (!filename && !file) throw CImgArgumentException("CImgList<%s>::load_cimg() : Cannot load (null) filename.", pixel_type()); typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned int uint; typedef unsigned long ulong; if (n1::load_cimg() : File '%s', Bad sub-region coordinates [%u->%u] " "(%u,%u,%u,%u)->(%u,%u,%u,%u).", pixel_type(),filename?filename:"(FILE*)", n0,n1,x0,y0,z0,v0,x1,y1,z1,v1); cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); bool loaded = false, endian = cimg::endianness(); char tmp[256], str_pixeltype[256], str_endian[256]; unsigned int j, err, N, W, H, D, V; int i; j = 0; while((i=cimg_std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = '\0'; err = cimg_std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype,str_endian); if (err<2) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImgList<%s>::load_cimg() : File '%s', Unknow CImg RAW header.", pixel_type(),filename?filename:"(FILE*)"); } if (!cimg::strncasecmp("little",str_endian,6)) endian = false; else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; const unsigned int nn1 = n1>=N?N-1:n1; assign(1+nn1-n0); _cimg_load_cimg_case2("bool",bool); _cimg_load_cimg_case2("unsigned_char",uchar); _cimg_load_cimg_case2("uchar",uchar); _cimg_load_cimg_case2("char",char); _cimg_load_cimg_case2("unsigned_short",ushort); _cimg_load_cimg_case2("ushort",ushort); _cimg_load_cimg_case2("short",short); _cimg_load_cimg_case2("unsigned_int",uint); _cimg_load_cimg_case2("uint",uint); _cimg_load_cimg_case2("int",int); _cimg_load_cimg_case2("unsigned_long",ulong); _cimg_load_cimg_case2("ulong",ulong); _cimg_load_cimg_case2("long",long); _cimg_load_cimg_case2("float",float); _cimg_load_cimg_case2("double",double); if (!loaded) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImgList<%s>::load_cimg() : File '%s', cannot read images of pixels coded as '%s'.", pixel_type(),filename?filename:"(FILE*)",str_pixeltype); } if (!file) cimg::fclose(nfile); return *this; } //! Load an image list from a PAR/REC (Philips) file. CImgList& load_parrec(const char *const filename) { if (!filename) throw CImgArgumentException("CImgList<%s>::load_parrec() : Cannot load (null) filename.", pixel_type()); char body[1024], filenamepar[1024], filenamerec[1024]; const char *ext = cimg::split_filename(filename,body); if (!cimg::strcmp(ext,"par")) { cimg_std::strcpy(filenamepar,filename); cimg_std::sprintf(filenamerec,"%s.rec",body); } if (!cimg::strcmp(ext,"PAR")) { cimg_std::strcpy(filenamepar,filename); cimg_std::sprintf(filenamerec,"%s.REC",body); } if (!cimg::strcmp(ext,"rec")) { cimg_std::strcpy(filenamerec,filename); cimg_std::sprintf(filenamepar,"%s.par",body); } if (!cimg::strcmp(ext,"REC")) { cimg_std::strcpy(filenamerec,filename); cimg_std::sprintf(filenamepar,"%s.PAR",body); } cimg_std::FILE *file = cimg::fopen(filenamepar,"r"); // Parse header file CImgList st_slices; CImgList st_global; int err; char line[256] = { 0 }; do { err=cimg_std::fscanf(file,"%255[^\n]%*c",line); } while (err!=EOF && (line[0]=='#' || line[0]=='.')); do { unsigned int sn,sizex,sizey,pixsize; float rs,ri,ss; err = cimg_std::fscanf(file,"%u%*u%*u%*u%*u%*u%*u%u%*u%u%u%g%g%g%*[^\n]",&sn,&pixsize,&sizex,&sizey,&ri,&rs,&ss); if (err==7) { st_slices.insert(CImg::vector((float)sn,(float)pixsize,(float)sizex,(float)sizey, ri,rs,ss,0)); unsigned int i; for (i=0; i::vector(sizex,sizey,sn)); else { CImg &vec = st_global[i]; if (sizex>vec[0]) vec[0] = sizex; if (sizey>vec[1]) vec[1] = sizey; vec[2] = sn; } st_slices[st_slices.size-1][7] = (float)i; } } while (err==7); // Read data cimg_std::FILE *file2 = cimg::fopen(filenamerec,"rb"); { cimglist_for(st_global,l) { const CImg& vec = st_global[l]; insert(CImg(vec[0],vec[1],vec[2])); }} cimglist_for(st_slices,l) { const CImg& vec = st_slices[l]; const unsigned int sn = (unsigned int)vec[0]-1, pixsize = (unsigned int)vec[1], sizex = (unsigned int)vec[2], sizey = (unsigned int)vec[3], imn = (unsigned int)vec[7]; const float ri = vec[4], rs = vec[5], ss = vec[6]; switch (pixsize) { case 8 : { CImg buf(sizex,sizey); cimg::fread(buf.data,sizex*sizey,file2); if (cimg::endianness()) cimg::invert_endianness(buf.data,sizex*sizey); CImg& img = (*this)[imn]; cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); } break; case 16 : { CImg buf(sizex,sizey); cimg::fread(buf.data,sizex*sizey,file2); if (cimg::endianness()) cimg::invert_endianness(buf.data,sizex*sizey); CImg& img = (*this)[imn]; cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); } break; case 32 : { CImg buf(sizex,sizey); cimg::fread(buf.data,sizex*sizey,file2); if (cimg::endianness()) cimg::invert_endianness(buf.data,sizex*sizey); CImg& img = (*this)[imn]; cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); } break; default : cimg::fclose(file); cimg::fclose(file2); throw CImgIOException("CImg<%s>::load_parrec() : File '%s', cannot handle image with pixsize = %d bits.", pixel_type(),filename,pixsize); } } cimg::fclose(file); cimg::fclose(file2); if (!size) throw CImgIOException("CImg<%s>::load_parrec() : File '%s' does not appear to be a valid PAR-REC file.", pixel_type(),filename); return *this; } static CImgList get_load_parrec(const char *const filename) { return CImgList().load_parrec(filename); } //! Load an image sequence from a YUV file. CImgList& load_yuv(const char *const filename, const unsigned int sizex, const unsigned int sizey, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1, const bool yuv2rgb=true) { return _load_yuv(0,filename,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb); } static CImgList get_load_yuv(const char *const filename, const unsigned int sizex, const unsigned int sizey=1, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1, const bool yuv2rgb=true) { return CImgList().load_yuv(filename,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb); } //! Load an image sequence from a YUV file. CImgList& load_yuv(cimg_std::FILE *const file, const unsigned int sizex, const unsigned int sizey, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1, const bool yuv2rgb=true) { return _load_yuv(file,0,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb); } static CImgList get_load_yuv(cimg_std::FILE *const file, const unsigned int sizex, const unsigned int sizey=1, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1, const bool yuv2rgb=true) { return CImgList().load_yuv(file,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb); } CImgList& _load_yuv(cimg_std::FILE *const file, const char *const filename, const unsigned int sizex, const unsigned int sizey, const unsigned int first_frame, const unsigned int last_frame, const unsigned int step_frame, const bool yuv2rgb) { if (!filename && !file) throw CImgArgumentException("CImgList<%s>::load_yuv() : Cannot load (null) filename.", pixel_type()); if (sizex%2 || sizey%2) throw CImgArgumentException("CImgList<%s>::load_yuv() : File '%s', image dimensions along X and Y must be " "even numbers (given are %ux%u)\n", pixel_type(),filename?filename:"(FILE*)",sizex,sizey); if (!sizex || !sizey) throw CImgArgumentException("CImgList<%s>::load_yuv() : File '%s', given image sequence size (%u,%u) is invalid", pixel_type(),filename?filename:"(FILE*)",sizex,sizey); const unsigned int nfirst_frame = first_frame tmp(sizex,sizey,1,3), UV(sizex/2,sizey/2,1,2); cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); bool stopflag = false; int err; if (nfirst_frame) { err = cimg_std::fseek(nfile,nfirst_frame*(sizex*sizey + sizex*sizey/2),SEEK_CUR); if (err) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImgList<%s>::load_yuv() : File '%s' doesn't contain frame number %u " "(out of range error).", pixel_type(),filename?filename:"(FILE*)",nfirst_frame); } } unsigned int frame; for (frame = nfirst_frame; !stopflag && frame<=nlast_frame; frame+=nstep_frame) { tmp.fill(0); // *TRY* to read the luminance part, do not replace by cimg::fread ! err = (int)cimg_std::fread((void*)(tmp.data),1,(size_t)(tmp.width*tmp.height),nfile); if (err!=(int)(tmp.width*tmp.height)) { stopflag = true; if (err>0) cimg::warn("CImgList<%s>::load_yuv() : File '%s' contains incomplete data," " or given image dimensions (%u,%u) are incorrect.", pixel_type(),filename?filename:"(FILE*)",sizex,sizey); } else { UV.fill(0); // *TRY* to read the luminance part, do not replace by cimg::fread ! err = (int)cimg_std::fread((void*)(UV.data),1,(size_t)(UV.size()),nfile); if (err!=(int)(UV.size())) { stopflag = true; if (err>0) cimg::warn("CImgList<%s>::load_yuv() : File '%s' contains incomplete data," " or given image dimensions (%u,%u) are incorrect.", pixel_type(),filename?filename:"(FILE*)",sizex,sizey); } else { cimg_forXY(UV,x,y) { const int x2 = x*2, y2 = y*2; tmp(x2,y2,1) = tmp(x2+1,y2,1) = tmp(x2,y2+1,1) = tmp(x2+1,y2+1,1) = UV(x,y,0); tmp(x2,y2,2) = tmp(x2+1,y2,2) = tmp(x2,y2+1,2) = tmp(x2+1,y2+1,2) = UV(x,y,1); } if (yuv2rgb) tmp.YCbCrtoRGB(); insert(tmp); if (nstep_frame>1) cimg_std::fseek(nfile,(nstep_frame-1)*(sizex*sizey + sizex*sizey/2),SEEK_CUR); } } } if (stopflag && nlast_frame!=~0U && frame!=nlast_frame) cimg::warn("CImgList<%s>::load_yuv() : File '%s', frame %d not reached since only %u frames were found in the file.", pixel_type(),filename?filename:"(FILE*)",nlast_frame,frame-1,filename); if (!file) cimg::fclose(nfile); return *this; } //! Load an image from a video file, using ffmpeg libraries. // This piece of code has been firstly created by David Starweather (starkdg(at)users(dot)sourceforge(dot)net) // I modified it afterwards for direct inclusion in the library core. CImgList& 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) { if (!filename) throw CImgArgumentException("CImgList<%s>::load_ffmpeg() : Cannot load (null) filename.", pixel_type()); const unsigned int nfirst_frame = first_frame1) || (resume && (pixel_format || !pixel_format))) throw CImgArgumentException("CImg<%s>::load_ffmpeg() : File '%s', reading sub-frames from a video file requires the use of ffmpeg.\n" "('cimg_use_ffmpeg' must be defined).", pixel_type(),filename); return load_ffmpeg_external(filename); #else const unsigned int ffmpeg_pixfmt = pixel_format?PIX_FMT_RGB24:PIX_FMT_GRAY8; avcodec_register_all(); av_register_all(); static AVFormatContext *format_ctx = 0; static AVCodecContext *codec_ctx = 0; static AVCodec *codec = 0; static AVFrame *avframe = avcodec_alloc_frame(), *converted_frame = avcodec_alloc_frame(); static int vstream = 0; if (resume) { if (!format_ctx || !codec_ctx || !codec || !avframe || !converted_frame) throw CImgArgumentException("CImgList<%s>::load_ffmpeg() : File '%s', cannot resume due to unallocated FFMPEG structures.", pixel_type(),filename); } else { // Open video file, find main video stream and codec. if (format_ctx) av_close_input_file(format_ctx); if (av_open_input_file(&format_ctx,filename,0,0,0)!=0) throw CImgIOException("CImgList<%s>::load_ffmpeg() : File '%s' cannot be opened.", pixel_type(),filename); if (!avframe || !converted_frame || av_find_stream_info(format_ctx)<0) { av_close_input_file(format_ctx); format_ctx = 0; cimg::warn("CImgList<%s>::load_ffmpeg() : File '%s', cannot retrieve stream information.\n" "Trying with external ffmpeg executable.", pixel_type(),filename); return load_ffmpeg_external(filename); } #if cimg_debug>=3 dump_format(format_ctx,0,0,0); #endif // Special command : Return informations on main video stream. // as a vector 1x4 containing : (nb_frames,width,height,fps). if (!first_frame && !last_frame && !step_frame) { for (vstream = 0; vstream<(int)(format_ctx->nb_streams); ++vstream) if (format_ctx->streams[vstream]->codec->codec_type==CODEC_TYPE_VIDEO) break; if (vstream==(int)format_ctx->nb_streams) assign(); else { CImgList timestamps; int nb_frames; AVPacket packet; // Count frames and store timestamps. for (nb_frames = 0; av_read_frame(format_ctx,&packet)>=0; av_free_packet(&packet)) if (packet.stream_index==vstream) { timestamps.insert(CImg::vector((double)packet.pts)); ++nb_frames; } // Get frame with, height and fps. const int framew = format_ctx->streams[vstream]->codec->width, frameh = format_ctx->streams[vstream]->codec->height; const float num = (float)(format_ctx->streams[vstream]->r_frame_rate).num, den = (float)(format_ctx->streams[vstream]->r_frame_rate).den, fps = num/den; // Return infos as a list. assign(2); (*this)[0].assign(1,4).fill((T)nb_frames,(T)framew,(T)frameh,(T)fps); (*this)[1] = timestamps.get_append('y'); } av_close_input_file(format_ctx); format_ctx = 0; return *this; } for (vstream = 0; vstream<(int)(format_ctx->nb_streams) && format_ctx->streams[vstream]->codec->codec_type!=CODEC_TYPE_VIDEO; ) ++vstream; if (vstream==(int)format_ctx->nb_streams) { cimg::warn("CImgList<%s>::load_ffmpeg() : File '%s', cannot retrieve video stream.\n" "Trying with external ffmpeg executable.", pixel_type(),filename); av_close_input_file(format_ctx); format_ctx = 0; return load_ffmpeg_external(filename); } codec_ctx = format_ctx->streams[vstream]->codec; codec = avcodec_find_decoder(codec_ctx->codec_id); if (!codec) { cimg::warn("CImgList<%s>::load_ffmpeg() : File '%s', cannot find video codec.\n" "Trying with external ffmpeg executable.", pixel_type(),filename); return load_ffmpeg_external(filename); } if (avcodec_open(codec_ctx,codec)<0) { // Open codec cimg::warn("CImgList<%s>::load_ffmpeg() : File '%s', cannot open video codec.\n" "Trying with external ffmpeg executable.", pixel_type(),filename); return load_ffmpeg_external(filename); } } // Read video frames const unsigned int numBytes = avpicture_get_size(ffmpeg_pixfmt,codec_ctx->width,codec_ctx->height); uint8_t *const buffer = new uint8_t[numBytes]; avpicture_fill((AVPicture *)converted_frame,buffer,ffmpeg_pixfmt,codec_ctx->width,codec_ctx->height); const T foo = (T)0; AVPacket packet; for (unsigned int frame = 0, next_frame = nfirst_frame; frame<=nlast_frame && av_read_frame(format_ctx,&packet)>=0; ) { if (packet.stream_index==(int)vstream) { int decoded = 0; avcodec_decode_video(codec_ctx,avframe,&decoded,packet.data,packet.size); if (decoded) { if (frame==next_frame) { SwsContext *c = sws_getContext(codec_ctx->width,codec_ctx->height,codec_ctx->pix_fmt,codec_ctx->width, codec_ctx->height,ffmpeg_pixfmt,1,0,0,0); sws_scale(c,avframe->data,avframe->linesize,0,codec_ctx->height,converted_frame->data,converted_frame->linesize); if (ffmpeg_pixfmt==PIX_FMT_RGB24) { CImg next_image(*converted_frame->data,3,codec_ctx->width,codec_ctx->height,1,true); insert(next_image._get_permute_axes("yzvx",foo)); } else { CImg next_image(*converted_frame->data,1,codec_ctx->width,codec_ctx->height,1,true); insert(next_image._get_permute_axes("yzvx",foo)); } next_frame+=nstep_frame; } ++frame; } av_free_packet(&packet); if (next_frame>nlast_frame) break; } } delete[] buffer; #endif return *this; } static CImgList 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) { return CImgList().load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format); } //! Load an image from a video file (MPEG,AVI) using the external tool 'ffmpeg'. CImgList& load_ffmpeg_external(const char *const filename) { if (!filename) throw CImgArgumentException("CImgList<%s>::load_ffmpeg_external() : Cannot load (null) filename.", pixel_type()); char command[1024], filetmp[512], filetmp2[512]; cimg_std::FILE *file = 0; do { cimg_std::sprintf(filetmp,"%s%s%s",cimg::temporary_path(),cimg_OS==2?"\\":"/",cimg::filenamerand()); cimg_std::sprintf(filetmp2,"%s_000001.ppm",filetmp); if ((file=cimg_std::fopen(filetmp2,"rb"))!=0) cimg_std::fclose(file); } while (file); cimg_std::sprintf(filetmp2,"%s_%%6d.ppm",filetmp); #if cimg_OS!=2 cimg_std::sprintf(command,"%s -i \"%s\" %s >/dev/null 2>&1",cimg::ffmpeg_path(),filename,filetmp2); #else cimg_std::sprintf(command,"\"%s -i \"%s\" %s\" >NUL 2>&1",cimg::ffmpeg_path(),filename,filetmp2); #endif cimg::system(command,0); const unsigned int odebug = cimg::exception_mode(); cimg::exception_mode() = 0; assign(); unsigned int i = 1; for (bool stopflag = false; !stopflag; ++i) { cimg_std::sprintf(filetmp2,"%s_%.6u.ppm",filetmp,i); CImg img; try { img.load_pnm(filetmp2); } catch (CImgException&) { stopflag = true; } if (img) { insert(img); cimg_std::remove(filetmp2); } } cimg::exception_mode() = odebug; if (is_empty()) throw CImgIOException("CImgList<%s>::load_ffmpeg_external() : Failed to open image sequence '%s'.\n" "Check the filename and if the 'ffmpeg' tool is installed on your system.", pixel_type(),filename); return *this; } static CImgList get_load_ffmpeg_external(const char *const filename) { return CImgList().load_ffmpeg_external(filename); } //! Load a gzipped list, using external tool 'gunzip'. CImgList& load_gzip_external(const char *const filename) { if (!filename) throw CImgIOException("CImg<%s>::load_gzip_external() : Cannot load (null) filename.", pixel_type()); char command[1024], filetmp[512], body[512]; const char *ext = cimg::split_filename(filename,body), *ext2 = cimg::split_filename(body,0); cimg_std::FILE *file = 0; do { if (!cimg::strcasecmp(ext,"gz")) { if (*ext2) cimg_std::sprintf(filetmp,"%s%s%s.%s",cimg::temporary_path(),cimg_OS==2?"\\":"/", cimg::filenamerand(),ext2); else cimg_std::sprintf(filetmp,"%s%s%s",cimg::temporary_path(),cimg_OS==2?"\\":"/", cimg::filenamerand()); } else { if (*ext) cimg_std::sprintf(filetmp,"%s%s%s.%s",cimg::temporary_path(),cimg_OS==2?"\\":"/", cimg::filenamerand(),ext); else cimg_std::sprintf(filetmp,"%s%s%s",cimg::temporary_path(),cimg_OS==2?"\\":"/", cimg::filenamerand()); } if ((file=cimg_std::fopen(filetmp,"rb"))!=0) cimg_std::fclose(file); } while (file); cimg_std::sprintf(command,"%s -c \"%s\" > %s",cimg::gunzip_path(),filename,filetmp); cimg::system(command); if (!(file = cimg_std::fopen(filetmp,"rb"))) { cimg::fclose(cimg::fopen(filename,"r")); throw CImgIOException("CImg<%s>::load_gzip_external() : File '%s' cannot be opened.", pixel_type(),filename); } else cimg::fclose(file); load(filetmp); cimg_std::remove(filetmp); return *this; } static CImgList get_load_gzip_external(const char *const filename) { return CImgList().load_gzip_external(filename); } //! Load a 3D object from a .OFF file. template CImgList& load_off(const char *const filename, CImgList& primitives, CImgList& colors, const bool invert_faces=false) { return get_load_off(filename,primitives,colors,invert_faces).transfer_to(*this); } template static CImgList get_load_off(const char *const filename, CImgList& primitives, CImgList& colors, const bool invert_faces=false) { return CImg().load_off(filename,primitives,colors,invert_faces).get_split('x'); } //! Load a TIFF file. CImgList& load_tiff(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1) { const unsigned int nfirst_frame = first_frame::load_tiff() : File '%s', reading sub-images from a tiff file requires the use of libtiff.\n" "('cimg_use_tiff' must be defined).", pixel_type(),filename); return assign(CImg::get_load_tiff(filename)); #else TIFF *tif = TIFFOpen(filename,"r"); if (tif) { unsigned int nb_images = 0; do ++nb_images; while (TIFFReadDirectory(tif)); if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images)) cimg::warn("CImgList<%s>::load_tiff() : File '%s' contains %u image(s), specified frame range is [%u,%u] (step %u).", pixel_type(),filename,nb_images,nfirst_frame,nlast_frame,nstep_frame); if (nfirst_frame>=nb_images) return assign(); if (nlast_frame>=nb_images) nlast_frame = nb_images-1; assign(1+(nlast_frame-nfirst_frame)/nstep_frame); TIFFSetDirectory(tif,0); #if cimg_debug>=3 TIFFSetWarningHandler(0); TIFFSetErrorHandler(0); #endif cimglist_for(*this,l) data[l]._load_tiff(tif,nfirst_frame+l*nstep_frame); TIFFClose(tif); } else throw CImgException("CImgList<%s>::load_tiff() : File '%s' cannot be opened.", pixel_type(),filename); return *this; #endif } static CImgList 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) { return CImgList().load_tiff(filename,first_frame,last_frame,step_frame); } //! Save an image list into a file. /** Depending on the extension of the given filename, a file format is chosen for the output file. **/ const CImgList& save(const char *const filename, const int number=-1) const { if (is_empty()) throw CImgInstanceException("CImgList<%s>::save() : File '%s, instance list (%u,%p) is empty.", pixel_type(),filename?filename:"(null)",size,data); if (!filename) throw CImgArgumentException("CImg<%s>::save() : Instance list (%u,%p), specified filename is (null).", pixel_type(),size,data); const char *ext = cimg::split_filename(filename); char nfilename[1024]; const char *const fn = (number>=0)?cimg::number_filename(filename,number,6,nfilename):filename; #ifdef cimglist_save_plugin cimglist_save_plugin(fn); #endif #ifdef cimglist_save_plugin1 cimglist_save_plugin1(fn); #endif #ifdef cimglist_save_plugin2 cimglist_save_plugin2(fn); #endif #ifdef cimglist_save_plugin3 cimglist_save_plugin3(fn); #endif #ifdef cimglist_save_plugin4 cimglist_save_plugin4(fn); #endif #ifdef cimglist_save_plugin5 cimglist_save_plugin5(fn); #endif #ifdef cimglist_save_plugin6 cimglist_save_plugin6(fn); #endif #ifdef cimglist_save_plugin7 cimglist_save_plugin7(fn); #endif #ifdef cimglist_save_plugin8 cimglist_save_plugin8(fn); #endif #ifdef cimg_use_tiff if (!cimg::strcasecmp(ext,"tif") || !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn); #endif if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true); if (!cimg::strcasecmp(ext,"cimg") || !ext[0]) return save_cimg(fn,false); if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,true); if (!cimg::strcasecmp(ext,"avi") || !cimg::strcasecmp(ext,"mov") || !cimg::strcasecmp(ext,"asf") || !cimg::strcasecmp(ext,"divx") || !cimg::strcasecmp(ext,"flv") || !cimg::strcasecmp(ext,"mpg") || !cimg::strcasecmp(ext,"m1v") || !cimg::strcasecmp(ext,"m2v") || !cimg::strcasecmp(ext,"m4v") || !cimg::strcasecmp(ext,"mjp") || !cimg::strcasecmp(ext,"mkv") || !cimg::strcasecmp(ext,"mpe") || !cimg::strcasecmp(ext,"movie") || !cimg::strcasecmp(ext,"ogm") || !cimg::strcasecmp(ext,"qt") || !cimg::strcasecmp(ext,"rm") || !cimg::strcasecmp(ext,"vob") || !cimg::strcasecmp(ext,"wmv") || !cimg::strcasecmp(ext,"xvid") || !cimg::strcasecmp(ext,"mpeg")) return save_ffmpeg(fn); if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn); if (size==1) data[0].save(fn,-1); else cimglist_for(*this,l) data[l].save(fn,l); return *this; } //! Save an image sequence, using FFMPEG library. // This piece of code has been originally written by David. G. Starkweather. const CImgList& save_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int fps=25) const { if (is_empty()) throw CImgInstanceException("CImgList<%s>::save_ffmpeg() : File '%s', instance list (%u,%p) is empty.", pixel_type(),filename?filename:"(null)",size,data); if (!filename) throw CImgArgumentException("CImgList<%s>::save_ffmpeg() : Instance list (%u,%p), specified filename is (null).", pixel_type(),size,data); if (!fps) throw CImgArgumentException("CImgList<%s>::save_ffmpeg() : File '%s', specified framerate is 0.", pixel_type(),filename); const unsigned int nlast_frame = last_frame==~0U?size-1:last_frame; if (first_frame>=size || nlast_frame>=size) throw CImgArgumentException("CImgList<%s>::save_ffmpeg() : File '%s', specified frames [%u,%u] are out of list range (%u elements).", pixel_type(),filename,first_frame,last_frame,size); for (unsigned int ll = first_frame; ll<=nlast_frame; ++ll) if (!data[ll].is_sameXYZ(data[0])) throw CImgInstanceException("CImgList<%s>::save_ffmpeg() : File '%s', images of the sequence have different dimensions.", pixel_type(),filename); #ifndef cimg_use_ffmpeg return save_ffmpeg_external(filename,first_frame,last_frame); #else avcodec_register_all(); av_register_all(); const int frame_dimx = data[first_frame].dimx(), frame_dimy = data[first_frame].dimy(), frame_dimv = data[first_frame].dimv(); if (frame_dimv!=1 && frame_dimv!=3) throw CImgInstanceException("CImgList<%s>::save_ffmpeg() : File '%s', image[0] (%u,%u,%u,%u,%p) has not 1 or 3 channels.", pixel_type(),filename,data[0].width,data[0].height,data[0].depth,data[0].dim,data); PixelFormat dest_pxl_fmt = PIX_FMT_YUV420P; PixelFormat src_pxl_fmt = (frame_dimv == 3)?PIX_FMT_RGB24:PIX_FMT_GRAY8; int sws_flags = SWS_FAST_BILINEAR; // Interpolation method (keeping same size images for now). AVOutputFormat *fmt = 0; fmt = guess_format(0,filename,0); if (!fmt) fmt = guess_format("mpeg",0,0); // Default format "mpeg". if (!fmt) throw CImgArgumentException("CImgList<%s>::save_ffmpeg() : File '%s', could not determine file format from filename.", pixel_type(),filename); AVFormatContext *oc = 0; oc = av_alloc_format_context(); if (!oc) // Failed to allocate format context. throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', failed to allocate structure for format context.", pixel_type(),filename); AVCodec *codec = 0; AVFrame *picture = 0; AVFrame *tmp_pict = 0; oc->oformat = fmt; cimg_std::sprintf(oc->filename,"%s",filename); // Add video stream. int stream_index = 0; AVStream *video_str = 0; if (fmt->video_codec!=CODEC_ID_NONE) { video_str = av_new_stream(oc,stream_index); if (!video_str) { // Failed to allocate stream. av_free(oc); throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', failed to allocate video stream structure.", pixel_type(),filename); } } else { // No codec identified. av_free(oc); throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', no proper codec identified.", pixel_type(),filename); } AVCodecContext *c = video_str->codec; c->codec_id = fmt->video_codec; c->codec_type = CODEC_TYPE_VIDEO; c->bit_rate = 400000; c->width = frame_dimx; c->height = frame_dimy; c->time_base.num = 1; c->time_base.den = fps; c->gop_size = 12; c->pix_fmt = dest_pxl_fmt; if (c->codec_id == CODEC_ID_MPEG2VIDEO) c->max_b_frames = 2; if (c->codec_id == CODEC_ID_MPEG1VIDEO) c->mb_decision = 2; if (av_set_parameters(oc,0)<0) { // Parameters not properly set. av_free(oc); throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', parameters for avcodec not properly set.", pixel_type(),filename); } // Open codecs and alloc buffers. codec = avcodec_find_encoder(c->codec_id); if (!codec) { // Failed to find codec. av_free(oc); throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', no codec found.", pixel_type(),filename); } if (avcodec_open(c,codec)<0) // Failed to open codec. throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', failed to open codec.", pixel_type(),filename); tmp_pict = avcodec_alloc_frame(); if (!tmp_pict) { // Failed to allocate memory for tmp_pict frame. avcodec_close(video_str->codec); av_free(oc); throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', failed to allocate memory for data buffer.", pixel_type(),filename); } tmp_pict->linesize[0] = (src_pxl_fmt==PIX_FMT_RGB24)?3*frame_dimx:frame_dimx; tmp_pict->type = FF_BUFFER_TYPE_USER; int tmp_size = avpicture_get_size(src_pxl_fmt,frame_dimx,frame_dimy); uint8_t *tmp_buffer = (uint8_t*)av_malloc(tmp_size); if (!tmp_buffer) { // Failed to allocate memory for tmp buffer. av_free(tmp_pict); avcodec_close(video_str->codec); av_free(oc); throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', failed to allocate memory for data buffer.", pixel_type(),filename); } // Associate buffer with tmp_pict. avpicture_fill((AVPicture*)tmp_pict,tmp_buffer,src_pxl_fmt,frame_dimx,frame_dimy); picture = avcodec_alloc_frame(); if (!picture) { // Failed to allocate picture frame. av_free(tmp_pict->data[0]); av_free(tmp_pict); avcodec_close(video_str->codec); av_free(oc); throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', failed to allocate memory for picture frame.", pixel_type(),filename); } int size = avpicture_get_size(c->pix_fmt,frame_dimx,frame_dimy); uint8_t *buffer = (uint8_t*)av_malloc(size); if (!buffer) { // Failed to allocate picture frame buffer. av_free(picture); av_free(tmp_pict->data[0]); av_free(tmp_pict); avcodec_close(video_str->codec); av_free(oc); throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', failed to allocate memory for picture frame buffer.", pixel_type(),filename); } // Associate the buffer with picture. avpicture_fill((AVPicture*)picture,buffer,c->pix_fmt,frame_dimx,frame_dimy); // Open file. if (!(fmt->flags&AVFMT_NOFILE)) { if (url_fopen(&oc->pb,filename,URL_WRONLY)<0) throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s' cannot be opened.", pixel_type(),filename); } if (av_write_header(oc)<0) throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', could not write header.", pixel_type(),filename); double video_pts; SwsContext *img_convert_context = 0; img_convert_context = sws_getContext(frame_dimx,frame_dimy,src_pxl_fmt, c->width,c->height,c->pix_fmt,sws_flags,0,0,0); if (!img_convert_context) { // Failed to get swscale context. // if (!(fmt->flags & AVFMT_NOFILE)) url_fclose(&oc->pb); av_free(picture->data); av_free(picture); av_free(tmp_pict->data[0]); av_free(tmp_pict); avcodec_close(video_str->codec); av_free(oc); throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%', failed to get conversion context.", pixel_type(),filename); } int ret = 0, out_size; uint8_t *video_outbuf = 0; int video_outbuf_size = 1000000; video_outbuf = (uint8_t*)av_malloc(video_outbuf_size); if (!video_outbuf) { // if (!(fmt->flags & AVFMT_NOFILE)) url_fclose(&oc->pb); av_free(picture->data); av_free(picture); av_free(tmp_pict->data[0]); av_free(tmp_pict); avcodec_close(video_str->codec); av_free(oc); throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', memory allocation error.", pixel_type(),filename); } // Loop through each desired image in list. for (unsigned int i = first_frame; i<=nlast_frame; ++i) { CImg currentIm = data[i], red, green, blue, gray; if (src_pxl_fmt == PIX_FMT_RGB24) { red = currentIm.get_shared_channel(0); green = currentIm.get_shared_channel(1); blue = currentIm.get_shared_channel(2); cimg_forXY(currentIm,X,Y) { // Assign pizel values to data buffer in interlaced RGBRGB ... format. tmp_pict->data[0][Y*tmp_pict->linesize[0] + 3*X] = red(X,Y); tmp_pict->data[0][Y*tmp_pict->linesize[0] + 3*X + 1] = green(X,Y); tmp_pict->data[0][Y*tmp_pict->linesize[0] + 3*X + 2] = blue(X,Y); } } else { gray = currentIm.get_shared_channel(0); cimg_forXY(currentIm,X,Y) tmp_pict->data[0][Y*tmp_pict->linesize[0] + X] = gray(X,Y); } if (video_str) video_pts = (video_str->pts.val * video_str->time_base.num)/(video_str->time_base.den); else video_pts = 0.0; if (!video_str) break; if (sws_scale(img_convert_context,tmp_pict->data,tmp_pict->linesize,0,c->height,picture->data,picture->linesize)<0) break; out_size = avcodec_encode_video(c,video_outbuf,video_outbuf_size,picture); if (out_size>0) { AVPacket pkt; av_init_packet(&pkt); pkt.pts = av_rescale_q(c->coded_frame->pts,c->time_base,video_str->time_base); if (c->coded_frame->key_frame) pkt.flags|=PKT_FLAG_KEY; pkt.stream_index = video_str->index; pkt.data = video_outbuf; pkt.size = out_size; ret = av_write_frame(oc,&pkt); } else if (out_size<0) break; if (ret) break; // Error occured in writing frame. } // Close codec. if (video_str) { avcodec_close(video_str->codec); av_free(picture->data[0]); av_free(picture); av_free(tmp_pict->data[0]); av_free(tmp_pict); } if (av_write_trailer(oc)<0) throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', failed to write trailer.", pixel_type(),filename); av_freep(&oc->streams[stream_index]->codec); av_freep(&oc->streams[stream_index]); if (!(fmt->flags&AVFMT_NOFILE)) { /*if (url_fclose(oc->pb)<0) throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', failed to close file.", pixel_type(),filename); */ } av_free(oc); av_free(video_outbuf); #endif return *this; } // Save an image sequence into a YUV file (internal). const CImgList& _save_yuv(cimg_std::FILE *const file, const char *const filename, const bool rgb2yuv) const { if (is_empty()) throw CImgInstanceException("CImgList<%s>::save_yuv() : File '%s', instance list (%u,%p) is empty.", pixel_type(),filename?filename:"(FILE*)",size,data); if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_yuv() : Instance list (%u,%p), specified file is (null).", pixel_type(),size,data); if ((*this)[0].dimx()%2 || (*this)[0].dimy()%2) throw CImgInstanceException("CImgList<%s>::save_yuv() : File '%s', image dimensions must be even numbers (current are %ux%u).", pixel_type(),filename?filename:"(FILE*)",(*this)[0].dimx(),(*this)[0].dimy()); cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); cimglist_for(*this,l) { CImg YCbCr((*this)[l]); if (rgb2yuv) YCbCr.RGBtoYCbCr(); cimg::fwrite(YCbCr.data,YCbCr.width*YCbCr.height,nfile); cimg::fwrite(YCbCr.get_resize(YCbCr.width/2, YCbCr.height/2,1,3,3).ptr(0,0,0,1), YCbCr.width*YCbCr.height/2,nfile); } if (!file) cimg::fclose(nfile); return *this; } //! Save an image sequence into a YUV file. const CImgList& save_yuv(const char *const filename=0, const bool rgb2yuv=true) const { return _save_yuv(0,filename,rgb2yuv); } //! Save an image sequence into a YUV file. const CImgList& save_yuv(cimg_std::FILE *const file, const bool rgb2yuv=true) const { return _save_yuv(file,0,rgb2yuv); } //! Save an image list into a .cimg file. /** A CImg RAW file is a simple uncompressed binary file that may be used to save list of CImg images. \param filename : name of the output file. \return A reference to the current CImgList instance is returned. **/ const CImgList& _save_cimg(cimg_std::FILE *const file, const char *const filename, const bool compression) const { if (is_empty()) throw CImgInstanceException("CImgList<%s>::save_cimg() : File '%s', instance list (%u,%p) is empty.", pixel_type(),filename?filename:"(FILE*)",size,data); if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_cimg() : Instance list (%u,%p), specified file is (null).", pixel_type(),size,data); cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little"; if (cimg_std::strstr(ptype,"unsigned")==ptype) cimg_std::fprintf(nfile,"%u unsigned_%s %s_endian\n",size,ptype+9,etype); else cimg_std::fprintf(nfile,"%u %s %s_endian\n",size,ptype,etype); cimglist_for(*this,l) { const CImg& img = data[l]; cimg_std::fprintf(nfile,"%u %u %u %u",img.width,img.height,img.depth,img.dim); if (img.data) { CImg tmp; if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp.data,tmp.size()); } const CImg& ref = cimg::endianness()?tmp:img; bool compressed = false; if (compression) { #ifdef cimg_use_zlib const unsigned long siz = sizeof(T)*ref.size(); unsigned long csiz = siz + siz/10 + 16; Bytef *const cbuf = new Bytef[csiz]; if (compress(cbuf,&csiz,(Bytef*)ref.data,siz)) { cimg::warn("CImgList<%s>::save_cimg() : File '%s', failed to save compressed data.\n Data will be saved uncompressed.", pixel_type(),filename?filename:"(FILE*)"); compressed = false; } else { cimg_std::fprintf(nfile," #%lu\n",csiz); cimg::fwrite(cbuf,csiz,nfile); delete[] cbuf; compressed = true; } #else cimg::warn("CImgList<%s>::save_cimg() : File '%s', cannot save compressed data unless zlib is used " "('cimg_use_zlib' must be defined).\n Data will be saved uncompressed.", pixel_type(),filename?filename:"(FILE*)"); compressed = false; #endif } if (!compressed) { cimg_std::fputc('\n',nfile); cimg::fwrite(ref.data,ref.size(),nfile); } } else cimg_std::fputc('\n',nfile); } if (!file) cimg::fclose(nfile); return *this; } //! Save an image list into a CImg file (RAW binary file + simple header) const CImgList& save_cimg(cimg_std::FILE *file, const bool compress=false) const { return _save_cimg(file,0,compress); } //! Save an image list into a CImg file (RAW binary file + simple header) const CImgList& save_cimg(const char *const filename, const bool compress=false) const { return _save_cimg(0,filename,compress); } // Insert the instance image into into an existing .cimg file, at specified coordinates. const CImgList& _save_cimg(cimg_std::FILE *const file, const char *const filename, const unsigned int n0, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int v0) const { #define _cimg_save_cimg_case(Ts,Tss) \ if (!saved && !cimg::strcasecmp(Ts,str_pixeltype)) { \ for (unsigned int l=0; l::save_cimg() : File '%s', Image %u has an invalid size (%u,%u,%u,%u)\n", \ pixel_type(), filename?filename:("(FILE*)"), W, H, D, V); \ if (W*H*D*V>0) { \ if (l=W || y0>=H || z0>=D || v0>=D) cimg_std::fseek(nfile,W*H*D*V*sizeof(Tss),SEEK_CUR); \ else { \ const CImg& img = (*this)[l-n0]; \ const T *ptrs = img.data; \ const unsigned int \ x1 = x0 + img.width - 1, \ y1 = y0 + img.height - 1, \ z1 = z0 + img.depth - 1, \ v1 = v0 + img.dim - 1, \ nx1 = x1>=W?W-1:x1, \ ny1 = y1>=H?H-1:y1, \ nz1 = z1>=D?D-1:z1, \ nv1 = v1>=V?V-1:v1; \ CImg raw(1+nx1-x0); \ const unsigned int skipvb = v0*W*H*D*sizeof(Tss); \ if (skipvb) cimg_std::fseek(nfile,skipvb,SEEK_CUR); \ for (unsigned int v=1+nv1-v0; v; --v) { \ const unsigned int skipzb = z0*W*H*sizeof(Tss); \ if (skipzb) cimg_std::fseek(nfile,skipzb,SEEK_CUR); \ for (unsigned int z=1+nz1-z0; z; --z) { \ const unsigned int skipyb = y0*W*sizeof(Tss); \ if (skipyb) cimg_std::fseek(nfile,skipyb,SEEK_CUR); \ for (unsigned int y=1+ny1-y0; y; --y) { \ const unsigned int skipxb = x0*sizeof(Tss); \ if (skipxb) cimg_std::fseek(nfile,skipxb,SEEK_CUR); \ raw.assign(ptrs, raw.width); \ ptrs+=img.width; \ if (endian) cimg::invert_endianness(raw.data,raw.width); \ cimg::fwrite(raw.data,raw.width,nfile); \ const unsigned int skipxe = (W-1-nx1)*sizeof(Tss); \ if (skipxe) cimg_std::fseek(nfile,skipxe,SEEK_CUR); \ } \ const unsigned int skipye = (H-1-ny1)*W*sizeof(Tss); \ if (skipye) cimg_std::fseek(nfile,skipye,SEEK_CUR); \ } \ const unsigned int skipze = (D-1-nz1)*W*H*sizeof(Tss); \ if (skipze) cimg_std::fseek(nfile,skipze,SEEK_CUR); \ } \ const unsigned int skipve = (V-1-nv1)*W*H*D*sizeof(Tss); \ if (skipve) cimg_std::fseek(nfile,skipve,SEEK_CUR); \ } \ } \ } \ saved = true; \ } if (is_empty()) throw CImgInstanceException("CImgList<%s>::save_cimg() : File '%s', instance list (%u,%p) is empty.", pixel_type(),filename?filename:"(FILE*)",size,data); if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_cimg() : Instance list (%u,%p), specified file is (null).", pixel_type(),size,data); typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned int uint; typedef unsigned long ulong; cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb+"); bool saved = false, endian = cimg::endianness(); char tmp[256], str_pixeltype[256], str_endian[256]; unsigned int j, err, N, W, H, D, V; int i; j = 0; while((i=cimg_std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = '\0'; err = cimg_std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype,str_endian); if (err<2) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImgList<%s>::save_cimg() : File '%s', Unknow CImg RAW header.", pixel_type(),filename?filename:"(FILE*)"); } if (!cimg::strncasecmp("little",str_endian,6)) endian = false; else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; const unsigned int lmax = cimg::min(N,n0+size); _cimg_save_cimg_case("bool",bool); _cimg_save_cimg_case("unsigned_char",uchar); _cimg_save_cimg_case("uchar",uchar); _cimg_save_cimg_case("char",char); _cimg_save_cimg_case("unsigned_short",ushort); _cimg_save_cimg_case("ushort",ushort); _cimg_save_cimg_case("short",short); _cimg_save_cimg_case("unsigned_int",uint); _cimg_save_cimg_case("uint",uint); _cimg_save_cimg_case("int",int); _cimg_save_cimg_case("unsigned_long",ulong); _cimg_save_cimg_case("ulong",ulong); _cimg_save_cimg_case("long",long); _cimg_save_cimg_case("float",float); _cimg_save_cimg_case("double",double); if (!saved) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImgList<%s>::save_cimg() : File '%s', cannot save images of pixels coded as '%s'.", pixel_type(),filename?filename:"(FILE*)",str_pixeltype); } if (!file) cimg::fclose(nfile); return *this; } //! Insert the instance image into into an existing .cimg file, at specified coordinates. const CImgList& 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 v0) const { return _save_cimg(0,filename,n0,x0,y0,z0,v0); } //! Insert the instance image into into an existing .cimg file, at specified coordinates. const CImgList& save_cimg(cimg_std::FILE *const file, const unsigned int n0, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int v0) const { return _save_cimg(file,0,n0,x0,y0,z0,v0); } // Create an empty .cimg file with specified dimensions (internal) static void _save_empty_cimg(cimg_std::FILE *const file, const char *const filename, const unsigned int nb, const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv) { cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); const unsigned int siz = dx*dy*dz*dv*sizeof(T); cimg_std::fprintf(nfile,"%u %s\n",nb,pixel_type()); for (unsigned int i=nb; i; --i) { cimg_std::fprintf(nfile,"%u %u %u %u\n",dx,dy,dz,dv); for (unsigned int off=siz; off; --off) cimg_std::fputc(0,nfile); } if (!file) cimg::fclose(nfile); } //! Create an empty .cimg file with specified dimensions. 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 dv=1) { return _save_empty_cimg(0,filename,nb,dx,dy,dz,dv); } //! Create an empty .cimg file with specified dimensions. static void save_empty_cimg(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 dv=1) { return _save_empty_cimg(file,0,nb,dx,dy,dz,dv); } //! Save a file in TIFF format. #ifdef cimg_use_tiff const CImgList& save_tiff(const char *const filename) const { if (is_empty()) throw CImgInstanceException("CImgList<%s>::save_tiff() : File '%s', instance list (%u,%p) is empty.", pixel_type(),filename?filename:"(null)",size,data); if (!filename) throw CImgArgumentException("CImgList<%s>::save_tiff() : Specified filename is (null) for instance list (%u,%p).", pixel_type(),size,data); TIFF *tif = TIFFOpen(filename,"w"); if (tif) { for (unsigned int dir=0, l=0; l& img = (*this)[l]; if (img) { if (img.depth==1) img._save_tiff(tif,dir++); else cimg_forZ(img,z) img.get_slice(z)._save_tiff(tif,dir++); } } TIFFClose(tif); } else throw CImgException("CImgList<%s>::save_tiff() : File '%s', error while opening stream for tiff file.", pixel_type(),filename); return *this; } #endif //! Save an image list as a gzipped file, using external tool 'gzip'. const CImgList& save_gzip_external(const char *const filename) const { if (!filename) throw CImgIOException("CImg<%s>::save_gzip_external() : Cannot save (null) filename.", pixel_type()); char command[1024], filetmp[512], body[512]; const char *ext = cimg::split_filename(filename,body), *ext2 = cimg::split_filename(body,0); cimg_std::FILE *file; do { if (!cimg::strcasecmp(ext,"gz")) { if (*ext2) cimg_std::sprintf(filetmp,"%s%s%s.%s",cimg::temporary_path(),cimg_OS==2?"\\":"/", cimg::filenamerand(),ext2); else cimg_std::sprintf(filetmp,"%s%s%s.cimg",cimg::temporary_path(),cimg_OS==2?"\\":"/", cimg::filenamerand()); } else { if (*ext) cimg_std::sprintf(filetmp,"%s%s%s.%s",cimg::temporary_path(),cimg_OS==2?"\\":"/", cimg::filenamerand(),ext); else cimg_std::sprintf(filetmp,"%s%s%s.cimg",cimg::temporary_path(),cimg_OS==2?"\\":"/", cimg::filenamerand()); } if ((file=cimg_std::fopen(filetmp,"rb"))!=0) cimg_std::fclose(file); } while (file); save(filetmp); cimg_std::sprintf(command,"%s -c %s > \"%s\"",cimg::gzip_path(),filetmp,filename); cimg::system(command); file = cimg_std::fopen(filename,"rb"); if (!file) throw CImgIOException("CImgList<%s>::save_gzip_external() : File '%s' cannot be saved.", pixel_type(),filename); else cimg::fclose(file); cimg_std::remove(filetmp); return *this; } //! Save an image list into a OFF file. template const CImgList& save_off(const char *const filename, const CImgList& primitives, const CImgList& colors, const bool invert_faces=false) const { get_append('x','y').save_off(filename,primitives,colors,invert_faces); return *this; } //! Save an image list into a OFF file. template const CImgList& save_off(cimg_std::FILE *const file, const CImgList& primitives, const CImgList& colors, const bool invert_faces=false) const { get_append('x','y').save_off(file,primitives,colors,invert_faces); return *this; } //! Save an image sequence using the external tool 'ffmpeg'. const CImgList& save_ffmpeg_external(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const char *const codec="mpeg2video") const { if (is_empty()) throw CImgInstanceException("CImgList<%s>::save_ffmpeg_external() : File '%s', instance list (%u,%p) is empty.", pixel_type(),filename?filename:"(null)",size,data); if (!filename) throw CImgArgumentException("CImgList<%s>::save_ffmpeg_external() : Instance list (%u,%p), specified filename is (null).", pixel_type(),size,data); char command[1024], filetmp[512], filetmp2[512]; cimg_std::FILE *file = 0; const unsigned int nlast_frame = last_frame==~0U?size-1:last_frame; if (first_frame>=size || nlast_frame>=size) throw CImgArgumentException("CImgList<%s>::save_ffmpeg_external() : File '%s', specified frames [%u,%u] are out of list range (%u elements).", pixel_type(),filename,first_frame,last_frame,size); for (unsigned int ll = first_frame; ll<=nlast_frame; ++ll) if (!data[ll].is_sameXYZ(data[0])) throw CImgInstanceException("CImgList<%s>::save_ffmpeg_external() : File '%s', all images of the sequence must be of the same dimension.", pixel_type(),filename); do { cimg_std::sprintf(filetmp,"%s%s%s",cimg::temporary_path(),cimg_OS==2?"\\":"/",cimg::filenamerand()); cimg_std::sprintf(filetmp2,"%s_000001.ppm",filetmp); if ((file=cimg_std::fopen(filetmp2,"rb"))!=0) cimg_std::fclose(file); } while (file); for (unsigned int l = first_frame; l<=nlast_frame; ++l) { cimg_std::sprintf(filetmp2,"%s_%.6u.ppm",filetmp,l+1); if (data[l].depth>1 || data[l].dim!=3) data[l].get_resize(-100,-100,1,3).save_pnm(filetmp2); else data[l].save_pnm(filetmp2); } #if cimg_OS!=2 cimg_std::sprintf(command,"ffmpeg -i %s_%%6d.ppm -vcodec %s -sameq -y \"%s\" >/dev/null 2>&1",filetmp,codec,filename); #else cimg_std::sprintf(command,"\"ffmpeg -i %s_%%6d.ppm -vcodec %s -sameq -y \"%s\"\" >NUL 2>&1",filetmp,codec,filename); #endif cimg::system(command); file = cimg_std::fopen(filename,"rb"); if (!file) throw CImgIOException("CImg<%s>::save_ffmpeg_external() : Failed to save image sequence '%s'.\n\n", pixel_type(),filename); else cimg::fclose(file); cimglist_for(*this,lll) { cimg_std::sprintf(filetmp2,"%s_%.6u.ppm",filetmp,lll+1); cimg_std::remove(filetmp2); } return *this; } }; /* #--------------------------------------------- # # Completion of previously declared functions # #---------------------------------------------- */ namespace cimg { //! Display a dialog box, where a user can click standard buttons. /** Up to 6 buttons can be defined in the dialog window. This function returns when a user clicked one of the button or closed the dialog window. \param title = Title of the dialog window. \param msg = Main message displayed inside the dialog window. \param button1_txt = Label of the 1st button. \param button2_txt = Label of the 2nd button. \param button3_txt = Label of the 3rd button. \param button4_txt = Label of the 4th button. \param button5_txt = Label of the 5th button. \param button6_txt = Label of the 6th button. \param logo = Logo image displayed at the left of the main message. This parameter is optional. \param centering = Tell to center the dialog window on the screen. \return The button number (from 0 to 5), or -1 if the dialog window has been closed by the user. \note If a button text is set to 0, then the corresponding button (and the followings) won't appear in the dialog box. At least one button is necessary. **/ template inline int dialog(const char *title, const char *msg, const char *button1_txt, const char *button2_txt, const char *button3_txt, const char *button4_txt, const char *button5_txt, const char *button6_txt, const CImg& logo, const bool centering = false) { #if cimg_display!=0 const unsigned char black[] = { 0,0,0 }, white[] = { 255,255,255 }, gray[] = { 200,200,200 }, gray2[] = { 150,150,150 }; // Create buttons and canvas graphics CImgList buttons, cbuttons, sbuttons; if (button1_txt) { buttons.insert(CImg().draw_text(0,0,button1_txt,black,gray,1,13)); if (button2_txt) { buttons.insert(CImg().draw_text(0,0,button2_txt,black,gray,1,13)); if (button3_txt) { buttons.insert(CImg().draw_text(0,0,button3_txt,black,gray,1,13)); if (button4_txt) { buttons.insert(CImg().draw_text(0,0,button4_txt,black,gray,1,13)); if (button5_txt) { buttons.insert(CImg().draw_text(0,0,button5_txt,black,gray,1,13)); if (button6_txt) { buttons.insert(CImg().draw_text(0,0,button6_txt,black,gray,1,13)); }}}}}} if (!buttons.size) throw CImgArgumentException("cimg::dialog() : No buttons have been defined. At least one is necessary"); unsigned int bw = 0, bh = 0; cimglist_for(buttons,l) { bw = cimg::max(bw,buttons[l].width); bh = cimg::max(bh,buttons[l].height); } bw+=8; bh+=8; if (bw<64) bw=64; if (bw>128) bw=128; if (bh<24) bh=24; if (bh>48) bh=48; CImg button(bw,bh,1,3); button.draw_rectangle(0,0,bw-1,bh-1,gray); button.draw_line(0,0,bw-1,0,white).draw_line(0,bh-1,0,0,white); button.draw_line(bw-1,0,bw-1,bh-1,black).draw_line(bw-1,bh-1,0,bh-1,black); button.draw_line(1,bh-2,bw-2,bh-2,gray2).draw_line(bw-2,bh-2,bw-2,1,gray2); CImg sbutton(bw,bh,1,3); sbutton.draw_rectangle(0,0,bw-1,bh-1,gray); sbutton.draw_line(0,0,bw-1,0,black).draw_line(bw-1,0,bw-1,bh-1,black); sbutton.draw_line(bw-1,bh-1,0,bh-1,black).draw_line(0,bh-1,0,0,black); sbutton.draw_line(1,1,bw-2,1,white).draw_line(1,bh-2,1,1,white); sbutton.draw_line(bw-2,1,bw-2,bh-2,black).draw_line(bw-2,bh-2,1,bh-2,black); sbutton.draw_line(2,bh-3,bw-3,bh-3,gray2).draw_line(bw-3,bh-3,bw-3,2,gray2); 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); 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); CImg cbutton(bw,bh,1,3); 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); 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); 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); cimglist_for(buttons,ll) { cbuttons.insert(CImg(cbutton).draw_image(1+(bw-buttons[ll].dimx())/2,1+(bh-buttons[ll].dimy())/2,buttons[ll])); sbuttons.insert(CImg(sbutton).draw_image((bw-buttons[ll].dimx())/2,(bh-buttons[ll].dimy())/2,buttons[ll])); buttons[ll] = CImg(button).draw_image((bw-buttons[ll].dimx())/2,(bh-buttons[ll].dimy())/2,buttons[ll]); } CImg canvas; if (msg) canvas = CImg().draw_text(0,0,msg,black,gray,1,13); const unsigned int bwall = (buttons.size-1)*(12+bw) + bw, w = cimg::max(196U,36+logo.width+canvas.width, 24+bwall), h = cimg::max(96U,36+canvas.height+bh,36+logo.height+bh), lx = 12 + (canvas.data?0:((w-24-logo.width)/2)), ly = (h-12-bh-logo.height)/2, tx = lx+logo.width+12, ty = (h-12-bh-canvas.height)/2, bx = (w-bwall)/2, by = h-12-bh; if (canvas.data) canvas = CImg(w,h,1,3). draw_rectangle(0,0,w-1,h-1,gray). draw_line(0,0,w-1,0,white).draw_line(0,h-1,0,0,white). draw_line(w-1,0,w-1,h-1,black).draw_line(w-1,h-1,0,h-1,black). draw_image(tx,ty,canvas); else canvas = CImg(w,h,1,3). draw_rectangle(0,0,w-1,h-1,gray). draw_line(0,0,w-1,0,white).draw_line(0,h-1,0,0,white). draw_line(w-1,0,w-1,h-1,black).draw_line(w-1,h-1,0,h-1,black); if (logo.data) canvas.draw_image(lx,ly,logo); unsigned int xbuttons[6]; cimglist_for(buttons,lll) { xbuttons[lll] = bx+(bw+12)*lll; canvas.draw_image(xbuttons[lll],by,buttons[lll]); } // Open window and enter events loop CImgDisplay disp(canvas,title?title:" ",0,false,centering?true:false); if (centering) disp.move((CImgDisplay::screen_dimx()-disp.dimx())/2, (CImgDisplay::screen_dimy()-disp.dimy())/2); bool stopflag = false, refresh = false; int oselected = -1, oclicked = -1, selected = -1, clicked = -1; while (!disp.is_closed && !stopflag) { if (refresh) { if (clicked>=0) CImg(canvas).draw_image(xbuttons[clicked],by,cbuttons[clicked]).display(disp); else { if (selected>=0) CImg(canvas).draw_image(xbuttons[selected],by,sbuttons[selected]).display(disp); else canvas.display(disp); } refresh = false; } disp.wait(15); if (disp.is_resized) disp.resize(disp); if (disp.button&1) { oclicked = clicked; clicked = -1; cimglist_for(buttons,l) if (disp.mouse_y>=(int)by && disp.mouse_y<(int)(by+bh) && disp.mouse_x>=(int)xbuttons[l] && disp.mouse_x<(int)(xbuttons[l]+bw)) { clicked = selected = l; refresh = true; } if (clicked!=oclicked) refresh = true; } else if (clicked>=0) stopflag = true; if (disp.key) { oselected = selected; switch (disp.key) { case cimg::keyESC : selected=-1; stopflag=true; break; case cimg::keyENTER : if (selected<0) selected = 0; stopflag = true; break; case cimg::keyTAB : case cimg::keyARROWRIGHT : case cimg::keyARROWDOWN : selected = (selected+1)%buttons.size; break; case cimg::keyARROWLEFT : case cimg::keyARROWUP : selected = (selected+buttons.size-1)%buttons.size; break; } disp.key = 0; if (selected!=oselected) refresh = true; } } if (!disp) selected = -1; return selected; #else cimg_std::fprintf(cimg_stdout,"<%s>\n\n%s\n\n",title,msg); return -1+0*(int)(button1_txt-button2_txt+button3_txt-button4_txt+button5_txt-button6_txt+logo.width+(int)centering); #endif } inline int dialog(const char *title, const char *msg, const char *button1_txt, const char *button2_txt, const char *button3_txt, const char *button4_txt, const char *button5_txt, const char *button6_txt, const bool centering) { return dialog(title,msg,button1_txt,button2_txt,button3_txt,button4_txt,button5_txt,button6_txt, CImg::logo40x38(),centering); } // End of cimg:: namespace } // End of cimg_library:: namespace } #ifdef _cimg_redefine_min #define min(a,b) (((a)<(b))?(a):(b)) #endif #ifdef _cimg_redefine_max #define max(a,b) (((a)>(b))?(a):(b)) #endif #endif