summaryrefslogtreecommitdiffstats
path: root/src/libs/greycstoration
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/greycstoration')
-rw-r--r--src/libs/greycstoration/CImg.h36837
-rw-r--r--src/libs/greycstoration/LICENSE.txt505
-rw-r--r--src/libs/greycstoration/Makefile.am20
-rw-r--r--src/libs/greycstoration/greycstoration.h481
-rw-r--r--src/libs/greycstoration/greycstorationiface.cpp473
-rw-r--r--src/libs/greycstoration/greycstorationiface.h89
-rw-r--r--src/libs/greycstoration/greycstorationsettings.h144
-rw-r--r--src/libs/greycstoration/greycstorationwidget.cpp377
-rw-r--r--src/libs/greycstoration/greycstorationwidget.h70
9 files changed, 38996 insertions, 0 deletions
diff --git a/src/libs/greycstoration/CImg.h b/src/libs/greycstoration/CImg.h
new file mode 100644
index 00000000..9f6662e1
--- /dev/null
+++ b/src/libs/greycstoration/CImg.h
@@ -0,0 +1,36837 @@
+/*
+ #
+ # 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 <cstdio>
+#include <cstdlib>
+#include <cstdarg>
+#include <cstring>
+#include <cmath>
+#include <ctime>
+
+// 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 <sys/time.h>
+#include <unistd.h>
+#elif cimg_OS==2
+#include <windows.h>
+#ifndef _WIN32_IE
+#define _WIN32_IE 0x0400
+#endif
+#include <shlobj.h>
+#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 <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+#include <pthread.h>
+#ifdef cimg_use_xshm
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <X11/extensions/XShm.h>
+#endif
+#ifdef cimg_use_xrandr
+#include <X11/extensions/Xrandr.h>
+#endif
+#elif cimg_display==3
+#include <Carbon/Carbon.h>
+#include <pthread.h>
+#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<T>::{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<T>::{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]<T>::{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]<T>::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]<T>::{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<T>::{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]<T>::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<T>::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<typename T=float> struct CImg;
+ template<typename T=float> 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<float> 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<float> 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<float> 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 <tt>try { ... } catch() { ... }</tt>.
+ In this case, you can avoid the apparition of the modal window, by
+ defining the environment variable <tt>cimg_debug</tt> 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 <tt>using namespace cimg_library::cimg;</tt> in your source code, since a lot of functions of the
+ <tt>cimg::</tt> namespace have prototypes similar to standard C functions that could defined in the global namespace <tt>::</tt>.
+ **/
+ namespace cimg {
+
+ // Define the traits that will be used to determine the best data type to work with.
+ //
+ template<typename T> 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<bool> {
+ 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<unsigned char> {
+ 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<char> {
+ 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<signed char> {
+ 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<unsigned short> {
+ 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<short> {
+ 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<unsigned int> {
+ 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<int> {
+ 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<unsigned long> {
+ 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<long> {
+ 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<float> {
+ 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<double> {
+ 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<typename T, typename t> struct superset { typedef T type; };
+ template<> struct superset<bool,unsigned char> { typedef unsigned char type; };
+ template<> struct superset<bool,char> { typedef char type; };
+ template<> struct superset<bool,signed char> { typedef signed char type; };
+ template<> struct superset<bool,unsigned short> { typedef unsigned short type; };
+ template<> struct superset<bool,short> { typedef short type; };
+ template<> struct superset<bool,unsigned int> { typedef unsigned int type; };
+ template<> struct superset<bool,int> { typedef int type; };
+ template<> struct superset<bool,unsigned long> { typedef unsigned long type; };
+ template<> struct superset<bool,long> { typedef long type; };
+ template<> struct superset<bool,float> { typedef float type; };
+ template<> struct superset<bool,double> { typedef double type; };
+ template<> struct superset<unsigned char,char> { typedef short type; };
+ template<> struct superset<unsigned char,signed char> { typedef short type; };
+ template<> struct superset<unsigned char,unsigned short> { typedef unsigned short type; };
+ template<> struct superset<unsigned char,short> { typedef short type; };
+ template<> struct superset<unsigned char,unsigned int> { typedef unsigned int type; };
+ template<> struct superset<unsigned char,int> { typedef int type; };
+ template<> struct superset<unsigned char,unsigned long> { typedef unsigned long type; };
+ template<> struct superset<unsigned char,long> { typedef long type; };
+ template<> struct superset<unsigned char,float> { typedef float type; };
+ template<> struct superset<unsigned char,double> { typedef double type; };
+ template<> struct superset<signed char,unsigned char> { typedef short type; };
+ template<> struct superset<signed char,char> { typedef short type; };
+ template<> struct superset<signed char,unsigned short> { typedef int type; };
+ template<> struct superset<signed char,short> { typedef short type; };
+ template<> struct superset<signed char,unsigned int> { typedef long type; };
+ template<> struct superset<signed char,int> { typedef int type; };
+ template<> struct superset<signed char,unsigned long> { typedef long type; };
+ template<> struct superset<signed char,long> { typedef long type; };
+ template<> struct superset<signed char,float> { typedef float type; };
+ template<> struct superset<signed char,double> { typedef double type; };
+ template<> struct superset<char,unsigned char> { typedef short type; };
+ template<> struct superset<char,signed char> { typedef short type; };
+ template<> struct superset<char,unsigned short> { typedef int type; };
+ template<> struct superset<char,short> { typedef short type; };
+ template<> struct superset<char,unsigned int> { typedef long type; };
+ template<> struct superset<char,int> { typedef int type; };
+ template<> struct superset<char,unsigned long> { typedef long type; };
+ template<> struct superset<char,long> { typedef long type; };
+ template<> struct superset<char,float> { typedef float type; };
+ template<> struct superset<char,double> { typedef double type; };
+ template<> struct superset<unsigned short,char> { typedef int type; };
+ template<> struct superset<unsigned short,signed char> { typedef int type; };
+ template<> struct superset<unsigned short,short> { typedef int type; };
+ template<> struct superset<unsigned short,unsigned int> { typedef unsigned int type; };
+ template<> struct superset<unsigned short,int> { typedef int type; };
+ template<> struct superset<unsigned short,unsigned long> { typedef unsigned long type; };
+ template<> struct superset<unsigned short,long> { typedef long type; };
+ template<> struct superset<unsigned short,float> { typedef float type; };
+ template<> struct superset<unsigned short,double> { typedef double type; };
+ template<> struct superset<short,unsigned short> { typedef int type; };
+ template<> struct superset<short,unsigned int> { typedef long type; };
+ template<> struct superset<short,int> { typedef int type; };
+ template<> struct superset<short,unsigned long> { typedef long type; };
+ template<> struct superset<short,long> { typedef long type; };
+ template<> struct superset<short,float> { typedef float type; };
+ template<> struct superset<short,double> { typedef double type; };
+ template<> struct superset<unsigned int,char> { typedef long type; };
+ template<> struct superset<unsigned int,signed char> { typedef long type; };
+ template<> struct superset<unsigned int,short> { typedef long type; };
+ template<> struct superset<unsigned int,int> { typedef long type; };
+ template<> struct superset<unsigned int,unsigned long> { typedef unsigned long type; };
+ template<> struct superset<unsigned int,long> { typedef long type; };
+ template<> struct superset<unsigned int,float> { typedef float type; };
+ template<> struct superset<unsigned int,double> { typedef double type; };
+ template<> struct superset<int,unsigned int> { typedef long type; };
+ template<> struct superset<int,unsigned long> { typedef long type; };
+ template<> struct superset<int,long> { typedef long type; };
+ template<> struct superset<int,float> { typedef float type; };
+ template<> struct superset<int,double> { typedef double type; };
+ template<> struct superset<unsigned long,char> { typedef long type; };
+ template<> struct superset<unsigned long,signed char> { typedef long type; };
+ template<> struct superset<unsigned long,short> { typedef long type; };
+ template<> struct superset<unsigned long,int> { typedef long type; };
+ template<> struct superset<unsigned long,long> { typedef long type; };
+ template<> struct superset<unsigned long,float> { typedef float type; };
+ template<> struct superset<unsigned long,double> { typedef double type; };
+ template<> struct superset<long,float> { typedef float type; };
+ template<> struct superset<long,double> { typedef double type; };
+ template<> struct superset<float,double> { typedef double type; };
+
+ template<typename t1, typename t2, typename t3> struct superset2 {
+ typedef typename superset<t1, typename superset<t2,t3>::type>::type type;
+ };
+
+ template<typename t1, typename t2, typename t3, typename t4> struct superset3 {
+ typedef typename superset<t1, typename superset2<t2,t3,t4>::type>::type type;
+ };
+
+ template<typename t1, typename t2> struct last { typedef t2 type; };
+
+#define _cimg_Tuchar typename cimg::superset<T,unsigned char>::type
+#define _cimg_Tint typename cimg::superset<T,int>::type
+#define _cimg_Tfloat typename cimg::superset<T,float>::type
+#define _cimg_Tdouble typename cimg::superset<T,double>::type
+#define _cimg_Tt typename cimg::superset<T,t>::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 <tt>std::printf()</tt>.
+ **/
+ 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 <tt>std::system()</tt>
+ and is here because using the <tt>std::</tt> 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<typename T>
+ inline T& temporary(const T&) {
+ static T temp;
+ return temp;
+ }
+
+ //! Exchange values of variables \p a and \p b.
+ template<typename T>
+ 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<typename T1, typename T2>
+ 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<typename T1, typename T2, typename T3>
+ 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<typename T1, typename T2, typename T3, typename T4>
+ 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<typename T1, typename T2, typename T3, typename T4, typename T5>
+ 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<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
+ 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<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
+ 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<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
+ 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<typename T>
+ 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<typename T>
+ 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<typename T>
+ inline const T rol(const T a, const unsigned int n=1) {
+ return n?(T)((a<<n)|(a>>((sizeof(T)<<3)-n))):a;
+ }
+
+ //! Return a right bitwise-rotated number.
+ template<typename T>
+ 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 <tt>std::abs()</tt> or <tt>std::fabs()</tt>
+ because it is able to consider a variable of any type, without cast needed.
+ **/
+ template<typename T>
+ 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<typename T>
+ 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<typename t1, typename t2>
+ inline typename cimg::superset<t1,t2>::type min(const t1& a, const t2& b) {
+ typedef typename cimg::superset<t1,t2>::type t1t2;
+ return (t1t2)(a<=b?a:b);
+ }
+
+ //! Return the minimum value between three numbers.
+ template<typename t1, typename t2, typename t3>
+ inline typename cimg::superset2<t1,t2,t3>::type min(const t1& a, const t2& b, const t3& c) {
+ typedef typename cimg::superset2<t1,t2,t3>::type t1t2t3;
+ return (t1t2t3)cimg::min(cimg::min(a,b),c);
+ }
+
+ //! Return the minimum value between four numbers.
+ template<typename t1, typename t2, typename t3, typename t4>
+ inline typename cimg::superset3<t1,t2,t3,t4>::type min(const t1& a, const t2& b, const t3& c, const t4& d) {
+ typedef typename cimg::superset3<t1,t2,t3,t4>::type t1t2t3t4;
+ return (t1t2t3t4)cimg::min(cimg::min(a,b,c),d);
+ }
+
+ //! Return the maximum value between two numbers.
+ template<typename t1, typename t2>
+ inline typename cimg::superset<t1,t2>::type max(const t1& a, const t2& b) {
+ typedef typename cimg::superset<t1,t2>::type t1t2;
+ return (t1t2)(a>=b?a:b);
+ }
+
+ //! Return the maximum value between three numbers.
+ template<typename t1, typename t2, typename t3>
+ inline typename cimg::superset2<t1,t2,t3>::type max(const t1& a, const t2& b, const t3& c) {
+ typedef typename cimg::superset2<t1,t2,t3>::type t1t2t3;
+ return (t1t2t3)cimg::max(cimg::max(a,b),c);
+ }
+
+ //! Return the maximum value between four numbers.
+ template<typename t1, typename t2, typename t3, typename t4>
+ inline typename cimg::superset3<t1,t2,t3,t4>::type max(const t1& a, const t2& b, const t3& c, const t4& d) {
+ typedef typename cimg::superset3<t1,t2,t3,t4>::type t1t2t3t4;
+ return (t1t2t3t4)cimg::max(cimg::max(a,b,c),d);
+ }
+
+ //! Return the sign of a number.
+ template<typename T>
+ 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<typename T>
+ 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<typename T>
+ 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.
+ /**
+ <i>minmod(\p a,\p b)</i> is defined to be :
+ - <i>minmod(\p a,\p b) = min(\p a,\p b)</i>, if \p a and \p b have the same sign.
+ - <i>minmod(\p a,\p b) = 0</i>, if \p a and \p b have different signs.
+ **/
+ template<typename T>
+ inline T minmod(const T a, const T b) {
+ return a*b<=0?0:(a>0?(a<b?a:b):(a<b?b:a));
+ }
+
+ //! Return a random variable between [0,1] with respect to an uniform distribution.
+ inline double rand() {
+ static bool first_time = true;
+ if (first_time) { cimg::srand(); first_time = false; }
+ return (double)cimg_std::rand()/RAND_MAX;
+ }
+
+ //! Return a random variable between [-1,1] with respect to an uniform distribution.
+ inline double crand() {
+ return 1-2*cimg::rand();
+ }
+
+ //! Return a random variable following a gaussian distribution and a standard deviation of 1.
+ inline double grand() {
+ double x1, w;
+ do {
+ const double x2 = 2*cimg::rand() - 1.0;
+ x1 = 2*cimg::rand()-1.0;
+ w = x1*x1 + x2*x2;
+ } while (w<=0 || w>=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*delta<y?backward:forward));
+ }
+
+ inline double _pythagore(double a, double b) {
+ const double absa = cimg::abs(a), absb = cimg::abs(b);
+ if (absa>absb) { 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 <tt>std::atof()</tt>,
+ 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 <tt>std::strlen()</tt>
+ and is here because some old compilers do not
+ define the <tt>std::</tt> 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 <tt>std::strncmp()</tt>
+ and is here because some old compilers do not
+ define the <tt>std::</tt> 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; k<l && !(diff = *ns1-*ns2); ++k) { ++ns1; ++ns2; }
+ return k!=l?diff:0;
+ }
+
+ //! Compare the first \p n characters of two C-strings, ignoring the case.
+ /**
+ \note This function is similar to <tt>std::strncasecmp()</tt>
+ and is here because some old compilers do not
+ define the <tt>std::</tt> 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; k<l && !(diff = uncase(*ns1)-uncase(*ns2)); ++k) { ++ns1; ++ns2; }
+ return k!=l?diff:0;
+ }
+
+ //! Compare two C-strings.
+ /**
+ \note This function is similar to <tt>std::strcmp()</tt>
+ and is here because some old compilers do not
+ define the <tt>std::</tt> 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+(l1<l2?l1:l2));
+ }
+
+ //! Compare two C-strings, ignoring the case.
+ /**
+ \note This function is similar to <tt>std::strcasecmp()</tt>
+ and is here because some old compilers do not
+ define the <tt>std::</tt> 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<l2?l1:l2));
+ }
+
+ //! Find a character in a C-string.
+ inline int strfind(const char *const s, const char c) {
+ if (!s) return -1;
+ int l; for (l = cimg::strlen(s); l>=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; p<q && s[p]==delimiter && s[q]==delimiter; ++p) --q;
+ else {
+ for (p = 0; p<l && s[p]==delimiter; ) ++p;
+ for (q = l-1; q>p && 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<header+siz && (err=cimg_std::sscanf(head,"%1023[^\n]",item))!=EOF && (item[0]=='#' || !err))
+ head+=1+(err?cimg::strlen(item):0);
+ if (cimg_std::sscanf(item," P%d",&err)==1) ftype = _pnm;
+ }
+ if (!ftype) { // Check for TIFF format.
+ if ((uheader[0]==0x49 && uheader[1]==0x49) || (uheader[0]==0x4D && uheader[1]==0x4D)) ftype = _tiff;
+ }
+ return ftype;
+ }
+
+ //! Read file data, and check for possible errors.
+ template<typename T>
+ 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))<wlimitT?toread:wlimit;
+ lalread = (unsigned int)cimg_std::fread((void*)(ptr+alread),sizeof(T),ltoread,stream);
+ alread+=lalread;
+ toread-=lalread;
+ } while (ltoread==lalread && toread>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<typename T>
+ 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))<wlimitT?towrite:wlimit;
+ lalwrite = (unsigned int)cimg_std::fwrite((void*)(ptr+alwrite),sizeof(T),ltowrite,stream);
+ alwrite+=lalwrite;
+ towrite-=lalwrite;
+ } while (ltowrite==lalwrite && towrite>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<argc && cimg::strcmp(argv[k],name)) ++k;
+ res = (k++==argc?defaut:(k==argc?argv[--k]:argv[k]));
+ } else res = defaut;
+ if (visu && usage) cimg_std::fprintf(cimg_stdout," %s%-16s%s %-24s %s%s%s\n",
+ cimg::t_bold,name,cimg::t_normal,res?res:"0",cimg::t_green,usage,cimg::t_normal);
+ }
+ return res;
+ }
+
+ inline bool option(const char *const name, const int argc, const char *const *const argv,
+ const bool defaut, const char *const usage=0) {
+ const char *s = cimg::option(name,argc,argv,(char*)0);
+ const bool res = s?(cimg::strcasecmp(s,"false") && cimg::strcasecmp(s,"off") && cimg::strcasecmp(s,"0")):defaut;
+ cimg::option(name,0,0,res?"true":"false",usage);
+ return res;
+ }
+
+ inline int option(const char *const name, const int argc, const char *const *const argv,
+ const int defaut, const char *const usage=0) {
+ const char *s = cimg::option(name,argc,argv,(char*)0);
+ const int res = s?cimg_std::atoi(s):defaut;
+ char tmp[256];
+ cimg_std::sprintf(tmp,"%d",res);
+ cimg::option(name,0,0,tmp,usage);
+ return res;
+ }
+
+ inline char option(const char *const name, const int argc, const char *const *const argv,
+ const char defaut, const char *const usage=0) {
+ const char *s = cimg::option(name,argc,argv,(char*)0);
+ const char res = s?s[0]:defaut;
+ char tmp[8];
+ tmp[0] = res; tmp[1] ='\0';
+ cimg::option(name,0,0,tmp,usage);
+ return res;
+ }
+
+ inline float option(const char *const name, const int argc, const char *const *const argv,
+ const float defaut, const char *const usage=0) {
+ const char *s = cimg::option(name,argc,argv,(char*)0);
+ const float res = s?cimg::atof(s):defaut;
+ char tmp[256];
+ cimg_std::sprintf(tmp,"%g",res);
+ cimg::option(name,0,0,tmp,usage);
+ return res;
+ }
+
+ inline double option(const char *const name, const int argc, const char *const *const argv,
+ const double defaut, const char *const usage=0) {
+ const char *s = cimg::option(name,argc,argv,(char*)0);
+ const double res = s?cimg::atof(s):defaut;
+ char tmp[256];
+ cimg_std::sprintf(tmp,"%g",res);
+ cimg::option(name,0,0,tmp,usage);
+ return res;
+ }
+
+ inline const char* argument(const unsigned int nb, const int argc, const char *const *const argv, const unsigned int nb_singles=0, ...) {
+ for (int k = 1, pos = 0; k<argc;) {
+ const char *const item = argv[k];
+ bool option = (*item=='-'), single_option = false;
+ if (option) {
+ va_list ap;
+ va_start(ap,nb_singles);
+ for (unsigned int i=0; i<nb_singles; ++i) if (!cimg::strcasecmp(item,va_arg(ap,char*))) { single_option = true; break; }
+ va_end(ap);
+ }
+ if (option) { ++k; if (!single_option) ++k; }
+ else { if (pos++==(int)nb) return item; else ++k; }
+ }
+ return 0;
+ }
+
+ //! Print informations about %CImg environement variables.
+ /**
+ Printing is done on the standard error output.
+ **/
+ inline void info() {
+ char tmp[1024] = { 0 };
+ cimg_std::fprintf(cimg_stdout,"\n %sCImg Library %u.%u.%u%s, compiled %s ( %s ) with the following flags :\n\n",
+ cimg::t_red,cimg_version/100,(cimg_version/10)%10,cimg_version%10,
+ cimg::t_normal,__DATE__,__TIME__);
+
+ cimg_std::fprintf(cimg_stdout," > 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<typename T>
+ 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<typename T>
+ 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<typename T>
+ 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<typename T>
+ 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<typename T>
+ 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<typename t>
+ inline CImg<t> operator+(const CImg<t>& img, const t val) {
+ return CImg<t>(img,false)+=val;
+ }
+#else
+ template<typename t1, typename t2>
+ inline CImg<typename cimg::superset<t1,t2>::type> operator+(const CImg<t1>& img, const t2 val) {
+ typedef typename cimg::superset<t1,t2>::type t1t2;
+ return CImg<t1t2>(img,false)+=val;
+ }
+#endif
+
+#ifdef cimg_use_visualcpp6
+ template<typename t>
+ inline CImg<t> operator+(const t val, const CImg<t>& img) {
+ return img + val;
+ }
+#else
+ template<typename t1, typename t2>
+ inline CImg<typename cimg::superset<t1,t2>::type> operator+(const t1 val, const CImg<t2>& img) {
+ return img + val;
+ }
+#endif
+
+#ifdef cimg_use_visualcpp6
+ template<typename t>
+ inline CImgList<t> operator+(const CImgList<t>& list, const t val) {
+ return CImgList<t>(list)+=val;
+ }
+#else
+ template<typename t1, typename t2>
+ inline CImgList<typename cimg::superset<t1,t2>::type> operator+(const CImgList<t1>& list, const t2 val) {
+ typedef typename cimg::superset<t1,t2>::type t1t2;
+ return CImgList<t1t2>(list)+=val;
+ }
+#endif
+
+#ifdef cimg_use_visualcpp6
+ template<typename t>
+ inline CImgList<t> operator+(const t val, const CImgList<t>& list) {
+ return list + val;
+ }
+#else
+ template<typename t1, typename t2>
+ inline CImgList<typename cimg::superset<t1,t2>::type> operator+(const t1 val, const CImgList<t2>& list) {
+ return list + val;
+ }
+#endif
+
+ template<typename t1, typename t2>
+ inline CImg<typename cimg::superset<t1,t2>::type> operator+(const CImg<t1>& img1, const CImg<t2>& img2) {
+ typedef typename cimg::superset<t1,t2>::type t1t2;
+ return CImg<t1t2>(img1,false)+=img2;
+ }
+
+ template<typename t1, typename t2>
+ inline CImgList<typename cimg::superset<t1,t2>::type> operator+(const CImg<t1>& img, const CImgList<t2>& list) {
+ typedef typename cimg::superset<t1,t2>::type t1t2;
+ return CImgList<t1t2>(list)+=img;
+ }
+
+ template<typename t1, typename t2>
+ inline CImgList<typename cimg::superset<t1,t2>::type> operator+(const CImgList<t1>& list, const CImg<t2>& img) {
+ return img + list;
+ }
+
+ template<typename t1, typename t2>
+ inline CImgList<typename cimg::superset<t1,t2>::type> operator+(const CImgList<t1>& list1, const CImgList<t2>& list2) {
+ typedef typename cimg::superset<t1,t2>::type t1t2;
+ return CImgList<t1t2>(list1)+=list2;
+ }
+
+#ifdef cimg_use_visualcpp6
+ template<typename t>
+ inline CImg<t> operator-(const CImg<t>& img, const t val) {
+ return CImg<t>(img,false)-=val;
+ }
+#else
+ template<typename t1, typename t2>
+ inline CImg<typename cimg::superset<t1,t2>::type> operator-(const CImg<t1>& img, const t2 val) {
+ typedef typename cimg::superset<t1,t2>::type t1t2;
+ return CImg<t1t2>(img,false)-=val;
+ }
+#endif
+
+#ifdef cimg_use_visualcpp6
+ template<typename t>
+ inline CImg<t> operator-(const t val, const CImg<t>& img) {
+ return CImg<t>(img.width,img.height,img.depth,img.dim,val)-=img;
+ }
+#else
+ template<typename t1, typename t2>
+ inline CImg<typename cimg::superset<t1,t2>::type> operator-(const t1 val, const CImg<t2>& img) {
+ typedef typename cimg::superset<t1,t2>::type t1t2;
+ return CImg<t1t2>(img.width,img.height,img.depth,img.dim,(t1t2)val)-=img;
+ }
+#endif
+
+#ifdef cimg_use_visualcpp6
+ template<typename t>
+ inline CImgList<t> operator-(const CImgList<t>& list, const t val) {
+ return CImgList<t>(list)-=val;
+ }
+#else
+ template<typename t1, typename t2>
+ inline CImgList<typename cimg::superset<t1,t2>::type> operator-(const CImgList<t1>& list, const t2 val) {
+ typedef typename cimg::superset<t1,t2>::type t1t2;
+ return CImgList<t1t2>(list)-=val;
+ }
+#endif
+
+#ifdef cimg_use_visualcpp6
+ template<typename t>
+ inline CImgList<double> operator-(const t val, const CImgList<t>& list) {
+ CImgList<t> res(list.size);
+ cimglist_for(res,l) res[l] = val - list[l];
+ return res;
+ }
+#else
+ template<typename t1, typename t2>
+ inline CImgList<typename cimg::superset<t1,t2>::type> operator-(const t1 val, const CImgList<t2>& list) {
+ typedef typename cimg::superset<t1,t2>::type t1t2;
+ CImgList<t1t2> res(list.size);
+ cimglist_for(res,l) res[l] = val - list[l];
+ return res;
+ }
+#endif
+
+ template<typename t1, typename t2>
+ inline CImg<typename cimg::superset<t1,t2>::type> operator-(const CImg<t1>& img1, const CImg<t2>& img2) {
+ typedef typename cimg::superset<t1,t2>::type t1t2;
+ return CImg<t1t2>(img1,false)-=img2;
+ }
+
+ template<typename t1, typename t2>
+ inline CImgList<typename cimg::superset<t1,t2>::type> operator-(const CImg<t1>& img, const CImgList<t2>& list) {
+ typedef typename cimg::superset<t1,t2>::type t1t2;
+ CImgList<t1t2> res(list.size);
+ cimglist_for(res,l) res[l] = img - list[l];
+ return res;
+ }
+
+ template<typename t1, typename t2>
+ inline CImgList<typename cimg::superset<t1,t2>::type> operator-(const CImgList<t1>& list, const CImg<t2>& img) {
+ typedef typename cimg::superset<t1,t2>::type t1t2;
+ return CImgList<t1t2>(list)-=img;
+ }
+
+ template<typename t1, typename t2>
+ inline CImgList<typename cimg::superset<t1,t2>::type> operator-(const CImgList<t1>& list1, const CImgList<t2>& list2) {
+ typedef typename cimg::superset<t1,t2>::type t1t2;
+ return CImgList<t1t2>(list1)-=list2;
+ }
+
+#ifdef cimg_use_visualcpp6
+ template<typename t>
+ inline CImg<t> operator*(const CImg<t>& img, const double val) {
+ return CImg<t>(img,false)*=val;
+ }
+#else
+ template<typename t1, typename t2>
+ inline CImg<typename cimg::superset<t1,t2>::type> operator*(const CImg<t1>& img, const t2 val) {
+ typedef typename cimg::superset<t1,t2>::type t1t2;
+ return CImg<t1t2>(img,false)*=val;
+ }
+#endif
+
+#ifdef cimg_use_visualcpp6
+ template<typename t>
+ inline CImg<t> operator*(const double val, const CImg<t>& img) {
+ return img*val;
+ }
+#else
+ template<typename t1, typename t2>
+ inline CImg<typename cimg::superset<t1,t2>::type> operator*(const t1 val, const CImg<t2>& img) {
+ return img*val;
+ }
+#endif
+
+#ifdef cimg_use_visualcpp6
+ template<typename t>
+ inline CImgList<t> operator*(const CImgList<t>& list, const double val) {
+ return CImgList<t>(list)*=val;
+ }
+#else
+ template<typename t1, typename t2>
+ inline CImgList<typename cimg::superset<t1,t2>::type> operator*(const CImgList<t1>& list, const t2 val) {
+ typedef typename cimg::superset<t1,t2>::type t1t2;
+ return CImgList<t1t2>(list)*=val;
+ }
+#endif
+
+#ifdef cimg_use_visualcpp6
+ template<typename t>
+ inline CImgList<t> operator*(const double val, const CImgList<t>& list) {
+ return list*val;
+ }
+#else
+ template<typename t1, typename t2>
+ inline CImgList<typename cimg::superset<t1,t2>::type> operator*(const t1 val, const CImgList<t2>& list) {
+ return list*val;
+ }
+#endif
+
+ template<typename t1, typename t2>
+ inline CImg<typename cimg::superset<t1,t2>::type> operator*(const CImg<t1>& img1, const CImg<t2>& img2) {
+ typedef typename cimg::superset<t1,t2>::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<t1t2> 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<typename t1, typename t2>
+ inline CImgList<typename cimg::superset<t1,t2>::type> operator*(const CImg<t1>& img, const CImgList<t2>& list) {
+ typedef typename cimg::superset<t1,t2>::type t1t2;
+ CImgList<t1t2> res(list.size);
+ cimglist_for(res,l) res[l] = img*list[l];
+ return res;
+ }
+
+ template<typename t1, typename t2>
+ inline CImgList<typename cimg::superset<t1,t2>::type> operator*(const CImgList<t1>& list, const CImg<t2>& img) {
+ typedef typename cimg::superset<t1,t2>::type t1t2;
+ CImgList<t1t2> res(list.size);
+ cimglist_for(res,l) res[l] = list[l]*img;
+ return res;
+ }
+
+ template<typename t1, typename t2>
+ inline CImgList<typename cimg::superset<t1,t2>::type> operator*(const CImgList<t1>& list1, const CImgList<t2>& list2) {
+ typedef typename cimg::superset<t1,t2>::type t1t2;
+ CImgList<t1t2> res(cimg::min(list1.size,list2.size));
+ cimglist_for(res,l) res[l] = list1[l]*list2[l];
+ return res;
+ }
+
+#ifdef cimg_use_visualcpp6
+ template<typename t>
+ inline CImg<t> operator/(const CImg<t>& img, const double val) {
+ return CImg<t>(img,false)/=val;
+ }
+#else
+ template<typename t1, typename t2>
+ inline CImg<typename cimg::superset<t1,t2>::type> operator/(const CImg<t1>& img, const t2 val) {
+ typedef typename cimg::superset<t1,t2>::type t1t2;
+ return CImg<t1t2>(img,false)/=val;
+ }
+#endif
+
+#ifdef cimg_use_visualcpp6
+ template<typename t>
+ inline CImg<t> operator/(const double val, CImg<t>& img) {
+ return val*img.get_invert();
+ }
+#else
+ template<typename t1, typename t2>
+ inline CImg<typename cimg::superset<t1,t2>::type> operator/(const t1 val, CImg<t2>& img) {
+ return val*img.get_invert();
+ }
+#endif
+
+#ifdef cimg_use_visualcpp6
+ template<typename t>
+ inline CImgList<t> operator/(const CImgList<t>& list, const double val) {
+ return CImgList<t>(list)/=val;
+ }
+#else
+ template<typename t1, typename t2>
+ inline CImgList<typename cimg::superset<t1,t2>::type> operator/(const CImgList<t1>& list, const t2 val) {
+ typedef typename cimg::superset<t1,t2>::type t1t2;
+ return CImgList<t1t2>(list)/=val;
+ }
+#endif
+
+#ifdef cimg_use_visualcpp6
+ template<typename t>
+ inline CImgList<t> operator/(const double val, const CImgList<t>& list) {
+ CImgList<t> res(list.size);
+ cimglist_for(res,l) res[l] = val/list[l];
+ return res;
+ }
+#else
+ template<typename t1, typename t2>
+ inline CImgList<typename cimg::superset<t1,t2>::type> operator/(const t1 val, const CImgList<t2>& list) {
+ typedef typename cimg::superset<t1,t2>::type t1t2;
+ CImgList<t1t2> res(list.size);
+ cimglist_for(res,l) res[l] = val/list[l];
+ return res;
+ }
+#endif
+
+ template<typename t1, typename t2>
+ inline CImg<typename cimg::superset<t1,t2>::type> operator/(const CImg<t1>& img1, const CImg<t2>& img2) {
+ typedef typename cimg::superset<t1,t2>::type t1t2;
+ return CImg<t1t2>(img1,false)*=img2.get_invert();
+ }
+
+ template<typename t1, typename t2>
+ inline CImg<typename cimg::superset<t1,t2>::type> operator/(const CImg<t1>& img, const CImgList<t2>& list) {
+ typedef typename cimg::superset<t1,t2>::type t1t2;
+ CImgList<t1t2> res(list.size);
+ cimglist_for(res,l) res[l] = img/list[l];
+ return res;
+ }
+
+ template<typename t1, typename t2>
+ inline CImgList<typename cimg::superset<t1,t2>::type> operator/(const CImgList<t1>& list, const CImg<t2>& img) {
+ typedef typename cimg::superset<t1,t2>::type t1t2;
+ return CImgList<t1t2>(list)/=img;
+ }
+
+ template<typename t1, typename t2>
+ inline CImgList<typename cimg::superset<t1,t2>::type> operator/(const CImgList<t1>& list1, const CImgList<t2>& list2) {
+ typedef typename cimg::superset<t1,t2>::type t1t2;
+ return CImgList<t1t2>(list1)/=list2;
+ }
+
+ template<typename T>
+ inline CImg<_cimg_Tfloat> sqr(const CImg<T>& instance) {
+ return instance.get_sqr();
+ }
+
+ template<typename T>
+ inline CImg<_cimg_Tfloat> sqrt(const CImg<T>& instance) {
+ return instance.get_sqrt();
+ }
+
+ template<typename T>
+ inline CImg<_cimg_Tfloat> exp(const CImg<T>& instance) {
+ return instance.get_exp();
+ }
+
+ template<typename T>
+ inline CImg<_cimg_Tfloat> log(const CImg<T>& instance) {
+ return instance.get_log();
+ }
+
+ template<typename T>
+ inline CImg<_cimg_Tfloat> log10(const CImg<T>& instance) {
+ return instance.get_log10();
+ }
+
+ template<typename T>
+ inline CImg<_cimg_Tfloat> abs(const CImg<T>& instance) {
+ return instance.get_abs();
+ }
+
+ template<typename T>
+ inline CImg<_cimg_Tfloat> cos(const CImg<T>& instance) {
+ return instance.get_cos();
+ }
+
+ template<typename T>
+ inline CImg<_cimg_Tfloat> sin(const CImg<T>& instance) {
+ return instance.get_sin();
+ }
+
+ template<typename T>
+ inline CImg<_cimg_Tfloat> tan(const CImg<T>& instance) {
+ return instance.get_tan();
+ }
+
+ template<typename T>
+ inline CImg<_cimg_Tfloat> acos(const CImg<T>& instance) {
+ return instance.get_acos();
+ }
+
+ template<typename T>
+ inline CImg<_cimg_Tfloat> asin(const CImg<T>& instance) {
+ return instance.get_asin();
+ }
+
+ template<typename T>
+ inline CImg<_cimg_Tfloat> atan(const CImg<T>& instance) {
+ return instance.get_atan();
+ }
+
+ template<typename T>
+ inline CImg<T> transpose(const CImg<T>& instance) {
+ return instance.get_transpose();
+ }
+
+ template<typename T>
+ inline CImg<_cimg_Tfloat> invert(const CImg<T>& instance) {
+ return instance.get_invert();
+ }
+
+ template<typename T>
+ inline CImg<_cimg_Tfloat> pseudoinvert(const CImg<T>& 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<T> image
+ of a \c CImgList<T> 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<typename T>
+ CImgDisplay(const CImg<T>& 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<typename T>
+ CImgDisplay(const CImgList<T>& 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<T> 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<typename T>
+ CImgDisplay& display(const CImgList<T>& list, const char axis='x', const char align='p') {
+ return display(list.get_append(axis,align));
+ }
+
+ //! Display an image CImg<T> into a display window.
+ template<typename T>
+ CImgDisplay& operator<<(const CImg<T>& img) {
+ return display(img);
+ }
+
+ //! Display an image CImg<T> into a display window.
+ template<typename T>
+ CImgDisplay& operator<<(const CImgList<T>& 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<typename T>
+ CImgDisplay& resize(const CImg<T>& 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<typename t, typename T>
+ 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<wd; ++x) { old=curr; curr+=s; *(poffx++) = (unsigned int)curr-(unsigned int)old; }
+ s = (float)hs/hd;
+ poffy = offy; curr = 0; for (unsigned int y=0; y<hd; ++y) { old=curr; curr+=s; *(poffy++) = ws*((unsigned int)curr-(unsigned int)old); }
+ *poffy = 0;
+ poffy = offy;
+ {for (unsigned int y=0; y<hd; ) {
+ const T *ptr = ptrs;
+ poffx = offx;
+ for (unsigned int x=0; x<wd; ++x) { *(ptrd++) = *ptr; ptr+=*(poffx++); }
+ ++y;
+ unsigned int dy=*(poffy++);
+ for (;!dy && y<hd; cimg_std::memcpy(ptrd, ptrd-wd, sizeof(t)*wd), ++y, ptrd+=wd, dy=*(poffy++)) {}
+ ptrs+=dy;
+ }}
+ delete[] offx; delete[] offy;
+ }
+
+ //! Clear all events of the current display.
+ CImgDisplay& flush() {
+ cimg_std::memset((void*)buttons,0,512*sizeof(unsigned int));
+ cimg_std::memset((void*)keys,0,512*sizeof(unsigned int));
+ cimg_std::memset((void*)released_keys,0,512*sizeof(unsigned int));
+ is_keyESC = is_keyF1 = is_keyF2 = is_keyF3 = is_keyF4 = is_keyF5 = is_keyF6 = is_keyF7 = is_keyF8 = is_keyF9 =
+ is_keyF10 = is_keyF11 = is_keyF12 = is_keyPAUSE = is_key1 = is_key2 = is_key3 = is_key4 = is_key5 = is_key6 =
+ is_key7 = is_key8 = is_key9 = is_key0 = is_keyBACKSPACE = is_keyINSERT = is_keyHOME = is_keyPAGEUP = is_keyTAB =
+ is_keyQ = is_keyW = is_keyE = is_keyR = is_keyT = is_keyY = is_keyU = is_keyI = is_keyO = is_keyP = is_keyDELETE =
+ is_keyEND = is_keyPAGEDOWN = is_keyCAPSLOCK = is_keyA = is_keyS = is_keyD = is_keyF = is_keyG = is_keyH = is_keyJ =
+ is_keyK = is_keyL = is_keyENTER = is_keySHIFTLEFT = is_keyZ = is_keyX = is_keyC = is_keyV = is_keyB = is_keyN =
+ is_keyM = is_keySHIFTRIGHT = is_keyARROWUP = is_keyCTRLLEFT = is_keyAPPLEFT = is_keyALT = is_keySPACE = is_keyALTGR = is_keyAPPRIGHT =
+ is_keyMENU = is_keyCTRLRIGHT = is_keyARROWLEFT = is_keyARROWDOWN = is_keyARROWRIGHT = is_keyPAD0 = is_keyPAD1 = is_keyPAD2 =
+ is_keyPAD3 = is_keyPAD4 = is_keyPAD5 = is_keyPAD6 = is_keyPAD7 = is_keyPAD8 = is_keyPAD9 = is_keyPADADD = is_keyPADSUB =
+ is_keyPADMUL = is_keyPADDIV = false;
+ is_resized = is_moved = is_event = false;
+ fps_timer = fps_frames = timer = wheel = 0;
+ mouse_x = mouse_y = -1;
+ fps_fps = 0;
+ return *this;
+ }
+
+ // Update 'is_key' fields.
+ void update_iskey(const unsigned int key, const bool pressed=true) {
+#define _cimg_iskey_case(k) if (key==cimg::key##k) is_key##k = pressed;
+ _cimg_iskey_case(ESC); _cimg_iskey_case(F1); _cimg_iskey_case(F2); _cimg_iskey_case(F3);
+ _cimg_iskey_case(F4); _cimg_iskey_case(F5); _cimg_iskey_case(F6); _cimg_iskey_case(F7);
+ _cimg_iskey_case(F8); _cimg_iskey_case(F9); _cimg_iskey_case(F10); _cimg_iskey_case(F11);
+ _cimg_iskey_case(F12); _cimg_iskey_case(PAUSE); _cimg_iskey_case(1); _cimg_iskey_case(2);
+ _cimg_iskey_case(3); _cimg_iskey_case(4); _cimg_iskey_case(5); _cimg_iskey_case(6);
+ _cimg_iskey_case(7); _cimg_iskey_case(8); _cimg_iskey_case(9); _cimg_iskey_case(0);
+ _cimg_iskey_case(BACKSPACE); _cimg_iskey_case(INSERT); _cimg_iskey_case(HOME);
+ _cimg_iskey_case(PAGEUP); _cimg_iskey_case(TAB); _cimg_iskey_case(Q); _cimg_iskey_case(W);
+ _cimg_iskey_case(E); _cimg_iskey_case(R); _cimg_iskey_case(T); _cimg_iskey_case(Y);
+ _cimg_iskey_case(U); _cimg_iskey_case(I); _cimg_iskey_case(O); _cimg_iskey_case(P);
+ _cimg_iskey_case(DELETE); _cimg_iskey_case(END); _cimg_iskey_case(PAGEDOWN);
+ _cimg_iskey_case(CAPSLOCK); _cimg_iskey_case(A); _cimg_iskey_case(S); _cimg_iskey_case(D);
+ _cimg_iskey_case(F); _cimg_iskey_case(G); _cimg_iskey_case(H); _cimg_iskey_case(J);
+ _cimg_iskey_case(K); _cimg_iskey_case(L); _cimg_iskey_case(ENTER);
+ _cimg_iskey_case(SHIFTLEFT); _cimg_iskey_case(Z); _cimg_iskey_case(X); _cimg_iskey_case(C);
+ _cimg_iskey_case(V); _cimg_iskey_case(B); _cimg_iskey_case(N); _cimg_iskey_case(M);
+ _cimg_iskey_case(SHIFTRIGHT); _cimg_iskey_case(ARROWUP); _cimg_iskey_case(CTRLLEFT);
+ _cimg_iskey_case(APPLEFT); _cimg_iskey_case(ALT); _cimg_iskey_case(SPACE); _cimg_iskey_case(ALTGR);
+ _cimg_iskey_case(APPRIGHT); _cimg_iskey_case(MENU); _cimg_iskey_case(CTRLRIGHT);
+ _cimg_iskey_case(ARROWLEFT); _cimg_iskey_case(ARROWDOWN); _cimg_iskey_case(ARROWRIGHT);
+ _cimg_iskey_case(PAD0); _cimg_iskey_case(PAD1); _cimg_iskey_case(PAD2);
+ _cimg_iskey_case(PAD3); _cimg_iskey_case(PAD4); _cimg_iskey_case(PAD5);
+ _cimg_iskey_case(PAD6); _cimg_iskey_case(PAD7); _cimg_iskey_case(PAD8);
+ _cimg_iskey_case(PAD9); _cimg_iskey_case(PADADD); _cimg_iskey_case(PADSUB);
+ _cimg_iskey_case(PADMUL); _cimg_iskey_case(PADDIV);
+ }
+
+ //! Test if any key has been pressed.
+ bool is_key(const bool remove=false) {
+ for (unsigned int *ptrs=(unsigned int*)keys+512-1; ptrs>=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; pk<pk_end; ) {
+ if (*(pk++)==k) {
+ bool res = true;
+ const unsigned int *ps = ps_end, *pk2 = pk;
+ for (unsigned int i=1; i<N; ++i) res = (*(--ps)==*(pk2++));
+ if (res) {
+ if (remove) cimg_std::memset((void*)(pk-1),0,sizeof(unsigned int)*N);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ // Find the good width and height of a window to display an image (internal routine).
+#define cimg_fitscreen(dx,dy,dz) CImgDisplay::_fitscreen(dx,dy,dz,128,-85,false),CImgDisplay::_fitscreen(dx,dy,dz,128,-85,true)
+ static unsigned int _fitscreen(const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1,
+ const int dmin=128, const int dmax=-85,const bool return_last=false) {
+ unsigned int nw = dx + (dz>1?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 (nw<mw) { nh = nh*mw/nw; nh+=(nh==0); nw = mw; }
+ if (nh<mh) { nw = nw*mh/nh; nw+=(nw==0); nh = mh; }
+ if (nw>Mw) { nh = nh*Mw/nw; nh+=(nh==0); nw = Mw; }
+ if (nh>Mh) { nw = nw*Mh/nh; nw+=(nw==0); nh = Mh; }
+ if (nw<mw) nw = mw;
+ if (nh<mh) nh = mh;
+ if (return_last) return nh;
+ return nw;
+ }
+
+ // When no display available
+ //---------------------------
+#if cimg_display==0
+
+ //! Return the width of the screen resolution.
+ static int screen_dimx() {
+ return 0;
+ }
+
+ //! Return the height of the screen resolution.
+ static int screen_dimy() {
+ return 0;
+ }
+
+ //! Wait for a window event in any CImg window.
+ static void wait_all() {}
+
+ //! In-place version of the destructor.
+ CImgDisplay& assign() {
+ return *this;
+ }
+
+ //! In-place version of the previous constructor.
+ 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) {
+ throw CImgDisplayException("CImgDisplay() : Display has been required but is not available (cimg_display=0)");
+ const char* avoid_warning = title + dimw + dimh + normalization_type + (int)fullscreen_flag + (int)closed_flag;
+ avoid_warning = 0;
+ return *this;
+ }
+
+ //! In-place version of the previous constructor.
+ template<typename T>
+ CImgDisplay& assign(const CImg<T>& 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<typename T>
+ CImgDisplay& assign(const CImgList<T>& 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<typename T>
+ CImgDisplay& display(const CImg<T>& 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<typename T>
+ CImgDisplay& render(const CImg<T>& img) {
+ unsigned int avoid_warning = img.width;
+ avoid_warning = 0;
+ return *this;
+ }
+
+ //! Take a snapshot of the display in the specified image.
+ template<typename T>
+ const CImgDisplay& snapshot(CImg<T>& 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; i<cimg::X11attr().nb_wins; ++i)
+ if (!cimg::X11attr().wins[i]->is_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; i<cimg::X11attr().nb_wins; ++i)
+ if (!cimg::X11attr().wins[i]->is_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<typename T>
+ 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<cimg::X11attr().nb_resolutions; ++i) {
+ const unsigned int
+ nw = (unsigned int)(cimg::X11attr().resolutions[i].width),
+ nh = (unsigned int)(cimg::X11attr().resolutions[i].height);
+ if (nw>=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_mask<vinfo->blue_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<int>::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; i<cimg::X11attr().nb_wins && cimg::X11attr().wins[i]!=this; ++i) {}
+ for (; i<cimg::X11attr().nb_wins-1; ++i) cimg::X11attr().wins[i] = cimg::X11attr().wins[i+1];
+ --cimg::X11attr().nb_wins;
+
+ // Destroy window, image, colormap and title.
+ if (is_fullscreen && !is_closed) _desinit_fullscreen();
+ XDestroyWindow(cimg::X11attr().display,window);
+ window = 0;
+#ifdef cimg_use_xshm
+ if (shminfo) {
+ XShmDetach(cimg::X11attr().display, shminfo);
+ XDestroyImage(image);
+ shmdt(shminfo->shmaddr);
+ 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<typename T>
+ CImgDisplay& assign(const CImg<T>& 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<T> tmp;
+ const CImg<T>& 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<typename T>
+ CImgDisplay& assign(const CImgList<T>& 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<T> tmp;
+ const CImg<T> 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<typename T>
+ CImgDisplay& display(const CImg<T>& 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<typename T>
+ CImgDisplay& render(const CImg<T>& 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<T>::string()==cimg::type<unsigned char>::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<T>::is_float()) min = (float)img.minmax(max);
+ else { min = (float)cimg::type<T>::min(); max = (float)cimg::type<T>::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<typename T>
+ const CImgDisplay& snapshot(CImg<T>& 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<typename T>
+ CImgDisplay& assign(const CImg<T>& 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<T> tmp;
+ const CImg<T>& 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<typename T>
+ CImgDisplay& assign(const CImgList<T>& 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<T> tmp;
+ const CImg<T> 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<typename T>
+ CImgDisplay& display(const CImg<T>& 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<typename T>
+ CImgDisplay& render(const CImg<T>& 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<T>::string()==cimg::type<unsigned char>::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<T>::is_float()) min = (float)img.minmax(max);
+ else { min = (float)cimg::type<T>::min(); max = (float)cimg::type<T>::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<typename T>
+ const CImgDisplay& snapshot(CImg<T>& 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<typename T>
+ CImgDisplay& assign(const CImg<T>& 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<T> tmp;
+ const CImg<T>& 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<typename T>
+ CImgDisplay& assign(const CImgList<T>& 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<T> tmp;
+ const CImg<T> 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<typename T>
+ CImgDisplay& display(const CImg<T>& 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,&currentContext);
+ 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, &currentContext);
+ MPExitCriticalRegion(paintCriticalRegion);
+ }
+ return *this;
+ }
+
+ template<typename T>
+ CImgDisplay& render(const CImg<T>& 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<T>::string()==cimg::type<unsigned char>::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<T>::is_float()) min = (float)img.minmax(max);
+ else {
+ min = (float)cimg::type<T>::min();
+ max = (float)cimg::type<T>::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<typename T>
+ const CImgDisplay& snapshot(CImg<T>& 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<T> 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 <tt>(x,y,z)</tt>, 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 : <tt>unsigned char, char, short, unsigned int, int,
+ unsigned long, long, float, double, ... </tt>.
+ Typically, fast image display can be done using <tt>CImg<unsigned char></tt> images,
+ while complex image processing algorithms may be rather coded using <tt>CImg<float></tt> or <tt>CImg<double></tt>
+ 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 :
+ - <tt>CImg<char> img;</tt> declares an empty image.
+ - <tt>CImg<unsigned char> img(128,128);</tt> declares a 128x128 greyscale image with
+ \c unsigned \c char pixel values.
+ - <tt>CImg<double> img(3,3);</tt> declares a 3x3 matrix with \c double coefficients.
+ - <tt>CImg<unsigned char> img(256,256,1,3);</tt> declares a 256x256x1x3 (color) image
+ (colors are stored as an image with three channels).
+ - <tt>CImg<double> img(128,128,128);</tt> declares a 128x128x128 volumetric and greyscale image
+ (with \c double pixel values).
+ - <tt>CImg<> img(128,128,128,3);</tt> 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 <b>not automatically initialized to 0</b>. You may use the function \ref fill() to
+ do it, or use the specific constructor taking 5 parameters like this :
+ <tt>CImg<> img(128,128,128,3,0);</tt> declares a 128x128x128 volumetric color image with all pixel values to 0.
+
+ - Construct images from filenames :
+ - <tt>CImg<unsigned char> img("image.jpg");</tt> reads a JPEG color image from the file "image.jpg".
+ - <tt>CImg<float> img("analyze.hdr");</tt> reads a volumetric image (ANALYZE7.5 format) from the file "analyze.hdr".
+ - \b Note : You need to install <a href="http://www.imagemagick.org">ImageMagick</a>
+ to be able to read common compressed image formats (JPG,PNG, ...) (See \ref cimg_files_io).
+
+ - Construct images from C-style arrays :
+ - <tt>CImg<int> img(data_buffer,256,256);</tt> constructs a 256x256 greyscale image from a \c int* buffer
+ \c data_buffer (of size 256x256=65536).
+ - <tt>CImg<unsigned char> img(data_buffer,256,256,1,3,false);</tt> constructs a 256x256 color image
+ from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels follow each others).
+ - <tt>CImg<unsigned char> img(data_buffer,256,256,1,3,true);</tt> 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 <a href="#constructors">here</a>.
+
+ \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<typename T>
+ struct CImg {
+
+ //! Variable representing the width of the instance image (i.e. dimensions along the X-axis).
+ /**
+ \remark
+ - Prefer using the function CImg<T>::dimx() to get information about the width of an image.
+ - Use function CImg<T>::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<T>::dimy() to get information about the height of an image.
+ - Use function CImg<T>::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<T>::dimz() to get information about the depth of an image.
+ - Use function CImg<T>::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<T>::dimv() to get information about the depth of an image.
+ - Use function CImg<T>::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<T>.
+ /**
+ \remark
+ - An \p iterator is a <tt>T*</tt> 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<T>.
+ /**
+ \remark
+ - A \p const_iterator is a <tt>const T*</tt> 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<T,bool>::type Tbool;
+ typedef typename cimg::superset<T,unsigned char>::type Tuchar;
+ typedef typename cimg::superset<T,char>::type Tchar;
+ typedef typename cimg::superset<T,unsigned short>::type Tushort;
+ typedef typename cimg::superset<T,short>::type Tshort;
+ typedef typename cimg::superset<T,unsigned int>::type Tuint;
+ typedef typename cimg::superset<T,int>::type Tint;
+ typedef typename cimg::superset<T,unsigned long>::type Tulong;
+ typedef typename cimg::superset<T,long>::type Tlong;
+ typedef typename cimg::superset<T,float>::type Tfloat;
+ typedef typename cimg::superset<T,double>::type Tdouble;
+ typedef typename cimg::last<T,bool>::type boolT;
+ typedef typename cimg::last<T,unsigned char>::type ucharT;
+ typedef typename cimg::last<T,char>::type charT;
+ typedef typename cimg::last<T,unsigned short>::type ushortT;
+ typedef typename cimg::last<T,short>::type shortT;
+ typedef typename cimg::last<T,unsigned int>::type uintT;
+ typedef typename cimg::last<T,int>::type intT;
+ typedef typename cimg::last<T,unsigned long>::type ulongT;
+ typedef typename cimg::last<T,long>::type longT;
+ typedef typename cimg::last<T,float>::type floatT;
+ typedef typename cimg::last<T,double>::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<typename t>
+ 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<t>::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<T*>(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<typename t>
+ CImg(const CImg<t>& 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<T>& 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<T*>(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<typename t>
+ CImg(const CImg<t>& 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<t>::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<T>& 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<T*>(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<typename t>
+ CImg(const CImg<t>& 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<typename t>
+ CImg(const CImg<t>& 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<T>& 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<T>& 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<T>& 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<T>& 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<T>& 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<T>& 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<typename t>
+ CImg<T>& 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<T>& 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<T>& 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 || data_buffer>=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<typename t>
+ CImg<T>& 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<t>::pixel_type(),width,height,depth,dim,data);
+ return assign(data_buffer,dx,dy,dz,dv);
+ }
+
+ CImg<T>& 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 || data_buffer>=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<T*>(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<typename t>
+ CImg<T>& assign(const CImg<t>& 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<typename t>
+ CImg<T>& assign(const CImg<t>& 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<typename t>
+ CImg<T>& assign(const CImg<t>& 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<typename t>
+ CImg<T>& assign(const CImg<t>& 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<T>& assign(const char *const filename) {
+ return load(filename);
+ }
+
+ //! In-place version of the previous constructor.
+ CImg<T>& 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<typename t>
+ CImg<t>& transfer_to(CImg<t>& img) {
+ img.assign(*this);
+ assign();
+ return img;
+ }
+
+ CImg<T>& transfer_to(CImg<T>& 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<T>& swap(CImg<T>& 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 (<tt>"unsigned char"</tt>).
+ - If the template parameter T does not correspond to a registered type, the string <tt>"unknown"</tt> is returned.
+ **/
+ static const char* pixel_type() {
+ return cimg::type<T>::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<typename t>
+ bool is_sameX(const CImg<t>& 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<typename t>
+ bool is_sameY(const CImg<t>& 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<typename t>
+ bool is_sameZ(const CImg<t>& 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<typename t>
+ bool is_sameV(const CImg<t>& 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<typename t>
+ bool is_sameXY(const CImg<t>& 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<typename t>
+ bool is_sameXZ(const CImg<t>& 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<typename t>
+ bool is_sameXV(const CImg<t>& 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<typename t>
+ bool is_sameYZ(const CImg<t>& 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<typename t>
+ bool is_sameYV(const CImg<t>& 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<typename t>
+ bool is_sameZV(const CImg<t>& 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<typename t>
+ bool is_sameXYZ(const CImg<t>& 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<typename t>
+ bool is_sameXYV(const CImg<t>& 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<typename t>
+ bool is_sameXZV(const CImg<t>& 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<typename t>
+ bool is_sameYZV(const CImg<t>& 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<typename t>
+ bool is_sameXYZV(const CImg<t>& 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<float> 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<CImg<T>*>(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<typename t>
+ bool is_overlapped(const CImg<t>& 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<float> 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<float> 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<CImg<T>*>(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<float> 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<CImg<T>*>(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<dimx() && y>=0 && y<dimy() && z>=0 && z<dimz() && v>=0 && v<dimv();
+ }
+
+ //! Return \c true if specified referenced value is inside image boundaries. If true, returns pixel coordinates in (x,y,z,v).
+ template<typename t>
+ 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 || 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<typename t>
+ 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 || 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<typename t>
+ 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 || 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<typename t>
+ bool contains(const T& pixel, t& x) const {
+ const T *const ppixel = &pixel;
+ if (is_empty() || ppixel<data || 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<data+size();
+ }
+
+ //! Read a pixel value with Dirichlet boundary conditions.
+ T& at(const int off, const T out_val) {
+ return (off<0 || off>=(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 val<valm?valm:(val>valM?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 val<valm?valm:(val>valM?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 val<valm?valm:(val>valM?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 val<valm?valm:(val>valM?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<dimv()) {
+ if (z>=0 && z<dimz()) {
+ if (y>=0 && y<dimy()) {
+ if (x>=0 && x<dimx()) {
+ const float w1 = (1-dx)*(1-dy)*(1-dz), w2 = add?1:(1-w1);
+ (*this)(x,y,z,v) = (T)(w1*val + w2*(*this)(x,y,z,v));
+ }
+ if (nx>=0 && nx<dimx()) {
+ const float w1 = dx*(1-dy)*(1-dz), w2 = add?1:(1-w1);
+ (*this)(nx,y,z,v) = (T)(w1*val + w2*(*this)(nx,y,z,v));
+ }
+ }
+ if (ny>=0 && ny<dimy()) {
+ if (x>=0 && x<dimx()) {
+ const float w1 = (1-dx)*dy*(1-dz), w2 = add?1:(1-w1);
+ (*this)(x,ny,z,v) = (T)(w1*val + w2*(*this)(x,ny,z,v));
+ }
+ if (nx>=0 && nx<dimx()) {
+ const float w1 = dx*dy*(1-dz), w2 = add?1:(1-w1);
+ (*this)(nx,ny,z,v) = (T)(w1*val + w2*(*this)(nx,ny,z,v));
+ }
+ }
+ }
+ if (nz>=0 && nz<dimz()) {
+ if (y>=0 && y<dimy()) {
+ if (x>=0 && x<dimx()) {
+ const float w1 = (1-dx)*(1-dy), w2 = add?1:(1-w1);
+ (*this)(x,y,nz,v) = (T)(w1*val + w2*(*this)(x,y,nz,v));
+ }
+ if (nx>=0 && nx<dimx()) {
+ const float w1 = dx*(1-dy), w2 = add?1:(1-w1);
+ (*this)(nx,y,nz,v) = (T)(w1*val + w2*(*this)(nx,y,nz,v));
+ }
+ }
+ if (ny>=0 && ny<dimy()) {
+ if (x>=0 && x<dimx()) {
+ const float w1 = (1-dx)*dy, w2 = add?1:(1-w1);
+ (*this)(x,ny,nz,v) = (T)(w1*val + w2*(*this)(x,ny,nz,v));
+ }
+ if (nx>=0 && nx<dimx()) {
+ const float w1 = dx*dy, w2 = add?1:(1-w1);
+ (*this)(nx,ny,nz,v) = (T)(w1*val + w2*(*this)(nx,ny,nz,v));
+ }
+ }
+ }
+ }
+ return *this;
+ }
+
+ //! Set a pixel value, with 2D float coordinates, using linear interpolation.
+ CImg& set_linear_atXY(const T& val, const float fx, const float fy=0, const int z=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;
+ const float
+ dx = fx-x,
+ dy = fy-y;
+ if (z>=0 && z<dimz() && v>=0 && v<dimv()) {
+ if (y>=0 && y<dimy()) {
+ if (x>=0 && x<dimx()) {
+ const float w1 = (1-dx)*(1-dy), w2 = add?1:(1-w1);
+ (*this)(x,y,z,v) = (T)(w1*val + w2*(*this)(x,y,z,v));
+ }
+ if (nx>=0 && nx<dimx()) {
+ const float w1 = dx*(1-dy), w2 = add?1:(1-w1);
+ (*this)(nx,y,z,v) = (T)(w1*val + w2*(*this)(nx,y,z,v));
+ }
+ }
+ if (ny>=0 && ny<dimy()) {
+ if (x>=0 && x<dimx()) {
+ const float w1 = (1-dx)*dy, w2 = add?1:(1-w1);
+ (*this)(x,ny,z,v) = (T)(w1*val + w2*(*this)(x,ny,z,v));
+ }
+ if (nx>=0 && nx<dimx()) {
+ const float w1 = dx*dy, w2 = add?1:(1-w1);
+ (*this)(nx,ny,z,v) = (T)(w1*val + w2*(*this)(nx,ny,z,v));
+ }
+ }
+ }
+ return *this;
+ }
+
+ //! Return a reference to the minimum pixel value of the instance image
+ const T& min() const {
+ if (is_empty())
+ throw CImgInstanceException("CImg<%s>::min() : Instance image is empty.",
+ pixel_type());
+ const T *ptrmin = data;
+ T min_value = *ptrmin;
+ cimg_for(*this,ptr,T) if ((*ptr)<min_value) min_value = *(ptrmin=ptr);
+ return *ptrmin;
+ }
+
+ //! Return a reference to the minimum pixel value of the instance image
+ T& min() {
+ if (is_empty())
+ throw CImgInstanceException("CImg<%s>::min() : Instance image is empty.",
+ pixel_type());
+ T *ptrmin = data;
+ T min_value = *ptrmin;
+ cimg_for(*this,ptr,T) if ((*ptr)<min_value) min_value = *(ptrmin=ptr);
+ return *ptrmin;
+ }
+
+ //! Return a reference to the maximum pixel value of the instance image
+ const T& max() const {
+ if (is_empty())
+ throw CImgInstanceException("CImg<%s>::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<typename t>
+ 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 (val<min_value) { min_value = val; ptrmin = ptr; }
+ if (val>max_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<typename t>
+ 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 (val<min_value) { min_value = val; ptrmin = ptr; }
+ if (val>max_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<typename t>
+ 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<min_value) min_value = val;
+ }
+ min_val = (t)min_value;
+ return *ptrmax;
+ }
+
+ //! Return a reference to the maximum pixel value and return also the minimum pixel value.
+ template<typename t>
+ 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<min_value) min_value = val;
+ }
+ min_val = (t)min_value;
+ return *ptrmax;
+ }
+
+ //! Return the sum of all the pixel values in an image.
+ Tfloat sum() const {
+ if (is_empty())
+ throw CImgInstanceException("CImg<%s>::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
+ <table border="0">
+ <tr><td>0</td>
+ <td>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$</td></tr>
+ <tr><td>1</td>
+ <td>Best unbiased estimator: @f$ v = \frac{1}{N-1} \sum\limits_{k=1}^{N} (x_k - \bar x)^2 @f$</td></tr>
+ <tr><td>2</td>
+ <td>Least median of squares</td></tr>
+ <tr><td>3</td>
+ <td>Least trimmed of squares</td></tr>
+ </table>
+ */
+ 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<typename t>
+ 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<Tfloat> 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<siz2; ++j) a+=*(ptrs++);
+ const Tfloat sig = (Tfloat)(2.6477*cimg_std::sqrt(a/siz2));
+ variance = sig*sig;
+ } break;
+ case 2 : { // Least Median of Squares (MAD)
+ CImg<Tfloat> 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<T> arr(*this);
+ unsigned long l = 0, ir = size()-1;
+ for (;;) {
+ if (ir<=l+1) {
+ if (ir==l+1 && arr[ir]<arr[l]) cimg::swap(arr[l],arr[ir]);
+ return arr[k];
+ } else {
+ const unsigned long mid = (l+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);
+ do --j; while (arr[j]>pivot);
+ if (j<i) break;
+ cimg::swap(arr[i],arr[j]);
+ }
+ arr[l+1] = arr[j];
+ arr[j] = 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<T>& stats(const unsigned int variance_method=1) {
+ return get_stats(variance_method).transfer_to(*this);
+ }
+
+ CImg<Tfloat> get_stats(const unsigned int variance_method=1) const {
+ if (is_empty()) return CImg<Tfloat>();
+ 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 (val<m) { m = val; pm = ptr; }
+ if (val>M) { 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<Tfloat>(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<typename t>
+ Tfloat MSE(const CImg<t>& 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<typename t>
+ Tfloat PSNR(const CImg<t>& 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<Tfloat>::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<typename t>
+ Tfloat dot(const CImg<t>& 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<nb; ++off) res+=(Tfloat)data[off]*(Tfloat)img[off];
+ return res;
+ }
+
+ //! Return the determinant of the image, viewed as a matrix.
+ Tfloat det() const {
+ if (is_empty() || width!=height || depth!=1 || dim!=1)
+ throw CImgInstanceException("CImg<%s>::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<Tfloat> lu(*this);
+ CImg<uintT> 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<charT> value_string(const char separator=',', const unsigned int max_size=0) const {
+ if (is_empty()) return CImg<charT>(1,1,1,1,0);
+ const unsigned int siz = (unsigned int)size();
+ CImgList<charT> items;
+ char item[256] = { 0 };
+ const T *ptrs = ptr();
+ for (unsigned int off = 0; off<siz-1; ++off) {
+ cimg_std::sprintf(item,cimg::type<T>::format(),cimg::type<T>::format(*(ptrs++)));
+ const int l = cimg::strlen(item);
+ items.insert(CImg<charT>(item,l+1));
+ items[items.size-1](l) = separator;
+ }
+ cimg_std::sprintf(item,cimg::type<T>::format(),cimg::type<T>::format(*ptrs));
+ items.insert(CImg<charT>(item,cimg::strlen(item)+1));
+ CImg<ucharT> 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<T>& 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<doubleT> 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<T>::format(),cimg::type<T>::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<typename t>
+ CImg<T>& operator=(const CImg<t>& img) {
+ return assign(img);
+ }
+
+ CImg<T>& operator=(const CImg<T>& 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) <tt>this->size()</tt>.
+
+ - 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<float> matrice(4,4); // Define a 4x4 greyscale image.
+ matrice = tab; // Fill the image by the values in tab.
+ \endcode
+ **/
+ CImg<T>& operator=(const T *buf) {
+ return assign(buf,width,height,depth,dim);
+ }
+
+ //! Assign a value to each image pixel of the instance image.
+ CImg<T>& operator=(const T val) {
+ return fill(val);
+ }
+
+ //! Operator+
+ /**
+ \remark
+ - This operator can be used to get a non-shared copy of an image.
+ **/
+ CImg<T> operator+() const {
+ return CImg<T>(*this,false);
+ }
+
+ //! Operator+=;
+#ifdef cimg_use_visualcpp6
+ CImg<T>& operator+=(const T val)
+#else
+ template<typename t>
+ CImg<T>& operator+=(const t val)
+#endif
+ {
+ cimg_for(*this,ptr,T) (*ptr) = (T)((*ptr)+val);
+ return *this;
+ }
+
+ //! Operator+=
+ template<typename t>
+ CImg<T>& operator+=(const CImg<t>& 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<T>& operator++() {
+ cimg_for(*this,ptr,T) ++(*ptr);
+ return *this;
+ }
+
+ //! Operator++ (postfix)
+ CImg<T> operator++(int) {
+ const CImg<T> copy(*this,false);
+ ++*this;
+ return copy;
+ }
+
+ //! Operator-.
+ CImg<T> operator-() const {
+ return CImg<T>(width,height,depth,dim,0)-=*this;
+ }
+
+ //! Operator-=.
+#ifdef cimg_use_visualcpp6
+ CImg<T>& operator-=(const T val)
+#else
+ template<typename t>
+ CImg<T>& operator-=(const t val)
+#endif
+ {
+ cimg_for(*this,ptr,T) (*ptr) = (T)((*ptr)-val);
+ return *this;
+ }
+
+ //! Operator-=.
+ template<typename t>
+ CImg<T>& operator-=(const CImg<t>& 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<T>& operator--() {
+ cimg_for(*this,ptr,T) *ptr = *ptr-(T)1;
+ return *this;
+ }
+
+ //! Operator-- (postfix).
+ CImg<T> operator--(int) {
+ CImg<T> copy(*this,false);
+ --*this;
+ return copy;
+ }
+
+ //! Operator*=.
+#ifdef cimg_use_visualcpp6
+ CImg<T>& operator*=(const double val)
+#else
+ template<typename t>
+ CImg<T>& operator*=(const t val)
+#endif
+ {
+ cimg_for(*this,ptr,T) (*ptr) = (T)((*ptr)*val);
+ return *this;
+ }
+
+ //! Operator*=.
+ template<typename t>
+ CImg<T>& operator*=(const CImg<t>& img) {
+ return ((*this)*img).transfer_to(*this);
+ }
+
+ //! Operator/=.
+#ifdef cimg_use_visualcpp6
+ CImg<T>& operator/=(const double val)
+#else
+ template<typename t>
+ CImg<T>& operator/=(const t val)
+#endif
+ {
+ cimg_for(*this,ptr,T) (*ptr) = (T)((*ptr)/val);
+ return *this;
+ }
+
+ //! Operator/=.
+ template<typename t>
+ CImg<T>& operator/=(const CImg<t>& img) {
+ return assign(*this*img.get_invert());
+ }
+
+ //! Modulo.
+ template<typename t>
+ CImg<typename cimg::superset<T,t>::type> operator%(const CImg<t>& img) const {
+ typedef typename cimg::superset<T,t>::type Tt;
+ return CImg<Tt>(*this,false)%=img;
+ }
+
+ //! Modulo.
+ CImg<T> operator%(const T val) const {
+ return (+*this)%=val;
+ }
+
+ //! In-place modulo.
+ CImg<T>& operator%=(const T val) {
+ cimg_for(*this,ptr,T) (*ptr) = (T)cimg::mod(*ptr,val);
+ return *this;
+ }
+
+ //! In-place modulo.
+ template<typename t>
+ CImg<T>& operator%=(const CImg<t>& img) {
+ if (is_overlapped(img)) return *this%=+img;
+ typedef typename cimg::superset<T,t>::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<typename t>
+ CImg<typename cimg::superset<T,t>::type> operator&(const CImg<t>& img) const {
+ typedef typename cimg::superset<T,t>::type Tt;
+ return CImg<Tt>(*this,false)&=img;
+ }
+
+ //! Bitwise AND.
+ CImg<T> operator&(const T val) const {
+ return (+*this)&=val;
+ }
+
+ //! In-place bitwise AND.
+ template<typename t>
+ CImg<T>& operator&=(const CImg<t>& 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<T>& operator&=(const T val) {
+ cimg_for(*this,ptr,T) *ptr = (T)((unsigned long)*ptr & (unsigned long)val);
+ return *this;
+ }
+
+ //! Bitwise OR.
+ template<typename t>
+ CImg<typename cimg::superset<T,t>::type> operator|(const CImg<t>& img) const {
+ typedef typename cimg::superset<T,t>::type Tt;
+ return CImg<Tt>(*this,false)|=img;
+ }
+
+ //! Bitwise OR.
+ CImg<T> operator|(const T val) const {
+ return (+*this)|=val;
+ }
+
+ //! In-place bitwise OR.
+ template<typename t>
+ CImg<T>& operator|=(const CImg<t>& 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<T>& operator|=(const T val) {
+ cimg_for(*this,ptr,T) *ptr = (T)((unsigned long)*ptr | (unsigned long)val);
+ return *this;
+ }
+
+ //! Bitwise XOR.
+ template<typename t>
+ CImg<typename cimg::superset<T,t>::type> operator^(const CImg<t>& img) const {
+ typedef typename cimg::superset<T,t>::type Tt;
+ return CImg<Tt>(*this,false)^=img;
+ }
+
+ //! Bitwise XOR.
+ CImg<T> operator^(const T val) const {
+ return (+*this)^=val;
+ }
+
+ //! In-place bitwise XOR.
+ template<typename t>
+ CImg<T>& operator^=(const CImg<t>& 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<T>& operator^=(const T val) {
+ cimg_for(*this,ptr,T) *ptr = (T)((unsigned long)*ptr ^ (unsigned long)val);
+ return *this;
+ }
+
+ //! Bitwise NOT.
+ CImg<T> operator~() const {
+ CImg<T> 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<T>& operator<<=(const int n) {
+ cimg_for(*this,ptr,T) *ptr = (T)(((long)*ptr)<<n);
+ return *this;
+ }
+
+ //! Bitwise left shift.
+ CImg<T> operator<<(const int n) const {
+ return (+*this)<<=n;
+ }
+
+ //! Bitwise right shift.
+ CImg<T>& operator>>=(const int n) {
+ cimg_for(*this,ptr,T) *ptr = (T)(((long)*ptr)>>n);
+ return *this;
+ }
+
+ //! Bitwise right shift.
+ CImg<T> operator>>(const int n) const {
+ return (+*this)>>=n;
+ }
+
+ //! Boolean equality.
+ template<typename t>
+ bool operator==(const CImg<t>& 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<typename t>
+ bool operator!=(const CImg<t>& img) const {
+ return !((*this)==img);
+ }
+
+ //! Return a list of two images { *this, img }.
+ template<typename t>
+ CImgList<typename cimg::superset<T,t>::type> operator<<(const CImg<t>& img) const {
+ typedef typename cimg::superset<T,t>::type Tt;
+ return CImgList<Tt>(*this,img);
+ }
+
+ //! Return a copy of \p list, where image *this has been inserted at first position.
+ template<typename t>
+ CImgList<typename cimg::superset<T,t>::type> operator<<(const CImgList<t>& list) const {
+ typedef typename cimg::superset<T,t>::type Tt;
+ return CImgList<Tt>(list).insert(*this,0);
+ }
+
+ //! Return a list of two images { *this, img }.
+ template<typename t>
+ CImgList<typename cimg::superset<T,t>::type> operator>>(const CImg<t>& img) const {
+ return (*this)<<img;
+ }
+
+ //! Insert an image into the begining of an image list.
+ template<typename t>
+ CImgList<t>& operator>>(const CImgList<t>& list) const {
+ return list.insert(*this,0);
+ }
+
+ //! Display an image into a CImgDisplay.
+ const CImg<T>& operator>>(CImgDisplay& disp) const {
+ return display(disp);
+ }
+
+ //@}
+ //---------------------------------------
+ //
+ //! \name Usual Mathematics Functions
+ //@{
+ //---------------------------------------
+
+ //! Apply a R->R function on all pixel values.
+ template<typename t>
+ CImg<T>& apply(t& func) {
+ cimg_for(*this,ptr,T) *ptr = func(*ptr);
+ return *this;
+ }
+
+ template<typename t>
+ CImg<T> get_apply(t& func) const {
+ return (+*this).apply(func);
+ }
+
+ //! Pointwise multiplication between two images.
+ template<typename t>
+ CImg<T>& mul(const CImg<t>& 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<ptrf; ++ptrd) (*ptrd) = (T)(*ptrd*(*(ptrs++)));
+ return *this;
+ }
+
+ template<typename t>
+ CImg<typename cimg::superset<T,t>::type> get_mul(const CImg<t>& img) const {
+ typedef typename cimg::superset<T,t>::type Tt;
+ return CImg<Tt>(*this,false).mul(img);
+ }
+
+ //! Pointwise division between two images.
+ template<typename t>
+ CImg<T>& div(const CImg<t>& 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<ptrf; ++ptrd) (*ptrd) = (T)(*ptrd/(*(ptrs++)));
+ return *this;
+ }
+
+ template<typename t>
+ CImg<typename cimg::superset<T,t>::type> get_div(const CImg<t>& img) const {
+ typedef typename cimg::superset<T,t>::type Tt;
+ return CImg<Tt>(*this,false).div(img);
+ }
+
+ //! Pointwise max operator between two images.
+ template<typename t>
+ CImg<T>& max(const CImg<t>& 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<ptrf; ++ptrd) (*ptrd) = cimg::max((T)*(ptrs++),*ptrd);
+ return *this;
+ }
+
+ template<typename t>
+ CImg<typename cimg::superset<T,t>::type> get_max(const CImg<t>& img) const {
+ typedef typename cimg::superset<T,t>::type Tt;
+ return CImg<Tt>(*this,false).max(img);
+ }
+
+ //! Pointwise max operator between an image and a value.
+ CImg<T>& max(const T val) {
+ cimg_for(*this,ptr,T) (*ptr) = cimg::max(*ptr,val);
+ return *this;
+ }
+
+ CImg<T> get_max(const T val) const {
+ return (+*this).max(val);
+ }
+
+ //! Pointwise min operator between two images.
+ template<typename t>
+ CImg<T>& min(const CImg<t>& 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<ptrf; ++ptrd) (*ptrd) = cimg::min((T)*(ptrs++),*ptrd);
+ return *this;
+ }
+
+ template<typename t>
+ CImg<typename cimg::superset<T,t>::type> get_min(const CImg<t>& img) const {
+ typedef typename cimg::superset<T,t>::type Tt;
+ return CImg<Tt>(*this,false).min(img);
+ }
+
+ //! Pointwise min operator between an image and a value.
+ CImg<T>& min(const T val) {
+ cimg_for(*this,ptr,T) (*ptr) = cimg::min(*ptr,val);
+ return *this;
+ }
+
+ CImg<T> get_min(const T val) const {
+ return (+*this).min(val);
+ }
+
+ //! Compute the square value of each pixel.
+ CImg<T>& sqr() {
+ cimg_for(*this,ptr,T) { const T val = *ptr; *ptr = (T)(val*val); };
+ return *this;
+ }
+
+ CImg<Tfloat> get_sqr() const {
+ return CImg<Tfloat>(*this,false).sqr();
+ }
+
+ //! Compute the square root of each pixel value.
+ CImg<T>& sqrt() {
+ cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::sqrt((double)(*ptr));
+ return *this;
+ }
+
+ CImg<Tfloat> get_sqrt() const {
+ return CImg<Tfloat>(*this,false).sqrt();
+ }
+
+ //! Compute the exponential of each pixel value.
+ CImg<T>& exp() {
+ cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::exp((double)(*ptr));
+ return *this;
+ }
+
+ CImg<Tfloat> get_exp() const {
+ return CImg<Tfloat>(*this,false).exp();
+ }
+
+ //! Compute the log of each each pixel value.
+ CImg<T>& log() {
+ cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::log((double)(*ptr));
+ return *this;
+ }
+
+ CImg<Tfloat> get_log() const {
+ return CImg<Tfloat>(*this,false).log();
+ }
+
+ //! Compute the log10 of each each pixel value.
+ CImg<T>& log10() {
+ cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::log10((double)(*ptr));
+ return *this;
+ }
+
+ CImg<Tfloat> get_log10() const {
+ return CImg<Tfloat>(*this,false).log10();
+ }
+
+ //! Compute the power by p of each pixel value.
+ CImg<T>& 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<Tfloat> get_pow(const double p) const {
+ return CImg<Tfloat>(*this,false).pow(p);
+ }
+
+ //! Compute the power of each pixel value.
+ template<typename t>
+ CImg<T>& pow(const CImg<t>& 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<ptrf; ++ptrd) (*ptrd) = (T)cimg_std::pow((double)*ptrd,(double)(*(ptrs++)));
+ return *this;
+ }
+
+ template<typename t>
+ CImg<Tfloat> get_pow(const CImg<t>& img) const {
+ return CImg<Tfloat>(*this,false).pow(img);
+ }
+
+ //! Compute the absolute value of each pixel value.
+ CImg<T>& abs() {
+ cimg_for(*this,ptr,T) (*ptr) = cimg::abs(*ptr);
+ return *this;
+ }
+
+ CImg<Tfloat> get_abs() const {
+ return CImg<Tfloat>(*this,false).abs();
+ }
+
+ //! Compute the cosinus of each pixel value.
+ CImg<T>& cos() {
+ cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::cos((double)(*ptr));
+ return *this;
+ }
+
+ CImg<Tfloat> get_cos() const {
+ return CImg<Tfloat>(*this,false).cos();
+ }
+
+ //! Compute the sinus of each pixel value.
+ CImg<T>& sin() {
+ cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::sin((double)(*ptr));
+ return *this;
+ }
+
+ CImg<Tfloat> get_sin() const {
+ return CImg<Tfloat>(*this,false).sin();
+ }
+
+ //! Compute the tangent of each pixel.
+ CImg<T>& tan() {
+ cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::tan((double)(*ptr));
+ return *this;
+ }
+
+ CImg<Tfloat> get_tan() const {
+ return CImg<Tfloat>(*this,false).tan();
+ }
+
+ //! Compute the arc-cosine of each pixel value.
+ CImg<T>& acos() {
+ cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::acos((double)(*ptr));
+ return *this;
+ }
+
+ CImg<Tfloat> get_acos() const {
+ return CImg<Tfloat>(*this,false).acos();
+ }
+
+ //! Compute the arc-sinus of each pixel value.
+ CImg<T>& asin() {
+ cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::asin((double)(*ptr));
+ return *this;
+ }
+
+ CImg<Tfloat> get_asin() const {
+ return CImg<Tfloat>(*this,false).asin();
+ }
+
+ //! Compute the arc-tangent of each pixel.
+ CImg<T>& atan() {
+ cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::atan((double)(*ptr));
+ return *this;
+ }
+
+ CImg<Tfloat> get_atan() const {
+ return CImg<Tfloat>(*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<T>& 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<T> 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<T>& 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<T> 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<T>& 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<T> get_fill(const T val) const {
+ return CImg<T>(width,height,depth,dim).fill(val);
+ }
+
+ //! Fill sequentially all pixel values with values \a val0 and \a val1 respectively.
+ CImg<T>& fill(const T val0, const T val1) {
+ if (is_empty()) return *this;
+ T *ptr, *ptr_end = end()-1;
+ for (ptr = data; ptr<ptr_end; ) { *(ptr++) = val0; *(ptr++) = val1; }
+ if (ptr!=ptr_end+1) *(ptr++) = val0;
+ return *this;
+ }
+
+ CImg<T> get_fill(const T val0, const T val1) const {
+ return CImg<T>(width,height,depth,dim).fill(val0,val1);
+ }
+
+ //! Fill sequentially all pixel values with values \a val0 and \a val1 and \a val2.
+ CImg<T>& 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<ptr_end; ) { *(ptr++) = val0; *(ptr++) = val1; *(ptr++) = val2; }
+ ptr_end+=2;
+ switch (ptr_end-ptr) {
+ case 2 : *(--ptr_end) = val1;
+ case 1 : *(--ptr_end) = val0;
+ }
+ return *this;
+ }
+
+ CImg<T> get_fill(const T val0, const T val1, const T val2) const {
+ return CImg<T>(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<T>& 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<ptr_end; ) { *(ptr++) = val0; *(ptr++) = val1; *(ptr++) = val2; *(ptr++) = val3; }
+ ptr_end+=3;
+ switch (ptr_end-ptr) {
+ case 3 : *(--ptr_end) = val2;
+ case 2 : *(--ptr_end) = val1;
+ case 1 : *(--ptr_end) = val0;
+ }
+ return *this;
+ }
+
+ CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3) const {
+ return CImg<T>(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<T>& 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<ptr_end; ) { *(ptr++) = val0; *(ptr++) = val1; *(ptr++) = val2; *(ptr++) = val3; *(ptr++) = val4; }
+ ptr_end+=4;
+ switch (ptr_end-ptr) {
+ case 4 : *(--ptr_end) = val3;
+ case 3 : *(--ptr_end) = val2;
+ case 2 : *(--ptr_end) = val1;
+ case 1 : *(--ptr_end) = val0;
+ }
+ return *this;
+ }
+
+ CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4) const {
+ return CImg<T>(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<T>& 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<ptr_end; ) {
+ *(ptr++) = val0; *(ptr++) = val1; *(ptr++) = val2; *(ptr++) = val3; *(ptr++) = val4; *(ptr++) = val5;
+ }
+ ptr_end+=5;
+ switch (ptr_end-ptr) {
+ case 5 : *(--ptr_end) = val4;
+ case 4 : *(--ptr_end) = val3;
+ case 3 : *(--ptr_end) = val2;
+ case 2 : *(--ptr_end) = val1;
+ case 1 : *(--ptr_end) = val0;
+ }
+ return *this;
+ }
+
+ CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5) const {
+ return CImg<T>(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5);
+ }
+
+ //! Fill sequentially pixel values.
+ CImg<T>& 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<ptr_end; ) {
+ *(ptr++) = val0; *(ptr++) = val1; *(ptr++) = val2; *(ptr++) = val3; *(ptr++) = val4; *(ptr++) = val5; *(ptr++) = val6;
+ }
+ ptr_end+=6;
+ switch (ptr_end-ptr) {
+ case 6 : *(--ptr_end) = val5;
+ case 5 : *(--ptr_end) = val4;
+ case 4 : *(--ptr_end) = val3;
+ case 3 : *(--ptr_end) = val2;
+ case 2 : *(--ptr_end) = val1;
+ case 1 : *(--ptr_end) = val0;
+ }
+ return *this;
+ }
+
+ CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6) const {
+ return CImg<T>(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6);
+ }
+
+ //! Fill sequentially pixel values.
+ CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
+ const T val7) {
+ if (is_empty()) return *this;
+ T *ptr, *ptr_end = end()-7;
+ for (ptr = data; ptr<ptr_end; ) {
+ *(ptr++) = val0; *(ptr++) = val1; *(ptr++) = val2; *(ptr++) = val3;
+ *(ptr++) = val4; *(ptr++) = val5; *(ptr++) = val6; *(ptr++) = val7;
+ }
+ ptr_end+=7;
+ switch (ptr_end-ptr) {
+ case 7 : *(--ptr_end) = val6;
+ case 6 : *(--ptr_end) = val5;
+ case 5 : *(--ptr_end) = val4;
+ case 4 : *(--ptr_end) = val3;
+ case 3 : *(--ptr_end) = val2;
+ case 2 : *(--ptr_end) = val1;
+ case 1 : *(--ptr_end) = val0;
+ }
+ return *this;
+ }
+
+ CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
+ const T val7) const {
+ return CImg<T>(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7);
+ }
+
+ //! Fill sequentially pixel values.
+ CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
+ const T val7, const T val8) {
+ if (is_empty()) return *this;
+ T *ptr, *ptr_end = end()-8;
+ for (ptr = data; ptr<ptr_end; ) {
+ *(ptr++) = val0; *(ptr++) = val1; *(ptr++) = val2;
+ *(ptr++) = val3; *(ptr++) = val4; *(ptr++) = val5;
+ *(ptr++) = val6; *(ptr++) = val7; *(ptr++) = val8;
+ }
+ ptr_end+=8;
+ switch (ptr_end-ptr) {
+ case 8 : *(--ptr_end) = val7;
+ case 7 : *(--ptr_end) = val6;
+ case 6 : *(--ptr_end) = val5;
+ case 5 : *(--ptr_end) = val4;
+ case 4 : *(--ptr_end) = val3;
+ case 3 : *(--ptr_end) = val2;
+ case 2 : *(--ptr_end) = val1;
+ case 1 : *(--ptr_end) = val0;
+ }
+ return *this;
+ }
+
+ CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
+ const T val7, const T val8) const {
+ return CImg<T>(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8);
+ }
+
+ //! Fill sequentially pixel values.
+ CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
+ const T val7, const T val8, const T val9) {
+ if (is_empty()) return *this;
+ T *ptr, *ptr_end = end()-9;
+ for (ptr = data; ptr<ptr_end; ) {
+ *(ptr++) = val0; *(ptr++) = val1; *(ptr++) = val2; *(ptr++) = val3; *(ptr++) = val4;
+ *(ptr++) = val5; *(ptr++) = val6; *(ptr++) = val7; *(ptr++) = val8; *(ptr++) = val9;
+ }
+ ptr_end+=9;
+ switch (ptr_end-ptr) {
+ case 9 : *(--ptr_end) = val8;
+ case 8 : *(--ptr_end) = val7;
+ case 7 : *(--ptr_end) = val6;
+ case 6 : *(--ptr_end) = val5;
+ case 5 : *(--ptr_end) = val4;
+ case 4 : *(--ptr_end) = val3;
+ case 3 : *(--ptr_end) = val2;
+ case 2 : *(--ptr_end) = val1;
+ case 1 : *(--ptr_end) = val0;
+ }
+ return *this;
+ }
+
+ CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
+ const T val7, const T val8, const T val9) const {
+ return CImg<T>(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9);
+ }
+
+ //! Fill sequentially pixel values.
+ CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
+ const T val7, const T val8, const T val9, const T val10) {
+ if (is_empty()) return *this;
+ T *ptr, *ptr_end = end()-10;
+ for (ptr = data; ptr<ptr_end; ) {
+ *(ptr++) = val0; *(ptr++) = val1; *(ptr++) = val2; *(ptr++) = val3; *(ptr++) = val4;
+ *(ptr++) = val5; *(ptr++) = val6; *(ptr++) = val7; *(ptr++) = val8; *(ptr++) = val9;
+ *(ptr++) = val10;
+ }
+ ptr_end+=10;
+ switch (ptr_end-ptr) {
+ case 10 : *(--ptr_end) = val9;
+ case 9 : *(--ptr_end) = val8;
+ case 8 : *(--ptr_end) = val7;
+ case 7 : *(--ptr_end) = val6;
+ case 6 : *(--ptr_end) = val5;
+ case 5 : *(--ptr_end) = val4;
+ case 4 : *(--ptr_end) = val3;
+ case 3 : *(--ptr_end) = val2;
+ case 2 : *(--ptr_end) = val1;
+ case 1 : *(--ptr_end) = val0;
+ }
+ return *this;
+ }
+
+ CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
+ const T val7, const T val8, const T val9, const T val10) const {
+ return CImg<T>(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10);
+ }
+
+ //! Fill sequentially pixel values.
+ CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
+ const T val7, const T val8, const T val9, const T val10, const T val11) {
+ if (is_empty()) return *this;
+ T *ptr, *ptr_end = end()-11;
+ for (ptr = data; ptr<ptr_end; ) {
+ *(ptr++) = val0; *(ptr++) = val1; *(ptr++) = val2; *(ptr++) = val3; *(ptr++) = val4; *(ptr++) = val5;
+ *(ptr++) = val6; *(ptr++) = val7; *(ptr++) = val8; *(ptr++) = val9; *(ptr++) = val10; *(ptr++) = val11;
+ }
+ ptr_end+=11;
+ switch (ptr_end-ptr) {
+ case 11 : *(--ptr_end) = val10;
+ case 10 : *(--ptr_end) = val9;
+ case 9 : *(--ptr_end) = val8;
+ case 8 : *(--ptr_end) = val7;
+ case 7 : *(--ptr_end) = val6;
+ case 6 : *(--ptr_end) = val5;
+ case 5 : *(--ptr_end) = val4;
+ case 4 : *(--ptr_end) = val3;
+ case 3 : *(--ptr_end) = val2;
+ case 2 : *(--ptr_end) = val1;
+ case 1 : *(--ptr_end) = val0;
+ }
+ return *this;
+ }
+
+ CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
+ const T val7, const T val8, const T val9, const T val10, const T val11) const {
+ return CImg<T>(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11);
+ }
+
+ //! Fill sequentially pixel values.
+ CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
+ const T val7, const T val8, const T val9, const T val10, const T val11, const T val12) {
+ if (is_empty()) return *this;
+ T *ptr, *ptr_end = end()-12;
+ for (ptr = data; ptr<ptr_end; ) {
+ *(ptr++) = val0; *(ptr++) = val1; *(ptr++) = val2; *(ptr++) = val3; *(ptr++) = val4; *(ptr++) = val5;
+ *(ptr++) = val6; *(ptr++) = val7; *(ptr++) = val8; *(ptr++) = val9; *(ptr++) = val10; *(ptr++) = val11;
+ *(ptr++) = val12;
+ }
+ ptr_end+=12;
+ switch (ptr_end-ptr) {
+ case 12 : *(--ptr_end) = val11;
+ case 11 : *(--ptr_end) = val10;
+ case 10 : *(--ptr_end) = val9;
+ case 9 : *(--ptr_end) = val8;
+ case 8 : *(--ptr_end) = val7;
+ case 7 : *(--ptr_end) = val6;
+ case 6 : *(--ptr_end) = val5;
+ case 5 : *(--ptr_end) = val4;
+ case 4 : *(--ptr_end) = val3;
+ case 3 : *(--ptr_end) = val2;
+ case 2 : *(--ptr_end) = val1;
+ case 1 : *(--ptr_end) = val0;
+ }
+ return *this;
+ }
+
+ CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
+ const T val7, const T val8, const T val9, const T val10, const T val11, const T val12) const {
+ return CImg<T>(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12);
+ }
+
+ //! Fill sequentially pixel values.
+ CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
+ const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
+ const T val13) {
+ if (is_empty()) return *this;
+ T *ptr, *ptr_end = end()-13;
+ for (ptr = data; ptr<ptr_end; ) {
+ *(ptr++) = val0; *(ptr++) = val1; *(ptr++) = val2; *(ptr++) = val3; *(ptr++) = val4; *(ptr++) = val5;
+ *(ptr++) = val6; *(ptr++) = val7; *(ptr++) = val8; *(ptr++) = val9; *(ptr++) = val10; *(ptr++) = val11;
+ *(ptr++) = val12; *(ptr++) = val13;
+ }
+ ptr_end+=13;
+ switch (ptr_end-ptr) {
+ case 13 : *(--ptr_end) = val12;
+ case 12 : *(--ptr_end) = val11;
+ case 11 : *(--ptr_end) = val10;
+ case 10 : *(--ptr_end) = val9;
+ case 9 : *(--ptr_end) = val8;
+ case 8 : *(--ptr_end) = val7;
+ case 7 : *(--ptr_end) = val6;
+ case 6 : *(--ptr_end) = val5;
+ case 5 : *(--ptr_end) = val4;
+ case 4 : *(--ptr_end) = val3;
+ case 3 : *(--ptr_end) = val2;
+ case 2 : *(--ptr_end) = val1;
+ case 1 : *(--ptr_end) = val0;
+ }
+ return *this;
+ }
+
+ CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
+ const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
+ const T val13) const {
+ return CImg<T>(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,
+ val13);
+ }
+
+ //! Fill sequentially pixel values.
+ CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
+ const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
+ const T val13, const T val14) {
+ if (is_empty()) return *this;
+ T *ptr, *ptr_end = end()-14;
+ for (ptr = data; ptr<ptr_end; ) {
+ *(ptr++) = val0; *(ptr++) = val1; *(ptr++) = val2; *(ptr++) = val3; *(ptr++) = val4; *(ptr++) = val5;
+ *(ptr++) = val6; *(ptr++) = val7; *(ptr++) = val8; *(ptr++) = val9; *(ptr++) = val10; *(ptr++) = val11;
+ *(ptr++) = val12; *(ptr++) = val13; *(ptr++) = val14;
+ }
+ ptr_end+=14;
+ switch (ptr_end-ptr) {
+ case 14 : *(--ptr_end) = val13;
+ case 13 : *(--ptr_end) = val12;
+ case 12 : *(--ptr_end) = val11;
+ case 11 : *(--ptr_end) = val10;
+ case 10 : *(--ptr_end) = val9;
+ case 9 : *(--ptr_end) = val8;
+ case 8 : *(--ptr_end) = val7;
+ case 7 : *(--ptr_end) = val6;
+ case 6 : *(--ptr_end) = val5;
+ case 5 : *(--ptr_end) = val4;
+ case 4 : *(--ptr_end) = val3;
+ case 3 : *(--ptr_end) = val2;
+ case 2 : *(--ptr_end) = val1;
+ case 1 : *(--ptr_end) = val0;
+ }
+ return *this;
+ }
+
+ CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
+ const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
+ const T val13, const T val14) const {
+ return CImg<T>(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<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
+ const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
+ const T val13, const T val14, const T val15) {
+ if (is_empty()) return *this;
+ T *ptr, *ptr_end = end()-15;
+ for (ptr = data; ptr<ptr_end; ) {
+ *(ptr++) = val0; *(ptr++) = val1; *(ptr++) = val2; *(ptr++) = val3; *(ptr++) = val4; *(ptr++) = val5;
+ *(ptr++) = val6; *(ptr++) = val7; *(ptr++) = val8; *(ptr++) = val9; *(ptr++) = val10; *(ptr++) = val11;
+ *(ptr++) = val12; *(ptr++) = val13; *(ptr++) = val14; *(ptr++) = val15;
+ }
+ ptr_end+=15;
+ switch (ptr_end-ptr) {
+ case 15 : *(--ptr_end) = val14;
+ case 14 : *(--ptr_end) = val13;
+ case 13 : *(--ptr_end) = val12;
+ case 12 : *(--ptr_end) = val11;
+ case 11 : *(--ptr_end) = val10;
+ case 10 : *(--ptr_end) = val9;
+ case 9 : *(--ptr_end) = val8;
+ case 8 : *(--ptr_end) = val7;
+ case 7 : *(--ptr_end) = val6;
+ case 6 : *(--ptr_end) = val5;
+ case 5 : *(--ptr_end) = val4;
+ case 4 : *(--ptr_end) = val3;
+ case 3 : *(--ptr_end) = val2;
+ case 2 : *(--ptr_end) = val1;
+ case 1 : *(--ptr_end) = val0;
+ }
+ return *this;
+ }
+
+ CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
+ const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
+ const T val13, const T val14, const T val15) const {
+ return CImg<T>(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<T>& 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<siz) {
+ nvalues += cimg::strlen(cval);
+ *(ptrd++) = (T)val;
+ ++nb;
+ if (err!=2) break; else ++nvalues;
+ }
+ if (repeat_pattern && nb) for (T *ptrs = data; ptrd<ptr_end; ++ptrs) *(ptrd++) = *ptrs;
+ return *this;
+ }
+
+ CImg<T> get_fill(const char *const values, const bool repeat_pattern) const {
+ return repeat_pattern?CImg<T>(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<typename t>
+ CImg<T>& fill(const CImg<t>& 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<ptrs_end && ptrd<ptrd_end; ++ptrs) *(ptrd++) = (T)*ptrs;
+ if (repeat_pattern && ptrd<ptrd_end) for (T *ptrs = data; ptrd<ptrd_end; ++ptrs) *(ptrd++) = *ptrs;
+ return *this;
+ }
+
+ template<typename t>
+ CImg<T> get_fill(const CImg<t>& values, const bool repeat_pattern=true) const {
+ return repeat_pattern?CImg<T>(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<T>& 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<siz; ++k) { ptrd+=off; *ptrd = (T)va_arg(ap,t); } \
+ va_end(ap); }
+ if (y<height && z<depth && v<dim) _cimg_fill1(0,y,z,v,1,width,int);
+ return *this;
+ }
+
+ CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int v, const double a0, ...) {
+ if (y<height && z<depth && v<dim) _cimg_fill1(0,y,z,v,1,width,double);
+ return *this;
+ }
+
+ //! Fill image values along the Y-axis at the specified pixel position (x,z,v).
+ CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int v, const int a0, ...) {
+ if (x<width && z<depth && v<dim) _cimg_fill1(x,0,z,v,width,height,int);
+ return *this;
+ }
+
+ CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int v, const double a0, ...) {
+ if (x<width && z<depth && v<dim) _cimg_fill1(x,0,z,v,width,height,double);
+ return *this;
+ }
+
+ //! Fill image values along the Z-axis at the specified pixel position (x,y,v).
+ CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int v, const int a0, ...) {
+ const unsigned int wh = width*height;
+ if (x<width && y<height && v<dim) _cimg_fill1(x,y,0,v,wh,depth,int);
+ return *this;
+ }
+
+ CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int v, const double a0, ...) {
+ const unsigned int wh = width*height;
+ if (x<width && y<height && v<dim) _cimg_fill1(x,y,0,v,wh,depth,double);
+ return *this;
+ }
+
+ //! Fill image values along the V-axis at the specified pixel position (x,y,z).
+ CImg<T>& 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<width && y<height && z<depth) _cimg_fill1(x,y,z,0,whz,dim,int);
+ return *this;
+ }
+
+ CImg<T>& 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<width && y<height && z<depth) _cimg_fill1(x,y,z,0,whz,dim,double);
+ return *this;
+ }
+
+ //! Linear normalization of the pixel values between \a a and \a b.
+ CImg<T>& normalize(const T a, const T b) {
+ if (is_empty()) return *this;
+ const T na = a<b?a:b, nb = a<b?b:a;
+ T m, M = maxmin(m);
+ const Tfloat fm = (Tfloat)m, fM = (Tfloat)M;
+ if (m==M) return fill(0);
+ if (m!=na || M!=nb) cimg_for(*this,ptr,T) *ptr = (T)((*ptr-fm)/(fM-fm)*(nb-na)+na);
+ return *this;
+ }
+
+ CImg<T> get_normalize(const T a, const T b) const {
+ return (+*this).normalize(a,b);
+ }
+
+ //! Cut pixel values between \a a and \a b.
+ CImg<T>& cut(const T a, const T b) {
+ if (is_empty()) return *this;
+ const T na = a<b?a:b, nb = a<b?b:a;
+ cimg_for(*this,ptr,T) *ptr = (*ptr<na)?na:((*ptr>nb)?nb:*ptr);
+ return *this;
+ }
+
+ CImg<T> get_cut(const T a, const T b) const {
+ return (+*this).cut(a,b);
+ }
+
+ //! Quantize pixel values into \n levels.
+ CImg<T>& 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<T> 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<T>& 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<T> 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<T>& 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<T> get_rotate(const float angle, const unsigned int border_conditions=3, const unsigned int interpolation=1) const {
+ if (is_empty()) return *this;
+ CImg<T> 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<T>& 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<T> 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<T> 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<ymax; ++y) for (unsigned int x = xmin; x<xmax; ++x)
+ dest(x,y,z,v) = (*this)(y-yoff,height-1-x+xoff,z,v);
+ } break;
+ case 2 : {
+ cimg_forXYZV(dest,x,y,z,v) dest(x,y,z,v) = (*this)(width-1-x,height-1-y,z,v);
+ } break;
+ case 3 : {
+ 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<ymax; ++y) for (unsigned int x = xmin; x<xmax; ++x)
+ dest(x,y,z,v) = (*this)(width-1-y+yoff,x-xoff,z,v);
+ } break;
+ default :
+ return *this;
+ }
+ } else {
+ const float
+ rad = (float)((nangle*cimg::valuePI)/180.0),
+ ca = (float)cimg_std::cos(rad)/zoom,
+ sa = (float)cimg_std::sin(rad)/zoom;
+ switch (border_conditions) { // generic version
+ case 0 : {
+ switch (interpolation) {
+ case 2 : {
+ cimg_forXY(dest,x,y)
+ cimg_forZV(*this,z,v)
+ dest(x,y,z,v) = (T)cubic_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*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(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,v,0);
+ } break;
+ default : {
+ cimg_forXY(dest,x,y)
+ cimg_forZV(*this,z,v)
+ dest(x,y,z,v) = atXY((int)(cx + (x-cx)*ca + (y-cy)*sa),(int)(cy - (x-cx)*sa + (y-cy)*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(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,v);
+ } break;
+ case 1 : {
+ cimg_forXY(dest,x,y)
+ cimg_forZV(*this,z,v)
+ dest(x,y,z,v) = (T)linear_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,v);
+ } break;
+ default : {
+ cimg_forXY(dest,x,y)
+ cimg_forZV(*this,z,v)
+ dest(x,y,z,v) = atXY((int)(cx + (x-cx)*ca + (y-cy)*sa),(int)(cy - (x-cx)*sa + (y-cy)*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(cx + (x-cx)*ca + (y-cy)*sa,(float)dimx()),
+ cimg::mod(cy - (x-cx)*sa + (y-cy)*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(cx + (x-cx)*ca + (y-cy)*sa,(float)dimx()),
+ cimg::mod(cy - (x-cx)*sa + (y-cy)*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)(cx + (x-cx)*ca + (y-cy)*sa),dimx()),
+ cimg::mod((int)(cy - (x-cx)*sa + (y-cy)*ca),dimy()),z,v);
+ }
+ }
+ } break;
+ default :
+ throw CImgArgumentException("CImg<%s>::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<T>& 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<T> 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<T>();
+ 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<T>(dx,dy,dz,dv,0);
+
+ CImg<T> 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<dv; ) {
+ const T *ptrz = ptrv;
+ poffz = offz;
+ for (unsigned int z=0; z<dz; ) {
+ const T *ptry = ptrz;
+ poffy = offy;
+ for (unsigned int y=0; y<dy; ) {
+ const T *ptrx = ptry;
+ poffx = offx;
+ cimg_forX(res,x) { *(ptrd++) = *ptrx; ptrx+=*(poffx++); }
+ ++y;
+ unsigned int dy = *(poffy++);
+ for (;!dy && y<dy; cimg_std::memcpy(ptrd, ptrd-dx, sizeof(T)*dx), ++y, ptrd+=dx, dy=*(poffy++)) {}
+ ptry+=dy;
+ }
+ ++z;
+ unsigned int dz = *(poffz++);
+ for (;!dz && z<dz; cimg_std::memcpy(ptrd, ptrd-rwh, sizeof(T)*rwh), ++z, ptrd+=rwh, dz=*(poffz++)) {}
+ ptrz+=dz;
+ }
+ ++k;
+ unsigned int dv = *(poffv++);
+ for (;!dv && k<dv; cimg_std::memcpy(ptrd, ptrd-rwhd, sizeof(T)*rwhd), ++k, ptrd+=rwhd, dv=*(poffv++)) {}
+ ptrv+=dv;
+ }
+ delete[] offx; delete[] offy; delete[] offz; delete[] offv;
+ } break;
+
+ case 2 : { // Moving average
+ bool instance_first = true;
+ if (dx!=width) {
+ CImg<Tfloat> 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<Tfloat> 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<Tfloat> 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<Tfloat> 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<T> 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 = ptrs<ptrsmax?*(ptrs+1):(border_condition?val1:(T)0);
+ *(ptrd++) = (T)((1-alpha)*val1 + alpha*val2);
+ ptrs+=*(poff++);
+ }
+ ptrs0+=width;
+ }
+ }
+ } else resx.assign(*this,true);
+
+ if (dy!=height) {
+ if (height==1) resy = resx.get_resize(dx,dy,depth,dim,1,0);
+ else {
+ resy.assign(dx,dy,depth,dim);
+ curr = old = 0; poff = off; pfoff = foff;
+ cimg_forY(resy,y) { *(pfoff++) = curr-(unsigned int)curr; old = curr; curr+=sy; *(poff++) = dx*((unsigned int)curr-(unsigned int)old); }
+ cimg_forXZV(resy,x,z,k) {
+ ptrd = resy.ptr(x,0,z,k);
+ const T *ptrs = resx.ptr(x,0,z,k), *const ptrsmax = ptrs + (height-1)*dx;
+ poff = off; pfoff = foff;
+ cimg_forY(resy,y) {
+ const float alpha = *(pfoff++);
+ const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs+dx):(border_condition?val1:(T)0);
+ *ptrd = (T)((1-alpha)*val1 + alpha*val2);
+ ptrd+=dx;
+ ptrs+=*(poff++);
+ }
+ }
+ }
+ resx.assign();
+ } else resy.assign(resx,true);
+
+ if (dz!=depth) {
+ if (depth==1) resz = resy.get_resize(dx,dy,dz,dim,1,0);
+ else {
+ const unsigned int wh = dx*dy;
+ resz.assign(dx,dy,dz,dim);
+ curr = old = 0; poff = off; pfoff = foff;
+ cimg_forZ(resz,z) { *(pfoff++) = curr-(unsigned int)curr; old = curr; curr+=sz; *(poff++) = wh*((unsigned int)curr-(unsigned int)old); }
+ cimg_forXYV(resz,x,y,k) {
+ ptrd = resz.ptr(x,y,0,k);
+ const T *ptrs = resy.ptr(x,y,0,k), *const ptrsmax = ptrs + (depth-1)*wh;
+ poff = off; pfoff = foff;
+ cimg_forZ(resz,z) {
+ const float alpha = *(pfoff++);
+ const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs+wh):(border_condition?val1:(T)0);
+ *ptrd = (T)((1-alpha)*val1 + alpha*val2);
+ ptrd+=wh;
+ ptrs+=*(poff++);
+ }
+ }
+ }
+ resy.assign();
+ } else resz.assign(resy,true);
+
+ if (dv!=dim) {
+ if (dim==1) resv = resz.get_resize(dx,dy,dz,dv,1,0);
+ else {
+ const unsigned int whd = dx*dy*dz;
+ resv.assign(dx,dy,dz,dv);
+ curr = old = 0; poff = off; pfoff = foff;
+ cimg_forV(resv,k) { *(pfoff++) = curr-(unsigned int)curr; old = curr; curr+=sv; *(poff++) = whd*((unsigned int)curr-(unsigned int)old); }
+ cimg_forXYZ(resv,x,y,z) {
+ ptrd = resv.ptr(x,y,z,0);
+ const T *ptrs = resz.ptr(x,y,z,0), *const ptrsmax = ptrs + (dim-1)*whd;
+ poff = off; pfoff = foff;
+ cimg_forV(resv,k) {
+ const float alpha = *(pfoff++);
+ const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs+whd):(border_condition?val1:(T)0);
+ *ptrd = (T)((1-alpha)*val1 + alpha*val2);
+ ptrd+=whd;
+ ptrs+=*(poff++);
+ }
+ }
+ }
+ resz.assign();
+ } else resv.assign(resz,true);
+
+ delete[] off; delete[] foff;
+ return resv.is_shared?(resz.is_shared?(resy.is_shared?(resx.is_shared?(+(*this)):resx):resy):resz):resv;
+ } break;
+
+ case 4 : { // Grid filling
+ res.assign(dx,dy,dz,dv,0);
+ cimg_forXYZV(*this,x,y,z,k) res(x*dx/width,y*dy/height,z*dz/depth,k*dv/dim) = (*this)(x,y,z,k);
+ } break;
+
+ case 5 : { // Cubic interpolation
+ 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;
+ 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<typename t>
+ CImg<T>& resize(const CImg<t>& 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<typename t>
+ CImg<T> get_resize(const CImg<t>& 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<T>& 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<T> 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<T>& resize_halfXY() {
+ return get_resize_halfXY().transfer_to(*this);
+ }
+
+ CImg<T> 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<T> 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<T>& resize_doubleXY() {
+ return get_resize_doubleXY().transfer_to(*this);
+ }
+
+ CImg<T> 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<T> 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<T>& resize_tripleXY() {
+ return get_resize_tripleXY().transfer_to(*this);
+ }
+
+ CImg<T> 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<T> 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<typename t>
+ CImg<T>& warp(const CImg<t>& 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<typename t>
+ CImg<T> get_warp(const CImg<t>& 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<T> 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<typename t>
+ CImg<t> _get_permute_axes(const char *permut, const t&) const {
+ if (is_empty() || !permut) return CImg<t>(*this,false);
+ CImg<t> 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<T>& permute_axes(const char *order) {
+ return get_permute_axes(order).transfer_to(*this);
+ }
+
+ CImg<T> get_permute_axes(const char *order) const {
+ const T foo = (T)0;
+ return _get_permute_axes(order,foo);
+ }
+
+ //! Invert endianness.
+ CImg<T>& invert_endianness() {
+ cimg::invert_endianness(data,size());
+ return *this;
+ }
+
+ CImg<T> get_invert_endianness() const {
+ return (+*this).invert_endianness();
+ }
+
+ //! Mirror an image along the specified axis.
+ CImg<T>& 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<height*depth*dim; ++yzv) {
+ for (unsigned int x = 0; x<width2; ++x) { const T val = *pf; *(pf++) = *pb; *(pb--) = val; }
+ pf+=width - width2;
+ pb+=width + width2;
+ }
+ } break;
+ case 'y' : {
+ buf = new T[width];
+ pf = data; pb = ptr(0,height-1);
+ const unsigned int height2 = height/2;
+ for (unsigned int zv=0; zv<depth*dim; ++zv) {
+ for (unsigned int y=0; y<height2; ++y) {
+ cimg_std::memcpy(buf,pf,width*sizeof(T));
+ cimg_std::memcpy(pf,pb,width*sizeof(T));
+ cimg_std::memcpy(pb,buf,width*sizeof(T));
+ pf+=width;
+ pb-=width;
+ }
+ pf+=width*(height - height2);
+ pb+=width*(height + height2);
+ }
+ } break;
+ case 'z' : {
+ buf = new T[width*height];
+ pf = data; pb = ptr(0,0,depth-1);
+ const unsigned int depth2 = depth/2;
+ cimg_forV(*this,v) {
+ for (unsigned int z=0; z<depth2; ++z) {
+ cimg_std::memcpy(buf,pf,width*height*sizeof(T));
+ cimg_std::memcpy(pf,pb,width*height*sizeof(T));
+ cimg_std::memcpy(pb,buf,width*height*sizeof(T));
+ pf+=width*height;
+ pb-=width*height;
+ }
+ pf+=width*height*(depth - depth2);
+ pb+=width*height*(depth + depth2);
+ }
+ } break;
+ case 'v' : {
+ buf = new T[width*height*depth];
+ pf = data; pb = ptr(0,0,0,dim-1);
+ const unsigned int dim2 = dim/2;
+ for (unsigned int v=0; v<dim2; ++v) {
+ cimg_std::memcpy(buf,pf,width*height*depth*sizeof(T));
+ cimg_std::memcpy(pf,pb,width*height*depth*sizeof(T));
+ cimg_std::memcpy(pb,buf,width*height*depth*sizeof(T));
+ pf+=width*height*depth;
+ pb-=width*height*depth;
+ }
+ } break;
+ default :
+ throw CImgArgumentException("CImg<%s>::mirror() : unknow axis '%c', must be 'x','y','z' or 'v'.",
+ pixel_type(),axis);
+ }
+ if (buf) delete[] buf;
+ return *this;
+ }
+
+ CImg<T> 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<T>& 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<ndeltax-1; ++l) *(--ptrd) = val;
+ }
+ } else {
+ const int ndeltax = (-deltax>=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; l<ndeltax-1; ++l) *(++ptrd) = val;
+ }
+ }
+ break;
+ case 2 : {
+ const int ml = cimg::mod(deltax,dimx()), ndeltax = (ml<=dimx()/2)?ml:(ml-dimx());
+ if (!ndeltax) return *this;
+ T* buf = new T[cimg::abs(ndeltax)];
+ if (ndeltax>0) 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<ndeltay-1; ++l) { cimg_std::memcpy(ptrd,ptrs,width*sizeof(T)); ptrd+=width; }
+ }
+ } else {
+ const int ndeltay = (-deltay>=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; l<ndeltay-1; ++l) { cimg_std::memcpy(ptrd,ptrs,width*sizeof(T)); ptrd+=width; }
+ }
+ }
+ break;
+ case 2 : {
+ const int ml = cimg::mod(deltay,dimy()), ndeltay = (ml<=dimy()/2)?ml:(ml-dimy());
+ if (!ndeltay) return *this;
+ T* buf = new T[width*cimg::abs(ndeltay)];
+ if (ndeltay>0) 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<ndeltaz-1; ++l) { cimg_std::memcpy(ptrd,ptrs,width*height*sizeof(T)); ptrd+=width*height; }
+ }
+ } else {
+ const int ndeltaz = (-deltaz>=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; l<ndeltaz-1; ++l) { cimg_std::memcpy(ptrd,ptrs,width*height*sizeof(T)); ptrd+=width*height; }
+ }
+ }
+ break;
+ case 2 : {
+ const int ml = cimg::mod(deltaz,dimz()), ndeltaz = (ml<=dimz()/2)?ml:(ml-dimz());
+ if (!ndeltaz) return *this;
+ T* buf = new T[width*height*cimg::abs(ndeltaz)];
+ if (ndeltaz>0) 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<ndeltav-1; ++l) { cimg_std::memcpy(ptrd,ptrs,width*height*depth*sizeof(T)); ptrd+=width*height*depth; }
+ } else {
+ const int ndeltav = (-deltav>=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; l<ndeltav-1; ++l) { cimg_std::memcpy(ptrd,data,width*height*depth*sizeof(T)); ptrd+=width*height*depth; }
+ }
+ break;
+ case 2 : {
+ const int ml = cimg::mod(deltav,dimv()), ndeltav = (ml<=dimv()/2)?ml:(ml-dimv());
+ if (!ndeltav) return *this;
+ T* buf = new T[width*height*depth*cimg::abs(ndeltav)];
+ if (ndeltav>0) {
+ 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<T> 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<T>& 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<T> 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<x1?x0:x1, nx1 = x0^x1^nx0,
+ ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0,
+ nz0 = z0<z1?z0:z1, nz1 = z0^z1^nz0,
+ nv0 = v0<v1?v0:v1, nv1 = v0^v1^nv0;
+ CImg<T> 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<T>& 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<T> 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<T>& 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<T> 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<T>& 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<T> 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<T>& 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<lmax; ++l) autocrop(value,axes[l]);
+ return *this;
+ }
+
+ CImg<T> 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<T>& 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<lmax; ++l) autocrop(color,axes[l]);
+ return *this;
+ }
+
+ CImg<T> 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<typename t> CImg<T>& autocrop(const CImg<t>& color, const char *const axes="zyx") {
+ return get_autocrop(color,axes).transfer_to(*this);
+ }
+
+ template<typename t> CImg<T> get_autocrop(const CImg<t>& 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<T>& autocrop(const T value, const char axis) {
+ return get_autocrop(value,axis).transfer_to(*this);
+ }
+
+ CImg<T> get_autocrop(const T value, const char axis) const {
+ if (is_empty()) return *this;
+ CImg<T> res;
+ const CImg<intT> 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<T>& autocrop(const T *const color, const char axis) {
+ return get_autocrop(color,axis).transfer_to(*this);
+ }
+
+ CImg<T> get_autocrop(const T *const color, const char axis) const {
+ if (is_empty()) return *this;
+ CImg<T> res;
+ switch (cimg::uncase(axis)) {
+ case 'x' : {
+ int x0 = width, x1 = -1;
+ cimg_forV(*this,k) {
+ const CImg<intT> 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<intT> 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<intT> 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<typename t> CImg<T>& autocrop(const CImg<t>& color, const char axis) {
+ return get_autocrop(color,axis).transfer_to(*this);
+ }
+
+ template<typename t> CImg<T> get_autocrop(const CImg<t>& color, const char axis) const {
+ return get_autocrop(color.data,axis);
+ }
+
+ CImg<intT> _get_autocrop(const T value, const char axis) const {
+ CImg<intT> 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<intT>::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<intT>::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<intT>::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<intT>::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<T>& columns(const unsigned int x0, const unsigned int x1) {
+ return get_columns(x0,x1).transfer_to(*this);
+ }
+
+ CImg<T> 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<T>& column(const unsigned int x0) {
+ return columns(x0,x0);
+ }
+
+ CImg<T> get_column(const unsigned int x0) const {
+ return get_columns(x0,x0);
+ }
+
+ //! Get a set of lines.
+ CImg<T>& lines(const unsigned int y0, const unsigned int y1) {
+ return get_lines(y0,y1).transfer_to(*this);
+ }
+
+ CImg<T> 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<T>& line(const unsigned int y0) {
+ return lines(y0,y0);
+ }
+
+ CImg<T> get_line(const unsigned int y0) const {
+ return get_lines(y0,y0);
+ }
+
+ //! Get a set of slices.
+ CImg<T>& slices(const unsigned int z0, const unsigned int z1) {
+ return get_slices(z0,z1).transfer_to(*this);
+ }
+
+ CImg<T> 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<T>& slice(const unsigned int z0) {
+ return slices(z0,z0);
+ }
+
+ CImg<T> get_slice(const unsigned int z0) const {
+ return get_slices(z0,z0);
+ }
+
+ //! Get a set of channels.
+ CImg<T>& channels(const unsigned int v0, const unsigned int v1) {
+ return get_channels(v0,v1).transfer_to(*this);
+ }
+
+ CImg<T> 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<T>& channel(const unsigned int v0) {
+ return channels(v0,v0);
+ }
+
+ CImg<T> 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<T> get_shared_points(const unsigned int x0, const unsigned int x1,
+ const unsigned int y0=0, const unsigned int z0=0, const unsigned int 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<T>(data+beg,x1-x0+1,1,1,1,true);
+ }
+
+ const CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
+ const unsigned int y0=0, const unsigned int z0=0, const unsigned int 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<T>(data+beg,x1-x0+1,1,1,1,true);
+ }
+
+ //! Return a shared-memory image referencing a set of lines of the instance image.
+ CImg<T> 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<T>(data+beg,width,y1-y0+1,1,1,true);
+ }
+
+ const CImg<T> 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<T>(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<T> 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<T> 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<T> 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<T>(data+beg,width,height,z1-z0+1,1,true);
+ }
+
+ const CImg<T> 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<T>(data+beg,width,height,z1-z0+1,1,true);
+ }
+
+ //! Return a shared-memory image referencing one plane (z0,v0) of the instance image.
+ CImg<T> get_shared_plane(const unsigned int z0, const unsigned int v0=0) {
+ return get_shared_planes(z0,z0,v0);
+ }
+
+ const CImg<T> 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<T> 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<T>(data+beg,width,height,depth,v1-v0+1,true);
+ }
+
+ const CImg<T> 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<T>(data+beg,width,height,depth,v1-v0+1,true);
+ }
+
+ //! Return a shared-memory image referencing one channel v0 of the instance image.
+ CImg<T> get_shared_channel(const unsigned int v0) {
+ return get_shared_channels(v0,v0);
+ }
+
+ const CImg<T> get_shared_channel(const unsigned int v0) const {
+ return get_shared_channels(v0,v0);
+ }
+
+ //! Return a shared version of the instance image.
+ CImg<T> get_shared() {
+ return CImg<T>(data,width,height,depth,dim,true);
+ }
+
+ const CImg<T> get_shared() const {
+ return CImg<T>(data,width,height,depth,dim,true);
+ }
+
+ //! Return a 2D representation of a 3D image, with three slices.
+ CImg<T>& 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<T> 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<T>
+ 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<T>(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<float> 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<float> 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<T>& 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<floatT> get_histogram(const unsigned int nblevels, const T val_min=(T)0, const T val_max=(T)0) const {
+ if (is_empty()) return CImg<floatT>();
+ 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<floatT> res(nblevels,1,1,1,0);
+ if (vmin>=vmax && vmin==0) vmin = minmax(vmax);
+ if (vmin<vmax) cimg_for(*this,ptr,T) {
+ const int pos = (int)((*ptr-vmin)*(nblevels-1)/(vmax-vmin));
+ if (pos>=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<float> 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<T>& 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<vmax) {
+ CImg<floatT> 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<T> 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<T>& label_regions() {
+ return get_label_regions().transfer_to(*this);
+ }
+
+ CImg<uintT> 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<uintT> 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<cptr; ++ptr) if (*ptr==lold) *ptr = lab;
+ }
+ }
+ }
+ }
+ if (x && !done) { _cimg_get_label_test(x-1,y); if (flag) { res(x,y) = res(x-1,y); done = true; }}
+ if (!done) res(x,y) = label++;
+ }
+ { for (int y = H1; y>=0; --y) for (int x=W1; x>=0; --x) {
+ bool done = false;
+ if (y<H1) {
+ _cimg_get_label_test(x,y+1);
+ if (flag) {
+ const unsigned int lab = (res(x,y) = res(x,y+1));
+ done = true;
+ if (x<W1 && 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()+res.size()-1; ptr>cptr; --ptr) if (*ptr==lold) *ptr = lab;
+ }
+ }
+ }
+ }
+ if (x<W1 && !done) { _cimg_get_label_test(x+1,y); if (flag) res(x,y) = res(x+1,y); done = true; }
+ }}
+ const unsigned int lab0 = res.max()+1;
+ label = lab0;
+ cimg_foroff(res,off) { // Relabel regions
+ const unsigned int lab = res[off];
+ if (lab<lab0) { cimg_for(res,ptr,unsigned int) if (*ptr==lab) *ptr = label; ++label; }
+ }
+ return (res-=lab0);
+ }
+
+ //! Compute the scalar image of vector norms.
+ /**
+ When dealing with vector-valued images (i.e images with dimv()>1), 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<float> with size (dimx(),dimy(),dimz(),1), where each pixel is the norm
+ of the corresponding pixels in the original vector-valued image.
+ **/
+ CImg<T>& pointwise_norm(int norm_type=2) {
+ return get_pointwise_norm(norm_type).transfer_to(*this);
+ }
+
+ CImg<Tfloat> get_pointwise_norm(int norm_type=2) const {
+ if (is_empty()) return *this;
+ if (dim==1) return get_abs();
+ CImg<Tfloat> 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<T>& 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<Tfloat> get_pointwise_orientation() const {
+ if (is_empty()) return *this;
+ return CImg<Tfloat>(*this,false).pointwise_orientation();
+ }
+
+ //! Split image into a list.
+ CImgList<T> get_split(const char axis, const unsigned int nb=0) const {
+ if (is_empty()) return CImgList<T>();
+ CImgList<T> 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; l<res.size-1; ++l, x+=delta) res[l] = get_crop(x,0,0,0,x+delta-1,height-1,depth-1,dim-1);
+ res[res.size-1] = get_crop(x,0,0,0,width-1,height-1,depth-1,dim-1);
+ } break;
+ case 'y' : {
+ if (nb>height)
+ 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; l<res.size-1; ++l, y+=delta) res[l] = get_crop(0,y,0,0,width-1,y+delta-1,depth-1,dim-1);
+ res[res.size-1] = get_crop(0,y,0,0,width-1,height-1,depth-1,dim-1);
+ } break;
+ case 'z' : {
+ if (nb>depth)
+ 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; l<res.size-1; ++l, z+=delta) res[l] = get_crop(0,0,z,0,width-1,height-1,z+delta-1,dim-1);
+ res[res.size-1] = get_crop(0,0,z,0,width-1,height-1,depth-1,dim-1);
+ } break;
+ case 'v' : {
+ if (nb>dim)
+ 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<res.size-1; ++l, v+=delta) res[l] = get_crop(0,0,0,v,width-1,height-1,depth-1,v+delta-1);
+ res[res.size-1] = get_crop(0,0,0,v,width-1,height-1,depth-1,dim-1);
+ } break;
+ default :
+ throw CImgArgumentException("CImg<%s>::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<T> get_split(const T value, const bool keep_values, const bool shared) const {
+ CImgList<T> res;
+ const T *ptr0 = data, *const ptr_end = data + size();
+ while (ptr0<ptr_end) {
+ const T *ptr1 = ptr0;
+ while (ptr1<ptr_end && *ptr1==value) ++ptr1;
+ const unsigned int siz0 = ptr1 - ptr0;
+ if (siz0 && keep_values) res.insert(CImg<T>(ptr0,1,siz0,1,1,shared));
+ ptr0 = ptr1;
+ while (ptr1<ptr_end && *ptr1!=value) ++ptr1;
+ const unsigned int siz1 = ptr1 - ptr0;
+ if (siz1) res.insert(CImg<T>(ptr0,1,siz1,1,1,shared),~0U,shared);
+ ptr0 = ptr1;
+ }
+ return res;
+ }
+
+ //! Append an image to another one.
+ CImg<T>& append(const CImg<T>& 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<T> get_append(const CImg<T>& img, const char axis, const char align='p') const {
+ if (!img) return *this;
+ if (is_empty()) return img;
+ CImgList<T> 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<T> 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<Tfloat> get_gradient(const char *const axes=0, const int scheme=3) const {
+ CImgList<Tfloat> 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<Tfloat> 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<T>& structure_tensor(const bool central_scheme=false) {
+ return get_structure_tensor(central_scheme).transfer_to(*this);
+ }
+
+ CImg<Tfloat> get_structure_tensor(const bool central_scheme=false) const {
+ if (is_empty()) return *this;
+ CImg<Tfloat> 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<Tfloat> 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<Tfloat> 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; l<lmax; ) { // Version with custom axes.
+ const int l2 = l/2;
+ char axis1 = naxes[l++], axis2 = naxes[l++];
+ if (axis1>axis2) 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<T>& distance_hamilton(const unsigned int nb_iter, const float band_size=0, const float precision=0.5f) {
+ if (is_empty()) return *this;
+ CImg<Tfloat> veloc(*this);
+ for (unsigned int iter = 0; iter<nb_iter; ++iter) {
+ veloc.fill(0);
+ if (depth>1) { // 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)<band_size) {
+ const Tfloat
+ gx = 0.5f*((Tfloat)Incc - Ipcc),
+ gy = 0.5f*((Tfloat)Icnc - Icpc),
+ gz = 0.5f*((Tfloat)Iccn - Iccp),
+ sgn = -cimg::sign((Tfloat)Iccc),
+ ix = gx*sgn>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)<band_size) {
+ const Tfloat
+ gx = 0.5f*((Tfloat)Inc - Ipc),
+ gy = 0.5f*((Tfloat)Icn - Icp),
+ sgn = -cimg::sign((Tfloat)Icc),
+ ix = gx*sgn>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<Tfloat> get_distance_hamilton(const unsigned int nb_iter, const float band_size=0, const float precision=0.5f) const {
+ return CImg<Tfloat>(*this,false).distance_hamilton(nb_iter,band_size,precision);
+ }
+
+ //! Compute the Euclidean distance map to a shape of specified isovalue.
+ CImg<T>& 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<floatT> 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<floatT> 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<dx; ++x) if ((*this)(x,y,z,k)==isovalue) { is_isophote = true; res(x,y,z,k) = 0; }
+ else res(x,y,z,k) = res(x-1,y,z,k) + sizex;
+ { for (int x = dx-2; x>=0; --x) if (res(x+1,y,z,k)<res(x,y,z,k)) res(x,y,z,k) = res(x+1,y,z,k) + sizex; }
+ }}
+ if (!is_isophote) { res.get_shared_channel(k).fill(cimg::type<float>::max()); continue; }
+ CImg<floatT> tmp(cimg::max(dy,dz));
+ CImg<intT> 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<dy; ++y) {
+ const float val = tmp[y], val2 = val*val;
+ while (q>=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<dy) { s[++q] = y; t[q] = w; }
+ }
+ }}
+ { for (int y = dy - 1; y>=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<dz; ++z) {
+ const float val = tmp[z];
+ while (q>=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<dz) { s[++q] = z; t[q] = w; }
+ }
+ }}
+ { for (int z = dz - 1; z>=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<dy; ++y) { ptrs+=width; ptrd+=width; d = *ptrd = *ptrs==isovalue?(is_isophote=true),0:d+sizey; }
+ { for (int y = dy - 2; 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<float>::max()); continue; }
+ CImg<floatT> tmp(dx);
+ CImg<intT> 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<dx; ++x) {
+ const float val = *(++ptmp), val2 = val*val;
+ while (q>=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<dx) { q++; s[q] = x; t[q] = w; }
+ }
+ }
+ float *pres = res.ptr(0,y,0,k) + width;
+ { for (int x = dx - 1; x>=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<typename tf, typename t>
+ static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
+ const unsigned int starting_node, const unsigned int ending_node,
+ CImg<t>& previous) {
+
+ CImg<T> dist(1,nb_nodes,1,1,cimg::type<T>::max());
+ dist(starting_node) = 0;
+ previous.assign(1,nb_nodes,1,1,(t)-1);
+ previous(starting_node) = (t)starting_node;
+ CImg<uintT> 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<T>::max();
+ for (unsigned int q=1; q<sizeQ; ++q) {
+ const unsigned int v = Q(q);
+ const T d = (T)distance(v,umin);
+ if (d<infty) {
+ const T alt = dmin + d;
+ if (alt<dist(v)) {
+ dist(v) = alt;
+ previous(v) = (t)umin;
+ const T distpos = dist(Q(q));
+ for (unsigned int pos = q, par = 0; pos && distpos<dist(Q(par=(pos+1)/2-1)); pos=par) cimg::swap(Q(pos),Q(par));
+ }
+ }
+ }
+ // Remove minimal vertex from queue
+ Q(0) = Q(--sizeQ);
+ const T distpos = dist(Q(0));
+ for (unsigned int pos = 0, left = 0, right = 0;
+ ((right=2*(pos+1),(left=right-1))<sizeQ && distpos>dist(Q(left))) || (right<sizeQ && distpos>dist(Q(right)));) {
+ if (right<sizeQ) {
+ if (dist(Q(left))<dist(Q(right))) { cimg::swap(Q(pos),Q(left)); pos = left; }
+ else { cimg::swap(Q(pos),Q(right)); pos = right; }
+ } else { cimg::swap(Q(pos),Q(left)); pos = left; }
+ }
+ }
+ }
+ return dist;
+ }
+
+ //! Return minimal path in a graph, using the Dijkstra algorithm.
+ template<typename tf, typename t>
+ static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
+ const unsigned int starting_node, const unsigned int ending_node=~0U) {
+ CImg<uintT> 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<typename t>
+ CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node, CImg<t>& previous) {
+ return get_dijkstra(starting_node,ending_node,previous).transfer_to(*this);
+ }
+
+ template<typename t>
+ CImg<T> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node, CImg<t>& 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<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) {
+ return get_dijkstra(starting_node,ending_node).transfer_to(*this);
+ }
+
+ CImg<Tfloat> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) const {
+ CImg<uintT> foo;
+ return get_dijkstra(starting_node,ending_node,foo);
+ }
+
+ //@}
+ //-------------------------------------
+ //
+ //! \name Meshes and Triangulations
+ //@{
+ //-------------------------------------
+
+ //! Return a 3D centered cube.
+ template<typename tf>
+ static CImg<floatT> cube3d(CImgList<tf>& 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<floatT>(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<typename tf>
+ static CImg<floatT> cuboid3d(CImgList<tf>& 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<floatT>(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<typename tf>
+ static CImg<floatT> cone3d(CImgList<tf>& primitives, const float radius=50, const float height=100,
+ const unsigned int subdivisions=24, const bool symetrize=false) {
+ primitives.assign();
+ if (!subdivisions) return CImg<floatT>();
+ const double r = (double)radius, h = (double)height/2;
+ CImgList<floatT> 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<floatT>::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<nbr; ++p) {
+ const unsigned int curr = 2+p, next = 2+((p+1)%nbr);
+ primitives.insert(CImg<tf>::vector(1,next,curr)).
+ insert(CImg<tf>::vector(0,curr,next));
+ }
+ return points.get_append('x');
+ }
+
+ //! Return a 3D centered cylinder.
+ template<typename tf>
+ static CImg<floatT> cylinder3d(CImgList<tf>& primitives, const float radius=50, const float height=100,
+ const unsigned int subdivisions=24) {
+ primitives.assign();
+ if (!subdivisions) return CImg<floatT>();
+ const double r = (double)radius, h = (double)height/2;
+ CImgList<floatT> 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<floatT>::vector((float)(r*cimg_std::cos(a)),(float)(r*cimg_std::sin(a)),-(float)h));
+ points.insert(CImg<floatT>::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<nbr; ++p) {
+ const unsigned int curr = 2+2*p, next = 2+(2*((p+1)%nbr));
+ primitives.insert(CImg<tf>::vector(0,next,curr)).
+ insert(CImg<tf>::vector(1,curr+1,next+1)).
+ insert(CImg<tf>::vector(curr,next,next+1,curr+1));
+ }
+ return points.get_append('x');
+ }
+
+ //! Return a 3D centered torus.
+ template<typename tf>
+ static CImg<floatT> torus3d(CImgList<tf>& primitives, const float radius1=100, const float radius2=30,
+ const unsigned int subdivisions1=24, const unsigned int subdivisions2=12) {
+ primitives.assign();
+ if (!subdivisions1 || !subdivisions2) return CImg<floatT>();
+ CImgList<floatT> points;
+ for (unsigned int v = 0; v<subdivisions1; ++v) {
+ const float
+ beta = (float)(v*2*cimg::valuePI/subdivisions1),
+ xc = radius1*(float)cimg_std::cos(beta),
+ yc = radius1*(float)cimg_std::sin(beta);
+ for (unsigned int u=0; u<subdivisions2; ++u) {
+ const float
+ alpha = (float)(u*2*cimg::valuePI/subdivisions2),
+ x = xc + radius2*(float)(cimg_std::cos(alpha)*cimg_std::cos(beta)),
+ y = yc + radius2*(float)(cimg_std::cos(alpha)*cimg_std::sin(beta)),
+ z = radius2*(float)cimg_std::sin(alpha);
+ points.insert(CImg<floatT>::vector(x,y,z));
+ }
+ }
+ for (unsigned int vv = 0; vv<subdivisions1; ++vv) {
+ const unsigned int nv = (vv+1)%subdivisions1;
+ for (unsigned int uu = 0; uu<subdivisions2; ++uu) {
+ const unsigned int nu = (uu+1)%subdivisions2, svv = subdivisions2*vv, snv = subdivisions2*nv;
+ primitives.insert(CImg<tf>::vector(svv+nu,svv+uu,snv+uu));
+ primitives.insert(CImg<tf>::vector(svv+nu,snv+uu,snv+nu));
+ }
+ }
+ return points.get_append('x');
+ }
+
+ //! Return a 3D centered XY plane.
+ template<typename tf>
+ static CImg<floatT> plane3d(CImgList<tf>& 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<floatT>();
+ CImgList<floatT> 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<h; ++yy)
+ for (unsigned int xx = 0; xx<w; ++xx)
+ points.insert(CImg<floatT>::vector(fx*(xx-w2),fy*(yy-h2),0));
+ for (unsigned int y = 0; y<subdivisionsy; ++y) for (unsigned int x = 0; x<subdivisionsx; ++x) {
+ const int off1 = x+y*w, off2 = x+1+y*w, off3 = x+1+(y+1)*w, off4 = x+(y+1)*w;
+ primitives.insert(CImg<tf>::vector(off1,off4,off3,off2));
+ if (double_sided) primitives.insert(CImg<tf>::vector(off1,off2,off3,off4));
+ }
+ return points.get_append('x');
+ }
+
+ //! Return a 3D centered sphere.
+ template<typename tf>
+ static CImg<floatT> sphere3d(CImgList<tf>& primitives, const float radius=50, const unsigned int subdivisions=3) {
+
+ // Create initial icosahedron
+ primitives.assign();
+ if (!subdivisions) return CImg<floatT>();
+ const double tmp = (1+cimg_std::sqrt(5.0f))/2, a = 1.0/cimg_std::sqrt(1+tmp*tmp), b = tmp*a;
+ CImgList<floatT> 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<subdivisions; ++i) {
+ const unsigned int L = primitives.size;
+ for (unsigned int l = 0; l<L; ++l) {
+ const unsigned int
+ p0 = (unsigned int)primitives(0,0), p1 = (unsigned int)primitives(0,1), p2 = (unsigned int)primitives(0,2);
+ const float
+ x0 = points(p0,0), y0 = points(p0,1), z0 = points(p0,2),
+ x1 = points(p1,0), y1 = points(p1,1), z1 = points(p1,2),
+ x2 = points(p2,0), y2 = points(p2,1), z2 = points(p2,2),
+ tnx0 = (x0+x1)/2, tny0 = (y0+y1)/2, tnz0 = (z0+z1)/2, nn0 = (float)cimg_std::sqrt(tnx0*tnx0+tny0*tny0+tnz0*tnz0),
+ tnx1 = (x0+x2)/2, tny1 = (y0+y2)/2, tnz1 = (z0+z2)/2, nn1 = (float)cimg_std::sqrt(tnx1*tnx1+tny1*tny1+tnz1*tnz1),
+ tnx2 = (x1+x2)/2, tny2 = (y1+y2)/2, tnz2 = (z1+z2)/2, nn2 = (float)cimg_std::sqrt(tnx2*tnx2+tny2*tny2+tnz2*tnz2),
+ nx0 = tnx0/nn0, ny0 = tny0/nn0, nz0 = tnz0/nn0,
+ nx1 = tnx1/nn1, ny1 = tny1/nn1, nz1 = tnz1/nn1,
+ nx2 = tnx2/nn2, ny2 = tny2/nn2, nz2 = tnz2/nn2;
+ int i0 = -1, i1 = -1, i2 = -1;
+ cimglist_for(points,p) {
+ const float x = (float)points(p,0), y = (float)points(p,1), z = (float)points(p,2);
+ if (x==nx0 && y==ny0 && z==nz0) i0 = p;
+ if (x==nx1 && y==ny1 && z==nz1) i1 = p;
+ if (x==nx2 && y==ny2 && z==nz2) i2 = p;
+ }
+ if (i0<0) { points.insert(CImg<floatT>::vector(nx0,ny0,nz0)); i0 = points.size-1; }
+ if (i1<0) { points.insert(CImg<floatT>::vector(nx1,ny1,nz1)); i1 = points.size-1; }
+ if (i2<0) { points.insert(CImg<floatT>::vector(nx2,ny2,nz2)); i2 = points.size-1; }
+ primitives.remove(0);
+ primitives.insert(CImg<tf>::vector(p0,i0,i1)).
+ insert(CImg<tf>::vector((tf)i0,(tf)p1,(tf)i2)).
+ insert(CImg<tf>::vector((tf)i1,(tf)i2,(tf)p2)).
+ insert(CImg<tf>::vector((tf)i1,(tf)i0,(tf)i2));
+ }
+ }
+ return points.get_append('x')*=radius;
+ }
+
+ //! Return a 3D centered ellipsoid.
+ template<typename tf, typename t>
+ static CImg<floatT> ellipsoid3d(CImgList<tf>& primitives, const CImg<t>& tensor,
+ const unsigned int subdivisions=3) {
+ primitives.assign();
+ if (!subdivisions) return CImg<floatT>();
+ typedef typename cimg::superset<t,float>::type tfloat;
+ CImg<tfloat> S,V;
+ tensor.symmetric_eigen(S,V);
+ const tfloat l0 = S[0], l1 = S[1], l2 = S[2];
+ CImg<floatT> 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<typename tf, typename tc, typename te>
+ CImg<floatT> get_elevation3d(CImgList<tf>& primitives, CImgList<tc>& colors, const CImg<te>& 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<floatT> 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<tf>::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<tc>::vector((tc)r,(tc)g,(tc)b));
+ }
+ return points;
+ }
+
+ // Inner routine used by the Marching square algorithm.
+ template<typename t>
+ static int _marching_squares_indice(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& 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<typename tf, typename tfunc>
+ static CImg<floatT> marching_squares(CImgList<tf>& 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<floatT>();
+
+ primitives.assign();
+ CImgList<floatT> points;
+ CImg<intT> indices1(nx,1,1,2,-1), indices2(nx,1,1,2);
+ CImg<floatT> 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<nym1; ++yi, ++nyi, Y=nY, nY+=resy) {
+ X = x0; nX = X + resx;
+ indices2.fill(-1);
+ for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X=nX, nX+=resx) {
+
+ // Determine cube configuration
+ const float
+ val0 = values1(xi), val1 = values1(nxi),
+ val2 = values2(nxi) = (float)func(nX,nY),
+ val3 = values2(xi) = (float)func(X,nY);
+
+ const unsigned int configuration = (val0<isovalue?1:0) | (val1<isovalue?2:0) | (val2<isovalue?4:0) | (val3<isovalue?8:0),
+ edge = edges[configuration];
+
+ // Compute intersection points
+ if (edge) {
+ if ((edge&1) && indices1(xi,0)<0) {
+ const float Xi = X + (isovalue-val0)*resx/(val1-val0);
+ indices1(xi,0) = points.size;
+ points.insert(CImg<floatT>::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<floatT>::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<floatT>::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<floatT>::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<tf>::vector(i0,i1));
+ }
+ }
+ }
+ values1.swap(values2);
+ indices1.swap(indices2);
+ }
+ return points.get_append('x');
+ }
+
+ // Inner routine used by the Marching cube algorithm.
+ template<typename t>
+ static int _marching_cubes_indice(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& 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<typename tf, typename tfunc>
+ static CImg<floatT> marching_cubes(CImgList<tf>& primitives,
+ const tfunc& func, const float isovalue,
+ const float x0, const float y0, const float z0,
+ const float x1, const float y1, const float z1,
+ const 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<floatT>();
+
+ primitives.assign();
+ CImgList<floatT> points;
+ CImg<intT> indices1(nx,ny,1,3,-1), indices2(indices1);
+ CImg<floatT> 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<nzm1; ++zi, Z = nZ, nZ+=resz) {
+ Y = y0; nY = Y + resy;
+ indices2.fill(-1);
+ for (unsigned int yi = 0, nyi = 1; yi<nym1; ++yi, ++nyi, Y = nY, nY+=resy) {
+ X = x0; nX = X + resx;
+ for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X = nX, nX+=resx) {
+
+ // Determine cube configuration
+ const float
+ val0 = values1(xi,yi), val1 = values1(nxi,yi), val2 = values1(nxi,nyi), val3 = values1(xi,nyi),
+ val4 = values2(xi,yi) = (float)func(X,Y,nZ),
+ val5 = values2(nxi,yi) = (float)func(nX,Y,nZ),
+ val6 = values2(nxi,nyi) = (float)func(nX,nY,nZ),
+ val7 = values2(xi,nyi) = (float)func(X,nY,nZ);
+
+ const unsigned int configuration =
+ (val0<isovalue?1:0) | (val1<isovalue?2:0) | (val2<isovalue?4:0) | (val3<isovalue?8:0) |
+ (val4<isovalue?16:0) | (val5<isovalue?32:0) | (val6<isovalue?64:0) | (val7<isovalue?128:0),
+ edge = edges[configuration];
+
+ // Compute intersection points
+ if (edge) {
+ if ((edge&1) && indices1(xi,yi,0)<0) {
+ const float Xi = X + (isovalue-val0)*resx/(val1-val0);
+ indices1(xi,yi,0) = points.size;
+ points.insert(CImg<floatT>::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<floatT>::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<floatT>::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<floatT>::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<floatT>::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<floatT>::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<floatT>::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<floatT>::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<floatT>::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<floatT>::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<floatT>::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<floatT>::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<tf>::vector(i0,i1,i2));
+ else primitives.insert(CImg<tf>::vector(i0,i2,i1));
+ }
+ }
+ }
+ }
+ cimg::swap(values1,values2);
+ cimg::swap(indices1,indices2);
+ }
+ return points.get_append('x');
+ }
+
+ struct _marching_squares_func {
+ const CImg<T>& ref;
+ _marching_squares_func(const CImg<T>& 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<T>& ref;
+ _marching_cubes_func(const CImg<T>& 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<T>& ref;
+ _marching_squares_func_float(const CImg<T>& 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<T>& ref;
+ _marching_cubes_func_float(const CImg<T>& 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<typename tf>
+ CImg<floatT> get_isovalue3d(CImgList<tf>& 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<floatT> 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<T>& 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<Tfloat> get_translate_object3d(const float tx, const float ty=0, const float tz=0) const {
+ return CImg<Tfloat>(*this,false).translate_object3d(tx,ty,tz);
+ }
+
+ //! Translate a 3D object so that it becomes centered.
+ CImg<T>& translate_object3d() {
+ CImg<T> 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<Tfloat> get_translate_object3d() const {
+ return CImg<Tfloat>(*this,false).translate_object3d();
+ }
+
+ //! Resize a 3D object.
+ CImg<T>& resize_object3d(const float sx, const float sy=-100, const float sz=-100) {
+ CImg<T> 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 (xm<xM) { if (sx>0) xcoords*=sx/(xM-xm); else xcoords*=-sx/100; }
+ if (ym<yM) { if (sy>0) ycoords*=sy/(yM-ym); else ycoords*=-sy/100; }
+ if (zm<zM) { if (sz>0) zcoords*=sz/(zM-zm); else zcoords*=-sz/100; }
+ return *this;
+ }
+
+ CImg<Tfloat> get_resize_object3d(const float sx, const float sy=-100, const float sz=-100) const {
+ return CImg<Tfloat>(*this,false).resize_object3d(sx,sy,sz);
+ }
+
+ // Resize a 3D object so that its max dimension if one.
+ CImg<T> resize_object3d() const {
+ CImg<T> 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<Tfloat> get_resize_object3d() const {
+ return CImg<Tfloat>(*this,false).resize_object3d();
+ }
+
+ //! Append a 3D object to another one.
+ template<typename tf, typename tp, typename tff>
+ CImg<T>& append_object3d(CImgList<tf>& primitives, const CImg<tp>& obj_points, const CImgList<tff>& 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<primitives.size; ++i) {
+ CImg<tf> &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<Tuchar> default_LUT8() {
+ static CImg<Tuchar> 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<Tuchar> rainbow_LUT8() {
+ static CImg<Tuchar> palette;
+ if (!palette) {
+ CImg<Tint> 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<Tuchar> 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<Tuchar> palette(pal,1,256,1,3,false);
+ return palette;
+ }
+
+ //! Convert (R,G,B) color image to indexed color image.
+ template<typename t>
+ CImg<T>& RGBtoLUT(const CImg<t>& palette, const bool dithering=true, const bool indexing=false) {
+ return get_RGBtoLUT(palette,dithering,indexing).transfer_to(*this);
+ }
+
+ template<typename t>
+ CImg<t> get_RGBtoLUT(const CImg<t>& palette, const bool dithering=true, const bool indexing=false) const {
+ if (is_empty()) return CImg<t>();
+ 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<t> 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 (y<dimy()-1) {
+ const int ny = y + 1;
+ const T *pRs = ptr(0,ny,z,0), *pGs = ptr(0,ny,z,1), *pBs = ptr(0,ny,z,2);
+ float *ptrd = line2; cimg_forX(*this,x) { *(ptrd++) = (float)*(pRs++); *(ptrd++) = (float)*(pGs++); *(ptrd++) = (float)*(pBs++); }
+ }
+ float *ptr1 = line1, *ptr2 = line2;
+ cimg_forX(*this,x) {
+ float R = *(ptr1++), G = *(ptr1++), B = *(ptr1++);
+ R = R<0?0:(R>255?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<float>::max();
+ for (unsigned int off = 0; off<Npal; ++off) {
+ const t Rp = *(pRs++), Gp = *(pGs++), Bp = *(pBs++);
+ const float error = cimg::sqr((float)Rp-(float)R) + cimg::sqr((float)Gp-(float)G) + cimg::sqr((float)Bp-(float)B);
+ if (error<min) { min = error; best_index = off; Rbest = Rp; Gbest = Gp; Bbest = Bp; }
+ }
+ } else {
+ Rbest = (t)((unsigned char)R&0xe0); Gbest = (t)((unsigned char)G&0xe0); Bbest = (t)((unsigned char)B&0xc0);
+ best_index = (unsigned char)Rbest | ((unsigned char)Gbest>>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 (x<dimx()-1) { *(ptr1++)+= dR*7/16; *(ptr1++)+= dG*7/16; *(ptr1++)+= dB*7/16; ptr1-=3; }
+ if (y<dimy()-1) {
+ *(ptr2++)+= dR*5/16; *(ptr2++)+= dG*5/16; *ptr2+= dB*5/16; ptr2-=2;
+ if (x>0) { *(--ptr2)+= dB*3/16; *(--ptr2)+= dG*3/16; *(--ptr2)+= dR*3/16; ptr2+=3; }
+ if (x<dimx()-1) { ptr2+=3; *(ptr2++)+= dR/16; *(ptr2++)+= dG/16; *ptr2+= dB/16; ptr2-=5; }
+ }
+ }
+ ptr2+=3;
+ }
+ }
+ }
+ delete[] line1; delete[] line2;
+ return res;
+ }
+
+ //! Convert color pixels from (R,G,B) to match the default palette.
+ CImg<T>& RGBtoLUT(const bool dithering=true, const bool indexing=false) {
+ return get_RGBtoLUT(dithering,indexing).transfer_to(*this);
+ }
+
+ CImg<Tuchar> get_RGBtoLUT(const bool dithering=true, const bool indexing=false) const {
+ static const CImg<Tuchar> empty;
+ return get_RGBtoLUT(empty,dithering,indexing);
+ }
+
+ //! Convert an indexed image to a (R,G,B) image using the specified color palette.
+ CImg<T>& LUTtoRGB(const CImg<T>& palette) {
+ return get_LUTtoRGB(palette).transfer_to(*this);
+ }
+
+ template<typename t>
+ CImg<t> get_LUTtoRGB(const CImg<t>& palette) const {
+ if (is_empty()) return CImg<t>();
+ 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<t> pal = palette.data?palette:CImg<t>(default_LUT8());
+ CImg<t> 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<T>& LUTtoRGB() {
+ return get_LUTtoRGB().transfer_to(*this);
+ }
+
+ CImg<Tuchar> get_LUTtoRGB() const {
+ static const CImg<Tuchar> empty;
+ return get_LUTtoRGB(empty);
+ }
+
+ //! Convert color pixels from (R,G,B) to (H,S,V).
+ CImg<T>& 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<Tfloat> get_RGBtoHSV() const {
+ return CImg<Tfloat>(*this,false).RGBtoHSV();
+ }
+
+ //! Convert color pixels from (H,S,V) to (R,G,B).
+ CImg<T>& 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<Tuchar> get_HSVtoRGB() const {
+ return CImg<Tuchar>(*this,false).HSVtoRGB();
+ }
+
+ //! Convert color pixels from (R,G,B) to (H,S,L).
+ CImg<T>& 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<Tfloat> get_RGBtoHSL() const {
+ return CImg< Tfloat>(*this,false).RGBtoHSL();
+ }
+
+ //! Convert color pixels from (H,S,L) to (R,G,B).
+ CImg<T>& 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<Tuchar> get_HSLtoRGB() const {
+ return CImg<Tuchar>(*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<T>& 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<Tfloat> get_RGBtoHSI() const {
+ return CImg<Tfloat>(*this,false).RGBtoHSI();
+ }
+
+ //! Convert color pixels from (H,S,I) to (R,G,B).
+ CImg<T>& 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<Tfloat> get_HSItoRGB() const {
+ return CImg< Tuchar>(*this,false).HSItoRGB();
+ }
+
+ //! Convert color pixels from (R,G,B) to (Y,Cb,Cr)_8.
+ CImg<T>& 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<Tuchar> get_RGBtoYCbCr() const {
+ return CImg<Tuchar>(*this,false).RGBtoYCbCr();
+ }
+
+ //! Convert color pixels from (R,G,B) to (Y,Cb,Cr)_8.
+ CImg<T>& 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<Tuchar> get_YCbCrtoRGB() const {
+ return CImg<Tuchar>(*this,false).YCbCrtoRGB();
+ }
+
+ //! Convert color pixels from (R,G,B) to (Y,U,V).
+ CImg<T>& 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<Tfloat> get_RGBtoYUV() const {
+ return CImg<Tfloat>(*this,false).RGBtoYUV();
+ }
+
+ //! Convert color pixels from (Y,U,V) to (R,G,B).
+ CImg<T>& 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<Tuchar> get_YUVtoRGB() const {
+ return CImg< Tuchar>(*this,false).YUVtoRGB();
+ }
+
+ //! Convert color pixels from (R,G,B) to (C,M,Y).
+ CImg<T>& 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<Tfloat> get_RGBtoCMY() const {
+ return CImg<Tfloat>(*this,false).RGBtoCMY();
+ }
+
+ //! Convert (C,M,Y) pixels of a color image into the (R,G,B) color space.
+ CImg<T>& 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<Tuchar> get_CMYtoRGB() const {
+ return CImg<Tuchar>(*this,false).CMYtoRGB();
+ }
+
+ //! Convert color pixels from (C,M,Y) to (C,M,Y,K).
+ CImg<T>& CMYtoCMYK() {
+ return get_CMYtoCMYK().transfer_to(*this);
+ }
+
+ CImg<Tfloat> 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<Tfloat> 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<T>& CMYKtoCMY() {
+ return get_CMYKtoCMY().transfer_to(*this);
+ }
+
+ CImg<Tfloat> 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<Tfloat> 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<T>& 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<Tfloat> get_RGBtoXYZ() const {
+ return CImg<Tfloat>(*this,false).RGBtoXYZ();
+ }
+
+ //! Convert (X,Y,Z)_709 pixels of a color image into the (R,G,B) color space.
+ CImg<T>& 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<Tuchar> get_XYZtoRGB() const {
+ return CImg<Tuchar>(*this,false).XYZtoRGB();
+ }
+
+ //! Convert (X,Y,Z)_709 pixels of a color image into the (L*,a*,b*) color space.
+ CImg<T>& 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<Tfloat> get_XYZtoLab() const {
+ return CImg<Tfloat>(*this,false).XYZtoLab();
+ }
+
+ //! Convert (L,a,b) pixels of a color image into the (X,Y,Z) color space.
+ CImg<T>& 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<Tfloat> get_LabtoXYZ() const {
+ return CImg<Tfloat>(*this,false).LabtoXYZ();
+ }
+
+ //! Convert (X,Y,Z)_709 pixels of a color image into the (x,y,Y) color space.
+ CImg<T>& 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<Tfloat> get_XYZtoxyY() const {
+ return CImg<Tfloat>(*this,false).XYZtoxyY();
+ }
+
+ //! Convert (x,y,Y) pixels of a color image into the (X,Y,Z)_709 color space.
+ CImg<T>& 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<Tfloat> get_xyYtoXYZ() const {
+ return CImg<Tfloat>(*this,false).xyYtoXYZ();
+ }
+
+ //! Convert a (R,G,B) image to a (L,a,b) one.
+ CImg<T>& RGBtoLab() {
+ return RGBtoXYZ().XYZtoLab();
+ }
+
+ CImg<Tfloat> get_RGBtoLab() const {
+ return CImg<Tfloat>(*this,false).RGBtoLab();
+ }
+
+ //! Convert a (L,a,b) image to a (R,G,B) one.
+ CImg<T>& LabtoRGB() {
+ return LabtoXYZ().XYZtoRGB();
+ }
+
+ CImg<Tuchar> get_LabtoRGB() const {
+ return CImg<Tuchar>(*this,false).LabtoRGB();
+ }
+
+ //! Convert a (R,G,B) image to a (x,y,Y) one.
+ CImg<T>& RGBtoxyY() {
+ return RGBtoXYZ().XYZtoxyY();
+ }
+
+ CImg<Tfloat> get_RGBtoxyY() const {
+ return CImg<Tfloat>(*this,false).RGBtoxyY();
+ }
+
+ //! Convert a (x,y,Y) image to a (R,G,B) one.
+ CImg<T>& xyYtoRGB() {
+ return xyYtoXYZ().XYZtoRGB();
+ }
+
+ CImg<Tuchar> get_xyYtoRGB() const {
+ return CImg<Tuchar>(*this,false).xyYtoRGB();
+ }
+
+ //! Convert a (R,G,B) image to a (C,M,Y,K) one.
+ CImg<T>& RGBtoCMYK() {
+ return RGBtoCMY().CMYtoCMYK();
+ }
+
+ CImg<Tfloat> get_RGBtoCMYK() const {
+ return CImg<Tfloat>(*this,false).RGBtoCMYK();
+ }
+
+ //! Convert a (C,M,Y,K) image to a (R,G,B) one.
+ CImg<T>& CMYKtoRGB() {
+ return CMYKtoCMY().CMYtoRGB();
+ }
+
+ CImg<Tuchar> get_CMYKtoRGB() const {
+ return CImg<Tuchar>(*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<T>& RGBtoBayer() {
+ return get_RGBtoBayer().transfer_to(*this);
+ }
+
+ CImg<T> 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<T> 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<T>& BayertoRGB(const unsigned int interpolation_type=3) {
+ return get_BayertoRGB(interpolation_type).transfer_to(*this);
+ }
+
+ CImg<Tuchar> 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<Tuchar> 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<dimx()-1?x+1:x-1, _n1y = y<dimy()-1?y+1:y-1;
+ cimg_get3x3(*this,x,y,z,0,I);
+ if (y%2) {
+ if (x%2) {
+ const Tfloat alpha = cimg::sqr((Tfloat)Inc - Ipc), beta = cimg::sqr((Tfloat)Icn - Icp), cx = 1/(1+alpha), cy = 1/(1+beta);
+ *pG = (Tuchar)((cx*(Inc+Ipc) + cy*(Icn+Icp))/(2*(cx+cy)));
+ } else *pG = (Tuchar)Icc;
+ } else {
+ if (x%2) *pG = (Tuchar)Icc;
+ else {
+ const Tfloat alpha = cimg::sqr((Tfloat)Inc - Ipc), beta = cimg::sqr((Tfloat)Icn - Icp), cx = 1/(1+alpha), cy = 1/(1+beta);
+ *pG = (Tuchar)((cx*(Inc+Ipc) + cy*(Icn+Icp))/(2*(cx+cy)));
+ }
+ }
+ ++pG;
+ }
+ cimg_forXYZ(*this,x,y,z) {
+ const int _p1x = x?x-1:1, _p1y = y?y-1:1, _n1x = x<dimx()-1?x+1:x-1, _n1y = y<dimy()-1?y+1:y-1;
+ cimg_get3x3(*this,x,y,z,0,I);
+ cimg_get3x3(res,x,y,z,1,G);
+ if (y%2) {
+ if (x%2) *pB = (Tuchar)Icc;
+ else { *pR = (Tuchar)((Icn+Icp)/2); *pB = (Tuchar)((Inc+Ipc)/2); }
+ } else {
+ if (x%2) { *pR = (Tuchar)((Inc+Ipc)/2); *pB = (Tuchar)((Icn+Icp)/2); }
+ else *pR = (Tuchar)Icc;
+ }
+ ++pR; ++pB;
+ }
+ pR = res.ptr(0,0,0,0);
+ pG = res.ptr(0,0,0,1);
+ pB = res.ptr(0,0,0,2);
+ cimg_forXYZ(*this,x,y,z) {
+ const int _p1x = x?x-1:1, _p1y = y?y-1:1, _n1x = x<dimx()-1?x+1:x-1, _n1y = y<dimy()-1?y+1:y-1;
+ cimg_get3x3(res,x,y,z,0,R);
+ cimg_get3x3(res,x,y,z,1,G);
+ cimg_get3x3(res,x,y,z,2,B);
+ if (y%2) {
+ if (x%2) {
+ const float alpha = (float)cimg::sqr(Rnc-Rpc), beta = (float)cimg::sqr(Rcn-Rcp), cx = 1/(1+alpha), cy = 1/(1+beta);
+ *pR = (Tuchar)((cx*(Rnc+Rpc) + cy*(Rcn+Rcp))/(2*(cx+cy)));
+ }
+ } else {
+ if (!(x%2)) {
+ const float alpha = (float)cimg::sqr(Bnc-Bpc), beta = (float)cimg::sqr(Bcn-Bcp), cx = 1/(1+alpha), cy = 1/(1+beta);
+ *pB = (Tuchar)((cx*(Bnc+Bpc) + cy*(Bcn+Bcp))/(2*(cx+cy)));
+ }
+ }
+ ++pR; ++pG; ++pB;
+ }
+ } break;
+ case 2 : { // Linear interpolation
+ cimg_forXYZ(*this,x,y,z) {
+ const int _p1x = x?x-1:1, _p1y = y?y-1:1, _n1x = x<dimx()-1?x+1:x-1, _n1y = y<dimy()-1?y+1:y-1;
+ cimg_get3x3(*this,x,y,z,0,I);
+ if (y%2) {
+ if (x%2) { *pR = (Tuchar)((Ipp+Inn+Ipn+Inp)/4); *pG = (Tuchar)((Inc+Ipc+Icn+Icp)/4); *pB = (Tuchar)Icc; }
+ else { *pR = (Tuchar)((Icp+Icn)/2); *pG = (Tuchar)Icc; *pB = (Tuchar)((Inc+Ipc)/2); }
+ } else {
+ if (x%2) { *pR = (Tuchar)((Ipc+Inc)/2); *pG = (Tuchar)Icc; *pB = (Tuchar)((Icn+Icp)/2); }
+ else { *pR = (Tuchar)Icc; *pG = (Tuchar)((Inc+Ipc+Icn+Icp)/4); *pB = (Tuchar)((Ipp+Inn+Ipn+Inp)/4); }
+ }
+ ++pR; ++pG; ++pB;
+ }
+ } break;
+ case 1 : { // Nearest neighbor interpolation
+ cimg_forXYZ(*this,x,y,z) {
+ const int _p1x = x?x-1:1, _p1y = y?y-1:1, _n1x = x<dimx()-1?x+1:x-1, _n1y = y<dimy()-1?y+1:y-1;
+ cimg_get3x3(*this,x,y,z,0,I);
+ if (y%2) {
+ if (x%2) { *pR = (Tuchar)cimg::min(Ipp,Inn,Ipn,Inp); *pG = (Tuchar)cimg::min(Inc,Ipc,Icn,Icp); *pB = (Tuchar)Icc; }
+ else { *pR = (Tuchar)cimg::min(Icn,Icp); *pG = (Tuchar)Icc; *pB = (Tuchar)cimg::min(Inc,Ipc); }
+ } else {
+ if (x%2) { *pR = (Tuchar)cimg::min(Inc,Ipc); *pG = (Tuchar)Icc; *pB = (Tuchar)cimg::min(Icn,Icp); }
+ else { *pR = (Tuchar)Icc; *pG = (Tuchar)cimg::min(Inc,Ipc,Icn,Icp); *pB = (Tuchar)cimg::min(Ipp,Inn,Ipn,Inp); }
+ }
+ ++pR; ++pG; ++pB;
+ }
+ } break;
+ default : { // 0-filling interpolation
+ const T *ptrs = data;
+ res.fill(0);
+ cimg_forXYZ(*this,x,y,z) {
+ const T val = *(ptrs++);
+ if (y%2) { if (x%2) *pB = val; else *pG = val; } else { if (x%2) *pG = val; else *pR = val; }
+ ++pR; ++pG; ++pB;
+ }
+ }
+ }
+ return res;
+ }
+
+ //@}
+ //-------------------
+ //
+ //! \name Drawing
+ //@{
+ //-------------------
+
+ // The following _draw_scanline() routines are *non user-friendly functions*, used only for internal purpose.
+ // Pre-requisites : x0<x1, y-coordinate is valid, col is valid.
+ template<typename tc>
+ CImg<T>& _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<T>::max(),cimg::type<tc>::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<dimx()?x1:dimx()-1, dx = nx1 - nx0;
+ if (dx>=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<typename tc>
+ CImg<T>& _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<unsigned char> img(100,100,1,3,0);
+ const unsigned char color[] = { 255,128,64 };
+ img.draw_point(50,50,color);
+ \endcode
+ **/
+ template<typename tc>
+ CImg<T>& 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<typename tc>
+ CImg<T>& draw_point(const int x0, const int y0,
+ const CImg<tc>& color, const float opacity=1) {
+ return draw_point(x0,y0,color.data,opacity);
+ }
+
+ //! Draw a 3D colored point (voxel).
+ template<typename tc>
+ CImg<T>& 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<dimx() && y0<dimy() && z0<dimz()) {
+ const unsigned int whz = width*height*depth;
+ const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
+ T *ptrd = ptr(x0,y0,z0,0);
+ const tc *col = color;
+ 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; }
+ }
+ return *this;
+ }
+
+ //! Draw a 3D colored point (voxel).
+ template<typename tc>
+ CImg<T>& draw_point(const int x0, const int y0, const int z0,
+ const CImg<tc>& color, const float opacity=1) {
+ return draw_point(x0,y0,z0,color.data,opacity);
+ }
+
+ // Draw a cloud of colored point (internal).
+ template<typename t, typename tc>
+ CImg<T>& _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<W; ++i) {
+ const int x = (int)points(i,0), y = (int)points(i,1);
+ draw_point(x,y,color,opacity);
+ }
+ } break;
+ default : {
+ for (unsigned int i = 0; i<W; ++i) {
+ const int x = (int)points(i,0), y = (int)points(i,1), z = (int)points(i,2);
+ draw_point(x,y,z,color,opacity);
+ }
+ }
+ }
+ return *this;
+ }
+
+ //! Draw a cloud of colored points.
+ /**
+ \param points Coordinates of vertices, stored as a list of vectors.
+ \param color Pointer to \c dimv() consecutive values of type \c T, defining the drawing color.
+ \param opacity Drawing opacity (optional).
+ \note
+ - This function uses several call to the single CImg::draw_point() procedure,
+ depending on the vectors size in \p points.
+ \par Example:
+ \code
+ CImg<unsigned char> img(100,100,1,3,0);
+ const unsigned char color[] = { 255,128,64 };
+ CImgList<int> points;
+ points.insert(CImg<int>::vector(0,0)).
+ .insert(CImg<int>::vector(70,10)).
+ .insert(CImg<int>::vector(80,60)).
+ .insert(CImg<int>::vector(10,90));
+ img.draw_point(points,color);
+ \endcode
+ **/
+ template<typename t, typename tc>
+ CImg<T>& draw_point(const CImgList<t>& 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<typename t, typename tc>
+ CImg<T>& draw_point(const CImgList<t>& points,
+ const CImg<tc>& 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<typename t, typename tc>
+ CImg<T>& draw_point(const CImg<t>& 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<typename t, typename tc>
+ CImg<T>& draw_point(const CImg<t>& points,
+ const CImg<tc>& 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<unsigned char> img(100,100,1,3,0);
+ const unsigned char color[] = { 255,128,64 };
+ img.draw_line(40,40,80,70,color);
+ \endcode
+ **/
+ template<typename tc>
+ CImg<T>& draw_line(const int x0, const int y0,
+ const int x1, const int y1,
+ const tc *const color, const float opacity=1,
+ const unsigned int pattern=~0U, const bool init_hatch=true) {
+ 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<x1, ydir = y0<y1;
+ int
+ nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
+ &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
+ &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
+ &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
+ &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
+ if (xright<0 || xleft>=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<nx1?1:-1)*(steep?width:1),
+ offy = (ny0<ny1?1:-1)*(steep?1:width),
+ wh = width*height;
+ if (opacity>=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<typename tc>
+ CImg<T>& draw_line(const int x0, const int y0,
+ const int x1, const int y1,
+ const CImg<tc>& 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<typename tc>
+ CImg<T>& 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<x1, ydir = y0<y1;
+ int
+ nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
+ &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
+ &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
+ &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
+ &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
+ float
+ Z0 = 1/z0, Z1 = 1/z1, nz0 = Z0, nz1 = Z1, dz = Z1 - Z0,
+ &zleft = xdir?nz0:nz1,
+ &zright = xdir?nz1:nz0,
+ &zup = ydir?nz0:nz1,
+ &zdown = ydir?nz1:nz0;
+ if (xright<0 || xleft>=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 = (nx0<nx1?1:-1)*(steep?width:1),
+ offy = (ny0<ny1?1:-1)*(steep?1:width),
+ wh = width*height,
+ ndx = dx>0?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<typename tc>
+ CImg<T>& 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<tc>& 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<typename tc>
+ CImg<T>& draw_line(const int x0, const int y0, const int z0,
+ const int x1, const int y1, const int z1,
+ const tc *const color, const float opacity=1,
+ const unsigned int pattern=~0U, const bool init_hatch=true) {
+ 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<typename tc>
+ CImg<T>& draw_line(const int x0, const int y0, const int z0,
+ const int x1, const int y1, const int z1,
+ const CImg<tc>& 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<unsigned char> 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<typename tc>
+ CImg<T>& draw_line(const int x0, const int y0,
+ const int x1, const int y1,
+ const CImg<tc>& texture,
+ const int tx0, const int ty0,
+ const int tx1, const int ty1,
+ const float opacity=1,
+ const unsigned int pattern=~0U, const bool init_hatch=true) {
+ if (is_empty()) return *this;
+ if (!texture || texture.dim<dim)
+ throw CImgArgumentException("CImg<%s>::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<x1, ydir = y0<y1;
+ int
+ dtx = tx1-tx0, dty = ty1-ty0,
+ nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
+ tnx0 = tx0, tnx1 = tx1, tny0 = ty0, tny1 = ty1,
+ &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1, &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
+ &txleft = xdir?tnx0:tnx1, &tyleft = xdir?tny0:tny1, &txright = xdir?tnx1:tnx0, &tyright = xdir?tny1:tny0,
+ &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1, &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0,
+ &txup = ydir?tnx0:tnx1, &tyup = ydir?tny0:tny1, &txdown = ydir?tnx1:tnx0, &tydown = ydir?tny1:tny0;
+ if (xright<0 || xleft>=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 = (nx0<nx1?1:-1)*(steep?width:1),
+ offy = (ny0<ny1?1:-1)*(steep?1:width),
+ wh = width*height,
+ ndx = dx>0?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<typename tc>
+ CImg<T>& draw_line(const int x0, const int y0, const float z0,
+ const int x1, const int y1, const float z1,
+ const CImg<tc>& texture,
+ const int tx0, const int ty0,
+ const int tx1, const int ty1,
+ const float opacity=1,
+ const unsigned int pattern=~0U, const bool init_hatch=true) {
+ if (is_empty() && z0<=0 && z1<=0) return *this;
+ if (!texture || texture.dim<dim)
+ throw CImgArgumentException("CImg<%s>::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<x1, ydir = y0<y1;
+ int
+ nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
+ &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
+ &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
+ &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
+ &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
+ float
+ Tx0 = tx0/z0, Tx1 = tx1/z1,
+ Ty0 = ty0/z0, Ty1 = ty1/z1,
+ Z0 = 1/z0, Z1 = 1/z1,
+ dz = Z1 - Z0, dtx = Tx1 - Tx0, dty = Ty1 - Ty0,
+ tnx0 = Tx0, tnx1 = Tx1, tny0 = Ty0, tny1 = Ty1, nz0 = Z0, nz1 = Z1,
+ &zleft = xdir?nz0:nz1, &txleft = xdir?tnx0:tnx1, &tyleft = xdir?tny0:tny1,
+ &zright = xdir?nz1:nz0, &txright = xdir?tnx1:tnx0, &tyright = xdir?tny1:tny0,
+ &zup = ydir?nz0:nz1, &txup = ydir?tnx0:tnx1, &tyup = ydir?tny0:tny1,
+ &zdown = ydir?nz1:nz0, &txdown = ydir?tnx1:tnx0, &tydown = ydir?tny1:tny0;
+ if (xright<0 || xleft>=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 = (nx0<nx1?1:-1)*(steep?width:1),
+ offy = (ny0<ny1?1:-1)*(steep?1:width),
+ wh = width*height,
+ ndx = dx>0?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<typename tc>
+ CImg<T>& 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<tc>& texture,
+ const int tx0, const int ty0,
+ const int tx1, const int ty1,
+ const float opacity=1,
+ const unsigned int pattern=~0U, const bool init_hatch=true) {
+ if (!is_empty() && z0>0 && z1>0) {
+ if (!texture || texture.dim<dim)
+ throw CImgArgumentException("CImg<%s>::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<x1, ydir = y0<y1;
+ int
+ nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
+ &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
+ &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
+ &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
+ &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
+ float
+ Tx0 = tx0/z0, Tx1 = tx1/z1,
+ Ty0 = ty0/z0, Ty1 = ty1/z1,
+ Z0 = 1/z0, Z1 = 1/z1,
+ dz = Z1 - Z0, dtx = Tx1 - Tx0, dty = Ty1 - Ty0,
+ tnx0 = Tx0, tnx1 = Tx1, tny0 = Ty0, tny1 = Ty1, nz0 = Z0, nz1 = Z1,
+ &zleft = xdir?nz0:nz1, &txleft = xdir?tnx0:tnx1, &tyleft = xdir?tny0:tny1,
+ &zright = xdir?nz1:nz0, &txright = xdir?tnx1:tnx0, &tyright = xdir?tny1:tny0,
+ &zup = ydir?nz0:nz1, &txup = ydir?tnx0:tnx1, &tyup = ydir?tny0:tny1,
+ &zdown = ydir?nz1:nz0, &txdown = ydir?tnx1:tnx0, &tydown = ydir?tny1:tny0;
+ if (xright<0 || xleft>=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 = (nx0<nx1?1:-1)*(steep?width:1),
+ offy = (ny0<ny1?1:-1)*(steep?1:width),
+ wh = width*height,
+ ndx = dx>0?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<typename t, typename tc>
+ CImg<T>& _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<W; ++i) {
+ const int x = (int)points(i,0), y = (int)points(i,1);
+ draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch);
+ ninit_hatch = false;
+ ox = x; oy = y;
+ }
+ } break;
+ default : {
+ const int x0 = (int)points(0,0), y0 = (int)points(0,1), z0 = (int)points(0,2);
+ int ox = x0, oy = y0, oz = z0;
+ for (unsigned int i = 1; i<W; ++i) {
+ const int x = (int)points(i,0), y = (int)points(i,1), z = (int)points(i,2);
+ draw_line(ox,oy,oz,x,y,z,color,opacity,pattern,ninit_hatch);
+ ninit_hatch = false;
+ ox = x; oy = y; oz = z;
+ }
+ }
+ }
+ return *this;
+ }
+
+ //! Draw a set of consecutive colored lines in the instance image.
+ /**
+ \param points Coordinates of vertices, stored as a list of vectors.
+ \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 If set to true, init hatch motif.
+ \note
+ - This function uses several call to the single CImg::draw_line() procedure,
+ depending on the vectors size in \p points.
+ \par Example:
+ \code
+ CImg<unsigned char> img(100,100,1,3,0);
+ const unsigned char color[] = { 255,128,64 };
+ CImgList<int> points;
+ points.insert(CImg<int>::vector(0,0)).
+ .insert(CImg<int>::vector(70,10)).
+ .insert(CImg<int>::vector(80,60)).
+ .insert(CImg<int>::vector(10,90));
+ img.draw_line(points,color);
+ \endcode
+ **/
+ template<typename t, typename tc>
+ CImg<T>& draw_line(const CImgList<t>& 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<typename t, typename tc>
+ CImg<T>& draw_line(const CImgList<t>& points,
+ const CImg<tc>& 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<typename t, typename tc>
+ CImg<T>& draw_line(const CImg<t>& points,
+ const tc *const color, const float opacity=1,
+ const unsigned int pattern=~0U, const bool init_hatch=true) {
+ 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<typename t, typename tc>
+ CImg<T>& draw_line(const CImg<t>& points,
+ const CImg<tc>& 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<typename t, typename tc>
+ CImg<T>& _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; p<N; ++p) {
+ const int x = (int)points(p,0), y = (int)points(p,1);
+ if (x<xmin) xmin = x;
+ if (x>xmax) xmax = x;
+ if (y<ymin) ymin = y;
+ if (y>ymax) 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<intT> 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; p<N; ++p) {
+ const unsigned int np = (p!=N-1)?p+1:0, ap = (np!=N-1)?np+1:0;
+ const int
+ nx = (int)points(np,0), ny = (int)points(np,1), ay = (int)points(ap,1),
+ y0 = cy - nymin, y1 = ny - nymin;
+ if (y0!=y1) {
+ const int countermin = ((ny<ay && cy<ny) || (ny>ay && 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<py && ay<cy)) X(++X(0,y0),y0) = nx;
+ if (cy!=ay) { cp = np; cx = nx; cy = ny; }
+ }
+ }
+ for (int y = 0; y<(int)dy; ++y) {
+ tmp.assign(X.ptr(1,y),X(0,y),1,1,1,true).sort();
+ for (int i = 1; i<=X(0,y); ) {
+ const int xb = X(i++,y), xe = X(i++,y);
+ _draw_scanline(xb,xe,nymin+y,color,opacity);
+ }
+ }
+ return *this;
+ }
+
+ //! Draw a filled polygon in the instance image.
+ template<typename t, typename tc>
+ CImg<T>& draw_polygon(const CImgList<t>& 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<typename t, typename tc>
+ CImg<T>& draw_polygon(const CImgList<t>& points,
+ const CImg<tc>& color, const float opacity=1) {
+ return draw_polygon(points,color.data,opacity);
+ }
+
+ //! Draw a filled polygon in the instance image.
+ template<typename t, typename tc>
+ CImg<T>& draw_polygon(const CImg<t>& 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<typename t, typename tc>
+ CImg<T>& draw_polygon(const CImg<t>& points,
+ const CImg<tc>& color, const float opacity=1) {
+ return draw_polygon(points,color.data,opacity);
+ }
+
+ // Inner routine for drawing an outlined polygon with generic point coordinates.
+ template<typename t, typename tc>
+ CImg<T>& _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<W; ++i) {
+ const int x = (int)points(i,0), y = (int)points(i,1);
+ draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch);
+ ninit_hatch = false;
+ ox = x; oy = y;
+ }
+ draw_line(ox,oy,x0,y0,color,opacity,pattern,false);
+ } break;
+ default : {
+ const int x0 = (int)points(0,0), y0 = (int)points(0,1), z0 = (int)points(0,2);
+ int ox = x0, oy = y0, oz = z0;
+ for (unsigned int i = 1; i<W; ++i) {
+ const int x = (int)points(i,0), y = (int)points(i,1), z = (int)points(i,2);
+ draw_line(ox,oy,oz,x,y,z,color,opacity,pattern,ninit_hatch);
+ ninit_hatch = false;
+ ox = x; oy = y; oz = z;
+ }
+ draw_line(ox,oy,oz,x0,y0,z0,color,opacity,pattern,false);
+ }
+ }
+ return *this;
+ }
+
+ //! Draw a polygon outline.
+ template<typename t, typename tc>
+ CImg<T>& draw_polygon(const CImgList<t>& 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<typename t, typename tc>
+ CImg<T>& draw_polygon(const CImgList<t>& points,
+ const CImg<tc>& color, const float opacity,
+ const unsigned int pattern) {
+ return draw_polygon(points,color.data,opacity,pattern);
+ }
+
+ //! Draw a polygon outline.
+ template<typename t, typename tc>
+ CImg<T>& draw_polygon(const CImg<t>& 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<typename t, typename tc>
+ CImg<T>& draw_polygon(const CImg<t>& points,
+ const CImg<tc>& 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<unsigned char> 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<typename tc>
+ CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0,
+ const int x1, const int y1, const float u1, const float v1,
+ const tc *const color, const float opacity=1,
+ const float precision=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<tmax; t+=xprecision) {
+ const float
+ t2 = t*t,
+ t3 = t2*t;
+ const int
+ nx = (int)(ax*t3 + bx*t2 + u0*t + x0),
+ ny = (int)(ay*t3 + by*t2 + v0*t + y0);
+ draw_line(ox,oy,nx,ny,color,opacity,pattern,ninit_hatch);
+ ninit_hatch = false;
+ ox = nx; oy = ny;
+ }
+ return *this;
+ }
+
+ //! Draw a cubic spline curve in the instance image.
+ template<typename tc>
+ CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0,
+ const int x1, const int y1, const float u1, const float v1,
+ const CImg<tc>& 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<typename tc>
+ CImg<T>& draw_spline(const int x0, const int y0, const int z0, const float u0, const float v0, const float w0,
+ const int x1, const int y1, const int z1, const float u1, const float v1, const float w1,
+ const tc *const color, const float opacity=1,
+ const float precision=4, const unsigned int pattern=~0U,
+ const bool init_hatch=true) {
+ 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<tmax; t+=xprecision) {
+ const float
+ t2 = t*t,
+ t3 = t2*t;
+ const int
+ nx = (int)(ax*t3 + bx*t2 + u0*t + x0),
+ ny = (int)(ay*t3 + by*t2 + v0*t + y0),
+ nz = (int)(az*t3 + bz*t2 + w0*t + z0);
+ draw_line(ox,oy,oz,nx,ny,nz,color,opacity,pattern,ninit_hatch);
+ ninit_hatch = false;
+ ox = nx; oy = ny; oz = nz;
+ }
+ return *this;
+ }
+
+ //! Draw a cubic spline curve in the instance image (for volumetric images).
+ template<typename tc>
+ CImg<T>& draw_spline(const int x0, const int y0, const int z0, const float u0, const float v0, const float w0,
+ const int x1, const int y1, const int z1, const float u1, const float v1, const float w1,
+ const CImg<tc>& 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<typename t>
+ CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0,
+ const int x1, const int y1, const float u1, const float v1,
+ const CImg<t>& texture,
+ const int tx0, const int ty0, const int tx1, const int ty1,
+ const float opacity=1,
+ const float precision=4, const unsigned int pattern=~0U,
+ const bool init_hatch=true) {
+ if (is_empty()) return *this;
+ if (!texture || texture.dim<dim)
+ throw CImgArgumentException("CImg<%s>::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<tmax; t1+=xprecision) {
+ const float
+ t2 = t1*t1,
+ t3 = t2*t1;
+ const int
+ nx = (int)(ax*t3 + bx*t2 + u0*t1 + x0),
+ ny = (int)(ay*t3 + by*t2 + v0*t1 + y0),
+ ntx = tx0 + (int)((tx1-tx0)*t1/tmax),
+ nty = ty0 + (int)((ty1-ty0)*t1/tmax);
+ draw_line(ox,oy,nx,ny,texture,otx,oty,ntx,nty,opacity,pattern,ninit_hatch);
+ ninit_hatch = false;
+ ox = nx; oy = ny; otx = ntx; oty = nty;
+ }
+ return *this;
+ }
+
+ // Draw a set of connected spline curves in the instance image (internal).
+ template<typename tp, typename tt, typename tc>
+ CImg<T>& _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<W; ++i) {
+ const int x = (int)points(i,0), y = (int)points(i,1);
+ const float u = (float)tangents(i,0), v = (float)tangents(i,1);
+ draw_spline(ox,oy,ou,ov,x,y,u,v,color,precision,opacity,pattern,ninit_hatch);
+ ninit_hatch = false;
+ ox = x; oy = y; ou = u; ov = v;
+ }
+ if (close_set) draw_spline(ox,oy,ou,ov,x0,y0,u0,v0,color,precision,opacity,pattern,false);
+ } break;
+ default : {
+ const int x0 = (int)points(0,0), y0 = (int)points(0,1), z0 = (int)points(0,2);
+ const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1), w0 = (float)tangents(0,2);
+ int ox = x0, oy = y0, oz = z0;
+ float ou = u0, ov = v0, ow = w0;
+ for (unsigned int i = 1; i<W; ++i) {
+ const int x = (int)points(i,0), y = (int)points(i,1), z = (int)points(i,2);
+ const float u = (float)tangents(i,0), v = (float)tangents(i,1), w = (float)tangents(i,2);
+ draw_spline(ox,oy,oz,ou,ov,ow,x,y,z,u,v,w,color,opacity,pattern,ninit_hatch);
+ ninit_hatch = false;
+ ox = x; oy = y; oz = z; ou = u; ov = v; ow = w;
+ }
+ if (close_set) draw_spline(ox,oy,oz,ou,ov,ow,x0,y0,z0,u0,v0,w0,color,precision,opacity,pattern,false);
+ }
+ }
+ return *this;
+ }
+
+ // Draw a set of connected spline curves in the instance image (internal).
+ template<typename tp, typename tc>
+ CImg<T>& _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<Tfloat> 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<W; ++p) {
+ const unsigned int
+ p0 = close_set?(p+W-1)%W:(p?p-1:0),
+ p1 = close_set?(p+1)%W:(p+1<W?p+1:p);
+ const float
+ x = (float)points(p,0),
+ y = (float)points(p,1),
+ x0 = (float)points(p0,0),
+ y0 = (float)points(p0,1),
+ x1 = (float)points(p1,0),
+ y1 = (float)points(p1,1),
+ u0 = x - x0,
+ v0 = y - y0,
+ n0 = 1e-8f + (float)cimg_std::sqrt(u0*u0 + v0*v0),
+ u1 = x1 - x,
+ v1 = y1 - y,
+ n1 = 1e-8f + (float)cimg_std::sqrt(u1*u1 + v1*v1),
+ u = u0/n0 + u1/n1,
+ v = v0/n0 + v1/n1,
+ n = 1e-8f + (float)cimg_std::sqrt(u*u + v*v),
+ fact = 0.5f*(n0 + n1);
+ tangents(p,0) = (Tfloat)(fact*u/n);
+ tangents(p,1) = (Tfloat)(fact*v/n);
+ }
+ } break;
+ default : {
+ tangents.assign(W,H);
+ for (unsigned int p = 0; p<W; ++p) {
+ const unsigned int
+ p0 = close_set?(p+W-1)%W:(p?p-1:0),
+ p1 = close_set?(p+1)%W:(p+1<W?p+1:p);
+ const float
+ x = (float)points(p,0),
+ y = (float)points(p,1),
+ z = (float)points(p,2),
+ x0 = (float)points(p0,0),
+ y0 = (float)points(p0,1),
+ z0 = (float)points(p0,2),
+ x1 = (float)points(p1,0),
+ y1 = (float)points(p1,1),
+ z1 = (float)points(p1,2),
+ u0 = x - x0,
+ v0 = y - y0,
+ w0 = z - z0,
+ n0 = 1e-8f + (float)cimg_std::sqrt(u0*u0 + v0*v0 + w0*w0),
+ u1 = x1 - x,
+ v1 = y1 - y,
+ w1 = z1 - z,
+ n1 = 1e-8f + (float)cimg_std::sqrt(u1*u1 + v1*v1 + w1*w1),
+ u = u0/n0 + u1/n1,
+ v = v0/n0 + v1/n1,
+ w = w0/n0 + w1/n1,
+ n = 1e-8f + (float)cimg_std::sqrt(u*u + v*v + w*w),
+ fact = 0.5f*(n0 + n1);
+ tangents(p,0) = (Tfloat)(fact*u/n);
+ tangents(p,1) = (Tfloat)(fact*v/n);
+ tangents(p,2) = (Tfloat)(fact*w/n);
+ }
+ }
+ }
+ return _draw_spline(points,tangents,W,H,color,opacity,close_set,precision,pattern,init_hatch);
+ }
+
+ //! Draw a set of consecutive colored splines in the instance image.
+ template<typename tp, typename tt, typename tc>
+ CImg<T>& draw_spline(const CImgList<tp>& points, const CImgList<tt>& 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<typename tp, typename tt, typename tc>
+ CImg<T>& draw_spline(const CImgList<tp>& points, const CImgList<tt>& tangents,
+ const CImg<tc>& 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<typename tp, typename tt, typename tc>
+ CImg<T>& draw_spline(const CImg<tp>& points, const CImg<tt>& 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<typename tp, typename tt, typename tc>
+ CImg<T>& draw_spline(const CImg<tp>& points, const CImg<tt>& tangents,
+ const CImg<tc>& 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<typename t, typename tc>
+ CImg<T>& draw_spline(const CImgList<t>& 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<H) H = s; }
+ return _draw_spline(points,color,opacity,close_set,precision,pattern,init_hatch,points.size,H);
+ }
+
+ //! Draw a set of consecutive colored splines in the instance image.
+ template<typename t, typename tc>
+ CImg<T>& draw_spline(const CImgList<t>& points,
+ CImg<tc>& 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<typename t, typename tc>
+ CImg<T>& draw_spline(const CImg<t>& 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<typename t, typename tc>
+ CImg<T>& draw_spline(const CImg<t>& points,
+ const CImg<tc>& 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<typename tc>
+ CImg<T>& draw_arrow(const int x0, const int y0,
+ const int x1, const int y1,
+ const tc *const color, const float opacity=1,
+ const float angle=30, const float length=-10,
+ const unsigned int pattern=~0U) {
+ 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<typename tc>
+ CImg<T>& draw_arrow(const int x0, const int y0,
+ const int x1, const int y1,
+ const CImg<tc>& 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<typename t>
+ CImg<T>& draw_image(const int x0, const int y0, const int z0, const int v0,
+ const CImg<t>& 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<lV; ++v) {
+ for (int z = 0; z<lZ; ++z) {
+ for (int y = 0; y<lY; ++y) {
+ if (opacity>=1) for (int x = 0; x<lX; ++x) *(ptrd++) = (T)*(ptrs++);
+ else for (int x = 0; x<lX; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; }
+ ptrd+=offX; ptrs+=soffX;
+ }
+ ptrd+=offY; ptrs+=soffY;
+ }
+ ptrd+=offZ; ptrs+=soffZ;
+ }
+ }
+ return *this;
+ }
+
+#ifndef cimg_use_visualcpp6
+ // Otimized version (internal).
+ CImg<T>& draw_image(const int x0, const int y0, const int z0, const int v0,
+ const CImg<T>& 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<lV; ++v) {
+ for (int z = 0; z<lZ; ++z) {
+ if (opacity>=1) for (int y = 0; y<lY; ++y) { cimg_std::memcpy(ptrd,ptrs,slX); ptrd+=width; ptrs+=sprite.width; }
+ else for (int y = 0; y<lY; ++y) {
+ for (int x = 0; x<lX; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; }
+ ptrd+=offX; ptrs+=soffX;
+ }
+ ptrd+=offY; ptrs+=soffY;
+ }
+ ptrd+=offZ; ptrs+=soffZ;
+ }
+ }
+ return *this;
+ }
+#endif
+
+ //! Draw an image.
+ template<typename t>
+ CImg<T>& draw_image(const int x0, const int y0, const int z0,
+ const CImg<t>& sprite, const float opacity=1) {
+ return draw_image(x0,y0,z0,0,sprite,opacity);
+ }
+
+ //! Draw an image.
+ template<typename t>
+ CImg<T>& draw_image(const int x0, const int y0,
+ const CImg<t>& sprite, const float opacity=1) {
+ return draw_image(x0,y0,0,sprite,opacity);
+ }
+
+ //! Draw an image.
+ template<typename t>
+ CImg<T>& draw_image(const int x0,
+ const CImg<t>& sprite, const float opacity=1) {
+ return draw_image(x0,0,sprite,opacity);
+ }
+
+ //! Draw an image.
+ template<typename t>
+ CImg<T>& draw_image(const CImg<t>& 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<typename ti, typename tm>
+ CImg<T>& draw_image(const int x0, const int y0, const int z0, const int v0,
+ const CImg<ti>& sprite, const CImg<tm>& 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<lV; ++v) {
+ ptrm = mask.data + (ptrm - mask.data)%ssize;
+ for (int z = 0; z<lZ; ++z) {
+ for (int y = 0; y<lY; ++y) {
+ for (int x=0; x<lX; ++x) {
+ const float mopacity = (float)(*(ptrm++)*opacity),
+ nopacity = cimg::abs(mopacity), copacity = mask_valmax - cimg::max(mopacity,0);
+ *ptrd = (T)((nopacity*(*(ptrs++)) + *ptrd*copacity)/mask_valmax);
+ ++ptrd;
+ }
+ ptrd+=offX; ptrs+=soffX; ptrm+=soffX;
+ }
+ ptrd+=offY; ptrs+=soffY; ptrm+=soffY;
+ }
+ ptrd+=offZ; ptrs+=soffZ; ptrm+=soffZ;
+ }
+ }
+ return *this;
+ }
+
+ //! Draw an image.
+ template<typename ti, typename tm>
+ CImg<T>& draw_image(const int x0, const int y0, const int z0,
+ const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
+ const float mask_valmax=1) {
+ return draw_image(x0,y0,z0,0,sprite,mask,opacity,mask_valmax);
+ }
+
+ //! Draw an image.
+ template<typename ti, typename tm>
+ CImg<T>& draw_image(const int x0, const int y0,
+ const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
+ const float mask_valmax=1) {
+ return draw_image(x0,y0,0,sprite,mask,opacity,mask_valmax);
+ }
+
+ //! Draw an image.
+ template<typename ti, typename tm>
+ CImg<T>& draw_image(const int x0,
+ const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
+ const float mask_valmax=1) {
+ return draw_image(x0,0,sprite,mask,opacity,mask_valmax);
+ }
+
+ //! Draw an image.
+ template<typename ti, typename tm>
+ CImg<T>& draw_image(const CImg<ti>& sprite, const CImg<tm>& 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<T>& 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<x1), by = (y0<y1), bz = (z0<z1), bv = (v0<v1);
+ const int
+ nx0 = bx?x0:x1, nx1 = bx?x1:x0,
+ ny0 = by?y0:y1, ny1 = by?y1:y0,
+ nz0 = bz?z0:z1, nz1 = bz?z1:z0,
+ nv0 = bv?v0:v1, nv1 = bv?v1:v0;
+ const int
+ lX = (1 + nx1 - nx0) + (nx1>=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<lV; ++v) {
+ for (int z = 0; z<lZ; ++z) {
+ for (int y = 0; y<lY; ++y) {
+ if (opacity>=1) {
+ if (sizeof(T)!=1) { for (int x = 0; x<lX; ++x) *(ptrd++) = val; ptrd+=offX; }
+ else { cimg_std::memset(ptrd,(int)val,lX); ptrd+=width; }
+ } else { for (int x = 0; x<lX; ++x) { *ptrd = (T)(nopacity*val + *ptrd*copacity); ++ptrd; } ptrd+=offX; }
+ }
+ ptrd+=offY;
+ }
+ ptrd+=offZ;
+ }
+ 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).
+ /**
+ \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 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 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<typename tc>
+ CImg<T>& draw_rectangle(const int x0, const int y0, const int z0,
+ const int x1, const int y1, const int z1,
+ const tc *const color, const float opacity=1) {
+ 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<typename tc>
+ CImg<T>& draw_rectangle(const int x0, const int y0, const int z0,
+ const int x1, const int y1, const int z1,
+ const CImg<tc>& 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<typename tc>
+ CImg<T>& draw_rectangle(const int x0, const int y0, const int z0,
+ const int x1, const int y1, const int z1,
+ const tc *const color, const float opacity,
+ const unsigned int pattern) {
+ 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<typename tc>
+ CImg<T>& draw_rectangle(const int x0, const int y0, const int z0,
+ const int x1, const int y1, const int z1,
+ const CImg<tc>& 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<typename tc>
+ CImg<T>& 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<typename tc>
+ CImg<T>& draw_rectangle(const int x0, const int y0,
+ const int x1, const int y1,
+ const CImg<tc>& color, const float opacity=1) {
+ return draw_rectangle(x0,y0,x1,y1,color.data,opacity);
+ }
+
+ //! Draw a 2D outlined colored rectangle.
+ template<typename tc>
+ CImg<T>& draw_rectangle(const int x0, const int y0,
+ const int x1, const int y1,
+ const tc *const color, const float opacity,
+ const unsigned int pattern) {
+ 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<x1), by = (y0<y1);
+ const int
+ nx0 = bx?x0:x1, nx1 = bx?x1:x0,
+ ny0 = by?y0:y1, ny1 = by?y1:y0;
+ if (ny1==ny0+1) return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true).
+ draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false);
+ return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true).
+ draw_line(nx1,ny0+1,nx1,ny1-1,color,opacity,pattern,false).
+ draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false).
+ draw_line(nx0,ny1-1,nx0,ny0+1,color,opacity,pattern,false);
+ }
+
+ //! Draw a 2D outlined colored rectangle.
+ template<typename tc>
+ CImg<T>& draw_rectangle(const int x0, const int y0,
+ const int x1, const int y1,
+ const CImg<tc>& 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<typename tc>
+ CImg<T>& _draw_triangle(const int x0, const int y0,
+ const int x1, const int y1,
+ const int x2, const int y2,
+ const tc *const color, const float opacity,
+ const 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<dimy() && ny2>=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<typename tc>
+ CImg<T>& draw_triangle(const int x0, const int y0,
+ const int x1, const int y1,
+ const int x2, const int y2,
+ const tc *const color, const float opacity=1) {
+ 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<typename tc>
+ CImg<T>& draw_triangle(const int x0, const int y0,
+ const int x1, const int y1,
+ const int x2, const int y2,
+ const CImg<tc>& color, const float opacity=1) {
+ return draw_triangle(x0,y0,x1,y1,x2,y2,color.data,opacity);
+ }
+
+ //! Draw a 2D outlined colored triangle.
+ template<typename tc>
+ CImg<T>& draw_triangle(const int x0, const int y0,
+ const int x1, const int y1,
+ const int x2, const int y2,
+ const tc *const color, const float opacity,
+ const unsigned int pattern) {
+ 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<typename tc>
+ CImg<T>& draw_triangle(const int x0, const int y0,
+ const int x1, const int y1,
+ const int x2, const int y2,
+ const CImg<tc>& 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<typename tc>
+ CImg<T>& 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<T>::max(),cimg::type<tc>::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<xleft) cimg::swap(xleft,xright,zleft,zright);
+ const int dx = xright - xleft;
+ const float pentez = (zright - zleft)/dx;
+ if (xleft<0 && dx) zleft-=xleft*(zright - zleft)/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) {
+ 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<typename tc>
+ CImg<T>& 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<tc>& 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<typename tc>
+ CImg<T>& draw_triangle(const int x0, const int y0,
+ const int x1, const int y1,
+ const int x2, const int y2,
+ const tc *const color,
+ const float brightness0,
+ const float brightness1,
+ const float brightness2,
+ const float opacity=1) {
+ 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<T>::max(),cimg::type<tc>::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 (xright<xleft) cimg::swap(xleft,xright,cleft,cright);
+ const int
+ dx = xright - xleft,
+ dc = cright>cleft?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<typename tc>
+ CImg<T>& draw_triangle(const int x0, const int y0,
+ const int x1, const int y1,
+ const int x2, const int y2,
+ const CImg<tc>& 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<typename tc>
+ CImg<T>& 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<T>::max(),cimg::type<tc>::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 (xright<xleft) cimg::swap(xleft,xright,zleft,zright,cleft,cright);
+ const int
+ dx = xright - xleft,
+ dc = cright>cleft?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<typename tc>
+ CImg<T>& 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<tc>& 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<typename tc>
+ CImg<T>& draw_triangle(const int x0, const int y0,
+ const int x1, const int y1,
+ const int x2, const int y2,
+ const CImg<tc>& texture,
+ const int tx0, const int ty0,
+ const int tx1, const int ty1,
+ const int tx2, const int ty2,
+ const float opacity=1,
+ const float brightness=1) {
+ if (is_empty()) return *this;
+ if (!texture || texture.dim<dim)
+ throw CImgArgumentException("CImg<%s>::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<T>::max(),cimg::type<tc>::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 (xright<xleft) cimg::swap(xleft,xright,txleft,txright,tyleft,tyright);
+ const int
+ dx = xright - xleft,
+ dtx = txright>txleft?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<typename tc>
+ CImg<T>& draw_triangle(const int x0, const int y0, const float z0,
+ const int x1, const int y1, const float z1,
+ const int x2, const int y2, const float z2,
+ const CImg<tc>& texture,
+ const int tx0, const int ty0,
+ const int tx1, const int ty1,
+ const int tx2, const int ty2,
+ const float opacity=1,
+ const float brightness=1) {
+ if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
+ if (!texture || texture.dim<dim)
+ throw CImgArgumentException("CImg<%s>::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<T>::max(),cimg::type<tc>::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<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright);
+ const int dx = xright - xleft;
+ const float
+ pentez = (zright - zleft)/dx,
+ pentetx = (txright - txleft)/dx,
+ pentety = (tyright - tyleft)/dx;
+ if (xleft<0 && 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) {
+ 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<typename tc>
+ CImg<T>& 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<tc>& texture,
+ const int tx0, const int ty0,
+ const int tx1, const int ty1,
+ const int tx2, const int ty2,
+ const float opacity=1,
+ const float brightness=1) {
+ if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
+ if (!texture || texture.dim<dim)
+ throw CImgArgumentException("CImg<%s>::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<T>::max(),cimg::type<tc>::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<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright);
+ const int dx = xright - xleft;
+ const float
+ pentez = (zright - zleft)/dx,
+ pentetx = (txright - txleft)/dx,
+ pentety = (tyright - tyleft)/dx;
+ if (xleft<0 && 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);
+ 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<typename tc, typename tl>
+ CImg<T>& draw_triangle(const int x0, const int y0,
+ const int x1, const int y1,
+ const int x2, const int y2,
+ const tc *const color,
+ const CImg<tl>& light,
+ const int lx0, const int ly0,
+ const int lx1, const int ly1,
+ const int lx2, const int ly2,
+ const float opacity=1) {
+ 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<T>::max(),cimg::type<tc>::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 (xright<xleft) cimg::swap(xleft,xright,lxleft,lxright,lyleft,lyright);
+ const int
+ dx = xright - xleft,
+ dlx = lxright>lxleft?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<typename tc, typename tl>
+ CImg<T>& draw_triangle(const int x0, const int y0,
+ const int x1, const int y1,
+ const int x2, const int y2,
+ const CImg<tc>& color,
+ const CImg<tl>& light,
+ const int lx0, const int ly0,
+ const int lx1, const int ly1,
+ const int lx2, const int ly2,
+ const float opacity=1) {
+ 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<typename tc, typename tl>
+ CImg<T>& 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<tl>& 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<T>::max(),cimg::type<tc>::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 (xright<xleft) cimg::swap(xleft,xright,zleft,zright,lxleft,lxright,lyleft,lyright);
+ const int
+ dx = xright - xleft,
+ dlx = lxright>lxleft?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<typename tc, typename tl>
+ CImg<T>& 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<tc>& color,
+ const CImg<tl>& light,
+ const int lx0, const int ly0,
+ const int lx1, const int ly1,
+ const int lx2, const int ly2,
+ const float opacity=1) {
+ 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<typename tc>
+ CImg<T>& draw_triangle(const int x0, const int y0,
+ const int x1, const int y1,
+ const int x2, const int y2,
+ const CImg<tc>& texture,
+ const int tx0, const int ty0,
+ const int tx1, const int ty1,
+ const int tx2, const int ty2,
+ const float brightness0,
+ const float brightness1,
+ const float brightness2,
+ const float opacity=1) {
+ if (is_empty()) return *this;
+ if (!texture || texture.dim<dim)
+ throw CImgArgumentException("CImg<%s>::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<T>::max(),cimg::type<tc>::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 (xright<xleft) cimg::swap(xleft,xright,cleft,cright,txleft,txright,tyleft,tyright);
+ const int
+ dx = xright - xleft,
+ dc = cright>cleft?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<typename tc>
+ CImg<T>& draw_triangle(const int x0, const int y0, const float z0,
+ const int x1, const int y1, const float z1,
+ const int x2, const int y2, const float z2,
+ const CImg<tc>& texture,
+ const int tx0, const int ty0,
+ const int tx1, const int ty1,
+ const int tx2, const int ty2,
+ const float brightness0,
+ const float brightness1,
+ const float brightness2,
+ const float opacity=1) {
+ if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
+ if (!texture || texture.dim<dim)
+ throw CImgArgumentException("CImg<%s>::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<T>::max(),cimg::type<tc>::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 (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,cleft,cright);
+ const int
+ dx = xright - xleft,
+ dc = cright>cleft?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<typename tc>
+ CImg<T>& 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<tc>& texture,
+ const int tx0, const int ty0,
+ const int tx1, const int ty1,
+ const int tx2, const int ty2,
+ const float brightness0,
+ const float brightness1,
+ const float brightness2,
+ const float opacity=1) {
+ if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
+ if (!texture || texture.dim<dim)
+ throw CImgArgumentException("CImg<%s>::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<T>::max(),cimg::type<tc>::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 (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,cleft,cright);
+ const int
+ dx = xright - xleft,
+ dc = cright>cleft?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<typename tc, typename tl>
+ CImg<T>& draw_triangle(const int x0, const int y0,
+ const int x1, const int y1,
+ const int x2, const int y2,
+ const CImg<tc>& texture,
+ const int tx0, const int ty0,
+ const int tx1, const int ty1,
+ const int tx2, const int ty2,
+ const CImg<tl>& light,
+ const int lx0, const int ly0,
+ const int lx1, const int ly1,
+ const int lx2, const int ly2,
+ const float opacity=1) {
+ if (is_empty()) return *this;
+ if (!texture || texture.dim<dim)
+ throw CImgArgumentException("CImg<%s>::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<T>::max(),cimg::type<tc>::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 (xright<xleft) cimg::swap(xleft,xright,lxleft,lxright,lyleft,lyright,txleft,txright,tyleft,tyright);
+ const int
+ dx = xright - xleft,
+ dlx = lxright>lxleft?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<typename tc, typename tl>
+ CImg<T>& draw_triangle(const int x0, const int y0, const float z0,
+ const int x1, const int y1, const float z1,
+ const int x2, const int y2, const float z2,
+ const CImg<tc>& texture,
+ const int tx0, const int ty0,
+ const int tx1, const int ty1,
+ const int tx2, const int ty2,
+ const CImg<tl>& light,
+ const int lx0, const int ly0,
+ const int lx1, const int ly1,
+ const int lx2, const int ly2,
+ const float opacity=1) {
+ if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
+ if (!texture || texture.dim<dim)
+ throw CImgArgumentException("CImg<%s>::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<T>::max(),cimg::type<tc>::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 (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,lxleft,lxright,lyleft,lyright);
+ const int
+ dx = xright - xleft,
+ dlx = lxright>lxleft?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<typename tc, typename tl>
+ CImg<T>& 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<tc>& texture,
+ const int tx0, const int ty0,
+ const int tx1, const int ty1,
+ const int tx2, const int ty2,
+ const CImg<tl>& light,
+ const int lx0, const int ly0,
+ const int lx1, const int ly1,
+ const int lx2, const int ly2,
+ const float opacity=1) {
+ if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
+ if (!texture || texture.dim<dim)
+ throw CImgArgumentException("CImg<%s>::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<T>::max(),cimg::type<tc>::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 (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,lxleft,lxright,lyleft,lyright);
+ const int
+ dx = xright - xleft,
+ dlx = lxright>lxleft?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<typename tc>
+ CImg<T>& _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 + (y<y0?0.5f:-0.5f),
+ delta = b*b*Y*Y-a*(c*Y*Y-rmax*rmax),
+ sdelta = delta>0?(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<oxmin) _draw_scanline(xmin,oxmin-1,y,color,opacity);
+ else _draw_scanline(oxmin+(oxmin==xmin?0:1),xmin,y,color,opacity);
+ if (xmax<oxmax) _draw_scanline(xmax,oxmax-1,y,color,opacity);
+ else _draw_scanline(oxmax+(oxmax==xmax?0:1),xmax,y,color,opacity);
+ if (y==tymax) _draw_scanline(xmin+1,xmax-1,y,color,opacity);
+ }
+ }
+ oxmin = xmin; oxmax = xmax;
+ }
+ return *this;
+ }
+
+ //! Draw a filled 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 opacity = opacity of the drawing.
+ **/
+ template<typename tc>
+ CImg<T>& 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<typename tc>
+ CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float ru, const float rv,
+ const CImg<tc>& 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<typename t, typename tc>
+ CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
+ const tc *const color, const float opacity=1) {
+ CImgList<t> eig = tensor.get_symmetric_eigen();
+ const CImg<t> &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<typename t, typename tc>
+ CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
+ const CImg<tc>& 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<typename tc>
+ CImg<T>& 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<typename tc>
+ CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float ru, const float rv,
+ const CImg<tc>& 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<typename t, typename tc>
+ CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
+ const tc *const color, const float opacity,
+ const unsigned int pattern) {
+ CImgList<t> eig = tensor.get_symmetric_eigen();
+ const CImg<t> &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<typename t, typename tc>
+ CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
+ const CImg<tc>& 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<typename tc>
+ CImg<T>& 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<dimy()) _draw_scanline(x0-radius,x0+radius,y0,color,opacity);
+ for (int f=1-radius, ddFx=0, ddFy=-(radius<<1), x=0, y=radius; x<y; ) {
+ if (f>=0) {
+ const int x1 = x0-x, x2 = x0+x, y1 = y0-y, y2 = y0+y;
+ if (y1>=0 && y1<dimy()) _draw_scanline(x1,x2,y1,color,opacity);
+ if (y2>=0 && y2<dimy()) _draw_scanline(x1,x2,y2,color,opacity);
+ f+=(ddFy+=2); --y;
+ }
+ const bool no_diag = y!=(x++);
+ ++(f+=(ddFx+=2));
+ const int x1 = x0-y, x2 = x0+y, y1 = y0-x, y2 = y0+x;
+ if (no_diag) {
+ if (y1>=0 && y1<dimy()) _draw_scanline(x1,x2,y1,color,opacity);
+ if (y2>=0 && y2<dimy()) _draw_scanline(x1,x2,y2,color,opacity);
+ }
+ }
+ }
+ return *this;
+ }
+
+ //! Draw a filled circle.
+ template<typename tc>
+ CImg<T>& draw_circle(const int x0, const int y0, int radius,
+ const CImg<tc>& 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<typename tc>
+ CImg<T>& 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<y; ) {
+ if (f>=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<typename tc>
+ CImg<T>& draw_circle(const int x0, const int y0, int radius, const CImg<tc>& color,
+ const float opacity,
+ const unsigned int pattern) {
+ return draw_circle(x0,y0,radius,color.data,opacity,pattern);
+ }
+
+ // Draw a text (internal).
+ template<typename tc1, typename tc2, typename t>
+ CImg<T>& _draw_text(const int x0, const int y0, const char *const text,
+ const tc1 *const foreground_color, const tc2 *const background_color,
+ const float opacity, const CImgList<t>& font) {
+ 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; i<text_length; ++i) {
+ c = text[i];
+ switch (c) {
+ case '\n' : y+=font[' '].height; if (x>w) w = x; x = 0; break;
+ case '\t' : x+=4*font[' '].width; break;
+ default : if (c<font.size) x+=font[c].width;
+ }
+ }
+ if (x!=0 || c=='\n') {
+ if (x>w) 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<T> letter;
+ for (int i = 0; i<text_length; ++i) {
+ const unsigned char c = text[i];
+ switch (c) {
+ case '\n' : y+=font[' '].height; x = x0; break;
+ case '\t' : x+=4*font[' '].width; break;
+ default : if (c<font.size) {
+ letter = font[c];
+ const CImg<T>& mask = (c+256)<(int)font.size?font[c+256]:font[c];
+ if (foreground_color) for (unsigned int p = 0; p<letter.width*letter.height; ++p)
+ if (mask(p)) cimg_forV(*this,k) letter(p,0,0,k) = (T)(letter(p,0,0,k)*foreground_color[k]);
+ if (background_color) for (unsigned int p = 0; p<letter.width*letter.height; ++p)
+ if (!mask(p)) cimg_forV(*this,k) letter(p,0,0,k) = (T)background_color[k];
+ if (!background_color && font.size>=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<typename tc1, typename tc2, typename t>
+ CImg<T>& draw_text(const int x0, const int y0, const char *const text,
+ const tc1 *const foreground_color, const tc2 *const background_color,
+ const float opacity, const CImgList<t>& font, ...) {
+ 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<typename tc1, typename tc2, typename t>
+ CImg<T>& draw_text(const int x0, const int y0, const char *const text,
+ const CImg<tc1>& foreground_color, const CImg<tc2>& background_color,
+ const float opacity, const CImgList<t>& 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<typename tc, typename t>
+ CImg<T>& 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<t>& 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<typename tc, typename t>
+ CImg<T>& 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<t>& 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<typename tc1, typename tc2>
+ CImg<T>& draw_text(const int x0, const int y0, const char *const text,
+ const tc1 *const foreground_color, const tc2 *const background_color,
+ const float opacity=1, const unsigned int font_size=11, ...) {
+ static CImgList<T> font;
+ static unsigned int fsize = 0;
+ if (fsize!=font_size) { font = CImgList<T>::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<typename tc1, typename tc2>
+ CImg<T>& draw_text(const int x0, const int y0, const char *const text,
+ const CImg<tc1>& foreground_color, const CImg<tc2>& background_color,
+ const float opacity=1, const unsigned int font_size=11, ...) {
+ static CImgList<T> font;
+ static unsigned int fsize = 0;
+ if (fsize!=font_size) { font = CImgList<T>::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<typename tc>
+ CImg<T>& draw_text(const int x0, const int y0, const char *const text,
+ const tc *const foreground_color, const int background_color=0,
+ const float opacity=1, const unsigned int font_size=11, ...) {
+ static CImgList<T> font;
+ static unsigned int fsize = 0;
+ if (fsize!=font_size) { font = CImgList<T>::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<typename tc>
+ CImg<T>& 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<T> font;
+ static unsigned int fsize = 0;
+ if (fsize!=font_size) { font = CImgList<T>::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<typename t1, typename t2>
+ CImg<T>& draw_quiver(const CImg<t1>& flow,
+ const t2 *const color, const float opacity=1,
+ const unsigned int sampling=25, const float factor=-20,
+ const int quiver_type=0, const unsigned int pattern=~0U) {
+ return draw_quiver(flow,CImg<t2>(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<typename t1, typename t2>
+ CImg<T>& draw_quiver(const CImg<t1>& flow,
+ const CImg<t2>& color, const float opacity=1,
+ const unsigned int sampling=25, const float factor=-20,
+ const 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<height; y+=sampling)
+ for (unsigned int x=sampling/2; x<width; x+=sampling) {
+ const unsigned int X = x*flow.width/width, Y = y*flow.height/height;
+ float u = (float)flow(X,Y,0,0)*fact/vmax, v = (float)flow(X,Y,0,1)*fact/vmax;
+ if (!quiver_type) {
+ const int xx = x+(int)u, yy = y+(int)v;
+ if (colorfield) draw_arrow(x,y,xx,yy,color.get_vector_at(X,Y).data,opacity,45,sampling/5.0f,pattern);
+ else draw_arrow(x,y,xx,yy,color,opacity,45,sampling/5.0f,pattern);
+ } else {
+ if (colorfield) draw_line((int)(x-0.5*u),(int)(y-0.5*v),(int)(x+0.5*u),(int)(y+0.5*v),color.get_vector_at(X,Y),opacity,pattern);
+ else draw_line((int)(x-0.5*u),(int)(y-0.5*v),(int)(x+0.5*u),(int)(y+0.5*v),color,opacity,pattern);
+ }
+ }
+ }
+ return *this;
+ }
+
+ //! Draw a 1D graph on the instance image.
+ /**
+ \param data Image containing the graph values I = f(x).
+ \param color Array of dimv() values of type \c T, defining the drawing color.
+ \param gtype Define the type of the plot :
+ - 0 = Plot using points clouds.
+ - 1 = Plot using linear interpolation (segments).
+ - 2 = Plot with bars.
+ - 3 = Plot using cubic interpolation (3-polynomials).
+ - 4 = Plot using cross clouds.
+ \param ymin Lower bound of the y-range.
+ \param ymax Upper bound of the y-range.
+ \param opacity Drawing opacity.
+ \param pattern Drawing pattern.
+ \note
+ - if \c ymin==ymax==0, the y-range is computed automatically from the input sample.
+ **/
+ template<typename t, typename tc>
+ CImg<T>& draw_graph(const CImg<t>& 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<siz; ++off) {
+ const int
+ X = (int)(off*width/siz),
+ Y = (int)((data[off]-m)/ca);
+ draw_line(oX,oY,X,Y,color,opacity,pattern,init_hatch);
+ oX = X; oY = Y;
+ init_hatch = false;
+ }
+ } break;
+ case 2 : { // Spline
+ const CImg<t> 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<typename t, typename tc>
+ CImg<T>& draw_graph(const CImg<t>& data,
+ const CImg<tc>& 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<typename t, typename tc>
+ CImg<T>& draw_axis(const CImg<t>& 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]<xvalues[siz]) draw_arrow(0,y,width-1,y,color,opacity,30,5,pattern);
+ else draw_arrow(width-1,y,0,y,color,opacity,30,5,pattern);
+ const int yt = (y+14)<dimy()?(y+3):(y-14);
+ char txt[32];
+ cimg_foroff(xvalues,x) {
+ cimg_std::sprintf(txt,"%g",(double)xvalues(x));
+ const int xi = (int)(x*(width-1)/siz), xt = xi-(int)cimg::strlen(txt)*3;
+ draw_point(xi,y-1,color,opacity).draw_point(xi,y+1,color,opacity).
+ draw_text(xt<0?0:xt,yt,txt,color,(tc*)0,opacity,11);
+ }
+ }
+ }
+ return *this;
+ }
+
+ //! Draw a labeled horizontal axis on the instance image.
+ template<typename t, typename tc>
+ CImg<T>& draw_axis(const CImg<t>& xvalues, const int y,
+ const CImg<tc>& 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<typename t, typename tc>
+ CImg<T>& draw_axis(const int x, const CImg<t>& 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]<yvalues[siz]) draw_arrow(x,0,x,height-1,color,opacity,30,5,pattern);
+ else draw_arrow(x,height-1,x,0,color,opacity,30,5,pattern);
+ char txt[32];
+ cimg_foroff(yvalues,y) {
+ cimg_std::sprintf(txt,"%g",(double)yvalues(y));
+ const int
+ yi = (int)(y*(height-1)/siz),
+ tmp = yi-5,
+ nyi = tmp<0?0:(tmp>=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<typename t, typename tc>
+ CImg<T>& draw_axis(const int x, const CImg<t>& yvalues,
+ const CImg<tc>& 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<typename tx, typename ty, typename tc>
+ CImg<T>& draw_axis(const CImg<tx>& xvalues, const CImg<ty>& yvalues,
+ const tc *const color, const float opacity=1,
+ const unsigned int patternx=~0U, const unsigned int patterny=~0U) {
+ if (!is_empty()) {
+ const CImg<tx> 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<width; ++x) {
+ const float nx = (float)nxvalues._linear_atX((float)x*sizx/wm1);
+ if (nx*ox<=0) { draw_axis(nx==0?x:x-1,yvalues,color,opacity,patterny); break; }
+ ox = nx;
+ }
+ }
+ const CImg<ty> 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<height; ++y) {
+ const float ny = (float)nyvalues._linear_atX((float)y*sizy/hm1);
+ if (ny*oy<=0) { draw_axis(xvalues,ny==0?y:y-1,color,opacity,patternx); break; }
+ oy = ny;
+ }
+ }
+ }
+ return *this;
+ }
+
+ //! Draw a labeled horizontal+vertical axis on the instance image.
+ template<typename tx, typename ty, typename tc>
+ CImg<T>& draw_axis(const CImg<tx>& xvalues, const CImg<ty>& yvalues,
+ const CImg<tc>& 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<typename tc>
+ CImg<T>& 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<floatT>::sequence(subdivisionx>0?subdivisionx:1-dimx()/subdivisionx,x0,x1).round(px),
+ CImg<floatT>::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<typename tc>
+ CImg<T>& draw_axis(const float x0, const float x1, const float y0, const float y1,
+ const CImg<tc>& 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<typename tx, typename ty, typename tc>
+ CImg<T>& draw_grid(const CImg<tx>& xvalues, const CImg<ty>& 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<dimx()) draw_line(xi,0,xi,height-1,color,opacity,patternx);
+ }
+ if (yvalues) cimg_foroff(yvalues,y) {
+ const int yi = (int)yvalues[y];
+ if (yi>=0 && yi<dimy()) draw_line(0,yi,width-1,yi,color,opacity,patterny);
+ }
+ }
+ return *this;
+ }
+
+ //! Draw grid.
+ template<typename tx, typename ty, typename tc>
+ CImg<T>& draw_grid(const CImg<tx>& xvalues, const CImg<ty>& yvalues,
+ const CImg<tc>& 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<typename tc>
+ CImg<T>& 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<uintT> seqx, seqy;
+ if (deltax!=0) {
+ const float dx = deltax>0?deltax:width*-deltax/100;
+ const unsigned int nx = (unsigned int)(width/dx);
+ seqx = CImg<uintT>::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<uintT>::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<typename tc>
+ CImg<T>& draw_grid(const float deltax, const float deltay,
+ const float offsetx, const float offsety,
+ const bool invertx, const bool inverty,
+ const CImg<tc>& 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<typename tc, typename t>
+ CImg<T>& draw_fill(const int x, const int y, const int z,
+ const tc *const color, const float opacity,
+ CImg<t>& region, const float sigma=0,
+ const bool 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<dimx() && y>=0 && y<dimy() && z>=0 && z<dimz()) {
+ const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
+ const unsigned int whz = width*height*depth, siz = dim*whz, W1 = width-1, H1 = height-1, D1 = depth-1;
+ const bool threed = depth>1;
+ const CImg<T> reference_color = get_vector_at(x,y,z);
+ CImg<uintT> 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,yc<H1);
+ _cimg_draw_fill_test_neighbor(nxc,yc,zc-1,zc!=0);
+ _cimg_draw_fill_test_neighbor(nxc,yc,zc+1,zc<D1);
+ if (nxc) { --nxc; _cimg_draw_fill_test(nxc,yc,zc,cont); } else cont = false;
+ } while (cont);
+ nxc = xc;
+ do { // X-forward
+ if ((++nxc)<=W1) { _cimg_draw_fill_test(nxc,yc,zc,cont); } else cont = false;
+ if (cont) {
+ _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,yc<H1);
+ _cimg_draw_fill_test_neighbor(nxc,yc,zc-1,zc!=0);
+ _cimg_draw_fill_test_neighbor(nxc,yc,zc+1,zc<D1);
+ }
+ } while (cont);
+ unsigned int nyc = yc;
+ do { // Y-backward
+ if (nyc) { --nyc; _cimg_draw_fill_test(xc,nyc,zc,cont); } else cont = false;
+ if (cont) {
+ _cimg_draw_fill_set(xc,nyc,zc);
+ _cimg_draw_fill_test_neighbor(xc-1,nyc,zc,xc!=0);
+ _cimg_draw_fill_test_neighbor(xc+1,nyc,zc,xc<W1);
+ _cimg_draw_fill_test_neighbor(xc,nyc,zc-1,zc!=0);
+ _cimg_draw_fill_test_neighbor(xc,nyc,zc+1,zc<D1);
+ }
+ } while (cont);
+ nyc = yc;
+ do { // Y-forward
+ if ((++nyc)<=H1) { _cimg_draw_fill_test(xc,nyc,zc,cont); } else cont = false;
+ if (cont) {
+ _cimg_draw_fill_set(xc,nyc,zc);
+ _cimg_draw_fill_test_neighbor(xc-1,nyc,zc,xc!=0);
+ _cimg_draw_fill_test_neighbor(xc+1,nyc,zc,xc<W1);
+ _cimg_draw_fill_test_neighbor(xc,nyc,zc-1,zc!=0);
+ _cimg_draw_fill_test_neighbor(xc,nyc,zc+1,zc<D1);
+ }
+ } while (cont);
+ unsigned int nzc = zc;
+ do { // Z-backward
+ if (nzc) { --nzc; _cimg_draw_fill_test(xc,yc,nzc,cont); } else cont = false;
+ if (cont) {
+ _cimg_draw_fill_set(xc,yc,nzc);
+ _cimg_draw_fill_test_neighbor(xc-1,yc,nzc,xc!=0);
+ _cimg_draw_fill_test_neighbor(xc+1,yc,nzc,xc<W1);
+ _cimg_draw_fill_test_neighbor(xc,yc-1,nzc,yc!=0);
+ _cimg_draw_fill_test_neighbor(xc,yc+1,nzc,yc<H1);
+ }
+ } while (cont);
+ nzc = zc;
+ do { // Z-forward
+ if ((++nzc)<=D1) { _cimg_draw_fill_test(xc,yc,nzc,cont); } else cont = false;
+ if (cont) {
+ _cimg_draw_fill_set(xc,nyc,zc);
+ _cimg_draw_fill_test_neighbor(xc-1,yc,nzc,xc!=0);
+ _cimg_draw_fill_test_neighbor(xc+1,yc,nzc,xc<W1);
+ _cimg_draw_fill_test_neighbor(xc,yc-1,nzc,yc!=0);
+ _cimg_draw_fill_test_neighbor(xc,yc+1,nzc,yc<H1);
+ }
+ } while (cont);
+ } while (posr1>posr0);
+ 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,yc<H1);
+ if (high_connexity) {
+ _cimg_draw_fill_test_neighbor(nxc-1,yc-1,0,(nxc!=0 && yc!=0));
+ _cimg_draw_fill_test_neighbor(nxc+1,yc-1,0,(nxc<W1 && yc!=0));
+ _cimg_draw_fill_test_neighbor(nxc-1,yc+1,0,(nxc!=0 && yc<H1));
+ _cimg_draw_fill_test_neighbor(nxc+1,yc+1,0,(nxc<W1 && yc<H1));
+ }
+ if (nxc) { --nxc; _cimg_draw_fill_test(nxc,yc,0,cont); } else cont = false;
+ } while (cont);
+ nxc = xc;
+ do { // X-forward
+ if ((++nxc)<=W1) { _cimg_draw_fill_test(nxc,yc,0,cont); } else cont = false;
+ if (cont) {
+ _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,yc<H1);
+ if (high_connexity) {
+ _cimg_draw_fill_test_neighbor(nxc-1,yc-1,0,(nxc!=0 && yc!=0));
+ _cimg_draw_fill_test_neighbor(nxc+1,yc-1,0,(nxc<W1 && yc!=0));
+ _cimg_draw_fill_test_neighbor(nxc-1,yc+1,0,(nxc!=0 && yc<H1));
+ _cimg_draw_fill_test_neighbor(nxc+1,yc+1,0,(nxc<W1 && yc<H1));
+ }
+ }
+ } while (cont);
+ unsigned int nyc = yc;
+ do { // Y-backward
+ if (nyc) { --nyc; _cimg_draw_fill_test(xc,nyc,0,cont); } else cont = false;
+ if (cont) {
+ _cimg_draw_fill_set(xc,nyc,0);
+ _cimg_draw_fill_test_neighbor(xc-1,nyc,0,xc!=0);
+ _cimg_draw_fill_test_neighbor(xc+1,nyc,0,xc<W1);
+ if (high_connexity) {
+ _cimg_draw_fill_test_neighbor(xc-1,nyc-1,0,(xc!=0 && nyc!=0));
+ _cimg_draw_fill_test_neighbor(xc+1,nyc-1,0,(xc<W1 && nyc!=0));
+ _cimg_draw_fill_test_neighbor(xc-1,nyc+1,0,(xc!=0 && nyc<H1));
+ _cimg_draw_fill_test_neighbor(xc+1,nyc+1,0,(xc<W1 && nyc<H1));
+ }
+ }
+ } while (cont);
+ nyc = yc;
+ do { // Y-forward
+ if ((++nyc)<=H1) { _cimg_draw_fill_test(xc,nyc,0,cont); } else cont = false;
+ if (cont) {
+ _cimg_draw_fill_set(xc,nyc,0);
+ _cimg_draw_fill_test_neighbor(xc-1,nyc,0,xc!=0);
+ _cimg_draw_fill_test_neighbor(xc+1,nyc,0,xc<W1);
+ if (high_connexity) {
+ _cimg_draw_fill_test_neighbor(xc-1,nyc-1,0,(xc!=0 && nyc!=0));
+ _cimg_draw_fill_test_neighbor(xc+1,nyc-1,0,(xc<W1 && nyc!=0));
+ _cimg_draw_fill_test_neighbor(xc-1,nyc+1,0,(xc!=0 && nyc<H1));
+ _cimg_draw_fill_test_neighbor(xc+1,nyc+1,0,(xc<W1 && nyc<H1));
+ }
+ }
+ } while (cont);
+ } while (posr1>posr0);
+ 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<typename tc, typename t>
+ CImg<T>& draw_fill(const int x, const int y, const int z,
+ const CImg<tc>& color, const float opacity,
+ CImg<t>& 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<typename tc>
+ CImg<T>& draw_fill(const int x, const int y, const int z,
+ const tc *const color, const float opacity=1,
+ const float sigma=0, const bool high_connexity=false) {
+ CImg<boolT> 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<typename tc>
+ CImg<T>& draw_fill(const int x, const int y, const int z,
+ const CImg<tc>& 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<typename tc>
+ CImg<T>& 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<boolT> 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<typename tc>
+ CImg<T>& draw_fill(const int x, const int y,
+ const CImg<tc>& 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<T>& 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<nx0) cimg::swap(nx0,nx1);
+ if (ny1<ny0) cimg::swap(ny0,ny1);
+ if (nx0<0) nx0 = 0;
+ 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<T>::min() || val>(Tfloat)cimg::type<T>::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<T>::min() || val>(Tfloat)cimg::type<T>::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<T>& 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<typename tc>
+ CImg<T>& draw_mandelbrot(const int x0, const int y0, const int x1, const int y1,
+ const CImg<tc>& 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<tc> 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<typename tc>
+ CImg<T>& draw_mandelbrot(const CImg<tc>& 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<typename tc>
+ CImg<T>& 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<typename tc>
+ CImg<T>& draw_gaussian(const float xc, const float sigma,
+ const CImg<tc>& 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<typename t, typename tc>
+ CImg<T>& draw_gaussian(const float xc, const float yc, const CImg<t>& tensor,
+ const tc *const color, const float opacity=1) {
+ if (is_empty()) return *this;
+ typedef typename cimg::superset<t,float>::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<tfloat> 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<typename t, typename tc>
+ CImg<T>& draw_gaussian(const float xc, const float yc, const CImg<t>& tensor,
+ const CImg<tc>& color, const float opacity=1) {
+ return draw_gaussian(xc,yc,tensor,color.data,opacity);
+ }
+
+ //! Draw an anisotropic 2D gaussian function.
+ template<typename tc>
+ CImg<T>& draw_gaussian(const int xc, const int yc, const float r1, const float r2, const float ru, const float rv,
+ const tc *const color, const float opacity=1) {
+ const double
+ a = r1*ru*ru + r2*rv*rv,
+ b = (r1-r2)*ru*rv,
+ c = r1*rv*rv + r2*ru*ru;
+ const CImg<Tfloat> tensor(2,2,1,1, a,b,b,c);
+ return draw_gaussian(xc,yc,tensor,color,opacity);
+ }
+
+ //! Draw an anisotropic 2D gaussian function.
+ template<typename tc>
+ CImg<T>& draw_gaussian(const int xc, const int yc, const float r1, const float r2, const float ru, const float rv,
+ const CImg<tc>& 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<typename tc>
+ CImg<T>& 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<floatT>::diagonal(sigma,sigma),color,opacity);
+ }
+
+ //! Draw an isotropic 2D gaussian function.
+ template<typename tc>
+ CImg<T>& draw_gaussian(const float xc, const float yc, const float sigma,
+ const CImg<tc>& 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<typename t, typename tc>
+ CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const CImg<t>& tensor,
+ const tc *const color, const float opacity=1) {
+ if (is_empty()) return *this;
+ typedef typename cimg::superset<t,float>::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<tfloat> 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<typename t, typename tc>
+ CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const CImg<t>& tensor,
+ const CImg<tc>& 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<typename tc>
+ CImg<T>& 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<floatT>::diagonal(sigma,sigma,sigma),color,opacity);
+ }
+
+ //! Draw an isotropic 3D gaussian function.
+ template<typename tc>
+ CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const float sigma,
+ const CImg<tc>& color, const float opacity=1) {
+ return draw_gaussian(xc,yc,zc,sigma,color.data,opacity);
+ }
+
+ // Draw a 3D object (internal)
+ template<typename tc, typename to>
+ void _draw_object3d_sprite(const int x, const int y,
+ const CImg<tc>& color, const CImg<to>& opacity, const CImg<T>& 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<typename tc>
+ void _draw_object3d_sprite(const int x, const int y,
+ const CImg<tc>& color, const float opacity, const CImg<T>& sprite) {
+ if (color) draw_image(x,y,sprite,opacity);
+ }
+
+ template<typename tp, typename tf, typename tc, typename to>
+ CImg<T>& _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<tf>& primitives,
+ const CImgList<tc>& 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<floatT> 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<floatT> 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<uintT> visibles(primitives.size);
+ CImg<floatT> zrange(primitives.size);
+ unsigned int nb_visibles = 0;
+ const float zmin = -focale+1.5f;
+ { cimglist_for(primitives,l) {
+ const CImg<tf>& 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<width && y0>=0 && y0<height) {
+ visibles(nb_visibles) = (unsigned int)l;
+ zrange(nb_visibles++) = z0;
+ }
+ } break;
+ case 5 : { // Sphere
+ const unsigned int
+ i0 = (unsigned int)primitive(0),
+ i1 = (unsigned int)primitive(1),
+ i2 = (unsigned int)primitive(2);
+ const float x0 = projections(i0,0), y0 = projections(i0,1), z0 = (float)(Z+points(i0,2));
+ int radius;
+ if (i2) radius = (int)(i2*focale/(z0+focale));
+ else {
+ const float x1 = projections(i1,0), y1 = projections(i1,1);
+ const int deltax = (int)(x1-x0), deltay = (int)(y1-y0);
+ radius = (int)cimg_std::sqrt((float)(deltax*deltax + deltay*deltay));
+ }
+ if (z0>zmin && x0+radius>=0 && x0-radius<width && y0+radius>=0 && y0-radius<height) {
+ visibles(nb_visibles) = (unsigned int)l;
+ zrange(nb_visibles++) = z0;
+ }
+ } break;
+ case 2 : // Line
+ case 6 : {
+ const unsigned int
+ i0 = (unsigned int)primitive(0),
+ i1 = (unsigned int)primitive(1);
+ const float
+ x0 = projections(i0,0), y0 = projections(i0,1), z0 = (float)(Z+points(i0,2)),
+ x1 = projections(i1,0), y1 = projections(i1,1), z1 = (float)(Z+points(i1,2));
+ float xm, xM, ym, yM;
+ if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
+ if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
+ if (z0>zmin && z1>zmin && xM>=0 && xm<width && yM>=0 && ym<height) {
+ visibles(nb_visibles) = (unsigned int)l;
+ zrange(nb_visibles++) = 0.5f*(z0+z1);
+ }
+ } break;
+ case 3 : // Triangle
+ case 9 : {
+ const unsigned int
+ i0 = (unsigned int)primitive(0),
+ i1 = (unsigned int)primitive(1),
+ i2 = (unsigned int)primitive(2);
+ const float
+ x0 = projections(i0,0), y0 = projections(i0,1), z0 = (float)(Z+points(i0,2)),
+ x1 = projections(i1,0), y1 = projections(i1,1), z1 = (float)(Z+points(i1,2)),
+ x2 = projections(i2,0), y2 = projections(i2,1), z2 = (float)(Z+points(i2,2));
+ float xm, xM, ym, yM;
+ if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
+ if (x2<xm) xm = x2;
+ if (x2>xM) xM = x2;
+ if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
+ if (y2<ym) ym = y2;
+ if (y2>yM) yM = y2;
+ if (z0>zmin && z1>zmin && z2>zmin && xM>=0 && xm<width && yM>=0 && ym<height) {
+ const float d = (x1-x0)*(y2-y0)-(x2-x0)*(y1-y0);
+ if (double_sided || d<0) {
+ visibles(nb_visibles) = (unsigned int)l;
+ zrange(nb_visibles++) = (z0+z1+z2)/3;
+ }
+ }
+ } break;
+ case 4 : // Rectangle
+ case 12 : {
+ const unsigned int
+ i0 = (unsigned int)primitive(0),
+ i1 = (unsigned int)primitive(1),
+ i2 = (unsigned int)primitive(2),
+ i3 = (unsigned int)primitive(3);
+ const float
+ x0 = projections(i0,0), y0 = projections(i0,1), z0 = (float)(Z+points(i0,2)),
+ x1 = projections(i1,0), y1 = projections(i1,1), z1 = (float)(Z+points(i1,2)),
+ x2 = projections(i2,0), y2 = projections(i2,1), z2 = (float)(Z+points(i2,2)),
+ x3 = projections(i3,0), y3 = projections(i3,1), z3 = (float)(Z+points(i3,2));
+ float xm, xM, ym, yM;
+ if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
+ if (x2<xm) xm = x2;
+ if (x2>xM) xM = x2;
+ if (x3<xm) xm = x3;
+ if (x3>xM) xM = x3;
+ if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
+ if (y2<ym) ym = y2;
+ if (y2>yM) yM = y2;
+ if (y3<ym) ym = y3;
+ if (y3>yM) yM = y3;
+ if (z0>zmin && z1>zmin && z2>zmin && z3>zmin && xM>=0 && xm<width && yM>=0 && ym<height) {
+ const float d = (x1 - x0)*(y2 - y0) - (x2 - x0)*(y1 - y0);
+ if (double_sided || d<0) {
+ visibles(nb_visibles) = (unsigned int)l;
+ zrange(nb_visibles++) = (z0 + z1 + z2 + z3)/4;
+ }
+ }
+ } break;
+ default :
+ throw CImgArgumentException("CImg<%s>::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<uintT> permutations;
+ CImg<floatT>(zrange.data,nb_visibles,1,1,1,true).sort(permutations,false);
+
+ // Compute light properties
+ CImg<floatT> lightprops;
+ switch (render_type) {
+ case 3 : { // Flat Shading
+ lightprops.assign(nb_visibles);
+ cimg_forX(lightprops,l) {
+ const CImg<tf>& 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<floatT> points_normals(nb_points,3,1,1,0);
+ for (unsigned int l=0; l<nb_visibles; ++l) {
+ const CImg<tf>& 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<tc> default_color(1,dim,1,1,(tc)200);
+ { for (unsigned int l = 0; l<nb_visibles; ++l) {
+ const unsigned int n_primitive = visibles(permutations(l));
+ const CImg<tf>& primitive = primitives[n_primitive];
+ const CImg<tc>& color = n_primitive<colors.size?colors[n_primitive]:default_color;
+ const float opac = n_primitive<nb_opacities?opacities(n_primitive,0):1.0f;
+#ifdef cimg_use_board
+ BoardLib::Board &board = *(BoardLib::Board*)pboard;
+#endif
+
+ switch (primitive.size()) {
+ case 1 : { // Colored point or sprite
+ const unsigned int n0 = (unsigned int)primitive[0];
+ const int x0 = (int)projections(n0,0), y0 = (int)projections(n0,1);
+ if (color.size()==dim) {
+ 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
+ } else {
+ const float z = Z + points(n0,2);
+ const int
+ factor = (int)(focale*100/(z+focale)),
+ sw = color.width*factor/200,
+ sh = color.height*factor/200;
+ if (x0+sw>=0 && x0-sw<dimx() && y0+sh>=0 && y0-sh<dimy()) {
+ const CImg<T> 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<typename tp, typename tf, typename tc, typename to>
+ CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
+ const CImg<tp>& points, const CImgList<tf>& primitives,
+ const CImgList<tc>& colors, const CImgList<to>& 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<typename tp, typename tf, typename tc, typename to>
+ CImg<T>& draw_object3d(BoardLib::Board& board,
+ const float x0, const float y0, const float z0,
+ const CImg<tp>& points, const CImgList<tf>& primitives,
+ const CImgList<tc>& colors, const CImgList<to>& 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<typename tp, typename tf, typename tc, typename to>
+ CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
+ const CImgList<tp>& points, const CImgList<tf>& primitives,
+ const CImgList<tc>& colors, const CImgList<to>& 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<typename tp, typename tf, typename tc, typename to>
+ CImg<T>& draw_object3d(BoardLib::Board& board,
+ const float x0, const float y0, const float z0,
+ const CImgList<tp>& points, const CImgList<tf>& primitives,
+ const CImgList<tc>& colors, const CImgList<to>& 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<typename tp, typename tf, typename tc, typename to>
+ CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
+ const CImg<tp>& points, const CImgList<tf>& primitives,
+ const CImgList<tc>& colors, const CImg<to>& 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<typename tp, typename tf, typename tc, typename to>
+ CImg<T>& draw_object3d(BoardLib::Board& board,
+ const float x0, const float y0, const float z0,
+ const CImg<tp>& points, const CImgList<tf>& primitives,
+ const CImgList<tc>& colors, const CImg<to>& 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<typename tp, typename tf, typename tc, typename to>
+ CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
+ const CImgList<tp>& points, const CImgList<tf>& primitives,
+ const CImgList<tc>& colors, const CImg<to>& 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<typename tp, typename tf, typename tc, typename to>
+ CImg<T>& draw_object3d(BoardLib::Board& board,
+ const float x0, const float y0, const float z0,
+ const CImgList<tp>& points, const CImgList<tf>& primitives,
+ const CImgList<tc>& colors, const CImg<to>& 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<typename tp, typename tf, typename tc>
+ CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
+ const tp& points, const CImgList<tf>& primitives,
+ const CImgList<tc>& 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<floatT> 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<typename tp, typename tf, typename tc, typename to>
+ CImg<T>& draw_object3d(BoardLib::Board& board,
+ const float x0, const float y0, const float z0,
+ const tp& points, const CImgList<tf>& primitives,
+ const CImgList<tc>& 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<floatT> 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<typename t>
+ CImg<T>& correlate(const CImg<t>& mask, const unsigned int cond=1, const bool weighted_correl=false) {
+ return get_correlate(mask,cond,weighted_correl).transfer_to(*this);
+ }
+
+ template<typename t>
+ CImg<typename cimg::superset2<T,t,float>::type> get_correlate(const CImg<t>& mask, const unsigned int cond=1,
+ const bool weighted_correl=false) const {
+ typedef typename cimg::superset2<T,t,float>::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<Ttfloat> 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<mze; ++z) for (int y = my1; y<mye; ++y) for (int x = mx1; x<mxe; ++x) {
+ 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+=(*this)(x+xm,y+ym,z+zm,v)*mask(mx1+xm,my1+ym,mz1+zm);
+ dest(x,y,z,v) = (Ttfloat)val;
+ }
+ if (cond)
+ cimg_forYZV(*this,y,z,v)
+ for (int x = 0; x<dimx(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || 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<dimx(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || 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<mze; ++z) for (int y = my1; y<mye; ++y) for (int x = mx1; x<mxe; ++x) {
+ 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)(*this)(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;
+ }
+ if (cond)
+ cimg_forYZV(*this,y,z,v)
+ for (int x = 0; x<dimx(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || 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<dimx(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || 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<typename t>
+ CImg<T>& convolve(const CImg<t>& mask, const unsigned int cond=1, const bool weighted_convol=false) {
+ return get_convolve(mask,cond,weighted_convol).transfer_to(*this);
+ }
+
+ template<typename t>
+ CImg<typename cimg::superset2<T,t,float>::type> get_convolve(const CImg<t>& mask, const unsigned int cond=1,
+ const bool weighted_convol=false) const {
+ typedef typename cimg::superset2<T,t,float>::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<t>(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<typename t>
+ CImg<T>& erode(const CImg<t>& mask, const unsigned int cond=1, const bool weighted_erosion=false) {
+ return get_erode(mask,cond,weighted_erosion).transfer_to(*this);
+ }
+
+ template<typename t>
+ CImg<typename cimg::superset<T,t>::type> get_erode(const CImg<t>& mask, const unsigned int cond=1,
+ const bool weighted_erosion=false) const {
+ typedef typename cimg::superset<T,t>::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<Tt> 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<mze; ++z) for (int y = my1; y<mye; ++y) for (int x = mx1; x<mxe; ++x) {
+ Tt min_val = cimg::type<Tt>::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<min_val) min_val = cval;
+ }
+ dest(x,y,z,v) = min_val;
+ }
+ if (cond)
+ cimg_forYZV(*this,y,z,v)
+ for (int x = 0; x<dimx(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
+ Tt min_val = cimg::type<Tt>::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<min_val) min_val = cval;
+ }
+ dest(x,y,z,v) = min_val;
+ }
+ else
+ cimg_forYZV(*this,y,z,v)
+ for (int x = 0; x<dimx(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
+ Tt min_val = cimg::type<Tt>::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<min_val) min_val = cval;
+ }
+ dest(x,y,z,v) = min_val;
+ }
+ } else { // Weighted erosion
+ for (int z = mz1; z<mze; ++z) for (int y = my1; y<mye; ++y) for (int x = mx1; x<mxe; ++x) {
+ Tt min_val = cimg::type<Tt>::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<min_val) min_val = cval;
+ }
+ dest(x,y,z,v) = min_val;
+ }
+ if (cond)
+ cimg_forYZV(*this,y,z,v)
+ for (int x = 0; x<dimx(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
+ Tt min_val = cimg::type<Tt>::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<min_val) min_val = cval;
+ }
+ dest(x,y,z,v) = min_val;
+ }
+ else
+ cimg_forYZV(*this,y,z,v)
+ for (int x = 0; x<dimx(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
+ Tt min_val = cimg::type<Tt>::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<min_val) min_val = cval;
+ }
+ dest(x,y,z,v) = min_val;
+ }
+ }
+ return dest;
+ }
+
+ //! Erode the image by a square structuring element of size n.
+ CImg<T>& erode(const unsigned int n, const unsigned int cond=1) {
+ if (n<2) return *this;
+ return get_erode(n,cond).transfer_to(*this);
+ }
+
+ CImg<T> get_erode(const unsigned int n, const unsigned int cond=1) const {
+ static CImg<T> mask;
+ if (n<2) return *this;
+ if (mask.width!=n) mask.assign(n,n,1,1,1);
+ const CImg<T> res = get_erode(mask,cond,false);
+ if (n>20) mask.assign();
+ return res;
+ }
+
+ //! Dilate the image by a structuring element.
+ template<typename t>
+ CImg<T>& dilate(const CImg<t>& mask, const unsigned int cond=1, const bool weighted_dilatation=false) {
+ return get_dilate(mask,cond,weighted_dilatation).transfer_to(*this);
+ }
+
+ template<typename t>
+ CImg<typename cimg::superset<T,t>::type> get_dilate(const CImg<t>& mask, const unsigned int cond=1,
+ const bool weighted_dilatation=false) const {
+ typedef typename cimg::superset<T,t>::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<Tt> 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<mze; ++z) for (int y = my1; y<mye; ++y) for (int x = mx1; x<mxe; ++x) {
+ Tt max_val = cimg::type<Tt>::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<dimx(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
+ Tt max_val = cimg::type<Tt>::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<dimx(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
+ Tt max_val = cimg::type<Tt>::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<mze; ++z) for (int y = my1; y<mye; ++y) for (int x = mx1; x<mxe; ++x) {
+ Tt max_val = cimg::type<Tt>::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<dimx(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
+ Tt max_val = cimg::type<Tt>::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<dimx(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
+ Tt max_val = cimg::type<Tt>::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<T>& dilate(const unsigned int n, const unsigned int cond=1) {
+ if (n<2) return *this;
+ return get_dilate(n,cond).transfer_to(*this);
+ }
+
+ CImg<T> get_dilate(const unsigned int n, const unsigned int cond=1) const {
+ static CImg<T> mask;
+ if (n<2) return *this;
+ if (mask.width!=n) mask.assign(n,n,1,1,1);
+ const CImg<T> 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<T>& noise(const double sigma, const unsigned int noise_type=0) {
+ if (!is_empty()) {
+ double nsigma = sigma, max = (double)cimg::type<T>::max(), min = (double)cimg::type<T>::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 (val<min) val = min;
+ *ptr = (T)val;
+ }
+ } break;
+ case 1 : { // Uniform noise
+ cimg_for(*this,ptr,T) {
+ double val = *ptr + nsigma*cimg::crand();
+ if (val>max) val = max;
+ if (val<min) val = min;
+ *ptr = (T)val;
+ }
+ } break;
+ case 2 : { // Salt & Pepper noise
+ if (nsigma<0) nsigma = -nsigma;
+ if (M==m) { m = 0; M = (float)(cimg::type<T>::is_float()?1:cimg::type<T>::max()); }
+ cimg_for(*this,ptr,T) if (cimg::rand()*100<nsigma) *ptr = (T)(cimg::rand()<0.5?M:m);
+ } break;
+
+ case 3 : { // Poisson Noise
+ cimg_for(*this,ptr,T) *ptr = (T)cimg::prand(*ptr);
+ } break;
+
+ case 4 : { // Rice noise
+ const double sqrt2 = (double)cimg_std::sqrt(2.0);
+ cimg_for(*this,ptr,T) {
+ const double
+ val0 = (double)*ptr/sqrt2,
+ re = val0 + nsigma*cimg::grand(),
+ im = val0 + nsigma*cimg::grand();
+ double val = cimg_std::sqrt(re*re + im*im);
+ if (val>max) val = max;
+ if (val<min) val = min;
+ *ptr = (T)val;
+ }
+ } break;
+ default :
+ throw CImgArgumentException("CImg<%s>::noise() : Invalid noise type %d "
+ "(should be {0=Gaussian, 1=Uniform, 2=Salt&Pepper, 3=Poisson}).",pixel_type(),noise_type);
+ }
+ }
+ return *this;
+ }
+
+ CImg<T> 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<T>& 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<N; ++m) { \
+ const T xc = *ptrX; ptrX+=off; \
+ const Tfloat yc = *(ptrY++) = (Tfloat)(a0*xc + a1*xp - b1*yp - b2*yb); \
+ xp = xc; yb = yp; yp = yc; \
+ } \
+ T xn = (T)0, xa = (T)0; \
+ Tfloat yn = 0, ya = 0; \
+ if (cond) { xn = xa = *(ptrX-off); yn = ya = (Tfloat)coefn*xn; } \
+ for (int n=N-1; n>=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<Tfloat> 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<Tfloat> 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<Tfloat> 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<Tfloat> Y(N);
+ cimg_forXYZ(*this,x,y,z) { T *ptrX = ptr(x,y,z,0); _cimg_deriche2_apply; }
+ } break;
+ }
+ return *this;
+ }
+
+ CImg<Tfloat> get_deriche(const float sigma, const int order=0, const char axis='x', const bool cond=true) const {
+ return CImg<Tfloat>(*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<T>& 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<Tfloat> get_blur(const float sigmax, const float sigmay, const float sigmaz,
+ const bool cond=true) const {
+ return CImg<Tfloat>(*this,false).blur(sigmax,sigmay,sigmaz,cond);
+ }
+
+ //! Return a blurred version of the image, using a Canny-Deriche filter.
+ CImg<T>& blur(const float sigma, const bool cond=true) {
+ return blur(sigma,sigma,sigma,cond);
+ }
+
+ CImg<Tfloat> get_blur(const float sigma, const bool cond=true) const {
+ return CImg<Tfloat>(*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<typename t>
+ CImg<T>& blur_anisotropic(const CImg<t>& G, const float amplitude=60, const float dl=0.8f, const float da=30,
+ const float gauss_prec=2, const unsigned int interpolation_type=0, const bool 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<Tfloat>
+ 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<length && X>=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<length && X>=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<length && X>=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<length && X>=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<length && X>=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<length && X>=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<T>::min(), M = cimg::type<T>::max();
+ cimg_for(*this,ptrd,T) { const Tfloat val = *(--ptrs)/N; *ptrd = val<m?m:(val>M?M:(T)val); }
+ }
+ return *this;
+ }
+
+ template<typename t>
+ CImg<T> get_blur_anisotropic(const CImg<t>& G, const float amplitude=60, const float dl=0.8f, const float da=30,
+ const float gauss_prec=2, const unsigned int interpolation_type=0, const bool 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<typename tm>
+ CImg<T>& blur_anisotropic(const CImg<tm>& 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<floatT> blurred = CImg<floatT>(*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<floatT> 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<floatT> 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<typename tm>
+ CImg<T> get_blur_anisotropic(const CImg<tm>& 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<T>& 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<T>(),amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,interpolation_type,fast_approx,geom_factor);
+ }
+
+ CImg<T> 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<T>& 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<floatT> 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<floatT> 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<T> 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<T>& 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<T> 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<T>& 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<Tfloat> res(width,height,depth,dim,0);
+ CImg<T> 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<T> 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<Tfloat> get_FFT(const char axis, const bool invert=false) const {
+ return CImgList<Tfloat>(*this).FFT(axis,invert);
+ }
+
+ //! Compute the Fast Fourier Transform on an image.
+ CImgList<Tfloat> get_FFT(const bool invert=false) const {
+ return CImgList<Tfloat>(*this).FFT(invert);
+ }
+
+ //! Apply a median filter.
+ CImg<T>& blur_median(const unsigned int n) {
+ return get_blur_median(n).transfer_to(*this);
+ }
+
+ CImg<T> get_blur_median(const unsigned int n) {
+ CImg<T> 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<T> 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<T> 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]<I[4]?
+ (I[4]<I[5]?I[4]:
+ (I[3]<I[5]?I[5]:I[3])):
+ (I[3]<I[5]?I[3]:
+ (I[4]<I[5]?I[5]:I[4]));
+ }
+ } break;
+ default : {
+ CImg<T> 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<T>& 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<Tfloat> val, vec, veloc(width,height,depth,dim);
+
+ if (threed) {
+ CImg_3x3x3(I,T);
+ if (sharpen_type) { // 3D Shock filter.
+ CImg<Tfloat> 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<Tfloat> 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<T> 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<T>& 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<Tfloat> get_haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) const {
+ if (is_empty() || !nb_scales) return *this;
+ CImg<Tfloat> 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<w; ++x, ++xw) {
+ const Tfloat val0 = (Tfloat)(*this)(x,y,z,v), val1 = (Tfloat)(*this)(xw,y,z,v);
+ res(x2++,y,z,v) = val0 - val1;
+ res(x2++,y,z,v) = val0 + val1;
+ }
+ } else cimg_forYZV(*this,y,z,v) { // Direct transform along X
+ for (unsigned int x=0, xw=w, x2=0; x<w; ++x, ++xw) {
+ const Tfloat val0 = (Tfloat)(*this)(x2++,y,z,v), val1 = (Tfloat)(*this)(x2++,y,z,v);
+ res(x,y,z,v) = (val0 + val1)/2;
+ res(xw,y,z,v) = (val1 - val0)/2;
+ }
+ }
+ } else return *this;
+ } break;
+ case 'y' : {
+ const unsigned int h = height/2;
+ if (h) {
+ if (h%2)
+ throw CImgInstanceException("CImg<%s>::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<h; ++y, ++yh) {
+ const Tfloat val0 = (Tfloat)(*this)(x,y,z,v), val1 = (Tfloat)(*this)(x,yh,z,v);
+ res(x,y2++,z,v) = val0 - val1;
+ res(x,y2++,z,v) = val0 + val1;
+ }
+ } else cimg_forXZV(*this,x,z,v) {
+ for (unsigned int y=0, yh=h, y2=0; y<h; ++y, ++yh) { // Direct transform along Y
+ const Tfloat val0 = (Tfloat)(*this)(x,y2++,z,v), val1 = (Tfloat)(*this)(x,y2++,z,v);
+ res(x,y,z,v) = (val0 + val1)/2;
+ res(x,yh,z,v) = (val1 - val0)/2;
+ }
+ }
+ } else return *this;
+ } break;
+ case 'z' : {
+ const unsigned int d = depth/2;
+ if (d) {
+ if (d%2)
+ throw CImgInstanceException("CImg<%s>::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<d; ++z, ++zd) {
+ const Tfloat val0 = (Tfloat)(*this)(x,y,z,v), val1 = (Tfloat)(*this)(x,y,zd,v);
+ res(x,y,z2++,v) = val0 - val1;
+ res(x,y,z2++,v) = val0 + val1;
+ }
+ } else cimg_forXYV(*this,x,y,v) {
+ for (unsigned int z=0, zd=d, z2=0; z<d; ++z, ++zd) { // Direct transform along Z
+ const Tfloat val0 = (Tfloat)(*this)(x,y,z2++,v), val1 = (Tfloat)(*this)(x,y,z2++,v);
+ res(x,y,z,v) = (val0 + val1)/2;
+ res(x,y,zd,v) = (val1 - val0)/2;
+ }
+ }
+ } else return *this;
+ } break;
+ default :
+ throw CImgArgumentException("CImg<%s>::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<nb_scales; ++s) w/=2;
+ for (w=w?w:1; w<=width; w*=2) res.draw_image(res.get_crop(0,w-1).get_haar('x',true,1));
+ } break;
+ case 'y' : {
+ unsigned int h = width;
+ for (unsigned int s=1; h && s<nb_scales; ++s) h/=2;
+ for (h=h?h:1; h<=height; h*=2) res.draw_image(res.get_crop(0,0,width-1,h-1).get_haar('y',true,1));
+ } break;
+ case 'z' : {
+ unsigned int d = depth;
+ for (unsigned int s=1; d && s<nb_scales; ++s) d/=2;
+ for (d=d?d:1; d<=depth; d*=2) res.draw_image(res.get_crop(0,0,0,width-1,height-1,d-1).get_haar('z',true,1));
+ } break;
+ default :
+ throw CImgArgumentException("CImg<%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<nb_scales; ++s, w/=2) res.draw_image(res.get_crop(0,w-1).get_haar('x',false,1));
+ } break;
+ case 'y' : {
+ for (unsigned int s=1, h=height/2; h && s<nb_scales; ++s, h/=2) res.draw_image(res.get_crop(0,0,width-1,h-1).get_haar('y',false,1));
+ } break;
+ case 'z' : {
+ for (unsigned int s=1, d=depth/2; d && s<nb_scales; ++s, d/=2) res.draw_image(res.get_crop(0,0,0,width-1,height-1,d-1).get_haar('z',false,1));
+ } break;
+ default :
+ throw CImgArgumentException("CImg<%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<T>& haar(const bool invert=false, const unsigned int nb_scales=1) {
+ return get_haar(invert,nb_scales).transfer_to(*this);
+ }
+
+ CImg<Tfloat> get_haar(const bool invert=false, const unsigned int nb_scales=1) const {
+ CImg<Tfloat> 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 && s<nb_scales; ++s) { w/=2; h/=2; d/=2; }
+ for (w=w?w:1, h=h?h:1, d=d?d:1; w<=width && h<=height && d<=depth; w*=2, h*=2, d*=2)
+ res.draw_image(res.get_crop(0,0,0,w-1,h-1,d-1).get_haar(true,1));
+ } else {
+ unsigned int w = width, h = height; for (unsigned int s=1; w && h && s<nb_scales; ++s) { w/=2; h/=2; }
+ for (w=w?w:1, h=h?h:1; w<=width && h<=height; w*=2, h*=2)
+ res.draw_image(res.get_crop(0,0,0,w-1,h-1,0).get_haar(true,1));
+ }
+ } else {
+ if (depth>1) {
+ unsigned int w = width, d = depth; for (unsigned int s=1; w && d && s<nb_scales; ++s) { w/=2; d/=2; }
+ for (w=w?w:1, d=d?d:1; w<=width && d<=depth; w*=2, d*=2)
+ res.draw_image(res.get_crop(0,0,0,w-1,0,d-1).get_haar(true,1));
+ } else {
+ unsigned int w = width; for (unsigned int s=1; w && s<nb_scales; ++s) w/=2;
+ for (w=w?w:1; w<=width; w*=2)
+ res.draw_image(res.get_crop(0,0,0,w-1,0,0).get_haar(true,1));
+ }
+ }
+ } else {
+ if (height>1) {
+ if (depth>1) {
+ unsigned int h = height, d = depth; for (unsigned int s=1; h && d && s<nb_scales; ++s) { h/=2; d/=2; }
+ for (h=h?h:1, d=d?d:1; h<=height && d<=depth; h*=2, d*=2)
+ res.draw_image(res.get_crop(0,0,0,0,h-1,d-1).get_haar(true,1));
+ } else {
+ unsigned int h = height; for (unsigned int s=1; h && s<nb_scales; ++s) h/=2;
+ for (h=h?h:1; h<=height; h*=2)
+ res.draw_image(res.get_crop(0,0,0,0,h-1,0).get_haar(true,1));
+ }
+ } else {
+ if (depth>1) {
+ unsigned int d = depth; for (unsigned int s=1; d && s<nb_scales; ++s) d/=2;
+ for (d=d?d:1; d<=depth; d*=2)
+ res.draw_image(res.get_crop(0,0,0,0,0,d-1).get_haar(true,1));
+ } else return *this;
+ }
+ }
+ } else { // Direct transform
+ res = get_haar(false,1);
+ if (width>1) {
+ if (height>1) {
+ if (depth>1) for (unsigned int s=1, w=width/2, h=height/2, d=depth/2; w && h && d && s<nb_scales; ++s, w/=2, h/=2, d/=2)
+ res.draw_image(res.get_crop(0,0,0,w-1,h-1,d-1).haar(false,1));
+ else for (unsigned int s=1, w=width/2, h=height/2; w && h && s<nb_scales; ++s, w/=2, h/=2)
+ res.draw_image(res.get_crop(0,0,0,w-1,h-1,0).haar(false,1));
+ } else {
+ if (depth>1) for (unsigned int s=1, w=width/2, d=depth/2; w && d && s<nb_scales; ++s, w/=2, d/=2)
+ res.draw_image(res.get_crop(0,0,0,w-1,0,d-1).haar(false,1));
+ else for (unsigned int s=1, w=width/2; w && s<nb_scales; ++s, w/=2)
+ res.draw_image(res.get_crop(0,0,0,w-1,0,0).haar(false,1));
+ }
+ } else {
+ if (height>1) {
+ if (depth>1) for (unsigned int s=1, h=height/2, d=depth/2; h && d && s<nb_scales; ++s, h/=2, d/=2)
+ res.draw_image(res.get_crop(0,0,0,0,h-1,d-1).haar(false,1));
+ else for (unsigned int s=1, h=height/2; h && s<nb_scales; ++s, h/=2)
+ res.draw_image(res.get_crop(0,0,0,0,h-1,0).haar(false,1));
+ } else {
+ if (depth>1) for (unsigned int s=1, d=depth/2; d && s<nb_scales; ++s, d/=2)
+ res.draw_image(res.get_crop(0,0,0,0,0,d-1).haar(false,1));
+ else return *this;
+ }
+ }
+ }
+ return res;
+ }
+ return *this;
+ }
+
+ //! Estimate a displacement field between instance image and given target image.
+ CImg<T>& displacement_field(const CImg<T>& 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<Tfloat> get_displacement_field(const CImg<T>& 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<Tfloat> 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<Tfloat>
+ I1 = get_resize(swidth,sheight,sdepth,-100,2),
+ I2 = target.get_resize(swidth,sheight,sdepth,-100,2);
+ I1/=factor; I2/=factor;
+ CImg<Tfloat> 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<Tfloat> veloc(U);
+ float dt = 2, Energy = cimg::type<float>::max();
+ const CImgList<Tfloat> dI = I2.get_gradient();
+ for (unsigned int iter=0; iter<itermax; iter++) {
+ veloc.fill(0);
+ float nEnergy = 0;
+ if (threed) {
+ cimg_for3XYZ(U,x,y,z) {
+ const float X = (float)(x + U(x,y,z,0)), Y = (float)(y + U(x,y,z,1)), Z = (float)(z + U(x,y,z,2));
+ cimg_forV(U,k) {
+ const Tfloat
+ Ux = 0.5f*(U(_n1x,y,z,k) - U(_p1x,y,z,k)),
+ Uy = 0.5f*(U(x,_n1y,z,k) - U(x,_p1y,z,k)),
+ Uz = 0.5f*(U(x,y,_n1z,k) - U(x,y,_p1z,k)),
+ Uxx = U(_n1x,y,z,k) + U(_p1x,y,z,k) - 2*U(x,y,z,k),
+ Uyy = U(x,_n1y,z,k) + U(x,_p1y,z,k) - 2*U(x,y,z,k),
+ Uzz = U(x,y,_n1z,k) + U(x,y,_n1z,k) - 2*U(x,y,z,k);
+ nEnergy += (float)(smoothness*(Ux*Ux + Uy*Uy + Uz*Uz));
+ Tfloat deltaIgrad = 0;
+ cimg_forV(I1,i) {
+ const Tfloat deltaIi = (float)(I2._linear_atXYZ(X,Y,Z,i) - I1(x,y,z,i));
+ nEnergy += (float)(deltaIi*deltaIi/2);
+ deltaIgrad+=-deltaIi*dI[k]._linear_atXYZ(X,Y,Z,i);
+ }
+ veloc(x,y,z,k) = deltaIgrad + smoothness*(Uxx + Uyy + Uzz);
+ }
+ }
+ } else {
+ cimg_for3XY(U,x,y) {
+ const float X = (float)(x + U(x,y,0)), Y = (float)(y + U(x,y,1));
+ cimg_forV(U,k) {
+ const Tfloat
+ Ux = 0.5f*(U(_n1x,y,k) - U(_p1x,y,k)),
+ Uy = 0.5f*(U(x,_n1y,k) - U(x,_p1y,k)),
+ Uxx = U(_n1x,y,k) + U(_p1x,y,k) - 2*U(x,y,k),
+ Uyy = U(x,_n1y,k) + U(x,_p1y,k) - 2*U(x,y,k);
+ nEnergy += (float)(smoothness*(Ux*Ux + Uy*Uy));
+ Tfloat deltaIgrad = 0;
+ cimg_forV(I1,i) {
+ const Tfloat deltaIi = (float)(I2._linear_atXY(X,Y,i) - I1(x,y,i));
+ nEnergy += (float)(deltaIi*deltaIi/2);
+ deltaIgrad+=-deltaIi*dI[k]._linear_atXY(X,Y,i);
+ }
+ veloc(x,y,k) = deltaIgrad + smoothness*(Uxx + Uyy);
+ }
+ }
+ }
+ const Tfloat vmax = cimg::max(cimg::abs(veloc.min()), cimg::abs(veloc.max()));
+ U+=(veloc*=dt/vmax);
+ if (cimg::abs(nEnergy-Energy)<sprecision) break;
+ if (nEnergy<Energy) dt*=0.5f;
+ Energy = nEnergy;
+ }
+ U.transfer_to(U0);
+ }
+ return U0;
+ }
+
+ //@}
+ //-----------------------------
+ //
+ //! \name Matrix and Vectors
+ //@{
+ //-----------------------------
+
+ //! Return a vector with specified coefficients.
+ static CImg<T> vector(const T& a0) {
+ static CImg<T> r(1,1); r[0] = a0;
+ return r;
+ }
+
+ //! Return a vector with specified coefficients.
+ static CImg<T> vector(const T& a0, const T& a1) {
+ static CImg<T> r(1,2); T *ptr = r.data;
+ *(ptr++) = a0; *(ptr++) = a1;
+ return r;
+ }
+
+ //! Return a vector with specified coefficients.
+ static CImg<T> vector(const T& a0, const T& a1, const T& a2) {
+ static CImg<T> r(1,3); T *ptr = r.data;
+ *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2;
+ return r;
+ }
+
+ //! Return a vector with specified coefficients.
+ static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3) {
+ static CImg<T> 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<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
+ static CImg<T> 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<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
+ static CImg<T> 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<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
+ const T& a4, const T& a5, const T& a6) {
+ static CImg<T> 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<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
+ const T& a4, const T& a5, const T& a6, const T& a7) {
+ static CImg<T> 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<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
+ const T& a4, const T& a5, const T& a6, const T& a7,
+ const T& a8) {
+ static CImg<T> 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<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
+ const T& a4, const T& a5, const T& a6, const T& a7,
+ const T& a8, const T& a9) {
+ static CImg<T> 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<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
+ const T& a4, const T& a5, const T& a6, const T& a7,
+ const T& a8, const T& a9, const T& a10) {
+ static CImg<T> 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<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
+ const T& a4, const T& a5, const T& a6, const T& a7,
+ const T& a8, const T& a9, const T& a10, const T& a11) {
+ static CImg<T> 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<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
+ const T& a4, const T& a5, const T& a6, const T& a7,
+ const T& a8, const T& a9, const T& a10, const T& a11,
+ const T& a12) {
+ static CImg<T> 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<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
+ const T& a4, const T& a5, const T& a6, const T& a7,
+ const T& a8, const T& a9, const T& a10, const T& a11,
+ const T& a12, const T& a13) {
+ static CImg<T> 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<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
+ const T& a4, const T& a5, const T& a6, const T& a7,
+ const T& a8, const T& a9, const T& a10, const T& a11,
+ const T& a12, const T& a13, const T& a14) {
+ static CImg<T> 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<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
+ const T& a4, const T& a5, const T& a6, const T& a7,
+ const T& a8, const T& a9, const T& a10, const T& a11,
+ const T& a12, const T& a13, const T& a14, const T& a15) {
+ static CImg<T> 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<T> matrix(const T& a0) {
+ return vector(a0);
+ }
+
+ //! Return a 2x2 square matrix with specified coefficients.
+ static CImg<T> matrix(const T& a0, const T& a1,
+ const T& a2, const T& a3) {
+ static CImg<T> 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<T> matrix(const T& a0, const T& a1, const T& a2,
+ const T& a3, const T& a4, const T& a5,
+ const T& a6, const T& a7, const T& a8) {
+ static CImg<T> 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<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3,
+ const T& a4, const T& a5, const T& a6, const T& a7,
+ const T& a8, const T& a9, const T& a10, const T& a11,
+ const T& a12, const T& a13, const T& a14, const T& a15) {
+ static CImg<T> 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<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4,
+ const T& a5, const T& a6, const T& a7, const T& a8, const T& a9,
+ const T& a10, const T& a11, const T& a12, const T& a13, const T& a14,
+ const T& a15, const T& a16, const T& a17, const T& a18, const T& a19,
+ const T& a20, const T& a21, const T& a22, const T& a23, const T& a24) {
+ static CImg<T> 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<T> tensor(const T& a1) {
+ return matrix(a1);
+ }
+
+ //! Return a 2x2 symmetric matrix tensor with specified coefficients.
+ static CImg<T> 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<T> 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<T> diagonal(const T& a0) {
+ return matrix(a0);
+ }
+
+ //! Return a 2x2 diagonal matrix with specified coefficients.
+ static CImg<T> diagonal(const T& a0, const T& a1) {
+ return matrix(a0,0,0,a1);
+ }
+
+ //! Return a 3x3 diagonal matrix with specified coefficients.
+ static CImg<T> 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<T> 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<T> 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<T> identity_matrix(const unsigned int N) {
+ CImg<T> 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<T> sequence(const unsigned int N, const T a0, const T a1) {
+ if (N) return CImg<T>(1,N).sequence(a0,a1);
+ return CImg<T>();
+ }
+
+ //! Return a 3x3 rotation matrix along the (x,y,z)-axis with an angle w.
+ static CImg<T> 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<T>::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<T> get_vector_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const {
+ static CImg<T> 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<typename t>
+ CImg<T>& set_vector_at(const CImg<t>& vec, const unsigned int x, const unsigned int y=0, const unsigned int z=0) {
+ if (x<width && y<height && z<depth) {
+ const unsigned int whz = width*height*depth;
+ const t *ptrs = vec.data;
+ T *ptrd = ptr(x,y,z);
+ for (unsigned int k=cimg::min((unsigned int)vec.size(),dim); k; --k) { *ptrd = (T)*(ptrs++); ptrd+=whz; }
+ }
+ return *this;
+ }
+
+ //! Return a new image corresponding to the \a square \a matrix located at (\p x,\p y,\p z) of the current vector-valued image.
+ CImg<T> 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<T> 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<typename t>
+ CImg<T>& set_matrix_at(const CImg<t>& 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<T> 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<typename t>
+ CImg<T>& set_tensor_at(const CImg<t>& 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<T>& vector() {
+ return unroll('y');
+ }
+
+ CImg<T> get_vector() const {
+ return get_unroll('y');
+ }
+
+ //! Realign pixel values of the instance image as a square matrix
+ CImg<T>& 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<siz) { i2+=2*i+1; ++i; }
+ if (i2==siz) width = height = i;
+ else throw CImgInstanceException("CImg<%s>::matrix() : Image size = %u is not a square number",
+ pixel_type(),siz);
+ }
+ }
+ return *this;
+ }
+
+ CImg<T> get_matrix() const {
+ return (+*this).matrix();
+ }
+
+ //! Realign pixel values of the instance image as a symmetric tensor.
+ CImg<T>& tensor() {
+ return get_tensor().transfer_to(*this);
+ }
+
+ CImg<T> get_tensor() const {
+ CImg<T> 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<T>& 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<T> 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<T>& diagonal() {
+ return get_diagonal().transfer_to(*this);
+ }
+
+ CImg<T> get_diagonal() const {
+ if (is_empty()) return *this;
+ CImg<T> 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<T>& identity_matrix() {
+ return identity_matrix(cimg::max(width,height)).transfer_to(*this);
+ }
+
+ CImg<T> get_identity_matrix() const {
+ return identity_matrix(cimg::max(width,height));
+ }
+
+ //! Return a N-numbered sequence vector from \p a0 to \p a1.
+ CImg<T>& 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<T> get_sequence(const T a0, const T a1) const {
+ return (+*this).sequence(a0,a1);
+ }
+
+ //! Transpose the current matrix.
+ CImg<T>& 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<dimx(); ++x) cimg::swap((*this)(x,y,z,v),(*this)(y,x,z,v));
+ return *this;
+ }
+ return get_transpose().transfer_to(*this);
+ }
+
+ CImg<T> get_transpose() const {
+ return get_permute_axes("yxzv");
+ }
+
+ //! Invert the current matrix.
+ CImg<T>& 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<Tfloat> 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<Tfloat> 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<Tfloat> get_invert(const bool use_LU=true) const {
+ return CImg<Tfloat>(*this,false).invert(use_LU);
+ }
+
+ //! Compute the pseudo-inverse (Moore-Penrose) of the matrix.
+ CImg<T>& pseudoinvert() {
+ return get_pseudoinvert().transfer_to(*this);
+ }
+
+ CImg<Tfloat> get_pseudoinvert() const {
+ CImg<Tfloat> 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<typename t>
+ CImg<T>& cross(const CImg<t>& 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<typename t>
+ CImg<typename cimg::superset<T,t>::type> get_cross(const CImg<t>& img) const {
+ typedef typename cimg::superset<T,t>::type Tt;
+ return CImg<Tt>(*this).cross(img);
+ }
+
+ //! Solve a linear system AX=B where B=*this.
+ template<typename t>
+ CImg<T>& solve(const CImg<t>& 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<T,t,float>::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<Ttfloat> lu(A);
+ CImg<Ttfloat> indx;
+ bool d;
+ lu._LU(indx,d);
+ _solve(lu,indx);
+#endif
+ } else assign(A.get_pseudoinvert()*(*this));
+ return *this;
+ }
+
+ template<typename t>
+ CImg<typename cimg::superset2<T,t,float>::type> get_solve(const CImg<t>& A) const {
+ typedef typename cimg::superset2<T,t,float>::type Ttfloat;
+ return CImg<Ttfloat>(*this,false).solve(A);
+ }
+
+ template<typename t, typename ti>
+ CImg<T>& _solve(const CImg<t>& A, const CImg<ti>& indx) {
+ typedef typename cimg::superset2<T,t,float>::type Ttfloat;
+ const int N = size();
+ int ii = -1;
+ Ttfloat sum;
+ for (int i=0; i<N; ++i) {
+ const int ip = (int)indx[i];
+ Ttfloat sum = (*this)(ip);
+ (*this)(ip) = (*this)(i);
+ if (ii>=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<N; ++j) sum-=A(j,i)*(*this)(j);
+ (*this)(i) = (T)(sum/A(i,i));
+ }}
+ return *this;
+ }
+
+ //! Solve a linear system AX=B where B=*this and A is a tridiagonal matrix A = [ b0,c0,0,...; a1,b1,c1,0,... ; ... ; ...,0,aN,bN ].
+ // (Use the Thomas Algorithm).
+ template<typename t>
+ CImg<T>& solve_tridiagonal(const CImg<t>& a, const CImg<t>& b, const CImg<t>& 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<T,t,float>::type Ttfloat;
+ CImg<Ttfloat> 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<siz; ++i) {
+ const Ttfloat
+ vala = (Tfloat)*(ptra++),
+ id = 1/(*(ptrb++) - *(ptrnc++)*vala);
+ *ptrnc = *(ptrc++)*id;
+ vald = ((*ptrd-=vala*vald)*=id);
+ ++ptrd;
+ }
+ vald = *(--ptrd);
+ for (int i = siz-2; i>=0; --i) vald = (*(--ptrd)-=*(--ptrnc)*vald);
+ return *this;
+ }
+
+ template<typename t>
+ CImg<typename cimg::superset2<T,t,float>::type> get_solve_tridiagonal(const CImg<t>& a, const CImg<t>& b, const CImg<t>& c) const {
+ typedef typename cimg::superset2<T,t,float>::type Ttfloat;
+ return CImg<Ttfloat>(*this,false).solve_tridiagonal(a,b,c);
+ }
+
+ //! Sort values of a vector and get permutations.
+ template<typename t>
+ CImg<T>& sort(CImg<t>& 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<typename t>
+ CImg<T> get_sort(CImg<t>& permutations, const bool increasing=true) const {
+ return (+*this).sort(permutations,increasing);
+ }
+
+ // Sort image values.
+ CImg<T>& sort(const bool increasing=true) {
+ CImg<T> foo;
+ return sort(foo,increasing);
+ }
+
+ CImg<T> get_sort(const bool increasing=true) const {
+ return (+*this).sort(increasing);
+ }
+
+ template<typename t>
+ CImg<T>& _quicksort(const int min, const int max, CImg<t>& permutations, const bool increasing) {
+ if (min<max) {
+ const int mid = (min+max)/2;
+ if (increasing) {
+ 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]); }
+ } 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) ++i;
+ while ((*this)[j]>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]<pivot) --j;
+ if (i<=j) {
+ cimg::swap((*this)[i],(*this)[j]);
+ cimg::swap(permutations[i++],permutations[j--]);
+ }
+ } while (i<=j);
+ }
+ if (min<j) _quicksort(min,j,permutations,increasing);
+ if (i<max) _quicksort(i,max,permutations,increasing);
+ }
+ }
+ return *this;
+ }
+
+ //! Get a permutation of the pixels.
+ template<typename t>
+ CImg<T>& permute(const CImg<t>& permutation) {
+ return get_permute(permutation).transfer_to(*this);
+ }
+
+ template<typename t>
+ CImg<T> get_permute(const CImg<t>& 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<T> 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<typename t>
+ const CImg<T>& SVD(CImg<t>& U, CImg<t>& S, CImg<t>& 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<delta; ++i) U(i,i) = (t)(U(i,i) + lambda);
+ }
+ if (S.size()<width) S.assign(1,width);
+ if (V.width<width || V.height<height) V.assign(width,width);
+ CImg<t> 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<dimy()) {
+ for (int k=i; k<dimy(); ++k) scale+= cimg::abs(U(i,k));
+ if (scale) {
+ for (int k=i; k<dimy(); ++k) { U(i,k)/=scale; s+= U(i,k)*U(i,k); }
+ f = U(i,i); g = (t)((f>=0?-1:1)*cimg_std::sqrt(s)); h=f*g-s; U(i,i) = f-g;
+ for (int j=l; j<dimx(); ++j) {
+ s = 0; for (int k=i; k<dimy(); ++k) s+= U(i,k)*U(j,k);
+ f = s/h;
+ { for (int k=i; k<dimy(); ++k) U(j,k)+= f*U(i,k); }
+ }
+ { for (int k=i; k<dimy(); ++k) U(i,k)*= scale; }
+ }
+ }
+ S[i]=scale*g;
+
+ g = s = scale = 0;
+ if (i<dimy() && i!=dimx()-1) {
+ for (int k=l; k<dimx(); ++k) scale += cimg::abs(U(k,i));
+ if (scale) {
+ for (int k=l; k<dimx(); ++k) { U(k,i)/= scale; s+= U(k,i)*U(k,i); }
+ f = U(l,i); g = (t)((f>=0?-1:1)*cimg_std::sqrt(s)); h = f*g-s; U(l,i) = f-g;
+ { for (int k=l; k<dimx(); ++k) rv1[k]=U(k,i)/h; }
+ for (int j=l; j<dimy(); ++j) {
+ s = 0; for (int k=l; k<dimx(); ++k) s+= U(k,j)*U(k,i);
+ { for (int k=l; k<dimx(); ++k) U(k,j)+= s*rv1[k]; }
+ }
+ { for (int k=l; k<dimx(); ++k) U(k,i)*= scale; }
+ }
+ }
+ anorm = (t)cimg::max((float)anorm,(float)(cimg::abs(S[i])+cimg::abs(rv1[i])));
+ }
+
+ { for (int i=dimx()-1; i>=0; --i) {
+ if (i<dimx()-1) {
+ if (g) {
+ { for (int j=l; j<dimx(); ++j) V(i,j) =(U(j,i)/U(l,i))/g; }
+ for (int j=l; j<dimx(); ++j) {
+ s = 0; for (int k=l; k<dimx(); ++k) s+= U(k,i)*V(j,k);
+ { for (int k=l; k<dimx(); ++k) V(j,k)+= s*V(i,k); }
+ }
+ }
+ for (int j=l; j<dimx(); ++j) V(j,i) = V(i,j) = (t)0.0;
+ }
+ V(i,i) = (t)1.0; g = rv1[i]; l = i;
+ }
+ }
+
+ { for (int i=cimg::min(dimx(),dimy())-1; i>=0; --i) {
+ l = i+1; g = S[i];
+ for (int j=l; j<dimx(); ++j) U(j,i) = 0;
+ if (g) {
+ g = 1/g;
+ for (int j=l; j<dimx(); ++j) {
+ s = 0; for (int k=l; k<dimy(); ++k) s+= U(i,k)*U(j,k);
+ f = (s/U(i,i))*g;
+ { for (int k=i; k<dimy(); ++k) U(j,k)+= f*U(i,k); }
+ }
+ { for (int j=i; j<dimy(); ++j) U(i,j)*= g; }
+ } else for (int j=i; j<dimy(); ++j) U(i,j) = 0;
+ ++U(i,i);
+ }
+ }
+
+ for (int k=dimx()-1; k>=0; --k) {
+ for (unsigned int its=0; its<max_iter; ++its) {
+ bool flag = true;
+ for (l=k; l>=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<intT> permutations(width);
+ CImg<t> 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<typename t>
+ const CImg<T>& SVD(CImgList<t>& 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<Tfloat> get_SVD(const bool sorting=true) const {
+ CImgList<Tfloat> 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<typename t>
+ CImg<T>& _LU(CImg<t>& indx, bool& d) {
+ const int N = dimx();
+ int imax = 0;
+ CImg<Tfloat> 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<j; ++i) {
+ Tfloat sum=(*this)(j,i);
+ for (int k=0; k<i; ++k) sum-=(*this)(k,i)*(*this)(j,k);
+ (*this)(j,i) = (T)sum;
+ }
+ Tfloat vmax = 0;
+ { for (int i=j; i<dimx(); ++i) {
+ Tfloat sum=(*this)(j,i);
+ for (int k=0; k<j; ++k) sum-=(*this)(k,i)*(*this)(j,k);
+ (*this)(j,i) = (T)sum;
+ const Tfloat tmp = vv[i]*cimg::abs(sum);
+ if (tmp>=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<N) {
+ const Tfloat tmp = 1/(Tfloat)(*this)(j,j);
+ for (int i=j+1; i<N; ++i) (*this)(j,i) = (T)((*this)(j,i)*tmp);
+ }
+ }
+ return *this;
+ }
+
+ //! Compute the eigenvalues and eigenvectors of a matrix.
+ template<typename t>
+ const CImg<T>& eigen(CImg<t>& val, CImg<t> &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()<width) val.assign(1,width);
+ if (vec.size()<width*width) vec.assign(width,width);
+ switch (width) {
+ case 1 : { val[0]=(t)(*this)[0]; vec[0]=(t)1; } break;
+ case 2 : {
+ const double a = (*this)[0], b = (*this)[1], c = (*this)[2], d = (*this)[3], e = a+d;
+ double f = e*e-4*(a*d-b*c);
+ if (f<0)
+ cimg::warn("CImg<%s>::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<Tfloat> get_eigen() const {
+ CImgList<Tfloat> res(2);
+ eigen(res[0],res[1]);
+ return res;
+ }
+
+ //! Compute the eigenvalues and eigenvectors of a symmetric matrix.
+ template<typename t>
+ const CImg<T>& symmetric_eigen(CImg<t>& val, CImg<t>& 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<t> 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<intT> permutations(width); // sort eigenvalues in decreasing order
+ CImg<t> 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<Tfloat> get_symmetric_eigen() const {
+ CImgList<Tfloat> res(2);
+ symmetric_eigen(res[0],res[1]);
+ return res;
+ }
+
+ //@}
+ //-------------------
+ //
+ //! \name Display
+ //@{
+ //-------------------
+
+ //! Display an image into a CImgDisplay window.
+ const CImg<T>& 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<T>& 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<T>& display(const char *const title=0, const bool display_info=true) const {
+ CImgDisplay disp;
+ return _display(disp,title,display_info);
+ }
+
+ const CImg<T>& _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<T> 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<T>& visu = zoom?zoom:*this;
+ const CImg<intT> 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<dimx() && mY<dimy()) { X = x0 + mX*(1+x1-x0)/width; Y = y0 + mY*(1+y1-y0)/height; Z = XYZ[2]; }
+ if (mX<dimx() && mY>=dimy()) { X = x0 + mX*(1+x1-x0)/width; Z = z0 + (mY-height)*(1+z1-z0)/depth; Y = XYZ[1]; }
+ if (mX>=dimx() && mY<dimy()) { Y = y0 + mY*(1+y1-y0)/height; Z = z0 + (mX-width)*(1+z1-z0)/depth; X = XYZ[0]; }
+ if (x1-x0>4) { 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+ndelta<dimx()) { x0+=ndelta; x1+=ndelta; }
+ else { x0+=(dimx()-1-x1); x1 = dimx()-1; }
+ }
+ if (go_up) {
+ const int delta = (y1-y0)/5, ndelta = delta?delta:(height>1?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+ndelta<dimy()) { y0+=ndelta; y1+=ndelta; }
+ else { y0+=(dimy()-1-y1); y1 = dimy()-1; }
+ }
+ if (go_inc) {
+ const int delta = (z1-z0)/5, ndelta = delta?delta:(depth>1?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<dimz()) { z0+=ndelta; z1+=ndelta; }
+ else { z0+=(depth-1-z1); z1 = depth-1; }
+ }
+ }
+ disp.key = key;
+ return *this;
+ }
+
+ //! Simple interface to select a shape from an image.
+ /**
+ \param selection Array of 6 values containing the selection result
+ \param coords_type Determine shape type to select (0=point, 1=vector, 2=rectangle, 3=circle)
+ \param disp Display window used to make the selection
+ \param XYZ Initial XYZ position (for volumetric images only)
+ \param color Color of the shape selector.
+ **/
+ CImg<T>& 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<T>& 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<intT> 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<intT> 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<intT> _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<ucharT> 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<dimx() && mY<dimy()) { area = 1; X = mX; Y = mY; Z = phase?Z1:Z0; }
+ if (mX<dimx() && mY>=dimy()) { area = 2; X = mX; Z = mY-height; Y = phase?Y1:Y0; }
+ if (mX>=dimx() && mY<dimy()) { area = 3; Y = mY; Z = mX-width; X = phase?X1:X0; }
+
+ switch (key = disp.key) {
+ case 0 : case cimg::keyCTRLLEFT : disp.key = key = 0; break;
+ case cimg::keyPAGEUP : if (disp.is_keyCTRLLEFT) { ++disp.wheel; key = 0; } break;
+ case cimg::keyPAGEDOWN : if (disp.is_keyCTRLLEFT) { --disp.wheel; 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; visu0.assign();
+ } break;
+ case cimg::keyR : if (disp.is_keyCTRLLEFT) {
+ disp.normalscreen().resize(cimg_fitscreen(width,height,depth),false).is_resized = true;
+ disp.key = key = 0; visu0.assign();
+ } break;
+ case cimg::keyF : if (disp.is_keyCTRLLEFT) {
+ disp.resize(disp.screen_dimx(),disp.screen_dimy(),false).toggle_fullscreen().is_resized = true;
+ disp.key = key = 0; visu0.assign();
+ } break;
+ case cimg::keyS : if (disp.is_keyCTRLLEFT) {
+ 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);
+ if (visu0) {
+ visu.draw_text(2,2,"Saving snapshot...",foreground_color,background_color,0.8f,11).display(disp);
+ visu0.save(filename);
+ visu.draw_text(2,2,"Snapshot '%s' saved.",foreground_color,background_color,0.8f,11,filename).display(disp);
+ }
+ disp.key = key = 0;
+ } break;
+ case cimg::keyO : if (disp.is_keyCTRLLEFT) {
+ static unsigned int snap_number = 0;
+ char filename[32] = { 0 };
+ cimg_std::FILE *file;
+ do {
+ cimg_std::sprintf(filename,"CImg_%.4u.cimg",snap_number++);
+ if ((file=cimg_std::fopen(filename,"r"))!=0) cimg_std::fclose(file);
+ } while (file);
+ visu.draw_text(2,2,"Saving instance...",foreground_color,background_color,0.8f,11).display(disp);
+ save(filename);
+ visu.draw_text(2,2,"Instance '%s' saved.",foreground_color,background_color,0.8f,11,filename).display(disp);
+ disp.key = key = 0;
+ } break;
+ }
+
+ if (!area) mx = my = X = Y = Z = -1;
+ else {
+ if (disp.button&1 && phase<2) { X1 = X; Y1 = Y; Z1 = Z; }
+ if (!(disp.button&1) && phase>=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<Tuchar> 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<T>::is_float()) visu0 = tmp.normalize(0,(T)255);
+ else {
+ const float m = (float)cimg::type<T>::min(), M = (float)cimg::type<T>::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<X1?X0:X1)*disp.width/(width+d), y0 = (Y0<Y1?Y0:Y1)*disp.height/(height+d),
+ x1 = ((X0<X1?X1:X0)+1)*disp.width/(width+d)-1, y1 = ((Y0<Y1?Y1:Y0)+1)*disp.height/(height+d)-1;
+ visu.draw_rectangle(x0,y0,x1,y1,foreground_color,0.2f).draw_rectangle(x0,y0,x1,y1,foreground_color,0.6f,hatch);
+ if (d) {
+ const int
+ zx0 = (int)((width+(Z0<Z1?Z0:Z1))*disp.width/(width+d)),
+ zy0 = (int)((height+(Z0<Z1?Z0:Z1))*disp.height/(height+d)),
+ zx1 = (int)((width+(Z0<Z1?Z1:Z0)+1)*disp.width/(width+d))-1,
+ zy1 = (int)((height+(Z0<Z1?Z1:Z0)+1)*disp.height/(height+d))-1;
+ visu.draw_rectangle(zx0,y0,zx1,y1,foreground_color,0.2f).draw_rectangle(zx0,y0,zx1,y1,foreground_color,0.6f,hatch);
+ visu.draw_rectangle(x0,zy0,x1,zy1,foreground_color,0.2f).draw_rectangle(x0,zy0,x1,zy1,foreground_color,0.6f,hatch);
+ }
+ } break;
+ case 3 : {
+ const int
+ x0 = X0*disp.width/(width+d),
+ y0 = Y0*disp.height/(height+d),
+ x1 = X1*disp.width/(width+d)-1,
+ y1 = Y1*disp.height/(height+d)-1;
+ visu.draw_ellipse(x0,y0,(float)(x1-x0),(float)(y1-y0),1,0,foreground_color,0.2f).
+ draw_ellipse(x0,y0,(float)(x1-x0),(float)(y1-y0),1,0,foreground_color,0.6f,hatch);
+ if (d) {
+ const int
+ zx0 = (int)((width+Z0)*disp.width/(width+d)),
+ zy0 = (int)((height+Z0)*disp.height/(height+d)),
+ zx1 = (int)((width+Z1+1)*disp.width/(width+d))-1,
+ zy1 = (int)((height+Z1+1)*disp.height/(height+d))-1;
+ visu.draw_ellipse(zx0,y0,(float)(zx1-zx0),(float)(y1-y0),1,0,foreground_color,0.2f).
+ draw_ellipse(zx0,y0,(float)(zx1-zx0),(float)(y1-y0),1,0,foreground_color,0.6f,hatch).
+ draw_ellipse(x0,zy0,(float)(x1-x0),(float)(zy1-zy0),1,0,foreground_color,0.2f).
+ draw_ellipse(x0,zy0,(float)(x1-x0),(float)(zy1-zy0),1,0,foreground_color,0.6f,hatch);
+ }
+ } break;
+ } else {
+ const int
+ x0 = X*disp.width/(width+d),
+ y0 = Y*disp.height/(height+d),
+ x1 = (X+1)*disp.width/(width+d)-1,
+ y1 = (Y+1)*disp.height/(height+d)-1;
+ if (x1-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 && X<dimx() && Y<dimy() && Z<dimz()) {
+ if (depth>1) 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<dim && ctext<ltext; ++k) {
+ cimg_std::sprintf(ctext,cimg::type<T>::format(),cimg::type<T>::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+(X0<X1?X0:X1),origY+(Y0<Y1?Y0:Y1),origZ+(Z0<Z1?Z0:Z1),
+ origX+(X0<X1?X1:X0),origY+(Y0<Y1?Y1:Y0),origZ+(Z0<Z1?Z1:Z0),
+ 1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1),1+cimg::abs(Z0-Z1));
+ else cimg_std::sprintf(text,"Box (%d,%d)-(%d,%d), Size = (%d,%d)",
+ origX+(X0<X1?X0:X1),origY+(Y0<Y1?Y0:Y1),origX+(X0<X1?X1:X0),origY+(Y0<Y1?Y1:Y0),
+ 1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1));
+ break;
+ default :
+ if (depth>1) 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<intT> 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<typename tp, typename tf, typename tc, typename to>
+ const CImg<T>& display_object3d(CImgDisplay& disp,
+ const CImg<tp>& points, const CImgList<tf>& primitives,
+ const CImgList<tc>& colors, const to& opacities,
+ const bool centering=true,
+ const int render_static=4, const int render_motion=1,
+ const bool 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<typename tp, typename tf, typename tc, typename to>
+ const CImg<T>& display_object3d(const char *const title,
+ const CImg<tp>& points, const CImgList<tf>& primitives,
+ const CImgList<tc>& colors, const to& opacities,
+ const bool centering=true,
+ const int render_static=4, const int render_motion=1,
+ const bool 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<typename tp, typename tf, typename tc, typename to>
+ const CImg<T>& display_object3d(CImgDisplay& disp,
+ const CImgList<tp>& points, const CImgList<tf>& primitives,
+ const CImgList<tc>& colors, const to& opacities,
+ const bool centering=true,
+ const int render_static=4, const int render_motion=1,
+ const bool 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<typename tp, typename tf, typename tc, typename to>
+ const CImg<T>& display_object3d(const char *const title,
+ const CImgList<tp>& points, const CImgList<tf>& primitives,
+ const CImgList<tc>& colors, const to& opacities,
+ const bool centering=true,
+ const int render_static=4, const int render_motion=1,
+ const bool 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<typename tp, typename tf, typename tc>
+ const CImg<T>& display_object3d(CImgDisplay &disp,
+ const tp& points, const CImgList<tf>& primitives,
+ const CImgList<tc>& 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<floatT>(),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<typename tp, typename tf, typename tc>
+ const CImg<T>& display_object3d(const char *const title,
+ const tp& points, const CImgList<tf>& primitives,
+ const CImgList<tc>& 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<floatT>(),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<typename tp, typename tf>
+ const CImg<T>& display_object3d(CImgDisplay &disp,
+ const tp& points, const CImgList<tf>& 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<T>(),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<typename tp, typename tf>
+ const CImg<T>& display_object3d(const char *const title,
+ const tp& points, const CImgList<tf>& 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<T>(),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<typename tp>
+ const CImg<T>& 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<uintT>(),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<typename tp>
+ const CImg<T>& 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<uintT>(),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<typename tp, typename tf, typename tc, typename to>
+ const CImg<T>& _display_object3d(CImgDisplay& disp, const char *const title,
+ const tp& points, const unsigned int Npoints,
+ const CImgList<tf>& primitives,
+ const CImgList<tc>& 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<T>(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<T>(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<tf> 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<tc> _colors;
+ if (!colors) _colors.insert(primitives.size,CImg<tc>::vector(200,200,200));
+ const CImgList<tc> &ncolors = colors?colors:_colors;
+
+ // Init 3D objects and compute object statistics
+ CImg<floatT>
+ pose, rot_mat, zbuffer,
+ centered_points = centering?CImg<floatT>(Npoints,3):CImg<floatT>(),
+ rotated_points(Npoints,3),
+ bbox_points, rotated_bbox_points,
+ axes_points, rotated_axes_points,
+ bbox_opacities, axes_opacities;
+ CImgList<uintT> bbox_primitives, axes_primitives;
+ CImgList<T> 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<T>
+ background_color(1,1,1,dim,color_model?minval:maxval),
+ foreground_color(1,1,1,dim,color_model?maxval:minval);
+
+ float xm = cimg::type<float>::max(), xM = 0, ym = xm, yM = 0, zm = xm, zM = 0;
+ for (unsigned int i = 0; i<Npoints; ++i) {
+ const float
+ x = points._display_object3d_at2(i,0),
+ y = points._display_object3d_at2(i,1),
+ z = points._display_object3d_at2(i,2);
+ if (x<xm) xm = x;
+ if (x>xM) xM = x;
+ if (y<ym) ym = y;
+ if (y>yM) yM = y;
+ if (z<zm) zm = z;
+ if (z>zM) 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<T> 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<floatT>(pose_matrix,4,4,1,1,false);
+ else pose = CImg<floatT>::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; l<Npoints; ++l) {
+ const float
+ x = (float)points._display_object3d_at2(l,0),
+ y = (float)points._display_object3d_at2(l,1),
+ z = (float)points._display_object3d_at2(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 {
+ if (!centering) cimg_forX(bbox_points,l) {
+ const float x = bbox_points(l,0), y = bbox_points(l,1), z = bbox_points(l,2);
+ rotated_bbox_points(l,0) = r00*x + r10*y + r20*z + r30;
+ rotated_bbox_points(l,1) = r01*x + r11*y + r21*z + r31;
+ rotated_bbox_points(l,2) = r02*x + r12*y + r22*z + r32;
+ } else cimg_forX(bbox_points,l) {
+ const float x = (bbox_points(l,0)-dx)*ratio, y = (bbox_points(l,1)-dy)*ratio, z = (bbox_points(l,2)-dz)*ratio;
+ rotated_bbox_points(l,0) = r00*x + r10*y + r20*z + r30;
+ rotated_bbox_points(l,1) = r01*x + r11*y + r21*z + r31;
+ rotated_bbox_points(l,2) = r02*x + r12*y + r22*z + r32;
+ }
+ }
+
+ // Draw object
+ visu = visu0;
+ if ((clicked && render_motion<0) || (!clicked && render_static<0))
+ visu.draw_object3d(visu.width/2.0f,visu.height/2.0f,0,rotated_bbox_points,bbox_primitives,bbox_colors,bbox_opacities,2,false,focale).
+ draw_object3d(visu.width/2.0f,visu.height/2.0f,0,rotated_bbox_points,bbox_primitives,bbox_colors2,1,false,focale);
+ else visu.draw_object3d(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,
+ (!clicked && render_static>0)?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<floatT>::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<floatT>::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<T>& display_graph(CImgDisplay &disp,
+ const unsigned int plot_type=1, const unsigned int vertex_type=1,
+ const char *const labelx=0, const double xmin=0, const double xmax=0,
+ const char *const labely=0, const double ymin=0, const double ymax=0) const {
+ 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<T> zoom(x1-x0+1,1,1,dimv());
+ cimg_forV(*this,k) zoom.get_shared_channel(k) = CImg<T>(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<intT> 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<T>& display_graph(const char *const title=0,
+ const unsigned int plot_type=1, const unsigned int vertex_type=1,
+ const char *const labelx=0, const double xmin=0, const double xmax=0,
+ const char *const labely=0, const double ymin=0, const double ymax=0) const {
+ 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<intT> get_select_graph(CImgDisplay &disp,
+ const unsigned int plot_type=1, const unsigned int vertex_type=1,
+ const char *const labelx=0, const double xmin=0, const double xmax=0,
+ const char *const labely=0, const double ymin=0, const double ymax=0) const {
+ 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<ucharT> palette;
+ if (odimv!=dim) {
+ odimv = dim;
+ palette = CImg<ucharT>(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<ucharT> 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<Tdouble>
+ seqx = CImg<Tdouble>::sequence(1 + gdimx/60,nxmin,nxmax).round(px),
+ seqy = CImg<Tdouble>::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<visu.dimx()-16 && mouse_y<visu.dimy()-16) {
+ if (graph) visu.draw_line(mouse_x,16,mouse_x,visu.dimy()-17,black,0.5f,0x55555555U);
+ const unsigned x = (mouse_x-16)*whz/(disp.dimx()-32);
+ const double cx = nxmin + x*(nxmax-nxmin)/whz;
+ if (dim>=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<ucharT> &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<x0) cimg::swap(x0,x1);
+ if (y1<y0) cimg::swap(y0,y1);
+ disp.key = okey;
+ return CImg<intT>(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<T>::get_load() will try to load a .cimg file.
+ **/
+ CImg<T>& 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<T> get_load(const char *const filename) {
+ return CImg<T>().load(filename);
+ }
+
+ //! Load an image from an ASCII file.
+ CImg<T>& load_ascii(const char *const filename) {
+ return _load_ascii(0,filename);
+ }
+
+ static CImg<T> get_load_ascii(const char *const filename) {
+ return CImg<T>().load_ascii(filename);
+ }
+
+ //! Load an image from an ASCII file.
+ CImg<T>& load_ascii(cimg_std::FILE *const file) {
+ return _load_ascii(file,0);
+ }
+
+ static CImg<T> get_load_ascii(cimg_std::FILE *const file) {
+ return CImg<T>().load_ascii(file);
+ }
+
+ CImg<T>& _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<siz && err==1; ++off) {
+ err = cimg_std::fscanf(nfile,"%lf%*[^0-9.+-]",&val);
+ *(ptr++) = (T)val;
+ }
+ if (err!=1)
+ cimg::warn("CImg<%s>::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<T>& load_dlm(const char *const filename) {
+ return _load_dlm(0,filename);
+ }
+
+ static CImg<T> get_load_dlm(const char *const filename) {
+ return CImg<T>().load_dlm(filename);
+ }
+
+ //! Load an image from a DLM file.
+ CImg<T>& load_dlm(cimg_std::FILE *const file) {
+ return _load_dlm(file,0);
+ }
+
+ static CImg<T> get_load_dlm(cimg_std::FILE *const file) {
+ return CImg<T>().load_dlm(file);
+ }
+
+ CImg<T>& _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<T>& load_bmp(const char *const filename) {
+ return _load_bmp(0,filename);
+ }
+
+ static CImg<T> get_load_bmp(const char *const filename) {
+ return CImg<T>().load_bmp(filename);
+ }
+
+ //! Load an image from a BMP file.
+ CImg<T>& load_bmp(cimg_std::FILE *const file) {
+ return _load_bmp(file,0);
+ }
+
+ static CImg<T> get_load_bmp(cimg_std::FILE *const file) {
+ return CImg<T>().load_bmp(file);
+ }
+
+ CImg<T>& _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<<bpp; } else nb_colors = 0;
+ if (nb_colors) { palette = new int[nb_colors]; cimg::fread(palette,nb_colors,nfile); }
+ const int xoffset = offset-54-4*nb_colors;
+ if (xoffset>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<T>& load_jpeg(const char *const filename) {
+ return _load_jpeg(0,filename);
+ }
+
+ static CImg<T> get_load_jpeg(const char *const filename) {
+ return CImg<T>().load_jpeg(filename);
+ }
+
+ //! Load an image from a JPEG file.
+ CImg<T>& load_jpeg(cimg_std::FILE *const file) {
+ return _load_jpeg(file,0);
+ }
+
+ static CImg<T> get_load_jpeg(cimg_std::FILE *const file) {
+ return CImg<T>().load_jpeg(file);
+ }
+
+ CImg<T>& _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 <chris_hormann@gmx.de>
+ // This is experimental code, not much tested, use with care.
+ CImg<T>& 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<T> get_load_magick(const char *const filename) {
+ return CImg<T>().load_magick(filename);
+ }
+
+ //! Load an image from a PNG file.
+ CImg<T>& load_png(const char *const filename) {
+ return _load_png(0,filename);
+ }
+
+ static CImg<T> get_load_png(const char *const filename) {
+ return CImg<T>().load_png(filename);
+ }
+
+ //! Load an image from a PNG file.
+ CImg<T>& load_png(cimg_std::FILE *const file) {
+ return _load_png(file,0);
+ }
+
+ static CImg<T> get_load_png(cimg_std::FILE *const file) {
+ return CImg<T>().load_png(file);
+ }
+
+ // (Note : Most of this function has been written by Eric Fausett)
+ CImg<T>& _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<H; ++row) imgData[row] = new png_byte[byte_depth*4*W];
+ png_read_image(png_ptr,imgData);
+ png_read_end(png_ptr,end_info);
+
+ // Read pixel data
+ if (!(new_color_type==PNG_COLOR_TYPE_RGB || new_color_type==PNG_COLOR_TYPE_RGB_ALPHA)) {
+ 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 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<T>& load_pnm(const char *const filename) {
+ return _load_pnm(0,filename);
+ }
+
+ static CImg<T> get_load_pnm(const char *const filename) {
+ return CImg<T>().load_pnm(filename);
+ }
+
+ //! Load an image from a PNM file.
+ CImg<T>& load_pnm(cimg_std::FILE *const file) {
+ return _load_pnm(file,0);
+ }
+
+ static CImg<T> get_load_pnm(cimg_std::FILE *const file) {
+ return CImg<T>().load_pnm(file);
+ }
+
+ CImg<T>& _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<ucharT> 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<ushortT> 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<ucharT> 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<ushortT> 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<T>& load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
+ return _load_rgb(0,filename,dimw,dimh);
+ }
+
+ static CImg<T> get_load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
+ return CImg<T>().load_rgb(filename,dimw,dimh);
+ }
+
+ //! Load an image from a RGB file.
+ CImg<T>& 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<T> get_load_rgb(cimg_std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
+ return CImg<T>().load_rgb(file,dimw,dimh);
+ }
+
+ CImg<T>& _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<ucharT> 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<T>& load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
+ return _load_rgba(0,filename,dimw,dimh);
+ }
+
+ static CImg<T> get_load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
+ return CImg<T>().load_rgba(filename,dimw,dimh);
+ }
+
+ //! Load an image from a RGBA file.
+ CImg<T>& 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<T> get_load_rgba(cimg_std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
+ return CImg<T>().load_rgba(file,dimw,dimh);
+ }
+
+ CImg<T>& _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<ucharT> 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<T>& load_tiff(const char *const filename,
+ const unsigned int first_frame=0, const unsigned int last_frame=~0U,
+ const unsigned int step_frame=1) {
+ if (!filename)
+ throw CImgArgumentException("CImg<%s>::load_tiff() : Cannot load (null) filename.",
+ pixel_type());
+ const unsigned int
+ nfirst_frame = first_frame<last_frame?first_frame:last_frame,
+ nstep_frame = step_frame?step_frame:1;
+ unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
+
+#ifndef cimg_use_tiff
+ if (nfirst_frame || nlast_frame!=~0U || nstep_frame>1)
+ 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<T> 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<T> get_load_tiff(const char *const filename,
+ const unsigned int first_frame=0, const unsigned int last_frame=~0U,
+ const unsigned int step_frame=1) {
+ return CImg<T>().load_tiff(filename,first_frame,last_frame,step_frame);
+ }
+
+ // (Original contribution by Jerome Boulanger).
+#ifdef cimg_use_tiff
+ CImg<T>& _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<ny; row+=th)
+ for (unsigned int col = 0; col<nx; col+=tw) {
+ if (TIFFReadTile(tif,buf,col,row,0,0)<0) {
+ _TIFFfree(buf); TIFFClose(tif);
+ throw CImgException("CImg<%s>::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<cimg::min((unsigned int)(row+th),(unsigned int)ny); ++rr)
+ for (unsigned int cc = col; cc<cimg::min((unsigned int)(col+tw),(unsigned int)nx); ++cc)
+ for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
+ (*this)(cc,rr,vv) = (T)(float)(ptr[(rr-row)*th*samplesperpixel + (cc-col)*samplesperpixel + vv]);
+ }
+ }
+ _TIFFfree(buf);
+ }
+ } break;
+ case 16 : {
+ unsigned short *buf = (unsigned short*)_TIFFmalloc(TIFFTileSize(tif));
+ if (buf) {
+ for (unsigned int row = 0; row<ny; row+=th)
+ for (unsigned int col = 0; col<nx; col+=tw) {
+ if (TIFFReadTile(tif,buf,col,row,0,0)<0) {
+ _TIFFfree(buf); TIFFClose(tif);
+ throw CImgException("CImg<%s>::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<cimg::min((unsigned int)(row+th),(unsigned int)ny); ++rr)
+ for (unsigned int cc = col; cc<cimg::min((unsigned int)(col+tw),(unsigned int)nx); ++cc)
+ for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
+ (*this)(cc,rr,vv) = (T)(float)(ptr[(rr-row)*th*samplesperpixel + (cc-col)*samplesperpixel + vv]);
+ }
+ }
+ _TIFFfree(buf);
+ }
+ } break;
+ case 32 : {
+ float *buf = (float*)_TIFFmalloc(TIFFTileSize(tif));
+ if (buf) {
+ for (unsigned int row = 0; row<ny; row+=th)
+ for (unsigned int col = 0; col<nx; col+=tw) {
+ if (TIFFReadTile(tif,buf,col,row,0,0)<0) {
+ _TIFFfree(buf); TIFFClose(tif);
+ throw CImgException("CImg<%s>::load_tiff() : File '%s', an error occure while reading a tile.",
+ pixel_type(),filename);
+ } else {
+ float *ptr = buf;
+ for (unsigned int rr = row; rr<cimg::min((unsigned int)(row+th),(unsigned int)ny); ++rr)
+ for (unsigned int cc = col; cc<cimg::min((unsigned int)(col+tw),(unsigned int)nx); ++cc)
+ for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
+ (*this)(cc,rr,vv) = (T)(float)(ptr[(rr-row)*th*samplesperpixel + (cc-col)*samplesperpixel + vv]);
+ }
+ }
+ _TIFFfree(buf);
+ }
+ } break;
+ } else switch (bitspersample) {
+ case 8 : {
+ unsigned char *buf = (unsigned char*)_TIFFmalloc(TIFFTileSize(tif));
+ if (buf) {
+ for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
+ for (unsigned int row = 0; row<ny; row+=th)
+ for (unsigned int col = 0; col<nx; col+=tw) {
+ if (TIFFReadTile(tif,buf,col,row,0,vv)<0) {
+ _TIFFfree(buf); TIFFClose(tif);
+ throw CImgException("CImg<%s>::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<cimg::min((unsigned int)(row+th),(unsigned int)ny); ++rr)
+ for (unsigned int cc = col; cc<cimg::min((unsigned int)(col+tw),(unsigned int)nx); ++cc)
+ (*this)(cc,rr,vv) = (T)(float)*(ptr++);
+ }
+ }
+ _TIFFfree(buf);
+ }
+ } break;
+ case 16 : {
+ unsigned short *buf = (unsigned short*)_TIFFmalloc(TIFFTileSize(tif));
+ if (buf) {
+ for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
+ for (unsigned int row = 0; row<ny; row+=th)
+ for (unsigned int col = 0; col<nx; col+=tw) {
+ if (TIFFReadTile(tif,buf,col,row,0,vv)<0) {
+ _TIFFfree(buf); TIFFClose(tif);
+ throw CImgException("CImg<%s>::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<cimg::min((unsigned int)(row+th),(unsigned int)ny); ++rr)
+ for (unsigned int cc = col; cc<cimg::min((unsigned int)(col+tw),(unsigned int)nx); ++cc)
+ (*this)(cc,rr,vv) = (T)(float)*(ptr++);
+ }
+ }
+ _TIFFfree(buf);
+ }
+ } break;
+ case 32 : {
+ float *buf = (float*)_TIFFmalloc(TIFFTileSize(tif));
+ if (buf) {
+ for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
+ for (unsigned int row = 0; row<ny; row+=th)
+ for (unsigned int col = 0; col<nx; col+=tw) {
+ if (TIFFReadTile(tif,buf,col,row,0,vv)<0) {
+ _TIFFfree(buf); TIFFClose(tif);
+ throw CImgException("CImg<%s>::load_tiff() : File '%s', an error occure while reading a tile.",
+ pixel_type(),filename);
+ } else {
+ float *ptr = buf;
+ for (unsigned int rr = row; rr<cimg::min((unsigned int)(row+th),(unsigned int)ny); ++rr)
+ for (unsigned int cc = col; cc<cimg::min((unsigned int)(col+tw),(unsigned int)nx); ++cc)
+ (*this)(cc,rr,vv) = (T)(float)*(ptr++);
+ }
+ }
+ _TIFFfree(buf);
+ }
+ } break;
+ }
+ } else {
+ if (config==PLANARCONFIG_CONTIG) switch (bitspersample) {
+ case 8 : {
+ unsigned char *buf = (unsigned char*)_TIFFmalloc(TIFFStripSize(tif));
+ if (buf) {
+ uint32 row, rowsperstrip = (uint32)-1;
+ TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
+ for (row = 0; row<ny; row+= rowsperstrip) {
+ uint32 nrow = (row+rowsperstrip>ny?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; rr<nrow; ++rr)
+ for (unsigned int cc = 0; cc<nx; ++cc)
+ for (unsigned int vv = 0; vv<samplesperpixel; ++vv) (*this)(cc,row+rr,vv) = (T)(float)*(ptr++);
+ }
+ _TIFFfree(buf);
+ }
+ } break;
+ case 16 : {
+ unsigned short *buf = (unsigned short*)_TIFFmalloc(TIFFStripSize(tif));
+ if (buf) {
+ uint32 row, rowsperstrip = (uint32)-1;
+ TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
+ for (row = 0; row<ny; row+= rowsperstrip) {
+ uint32 nrow = (row+rowsperstrip>ny?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; rr<nrow; ++rr)
+ for (unsigned int cc = 0; cc<nx; ++cc)
+ for (unsigned int vv = 0; vv<samplesperpixel; ++vv) (*this)(cc,row+rr,vv) = (T)(float)*(ptr++);
+ }
+ _TIFFfree(buf);
+ }
+ } break;
+ case 32 : {
+ float *buf = (float*)_TIFFmalloc(TIFFStripSize(tif));
+ if (buf) {
+ uint32 row, rowsperstrip = (uint32)-1;
+ TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
+ for (row = 0; row<ny; row+= rowsperstrip) {
+ uint32 nrow = (row+rowsperstrip>ny?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; rr<nrow; ++rr)
+ for (unsigned int cc = 0; cc<nx; ++cc)
+ for (unsigned int vv = 0; vv<samplesperpixel; ++vv) (*this)(cc,row+rr,vv) = (T)(float)*(ptr++);
+ }
+ _TIFFfree(buf);
+ }
+ } break;
+ } else switch (bitspersample){
+ case 8 : {
+ unsigned char *buf = (unsigned char*)_TIFFmalloc(TIFFStripSize(tif));
+ if (buf) {
+ uint32 row, rowsperstrip = (uint32)-1;
+ TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
+ for (unsigned int vv=0; vv<samplesperpixel; ++vv)
+ for (row = 0; row<ny; row+= rowsperstrip) {
+ uint32 nrow = (row+rowsperstrip>ny?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;rr<nrow; ++rr)
+ for (unsigned int cc = 0; cc<nx; ++cc)
+ (*this)(cc,row+rr,vv) = (T)(float)*(ptr++);
+ }
+ _TIFFfree(buf);
+ }
+ } break;
+ case 16 : {
+ unsigned short *buf = (unsigned short*)_TIFFmalloc(TIFFStripSize(tif));
+ if (buf) {
+ uint32 row, rowsperstrip = (uint32)-1;
+ TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
+ for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
+ for (row = 0; row<ny; row+= rowsperstrip) {
+ uint32 nrow = (row+rowsperstrip>ny?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; rr<nrow; ++rr)
+ for (unsigned int cc = 0; cc<nx; ++cc)
+ (*this)(cc,row+rr,vv) = (T)(float)*(ptr++);
+ }
+ _TIFFfree(buf);
+ }
+ } break;
+ case 32 : {
+ float *buf = (float*)_TIFFmalloc(TIFFStripSize(tif));
+ if (buf) {
+ uint32 row, rowsperstrip = (uint32)-1;
+ TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
+ for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
+ for (row = 0; row<ny; row+= rowsperstrip) {
+ uint32 nrow = (row+rowsperstrip>ny?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<nrow; ++rr) for (unsigned int cc = 0; cc<nx; ++cc)
+ (*this)(cc,row+rr,vv) = (T)(float)*(ptr++);
+ }
+ _TIFFfree(buf);
+ }
+ } break;
+ }
+ }
+ } else {
+ uint32* raster = (uint32*)_TIFFmalloc(nx * ny * sizeof (uint32));
+ if (!raster) {
+ _TIFFfree(raster); TIFFClose(tif);
+ throw CImgException("CImg<%s>::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<T>& load_analyze(const char *const filename, float *const voxsize=0) {
+ return _load_analyze(0,filename,voxsize);
+ }
+
+ static CImg<T> get_load_analyze(const char *const filename, float *const voxsize=0) {
+ return CImg<T>().load_analyze(filename,voxsize);
+ }
+
+ //! Load an image from an ANALYZE7.5/NIFTI file.
+ CImg<T>& load_analyze(cimg_std::FILE *const file, float *const voxsize=0) {
+ return _load_analyze(file,0,voxsize);
+ }
+
+ static CImg<T> get_load_analyze(cimg_std::FILE *const file, float *const voxsize=0) {
+ return CImg<T>().load_analyze(file,voxsize);
+ }
+
+ CImg<T>& _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<T>& load_cimg(const char *const filename, const char axis='z', const char align='p') {
+ CImgList<T> list;
+ list.load_cimg(filename);
+ if (list.size==1) return list[0].transfer_to(*this);
+ return assign(list.get_append(axis,align));
+ }
+
+ static CImg<T> get_load_cimg(const char *const filename, const char axis='z', const char align='p') {
+ return CImg<T>().load_cimg(filename,axis,align);
+ }
+
+ //! Load an image (list) from a .cimg file.
+ CImg<T>& load_cimg(cimg_std::FILE *const file, const char axis='z', const char align='p') {
+ CImgList<T> list;
+ list.load_cimg(file);
+ if (list.size==1) return list[0].transfer_to(*this);
+ return assign(list.get_append(axis,align));
+ }
+
+ static CImg<T> get_load_cimg(cimg_std::FILE *const file, const char axis='z', const char align='p') {
+ return CImg<T>().load_cimg(file,axis,align);
+ }
+
+ //! Load a sub-image (list) from a .cimg file.
+ CImg<T>& load_cimg(const char *const filename,
+ const unsigned int n0, const unsigned int n1,
+ const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int 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<T> 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<T> get_load_cimg(const char *const filename,
+ const unsigned int n0, const unsigned int n1,
+ const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int 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<T>().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<T>& 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<T> 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<T> 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<T>().load_cimg(file,n0,n1,x0,y0,z0,v0,x1,y1,z1,v1,axis,align);
+ }
+
+ //! Load an image from an INRIMAGE-4 file.
+ CImg<T>& load_inr(const char *const filename, float *const voxsize=0) {
+ return _load_inr(0,filename,voxsize);
+ }
+
+ static CImg<T> get_load_inr(const char *const filename, float *const voxsize=0) {
+ return CImg<T>().load_inr(filename,voxsize);
+ }
+
+ //! Load an image from an INRIMAGE-4 file.
+ CImg<T>& load_inr(cimg_std::FILE *const file, float *const voxsize=0) {
+ return _load_inr(file,0,voxsize);
+ }
+
+ static CImg<T> get_load_inr(cimg_std::FILE *const file, float *voxsize=0) {
+ return CImg<T>().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<T>& _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<T>& load_pandore(const char *const filename) {
+ return _load_pandore(0,filename);
+ }
+
+ static CImg<T> get_load_pandore(const char *const filename) {
+ return CImg<T>().load_pandore(filename);
+ }
+
+ //! Load an image from a PANDORE file.
+ CImg<T>& load_pandore(cimg_std::FILE *const file) {
+ return _load_pandore(file,0);
+ }
+
+ static CImg<T> get_load_pandore(cimg_std::FILE *const file) {
+ return CImg<T>().load_pandore(file);
+ }
+
+ CImg<T>& _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<T>& load_parrec(const char *const filename, const char axis='v', const char align='p') {
+ CImgList<T> list;
+ list.load_parrec(filename);
+ if (list.size==1) return list[0].transfer_to(*this);
+ return assign(list.get_append(axis,align));
+ }
+
+ static CImg<T> get_load_parrec(const char *const filename, const char axis='v', const char align='p') {
+ return CImg<T>().load_parrec(filename,axis,align);
+ }
+
+ //! Load an image from a .RAW file.
+ CImg<T>& 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<T> 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<T>().load_raw(filename,sizex,sizey,sizez,sizev,multiplexed,invert_endianness);
+ }
+
+ //! Load an image from a .RAW file.
+ CImg<T>& 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<T> 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<T>().load_raw(file,sizex,sizey,sizez,sizev,multiplexed,invert_endianness);
+ }
+
+ CImg<T>& _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<T> 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<T>& load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
+ const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false,
+ const char axis='z', const char align='p') {
+ return get_load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format,resume,axis,align).transfer_to(*this);
+ }
+
+ static CImg<T> get_load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
+ const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false,
+ const char axis='z', const char align='p') {
+ return CImgList<T>().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<T>& 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<T> 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<T>().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<T>& 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<T> 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<T>().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<typename tf, typename tc>
+ CImg<T>& load_off(const char *const filename, CImgList<tf>& primitives, CImgList<tc>& colors, const bool invert_faces=false) {
+ return _load_off(0,filename,primitives,colors,invert_faces);
+ }
+
+ template<typename tf, typename tc>
+ static CImg<T> get_load_off(const char *const filename, CImgList<tf>& primitives, CImgList<tc>& colors,
+ const bool invert_faces=false) {
+ return CImg<T>().load_off(filename,primitives,colors,invert_faces);
+ }
+
+ //! Load a 3D object from a .OFF file.
+ template<typename tf, typename tc>
+ CImg<T>& load_off(cimg_std::FILE *const file, CImgList<tf>& primitives, CImgList<tc>& colors, const bool invert_faces=false) {
+ return _load_off(file,0,primitives,colors,invert_faces);
+ }
+
+ template<typename tf, typename tc>
+ static CImg<T> get_load_off(cimg_std::FILE *const file, CImgList<tf>& primitives, CImgList<tc>& colors,
+ const bool invert_faces=false) {
+ return CImg<T>().load_off(file,primitives,colors,invert_faces);
+ }
+
+ template<typename tf, typename tc>
+ CImg<T>& _load_off(cimg_std::FILE *const file, const char *const filename,
+ CImgList<tf>& primitives, CImgList<tc>& 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<tf>::vector(i0));
+ colors.insert(CImg<tc>::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<tf>::vector(i0,i1));
+ colors.insert(CImg<tc>::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<tf>::vector(i0,i1,i2));
+ else primitives.insert(CImg<tf>::vector(i0,i2,i1));
+ colors.insert(CImg<tc>::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<tf>::vector(i0,i1,i2,i3));
+ else primitives.insert(CImg<tf>::vector(i0,i3,i2,i1));
+ colors.insert(CImg<tc>::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<tf>::vector(i0,i1,i2,i3));
+ primitives.insert(CImg<tf>::vector(i0,i3,i4));
+ }
+ else {
+ primitives.insert(CImg<tf>::vector(i0,i3,i2,i1));
+ primitives.insert(CImg<tf>::vector(i0,i4,i3));
+ }
+ colors.insert(2,CImg<tc>::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<tf>::vector(i0,i1,i2,i3));
+ primitives.insert(CImg<tf>::vector(i0,i3,i4,i5));
+ }
+ else {
+ primitives.insert(CImg<tf>::vector(i0,i3,i2,i1));
+ primitives.insert(CImg<tf>::vector(i0,i5,i4,i3));
+ }
+ colors.insert(2,CImg<tc>::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<tf>::vector(i0,i1,i3,i4));
+ primitives.insert(CImg<tf>::vector(i0,i4,i5,i6));
+ primitives.insert(CImg<tf>::vector(i1,i2,i3));
+ }
+ else {
+ primitives.insert(CImg<tf>::vector(i0,i4,i3,i1));
+ primitives.insert(CImg<tf>::vector(i0,i6,i5,i4));
+ primitives.insert(CImg<tf>::vector(i3,i2,i1));
+ }
+ colors.insert(2,CImg<tc>::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<tf>::vector(i0,i1,i2,i3));
+ primitives.insert(CImg<tf>::vector(i0,i3,i4,i5));
+ primitives.insert(CImg<tf>::vector(i0,i5,i6,i7));
+ }
+ else {
+ primitives.insert(CImg<tf>::vector(i0,i3,i2,i1));
+ primitives.insert(CImg<tf>::vector(i0,i5,i4,i3));
+ primitives.insert(CImg<tf>::vector(i0,i7,i6,i5));
+ }
+ colors.insert(2,CImg<tc>::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<T>& 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<T> get_load_ffmpeg_external(const char *const filename, const char axis='z', const char align='p') {
+ return CImgList<T>().load_ffmpeg_external(filename).get_append(axis,align);
+ }
+
+ //! Load an image using GraphicsMagick's external tool 'gm'.
+ CImg<T>& 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<T> get_load_graphicsmagick_external(const char *const filename) {
+ return CImg<T>().load_graphicsmagick_external(filename);
+ }
+
+ //! Load a gzipped image file, using external tool 'gunzip'.
+ CImg<T>& 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<T> get_load_gzip_external(const char *const filename) {
+ return CImg<T>().load_gzip_external(filename);
+ }
+
+ //! Load an image using ImageMagick's external tool 'convert'.
+ CImg<T>& 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<T> get_load_imagemagick_external(const char *const filename) {
+ return CImg<T>().load_imagemagick_external(filename);
+ }
+
+ //! Load a DICOM image file, using XMedcon's external tool 'medcon'.
+ CImg<T>& 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<T> get_load_medcon_external(const char *const filename) {
+ return CImg<T>().load_medcon_external(filename);
+ }
+
+ //! Load a RAW Color Camera image file, using external tool 'dcraw'.
+ CImg<T>& 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<T> get_load_dcraw_external(const char *const filename) {
+ return CImg<T>().load_dcraw_external(filename);
+ }
+
+ //! Load an image using ImageMagick's or GraphicsMagick's executables.
+ CImg<T>& 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<T> get_load_other(const char *const filename) {
+ return CImg<T>().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<T>& 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<T>& _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<T>& 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<T>& 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<T>& _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<T>::format(),cimg::type<T>::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<T>& save_cpp(const char *const filename) const {
+ return _save_cpp(0,filename);
+ }
+
+ //! Save the image as a CPP source file.
+ const CImg<T>& save_cpp(cimg_std::FILE *const file) const {
+ return _save_cpp(file,0);
+ }
+
+ // Save the image as a DLM file (internal).
+ const CImg<T>& _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<T>& save_dlm(const char *const filename) const {
+ return _save_dlm(0,filename);
+ }
+
+ //! Save the image as a DLM file.
+ const CImg<T>& save_dlm(cimg_std::FILE *const file) const {
+ return _save_dlm(file,0);
+ }
+
+ // Save the image as a BMP file (internal).
+ const CImg<T>& _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<T>& save_bmp(const char *const filename) const {
+ return _save_bmp(0,filename);
+ }
+
+ //! Save the image as a BMP file.
+ const CImg<T>& save_bmp(cimg_std::FILE *const file) const {
+ return _save_bmp(file,0);
+ }
+
+ // Save a file in JPEG format (internal).
+ const CImg<T>& _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<T>& 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<T>& 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<T>& 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<T>& _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<height; ++row) imgData[row] = new png_byte[byte_depth*numChan*width];
+ const T *pC0 = ptr(0,0,0,0);
+ switch (pixel_bit_depth_flag) {
+ case 7 : { // Gray 8-bit
+ cimg_forY(*this,y) {
+ unsigned char *ptrd = imgData[y];
+ cimg_forX(*this,x) *(ptrd++) = (unsigned char)*(pC0++);
+ }
+ } break;
+ case 14 : { // Gray w/ Alpha 8-bit
+ const T *pC1 = ptr(0,0,0,1);
+ cimg_forY(*this,y) {
+ unsigned char *ptrd = imgData[y];
+ cimg_forX(*this,x) {
+ *(ptrd++) = (unsigned char)*(pC0++);
+ *(ptrd++) = (unsigned char)*(pC1++);
+ }
+ }
+ } break;
+ case 21 : { // RGB 8-bit
+ const T *pC1 = ptr(0,0,0,1), *pC2 = ptr(0,0,0,2);
+ cimg_forY(*this,y) {
+ unsigned char *ptrd = imgData[y];
+ cimg_forX(*this,x) {
+ *(ptrd++) = (unsigned char)*(pC0++);
+ *(ptrd++) = (unsigned char)*(pC1++);
+ *(ptrd++) = (unsigned char)*(pC2++);
+ }
+ }
+ } break;
+ case 28 : { // RGB x/ Alpha 8-bit
+ const T *pC1 = ptr(0,0,0,1), *pC2 = ptr(0,0,0,2), *pC3 = ptr(0,0,0,3);
+ cimg_forY(*this,y){
+ unsigned char *ptrd = imgData[y];
+ cimg_forX(*this,x){
+ *(ptrd++) = (unsigned char)*(pC0++);
+ *(ptrd++) = (unsigned char)*(pC1++);
+ *(ptrd++) = (unsigned char)*(pC2++);
+ *(ptrd++) = (unsigned char)*(pC3++);
+ }
+ }
+ } break;
+ case 15 : { // Gray 16-bit
+ cimg_forY(*this,y){
+ unsigned short *ptrd = (unsigned short*)(imgData[y]);
+ cimg_forX(*this,x) *(ptrd++) = (unsigned short)*(pC0++);
+ if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],width);
+ }
+ } break;
+ case 30 : { // Gray w/ Alpha 16-bit
+ const T *pC1 = ptr(0,0,0,1);
+ cimg_forY(*this,y){
+ unsigned short *ptrd = (unsigned short*)(imgData[y]);
+ cimg_forX(*this,x) {
+ *(ptrd++) = (unsigned short)*(pC0++);
+ *(ptrd++) = (unsigned short)*(pC1++);
+ }
+ if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],2*width);
+ }
+ } break;
+ case 45 : { // RGB 16-bit
+ const T *pC1 = ptr(0,0,0,1), *pC2 = ptr(0,0,0,2);
+ cimg_forY(*this,y) {
+ unsigned short *ptrd = (unsigned short*)(imgData[y]);
+ cimg_forX(*this,x) {
+ *(ptrd++) = (unsigned short)*(pC0++);
+ *(ptrd++) = (unsigned short)*(pC1++);
+ *(ptrd++) = (unsigned short)*(pC2++);
+ }
+ if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],3*width);
+ }
+ } break;
+ case 60 : { // RGB w/ Alpha 16-bit
+ const T *pC1 = ptr(0,0,0,1), *pC2 = ptr(0,0,0,2), *pC3 = ptr(0,0,0,3);
+ cimg_forY(*this,y) {
+ unsigned short *ptrd = (unsigned short*)(imgData[y]);
+ cimg_forX(*this,x) {
+ *(ptrd++) = (unsigned short)*(pC0++);
+ *(ptrd++) = (unsigned short)*(pC1++);
+ *(ptrd++) = (unsigned short)*(pC2++);
+ *(ptrd++) = (unsigned short)*(pC3++);
+ }
+ if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],4*width);
+ }
+ } break;
+ default :
+ if (!file) cimg::fclose(nfile);
+ throw CImgIOException("CImg<%s>::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<T>& save_png(const char *const filename) const {
+ return _save_png(0,filename);
+ }
+
+ //! Save a file in PNG format
+ const CImg<T>& save_png(cimg_std::FILE *const file) const {
+ return _save_png(file,0);
+ }
+
+ // Save the image as a PNM file (internal function).
+ const CImg<T>& _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<T>& save_pnm(const char *const filename) const {
+ return _save_pnm(0,filename);
+ }
+
+ //! Save the image as a PNM file.
+ const CImg<T>& save_pnm(cimg_std::FILE *const file) const {
+ return _save_pnm(file,0);
+ }
+
+ // Save the image as a RGB file (internal).
+ const CImg<T>& _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<wh; ++k) {
+ const unsigned char val = (unsigned char)*(ptr1++);
+ *(nbuffer++) = val;
+ *(nbuffer++) = val;
+ *(nbuffer++) = val;
+ }} break;
+ case 2 : { // RG image
+ for (unsigned int k=0; k<wh; ++k) {
+ *(nbuffer++) = (unsigned char)(*(ptr1++));
+ *(nbuffer++) = (unsigned char)(*(ptr2++));
+ *(nbuffer++) = 0;
+ }} break;
+ default : { // RGB image
+ for (unsigned int k=0; k<wh; ++k) {
+ *(nbuffer++) = (unsigned char)(*(ptr1++));
+ *(nbuffer++) = (unsigned char)(*(ptr2++));
+ *(nbuffer++) = (unsigned char)(*(ptr3++));
+ }
+ }
+ }
+ cimg::fwrite(buffer,3*wh,nfile);
+ if (!file) cimg::fclose(nfile);
+ delete[] buffer;
+ return *this;
+ }
+
+ //! Save the image as a RGB file.
+ const CImg<T>& save_rgb(const char *const filename) const {
+ return _save_rgb(0,filename);
+ }
+
+ //! Save the image as a RGB file.
+ const CImg<T>& save_rgb(cimg_std::FILE *const file) const {
+ return _save_rgb(file,0);
+ }
+
+ // Save the image as a RGBA file (internal).
+ const CImg<T>& _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<wh; ++k) {
+ const unsigned char val = (unsigned char)*(ptr1++);
+ *(nbuffer++) = val;
+ *(nbuffer++) = val;
+ *(nbuffer++) = val;
+ *(nbuffer++) = 255;
+ }} break;
+ case 2 : { // RG images
+ for (unsigned int k=0; k<wh; ++k) {
+ *(nbuffer++) = (unsigned char)(*(ptr1++));
+ *(nbuffer++) = (unsigned char)(*(ptr2++));
+ *(nbuffer++) = 0;
+ *(nbuffer++) = 255;
+ }} break;
+ case 3 : { // RGB images
+ for (unsigned int k=0; k<wh; ++k) {
+ *(nbuffer++) = (unsigned char)(*(ptr1++));
+ *(nbuffer++) = (unsigned char)(*(ptr2++));
+ *(nbuffer++) = (unsigned char)(*(ptr3++));
+ *(nbuffer++) = 255;
+ }} break;
+ default : { // RGBA images
+ for (unsigned int k=0; k<wh; ++k) {
+ *(nbuffer++) = (unsigned char)(*(ptr1++));
+ *(nbuffer++) = (unsigned char)(*(ptr2++));
+ *(nbuffer++) = (unsigned char)(*(ptr3++));
+ *(nbuffer++) = (unsigned char)(*(ptr4++));
+ }
+ }
+ }
+ cimg::fwrite(buffer,4*wh,nfile);
+ if (!file) cimg::fclose(nfile);
+ delete[] buffer;
+ return *this;
+ }
+
+ //! Save the image as a RGBA file.
+ const CImg<T>& save_rgba(const char *const filename) const {
+ return _save_rgba(0,filename);
+ }
+
+ //! Save the image as a RGBA file.
+ const CImg<T>& 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<T>& _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<T>::is_float()) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,3);
+ else if (cimg::type<T>::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; row<height; row+=rowsperstrip) {
+ uint32 nrow = (row+rowsperstrip>height?height-row:rowsperstrip);
+ tstrip_t strip = TIFFComputeStrip(tif,row,0);
+ tsize_t i = 0;
+ for (unsigned int rr = 0; rr<nrow; ++rr)
+ for (unsigned int cc = 0; cc<width; ++cc)
+ for (unsigned int vv = 0; vv<spp; ++vv)
+ buf[i++] = (*this)(cc,row+rr,vv);
+ if (TIFFWriteEncodedStrip(tif,strip,buf,i*sizeof(T))<0)
+ throw CImgException("CImg<%s>::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<T>& 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<T>& 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<T>& save_cimg(const char *const filename, const bool compress=false) const {
+ CImgList<T>(*this,true).save_cimg(filename,compress);
+ return *this;
+ }
+
+ // Save the image as a .cimg file.
+ const CImg<T>& save_cimg(cimg_std::FILE *const file, const bool compress=false) const {
+ CImgList<T>(*this,true).save_cimg(file,compress);
+ return *this;
+ }
+
+ //! Insert the image into an existing .cimg file, at specified coordinates.
+ const CImg<T>& save_cimg(const char *const filename,
+ const unsigned int n0,
+ const unsigned int x0, const unsigned int y0,
+ const unsigned int z0, const unsigned int v0) const {
+ CImgList<T>(*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<T>& 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<T>(*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<T>::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<T>::save_empty_cimg(file,1,dx,dy,dz,dv);
+ }
+
+ // Save the image as an INRIMAGE-4 file (internal).
+ const CImg<T>& _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<T>& 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<T>& 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<T>& _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<T>& 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<T>& 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<T>& _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<T> 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<T>& 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<T>& 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<T>& 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<T>& 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<T>& 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<typename tf, typename tc>
+ const CImg<T>& _save_off(cimg_std::FILE *const file, const char *const filename,
+ const CImgList<tf>& primitives, const CImgList<tc>& 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<tc> _colors;
+ if (!colors) _colors.insert(primitives.size,CImg<tc>::vector(200,200,200));
+ const CImgList<tc>& 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<tc>& 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<typename tf, typename tc>
+ const CImg<T>& save_off(const char *const filename,
+ const CImgList<tf>& primitives, const CImgList<tc>& colors, const bool invert_faces=false) const {
+ return _save_off(0,filename,primitives,colors,invert_faces);
+ }
+
+ //! Save OFF files.
+ template<typename tf, typename tc>
+ const CImg<T>& save_off(cimg_std::FILE *const file,
+ const CImgList<tf>& primitives, const CImgList<tc>& 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<T>& 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<T>& 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<T>& 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<T>& 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<T>& 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<T>& 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<T> logo40x38() {
+ static bool first_time = true;
+ static CImg<T> 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<res.width*res.height;) {
+ const unsigned char n = *(ptrs++), r = *(ptrs++), g = *(ptrs++), b = *(ptrs++);
+ for (unsigned int l=0; l<n; ++off, ++l) { *(ptr1++) = (T)r; *(ptr2++) = (T)g; *(ptr3++) = (T)b; }
+ }
+ first_time = false;
+ }
+ return res;
+ }
+
+ };
+
+ /*
+ #-----------------------------------------
+ #
+ #
+ #
+ # Definition of the CImgList<> structure
+ #
+ #
+ #
+ #------------------------------------------
+ */
+
+ //! Class representing list of images CImg<T>.
+ template<typename T>
+ 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<T> *data;
+
+ //! Define a CImgList<T>::iterator.
+ typedef CImg<T>* iterator;
+
+ //! Define a CImgList<T>::const_iterator.
+ typedef const CImg<T>* const_iterator;
+
+ //! Get value type.
+ typedef T value_type;
+
+ // Define common T-dependant types.
+ typedef typename cimg::superset<T,bool>::type Tbool;
+ typedef typename cimg::superset<T,unsigned char>::type Tuchar;
+ typedef typename cimg::superset<T,char>::type Tchar;
+ typedef typename cimg::superset<T,unsigned short>::type Tushort;
+ typedef typename cimg::superset<T,short>::type Tshort;
+ typedef typename cimg::superset<T,unsigned int>::type Tuint;
+ typedef typename cimg::superset<T,int>::type Tint;
+ typedef typename cimg::superset<T,unsigned long>::type Tulong;
+ typedef typename cimg::superset<T,long>::type Tlong;
+ typedef typename cimg::superset<T,float>::type Tfloat;
+ typedef typename cimg::superset<T,double>::type Tdouble;
+ typedef typename cimg::last<T,bool>::type boolT;
+ typedef typename cimg::last<T,unsigned char>::type ucharT;
+ typedef typename cimg::last<T,char>::type charT;
+ typedef typename cimg::last<T,unsigned short>::type ushortT;
+ typedef typename cimg::last<T,short>::type shortT;
+ typedef typename cimg::last<T,unsigned int>::type uintT;
+ typedef typename cimg::last<T,int>::type intT;
+ typedef typename cimg::last<T,unsigned long>::type ulongT;
+ typedef typename cimg::last<T,long>::type longT;
+ typedef typename cimg::last<T,float>::type floatT;
+ typedef typename cimg::last<T,double>::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<T>[allocsize = cimg::max(16UL,cimg::nearest_pow2(n))];
+ }
+
+ //! Default copy constructor.
+ template<typename t>
+ CImgList(const CImgList<t>& list):
+ size(0),allocsize(0),data(0) {
+ assign(list.size);
+ cimglist_for(*this,l) data[l].assign(list[l],false);
+ }
+
+ CImgList(const CImgList<T>& 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<typename t>
+ CImgList(const CImgList<t>& 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<t>::pixel_type());
+ cimglist_for(*this,l) data[l].assign(list[l],false);
+ }
+
+ CImgList(const CImgList<T>& 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<nsiz; ++i) { \
+ *(ptrd++) = (T)(i==0?val0:(i==1?val1:va_arg(ap,t))); \
+ if ((++s)==siz) { ptrd = data[++l].data; s=0; } \
+ } \
+ va_end(ap); \
+ }
+ _CImgList_stdarg(int);
+ }
+
+ //! Construct an image list containing n images with specified size and specified pixel values (double version).
+ CImgList(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, ...):
+ size(0),allocsize(0),data(0) {
+ _CImgList_stdarg(double);
+ }
+
+ //! Construct a list containing n copies of the image img.
+ template<typename t>
+ CImgList(const unsigned int n, const CImg<t>& 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<typename t>
+ CImgList(const unsigned int n, const CImg<t>& 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<typename t>
+ explicit CImgList(const CImg<t>& 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<typename t>
+ explicit CImgList(const CImg<t>& 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<typename t1, typename t2>
+ CImgList(const CImg<t1>& img1, const CImg<t2>& 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<typename t1, typename t2>
+ CImgList(const CImg<t1>& img1, const CImg<t2>& 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<typename t1, typename t2, typename t3>
+ CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& 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<typename t1, typename t2, typename t3>
+ CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& 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<typename t1, typename t2, typename t3, typename t4>
+ CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& 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<typename t1, typename t2, typename t3, typename t4>
+ CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& 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<typename t1, typename t2, typename t3, typename t4, typename t5>
+ CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
+ const CImg<t5>& 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<typename t1, typename t2, typename t3, typename t4, typename t5>
+ CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
+ const CImg<t5>& 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<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
+ CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
+ const CImg<t5>& img5, const CImg<t6>& img6):
+ 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<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
+ CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
+ const CImg<t5>& img5, const CImg<t6>& img6, const bool 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<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
+ CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
+ const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7):
+ 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<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
+ CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
+ const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool 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<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
+ CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
+ const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8):
+ 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<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
+ CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
+ const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8, const bool 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<T>& assign() {
+ if (data) delete[] data;
+ size = allocsize = 0;
+ data = 0;
+ return *this;
+ }
+
+ //! Equivalent to assign() (STL-compliant name).
+ CImgList<T>& clear() {
+ return assign();
+ }
+
+ //! In-place version of the corresponding constructor.
+ CImgList<T>& assign(const unsigned int n) {
+ if (n) {
+ if (allocsize<n || allocsize>(n<<2)) {
+ if (data) delete[] data;
+ data = new CImg<T>[allocsize=cimg::max(16UL,cimg::nearest_pow2(n))];
+ }
+ size = n;
+ } else assign();
+ return *this;
+ }
+
+ //! In-place version of the corresponding constructor.
+ CImgList<T>& 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<T>& 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<T>& 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<T>& 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<typename t>
+ CImgList<T>& assign(const CImgList<t>& 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<typename t>
+ CImgList<T>& assign(const CImgList<t>& 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<typename t>
+ CImgList<T>& assign(const unsigned int n, const CImg<t>& 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<typename t>
+ CImgList<T>& assign(const CImg<t>& img, const bool shared=false) {
+ assign(1);
+ data[0].assign(img,shared);
+ return *this;
+ }
+
+ //! In-place version of the corresponding constructor.
+ template<typename t1, typename t2>
+ CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& 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<typename t1, typename t2, typename t3>
+ CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& 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<typename t1, typename t2, typename t3, typename t4>
+ CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& 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<typename t1, typename t2, typename t3, typename t4, typename t5>
+ CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
+ const CImg<t5>& img5, const bool 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<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
+ CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
+ const CImg<t5>& img5, const CImg<t6>& img6, const bool 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<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
+ CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
+ const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool 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<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
+ CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
+ const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8, const bool 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<T>& assign(const char *const filename) {
+ return load(filename);
+ }
+
+ //! Transfer the content of the instance image list into another one.
+ template<typename t>
+ CImgList<T>& transfer_to(CImgList<t>& list) {
+ list.assign(*this);
+ assign();
+ return list;
+ }
+
+ CImgList<T>& transfer_to(CImgList<T>& list) {
+ list.assign();
+ return swap(list);
+ }
+
+ //! Swap all fields of two CImgList instances (use with care !)
+ CImgList<T>& swap(CImgList<T>& 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<T>::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<typename t>
+ bool is_sameN(const CImgList<t>& 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<size && res; ++l) res = data[l].is_same##axis(val); return res; \
+ } \
+ bool is_sameN##axis(const unsigned int n, const unsigned int val) const { \
+ return is_sameN(n) && is_same##axis(val); \
+ } \
+
+#define _cimglist_def_is_same2(axis1,axis2) \
+ bool is_same##axis1##axis2(const unsigned int val1, const unsigned int val2) const { \
+ bool res = true; for (unsigned int l = 0; l<size && res; ++l) res = data[l].is_same##axis1##axis2(val1,val2); return res; \
+ } \
+ bool is_sameN##axis1##axis2(const unsigned int n, const unsigned int val1, const unsigned int val2) const { \
+ return is_sameN(n) && is_same##axis1##axis2(val1,val2); \
+ } \
+
+#define _cimglist_def_is_same3(axis1,axis2,axis3) \
+ bool is_same##axis1##axis2##axis3(const unsigned int val1, const unsigned int val2, const unsigned int val3) const { \
+ bool res = true; for (unsigned int l = 0; l<size && res; ++l) res = data[l].is_same##axis1##axis2##axis3(val1,val2,val3); return res; \
+ } \
+ bool is_sameN##axis1##axis2##axis3(const unsigned int n, const unsigned int val1, const unsigned int val2, const unsigned int val3) const { \
+ return is_sameN(n) && is_same##axis1##axis2##axis3(val1,val2,val3); \
+ } \
+
+#define _cimglist_def_is_same(axis) \
+ template<typename t> bool is_same##axis(const CImg<t>& img) const { \
+ bool res = true; for (unsigned int l = 0; l<size && res; ++l) res = data[l].is_same##axis(img); return res; \
+ } \
+ template<typename t> bool is_same##axis(const CImgList<t>& list) const { \
+ const unsigned int lmin = cimg::min(size,list.size); \
+ bool res = true; for (unsigned int l = 0; l<lmin && res; ++l) res = data[l].is_same##axis(list[l]); return res; \
+ } \
+ template<typename t> bool is_sameN##axis(const unsigned int n, const CImg<t>& img) const { \
+ return (is_sameN(n) && is_same##axis(img)); \
+ } \
+ template<typename t> bool is_sameN##axis(const CImgList<t>& 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<size && res; ++l) res = data[l].is_sameXYZV(dx,dy,dz,dv);
+ return res;
+ }
+
+ bool is_sameNXYZV(const unsigned int n, const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv) const {
+ return is_sameN(n) && is_sameXYZV(dx,dy,dz,dv);
+ }
+
+ //! Return \c true if the list contains the pixel (n,x,y,z,v).
+ bool containsNXYZV(const int n, const int x=0, const int y=0, const int z=0, const int v=0) const {
+ if (is_empty()) return false;
+ return n>=0 && n<(int)size && x>=0 && x<data[n].dimx() && y>=0 && y<data[n].dimy() && z>=0 && z<data[n].dimz() && v>=0 && v<data[n].dimv();
+ }
+
+ //! Return \c true if the list contains the image (n).
+ bool containsN(const int n) const {
+ if (is_empty()) return false;
+ return n>=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<typename t>
+ 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<typename t>
+ 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<typename t>
+ 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<typename t>
+ 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<typename t>
+ 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<typename t>
+ bool contains(const CImg<T>& img, t& n) const {
+ if (is_empty()) return false;
+ const CImg<T> *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<T>& img) const {
+ unsigned int n;
+ return contains(img,n);
+ }
+
+ //@}
+ //------------------------------
+ //
+ //! \name Arithmetics Operators
+ //@{
+ //------------------------------
+
+ //! Assignment operator
+ template<typename t>
+ CImgList<T>& operator=(const CImgList<t>& list) {
+ return assign(list);
+ }
+
+ CImgList<T>& operator=(const CImgList<T>& list) {
+ return assign(list);
+ }
+
+ //! Assignment operator.
+ template<typename t>
+ CImgList<T>& operator=(const CImg<t>& img) {
+ cimglist_for(*this,l) data[l] = img;
+ return *this;
+ }
+
+ //! Assignment operator.
+ CImgList<T>& operator=(const T val) {
+ cimglist_for(*this,l) data[l].fill(val);
+ return *this;
+ }
+
+ //! Operator+.
+ CImgList<T> operator+() const {
+ return CImgList<T>(*this);
+ }
+
+ //! Operator+=.
+#ifdef cimg_use_visualcpp6
+ CImgList<T>& operator+=(const T val)
+#else
+ template<typename t>
+ CImgList<T>& operator+=(const t val)
+#endif
+ {
+ cimglist_for(*this,l) (*this)[l]+=val;
+ return *this;
+ }
+
+ //! Operator+=.
+ template<typename t>
+ CImgList<T>& operator+=(const CImgList<t>& list) {
+ const unsigned int sizemax = cimg::min(size,list.size);
+ for (unsigned int l=0; l<sizemax; ++l) (*this)[l]+=list[l];
+ return *this;
+ }
+
+ //! Operator++ (prefix).
+ CImgList<T>& operator++() {
+ cimglist_for(*this,l) ++(*this)[l];
+ return *this;
+ }
+
+ //! Operator++ (postfix).
+ CImgList<T> operator++(int) {
+ CImgList<T> copy(*this);
+ ++*this;
+ return copy;
+ }
+
+ //! Operator-.
+ CImgList<T> operator-() const {
+ CImgList<T> res(size);
+ cimglist_for(res,l) res[l].assign(-data[l]);
+ return res;
+ }
+
+ //! Operator-=.
+#ifdef cimg_use_visualcpp6
+ CImgList<T>& operator-=(const T val)
+#else
+ template<typename t>
+ CImgList<T>& operator-=(const t val)
+#endif
+ {
+ cimglist_for(*this,l) (*this)[l]-=val;
+ return *this;
+ }
+
+ //! Operator-=.
+ template<typename t>
+ CImgList<T>& operator-=(const CImgList<t>& list) {
+ const unsigned int sizemax = min(size,list.size);
+ for (unsigned int l=0; l<sizemax; ++l) (*this)[l]-=list[l];
+ return *this;
+ }
+
+ //! Operator-- (prefix).
+ CImgList<T>& operator--() {
+ cimglist_for(*this,l) --(*this)[l];
+ return *this;
+ }
+
+ //! Operator-- (postfix).
+ CImgList<T> operator--(int) {
+ CImgList<T> copy(*this);
+ --*this;
+ return copy;
+ }
+
+ //! Operator*=.
+#ifdef cimg_use_visualcpp6
+ CImgList<T>& operator*=(const double val)
+#else
+ template<typename t>
+ CImgList<T>& operator*=(const t val)
+#endif
+ {
+ cimglist_for(*this,l) (*this)[l]*=val;
+ return *this;
+ }
+
+ //! Operator*=.
+ template<typename t>
+ CImgList<T>& operator*=(const CImgList<t>& list) {
+ const unsigned int N = cimg::min(size,list.size);
+ for (unsigned int l=0; l<N; ++l) (*this)[l]*=list[l];
+ return this;
+ }
+
+ //! Operator/=.
+#ifdef cimg_use_visualcpp6
+ CImgList<T>& operator/=(const double val)
+#else
+ template<typename t>
+ CImgList<T>& operator/=(const t val)
+#endif
+ {
+ cimglist_for(*this,l) (*this)[l]/=val;
+ return *this;
+ }
+
+ //! Operator/=.
+ template<typename t>
+ CImgList<T>& operator/=(const CImgList<t>& list) {
+ const unsigned int N = cimg::min(size,list.size);
+ for (unsigned int l=0; l<N; ++l) (*this)[l]/=list[l];
+ return this;
+ }
+
+ //! Return a reference to the maximum pixel value of the instance list.
+ const T& max() const {
+ if (is_empty())
+ throw CImgInstanceException("CImgList<%s>::max() : Instance image list is empty.",
+ pixel_type());
+ const T *ptrmax = data->data;
+ T max_value = *ptrmax;
+ cimglist_for(*this,l) {
+ const CImg<T>& 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<T>& 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<T>& img = data[l];
+ cimg_for(img,ptr,T) if ((*ptr)<min_value) min_value = *(ptrmin=ptr);
+ }
+ return *ptrmin;
+ }
+
+ //! Return a reference to the minimum pixel value of the instance list.
+ T& min() {
+ if (is_empty())
+ throw CImgInstanceException("CImgList<%s>::min() : Instance image list is empty.",
+ pixel_type());
+ T *ptrmin = data->data;
+ T min_value = *ptrmin;
+ cimglist_for(*this,l) {
+ const CImg<T>& img = data[l];
+ cimg_for(img,ptr,T) if ((*ptr)<min_value) min_value = *(ptrmin=ptr);
+ }
+ return *ptrmin;
+ }
+
+ //! Return a reference to the minimum pixel value of the instance list.
+ template<typename t>
+ 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<T>& img = data[l];
+ cimg_for(img,ptr,T) {
+ const T val = *ptr;
+ if (val<min_value) { min_value = val; ptrmin = ptr; }
+ if (val>max_value) max_value = val;
+ }
+ }
+ max_val = (t)max_value;
+ return *ptrmin;
+ }
+
+ //! Return a reference to the minimum pixel value of the instance list.
+ template<typename t>
+ 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<T>& img = data[l];
+ cimg_for(img,ptr,T) {
+ const T val = *ptr;
+ if (val<min_value) { min_value = val; ptrmin = ptr; }
+ if (val>max_value) max_value = val;
+ }
+ }
+ max_val = (t)max_value;
+ return *ptrmin;
+ }
+
+ //! Return a reference to the minimum pixel value of the instance list.
+ template<typename t>
+ 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<T>& img = data[l];
+ cimg_for(img,ptr,T) {
+ const T val = *ptr;
+ if (val>max_value) { max_value = val; ptrmax = ptr; }
+ if (val<min_value) min_value = val;
+ }
+ }
+ min_val = (t)min_value;
+ return *ptrmax;
+ }
+
+ //! Return a reference to the minimum pixel value of the instance list.
+ template<typename t>
+ 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<T>& img = data[l];
+ cimg_for(img,ptr,T) {
+ const T val = *ptr;
+ if (val>max_value) { max_value = val; ptrmax = ptr; }
+ if (val<min_value) min_value = val;
+ }
+ }
+ min_val = (t)min_value;
+ return *ptrmax;
+ }
+
+ //! Return the mean pixel value of the instance list.
+ double mean() const {
+ if (is_empty())
+ throw CImgInstanceException("CImgList<%s>::mean() : Instance image list is empty.",
+ pixel_type());
+ double val = 0;
+ unsigned int siz = 0;
+ cimglist_for(*this,l) {
+ const CImg<T>& 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<T>& 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<T>& stats(const unsigned int variance_method=1) {
+ if (is_empty()) return *this;
+ cimglist_for(*this,l) data[l].stats(variance_method);
+ return *this;
+ }
+
+ CImgList<Tfloat> get_stats(const unsigned int variance_method=1) const {
+ CImgList<Tfloat> 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<T>& 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<T>& 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<T>::operator[]
+ CImg<T>& operator()(const unsigned int pos) {
+ return (*this)[pos];
+ }
+
+ const CImg<T>& 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<T>& 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<T>& back() {
+ return (*this)(size-1);
+ }
+
+ const CImg<T>& back() const {
+ return (*this)(size-1);
+ }
+
+ //! Returns a reference to the first element.
+ CImg<T>& front() {
+ return *data;
+ }
+
+ const CImg<T>& 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<T>& first() const {
+ return *data;
+ }
+
+ CImg<T>& 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<T>& last() const {
+ return data[size - 1];
+ }
+
+ CImg<T>& last() {
+ return data[size - 1];
+ }
+
+ //! Insert a copy of the image \p img into the current image list, at position \p pos.
+ template<typename t>
+ CImgList<T>& insert(const CImg<t>& 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<T> *new_data = (++size>allocsize)?new CImg<T>[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<T>)*npos);
+ if (npos!=size-1) cimg_std::memcpy(new_data+npos+1,data+npos,sizeof(CImg<T>)*(size-1-npos));
+ cimg_std::memset(data,0,sizeof(CImg<T>)*(size-1));
+ delete[] data;
+ data = new_data;
+ }
+ else if (npos!=size-1) cimg_std::memmove(data+npos+1,data+npos,sizeof(CImg<T>)*(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<T>& insert(const CImg<T>& 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<data+size) return insert(+img,pos,shared);
+ CImg<T> *new_data = (++size>allocsize)?new CImg<T>[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<T>)*npos);
+ if (npos!=size-1) cimg_std::memcpy(new_data+npos+1,data+npos,sizeof(CImg<T>)*(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<T>)*(size-1));
+ delete[] data;
+ data = new_data;
+ } else {
+ if (npos!=size-1) cimg_std::memmove(data+npos+1,data+npos,sizeof(CImg<T>)*(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<typename t>
+ CImgList<T>& insert(const CImg<t>& 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<typename t>
+ CImgList<T>& insert(const CImg<t>& img) {
+ return insert(img,~0U,false);
+ }
+
+ template<typename t>
+ CImgList<T> get_insert(const CImg<t>& 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<T>& insert(const unsigned int n, const unsigned int pos=~0U) {
+ CImg<T> foo;
+ if (!n) return *this;
+ const unsigned int npos = pos==~0U?size:pos;
+ for (unsigned int i=0; i<n; ++i) insert(foo,npos+i);
+ return *this;
+ }
+
+ CImgList<T> 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<typename t>
+ CImgList<T>& insert(const unsigned int n, const CImg<t>& 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<n; ++i) insert(data[npos],npos+i,shared);
+ return *this;
+ }
+
+ template<typename t>
+ CImgList<T> get_insert(const unsigned int n, const CImg<t>& 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<typename t>
+ CImgList<T>& insert(const CImgList<t>& 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<T>(list),npos,shared);
+ return *this;
+ }
+
+ template<typename t>
+ CImgList<T> get_insert(const CImgList<t>& 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<typename t>
+ CImgList<T>& insert(const unsigned int n, const CImgList<t>& 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<n; ++i) insert(list,npos,shared);
+ return *this;
+ }
+
+ template<typename t>
+ CImgList<T> get_insert(const unsigned int n, const CImgList<t>& 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<typename t>
+ CImgList<T>& operator<<(const CImg<t>& img) {
+ return insert(img);
+ }
+
+ //! Insert a copy of the image list \p list at the end of the current image list.
+ template<typename t>
+ CImgList<T>& operator<<(const CImgList<t>& list) {
+ return insert(list);
+ }
+
+ //! Return a copy of the current image list, where the image \p img has been inserted at the end.
+ template<typename t>
+ CImgList<T>& operator>>(CImg<t>& img) const {
+ typedef typename cimg::superset<T,t>::type Tt;
+ return CImgList<Tt>(*this).insert(img);
+ }
+
+ //! Insert a copy of the current image list at the beginning of the image list \p list.
+ template<typename t>
+ CImgList<T>& operator>>(CImgList<t>& list) const {
+ return list.insert(*this,0);
+ }
+
+ //! Remove the images at positions \p pos1 to \p pos2 from the image list.
+ CImgList<T>& remove(const unsigned int pos1, const unsigned int pos2) {
+ const unsigned int
+ npos1 = pos1<pos2?pos1:pos2,
+ tpos2 = pos1<pos2?pos2:pos1,
+ npos2 = tpos2<size?tpos2:size-1;
+ if (npos1>=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<T>)*(size-npos1));
+ cimg_std::memset(data+size,0,sizeof(CImg<T>)*nb);
+ } else { // Removing items with reallocation.
+ allocsize>>=2;
+ while (allocsize>8 && size<(allocsize>>1)) allocsize>>=1;
+ CImg<T> *new_data = new CImg<T>[allocsize];
+ if (npos1) cimg_std::memcpy(new_data,data,sizeof(CImg<T>)*npos1);
+ if (npos1!=size) cimg_std::memcpy(new_data+npos1,data+npos2+1,sizeof(CImg<T>)*(size-npos1));
+ if (size!=allocsize) cimg_std::memset(new_data+size,0,sizeof(allocsize-size));
+ cimg_std::memset(data,0,sizeof(CImg<T>)*(size+nb));
+ delete[] data;
+ data = new_data;
+ }
+ }
+ return *this;
+ }
+
+ CImgList<T> 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<T>& remove(const unsigned int pos) {
+ return remove(pos,pos);
+ }
+
+ CImgList<T> get_remove(const unsigned int pos) const {
+ return (+*this).remove(pos);
+ }
+
+ //! Remove the last image from the image list.
+ CImgList<T>& remove() {
+ if (size) return remove(size-1);
+ else cimg::warn("CImgList<%s>::remove() : List is empty",
+ pixel_type());
+ return *this;
+ }
+
+ CImgList<T> get_remove() const {
+ return (+*this).remove();
+ }
+
+ //! Reverse list order.
+ CImgList<T>& reverse() {
+ for (unsigned int l=0; l<size/2; ++l) (*this)[l].swap((*this)[size-1-l]);
+ return *this;
+ }
+
+ CImgList<T> get_reverse() const {
+ return (+*this).reverse();
+ }
+
+ //! Get a sub-list.
+ CImgList<T>& crop(const unsigned int i0, const unsigned int i1, const bool shared=false) {
+ return get_crop(i0,i1,shared).transfer_to(*this);
+ }
+
+ CImgList<T> 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<T> res(i1-i0+1);
+ cimglist_for(res,l) res[l].assign((*this)[i0+l],shared);
+ return res;
+ }
+
+ //! Get sub-images of a sublist.
+ CImgList<T>& 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<T> 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<T> 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<T>& 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<T> 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<T> 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<T>& 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<T> 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<T> 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<T>& 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<T> 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<T> 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<T>& operator>>(CImgDisplay& disp) const {
+ return display(disp);
+ }
+
+ //! Insert image \p img at the end of the list.
+ template<typename t>
+ CImgList<T>& push_back(const CImg<t>& img) {
+ return insert(img);
+ }
+
+ //! Insert image \p img at the front of the list.
+ template<typename t>
+ CImgList<T>& push_front(const CImg<t>& img) {
+ return insert(img,0);
+ }
+
+ //! Insert list \p list at the end of the current list.
+ template<typename t>
+ CImgList<T>& push_back(const CImgList<t>& list) {
+ return insert(list);
+ }
+
+ //! Insert list \p list at the front of the current list.
+ template<typename t>
+ CImgList<T>& push_front(const CImgList<t>& list) {
+ return insert(list,0);
+ }
+
+ //! Remove last element of the list.
+ CImgList<T>& pop_back() {
+ return remove(size-1);
+ }
+
+ //! Remove first element of the list.
+ CImgList<T>& pop_front() {
+ return remove(0);
+ }
+
+ //! Remove the element pointed by iterator \p iter.
+ CImgList<T>& erase(const iterator iter) {
+ return remove(iter-data);
+ }
+
+ //@}
+ //----------------------------
+ //
+ //! \name Fourier Transforms
+ //@{
+ //----------------------------
+
+ //! Compute the Fast Fourier Transform (along the specified axis).
+ CImgList<T>& 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<T>(data[0].width,data[0].height,data[0].depth,data[0].dim,0));
+ CImg<T> &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; i<N2; ++i) {
+ if (j>i) 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<N2) {
+ const unsigned int ri = N-1-i, rj = N-1-j;
+ cimg::swap(Ir(ri,y,z,v),Ir(rj,y,z,v)); cimg::swap(Ii(ri,y,z,v),Ii(rj,y,z,v));
+ }}
+ for (unsigned int m=N, n=N2; (j+=n)>=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<N; i+=delta) {
+ float wr = 1, wi = 0;
+ const float angle = (float)((invert?+1:-1)*2*cimg::valuePI/delta),
+ ca = (float)cimg_std::cos(angle),
+ sa = (float)cimg_std::sin(angle);
+ for (unsigned int k=0; k<delta2; ++k) {
+ const unsigned int j = i + k, nj = j + delta2;
+ cimg_forYZV(Ir,y,z,k) {
+ T &ir = Ir(j,y,z,k), &ii = Ii(j,y,z,k), &nir = Ir(nj,y,z,k), &nii = Ii(nj,y,z,k);
+ const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
+ nir = (T)(ir - tmpr);
+ nii = (T)(ii - tmpi);
+ ir += (T)tmpr;
+ ii += (T)tmpi;
+ }
+ const float nwr = wr*ca-wi*sa;
+ wi = wi*ca + wr*sa;
+ wr = nwr;
+ }
+ }
+ }
+ if (invert) (*this)/=N;
+ } break;
+
+ case 'y' : { // Fourier along Y
+ const unsigned int N = Ir.height, N2 = (N>>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; i<N2; ++i) {
+ if (j>i) 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<N2) {
+ const unsigned int ri = N-1-i, rj = N-1-j;
+ cimg::swap(Ir(x,ri,z,v),Ir(x,rj,z,v)); cimg::swap(Ii(x,ri,z,v),Ii(x,rj,z,v));
+ }}
+ for (unsigned int m=N, n=N2; (j+=n)>=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<N; i+=delta) {
+ float wr = 1, wi = 0;
+ const float angle = (float)((invert?+1:-1)*2*cimg::valuePI/delta),
+ ca = (float)cimg_std::cos(angle), sa = (float)cimg_std::sin(angle);
+ for (unsigned int k=0; k<delta2; ++k) {
+ const unsigned int j = i + k, nj = j + delta2;
+ cimg_forXZV(Ir,x,z,k) {
+ T &ir = Ir(x,j,z,k), &ii = Ii(x,j,z,k), &nir = Ir(x,nj,z,k), &nii = Ii(x,nj,z,k);
+ const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
+ nir = (T)(ir - tmpr);
+ nii = (T)(ii - tmpi);
+ ir += (T)tmpr;
+ ii += (T)tmpi;
+ }
+ const float nwr = wr*ca-wi*sa;
+ wi = wi*ca + wr*sa;
+ wr = nwr;
+ }
+ }
+ }
+ if (invert) (*this)/=N;
+ } break;
+
+ case 'z' : { // Fourier along Z
+ const unsigned int N = Ir.depth, N2 = (N>>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; i<N2; ++i) {
+ if (j>i) 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<N2) {
+ const unsigned int ri = N-1-i, rj = N-1-j;
+ cimg::swap(Ir(x,y,ri,v),Ir(x,y,rj,v)); cimg::swap(Ii(x,y,ri,v),Ii(x,y,rj,v));
+ }}
+ for (unsigned int m=N, n=N2; (j+=n)>=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<N; i+=delta) {
+ float wr = 1, wi = 0;
+ const float angle = (float)((invert?+1:-1)*2*cimg::valuePI/delta),
+ ca = (float)cimg_std::cos(angle), sa = (float)cimg_std::sin(angle);
+ for (unsigned int k=0; k<delta2; ++k) {
+ const unsigned int j = i + k, nj = j + delta2;
+ cimg_forXYV(Ir,x,y,k) {
+ T &ir = Ir(x,y,j,k), &ii = Ii(x,y,j,k), &nir = Ir(x,y,nj,k), &nii = Ii(x,y,nj,k);
+ const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
+ nir = (T)(ir - tmpr);
+ nii = (T)(ii - tmpi);
+ ir += (T)tmpr;
+ ii += (T)tmpi;
+ }
+ const float nwr = wr*ca-wi*sa;
+ wi = wi*ca + wr*sa;
+ wr = nwr;
+ }
+ }
+ }
+ if (invert) (*this)/=N;
+ } break;
+
+ default :
+ throw CImgArgumentException("CImgList<%s>::FFT() : Invalid axis '%c', must be 'x','y' or 'z'.");
+ }
+#endif
+ return *this;
+ }
+
+ CImgList<Tfloat> get_FFT(const char axis, const bool invert=false) const {
+ return CImgList<Tfloat>(*this).FFT(axis,invert);
+ }
+
+ //! Compute the Fast Fourier Transform of a complex image.
+ CImgList<T>& 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<T>(data->width,data->height,data->depth,data->dim,0));
+ CImg<T> &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; x<Ir.width; ++x, ptrr-=wh-1, ptri-=wh-1)
+ for (unsigned int y = 0; y<Ir.height; ++y, ptrr-=whd-w, ptri-=whd-w)
+ for (unsigned int z = 0; z<Ir.depth; ++z, ptrr+=wh, ptri+=wh) {
+ *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri;
+ }
+ fftw_execute(data_plan);
+ ptrd = (double*)data_in;
+ ptrr = Ir.ptr(0,0,0,k);
+ ptri = Ii.ptr(0,0,0,k);
+ if (!invert) for (unsigned int x = 0; x<Ir.width; ++x, ptrr-=wh-1, ptri-=wh-1)
+ for (unsigned int y = 0; y<Ir.height; ++y, ptrr-=whd-w, ptri-=whd-w)
+ for (unsigned int z = 0; z<Ir.depth; ++z, ptrr+=wh, ptri+=wh) {
+ *ptrr = (T)*(ptrd++); *ptri = (T)*(ptrd++);
+ }
+ else for (unsigned int x = 0; x<Ir.width; ++x, ptrr-=wh-1, ptri-=wh-1)
+ for (unsigned int y = 0; y<Ir.height; ++y, ptrr-=whd-w, ptri-=whd-w)
+ for (unsigned int z = 0; z<Ir.depth; ++z, ptrr+=wh, ptri+=wh) {
+ *ptrr = (T)(*(ptrd++)/whd); *ptri = (T)(*(ptrd++)/whd);
+ }
+ }
+ fftw_destroy_plan(data_plan);
+ fftw_free(data_in);
+#else
+ if (Ir.depth>1) FFT('z',invert);
+ if (Ir.height>1) FFT('y',invert);
+ if (Ir.width>1) FFT('x',invert);
+#endif
+ return *this;
+ }
+
+ CImgList<Tfloat> get_FFT(const bool invert=false) const {
+ return CImgList<Tfloat>(*this).FFT(invert);
+ }
+
+ // Return a list where each image has been split along the specified axis.
+ CImgList<T>& split(const char axis) {
+ return get_split(axis).transfer_to(*this);
+ }
+
+ CImgList<T> get_split(const char axis) const {
+ CImgList<T> res;
+ cimglist_for(*this,l) {
+ CImgList<T> 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<T> image corresponding to the concatenation is returned.
+ **/
+ CImg<T> get_append(const char axis, const char align='p') const {
+ if (is_empty()) return CImg<T>();
+ if (size==1) return +((*this)[0]);
+ unsigned int dx = 0, dy = 0, dz = 0, dv = 0, pos = 0;
+ CImg<T> 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<T>& 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<T>((*this)[l],true).unroll('x'));
+ pos+=(*this)[l].size();
+ }
+ } break;
+ case 'y' : {
+ cimglist_for(*this,l) res.draw_image(pos++,CImg<T>((*this)[l],true).unroll('y'));
+ } break;
+ case 'z' : {
+ cimglist_for(*this,l) res.draw_image(pos++,CImg<T>((*this)[l],true).unroll('z'));
+ } break;
+ case 'v' : {
+ cimglist_for(*this,l) res.draw_image(pos++,CImg<T>((*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<T>& 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<T>((*this)[l],true).unroll('x'));
+ } break;
+ case 'y' : {
+ cimglist_for(*this,l) {
+ res.draw_image(0,pos,CImg<T>((*this)[l],true).unroll('y'));
+ pos+=(*this)[l].size();
+ }
+ } break;
+ case 'z' : {
+ cimglist_for(*this,l) res.draw_image(0,pos++,CImg<T>((*this)[l],true).unroll('z'));
+ } break;
+ case 'v' : {
+ cimglist_for(*this,l) res.draw_image(0,pos++,CImg<T>((*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<T>& 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<T>((*this)[l],true).unroll('x'));
+ } break;
+ case 'y' : {
+ cimglist_for(*this,l) res.draw_image(0,0,pos++,CImg<T>((*this)[l],true).unroll('y'));
+ } break;
+ case 'z' : {
+ cimglist_for(*this,l) {
+ res.draw_image(0,0,pos,CImg<T>((*this)[l],true).unroll('z'));
+ pos+=(*this)[l].size();
+ }
+ } break;
+ case 'v' : {
+ cimglist_for(*this,l) res.draw_image(0,0,pos++,CImg<T>((*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<T>& 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<T>((*this)[l],true).unroll('x'));
+ } break;
+ case 'y' : {
+ cimglist_for(*this,l) res.draw_image(0,0,0,pos++,CImg<T>((*this)[l],true).unroll('y'));
+ } break;
+ case 'z' : {
+ cimglist_for(*this,l) res.draw_image(0,0,0,pos++,CImg<T>((*this)[l],true).unroll('v'));
+ } break;
+ case 'v' : {
+ cimglist_for(*this,l) {
+ res.draw_image(0,0,0,pos,CImg<T>((*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<T>& crop_font() {
+ return get_crop_font().transfer_to(*this);
+ }
+
+ CImgList<T> get_crop_font() const {
+ CImgList<T> res;
+ cimglist_for(*this,l) {
+ const CImg<T>& letter = (*this)[l];
+ int xmin = letter.width, xmax = 0;
+ cimg_forXY(letter,x,y) if (letter(x,y)) { if (x<xmin) xmin=x; if (x>xmax) xmax=x; }
+ if (xmin>xmax) res.insert(CImg<T>(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<T>& invert_object3d() {
+ cimglist_for(*this,l) {
+ CImg<T>& 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<T> 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<T> font(const unsigned int font_width, const bool variable_size=true) {
+ if (font_width<=11) {
+ static CImgList<T> 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<T> 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<T> 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<T> 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<T> 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<T> 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<T> 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<T> 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<T> _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<T> res = CImgList<T>(256,w,h,1,3).insert(CImgList<T>(256,w,h,1,1));
+ const unsigned int *ptr = font;
+ unsigned int m = 0, val = 0;
+ for (unsigned int y=0; y<h; ++y)
+ for (unsigned int x=0; x<256*w; ++x) {
+ m>>=1; if (!m) { m = 0x80000000; val = *(ptr++); }
+ CImg<T>& 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<T>& 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<T>& 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<T> 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<T>& display(const char *const title=0,
+ const bool display_info=true, const char axis='x', const char align='p') const {
+ const CImg<T> 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<charT> value_string(const char separator=',', const unsigned int max_size=0) const {
+ if (is_empty()) return CImg<ucharT>(1,1,1,1,0);
+ CImgList<charT> items;
+ for (unsigned int l = 0; l<size-1; ++l) {
+ CImg<charT> 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<charT> 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<T>& 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<T>& 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<T> get_load(const char *const filename) {
+ return CImgList<T>().load(filename);
+ }
+
+ //! Load an image list from a .cimg file.
+ CImgList<T>& load_cimg(const char *const filename) {
+ return _load_cimg(0,filename);
+ }
+
+ static CImgList<T> get_load_cimg(const char *const filename) {
+ return CImgList<T>().load_cimg(filename);
+ }
+
+ //! Load an image list from a .cimg file.
+ CImgList<T>& load_cimg(cimg_std::FILE *const file) {
+ return _load_cimg(file,0);
+ }
+
+ static CImgList<T> get_load_cimg(cimg_std::FILE *const file) {
+ return CImgList<T>().load_cimg(file);
+ }
+
+ CImgList<T>& _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<N; ++l) { \
+ j = 0; while ((i=cimg_std::fgetc(nfile))!='\n' && i>=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<Tss> raw; \
+ CImg<T> &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<T>& load_cimg(const char *const filename,
+ const unsigned int n0, const unsigned int n1,
+ const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int 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<T> get_load_cimg(const char *const filename,
+ const unsigned int n0, const unsigned int n1,
+ const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int v0,
+ const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1) {
+ return CImgList<T>().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<T>& 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<T> 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<T>().load_cimg(file,n0,n1,x0,y0,z0,v0,x1,y1,z1,v1);
+ }
+
+ CImgList<T>& _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<n0 || x0>=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<Tss> raw(1+nx1-x0); \
+ CImg<T> &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<n0 || x1<x0 || y1<y0 || z1<z0 || v1<v0)
+ throw CImgArgumentException("CImgList<%s>::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<T>& 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<floatT> st_slices;
+ CImgList<uintT> 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<floatT>::vector((float)sn,(float)pixsize,(float)sizex,(float)sizey,
+ ri,rs,ss,0));
+ unsigned int i; for (i=0; i<st_global.size && sn<=st_global[i][2]; ++i) {}
+ if (i==st_global.size) st_global.insert(CImg<uintT>::vector(sizex,sizey,sn));
+ else {
+ CImg<uintT> &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<uintT>& vec = st_global[l];
+ insert(CImg<T>(vec[0],vec[1],vec[2]));
+ }}
+
+ cimglist_for(st_slices,l) {
+ const CImg<floatT>& 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<ucharT> buf(sizex,sizey);
+ cimg::fread(buf.data,sizex*sizey,file2);
+ if (cimg::endianness()) cimg::invert_endianness(buf.data,sizex*sizey);
+ CImg<T>& img = (*this)[imn];
+ cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
+ } break;
+ case 16 : {
+ CImg<ushortT> buf(sizex,sizey);
+ cimg::fread(buf.data,sizex*sizey,file2);
+ if (cimg::endianness()) cimg::invert_endianness(buf.data,sizex*sizey);
+ CImg<T>& img = (*this)[imn];
+ cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
+ } break;
+ case 32 : {
+ CImg<uintT> buf(sizex,sizey);
+ cimg::fread(buf.data,sizex*sizey,file2);
+ if (cimg::endianness()) cimg::invert_endianness(buf.data,sizex*sizey);
+ CImg<T>& 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<T> get_load_parrec(const char *const filename) {
+ return CImgList<T>().load_parrec(filename);
+ }
+
+ //! Load an image sequence from a YUV file.
+ CImgList<T>& 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<T> 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<T>().load_yuv(filename,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb);
+ }
+
+ //! Load an image sequence from a YUV file.
+ CImgList<T>& 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<T> 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<T>().load_yuv(file,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb);
+ }
+
+ CImgList<T>& _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<last_frame?first_frame:last_frame,
+ nlast_frame = first_frame<last_frame?last_frame:first_frame,
+ nstep_frame = step_frame?step_frame:1;
+
+ CImg<ucharT> 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<T>& load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
+ const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false) {
+ if (!filename)
+ throw CImgArgumentException("CImgList<%s>::load_ffmpeg() : Cannot load (null) filename.",
+ pixel_type());
+ const unsigned int
+ nfirst_frame = first_frame<last_frame?first_frame:last_frame,
+ nlast_frame = first_frame<last_frame?last_frame:first_frame,
+ nstep_frame = step_frame?step_frame:1;
+ assign();
+
+#ifndef cimg_use_ffmpeg
+ if ((nfirst_frame || nlast_frame!=~0U || nstep_frame>1) || (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<doubleT> 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<doubleT>::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<ucharT> next_image(*converted_frame->data,3,codec_ctx->width,codec_ctx->height,1,true);
+ insert(next_image._get_permute_axes("yzvx",foo));
+ } else {
+ CImg<ucharT> 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<T> get_load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
+ const unsigned int step_frame=1, const bool pixel_format=true) {
+ return CImgList<T>().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<T>& 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<T> 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<T> get_load_ffmpeg_external(const char *const filename) {
+ return CImgList<T>().load_ffmpeg_external(filename);
+ }
+
+ //! Load a gzipped list, using external tool 'gunzip'.
+ CImgList<T>& 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<T> get_load_gzip_external(const char *const filename) {
+ return CImgList<T>().load_gzip_external(filename);
+ }
+
+ //! Load a 3D object from a .OFF file.
+ template<typename tf, typename tc>
+ CImgList<T>& load_off(const char *const filename,
+ CImgList<tf>& primitives, CImgList<tc>& colors,
+ const bool invert_faces=false) {
+ return get_load_off(filename,primitives,colors,invert_faces).transfer_to(*this);
+ }
+
+ template<typename tf, typename tc>
+ static CImgList<T> get_load_off(const char *const filename,
+ CImgList<tf>& primitives, CImgList<tc>& colors,
+ const bool invert_faces=false) {
+ return CImg<T>().load_off(filename,primitives,colors,invert_faces).get_split('x');
+ }
+
+ //! Load a TIFF file.
+ CImgList<T>& load_tiff(const char *const filename,
+ const unsigned int first_frame=0, const unsigned int last_frame=~0U,
+ const unsigned int step_frame=1) {
+ const unsigned int
+ nfirst_frame = first_frame<last_frame?first_frame:last_frame,
+ nstep_frame = step_frame?step_frame:1;
+ unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
+#ifndef cimg_use_tiff
+ if (nfirst_frame || nlast_frame!=~0U || nstep_frame!=1)
+ throw CImgArgumentException("CImgList<%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 assign(CImg<T>::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<T> get_load_tiff(const char *const filename,
+ const unsigned int first_frame=0, const unsigned int last_frame=~0U,
+ const unsigned int step_frame=1) {
+ return CImgList<T>().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<T>& 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<T>& 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<uint8_t> 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<T>& _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<ucharT> 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<T>& 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<T>& 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<T> images.
+ \param filename : name of the output file.
+ \return A reference to the current CImgList instance is returned.
+ **/
+ const CImgList<T>& _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<T>& img = data[l];
+ cimg_std::fprintf(nfile,"%u %u %u %u",img.width,img.height,img.depth,img.dim);
+ if (img.data) {
+ CImg<T> tmp;
+ if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp.data,tmp.size()); }
+ const CImg<T>& 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<T>& 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<T>& 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<T>& _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<lmax; ++l) { \
+ j = 0; while((i=cimg_std::fgetc(nfile))!='\n') 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>::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<n0 || x0>=W || y0>=H || z0>=D || v0>=D) cimg_std::fseek(nfile,W*H*D*V*sizeof(Tss),SEEK_CUR); \
+ else { \
+ const CImg<T>& 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<Tss> 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<T>& save_cimg(const char *const filename,
+ const unsigned int n0,
+ const unsigned int x0, const unsigned int y0,
+ const unsigned int z0, const unsigned int 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<T>& 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<T>& 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<size; ++l) {
+ const CImg<T>& 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<T>& 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<typename tf, typename tc>
+ const CImgList<T>& save_off(const char *const filename,
+ const CImgList<tf>& primitives, const CImgList<tc>& 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<typename tf, typename tc>
+ const CImgList<T>& save_off(cimg_std::FILE *const file,
+ const CImgList<tf>& primitives, const CImgList<tc>& 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<T>& 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<typename t>
+ 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<t>& 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<unsigned char> buttons, cbuttons, sbuttons;
+ if (button1_txt) { buttons.insert(CImg<unsigned char>().draw_text(0,0,button1_txt,black,gray,1,13));
+ if (button2_txt) { buttons.insert(CImg<unsigned char>().draw_text(0,0,button2_txt,black,gray,1,13));
+ if (button3_txt) { buttons.insert(CImg<unsigned char>().draw_text(0,0,button3_txt,black,gray,1,13));
+ if (button4_txt) { buttons.insert(CImg<unsigned char>().draw_text(0,0,button4_txt,black,gray,1,13));
+ if (button5_txt) { buttons.insert(CImg<unsigned char>().draw_text(0,0,button5_txt,black,gray,1,13));
+ if (button6_txt) { buttons.insert(CImg<unsigned char>().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<unsigned char> 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<unsigned char> 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<unsigned char> 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<unsigned char>(cbutton).draw_image(1+(bw-buttons[ll].dimx())/2,1+(bh-buttons[ll].dimy())/2,buttons[ll]));
+ sbuttons.insert(CImg<unsigned char>(sbutton).draw_image((bw-buttons[ll].dimx())/2,(bh-buttons[ll].dimy())/2,buttons[ll]));
+ buttons[ll] = CImg<unsigned char>(button).draw_image((bw-buttons[ll].dimx())/2,(bh-buttons[ll].dimy())/2,buttons[ll]);
+ }
+
+ CImg<unsigned char> canvas;
+ if (msg) canvas = CImg<unsigned char>().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<unsigned char>(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<unsigned char>(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<unsigned char>(canvas).draw_image(xbuttons[clicked],by,cbuttons[clicked]).display(disp);
+ else {
+ if (selected>=0) CImg<unsigned char>(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<unsigned char>::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
diff --git a/src/libs/greycstoration/LICENSE.txt b/src/libs/greycstoration/LICENSE.txt
new file mode 100644
index 00000000..a36324de
--- /dev/null
+++ b/src/libs/greycstoration/LICENSE.txt
@@ -0,0 +1,505 @@
+
+ CeCILL FREE SOFTWARE LICENSE AGREEMENT
+
+
+ Notice
+
+This Agreement is a Free Software license agreement that is the result
+of discussions between its authors in order to ensure compliance with
+the two main principles guiding its drafting:
+
+ * firstly, compliance with the principles governing the distribution
+ of Free Software: access to source code, broad rights granted to
+ users,
+ * secondly, the election of a governing law, French law, with which
+ it is conformant, both as regards the law of torts and
+ intellectual property law, and the protection that it offers to
+ both authors and holders of the economic rights over software.
+
+The authors of the CeCILL (for Ce[a] C[nrs] I[nria] L[logiciel] L[ibre])
+license are:
+
+Commissariat à l'Energie Atomique - CEA, a public scientific, technical
+and industrial establishment, having its principal place of business at
+31-33 rue de la Fédération, 75752 Paris cedex 15, France.
+
+Centre National de la Recherche Scientifique - CNRS, a public scientific
+and technological establishment, having its principal place of business
+at 3 rue Michel-Ange 75794 Paris cedex 16, France.
+
+Institut National de Recherche en Informatique et en Automatique -
+INRIA, a public scientific and technological establishment, having its
+principal place of business at Domaine de Voluceau, Rocquencourt, BP
+105, 78153 Le Chesnay cedex, France.
+
+
+ Preamble
+
+The purpose of this Free Software license agreement is to grant users
+the right to modify and redistribute the software governed by this
+license within the framework of an open source distribution model.
+
+The exercising of these rights is conditional upon certain obligations
+for users so as to preserve this status for all subsequent redistributions.
+
+In consideration of access to the source code and the 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 only have limited liability.
+
+In this respect, the risks associated with loading, using, modifying
+and/or developing or reproducing the software by the user are brought to
+the user's attention, given its Free Software status, which may make it
+complicated to use, with the result that its use 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 of
+security. This Agreement may be freely reproduced and published,
+provided it is not altered, and that no provisions are either added or
+removed herefrom.
+
+This Agreement may apply to any or all software for which the holder of
+the economic rights decides to submit the use thereof to its provisions.
+
+
+ Article 1 - DEFINITIONS
+
+For the purpose of this Agreement, when the following expressions
+commence with a capital letter, they shall have the following meaning:
+
+Agreement: means this license agreement, and its possible subsequent
+versions and annexes.
+
+Software: means the software in its Object Code and/or Source Code form
+and, where applicable, its documentation, "as is" when the Licensee
+accepts the Agreement.
+
+Initial Software: means the Software in its Source Code and possibly its
+Object Code form and, where applicable, its documentation, "as is" when
+it is first distributed under the terms and conditions of the Agreement.
+
+Modified Software: means the Software modified by at least one
+Contribution.
+
+Source Code: means all the Software's instructions and program lines to
+which access is required so as to modify the Software.
+
+Object Code: means the binary files originating from the compilation of
+the Source Code.
+
+Holder: means the holder(s) of the economic rights over the Initial
+Software.
+
+Licensee: means the Software user(s) having accepted the Agreement.
+
+Contributor: means a Licensee having made at least one Contribution.
+
+Licensor: means the Holder, or any other individual or legal entity, who
+distributes the Software under the Agreement.
+
+Contribution: means any or all modifications, corrections, translations,
+adaptations and/or new functions integrated into the Software by any or
+all Contributors, as well as any or all Internal Modules.
+
+Module: means a set of sources files including their documentation that
+enables supplementary functions or services in addition to those offered
+by the Software.
+
+External Module: means any or all Modules, not derived from the
+Software, so that this Module and the Software run in separate address
+spaces, with one calling the other when they are run.
+
+Internal Module: means any or all Module, connected to the Software so
+that they both execute in the same address space.
+
+GNU GPL: means the GNU General Public License version 2 or any
+subsequent version, as published by the Free Software Foundation Inc.
+
+Parties: mean both the Licensee and the Licensor.
+
+These expressions may be used both in singular and plural form.
+
+
+ Article 2 - PURPOSE
+
+The purpose of the Agreement is the grant by the Licensor to the
+Licensee of a non-exclusive, transferable and worldwide license for the
+Software as set forth in Article 5 hereinafter for the whole term of the
+protection granted by the rights over said Software.
+
+
+ Article 3 - ACCEPTANCE
+
+3.1 The Licensee shall be deemed as having accepted the terms and
+conditions of this Agreement upon the occurrence of the first of the
+following events:
+
+ * (i) loading the Software by any or all means, notably, by
+ downloading from a remote server, or by loading from a physical
+ medium;
+ * (ii) the first time the Licensee exercises any of the rights
+ granted hereunder.
+
+3.2 One copy of the Agreement, containing a notice relating to the
+characteristics of the Software, to the limited warranty, and to the
+fact that its use is restricted to experienced users has been provided
+to the Licensee prior to its acceptance as set forth in Article 3.1
+hereinabove, and the Licensee hereby acknowledges that it has read and
+understood it.
+
+
+ Article 4 - EFFECTIVE DATE AND TERM
+
+
+ 4.1 EFFECTIVE DATE
+
+The Agreement shall become effective on the date when it is accepted by
+the Licensee as set forth in Article 3.1.
+
+
+ 4.2 TERM
+
+The Agreement shall remain in force for the entire legal term of
+protection of the economic rights over the Software.
+
+
+ Article 5 - SCOPE OF RIGHTS GRANTED
+
+The Licensor hereby grants to the Licensee, who accepts, the following
+rights over the Software for any or all use, and for the term of the
+Agreement, on the basis of the terms and conditions set forth hereinafter.
+
+Besides, if the Licensor owns or comes to own one or more patents
+protecting all or part of the functions of the Software or of its
+components, the Licensor undertakes not to enforce the rights granted by
+these patents against successive Licensees using, exploiting or
+modifying the Software. If these patents are transferred, the Licensor
+undertakes to have the transferees subscribe to the obligations set
+forth in this paragraph.
+
+
+ 5.1 RIGHT OF USE
+
+The Licensee is authorized to use the Software, without any limitation
+as to its fields of application, with it being hereinafter specified
+that this comprises:
+
+ 1. permanent or temporary reproduction of all or part of the Software
+ by any or all means and in any or all form.
+
+ 2. loading, displaying, running, or storing the Software on any or
+ all medium.
+
+ 3. entitlement to observe, study or test its operation so as to
+ determine the ideas and principles behind any or all constituent
+ elements of said Software. This shall apply when the Licensee
+ carries out any or all loading, displaying, running, transmission
+ or storage operation as regards the Software, that it is entitled
+ to carry out hereunder.
+
+
+ 5.2 ENTITLEMENT TO MAKE CONTRIBUTIONS
+
+The right to make Contributions includes the right to translate, adapt,
+arrange, or make any or all modifications to the Software, and the right
+to reproduce the resulting Software.
+
+The Licensee is authorized to make any or all Contributions to the
+Software provided that it includes an explicit notice that it is the
+author of said Contribution and indicates the date of the creation thereof.
+
+
+ 5.3 RIGHT OF DISTRIBUTION
+
+In particular, the right of distribution includes the right to publish,
+transmit and communicate the Software to the general public on any or
+all medium, and by any or all means, and the right to market, either in
+consideration of a fee, or free of charge, one or more copies of the
+Software by any means.
+
+The Licensee is further authorized to distribute copies of the modified
+or unmodified Software to third parties according to the terms and
+conditions set forth hereinafter.
+
+
+ 5.3.1 DISTRIBUTION OF SOFTWARE WITHOUT MODIFICATION
+
+The Licensee is authorized to distribute true copies of the Software in
+Source Code or Object Code form, provided that said distribution
+complies with all the provisions of the Agreement and is accompanied by:
+
+ 1. a copy of the Agreement,
+
+ 2. a notice relating to the limitation of both the Licensor's
+ warranty and liability as set forth in Articles 8 and 9,
+
+and that, in the event that only the Object Code of the Software is
+redistributed, the Licensee allows future Licensees unhindered access to
+the full Source Code of the Software by indicating how to access it, it
+being understood that the additional cost of acquiring the Source Code
+shall not exceed the cost of transferring the data.
+
+
+ 5.3.2 DISTRIBUTION OF MODIFIED SOFTWARE
+
+When the Licensee makes a Contribution to the Software, the terms and
+conditions for the distribution of the Modified Software become subject
+to all the provisions of this Agreement.
+
+The Licensee is authorized to distribute the Modified Software, in
+Source Code or Object Code form, provided that said distribution
+complies with all the provisions of the Agreement and is accompanied by:
+
+ 1. a copy of the Agreement,
+
+ 2. a notice relating to the limitation of both the Licensor's
+ warranty and liability as set forth in Articles 8 and 9,
+
+and that, in the event that only the Object Code of the Modified
+Software is redistributed, the Licensee allows future Licensees
+unhindered access to the full Source Code of the Modified Software by
+indicating how to access it, it being understood that the additional
+cost of acquiring the Source Code shall not exceed the cost of
+transferring the data.
+
+
+ 5.3.3 DISTRIBUTION OF EXTERNAL MODULES
+
+When the Licensee has developed an External Module, the terms and
+conditions of this Agreement do not apply to said External Module, that
+may be distributed under a separate license agreement.
+
+
+ 5.3.4 COMPATIBILITY WITH THE GNU GPL
+
+The Licensee can include a code that is subject to the provisions of one
+of the versions of the GNU GPL in the Modified or unmodified Software,
+and distribute that entire code under the terms of the same version of
+the GNU GPL.
+
+The Licensee can include the Modified or unmodified Software in a code
+that is subject to the provisions of one of the versions of the GNU GPL,
+and distribute that entire code under the terms of the same version of
+the GNU GPL.
+
+
+ Article 6 - INTELLECTUAL PROPERTY
+
+
+ 6.1 OVER THE INITIAL SOFTWARE
+
+The Holder owns the economic rights over the Initial Software. Any or
+all use of the Initial Software is subject to compliance with the terms
+and conditions under which the Holder has elected to distribute its work
+and no one shall be entitled to modify the terms and conditions for the
+distribution of said Initial Software.
+
+The Holder undertakes that the Initial Software will remain ruled at
+least by the current license, for the duration set forth in article 4.2.
+
+
+ 6.2 OVER THE CONTRIBUTIONS
+
+A Licensee who develops a Contribution is the owner of the intellectual
+property rights over this Contribution as defined by applicable law.
+
+
+ 6.3 OVER THE EXTERNAL MODULES
+
+A Licensee who develops an External Module is the owner of the
+intellectual property rights over this External Module as defined by
+applicable law and is free to choose the type of agreement that shall
+govern its distribution.
+
+
+ 6.4 JOINT PROVISIONS
+
+The Licensee expressly undertakes:
+
+ 1. not to remove, or modify, in any manner, the intellectual property
+ notices attached to the Software;
+
+ 2. to reproduce said notices, in an identical manner, in the copies
+ of the Software modified or not.
+
+The Licensee undertakes not to directly or indirectly infringe the
+intellectual property rights of the Holder and/or Contributors on the
+Software and to take, where applicable, vis-à-vis its staff, any and all
+measures required to ensure respect of said intellectual property rights
+of the Holder and/or Contributors.
+
+
+ Article 7 - RELATED SERVICES
+
+7.1 Under no circumstances shall the Agreement oblige the Licensor to
+provide technical assistance or maintenance services for the Software.
+
+However, the Licensor is entitled to offer this type of services. The
+terms and conditions of such technical assistance, and/or such
+maintenance, shall be set forth in a separate instrument. Only the
+Licensor offering said maintenance and/or technical assistance services
+shall incur liability therefor.
+
+7.2 Similarly, any Licensor is entitled to offer to its licensees, under
+its sole responsibility, a warranty, that shall only be binding upon
+itself, for the redistribution of the Software and/or the Modified
+Software, under terms and conditions that it is free to decide. Said
+warranty, and the financial terms and conditions of its application,
+shall be subject of a separate instrument executed between the Licensor
+and the Licensee.
+
+
+ Article 8 - LIABILITY
+
+8.1 Subject to the provisions of Article 8.2, the Licensee shall be
+entitled to claim compensation for any direct loss it may have suffered
+from the Software as a result of a fault on the part of the relevant
+Licensor, subject to providing evidence thereof.
+
+8.2 The Licensor's liability is limited to the commitments made under
+this Agreement and shall not be incurred as a result of in particular:
+(i) loss due the Licensee's total or partial failure to fulfill its
+obligations, (ii) direct or consequential loss that is suffered by the
+Licensee due to the use or performance of the Software, and (iii) more
+generally, any consequential loss. In particular the Parties expressly
+agree that any or all pecuniary or business loss (i.e. loss of data,
+loss of profits, operating loss, loss of customers or orders,
+opportunity cost, any disturbance to business activities) or any or all
+legal proceedings instituted against the Licensee by a third party,
+shall constitute consequential loss and shall not provide entitlement to
+any or all compensation from the Licensor.
+
+
+ Article 9 - WARRANTY
+
+9.1 The Licensee acknowledges that the scientific and technical
+state-of-the-art when the Software was distributed did not enable all
+possible uses to be tested and verified, nor for the presence of
+possible defects to be detected. In this respect, the Licensee's
+attention has been drawn to the risks associated with loading, using,
+modifying and/or developing and reproducing the Software which are
+reserved for experienced users.
+
+The Licensee shall be responsible for verifying, by any or all means,
+the product's suitability for its requirements, its good working order,
+and for ensuring that it shall not cause damage to either persons or
+properties.
+
+9.2 The Licensor hereby represents, in good faith, that it is entitled
+to grant all the rights over the Software (including in particular the
+rights set forth in Article 5).
+
+9.3 The Licensee acknowledges that the Software is supplied "as is" by
+the Licensor without any other express or tacit warranty, other than
+that provided for in Article 9.2 and, in particular, without any warranty
+as to its commercial value, its secured, safe, innovative or relevant
+nature.
+
+Specifically, the Licensor does not warrant that the Software is free
+from any error, that it will operate without interruption, that it will
+be compatible with the Licensee's own equipment and software
+configuration, nor that it will meet the Licensee's requirements.
+
+9.4 The Licensor does not either expressly or tacitly warrant that the
+Software does not infringe any third party intellectual property right
+relating to a patent, software or any other property right. Therefore,
+the Licensor disclaims any and all liability towards the Licensee
+arising out of any or all proceedings for infringement that may be
+instituted in respect of the use, modification and redistribution of the
+Software. Nevertheless, should such proceedings be instituted against
+the Licensee, the Licensor shall provide it with technical and legal
+assistance for its defense. Such technical and legal assistance shall be
+decided on a case-by-case basis between the relevant Licensor and the
+Licensee pursuant to a memorandum of understanding. The Licensor
+disclaims any and all liability as regards the Licensee's use of the
+name of the Software. No warranty is given as regards the existence of
+prior rights over the name of the Software or as regards the existence
+of a trademark.
+
+
+ Article 10 - TERMINATION
+
+10.1 In the event of a breach by the Licensee of its obligations
+hereunder, the Licensor may automatically terminate this Agreement
+thirty (30) days after notice has been sent to the Licensee and has
+remained ineffective.
+
+10.2 A Licensee whose Agreement is terminated shall no longer be
+authorized to use, modify or distribute the Software. However, any
+licenses that it may have granted prior to termination of the Agreement
+shall remain valid subject to their having been granted in compliance
+with the terms and conditions hereof.
+
+
+ Article 11 - MISCELLANEOUS
+
+
+ 11.1 EXCUSABLE EVENTS
+
+Neither Party shall be liable for any or all delay, or failure to
+perform the Agreement, that may be attributable to an event of force
+majeure, an act of God or an outside cause, such as defective
+functioning or interruptions of the electricity or telecommunications
+networks, network paralysis following a virus attack, intervention by
+government authorities, natural disasters, water damage, earthquakes,
+fire, explosions, strikes and labor unrest, war, etc.
+
+11.2 Any Failure by either Party, on one or more occasions, to invoke
+one or more of the provisions hereof, shall under no circumstances be
+interpreted as being a waiver by the interested Party of its right to
+invoke said provision(s) subsequently.
+
+11.3 The Agreement cancels and replaces any or all previous agreements,
+whether written or oral, between the Parties and having the same
+purpose, and constitutes the entirety of the agreement between said
+Parties concerning said purpose. No supplement or modification to the
+terms and conditions hereof shall be effective as between the Parties
+unless it is made in writing and signed by their duly authorized
+representatives.
+
+11.4 In the event that one or more of the provisions hereof were to
+conflict with a current or future applicable act or legislative text,
+said act or legislative text shall prevail, and the Parties shall make
+the necessary amendments so as to comply with said act or legislative
+text. All other provisions shall remain effective. Similarly, invalidity
+of a provision of the Agreement, for any reason whatsoever, shall not
+cause the Agreement as a whole to be invalid.
+
+
+ 11.5 LANGUAGE
+
+The Agreement is drafted in both French and English and both versions
+are deemed authentic.
+
+
+ Article 12 - NEW VERSIONS OF THE AGREEMENT
+
+12.1 Any person is authorized to duplicate and distribute copies of this
+Agreement.
+
+12.2 So as to ensure coherence, the wording of this Agreement is
+protected and may only be modified by the authors of the License, who
+reserve the right to periodically publish updates or new versions of the
+Agreement, each with a separate number. These subsequent versions may
+address new issues encountered by Free Software.
+
+12.3 Any Software distributed under a given version of the Agreement may
+only be subsequently distributed under the same version of the Agreement
+or a subsequent version, subject to the provisions of Article 5.3.4.
+
+
+ Article 13 - GOVERNING LAW AND JURISDICTION
+
+13.1 The Agreement is governed by French law. The Parties agree to
+endeavor to seek an amicable solution to any disagreements or disputes
+that may arise during the performance of the Agreement.
+
+13.2 Failing an amicable solution within two (2) months as from their
+occurrence, and unless emergency proceedings are necessary, the
+disagreements or disputes shall be referred to the Paris Courts having
+jurisdiction, by the more diligent Party.
+
+
+Version 2.0 dated 2005-05-21.
diff --git a/src/libs/greycstoration/Makefile.am b/src/libs/greycstoration/Makefile.am
new file mode 100644
index 00000000..f7cc569f
--- /dev/null
+++ b/src/libs/greycstoration/Makefile.am
@@ -0,0 +1,20 @@
+METASOURCES = AUTO
+
+KDE_CXXFLAGS = $(USE_EXCEPTIONS) -w
+
+noinst_LTLIBRARIES = libgreycstoration.la
+
+libgreycstoration_la_SOURCES = greycstorationiface.cpp greycstorationwidget.cpp
+
+libgreycstoration_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TQT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor
+
+INCLUDES = -I$(top_srcdir)/src/libs/dimg \
+ -I$(top_srcdir)/src/libs/dimg/filters \
+ -I$(top_srcdir)/src/digikam \
+ $(LIBKDCRAW_CFLAGS) \
+ $(all_includes)
+
+digikaminclude_HEADERS = greycstorationiface.h greycstorationwidget.h greycstorationsettings.h
+
+digikamincludedir = $(includedir)/digikam
+
diff --git a/src/libs/greycstoration/greycstoration.h b/src/libs/greycstoration/greycstoration.h
new file mode 100644
index 00000000..36b2c0e2
--- /dev/null
+++ b/src/libs/greycstoration/greycstoration.h
@@ -0,0 +1,481 @@
+/*
+ #
+ # File : greycstoration.h
+ # ( C++ header file - CImg plug-in )
+ #
+ # Description : GREYCstoration plug-in allowing easy integration in
+ # third parties softwares.
+ # ( http://www.greyc.ensicaen.fr/~dtschump/greycstoration/ )
+ # This file is a part of the CImg Library project.
+ # ( http://cimg.sourceforge.net )
+ #
+ # THIS PLUG-IN IS INTENDED FOR DEVELOPERS ONLY. IT EASES THE INTEGRATION ALGORITHM IN
+ # THIRD PARTIES SOFTWARES. IF YOU ARE A USER OF GREYCSTORATION, PLEASE LOOK
+ # AT THE FILE 'greycstoration.cpp' WHICH IS THE SOURCE OF THE COMPLETE
+ # COMMAND LINE GREYCSTORATION TOOL.
+ #
+ # Copyright : David Tschumperle
+ # ( http://www.greyc.ensicaen.fr/~dtschump/ )
+ #
+ # License : CeCILL v2.0
+ # ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html )
+ #
+ # This software is governed by the CeCILL 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
+ # license 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 license and that you accept its terms.
+ #
+*/
+
+#ifndef cimg_plugin_greycstoration
+#define cimg_plugin_greycstoration
+
+//------------------------------------------------------------------------------
+// GREYCstoration parameter structure, storing important informations about
+// algorithm parameters and computing threads.
+// ** This structure has not to be manipulated by the API user, so please just
+// ignore it if you want to **
+//-------------------------------------------------------------------------------
+struct _greycstoration_params {
+
+ // Tell if the patch-based algorithm is selected
+ bool patch_based;
+
+ // Parameters specific to the non-patch regularization algorithm
+ float amplitude;
+ float sharpness;
+ float anisotropy;
+ float alpha;
+ float sigma;
+ float gfact;
+ float dl;
+ float da;
+ float gauss_prec;
+ unsigned int interpolation;
+
+ // Parameters specific to the patch-based regularization algorithm
+ unsigned int patch_size;
+ float sigma_s;
+ float sigma_p;
+ unsigned int lookup_size;
+
+ // Non-specific parameters of the algorithms.
+ CImg<T> *source;
+ const CImg<unsigned char> *mask;
+ CImg<T> *temporary;
+ unsigned long *counter;
+ unsigned int tile;
+ unsigned int tile_border;
+ unsigned int thread;
+ unsigned int nb_threads;
+ bool fast_approx;
+ bool is_running;
+ bool *stop_request;
+#if cimg_OS==1 && defined(_PTHREAD_H)
+ pthread_mutex_t
+ *mutex;
+#elif cimg_OS==2
+ HANDLE mutex;
+#else
+ void *mutex;
+#endif
+
+ // Default constructor
+ _greycstoration_params():patch_based(false),amplitude(0),sharpness(0),anisotropy(0),alpha(0),sigma(0),gfact(1),
+ dl(0),da(0),gauss_prec(0),interpolation(0),patch_size(0),
+ sigma_s(0),sigma_p(0),lookup_size(0),source(0),mask(0),temporary(0),counter(0),tile(0),
+ tile_border(0),thread(0),nb_threads(0),fast_approx(false),is_running(false), stop_request(0), mutex(0) {}
+};
+
+_greycstoration_params greycstoration_params[16];
+
+//----------------------------------------------------------
+// Public functions of the GREYCstoration API.
+// Use the functions below for integrating GREYCstoration
+// in your own C++ code.
+//----------------------------------------------------------
+
+//! Test if GREYCstoration threads are still running.
+bool greycstoration_is_running() const {
+ return greycstoration_params->is_running;
+}
+
+//! Force the GREYCstoration threads to stop.
+CImg& greycstoration_stop() {
+ if (greycstoration_is_running()) {
+ *(greycstoration_params->stop_request) = true;
+ while (greycstoration_params->is_running) cimg::wait(50);
+ }
+ return *this;
+}
+
+//! Return the GREYCstoration progress bar indice (between 0 and 100).
+float greycstoration_progress() const {
+ if (!greycstoration_is_running()) return 0.0f;
+ const unsigned long counter = greycstoration_params->counter?*(greycstoration_params->counter):0;
+ const float
+ da = greycstoration_params->da,
+ factor = greycstoration_params->patch_based?1:(1+360/da);
+ float maxcounter = 0;
+ if (greycstoration_params->tile==0) maxcounter = width*height*depth*factor;
+ else {
+ const unsigned int
+ t = greycstoration_params->tile,
+ b = greycstoration_params->tile_border,
+ n = (1+(width-1)/t)*(1+(height-1)/t)*(1+(depth-1)/t);
+ maxcounter = (width*height*depth + n*4*b*(b + t))*factor;
+ }
+ return cimg::min(counter*99.9f/maxcounter,99.9f);
+}
+
+//! Run the non-patch version of the GREYCstoration algorithm on the instance image, using a mask.
+CImg& greycstoration_run(const CImg<unsigned char>& mask,
+ const float amplitude=60, const float sharpness=0.7f, const float anisotropy=0.3f,
+ const float alpha=0.6f, const float sigma=1.1f, const float gfact=1.0f,
+ const float dl=0.8f, const float da=30.0f,
+ const float gauss_prec=2.0f, const unsigned int interpolation=0, const bool fast_approx=true,
+ const unsigned int tile=0, const unsigned int tile_border=0, const unsigned int nb_threads=1) {
+
+ if (greycstoration_is_running())
+ throw CImgInstanceException("CImg<T>::greycstoration_run() : A GREYCstoration thread is already running on"
+ " the instance image (%u,%u,%u,%u,%p).",width,height,depth,dim,data);
+
+ else {
+ if (!mask.is_empty() && !mask.is_sameXY(*this))
+ throw CImgArgumentException("CImg<%s>::greycstoration_run() : Given mask (%u,%u,%u,%u,%p) and instance image "
+ "(%u,%u,%u,%u,%p) have different dimensions.",
+ pixel_type(),mask.width,mask.height,mask.depth,mask.dim,mask.data,width,height,depth,dim,data);
+ if (nb_threads>16) cimg::warn("CImg<%s>::greycstoration_run() : Multi-threading mode limited to 16 threads max.");
+ const unsigned int
+ ntile = (tile && (tile<width || tile<height || (depth>1 && tile<depth)))?tile:0,
+#if cimg_OS==1 && !defined(_PTHREAD_H)
+ nthreads = 0;
+#else
+ nthreads = ntile?cimg::min(nb_threads,16U):cimg::min(nb_threads,1U);
+#endif
+
+ CImg<T> *const temporary = ntile?new CImg<T>(*this):0;
+ unsigned long *const counter = new unsigned long;
+ *counter = 0;
+ bool *const stop_request = new bool;
+ *stop_request = false;
+
+ for (unsigned int k=0; k<(nthreads?nthreads:1); k++) {
+ greycstoration_params[k].patch_based = false;
+ greycstoration_params[k].amplitude = amplitude;
+ greycstoration_params[k].sharpness = sharpness;
+ greycstoration_params[k].anisotropy = anisotropy;
+ greycstoration_params[k].alpha = alpha;
+ greycstoration_params[k].sigma = sigma;
+ greycstoration_params[k].gfact = gfact;
+ greycstoration_params[k].dl = dl;
+ greycstoration_params[k].da = da;
+ greycstoration_params[k].gauss_prec = gauss_prec;
+ greycstoration_params[k].interpolation = interpolation;
+ greycstoration_params[k].fast_approx = fast_approx;
+ greycstoration_params[k].source = this;
+ greycstoration_params[k].mask = &mask;
+ greycstoration_params[k].temporary = temporary;
+ greycstoration_params[k].counter = counter;
+ greycstoration_params[k].tile = ntile;
+ greycstoration_params[k].tile_border = tile_border;
+ greycstoration_params[k].thread = k;
+ greycstoration_params[k].nb_threads = nthreads;
+ greycstoration_params[k].is_running = true;
+ greycstoration_params[k].stop_request = stop_request;
+ if (k) greycstoration_params[k].mutex = greycstoration_params[0].mutex;
+ else greycstoration_mutex_create(greycstoration_params[0]);
+ }
+ if (nthreads) { // Threaded version
+#if cimg_OS==1
+#ifdef _PTHREAD_H
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ for (unsigned int k=0; k<greycstoration_params->nb_threads; k++) {
+ pthread_t thread;
+ const int err = pthread_create(&thread, &attr, greycstoration_thread, (void*)(greycstoration_params+k));
+ if (err) throw CImgException("CImg<%s>::greycstoration_run() : pthread_create returned error %d",
+ pixel_type(), err);
+ }
+#endif
+#elif cimg_OS==2
+ for (unsigned int k=0; k<greycstoration_params->nb_threads; k++) {
+ unsigned long ThreadID = 0;
+ CreateThread(0,0,greycstoration_thread,(void*)(greycstoration_params+k),0,&ThreadID);
+ }
+#else
+ throw CImgInstanceException("CImg<T>::greycstoration_run() : Threads are not supported, please define cimg_OS first.");
+#endif
+ } else greycstoration_thread((void*)greycstoration_params); // Non-threaded version
+ }
+ return *this;
+}
+
+//! Run the non-patch version of the GREYCstoration algorithm on the instance image.
+CImg& greycstoration_run(const float amplitude=50, const float sharpness=0.7f, const float anisotropy=0.3f,
+ const float alpha=0.6f, const float sigma=1.1f, const float gfact=1.0f,
+ const float dl=0.8f, const float da=30.0f,
+ const float gauss_prec=2.0f, const unsigned int interpolation=0, const bool fast_approx=true,
+ const unsigned int tile=0, const unsigned int tile_border=0, const unsigned int nb_threads=1) {
+ static const CImg<unsigned char> empty_mask;
+ return greycstoration_run(empty_mask,amplitude,sharpness,anisotropy,alpha,sigma,gfact,dl,da,gauss_prec,
+ interpolation,fast_approx,tile,tile_border,nb_threads);
+}
+
+//! Run the patch-based version of the GREYCstoration algorithm on the instance image.
+CImg& greycstoration_patch_run(const unsigned int patch_size=5, const float sigma_p=10, const float sigma_s=100,
+ const unsigned int lookup_size=20, const bool fast_approx=true,
+ const unsigned int tile=0, const unsigned int tile_border=0, const unsigned int nb_threads=1) {
+
+ static const CImg<unsigned char> empty_mask;
+ if (greycstoration_is_running())
+ throw CImgInstanceException("CImg<T>::greycstoration_run() : A GREYCstoration thread is already running on"
+ " the instance image (%u,%u,%u,%u,%p).",width,height,depth,dim,data);
+
+ else {
+ if (nb_threads>16) cimg::warn("CImg<%s>::greycstoration_run() : Multi-threading mode limited to 16 threads max.");
+ const unsigned int
+ ntile = (tile && (tile<width || tile<height || (depth>1 && tile<depth)))?tile:0,
+#if cimg_OS==1 && !defined(_PTHREAD_H)
+ nthreads = 0;
+#else
+ nthreads = ntile?cimg::min(nb_threads,16U):cimg::min(nb_threads,1U);
+#endif
+
+ CImg<T> *const temporary = ntile?new CImg<T>(*this):0;
+ unsigned long *const counter = new unsigned long;
+ *counter = 0;
+ bool *const stop_request = new bool;
+ *stop_request = false;
+
+ for (unsigned int k=0; k<(nthreads?nthreads:1); k++) {
+ greycstoration_params[k].patch_based = true;
+ greycstoration_params[k].patch_size = patch_size;
+ greycstoration_params[k].sigma_s = sigma_s;
+ greycstoration_params[k].sigma_p = sigma_p;
+ greycstoration_params[k].lookup_size = lookup_size;
+ greycstoration_params[k].source = this;
+ greycstoration_params[k].mask = &empty_mask;
+ greycstoration_params[k].temporary = temporary;
+ greycstoration_params[k].counter = counter;
+ greycstoration_params[k].tile = ntile;
+ greycstoration_params[k].tile_border = tile_border;
+ greycstoration_params[k].thread = k;
+ greycstoration_params[k].nb_threads = nthreads;
+ greycstoration_params[k].fast_approx = fast_approx;
+ greycstoration_params[k].is_running = true;
+ greycstoration_params[k].stop_request = stop_request;
+ if (k) greycstoration_params[k].mutex = greycstoration_params[0].mutex;
+ else greycstoration_mutex_create(greycstoration_params[0]);
+ }
+ if (nthreads) { // Threaded version
+#if cimg_OS==1
+#ifdef _PTHREAD_H
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ for (unsigned int k=0; k<greycstoration_params->nb_threads; k++) {
+ pthread_t thread;
+ const int err = pthread_create(&thread, &attr, greycstoration_thread, (void*)(greycstoration_params+k));
+ if (err) throw CImgException("CImg<%s>::greycstoration_run() : pthread_create returned error %d",
+ pixel_type(), err);
+ }
+#endif
+#elif cimg_OS==2
+ for (unsigned int k=0; k<greycstoration_params->nb_threads; k++) {
+ unsigned long ThreadID = 0;
+ CreateThread(0,0,greycstoration_thread,(void*)(greycstoration_params+k),0,&ThreadID);
+ }
+#else
+ throw CImgInstanceException("CImg<T>::greycstoration_run() : Threads support have not been enabled in this version of GREYCstoration.");
+#endif
+ } else greycstoration_thread((void*)greycstoration_params); // Non-threaded version
+ }
+ return *this;
+}
+
+//------------------------------------------------------------------------------
+// GREYCstoration private functions.
+// Should not be used directly by the API user.
+//-------------------------------------------------------------------------------
+
+static void greycstoration_mutex_create(_greycstoration_params &p) {
+ if (p.nb_threads>1) {
+#if cimg_OS==1 && defined(_PTHREAD_H)
+ p.mutex = new pthread_mutex_t;
+ pthread_mutex_init(p.mutex,0);
+#elif cimg_OS==2
+ p.mutex = CreateMutex(0,FALSE,0);
+#endif
+ }
+}
+
+static void greycstoration_mutex_lock(_greycstoration_params &p) {
+ if (p.nb_threads>1) {
+#if cimg_OS==1 && defined(_PTHREAD_H)
+ if (p.mutex) pthread_mutex_lock(p.mutex);
+#elif cimg_OS==2
+ WaitForSingleObject(p.mutex,INFINITE);
+#endif
+ }
+}
+
+static void greycstoration_mutex_unlock(_greycstoration_params &p) {
+ if (p.nb_threads>1) {
+#if cimg_OS==1 && defined(_PTHREAD_H)
+ if (p.mutex) pthread_mutex_unlock(p.mutex);
+#elif cimg_OS==2
+ ReleaseMutex(p.mutex);
+#endif
+ }
+}
+
+static void greycstoration_mutex_destroy(_greycstoration_params &p) {
+ if (p.nb_threads>1) {
+#if cimg_OS==1 && defined(_PTHREAD_H)
+ if (p.mutex) pthread_mutex_destroy(p.mutex);
+#elif cimg_OS==2
+ CloseHandle(p.mutex);
+#endif
+ p.mutex = 0;
+ }
+}
+
+#if cimg_OS==1
+static void* greycstoration_thread(void *arg) {
+#elif cimg_OS==2
+ static DWORD WINAPI greycstoration_thread(void *arg) {
+#endif
+ _greycstoration_params &p = *(_greycstoration_params*)arg;
+ greycstoration_mutex_lock(p);
+ const CImg<unsigned char> &mask = *(p.mask);
+ CImg<T> &source = *(p.source);
+
+ if (!p.tile) {
+
+ // Non-tiled version
+ //------------------
+ if (p.patch_based) source.blur_patch(p.patch_size,p.sigma_p,p.sigma_s,p.lookup_size,p.fast_approx);
+ else source.blur_anisotropic(mask,p.amplitude,p.sharpness,p.anisotropy,p.alpha,p.sigma,p.dl,p.da,p.gauss_prec,
+ p.interpolation,p.fast_approx,p.gfact);
+
+ } else {
+
+ // Tiled version
+ //---------------
+ CImg<T> &temporary = *(p.temporary);
+ const bool threed = (source.depth>1);
+ const unsigned int b = p.tile_border;
+ unsigned int ctile = 0;
+ if (threed) {
+ for (unsigned int z=0; z<source.depth && !*(p.stop_request); z+=p.tile)
+ for (unsigned int y=0; y<source.height && !*(p.stop_request); y+=p.tile)
+ for (unsigned int x=0; x<source.width && !*(p.stop_request); x+=p.tile)
+ if (!p.nb_threads || ((ctile++)%p.nb_threads)==p.thread) {
+ const unsigned int
+ x1 = x+p.tile-1,
+ y1 = y+p.tile-1,
+ z1 = z+p.tile-1,
+ xe = x1<source.width?x1:source.width-1,
+ ye = y1<source.height?y1:source.height-1,
+ ze = z1<source.depth?z1:source.depth-1;
+ CImg<T> img = source.get_crop(x-b,y-b,z-b,xe+b,ye+b,ze+b,true);
+ CImg<unsigned char> mask_tile = mask.is_empty()?mask:mask.get_crop(x-b,y-b,z-b,xe+b,ye+b,ze+b,true);
+ img.greycstoration_params[0] = p;
+ greycstoration_mutex_unlock(p);
+ if (p.patch_based) img.blur_patch(p.patch_size,p.sigma_p,p.sigma_s,p.lookup_size,p.fast_approx);
+ else img.blur_anisotropic(mask_tile,p.amplitude,p.sharpness,p.anisotropy,
+ p.alpha,p.sigma,p.dl,p.da,p.gauss_prec,p.interpolation,p.fast_approx,p.gfact);
+ greycstoration_mutex_lock(p);
+ temporary.draw_image(x,y,z,img.crop(b,b,b,img.width-b,img.height-b,img.depth-b));
+ }
+ } else {
+ for (unsigned int y=0; y<source.height && !*(p.stop_request); y+=p.tile)
+ for (unsigned int x=0; x<source.width && !*(p.stop_request); x+=p.tile)
+ if (!p.nb_threads || ((ctile++)%p.nb_threads)==p.thread) {
+ const unsigned int
+ x1 = x+p.tile-1,
+ y1 = y+p.tile-1,
+ xe = x1<source.width?x1:source.width-1,
+ ye = y1<source.height?y1:source.height-1;
+ CImg<T> img = source.get_crop(x-b,y-b,xe+b,ye+b,true);
+ CImg<unsigned char> mask_tile = mask.is_empty()?mask:mask.get_crop(x-b,y-b,xe+b,ye+b,true);
+ img.greycstoration_params[0] = p;
+ greycstoration_mutex_unlock(p);
+ if (p.patch_based) img.blur_patch(p.patch_size,p.sigma_p,p.sigma_s,p.lookup_size,p.fast_approx);
+ else img.blur_anisotropic(mask_tile,p.amplitude,p.sharpness,p.anisotropy,
+ p.alpha,p.sigma,p.dl,p.da,p.gauss_prec,p.interpolation,p.fast_approx,p.gfact);
+ temporary.draw_image(x,y,img.crop(b,b,img.width-b,img.height-b));
+ greycstoration_mutex_lock(p);
+ }
+ }
+ }
+ greycstoration_mutex_unlock(p);
+
+ if (!p.thread) {
+ if (p.nb_threads>1) {
+ bool stopflag = true;
+ do {
+ stopflag = true;
+ for (unsigned int k=1; k<p.nb_threads; k++) if (source.greycstoration_params[k].is_running) stopflag = false;
+ if (!stopflag) cimg::wait(50);
+ } while (!stopflag);
+ }
+ if (p.counter) delete p.counter;
+ if (p.temporary) { source = *(p.temporary); delete p.temporary; }
+ if (p.stop_request) delete p.stop_request;
+ p.mask = 0;
+ p.amplitude = p.sharpness = p.anisotropy = p.alpha = p.sigma = p.gfact = p.dl = p.da = p.gauss_prec = p.sigma_s = p.sigma_p = 0;
+ p.patch_size = p.interpolation = p.lookup_size = 0;
+ p.fast_approx = false;
+ p.source = 0;
+ p.temporary = 0;
+ p.counter = 0;
+ p.tile = p.tile_border = p.thread = p.nb_threads = 0;
+ p.stop_request = 0;
+ greycstoration_mutex_destroy(p);
+ }
+ p.is_running = false;
+
+ if (p.nb_threads) {
+#if cimg_OS==1 && defined(_PTHREAD_H)
+ pthread_exit(arg);
+ return arg;
+#elif cimg_OS==2
+ ExitThread(0);
+#endif
+ }
+ return 0;
+ }
+
+
+#define cimg_plugin_greycstoration_count \
+ if (!*(greycstoration_params->stop_request)) ++(*greycstoration_params->counter); else return *this;
+#define cimg_plugin_greycstoration_lock \
+ greycstoration_mutex_lock(greycstoration_params[0]);
+#define cimg_plugin_greycstoration_unlock \
+ greycstoration_mutex_unlock(greycstoration_params[0]);
+
+#endif
diff --git a/src/libs/greycstoration/greycstorationiface.cpp b/src/libs/greycstoration/greycstorationiface.cpp
new file mode 100644
index 00000000..32f60514
--- /dev/null
+++ b/src/libs/greycstoration/greycstorationiface.cpp
@@ -0,0 +1,473 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2007-12-03
+ * Description : Greycstoration interface.
+ *
+ * Copyright (C) 2007-2008 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * This program is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation;
+ * either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * ============================================================ */
+
+/** Don't use CImg interface (keyboard/mouse interaction) */
+#define cimg_display 0
+/** Only print debug information on the console */
+#define cimg_debug 1
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+// C++ includes.
+
+#include <cassert>
+
+// Local includes.
+
+#define cimg_plugin "greycstoration.h"
+// Unix-like (Linux, Solaris, BSD, MacOSX, Irix,...).
+#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(__MACOSX__) || defined(__APPLE__) \
+ || defined(sgi) || defined(__sgi) \
+ || defined(__CYGWIN__)
+#include <pthread.h>
+#endif
+
+/** Number of children threads used to run Greystoration algorithm
+ For the moment we use only one thread. See B.K.O #186642 for details.
+ Multithreading management need to be fixed into CImg.
+ */
+#define COMPUTATION_THREAD 2
+
+/** Uncomment this line if you use future GreycStoration implementation with GFact parameter
+ */
+#define GREYSTORATION_USING_GFACT 1
+
+// Local includes.
+
+#include "ddebug.h"
+#include "greycstorationsettings.h"
+#include "greycstorationiface.h"
+
+// CImg includes.
+
+#include "CImg.h"
+
+using namespace cimg_library;
+
+namespace Digikam
+{
+
+class GreycstorationIfacePriv
+{
+
+public:
+
+ GreycstorationIfacePriv()
+ {
+ mode = GreycstorationIface::Restore;
+ gfact = 1.0;
+ }
+
+ float gfact;
+
+ int mode; // The interface running mode.
+
+ TQImage inPaintingMask; // Mask for inpainting.
+
+ GreycstorationSettings settings; // Current Greycstoraion algorithm settings.
+
+ CImg<> img; // Main image.
+ CImg<uchar> mask; // The mask used with inpaint or resize mode
+};
+
+GreycstorationIface::GreycstorationIface(DImg *orgImage,
+ GreycstorationSettings settings,
+ int mode,
+ int newWidth, int newHeight,
+ const TQImage& inPaintingMask,
+ TQObject *parent)
+ : DImgThreadedFilter(orgImage, parent)
+{
+ d = new GreycstorationIfacePriv;
+ d->settings = settings;
+ d->mode = mode;
+ d->inPaintingMask = inPaintingMask;
+
+ if (m_orgImage.sixteenBit()) // 16 bits image.
+ d->gfact = 1.0/256.0;
+
+ if (d->mode == Resize || d->mode == SimpleResize)
+ {
+ m_destImage = DImg(newWidth, newHeight,
+ m_orgImage.sixteenBit(), m_orgImage.hasAlpha());
+ DDebug() << "GreycstorationIface::Resize: new size: ("
+ << newWidth << ", " << newHeight << ")" << endl;
+ }
+ else
+ {
+ m_destImage = DImg(m_orgImage.width(), m_orgImage.height(),
+ m_orgImage.sixteenBit(), m_orgImage.hasAlpha());
+ }
+
+ initFilter();
+}
+
+GreycstorationIface::~GreycstorationIface()
+{
+ delete d;
+}
+
+// We need to re-implemente this method from DImgThreadedFilter class because
+// target image size can be different from original if d->mode = Resize.
+
+void GreycstorationIface::initFilter()
+{
+ if (m_orgImage.width() && m_orgImage.height())
+ {
+ if (m_parent)
+ start(); // m_parent is valide, start thread ==> run()
+ else
+ startComputation(); // no parent : no using thread.
+ }
+ else // No image data
+ {
+ if (m_parent) // If parent then send event about a problem.
+ {
+ postProgress(0, false, false);
+ DDebug() << m_name << "::No valid image data !!! ..." << endl;
+ }
+ }
+}
+
+void GreycstorationIface::stopComputation()
+{
+ // Because Greycstoration algorithm run in a child thread, we need
+ // to stop it before to stop this thread.
+ if (d->img.greycstoration_is_running())
+ {
+ // If the user abort, we stop the algorithm.
+ DDebug() << "Stop Greycstoration computation..." << endl;
+ d->img.greycstoration_stop();
+ }
+
+ // And now when stop main loop and clean up all
+ DImgThreadedFilter::stopComputation();
+}
+
+void GreycstorationIface::filterImage()
+{
+ int x, y;
+
+ DDebug() << "GreycstorationIface::Initialization..." << endl;
+
+ // Copy the src image data into a CImg type image with three channels and no alpha.
+
+ uchar* imageData = m_orgImage.bits();
+ int imageWidth = m_orgImage.width();
+ int imageHeight = m_orgImage.height();
+ d->img = CImg<>(imageWidth, imageHeight, 1, 4);
+
+ if (!m_orgImage.sixteenBit()) // 8 bits image.
+ {
+ uchar *ptr = imageData;
+
+ for (y = 0; y < imageHeight; y++)
+ {
+ for (x = 0; x < imageWidth; x++)
+ {
+ d->img(x, y, 0) = ptr[0]; // Blue.
+ d->img(x, y, 1) = ptr[1]; // Green.
+ d->img(x, y, 2) = ptr[2]; // Red.
+ d->img(x, y, 3) = ptr[3]; // Alpha.
+ ptr += 4;
+ }
+ }
+ }
+ else // 16 bits image.
+ {
+ unsigned short *ptr = (unsigned short *)imageData;
+
+ for (y = 0; y < imageHeight; y++)
+ {
+ for (x = 0; x < imageWidth; x++)
+ {
+ d->img(x, y, 0) = ptr[0]; // Blue.
+ d->img(x, y, 1) = ptr[1]; // Green.
+ d->img(x, y, 2) = ptr[2]; // Red.
+ d->img(x, y, 3) = ptr[3]; // Alpha.
+ ptr += 4;
+ }
+ }
+ }
+
+ DDebug() << "GreycstorationIface::Process Computation..." << endl;
+
+ try
+ {
+ switch (d->mode)
+ {
+ case Restore:
+ restoration();
+ break;
+
+ case InPainting:
+ inpainting();
+ break;
+
+ case Resize:
+ resize();
+ break;
+
+ case SimpleResize:
+ simpleResize();
+ break;
+ }
+ }
+ catch(...) // Everything went wrong.
+ {
+ DDebug() << "GreycstorationIface::Error during Greycstoration filter computation!" << endl;
+
+ if (m_parent)
+ postProgress( 0, false, false );
+
+ return;
+ }
+
+ if (m_cancel)
+ return;
+
+ // Copy CImg onto destination.
+
+ DDebug() << "GreycstorationIface::Finalization..." << endl;
+
+ uchar* newData = m_destImage.bits();
+ int newWidth = m_destImage.width();
+ int newHeight = m_destImage.height();
+
+ if (!m_orgImage.sixteenBit()) // 8 bits image.
+ {
+ uchar *ptr = newData;
+
+ for (y = 0; y < newHeight; y++)
+ {
+ for (x = 0; x < newWidth; x++)
+ {
+ // Overwrite RGB values to destination.
+ ptr[0] = static_cast<uchar>(d->img(x, y, 0)); // Blue
+ ptr[1] = static_cast<uchar>(d->img(x, y, 1)); // Green
+ ptr[2] = static_cast<uchar>(d->img(x, y, 2)); // Red
+ ptr[3] = static_cast<uchar>(d->img(x, y, 3)); // Alpha
+ ptr += 4;
+ }
+ }
+ }
+ else // 16 bits image.
+ {
+ unsigned short *ptr = (unsigned short *)newData;
+
+ for (y = 0; y < newHeight; y++)
+ {
+ for (x = 0; x < newWidth; x++)
+ {
+ // Overwrite RGB values to destination.
+ ptr[0] = static_cast<unsigned short>(d->img(x, y, 0)); // Blue
+ ptr[1] = static_cast<unsigned short>(d->img(x, y, 1)); // Green
+ ptr[2] = static_cast<unsigned short>(d->img(x, y, 2)); // Red
+ ptr[3] = static_cast<unsigned short>(d->img(x, y, 3)); // Alpha
+ ptr += 4;
+ }
+ }
+ }
+}
+
+void GreycstorationIface::restoration()
+{
+ for (uint iter = 0 ; !m_cancel && (iter < d->settings.nbIter) ; iter++)
+ {
+ // This function will start a thread running one iteration of the GREYCstoration filter.
+ // It returns immediately, so you can do what you want after (update a progress bar for
+ // instance).
+ d->img.greycstoration_run(d->settings.amplitude,
+ d->settings.sharpness,
+ d->settings.anisotropy,
+ d->settings.alpha,
+ d->settings.sigma,
+#ifdef GREYSTORATION_USING_GFACT
+ d->gfact,
+#endif
+ d->settings.dl,
+ d->settings.da,
+ d->settings.gaussPrec,
+ d->settings.interp,
+ d->settings.fastApprox,
+ d->settings.tile,
+ d->settings.btile,
+ COMPUTATION_THREAD);
+
+ iterationLoop(iter);
+ }
+}
+
+void GreycstorationIface::inpainting()
+{
+ if (!d->inPaintingMask.isNull())
+ {
+ // Copy the inpainting image data into a CImg type image with three channels and no alpha.
+
+ int x, y;
+
+ d->mask = CImg<uchar>(d->inPaintingMask.width(), d->inPaintingMask.height(), 1, 3);
+ uchar *ptr = d->inPaintingMask.bits();
+
+ for (y = 0; y < d->inPaintingMask.height(); y++)
+ {
+ for (x = 0; x < d->inPaintingMask.width(); x++)
+ {
+ d->mask(x, y, 0) = ptr[2]; // blue.
+ d->mask(x, y, 1) = ptr[1]; // green.
+ d->mask(x, y, 2) = ptr[0]; // red.
+ ptr += 4;
+ }
+ }
+ }
+ else
+ {
+ DDebug() << "Inpainting image: mask is null!" << endl;
+ m_cancel = true;
+ return;
+ }
+
+ for (uint iter=0 ; !m_cancel && (iter < d->settings.nbIter) ; iter++)
+ {
+ // This function will start a thread running one iteration of the GREYCstoration filter.
+ // It returns immediately, so you can do what you want after (update a progress bar for
+ // instance).
+ d->img.greycstoration_run(d->mask,
+ d->settings.amplitude,
+ d->settings.sharpness,
+ d->settings.anisotropy,
+ d->settings.alpha,
+ d->settings.sigma,
+#ifdef GREYSTORATION_USING_GFACT
+ d->gfact,
+#endif
+ d->settings.dl,
+ d->settings.da,
+ d->settings.gaussPrec,
+ d->settings.interp,
+ d->settings.fastApprox,
+ d->settings.tile,
+ d->settings.btile,
+ COMPUTATION_THREAD);
+
+ iterationLoop(iter);
+ }
+}
+
+void GreycstorationIface::resize()
+{
+ const bool anchor = true; // Anchor original pixels.
+ const unsigned int init = 5; // Initial estimate (1=block, 3=linear, 5=bicubic).
+
+ int w = m_destImage.width();
+ int h = m_destImage.height();
+
+ d->mask.assign(d->img.dimx(), d->img.dimy(), 1, 1, 255);
+
+ if (!anchor)
+ d->mask.resize(w, h, 1, 1, 1);
+ else
+ d->mask = !d->mask.resize(w, h, 1, 1, 4);
+
+ d->img.resize(w, h, 1, -100, init);
+
+ for (uint iter = 0 ; !m_cancel && (iter < d->settings.nbIter) ; iter++)
+ {
+ // This function will start a thread running one iteration of the GREYCstoration filter.
+ // It returns immediately, so you can do what you want after (update a progress bar for
+ // instance).
+ d->img.greycstoration_run(d->mask,
+ d->settings.amplitude,
+ d->settings.sharpness,
+ d->settings.anisotropy,
+ d->settings.alpha,
+ d->settings.sigma,
+#ifdef GREYSTORATION_USING_GFACT
+ d->gfact,
+#endif
+ d->settings.dl,
+ d->settings.da,
+ d->settings.gaussPrec,
+ d->settings.interp,
+ d->settings.fastApprox,
+ d->settings.tile,
+ d->settings.btile,
+ COMPUTATION_THREAD);
+
+ iterationLoop(iter);
+ }
+}
+
+void GreycstorationIface::simpleResize()
+{
+ const unsigned int method = 3; // Initial estimate (0, none, 1=block, 3=linear, 4=grid, 5=bicubic).
+
+ int w = m_destImage.width();
+ int h = m_destImage.height();
+
+ while (d->img.dimx() > 2*w &&
+ d->img.dimy() > 2*h)
+ {
+ d->img.resize_halfXY();
+ }
+
+ d->img.resize(w, h, -100, -100, method);
+}
+
+void GreycstorationIface::iterationLoop(uint iter)
+{
+ uint mp = 0;
+ uint p = 0;
+
+ do
+ {
+ usleep(100000);
+
+ if (m_parent && !m_cancel)
+ {
+ // Update the progress bar in dialog. We simply computes the global
+ // progression index (including all iterations).
+
+ p = (uint)((iter*100 + d->img.greycstoration_progress())/d->settings.nbIter);
+
+ if (p > mp)
+ {
+ postProgress(p);
+ mp = p;
+ }
+ }
+ }
+ while (d->img.greycstoration_is_running() && !m_cancel);
+
+ // A delay is require here. I suspect a sync problem between threads
+ // used by GreycStoration algorithm.
+ usleep(100000);
+}
+
+} // NameSpace Digikam
diff --git a/src/libs/greycstoration/greycstorationiface.h b/src/libs/greycstoration/greycstorationiface.h
new file mode 100644
index 00000000..fd7b248b
--- /dev/null
+++ b/src/libs/greycstoration/greycstorationiface.h
@@ -0,0 +1,89 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2007-12-03
+ * Description : Greycstoration interface.
+ *
+ * Copyright (C) 2007-2008 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * This program is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation;
+ * either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * ============================================================ */
+
+#ifndef GREYCSTORATIONIFACE_H
+#define GREYCSTORATIONIFACE_H
+
+// TQt includes.
+
+#include <tqimage.h>
+
+// Local includes.
+
+#include "dimg.h"
+#include "dimgthreadedfilter.h"
+#include "greycstorationsettings.h"
+#include "digikam_export.h"
+
+class TQObject;
+
+namespace Digikam
+{
+
+class GreycstorationIfacePriv;
+
+class DIGIKAM_EXPORT GreycstorationIface : public DImgThreadedFilter
+{
+
+public:
+
+ enum MODE
+ {
+ Restore = 0,
+ InPainting,
+ Resize,
+ SimpleResize // Mode to resize image without to use Greycstoration algorithm.
+ };
+
+public:
+
+ GreycstorationIface(DImg *orgImage,
+ GreycstorationSettings settings,
+ int mode=Restore,
+ int newWidth=0, int newHeight=0,
+ const TQImage& inPaintingMask=TQImage(),
+ TQObject *parent=0);
+
+ ~GreycstorationIface();
+
+ void stopComputation();
+
+private:
+
+ void initFilter();
+ void filterImage();
+
+ void restoration();
+ void inpainting();
+ void resize();
+ void simpleResize();
+ void iterationLoop(uint iter);
+
+private:
+
+ GreycstorationIfacePriv *d;
+};
+
+} // NameSpace Digikam
+
+#endif /* GREYCSTORATIONIFACE_H */
diff --git a/src/libs/greycstoration/greycstorationsettings.h b/src/libs/greycstoration/greycstorationsettings.h
new file mode 100644
index 00000000..e2686965
--- /dev/null
+++ b/src/libs/greycstoration/greycstorationsettings.h
@@ -0,0 +1,144 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2006-21-07
+ * Description : Greycstoration settings container.
+ *
+ * Copyright (C) 2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * For a full settings description, look at this url :
+ * http://www.greyc.ensicaen.fr/~dtschump/greycstoration/guide.html
+ *
+ * For demonstration of settings, look at this url :
+ *
+ * http://www.greyc.ensicaen.fr/~dtschump/greycstoration/demonstration.html
+ *
+ * This program is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation;
+ * either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * ============================================================ */
+
+#ifndef GREYCSTORATIONSETTINGS_H
+#define GREYCSTORATIONSETTINGS_H
+
+// Local includes.
+
+#include "digikam_export.h"
+
+namespace Digikam
+{
+
+class DIGIKAM_EXPORT GreycstorationSettings
+{
+
+public:
+
+ enum INTERPOLATION
+ {
+ NearestNeighbor = 0,
+ Linear,
+ RungeKutta
+ };
+
+public:
+
+ GreycstorationSettings()
+ {
+ setRestorationDefaultSettings();
+ };
+
+ ~GreycstorationSettings(){};
+
+ void setRestorationDefaultSettings()
+ {
+ fastApprox = true;
+
+ tile = 256;
+ btile = 4;
+
+ nbIter = 1;
+ interp = NearestNeighbor;
+
+ amplitude = 60.0;
+ sharpness = 0.7;
+ anisotropy = 0.3;
+ alpha = 0.6;
+ sigma = 1.1;
+ gaussPrec = 2.0;
+ dl = 0.8;
+ da = 30.0;
+ };
+
+ void setInpaintingDefaultSettings()
+ {
+ fastApprox = true;
+
+ tile = 256;
+ btile = 4;
+
+ nbIter = 30;
+ interp = NearestNeighbor;
+
+ amplitude = 20.0;
+ sharpness = 0.3;
+ anisotropy = 1.0;
+ alpha = 0.8;
+ sigma = 2.0;
+ gaussPrec = 2.0;
+ dl = 0.8;
+ da = 30.0;
+ };
+
+ void setResizeDefaultSettings()
+ {
+ fastApprox = true;
+
+ tile = 256;
+ btile = 4;
+
+ nbIter = 3;
+ interp = NearestNeighbor;
+
+ amplitude = 20.0;
+ sharpness = 0.2;
+ anisotropy = 0.9;
+ alpha = 0.1;
+ sigma = 1.5;
+ gaussPrec = 2.0;
+ dl = 0.8;
+ da = 30.0;
+ };
+
+public:
+
+ bool fastApprox;
+
+ int tile;
+ int btile;
+
+ uint nbIter;
+ uint interp;
+
+ float amplitude;
+ float sharpness;
+ float anisotropy;
+ float alpha;
+ float sigma;
+ float gaussPrec;
+ float dl;
+ float da;
+};
+
+} // namespace Digikam
+
+#endif // GREYCSTORATIONSETTINGS_H
diff --git a/src/libs/greycstoration/greycstorationwidget.cpp b/src/libs/greycstoration/greycstorationwidget.cpp
new file mode 100644
index 00000000..93f25799
--- /dev/null
+++ b/src/libs/greycstoration/greycstorationwidget.cpp
@@ -0,0 +1,377 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2007-09-13
+ * Description : Greycstoration settings widgets
+ *
+ * Copyright (C) 2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * This program is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation;
+ * either version 2, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * ============================================================ */
+
+// TQt includes.
+
+#include <tqcheckbox.h>
+#include <tqcombobox.h>
+#include <tqlabel.h>
+#include <tqlayout.h>
+#include <tqtabwidget.h>
+#include <tqtextstream.h>
+#include <tqtooltip.h>
+#include <tqwhatsthis.h>
+
+// KDE includes.
+
+#include <kdialog.h>
+#include <tdelocale.h>
+
+// LibKDcraw includes.
+
+#include <libkdcraw/rnuminput.h>
+#include <libkdcraw/rcombobox.h>
+
+// Local includes.
+
+#include "greycstorationwidget.h"
+#include "greycstorationwidget.moc"
+
+using namespace KDcrawIface;
+
+namespace Digikam
+{
+
+class GreycstorationWidgetPriv
+{
+
+public:
+
+ GreycstorationWidgetPriv()
+ {
+ parent = 0;
+
+ advancedPage = 0;
+ alphaInput = 0;
+ alphaLabel = 0;
+ amplitudeInput = 0;
+ amplitudeLabel = 0;
+ anisotropyInput = 0;
+ anisotropyLabel = 0;
+ btileInput = 0;
+ btileLabel = 0;
+ daInput = 0;
+ daLabel = 0;
+ dlInput = 0;
+ dlLabel = 0;
+ fastApproxCBox = 0;
+ gaussianPrecInput = 0;
+ gaussianPrecLabel = 0;
+ generalPage = 0;
+ interpolationBox = 0;
+ interpolationLabel = 0;
+ iterationInput = 0;
+ iterationLabel = 0;
+ sharpnessInput = 0;
+ sharpnessLabel = 0;
+ sigmaInput = 0;
+ sigmaLabel = 0;
+ tileInput = 0;
+ tileLabel = 0;
+ }
+
+ TQLabel *alphaLabel;
+ TQLabel *amplitudeLabel;
+ TQLabel *anisotropyLabel;
+ TQLabel *btileLabel;
+ TQLabel *daLabel;
+ TQLabel *dlLabel;
+ TQLabel *gaussianPrecLabel;
+ TQLabel *interpolationLabel;
+ TQLabel *iterationLabel;
+ TQLabel *sharpnessLabel;
+ TQLabel *sigmaLabel;
+ TQLabel *tileLabel;
+
+ TQWidget *advancedPage;
+ TQWidget *generalPage;
+
+ TQCheckBox *fastApproxCBox;
+
+ TQTabWidget *parent;
+
+ RComboBox *interpolationBox;
+
+ RDoubleNumInput *alphaInput;
+ RDoubleNumInput *amplitudeInput;
+ RDoubleNumInput *anisotropyInput;
+ RDoubleNumInput *daInput;
+ RDoubleNumInput *dlInput;
+ RDoubleNumInput *gaussianPrecInput;
+ RDoubleNumInput *sharpnessInput;
+ RDoubleNumInput *sigmaInput;
+
+ RIntNumInput *btileInput;
+ RIntNumInput *iterationInput;
+ RIntNumInput *tileInput;
+};
+
+GreycstorationWidget::GreycstorationWidget(TQTabWidget *parent)
+ : TQObject(parent)
+{
+ d = new GreycstorationWidgetPriv;
+ d->parent = parent;
+
+ // -------------------------------------------------------------
+
+ d->generalPage = new TQWidget( parent );
+ TQGridLayout* grid1 = new TQGridLayout(d->generalPage, 6, 2, KDialog::spacingHint());
+ parent->addTab( d->generalPage, i18n("General") );
+
+ d->sharpnessLabel = new TQLabel(i18n("Detail preservation:"), d->generalPage);
+ d->sharpnessInput = new RDoubleNumInput(d->generalPage);
+ d->sharpnessInput->setPrecision(2);
+ d->sharpnessInput->setRange(0.01, 1.0, 0.1);
+ TQWhatsThis::add( d->sharpnessInput, i18n("<p>Preservation of details to set the sharpening level "
+ "of the small features in the target image. "
+ "Higher values leave details sharp."));
+ grid1->addMultiCellWidget(d->sharpnessLabel, 0, 0, 0, 0);
+ grid1->addMultiCellWidget(d->sharpnessInput, 0, 0, 1, 1);
+
+ d->anisotropyLabel = new TQLabel(i18n("Anisotropy:"), d->generalPage);
+ d->anisotropyInput = new RDoubleNumInput(d->generalPage);
+ d->anisotropyInput->setPrecision(2);
+ d->anisotropyInput->setRange(0.0, 1.0, 0.1);
+ TQWhatsThis::add( d->anisotropyInput, i18n("<p>Anisotropic (directional) modifier of the details. "
+ "Keep it small for Gaussian noise."));
+ grid1->addMultiCellWidget(d->anisotropyLabel, 1, 1, 0, 0);
+ grid1->addMultiCellWidget(d->anisotropyInput, 1, 1, 1, 1);
+
+ d->amplitudeLabel = new TQLabel(i18n("Smoothing:"), d->generalPage);
+ d->amplitudeInput = new RDoubleNumInput(d->generalPage);
+ d->amplitudeInput->setPrecision(2);
+ d->amplitudeInput->setRange(0.01, 500.0, 0.1);
+ TQWhatsThis::add( d->amplitudeInput, i18n("<p>Total smoothing power: if the Detail Factor sets the relative "
+ "smoothing and the Anisotropy Factor the direction, "
+ "the Smoothing Factor sets the overall effect."));
+ grid1->addMultiCellWidget(d->amplitudeLabel, 2, 2, 0, 0);
+ grid1->addMultiCellWidget(d->amplitudeInput, 2, 2, 1, 1);
+
+ d->sigmaLabel = new TQLabel(i18n("Regularity:"), d->generalPage);
+ d->sigmaInput = new RDoubleNumInput(d->generalPage);
+ d->sigmaInput->setPrecision(2);
+ d->sigmaInput->setRange(0.0, 10.0, 0.1);
+ TQWhatsThis::add( d->sigmaInput, i18n("<p>This value controls the evenness of smoothing to the image. "
+ "Do not use a high value here, or the "
+ "target image will be completely blurred."));
+ grid1->addMultiCellWidget(d->sigmaLabel, 3, 3, 0, 0);
+ grid1->addMultiCellWidget(d->sigmaInput, 3, 3, 1, 1);
+
+ d->iterationLabel = new TQLabel(i18n("Iterations:"), d->generalPage);
+ d->iterationInput = new RIntNumInput(d->generalPage);
+ d->iterationInput->setRange(1, 5000, 1);
+ TQWhatsThis::add( d->iterationInput, i18n("<p>Sets the number of times the filter is applied to "
+ "the image."));
+ grid1->addMultiCellWidget(d->iterationLabel, 4, 4, 0, 0);
+ grid1->addMultiCellWidget(d->iterationInput, 4, 4, 1, 1);
+
+ d->alphaLabel = new TQLabel(i18n("Noise:"), d->generalPage);
+ d->alphaInput = new RDoubleNumInput(d->generalPage);
+ d->alphaInput->setPrecision(2);
+ d->alphaInput->setRange(0.01, 1.0, 0.1);
+ TQWhatsThis::add( d->alphaInput, i18n("<p>Sets the noise scale."));
+ grid1->addMultiCellWidget(d->alphaLabel, 5, 5, 0, 0);
+ grid1->addMultiCellWidget(d->alphaInput, 5, 5, 1, 1);
+ grid1->setRowStretch(6, 10);
+
+ // -------------------------------------------------------------
+
+ d->advancedPage = new TQWidget( parent );
+ TQGridLayout* grid2 = new TQGridLayout(d->advancedPage, 6, 2, KDialog::spacingHint());
+ parent->addTab( d->advancedPage, i18n("Advanced Settings") );
+
+ d->daLabel = new TQLabel(i18n("Angular step:"), d->advancedPage);
+ d->daInput = new RDoubleNumInput(d->advancedPage);
+ d->daInput->setPrecision(2);
+ d->daInput->setRange(0.0, 90.0, 1.0);
+ TQWhatsThis::add( d->daInput, i18n("<p>Set here the angular integration step (in degrees) "
+ "analogous to anisotropy."));
+ grid2->addMultiCellWidget(d->daLabel, 0, 0, 0, 0);
+ grid2->addMultiCellWidget(d->daInput, 0, 0, 1, 1);
+
+ d->dlLabel = new TQLabel(i18n("Integral step:"), d->advancedPage);
+ d->dlInput = new RDoubleNumInput(d->advancedPage);
+ d->dlInput->setPrecision(2);
+ d->dlInput->setRange(0.0, 1.0, 0.1);
+ TQWhatsThis::add( d->dlInput, i18n("<p>Set here the spatial integral step."));
+ grid2->addMultiCellWidget(d->dlLabel, 1, 1, 0, 0);
+ grid2->addMultiCellWidget(d->dlInput, 1, 1, 1, 1);
+
+ d->gaussianPrecLabel = new TQLabel(i18n("Gaussian:"), d->advancedPage);
+ d->gaussianPrecInput = new RDoubleNumInput(d->advancedPage);
+ d->gaussianPrecInput->setPrecision(2);
+ d->gaussianPrecInput->setRange(0.01, 20.0, 0.01);
+ TQWhatsThis::add( d->gaussianPrecInput, i18n("<p>Set here the precision of the Gaussian function."));
+ grid2->addMultiCellWidget(d->gaussianPrecLabel, 2, 2, 0, 0);
+ grid2->addMultiCellWidget(d->gaussianPrecInput, 2, 2, 1, 1);
+
+ d->tileLabel = new TQLabel(i18n("Tile size:"), d->advancedPage);
+ d->tileInput = new RIntNumInput(d->advancedPage);
+ d->tileInput->setRange(0, 2000, 1);
+ TQWhatsThis::add( d->tileInput, i18n("<p>Sets the tile size."));
+ grid2->addMultiCellWidget(d->tileLabel, 3, 3, 0, 0);
+ grid2->addMultiCellWidget(d->tileInput, 3, 3, 1, 1);
+
+ d->btileLabel = new TQLabel(i18n("Tile border:"), d->advancedPage);
+ d->btileInput = new RIntNumInput(d->advancedPage);
+ d->btileInput->setRange(1, 20, 1);
+ TQWhatsThis::add( d->btileInput, i18n("<p>Sets the size of each tile border."));
+ grid2->addMultiCellWidget(d->btileLabel, 4, 4, 0, 0);
+ grid2->addMultiCellWidget(d->btileInput, 4, 4, 1, 1);
+
+ d->interpolationLabel = new TQLabel(i18n("Interpolation:"), d->advancedPage);
+ d->interpolationBox = new RComboBox(d->advancedPage);
+ d->interpolationBox->insertItem( i18n("Nearest Neighbor"), GreycstorationSettings::NearestNeighbor );
+ d->interpolationBox->insertItem( i18n("Linear"), GreycstorationSettings::Linear );
+ d->interpolationBox->insertItem( i18n("Runge-Kutta"), GreycstorationSettings::RungeKutta);
+ TQWhatsThis::add( d->interpolationBox, i18n("<p>Select the right interpolation method for the "
+ "desired image quality."));
+ grid2->addMultiCellWidget(d->interpolationLabel, 5, 5, 0, 0);
+ grid2->addMultiCellWidget(d->interpolationBox, 5, 5, 1, 1);
+
+ d->fastApproxCBox = new TQCheckBox(i18n("Fast approximation"), d->advancedPage);
+ TQWhatsThis::add( d->fastApproxCBox, i18n("<p>Enable fast approximation when rendering images."));
+ grid2->addMultiCellWidget(d->fastApproxCBox, 6, 6, 0, 1);
+}
+
+GreycstorationWidget::~GreycstorationWidget()
+{
+ delete d;
+}
+
+void GreycstorationWidget::setEnabled(bool b)
+{
+ d->generalPage->setEnabled(b);
+ d->advancedPage->setEnabled(b);
+ d->parent->setTabEnabled(d->generalPage, b);
+ d->parent->setTabEnabled(d->advancedPage, b);
+}
+
+void GreycstorationWidget::setSettings(GreycstorationSettings settings)
+{
+ blockSignals(true);
+ d->alphaInput->setValue(settings.alpha);
+ d->amplitudeInput->setValue(settings.amplitude);
+ d->anisotropyInput->setValue(settings.anisotropy);
+ d->btileInput->setValue(settings.btile);
+ d->daInput->setValue(settings.da);
+ d->dlInput->setValue(settings.dl);
+ d->fastApproxCBox->setChecked(settings.fastApprox);
+ d->gaussianPrecInput->setValue(settings.gaussPrec);
+ d->interpolationBox->setCurrentItem(settings.interp);
+ d->iterationInput->setValue(settings.nbIter);
+ d->sharpnessInput->setValue(settings.sharpness);
+ d->sigmaInput->setValue(settings.sigma);
+ d->tileInput->setValue(settings.tile);
+ blockSignals(false);
+}
+
+void GreycstorationWidget::setDefaultSettings(GreycstorationSettings settings)
+{
+ blockSignals(true);
+ d->alphaInput->setDefaultValue(settings.alpha);
+ d->amplitudeInput->setDefaultValue(settings.amplitude);
+ d->anisotropyInput->setDefaultValue(settings.anisotropy);
+ d->btileInput->setDefaultValue(settings.btile);
+ d->daInput->setDefaultValue(settings.da);
+ d->dlInput->setDefaultValue(settings.dl);
+ d->fastApproxCBox->setChecked(settings.fastApprox);
+ d->gaussianPrecInput->setDefaultValue(settings.gaussPrec);
+ d->interpolationBox->setDefaultItem(settings.interp);
+ d->iterationInput->setDefaultValue(settings.nbIter);
+ d->sharpnessInput->setDefaultValue(settings.sharpness);
+ d->sigmaInput->setDefaultValue(settings.sigma);
+ d->tileInput->setDefaultValue(settings.tile);
+ blockSignals(false);
+}
+
+GreycstorationSettings GreycstorationWidget::getSettings()
+{
+ GreycstorationSettings settings;
+
+ settings.fastApprox = d->fastApproxCBox->isChecked();
+ settings.interp = d->interpolationBox->currentItem();
+ settings.amplitude = d->amplitudeInput->value();
+ settings.sharpness = d->sharpnessInput->value();
+ settings.anisotropy = d->anisotropyInput->value();
+ settings.alpha = d->alphaInput->value();
+ settings.sigma = d->sigmaInput->value();
+ settings.gaussPrec = d->gaussianPrecInput->value();
+ settings.dl = d->dlInput->value();
+ settings.da = d->daInput->value();
+ settings.nbIter = d->iterationInput->value();
+ settings.tile = d->tileInput->value();
+ settings.btile = d->btileInput->value();
+
+ return settings;
+}
+
+bool GreycstorationWidget::loadSettings(TQFile& file, const TQString& header)
+{
+ TQTextStream stream( &file );
+
+ if (stream.readLine() != header)
+ return false;
+
+ blockSignals(true);
+
+ GreycstorationSettings settings;
+ settings.fastApprox = stream.readLine().toInt();
+ settings.interp = stream.readLine().toInt();
+ settings.amplitude = stream.readLine().toDouble();
+ settings.sharpness = stream.readLine().toDouble();
+ settings.anisotropy = stream.readLine().toDouble();
+ settings.alpha = stream.readLine().toDouble();
+ settings.sigma = stream.readLine().toDouble();
+ settings.gaussPrec = stream.readLine().toDouble();
+ settings.dl = stream.readLine().toDouble();
+ settings.da = stream.readLine().toDouble();
+ settings.nbIter = stream.readLine().toInt();
+ settings.tile = stream.readLine().toInt();
+ settings.btile = stream.readLine().toInt();
+ setSettings(settings);
+
+ blockSignals(false);
+ return true;
+}
+
+void GreycstorationWidget::saveSettings(TQFile& file, const TQString& header)
+{
+ GreycstorationSettings settings = getSettings();
+ TQTextStream stream( &file );
+ stream << header << "\n";
+ stream << settings.fastApprox << "\n";
+ stream << settings.interp << "\n";
+ stream << settings.amplitude << "\n";
+ stream << settings.sharpness << "\n";
+ stream << settings.anisotropy << "\n";
+ stream << settings.alpha << "\n";
+ stream << settings.sigma << "\n";
+ stream << settings.gaussPrec << "\n";
+ stream << settings.dl << "\n";
+ stream << settings.da << "\n";
+ stream << settings.nbIter << "\n";
+ stream << settings.tile << "\n";
+ stream << settings.btile << "\n";
+}
+
+} // NameSpace Digikam
diff --git a/src/libs/greycstoration/greycstorationwidget.h b/src/libs/greycstoration/greycstorationwidget.h
new file mode 100644
index 00000000..22797071
--- /dev/null
+++ b/src/libs/greycstoration/greycstorationwidget.h
@@ -0,0 +1,70 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2007-09-13
+ * Description : Greycstoration settings widgets
+ *
+ * Copyright (C) 2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * This program is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation;
+ * either version 2, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * ============================================================ */
+
+#ifndef GREYCSTORATION_WIDGET_H
+#define GREYCSTORATION_WIDGET_H
+
+// TQt includes.
+
+#include <tqobject.h>
+#include <tqfile.h>
+#include <tqstring.h>
+
+// Local includes.
+
+#include "digikam_export.h"
+#include "greycstorationsettings.h"
+
+class TQTabWidget;
+
+namespace Digikam
+{
+
+class GreycstorationWidgetPriv;
+
+class DIGIKAM_EXPORT GreycstorationWidget : public TQObject
+{
+ TQ_OBJECT
+
+
+public:
+
+ GreycstorationWidget(TQTabWidget *parent);
+ ~GreycstorationWidget();
+
+ void setSettings(GreycstorationSettings settings);
+ void setDefaultSettings(GreycstorationSettings settings);
+ GreycstorationSettings getSettings();
+
+ bool loadSettings(TQFile& file, const TQString& header);
+ void saveSettings(TQFile& file, const TQString& header);
+
+ void setEnabled(bool);
+
+private:
+
+ GreycstorationWidgetPriv* d;
+};
+
+} // NameSpace Digikam
+
+#endif /* GREYCSTORATION_WIDGET_H */